UNPKG

@owloops/claude-powerline

Version:

Beautiful vim-style powerline statusline for Claude Code with real-time usage tracking, git integration, and custom themes

185 lines (161 loc) 4.68 kB
import type { BoxChars } from "./types"; import { visibleLength, stripAnsi, ESC, ANSI_SPLIT } from "../utils/terminal"; export function colorize( text: string, fgColor: string, reset: string, bold = false, ): string { const useBold = bold && reset !== ""; if (!fgColor && !useBold) { return text; } if (!useBold) { return `${fgColor}${text}${reset}`; } return `${fgColor}\x1b[1m${text}\x1b[22m${reset}`; } export function padRight(text: string, width: number): string { const visible = visibleLength(text); if (visible >= width) { return text; } return text + " ".repeat(width - visible); } export function padLeft(text: string, width: number): string { const visible = visibleLength(text); if (visible >= width) { return text; } return " ".repeat(width - visible) + text; } export function padCenter(text: string, width: number): string { const visible = visibleLength(text); if (visible >= width) { return text; } const totalPad = width - visible; const leftPad = Math.floor(totalPad / 2); const rightPad = totalPad - leftPad; return " ".repeat(leftPad) + text + " ".repeat(rightPad); } export function truncateAnsi(text: string, maxWidth: number): string { if (stripAnsi(text).length <= maxWidth) { return text; } let width = 0; let result = ""; const parts = text.split(ANSI_SPLIT); for (const part of parts) { if (part.startsWith(ESC)) { result += part; continue; } for (const char of part) { if (width >= maxWidth - 1) { result += "…\x1b[0m"; return result; } result += char; width++; } } return result; } export function contentRow( box: BoxChars, content: string, innerWidth: number, ): string { const maxContent = innerWidth - 2; const truncated = truncateAnsi(content, maxContent); const padded = padRight(truncated, maxContent); return box.vertical + " " + padded + " " + box.vertical; } export function divider(box: BoxChars, innerWidth: number): string { return box.teeLeft + box.horizontal.repeat(innerWidth) + box.teeRight; } export function bottomBorder( box: BoxChars, innerWidth: number, leftText?: string, rightText?: string, ): string { if (!leftText && !rightText) { return box.bottomLeft + box.horizontal.repeat(innerWidth) + box.bottomRight; } let left = leftText ? ` ${leftText} ` : ""; let right = rightText ? ` ${rightText} ` : ""; let leftLen = visibleLength(left); let rightLen = visibleLength(right); // Truncate if combined text exceeds innerWidth if (leftLen + rightLen > innerWidth) { const maxLeft = Math.max(0, innerWidth - rightLen); if (leftLen > maxLeft) { left = truncateAnsi(left, maxLeft); leftLen = visibleLength(left); } if (leftLen + rightLen > innerWidth) { const maxRight = Math.max(0, innerWidth - leftLen); right = truncateAnsi(right, maxRight); rightLen = visibleLength(right); } } const fillCount = innerWidth - leftLen - rightLen; return ( box.bottomLeft + left + box.horizontal.repeat(Math.max(0, fillCount)) + right + box.bottomRight ); } export function spreadEven(parts: string[], totalWidth: number): string { if (parts.length === 0) { return ""; } if (parts.length === 1) { return parts[0] ?? ""; } const widths = parts.map((p) => visibleLength(p)); const totalContentWidth = widths.reduce((sum, w) => sum + w, 0); const totalGap = totalWidth - totalContentWidth; const gapPerSlot = Math.max(2, Math.floor(totalGap / (parts.length - 1))); const suffixWidths = Array.from<number>({ length: parts.length }); suffixWidths[parts.length - 1] = widths[parts.length - 1] ?? 0; for (let i = parts.length - 2; i >= 0; i--) { suffixWidths[i] = (suffixWidths[i + 1] ?? 0) + (widths[i] ?? 0); } let result = parts[0] ?? ""; let usedWidth = widths[0] ?? 0; for (let i = 1; i < parts.length; i++) { const remaining = totalWidth - usedWidth - (suffixWidths[i] ?? 0) - (parts.length - 1 - i) * 2; const gap = Math.max(2, Math.min(gapPerSlot, remaining)); result += " ".repeat(gap) + (parts[i] ?? ""); usedWidth += gap + (widths[i] ?? 0); } return result; } export function spreadTwo( left: string, right: string, totalWidth: number, ): string { if (!right) { return left; } if (!left) { return right; } const leftLen = visibleLength(left); const rightLen = visibleLength(right); const gap = totalWidth - leftLen - rightLen; if (gap < 2) { return `${left} ${right}`; } return left + " ".repeat(gap) + right; }