@claude-code-kit/ink-renderer 0.1.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.
- package/dist/index.d.mts +2124 -0
- package/dist/index.d.ts +2124 -0
- package/dist/index.js +11183 -0
- package/dist/index.mjs +11133 -0
- package/package.json +75 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,2124 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import React__default, { ReactNode, PropsWithChildren, Ref } from 'react';
|
|
3
|
+
import { Boxes, BoxStyle } from 'cli-boxes';
|
|
4
|
+
import { Except } from 'type-fest';
|
|
5
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
6
|
+
import { EventEmitter as EventEmitter$1 } from 'events';
|
|
7
|
+
|
|
8
|
+
type Cursor = {
|
|
9
|
+
x: number;
|
|
10
|
+
y: number;
|
|
11
|
+
visible: boolean;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
type Point$1 = {
|
|
15
|
+
x: number;
|
|
16
|
+
y: number;
|
|
17
|
+
};
|
|
18
|
+
type Size = {
|
|
19
|
+
width: number;
|
|
20
|
+
height: number;
|
|
21
|
+
};
|
|
22
|
+
type Rectangle = Point$1 & Size;
|
|
23
|
+
declare function clamp(value: number, min?: number, max?: number): number;
|
|
24
|
+
|
|
25
|
+
declare class Event {
|
|
26
|
+
private _didStopImmediatePropagation;
|
|
27
|
+
didStopImmediatePropagation(): boolean;
|
|
28
|
+
stopImmediatePropagation(): void;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
type EventPhase = 'none' | 'capturing' | 'at_target' | 'bubbling';
|
|
32
|
+
type TerminalEventInit = {
|
|
33
|
+
bubbles?: boolean;
|
|
34
|
+
cancelable?: boolean;
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* Base class for all terminal events with DOM-style propagation.
|
|
38
|
+
*
|
|
39
|
+
* Extends Event so existing event types (ClickEvent, InputEvent,
|
|
40
|
+
* TerminalFocusEvent) share a common ancestor and can migrate later.
|
|
41
|
+
*
|
|
42
|
+
* Mirrors the browser's Event API: target, currentTarget, eventPhase,
|
|
43
|
+
* stopPropagation(), preventDefault(), timeStamp.
|
|
44
|
+
*/
|
|
45
|
+
declare class TerminalEvent extends Event {
|
|
46
|
+
readonly type: string;
|
|
47
|
+
readonly timeStamp: number;
|
|
48
|
+
readonly bubbles: boolean;
|
|
49
|
+
readonly cancelable: boolean;
|
|
50
|
+
private _target;
|
|
51
|
+
private _currentTarget;
|
|
52
|
+
private _eventPhase;
|
|
53
|
+
private _propagationStopped;
|
|
54
|
+
private _defaultPrevented;
|
|
55
|
+
constructor(type: string, init?: TerminalEventInit);
|
|
56
|
+
get target(): EventTarget | null;
|
|
57
|
+
get currentTarget(): EventTarget | null;
|
|
58
|
+
get eventPhase(): EventPhase;
|
|
59
|
+
get defaultPrevented(): boolean;
|
|
60
|
+
stopPropagation(): void;
|
|
61
|
+
stopImmediatePropagation(): void;
|
|
62
|
+
preventDefault(): void;
|
|
63
|
+
/** @internal */
|
|
64
|
+
_setTarget(target: EventTarget): void;
|
|
65
|
+
/** @internal */
|
|
66
|
+
_setCurrentTarget(target: EventTarget | null): void;
|
|
67
|
+
/** @internal */
|
|
68
|
+
_setEventPhase(phase: EventPhase): void;
|
|
69
|
+
/** @internal */
|
|
70
|
+
_isPropagationStopped(): boolean;
|
|
71
|
+
/** @internal */
|
|
72
|
+
_isImmediatePropagationStopped(): boolean;
|
|
73
|
+
/**
|
|
74
|
+
* Hook for subclasses to do per-node setup before each handler fires.
|
|
75
|
+
* Default is a no-op.
|
|
76
|
+
*/
|
|
77
|
+
_prepareForTarget(_target: EventTarget): void;
|
|
78
|
+
}
|
|
79
|
+
type EventTarget = {
|
|
80
|
+
parentNode: EventTarget | undefined;
|
|
81
|
+
_eventHandlers?: Record<string, unknown>;
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Focus event for component focus changes.
|
|
86
|
+
*
|
|
87
|
+
* Dispatched when focus moves between elements. 'focus' fires on the
|
|
88
|
+
* newly focused element, 'blur' fires on the previously focused one.
|
|
89
|
+
* Both bubble, matching react-dom's use of focusin/focusout semantics
|
|
90
|
+
* so parent components can observe descendant focus changes.
|
|
91
|
+
*/
|
|
92
|
+
declare class FocusEvent extends TerminalEvent {
|
|
93
|
+
readonly relatedTarget: EventTarget | null;
|
|
94
|
+
constructor(type: 'focus' | 'blur', relatedTarget?: EventTarget | null);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* DOM-like focus manager for the Ink terminal UI.
|
|
99
|
+
*
|
|
100
|
+
* Pure state — tracks activeElement and a focus stack. Has no reference
|
|
101
|
+
* to the tree; callers pass the root when tree walks are needed.
|
|
102
|
+
*
|
|
103
|
+
* Stored on the root DOMElement so any node can reach it by walking
|
|
104
|
+
* parentNode (like browser's `node.ownerDocument`).
|
|
105
|
+
*/
|
|
106
|
+
declare class FocusManager {
|
|
107
|
+
activeElement: DOMElement | null;
|
|
108
|
+
private dispatchFocusEvent;
|
|
109
|
+
private enabled;
|
|
110
|
+
private focusStack;
|
|
111
|
+
constructor(dispatchFocusEvent: (target: DOMElement, event: FocusEvent) => boolean);
|
|
112
|
+
focus(node: DOMElement): void;
|
|
113
|
+
blur(): void;
|
|
114
|
+
/**
|
|
115
|
+
* Called by the reconciler when a node is removed from the tree.
|
|
116
|
+
* Handles both the exact node and any focused descendant within
|
|
117
|
+
* the removed subtree. Dispatches blur and restores focus from stack.
|
|
118
|
+
*/
|
|
119
|
+
handleNodeRemoved(node: DOMElement, root: DOMElement): void;
|
|
120
|
+
handleAutoFocus(node: DOMElement): void;
|
|
121
|
+
handleClickFocus(node: DOMElement): void;
|
|
122
|
+
enable(): void;
|
|
123
|
+
disable(): void;
|
|
124
|
+
focusNext(root: DOMElement): void;
|
|
125
|
+
focusPrevious(root: DOMElement): void;
|
|
126
|
+
private moveFocus;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
declare const LayoutEdge: {
|
|
130
|
+
readonly All: "all";
|
|
131
|
+
readonly Horizontal: "horizontal";
|
|
132
|
+
readonly Vertical: "vertical";
|
|
133
|
+
readonly Left: "left";
|
|
134
|
+
readonly Right: "right";
|
|
135
|
+
readonly Top: "top";
|
|
136
|
+
readonly Bottom: "bottom";
|
|
137
|
+
readonly Start: "start";
|
|
138
|
+
readonly End: "end";
|
|
139
|
+
};
|
|
140
|
+
type LayoutEdge = (typeof LayoutEdge)[keyof typeof LayoutEdge];
|
|
141
|
+
declare const LayoutGutter: {
|
|
142
|
+
readonly All: "all";
|
|
143
|
+
readonly Column: "column";
|
|
144
|
+
readonly Row: "row";
|
|
145
|
+
};
|
|
146
|
+
type LayoutGutter = (typeof LayoutGutter)[keyof typeof LayoutGutter];
|
|
147
|
+
declare const LayoutDisplay: {
|
|
148
|
+
readonly Flex: "flex";
|
|
149
|
+
readonly None: "none";
|
|
150
|
+
};
|
|
151
|
+
type LayoutDisplay = (typeof LayoutDisplay)[keyof typeof LayoutDisplay];
|
|
152
|
+
declare const LayoutFlexDirection: {
|
|
153
|
+
readonly Row: "row";
|
|
154
|
+
readonly RowReverse: "row-reverse";
|
|
155
|
+
readonly Column: "column";
|
|
156
|
+
readonly ColumnReverse: "column-reverse";
|
|
157
|
+
};
|
|
158
|
+
type LayoutFlexDirection = (typeof LayoutFlexDirection)[keyof typeof LayoutFlexDirection];
|
|
159
|
+
declare const LayoutAlign: {
|
|
160
|
+
readonly Auto: "auto";
|
|
161
|
+
readonly Stretch: "stretch";
|
|
162
|
+
readonly FlexStart: "flex-start";
|
|
163
|
+
readonly Center: "center";
|
|
164
|
+
readonly FlexEnd: "flex-end";
|
|
165
|
+
};
|
|
166
|
+
type LayoutAlign = (typeof LayoutAlign)[keyof typeof LayoutAlign];
|
|
167
|
+
declare const LayoutJustify: {
|
|
168
|
+
readonly FlexStart: "flex-start";
|
|
169
|
+
readonly Center: "center";
|
|
170
|
+
readonly FlexEnd: "flex-end";
|
|
171
|
+
readonly SpaceBetween: "space-between";
|
|
172
|
+
readonly SpaceAround: "space-around";
|
|
173
|
+
readonly SpaceEvenly: "space-evenly";
|
|
174
|
+
};
|
|
175
|
+
type LayoutJustify = (typeof LayoutJustify)[keyof typeof LayoutJustify];
|
|
176
|
+
declare const LayoutWrap: {
|
|
177
|
+
readonly NoWrap: "nowrap";
|
|
178
|
+
readonly Wrap: "wrap";
|
|
179
|
+
readonly WrapReverse: "wrap-reverse";
|
|
180
|
+
};
|
|
181
|
+
type LayoutWrap = (typeof LayoutWrap)[keyof typeof LayoutWrap];
|
|
182
|
+
declare const LayoutPositionType: {
|
|
183
|
+
readonly Relative: "relative";
|
|
184
|
+
readonly Absolute: "absolute";
|
|
185
|
+
};
|
|
186
|
+
type LayoutPositionType = (typeof LayoutPositionType)[keyof typeof LayoutPositionType];
|
|
187
|
+
declare const LayoutOverflow: {
|
|
188
|
+
readonly Visible: "visible";
|
|
189
|
+
readonly Hidden: "hidden";
|
|
190
|
+
readonly Scroll: "scroll";
|
|
191
|
+
};
|
|
192
|
+
type LayoutOverflow = (typeof LayoutOverflow)[keyof typeof LayoutOverflow];
|
|
193
|
+
type LayoutMeasureFunc = (width: number, widthMode: LayoutMeasureMode) => {
|
|
194
|
+
width: number;
|
|
195
|
+
height: number;
|
|
196
|
+
};
|
|
197
|
+
declare const LayoutMeasureMode: {
|
|
198
|
+
readonly Undefined: "undefined";
|
|
199
|
+
readonly Exactly: "exactly";
|
|
200
|
+
readonly AtMost: "at-most";
|
|
201
|
+
};
|
|
202
|
+
type LayoutMeasureMode = (typeof LayoutMeasureMode)[keyof typeof LayoutMeasureMode];
|
|
203
|
+
type LayoutNode = {
|
|
204
|
+
insertChild(child: LayoutNode, index: number): void;
|
|
205
|
+
removeChild(child: LayoutNode): void;
|
|
206
|
+
getChildCount(): number;
|
|
207
|
+
getParent(): LayoutNode | null;
|
|
208
|
+
calculateLayout(width?: number, height?: number): void;
|
|
209
|
+
setMeasureFunc(fn: LayoutMeasureFunc): void;
|
|
210
|
+
unsetMeasureFunc(): void;
|
|
211
|
+
markDirty(): void;
|
|
212
|
+
getComputedLeft(): number;
|
|
213
|
+
getComputedTop(): number;
|
|
214
|
+
getComputedWidth(): number;
|
|
215
|
+
getComputedHeight(): number;
|
|
216
|
+
getComputedBorder(edge: LayoutEdge): number;
|
|
217
|
+
getComputedPadding(edge: LayoutEdge): number;
|
|
218
|
+
setWidth(value: number): void;
|
|
219
|
+
setWidthPercent(value: number): void;
|
|
220
|
+
setWidthAuto(): void;
|
|
221
|
+
setHeight(value: number): void;
|
|
222
|
+
setHeightPercent(value: number): void;
|
|
223
|
+
setHeightAuto(): void;
|
|
224
|
+
setMinWidth(value: number): void;
|
|
225
|
+
setMinWidthPercent(value: number): void;
|
|
226
|
+
setMinHeight(value: number): void;
|
|
227
|
+
setMinHeightPercent(value: number): void;
|
|
228
|
+
setMaxWidth(value: number): void;
|
|
229
|
+
setMaxWidthPercent(value: number): void;
|
|
230
|
+
setMaxHeight(value: number): void;
|
|
231
|
+
setMaxHeightPercent(value: number): void;
|
|
232
|
+
setFlexDirection(dir: LayoutFlexDirection): void;
|
|
233
|
+
setFlexGrow(value: number): void;
|
|
234
|
+
setFlexShrink(value: number): void;
|
|
235
|
+
setFlexBasis(value: number): void;
|
|
236
|
+
setFlexBasisPercent(value: number): void;
|
|
237
|
+
setFlexWrap(wrap: LayoutWrap): void;
|
|
238
|
+
setAlignItems(align: LayoutAlign): void;
|
|
239
|
+
setAlignSelf(align: LayoutAlign): void;
|
|
240
|
+
setJustifyContent(justify: LayoutJustify): void;
|
|
241
|
+
setDisplay(display: LayoutDisplay): void;
|
|
242
|
+
getDisplay(): LayoutDisplay;
|
|
243
|
+
setPositionType(type: LayoutPositionType): void;
|
|
244
|
+
setPosition(edge: LayoutEdge, value: number): void;
|
|
245
|
+
setPositionPercent(edge: LayoutEdge, value: number): void;
|
|
246
|
+
setOverflow(overflow: LayoutOverflow): void;
|
|
247
|
+
setMargin(edge: LayoutEdge, value: number): void;
|
|
248
|
+
setPadding(edge: LayoutEdge, value: number): void;
|
|
249
|
+
setBorder(edge: LayoutEdge, value: number): void;
|
|
250
|
+
setGap(gutter: LayoutGutter, value: number): void;
|
|
251
|
+
free(): void;
|
|
252
|
+
freeRecursive(): void;
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
declare class CharPool {
|
|
256
|
+
private strings;
|
|
257
|
+
private stringMap;
|
|
258
|
+
private ascii;
|
|
259
|
+
intern(char: string): number;
|
|
260
|
+
get(index: number): string;
|
|
261
|
+
}
|
|
262
|
+
declare class HyperlinkPool {
|
|
263
|
+
private strings;
|
|
264
|
+
private stringMap;
|
|
265
|
+
intern(hyperlink: string | undefined): number;
|
|
266
|
+
get(id: number): string | undefined;
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Screen uses a packed Int32Array instead of Cell objects to eliminate GC
|
|
270
|
+
* pressure. For a 200x120 screen, this avoids allocating 24,000 objects.
|
|
271
|
+
*
|
|
272
|
+
* Cell data is stored as 2 Int32s per cell in a single contiguous array:
|
|
273
|
+
* word0: charId (full 32 bits — index into CharPool)
|
|
274
|
+
* word1: styleId[31:17] | hyperlinkId[16:2] | width[1:0]
|
|
275
|
+
*
|
|
276
|
+
* This layout halves memory accesses in diffEach (2 int loads vs 4) and
|
|
277
|
+
* enables future SIMD comparison via Bun.indexOfFirstDifference.
|
|
278
|
+
*/
|
|
279
|
+
type Screen = Size & {
|
|
280
|
+
cells: Int32Array;
|
|
281
|
+
cells64: BigInt64Array;
|
|
282
|
+
charPool: CharPool;
|
|
283
|
+
hyperlinkPool: HyperlinkPool;
|
|
284
|
+
emptyStyleId: number;
|
|
285
|
+
/**
|
|
286
|
+
* Bounding box of cells that were written to (not blitted) during rendering.
|
|
287
|
+
* Used by diff() to limit iteration to only the region that could have changed.
|
|
288
|
+
*/
|
|
289
|
+
damage: Rectangle | undefined;
|
|
290
|
+
/**
|
|
291
|
+
* Per-cell noSelect bitmap — 1 byte per cell, 1 = exclude from text
|
|
292
|
+
* selection (copy + highlight). Used by <NoSelect> to mark gutters
|
|
293
|
+
* (line numbers, diff sigils) so click-drag over a diff yields clean
|
|
294
|
+
* copyable code. Fully reset each frame in resetScreen; blitRegion
|
|
295
|
+
* copies it alongside cells so the blit optimization preserves marks.
|
|
296
|
+
*/
|
|
297
|
+
noSelect: Uint8Array;
|
|
298
|
+
/**
|
|
299
|
+
* Per-ROW soft-wrap continuation marker. softWrap[r]=N>0 means row r
|
|
300
|
+
* is a word-wrap continuation of row r-1 (the `\n` before it was
|
|
301
|
+
* inserted by wrapAnsi, not in the source), and row r-1's written
|
|
302
|
+
* content ends at absolute column N (exclusive — cells [0..N) are the
|
|
303
|
+
* fragment, past N is unwritten padding). 0 means row r is NOT a
|
|
304
|
+
* continuation (hard newline or first row). Selection copy checks
|
|
305
|
+
* softWrap[r]>0 to join row r onto row r-1 without a newline, and
|
|
306
|
+
* reads softWrap[r+1] to know row r's content end when row r+1
|
|
307
|
+
* continues from it. The content-end column is needed because an
|
|
308
|
+
* unwritten cell and a written-unstyled-space are indistinguishable in
|
|
309
|
+
* the packed typed array (both all-zero) — without it we'd either drop
|
|
310
|
+
* the word-separator space (trim) or include trailing padding (no
|
|
311
|
+
* trim). This encoding (continuation-on-self, prev-content-end-here)
|
|
312
|
+
* is chosen so shiftRows preserves the is-continuation semantics: when
|
|
313
|
+
* row r scrolls off the top and row r+1 shifts to row r, sw[r] gets
|
|
314
|
+
* old sw[r+1] — which correctly says the new row r is a continuation
|
|
315
|
+
* of what's now in scrolledOffAbove. Reset each frame; copied by
|
|
316
|
+
* blitRegion/shiftRows.
|
|
317
|
+
*/
|
|
318
|
+
softWrap: Int32Array;
|
|
319
|
+
};
|
|
320
|
+
|
|
321
|
+
type BorderTextOptions = {
|
|
322
|
+
content: string;
|
|
323
|
+
position: 'top' | 'bottom';
|
|
324
|
+
align: 'start' | 'end' | 'center';
|
|
325
|
+
offset?: number;
|
|
326
|
+
};
|
|
327
|
+
declare const CUSTOM_BORDER_STYLES: {
|
|
328
|
+
readonly dashed: {
|
|
329
|
+
readonly top: "╌";
|
|
330
|
+
readonly left: "╎";
|
|
331
|
+
readonly right: "╎";
|
|
332
|
+
readonly bottom: "╌";
|
|
333
|
+
readonly topLeft: " ";
|
|
334
|
+
readonly topRight: " ";
|
|
335
|
+
readonly bottomLeft: " ";
|
|
336
|
+
readonly bottomRight: " ";
|
|
337
|
+
};
|
|
338
|
+
};
|
|
339
|
+
type BorderStyle = keyof Boxes | keyof typeof CUSTOM_BORDER_STYLES | BoxStyle;
|
|
340
|
+
|
|
341
|
+
type RGBColor = `rgb(${number},${number},${number})`;
|
|
342
|
+
type HexColor = `#${string}`;
|
|
343
|
+
type Ansi256Color = `ansi256(${number})`;
|
|
344
|
+
type AnsiColor = 'ansi:black' | 'ansi:red' | 'ansi:green' | 'ansi:yellow' | 'ansi:blue' | 'ansi:magenta' | 'ansi:cyan' | 'ansi:white' | 'ansi:blackBright' | 'ansi:redBright' | 'ansi:greenBright' | 'ansi:yellowBright' | 'ansi:blueBright' | 'ansi:magentaBright' | 'ansi:cyanBright' | 'ansi:whiteBright';
|
|
345
|
+
/** Raw color value or theme key. The `(string & {})` allows theme keys like
|
|
346
|
+
* "suggestion", "success", etc. to pass through while preserving autocomplete
|
|
347
|
+
* for structured color formats. */
|
|
348
|
+
type Color = RGBColor | HexColor | Ansi256Color | AnsiColor | string;
|
|
349
|
+
/**
|
|
350
|
+
* Structured text styling properties.
|
|
351
|
+
* Used to style text without relying on ANSI string transforms.
|
|
352
|
+
* Colors are raw values - theme resolution happens at the component layer.
|
|
353
|
+
*/
|
|
354
|
+
type TextStyles = {
|
|
355
|
+
readonly color?: Color;
|
|
356
|
+
readonly backgroundColor?: Color;
|
|
357
|
+
readonly dim?: boolean;
|
|
358
|
+
readonly bold?: boolean;
|
|
359
|
+
readonly italic?: boolean;
|
|
360
|
+
readonly underline?: boolean;
|
|
361
|
+
readonly strikethrough?: boolean;
|
|
362
|
+
readonly inverse?: boolean;
|
|
363
|
+
};
|
|
364
|
+
type Styles = {
|
|
365
|
+
readonly textWrap?: 'wrap' | 'wrap-trim' | 'end' | 'middle' | 'truncate-end' | 'truncate' | 'truncate-middle' | 'truncate-start';
|
|
366
|
+
readonly position?: 'absolute' | 'relative';
|
|
367
|
+
readonly top?: number | `${number}%`;
|
|
368
|
+
readonly bottom?: number | `${number}%`;
|
|
369
|
+
readonly left?: number | `${number}%`;
|
|
370
|
+
readonly right?: number | `${number}%`;
|
|
371
|
+
/**
|
|
372
|
+
* Size of the gap between an element's columns.
|
|
373
|
+
*/
|
|
374
|
+
readonly columnGap?: number;
|
|
375
|
+
/**
|
|
376
|
+
* Size of the gap between element's rows.
|
|
377
|
+
*/
|
|
378
|
+
readonly rowGap?: number;
|
|
379
|
+
/**
|
|
380
|
+
* Size of the gap between an element's columns and rows. Shorthand for `columnGap` and `rowGap`.
|
|
381
|
+
*/
|
|
382
|
+
readonly gap?: number;
|
|
383
|
+
/**
|
|
384
|
+
* Margin on all sides. Equivalent to setting `marginTop`, `marginBottom`, `marginLeft` and `marginRight`.
|
|
385
|
+
*/
|
|
386
|
+
readonly margin?: number;
|
|
387
|
+
/**
|
|
388
|
+
* Horizontal margin. Equivalent to setting `marginLeft` and `marginRight`.
|
|
389
|
+
*/
|
|
390
|
+
readonly marginX?: number;
|
|
391
|
+
/**
|
|
392
|
+
* Vertical margin. Equivalent to setting `marginTop` and `marginBottom`.
|
|
393
|
+
*/
|
|
394
|
+
readonly marginY?: number;
|
|
395
|
+
/**
|
|
396
|
+
* Top margin.
|
|
397
|
+
*/
|
|
398
|
+
readonly marginTop?: number;
|
|
399
|
+
/**
|
|
400
|
+
* Bottom margin.
|
|
401
|
+
*/
|
|
402
|
+
readonly marginBottom?: number;
|
|
403
|
+
/**
|
|
404
|
+
* Left margin.
|
|
405
|
+
*/
|
|
406
|
+
readonly marginLeft?: number;
|
|
407
|
+
/**
|
|
408
|
+
* Right margin.
|
|
409
|
+
*/
|
|
410
|
+
readonly marginRight?: number;
|
|
411
|
+
/**
|
|
412
|
+
* Padding on all sides. Equivalent to setting `paddingTop`, `paddingBottom`, `paddingLeft` and `paddingRight`.
|
|
413
|
+
*/
|
|
414
|
+
readonly padding?: number;
|
|
415
|
+
/**
|
|
416
|
+
* Horizontal padding. Equivalent to setting `paddingLeft` and `paddingRight`.
|
|
417
|
+
*/
|
|
418
|
+
readonly paddingX?: number;
|
|
419
|
+
/**
|
|
420
|
+
* Vertical padding. Equivalent to setting `paddingTop` and `paddingBottom`.
|
|
421
|
+
*/
|
|
422
|
+
readonly paddingY?: number;
|
|
423
|
+
/**
|
|
424
|
+
* Top padding.
|
|
425
|
+
*/
|
|
426
|
+
readonly paddingTop?: number;
|
|
427
|
+
/**
|
|
428
|
+
* Bottom padding.
|
|
429
|
+
*/
|
|
430
|
+
readonly paddingBottom?: number;
|
|
431
|
+
/**
|
|
432
|
+
* Left padding.
|
|
433
|
+
*/
|
|
434
|
+
readonly paddingLeft?: number;
|
|
435
|
+
/**
|
|
436
|
+
* Right padding.
|
|
437
|
+
*/
|
|
438
|
+
readonly paddingRight?: number;
|
|
439
|
+
/**
|
|
440
|
+
* This property defines the ability for a flex item to grow if necessary.
|
|
441
|
+
* See [flex-grow](https://css-tricks.com/almanac/properties/f/flex-grow/).
|
|
442
|
+
*/
|
|
443
|
+
readonly flexGrow?: number;
|
|
444
|
+
/**
|
|
445
|
+
* It specifies the “flex shrink factor”, which determines how much the flex item will shrink relative to the rest of the flex items in the flex container when there isn’t enough space on the row.
|
|
446
|
+
* See [flex-shrink](https://css-tricks.com/almanac/properties/f/flex-shrink/).
|
|
447
|
+
*/
|
|
448
|
+
readonly flexShrink?: number;
|
|
449
|
+
/**
|
|
450
|
+
* It establishes the main-axis, thus defining the direction flex items are placed in the flex container.
|
|
451
|
+
* See [flex-direction](https://css-tricks.com/almanac/properties/f/flex-direction/).
|
|
452
|
+
*/
|
|
453
|
+
readonly flexDirection?: 'row' | 'column' | 'row-reverse' | 'column-reverse';
|
|
454
|
+
/**
|
|
455
|
+
* It specifies the initial size of the flex item, before any available space is distributed according to the flex factors.
|
|
456
|
+
* See [flex-basis](https://css-tricks.com/almanac/properties/f/flex-basis/).
|
|
457
|
+
*/
|
|
458
|
+
readonly flexBasis?: number | string;
|
|
459
|
+
/**
|
|
460
|
+
* It defines whether the flex items are forced in a single line or can be flowed into multiple lines. If set to multiple lines, it also defines the cross-axis which determines the direction new lines are stacked in.
|
|
461
|
+
* See [flex-wrap](https://css-tricks.com/almanac/properties/f/flex-wrap/).
|
|
462
|
+
*/
|
|
463
|
+
readonly flexWrap?: 'nowrap' | 'wrap' | 'wrap-reverse';
|
|
464
|
+
/**
|
|
465
|
+
* The align-items property defines the default behavior for how items are laid out along the cross axis (perpendicular to the main axis).
|
|
466
|
+
* See [align-items](https://css-tricks.com/almanac/properties/a/align-items/).
|
|
467
|
+
*/
|
|
468
|
+
readonly alignItems?: 'flex-start' | 'center' | 'flex-end' | 'stretch';
|
|
469
|
+
/**
|
|
470
|
+
* It makes possible to override the align-items value for specific flex items.
|
|
471
|
+
* See [align-self](https://css-tricks.com/almanac/properties/a/align-self/).
|
|
472
|
+
*/
|
|
473
|
+
readonly alignSelf?: 'flex-start' | 'center' | 'flex-end' | 'auto';
|
|
474
|
+
/**
|
|
475
|
+
* It defines the alignment along the main axis.
|
|
476
|
+
* See [justify-content](https://css-tricks.com/almanac/properties/j/justify-content/).
|
|
477
|
+
*/
|
|
478
|
+
readonly justifyContent?: 'flex-start' | 'flex-end' | 'space-between' | 'space-around' | 'space-evenly' | 'center';
|
|
479
|
+
/**
|
|
480
|
+
* Width of the element in spaces.
|
|
481
|
+
* You can also set it in percent, which will calculate the width based on the width of parent element.
|
|
482
|
+
*/
|
|
483
|
+
readonly width?: number | string;
|
|
484
|
+
/**
|
|
485
|
+
* Height of the element in lines (rows).
|
|
486
|
+
* You can also set it in percent, which will calculate the height based on the height of parent element.
|
|
487
|
+
*/
|
|
488
|
+
readonly height?: number | string;
|
|
489
|
+
/**
|
|
490
|
+
* Sets a minimum width of the element.
|
|
491
|
+
*/
|
|
492
|
+
readonly minWidth?: number | string;
|
|
493
|
+
/**
|
|
494
|
+
* Sets a minimum height of the element.
|
|
495
|
+
*/
|
|
496
|
+
readonly minHeight?: number | string;
|
|
497
|
+
/**
|
|
498
|
+
* Sets a maximum width of the element.
|
|
499
|
+
*/
|
|
500
|
+
readonly maxWidth?: number | string;
|
|
501
|
+
/**
|
|
502
|
+
* Sets a maximum height of the element.
|
|
503
|
+
*/
|
|
504
|
+
readonly maxHeight?: number | string;
|
|
505
|
+
/**
|
|
506
|
+
* Set this property to `none` to hide the element.
|
|
507
|
+
*/
|
|
508
|
+
readonly display?: 'flex' | 'none';
|
|
509
|
+
/**
|
|
510
|
+
* Add a border with a specified style.
|
|
511
|
+
* If `borderStyle` is `undefined` (which it is by default), no border will be added.
|
|
512
|
+
*/
|
|
513
|
+
readonly borderStyle?: BorderStyle;
|
|
514
|
+
/**
|
|
515
|
+
* Determines whether top border is visible.
|
|
516
|
+
*
|
|
517
|
+
* @default true
|
|
518
|
+
*/
|
|
519
|
+
readonly borderTop?: boolean;
|
|
520
|
+
/**
|
|
521
|
+
* Determines whether bottom border is visible.
|
|
522
|
+
*
|
|
523
|
+
* @default true
|
|
524
|
+
*/
|
|
525
|
+
readonly borderBottom?: boolean;
|
|
526
|
+
/**
|
|
527
|
+
* Determines whether left border is visible.
|
|
528
|
+
*
|
|
529
|
+
* @default true
|
|
530
|
+
*/
|
|
531
|
+
readonly borderLeft?: boolean;
|
|
532
|
+
/**
|
|
533
|
+
* Determines whether right border is visible.
|
|
534
|
+
*
|
|
535
|
+
* @default true
|
|
536
|
+
*/
|
|
537
|
+
readonly borderRight?: boolean;
|
|
538
|
+
/**
|
|
539
|
+
* Change border color.
|
|
540
|
+
* Shorthand for setting `borderTopColor`, `borderRightColor`, `borderBottomColor` and `borderLeftColor`.
|
|
541
|
+
*/
|
|
542
|
+
readonly borderColor?: Color;
|
|
543
|
+
/**
|
|
544
|
+
* Change top border color.
|
|
545
|
+
* Accepts raw color values (rgb, hex, ansi).
|
|
546
|
+
*/
|
|
547
|
+
readonly borderTopColor?: Color;
|
|
548
|
+
/**
|
|
549
|
+
* Change bottom border color.
|
|
550
|
+
* Accepts raw color values (rgb, hex, ansi).
|
|
551
|
+
*/
|
|
552
|
+
readonly borderBottomColor?: Color;
|
|
553
|
+
/**
|
|
554
|
+
* Change left border color.
|
|
555
|
+
* Accepts raw color values (rgb, hex, ansi).
|
|
556
|
+
*/
|
|
557
|
+
readonly borderLeftColor?: Color;
|
|
558
|
+
/**
|
|
559
|
+
* Change right border color.
|
|
560
|
+
* Accepts raw color values (rgb, hex, ansi).
|
|
561
|
+
*/
|
|
562
|
+
readonly borderRightColor?: Color;
|
|
563
|
+
/**
|
|
564
|
+
* Dim the border color.
|
|
565
|
+
* Shorthand for setting `borderTopDimColor`, `borderBottomDimColor`, `borderLeftDimColor` and `borderRightDimColor`.
|
|
566
|
+
*
|
|
567
|
+
* @default false
|
|
568
|
+
*/
|
|
569
|
+
readonly borderDimColor?: boolean;
|
|
570
|
+
/**
|
|
571
|
+
* Dim the top border color.
|
|
572
|
+
*
|
|
573
|
+
* @default false
|
|
574
|
+
*/
|
|
575
|
+
readonly borderTopDimColor?: boolean;
|
|
576
|
+
/**
|
|
577
|
+
* Dim the bottom border color.
|
|
578
|
+
*
|
|
579
|
+
* @default false
|
|
580
|
+
*/
|
|
581
|
+
readonly borderBottomDimColor?: boolean;
|
|
582
|
+
/**
|
|
583
|
+
* Dim the left border color.
|
|
584
|
+
*
|
|
585
|
+
* @default false
|
|
586
|
+
*/
|
|
587
|
+
readonly borderLeftDimColor?: boolean;
|
|
588
|
+
/**
|
|
589
|
+
* Dim the right border color.
|
|
590
|
+
*
|
|
591
|
+
* @default false
|
|
592
|
+
*/
|
|
593
|
+
readonly borderRightDimColor?: boolean;
|
|
594
|
+
/**
|
|
595
|
+
* Add text within the border. Only applies to top or bottom borders.
|
|
596
|
+
*/
|
|
597
|
+
readonly borderText?: BorderTextOptions;
|
|
598
|
+
/**
|
|
599
|
+
* Background color for the box. Fills the interior with background-colored
|
|
600
|
+
* spaces and is inherited by child text nodes as their default background.
|
|
601
|
+
*/
|
|
602
|
+
readonly backgroundColor?: Color;
|
|
603
|
+
/**
|
|
604
|
+
* Fill the box's interior (padding included) with spaces before
|
|
605
|
+
* rendering children, so nothing behind it shows through. Like
|
|
606
|
+
* `backgroundColor` but without emitting any SGR — the terminal's
|
|
607
|
+
* default background is used. Useful for absolute-positioned overlays
|
|
608
|
+
* where Box padding/gaps would otherwise be transparent.
|
|
609
|
+
*/
|
|
610
|
+
readonly opaque?: boolean;
|
|
611
|
+
/**
|
|
612
|
+
* Behavior for an element's overflow in both directions.
|
|
613
|
+
* 'scroll' constrains the container's size (children do not expand it)
|
|
614
|
+
* and enables scrollTop-based virtualized scrolling at render time.
|
|
615
|
+
*
|
|
616
|
+
* @default 'visible'
|
|
617
|
+
*/
|
|
618
|
+
readonly overflow?: 'visible' | 'hidden' | 'scroll';
|
|
619
|
+
/**
|
|
620
|
+
* Behavior for an element's overflow in horizontal direction.
|
|
621
|
+
*
|
|
622
|
+
* @default 'visible'
|
|
623
|
+
*/
|
|
624
|
+
readonly overflowX?: 'visible' | 'hidden' | 'scroll';
|
|
625
|
+
/**
|
|
626
|
+
* Behavior for an element's overflow in vertical direction.
|
|
627
|
+
*
|
|
628
|
+
* @default 'visible'
|
|
629
|
+
*/
|
|
630
|
+
readonly overflowY?: 'visible' | 'hidden' | 'scroll';
|
|
631
|
+
/**
|
|
632
|
+
* Exclude this box's cells from text selection in fullscreen mode.
|
|
633
|
+
* Cells inside this region are skipped by both the selection highlight
|
|
634
|
+
* and the copied text — useful for fencing off gutters (line numbers,
|
|
635
|
+
* diff sigils) so click-drag over a diff yields clean copyable code.
|
|
636
|
+
* Only affects alt-screen text selection; no-op otherwise.
|
|
637
|
+
*
|
|
638
|
+
* `'from-left-edge'` extends the exclusion from column 0 to the box's
|
|
639
|
+
* right edge for every row it occupies — this covers any upstream
|
|
640
|
+
* indentation (tool message prefix, tree lines) so a multi-row drag
|
|
641
|
+
* doesn't pick up leading whitespace from middle rows.
|
|
642
|
+
*/
|
|
643
|
+
readonly noSelect?: boolean | 'from-left-edge';
|
|
644
|
+
};
|
|
645
|
+
|
|
646
|
+
type InkNode = {
|
|
647
|
+
parentNode: DOMElement | undefined;
|
|
648
|
+
yogaNode?: LayoutNode;
|
|
649
|
+
style: Styles;
|
|
650
|
+
};
|
|
651
|
+
type TextName = '#text';
|
|
652
|
+
type ElementNames = 'ink-root' | 'ink-box' | 'ink-text' | 'ink-virtual-text' | 'ink-link' | 'ink-progress' | 'ink-raw-ansi';
|
|
653
|
+
type NodeNames = ElementNames | TextName;
|
|
654
|
+
type DOMElement = {
|
|
655
|
+
nodeName: ElementNames;
|
|
656
|
+
attributes: Record<string, DOMNodeAttribute>;
|
|
657
|
+
childNodes: DOMNode[];
|
|
658
|
+
textStyles?: TextStyles;
|
|
659
|
+
onComputeLayout?: () => void;
|
|
660
|
+
onRender?: () => void;
|
|
661
|
+
onImmediateRender?: () => void;
|
|
662
|
+
hasRenderedContent?: boolean;
|
|
663
|
+
dirty: boolean;
|
|
664
|
+
isHidden?: boolean;
|
|
665
|
+
_eventHandlers?: Record<string, unknown>;
|
|
666
|
+
scrollTop?: number;
|
|
667
|
+
pendingScrollDelta?: number;
|
|
668
|
+
scrollClampMin?: number;
|
|
669
|
+
scrollClampMax?: number;
|
|
670
|
+
scrollHeight?: number;
|
|
671
|
+
scrollViewportHeight?: number;
|
|
672
|
+
scrollViewportTop?: number;
|
|
673
|
+
stickyScroll?: boolean;
|
|
674
|
+
scrollAnchor?: {
|
|
675
|
+
el: DOMElement;
|
|
676
|
+
offset: number;
|
|
677
|
+
};
|
|
678
|
+
focusManager?: FocusManager;
|
|
679
|
+
debugOwnerChain?: string[];
|
|
680
|
+
} & InkNode;
|
|
681
|
+
type TextNode = {
|
|
682
|
+
nodeName: TextName;
|
|
683
|
+
nodeValue: string;
|
|
684
|
+
} & InkNode;
|
|
685
|
+
type DOMNode<T = {
|
|
686
|
+
nodeName: NodeNames;
|
|
687
|
+
}> = T extends {
|
|
688
|
+
nodeName: infer U;
|
|
689
|
+
} ? U extends '#text' ? TextNode : DOMElement : never;
|
|
690
|
+
type DOMNodeAttribute = boolean | string | number;
|
|
691
|
+
|
|
692
|
+
type ScrollHint = {
|
|
693
|
+
top: number;
|
|
694
|
+
bottom: number;
|
|
695
|
+
delta: number;
|
|
696
|
+
};
|
|
697
|
+
|
|
698
|
+
type Frame = {
|
|
699
|
+
readonly screen: Screen;
|
|
700
|
+
readonly viewport: Size;
|
|
701
|
+
readonly cursor: Cursor;
|
|
702
|
+
/** DECSTBM scroll optimization hint (alt-screen only, null otherwise). */
|
|
703
|
+
readonly scrollHint?: ScrollHint | null;
|
|
704
|
+
/** A ScrollBox has remaining pendingScrollDelta — schedule another frame. */
|
|
705
|
+
readonly scrollDrainPending?: boolean;
|
|
706
|
+
};
|
|
707
|
+
type FlickerReason = 'resize' | 'offscreen' | 'clear';
|
|
708
|
+
type FrameEvent = {
|
|
709
|
+
durationMs: number;
|
|
710
|
+
/** Phase breakdown in ms + patch count. Populated when the ink instance
|
|
711
|
+
* has frame-timing instrumentation enabled (via onFrame wiring). */
|
|
712
|
+
phases?: {
|
|
713
|
+
/** createRenderer output: DOM → yoga layout → screen buffer */
|
|
714
|
+
renderer: number;
|
|
715
|
+
/** LogUpdate.render(): screen diff → Patch[] (the hot path this PR optimizes) */
|
|
716
|
+
diff: number;
|
|
717
|
+
/** optimize(): patch merge/dedupe */
|
|
718
|
+
optimize: number;
|
|
719
|
+
/** writeDiffToTerminal(): serialize patches → ANSI → stdout */
|
|
720
|
+
write: number;
|
|
721
|
+
/** Pre-optimize patch count (proxy for how much changed this frame) */
|
|
722
|
+
patches: number;
|
|
723
|
+
/** yoga calculateLayout() time (runs in resetAfterCommit, before onRender) */
|
|
724
|
+
yoga: number;
|
|
725
|
+
/** React reconcile time: scrollMutated → resetAfterCommit. 0 if no commit. */
|
|
726
|
+
commit: number;
|
|
727
|
+
/** layoutNode() calls this frame (recursive, includes cache-hit returns) */
|
|
728
|
+
yogaVisited: number;
|
|
729
|
+
/** measureFunc (text wrap/width) calls — the expensive part */
|
|
730
|
+
yogaMeasured: number;
|
|
731
|
+
/** early returns via _hasL single-slot cache */
|
|
732
|
+
yogaCacheHits: number;
|
|
733
|
+
/** total yoga Node instances alive (create - free). Growth = leak. */
|
|
734
|
+
yogaLive: number;
|
|
735
|
+
};
|
|
736
|
+
flickers: Array<{
|
|
737
|
+
desiredHeight: number;
|
|
738
|
+
availableHeight: number;
|
|
739
|
+
reason: FlickerReason;
|
|
740
|
+
}>;
|
|
741
|
+
};
|
|
742
|
+
|
|
743
|
+
/**
|
|
744
|
+
* Keyboard input parser - converts terminal input to key events
|
|
745
|
+
*
|
|
746
|
+
* Uses the termio tokenizer for escape sequence boundary detection,
|
|
747
|
+
* then interprets sequences as keypresses.
|
|
748
|
+
*/
|
|
749
|
+
|
|
750
|
+
/**
|
|
751
|
+
* A response sequence received from the terminal (not a keypress).
|
|
752
|
+
* Emitted in answer to queries like DECRQM, DA1, OSC 11, etc.
|
|
753
|
+
*/
|
|
754
|
+
type TerminalResponse =
|
|
755
|
+
/** DECRPM: answer to DECRQM (request DEC private mode status) */
|
|
756
|
+
{
|
|
757
|
+
type: 'decrpm';
|
|
758
|
+
mode: number;
|
|
759
|
+
status: number;
|
|
760
|
+
}
|
|
761
|
+
/** DA1: primary device attributes (used as a universal sentinel) */
|
|
762
|
+
| {
|
|
763
|
+
type: 'da1';
|
|
764
|
+
params: number[];
|
|
765
|
+
}
|
|
766
|
+
/** DA2: secondary device attributes (terminal version info) */
|
|
767
|
+
| {
|
|
768
|
+
type: 'da2';
|
|
769
|
+
params: number[];
|
|
770
|
+
}
|
|
771
|
+
/** Kitty keyboard protocol: current flags (answer to CSI ? u) */
|
|
772
|
+
| {
|
|
773
|
+
type: 'kittyKeyboard';
|
|
774
|
+
flags: number;
|
|
775
|
+
}
|
|
776
|
+
/** DSR: cursor position report (answer to CSI 6 n) */
|
|
777
|
+
| {
|
|
778
|
+
type: 'cursorPosition';
|
|
779
|
+
row: number;
|
|
780
|
+
col: number;
|
|
781
|
+
}
|
|
782
|
+
/** OSC response: generic operating-system-command reply (e.g. OSC 11 bg color) */
|
|
783
|
+
| {
|
|
784
|
+
type: 'osc';
|
|
785
|
+
code: number;
|
|
786
|
+
data: string;
|
|
787
|
+
}
|
|
788
|
+
/** XTVERSION: terminal name/version string (answer to CSI > 0 q).
|
|
789
|
+
* Example values: "xterm.js(5.5.0)", "ghostty 1.2.0", "iTerm2 3.6". */
|
|
790
|
+
| {
|
|
791
|
+
type: 'xtversion';
|
|
792
|
+
name: string;
|
|
793
|
+
};
|
|
794
|
+
type ParsedKey = {
|
|
795
|
+
kind: 'key';
|
|
796
|
+
fn: boolean;
|
|
797
|
+
name: string | undefined;
|
|
798
|
+
ctrl: boolean;
|
|
799
|
+
meta: boolean;
|
|
800
|
+
shift: boolean;
|
|
801
|
+
option: boolean;
|
|
802
|
+
super: boolean;
|
|
803
|
+
sequence: string | undefined;
|
|
804
|
+
raw: string | undefined;
|
|
805
|
+
code?: string;
|
|
806
|
+
isPasted: boolean;
|
|
807
|
+
};
|
|
808
|
+
|
|
809
|
+
/** Position of a match within a rendered message, relative to the message's
|
|
810
|
+
* own bounding box (row 0 = message top). Stable across scroll — to
|
|
811
|
+
* highlight on the real screen, add the message's screen-row offset. */
|
|
812
|
+
type MatchPosition = {
|
|
813
|
+
row: number;
|
|
814
|
+
col: number;
|
|
815
|
+
/** Number of CELLS the match spans (= query.length for ASCII, more
|
|
816
|
+
* for wide chars in the query). */
|
|
817
|
+
len: number;
|
|
818
|
+
};
|
|
819
|
+
|
|
820
|
+
/**
|
|
821
|
+
* Text selection state for fullscreen mode.
|
|
822
|
+
*
|
|
823
|
+
* Tracks a linear selection in screen-buffer coordinates (0-indexed col/row).
|
|
824
|
+
* Selection is line-based: cells from (startCol, startRow) through
|
|
825
|
+
* (endCol, endRow) inclusive, wrapping across line boundaries. This matches
|
|
826
|
+
* terminal-native selection behavior (not rectangular/block).
|
|
827
|
+
*
|
|
828
|
+
* The selection is stored as ANCHOR (where the drag started) + FOCUS (where
|
|
829
|
+
* the cursor is now). The rendered highlight normalizes to start ≤ end.
|
|
830
|
+
*/
|
|
831
|
+
|
|
832
|
+
type Point = {
|
|
833
|
+
col: number;
|
|
834
|
+
row: number;
|
|
835
|
+
};
|
|
836
|
+
type SelectionState = {
|
|
837
|
+
/** Where the mouse-down occurred. Null when no selection. */
|
|
838
|
+
anchor: Point | null;
|
|
839
|
+
/** Current drag position (updated on mouse-move while dragging). */
|
|
840
|
+
focus: Point | null;
|
|
841
|
+
/** True between mouse-down and mouse-up. */
|
|
842
|
+
isDragging: boolean;
|
|
843
|
+
/** For word/line mode: the initial word/line bounds from the first
|
|
844
|
+
* multi-click. Drag extends from this span to the word/line at the
|
|
845
|
+
* current mouse position so the original word/line stays selected
|
|
846
|
+
* even when dragging backward past it. Null ⇔ char mode. The kind
|
|
847
|
+
* tells extendSelection whether to snap to word or line boundaries. */
|
|
848
|
+
anchorSpan: {
|
|
849
|
+
lo: Point;
|
|
850
|
+
hi: Point;
|
|
851
|
+
kind: 'word' | 'line';
|
|
852
|
+
} | null;
|
|
853
|
+
/** Text from rows that scrolled out ABOVE the viewport during
|
|
854
|
+
* drag-to-scroll. The screen buffer only holds the current viewport,
|
|
855
|
+
* so without this accumulator, dragging down past the bottom edge
|
|
856
|
+
* loses the top of the selection once the anchor clamps. Prepended
|
|
857
|
+
* to the on-screen text by getSelectedText. Reset on start/clear. */
|
|
858
|
+
scrolledOffAbove: string[];
|
|
859
|
+
/** Symmetric: rows scrolled out BELOW when dragging up. Appended. */
|
|
860
|
+
scrolledOffBelow: string[];
|
|
861
|
+
/** Soft-wrap bits parallel to scrolledOffAbove — true means the row
|
|
862
|
+
* is a continuation of the one before it (the `\n` was inserted by
|
|
863
|
+
* word-wrap, not in the source). Captured alongside the text at
|
|
864
|
+
* scroll time since the screen's softWrap bitmap shifts with content.
|
|
865
|
+
* getSelectedText uses these to join wrapped rows back into logical
|
|
866
|
+
* lines. */
|
|
867
|
+
scrolledOffAboveSW: boolean[];
|
|
868
|
+
/** Parallel to scrolledOffBelow. */
|
|
869
|
+
scrolledOffBelowSW: boolean[];
|
|
870
|
+
/** Pre-clamp anchor row. Set when shiftSelection clamps anchor so a
|
|
871
|
+
* reverse scroll can restore the true position and pop accumulators.
|
|
872
|
+
* Without this, PgDn (clamps anchor) → PgUp leaves anchor at the wrong
|
|
873
|
+
* row AND scrolledOffAbove stale — highlight ≠ copy. Undefined when
|
|
874
|
+
* anchor is in-bounds (no clamp debt). Cleared on start/clear. */
|
|
875
|
+
virtualAnchorRow?: number;
|
|
876
|
+
/** Same for focus. */
|
|
877
|
+
virtualFocusRow?: number;
|
|
878
|
+
/** True if the mouse-down that started this selection had the alt
|
|
879
|
+
* modifier set (SGR button bit 0x08). On macOS xterm.js this is a
|
|
880
|
+
* signal that VS Code's macOptionClickForcesSelection is OFF — if it
|
|
881
|
+
* were on, xterm.js would have consumed the event for native selection
|
|
882
|
+
* and we'd never receive it. Used by the footer to show the right hint. */
|
|
883
|
+
lastPressHadAlt: boolean;
|
|
884
|
+
};
|
|
885
|
+
/** Semantic keyboard focus moves. See moveSelectionFocus in ink.tsx for
|
|
886
|
+
* how screen bounds + row-wrap are applied. */
|
|
887
|
+
type FocusMove = 'left' | 'right' | 'up' | 'down' | 'lineStart' | 'lineEnd';
|
|
888
|
+
|
|
889
|
+
type Options$1 = {
|
|
890
|
+
stdout: NodeJS.WriteStream;
|
|
891
|
+
stdin: NodeJS.ReadStream;
|
|
892
|
+
stderr: NodeJS.WriteStream;
|
|
893
|
+
exitOnCtrlC: boolean;
|
|
894
|
+
patchConsole: boolean;
|
|
895
|
+
waitUntilExit?: () => Promise<void>;
|
|
896
|
+
onFrame?: (event: FrameEvent) => void;
|
|
897
|
+
};
|
|
898
|
+
declare class Ink {
|
|
899
|
+
private readonly options;
|
|
900
|
+
private readonly log;
|
|
901
|
+
private readonly terminal;
|
|
902
|
+
private scheduleRender;
|
|
903
|
+
private isUnmounted;
|
|
904
|
+
private isPaused;
|
|
905
|
+
private readonly container;
|
|
906
|
+
private rootNode;
|
|
907
|
+
readonly focusManager: FocusManager;
|
|
908
|
+
private renderer;
|
|
909
|
+
private readonly stylePool;
|
|
910
|
+
private charPool;
|
|
911
|
+
private hyperlinkPool;
|
|
912
|
+
private exitPromise?;
|
|
913
|
+
private restoreConsole?;
|
|
914
|
+
private restoreStderr?;
|
|
915
|
+
private readonly unsubscribeTTYHandlers?;
|
|
916
|
+
private terminalColumns;
|
|
917
|
+
private terminalRows;
|
|
918
|
+
private currentNode;
|
|
919
|
+
private frontFrame;
|
|
920
|
+
private backFrame;
|
|
921
|
+
private lastPoolResetTime;
|
|
922
|
+
private drainTimer;
|
|
923
|
+
private lastYogaCounters;
|
|
924
|
+
private altScreenParkPatch;
|
|
925
|
+
readonly selection: SelectionState;
|
|
926
|
+
private searchHighlightQuery;
|
|
927
|
+
private searchPositions;
|
|
928
|
+
private readonly selectionListeners;
|
|
929
|
+
private readonly hoveredNodes;
|
|
930
|
+
private altScreenActive;
|
|
931
|
+
private altScreenMouseTracking;
|
|
932
|
+
private prevFrameContaminated;
|
|
933
|
+
private needsEraseBeforePaint;
|
|
934
|
+
private cursorDeclaration;
|
|
935
|
+
private displayCursor;
|
|
936
|
+
constructor(options: Options$1);
|
|
937
|
+
private handleResume;
|
|
938
|
+
private handleResize;
|
|
939
|
+
resolveExitPromise: () => void;
|
|
940
|
+
rejectExitPromise: (reason?: Error) => void;
|
|
941
|
+
unsubscribeExit: () => void;
|
|
942
|
+
/**
|
|
943
|
+
* Pause Ink and hand the terminal over to an external TUI (e.g. git
|
|
944
|
+
* commit editor). In non-fullscreen mode this enters the alt screen;
|
|
945
|
+
* in fullscreen mode we're already in alt so we just clear it.
|
|
946
|
+
* Call `exitAlternateScreen()` when done to restore Ink.
|
|
947
|
+
*/
|
|
948
|
+
enterAlternateScreen(): void;
|
|
949
|
+
/**
|
|
950
|
+
* Resume Ink after an external TUI handoff with a full repaint.
|
|
951
|
+
* In non-fullscreen mode this exits the alt screen back to main;
|
|
952
|
+
* in fullscreen mode we re-enter alt and clear + repaint.
|
|
953
|
+
*
|
|
954
|
+
* The re-enter matters: terminal editors (vim, nano, less) write
|
|
955
|
+
* smcup/rmcup (?1049h/?1049l), so even though we started in alt,
|
|
956
|
+
* the editor's rmcup on exit drops us to main screen. Without
|
|
957
|
+
* re-entering, the 2J below wipes the user's main-screen scrollback
|
|
958
|
+
* and subsequent renders land in main — native terminal scroll
|
|
959
|
+
* returns, fullscreen scroll is dead.
|
|
960
|
+
*/
|
|
961
|
+
exitAlternateScreen(): void;
|
|
962
|
+
onRender(): void;
|
|
963
|
+
pause(): void;
|
|
964
|
+
resume(): void;
|
|
965
|
+
/**
|
|
966
|
+
* Reset frame buffers so the next render writes the full screen from scratch.
|
|
967
|
+
* Call this before resume() when the terminal content has been corrupted by
|
|
968
|
+
* an external process (e.g. tmux, shell, full-screen TUI).
|
|
969
|
+
*/
|
|
970
|
+
repaint(): void;
|
|
971
|
+
/**
|
|
972
|
+
* Clear the physical terminal and force a full redraw.
|
|
973
|
+
*
|
|
974
|
+
* The traditional readline ctrl+l — clears the visible screen and
|
|
975
|
+
* redraws the current content. Also the recovery path when the terminal
|
|
976
|
+
* was cleared externally (macOS Cmd+K) and Ink's diff engine thinks
|
|
977
|
+
* unchanged cells don't need repainting. Scrollback is preserved.
|
|
978
|
+
*/
|
|
979
|
+
forceRedraw(): void;
|
|
980
|
+
/**
|
|
981
|
+
* Mark the previous frame as untrustworthy for blit, forcing the next
|
|
982
|
+
* render to do a full-damage diff instead of the per-node fast path.
|
|
983
|
+
*
|
|
984
|
+
* Lighter than forceRedraw() — no screen clear, no extra write. Call
|
|
985
|
+
* from a useLayoutEffect cleanup when unmounting a tall overlay: the
|
|
986
|
+
* blit fast path can copy stale cells from the overlay frame into rows
|
|
987
|
+
* the shrunken layout no longer reaches, leaving a ghost title/divider.
|
|
988
|
+
* onRender resets the flag at frame end so it's one-shot.
|
|
989
|
+
*/
|
|
990
|
+
invalidatePrevFrame(): void;
|
|
991
|
+
/**
|
|
992
|
+
* Called by the <AlternateScreen> component on mount/unmount.
|
|
993
|
+
* Controls cursor.y clamping in the renderer and gates alt-screen-aware
|
|
994
|
+
* behavior in SIGCONT/resize/unmount handlers. Repaints on change so
|
|
995
|
+
* the first alt-screen frame (and first main-screen frame on exit) is
|
|
996
|
+
* a full redraw with no stale diff state.
|
|
997
|
+
*/
|
|
998
|
+
setAltScreenActive(active: boolean, mouseTracking?: boolean): void;
|
|
999
|
+
get isAltScreenActive(): boolean;
|
|
1000
|
+
/**
|
|
1001
|
+
* Re-assert terminal modes after a gap (>5s stdin silence or event-loop
|
|
1002
|
+
* stall). Catches tmux detach→attach, ssh reconnect, and laptop
|
|
1003
|
+
* sleep/wake — none of which send SIGCONT. The terminal may reset DEC
|
|
1004
|
+
* private modes on reconnect; this method restores them.
|
|
1005
|
+
*
|
|
1006
|
+
* Always re-asserts extended key reporting and mouse tracking. Mouse
|
|
1007
|
+
* tracking is idempotent (DEC private mode set-when-set is a no-op). The
|
|
1008
|
+
* Kitty keyboard protocol is NOT — CSI >1u is a stack push, so we pop
|
|
1009
|
+
* first to keep depth balanced (pop on empty stack is a no-op per spec,
|
|
1010
|
+
* so after a terminal reset this still restores depth 0→1). Without the
|
|
1011
|
+
* pop, each >5s idle gap adds a stack entry, and the single pop on exit
|
|
1012
|
+
* or suspend can't drain them — the shell is left in CSI u mode where
|
|
1013
|
+
* Ctrl+C/Ctrl+D leak as escape sequences. The alt-screen
|
|
1014
|
+
* re-entry (ERASE_SCREEN + frame reset) is NOT idempotent — it blanks the
|
|
1015
|
+
* screen — so it's opt-in via includeAltScreen. The stdin-gap caller fires
|
|
1016
|
+
* on ordinary >5s idle + keypress and must not erase; the event-loop stall
|
|
1017
|
+
* detector fires on genuine sleep/wake and opts in. tmux attach / ssh
|
|
1018
|
+
* reconnect typically send a resize, which already covers alt-screen via
|
|
1019
|
+
* handleResize.
|
|
1020
|
+
*/
|
|
1021
|
+
reassertTerminalModes: (includeAltScreen?: boolean) => void;
|
|
1022
|
+
/**
|
|
1023
|
+
* Mark this instance as unmounted so future unmount() calls early-return.
|
|
1024
|
+
* Called by gracefulShutdown's cleanupTerminalModes() after it has sent
|
|
1025
|
+
* EXIT_ALT_SCREEN but before the remaining terminal-reset sequences.
|
|
1026
|
+
* Without this, signal-exit's deferred ink.unmount() (triggered by
|
|
1027
|
+
* process.exit()) runs the full unmount path: onRender() + writeSync
|
|
1028
|
+
* cleanup block + updateContainerSync → AlternateScreen unmount cleanup.
|
|
1029
|
+
* The result is 2-3 redundant EXIT_ALT_SCREEN sequences landing on the
|
|
1030
|
+
* main screen AFTER printResumeHint(), which tmux (at least) interprets
|
|
1031
|
+
* as restoring the saved cursor position — clobbering the resume hint.
|
|
1032
|
+
*/
|
|
1033
|
+
detachForShutdown(): void;
|
|
1034
|
+
/** @see drainStdin */
|
|
1035
|
+
drainStdin(): void;
|
|
1036
|
+
/**
|
|
1037
|
+
* Re-enter alt-screen, clear, home, re-enable mouse tracking, and reset
|
|
1038
|
+
* frame buffers so the next render repaints from scratch. Self-heal for
|
|
1039
|
+
* SIGCONT, resize, and stdin-gap/event-loop-stall (sleep/wake) — any of
|
|
1040
|
+
* which can leave the terminal in main-screen mode while altScreenActive
|
|
1041
|
+
* stays true. ENTER_ALT_SCREEN is a terminal-side no-op if already in alt.
|
|
1042
|
+
*/
|
|
1043
|
+
private reenterAltScreen;
|
|
1044
|
+
/**
|
|
1045
|
+
* Seed prev/back frames with full-size BLANK screens (rows×cols of empty
|
|
1046
|
+
* cells, not 0×0). In alt-screen mode, next.screen.height is always
|
|
1047
|
+
* terminalRows; if prev.screen.height is 0 (emptyFrame's default),
|
|
1048
|
+
* log-update sees heightDelta > 0 ('growing') and calls renderFrameSlice,
|
|
1049
|
+
* whose trailing per-row CR+LF at the last row scrolls the alt screen,
|
|
1050
|
+
* permanently desyncing the virtual and physical cursors by 1 row.
|
|
1051
|
+
*
|
|
1052
|
+
* With a rows×cols blank prev, heightDelta === 0 → standard diffEach
|
|
1053
|
+
* → moveCursorTo (CSI cursorMove, no LF, no scroll).
|
|
1054
|
+
*
|
|
1055
|
+
* viewport.height = rows + 1 matches the renderer's alt-screen output,
|
|
1056
|
+
* preventing a spurious resize trigger on the first frame. cursor.y = 0
|
|
1057
|
+
* matches the physical cursor after ENTER_ALT_SCREEN + CSI H (home).
|
|
1058
|
+
*/
|
|
1059
|
+
private resetFramesForAltScreen;
|
|
1060
|
+
/**
|
|
1061
|
+
* Copy the current selection to the clipboard without clearing the
|
|
1062
|
+
* highlight. Matches iTerm2's copy-on-select behavior where the selected
|
|
1063
|
+
* region stays visible after the automatic copy.
|
|
1064
|
+
*/
|
|
1065
|
+
copySelectionNoClear(): string;
|
|
1066
|
+
/**
|
|
1067
|
+
* Copy the current text selection to the system clipboard via OSC 52
|
|
1068
|
+
* and clear the selection. Returns the copied text (empty if no selection).
|
|
1069
|
+
*/
|
|
1070
|
+
copySelection(): string;
|
|
1071
|
+
/** Clear the current text selection without copying. */
|
|
1072
|
+
clearTextSelection(): void;
|
|
1073
|
+
/**
|
|
1074
|
+
* Set the search highlight query. Non-empty → all visible occurrences
|
|
1075
|
+
* are inverted (SGR 7) on the next frame; first one also underlined.
|
|
1076
|
+
* Empty → clears (prevFrameContaminated handles the frame after). Same
|
|
1077
|
+
* damage-tracking machinery as selection — setCellStyleId doesn't track
|
|
1078
|
+
* damage, so the overlay forces full-frame damage while active.
|
|
1079
|
+
*/
|
|
1080
|
+
setSearchHighlight(query: string): void;
|
|
1081
|
+
/** Paint an EXISTING DOM subtree to a fresh Screen at its natural
|
|
1082
|
+
* height, scan for query. Returns positions relative to the element's
|
|
1083
|
+
* bounding box (row 0 = element top).
|
|
1084
|
+
*
|
|
1085
|
+
* The element comes from the MAIN tree — built with all real
|
|
1086
|
+
* providers, yoga already computed. We paint it to a fresh buffer
|
|
1087
|
+
* with offsets so it lands at (0,0). Same paint path as the main
|
|
1088
|
+
* render. Zero drift. No second React root, no context bridge.
|
|
1089
|
+
*
|
|
1090
|
+
* ~1-2ms (paint only, no reconcile — the DOM is already built). */
|
|
1091
|
+
scanElementSubtree(el: DOMElement): MatchPosition[];
|
|
1092
|
+
/** Set the position-based highlight state. Every frame, writes CURRENT
|
|
1093
|
+
* style at positions[currentIdx] + rowOffset. null clears. The scan-
|
|
1094
|
+
* highlight (inverse on all matches) still runs — this overlays yellow
|
|
1095
|
+
* on top. rowOffset changes as the user scrolls (= message's current
|
|
1096
|
+
* screen-top); positions stay stable (message-relative). */
|
|
1097
|
+
setSearchPositions(state: {
|
|
1098
|
+
positions: MatchPosition[];
|
|
1099
|
+
rowOffset: number;
|
|
1100
|
+
currentIdx: number;
|
|
1101
|
+
} | null): void;
|
|
1102
|
+
/**
|
|
1103
|
+
* Set the selection highlight background color. Replaces the per-cell
|
|
1104
|
+
* SGR-7 inverse with a solid theme-aware bg (matches native terminal
|
|
1105
|
+
* selection). Accepts the same color formats as Text backgroundColor
|
|
1106
|
+
* (rgb(), ansi:name, #hex, ansi256()) — colorize() routes through
|
|
1107
|
+
* chalk so the tmux/xterm.js level clamps in colorize.ts apply and
|
|
1108
|
+
* the emitted SGR is correct for the current terminal.
|
|
1109
|
+
*
|
|
1110
|
+
* Called by React-land once theme is known (ScrollKeybindingHandler's
|
|
1111
|
+
* useEffect watching useTheme). Before that call, withSelectionBg
|
|
1112
|
+
* falls back to withInverse so selection still renders on the first
|
|
1113
|
+
* frame; the effect fires before any mouse input so the fallback is
|
|
1114
|
+
* unobservable in practice.
|
|
1115
|
+
*/
|
|
1116
|
+
setSelectionBgColor(color: string): void;
|
|
1117
|
+
/**
|
|
1118
|
+
* Capture text from rows about to scroll out of the viewport during
|
|
1119
|
+
* drag-to-scroll. Must be called BEFORE the ScrollBox scrolls so the
|
|
1120
|
+
* screen buffer still holds the outgoing content. Accumulated into
|
|
1121
|
+
* the selection state and joined back in by getSelectedText.
|
|
1122
|
+
*/
|
|
1123
|
+
captureScrolledRows(firstRow: number, lastRow: number, side: 'above' | 'below'): void;
|
|
1124
|
+
/**
|
|
1125
|
+
* Shift anchor AND focus by dRow, clamped to [minRow, maxRow]. Used by
|
|
1126
|
+
* keyboard scroll handlers (PgUp/PgDn etc.) so the highlight tracks the
|
|
1127
|
+
* content instead of disappearing. Unlike shiftAnchor (drag-to-scroll),
|
|
1128
|
+
* this moves BOTH endpoints — the user isn't holding the mouse at one
|
|
1129
|
+
* edge. Supplies screen.width for the col-reset-on-clamp boundary.
|
|
1130
|
+
*/
|
|
1131
|
+
shiftSelectionForScroll(dRow: number, minRow: number, maxRow: number): void;
|
|
1132
|
+
/**
|
|
1133
|
+
* Keyboard selection extension (shift+arrow/home/end). Moves focus;
|
|
1134
|
+
* anchor stays fixed so the highlight grows or shrinks relative to it.
|
|
1135
|
+
* Left/right wrap across row boundaries — native macOS text-edit
|
|
1136
|
+
* behavior: shift+left at col 0 wraps to end of the previous row.
|
|
1137
|
+
* Up/down clamp at viewport edges (no scroll-to-extend yet). Drops to
|
|
1138
|
+
* char mode. No-op outside alt-screen or without an active selection.
|
|
1139
|
+
*/
|
|
1140
|
+
moveSelectionFocus(move: FocusMove): void;
|
|
1141
|
+
/** Whether there is an active text selection. */
|
|
1142
|
+
hasTextSelection(): boolean;
|
|
1143
|
+
/**
|
|
1144
|
+
* Subscribe to selection state changes. Fires whenever the selection
|
|
1145
|
+
* is started, updated, cleared, or copied. Returns an unsubscribe fn.
|
|
1146
|
+
*/
|
|
1147
|
+
subscribeToSelectionChange(cb: () => void): () => void;
|
|
1148
|
+
private notifySelectionChange;
|
|
1149
|
+
/**
|
|
1150
|
+
* Hit-test the rendered DOM tree at (col, row) and bubble a ClickEvent
|
|
1151
|
+
* from the deepest hit node up through ancestors with onClick handlers.
|
|
1152
|
+
* Returns true if a DOM handler consumed the click. Gated on
|
|
1153
|
+
* altScreenActive — clicks only make sense with a fixed viewport where
|
|
1154
|
+
* nodeCache rects map 1:1 to terminal cells (no scrollback offset).
|
|
1155
|
+
*/
|
|
1156
|
+
dispatchClick(col: number, row: number): boolean;
|
|
1157
|
+
dispatchHover(col: number, row: number): void;
|
|
1158
|
+
dispatchKeyboardEvent(parsedKey: ParsedKey): void;
|
|
1159
|
+
/**
|
|
1160
|
+
* Look up the URL at (col, row) in the current front frame. Checks for
|
|
1161
|
+
* an OSC 8 hyperlink first, then falls back to scanning the row for a
|
|
1162
|
+
* plain-text URL (mouse tracking intercepts the terminal's native
|
|
1163
|
+
* Cmd+Click URL detection, so we replicate it). This is a pure lookup
|
|
1164
|
+
* with no side effects — call it synchronously at click time so the
|
|
1165
|
+
* result reflects the screen the user actually clicked on, then defer
|
|
1166
|
+
* the browser-open action via a timer.
|
|
1167
|
+
*/
|
|
1168
|
+
getHyperlinkAt(col: number, row: number): string | undefined;
|
|
1169
|
+
/**
|
|
1170
|
+
* Optional callback fired when clicking an OSC 8 hyperlink in fullscreen
|
|
1171
|
+
* mode. Set by FullscreenLayout via useLayoutEffect.
|
|
1172
|
+
*/
|
|
1173
|
+
onHyperlinkClick: ((url: string) => void) | undefined;
|
|
1174
|
+
/**
|
|
1175
|
+
* Stable prototype wrapper for onHyperlinkClick. Passed to <App> as
|
|
1176
|
+
* onOpenHyperlink so the prop is a bound method (autoBind'd) that reads
|
|
1177
|
+
* the mutable field at call time — not the undefined-at-render value.
|
|
1178
|
+
*/
|
|
1179
|
+
openHyperlink(url: string): void;
|
|
1180
|
+
/**
|
|
1181
|
+
* Handle a double- or triple-click at (col, row): select the word or
|
|
1182
|
+
* line under the cursor by reading the current screen buffer. Called on
|
|
1183
|
+
* PRESS (not release) so the highlight appears immediately and drag can
|
|
1184
|
+
* extend the selection word-by-word / line-by-line. Falls back to
|
|
1185
|
+
* char-mode startSelection if the click lands on a noSelect cell.
|
|
1186
|
+
*/
|
|
1187
|
+
handleMultiClick(col: number, row: number, count: 2 | 3): void;
|
|
1188
|
+
/**
|
|
1189
|
+
* Handle a drag-motion at (col, row). In char mode updates focus to the
|
|
1190
|
+
* exact cell. In word/line mode snaps to word/line boundaries so the
|
|
1191
|
+
* selection extends by word/line like native macOS. Gated on
|
|
1192
|
+
* altScreenActive for the same reason as dispatchClick.
|
|
1193
|
+
*/
|
|
1194
|
+
handleSelectionDrag(col: number, row: number): void;
|
|
1195
|
+
private stdinListeners;
|
|
1196
|
+
private wasRawMode;
|
|
1197
|
+
suspendStdin(): void;
|
|
1198
|
+
resumeStdin(): void;
|
|
1199
|
+
private writeRaw;
|
|
1200
|
+
private setCursorDeclaration;
|
|
1201
|
+
render(node: ReactNode): void;
|
|
1202
|
+
unmount(error?: Error | number | null): void;
|
|
1203
|
+
waitUntilExit(): Promise<void>;
|
|
1204
|
+
resetLineCount(): void;
|
|
1205
|
+
/**
|
|
1206
|
+
* Replace char/hyperlink pools with fresh instances to prevent unbounded
|
|
1207
|
+
* growth during long sessions. Migrates the front frame's screen IDs into
|
|
1208
|
+
* the new pools so diffing remains correct. The back frame doesn't need
|
|
1209
|
+
* migration — resetScreen zeros it before any reads.
|
|
1210
|
+
*
|
|
1211
|
+
* Call between conversation turns or periodically.
|
|
1212
|
+
*/
|
|
1213
|
+
resetPools(): void;
|
|
1214
|
+
patchConsole(): () => void;
|
|
1215
|
+
/**
|
|
1216
|
+
* Intercept process.stderr.write so stray writes (config.ts, hooks.ts,
|
|
1217
|
+
* third-party deps) don't corrupt the alt-screen buffer. patchConsole only
|
|
1218
|
+
* hooks console.* methods — direct stderr writes bypass it, land at the
|
|
1219
|
+
* parked cursor, scroll the alt-screen, and desync frontFrame from the
|
|
1220
|
+
* physical terminal. Next diff writes only changed-in-React cells at
|
|
1221
|
+
* absolute coords → interleaved garbage.
|
|
1222
|
+
*
|
|
1223
|
+
* Swallows the write (routes text to the debug log) and, in alt-screen,
|
|
1224
|
+
* forces a full-damage repaint as a defensive recovery. Not patching
|
|
1225
|
+
* process.stdout — Ink itself writes there.
|
|
1226
|
+
*/
|
|
1227
|
+
private patchStderr;
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1230
|
+
type RenderOptions = {
|
|
1231
|
+
/**
|
|
1232
|
+
* Output stream where app will be rendered.
|
|
1233
|
+
*
|
|
1234
|
+
* @default process.stdout
|
|
1235
|
+
*/
|
|
1236
|
+
stdout?: NodeJS.WriteStream;
|
|
1237
|
+
/**
|
|
1238
|
+
* Input stream where app will listen for input.
|
|
1239
|
+
*
|
|
1240
|
+
* @default process.stdin
|
|
1241
|
+
*/
|
|
1242
|
+
stdin?: NodeJS.ReadStream;
|
|
1243
|
+
/**
|
|
1244
|
+
* Error stream.
|
|
1245
|
+
* @default process.stderr
|
|
1246
|
+
*/
|
|
1247
|
+
stderr?: NodeJS.WriteStream;
|
|
1248
|
+
/**
|
|
1249
|
+
* Configure whether Ink should listen to Ctrl+C keyboard input and exit the app. This is needed in case `process.stdin` is in raw mode, because then Ctrl+C is ignored by default and process is expected to handle it manually.
|
|
1250
|
+
*
|
|
1251
|
+
* @default true
|
|
1252
|
+
*/
|
|
1253
|
+
exitOnCtrlC?: boolean;
|
|
1254
|
+
/**
|
|
1255
|
+
* Patch console methods to ensure console output doesn't mix with Ink output.
|
|
1256
|
+
*
|
|
1257
|
+
* @default true
|
|
1258
|
+
*/
|
|
1259
|
+
patchConsole?: boolean;
|
|
1260
|
+
/**
|
|
1261
|
+
* Called after each frame render with timing and flicker information.
|
|
1262
|
+
*/
|
|
1263
|
+
onFrame?: (event: FrameEvent) => void;
|
|
1264
|
+
};
|
|
1265
|
+
type Instance = {
|
|
1266
|
+
/**
|
|
1267
|
+
* Replace previous root node with a new one or update props of the current root node.
|
|
1268
|
+
*/
|
|
1269
|
+
rerender: Ink['render'];
|
|
1270
|
+
/**
|
|
1271
|
+
* Manually unmount the whole Ink app.
|
|
1272
|
+
*/
|
|
1273
|
+
unmount: Ink['unmount'];
|
|
1274
|
+
/**
|
|
1275
|
+
* Returns a promise, which resolves when app is unmounted.
|
|
1276
|
+
*/
|
|
1277
|
+
waitUntilExit: Ink['waitUntilExit'];
|
|
1278
|
+
cleanup: () => void;
|
|
1279
|
+
};
|
|
1280
|
+
/**
|
|
1281
|
+
* A managed Ink root, similar to react-dom's createRoot API.
|
|
1282
|
+
* Separates instance creation from rendering so the same root
|
|
1283
|
+
* can be reused for multiple sequential screens.
|
|
1284
|
+
*/
|
|
1285
|
+
type Root = {
|
|
1286
|
+
render: (node: ReactNode) => void;
|
|
1287
|
+
unmount: () => void;
|
|
1288
|
+
waitUntilExit: () => Promise<void>;
|
|
1289
|
+
};
|
|
1290
|
+
/**
|
|
1291
|
+
* Mount a component and render the output.
|
|
1292
|
+
*/
|
|
1293
|
+
declare const renderSync: (node: ReactNode, options?: NodeJS.WriteStream | RenderOptions) => Instance;
|
|
1294
|
+
declare const wrappedRender: (node: ReactNode, options?: NodeJS.WriteStream | RenderOptions) => Promise<Instance>;
|
|
1295
|
+
|
|
1296
|
+
/**
|
|
1297
|
+
* Create an Ink root without rendering anything yet.
|
|
1298
|
+
* Like react-dom's createRoot — call root.render() to mount a tree.
|
|
1299
|
+
*/
|
|
1300
|
+
declare function createRoot({ stdout, stdin, stderr, exitOnCtrlC, patchConsole, onFrame, }?: RenderOptions): Promise<Root>;
|
|
1301
|
+
|
|
1302
|
+
/**
|
|
1303
|
+
* Mouse click event. Fired on left-button release without drag, only when
|
|
1304
|
+
* mouse tracking is enabled (i.e. inside <AlternateScreen>).
|
|
1305
|
+
*
|
|
1306
|
+
* Bubbles from the deepest hit node up through parentNode. Call
|
|
1307
|
+
* stopImmediatePropagation() to prevent ancestors' onClick from firing.
|
|
1308
|
+
*/
|
|
1309
|
+
declare class ClickEvent extends Event {
|
|
1310
|
+
/** 0-indexed screen column of the click */
|
|
1311
|
+
readonly col: number;
|
|
1312
|
+
/** 0-indexed screen row of the click */
|
|
1313
|
+
readonly row: number;
|
|
1314
|
+
/**
|
|
1315
|
+
* Click column relative to the current handler's Box (col - box.x).
|
|
1316
|
+
* Recomputed by dispatchClick before each handler fires, so an onClick
|
|
1317
|
+
* on a container sees coords relative to that container, not to any
|
|
1318
|
+
* child the click landed on.
|
|
1319
|
+
*/
|
|
1320
|
+
localCol: number;
|
|
1321
|
+
/** Click row relative to the current handler's Box (row - box.y). */
|
|
1322
|
+
localRow: number;
|
|
1323
|
+
/**
|
|
1324
|
+
* True if the clicked cell has no visible content (unwritten in the
|
|
1325
|
+
* screen buffer — both packed words are 0). Handlers can check this to
|
|
1326
|
+
* ignore clicks on blank space to the right of text, so accidental
|
|
1327
|
+
* clicks on empty terminal space don't toggle state.
|
|
1328
|
+
*/
|
|
1329
|
+
readonly cellIsBlank: boolean;
|
|
1330
|
+
constructor(col: number, row: number, cellIsBlank: boolean);
|
|
1331
|
+
}
|
|
1332
|
+
|
|
1333
|
+
/**
|
|
1334
|
+
* Keyboard event dispatched through the DOM tree via capture/bubble.
|
|
1335
|
+
*
|
|
1336
|
+
* Follows browser KeyboardEvent semantics: `key` is the literal character
|
|
1337
|
+
* for printable keys ('a', '3', ' ', '/') and a multi-char name for
|
|
1338
|
+
* special keys ('down', 'return', 'escape', 'f1'). The idiomatic
|
|
1339
|
+
* printable-char check is `e.key.length === 1`.
|
|
1340
|
+
*/
|
|
1341
|
+
declare class KeyboardEvent extends TerminalEvent {
|
|
1342
|
+
readonly key: string;
|
|
1343
|
+
readonly ctrl: boolean;
|
|
1344
|
+
readonly shift: boolean;
|
|
1345
|
+
readonly meta: boolean;
|
|
1346
|
+
readonly superKey: boolean;
|
|
1347
|
+
readonly fn: boolean;
|
|
1348
|
+
constructor(parsedKey: ParsedKey);
|
|
1349
|
+
}
|
|
1350
|
+
|
|
1351
|
+
type Props$b = Except<Styles, 'textWrap'> & {
|
|
1352
|
+
ref?: Ref<DOMElement>;
|
|
1353
|
+
/**
|
|
1354
|
+
* Tab order index. Nodes with `tabIndex >= 0` participate in
|
|
1355
|
+
* Tab/Shift+Tab cycling; `-1` means programmatically focusable only.
|
|
1356
|
+
*/
|
|
1357
|
+
tabIndex?: number;
|
|
1358
|
+
/**
|
|
1359
|
+
* Focus this element when it mounts. Like the HTML `autofocus`
|
|
1360
|
+
* attribute — the FocusManager calls `focus(node)` during the
|
|
1361
|
+
* reconciler's `commitMount` phase.
|
|
1362
|
+
*/
|
|
1363
|
+
autoFocus?: boolean;
|
|
1364
|
+
/**
|
|
1365
|
+
* Fired on left-button click (press + release without drag). Only works
|
|
1366
|
+
* inside `<AlternateScreen>` where mouse tracking is enabled — no-op
|
|
1367
|
+
* otherwise. The event bubbles from the deepest hit Box up through
|
|
1368
|
+
* ancestors; call `event.stopImmediatePropagation()` to stop bubbling.
|
|
1369
|
+
*/
|
|
1370
|
+
onClick?: (event: ClickEvent) => void;
|
|
1371
|
+
onFocus?: (event: FocusEvent) => void;
|
|
1372
|
+
onFocusCapture?: (event: FocusEvent) => void;
|
|
1373
|
+
onBlur?: (event: FocusEvent) => void;
|
|
1374
|
+
onBlurCapture?: (event: FocusEvent) => void;
|
|
1375
|
+
onKeyDown?: (event: KeyboardEvent) => void;
|
|
1376
|
+
onKeyDownCapture?: (event: KeyboardEvent) => void;
|
|
1377
|
+
/**
|
|
1378
|
+
* Fired when the mouse moves into this Box's rendered rect. Like DOM
|
|
1379
|
+
* `mouseenter`, does NOT bubble — moving between children does not
|
|
1380
|
+
* re-fire on the parent. Only works inside `<AlternateScreen>` where
|
|
1381
|
+
* mode-1003 mouse tracking is enabled.
|
|
1382
|
+
*/
|
|
1383
|
+
onMouseEnter?: () => void;
|
|
1384
|
+
/** Fired when the mouse moves out of this Box's rendered rect. */
|
|
1385
|
+
onMouseLeave?: () => void;
|
|
1386
|
+
};
|
|
1387
|
+
/**
|
|
1388
|
+
* `<Box>` is an essential Ink component to build your layout. It's like `<div style="display: flex">` in the browser.
|
|
1389
|
+
*/
|
|
1390
|
+
declare function Box(t0: PropsWithChildren<Props$b>): any;
|
|
1391
|
+
|
|
1392
|
+
type BaseProps = {
|
|
1393
|
+
/**
|
|
1394
|
+
* Change text color. Accepts a raw color value (rgb, hex, ansi).
|
|
1395
|
+
*/
|
|
1396
|
+
readonly color?: Color;
|
|
1397
|
+
/**
|
|
1398
|
+
* Same as `color`, but for background.
|
|
1399
|
+
*/
|
|
1400
|
+
readonly backgroundColor?: Color;
|
|
1401
|
+
/**
|
|
1402
|
+
* Make the text italic.
|
|
1403
|
+
*/
|
|
1404
|
+
readonly italic?: boolean;
|
|
1405
|
+
/**
|
|
1406
|
+
* Make the text underlined.
|
|
1407
|
+
*/
|
|
1408
|
+
readonly underline?: boolean;
|
|
1409
|
+
/**
|
|
1410
|
+
* Make the text crossed with a line.
|
|
1411
|
+
*/
|
|
1412
|
+
readonly strikethrough?: boolean;
|
|
1413
|
+
/**
|
|
1414
|
+
* Inverse background and foreground colors.
|
|
1415
|
+
*/
|
|
1416
|
+
readonly inverse?: boolean;
|
|
1417
|
+
/**
|
|
1418
|
+
* This property tells Ink to wrap or truncate text if its width is larger than container.
|
|
1419
|
+
* If `wrap` is passed (by default), Ink will wrap text and split it into multiple lines.
|
|
1420
|
+
* If `truncate-*` is passed, Ink will truncate text instead, which will result in one line of text with the rest cut off.
|
|
1421
|
+
*/
|
|
1422
|
+
readonly wrap?: Styles['textWrap'];
|
|
1423
|
+
readonly children?: ReactNode;
|
|
1424
|
+
};
|
|
1425
|
+
/**
|
|
1426
|
+
* Bold and dim are mutually exclusive in terminals.
|
|
1427
|
+
* This type ensures you can use one or the other, but not both.
|
|
1428
|
+
* `dimColor` is an alias for `dim` (compatibility with ink v3 API).
|
|
1429
|
+
*/
|
|
1430
|
+
type WeightProps = {
|
|
1431
|
+
bold?: boolean;
|
|
1432
|
+
dim?: boolean;
|
|
1433
|
+
dimColor?: boolean;
|
|
1434
|
+
};
|
|
1435
|
+
type Props$a = BaseProps & WeightProps;
|
|
1436
|
+
/**
|
|
1437
|
+
* This component can display text, and change its style to make it colorful, bold, underline, italic or strikethrough.
|
|
1438
|
+
*/
|
|
1439
|
+
declare function Text(t0: Props$a): any;
|
|
1440
|
+
|
|
1441
|
+
/**
|
|
1442
|
+
* A flexible space that expands along the major axis of its containing layout.
|
|
1443
|
+
* It's useful as a shortcut for filling all the available spaces between elements.
|
|
1444
|
+
*/
|
|
1445
|
+
declare function Spacer(): any;
|
|
1446
|
+
|
|
1447
|
+
type Props$9 = {
|
|
1448
|
+
/**
|
|
1449
|
+
* Number of newlines to insert.
|
|
1450
|
+
*
|
|
1451
|
+
* @default 1
|
|
1452
|
+
*/
|
|
1453
|
+
readonly count?: number;
|
|
1454
|
+
};
|
|
1455
|
+
/**
|
|
1456
|
+
* Adds one or more newline (\n) characters. Must be used within <Text> components.
|
|
1457
|
+
*/
|
|
1458
|
+
declare function Newline(t0: Props$9): any;
|
|
1459
|
+
|
|
1460
|
+
type Props$8 = {
|
|
1461
|
+
readonly children?: ReactNode;
|
|
1462
|
+
readonly url: string;
|
|
1463
|
+
readonly fallback?: ReactNode;
|
|
1464
|
+
};
|
|
1465
|
+
declare function Link(t0: Props$8): any;
|
|
1466
|
+
|
|
1467
|
+
type ButtonState = {
|
|
1468
|
+
focused: boolean;
|
|
1469
|
+
hovered: boolean;
|
|
1470
|
+
active: boolean;
|
|
1471
|
+
};
|
|
1472
|
+
type Props$7 = Except<Styles, 'textWrap'> & {
|
|
1473
|
+
ref?: Ref<DOMElement>;
|
|
1474
|
+
/**
|
|
1475
|
+
* Called when the button is activated via Enter, Space, or click.
|
|
1476
|
+
*/
|
|
1477
|
+
onAction: () => void;
|
|
1478
|
+
/**
|
|
1479
|
+
* Tab order index. Defaults to 0 (in tab order).
|
|
1480
|
+
* Set to -1 for programmatically focusable only.
|
|
1481
|
+
*/
|
|
1482
|
+
tabIndex?: number;
|
|
1483
|
+
/**
|
|
1484
|
+
* Focus this button when it mounts.
|
|
1485
|
+
*/
|
|
1486
|
+
autoFocus?: boolean;
|
|
1487
|
+
/**
|
|
1488
|
+
* Render prop receiving the interactive state. Use this to
|
|
1489
|
+
* style children based on focus/hover/active — Button itself
|
|
1490
|
+
* is intentionally unstyled.
|
|
1491
|
+
*
|
|
1492
|
+
* If not provided, children render as-is (no state-dependent styling).
|
|
1493
|
+
*/
|
|
1494
|
+
children: ((state: ButtonState) => React__default.ReactNode) | React__default.ReactNode;
|
|
1495
|
+
};
|
|
1496
|
+
declare function Button(t0: Props$7): any;
|
|
1497
|
+
|
|
1498
|
+
type ScrollBoxHandle = {
|
|
1499
|
+
scrollTo: (y: number) => void;
|
|
1500
|
+
scrollBy: (dy: number) => void;
|
|
1501
|
+
/**
|
|
1502
|
+
* Scroll so `el`'s top is at the viewport top (plus `offset`). Unlike
|
|
1503
|
+
* scrollTo which bakes a number that's stale by the time the throttled
|
|
1504
|
+
* render fires, this defers the position read to render time —
|
|
1505
|
+
* render-node-to-output reads `el.yogaNode.getComputedTop()` in the
|
|
1506
|
+
* SAME Yoga pass that computes scrollHeight. Deterministic. One-shot.
|
|
1507
|
+
*/
|
|
1508
|
+
scrollToElement: (el: DOMElement, offset?: number) => void;
|
|
1509
|
+
scrollToBottom: () => void;
|
|
1510
|
+
getScrollTop: () => number;
|
|
1511
|
+
getPendingDelta: () => number;
|
|
1512
|
+
getScrollHeight: () => number;
|
|
1513
|
+
/**
|
|
1514
|
+
* Like getScrollHeight, but reads Yoga directly instead of the cached
|
|
1515
|
+
* value written by render-node-to-output (throttled, up to 16ms stale).
|
|
1516
|
+
* Use when you need a fresh value in useLayoutEffect after a React commit
|
|
1517
|
+
* that grew content. Slightly more expensive (native Yoga call).
|
|
1518
|
+
*/
|
|
1519
|
+
getFreshScrollHeight: () => number;
|
|
1520
|
+
getViewportHeight: () => number;
|
|
1521
|
+
/**
|
|
1522
|
+
* Absolute screen-buffer row of the first visible content line (inside
|
|
1523
|
+
* padding). Used for drag-to-scroll edge detection.
|
|
1524
|
+
*/
|
|
1525
|
+
getViewportTop: () => number;
|
|
1526
|
+
/**
|
|
1527
|
+
* True when scroll is pinned to the bottom. Set by scrollToBottom, the
|
|
1528
|
+
* initial stickyScroll attribute, and by the renderer when positional
|
|
1529
|
+
* follow fires (scrollTop at prevMax, content grows). Cleared by
|
|
1530
|
+
* scrollTo/scrollBy. Stable signal for "at bottom" that doesn't depend on
|
|
1531
|
+
* layout values (unlike scrollTop+viewportH >= scrollHeight).
|
|
1532
|
+
*/
|
|
1533
|
+
isSticky: () => boolean;
|
|
1534
|
+
/**
|
|
1535
|
+
* Subscribe to imperative scroll changes (scrollTo/scrollBy/scrollToBottom).
|
|
1536
|
+
* Does NOT fire for stickyScroll updates done by the Ink renderer — those
|
|
1537
|
+
* happen during Ink's render phase after React has committed. Callers that
|
|
1538
|
+
* care about the sticky case should treat "at bottom" as a fallback.
|
|
1539
|
+
*/
|
|
1540
|
+
subscribe: (listener: () => void) => () => void;
|
|
1541
|
+
/**
|
|
1542
|
+
* Set the render-time scrollTop clamp to the currently-mounted children's
|
|
1543
|
+
* coverage span. Called by useVirtualScroll after computing its range;
|
|
1544
|
+
* render-node-to-output clamps scrollTop to [min, max] so burst scrollTo
|
|
1545
|
+
* calls that race past React's async re-render show the edge of mounted
|
|
1546
|
+
* content instead of blank spacer. Pass undefined to disable (sticky,
|
|
1547
|
+
* cold start).
|
|
1548
|
+
*/
|
|
1549
|
+
setClampBounds: (min: number | undefined, max: number | undefined) => void;
|
|
1550
|
+
};
|
|
1551
|
+
type ScrollBoxProps = Except<Styles, 'textWrap' | 'overflow' | 'overflowX' | 'overflowY'> & {
|
|
1552
|
+
ref?: Ref<ScrollBoxHandle>;
|
|
1553
|
+
/**
|
|
1554
|
+
* When true, automatically pins scroll position to the bottom when content
|
|
1555
|
+
* grows. Unset manually via scrollTo/scrollBy to break the stickiness.
|
|
1556
|
+
*/
|
|
1557
|
+
stickyScroll?: boolean;
|
|
1558
|
+
};
|
|
1559
|
+
/**
|
|
1560
|
+
* A Box with `overflow: scroll` and an imperative scroll API.
|
|
1561
|
+
*
|
|
1562
|
+
* Children are laid out at their full Yoga-computed height inside a
|
|
1563
|
+
* constrained container. At render time, only children intersecting the
|
|
1564
|
+
* visible window (scrollTop..scrollTop+height) are rendered (viewport
|
|
1565
|
+
* culling). Content is translated by -scrollTop and clipped to the box bounds.
|
|
1566
|
+
*
|
|
1567
|
+
* Works best inside a fullscreen (constrained-height root) Ink tree.
|
|
1568
|
+
*/
|
|
1569
|
+
declare function ScrollBox({ children, ref, stickyScroll, ...style }: PropsWithChildren<ScrollBoxProps>): React__default.ReactNode;
|
|
1570
|
+
|
|
1571
|
+
type Props$6 = PropsWithChildren<{
|
|
1572
|
+
/** Enable SGR mouse tracking (wheel + click/drag). Default true. */
|
|
1573
|
+
mouseTracking?: boolean;
|
|
1574
|
+
}>;
|
|
1575
|
+
/**
|
|
1576
|
+
* Run children in the terminal's alternate screen buffer, constrained to
|
|
1577
|
+
* the viewport height. While mounted:
|
|
1578
|
+
*
|
|
1579
|
+
* - Enters the alt screen (DEC 1049), clears it, homes the cursor
|
|
1580
|
+
* - Constrains its own height to the terminal row count, so overflow must
|
|
1581
|
+
* be handled via `overflow: scroll` / flexbox (no native scrollback)
|
|
1582
|
+
* - Optionally enables SGR mouse tracking (wheel + click/drag) — events
|
|
1583
|
+
* surface as `ParsedKey` (wheel) and update the Ink instance's
|
|
1584
|
+
* selection state (click/drag)
|
|
1585
|
+
*
|
|
1586
|
+
* On unmount, disables mouse tracking and exits the alt screen, restoring
|
|
1587
|
+
* the main screen's content. Safe for use in ctrl-o transcript overlays
|
|
1588
|
+
* and similar temporary fullscreen views — the main screen is preserved.
|
|
1589
|
+
*
|
|
1590
|
+
* Notifies the Ink instance via `setAltScreenActive()` so the renderer
|
|
1591
|
+
* keeps the cursor inside the viewport (preventing the cursor-restore LF
|
|
1592
|
+
* from scrolling content) and so signal-exit cleanup can exit the alt
|
|
1593
|
+
* screen if the component's own unmount doesn't run.
|
|
1594
|
+
*/
|
|
1595
|
+
declare function AlternateScreen(t0: Props$6): any;
|
|
1596
|
+
|
|
1597
|
+
type Props$5 = {
|
|
1598
|
+
/**
|
|
1599
|
+
* Pre-rendered ANSI lines. Each element must be exactly one terminal row
|
|
1600
|
+
* (already wrapped to `width` by the producer) with ANSI escape codes inline.
|
|
1601
|
+
*/
|
|
1602
|
+
lines: string[];
|
|
1603
|
+
/** Column width the producer wrapped to. Sent to Yoga as the fixed leaf width. */
|
|
1604
|
+
width: number;
|
|
1605
|
+
};
|
|
1606
|
+
/**
|
|
1607
|
+
* Bypass the <Ansi> → React tree → Yoga → squash → re-serialize roundtrip for
|
|
1608
|
+
* content that is already terminal-ready.
|
|
1609
|
+
*
|
|
1610
|
+
* Use this when an external renderer (e.g. the ColorDiff NAPI module) has
|
|
1611
|
+
* already produced ANSI-escaped, width-wrapped output. A normal <Ansi> mount
|
|
1612
|
+
* reparses that output into one React <Text> per style span, lays out each
|
|
1613
|
+
* span as a Yoga flex child, then walks the tree to re-emit the same escape
|
|
1614
|
+
* codes it was given. For a long transcript full of syntax-highlighted diffs
|
|
1615
|
+
* that roundtrip is the dominant cost of the render.
|
|
1616
|
+
*
|
|
1617
|
+
* This component emits a single Yoga leaf with a constant-time measure func
|
|
1618
|
+
* (width × lines.length) and hands the joined string straight to output.write(),
|
|
1619
|
+
* which already splits on '\n' and parses ANSI into the screen buffer.
|
|
1620
|
+
*/
|
|
1621
|
+
declare function RawAnsi(t0: Props$5): any;
|
|
1622
|
+
|
|
1623
|
+
type Props$4 = Omit<Props$b, 'noSelect'> & {
|
|
1624
|
+
/**
|
|
1625
|
+
* Extend the exclusion zone from column 0 to this box's right edge,
|
|
1626
|
+
* for every row this box occupies. Use for gutters rendered inside a
|
|
1627
|
+
* wider indented container (e.g. a diff inside a tool message row):
|
|
1628
|
+
* without this, a multi-row drag picks up the container's leading
|
|
1629
|
+
* indent on rows below the prefix.
|
|
1630
|
+
*
|
|
1631
|
+
* @default false
|
|
1632
|
+
*/
|
|
1633
|
+
fromLeftEdge?: boolean;
|
|
1634
|
+
};
|
|
1635
|
+
/**
|
|
1636
|
+
* Marks its contents as non-selectable in fullscreen text selection.
|
|
1637
|
+
* Cells inside this box are skipped by both the selection highlight and
|
|
1638
|
+
* the copied text — the gutter stays visually unchanged while the user
|
|
1639
|
+
* drags, making it clear what will be copied.
|
|
1640
|
+
*
|
|
1641
|
+
* Use to fence off gutters (line numbers, diff +/- sigils, list bullets)
|
|
1642
|
+
* so click-drag over rendered code yields clean pasteable content:
|
|
1643
|
+
*
|
|
1644
|
+
* <Box flexDirection="row">
|
|
1645
|
+
* <NoSelect fromLeftEdge><Text dimColor> 42 +</Text></NoSelect>
|
|
1646
|
+
* <Text>const x = 1</Text>
|
|
1647
|
+
* </Box>
|
|
1648
|
+
*
|
|
1649
|
+
* Only affects alt-screen text selection (<AlternateScreen> with mouse
|
|
1650
|
+
* tracking). No-op in the main-screen scrollback render where the
|
|
1651
|
+
* terminal's native selection is used instead.
|
|
1652
|
+
*/
|
|
1653
|
+
declare function NoSelect(t0: PropsWithChildren<Props$4>): any;
|
|
1654
|
+
|
|
1655
|
+
type Props$3 = {
|
|
1656
|
+
readonly error: Error;
|
|
1657
|
+
};
|
|
1658
|
+
declare function ErrorOverview({ error }: Props$3): react_jsx_runtime.JSX.Element;
|
|
1659
|
+
|
|
1660
|
+
type Props$2 = {
|
|
1661
|
+
children: string;
|
|
1662
|
+
/** When true, force all text to be rendered with dim styling */
|
|
1663
|
+
dimColor?: boolean;
|
|
1664
|
+
};
|
|
1665
|
+
/**
|
|
1666
|
+
* Component that parses ANSI escape codes and renders them using Text components.
|
|
1667
|
+
*
|
|
1668
|
+
* Use this as an escape hatch when you have pre-formatted ANSI strings from
|
|
1669
|
+
* external tools (like cli-highlight) that need to be rendered in Ink.
|
|
1670
|
+
*
|
|
1671
|
+
* Memoized to prevent re-renders when parent changes but children string is the same.
|
|
1672
|
+
*/
|
|
1673
|
+
declare const Ansi: React__default.NamedExoticComponent<Props$2>;
|
|
1674
|
+
|
|
1675
|
+
type Key = {
|
|
1676
|
+
upArrow: boolean;
|
|
1677
|
+
downArrow: boolean;
|
|
1678
|
+
leftArrow: boolean;
|
|
1679
|
+
rightArrow: boolean;
|
|
1680
|
+
pageDown: boolean;
|
|
1681
|
+
pageUp: boolean;
|
|
1682
|
+
wheelUp: boolean;
|
|
1683
|
+
wheelDown: boolean;
|
|
1684
|
+
home: boolean;
|
|
1685
|
+
end: boolean;
|
|
1686
|
+
return: boolean;
|
|
1687
|
+
escape: boolean;
|
|
1688
|
+
ctrl: boolean;
|
|
1689
|
+
shift: boolean;
|
|
1690
|
+
fn: boolean;
|
|
1691
|
+
tab: boolean;
|
|
1692
|
+
backspace: boolean;
|
|
1693
|
+
delete: boolean;
|
|
1694
|
+
meta: boolean;
|
|
1695
|
+
super: boolean;
|
|
1696
|
+
};
|
|
1697
|
+
declare class InputEvent extends Event {
|
|
1698
|
+
readonly keypress: ParsedKey;
|
|
1699
|
+
readonly key: Key;
|
|
1700
|
+
readonly input: string;
|
|
1701
|
+
constructor(keypress: ParsedKey);
|
|
1702
|
+
}
|
|
1703
|
+
|
|
1704
|
+
type Handler = (input: string, key: Key, event: InputEvent) => void;
|
|
1705
|
+
type Options = {
|
|
1706
|
+
/**
|
|
1707
|
+
* Enable or disable capturing of user input.
|
|
1708
|
+
* Useful when there are multiple useInput hooks used at once to avoid handling the same input several times.
|
|
1709
|
+
*
|
|
1710
|
+
* @default true
|
|
1711
|
+
*/
|
|
1712
|
+
isActive?: boolean;
|
|
1713
|
+
};
|
|
1714
|
+
/**
|
|
1715
|
+
* This hook is used for handling user input.
|
|
1716
|
+
* It's a more convenient alternative to using `StdinContext` and listening to `data` events.
|
|
1717
|
+
* The callback you pass to `useInput` is called for each character when user enters any input.
|
|
1718
|
+
* However, if user pastes text and it's more than one character, the callback will be called only once and the whole string will be passed as `input`.
|
|
1719
|
+
*
|
|
1720
|
+
* ```
|
|
1721
|
+
* import {useInput} from 'ink';
|
|
1722
|
+
*
|
|
1723
|
+
* const UserInput = () => {
|
|
1724
|
+
* useInput((input, key) => {
|
|
1725
|
+
* if (input === 'q') {
|
|
1726
|
+
* // Exit program
|
|
1727
|
+
* }
|
|
1728
|
+
*
|
|
1729
|
+
* if (key.leftArrow) {
|
|
1730
|
+
* // Left arrow key pressed
|
|
1731
|
+
* }
|
|
1732
|
+
* });
|
|
1733
|
+
*
|
|
1734
|
+
* return …
|
|
1735
|
+
* };
|
|
1736
|
+
* ```
|
|
1737
|
+
*/
|
|
1738
|
+
declare const useInput: (inputHandler: Handler, options?: Options) => void;
|
|
1739
|
+
|
|
1740
|
+
type Props$1 = {
|
|
1741
|
+
/**
|
|
1742
|
+
* Exit (unmount) the whole Ink app.
|
|
1743
|
+
*/
|
|
1744
|
+
readonly exit: (error?: Error) => void;
|
|
1745
|
+
};
|
|
1746
|
+
/**
|
|
1747
|
+
* `AppContext` is a React context, which exposes a method to manually exit the app (unmount).
|
|
1748
|
+
*/
|
|
1749
|
+
declare const AppContext: React.Context<Props$1>;
|
|
1750
|
+
|
|
1751
|
+
/**
|
|
1752
|
+
* `useApp` is a React hook, which exposes a method to manually exit the app (unmount).
|
|
1753
|
+
*/
|
|
1754
|
+
declare const useApp: () => Props$1;
|
|
1755
|
+
|
|
1756
|
+
declare class EventEmitter extends EventEmitter$1 {
|
|
1757
|
+
constructor();
|
|
1758
|
+
emit(type: string | symbol, ...args: unknown[]): boolean;
|
|
1759
|
+
}
|
|
1760
|
+
|
|
1761
|
+
/**
|
|
1762
|
+
* Query the terminal and await responses without timeouts.
|
|
1763
|
+
*
|
|
1764
|
+
* Terminal queries (DECRQM, DA1, OSC 11, etc.) share the stdin stream
|
|
1765
|
+
* with keyboard input. Response sequences are syntactically
|
|
1766
|
+
* distinguishable from key events, so the input parser recognizes them
|
|
1767
|
+
* and dispatches them here.
|
|
1768
|
+
*
|
|
1769
|
+
* To avoid timeouts, each query batch is terminated by a DA1 sentinel
|
|
1770
|
+
* (CSI c) — every terminal since VT100 responds to DA1, and terminals
|
|
1771
|
+
* answer queries in order. So: if your query's response arrives before
|
|
1772
|
+
* DA1's, the terminal supports it; if DA1 arrives first, it doesn't.
|
|
1773
|
+
*
|
|
1774
|
+
* Usage:
|
|
1775
|
+
* const [sync, grapheme] = await Promise.all([
|
|
1776
|
+
* querier.send(decrqm(2026)),
|
|
1777
|
+
* querier.send(decrqm(2027)),
|
|
1778
|
+
* querier.flush(),
|
|
1779
|
+
* ])
|
|
1780
|
+
* // sync and grapheme are DECRPM responses or undefined if unsupported
|
|
1781
|
+
*/
|
|
1782
|
+
|
|
1783
|
+
/** A terminal query: an outbound request sequence paired with a matcher
|
|
1784
|
+
* that recognizes the expected inbound response. Built by `decrqm()`,
|
|
1785
|
+
* `oscColor()`, `kittyKeyboard()`, etc. */
|
|
1786
|
+
type TerminalQuery<T extends TerminalResponse = TerminalResponse> = {
|
|
1787
|
+
/** Escape sequence to write to stdout */
|
|
1788
|
+
request: string;
|
|
1789
|
+
/** Recognizes the expected response in the inbound stream */
|
|
1790
|
+
match: (r: TerminalResponse) => r is T;
|
|
1791
|
+
};
|
|
1792
|
+
declare class TerminalQuerier {
|
|
1793
|
+
private stdout;
|
|
1794
|
+
/**
|
|
1795
|
+
* Interleaved queue of queries and sentinels in send order. Terminals
|
|
1796
|
+
* respond in order, so each flush() barrier only drains queries queued
|
|
1797
|
+
* before it — concurrent batches from independent callers stay isolated.
|
|
1798
|
+
*/
|
|
1799
|
+
private queue;
|
|
1800
|
+
constructor(stdout: NodeJS.WriteStream);
|
|
1801
|
+
/**
|
|
1802
|
+
* Send a query and wait for its response.
|
|
1803
|
+
*
|
|
1804
|
+
* Resolves with the response when `query.match` matches an incoming
|
|
1805
|
+
* TerminalResponse, or with `undefined` when a flush() sentinel arrives
|
|
1806
|
+
* before any matching response (meaning the terminal ignored the query).
|
|
1807
|
+
*
|
|
1808
|
+
* Never rejects; never times out on its own. If you never call flush()
|
|
1809
|
+
* and the terminal doesn't respond, the promise remains pending.
|
|
1810
|
+
*/
|
|
1811
|
+
send<T extends TerminalResponse>(query: TerminalQuery<T>): Promise<T | undefined>;
|
|
1812
|
+
/**
|
|
1813
|
+
* Send the DA1 sentinel. Resolves when DA1's response arrives.
|
|
1814
|
+
*
|
|
1815
|
+
* As a side effect, all queries still pending when DA1 arrives are
|
|
1816
|
+
* resolved with `undefined` (terminal didn't respond → doesn't support
|
|
1817
|
+
* the query). This is the barrier that makes send() timeout-free.
|
|
1818
|
+
*
|
|
1819
|
+
* Safe to call with no pending queries — still waits for a round-trip.
|
|
1820
|
+
*/
|
|
1821
|
+
flush(): Promise<void>;
|
|
1822
|
+
/**
|
|
1823
|
+
* Dispatch a response parsed from stdin. Called by App.tsx's
|
|
1824
|
+
* processKeysInBatch for every `kind: 'response'` item.
|
|
1825
|
+
*
|
|
1826
|
+
* Matching strategy:
|
|
1827
|
+
* - First, try to match a pending query (FIFO, first match wins).
|
|
1828
|
+
* This lets callers send(da1()) explicitly if they want the DA1
|
|
1829
|
+
* params — a separate DA1 write means the terminal sends TWO DA1
|
|
1830
|
+
* responses. The first matches the explicit query; the second
|
|
1831
|
+
* (unmatched) fires the sentinel.
|
|
1832
|
+
* - Otherwise, if this is a DA1, fire the FIRST pending sentinel:
|
|
1833
|
+
* resolve any queries queued before that sentinel with undefined
|
|
1834
|
+
* (the terminal answered DA1 without answering them → unsupported)
|
|
1835
|
+
* and signal its flush() completion. Only draining up to the first
|
|
1836
|
+
* sentinel keeps later batches intact when multiple callers have
|
|
1837
|
+
* concurrent queries in flight.
|
|
1838
|
+
* - Unsolicited responses (no match, no sentinel) are silently dropped.
|
|
1839
|
+
*/
|
|
1840
|
+
onResponse(r: TerminalResponse): void;
|
|
1841
|
+
}
|
|
1842
|
+
|
|
1843
|
+
type Props = {
|
|
1844
|
+
/**
|
|
1845
|
+
* Stdin stream passed to `render()` in `options.stdin` or `process.stdin` by default. Useful if your app needs to handle user input.
|
|
1846
|
+
*/
|
|
1847
|
+
readonly stdin: NodeJS.ReadStream;
|
|
1848
|
+
/**
|
|
1849
|
+
* Ink exposes this function via own `<StdinContext>` to be able to handle Ctrl+C, that's why you should use Ink's `setRawMode` instead of `process.stdin.setRawMode`.
|
|
1850
|
+
* If the `stdin` stream passed to Ink does not support setRawMode, this function does nothing.
|
|
1851
|
+
*/
|
|
1852
|
+
readonly setRawMode: (value: boolean) => void;
|
|
1853
|
+
/**
|
|
1854
|
+
* A boolean flag determining if the current `stdin` supports `setRawMode`. A component using `setRawMode` might want to use `isRawModeSupported` to nicely fall back in environments where raw mode is not supported.
|
|
1855
|
+
*/
|
|
1856
|
+
readonly isRawModeSupported: boolean;
|
|
1857
|
+
readonly internal_exitOnCtrlC: boolean;
|
|
1858
|
+
readonly internal_eventEmitter: EventEmitter;
|
|
1859
|
+
/** Query the terminal and await responses (DECRQM, OSC 11, etc.).
|
|
1860
|
+
* Null only in the never-reached default context value. */
|
|
1861
|
+
readonly internal_querier: TerminalQuerier | null;
|
|
1862
|
+
};
|
|
1863
|
+
/**
|
|
1864
|
+
* `StdinContext` is a React context, which exposes input stream.
|
|
1865
|
+
*/
|
|
1866
|
+
declare const StdinContext: React.Context<Props>;
|
|
1867
|
+
|
|
1868
|
+
/**
|
|
1869
|
+
* `useStdin` is a React hook, which exposes stdin stream.
|
|
1870
|
+
*/
|
|
1871
|
+
declare const useStdin: () => Props;
|
|
1872
|
+
|
|
1873
|
+
/**
|
|
1874
|
+
* Hook to check if the terminal has focus.
|
|
1875
|
+
*
|
|
1876
|
+
* Uses DECSET 1004 focus reporting - the terminal sends escape sequences
|
|
1877
|
+
* when it gains or loses focus. These are handled automatically
|
|
1878
|
+
* by Ink and filtered from useInput.
|
|
1879
|
+
*
|
|
1880
|
+
* @returns true if the terminal is focused (or focus state is unknown)
|
|
1881
|
+
*/
|
|
1882
|
+
declare function useTerminalFocus(): boolean;
|
|
1883
|
+
|
|
1884
|
+
type ViewportEntry = {
|
|
1885
|
+
/**
|
|
1886
|
+
* Whether the element is currently within the terminal viewport
|
|
1887
|
+
*/
|
|
1888
|
+
isVisible: boolean;
|
|
1889
|
+
};
|
|
1890
|
+
/**
|
|
1891
|
+
* Hook to detect if a component is within the terminal viewport.
|
|
1892
|
+
*
|
|
1893
|
+
* Returns a callback ref and a viewport entry object.
|
|
1894
|
+
* Attach the ref to the component you want to track.
|
|
1895
|
+
*
|
|
1896
|
+
* The entry is updated during the layout phase (useLayoutEffect) so callers
|
|
1897
|
+
* always read fresh values during render. Visibility changes do NOT trigger
|
|
1898
|
+
* re-renders on their own — callers that re-render for other reasons (e.g.
|
|
1899
|
+
* animation ticks, state changes) will pick up the latest value naturally.
|
|
1900
|
+
* This avoids infinite update loops when combined with other layout effects
|
|
1901
|
+
* that also call setState.
|
|
1902
|
+
*
|
|
1903
|
+
* @example
|
|
1904
|
+
* const [ref, entry] = useTerminalViewport()
|
|
1905
|
+
* return <Box ref={ref}><Animation enabled={entry.isVisible}>...</Animation></Box>
|
|
1906
|
+
*/
|
|
1907
|
+
declare function useTerminalViewport(): [
|
|
1908
|
+
ref: (element: DOMElement | null) => void,
|
|
1909
|
+
entry: ViewportEntry
|
|
1910
|
+
];
|
|
1911
|
+
|
|
1912
|
+
/**
|
|
1913
|
+
* Returns the clock time, updating at the given interval.
|
|
1914
|
+
* Subscribes as non-keepAlive — won't keep the clock alive on its own,
|
|
1915
|
+
* but updates whenever a keepAlive subscriber (e.g. the spinner)
|
|
1916
|
+
* is driving the clock.
|
|
1917
|
+
*
|
|
1918
|
+
* Use this to drive pure time-based computations (shimmer position,
|
|
1919
|
+
* frame index) from the shared clock.
|
|
1920
|
+
*/
|
|
1921
|
+
declare function useAnimationTimer(intervalMs: number): number;
|
|
1922
|
+
/**
|
|
1923
|
+
* Interval hook backed by the shared Clock.
|
|
1924
|
+
*
|
|
1925
|
+
* Unlike `useInterval` from `usehooks-ts` (which creates its own setInterval),
|
|
1926
|
+
* this piggybacks on the single shared clock so all timers consolidate into
|
|
1927
|
+
* one wake-up. Pass `null` for intervalMs to pause.
|
|
1928
|
+
*/
|
|
1929
|
+
declare function useInterval(callback: () => void, intervalMs: number | null): void;
|
|
1930
|
+
|
|
1931
|
+
/**
|
|
1932
|
+
* Hook for synchronized animations that pause when offscreen.
|
|
1933
|
+
*
|
|
1934
|
+
* Returns a ref to attach to the animated element and the current animation time.
|
|
1935
|
+
* All instances share the same clock, so animations stay in sync.
|
|
1936
|
+
* The clock only runs when at least one keepAlive subscriber exists.
|
|
1937
|
+
*
|
|
1938
|
+
* Pass `null` to pause — unsubscribes from the clock so no ticks fire.
|
|
1939
|
+
* Time freezes at the last value and resumes from the current clock time
|
|
1940
|
+
* when a number is passed again.
|
|
1941
|
+
*
|
|
1942
|
+
* @param intervalMs - How often to update, or null to pause
|
|
1943
|
+
* @returns [ref, time] - Ref to attach to element, elapsed time in ms
|
|
1944
|
+
*
|
|
1945
|
+
* @example
|
|
1946
|
+
* function Spinner() {
|
|
1947
|
+
* const [ref, time] = useAnimationFrame(120)
|
|
1948
|
+
* const frame = Math.floor(time / 120) % FRAMES.length
|
|
1949
|
+
* return <Box ref={ref}>{FRAMES[frame]}</Box>
|
|
1950
|
+
* }
|
|
1951
|
+
*
|
|
1952
|
+
* The clock automatically slows when the terminal is blurred,
|
|
1953
|
+
* so consumers don't need to handle focus state.
|
|
1954
|
+
*/
|
|
1955
|
+
declare function useAnimationFrame(intervalMs?: number | null): [ref: (element: DOMElement | null) => void, time: number];
|
|
1956
|
+
|
|
1957
|
+
/**
|
|
1958
|
+
* Declaratively set the terminal tab/window title.
|
|
1959
|
+
*
|
|
1960
|
+
* Pass a string to set the title. ANSI escape sequences are stripped
|
|
1961
|
+
* automatically so callers don't need to know about terminal encoding.
|
|
1962
|
+
* Pass `null` to opt out — the hook becomes a no-op and leaves the
|
|
1963
|
+
* terminal title untouched.
|
|
1964
|
+
*
|
|
1965
|
+
* On Windows, uses `process.title` (classic conhost doesn't support OSC).
|
|
1966
|
+
* Elsewhere, writes OSC 0 (set title+icon) via Ink's stdout.
|
|
1967
|
+
*/
|
|
1968
|
+
declare function useTerminalTitle(title: string | null): void;
|
|
1969
|
+
|
|
1970
|
+
type TabStatusKind = 'idle' | 'busy' | 'waiting';
|
|
1971
|
+
/**
|
|
1972
|
+
* Declaratively set the tab-status indicator (OSC 21337).
|
|
1973
|
+
*
|
|
1974
|
+
* Emits a colored dot + short status text to the tab sidebar. Terminals
|
|
1975
|
+
* that don't support OSC 21337 discard the sequence silently, so this is
|
|
1976
|
+
* safe to call unconditionally. Wrapped for tmux/screen passthrough.
|
|
1977
|
+
*
|
|
1978
|
+
* Pass `null` to opt out. If a status was previously set, transitioning to
|
|
1979
|
+
* `null` emits CLEAR_TAB_STATUS so toggling off mid-session doesn't leave
|
|
1980
|
+
* a stale dot. Process-exit cleanup is handled by ink.tsx's unmount path.
|
|
1981
|
+
*/
|
|
1982
|
+
declare function useTabStatus(kind: TabStatusKind | null): void;
|
|
1983
|
+
|
|
1984
|
+
/**
|
|
1985
|
+
* Declares where the terminal cursor should be parked after each frame.
|
|
1986
|
+
*
|
|
1987
|
+
* Terminal emulators render IME preedit text at the physical cursor
|
|
1988
|
+
* position, and screen readers / screen magnifiers track the native
|
|
1989
|
+
* cursor — so parking it at the text input's caret makes CJK input
|
|
1990
|
+
* appear inline and lets accessibility tools follow the input.
|
|
1991
|
+
*
|
|
1992
|
+
* Returns a ref callback to attach to the Box that contains the input.
|
|
1993
|
+
* The declared (line, column) is interpreted relative to that Box's
|
|
1994
|
+
* nodeCache rect (populated by renderNodeToOutput).
|
|
1995
|
+
*
|
|
1996
|
+
* Timing: Both ref attach and useLayoutEffect fire in React's layout
|
|
1997
|
+
* phase — after resetAfterCommit calls scheduleRender. scheduleRender
|
|
1998
|
+
* defers onRender via queueMicrotask, so onRender runs AFTER layout
|
|
1999
|
+
* effects commit and reads the fresh declaration on the first frame
|
|
2000
|
+
* (no one-keystroke lag). Test env uses onImmediateRender (synchronous,
|
|
2001
|
+
* no microtask), so tests compensate by calling ink.onRender()
|
|
2002
|
+
* explicitly after render.
|
|
2003
|
+
*/
|
|
2004
|
+
declare function useDeclaredCursor({ line, column, active, }: {
|
|
2005
|
+
line: number;
|
|
2006
|
+
column: number;
|
|
2007
|
+
active: boolean;
|
|
2008
|
+
}): (element: DOMElement | null) => void;
|
|
2009
|
+
|
|
2010
|
+
/**
|
|
2011
|
+
* Set the search highlight query on the Ink instance. Non-empty → all
|
|
2012
|
+
* visible occurrences are inverted on the next frame (SGR 7, screen-buffer
|
|
2013
|
+
* overlay, same damage machinery as selection). Empty → clears.
|
|
2014
|
+
*
|
|
2015
|
+
* This is a screen-space highlight — it matches the RENDERED text, not the
|
|
2016
|
+
* source message text. Works for anything visible (bash output, file paths,
|
|
2017
|
+
* error messages) regardless of where it came from in the message tree. A
|
|
2018
|
+
* query that matched in source but got truncated/ellipsized in rendering
|
|
2019
|
+
* won't highlight; that's acceptable — we highlight what you see.
|
|
2020
|
+
*/
|
|
2021
|
+
declare function useSearchHighlight(): {
|
|
2022
|
+
setQuery: (query: string) => void;
|
|
2023
|
+
/** Paint an existing DOM subtree (from the MAIN tree) to a fresh
|
|
2024
|
+
* Screen at its natural height, scan. Element-relative positions
|
|
2025
|
+
* (row 0 = element top). Zero context duplication — the element
|
|
2026
|
+
* IS the one built with all real providers. */
|
|
2027
|
+
scanElement: (el: DOMElement) => MatchPosition[];
|
|
2028
|
+
/** Position-based CURRENT highlight. Every frame writes yellow at
|
|
2029
|
+
* positions[currentIdx] + rowOffset. The scan-highlight (inverse on
|
|
2030
|
+
* all matches) still runs — this overlays on top. rowOffset tracks
|
|
2031
|
+
* scroll; positions stay stable (message-relative). null clears. */
|
|
2032
|
+
setPositions: (state: {
|
|
2033
|
+
positions: MatchPosition[];
|
|
2034
|
+
rowOffset: number;
|
|
2035
|
+
currentIdx: number;
|
|
2036
|
+
} | null) => void;
|
|
2037
|
+
};
|
|
2038
|
+
|
|
2039
|
+
/**
|
|
2040
|
+
* Access to text selection operations on the Ink instance (fullscreen only).
|
|
2041
|
+
* Returns no-op functions when fullscreen mode is disabled.
|
|
2042
|
+
*/
|
|
2043
|
+
declare function useSelection(): {
|
|
2044
|
+
copySelection: () => string;
|
|
2045
|
+
/** Copy without clearing the highlight (for copy-on-select). */
|
|
2046
|
+
copySelectionNoClear: () => string;
|
|
2047
|
+
clearSelection: () => void;
|
|
2048
|
+
hasSelection: () => boolean;
|
|
2049
|
+
/** Read the raw mutable selection state (for drag-to-scroll). */
|
|
2050
|
+
getState: () => SelectionState | null;
|
|
2051
|
+
/** Subscribe to selection mutations (start/update/finish/clear). */
|
|
2052
|
+
subscribe: (cb: () => void) => () => void;
|
|
2053
|
+
/** Shift the anchor row by dRow, clamped to [minRow, maxRow]. */
|
|
2054
|
+
shiftAnchor: (dRow: number, minRow: number, maxRow: number) => void;
|
|
2055
|
+
/** Shift anchor AND focus by dRow (keyboard scroll: whole selection
|
|
2056
|
+
* tracks content). Clamped points get col reset to the full-width edge
|
|
2057
|
+
* since their content was captured by captureScrolledRows. Reads
|
|
2058
|
+
* screen.width from the ink instance for the col-reset boundary. */
|
|
2059
|
+
shiftSelection: (dRow: number, minRow: number, maxRow: number) => void;
|
|
2060
|
+
/** Keyboard selection extension (shift+arrow): move focus, anchor fixed.
|
|
2061
|
+
* Left/right wrap across rows; up/down clamp at viewport edges. */
|
|
2062
|
+
moveFocus: (move: FocusMove) => void;
|
|
2063
|
+
/** Capture text from rows about to scroll out of the viewport (call
|
|
2064
|
+
* BEFORE scrollBy so the screen buffer still has the outgoing rows). */
|
|
2065
|
+
captureScrolledRows: (firstRow: number, lastRow: number, side: 'above' | 'below') => void;
|
|
2066
|
+
/** Set the selection highlight bg color (theme-piping; solid bg
|
|
2067
|
+
* replaces the old SGR-7 inverse so syntax highlighting stays readable
|
|
2068
|
+
* under selection). Call once on mount + whenever theme changes. */
|
|
2069
|
+
setSelectionBgColor: (color: string) => void;
|
|
2070
|
+
};
|
|
2071
|
+
/**
|
|
2072
|
+
* Reactive selection-exists state. Re-renders the caller when a text
|
|
2073
|
+
* selection is created or cleared. Always returns false outside
|
|
2074
|
+
* fullscreen mode (selection is only available in alt-screen).
|
|
2075
|
+
*/
|
|
2076
|
+
declare function useHasSelection(): boolean;
|
|
2077
|
+
|
|
2078
|
+
type TerminalSize = {
|
|
2079
|
+
columns: number;
|
|
2080
|
+
rows: number;
|
|
2081
|
+
};
|
|
2082
|
+
declare const TerminalSizeContext: React.Context<TerminalSize | null>;
|
|
2083
|
+
|
|
2084
|
+
type Output = {
|
|
2085
|
+
/**
|
|
2086
|
+
* Element width.
|
|
2087
|
+
*/
|
|
2088
|
+
width: number;
|
|
2089
|
+
/**
|
|
2090
|
+
* Element height.
|
|
2091
|
+
*/
|
|
2092
|
+
height: number;
|
|
2093
|
+
};
|
|
2094
|
+
/**
|
|
2095
|
+
* Measure the dimensions of a particular `<Box>` element.
|
|
2096
|
+
*/
|
|
2097
|
+
declare const measureElement: (node: DOMElement) => Output;
|
|
2098
|
+
|
|
2099
|
+
declare const stringWidth: (str: string) => number;
|
|
2100
|
+
|
|
2101
|
+
type WrapAnsiOptions = {
|
|
2102
|
+
hard?: boolean;
|
|
2103
|
+
wordWrap?: boolean;
|
|
2104
|
+
trim?: boolean;
|
|
2105
|
+
};
|
|
2106
|
+
declare const wrapAnsi: (input: string, columns: number, options?: WrapAnsiOptions) => string;
|
|
2107
|
+
|
|
2108
|
+
type ColorType = 'foreground' | 'background';
|
|
2109
|
+
declare const colorize: (str: string, color: string | undefined, type: ColorType) => string;
|
|
2110
|
+
/**
|
|
2111
|
+
* Apply TextStyles to a string using chalk.
|
|
2112
|
+
* This is the inverse of parsing ANSI codes - we generate them from structured styles.
|
|
2113
|
+
* Theme resolution happens at component layer, not here.
|
|
2114
|
+
*/
|
|
2115
|
+
declare function applyTextStyles(text: string, styles: TextStyles): string;
|
|
2116
|
+
/**
|
|
2117
|
+
* Apply a raw color value to text.
|
|
2118
|
+
* Theme resolution should happen at component layer, not here.
|
|
2119
|
+
*/
|
|
2120
|
+
declare function applyColor(text: string, color: Color | undefined): string;
|
|
2121
|
+
|
|
2122
|
+
declare function useTheme(): [string, (name: string) => void];
|
|
2123
|
+
|
|
2124
|
+
export { AlternateScreen, Ansi, AppContext, type BorderTextOptions, Box, Button, ClickEvent, type Color, type ColorType, type DOMElement, type DOMNode, ErrorOverview, FocusEvent, type Frame, type FrameEvent, InputEvent, type Instance, type Key, KeyboardEvent, Link, type MatchPosition, Newline, NoSelect, type ParsedKey, RawAnsi, type RenderOptions, type Root, ScrollBox, type ScrollBoxHandle, Spacer, StdinContext, type Styles, type TabStatusKind, TerminalSizeContext, Text, type Props$a as TextProps, type TextStyles, applyColor, applyTextStyles, clamp, colorize, createRoot, measureElement, wrappedRender as render, renderSync, stringWidth, useAnimationFrame, useAnimationTimer, useApp, useDeclaredCursor, useHasSelection, useInput, useInterval, useSearchHighlight, useSelection, useStdin, useTabStatus, useTerminalFocus, useTerminalTitle, useTerminalViewport, useTheme, wrapAnsi };
|