@prometheus-ai/tui 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -0
- package/README.md +704 -0
- package/dist/types/autocomplete.d.ts +76 -0
- package/dist/types/bracketed-paste.d.ts +26 -0
- package/dist/types/components/box.d.ts +17 -0
- package/dist/types/components/cancellable-loader.d.ts +21 -0
- package/dist/types/components/editor.d.ts +105 -0
- package/dist/types/components/image.d.ts +84 -0
- package/dist/types/components/input.d.ts +18 -0
- package/dist/types/components/loader.d.ts +13 -0
- package/dist/types/components/markdown.d.ts +61 -0
- package/dist/types/components/scroll-view.d.ts +40 -0
- package/dist/types/components/select-list.d.ts +48 -0
- package/dist/types/components/settings-list.d.ts +41 -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 +13 -0
- package/dist/types/components/truncated-text.d.ts +10 -0
- package/dist/types/deccara.d.ts +49 -0
- package/dist/types/editor-component.d.ts +36 -0
- package/dist/types/fuzzy.d.ts +15 -0
- package/dist/types/index.d.ts +28 -0
- package/dist/types/keybindings.d.ts +189 -0
- package/dist/types/keys.d.ts +208 -0
- package/dist/types/kill-ring.d.ts +27 -0
- package/dist/types/kitty-graphics.d.ts +94 -0
- package/dist/types/stdin-buffer.d.ts +43 -0
- package/dist/types/symbols.d.ts +25 -0
- package/dist/types/terminal-capabilities.d.ts +196 -0
- package/dist/types/terminal.d.ts +103 -0
- package/dist/types/ttyid.d.ts +9 -0
- package/dist/types/tui.d.ts +275 -0
- package/dist/types/utils.d.ts +89 -0
- package/package.json +73 -0
- package/src/autocomplete.ts +871 -0
- package/src/bracketed-paste.ts +47 -0
- package/src/components/box.ts +156 -0
- package/src/components/cancellable-loader.ts +40 -0
- package/src/components/editor.ts +2695 -0
- package/src/components/image.ts +318 -0
- package/src/components/input.ts +459 -0
- package/src/components/loader.ts +86 -0
- package/src/components/markdown.ts +1189 -0
- package/src/components/scroll-view.ts +166 -0
- package/src/components/select-list.ts +331 -0
- package/src/components/settings-list.ts +212 -0
- package/src/components/spacer.ts +28 -0
- package/src/components/tab-bar.ts +175 -0
- package/src/components/text.ts +110 -0
- package/src/components/truncated-text.ts +61 -0
- package/src/deccara.ts +314 -0
- package/src/editor-component.ts +71 -0
- package/src/fuzzy.ts +143 -0
- package/src/index.ts +44 -0
- package/src/keybindings.ts +279 -0
- package/src/keys.ts +537 -0
- package/src/kill-ring.ts +46 -0
- package/src/kitty-graphics.ts +270 -0
- package/src/stdin-buffer.ts +423 -0
- package/src/symbols.ts +26 -0
- package/src/terminal-capabilities.ts +1009 -0
- package/src/terminal.ts +1114 -0
- package/src/ttyid.ts +70 -0
- package/src/tui.ts +2988 -0
- package/src/utils.ts +452 -0
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/** DECSACE — select the rectangle change extent so DECCARA fills a rectangle. */
|
|
2
|
+
export declare const DECSACE_RECT = "\u001B[2*x";
|
|
3
|
+
/** DECSACE — restore the default (stream) change extent. */
|
|
4
|
+
export declare const DECSACE_DEFAULT = "\u001B[*x";
|
|
5
|
+
/**
|
|
6
|
+
* Encode a single DECCARA rectangle. `top`/`bottom` are 1-based inclusive screen
|
|
7
|
+
* rows, `left`/`right` 1-based inclusive columns, `sgr` the raw SGR parameter
|
|
8
|
+
* list to apply (e.g. `48;2;10;20;30`, `48;5;4`, `41`).
|
|
9
|
+
*/
|
|
10
|
+
export declare function encodeDeccara(top: number, left: number, bottom: number, right: number, sgr: string): string;
|
|
11
|
+
/** Where to cut a fillable line and the background to paint over the remainder. */
|
|
12
|
+
export interface BgFillAnalysis {
|
|
13
|
+
/** Byte index where droppable trailing background padding begins (0 = whole line). */
|
|
14
|
+
cut: number;
|
|
15
|
+
/** 0-based column where the trailing padding begins (DECCARA left = leftCol + 1). */
|
|
16
|
+
leftCol: number;
|
|
17
|
+
/** SGR parameter list of the background covering the trailing region. */
|
|
18
|
+
bg: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Decide whether `line` (a final, width-fit, reset-terminated ANSI string) is a
|
|
22
|
+
* full-width background fill whose trailing padding can be replaced by a DECCARA
|
|
23
|
+
* rectangle. Returns `null` unless it can *prove* the dropped bytes are literal
|
|
24
|
+
* trailing spaces under a single, constant, non-default background span (or the
|
|
25
|
+
* entire row is background-styled spaces).
|
|
26
|
+
*
|
|
27
|
+
* Conservative by construction: any OSC sequence (hyperlinks/images), any
|
|
28
|
+
* non-SGR CSI, a partial row, an inconsistent or default trailing background, or
|
|
29
|
+
* a malformed escape all yield `null` so the caller keeps the exact original.
|
|
30
|
+
*/
|
|
31
|
+
export declare function analyzeBgFillLine(line: string, width: number): BgFillAnalysis | null;
|
|
32
|
+
/** Per-frame plan: the (possibly shortened) row strings and the DECCARA batch. */
|
|
33
|
+
export interface DeccaraPlan {
|
|
34
|
+
/** Row strings to write, parallel to the input. Optimized rows are shortened. */
|
|
35
|
+
texts: string[];
|
|
36
|
+
/** DECSACE-wrapped rectangle batch to emit after the rows, or `""` if none. */
|
|
37
|
+
sequence: string;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Plan DECCARA rectangles for a contiguous block of visible rows.
|
|
41
|
+
*
|
|
42
|
+
* `lines[k]` is the final ANSI string for screen row `firstScreenRow + k`
|
|
43
|
+
* (0-based). For each fillable row the trailing background padding is removed
|
|
44
|
+
* (the row's cells are cleared/erased by the caller, then repainted by the
|
|
45
|
+
* rectangle), and vertically adjacent rows with an identical left/right/bg span
|
|
46
|
+
* coalesce into one rectangle. Rectangles are emitted only when they save more
|
|
47
|
+
* bytes than they cost, so the result never exceeds the original byte count.
|
|
48
|
+
*/
|
|
49
|
+
export declare function planDeccaraFills(lines: string[], width: number, firstScreenRow?: number): DeccaraPlan;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { AutocompleteProvider } from "./autocomplete";
|
|
2
|
+
import type { Component } from "./tui";
|
|
3
|
+
/**
|
|
4
|
+
* Interface for custom editor components.
|
|
5
|
+
*
|
|
6
|
+
* This allows extensions to provide their own editor implementation
|
|
7
|
+
* (e.g., vim mode, emacs mode, custom keybindings) while maintaining
|
|
8
|
+
* compatibility with the core application.
|
|
9
|
+
*/
|
|
10
|
+
export interface EditorComponent extends Component {
|
|
11
|
+
/** Get the current text content */
|
|
12
|
+
getText(): string;
|
|
13
|
+
/** Set the text content */
|
|
14
|
+
setText(text: string): void;
|
|
15
|
+
/** Handle raw terminal input (key presses, paste sequences, etc.) */
|
|
16
|
+
handleInput(data: string): void;
|
|
17
|
+
/** Called when user submits (e.g., Enter key) */
|
|
18
|
+
onSubmit?: (text: string) => void;
|
|
19
|
+
/** Called when text changes */
|
|
20
|
+
onChange?: (text: string) => void;
|
|
21
|
+
/** Add text to history for up/down navigation */
|
|
22
|
+
addToHistory?(text: string): void;
|
|
23
|
+
/** Insert text at current cursor position */
|
|
24
|
+
insertTextAtCursor?(text: string): void;
|
|
25
|
+
/**
|
|
26
|
+
* Get text with any markers expanded (e.g., paste markers).
|
|
27
|
+
* Falls back to getText() if not implemented.
|
|
28
|
+
*/
|
|
29
|
+
getExpandedText?(): string;
|
|
30
|
+
/** Set the autocomplete provider */
|
|
31
|
+
setAutocompleteProvider?(provider: AutocompleteProvider): void;
|
|
32
|
+
/** Border color function */
|
|
33
|
+
borderColor?: (str: string) => string;
|
|
34
|
+
/** Set horizontal padding */
|
|
35
|
+
setPaddingX?(padding: number): void;
|
|
36
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fuzzy matching utilities.
|
|
3
|
+
* Matches if all query characters appear in order (not necessarily consecutive).
|
|
4
|
+
* Lower score = better match.
|
|
5
|
+
*/
|
|
6
|
+
export interface FuzzyMatch {
|
|
7
|
+
matches: boolean;
|
|
8
|
+
score: number;
|
|
9
|
+
}
|
|
10
|
+
export declare function fuzzyMatch(query: string, text: string): FuzzyMatch;
|
|
11
|
+
/**
|
|
12
|
+
* Filter and sort items by fuzzy match quality (best matches first).
|
|
13
|
+
* Supports space-separated tokens: all tokens must match.
|
|
14
|
+
*/
|
|
15
|
+
export declare function fuzzyFilter<T>(items: T[], query: string, getText: (item: T) => string): T[];
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export * from "./autocomplete";
|
|
2
|
+
export * from "./components/box";
|
|
3
|
+
export * from "./components/cancellable-loader";
|
|
4
|
+
export * from "./components/editor";
|
|
5
|
+
export * from "./components/image";
|
|
6
|
+
export * from "./components/input";
|
|
7
|
+
export * from "./components/loader";
|
|
8
|
+
export * from "./components/markdown";
|
|
9
|
+
export * from "./components/scroll-view";
|
|
10
|
+
export * from "./components/select-list";
|
|
11
|
+
export * from "./components/settings-list";
|
|
12
|
+
export * from "./components/spacer";
|
|
13
|
+
export * from "./components/tab-bar";
|
|
14
|
+
export * from "./components/text";
|
|
15
|
+
export * from "./components/truncated-text";
|
|
16
|
+
export * from "./deccara";
|
|
17
|
+
export type * from "./editor-component";
|
|
18
|
+
export * from "./fuzzy";
|
|
19
|
+
export * from "./keybindings";
|
|
20
|
+
export * from "./keys";
|
|
21
|
+
export * from "./kitty-graphics";
|
|
22
|
+
export * from "./stdin-buffer";
|
|
23
|
+
export type * from "./symbols";
|
|
24
|
+
export * from "./terminal";
|
|
25
|
+
export * from "./terminal-capabilities";
|
|
26
|
+
export * from "./ttyid";
|
|
27
|
+
export * from "./tui";
|
|
28
|
+
export * from "./utils";
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { type KeyId } from "./keys";
|
|
2
|
+
/**
|
|
3
|
+
* Global keybinding registry.
|
|
4
|
+
* Downstream packages can add keybindings via declaration merging.
|
|
5
|
+
*/
|
|
6
|
+
export interface Keybindings {
|
|
7
|
+
"tui.editor.cursorUp": true;
|
|
8
|
+
"tui.editor.cursorDown": true;
|
|
9
|
+
"tui.editor.cursorLeft": true;
|
|
10
|
+
"tui.editor.cursorRight": true;
|
|
11
|
+
"tui.editor.cursorWordLeft": true;
|
|
12
|
+
"tui.editor.cursorWordRight": true;
|
|
13
|
+
"tui.editor.cursorLineStart": true;
|
|
14
|
+
"tui.editor.cursorLineEnd": true;
|
|
15
|
+
"tui.editor.jumpForward": true;
|
|
16
|
+
"tui.editor.jumpBackward": true;
|
|
17
|
+
"tui.editor.pageUp": true;
|
|
18
|
+
"tui.editor.pageDown": true;
|
|
19
|
+
"tui.editor.deleteCharBackward": true;
|
|
20
|
+
"tui.editor.deleteCharForward": true;
|
|
21
|
+
"tui.editor.deleteWordBackward": true;
|
|
22
|
+
"tui.editor.deleteWordForward": true;
|
|
23
|
+
"tui.editor.deleteToLineStart": true;
|
|
24
|
+
"tui.editor.deleteToLineEnd": true;
|
|
25
|
+
"tui.editor.yank": true;
|
|
26
|
+
"tui.editor.yankPop": true;
|
|
27
|
+
"tui.editor.undo": true;
|
|
28
|
+
"tui.input.newLine": true;
|
|
29
|
+
"tui.input.submit": true;
|
|
30
|
+
"tui.input.tab": true;
|
|
31
|
+
"tui.input.copy": true;
|
|
32
|
+
"tui.select.up": true;
|
|
33
|
+
"tui.select.down": true;
|
|
34
|
+
"tui.select.pageUp": true;
|
|
35
|
+
"tui.select.pageDown": true;
|
|
36
|
+
"tui.select.confirm": true;
|
|
37
|
+
"tui.select.cancel": true;
|
|
38
|
+
}
|
|
39
|
+
export type Keybinding = keyof Keybindings;
|
|
40
|
+
export type { KeyId };
|
|
41
|
+
export interface KeybindingDefinition {
|
|
42
|
+
defaultKeys: KeyId | KeyId[];
|
|
43
|
+
description?: string;
|
|
44
|
+
}
|
|
45
|
+
export type KeybindingDefinitions = Record<string, KeybindingDefinition>;
|
|
46
|
+
export type KeybindingsConfig = Record<string, KeyId | KeyId[] | undefined>;
|
|
47
|
+
export declare const TUI_KEYBINDINGS: {
|
|
48
|
+
readonly "tui.editor.cursorUp": {
|
|
49
|
+
readonly defaultKeys: "up";
|
|
50
|
+
readonly description: "Move cursor up";
|
|
51
|
+
};
|
|
52
|
+
readonly "tui.editor.cursorDown": {
|
|
53
|
+
readonly defaultKeys: "down";
|
|
54
|
+
readonly description: "Move cursor down";
|
|
55
|
+
};
|
|
56
|
+
readonly "tui.editor.cursorLeft": {
|
|
57
|
+
readonly defaultKeys: ["left", "ctrl+b"];
|
|
58
|
+
readonly description: "Move cursor left";
|
|
59
|
+
};
|
|
60
|
+
readonly "tui.editor.cursorRight": {
|
|
61
|
+
readonly defaultKeys: ["right", "ctrl+f"];
|
|
62
|
+
readonly description: "Move cursor right";
|
|
63
|
+
};
|
|
64
|
+
readonly "tui.editor.cursorWordLeft": {
|
|
65
|
+
readonly defaultKeys: ["alt+left", "ctrl+left", "alt+b"];
|
|
66
|
+
readonly description: "Move cursor word left";
|
|
67
|
+
};
|
|
68
|
+
readonly "tui.editor.cursorWordRight": {
|
|
69
|
+
readonly defaultKeys: ["alt+right", "ctrl+right", "alt+f"];
|
|
70
|
+
readonly description: "Move cursor word right";
|
|
71
|
+
};
|
|
72
|
+
readonly "tui.editor.cursorLineStart": {
|
|
73
|
+
readonly defaultKeys: ["home", "ctrl+a"];
|
|
74
|
+
readonly description: "Move to line start";
|
|
75
|
+
};
|
|
76
|
+
readonly "tui.editor.cursorLineEnd": {
|
|
77
|
+
readonly defaultKeys: ["end", "ctrl+e"];
|
|
78
|
+
readonly description: "Move to line end";
|
|
79
|
+
};
|
|
80
|
+
readonly "tui.editor.jumpForward": {
|
|
81
|
+
readonly defaultKeys: "ctrl+]";
|
|
82
|
+
readonly description: "Jump forward to character";
|
|
83
|
+
};
|
|
84
|
+
readonly "tui.editor.jumpBackward": {
|
|
85
|
+
readonly defaultKeys: "ctrl+alt+]";
|
|
86
|
+
readonly description: "Jump backward to character";
|
|
87
|
+
};
|
|
88
|
+
readonly "tui.editor.pageUp": {
|
|
89
|
+
readonly defaultKeys: "pageUp";
|
|
90
|
+
readonly description: "Page up";
|
|
91
|
+
};
|
|
92
|
+
readonly "tui.editor.pageDown": {
|
|
93
|
+
readonly defaultKeys: "pageDown";
|
|
94
|
+
readonly description: "Page down";
|
|
95
|
+
};
|
|
96
|
+
readonly "tui.editor.deleteCharBackward": {
|
|
97
|
+
readonly defaultKeys: "backspace";
|
|
98
|
+
readonly description: "Delete character backward";
|
|
99
|
+
};
|
|
100
|
+
readonly "tui.editor.deleteCharForward": {
|
|
101
|
+
readonly defaultKeys: ["delete", "ctrl+d"];
|
|
102
|
+
readonly description: "Delete character forward";
|
|
103
|
+
};
|
|
104
|
+
readonly "tui.editor.deleteWordBackward": {
|
|
105
|
+
readonly defaultKeys: ["ctrl+w", "alt+backspace", "ctrl+backspace"];
|
|
106
|
+
readonly description: "Delete word backward";
|
|
107
|
+
};
|
|
108
|
+
readonly "tui.editor.deleteWordForward": {
|
|
109
|
+
readonly defaultKeys: ["alt+delete", "alt+d"];
|
|
110
|
+
readonly description: "Delete word forward";
|
|
111
|
+
};
|
|
112
|
+
readonly "tui.editor.deleteToLineStart": {
|
|
113
|
+
readonly defaultKeys: "ctrl+u";
|
|
114
|
+
readonly description: "Delete to line start";
|
|
115
|
+
};
|
|
116
|
+
readonly "tui.editor.deleteToLineEnd": {
|
|
117
|
+
readonly defaultKeys: "ctrl+k";
|
|
118
|
+
readonly description: "Delete to line end";
|
|
119
|
+
};
|
|
120
|
+
readonly "tui.editor.yank": {
|
|
121
|
+
readonly defaultKeys: "ctrl+y";
|
|
122
|
+
readonly description: "Yank";
|
|
123
|
+
};
|
|
124
|
+
readonly "tui.editor.yankPop": {
|
|
125
|
+
readonly defaultKeys: "alt+y";
|
|
126
|
+
readonly description: "Yank pop";
|
|
127
|
+
};
|
|
128
|
+
readonly "tui.editor.undo": {
|
|
129
|
+
readonly defaultKeys: ["ctrl+-", "ctrl+_"];
|
|
130
|
+
readonly description: "Undo";
|
|
131
|
+
};
|
|
132
|
+
readonly "tui.input.newLine": {
|
|
133
|
+
readonly defaultKeys: "shift+enter";
|
|
134
|
+
readonly description: "Insert newline";
|
|
135
|
+
};
|
|
136
|
+
readonly "tui.input.submit": {
|
|
137
|
+
readonly defaultKeys: "enter";
|
|
138
|
+
readonly description: "Submit input";
|
|
139
|
+
};
|
|
140
|
+
readonly "tui.input.tab": {
|
|
141
|
+
readonly defaultKeys: "tab";
|
|
142
|
+
readonly description: "Tab / autocomplete";
|
|
143
|
+
};
|
|
144
|
+
readonly "tui.input.copy": {
|
|
145
|
+
readonly defaultKeys: "ctrl+c";
|
|
146
|
+
readonly description: "Copy selection";
|
|
147
|
+
};
|
|
148
|
+
readonly "tui.select.up": {
|
|
149
|
+
readonly defaultKeys: "up";
|
|
150
|
+
readonly description: "Move selection up";
|
|
151
|
+
};
|
|
152
|
+
readonly "tui.select.down": {
|
|
153
|
+
readonly defaultKeys: "down";
|
|
154
|
+
readonly description: "Move selection down";
|
|
155
|
+
};
|
|
156
|
+
readonly "tui.select.pageUp": {
|
|
157
|
+
readonly defaultKeys: "pageUp";
|
|
158
|
+
readonly description: "Selection page up";
|
|
159
|
+
};
|
|
160
|
+
readonly "tui.select.pageDown": {
|
|
161
|
+
readonly defaultKeys: "pageDown";
|
|
162
|
+
readonly description: "Selection page down";
|
|
163
|
+
};
|
|
164
|
+
readonly "tui.select.confirm": {
|
|
165
|
+
readonly defaultKeys: "enter";
|
|
166
|
+
readonly description: "Confirm selection";
|
|
167
|
+
};
|
|
168
|
+
readonly "tui.select.cancel": {
|
|
169
|
+
readonly defaultKeys: ["escape", "ctrl+c"];
|
|
170
|
+
readonly description: "Cancel selection";
|
|
171
|
+
};
|
|
172
|
+
};
|
|
173
|
+
export interface KeybindingConflict {
|
|
174
|
+
key: KeyId;
|
|
175
|
+
keybindings: string[];
|
|
176
|
+
}
|
|
177
|
+
export declare class KeybindingsManager {
|
|
178
|
+
#private;
|
|
179
|
+
constructor(definitions: KeybindingDefinitions, userBindings?: KeybindingsConfig);
|
|
180
|
+
matches(data: string, keybinding: Keybinding): boolean;
|
|
181
|
+
getKeys(keybinding: Keybinding): KeyId[];
|
|
182
|
+
getDefinition(keybinding: Keybinding): KeybindingDefinition;
|
|
183
|
+
getConflicts(): KeybindingConflict[];
|
|
184
|
+
setUserBindings(userBindings: KeybindingsConfig): void;
|
|
185
|
+
getUserBindings(): KeybindingsConfig;
|
|
186
|
+
getResolvedBindings(): KeybindingsConfig;
|
|
187
|
+
}
|
|
188
|
+
export declare function setKeybindings(keybindings: KeybindingsManager): void;
|
|
189
|
+
export declare function getKeybindings(): KeybindingsManager;
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Keyboard input handling for terminal applications.
|
|
3
|
+
*
|
|
4
|
+
* Supports both legacy terminal sequences and Kitty keyboard protocol.
|
|
5
|
+
* See: https://sw.kovidgoyal.net/kitty/keyboard-protocol/
|
|
6
|
+
* Reference: https://github.com/sst/opentui/blob/7da92b4088aebfe27b9f691c04163a48821e49fd/packages/core/src/lib/parse.keypress.ts
|
|
7
|
+
*
|
|
8
|
+
* Symbol keys are also supported, however some ctrl+symbol combos
|
|
9
|
+
* overlap with ASCII codes, e.g. ctrl+[ = ESC.
|
|
10
|
+
* See: https://sw.kovidgoyal.net/kitty/keyboard-protocol/#legacy-ctrl-mapping-of-ascii-keys
|
|
11
|
+
* Those can still be * used for ctrl+shift combos
|
|
12
|
+
*
|
|
13
|
+
* API:
|
|
14
|
+
* - matchesKey(data, keyId) - Check if input matches a key identifier
|
|
15
|
+
* - parseKey(data) - Parse input and return the key identifier
|
|
16
|
+
* - Key - Helper object for creating typed key identifiers
|
|
17
|
+
* - setKittyProtocolActive(active) - Set global Kitty protocol state
|
|
18
|
+
* - isKittyProtocolActive() - Query global Kitty protocol state
|
|
19
|
+
*/
|
|
20
|
+
import type { KeyEventType } from "@prometheus-ai/natives";
|
|
21
|
+
declare function isWindowsTerminalSession(): boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Raw 0x08 (BS) is ambiguous in legacy terminals.
|
|
24
|
+
*
|
|
25
|
+
* - Windows Terminal uses it for Ctrl+Backspace.
|
|
26
|
+
* - Some legacy terminals and tmux setups send it for plain Backspace.
|
|
27
|
+
*
|
|
28
|
+
* Prefer explicit Kitty / CSI-u / modifyOtherKeys sequences whenever they are
|
|
29
|
+
* available. Fall back to a Windows Terminal heuristic only for raw BS bytes.
|
|
30
|
+
*/
|
|
31
|
+
declare function matchesRawBackspace(data: string, expectedModifier: number): boolean;
|
|
32
|
+
export { isWindowsTerminalSession, matchesRawBackspace };
|
|
33
|
+
/**
|
|
34
|
+
* Set the global Kitty keyboard protocol state.
|
|
35
|
+
* Called by ProcessTerminal after detecting protocol support.
|
|
36
|
+
*/
|
|
37
|
+
export declare function setKittyProtocolActive(active: boolean): void;
|
|
38
|
+
/**
|
|
39
|
+
* Query whether Kitty keyboard protocol is currently active.
|
|
40
|
+
*/
|
|
41
|
+
export declare function isKittyProtocolActive(): boolean;
|
|
42
|
+
type Letter = "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | "j" | "k" | "l" | "m" | "n" | "o" | "p" | "q" | "r" | "s" | "t" | "u" | "v" | "w" | "x" | "y" | "z";
|
|
43
|
+
type Digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9";
|
|
44
|
+
type SymbolKey = "`" | "-" | "=" | "[" | "]" | "\\" | ";" | "'" | "," | "." | "/" | "!" | "@" | "#" | "$" | "%" | "^" | "&" | "*" | "(" | ")" | "_" | "+" | "|" | "~" | "{" | "}" | ":" | "<" | ">" | "?";
|
|
45
|
+
type SpecialKey = "escape" | "esc" | "enter" | "return" | "tab" | "space" | "backspace" | "delete" | "insert" | "clear" | "home" | "end" | "pageUp" | "pageDown" | "up" | "down" | "left" | "right" | "f1" | "f2" | "f3" | "f4" | "f5" | "f6" | "f7" | "f8" | "f9" | "f10" | "f11" | "f12";
|
|
46
|
+
type BaseKey = Letter | Digit | SymbolKey | SpecialKey;
|
|
47
|
+
type ModifierName = "ctrl" | "shift" | "alt" | "super";
|
|
48
|
+
type ModifiedKeyId<Key extends string, RemainingModifiers extends ModifierName = ModifierName> = {
|
|
49
|
+
[M in RemainingModifiers]: `${M}+${Key}` | `${M}+${ModifiedKeyId<Key, Exclude<RemainingModifiers, M>>}`;
|
|
50
|
+
}[RemainingModifiers];
|
|
51
|
+
/**
|
|
52
|
+
* Union type of all valid key identifiers.
|
|
53
|
+
* Provides autocomplete and catches typos at compile time.
|
|
54
|
+
*/
|
|
55
|
+
export type KeyId = BaseKey | ModifiedKeyId<BaseKey>;
|
|
56
|
+
/**
|
|
57
|
+
* Typed helper for constructing key identifiers with autocomplete.
|
|
58
|
+
*
|
|
59
|
+
* The runtime values are just the canonical key-name strings (so `Key.enter`
|
|
60
|
+
* is literally `"enter"`); the value of `Key` over a bag of magic strings is
|
|
61
|
+
* that each property is typed to the exact `KeyId` literal it produces and the
|
|
62
|
+
* modifier methods return precisely-typed concatenations (e.g. `Key.ctrl("c")`
|
|
63
|
+
* is `"ctrl+c"`, not just `string`). This mirrors the upstream
|
|
64
|
+
* `@mariozechner/pi-tui` `Key` export verbatim so plugins built against any
|
|
65
|
+
* scope alias (`@mariozechner`, `@earendil-works`, `@prometheus`) keep working
|
|
66
|
+
* once the specifier shim remaps them to this package.
|
|
67
|
+
*/
|
|
68
|
+
export declare const Key: {
|
|
69
|
+
readonly escape: "escape";
|
|
70
|
+
readonly esc: "esc";
|
|
71
|
+
readonly enter: "enter";
|
|
72
|
+
readonly return: "return";
|
|
73
|
+
readonly tab: "tab";
|
|
74
|
+
readonly space: "space";
|
|
75
|
+
readonly backspace: "backspace";
|
|
76
|
+
readonly delete: "delete";
|
|
77
|
+
readonly insert: "insert";
|
|
78
|
+
readonly clear: "clear";
|
|
79
|
+
readonly home: "home";
|
|
80
|
+
readonly end: "end";
|
|
81
|
+
readonly pageUp: "pageUp";
|
|
82
|
+
readonly pageDown: "pageDown";
|
|
83
|
+
readonly up: "up";
|
|
84
|
+
readonly down: "down";
|
|
85
|
+
readonly left: "left";
|
|
86
|
+
readonly right: "right";
|
|
87
|
+
readonly f1: "f1";
|
|
88
|
+
readonly f2: "f2";
|
|
89
|
+
readonly f3: "f3";
|
|
90
|
+
readonly f4: "f4";
|
|
91
|
+
readonly f5: "f5";
|
|
92
|
+
readonly f6: "f6";
|
|
93
|
+
readonly f7: "f7";
|
|
94
|
+
readonly f8: "f8";
|
|
95
|
+
readonly f9: "f9";
|
|
96
|
+
readonly f10: "f10";
|
|
97
|
+
readonly f11: "f11";
|
|
98
|
+
readonly f12: "f12";
|
|
99
|
+
readonly backtick: "`";
|
|
100
|
+
readonly hyphen: "-";
|
|
101
|
+
readonly equals: "=";
|
|
102
|
+
readonly leftbracket: "[";
|
|
103
|
+
readonly rightbracket: "]";
|
|
104
|
+
readonly backslash: "\\";
|
|
105
|
+
readonly semicolon: ";";
|
|
106
|
+
readonly quote: "'";
|
|
107
|
+
readonly comma: ",";
|
|
108
|
+
readonly period: ".";
|
|
109
|
+
readonly slash: "/";
|
|
110
|
+
readonly exclamation: "!";
|
|
111
|
+
readonly at: "@";
|
|
112
|
+
readonly hash: "#";
|
|
113
|
+
readonly dollar: "$";
|
|
114
|
+
readonly percent: "%";
|
|
115
|
+
readonly caret: "^";
|
|
116
|
+
readonly ampersand: "&";
|
|
117
|
+
readonly asterisk: "*";
|
|
118
|
+
readonly leftparen: "(";
|
|
119
|
+
readonly rightparen: ")";
|
|
120
|
+
readonly underscore: "_";
|
|
121
|
+
readonly plus: "+";
|
|
122
|
+
readonly pipe: "|";
|
|
123
|
+
readonly tilde: "~";
|
|
124
|
+
readonly leftbrace: "{";
|
|
125
|
+
readonly rightbrace: "}";
|
|
126
|
+
readonly colon: ":";
|
|
127
|
+
readonly lessthan: "<";
|
|
128
|
+
readonly greaterthan: ">";
|
|
129
|
+
readonly question: "?";
|
|
130
|
+
readonly ctrl: <K extends BaseKey>(key: K) => `ctrl+${K}`;
|
|
131
|
+
readonly shift: <K extends BaseKey>(key: K) => `shift+${K}`;
|
|
132
|
+
readonly alt: <K extends BaseKey>(key: K) => `alt+${K}`;
|
|
133
|
+
readonly super: <K extends BaseKey>(key: K) => `super+${K}`;
|
|
134
|
+
readonly ctrlShift: <K extends BaseKey>(key: K) => `ctrl+shift+${K}`;
|
|
135
|
+
readonly shiftCtrl: <K extends BaseKey>(key: K) => `shift+ctrl+${K}`;
|
|
136
|
+
readonly ctrlAlt: <K extends BaseKey>(key: K) => `ctrl+alt+${K}`;
|
|
137
|
+
readonly altCtrl: <K extends BaseKey>(key: K) => `alt+ctrl+${K}`;
|
|
138
|
+
readonly shiftAlt: <K extends BaseKey>(key: K) => `shift+alt+${K}`;
|
|
139
|
+
readonly altShift: <K extends BaseKey>(key: K) => `alt+shift+${K}`;
|
|
140
|
+
readonly ctrlSuper: <K extends BaseKey>(key: K) => `ctrl+super+${K}`;
|
|
141
|
+
readonly superCtrl: <K extends BaseKey>(key: K) => `super+ctrl+${K}`;
|
|
142
|
+
readonly shiftSuper: <K extends BaseKey>(key: K) => `shift+super+${K}`;
|
|
143
|
+
readonly superShift: <K extends BaseKey>(key: K) => `super+shift+${K}`;
|
|
144
|
+
readonly altSuper: <K extends BaseKey>(key: K) => `alt+super+${K}`;
|
|
145
|
+
readonly superAlt: <K extends BaseKey>(key: K) => `super+alt+${K}`;
|
|
146
|
+
readonly ctrlShiftAlt: <K extends BaseKey>(key: K) => `ctrl+shift+alt+${K}`;
|
|
147
|
+
readonly ctrlShiftSuper: <K extends BaseKey>(key: K) => `ctrl+shift+super+${K}`;
|
|
148
|
+
};
|
|
149
|
+
interface ParsedKittySequence {
|
|
150
|
+
codepoint: number;
|
|
151
|
+
shiftedKey?: number;
|
|
152
|
+
baseLayoutKey?: number;
|
|
153
|
+
modifier: number;
|
|
154
|
+
eventType?: KeyEventType;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Check if the input is a key release event.
|
|
158
|
+
* Only meaningful when Kitty keyboard protocol with flag 2 is active.
|
|
159
|
+
* Returns false if Kitty protocol is not active.
|
|
160
|
+
*/
|
|
161
|
+
export declare function isKeyRelease(data: string): boolean;
|
|
162
|
+
/**
|
|
163
|
+
* Check if the input is a key repeat event.
|
|
164
|
+
* Only meaningful when Kitty keyboard protocol with flag 2 is active.
|
|
165
|
+
* Returns false if Kitty protocol is not active.
|
|
166
|
+
*/
|
|
167
|
+
export declare function isKeyRepeat(data: string): boolean;
|
|
168
|
+
export declare function parseKittySequence(data: string): ParsedKittySequence | null;
|
|
169
|
+
/**
|
|
170
|
+
* Extract printable text from raw terminal input.
|
|
171
|
+
*
|
|
172
|
+
* Handles Kitty CSI-u text-producing keys so text-entry components can treat
|
|
173
|
+
* keypad digits, keypad operators, and shifted symbols the same as direct character input.
|
|
174
|
+
*/
|
|
175
|
+
export declare function extractPrintableText(data: string): string | undefined;
|
|
176
|
+
/**
|
|
177
|
+
* Decode terminal input into the printable character it represents.
|
|
178
|
+
*
|
|
179
|
+
* Tries Kitty CSI-u first, then falls back to xterm modifyOtherKeys. Returns
|
|
180
|
+
* undefined for control sequences and modifier-only events.
|
|
181
|
+
*/
|
|
182
|
+
export declare function decodePrintableKey(data: string): string | undefined;
|
|
183
|
+
/**
|
|
184
|
+
* Match input data against a key identifier string.
|
|
185
|
+
*
|
|
186
|
+
* Supported key identifiers:
|
|
187
|
+
* - Single keys: "escape", "tab", "enter", "backspace", "delete", "home", "end", "space"
|
|
188
|
+
* - Arrow keys: "up", "down", "left", "right"
|
|
189
|
+
* - Ctrl combinations: "ctrl+c", "ctrl+z", etc.
|
|
190
|
+
* - Shift combinations: "shift+tab", "shift+enter"
|
|
191
|
+
* - Alt combinations: "alt+enter", "alt+backspace"
|
|
192
|
+
* - Combined modifiers: "shift+ctrl+p", "ctrl+alt+x"
|
|
193
|
+
*
|
|
194
|
+
* Use the Key helper for autocomplete: Key.ctrl("c"), Key.escape, Key.ctrlShift("p")
|
|
195
|
+
*
|
|
196
|
+
* @param data - Raw input data from terminal
|
|
197
|
+
* @param keyId - Key identifier (e.g., "ctrl+c", "escape", Key.ctrl("c"))
|
|
198
|
+
*/
|
|
199
|
+
export declare function matchesKey(data: string, keyId: KeyId): boolean;
|
|
200
|
+
/**
|
|
201
|
+
* Parse terminal input and return a normalized key identifier.
|
|
202
|
+
*
|
|
203
|
+
* Returns key names like "escape", "ctrl+c", "shift+tab", "alt+enter".
|
|
204
|
+
* Returns undefined if the input is not a recognized key sequence.
|
|
205
|
+
*
|
|
206
|
+
* @param data - Raw input data from terminal
|
|
207
|
+
*/
|
|
208
|
+
export declare function parseKey(data: string): string | undefined;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ring buffer for Emacs-style kill/yank operations.
|
|
3
|
+
*
|
|
4
|
+
* Tracks killed (deleted) text entries. Consecutive kills can accumulate
|
|
5
|
+
* into a single entry. Supports yank (paste most recent) and yank-pop
|
|
6
|
+
* (cycle through older entries).
|
|
7
|
+
*/
|
|
8
|
+
export declare class KillRing {
|
|
9
|
+
#private;
|
|
10
|
+
/**
|
|
11
|
+
* Add text to the kill ring.
|
|
12
|
+
*
|
|
13
|
+
* @param text - The killed text to add
|
|
14
|
+
* @param opts - Push options
|
|
15
|
+
* @param opts.prepend - If accumulating, prepend (backward deletion) or append (forward deletion)
|
|
16
|
+
* @param opts.accumulate - Merge with the most recent entry instead of creating a new one
|
|
17
|
+
*/
|
|
18
|
+
push(text: string, opts: {
|
|
19
|
+
prepend: boolean;
|
|
20
|
+
accumulate?: boolean;
|
|
21
|
+
}): void;
|
|
22
|
+
/** Get most recent entry without modifying the ring. */
|
|
23
|
+
peek(): string | undefined;
|
|
24
|
+
/** Move last entry to front (for yank-pop cycling). */
|
|
25
|
+
rotate(): void;
|
|
26
|
+
get length(): number;
|
|
27
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/** Kitty Unicode placeholder base character (U+10EEEE, Plane 16 PUA). */
|
|
2
|
+
export declare const KITTY_PLACEHOLDER = "\uDBFB\uDEEE";
|
|
3
|
+
/** Largest row/column index expressible with the diacritic table (one cell each). */
|
|
4
|
+
export declare const KITTY_PLACEHOLDER_MAX_CELLS: number;
|
|
5
|
+
export type KittyTransmissionMedium = "direct" | "temp-file";
|
|
6
|
+
export interface KittyGraphicsFeatures {
|
|
7
|
+
/** Display images via Unicode placeholders instead of direct `a=p` placement. */
|
|
8
|
+
unicodePlaceholders: boolean;
|
|
9
|
+
/** How image data reaches the terminal: in-band base64 or a temp file. */
|
|
10
|
+
transmissionMedium: KittyTransmissionMedium;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Whether the detected terminal renders Kitty Unicode placeholders (`U=1` +
|
|
14
|
+
* U+10EEEE with row/column diacritics).
|
|
15
|
+
*
|
|
16
|
+
* Only `kitty` (the protocol's origin) and `ghostty` ship a working
|
|
17
|
+
* implementation; WezTerm advertises Kitty graphics but treats placeholder
|
|
18
|
+
* cells as literal PUA glyphs (see wezterm/wezterm#986, "placeholder support"
|
|
19
|
+
* still unchecked), and the tmux/screen fallback can land on any outer
|
|
20
|
+
* terminal. Enabling placeholders on those paths emits a `columns × rows`
|
|
21
|
+
* grid of U+10EEEE per image per frame; the cells render as boxed fallback
|
|
22
|
+
* glyphs and re-emit on every repaint, which is exactly the
|
|
23
|
+
* "stuck/laggy scrolling + ASCII artifact" symptom reported in #1877.
|
|
24
|
+
*
|
|
25
|
+
* `PROMETHEUS_NO_KITTY_PLACEHOLDERS=1` forces off (e.g. for tmux passthrough to a
|
|
26
|
+
* non-supporting outer terminal); `PROMETHEUS_KITTY_PLACEHOLDERS=1` forces on (e.g.
|
|
27
|
+
* for a wezterm nightly that has merged placeholder support).
|
|
28
|
+
*/
|
|
29
|
+
export declare function detectKittyUnicodePlaceholdersSupport(terminalId: string, env?: NodeJS.ProcessEnv): boolean;
|
|
30
|
+
export declare function getKittyGraphics(): Readonly<KittyGraphicsFeatures>;
|
|
31
|
+
export declare function setKittyGraphics(partial: Partial<KittyGraphicsFeatures>): void;
|
|
32
|
+
/**
|
|
33
|
+
* Whether temp-file transmission may be promoted at runtime: forced via env,
|
|
34
|
+
* disabled via env, otherwise auto (local sessions only — a temp file written
|
|
35
|
+
* locally is not readable by a terminal on the far side of an SSH link).
|
|
36
|
+
*/
|
|
37
|
+
export declare function kittyTempFileAllowed(): boolean;
|
|
38
|
+
/** Whether a `columns`×`rows` placeholder grid fits within the diacritic table. */
|
|
39
|
+
export declare function kittyPlaceholdersFit(columns: number, rows: number): boolean;
|
|
40
|
+
/** True when the base64 payload is a PNG (kitty `f=100` / temp-file path only). */
|
|
41
|
+
export declare function isPngBase64(base64Data: string): boolean;
|
|
42
|
+
/**
|
|
43
|
+
* Virtual placement APC (`a=p,U=1`): tells the terminal that placeholder cells
|
|
44
|
+
* carrying image id `i` should display the transmitted image, scaled to fit the
|
|
45
|
+
* `c`×`r` cell box. Re-emitting with a stable `placementId` replaces in place.
|
|
46
|
+
*/
|
|
47
|
+
export declare function encodeKittyVirtualPlacement(opts: {
|
|
48
|
+
imageId: number;
|
|
49
|
+
placementId?: number;
|
|
50
|
+
columns: number;
|
|
51
|
+
rows: number;
|
|
52
|
+
}): string;
|
|
53
|
+
/**
|
|
54
|
+
* Build the placeholder cell grid as one string per row. The image id is carried
|
|
55
|
+
* in each row's foreground color and the placement id (if any) in its underline
|
|
56
|
+
* color; every cell names its explicit row+column diacritic (robust to slicing,
|
|
57
|
+
* unlike left-inheritance). Returns exactly `rows` strings.
|
|
58
|
+
*/
|
|
59
|
+
export declare function encodeKittyPlaceholderGrid(opts: {
|
|
60
|
+
imageId: number;
|
|
61
|
+
placementId?: number;
|
|
62
|
+
columns: number;
|
|
63
|
+
rows: number;
|
|
64
|
+
}): string[];
|
|
65
|
+
/**
|
|
66
|
+
* Full placeholder render: the virtual-placement APC prefixes line 0, and every
|
|
67
|
+
* line carries placeholder cells. Returns exactly `rows` lines (no cursor moves).
|
|
68
|
+
*/
|
|
69
|
+
export declare function renderKittyPlaceholderLines(opts: {
|
|
70
|
+
imageId: number;
|
|
71
|
+
placementId?: number;
|
|
72
|
+
columns: number;
|
|
73
|
+
rows: number;
|
|
74
|
+
}): string[];
|
|
75
|
+
/**
|
|
76
|
+
* Transmit a PNG via a temp file (`t=t`): decode the base64 to bytes once, write
|
|
77
|
+
* them to a temp file, and send the base64-encoded file path as payload. Returns
|
|
78
|
+
* the APC string, or `null` on any failure (caller falls back to direct base64).
|
|
79
|
+
*
|
|
80
|
+
* Synchronous filesystem writes are mandated by the synchronous render pipeline
|
|
81
|
+
* (`Image.render` → `renderImage` are sync); there is no async seam here.
|
|
82
|
+
*/
|
|
83
|
+
export declare function encodeKittyTempFileTransmit(base64Png: string, imageId: number): string | null;
|
|
84
|
+
/**
|
|
85
|
+
* Encode a temp-file support probe: write a tiny PNG to a temp file and ask the
|
|
86
|
+
* terminal to query it (`a=q,t=t`). A conforming terminal replies
|
|
87
|
+
* `ESC _ G i=<probeId>;OK ESC \`. Returns the query APC plus a `cleanup` that
|
|
88
|
+
* removes the probe file (best-effort; kitty self-deletes the magic-named file).
|
|
89
|
+
* Returns `null` if the temp file cannot be written.
|
|
90
|
+
*/
|
|
91
|
+
export declare function encodeKittyTempFileProbe(probeId: number): {
|
|
92
|
+
sequence: string;
|
|
93
|
+
cleanup: () => void;
|
|
94
|
+
} | null;
|