/** * Presentation formatting: a value in, text out. * * Pure functions that know nothing about rows or widths, and exist as their own * module because the *same* reading is painted in several places (the status row, * the workspace row, `/status`, a transcript chip) and those call sites must not * each grow their own spelling of it. * * It also owns the context-pressure reading — parsing DSH's `contextPressure`, * its bands, and its alert text. That is a deliberate exception to "formatting * only": the chip, the status line and the thresholds are one decision about one * number, and splitting them would let the painted level drift from the level * compaction acts on. * * @module dsh-ssh-tui/footer-format */ import { type QuotaPeriod, type QuotaSource } from './quota.js'; /** Compact token count, matching the web stats line (517 / 12.2K / 1.2M). */ export declare function formatTokens(n: number): string; /** * Prompt occupancy of the next request, from DSH `contextPressure`. * Provider-agnostic: uses the routed model's advertised window, not a * hardcoded xAI size. Compaction-basic still owns in-turn pressure at 80%. */ export declare const CONTEXT_PRESSURE_WARN_RATIO = 0.8; export declare const CONTEXT_PRESSURE_DANGER_RATIO = 0.95; /** Idle auto-compact starts here so recovery finishes before the 80% in-turn trigger. */ export declare const CONTEXT_IDLE_COMPACT_RATIO = 0.72; export interface ContextPressureSample { usedTokens: number; contextWindow: number; } export interface ContextPressureView { usedTokens: number; contextWindow: number; percent: number; level: 'ok' | 'warn' | 'danger'; } /** Prompt-side occupancy of one usage sample: uncached input plus cache traffic. */ export declare function promptPressureTokens(usage: { inputTokens: number; cacheReadTokens?: number; cacheWriteTokens?: number; }): number; /** Prefer the next-request projection; fall back to last-request pressure. */ export declare function contextPressureUsedTokens(pressure: { projectedTokens?: number; pressureTokens?: number; } | undefined): number | undefined; export declare function parseContextPressure(value: unknown): ContextPressureSample | undefined; export declare function contextPressureView(sample: ContextPressureSample): ContextPressureView; /** * 8-segment Braille ring. Empty `⣀`; full `⣿`. Width is always 1 cell. * Index is `ceil(percent / 12.5)` clamped to 0..8. */ export declare const CONTEXT_RING_EMPTY = "\u28C0"; export declare const CONTEXT_RING_SEGMENTS: readonly ["⣀", "⠉", "⠋", "⠛", "⠞", "⠟", "⠿", "⡿", "⣿"]; /** The full cell of the same Braille family, for bars built like the ring. */ export declare const CONTEXT_RING_FULL = "\u28FF"; export declare function formatContextPressureRing(percent: number): string; export declare function contextPressureRingColor(level: ContextPressureView['level']): string; export declare function formatContextPressureChip(view: ContextPressureView, color?: boolean): string; export declare function formatContextPressureStatusLine(view: ContextPressureView | undefined): string; export declare function contextPressureAlertText(view: ContextPressureView): string; export declare function shouldIdleAutoCompact(view: ContextPressureView | undefined): boolean; /** * Compact elapsed time for the activity chip: `28s`, `3m12s`, `1h04m`. * * Distinct from {@link formatDuration} on purpose: that one is a session * statistic (`45.2s`), where a decimal is informative, while this one ticks * every second beside a spinner and should move in whole units. */ export declare function formatElapsedShort(ms: number): string; /** Compact duration, matching the web stats line (45.2s / 2m42s). */ export declare function formatDuration(ms: number): string; /** * @deprecated Legacy surface, removed in 0.9. * * The default row stopped using this when the footer became the status row * (`docs/plans/footer-debt.md`, D-7): what is left of it is the previous * generation's fitting and formatting, kept exported so 0.8.x does not break an * importer. New callers want the budget module's `runtimeStrip`, the meters, or * `formatStatusReport` for the spelled-out numbers. */ export declare function formatTokensPerSecond(tokensPerSecond: number): string; export declare function providerShortCode(provider: string): string; /** Short remaining-quota bar: 8 pips, filled from the left. */ export declare function formatQuotaBar(remainingPercent: number, width?: number): string; /** * `sub:` chip for the identity row: `sub:grok-4.5`, or * `sub:grok-4.5(xhigh)` when an explicit `/subeffort` is set. * * It shows the model name only, like the parent's own chip, unless `/submodel` * pinned a different provider — then `sub:xai/grok-4.5` so the foreign route * is visible on the identity row as well as the recolored chip. */ export declare function subagentRouteLabel(model: string, provider?: string, effort?: string): string; /** * The model as the footer shows it: the model's own name, without a * `provider/model` route prefix. * * Some providers carry the vendor in the model id (`xai/grok-4.6`, * `deepseek-official/deepseek-v4-flash`). The route is worth a row in the header * and in `/status`; in the status line it spent cells on something the reader * already knows, and it pushed the quota badge towards the drop edge. * `sub:` routes keep their provider on purpose — that is a *different* route * from the parent's, and hiding it would make the two indistinguishable. */ export declare function shortModelName(model: string): string; /** * `SuperGrok 5Hr ███████░ 82%` — short plan badge, the window the number belongs * to, then the bar. * * The tags are fixed technical labels (`5Hr` / `1Wk` / `1Mo`) rather than * localized words: they sit in a footer that is already fighting for cells, and * a window tag that changes with the locale would be unreadable in a screenshot * or a bug report. Everything else about the window (label, reset time, detail) * stays in `/quota`. */ /** * @deprecated Legacy surface, removed in 0.9. * * The default row stopped using this when the footer became the status row * (`docs/plans/footer-debt.md`, D-7): what is left of it is the previous * generation's fitting and formatting, kept exported so 0.8.x does not break an * importer. New callers want the budget module's `runtimeStrip`, the meters, or * `formatStatusReport` for the spelled-out numbers. */ export declare function formatFooterQuota(percent: number, code?: string, period?: QuotaPeriod): string; /** * The quota widget before the first reading arrives, or while the provider * cannot be reached: an empty bar and a question mark. * * Deliberately not `0%`: an unreachable quota API is not a used-up quota, and a * number the footer cannot back up is worse than no number. The bar is there * from the first frame so the row does not jump when the reading lands. */ /** * @deprecated Legacy surface, removed in 0.9. * * The default row stopped using this when the footer became the status row * (`docs/plans/footer-debt.md`, D-7): what is left of it is the previous * generation's fitting and formatting, kept exported so 0.8.x does not break an * importer. New callers want the budget module's `runtimeStrip`, the meters, or * `formatStatusReport` for the spelled-out numbers. */ export declare function formatQuotaUnknown(): string; /** Fixed-width tag for the window a footer quota number belongs to. */ export declare function quotaWindowTag(period: QuotaPeriod | undefined): string; /** * The window tag as an out-of-room row spells it: `5Hr` → `5h`, `1Wk` → `1w`. * * Deliberately not `toLowerCase()`: the tag encodes two facts in three cells * (how many, and which unit), and the unit letter is the one worth keeping — * `5hr` costs a cell to repeat that it is hours, which the `h` already said. */ export declare function quotaWindowTagShort(period: QuotaPeriod | undefined): string; /** * The badge the footer shows instead of the billing plan's own label: * `SuperGrok`, `OC·GO` (OpenCode Go), `CC·GOAT` (Command Code Goat). * * The long names live in `/quota`; this one has to survive next to a model id. * Without a `source` (a hand-built snapshot) the plan label is matched instead, * so old callers and tests keep a sensible badge. */ export declare function shortQuotaPlanName(snapshot: { plan: string; source?: QuotaSource; }): string; /** * The subscription a session is running on, spelled out for the footer's last * part: `OpenCode-GO`, `CommandCode-GOAT`. * * Only the two generations that carry a *named subscription* get one. SuperGrok * already says so in its quota chip's window tag, and DeepSeek is metered (its * balance line is the reading), so neither claims a badge here. The spelling is * longer than the identity row's old `OC·GO` / `CC·GOAT` on purpose: this one sits * at the very end of the row where the least load-bearing part goes, and a reader * who still has it on screen reads a plan name rather than an abbreviation. * * The provider id alone is enough for the Go route; Command Code's *tier* comes * from the billing reply, so before the first reading lands the badge names the * vendor without claiming a tier it has not seen. * @param provider - the live route's provider id. * @param snapshot - the quota reading, when it belongs to that provider. * @returns the badge, or undefined when this route has none. */ export declare function planRouteBadge(provider: string, snapshot?: { plan: string; source?: QuotaSource; }): string | undefined; /** * Drop the Go / SuperGrok plan name from a quota identity part, keeping the * remaining-percent bar. Returns true when a part was rewritten. */ /** * @deprecated Legacy surface, removed in 0.9. * * The default row stopped using this when the footer became the status row * (`docs/plans/footer-debt.md`, D-7): what is left of it is the previous * generation's fitting and formatting, kept exported so 0.8.x does not break an * importer. New callers want the budget module's `runtimeStrip`, the meters, or * `formatStatusReport` for the spelled-out numbers. */ export declare function dropFooterQuotaPlanName(parts: string[]): boolean; /** Human-facing kind for a live LLM route. */ export declare function describeProviderRoute(provider: string): { kind: string; short: string; }; /** Routes that authenticate without a harness API-key credential. */ export declare function providerUsesLocalOAuth(provider: string): boolean; /** * Whether a provider has a quota surface at all, without asking it. * * The footer paints a quota widget from the first frame — an empty bar and `?%` * until a reading arrives — so it needs to tell "this provider will have one" * from "this provider reports a balance instead" (DeepSeek) or nothing at all. * Every branch is a synchronous settings read, so a repaint never does IO. */ export declare function providerHasQuotaSurface(provider: string, llmPiAiSection: unknown): boolean;