@mariozechner/pi-tui 0.32.3 → 0.34.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/README.md +24 -22
- package/dist/components/cancellable-loader.d.ts.map +1 -1
- package/dist/components/cancellable-loader.js +3 -2
- package/dist/components/cancellable-loader.js.map +1 -1
- package/dist/components/editor.d.ts +10 -0
- package/dist/components/editor.d.ts.map +1 -1
- package/dist/components/editor.js +125 -156
- package/dist/components/editor.js.map +1 -1
- package/dist/components/input.d.ts.map +1 -1
- package/dist/components/input.js +36 -49
- package/dist/components/input.js.map +1 -1
- package/dist/components/select-list.d.ts.map +1 -1
- package/dist/components/select-list.js +6 -5
- package/dist/components/select-list.js.map +1 -1
- package/dist/components/settings-list.d.ts.map +1 -1
- package/dist/components/settings-list.js +6 -5
- package/dist/components/settings-list.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/dist/keybindings.d.ts +39 -0
- package/dist/keybindings.d.ts.map +1 -0
- package/dist/keybindings.js +94 -0
- package/dist/keybindings.js.map +1 -0
- package/dist/keys.d.ts +66 -242
- package/dist/keys.d.ts.map +1 -1
- package/dist/keys.js +331 -432
- package/dist/keys.js.map +1 -1
- package/dist/tui.d.ts.map +1 -1
- package/dist/tui.js +2 -2
- package/dist/tui.js.map +1 -1
- package/package.json +1 -1
package/dist/keys.js
CHANGED
|
@@ -1,73 +1,75 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
* The Kitty keyboard protocol sends enhanced escape sequences in the format:
|
|
5
|
-
* \x1b[<codepoint>;<modifier>u
|
|
6
|
-
*
|
|
7
|
-
* Modifier bits (before adding 1 for transmission):
|
|
8
|
-
* - Shift: 1 (value 2)
|
|
9
|
-
* - Alt: 2 (value 3)
|
|
10
|
-
* - Ctrl: 4 (value 5)
|
|
11
|
-
* - Super: 8 (value 9)
|
|
12
|
-
* - Hyper: 16
|
|
13
|
-
* - Meta: 32
|
|
14
|
-
* - Caps_Lock: 64
|
|
15
|
-
* - Num_Lock: 128
|
|
2
|
+
* Keyboard input handling for terminal applications.
|
|
16
3
|
*
|
|
4
|
+
* Supports both legacy terminal sequences and Kitty keyboard protocol.
|
|
17
5
|
* See: https://sw.kovidgoyal.net/kitty/keyboard-protocol/
|
|
18
6
|
*
|
|
19
|
-
*
|
|
20
|
-
* (
|
|
21
|
-
*
|
|
7
|
+
* API:
|
|
8
|
+
* - matchesKey(data, keyId) - Check if input matches a key identifier
|
|
9
|
+
* - parseKey(data) - Parse input and return the key identifier
|
|
10
|
+
* - Key - Helper object for creating typed key identifiers
|
|
22
11
|
*/
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
l: 108,
|
|
33
|
-
o: 111,
|
|
34
|
-
p: 112,
|
|
35
|
-
t: 116,
|
|
36
|
-
u: 117,
|
|
37
|
-
w: 119,
|
|
38
|
-
z: 122,
|
|
12
|
+
/**
|
|
13
|
+
* Helper object for creating typed key identifiers with autocomplete.
|
|
14
|
+
*
|
|
15
|
+
* Usage:
|
|
16
|
+
* - Key.escape, Key.enter, Key.tab, etc. for special keys
|
|
17
|
+
* - Key.ctrl("c"), Key.alt("x") for single modifier
|
|
18
|
+
* - Key.ctrlShift("p"), Key.ctrlAlt("x") for combined modifiers
|
|
19
|
+
*/
|
|
20
|
+
export const Key = {
|
|
39
21
|
// Special keys
|
|
22
|
+
escape: "escape",
|
|
23
|
+
esc: "esc",
|
|
24
|
+
enter: "enter",
|
|
25
|
+
return: "return",
|
|
26
|
+
tab: "tab",
|
|
27
|
+
space: "space",
|
|
28
|
+
backspace: "backspace",
|
|
29
|
+
delete: "delete",
|
|
30
|
+
home: "home",
|
|
31
|
+
end: "end",
|
|
32
|
+
up: "up",
|
|
33
|
+
down: "down",
|
|
34
|
+
left: "left",
|
|
35
|
+
right: "right",
|
|
36
|
+
// Single modifiers
|
|
37
|
+
ctrl: (key) => `ctrl+${key}`,
|
|
38
|
+
shift: (key) => `shift+${key}`,
|
|
39
|
+
alt: (key) => `alt+${key}`,
|
|
40
|
+
// Combined modifiers
|
|
41
|
+
ctrlShift: (key) => `ctrl+shift+${key}`,
|
|
42
|
+
shiftCtrl: (key) => `shift+ctrl+${key}`,
|
|
43
|
+
ctrlAlt: (key) => `ctrl+alt+${key}`,
|
|
44
|
+
altCtrl: (key) => `alt+ctrl+${key}`,
|
|
45
|
+
shiftAlt: (key) => `shift+alt+${key}`,
|
|
46
|
+
altShift: (key) => `alt+shift+${key}`,
|
|
47
|
+
// Triple modifiers
|
|
48
|
+
ctrlShiftAlt: (key) => `ctrl+shift+alt+${key}`,
|
|
49
|
+
};
|
|
50
|
+
// =============================================================================
|
|
51
|
+
// Constants
|
|
52
|
+
// =============================================================================
|
|
53
|
+
const MODIFIERS = {
|
|
54
|
+
shift: 1,
|
|
55
|
+
alt: 2,
|
|
56
|
+
ctrl: 4,
|
|
57
|
+
};
|
|
58
|
+
const LOCK_MASK = 64 + 128; // Caps Lock + Num Lock
|
|
59
|
+
const CODEPOINTS = {
|
|
40
60
|
escape: 27,
|
|
41
61
|
tab: 9,
|
|
42
62
|
enter: 13,
|
|
43
63
|
space: 32,
|
|
44
64
|
backspace: 127,
|
|
65
|
+
kpEnter: 57414, // Numpad Enter (Kitty protocol)
|
|
45
66
|
};
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
alt: 2,
|
|
52
|
-
ctrl: 4,
|
|
53
|
-
super: 8,
|
|
67
|
+
const ARROW_CODEPOINTS = {
|
|
68
|
+
up: -1,
|
|
69
|
+
down: -2,
|
|
70
|
+
right: -3,
|
|
71
|
+
left: -4,
|
|
54
72
|
};
|
|
55
|
-
/**
|
|
56
|
-
* Build a Kitty keyboard protocol sequence for a key with modifier.
|
|
57
|
-
*/
|
|
58
|
-
function kittySequence(codepoint, modifier) {
|
|
59
|
-
return `\x1b[${codepoint};${modifier + 1}u`;
|
|
60
|
-
}
|
|
61
|
-
/**
|
|
62
|
-
* Parse a Kitty keyboard protocol sequence.
|
|
63
|
-
* Handles formats:
|
|
64
|
-
* - \x1b[<codepoint>u (no modifier)
|
|
65
|
-
* - \x1b[<codepoint>;<modifier>u (with modifier)
|
|
66
|
-
* - \x1b[1;<modifier>A/B/C/D (arrow keys with modifier)
|
|
67
|
-
*
|
|
68
|
-
* Returns null if not a valid Kitty sequence.
|
|
69
|
-
*/
|
|
70
|
-
// Virtual codepoints for functional keys (negative to avoid conflicts)
|
|
71
73
|
const FUNCTIONAL_CODEPOINTS = {
|
|
72
74
|
delete: -10,
|
|
73
75
|
insert: -11,
|
|
@@ -77,43 +79,39 @@ const FUNCTIONAL_CODEPOINTS = {
|
|
|
77
79
|
end: -15,
|
|
78
80
|
};
|
|
79
81
|
function parseKittySequence(data) {
|
|
80
|
-
//
|
|
82
|
+
// CSI u format: \x1b[<num>u or \x1b[<num>;<mod>u
|
|
81
83
|
const csiUMatch = data.match(/^\x1b\[(\d+)(?:;(\d+))?u$/);
|
|
82
84
|
if (csiUMatch) {
|
|
83
85
|
const codepoint = parseInt(csiUMatch[1], 10);
|
|
84
86
|
const modValue = csiUMatch[2] ? parseInt(csiUMatch[2], 10) : 1;
|
|
85
87
|
return { codepoint, modifier: modValue - 1 };
|
|
86
88
|
}
|
|
87
|
-
//
|
|
89
|
+
// Arrow keys with modifier: \x1b[1;<mod>A/B/C/D
|
|
88
90
|
const arrowMatch = data.match(/^\x1b\[1;(\d+)([ABCD])$/);
|
|
89
91
|
if (arrowMatch) {
|
|
90
92
|
const modValue = parseInt(arrowMatch[1], 10);
|
|
91
|
-
// Map arrow letters to virtual codepoints for easier matching
|
|
92
93
|
const arrowCodes = { A: -1, B: -2, C: -3, D: -4 };
|
|
93
|
-
|
|
94
|
-
return { codepoint, modifier: modValue - 1 };
|
|
94
|
+
return { codepoint: arrowCodes[arrowMatch[2]], modifier: modValue - 1 };
|
|
95
95
|
}
|
|
96
|
-
//
|
|
97
|
-
// DELETE=3, INSERT=2, PAGEUP=5, PAGEDOWN=6, etc.
|
|
96
|
+
// Functional keys: \x1b[<num>~ or \x1b[<num>;<mod>~
|
|
98
97
|
const funcMatch = data.match(/^\x1b\[(\d+)(?:;(\d+))?~$/);
|
|
99
98
|
if (funcMatch) {
|
|
100
99
|
const keyNum = parseInt(funcMatch[1], 10);
|
|
101
100
|
const modValue = funcMatch[2] ? parseInt(funcMatch[2], 10) : 1;
|
|
102
|
-
// Map functional key numbers to virtual codepoints
|
|
103
101
|
const funcCodes = {
|
|
104
102
|
2: FUNCTIONAL_CODEPOINTS.insert,
|
|
105
103
|
3: FUNCTIONAL_CODEPOINTS.delete,
|
|
106
104
|
5: FUNCTIONAL_CODEPOINTS.pageUp,
|
|
107
105
|
6: FUNCTIONAL_CODEPOINTS.pageDown,
|
|
108
|
-
7: FUNCTIONAL_CODEPOINTS.home,
|
|
109
|
-
8: FUNCTIONAL_CODEPOINTS.end,
|
|
106
|
+
7: FUNCTIONAL_CODEPOINTS.home,
|
|
107
|
+
8: FUNCTIONAL_CODEPOINTS.end,
|
|
110
108
|
};
|
|
111
109
|
const codepoint = funcCodes[keyNum];
|
|
112
110
|
if (codepoint !== undefined) {
|
|
113
111
|
return { codepoint, modifier: modValue - 1 };
|
|
114
112
|
}
|
|
115
113
|
}
|
|
116
|
-
//
|
|
114
|
+
// Home/End with modifier: \x1b[1;<mod>H/F
|
|
117
115
|
const homeEndMatch = data.match(/^\x1b\[1;(\d+)([HF])$/);
|
|
118
116
|
if (homeEndMatch) {
|
|
119
117
|
const modValue = parseInt(homeEndMatch[1], 10);
|
|
@@ -122,381 +120,282 @@ function parseKittySequence(data) {
|
|
|
122
120
|
}
|
|
123
121
|
return null;
|
|
124
122
|
}
|
|
125
|
-
/**
|
|
126
|
-
* Check if a Kitty sequence matches the expected codepoint and modifier,
|
|
127
|
-
* ignoring lock key bits (Caps Lock, Num Lock).
|
|
128
|
-
*/
|
|
129
123
|
function matchesKittySequence(data, expectedCodepoint, expectedModifier) {
|
|
130
124
|
const parsed = parseKittySequence(data);
|
|
131
125
|
if (!parsed)
|
|
132
126
|
return false;
|
|
133
|
-
// Mask out lock bits from both sides for comparison
|
|
134
127
|
const actualMod = parsed.modifier & ~LOCK_MASK;
|
|
135
128
|
const expectedMod = expectedModifier & ~LOCK_MASK;
|
|
136
129
|
return parsed.codepoint === expectedCodepoint && actualMod === expectedMod;
|
|
137
130
|
}
|
|
138
|
-
//
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
*
|
|
165
|
-
*
|
|
166
|
-
*
|
|
167
|
-
*
|
|
131
|
+
// =============================================================================
|
|
132
|
+
// Generic Key Matching
|
|
133
|
+
// =============================================================================
|
|
134
|
+
function rawCtrlChar(letter) {
|
|
135
|
+
const code = letter.toLowerCase().charCodeAt(0) - 96;
|
|
136
|
+
return String.fromCharCode(code);
|
|
137
|
+
}
|
|
138
|
+
function parseKeyId(keyId) {
|
|
139
|
+
const parts = keyId.toLowerCase().split("+");
|
|
140
|
+
const key = parts[parts.length - 1];
|
|
141
|
+
if (!key)
|
|
142
|
+
return null;
|
|
143
|
+
return {
|
|
144
|
+
key,
|
|
145
|
+
ctrl: parts.includes("ctrl"),
|
|
146
|
+
shift: parts.includes("shift"),
|
|
147
|
+
alt: parts.includes("alt"),
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Match input data against a key identifier string.
|
|
152
|
+
*
|
|
153
|
+
* Supported key identifiers:
|
|
154
|
+
* - Single keys: "escape", "tab", "enter", "backspace", "delete", "home", "end", "space"
|
|
155
|
+
* - Arrow keys: "up", "down", "left", "right"
|
|
156
|
+
* - Ctrl combinations: "ctrl+c", "ctrl+z", etc.
|
|
157
|
+
* - Shift combinations: "shift+tab", "shift+enter"
|
|
158
|
+
* - Alt combinations: "alt+enter", "alt+backspace"
|
|
159
|
+
* - Combined modifiers: "shift+ctrl+p", "ctrl+alt+x"
|
|
160
|
+
*
|
|
161
|
+
* Use the Key helper for autocomplete: Key.ctrl("c"), Key.escape, Key.ctrlShift("p")
|
|
162
|
+
*
|
|
163
|
+
* @param data - Raw input data from terminal
|
|
164
|
+
* @param keyId - Key identifier (e.g., "ctrl+c", "escape", Key.ctrl("c"))
|
|
168
165
|
*/
|
|
169
|
-
export function
|
|
170
|
-
|
|
166
|
+
export function matchesKey(data, keyId) {
|
|
167
|
+
const parsed = parseKeyId(keyId);
|
|
168
|
+
if (!parsed)
|
|
171
169
|
return false;
|
|
172
|
-
const
|
|
173
|
-
|
|
174
|
-
if (
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
*/
|
|
311
|
-
export function isCtrlU(data) {
|
|
312
|
-
return data === RAW.CTRL_U || data === Keys.CTRL_U || matchesKittySequence(data, CODEPOINTS.u, MODIFIERS.ctrl);
|
|
313
|
-
}
|
|
314
|
-
/**
|
|
315
|
-
* Check if input matches Ctrl+W (raw byte or Kitty protocol).
|
|
316
|
-
* Ignores lock key bits.
|
|
317
|
-
*/
|
|
318
|
-
export function isCtrlW(data) {
|
|
319
|
-
return data === RAW.CTRL_W || data === Keys.CTRL_W || matchesKittySequence(data, CODEPOINTS.w, MODIFIERS.ctrl);
|
|
320
|
-
}
|
|
321
|
-
/**
|
|
322
|
-
* Check if input matches Ctrl+Z (raw byte or Kitty protocol).
|
|
323
|
-
* Ignores lock key bits.
|
|
324
|
-
*/
|
|
325
|
-
export function isCtrlZ(data) {
|
|
326
|
-
return data === RAW.CTRL_Z || data === Keys.CTRL_Z || matchesKittySequence(data, CODEPOINTS.z, MODIFIERS.ctrl);
|
|
327
|
-
}
|
|
328
|
-
/**
|
|
329
|
-
* Check if input matches Alt+Backspace (legacy or Kitty protocol).
|
|
330
|
-
* Ignores lock key bits.
|
|
331
|
-
*/
|
|
332
|
-
export function isAltBackspace(data) {
|
|
333
|
-
return (data === RAW.ALT_BACKSPACE ||
|
|
334
|
-
data === Keys.ALT_BACKSPACE ||
|
|
335
|
-
matchesKittySequence(data, CODEPOINTS.backspace, MODIFIERS.alt));
|
|
336
|
-
}
|
|
337
|
-
/**
|
|
338
|
-
* Check if input matches Shift+Tab (legacy or Kitty protocol).
|
|
339
|
-
* Ignores lock key bits.
|
|
340
|
-
*/
|
|
341
|
-
export function isShiftTab(data) {
|
|
342
|
-
return (data === RAW.SHIFT_TAB || data === Keys.SHIFT_TAB || matchesKittySequence(data, CODEPOINTS.tab, MODIFIERS.shift));
|
|
343
|
-
}
|
|
344
|
-
/**
|
|
345
|
-
* Check if input matches the Escape key (raw byte or Kitty protocol).
|
|
346
|
-
* Raw: \x1b (single byte)
|
|
347
|
-
* Kitty: \x1b[27u (codepoint 27 = escape)
|
|
348
|
-
* Ignores lock key bits.
|
|
349
|
-
*/
|
|
350
|
-
export function isEscape(data) {
|
|
351
|
-
return data === "\x1b" || data === `\x1b[${CODEPOINTS.escape}u` || matchesKittySequence(data, CODEPOINTS.escape, 0);
|
|
352
|
-
}
|
|
353
|
-
// Arrow key virtual codepoints (negative to avoid conflicts with real codepoints)
|
|
354
|
-
const ARROW_CODEPOINTS = {
|
|
355
|
-
up: -1,
|
|
356
|
-
down: -2,
|
|
357
|
-
right: -3,
|
|
358
|
-
left: -4,
|
|
359
|
-
};
|
|
360
|
-
/**
|
|
361
|
-
* Check if input matches Arrow Up key.
|
|
362
|
-
* Handles both legacy (\x1b[A) and Kitty protocol with modifiers.
|
|
363
|
-
*/
|
|
364
|
-
export function isArrowUp(data) {
|
|
365
|
-
return data === "\x1b[A" || matchesKittySequence(data, ARROW_CODEPOINTS.up, 0);
|
|
366
|
-
}
|
|
367
|
-
/**
|
|
368
|
-
* Check if input matches Arrow Down key.
|
|
369
|
-
* Handles both legacy (\x1b[B) and Kitty protocol with modifiers.
|
|
370
|
-
*/
|
|
371
|
-
export function isArrowDown(data) {
|
|
372
|
-
return data === "\x1b[B" || matchesKittySequence(data, ARROW_CODEPOINTS.down, 0);
|
|
373
|
-
}
|
|
374
|
-
/**
|
|
375
|
-
* Check if input matches Arrow Right key.
|
|
376
|
-
* Handles both legacy (\x1b[C) and Kitty protocol with modifiers.
|
|
377
|
-
*/
|
|
378
|
-
export function isArrowRight(data) {
|
|
379
|
-
return data === "\x1b[C" || matchesKittySequence(data, ARROW_CODEPOINTS.right, 0);
|
|
380
|
-
}
|
|
381
|
-
/**
|
|
382
|
-
* Check if input matches Arrow Left key.
|
|
383
|
-
* Handles both legacy (\x1b[D) and Kitty protocol with modifiers.
|
|
384
|
-
*/
|
|
385
|
-
export function isArrowLeft(data) {
|
|
386
|
-
return data === "\x1b[D" || matchesKittySequence(data, ARROW_CODEPOINTS.left, 0);
|
|
387
|
-
}
|
|
388
|
-
/**
|
|
389
|
-
* Check if input matches plain Tab key (no modifiers).
|
|
390
|
-
* Handles both legacy (\t) and Kitty protocol.
|
|
391
|
-
*/
|
|
392
|
-
export function isTab(data) {
|
|
393
|
-
return data === "\t" || matchesKittySequence(data, CODEPOINTS.tab, 0);
|
|
394
|
-
}
|
|
395
|
-
/**
|
|
396
|
-
* Check if input matches plain Enter/Return key (no modifiers).
|
|
397
|
-
* Handles both legacy (\r) and Kitty protocol.
|
|
398
|
-
*/
|
|
399
|
-
export function isEnter(data) {
|
|
400
|
-
return data === "\r" || matchesKittySequence(data, CODEPOINTS.enter, 0);
|
|
401
|
-
}
|
|
402
|
-
/**
|
|
403
|
-
* Check if input matches plain Backspace key (no modifiers).
|
|
404
|
-
* Handles both legacy (\x7f, \x08) and Kitty protocol.
|
|
405
|
-
*/
|
|
406
|
-
export function isBackspace(data) {
|
|
407
|
-
return data === "\x7f" || data === "\x08" || matchesKittySequence(data, CODEPOINTS.backspace, 0);
|
|
408
|
-
}
|
|
409
|
-
/**
|
|
410
|
-
* Check if input matches Shift+Backspace (Kitty protocol).
|
|
411
|
-
* Returns true so caller can treat it as regular backspace.
|
|
412
|
-
* Ignores lock key bits.
|
|
413
|
-
*/
|
|
414
|
-
export function isShiftBackspace(data) {
|
|
415
|
-
return matchesKittySequence(data, CODEPOINTS.backspace, MODIFIERS.shift);
|
|
416
|
-
}
|
|
417
|
-
/**
|
|
418
|
-
* Check if input matches Shift+Enter.
|
|
419
|
-
* Ignores lock key bits.
|
|
420
|
-
*/
|
|
421
|
-
export function isShiftEnter(data) {
|
|
422
|
-
return data === Keys.SHIFT_ENTER || matchesKittySequence(data, CODEPOINTS.enter, MODIFIERS.shift);
|
|
423
|
-
}
|
|
424
|
-
/**
|
|
425
|
-
* Check if input matches Alt+Enter.
|
|
426
|
-
* Ignores lock key bits.
|
|
427
|
-
*/
|
|
428
|
-
export function isAltEnter(data) {
|
|
429
|
-
return data === Keys.ALT_ENTER || data === "\x1b\r" || matchesKittySequence(data, CODEPOINTS.enter, MODIFIERS.alt);
|
|
430
|
-
}
|
|
431
|
-
/**
|
|
432
|
-
* Check if input matches Shift+Space (Kitty protocol).
|
|
433
|
-
* Returns true so caller can insert a regular space.
|
|
434
|
-
* Ignores lock key bits.
|
|
435
|
-
*/
|
|
436
|
-
export function isShiftSpace(data) {
|
|
437
|
-
return matchesKittySequence(data, CODEPOINTS.space, MODIFIERS.shift);
|
|
438
|
-
}
|
|
439
|
-
/**
|
|
440
|
-
* Check if input matches Option/Alt+Left (word navigation).
|
|
441
|
-
* Handles multiple formats including Kitty protocol.
|
|
442
|
-
*/
|
|
443
|
-
export function isAltLeft(data) {
|
|
444
|
-
return data === "\x1b[1;3D" || data === "\x1bb" || matchesKittySequence(data, ARROW_CODEPOINTS.left, MODIFIERS.alt);
|
|
445
|
-
}
|
|
446
|
-
/**
|
|
447
|
-
* Check if input matches Option/Alt+Right (word navigation).
|
|
448
|
-
* Handles multiple formats including Kitty protocol.
|
|
449
|
-
*/
|
|
450
|
-
export function isAltRight(data) {
|
|
451
|
-
return data === "\x1b[1;3C" || data === "\x1bf" || matchesKittySequence(data, ARROW_CODEPOINTS.right, MODIFIERS.alt);
|
|
452
|
-
}
|
|
453
|
-
/**
|
|
454
|
-
* Check if input matches Ctrl+Left (word navigation).
|
|
455
|
-
* Handles multiple formats including Kitty protocol.
|
|
456
|
-
*/
|
|
457
|
-
export function isCtrlLeft(data) {
|
|
458
|
-
return data === "\x1b[1;5D" || matchesKittySequence(data, ARROW_CODEPOINTS.left, MODIFIERS.ctrl);
|
|
459
|
-
}
|
|
460
|
-
/**
|
|
461
|
-
* Check if input matches Ctrl+Right (word navigation).
|
|
462
|
-
* Handles multiple formats including Kitty protocol.
|
|
463
|
-
*/
|
|
464
|
-
export function isCtrlRight(data) {
|
|
465
|
-
return data === "\x1b[1;5C" || matchesKittySequence(data, ARROW_CODEPOINTS.right, MODIFIERS.ctrl);
|
|
466
|
-
}
|
|
467
|
-
/**
|
|
468
|
-
* Check if input matches Home key.
|
|
469
|
-
* Handles legacy formats and Kitty protocol with lock key modifiers.
|
|
470
|
-
*/
|
|
471
|
-
export function isHome(data) {
|
|
472
|
-
return (data === "\x1b[H" ||
|
|
473
|
-
data === "\x1b[1~" ||
|
|
474
|
-
data === "\x1b[7~" ||
|
|
475
|
-
matchesKittySequence(data, FUNCTIONAL_CODEPOINTS.home, 0));
|
|
476
|
-
}
|
|
477
|
-
/**
|
|
478
|
-
* Check if input matches End key.
|
|
479
|
-
* Handles legacy formats and Kitty protocol with lock key modifiers.
|
|
480
|
-
*/
|
|
481
|
-
export function isEnd(data) {
|
|
482
|
-
return (data === "\x1b[F" ||
|
|
483
|
-
data === "\x1b[4~" ||
|
|
484
|
-
data === "\x1b[8~" ||
|
|
485
|
-
matchesKittySequence(data, FUNCTIONAL_CODEPOINTS.end, 0));
|
|
486
|
-
}
|
|
487
|
-
/**
|
|
488
|
-
* Check if input matches Delete key (forward delete).
|
|
489
|
-
* Handles legacy format and Kitty protocol with lock key modifiers.
|
|
490
|
-
*/
|
|
491
|
-
export function isDelete(data) {
|
|
492
|
-
return data === "\x1b[3~" || matchesKittySequence(data, FUNCTIONAL_CODEPOINTS.delete, 0);
|
|
170
|
+
const { key, ctrl, shift, alt } = parsed;
|
|
171
|
+
let modifier = 0;
|
|
172
|
+
if (shift)
|
|
173
|
+
modifier |= MODIFIERS.shift;
|
|
174
|
+
if (alt)
|
|
175
|
+
modifier |= MODIFIERS.alt;
|
|
176
|
+
if (ctrl)
|
|
177
|
+
modifier |= MODIFIERS.ctrl;
|
|
178
|
+
switch (key) {
|
|
179
|
+
case "escape":
|
|
180
|
+
case "esc":
|
|
181
|
+
if (modifier !== 0)
|
|
182
|
+
return false;
|
|
183
|
+
return data === "\x1b" || matchesKittySequence(data, CODEPOINTS.escape, 0);
|
|
184
|
+
case "space":
|
|
185
|
+
if (modifier === 0) {
|
|
186
|
+
return data === " " || matchesKittySequence(data, CODEPOINTS.space, 0);
|
|
187
|
+
}
|
|
188
|
+
return matchesKittySequence(data, CODEPOINTS.space, modifier);
|
|
189
|
+
case "tab":
|
|
190
|
+
if (shift && !ctrl && !alt) {
|
|
191
|
+
return data === "\x1b[Z" || matchesKittySequence(data, CODEPOINTS.tab, MODIFIERS.shift);
|
|
192
|
+
}
|
|
193
|
+
if (modifier === 0) {
|
|
194
|
+
return data === "\t" || matchesKittySequence(data, CODEPOINTS.tab, 0);
|
|
195
|
+
}
|
|
196
|
+
return matchesKittySequence(data, CODEPOINTS.tab, modifier);
|
|
197
|
+
case "enter":
|
|
198
|
+
case "return":
|
|
199
|
+
if (shift && !ctrl && !alt) {
|
|
200
|
+
return (matchesKittySequence(data, CODEPOINTS.enter, MODIFIERS.shift) ||
|
|
201
|
+
matchesKittySequence(data, CODEPOINTS.kpEnter, MODIFIERS.shift));
|
|
202
|
+
}
|
|
203
|
+
if (alt && !ctrl && !shift) {
|
|
204
|
+
return (data === "\x1b\r" ||
|
|
205
|
+
matchesKittySequence(data, CODEPOINTS.enter, MODIFIERS.alt) ||
|
|
206
|
+
matchesKittySequence(data, CODEPOINTS.kpEnter, MODIFIERS.alt));
|
|
207
|
+
}
|
|
208
|
+
if (modifier === 0) {
|
|
209
|
+
return (data === "\r" ||
|
|
210
|
+
data === "\x1bOM" || // SS3 M (numpad enter in some terminals)
|
|
211
|
+
matchesKittySequence(data, CODEPOINTS.enter, 0) ||
|
|
212
|
+
matchesKittySequence(data, CODEPOINTS.kpEnter, 0));
|
|
213
|
+
}
|
|
214
|
+
return (matchesKittySequence(data, CODEPOINTS.enter, modifier) ||
|
|
215
|
+
matchesKittySequence(data, CODEPOINTS.kpEnter, modifier));
|
|
216
|
+
case "backspace":
|
|
217
|
+
if (alt && !ctrl && !shift) {
|
|
218
|
+
return data === "\x1b\x7f" || matchesKittySequence(data, CODEPOINTS.backspace, MODIFIERS.alt);
|
|
219
|
+
}
|
|
220
|
+
if (modifier === 0) {
|
|
221
|
+
return data === "\x7f" || data === "\x08" || matchesKittySequence(data, CODEPOINTS.backspace, 0);
|
|
222
|
+
}
|
|
223
|
+
return matchesKittySequence(data, CODEPOINTS.backspace, modifier);
|
|
224
|
+
case "delete":
|
|
225
|
+
if (modifier === 0) {
|
|
226
|
+
return data === "\x1b[3~" || matchesKittySequence(data, FUNCTIONAL_CODEPOINTS.delete, 0);
|
|
227
|
+
}
|
|
228
|
+
return matchesKittySequence(data, FUNCTIONAL_CODEPOINTS.delete, modifier);
|
|
229
|
+
case "home":
|
|
230
|
+
if (modifier === 0) {
|
|
231
|
+
return (data === "\x1b[H" ||
|
|
232
|
+
data === "\x1b[1~" ||
|
|
233
|
+
data === "\x1b[7~" ||
|
|
234
|
+
matchesKittySequence(data, FUNCTIONAL_CODEPOINTS.home, 0));
|
|
235
|
+
}
|
|
236
|
+
return matchesKittySequence(data, FUNCTIONAL_CODEPOINTS.home, modifier);
|
|
237
|
+
case "end":
|
|
238
|
+
if (modifier === 0) {
|
|
239
|
+
return (data === "\x1b[F" ||
|
|
240
|
+
data === "\x1b[4~" ||
|
|
241
|
+
data === "\x1b[8~" ||
|
|
242
|
+
matchesKittySequence(data, FUNCTIONAL_CODEPOINTS.end, 0));
|
|
243
|
+
}
|
|
244
|
+
return matchesKittySequence(data, FUNCTIONAL_CODEPOINTS.end, modifier);
|
|
245
|
+
case "up":
|
|
246
|
+
if (modifier === 0) {
|
|
247
|
+
return data === "\x1b[A" || matchesKittySequence(data, ARROW_CODEPOINTS.up, 0);
|
|
248
|
+
}
|
|
249
|
+
return matchesKittySequence(data, ARROW_CODEPOINTS.up, modifier);
|
|
250
|
+
case "down":
|
|
251
|
+
if (modifier === 0) {
|
|
252
|
+
return data === "\x1b[B" || matchesKittySequence(data, ARROW_CODEPOINTS.down, 0);
|
|
253
|
+
}
|
|
254
|
+
return matchesKittySequence(data, ARROW_CODEPOINTS.down, modifier);
|
|
255
|
+
case "left":
|
|
256
|
+
if (alt && !ctrl && !shift) {
|
|
257
|
+
return (data === "\x1b[1;3D" ||
|
|
258
|
+
data === "\x1bb" ||
|
|
259
|
+
matchesKittySequence(data, ARROW_CODEPOINTS.left, MODIFIERS.alt));
|
|
260
|
+
}
|
|
261
|
+
if (ctrl && !alt && !shift) {
|
|
262
|
+
return data === "\x1b[1;5D" || matchesKittySequence(data, ARROW_CODEPOINTS.left, MODIFIERS.ctrl);
|
|
263
|
+
}
|
|
264
|
+
if (modifier === 0) {
|
|
265
|
+
return data === "\x1b[D" || matchesKittySequence(data, ARROW_CODEPOINTS.left, 0);
|
|
266
|
+
}
|
|
267
|
+
return matchesKittySequence(data, ARROW_CODEPOINTS.left, modifier);
|
|
268
|
+
case "right":
|
|
269
|
+
if (alt && !ctrl && !shift) {
|
|
270
|
+
return (data === "\x1b[1;3C" ||
|
|
271
|
+
data === "\x1bf" ||
|
|
272
|
+
matchesKittySequence(data, ARROW_CODEPOINTS.right, MODIFIERS.alt));
|
|
273
|
+
}
|
|
274
|
+
if (ctrl && !alt && !shift) {
|
|
275
|
+
return data === "\x1b[1;5C" || matchesKittySequence(data, ARROW_CODEPOINTS.right, MODIFIERS.ctrl);
|
|
276
|
+
}
|
|
277
|
+
if (modifier === 0) {
|
|
278
|
+
return data === "\x1b[C" || matchesKittySequence(data, ARROW_CODEPOINTS.right, 0);
|
|
279
|
+
}
|
|
280
|
+
return matchesKittySequence(data, ARROW_CODEPOINTS.right, modifier);
|
|
281
|
+
}
|
|
282
|
+
// Handle single letter keys (a-z)
|
|
283
|
+
if (key.length === 1 && key >= "a" && key <= "z") {
|
|
284
|
+
const codepoint = key.charCodeAt(0);
|
|
285
|
+
if (ctrl && !shift && !alt) {
|
|
286
|
+
const raw = rawCtrlChar(key);
|
|
287
|
+
if (data === raw)
|
|
288
|
+
return true;
|
|
289
|
+
if (data.length > 0 && data.charCodeAt(0) === raw.charCodeAt(0))
|
|
290
|
+
return true;
|
|
291
|
+
return matchesKittySequence(data, codepoint, MODIFIERS.ctrl);
|
|
292
|
+
}
|
|
293
|
+
if (ctrl && shift && !alt) {
|
|
294
|
+
return matchesKittySequence(data, codepoint, MODIFIERS.shift + MODIFIERS.ctrl);
|
|
295
|
+
}
|
|
296
|
+
if (shift && !ctrl && !alt) {
|
|
297
|
+
// Legacy: shift+letter produces uppercase
|
|
298
|
+
if (data === key.toUpperCase())
|
|
299
|
+
return true;
|
|
300
|
+
return matchesKittySequence(data, codepoint, MODIFIERS.shift);
|
|
301
|
+
}
|
|
302
|
+
if (modifier !== 0) {
|
|
303
|
+
return matchesKittySequence(data, codepoint, modifier);
|
|
304
|
+
}
|
|
305
|
+
return data === key;
|
|
306
|
+
}
|
|
307
|
+
return false;
|
|
493
308
|
}
|
|
494
309
|
/**
|
|
495
|
-
*
|
|
496
|
-
*
|
|
497
|
-
*
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
310
|
+
* Parse input data and return the key identifier if recognized.
|
|
311
|
+
*
|
|
312
|
+
* @param data - Raw input data from terminal
|
|
313
|
+
* @returns Key identifier string (e.g., "ctrl+c") or undefined
|
|
314
|
+
*/
|
|
315
|
+
export function parseKey(data) {
|
|
316
|
+
const kitty = parseKittySequence(data);
|
|
317
|
+
if (kitty) {
|
|
318
|
+
const { codepoint, modifier } = kitty;
|
|
319
|
+
const mods = [];
|
|
320
|
+
const effectiveMod = modifier & ~LOCK_MASK;
|
|
321
|
+
if (effectiveMod & MODIFIERS.shift)
|
|
322
|
+
mods.push("shift");
|
|
323
|
+
if (effectiveMod & MODIFIERS.ctrl)
|
|
324
|
+
mods.push("ctrl");
|
|
325
|
+
if (effectiveMod & MODIFIERS.alt)
|
|
326
|
+
mods.push("alt");
|
|
327
|
+
let keyName;
|
|
328
|
+
if (codepoint === CODEPOINTS.escape)
|
|
329
|
+
keyName = "escape";
|
|
330
|
+
else if (codepoint === CODEPOINTS.tab)
|
|
331
|
+
keyName = "tab";
|
|
332
|
+
else if (codepoint === CODEPOINTS.enter || codepoint === CODEPOINTS.kpEnter)
|
|
333
|
+
keyName = "enter";
|
|
334
|
+
else if (codepoint === CODEPOINTS.space)
|
|
335
|
+
keyName = "space";
|
|
336
|
+
else if (codepoint === CODEPOINTS.backspace)
|
|
337
|
+
keyName = "backspace";
|
|
338
|
+
else if (codepoint === FUNCTIONAL_CODEPOINTS.delete)
|
|
339
|
+
keyName = "delete";
|
|
340
|
+
else if (codepoint === FUNCTIONAL_CODEPOINTS.home)
|
|
341
|
+
keyName = "home";
|
|
342
|
+
else if (codepoint === FUNCTIONAL_CODEPOINTS.end)
|
|
343
|
+
keyName = "end";
|
|
344
|
+
else if (codepoint === ARROW_CODEPOINTS.up)
|
|
345
|
+
keyName = "up";
|
|
346
|
+
else if (codepoint === ARROW_CODEPOINTS.down)
|
|
347
|
+
keyName = "down";
|
|
348
|
+
else if (codepoint === ARROW_CODEPOINTS.left)
|
|
349
|
+
keyName = "left";
|
|
350
|
+
else if (codepoint === ARROW_CODEPOINTS.right)
|
|
351
|
+
keyName = "right";
|
|
352
|
+
else if (codepoint >= 97 && codepoint <= 122)
|
|
353
|
+
keyName = String.fromCharCode(codepoint);
|
|
354
|
+
if (keyName) {
|
|
355
|
+
return mods.length > 0 ? `${mods.join("+")}+${keyName}` : keyName;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
// Legacy sequences
|
|
359
|
+
if (data === "\x1b")
|
|
360
|
+
return "escape";
|
|
361
|
+
if (data === "\t")
|
|
362
|
+
return "tab";
|
|
363
|
+
if (data === "\r" || data === "\x1bOM")
|
|
364
|
+
return "enter";
|
|
365
|
+
if (data === " ")
|
|
366
|
+
return "space";
|
|
367
|
+
if (data === "\x7f" || data === "\x08")
|
|
368
|
+
return "backspace";
|
|
369
|
+
if (data === "\x1b[Z")
|
|
370
|
+
return "shift+tab";
|
|
371
|
+
if (data === "\x1b\r")
|
|
372
|
+
return "alt+enter";
|
|
373
|
+
if (data === "\x1b\x7f")
|
|
374
|
+
return "alt+backspace";
|
|
375
|
+
if (data === "\x1b[A")
|
|
376
|
+
return "up";
|
|
377
|
+
if (data === "\x1b[B")
|
|
378
|
+
return "down";
|
|
379
|
+
if (data === "\x1b[C")
|
|
380
|
+
return "right";
|
|
381
|
+
if (data === "\x1b[D")
|
|
382
|
+
return "left";
|
|
383
|
+
if (data === "\x1b[H")
|
|
384
|
+
return "home";
|
|
385
|
+
if (data === "\x1b[F")
|
|
386
|
+
return "end";
|
|
387
|
+
if (data === "\x1b[3~")
|
|
388
|
+
return "delete";
|
|
389
|
+
// Raw Ctrl+letter
|
|
390
|
+
if (data.length === 1) {
|
|
391
|
+
const code = data.charCodeAt(0);
|
|
392
|
+
if (code >= 1 && code <= 26) {
|
|
393
|
+
return `ctrl+${String.fromCharCode(code + 96)}`;
|
|
394
|
+
}
|
|
395
|
+
if (code >= 32 && code <= 126) {
|
|
396
|
+
return data;
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
return undefined;
|
|
501
400
|
}
|
|
502
401
|
//# sourceMappingURL=keys.js.map
|