{"version":3,"file":"StatusLine.d.ts","sourceRoot":"","sources":["../../src/components/StatusLine.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Status line — Claude Code v2.1.119 compatible.\n *\n * Schema (from `settings.json`):\n *\n *   {\n *     \"statusLine\": {\n *       \"type\": \"command\" | \"default\" | \"detailed\",\n *       \"command\": \"/path/to/script.sh\",   // when type === \"command\"\n *       \"padding\": 0\n *     }\n *   }\n *\n * When `type === \"command\"`, cave invokes the binary with a JSON context\n * payload on stdin and renders stdout (single-line, ANSI-stripped to\n * terminal width). The schema is identical to Claude Code so a user pasting\n * `~/.claude/settings.json#statusLine` into `~/.cave/settings.json` Just\n * Works.\n */\nimport type { Component } from \"../tui.js\";\nimport { truncateToWidth, visibleWidth } from \"../utils.js\";\n\n/**\n * Claude-Code-shaped statusLine setting. The `type` field is the union\n * authority; unknown types fall back to \"default\".\n */\nexport interface StatusLineSettings {\n\ttype?: \"command\" | \"default\" | \"detailed\";\n\t/** Shell command to run (only when `type === \"command\"`). */\n\tcommand?: string;\n\t/** Left padding in cells. */\n\tpadding?: number;\n}\n\n/**\n * JSON context that cave pipes to a `command`-type status line on stdin.\n * Matches Claude Code's documented shape closely enough that scripts written\n * for Claude Code work with cave (and vice versa).\n */\nexport interface StatusLineContext {\n\thook_event_name: \"Status\";\n\tsession_id: string;\n\ttranscript_path?: string;\n\tcwd: string;\n\tmodel: { id: string; display_name?: string };\n\tworkspace: { current_dir: string; project_dir: string };\n\tversion?: string;\n\toutput_style?: { name: string };\n\tcost?: { total_cost_usd: number; total_duration_ms: number };\n\texceeds_200k_tokens?: boolean;\n\t/** Cave-specific extras. Claude Code ignores keys it doesn't know. */\n\tcave?: {\n\t\tbranch?: string;\n\t\tgitDirty?: boolean;\n\t\tqueuedMessages?: number;\n\t\ttokensIn?: number;\n\t\ttokensOut?: number;\n\t\t/** Savings Meter (DD §10.8): caveman bytes eliminated this session. */\n\t\tsavedBytes?: number;\n\t};\n}\n\n/** Result of rendering — separated from Component so callers can compose. */\nexport interface StatusLineResult {\n\ttext: string;\n\t/** Where the text came from. Useful for surfacing errors in /doctor. */\n\tsource: \"default\" | \"detailed\" | \"command\" | \"command-failed\";\n\tstderr?: string;\n}\n\n/**\n * Build the default (terse) status line. Format:\n *   <model> · <cwd-tail>\n */\nexport function renderDefault(ctx: StatusLineContext): string {\n\tconst model = ctx.model.display_name ?? ctx.model.id;\n\treturn `${model} · ${tailPath(ctx.workspace.current_dir, 2)}`;\n}\n\n/**\n * Build the detailed status line. Format:\n *   <model> · <branch>(<dirty>) · <cwd-tail> · q:<n> · $<cost>\n */\nexport function renderDetailed(ctx: StatusLineContext): string {\n\tconst parts: string[] = [];\n\tconst model = ctx.model.display_name ?? ctx.model.id;\n\tparts.push(model);\n\n\tconst branch = ctx.cave?.branch;\n\tif (branch) {\n\t\tparts.push(`${branch}${ctx.cave?.gitDirty ? \"*\" : \"\"}`);\n\t}\n\n\tparts.push(tailPath(ctx.workspace.current_dir, 2));\n\n\tconst queued = ctx.cave?.queuedMessages ?? 0;\n\tif (queued > 0) parts.push(`q:${queued}`);\n\n\tconst savedBytes = ctx.cave?.savedBytes ?? 0;\n\tif (savedBytes > 0) parts.push(`saved ${formatSavedBytes(savedBytes)}`);\n\n\tif (ctx.cost && ctx.cost.total_cost_usd > 0) {\n\t\tparts.push(`$${ctx.cost.total_cost_usd.toFixed(4)}`);\n\t}\n\n\tif (ctx.exceeds_200k_tokens) parts.push(\"⚠ 200k\");\n\n\treturn parts.join(\" · \");\n}\n\n/** Compact saved-bytes for the status line: 940 → \"940B\", 4200 → \"4kB\", 1.5M → \"1.4MB\". */\nfunction formatSavedBytes(n: number): string {\n\tif (n < 1024) return `${n}B`;\n\tif (n < 1024 * 1024) return `${Math.round(n / 1024)}kB`;\n\treturn `${(n / (1024 * 1024)).toFixed(1)}MB`;\n}\n\n/**\n * Truncate a filesystem path to the trailing N components.\n *\n *   tailPath(\"/a/b/c/d\", 2) → \"c/d\"\n *   tailPath(\"/usr\", 2)     → \"/usr\"\n */\nexport function tailPath(path: string, components: number): string {\n\tif (!path) return \"\";\n\tconst parts = path.split(/[\\\\/]/).filter(Boolean);\n\tif (parts.length <= components) return path;\n\treturn parts.slice(-components).join(\"/\");\n}\n\n/**\n * Resolve a status line settings block to text. Synchronous helpers only —\n * the `command` type is async and lives on the StatusLine component below.\n */\nexport function renderStatusLineSync(settings: StatusLineSettings, ctx: StatusLineContext): StatusLineResult {\n\tconst type = settings.type ?? \"default\";\n\tif (type === \"detailed\") return { text: renderDetailed(ctx), source: \"detailed\" };\n\tif (type === \"command\") {\n\t\t// Sync caller cannot run the command; surface the default.\n\t\treturn { text: renderDefault(ctx), source: \"default\" };\n\t}\n\treturn { text: renderDefault(ctx), source: \"default\" };\n}\n\n/**\n * Validate and coerce an unknown settings.json `statusLine` value into a\n * concrete StatusLineSettings. Returns `undefined` only when the value is\n * malformed in a way that cannot be safely rendered.\n */\nexport function parseStatusLineSettings(raw: unknown): StatusLineSettings | undefined {\n\tif (raw === undefined || raw === null) return undefined;\n\tif (typeof raw !== \"object\" || Array.isArray(raw)) return undefined;\n\tconst o = raw as Record<string, unknown>;\n\n\tconst out: StatusLineSettings = {};\n\n\tif (typeof o.type === \"string\") {\n\t\tif (o.type === \"command\" || o.type === \"default\" || o.type === \"detailed\") {\n\t\t\tout.type = o.type;\n\t\t} else {\n\t\t\tout.type = \"default\";\n\t\t}\n\t}\n\tif (typeof o.command === \"string\" && o.command.trim().length > 0) {\n\t\tout.command = o.command;\n\t}\n\tif (typeof o.padding === \"number\" && Number.isFinite(o.padding) && o.padding >= 0) {\n\t\tout.padding = Math.floor(o.padding);\n\t}\n\n\t// If type is \"command\" but no command field, downgrade to \"default\".\n\tif (out.type === \"command\" && !out.command) {\n\t\tout.type = \"default\";\n\t}\n\n\treturn out;\n}\n\nexport interface StatusLineRenderer {\n\t/**\n\t * Async render — runs the configured command if any. Implementations are\n\t * provided by the coding-agent (which has access to bash-executor); the\n\t * TUI component takes a renderer and only does layout.\n\t */\n\trender(ctx: StatusLineContext): Promise<StatusLineResult>;\n}\n\nexport interface StatusLineComponentTheme {\n\tbg: (text: string) => string;\n\tmuted: (text: string) => string;\n\terror: (text: string) => string;\n}\n\nconst IDENTITY_THEME: StatusLineComponentTheme = {\n\tbg: (s) => s,\n\tmuted: (s) => s,\n\terror: (s) => s,\n};\n\n/**\n * Single-line status component. Renders the current cached text. Callers\n * call `setText()` when the renderer produces a new result.\n */\nexport class StatusLine implements Component {\n\tprivate text = \"\";\n\tprivate source: StatusLineResult[\"source\"] = \"default\";\n\tprivate theme: StatusLineComponentTheme;\n\tprivate padding: number;\n\n\tconstructor(options: { theme?: StatusLineComponentTheme; padding?: number } = {}) {\n\t\tthis.theme = options.theme ?? IDENTITY_THEME;\n\t\tthis.padding = options.padding ?? 0;\n\t}\n\n\tsetText(result: StatusLineResult): void {\n\t\tthis.text = sanitizeOneLine(result.text);\n\t\tthis.source = result.source;\n\t}\n\n\tinvalidate(): void {\n\t\t// Stateless render — nothing to clear.\n\t}\n\n\trender(width: number): string[] {\n\t\tconst pad = \" \".repeat(this.padding);\n\t\tconst inner = `${pad}${this.text}`;\n\t\tconst truncated = truncateToWidth(inner, width);\n\t\t// Pad to width so the status line owns its row even with diff backgrounds.\n\t\tconst w = visibleWidth(truncated);\n\t\tconst padded = w < width ? truncated + \" \".repeat(width - w) : truncated;\n\t\tconst styled = this.source === \"command-failed\" ? this.theme.error(padded) : this.theme.bg(padded);\n\t\treturn [styled];\n\t}\n}\n\n/** Strip newlines/carriage returns; status line is single-row by contract. */\nexport function sanitizeOneLine(s: string): string {\n\treturn s.replace(/[\\r\\n]+/g, \" \").trim();\n}\n"]}