import type { TuiFrame } from '../platform/tui/protocol.js'; /** Display width (terminal columns) of a string — CJK/full-width chars count as 2. */ export declare function displayWidth(s: string): number; /** Pad a string with spaces up to `width` DISPLAY columns (no-op if already ≥ width). */ export declare function padToWidth(s: string, width: number): string; /** Split a line into before/selected/after by DISPLAY-column range [startCol, endCol). Each char * is classified by its starting column (a wide char straddling a boundary goes with its start). */ export declare function splitByDisplayCols(text: string, startCol: number, endCol: number): { before: string; selected: string; after: string; }; export type FocusZone = 'modal' | 'dashboard' | 'input'; export declare function computeFocusZone(opts: { modalOpen: boolean; sidePanelVisible: boolean; }): FocusZone; export declare function isAgentResponseFrame(frame: TuiFrame): boolean; /** One ordered unit in a stream: a discrete assistant text message, or an updatable region * (tool-call trace). Each TUI stream frame is a whole message — NOT a token — so blocks render * one per line, in arrival order. */ export interface StreamBlock { kind: 'text' | 'region'; /** Set for 'region' blocks so stream.mutableUpdate can find and replace it in place. */ regionId?: string; text: string; } export interface StreamLike { blocks: StreamBlock[]; } /** Join a message's stream blocks into display text — one block per line, in order, so distinct * assistant messages and interleaved tool traces appear as separate lines (not one merged blob). */ export declare function collectStreamText(streams: Map): string; export declare function computeVisibleWindow(idsLength: number, visibleCount: number, scrollOffset: number): { start: number; end: number; }; /** Estimate how many terminal rows a block of text occupies at the given width. */ export declare function estimateLines(text: string, width: number): number; /** * Bottom-anchored window over variable-height rows. `lineCounts[i]` is the estimated * height of row i. Returns absolute [start, end) including as many rows from the bottom * (offset by `scrollOffset` rows) as fit `budget` lines — always at least one row. */ export declare function computeLineWindow(lineCounts: number[], budget: number, scrollOffset?: number): { start: number; end: number; }; export declare function computeFocusWindow(total: number, focusedIndex: number, maxVisible: number): { start: number; end: number; hiddenAbove: number; hiddenBelow: number; }; export interface InputHistoryState { /** Index into the history array currently displayed, or null when showing the live draft. */ index: number | null; /** The in-progress text saved when navigation began (restored on exit). */ draft: string; } /** Step to an OLDER history entry (Up arrow). Returns the value to show + the next state. */ export declare function historyPrev(history: string[], state: InputHistoryState, current: string): { value: string; state: InputHistoryState; }; /** Step to a NEWER history entry (Down arrow). Past the newest entry, restores the draft. */ export declare function historyNext(history: string[], state: InputHistoryState, current: string): { value: string; state: InputHistoryState; }; /** Append a submitted entry to history, collapsing consecutive duplicates. */ export declare function pushHistory(history: string[], entry: string): string[]; export declare function matchResumeTarget(sessions: Array<{ sessionId: string; name?: string | null; }>, target: string): string | null; export declare function isMouseSequence(input: string): boolean; /** Map a flat cursor index to its zero-based (row, col) within a newline-delimited value. */ export declare function cursorToRowCol(value: string, cursor: number): { row: number; col: number; }; /** Map a (row, col) back to a flat cursor index, clamping row to the line count and col to that line. */ export declare function rowColToCursor(value: string, row: number, col: number): number; /** Move the cursor one logical line up (dir -1) or down (dir +1), preserving the column. */ export declare function moveCursorVertical(value: string, cursor: number, dir: -1 | 1): number; /** Remove bracketed-paste begin/end markers — both ESC[200~/ESC[201~ and the BARE [200~/[201~ * form (Ink consumes the leading ESC and forwards the remainder as text, so the markers arrive * without their escape and otherwise leak into the buffer). */ export declare function stripPasteMarkers(s: string): string; /** Collapse CRLF and bare CR to LF so pasted line endings are uniform. */ export declare function normalizeNewlines(s: string): string; /** Clean a pasted chunk for literal insertion: drop paste markers + escape sequences, normalize newlines. */ export declare function sanitizePastedText(s: string): string; export declare function classifyDeleteChunk(s: string): 'backspace' | 'forward-delete' | null; export interface TuiMouseEvent { type: 'press' | 'release' | 'drag' | 'wheel'; button: number; col: number; row: number; } /** Parse ALL SGR mouse events from a raw stdin chunk (press, release, drag, wheel). */ export declare function parseAllMouseEvents(chunk: string): TuiMouseEvent[]; /** Normalize a selection so start is always before end (top-left to bottom-right). */ export declare function normalizeSelection(startLine: number, startCol: number, endLine: number, endCol: number): { startLine: number; startCol: number; endLine: number; endCol: number; }; /** Extract selected text from flat display lines given a normalized selection range. Columns are * DISPLAY columns (from the mouse), so slicing is width-aware (CJK safe). */ export declare function extractSelectionText(lines: Array<{ text: string; }>, sel: { startLine: number; startCol: number; endLine: number; endCol: number; }): string; /** Copy text to the system clipboard via the OSC 52 terminal escape (BEL terminator). */ export declare function osc52Copy(text: string): void; /** Parse SGR mouse wheel events from a raw stdin chunk. 64=up, 65=down (low bit = direction). */ export declare function parseWheelEvents(chunk: string): Array<'up' | 'down'>; export interface FlatLine { text: string; /** Render dimmed (tool/context lines, streamed reply, queued marker). */ dim: boolean; /** Render through InlineMarkdown (false → plain Text, e.g. tool/context lines). */ markdown: boolean; /** A user-message line — rendered with a full-width grey background. */ user: boolean; } export interface FlattenableMessage { text?: string; richBlocks?: Array<{ type: string; text?: string; }>; /** Pre-collected streamed text (caller concatenates the stream map). */ streamText?: string; queued?: boolean; /** Whether this message is the user's own input (grey-background highlight, no "You:"). */ user?: boolean; } /** The prefix the server/echo uses to encode a user-role message in plain text. */ export declare const USER_PREFIX = "**You:** "; /** Detect a user message (by the isUser flag OR the `**You:** ` prefix) and strip the prefix. */ export declare function detectUserMessage(text: string, isUserFlag?: boolean): { text: string; user: boolean; }; /** Word-wrap a single logical line to `width` DISPLAY columns, hard-splitting over-wide words. * Measured in terminal columns (CJK aware) so Chinese/full-width text wraps where it actually * reaches the edge — matching what the terminal renders, which the selection mapping relies on. */ export declare function wrapToWidth(text: string, width: number): string[]; /** Flatten one message into wrapped display lines. */ export declare function flattenMessageLines(msg: FlattenableMessage, cols: number): FlatLine[]; /** Flatten the whole transcript to display lines, with a blank separator between messages. */ export declare function flattenTranscript(messages: FlattenableMessage[], cols: number): FlatLine[];