@sayknow-cli/tui 0.4.2 → 0.4.4
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/types/animation-scheduler.d.ts +13 -0
- package/dist/types/autocomplete.d.ts +84 -0
- package/dist/types/bracketed-paste.d.ts +20 -0
- package/dist/types/components/box.d.ts +20 -0
- package/dist/types/components/cancellable-loader.d.ts +21 -0
- package/dist/types/components/editor.d.ts +126 -0
- package/dist/types/components/image.d.ts +18 -0
- package/dist/types/components/input.d.ts +16 -0
- package/dist/types/components/loader.d.ts +23 -0
- package/dist/types/components/markdown.d.ts +87 -0
- package/dist/types/components/sayknow-pet.d.ts +128 -0
- package/dist/types/components/secret-input.d.ts +35 -0
- package/dist/types/components/select-list.d.ts +52 -0
- package/dist/types/components/settings-list.d.ts +39 -0
- package/dist/types/components/spacer.d.ts +11 -0
- package/dist/types/components/tab-bar.d.ts +56 -0
- package/dist/types/components/text.d.ts +22 -0
- package/dist/types/components/truncated-text.d.ts +10 -0
- package/dist/types/editor-component.d.ts +36 -0
- package/dist/types/fuzzy.d.ts +15 -0
- package/dist/types/index.d.ts +29 -0
- package/dist/types/keybindings.d.ts +201 -0
- package/dist/types/keys.d.ts +208 -0
- package/dist/types/kill-ring.d.ts +27 -0
- package/dist/types/metrics.d.ts +85 -0
- package/dist/types/stdin-buffer.d.ts +52 -0
- package/dist/types/symbols.d.ts +23 -0
- package/dist/types/terminal-capabilities.d.ts +187 -0
- package/dist/types/terminal.d.ts +94 -0
- package/dist/types/ttyid.d.ts +9 -0
- package/dist/types/tui.d.ts +304 -0
- package/dist/types/utils.d.ts +110 -0
- package/package.json +9 -8
|
@@ -0,0 +1,13 @@
|
|
|
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 {};
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
export declare function getSlashCommandMatchRank(query: string, commandName: string): number;
|
|
2
|
+
export declare function isInsideInlineCodeSpan(text: string): boolean;
|
|
3
|
+
export declare function extractSlashCommandTokenPrefix(text: string): string | null;
|
|
4
|
+
export interface AutocompleteItem {
|
|
5
|
+
value: string;
|
|
6
|
+
label: string;
|
|
7
|
+
description?: string;
|
|
8
|
+
/** Dim hint text shown inline after cursor when this item is selected */
|
|
9
|
+
hint?: string;
|
|
10
|
+
}
|
|
11
|
+
type Awaitable<T> = T | Promise<T>;
|
|
12
|
+
export interface SlashCommand {
|
|
13
|
+
name: string;
|
|
14
|
+
description?: string;
|
|
15
|
+
argumentHint?: string;
|
|
16
|
+
/**
|
|
17
|
+
* Higher values surface first in autocomplete, ahead of fuzzy-score ordering.
|
|
18
|
+
* Use this to pin first-class commands (e.g. bundled SKC skills) to the top.
|
|
19
|
+
*/
|
|
20
|
+
priority?: number;
|
|
21
|
+
getArgumentCompletions?(argumentPrefix: string): Awaitable<AutocompleteItem[] | null>;
|
|
22
|
+
/** Return inline hint text for the current argument state (shown as dim ghost text after cursor) */
|
|
23
|
+
getInlineHint?(argumentText: string): string | null;
|
|
24
|
+
}
|
|
25
|
+
export interface AutocompleteProvider {
|
|
26
|
+
/** Get autocomplete suggestions for current text/cursor position */
|
|
27
|
+
getSuggestions(lines: string[], cursorLine: number, cursorCol: number): Promise<{
|
|
28
|
+
items: AutocompleteItem[];
|
|
29
|
+
prefix: string;
|
|
30
|
+
} | null>;
|
|
31
|
+
/** Apply the selected item and return new text + cursor position */
|
|
32
|
+
applyCompletion(lines: string[], cursorLine: number, cursorCol: number, item: AutocompleteItem, prefix: string): {
|
|
33
|
+
lines: string[];
|
|
34
|
+
cursorLine: number;
|
|
35
|
+
cursorCol: number;
|
|
36
|
+
onApplied?: () => void;
|
|
37
|
+
};
|
|
38
|
+
/** Get inline hint text to show as dim ghost text after the cursor */
|
|
39
|
+
getInlineHint?(lines: string[], cursorLine: number, cursorCol: number): string | null;
|
|
40
|
+
/** Synchronously try to complete a slash command at the start of a line (no async I/O). */
|
|
41
|
+
/** Returns matched items and the full prefix, or null if not applicable. */
|
|
42
|
+
trySyncSlashCompletion?(textBeforeCursor: string): {
|
|
43
|
+
items: AutocompleteItem[];
|
|
44
|
+
prefix: string;
|
|
45
|
+
} | null;
|
|
46
|
+
/**
|
|
47
|
+
* Synchronously try to expand text immediately before the cursor (no async I/O).
|
|
48
|
+
* Called after every single-character insert. Implementations MUST cheaply
|
|
49
|
+
* early-return when the trailing context cannot trigger them.
|
|
50
|
+
* Returns the number of characters to delete immediately before the cursor
|
|
51
|
+
* and the literal string to insert in their place, or null to leave the
|
|
52
|
+
* buffer untouched.
|
|
53
|
+
*/
|
|
54
|
+
trySyncInlineReplace?(textBeforeCursor: string): {
|
|
55
|
+
replaceLen: number;
|
|
56
|
+
insert: string;
|
|
57
|
+
} | null;
|
|
58
|
+
}
|
|
59
|
+
export declare class CombinedAutocompleteProvider implements AutocompleteProvider {
|
|
60
|
+
#private;
|
|
61
|
+
constructor(commands?: (SlashCommand | AutocompleteItem)[], basePath?: string);
|
|
62
|
+
getSuggestions(lines: string[], cursorLine: number, cursorCol: number): Promise<{
|
|
63
|
+
items: AutocompleteItem[];
|
|
64
|
+
prefix: string;
|
|
65
|
+
} | null>;
|
|
66
|
+
applyCompletion(lines: string[], cursorLine: number, cursorCol: number, item: AutocompleteItem, prefix: string): {
|
|
67
|
+
lines: string[];
|
|
68
|
+
cursorLine: number;
|
|
69
|
+
cursorCol: number;
|
|
70
|
+
};
|
|
71
|
+
invalidateDirCache(dir?: string): void;
|
|
72
|
+
getForceFileSuggestions(lines: string[], cursorLine: number, cursorCol: number): Promise<{
|
|
73
|
+
items: AutocompleteItem[];
|
|
74
|
+
prefix: string;
|
|
75
|
+
} | null>;
|
|
76
|
+
shouldTriggerFileCompletion(lines: string[], cursorLine: number, cursorCol: number): boolean;
|
|
77
|
+
/** Get inline hint text for slash commands with subcommand hints */
|
|
78
|
+
getInlineHint(lines: string[], cursorLine: number, cursorCol: number): string | null;
|
|
79
|
+
trySyncSlashCompletion(textBeforeCursor: string): {
|
|
80
|
+
items: AutocompleteItem[];
|
|
81
|
+
prefix: string;
|
|
82
|
+
} | null;
|
|
83
|
+
}
|
|
84
|
+
export {};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export declare const BRACKETED_PASTE_FRAME_TIMEOUT_MS = 1000;
|
|
2
|
+
export declare const BRACKETED_PASTE_FRAME_MAX_BYTES: number;
|
|
3
|
+
export type PasteResult = {
|
|
4
|
+
handled: false;
|
|
5
|
+
} | {
|
|
6
|
+
handled: true;
|
|
7
|
+
leading: string;
|
|
8
|
+
pasteContent?: string;
|
|
9
|
+
remaining: string;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Handles bracketed paste framing with bounded buffering. Leading ordinary
|
|
13
|
+
* input and split markers are retained byte-for-byte; stale or oversized
|
|
14
|
+
* incomplete frames are released as ordinary input on the next event.
|
|
15
|
+
*/
|
|
16
|
+
export declare class BracketedPasteHandler {
|
|
17
|
+
#private;
|
|
18
|
+
get hasPendingFrame(): boolean;
|
|
19
|
+
process(data: string, now?: number): PasteResult;
|
|
20
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
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 {};
|
|
@@ -0,0 +1,18 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import type { SymbolTheme } from "../symbols";
|
|
2
|
+
import type { Component } from "../tui";
|
|
3
|
+
import { type ViewportAnchorSpan } from "../utils";
|
|
4
|
+
/** Test-only clock seam for streaming throttle tests. */
|
|
5
|
+
export declare function __setMarkdownNowForTest(now: (() => number) | undefined): void;
|
|
6
|
+
/** Test/diagnostic seam: number of synchronous highlight invocations since the last reset. */
|
|
7
|
+
export declare function getMarkdownHighlightCallCount(): number;
|
|
8
|
+
export declare function resetMarkdownHighlightCallCount(): void;
|
|
9
|
+
/** Test-only performance counters for advisory baseline tests. */
|
|
10
|
+
export declare const __markdownPerfCounters: {
|
|
11
|
+
lexerInvocations: number;
|
|
12
|
+
lexedBytes: number;
|
|
13
|
+
reset(): void;
|
|
14
|
+
};
|
|
15
|
+
/** Drop all L2 cache entries. Call on theme change to prevent stale styled output. */
|
|
16
|
+
export declare function clearRenderCache(): void;
|
|
17
|
+
export declare function getRenderCacheRetainedBytes(): number;
|
|
18
|
+
/**
|
|
19
|
+
* Default text styling for markdown content.
|
|
20
|
+
* Applied to all text unless overridden by markdown formatting.
|
|
21
|
+
*/
|
|
22
|
+
export interface DefaultTextStyle {
|
|
23
|
+
/** Foreground color function */
|
|
24
|
+
color?: (text: string) => string;
|
|
25
|
+
/** Background color function */
|
|
26
|
+
bgColor?: (text: string) => string;
|
|
27
|
+
/** Bold text */
|
|
28
|
+
bold?: boolean;
|
|
29
|
+
/** Italic text */
|
|
30
|
+
italic?: boolean;
|
|
31
|
+
/** Strikethrough text */
|
|
32
|
+
strikethrough?: boolean;
|
|
33
|
+
/** Underline text */
|
|
34
|
+
underline?: boolean;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Theme functions for markdown elements.
|
|
38
|
+
* Each function takes text and returns styled text with ANSI codes.
|
|
39
|
+
*/
|
|
40
|
+
export interface MarkdownTheme {
|
|
41
|
+
heading: (text: string) => string;
|
|
42
|
+
link: (text: string) => string;
|
|
43
|
+
linkUrl: (text: string) => string;
|
|
44
|
+
code: (text: string) => string;
|
|
45
|
+
codeBlock: (text: string) => string;
|
|
46
|
+
codeBlockBorder: (text: string) => string;
|
|
47
|
+
quote: (text: string) => string;
|
|
48
|
+
quoteBorder: (text: string) => string;
|
|
49
|
+
hr: (text: string) => string;
|
|
50
|
+
listBullet: (text: string) => string;
|
|
51
|
+
bold: (text: string) => string;
|
|
52
|
+
italic: (text: string) => string;
|
|
53
|
+
strikethrough: (text: string) => string;
|
|
54
|
+
underline: (text: string) => string;
|
|
55
|
+
highlightCode?: (code: string, lang?: string) => string[];
|
|
56
|
+
/**
|
|
57
|
+
* Resolve a mermaid ASCII rendering by fenced block source text.
|
|
58
|
+
* Return null to fall back to fenced code rendering.
|
|
59
|
+
*/
|
|
60
|
+
resolveMermaidAscii?: (source: string) => string | null;
|
|
61
|
+
symbols: SymbolTheme;
|
|
62
|
+
}
|
|
63
|
+
export declare class Markdown implements Component {
|
|
64
|
+
#private;
|
|
65
|
+
constructor(text: string, paddingX: number, paddingY: number, theme: MarkdownTheme, defaultTextStyle?: DefaultTextStyle, codeBlockIndent?: number);
|
|
66
|
+
setOnStaleThrottle(callback: (() => void) | undefined): void;
|
|
67
|
+
setText(text: string, options?: {
|
|
68
|
+
streaming?: boolean;
|
|
69
|
+
}): void;
|
|
70
|
+
setStreaming(streaming: boolean): void;
|
|
71
|
+
dispose(): void;
|
|
72
|
+
invalidate(): void;
|
|
73
|
+
render(width: number): string[];
|
|
74
|
+
renderWithViewportAnchorSource(width: number, source: {
|
|
75
|
+
id: string;
|
|
76
|
+
}): {
|
|
77
|
+
lines: string[];
|
|
78
|
+
anchors: Array<({
|
|
79
|
+
id: string;
|
|
80
|
+
} & ViewportAnchorSpan) | null>;
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Render inline markdown (bold, italic, code, links, strikethrough) to a styled string.
|
|
85
|
+
* Unlike the full Markdown component, this produces a single line with no block-level elements.
|
|
86
|
+
*/
|
|
87
|
+
export declare function renderInlineMarkdown(text: string, mdTheme: MarkdownTheme, baseColor?: (t: string) => string): string;
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ┌─ SAYKNOW PET SPRITE SPEC ────────────────────────────────────────────────┐
|
|
3
|
+
* The pet is a 16×16 pixel octopus drawn beside the composer. Everything here
|
|
4
|
+
* is data: no PNGs, no assets — each frame is 16 strings of 16 chars, encoded
|
|
5
|
+
* to a sixel or kitty escape at runtime. Author a new frame by drawing a grid.
|
|
6
|
+
*
|
|
7
|
+
* GRID RULES
|
|
8
|
+
* - Exactly 16 rows × 16 columns. Only PALETTE keys below are valid chars.
|
|
9
|
+
* - `.` = transparent. Keep the outer columns transparent so the sprite sits
|
|
10
|
+
* snug beside the input box (the widget reserves +1 column of slack).
|
|
11
|
+
*
|
|
12
|
+
* PALETTE (char → role) — see PALETTE for exact RGB:
|
|
13
|
+
* .=transparent K=dark outline R=mantle body r=body highlight
|
|
14
|
+
* W=eye white V=pupil G=eye sparkle b=underside
|
|
15
|
+
* w=tear H h A=reserved (unused by the octopus art)
|
|
16
|
+
*
|
|
17
|
+
* FRAME CATALOG (SayknowPixelFrameName → PIXEL_GRIDS):
|
|
18
|
+
* base idle rest; also the dance "drop/settle" beat
|
|
19
|
+
* gazeL eyes glance left ┐ idle loop (see sayknow-pet-widget IDLE_LOOP)
|
|
20
|
+
* gazeR eyes glance right │
|
|
21
|
+
* flicker eyes blink ┘
|
|
22
|
+
* flex sparkly "yay" eyes; dance accent + random idle flex burst
|
|
23
|
+
* danceL tentacles sway left ┐ work loop (PARA_PARA_STEPS)
|
|
24
|
+
* danceR tentacles sway right ┘
|
|
25
|
+
* cry1..3 a tear trails from the outer eye corners (BlueOcto sob)
|
|
26
|
+
*
|
|
27
|
+
* RENDERING: buildSayknowPixelFrames({ protocol, cellWidthPx, cellHeightPx,
|
|
28
|
+
* targetRows: 2 }) scales the art to 2 terminal rows and encodes each frame
|
|
29
|
+
* once. Kitty uses a native `Y=` sub-cell drop (set by the widget) to sit on the
|
|
30
|
+
* composer border; sixel uses transparent top padding.
|
|
31
|
+
*
|
|
32
|
+
* BEHAVIOR (timing, positioning, on/off) lives in
|
|
33
|
+
* packages/coding-agent/src/modes/components/sayknow-pet-widget.ts.
|
|
34
|
+
*
|
|
35
|
+
* ADD A FRAME: draw the grid → add its name to SayknowPixelFrameName → register it in
|
|
36
|
+
* PIXEL_GRIDS → reference it from an idle/work loop or a skin burst.
|
|
37
|
+
*
|
|
38
|
+
* ADD A PET (skin): append one entry to PET_SKINS below — { id, label, description,
|
|
39
|
+
* palette, burst }. The id flows into PetSkinId/PetMode automatically, the settings
|
|
40
|
+
* enum, `/pet` command and both selectors derive their options from PET_SKINS, and the
|
|
41
|
+
* widget reads `burst` to animate — no other file needs editing. Recolor with a palette
|
|
42
|
+
* spread (see BLUE_PALETTE); add frames only for poses the catalog lacks.
|
|
43
|
+
* └────────────────────────────────────────────────────────────────────────┘
|
|
44
|
+
*/
|
|
45
|
+
type Rgb = readonly [number, number, number];
|
|
46
|
+
export type Palette = Record<string, Rgb | null>;
|
|
47
|
+
export declare const PET_SKIN_IDS: readonly ["red", "blue"];
|
|
48
|
+
export type PetSkinId = (typeof PET_SKIN_IDS)[number];
|
|
49
|
+
/** Every pet mode: "off" plus each skin id, in menu order. */
|
|
50
|
+
export declare const PET_MODE_IDS: readonly ["off", "red", "blue"];
|
|
51
|
+
export type PetMode = (typeof PET_MODE_IDS)[number];
|
|
52
|
+
/** Narrow an arbitrary string to a PetMode. */
|
|
53
|
+
export declare function isPetMode(value: string): value is PetMode;
|
|
54
|
+
/** Logical pixel-pet frame names shared by the overlay state machine. */
|
|
55
|
+
export type SayknowPixelFrameName = "base" | "gazeL" | "gazeR" | "flicker" | "flex" | "danceL" | "danceR" | "cry1" | "cry2" | "cry3";
|
|
56
|
+
/** Para-para work dance beats: the working loop and each skin's burst "work-in" intro. */
|
|
57
|
+
export declare const PARA_PARA_STEPS: ReadonlyArray<readonly [SayknowPixelFrameName, number]>;
|
|
58
|
+
/**
|
|
59
|
+
* A skin's idle burst: a short intro sequence, then an optional looping tail. It drives
|
|
60
|
+
* BOTH the random live show-off AND the selector's preview demo, so give every skin a
|
|
61
|
+
* real animation (reuse PARA_PARA_STEPS for a work-in intro) rather than one held frame.
|
|
62
|
+
*/
|
|
63
|
+
export interface PetBurst {
|
|
64
|
+
/** Frames played once, in order, at the start of the burst. */
|
|
65
|
+
intro: ReadonlyArray<readonly [SayknowPixelFrameName, number]>;
|
|
66
|
+
/** Frames cycled every `stepMs` for `ms` after the intro (a held or looping finish). */
|
|
67
|
+
tail?: {
|
|
68
|
+
frames: readonly SayknowPixelFrameName[];
|
|
69
|
+
stepMs: number;
|
|
70
|
+
ms: number;
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
/** Everything that defines a pet skin: identity, UI copy, colors and behavior. */
|
|
74
|
+
export interface PetSkin {
|
|
75
|
+
id: PetSkinId;
|
|
76
|
+
/** Selector/settings label, e.g. "RedOctopus". */
|
|
77
|
+
label: string;
|
|
78
|
+
/** One-line selector/settings description. */
|
|
79
|
+
description: string;
|
|
80
|
+
palette: Palette;
|
|
81
|
+
/** Idle burst animation played between quiet idle loops. */
|
|
82
|
+
burst: PetBurst;
|
|
83
|
+
}
|
|
84
|
+
/** Skin registry — the single source for palettes, behavior and selector/command copy. */
|
|
85
|
+
export declare const PET_SKINS: Record<PetSkinId, PetSkin>;
|
|
86
|
+
/** Total burst duration (intro beats plus the looping tail). */
|
|
87
|
+
export declare function petBurstDurationMs(burst: PetBurst): number;
|
|
88
|
+
/** The frame to show `elapsed` ms into a burst (`now` cycles the looping tail). */
|
|
89
|
+
export declare function petBurstFrame(burst: PetBurst, elapsed: number, now: number): SayknowPixelFrameName;
|
|
90
|
+
/** Test-only access to logical art; production rendering still uses encoded frames. */
|
|
91
|
+
export declare const __sayknowPetTestHooks: {
|
|
92
|
+
getPixelGrid(name: SayknowPixelFrameName): string[];
|
|
93
|
+
};
|
|
94
|
+
/** Encode a grid as a transparent SIXEL image, optionally bottom-aligned by top padding. */
|
|
95
|
+
export declare function encodeGridSixel(grid: string[], scale: number, topPaddingPx?: number, palette?: Palette): string;
|
|
96
|
+
/** Encode a bottom-aligned grid as kitty raw RGBA at `scale`. */
|
|
97
|
+
export declare function encodeGridKitty(grid: string[], scale: number, imageId: number, cols: number, rows: number, topPaddingPx?: number, cellYOffsetPx?: number, leftPaddingPx?: number, rightPaddingPx?: number, palette?: Palette): string;
|
|
98
|
+
export interface SayknowPixelFrames {
|
|
99
|
+
/** escape payload per logical frame (drawn at the current cursor cell) */
|
|
100
|
+
frames: Record<SayknowPixelFrameName, string>;
|
|
101
|
+
/** protocol the frames were encoded for */
|
|
102
|
+
protocol: "sixel" | "kitty";
|
|
103
|
+
widthPx: number;
|
|
104
|
+
heightPx: number;
|
|
105
|
+
columns: number;
|
|
106
|
+
rows: number;
|
|
107
|
+
/** terminal rows touched by the encoded raster, including pixel offset */
|
|
108
|
+
rasterRows: number;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Build overlay pixel frames exactly `targetRows` terminal rows tall when the
|
|
112
|
+
* terminal cells permit it. Nearest-neighbor sampling preserves the 16x16 art
|
|
113
|
+
* while allowing fractional scale factors such as 36px / 16px.
|
|
114
|
+
*/
|
|
115
|
+
export declare function buildSayknowPixelFrames(options: {
|
|
116
|
+
protocol: "sixel" | "kitty";
|
|
117
|
+
cellWidthPx: number;
|
|
118
|
+
cellHeightPx: number;
|
|
119
|
+
targetRows?: number;
|
|
120
|
+
/** Transparent pixel offset above sixel art for sub-cell vertical placement. */
|
|
121
|
+
sixelTopPaddingPx?: number;
|
|
122
|
+
/** Native sub-cell `Y=` pixel offset that drops the kitty sprite within its first cell. */
|
|
123
|
+
kittyCellYOffsetPx?: number;
|
|
124
|
+
kittyImageId?: number;
|
|
125
|
+
/** Color skin for the sprite palette (default "red"). */
|
|
126
|
+
skin?: PetSkinId;
|
|
127
|
+
}): SayknowPixelFrames;
|
|
128
|
+
export {};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { type Component, type Focusable } from "../tui";
|
|
2
|
+
declare const secretValueIssuer: unique symbol;
|
|
3
|
+
/**
|
|
4
|
+
* A one-shot secret transfer handle. The contained value is cleared immediately
|
|
5
|
+
* after it is consumed and cannot be read through any other public API.
|
|
6
|
+
*/
|
|
7
|
+
export declare class SecretValue {
|
|
8
|
+
#private;
|
|
9
|
+
/** @internal SecretInput is the sole issuer of usable SecretValue handles. */
|
|
10
|
+
constructor(value: string, issuer: typeof secretValueIssuer);
|
|
11
|
+
consume(): string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* A single-line masked input for credentials and other write-only secrets.
|
|
15
|
+
*
|
|
16
|
+
* The editing behavior follows Input, but render output is derived only from
|
|
17
|
+
* grapheme counts; the backing characters are never returned or rendered.
|
|
18
|
+
*/
|
|
19
|
+
export declare class SecretInput implements Component, Focusable {
|
|
20
|
+
#private;
|
|
21
|
+
readonly placeholder: string;
|
|
22
|
+
onSubmit?: (value: SecretValue) => void;
|
|
23
|
+
onEscape?: () => void;
|
|
24
|
+
/** Focusable interface - set by TUI when focus changes. */
|
|
25
|
+
focused: boolean;
|
|
26
|
+
constructor(options?: {
|
|
27
|
+
placeholder?: string;
|
|
28
|
+
});
|
|
29
|
+
handleInput(data: string): void;
|
|
30
|
+
clear(): void;
|
|
31
|
+
dispose(): void;
|
|
32
|
+
invalidate(): void;
|
|
33
|
+
render(width: number): string[];
|
|
34
|
+
}
|
|
35
|
+
export {};
|
|
@@ -0,0 +1,52 @@
|
|
|
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
|
+
/** Autocomplete hint consumed by Editor; SelectList does not render it. */
|
|
8
|
+
hint?: string;
|
|
9
|
+
/**
|
|
10
|
+
* Renders dimmed and can never be selected: navigation skips it, selection
|
|
11
|
+
* callbacks never fire for it, and a list whose visible items are all
|
|
12
|
+
* disabled reports no selection (`getSelectedItem()` returns `null`).
|
|
13
|
+
*/
|
|
14
|
+
disabled?: boolean;
|
|
15
|
+
}
|
|
16
|
+
export interface SelectListTheme {
|
|
17
|
+
selectedPrefix: (text: string) => string;
|
|
18
|
+
selectedText: (text: string) => string;
|
|
19
|
+
description: (text: string) => string;
|
|
20
|
+
scrollInfo: (text: string) => string;
|
|
21
|
+
noMatch: (text: string) => string;
|
|
22
|
+
symbols: SymbolTheme;
|
|
23
|
+
}
|
|
24
|
+
export interface SelectListTruncatePrimaryContext {
|
|
25
|
+
text: string;
|
|
26
|
+
maxWidth: number;
|
|
27
|
+
columnWidth: number;
|
|
28
|
+
item: SelectItem;
|
|
29
|
+
isSelected: boolean;
|
|
30
|
+
}
|
|
31
|
+
export interface SelectListLayoutOptions {
|
|
32
|
+
minPrimaryColumnWidth?: number;
|
|
33
|
+
maxPrimaryColumnWidth?: number;
|
|
34
|
+
truncatePrimary?: (context: SelectListTruncatePrimaryContext) => string;
|
|
35
|
+
}
|
|
36
|
+
export declare class SelectList implements Component {
|
|
37
|
+
#private;
|
|
38
|
+
private readonly items;
|
|
39
|
+
private readonly maxVisible;
|
|
40
|
+
private readonly theme;
|
|
41
|
+
private readonly layout;
|
|
42
|
+
onSelect?: (item: SelectItem) => void;
|
|
43
|
+
onCancel?: () => void;
|
|
44
|
+
onSelectionChange?: (item: SelectItem) => void;
|
|
45
|
+
constructor(items: ReadonlyArray<SelectItem>, maxVisible: number, theme: SelectListTheme, layout?: SelectListLayoutOptions);
|
|
46
|
+
setFilter(filter: string): void;
|
|
47
|
+
setSelectedIndex(index: number): void;
|
|
48
|
+
invalidate(): void;
|
|
49
|
+
render(width: number): string[];
|
|
50
|
+
handleInput(keyData: string): void;
|
|
51
|
+
getSelectedItem(): SelectItem | null;
|
|
52
|
+
}
|