/** * Display-boundary sanitization for externally sourced text (model output, * tool payloads, skill descriptions). Control characters — including ANSI * CSI/OSC escape sequences — would otherwise pass through Ink into the * terminal, letting output rewrite the screen or inject prompts. Newlines * survive; everything else in C0/C1 plus DEL becomes a visible `\xNN` * escape, and bidi overrides / invisible format controls / Unicode line and * paragraph separators become a visible `\uXXXX` escape (terminal emulators * that render bidirectional text would otherwise reorder the displayed * glyphs and let a command read as something it is not). * * @module @deepseek-ai/dsh-code/render/text */ import { stringWidth } from './width.ts'; export { stringWidth }; /** * Compact token count: exact below 1K, then one-decimal-ish K/M. * @param n - token count. * @returns display string. */ export declare function formatTokens(n: number): string; /** * Escape control and deceptive characters so externally sourced text cannot * drive the terminal. C0/C1/DEL render as a literal `\xNN` escape; bidi, * invisible-format, and separator controls render as a literal `\uXXXX` * escape. Newlines and tabs survive (budgeted callers normalize tabs). * @param text - raw text from a session event, tool payload, or catalog. * @returns display-safe text with every injectable character made visible. */ export declare function displayText(text: string): string; /** Collapse external text to one terminal-safe logical row. */ export declare function singleLineText(text: string): string; /** * Truncate one display-safe row without ever exceeding its physical-column * budget. The ellipsis is included inside the budget, matching Codex's popup * truncation contract; the cut walks grapheme clusters so emoji and * combining sequences never split mid-cluster. */ export declare function truncateColumns(text: string, columns: number): string; /** Sanitize, clip, and right-pad one cell to an exact terminal-column width. */ export declare function padColumns(text: string, columns: number): string; /** A display-safe suffix bounded by terminal rows and columns. */ export interface DisplayTail { /** Sanitized suffix suitable for direct terminal rendering. */ text: string; /** Whether content before the returned suffix was omitted. */ truncated: boolean; } /** * Keep the newest display-safe text that fits a terminal rectangle, wrapping * FORWARD from the start of the text and slicing the tail rows. * * Forward wrapping is what keeps a streaming tail calm: rows already produced * never re-wrap as tokens append (a backward scan recomputes every wrap point * per chunk and the whole visible block jumps), and the wrap rules match the * settled text's renderer so the flush at turn end does not reflow the block * a second time. CJK kinsoku applies at both edges: closing punctuation * overhangs up to two cells onto the filled row instead of starting the next * one (within the caret column the caller reserves), and opening punctuation * moves down instead of dangling at a row end. Tabs expand to two spaces so * terminal tab stops cannot inflate the physical row count; clusters carry * emoji presentation and combining marks whole. * @param text - raw externally sourced text. * @param columns - available terminal columns. * @param rows - available terminal rows. * @returns a sanitized bounded suffix and whether an earlier prefix was cut. */ export declare function displayTail(text: string, columns: number, rows: number): DisplayTail;