@prometheus-ai/tui 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/README.md +704 -0
  3. package/dist/types/autocomplete.d.ts +76 -0
  4. package/dist/types/bracketed-paste.d.ts +26 -0
  5. package/dist/types/components/box.d.ts +17 -0
  6. package/dist/types/components/cancellable-loader.d.ts +21 -0
  7. package/dist/types/components/editor.d.ts +105 -0
  8. package/dist/types/components/image.d.ts +84 -0
  9. package/dist/types/components/input.d.ts +18 -0
  10. package/dist/types/components/loader.d.ts +13 -0
  11. package/dist/types/components/markdown.d.ts +61 -0
  12. package/dist/types/components/scroll-view.d.ts +40 -0
  13. package/dist/types/components/select-list.d.ts +48 -0
  14. package/dist/types/components/settings-list.d.ts +41 -0
  15. package/dist/types/components/spacer.d.ts +11 -0
  16. package/dist/types/components/tab-bar.d.ts +56 -0
  17. package/dist/types/components/text.d.ts +13 -0
  18. package/dist/types/components/truncated-text.d.ts +10 -0
  19. package/dist/types/deccara.d.ts +49 -0
  20. package/dist/types/editor-component.d.ts +36 -0
  21. package/dist/types/fuzzy.d.ts +15 -0
  22. package/dist/types/index.d.ts +28 -0
  23. package/dist/types/keybindings.d.ts +189 -0
  24. package/dist/types/keys.d.ts +208 -0
  25. package/dist/types/kill-ring.d.ts +27 -0
  26. package/dist/types/kitty-graphics.d.ts +94 -0
  27. package/dist/types/stdin-buffer.d.ts +43 -0
  28. package/dist/types/symbols.d.ts +25 -0
  29. package/dist/types/terminal-capabilities.d.ts +196 -0
  30. package/dist/types/terminal.d.ts +103 -0
  31. package/dist/types/ttyid.d.ts +9 -0
  32. package/dist/types/tui.d.ts +275 -0
  33. package/dist/types/utils.d.ts +89 -0
  34. package/package.json +73 -0
  35. package/src/autocomplete.ts +871 -0
  36. package/src/bracketed-paste.ts +47 -0
  37. package/src/components/box.ts +156 -0
  38. package/src/components/cancellable-loader.ts +40 -0
  39. package/src/components/editor.ts +2695 -0
  40. package/src/components/image.ts +318 -0
  41. package/src/components/input.ts +459 -0
  42. package/src/components/loader.ts +86 -0
  43. package/src/components/markdown.ts +1189 -0
  44. package/src/components/scroll-view.ts +166 -0
  45. package/src/components/select-list.ts +331 -0
  46. package/src/components/settings-list.ts +212 -0
  47. package/src/components/spacer.ts +28 -0
  48. package/src/components/tab-bar.ts +175 -0
  49. package/src/components/text.ts +110 -0
  50. package/src/components/truncated-text.ts +61 -0
  51. package/src/deccara.ts +314 -0
  52. package/src/editor-component.ts +71 -0
  53. package/src/fuzzy.ts +143 -0
  54. package/src/index.ts +44 -0
  55. package/src/keybindings.ts +279 -0
  56. package/src/keys.ts +537 -0
  57. package/src/kill-ring.ts +46 -0
  58. package/src/kitty-graphics.ts +270 -0
  59. package/src/stdin-buffer.ts +423 -0
  60. package/src/symbols.ts +26 -0
  61. package/src/terminal-capabilities.ts +1009 -0
  62. package/src/terminal.ts +1114 -0
  63. package/src/ttyid.ts +70 -0
  64. package/src/tui.ts +2988 -0
  65. package/src/utils.ts +452 -0
@@ -0,0 +1,43 @@
1
+ /**
2
+ * StdinBuffer buffers input and emits complete sequences.
3
+ *
4
+ * This is necessary because stdin data events can arrive in partial chunks,
5
+ * especially for escape sequences like mouse events. Without buffering,
6
+ * partial sequences can be misinterpreted as regular keypresses.
7
+ *
8
+ * For example, the mouse SGR sequence `\x1b[<35;20;5m` might arrive as:
9
+ * - Event 1: `\x1b`
10
+ * - Event 2: `[<35`
11
+ * - Event 3: `;20;5m`
12
+ *
13
+ * The buffer accumulates these until a complete sequence is detected.
14
+ * Call the `process()` method to feed input data.
15
+ *
16
+ * Based on code from OpenTUI (https://github.com/anomalyco/opentui)
17
+ * MIT License - Copyright (c) 2025 opentui
18
+ */
19
+ import { EventEmitter } from "events";
20
+ export type StdinBufferOptions = {
21
+ /**
22
+ * Maximum time to wait for sequence completion (default: 75ms).
23
+ * After this time, a genuinely incomplete escape is flushed.
24
+ */
25
+ timeout?: number;
26
+ };
27
+ export type StdinBufferEventMap = {
28
+ data: [string];
29
+ paste: [string];
30
+ };
31
+ /**
32
+ * Buffers stdin input and emits complete sequences via the 'data' event.
33
+ * Handles partial escape sequences that arrive across multiple chunks.
34
+ */
35
+ export declare class StdinBuffer extends EventEmitter<StdinBufferEventMap> {
36
+ #private;
37
+ constructor(options?: StdinBufferOptions);
38
+ process(data: string | Buffer): void;
39
+ flush(): string[];
40
+ clear(): void;
41
+ getBuffer(): string;
42
+ destroy(): void;
43
+ }
@@ -0,0 +1,25 @@
1
+ export interface BoxSymbols {
2
+ topLeft: string;
3
+ topRight: string;
4
+ bottomLeft: string;
5
+ bottomRight: string;
6
+ horizontal: string;
7
+ vertical: string;
8
+ teeDown: string;
9
+ teeUp: string;
10
+ teeLeft: string;
11
+ teeRight: string;
12
+ cross: string;
13
+ }
14
+ export interface SymbolTheme {
15
+ cursor: string;
16
+ inputCursor: string;
17
+ boxRound: Omit<BoxSymbols, "teeDown" | "teeUp" | "teeLeft" | "teeRight" | "cross">;
18
+ boxSharp: BoxSymbols;
19
+ table: BoxSymbols;
20
+ quoteBorder: string;
21
+ hrChar: string;
22
+ /** Chip glyph drawn (painted with the referenced color) before inline hex colors. */
23
+ colorSwatch?: string;
24
+ spinnerFrames: string[];
25
+ }
@@ -0,0 +1,196 @@
1
+ export declare enum ImageProtocol {
2
+ Kitty = "\u001B_G",
3
+ Iterm2 = "\u001B]1337;File=",
4
+ Sixel = "\u001BPq"
5
+ }
6
+ export declare enum NotifyProtocol {
7
+ Bell = "\u0007",
8
+ Osc99 = "\u001B]99;;",
9
+ Osc9 = "\u001B]9;"
10
+ }
11
+ export type TerminalId = "kitty" | "ghostty" | "wezterm" | "iterm2" | "vscode" | "alacritty" | "base" | "trueColor";
12
+ /** Terminal capability details used for rendering and protocol selection. */
13
+ export declare class TerminalInfo {
14
+ readonly id: TerminalId;
15
+ readonly imageProtocol: ImageProtocol | null;
16
+ readonly trueColor: boolean;
17
+ readonly hyperlinks: boolean;
18
+ readonly notifyProtocol: NotifyProtocol;
19
+ readonly eagerEraseScrollbackRisk: boolean;
20
+ readonly deccara: boolean;
21
+ readonly supportsScreenToScrollback: boolean;
22
+ /** Renders the Kitty OSC 66 text-sizing protocol (scaled spans). Kitty only. */
23
+ readonly textSizing: boolean;
24
+ constructor(id: TerminalId, imageProtocol: ImageProtocol | null, trueColor: boolean, hyperlinks: boolean, notifyProtocol?: NotifyProtocol, eagerEraseScrollbackRisk?: boolean, deccara?: boolean, supportsScreenToScrollback?: boolean,
25
+ /** Renders the Kitty OSC 66 text-sizing protocol (scaled spans). Kitty only. */
26
+ textSizing?: boolean);
27
+ isImageLine(line: string): boolean;
28
+ formatNotification(message: string | TerminalNotification): string;
29
+ sendNotification(message: string | TerminalNotification): void;
30
+ }
31
+ export declare function isNotificationSuppressed(): boolean;
32
+ /**
33
+ * Returns true when running in Windows Terminal with known SIXEL support.
34
+ *
35
+ * Windows Terminal introduced SIXEL support in preview 1.22.
36
+ */
37
+ export declare function isWindowsTerminalPreviewSixelSupported(env?: NodeJS.ProcessEnv, platform?: NodeJS.Platform): boolean;
38
+ /**
39
+ * Whether live-frame native scrollback rebuilds are unsafe when the terminal
40
+ * viewport position is unobservable.
41
+ *
42
+ * A TUI history rebuild emits xterm ED3 (`CSI 3 J`, erase saved lines). Many
43
+ * terminals either clamp a scrolled reader back to the active tail or erase host
44
+ * scrollback when ED3 lands. The important property is not the brand name — it
45
+ * is that an unknown viewport position cannot be proven safe. Environment
46
+ * markers are therefore only used to prove *risk* or a strongly-known profile;
47
+ * unknown POSIX/remote/multiplexer shapes default to risky for passive renders.
48
+ *
49
+ * Native win32 is excluded here because the renderer has dedicated ConPTY
50
+ * deferral paths; a `WT_SESSION` sighting on POSIX means Windows Terminal is the
51
+ * outer host fronting WSL, where the same ED3 yank applies. See #1610/#1682/#1799.
52
+ */
53
+ export declare function detectTerminalEagerEraseScrollbackRisk(env?: NodeJS.ProcessEnv, platform?: NodeJS.Platform): boolean;
54
+ /** Whether DEC 2026 synchronized-output wrappers should be enabled by default. */
55
+ export declare function shouldEnableSynchronizedOutputByDefault(env?: NodeJS.ProcessEnv, platform?: NodeJS.Platform, terminalId?: TerminalId): boolean;
56
+ /**
57
+ * Whether the terminal applies Kitty-style DECCARA rectangular SGR changes
58
+ * (`CSI Pt ; Pl ; Pb ; Pr ; <sgr> $ r`) extended to background color, so large
59
+ * filled regions can be painted as rectangles instead of background-padded
60
+ * strings on every row.
61
+ *
62
+ * Verified against terminal sources rather than terminfo, because a bare
63
+ * `Cara`/DECCARA terminfo capability does not imply the Kitty SGR-background
64
+ * extension:
65
+ * - Kitty implements it for *all* SGR attributes including background (see
66
+ * kitty `docs/deccara.rst` and the `test_deccara` parser test).
67
+ * - Ghostty does NOT: its `CSI $ r` dispatch falls through to an "unknown CSI"
68
+ * warning and DECCARA/DECSACE are tracked as unsupported
69
+ * (ghostty-org/ghostty#632). Enabling it there would silently drop panel
70
+ * backgrounds, so ghostty stays on the padded-string fallback.
71
+ *
72
+ * Disabled under tmux/screen/zellij multiplexers — screen-coordinate rectangle
73
+ * protocols are not safe to assume through a multiplexer — and via the
74
+ * `PROMETHEUS_NO_DECCARA` kill switch. Pure helper for tests and `TERMINAL` construction.
75
+ */
76
+ export declare function detectRectangularSgrSupport(terminalId: TerminalId, env?: NodeJS.ProcessEnv): boolean;
77
+ export declare const TERMINAL_ID: TerminalId;
78
+ export declare const TERMINAL: TerminalInfo;
79
+ /**
80
+ * Override terminal image protocol at runtime after capability probes complete.
81
+ */
82
+ export declare function setTerminalImageProtocol(imageProtocol: ImageProtocol | null): void;
83
+ /**
84
+ * Override DECCARA rectangular-SGR capability at runtime. Used by tests to
85
+ * exercise the optimizer and fallback paths deterministically — the default is
86
+ * resolved once at import and force-disabled under the test runtime.
87
+ */
88
+ export declare function setTerminalDeccara(enabled: boolean): void;
89
+ /** Override screen-to-scrollback clear support for targeted renderer tests. */
90
+ export declare function setTerminalScreenToScrollback(enabled: boolean): void;
91
+ /**
92
+ * Enable/disable OSC 66 text-sizing at runtime. The coding-agent calls this from
93
+ * the `tui.textSizing` setting (gated on the terminal's static `textSizing`
94
+ * capability); tests flip it directly to exercise the scaled-heading path.
95
+ */
96
+ export declare function setTerminalTextSizing(enabled: boolean): void;
97
+ export declare function getTerminalInfo(terminalId: TerminalId): TerminalInfo;
98
+ export interface CellDimensions {
99
+ widthPx: number;
100
+ heightPx: number;
101
+ }
102
+ export interface ImageDimensions {
103
+ widthPx: number;
104
+ heightPx: number;
105
+ }
106
+ export interface ImageRenderOptions {
107
+ maxWidthCells?: number;
108
+ maxHeightCells?: number;
109
+ preserveAspectRatio?: boolean;
110
+ /**
111
+ * Stable Kitty image id (`i=`). When set, the image is displayed via a
112
+ * transmit-once + placement scheme keyed off this id instead of re-sending the
113
+ * base64 each frame.
114
+ */
115
+ imageId?: number;
116
+ /** Stable Kitty placement id (`p=`); defaults to {@link imageId}. */
117
+ placementId?: number;
118
+ /** When true (Kitty + {@link imageId}), also return the one-time transmit sequence. */
119
+ includeTransmit?: boolean;
120
+ }
121
+ export declare function getCellDimensions(): CellDimensions;
122
+ export declare function setCellDimensions(dims: CellDimensions): void;
123
+ /** Transmit-and-display (`a=T`) — the self-contained form used when no stable id is available. */
124
+ export declare function encodeKitty(base64Data: string, options?: {
125
+ columns?: number;
126
+ rows?: number;
127
+ imageId?: number;
128
+ }): string;
129
+ /**
130
+ * Transmit image data only (`a=t`), keyed by `imageId`, without displaying it.
131
+ * Sent once per image; the data then persists in the terminal's store (it
132
+ * survives scroll-off and text clears for images with a non-zero id), so
133
+ * subsequent frames display it with the tiny {@link encodeKittyPlacement}
134
+ * sequence instead of re-sending the base64.
135
+ */
136
+ export declare function encodeKittyTransmit(base64Data: string, imageId: number): string;
137
+ /**
138
+ * Display a previously transmitted image (`a=p`) at the cursor. Carrying a
139
+ * stable `placementId` (`p=`) means re-emitting the sequence on a repaint
140
+ * *replaces* the existing placement (moving/resizing it without flicker) rather
141
+ * than stacking a duplicate.
142
+ */
143
+ export declare function encodeKittyPlacement(options: {
144
+ imageId: number;
145
+ placementId?: number;
146
+ columns?: number;
147
+ rows?: number;
148
+ }): string;
149
+ /**
150
+ * Kitty graphics delete command for a single image id. Uses `d=I` (capital)
151
+ * which removes the image and every one of its placements — on screen *and* in
152
+ * scrollback — and frees the backing data. `q=2` suppresses the terminal reply.
153
+ * Text-clearing escapes (`CSI 2 J` / `CSI 3 J`) do not remove Kitty graphics, so
154
+ * this is the only way to actually purge a placed image.
155
+ */
156
+ export declare function encodeKittyDeleteImage(imageId: number): string;
157
+ export declare function encodeITerm2(base64Data: string, options?: {
158
+ width?: number | string;
159
+ height?: number | string;
160
+ name?: string;
161
+ preserveAspectRatio?: boolean;
162
+ inline?: boolean;
163
+ }): string;
164
+ export declare function calculateImageRows(imageDimensions: ImageDimensions, targetWidthCells: number, cellDimensions?: CellDimensions): number;
165
+ export declare function getPngDimensions(base64Data: string): ImageDimensions | null;
166
+ export declare function getJpegDimensions(base64Data: string): ImageDimensions | null;
167
+ export declare function getGifDimensions(base64Data: string): ImageDimensions | null;
168
+ export declare function getWebpDimensions(base64Data: string): ImageDimensions | null;
169
+ export declare function getImageDimensions(base64Data: string, mimeType: string): ImageDimensions | null;
170
+ export declare function renderImage(base64Data: string, imageDimensions: ImageDimensions, options?: ImageRenderOptions): {
171
+ sequence?: string;
172
+ lines?: string[];
173
+ rows: number;
174
+ transmit?: string;
175
+ } | null;
176
+ export declare function imageFallback(mimeType: string, dimensions?: ImageDimensions, filename?: string): string;
177
+ /**
178
+ * Structured terminal notification. Rich fields are honored only by OSC 99
179
+ * (Kitty) once support is confirmed; other protocols and the unconfirmed Kitty
180
+ * path collapse to a single `title: body` line.
181
+ */
182
+ export interface TerminalNotification {
183
+ title?: string;
184
+ body?: string;
185
+ id?: string;
186
+ type?: string | string[];
187
+ urgency?: "low" | "normal" | "critical";
188
+ iconName?: string;
189
+ sound?: "silent" | "system" | "info" | "warning" | "error" | "question";
190
+ actions?: "focus" | "report" | "focus-report" | "none";
191
+ expiresMs?: number;
192
+ }
193
+ /** Record the OSC 99 capability-probe result (called by ProcessTerminal). */
194
+ export declare function setOsc99Supported(supported: boolean): void;
195
+ /** True when OSC 99 structured notifications have been confirmed available. */
196
+ export declare function isOsc99Supported(): boolean;
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Emergency terminal restore - call this from signal/crash handlers
3
+ * Resets terminal state without requiring access to the ProcessTerminal instance
4
+ */
5
+ export declare function emergencyTerminalRestore(): void;
6
+ /** Terminal-reported appearance (dark/light mode). */
7
+ export type TerminalAppearance = "dark" | "light";
8
+ export interface Terminal {
9
+ start(onInput: (data: string) => void, onResize: () => void): void;
10
+ stop(): void;
11
+ /**
12
+ * Drain stdin before exiting to prevent Kitty key release events from
13
+ * leaking to the parent shell over slow SSH connections.
14
+ * @param maxMs - Maximum time to drain (default: 1000ms)
15
+ * @param idleMs - Exit early if no input arrives within this time (default: 50ms)
16
+ */
17
+ drainInput(maxMs?: number, idleMs?: number): Promise<void>;
18
+ write(data: string): void;
19
+ get columns(): number;
20
+ get rows(): number;
21
+ get kittyProtocolActive(): boolean;
22
+ moveBy(lines: number): void;
23
+ hideCursor(): void;
24
+ showCursor(): void;
25
+ clearLine(): void;
26
+ clearFromCursor(): void;
27
+ clearScreen(): void;
28
+ setTitle(title: string): void;
29
+ setProgress(active: boolean): void;
30
+ /**
31
+ * Returns whether the native terminal viewport is at the scrollback tail when
32
+ * the host exposes that state. `undefined` means the terminal cannot report it.
33
+ *
34
+ * `ProcessTerminal` deliberately does not implement this — no real terminal
35
+ * can answer it truthfully:
36
+ *
37
+ * - POSIX terminals expose no scrollback-position API at all.
38
+ * - Every modern Windows terminal host (Windows Terminal, VS Code, Tabby,
39
+ * Hyper, Alacritty, WezTerm, JetBrains, …) fronts console apps through
40
+ * ConPTY, where kernel32's `GetConsoleScreenBufferInfo` describes the
41
+ * pseudo-console buffer. That buffer is pinned to the visible grid —
42
+ * scrollback lives in the host UI, invisible to console APIs
43
+ * (microsoft/terminal#10191) — so a probe reads "at bottom" no matter
44
+ * where the user scrolled. Trusting it let streaming-time rebuilds emit
45
+ * `\x1b[3J` and yank scrolled readers: #1635 (Windows Terminal), #1746
46
+ * (Tabby and other ConPTY hosts). No env var distinguishes these hosts
47
+ * (Tabby sets none), so trust cannot be conditional on the environment.
48
+ * - Legacy conhost (the only non-ConPTY host) keeps a real scrollback
49
+ * buffer, but its window follows the output cursor: a probe comparing
50
+ * `srWindow.Bottom` against `dwSize.Y - 1` reads "scrolled up" for a user
51
+ * following live output until all ~9001 buffer rows fill, permanently
52
+ * blocking checkpoint scrollback reconciliation.
53
+ *
54
+ * The renderer treats a missing implementation / `undefined` as "unknown":
55
+ * live mutations defer destructive rebuilds and reconcile native scrollback
56
+ * at explicit checkpoints (prompt submit), where the user's keystroke has
57
+ * already pinned the host viewport to the bottom. Only test terminals
58
+ * (xterm.js-backed) implement this with a real answer.
59
+ */
60
+ isNativeViewportAtBottom?(): boolean | undefined;
61
+ /**
62
+ * Override the global terminal-profile ED3 risk decision for custom/test
63
+ * terminals. `undefined` falls back to the resolved `TERMINAL` profile.
64
+ */
65
+ hasEagerEraseScrollbackRisk?(): boolean | undefined;
66
+ /**
67
+ * Register a callback for terminal appearance (dark/light) changes.
68
+ * Detection uses OSC 11 background color query with Mode 2031 as a change trigger.
69
+ * Fires when the detected appearance changes, including the initial detection.
70
+ */
71
+ onAppearanceChange(callback: (appearance: TerminalAppearance) => void): void;
72
+ /** The last detected terminal appearance, or undefined if not yet known. */
73
+ get appearance(): TerminalAppearance | undefined;
74
+ /**
75
+ * Register a callback fired once per DEC private mode when its DECRQM support
76
+ * status resolves. Optional: only real terminals implement capability probing.
77
+ */
78
+ onPrivateModeReport?(callback: (mode: number, supported: boolean) => void): void;
79
+ }
80
+ /**
81
+ * Real terminal using process.stdin/stdout
82
+ */
83
+ export declare class ProcessTerminal implements Terminal {
84
+ #private;
85
+ get kittyProtocolActive(): boolean;
86
+ get appearance(): TerminalAppearance | undefined;
87
+ onAppearanceChange(callback: (appearance: TerminalAppearance) => void): void;
88
+ onPrivateModeReport(callback: (mode: number, supported: boolean) => void): void;
89
+ start(onInput: (data: string) => void, onResize: () => void): void;
90
+ drainInput(maxMs?: number, idleMs?: number): Promise<void>;
91
+ stop(): void;
92
+ write(data: string): void;
93
+ get columns(): number;
94
+ get rows(): number;
95
+ moveBy(lines: number): void;
96
+ hideCursor(): void;
97
+ showCursor(): void;
98
+ clearLine(): void;
99
+ clearFromCursor(): void;
100
+ clearScreen(): void;
101
+ setTitle(title: string): void;
102
+ setProgress(active: boolean): void;
103
+ }
@@ -0,0 +1,9 @@
1
+ /** Resolve the TTY device path for stdin (fd 0) via POSIX `ttyname(3)`. */
2
+ export declare function getTtyPath(): string | null;
3
+ /**
4
+ * Get a stable identifier for the current terminal.
5
+ * Uses the TTY device path (e.g., /dev/pts/3), falling back to environment
6
+ * variables for terminal multiplexers or terminal emulators.
7
+ * Returns null if no terminal can be identified (e.g., piped input).
8
+ */
9
+ export declare function getTerminalId(): string | null;
@@ -0,0 +1,275 @@
1
+ import { ImageBudget } from "./components/image";
2
+ import type { Terminal } from "./terminal";
3
+ import { visibleWidth } from "./utils";
4
+ type InputListenerResult = {
5
+ consume?: boolean;
6
+ data?: string;
7
+ } | undefined;
8
+ type InputListener = (data: string) => InputListenerResult;
9
+ export interface RenderTimer {
10
+ cancel(): void;
11
+ }
12
+ export interface RenderScheduler {
13
+ now(): number;
14
+ scheduleImmediate(callback: () => void): void;
15
+ scheduleRender(callback: () => void, delayMs: number): RenderTimer;
16
+ }
17
+ export interface TUIOptions {
18
+ renderScheduler?: RenderScheduler;
19
+ }
20
+ /**
21
+ * Component interface - all components must implement this
22
+ */
23
+ export interface Component {
24
+ /**
25
+ * Render the component to lines for the given viewport width
26
+ * @param width - Current viewport width
27
+ * @returns Array of strings, each representing a line
28
+ */
29
+ render(width: number): string[];
30
+ /**
31
+ * Optional handler for keyboard input when component has focus
32
+ */
33
+ handleInput?(data: string): void;
34
+ /**
35
+ * If true, component receives key release events (Kitty protocol).
36
+ * Default is false - release events are filtered out.
37
+ */
38
+ wantsKeyRelease?: boolean;
39
+ /**
40
+ * Invalidate any cached rendering state.
41
+ * Called when theme changes or when component needs to re-render from scratch.
42
+ */
43
+ invalidate(): void;
44
+ }
45
+ /**
46
+ * Optional component seam for native-scrollback pinning. A component that
47
+ * renders a stable prefix followed by a live/transient suffix reports the local
48
+ * line index where that suffix begins after each render. TUI treats that suffix
49
+ * — and every root child rendered below it — as not yet safe to commit to native
50
+ * scrollback on ED3-risk terminals whose viewport position is unobservable.
51
+ *
52
+ * `getNativeScrollbackCommitSafeEnd` optionally reports a *deeper* boundary
53
+ * inside that live suffix: the line index up to which the live region is
54
+ * append-only (its earlier rows never re-layout, only new rows append at the
55
+ * bottom — a streaming assistant message). Rows in `[liveRegionStart,
56
+ * commitSafeEnd)` that scroll above the viewport are safe to commit to native
57
+ * scrollback even though they are technically live, because they will never
58
+ * change. Without this, a single live block that alone overflows the viewport
59
+ * loses its scrolled-off head (committed nowhere, repainted nowhere). Volatile
60
+ * live blocks (tool previews that collapse) omit it, so their mutable rows stay
61
+ * deferred. Defaults to `liveRegionStart` when absent.
62
+ */
63
+ export interface NativeScrollbackLiveRegion {
64
+ getNativeScrollbackLiveRegionStart(): number | undefined;
65
+ getNativeScrollbackCommitSafeEnd?(): number | undefined;
66
+ }
67
+ /**
68
+ * Interface for components that can receive focus and display a cursor.
69
+ * When focused, the component should emit CURSOR_MARKER at the cursor position
70
+ * in its render output. TUI will find this marker and position the hardware
71
+ * cursor there for proper IME candidate window positioning.
72
+ *
73
+ * Components that can switch between terminal-cursor and software-cursor
74
+ * rendering expose `setUseTerminalCursor`; TUI keeps that mode in sync with
75
+ * its resolved hardware-cursor preference whenever focus or the preference
76
+ * changes.
77
+ */
78
+ export interface Focusable {
79
+ /** Set by TUI when focus changes. Component should emit CURSOR_MARKER when true. */
80
+ focused: boolean;
81
+ /** Set by TUI when hardware cursor rendering is enabled or disabled. */
82
+ setUseTerminalCursor?(useTerminalCursor: boolean): void;
83
+ }
84
+ /** Options for scheduling a TUI render. */
85
+ export interface RenderRequestOptions {
86
+ /** Clear terminal scrollback for intentional transcript replacement. */
87
+ clearScrollback?: boolean;
88
+ /**
89
+ * Bypass the unknown-Windows-viewport deferral for this render so the
90
+ * caller's intentional live UI mutation reaches the terminal even when
91
+ * `Terminal#isNativeViewportAtBottom()` cannot answer.
92
+ *
93
+ * Use only for renders driven by direct user interaction (autocomplete
94
+ * updates, IME, etc.). Any background/offscreen transcript change that
95
+ * coalesces into the same frame WILL also bypass the deferral and reach
96
+ * native scrollback — that is the trade-off, and the reason ordinary
97
+ * `requestRender()` calls must continue to omit this flag.
98
+ */
99
+ allowUnknownViewportMutation?: boolean;
100
+ }
101
+ /** Options for deferred native scrollback rebuild checkpoints. Reserved for API stability. */
102
+ export interface NativeScrollbackRefreshOptions {
103
+ allowUnknownViewport?: boolean;
104
+ }
105
+ /** Type guard to check if a component implements Focusable */
106
+ export declare function isFocusable(component: Component | null): component is Component & Focusable;
107
+ /**
108
+ * Cursor position marker - APC (Application Program Command) sequence.
109
+ * This is a zero-width escape sequence that terminals ignore.
110
+ * Components emit this at the cursor position when focused.
111
+ * TUI finds and strips this marker, then positions the hardware cursor there.
112
+ */
113
+ export declare const CURSOR_MARKER = "\u001B_pi:c\u0007";
114
+ export { visibleWidth };
115
+ /**
116
+ * Anchor position for overlays
117
+ */
118
+ export type OverlayAnchor = "center" | "top-left" | "top-right" | "bottom-left" | "bottom-right" | "top-center" | "bottom-center" | "left-center" | "right-center";
119
+ /**
120
+ * Margin configuration for overlays
121
+ */
122
+ export interface OverlayMargin {
123
+ top?: number;
124
+ right?: number;
125
+ bottom?: number;
126
+ left?: number;
127
+ }
128
+ /** Value that can be absolute (number) or percentage (string like "50%") */
129
+ export type SizeValue = number | `${number}%`;
130
+ /**
131
+ * Options for overlay positioning and sizing.
132
+ * Values can be absolute numbers or percentage strings (e.g., "50%").
133
+ */
134
+ export interface OverlayOptions {
135
+ /** Width in columns, or percentage of terminal width (e.g., "50%") */
136
+ width?: SizeValue;
137
+ /** Minimum width in columns */
138
+ minWidth?: number;
139
+ /** Maximum height in rows, or percentage of terminal height (e.g., "50%") */
140
+ maxHeight?: SizeValue;
141
+ /** Anchor point for positioning (default: 'center') */
142
+ anchor?: OverlayAnchor;
143
+ /** Horizontal offset from anchor position (positive = right) */
144
+ offsetX?: number;
145
+ /** Vertical offset from anchor position (positive = down) */
146
+ offsetY?: number;
147
+ /** Row position: absolute number, or percentage (e.g., "25%" = 25% from top) */
148
+ row?: SizeValue;
149
+ /** Column position: absolute number, or percentage (e.g., "50%" = centered horizontally) */
150
+ col?: SizeValue;
151
+ /** Margin from terminal edges. Number applies to all sides. */
152
+ margin?: OverlayMargin | number;
153
+ /**
154
+ * Control overlay visibility based on terminal dimensions.
155
+ * If provided, overlay is only rendered when this returns true.
156
+ * Called each render cycle with current terminal dimensions.
157
+ */
158
+ visible?: (termWidth: number, termHeight: number) => boolean;
159
+ }
160
+ /**
161
+ * Handle returned by showOverlay for controlling the overlay
162
+ */
163
+ export interface OverlayHandle {
164
+ /** Permanently remove the overlay (cannot be shown again) */
165
+ hide(): void;
166
+ /** Temporarily hide or show the overlay */
167
+ setHidden(hidden: boolean): void;
168
+ /** Check if overlay is temporarily hidden */
169
+ isHidden(): boolean;
170
+ }
171
+ /**
172
+ * Container - a component that contains other components
173
+ */
174
+ export declare class Container implements Component {
175
+ children: Component[];
176
+ addChild(component: Component): void;
177
+ removeChild(component: Component): void;
178
+ clear(): void;
179
+ invalidate(): void;
180
+ render(width: number): string[];
181
+ }
182
+ /**
183
+ * TUI - Main class for managing terminal UI with differential rendering
184
+ */
185
+ export declare class TUI extends Container {
186
+ #private;
187
+ terminal: Terminal;
188
+ /** Global callback for debug key (Shift+Ctrl+D). Called before input is forwarded to focused component. */
189
+ onDebug?: () => void;
190
+ overlayStack: {
191
+ component: Component;
192
+ options?: OverlayOptions;
193
+ preFocus: Component | null;
194
+ hidden: boolean;
195
+ }[];
196
+ constructor(terminal: Terminal, showHardwareCursor?: boolean, options?: TUIOptions);
197
+ render(width: number): string[];
198
+ get fullRedraws(): number;
199
+ /** Shared budget that caps how many inline images render as live graphics. */
200
+ get imageBudget(): ImageBudget;
201
+ /**
202
+ * Set how many inline images stay live graphics before older ones fall back
203
+ * to text (`0` disables the cap). Older images are hidden via a graphics purge
204
+ * plus a full redraw on the frame after a new image exceeds the cap.
205
+ */
206
+ setMaxInlineImages(cap: number): void;
207
+ getShowHardwareCursor(): boolean;
208
+ setShowHardwareCursor(enabled: boolean): void;
209
+ getClearOnShrink(): boolean;
210
+ /**
211
+ * Set whether to trigger full re-render when content shrinks.
212
+ * When true (default), empty rows are cleared when content shrinks.
213
+ * When false, empty rows remain (reduces redraws on slower terminals).
214
+ */
215
+ setClearOnShrink(enabled: boolean): void;
216
+ /**
217
+ * Whether DEC 2026 synchronized-output wrappers are currently emitted around
218
+ * paints. Starts from conservative terminal/env detection and is force-disabled
219
+ * at runtime if the terminal reports mode 2026 unsupported via DECRQM.
220
+ */
221
+ get synchronizedOutput(): boolean;
222
+ /**
223
+ * When enabled, live render frames rebuild native scrollback on offscreen and
224
+ * structural changes even when the viewport position is unobservable (POSIX,
225
+ * where `isNativeViewportAtBottom()` is `undefined`), instead of deferring to a
226
+ * non-destructive repaint. This trades the anti-yank guarantee for a clean,
227
+ * duplicate-free history and is meant for windows where output above the fold
228
+ * is actively re-rendering — e.g. a tool whose result is still streaming and
229
+ * re-laying-out rows that have already scrolled into history. A terminal that
230
+ * reports a *known*-scrolled viewport still defers, as does native Windows
231
+ * (the viewport is never observable there and ConPTY hosts erase host
232
+ * scrollback on ED3 — #1635/#1746); only the unknown POSIX case is forced to
233
+ * rebuild. POSIX hosts known to disturb scrolled readers on xterm ED3
234
+ * (`CSI 3 J`, erase saved lines) also defer the eager opt-in; checkpoint
235
+ * rebuilds are unaffected.
236
+ *
237
+ * Disabling stays active through one already-requested frame: the event batch
238
+ * that ends a foreground stream both removes its UI rows (loader/status
239
+ * teardown — a shrink) and clears this flag before the throttled render timer
240
+ * fires. If the flag dropped immediately, that teardown frame would hit the
241
+ * ED3-risk idle deferral and freeze on screen (stale spinner) until the next
242
+ * keystroke. When no render is pending, disable immediately so a later
243
+ * unrelated content mutation does not inherit foreground-stream privileges.
244
+ */
245
+ setEagerNativeScrollbackRebuild(enabled: boolean): void;
246
+ setFocus(component: Component | null): void;
247
+ /**
248
+ * Show an overlay component with configurable positioning and sizing.
249
+ * Returns a handle to control the overlay's visibility.
250
+ */
251
+ showOverlay(component: Component, options?: OverlayOptions): OverlayHandle;
252
+ /** Hide the topmost overlay and restore previous focus. */
253
+ hideOverlay(): void;
254
+ /** Check if there are any visible overlays */
255
+ hasOverlay(): boolean;
256
+ invalidate(): void;
257
+ start(): void;
258
+ addInputListener(listener: InputListener): () => void;
259
+ removeInputListener(listener: InputListener): void;
260
+ stop(): void;
261
+ /**
262
+ * Rebuild native terminal scrollback if live rendering deferred a history rewrite.
263
+ * Callers should only invoke this at checkpoints where the user is expected to be
264
+ * at the terminal bottom, such as after submitting a new prompt.
265
+ */
266
+ refreshNativeScrollbackIfDirty(_options?: NativeScrollbackRefreshOptions): boolean;
267
+ /**
268
+ * Force an immediate full replay of the current frame, including native
269
+ * scrollback. This is the keyboard-accessible equivalent of the resize reset:
270
+ * no queued diff frame or terminal scrollback probe can downgrade it to a
271
+ * viewport-only repaint.
272
+ */
273
+ resetDisplay(): void;
274
+ requestRender(force?: boolean, options?: RenderRequestOptions): void;
275
+ }