@owloops/claude-powerline
Version:
Beautiful vim-style powerline statusline for Claude Code with real-time usage tracking, git integration, and custom themes
322 lines (301 loc) • 7.25 kB
text/typescript
import type { RenderCtx } from "./types";
import { formatCost } from "../utils/formatters";
import { resolveIconVisibility } from "../utils/icon-visibility";
import {
contentRow,
divider,
spreadEven,
spreadTwo,
colorize,
} from "./primitives";
import {
collectMetricSegments,
collectActivityParts,
collectWorkspaceParts,
collectFooterParts,
formatBlockSegment,
formatWeeklySegment,
formatSessionSegment,
formatTodaySegment,
} from "./sections";
// --- Wide layout (80+ cols): metrics on 1 line, workspace+footer on 1 line ---
export function renderWideMetrics(ctx: RenderCtx): void {
const {
lines,
data,
box,
contentWidth,
innerWidth,
sym,
config,
reset,
colors,
} = ctx;
const segments = collectMetricSegments(data, sym, config, reset, colors);
if (segments.length > 0) {
lines.push(contentRow(box, spreadEven(segments, contentWidth), innerWidth));
}
}
export function renderWideBottom(ctx: RenderCtx): void {
const {
lines,
data,
box,
contentWidth,
innerWidth,
sym,
config,
reset,
colors,
} = ctx;
const leftParts = collectWorkspaceParts(data, sym, reset, colors, config);
const rightParts = collectFooterParts(data, sym, config, reset, colors);
const leftStr = leftParts.join(" ");
const rightStr = rightParts.join(" · ");
if (leftStr || rightStr) {
lines.push(divider(box, innerWidth));
lines.push(
contentRow(box, spreadTwo(leftStr, rightStr, contentWidth), innerWidth),
);
}
}
// --- Medium layout (55-79 cols): metrics on 2 lines, workspace and footer separate ---
export function renderMediumMetrics(ctx: RenderCtx): void {
const {
lines,
data,
box,
contentWidth,
innerWidth,
sym,
config,
reset,
colors,
} = ctx;
const line1Parts: string[] = [];
const line2Parts: string[] = [];
const showBlockIcon = resolveIconVisibility(config, "block");
const showWeeklyIcon = resolveIconVisibility(config, "weekly");
const showTodayIcon = resolveIconVisibility(config, "today");
const showSessionIcon = resolveIconVisibility(config, "session");
if (data.blockInfo) {
line1Parts.push(
colorize(
formatBlockSegment(data.blockInfo, sym, config, showBlockIcon),
colors.blockFg,
reset,
colors.blockBold,
),
);
}
const sevenDay = data.hookData.rate_limits?.seven_day;
if (sevenDay) {
line1Parts.push(
colorize(
formatWeeklySegment(sevenDay, sym, showWeeklyIcon),
colors.weeklyFg,
reset,
colors.weeklyBold,
),
);
}
if (data.todayInfo) {
line1Parts.push(
colorize(
formatTodaySegment(data.todayInfo, sym, config, showTodayIcon),
colors.todayFg,
reset,
colors.todayBold,
),
);
}
if (data.usageInfo) {
line2Parts.push(
colorize(
formatSessionSegment(data.usageInfo, sym, config, showSessionIcon),
colors.sessionFg,
reset,
colors.sessionBold,
),
);
}
const activityParts = collectActivityParts(data, sym);
if (activityParts.length > 0) {
line2Parts.push(
colorize(
activityParts.join(" · "),
colors.metricsFg,
reset,
colors.metricsBold,
),
);
}
if (line1Parts.length > 0) {
lines.push(
contentRow(box, spreadEven(line1Parts, contentWidth), innerWidth),
);
}
if (line2Parts.length > 0) {
lines.push(
contentRow(
box,
spreadTwo(line2Parts[0] ?? "", line2Parts[1] ?? "", contentWidth),
innerWidth,
),
);
}
}
export function renderMediumBottom(ctx: RenderCtx): void {
const {
lines,
data,
box,
contentWidth,
innerWidth,
sym,
config,
reset,
colors,
} = ctx;
const workspaceParts = collectWorkspaceParts(
data,
sym,
reset,
colors,
config,
);
if (workspaceParts.length > 0) {
lines.push(divider(box, innerWidth));
lines.push(
contentRow(
box,
spreadTwo(
workspaceParts[0] ?? "",
workspaceParts[1] ?? "",
contentWidth,
),
innerWidth,
),
);
}
const footerParts = collectFooterParts(data, sym, config, reset, colors);
if (footerParts.length > 0) {
lines.push(divider(box, innerWidth));
lines.push(contentRow(box, footerParts.join(" · "), innerWidth));
}
}
// --- Narrow layout (<55 cols): everything stacks ---
export function renderNarrowMetrics(ctx: RenderCtx): void {
const {
lines,
data,
box,
contentWidth,
innerWidth,
sym,
config,
reset,
colors,
} = ctx;
const showBlockIcon = resolveIconVisibility(config, "block");
const showWeeklyIcon = resolveIconVisibility(config, "weekly");
const showSessionIcon = resolveIconVisibility(config, "session");
const showTodayIcon = resolveIconVisibility(config, "today");
if (data.blockInfo) {
lines.push(
contentRow(
box,
colorize(
formatBlockSegment(data.blockInfo, sym, config, showBlockIcon),
colors.blockFg,
reset,
colors.blockBold,
),
innerWidth,
),
);
}
const narrowSevenDay = data.hookData.rate_limits?.seven_day;
if (narrowSevenDay) {
lines.push(
contentRow(
box,
colorize(
formatWeeklySegment(narrowSevenDay, sym, showWeeklyIcon),
colors.weeklyFg,
reset,
colors.weeklyBold,
),
innerWidth,
),
);
}
const sessionAndToday: string[] = [];
if (data.usageInfo) {
const sessionText = showSessionIcon
? `${sym.session_cost} ${formatCost(data.usageInfo.session.cost)}`
: formatCost(data.usageInfo.session.cost);
sessionAndToday.push(
colorize(sessionText, colors.sessionFg, reset, colors.sessionBold),
);
}
if (data.todayInfo) {
const todayText = showTodayIcon
? `${sym.today_cost} ${formatCost(data.todayInfo.cost)} today`
: `${formatCost(data.todayInfo.cost)} today`;
sessionAndToday.push(
colorize(todayText, colors.todayFg, reset, colors.todayBold),
);
}
if (sessionAndToday.length > 0) {
lines.push(
contentRow(
box,
spreadTwo(
sessionAndToday[0] ?? "",
sessionAndToday[1] ?? "",
contentWidth,
),
innerWidth,
),
);
}
}
export function renderNarrowBottom(ctx: RenderCtx): void {
const {
lines,
data,
box,
contentWidth,
innerWidth,
sym,
config,
reset,
colors,
} = ctx;
const workspaceParts = collectWorkspaceParts(
data,
sym,
reset,
colors,
config,
);
if (workspaceParts.length > 0) {
lines.push(divider(box, innerWidth));
lines.push(
contentRow(
box,
spreadTwo(
workspaceParts[0] ?? "",
workspaceParts[1] ?? "",
contentWidth,
),
innerWidth,
),
);
}
const footerParts = collectFooterParts(data, sym, config, reset, colors);
if (footerParts.length > 0) {
lines.push(contentRow(box, footerParts.join(" · "), innerWidth));
}
}