@sayknow-cli/tui 0.3.12 → 0.3.13
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/package.json +8 -9
- package/src/components/image.ts +23 -4
- package/src/components/markdown.ts +216 -29
- package/src/components/sayknow-pet.ts +435 -0
- package/src/components/text.ts +60 -36
- package/src/index.ts +1 -0
- package/src/terminal-capabilities.ts +42 -0
- package/src/tui.ts +453 -46
- package/src/utils.ts +144 -8
- package/dist/types/animation-scheduler.d.ts +0 -13
- package/dist/types/autocomplete.d.ts +0 -83
- package/dist/types/bracketed-paste.d.ts +0 -26
- package/dist/types/components/box.d.ts +0 -20
- package/dist/types/components/cancellable-loader.d.ts +0 -21
- package/dist/types/components/editor.d.ts +0 -126
- package/dist/types/components/image.d.ts +0 -18
- package/dist/types/components/input.d.ts +0 -16
- package/dist/types/components/loader.d.ts +0 -23
- package/dist/types/components/markdown.d.ts +0 -78
- package/dist/types/components/select-list.d.ts +0 -46
- package/dist/types/components/settings-list.d.ts +0 -39
- package/dist/types/components/spacer.d.ts +0 -11
- package/dist/types/components/tab-bar.d.ts +0 -56
- package/dist/types/components/text.d.ts +0 -13
- package/dist/types/components/truncated-text.d.ts +0 -10
- package/dist/types/editor-component.d.ts +0 -36
- package/dist/types/fuzzy.d.ts +0 -15
- package/dist/types/index.d.ts +0 -27
- package/dist/types/keybindings.d.ts +0 -201
- package/dist/types/keys.d.ts +0 -208
- package/dist/types/kill-ring.d.ts +0 -27
- package/dist/types/metrics.d.ts +0 -85
- package/dist/types/stdin-buffer.d.ts +0 -50
- package/dist/types/symbols.d.ts +0 -23
- package/dist/types/terminal-capabilities.d.ts +0 -143
- package/dist/types/terminal.d.ts +0 -90
- package/dist/types/ttyid.d.ts +0 -9
- package/dist/types/tui.d.ts +0 -215
- package/dist/types/utils.d.ts +0 -87
package/src/utils.ts
CHANGED
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
sliceWithWidth as nativeSliceWithWidth,
|
|
6
6
|
truncateLinesToWidth as nativeTruncateLinesToWidth,
|
|
7
7
|
truncateToWidth as nativeTruncateToWidth,
|
|
8
|
+
visibleWidth as nativeVisibleWidth,
|
|
8
9
|
visibleWidths as nativeVisibleWidths,
|
|
9
10
|
wrapTextWithAnsi as nativeWrapTextWithAnsi,
|
|
10
11
|
type SliceResult,
|
|
@@ -163,10 +164,142 @@ const segmenter = new Intl.Segmenter(undefined, { granularity: "grapheme" });
|
|
|
163
164
|
export function getSegmenter(): Intl.Segmenter {
|
|
164
165
|
return segmenter;
|
|
165
166
|
}
|
|
167
|
+
|
|
168
|
+
export interface ViewportAnchorSpan {
|
|
169
|
+
graphemeStart: number;
|
|
170
|
+
graphemeEnd: number;
|
|
171
|
+
cellStart: number;
|
|
172
|
+
cellEnd: number;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export interface ViewportAnchorAnnotation {
|
|
176
|
+
text: string;
|
|
177
|
+
nextGrapheme: number;
|
|
178
|
+
nextCell: number;
|
|
179
|
+
token: string;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// APC marker for viewport anchors. Must NOT start with "\x1b_G": that is the
|
|
183
|
+
// Kitty graphics prefix and TERMINAL.isImageLine() would misclassify every
|
|
184
|
+
// annotated line as an image line, which skips wrapping (issue: assistant
|
|
185
|
+
// prose overflowing the terminal on Kitty-protocol terminals).
|
|
186
|
+
export const VIEWPORT_ANCHOR_PREFIX = "\x1b_ASKC_ANCHOR:";
|
|
187
|
+
const VIEWPORT_ANCHOR_SUFFIX = "\x1b\\";
|
|
188
|
+
|
|
189
|
+
function ansiSequenceEnd(text: string, start: number): number {
|
|
190
|
+
if (text[start] !== "\x1b" || start + 1 >= text.length) return start;
|
|
191
|
+
const kind = text[start + 1];
|
|
192
|
+
if (kind === "[") {
|
|
193
|
+
let index = start + 2;
|
|
194
|
+
while (index < text.length) {
|
|
195
|
+
const code = text.charCodeAt(index++);
|
|
196
|
+
if (code >= 0x40 && code <= 0x7e) return index;
|
|
197
|
+
}
|
|
198
|
+
return text.length;
|
|
199
|
+
}
|
|
200
|
+
if (kind === "]" || kind === "_" || kind === "P" || kind === "^" || kind === "X") {
|
|
201
|
+
const bel = text.indexOf("\x07", start + 2);
|
|
202
|
+
const st = text.indexOf("\x1b\\", start + 2);
|
|
203
|
+
if (bel < 0) return st < 0 ? text.length : st + 2;
|
|
204
|
+
if (st < 0) return bel + 1;
|
|
205
|
+
return Math.min(bel + 1, st + 2);
|
|
206
|
+
}
|
|
207
|
+
return Math.min(text.length, start + 2);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Tag every visible grapheme with an APC marker that survives ANSI-aware
|
|
212
|
+
* wrapping. The marker contains source grapheme and monotonic cell offsets.
|
|
213
|
+
*/
|
|
214
|
+
export function annotateViewportAnchorGraphemes(
|
|
215
|
+
text: string,
|
|
216
|
+
startGrapheme = 0,
|
|
217
|
+
startCell = 0,
|
|
218
|
+
token = crypto.randomUUID(),
|
|
219
|
+
): ViewportAnchorAnnotation {
|
|
220
|
+
let result = "";
|
|
221
|
+
let grapheme = startGrapheme;
|
|
222
|
+
let cell = startCell;
|
|
223
|
+
let textStart = 0;
|
|
224
|
+
const appendVisible = (visible: string): void => {
|
|
225
|
+
for (const part of segmenter.segment(visible)) {
|
|
226
|
+
if (part.segment === "\n" || part.segment === "\r") {
|
|
227
|
+
result += part.segment;
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
const cellEnd = cell + Math.max(1, visibleWidth(part.segment));
|
|
231
|
+
result += `${part.segment}${VIEWPORT_ANCHOR_PREFIX}${token}:${grapheme}:${grapheme + 1}:${cell}:${cellEnd}${VIEWPORT_ANCHOR_SUFFIX}`;
|
|
232
|
+
grapheme += 1;
|
|
233
|
+
cell = cellEnd;
|
|
234
|
+
}
|
|
235
|
+
};
|
|
236
|
+
for (let index = 0; index < text.length; ) {
|
|
237
|
+
if (text[index] !== "\x1b") {
|
|
238
|
+
index += 1;
|
|
239
|
+
continue;
|
|
240
|
+
}
|
|
241
|
+
appendVisible(text.slice(textStart, index));
|
|
242
|
+
const end = ansiSequenceEnd(text, index);
|
|
243
|
+
result += text.slice(index, end);
|
|
244
|
+
index = end;
|
|
245
|
+
textStart = end;
|
|
246
|
+
}
|
|
247
|
+
appendVisible(text.slice(textStart));
|
|
248
|
+
return { text: result, nextGrapheme: grapheme, nextCell: cell, token };
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/** Remove viewport anchor markers and return the exact marked span for each row. */
|
|
252
|
+
export function extractViewportAnchorRows(
|
|
253
|
+
lines: readonly string[],
|
|
254
|
+
token: string,
|
|
255
|
+
): { lines: string[]; spans: Array<ViewportAnchorSpan | null> } {
|
|
256
|
+
const markerRegex = new RegExp(`\\x1b_ASKC_ANCHOR:${token}:(\\d+):(\\d+):(\\d+):(\\d+)\\x1b\\\\`, "g");
|
|
257
|
+
const cleanLines: string[] = [];
|
|
258
|
+
const spans: Array<ViewportAnchorSpan | null> = [];
|
|
259
|
+
for (const line of lines) {
|
|
260
|
+
let span: ViewportAnchorSpan | null = null;
|
|
261
|
+
const clean = line.replace(markerRegex, (_marker, start, end, cellStart, cellEnd) => {
|
|
262
|
+
const candidate = {
|
|
263
|
+
graphemeStart: Number(start),
|
|
264
|
+
graphemeEnd: Number(end),
|
|
265
|
+
cellStart: Number(cellStart),
|
|
266
|
+
cellEnd: Number(cellEnd),
|
|
267
|
+
};
|
|
268
|
+
if (!span) {
|
|
269
|
+
span = candidate;
|
|
270
|
+
} else {
|
|
271
|
+
span.graphemeStart = Math.min(span.graphemeStart, candidate.graphemeStart);
|
|
272
|
+
span.graphemeEnd = Math.max(span.graphemeEnd, candidate.graphemeEnd);
|
|
273
|
+
span.cellStart = Math.min(span.cellStart, candidate.cellStart);
|
|
274
|
+
span.cellEnd = Math.max(span.cellEnd, candidate.cellEnd);
|
|
275
|
+
}
|
|
276
|
+
return "";
|
|
277
|
+
});
|
|
278
|
+
cleanLines.push(clean);
|
|
279
|
+
spans.push(span);
|
|
280
|
+
}
|
|
281
|
+
return { lines: cleanLines, spans };
|
|
282
|
+
}
|
|
166
283
|
function normalizeForWidth(str: string): string {
|
|
167
284
|
const normalized = str.normalize("NFC");
|
|
168
285
|
return normalized === str ? str : normalized;
|
|
169
286
|
}
|
|
287
|
+
|
|
288
|
+
function hasUnpairedSurrogate(str: string): boolean {
|
|
289
|
+
for (let index = 0; index < str.length; index++) {
|
|
290
|
+
const code = str.charCodeAt(index);
|
|
291
|
+
if (code >= 0xd800 && code <= 0xdbff) {
|
|
292
|
+
const next = str.charCodeAt(index + 1);
|
|
293
|
+
if (next >= 0xdc00 && next <= 0xdfff) {
|
|
294
|
+
index += 1;
|
|
295
|
+
continue;
|
|
296
|
+
}
|
|
297
|
+
return true;
|
|
298
|
+
}
|
|
299
|
+
if (code >= 0xdc00 && code <= 0xdfff) return true;
|
|
300
|
+
}
|
|
301
|
+
return false;
|
|
302
|
+
}
|
|
170
303
|
export function visibleWidthRaw(str: string): number {
|
|
171
304
|
if (!str) {
|
|
172
305
|
return 0;
|
|
@@ -191,8 +324,8 @@ export function visibleWidthRaw(str: string): number {
|
|
|
191
324
|
return str.length + tabCount * (getCachedTabWidth() - 1);
|
|
192
325
|
}
|
|
193
326
|
const normalized = normalizeForWidth(str);
|
|
194
|
-
|
|
195
|
-
return
|
|
327
|
+
const text = tabCount === 0 ? normalized : normalized.replaceAll("\t", " ".repeat(getCachedTabWidth()));
|
|
328
|
+
return nativeVisibleWidth(text, getCachedTabWidth());
|
|
196
329
|
}
|
|
197
330
|
|
|
198
331
|
/**
|
|
@@ -205,15 +338,18 @@ export function visibleWidth(str: string): number {
|
|
|
205
338
|
|
|
206
339
|
export function visibleWidthsNative(lines: readonly string[]): number[] {
|
|
207
340
|
__textHelperPerfCounters.visibleWidthsCalls += 1;
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
341
|
+
const safeLines = lines.map(line => (typeof line === "string" ? line : String(line ?? "")));
|
|
342
|
+
const widths = nativeVisibleWidths(safeLines, getCachedTabWidth());
|
|
343
|
+
for (let index = 0; index < safeLines.length; index++) {
|
|
344
|
+
const line = safeLines[index]!;
|
|
345
|
+
if (hasUnpairedSurrogate(line)) widths[index] = visibleWidthRaw(line);
|
|
346
|
+
}
|
|
347
|
+
return widths;
|
|
212
348
|
}
|
|
213
349
|
|
|
214
350
|
export function visibleWidths(lines: readonly string[]): number[] {
|
|
215
|
-
|
|
216
|
-
return
|
|
351
|
+
if (!renderMetrics.enabled) return visibleWidthsNative(lines);
|
|
352
|
+
return recordTextHelper("text.visibleWidths", () => visibleWidthsNative(lines));
|
|
217
353
|
}
|
|
218
354
|
|
|
219
355
|
const THAI_LAO_AM_REGEX = /[\u0e33\u0eb3]/;
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
export type AnimationCadence = 16 | 80;
|
|
2
|
-
type AnimationCallback = (now: number) => void;
|
|
3
|
-
export interface AnimationRegistration {
|
|
4
|
-
unregister(): void;
|
|
5
|
-
}
|
|
6
|
-
export declare function registerAnimationCallback(callback: AnimationCallback, cadence?: AnimationCadence): AnimationRegistration;
|
|
7
|
-
export declare const __animationSchedulerTestHooks: {
|
|
8
|
-
getActiveTimerCount(cadence?: AnimationCadence): number;
|
|
9
|
-
getRegistrantCount(cadence?: AnimationCadence): number;
|
|
10
|
-
getStartedTimerCount(cadence?: AnimationCadence): number;
|
|
11
|
-
reset(): void;
|
|
12
|
-
};
|
|
13
|
-
export {};
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
export declare function getSlashCommandMatchRank(query: string, commandName: string): number;
|
|
2
|
-
export declare function extractSlashCommandTokenPrefix(text: string): string | null;
|
|
3
|
-
export interface AutocompleteItem {
|
|
4
|
-
value: string;
|
|
5
|
-
label: string;
|
|
6
|
-
description?: string;
|
|
7
|
-
/** Dim hint text shown inline after cursor when this item is selected */
|
|
8
|
-
hint?: string;
|
|
9
|
-
}
|
|
10
|
-
type Awaitable<T> = T | Promise<T>;
|
|
11
|
-
export interface SlashCommand {
|
|
12
|
-
name: string;
|
|
13
|
-
description?: string;
|
|
14
|
-
argumentHint?: string;
|
|
15
|
-
/**
|
|
16
|
-
* Higher values surface first in autocomplete, ahead of fuzzy-score ordering.
|
|
17
|
-
* Use this to pin first-class commands (e.g. bundled SKC skills) to the top.
|
|
18
|
-
*/
|
|
19
|
-
priority?: number;
|
|
20
|
-
getArgumentCompletions?(argumentPrefix: string): Awaitable<AutocompleteItem[] | null>;
|
|
21
|
-
/** Return inline hint text for the current argument state (shown as dim ghost text after cursor) */
|
|
22
|
-
getInlineHint?(argumentText: string): string | null;
|
|
23
|
-
}
|
|
24
|
-
export interface AutocompleteProvider {
|
|
25
|
-
/** Get autocomplete suggestions for current text/cursor position */
|
|
26
|
-
getSuggestions(lines: string[], cursorLine: number, cursorCol: number): Promise<{
|
|
27
|
-
items: AutocompleteItem[];
|
|
28
|
-
prefix: string;
|
|
29
|
-
} | null>;
|
|
30
|
-
/** Apply the selected item and return new text + cursor position */
|
|
31
|
-
applyCompletion(lines: string[], cursorLine: number, cursorCol: number, item: AutocompleteItem, prefix: string): {
|
|
32
|
-
lines: string[];
|
|
33
|
-
cursorLine: number;
|
|
34
|
-
cursorCol: number;
|
|
35
|
-
onApplied?: () => void;
|
|
36
|
-
};
|
|
37
|
-
/** Get inline hint text to show as dim ghost text after the cursor */
|
|
38
|
-
getInlineHint?(lines: string[], cursorLine: number, cursorCol: number): string | null;
|
|
39
|
-
/** Synchronously try to complete a slash command at the start of a line (no async I/O). */
|
|
40
|
-
/** Returns matched items and the full prefix, or null if not applicable. */
|
|
41
|
-
trySyncSlashCompletion?(textBeforeCursor: string): {
|
|
42
|
-
items: AutocompleteItem[];
|
|
43
|
-
prefix: string;
|
|
44
|
-
} | null;
|
|
45
|
-
/**
|
|
46
|
-
* Synchronously try to expand text immediately before the cursor (no async I/O).
|
|
47
|
-
* Called after every single-character insert. Implementations MUST cheaply
|
|
48
|
-
* early-return when the trailing context cannot trigger them.
|
|
49
|
-
* Returns the number of characters to delete immediately before the cursor
|
|
50
|
-
* and the literal string to insert in their place, or null to leave the
|
|
51
|
-
* buffer untouched.
|
|
52
|
-
*/
|
|
53
|
-
trySyncInlineReplace?(textBeforeCursor: string): {
|
|
54
|
-
replaceLen: number;
|
|
55
|
-
insert: string;
|
|
56
|
-
} | null;
|
|
57
|
-
}
|
|
58
|
-
export declare class CombinedAutocompleteProvider implements AutocompleteProvider {
|
|
59
|
-
#private;
|
|
60
|
-
constructor(commands?: (SlashCommand | AutocompleteItem)[], basePath?: string);
|
|
61
|
-
getSuggestions(lines: string[], cursorLine: number, cursorCol: number): Promise<{
|
|
62
|
-
items: AutocompleteItem[];
|
|
63
|
-
prefix: string;
|
|
64
|
-
} | null>;
|
|
65
|
-
applyCompletion(lines: string[], cursorLine: number, cursorCol: number, item: AutocompleteItem, prefix: string): {
|
|
66
|
-
lines: string[];
|
|
67
|
-
cursorLine: number;
|
|
68
|
-
cursorCol: number;
|
|
69
|
-
};
|
|
70
|
-
invalidateDirCache(dir?: string): void;
|
|
71
|
-
getForceFileSuggestions(lines: string[], cursorLine: number, cursorCol: number): Promise<{
|
|
72
|
-
items: AutocompleteItem[];
|
|
73
|
-
prefix: string;
|
|
74
|
-
} | null>;
|
|
75
|
-
shouldTriggerFileCompletion(lines: string[], cursorLine: number, cursorCol: number): boolean;
|
|
76
|
-
/** Get inline hint text for slash commands with subcommand hints */
|
|
77
|
-
getInlineHint(lines: string[], cursorLine: number, cursorCol: number): string | null;
|
|
78
|
-
trySyncSlashCompletion(textBeforeCursor: string): {
|
|
79
|
-
items: AutocompleteItem[];
|
|
80
|
-
prefix: string;
|
|
81
|
-
} | null;
|
|
82
|
-
}
|
|
83
|
-
export {};
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
export type PasteResult = {
|
|
2
|
-
handled: false;
|
|
3
|
-
} | {
|
|
4
|
-
handled: true;
|
|
5
|
-
pasteContent?: string;
|
|
6
|
-
remaining: string;
|
|
7
|
-
};
|
|
8
|
-
/**
|
|
9
|
-
* Handles bracketed paste mode buffering for terminal input components.
|
|
10
|
-
*
|
|
11
|
-
* Bracketed paste mode wraps pasted content between start (\x1b[200~) and
|
|
12
|
-
* end (\x1b[201~) markers, which may arrive split across multiple chunks.
|
|
13
|
-
* This class buffers incoming data and assembles complete paste payloads.
|
|
14
|
-
*/
|
|
15
|
-
export declare class BracketedPasteHandler {
|
|
16
|
-
#private;
|
|
17
|
-
/**
|
|
18
|
-
* Process incoming terminal data for bracketed paste sequences.
|
|
19
|
-
*
|
|
20
|
-
* @returns `{ handled: false }` if the data contains no paste sequence and
|
|
21
|
-
* should be processed normally. `{ handled: true }` if the data was
|
|
22
|
-
* consumed by paste buffering — `pasteContent` is set when a complete
|
|
23
|
-
* paste has been assembled; omitted when still buffering.
|
|
24
|
-
*/
|
|
25
|
-
process(data: string): PasteResult;
|
|
26
|
-
}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import type { Component } from "../tui";
|
|
2
|
-
/**
|
|
3
|
-
* Box component - a container that applies padding and background to all children
|
|
4
|
-
*/
|
|
5
|
-
export declare class Box implements Component {
|
|
6
|
-
#private;
|
|
7
|
-
children: Component[];
|
|
8
|
-
constructor(paddingX?: number, paddingY?: number, bgFn?: (text: string) => string);
|
|
9
|
-
addChild(component: Component): void;
|
|
10
|
-
removeChild(component: Component): void;
|
|
11
|
-
/** Remove a child without disposing it (for detach-then-readd reuse). */
|
|
12
|
-
detachChild(component: Component): void;
|
|
13
|
-
clear(): void;
|
|
14
|
-
/** Remove all children without disposing them (for detach-then-readd reuse). */
|
|
15
|
-
detachAll(): void;
|
|
16
|
-
dispose(): void;
|
|
17
|
-
setBgFn(bgFn?: (text: string) => string): void;
|
|
18
|
-
invalidate(): void;
|
|
19
|
-
render(width: number): string[];
|
|
20
|
-
}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { Loader } from "./loader";
|
|
2
|
-
/**
|
|
3
|
-
* Loader that can be cancelled with Escape.
|
|
4
|
-
* Extends Loader with an AbortSignal for cancelling async operations.
|
|
5
|
-
*
|
|
6
|
-
* @example
|
|
7
|
-
* const loader = new CancellableLoader(tui, cyan, dim, "Working...");
|
|
8
|
-
* loader.onAbort = () => done(null);
|
|
9
|
-
* doWork(loader.signal).then(done);
|
|
10
|
-
*/
|
|
11
|
-
export declare class CancellableLoader extends Loader {
|
|
12
|
-
#private;
|
|
13
|
-
/** Called when user presses Escape */
|
|
14
|
-
onAbort?: () => void;
|
|
15
|
-
/** AbortSignal that is aborted when user presses Escape */
|
|
16
|
-
get signal(): AbortSignal;
|
|
17
|
-
/** Whether the loader was aborted */
|
|
18
|
-
get aborted(): boolean;
|
|
19
|
-
handleInput(data: string): void;
|
|
20
|
-
dispose(): void;
|
|
21
|
-
}
|
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
import { type AutocompleteProvider } from "../autocomplete";
|
|
2
|
-
import type { SymbolTheme } from "../symbols";
|
|
3
|
-
import { type Component, type Focusable } from "../tui";
|
|
4
|
-
import { type SelectListTheme } from "./select-list";
|
|
5
|
-
export interface EditorTheme {
|
|
6
|
-
borderColor: (str: string) => string;
|
|
7
|
-
selectList: SelectListTheme;
|
|
8
|
-
symbols: SymbolTheme;
|
|
9
|
-
editorPaddingX?: number;
|
|
10
|
-
/** Style function for inline hint/ghost text (dim text after cursor) */
|
|
11
|
-
hintStyle?: (text: string) => string;
|
|
12
|
-
}
|
|
13
|
-
export interface EditorTopBorder {
|
|
14
|
-
/** The status content (already styled) */
|
|
15
|
-
content: string;
|
|
16
|
-
/** Visible width of the content */
|
|
17
|
-
width: number;
|
|
18
|
-
}
|
|
19
|
-
export type EditorBorderStyle = "round" | "sharp";
|
|
20
|
-
interface HistoryEntry {
|
|
21
|
-
prompt: string;
|
|
22
|
-
}
|
|
23
|
-
interface HistoryStorage {
|
|
24
|
-
add(prompt: string, cwd?: string): Promise<void>;
|
|
25
|
-
getRecent(limit: number, cwd?: string): HistoryEntry[];
|
|
26
|
-
}
|
|
27
|
-
/** Test-only performance counters for advisory baseline tests. */
|
|
28
|
-
export declare const __editorPerfCounters: {
|
|
29
|
-
layoutTextInvocations: number;
|
|
30
|
-
layoutLogicalLinesProcessed: number;
|
|
31
|
-
visibleWidthMeasurements: number;
|
|
32
|
-
reset(): void;
|
|
33
|
-
};
|
|
34
|
-
export declare class Editor implements Component, Focusable {
|
|
35
|
-
#private;
|
|
36
|
-
/** Focusable interface - set by TUI when focus changes */
|
|
37
|
-
focused: boolean;
|
|
38
|
-
/** When set, replaces the normal cursor glyph at end-of-text with this ANSI-styled string. */
|
|
39
|
-
cursorOverride: string | undefined;
|
|
40
|
-
/** Display width of the cursorOverride glyph (needed because override may contain ANSI escapes). */
|
|
41
|
-
cursorOverrideWidth: number | undefined;
|
|
42
|
-
borderColor: (str: string) => string;
|
|
43
|
-
onAutocompleteUpdate?: () => void;
|
|
44
|
-
onSubmit?: (text: string) => void;
|
|
45
|
-
onAltEnter?: (text: string) => void;
|
|
46
|
-
onChange?: (text: string) => void;
|
|
47
|
-
onAutocompleteCancel?: () => void;
|
|
48
|
-
onTabDeclined?: (text: string) => void;
|
|
49
|
-
/**
|
|
50
|
-
* Called before Tab opens/applies autocomplete. Return true to consume Tab
|
|
51
|
-
* for app-level behavior (for example, queueing a draft while a turn runs).
|
|
52
|
-
*/
|
|
53
|
-
onTab?: (text: string) => boolean | undefined;
|
|
54
|
-
disableSubmit: boolean;
|
|
55
|
-
constructor(theme: EditorTheme);
|
|
56
|
-
dispose(): void;
|
|
57
|
-
setAutocompleteProvider(provider: AutocompleteProvider): void;
|
|
58
|
-
getAutocompleteProvider(): AutocompleteProvider | undefined;
|
|
59
|
-
/** Whether the autocomplete dropdown is currently open. */
|
|
60
|
-
isAutocompleteOpen(): boolean;
|
|
61
|
-
/**
|
|
62
|
-
* Set custom content for the top border (e.g., status line).
|
|
63
|
-
* Pass undefined to use the default plain border.
|
|
64
|
-
*/
|
|
65
|
-
setTopBorder(content: EditorTopBorder | undefined): void;
|
|
66
|
-
/**
|
|
67
|
-
* Show or hide the editor border chrome.
|
|
68
|
-
*/
|
|
69
|
-
setBorderVisible(borderVisible: boolean): void;
|
|
70
|
-
setBorderStyle(borderStyle: EditorBorderStyle): void;
|
|
71
|
-
setClosedBorderBox(closedBorderBox: boolean): void;
|
|
72
|
-
setPromptGutter(promptGutter: string | undefined): void;
|
|
73
|
-
setInputPrefix(inputPrefix: string | undefined): void;
|
|
74
|
-
setPlaceholder(placeholder: string | undefined): void;
|
|
75
|
-
/**
|
|
76
|
-
* Get the available width for top border content given a total terminal width.
|
|
77
|
-
* Accounts for right gutter, border characters, and horizontal padding when visible.
|
|
78
|
-
*/
|
|
79
|
-
getTopBorderAvailableWidth(terminalWidth: number): number;
|
|
80
|
-
/**
|
|
81
|
-
* Use the real terminal cursor instead of rendering a cursor glyph.
|
|
82
|
-
*/
|
|
83
|
-
setUseTerminalCursor(useTerminalCursor: boolean): void;
|
|
84
|
-
getUseTerminalCursor(): boolean;
|
|
85
|
-
setMaxHeight(maxHeight: number | undefined): void;
|
|
86
|
-
setPaddingX(paddingX: number): void;
|
|
87
|
-
setRightGutterWidth(width: number): void;
|
|
88
|
-
getAutocompleteMaxVisible(): number;
|
|
89
|
-
setAutocompleteMaxVisible(maxVisible: number): void;
|
|
90
|
-
setHistoryStorage(storage: HistoryStorage): void;
|
|
91
|
-
/**
|
|
92
|
-
* Add a prompt to history for up/down arrow navigation.
|
|
93
|
-
* Called after successful submission.
|
|
94
|
-
*/
|
|
95
|
-
addToHistory(text: string): void;
|
|
96
|
-
invalidate(): void;
|
|
97
|
-
render(width: number): string[];
|
|
98
|
-
handleInput(data: string): void;
|
|
99
|
-
/** Test-only seam: current wrap-cache entry count (memory-bound assertions). */
|
|
100
|
-
get wrappedLineCacheSize(): number;
|
|
101
|
-
getText(): string;
|
|
102
|
-
/**
|
|
103
|
-
* Get text with paste markers expanded to their actual content.
|
|
104
|
-
* Use this when you need the full content (e.g., for external editor).
|
|
105
|
-
*/
|
|
106
|
-
getExpandedText(): string;
|
|
107
|
-
getLines(): string[];
|
|
108
|
-
getCursor(): {
|
|
109
|
-
line: number;
|
|
110
|
-
col: number;
|
|
111
|
-
};
|
|
112
|
-
moveToLineStart(): void;
|
|
113
|
-
moveToLineEnd(): void;
|
|
114
|
-
moveToMessageStart(): void;
|
|
115
|
-
moveToMessageEnd(): void;
|
|
116
|
-
/**
|
|
117
|
-
* Undo the last meaningful edit while ignoring transient text that is still present at the cursor.
|
|
118
|
-
* Used for command-like autocomplete actions whose typed trigger should not count as the edit being undone.
|
|
119
|
-
*/
|
|
120
|
-
undoPastTransientText(transientText: string): void;
|
|
121
|
-
setText(text: string): void;
|
|
122
|
-
/** Insert text at the current cursor position */
|
|
123
|
-
insertText(text: string): void;
|
|
124
|
-
isShowingAutocomplete(): boolean;
|
|
125
|
-
}
|
|
126
|
-
export {};
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { type ImageDimensions } from "../terminal-capabilities";
|
|
2
|
-
import type { Component } from "../tui";
|
|
3
|
-
export interface ImageTheme {
|
|
4
|
-
fallbackColor: (str: string) => string;
|
|
5
|
-
}
|
|
6
|
-
export interface ImageOptions {
|
|
7
|
-
maxWidthCells?: number;
|
|
8
|
-
maxHeightCells?: number;
|
|
9
|
-
filename?: string;
|
|
10
|
-
refetch?: () => string;
|
|
11
|
-
}
|
|
12
|
-
export declare class Image implements Component {
|
|
13
|
-
#private;
|
|
14
|
-
constructor(base64Data: string, mimeType: string, theme: ImageTheme, options?: ImageOptions, dimensions?: ImageDimensions);
|
|
15
|
-
invalidate(): void;
|
|
16
|
-
get retainedBase64DataForTest(): string | undefined;
|
|
17
|
-
render(width: number): string[];
|
|
18
|
-
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { type Component, type Focusable } from "../tui";
|
|
2
|
-
/**
|
|
3
|
-
* Input component - single-line text input with horizontal scrolling
|
|
4
|
-
*/
|
|
5
|
-
export declare class Input implements Component, Focusable {
|
|
6
|
-
#private;
|
|
7
|
-
onSubmit?: (value: string) => void;
|
|
8
|
-
onEscape?: () => void;
|
|
9
|
-
/** Focusable interface - set by TUI when focus changes */
|
|
10
|
-
focused: boolean;
|
|
11
|
-
getValue(): string;
|
|
12
|
-
setValue(value: string): void;
|
|
13
|
-
handleInput(data: string): void;
|
|
14
|
-
invalidate(): void;
|
|
15
|
-
render(width: number): string[];
|
|
16
|
-
}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import type { TUI } from "../tui";
|
|
2
|
-
import { Text } from "./text";
|
|
3
|
-
export interface LoaderOptions {
|
|
4
|
-
timeDependentColor?: boolean;
|
|
5
|
-
}
|
|
6
|
-
/** Test-only performance counters for advisory baseline tests. */
|
|
7
|
-
export declare const __loaderPerfCounters: {
|
|
8
|
-
liveIntervals: number;
|
|
9
|
-
startedIntervals: number;
|
|
10
|
-
reset(): void;
|
|
11
|
-
};
|
|
12
|
-
export declare class Loader extends Text {
|
|
13
|
-
#private;
|
|
14
|
-
private spinnerColorFn;
|
|
15
|
-
private messageColorFn;
|
|
16
|
-
private message;
|
|
17
|
-
constructor(ui: TUI, spinnerColorFn: (str: string) => string, messageColorFn: (str: string) => string, message?: string, spinnerFrames?: string[], options?: LoaderOptions);
|
|
18
|
-
render(width: number): string[];
|
|
19
|
-
start(): void;
|
|
20
|
-
stop(): void;
|
|
21
|
-
dispose(): void;
|
|
22
|
-
setMessage(message: string): void;
|
|
23
|
-
}
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
import type { SymbolTheme } from "../symbols";
|
|
2
|
-
import type { Component } from "../tui";
|
|
3
|
-
/** Test-only clock seam for streaming throttle tests. */
|
|
4
|
-
export declare function __setMarkdownNowForTest(now: (() => number) | undefined): void;
|
|
5
|
-
/** Test/diagnostic seam: number of synchronous highlight invocations since the last reset. */
|
|
6
|
-
export declare function getMarkdownHighlightCallCount(): number;
|
|
7
|
-
export declare function resetMarkdownHighlightCallCount(): void;
|
|
8
|
-
/** Test-only performance counters for advisory baseline tests. */
|
|
9
|
-
export declare const __markdownPerfCounters: {
|
|
10
|
-
lexerInvocations: number;
|
|
11
|
-
lexedBytes: number;
|
|
12
|
-
reset(): void;
|
|
13
|
-
};
|
|
14
|
-
/** Drop all L2 cache entries. Call on theme change to prevent stale styled output. */
|
|
15
|
-
export declare function clearRenderCache(): void;
|
|
16
|
-
export declare function getRenderCacheRetainedBytes(): number;
|
|
17
|
-
/**
|
|
18
|
-
* Default text styling for markdown content.
|
|
19
|
-
* Applied to all text unless overridden by markdown formatting.
|
|
20
|
-
*/
|
|
21
|
-
export interface DefaultTextStyle {
|
|
22
|
-
/** Foreground color function */
|
|
23
|
-
color?: (text: string) => string;
|
|
24
|
-
/** Background color function */
|
|
25
|
-
bgColor?: (text: string) => string;
|
|
26
|
-
/** Bold text */
|
|
27
|
-
bold?: boolean;
|
|
28
|
-
/** Italic text */
|
|
29
|
-
italic?: boolean;
|
|
30
|
-
/** Strikethrough text */
|
|
31
|
-
strikethrough?: boolean;
|
|
32
|
-
/** Underline text */
|
|
33
|
-
underline?: boolean;
|
|
34
|
-
}
|
|
35
|
-
/**
|
|
36
|
-
* Theme functions for markdown elements.
|
|
37
|
-
* Each function takes text and returns styled text with ANSI codes.
|
|
38
|
-
*/
|
|
39
|
-
export interface MarkdownTheme {
|
|
40
|
-
heading: (text: string) => string;
|
|
41
|
-
link: (text: string) => string;
|
|
42
|
-
linkUrl: (text: string) => string;
|
|
43
|
-
code: (text: string) => string;
|
|
44
|
-
codeBlock: (text: string) => string;
|
|
45
|
-
codeBlockBorder: (text: string) => string;
|
|
46
|
-
quote: (text: string) => string;
|
|
47
|
-
quoteBorder: (text: string) => string;
|
|
48
|
-
hr: (text: string) => string;
|
|
49
|
-
listBullet: (text: string) => string;
|
|
50
|
-
bold: (text: string) => string;
|
|
51
|
-
italic: (text: string) => string;
|
|
52
|
-
strikethrough: (text: string) => string;
|
|
53
|
-
underline: (text: string) => string;
|
|
54
|
-
highlightCode?: (code: string, lang?: string) => string[];
|
|
55
|
-
/**
|
|
56
|
-
* Resolve a mermaid ASCII rendering by fenced block source text.
|
|
57
|
-
* Return null to fall back to fenced code rendering.
|
|
58
|
-
*/
|
|
59
|
-
resolveMermaidAscii?: (source: string) => string | null;
|
|
60
|
-
symbols: SymbolTheme;
|
|
61
|
-
}
|
|
62
|
-
export declare class Markdown implements Component {
|
|
63
|
-
#private;
|
|
64
|
-
constructor(text: string, paddingX: number, paddingY: number, theme: MarkdownTheme, defaultTextStyle?: DefaultTextStyle, codeBlockIndent?: number);
|
|
65
|
-
setOnStaleThrottle(callback: (() => void) | undefined): void;
|
|
66
|
-
setText(text: string, options?: {
|
|
67
|
-
streaming?: boolean;
|
|
68
|
-
}): void;
|
|
69
|
-
setStreaming(streaming: boolean): void;
|
|
70
|
-
dispose(): void;
|
|
71
|
-
invalidate(): void;
|
|
72
|
-
render(width: number): string[];
|
|
73
|
-
}
|
|
74
|
-
/**
|
|
75
|
-
* Render inline markdown (bold, italic, code, links, strikethrough) to a styled string.
|
|
76
|
-
* Unlike the full Markdown component, this produces a single line with no block-level elements.
|
|
77
|
-
*/
|
|
78
|
-
export declare function renderInlineMarkdown(text: string, mdTheme: MarkdownTheme, baseColor?: (t: string) => string): string;
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import type { SymbolTheme } from "../symbols";
|
|
2
|
-
import type { Component } from "../tui";
|
|
3
|
-
export interface SelectItem {
|
|
4
|
-
value: string;
|
|
5
|
-
label: string;
|
|
6
|
-
description?: string;
|
|
7
|
-
/** Dim hint text shown inline after cursor when this item is selected */
|
|
8
|
-
hint?: string;
|
|
9
|
-
}
|
|
10
|
-
export interface SelectListTheme {
|
|
11
|
-
selectedPrefix: (text: string) => string;
|
|
12
|
-
selectedText: (text: string) => string;
|
|
13
|
-
description: (text: string) => string;
|
|
14
|
-
scrollInfo: (text: string) => string;
|
|
15
|
-
noMatch: (text: string) => string;
|
|
16
|
-
symbols: SymbolTheme;
|
|
17
|
-
}
|
|
18
|
-
export interface SelectListTruncatePrimaryContext {
|
|
19
|
-
text: string;
|
|
20
|
-
maxWidth: number;
|
|
21
|
-
columnWidth: number;
|
|
22
|
-
item: SelectItem;
|
|
23
|
-
isSelected: boolean;
|
|
24
|
-
}
|
|
25
|
-
export interface SelectListLayoutOptions {
|
|
26
|
-
minPrimaryColumnWidth?: number;
|
|
27
|
-
maxPrimaryColumnWidth?: number;
|
|
28
|
-
truncatePrimary?: (context: SelectListTruncatePrimaryContext) => string;
|
|
29
|
-
}
|
|
30
|
-
export declare class SelectList implements Component {
|
|
31
|
-
#private;
|
|
32
|
-
private readonly items;
|
|
33
|
-
private readonly maxVisible;
|
|
34
|
-
private readonly theme;
|
|
35
|
-
private readonly layout;
|
|
36
|
-
onSelect?: (item: SelectItem) => void;
|
|
37
|
-
onCancel?: () => void;
|
|
38
|
-
onSelectionChange?: (item: SelectItem) => void;
|
|
39
|
-
constructor(items: ReadonlyArray<SelectItem>, maxVisible: number, theme: SelectListTheme, layout?: SelectListLayoutOptions);
|
|
40
|
-
setFilter(filter: string): void;
|
|
41
|
-
setSelectedIndex(index: number): void;
|
|
42
|
-
invalidate(): void;
|
|
43
|
-
render(width: number): string[];
|
|
44
|
-
handleInput(keyData: string): void;
|
|
45
|
-
getSelectedItem(): SelectItem | null;
|
|
46
|
-
}
|