/** * Canonical text, number and terminal-width formatting for rolebox display * surfaces: durations, timestamps, byte counts, locale-independent counts, * ANSI stripping, display width, truncation/padding, markdown table cells and * progress bars. * * The session browser, CLI monitor, dispatch / graph / LSP duration display * paths, the checkpoint list, the download progress renderer and the * notification formatter delegate here. These are pure functions: no I/O, no * `process`, no imports and no ambient locale — the output is a deterministic * function of the arguments. * * Not every display path delegates here. These still implement their own * width, timestamp or duration logic: * - `src/terminal/screen-buffer.ts` `charWidth` — a second width table for * screen-buffer cell accounting. * - `src/cli/commands/renderer/table-helpers.ts` and * `src/cli/commands/renderer/layout.ts` — UTF-16 code-unit widths composed * with the monitor helpers' column truncation. * - `src/platform/adapters/dsh/web-ui/rolebox-monitor-panel.tsx` — an * ambient-locale `toLocaleTimeString` timestamp. * - `src/loop/loop-tools.ts`, `src/loop/worker-dispatch.ts` and * `src/loop/coordinator.ts` — seconds-only `toFixed(1)s` duration * renderings. * * Deliberately still local, because their contract differs from the inclusive * display-column budget here: * - `src/tui/helpers.ts` `formatTimeAgo` — scales an elapsed duration (`3s`) * and only some callers append their own `" ago"` (`formatIsoAgo` in * `src/tui/components/TaskDetail.tsx` does not); it is not a timestamp * helper. * - `src/copilot/transcript.ts` `truncate` — appends the ellipsis *beyond* * `max` (up to `max + 1` UTF-16 code units). * - `src/cli/commands/memory/memory-helpers.ts` `truncate` — a UTF-16 * code-unit budget whose callers `.padEnd()` afterwards. * - `src/graph/engine/approval-payload.ts` `truncateSummary` — a fixed * `slice(0, 200)` with no ellipsis on a structured (non-display) payload. * * Hardening contract: display paths must never throw and must never render * `NaN` or `Infinity` text. Malformed input degrades to a documented fallback. * * @module */ /** * Display style of {@link formatDuration}. * * - `"clock"` — `0s` · `42s` · `1m 5s` · `1h 0m` · `25h 0m` — session tables / inspect * - `"monitor"` — `?` · `0ms` · `500ms` · `59s` · `1m` · `1m 1s` · `61m 1s` — CLI monitor + TUI * - `"narrow"` — `0s` · `500ms` · `59s` · `61m` — single-unit inline * - `"decimal"` — `?` · `999ms` · `1.0s` · `1m 5s` — engine / loop notifications * - `"stall"` — `?` · `2.5s` · `60.0s` · `1m` · `1m 1s` — stall idle * - `"largest"` — `0s` · `42s` · `12m` · `3h` · `2d` — one rounded unit */ export type DurationStyle = "clock" | "monitor" | "narrow" | "decimal" | "stall" | "largest"; /** * Format a millisecond duration in one of the six display styles. * * Every style is total: invalid input (non-finite or negative) yields the * style's sentinel instead of `NaN`/`Infinity` text. Minutes never roll into * hours in `"monitor"` or `"decimal"`; `"clock"` never rolls hours into days. * * Rounding happens on the raw millisecond value before any unit roll-over, so * the last value of a range can round up into the next unit's label: `"stall"` * renders 59 999 ms as `60.0s`, `"decimal"` renders it as `60.0s` too, * `"largest"` renders 3 599 000 ms as `60m` and 86 399 999 ms as `24h`. Those * renderings are pinned by tests and deliberately preserved — `formatBytes` is * the only formatter that promotes at its unit boundary. */ export declare function formatDuration(ms: number, style?: DurationStyle): string; /** * Format an epoch-millisecond timestamp as `YYYY-MM-DD HH:mm:ss` in UTC. * * Never throws: a non-finite value or one outside the ECMAScript `Date` range * returns `fallback` instead of raising `RangeError: Invalid Date`. Negative * epochs render normally. */ export declare function formatTimestamp(ms: number, fallback?: string): string; /** * Format an epoch-millisecond timestamp relative to `now`, e.g. `"5s ago"`. * * A non-finite `ms` (or non-finite derived delta) returns `"unknown"`; any * delta below one second — including future timestamps — is `"just now"`. */ export declare function formatRelativeTime(ms: number, now?: number): string; /** * Format a byte count with binary (`1024`) units by default, e.g. `1.2MB`. * * The suffix is attached without a space. Non-finite input yields `invalid`; * negative input is clamped to `0B`. Pass `binary: false` for decimal * (`1000`) scaling with the same unit labels. * * A value that rounds up to the next unit is promoted, so `1048575` renders * `1.0MB` rather than `1024KB`; the largest known unit is never promoted past * it (`1024 ** 5` stays `1024TB`). */ export declare function formatBytes(bytes: number, opts?: { binary?: boolean; invalid?: string; }): string; /** * Format a number with `,` thousands separators, e.g. `1,234,567`. * * Fractional digits are preserved verbatim when they render in plain decimal * notation (`1234.5` → `1,234.5`). A magnitude below 1 whose fraction renders * in exponential notation contributes no tail, so the grouped integer part is * emitted instead (`1e-7` → `0`, never `0e-7`). * * Deterministic and locale-independent — deliberately not `toLocaleString`. * Non-finite input yields `invalid`. */ export declare function formatCount(n: number, opts?: { invalid?: string; }): string; /** * Remove ANSI escape sequences: CSI, OSC (BEL- or ST-terminated) and * two-character escapes. Single pass, no backtracking blowup. */ export declare function stripAnsi(input: string): string; /** * Count the terminal columns of an ANSI-stripped string. * * Targets terminal alignment, not full UAX #11: combining marks and zero-width * characters count 0, C0/C1 controls count 0, East-Asian Wide/Fullwidth and * emoji blocks count 2, everything else counts 1. Ambiguous-width characters * count 1. */ export declare function displayWidth(input: string): number; /** * Truncate to `maxWidth` display columns, ellipsis included. * * Three budget cases: `+Infinity` means "no truncation" and returns the input * unchanged; `NaN`, `-Infinity` and any value `<= 0` return `""`; a finite * positive budget clips to whole display columns. * * Never splits a surrogate pair and never leaves a dangling ZWJ or an * unattached combining mark. ANSI sequences are preserved and cost no columns. * When the ellipsis alone already fills the budget the clipped ellipsis is * returned. */ export declare function truncateText(input: string, maxWidth: number, ellipsis?: string): string; /** * Pad with spaces on the right until `displayWidth` reaches `width`. * Never truncates; `width <= 0` (or non-finite) returns the input unchanged. * The padding emitted by one call is clamped to {@link MAX_DISPLAY_PAD} * columns, so a hostile width returns a bounded string instead of throwing. */ export declare function padDisplayEnd(input: string, width: number): string; /** * Pad with spaces on the left until `displayWidth` reaches `width`. * Never truncates; `width <= 0` (or non-finite) returns the input unchanged. * The padding emitted by one call is clamped to {@link MAX_DISPLAY_PAD} * columns, so a hostile width returns a bounded string instead of throwing. */ export declare function padDisplayStart(input: string, width: number): string; /** * Make a value safe as a single markdown table cell: `\` and `|` are escaped * (backslash first, so a literal `\|` round-trips), newlines become `
`, * TAB becomes a space and remaining C0/C1 controls are dropped. * * The guarantee is row structure only: raw HTML and other markdown syntax are * intentionally passed through unchanged, so a cell can never add or remove a * table column but may still render as markup. */ export declare function escapeMarkdownTableCell(value: string): string; /** * Render a GitHub-flavoured markdown table. * * Every cell is escaped with {@link escapeMarkdownTableCell}. Rows shorter than * `headers` are padded with empty cells; extra cells are clipped. An empty * header list returns `""`. The result has no trailing newline. */ export declare function renderMarkdownTable(headers: readonly string[], rows: readonly (readonly string[])[]): string; /** Filled and empty segment counts of a progress bar. */ export interface ProgressParts { filled: number; empty: number; } /** * Segment counts for a bar of `width` characters. * * A zero, negative or non-finite `total` — and any non-finite `current` — * yields an empty bar, so `0/0` and `NaN/5` can never render as complete. * Negative widths clamp to 0; fractional widths truncate. The width is capped * at {@link MAX_DISPLAY_PAD} so a hostile width cannot drive the unbounded * `String.repeat` in {@link progressBar}. */ export declare function progressBarParts(current: number, total: number, width?: number): ProgressParts; /** * Draw a progress bar, e.g. `■■■□□□` for `5/10` at width 6. * * Glyphs default to `■` and `□`; see {@link progressBarParts} for the * invalid-input behaviour. */ export declare function progressBar(current: number, total: number, width?: number, glyphs?: { filled?: string; empty?: string; }): string; //# sourceMappingURL=text-format.d.ts.map