phi-code-tui 0.56.3 → 0.74.1
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 +29 -11
- package/dist/autocomplete.d.ts +18 -14
- package/dist/autocomplete.d.ts.map +1 -1
- package/dist/autocomplete.js +151 -112
- package/dist/autocomplete.js.map +1 -1
- package/dist/components/box.d.ts.map +1 -1
- package/dist/components/box.js +6 -1
- package/dist/components/box.js.map +1 -1
- package/dist/components/cancellable-loader.d.ts.map +1 -1
- package/dist/components/cancellable-loader.js +6 -7
- package/dist/components/cancellable-loader.js.map +1 -1
- package/dist/components/editor.d.ts +45 -1
- package/dist/components/editor.d.ts.map +1 -1
- package/dist/components/editor.js +505 -221
- package/dist/components/editor.js.map +1 -1
- package/dist/components/image.d.ts.map +1 -1
- package/dist/components/image.js +22 -7
- package/dist/components/image.js.map +1 -1
- package/dist/components/input.d.ts.map +1 -1
- package/dist/components/input.js +57 -74
- package/dist/components/input.js.map +1 -1
- package/dist/components/loader.d.ts +12 -2
- package/dist/components/loader.d.ts.map +1 -1
- package/dist/components/loader.js +36 -13
- package/dist/components/loader.js.map +1 -1
- package/dist/components/markdown.d.ts +0 -5
- package/dist/components/markdown.d.ts.map +1 -1
- package/dist/components/markdown.js +101 -114
- package/dist/components/markdown.js.map +1 -1
- package/dist/components/select-list.d.ts +19 -1
- package/dist/components/select-list.d.ts.map +1 -1
- package/dist/components/select-list.js +82 -71
- 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 +18 -10
- package/dist/components/settings-list.js.map +1 -1
- package/dist/components/spacer.d.ts.map +1 -1
- package/dist/components/spacer.js +1 -0
- package/dist/components/spacer.js.map +1 -1
- package/dist/components/text.d.ts.map +1 -1
- package/dist/components/text.js +8 -0
- package/dist/components/text.js.map +1 -1
- package/dist/components/truncated-text.d.ts.map +1 -1
- package/dist/components/truncated-text.js +3 -0
- package/dist/components/truncated-text.js.map +1 -1
- package/dist/editor-component.d.ts.map +1 -1
- package/dist/fuzzy.d.ts.map +1 -1
- package/dist/fuzzy.js +3 -0
- package/dist/fuzzy.js.map +1 -1
- package/dist/index.d.ts +5 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/dist/keybindings.d.ts +187 -33
- package/dist/keybindings.d.ts.map +1 -1
- package/dist/keybindings.js +156 -95
- package/dist/keybindings.js.map +1 -1
- package/dist/keys.d.ts +21 -12
- package/dist/keys.d.ts.map +1 -1
- package/dist/keys.js +270 -112
- package/dist/keys.js.map +1 -1
- package/dist/kill-ring.d.ts.map +1 -1
- package/dist/kill-ring.js +1 -3
- package/dist/kill-ring.js.map +1 -1
- package/dist/stdin-buffer.d.ts +2 -0
- package/dist/stdin-buffer.d.ts.map +1 -1
- package/dist/stdin-buffer.js +31 -8
- package/dist/stdin-buffer.js.map +1 -1
- package/dist/terminal-image.d.ts +17 -0
- package/dist/terminal-image.d.ts.map +1 -1
- package/dist/terminal-image.js +41 -5
- package/dist/terminal-image.js.map +1 -1
- package/dist/terminal.d.ts +4 -0
- package/dist/terminal.d.ts.map +1 -1
- package/dist/terminal.js +56 -8
- package/dist/terminal.js.map +1 -1
- package/dist/tui.d.ts +21 -5
- package/dist/tui.d.ts.map +1 -1
- package/dist/tui.js +234 -118
- package/dist/tui.js.map +1 -1
- package/dist/undo-stack.d.ts.map +1 -1
- package/dist/undo-stack.js +1 -3
- package/dist/undo-stack.js.map +1 -1
- package/dist/utils.d.ts +1 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +281 -81
- package/dist/utils.js.map +1 -1
- package/package.json +3 -3
package/dist/keys.js
CHANGED
|
@@ -40,8 +40,8 @@ export function isKittyProtocolActive() {
|
|
|
40
40
|
* Usage:
|
|
41
41
|
* - Key.escape, Key.enter, Key.tab, etc. for special keys
|
|
42
42
|
* - Key.backtick, Key.comma, Key.period, etc. for symbol keys
|
|
43
|
-
* - Key.ctrl("c"), Key.alt("x") for single
|
|
44
|
-
* - Key.ctrlShift("p"), Key.ctrlAlt("x") for combined modifiers
|
|
43
|
+
* - Key.ctrl("c"), Key.alt("x"), Key.super("k") for single modifiers
|
|
44
|
+
* - Key.ctrlShift("p"), Key.ctrlAlt("x"), Key.ctrlSuper("k") for combined modifiers
|
|
45
45
|
*/
|
|
46
46
|
export const Key = {
|
|
47
47
|
// Special keys
|
|
@@ -111,6 +111,7 @@ export const Key = {
|
|
|
111
111
|
ctrl: (key) => `ctrl+${key}`,
|
|
112
112
|
shift: (key) => `shift+${key}`,
|
|
113
113
|
alt: (key) => `alt+${key}`,
|
|
114
|
+
super: (key) => `super+${key}`,
|
|
114
115
|
// Combined modifiers
|
|
115
116
|
ctrlShift: (key) => `ctrl+shift+${key}`,
|
|
116
117
|
shiftCtrl: (key) => `shift+ctrl+${key}`,
|
|
@@ -118,8 +119,15 @@ export const Key = {
|
|
|
118
119
|
altCtrl: (key) => `alt+ctrl+${key}`,
|
|
119
120
|
shiftAlt: (key) => `shift+alt+${key}`,
|
|
120
121
|
altShift: (key) => `alt+shift+${key}`,
|
|
122
|
+
ctrlSuper: (key) => `ctrl+super+${key}`,
|
|
123
|
+
superCtrl: (key) => `super+ctrl+${key}`,
|
|
124
|
+
shiftSuper: (key) => `shift+super+${key}`,
|
|
125
|
+
superShift: (key) => `super+shift+${key}`,
|
|
126
|
+
altSuper: (key) => `alt+super+${key}`,
|
|
127
|
+
superAlt: (key) => `super+alt+${key}`,
|
|
121
128
|
// Triple modifiers
|
|
122
129
|
ctrlShiftAlt: (key) => `ctrl+shift+alt+${key}`,
|
|
130
|
+
ctrlShiftSuper: (key) => `ctrl+shift+super+${key}`,
|
|
123
131
|
};
|
|
124
132
|
// =============================================================================
|
|
125
133
|
// Constants
|
|
@@ -161,6 +169,7 @@ const MODIFIERS = {
|
|
|
161
169
|
shift: 1,
|
|
162
170
|
alt: 2,
|
|
163
171
|
ctrl: 4,
|
|
172
|
+
super: 8,
|
|
164
173
|
};
|
|
165
174
|
const LOCK_MASK = 64 + 128; // Caps Lock + Num Lock
|
|
166
175
|
const CODEPOINTS = {
|
|
@@ -185,6 +194,45 @@ const FUNCTIONAL_CODEPOINTS = {
|
|
|
185
194
|
home: -14,
|
|
186
195
|
end: -15,
|
|
187
196
|
};
|
|
197
|
+
const KITTY_FUNCTIONAL_KEY_EQUIVALENTS = new Map([
|
|
198
|
+
[57399, 48], // KP_0 -> 0
|
|
199
|
+
[57400, 49], // KP_1 -> 1
|
|
200
|
+
[57401, 50], // KP_2 -> 2
|
|
201
|
+
[57402, 51], // KP_3 -> 3
|
|
202
|
+
[57403, 52], // KP_4 -> 4
|
|
203
|
+
[57404, 53], // KP_5 -> 5
|
|
204
|
+
[57405, 54], // KP_6 -> 6
|
|
205
|
+
[57406, 55], // KP_7 -> 7
|
|
206
|
+
[57407, 56], // KP_8 -> 8
|
|
207
|
+
[57408, 57], // KP_9 -> 9
|
|
208
|
+
[57409, 46], // KP_DECIMAL -> .
|
|
209
|
+
[57410, 47], // KP_DIVIDE -> /
|
|
210
|
+
[57411, 42], // KP_MULTIPLY -> *
|
|
211
|
+
[57412, 45], // KP_SUBTRACT -> -
|
|
212
|
+
[57413, 43], // KP_ADD -> +
|
|
213
|
+
[57415, 61], // KP_EQUAL -> =
|
|
214
|
+
[57416, 44], // KP_SEPARATOR -> ,
|
|
215
|
+
[57417, ARROW_CODEPOINTS.left],
|
|
216
|
+
[57418, ARROW_CODEPOINTS.right],
|
|
217
|
+
[57419, ARROW_CODEPOINTS.up],
|
|
218
|
+
[57420, ARROW_CODEPOINTS.down],
|
|
219
|
+
[57421, FUNCTIONAL_CODEPOINTS.pageUp],
|
|
220
|
+
[57422, FUNCTIONAL_CODEPOINTS.pageDown],
|
|
221
|
+
[57423, FUNCTIONAL_CODEPOINTS.home],
|
|
222
|
+
[57424, FUNCTIONAL_CODEPOINTS.end],
|
|
223
|
+
[57425, FUNCTIONAL_CODEPOINTS.insert],
|
|
224
|
+
[57426, FUNCTIONAL_CODEPOINTS.delete],
|
|
225
|
+
]);
|
|
226
|
+
function normalizeKittyFunctionalCodepoint(codepoint) {
|
|
227
|
+
return KITTY_FUNCTIONAL_KEY_EQUIVALENTS.get(codepoint) ?? codepoint;
|
|
228
|
+
}
|
|
229
|
+
function normalizeShiftedLetterIdentityCodepoint(codepoint, modifier) {
|
|
230
|
+
const effectiveModifier = modifier & ~LOCK_MASK;
|
|
231
|
+
if ((effectiveModifier & MODIFIERS.shift) !== 0 && codepoint >= 65 && codepoint <= 90) {
|
|
232
|
+
return codepoint + 32;
|
|
233
|
+
}
|
|
234
|
+
return codepoint;
|
|
235
|
+
}
|
|
188
236
|
const LEGACY_KEY_SEQUENCES = {
|
|
189
237
|
up: ["\x1b[A", "\x1bOA"],
|
|
190
238
|
down: ["\x1b[B", "\x1bOB"],
|
|
@@ -436,8 +484,10 @@ function matchesKittySequence(data, expectedCodepoint, expectedModifier) {
|
|
|
436
484
|
// Check if modifiers match
|
|
437
485
|
if (actualMod !== expectedMod)
|
|
438
486
|
return false;
|
|
439
|
-
|
|
440
|
-
|
|
487
|
+
const normalizedCodepoint = normalizeShiftedLetterIdentityCodepoint(normalizeKittyFunctionalCodepoint(parsed.codepoint), parsed.modifier);
|
|
488
|
+
const normalizedExpectedCodepoint = normalizeShiftedLetterIdentityCodepoint(normalizeKittyFunctionalCodepoint(expectedCodepoint), expectedModifier);
|
|
489
|
+
// Primary match: codepoint matches directly after normalizing functional keys
|
|
490
|
+
if (normalizedCodepoint === normalizedExpectedCodepoint)
|
|
441
491
|
return true;
|
|
442
492
|
// Alternate match: use base layout key for non-Latin keyboard layouts.
|
|
443
493
|
// This allows Ctrl+С (Cyrillic) to match Ctrl+c (Latin) when terminal reports
|
|
@@ -452,7 +502,7 @@ function matchesKittySequence(data, expectedCodepoint, expectedModifier) {
|
|
|
452
502
|
// (letter remapping) and Ctrl+/ could falsely match Ctrl+[ (symbol remapping)
|
|
453
503
|
// if the base layout key were always considered.
|
|
454
504
|
if (parsed.baseLayoutKey !== undefined && parsed.baseLayoutKey === expectedCodepoint) {
|
|
455
|
-
const cp =
|
|
505
|
+
const cp = normalizedCodepoint;
|
|
456
506
|
const isLatinLetter = cp >= 97 && cp <= 122; // a-z
|
|
457
507
|
const isKnownSymbol = SYMBOL_KEYS.has(String.fromCharCode(cp));
|
|
458
508
|
if (!isLatinLetter && !isKnownSymbol)
|
|
@@ -460,20 +510,43 @@ function matchesKittySequence(data, expectedCodepoint, expectedModifier) {
|
|
|
460
510
|
}
|
|
461
511
|
return false;
|
|
462
512
|
}
|
|
513
|
+
function parseModifyOtherKeysSequence(data) {
|
|
514
|
+
const match = data.match(/^\x1b\[27;(\d+);(\d+)~$/);
|
|
515
|
+
if (!match)
|
|
516
|
+
return null;
|
|
517
|
+
const modValue = parseInt(match[1], 10);
|
|
518
|
+
const codepoint = parseInt(match[2], 10);
|
|
519
|
+
return { codepoint, modifier: modValue - 1 };
|
|
520
|
+
}
|
|
463
521
|
/**
|
|
464
522
|
* Match xterm modifyOtherKeys format: CSI 27 ; modifiers ; keycode ~
|
|
465
523
|
* This is used by terminals when Kitty protocol is not enabled.
|
|
466
524
|
* Modifier values are 1-indexed: 2=shift, 3=alt, 5=ctrl, etc.
|
|
467
525
|
*/
|
|
468
526
|
function matchesModifyOtherKeys(data, expectedKeycode, expectedModifier) {
|
|
469
|
-
const
|
|
470
|
-
if (!
|
|
527
|
+
const parsed = parseModifyOtherKeysSequence(data);
|
|
528
|
+
if (!parsed)
|
|
471
529
|
return false;
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
530
|
+
return parsed.codepoint === expectedKeycode && parsed.modifier === expectedModifier;
|
|
531
|
+
}
|
|
532
|
+
function isWindowsTerminalSession() {
|
|
533
|
+
return (Boolean(process.env.WT_SESSION) && !process.env.SSH_CONNECTION && !process.env.SSH_CLIENT && !process.env.SSH_TTY);
|
|
534
|
+
}
|
|
535
|
+
/**
|
|
536
|
+
* Raw 0x08 (BS) is ambiguous in legacy terminals.
|
|
537
|
+
*
|
|
538
|
+
* - Windows Terminal uses it for Ctrl+Backspace.
|
|
539
|
+
* - Some legacy terminals and tmux setups send it for plain Backspace.
|
|
540
|
+
*
|
|
541
|
+
* Prefer explicit Kitty / CSI-u / modifyOtherKeys sequences whenever they are
|
|
542
|
+
* available. Fall back to a Windows Terminal heuristic only for raw BS bytes.
|
|
543
|
+
*/
|
|
544
|
+
function matchesRawBackspace(data, expectedModifier) {
|
|
545
|
+
if (data === "\x7f")
|
|
546
|
+
return expectedModifier === 0;
|
|
547
|
+
if (data !== "\x08")
|
|
548
|
+
return false;
|
|
549
|
+
return isWindowsTerminalSession() ? expectedModifier === MODIFIERS.ctrl : expectedModifier === 0;
|
|
477
550
|
}
|
|
478
551
|
// =============================================================================
|
|
479
552
|
// Generic Key Matching
|
|
@@ -499,6 +572,34 @@ function rawCtrlChar(key) {
|
|
|
499
572
|
}
|
|
500
573
|
return null;
|
|
501
574
|
}
|
|
575
|
+
function isDigitKey(key) {
|
|
576
|
+
return key >= "0" && key <= "9";
|
|
577
|
+
}
|
|
578
|
+
function matchesPrintableModifyOtherKeys(data, expectedKeycode, expectedModifier) {
|
|
579
|
+
if (expectedModifier === 0)
|
|
580
|
+
return false;
|
|
581
|
+
const parsed = parseModifyOtherKeysSequence(data);
|
|
582
|
+
if (!parsed || parsed.modifier !== expectedModifier)
|
|
583
|
+
return false;
|
|
584
|
+
return (normalizeShiftedLetterIdentityCodepoint(parsed.codepoint, parsed.modifier) ===
|
|
585
|
+
normalizeShiftedLetterIdentityCodepoint(expectedKeycode, expectedModifier));
|
|
586
|
+
}
|
|
587
|
+
function formatKeyNameWithModifiers(keyName, modifier) {
|
|
588
|
+
const mods = [];
|
|
589
|
+
const effectiveMod = modifier & ~LOCK_MASK;
|
|
590
|
+
const supportedModifierMask = MODIFIERS.shift | MODIFIERS.ctrl | MODIFIERS.alt | MODIFIERS.super;
|
|
591
|
+
if ((effectiveMod & ~supportedModifierMask) !== 0)
|
|
592
|
+
return undefined;
|
|
593
|
+
if (effectiveMod & MODIFIERS.shift)
|
|
594
|
+
mods.push("shift");
|
|
595
|
+
if (effectiveMod & MODIFIERS.ctrl)
|
|
596
|
+
mods.push("ctrl");
|
|
597
|
+
if (effectiveMod & MODIFIERS.alt)
|
|
598
|
+
mods.push("alt");
|
|
599
|
+
if (effectiveMod & MODIFIERS.super)
|
|
600
|
+
mods.push("super");
|
|
601
|
+
return mods.length > 0 ? `${mods.join("+")}+${keyName}` : keyName;
|
|
602
|
+
}
|
|
502
603
|
function parseKeyId(keyId) {
|
|
503
604
|
const parts = keyId.toLowerCase().split("+");
|
|
504
605
|
const key = parts[parts.length - 1];
|
|
@@ -509,6 +610,7 @@ function parseKeyId(keyId) {
|
|
|
509
610
|
ctrl: parts.includes("ctrl"),
|
|
510
611
|
shift: parts.includes("shift"),
|
|
511
612
|
alt: parts.includes("alt"),
|
|
613
|
+
super: parts.includes("super"),
|
|
512
614
|
};
|
|
513
615
|
}
|
|
514
616
|
/**
|
|
@@ -520,9 +622,10 @@ function parseKeyId(keyId) {
|
|
|
520
622
|
* - Ctrl combinations: "ctrl+c", "ctrl+z", etc.
|
|
521
623
|
* - Shift combinations: "shift+tab", "shift+enter"
|
|
522
624
|
* - Alt combinations: "alt+enter", "alt+backspace"
|
|
523
|
-
* -
|
|
625
|
+
* - Super combinations: "super+k", "super+enter"
|
|
626
|
+
* - Combined modifiers: "shift+ctrl+p", "ctrl+alt+x", "ctrl+super+k"
|
|
524
627
|
*
|
|
525
|
-
* Use the Key helper for autocomplete: Key.ctrl("c"), Key.escape, Key.ctrlShift("p")
|
|
628
|
+
* Use the Key helper for autocomplete: Key.ctrl("c"), Key.escape, Key.ctrlShift("p"), Key.super("k")
|
|
526
629
|
*
|
|
527
630
|
* @param data - Raw input data from terminal
|
|
528
631
|
* @param keyId - Key identifier (e.g., "ctrl+c", "escape", Key.ctrl("c"))
|
|
@@ -531,7 +634,7 @@ export function matchesKey(data, keyId) {
|
|
|
531
634
|
const parsed = parseKeyId(keyId);
|
|
532
635
|
if (!parsed)
|
|
533
636
|
return false;
|
|
534
|
-
const { key, ctrl, shift, alt } = parsed;
|
|
637
|
+
const { key, ctrl, shift, alt, super: superModifier } = parsed;
|
|
535
638
|
let modifier = 0;
|
|
536
639
|
if (shift)
|
|
537
640
|
modifier |= MODIFIERS.shift;
|
|
@@ -539,36 +642,46 @@ export function matchesKey(data, keyId) {
|
|
|
539
642
|
modifier |= MODIFIERS.alt;
|
|
540
643
|
if (ctrl)
|
|
541
644
|
modifier |= MODIFIERS.ctrl;
|
|
645
|
+
if (superModifier)
|
|
646
|
+
modifier |= MODIFIERS.super;
|
|
542
647
|
switch (key) {
|
|
543
648
|
case "escape":
|
|
544
649
|
case "esc":
|
|
545
650
|
if (modifier !== 0)
|
|
546
651
|
return false;
|
|
547
|
-
return data === "\x1b" ||
|
|
652
|
+
return (data === "\x1b" ||
|
|
653
|
+
matchesKittySequence(data, CODEPOINTS.escape, 0) ||
|
|
654
|
+
matchesModifyOtherKeys(data, CODEPOINTS.escape, 0));
|
|
548
655
|
case "space":
|
|
549
656
|
if (!_kittyProtocolActive) {
|
|
550
|
-
if (
|
|
657
|
+
if (modifier === MODIFIERS.ctrl && data === "\x00") {
|
|
551
658
|
return true;
|
|
552
659
|
}
|
|
553
|
-
if (
|
|
660
|
+
if (modifier === MODIFIERS.alt && data === "\x1b ") {
|
|
554
661
|
return true;
|
|
555
662
|
}
|
|
556
663
|
}
|
|
557
664
|
if (modifier === 0) {
|
|
558
|
-
return data === " " ||
|
|
665
|
+
return (data === " " ||
|
|
666
|
+
matchesKittySequence(data, CODEPOINTS.space, 0) ||
|
|
667
|
+
matchesModifyOtherKeys(data, CODEPOINTS.space, 0));
|
|
559
668
|
}
|
|
560
|
-
return matchesKittySequence(data, CODEPOINTS.space, modifier)
|
|
669
|
+
return (matchesKittySequence(data, CODEPOINTS.space, modifier) ||
|
|
670
|
+
matchesModifyOtherKeys(data, CODEPOINTS.space, modifier));
|
|
561
671
|
case "tab":
|
|
562
|
-
if (
|
|
563
|
-
return data === "\x1b[Z" ||
|
|
672
|
+
if (modifier === MODIFIERS.shift) {
|
|
673
|
+
return (data === "\x1b[Z" ||
|
|
674
|
+
matchesKittySequence(data, CODEPOINTS.tab, MODIFIERS.shift) ||
|
|
675
|
+
matchesModifyOtherKeys(data, CODEPOINTS.tab, MODIFIERS.shift));
|
|
564
676
|
}
|
|
565
677
|
if (modifier === 0) {
|
|
566
678
|
return data === "\t" || matchesKittySequence(data, CODEPOINTS.tab, 0);
|
|
567
679
|
}
|
|
568
|
-
return matchesKittySequence(data, CODEPOINTS.tab, modifier)
|
|
680
|
+
return (matchesKittySequence(data, CODEPOINTS.tab, modifier) ||
|
|
681
|
+
matchesModifyOtherKeys(data, CODEPOINTS.tab, modifier));
|
|
569
682
|
case "enter":
|
|
570
683
|
case "return":
|
|
571
|
-
if (
|
|
684
|
+
if (modifier === MODIFIERS.shift) {
|
|
572
685
|
// CSI u sequences (standard Kitty protocol)
|
|
573
686
|
if (matchesKittySequence(data, CODEPOINTS.enter, MODIFIERS.shift) ||
|
|
574
687
|
matchesKittySequence(data, CODEPOINTS.kpEnter, MODIFIERS.shift)) {
|
|
@@ -586,7 +699,7 @@ export function matchesKey(data, keyId) {
|
|
|
586
699
|
}
|
|
587
700
|
return false;
|
|
588
701
|
}
|
|
589
|
-
if (
|
|
702
|
+
if (modifier === MODIFIERS.alt) {
|
|
590
703
|
// CSI u sequences (standard Kitty protocol)
|
|
591
704
|
if (matchesKittySequence(data, CODEPOINTS.enter, MODIFIERS.alt) ||
|
|
592
705
|
matchesKittySequence(data, CODEPOINTS.kpEnter, MODIFIERS.alt)) {
|
|
@@ -611,18 +724,32 @@ export function matchesKey(data, keyId) {
|
|
|
611
724
|
matchesKittySequence(data, CODEPOINTS.kpEnter, 0));
|
|
612
725
|
}
|
|
613
726
|
return (matchesKittySequence(data, CODEPOINTS.enter, modifier) ||
|
|
614
|
-
matchesKittySequence(data, CODEPOINTS.kpEnter, modifier)
|
|
727
|
+
matchesKittySequence(data, CODEPOINTS.kpEnter, modifier) ||
|
|
728
|
+
matchesModifyOtherKeys(data, CODEPOINTS.enter, modifier));
|
|
615
729
|
case "backspace":
|
|
616
|
-
if (
|
|
730
|
+
if (modifier === MODIFIERS.alt) {
|
|
617
731
|
if (data === "\x1b\x7f" || data === "\x1b\b") {
|
|
618
732
|
return true;
|
|
619
733
|
}
|
|
620
|
-
return matchesKittySequence(data, CODEPOINTS.backspace, MODIFIERS.alt)
|
|
734
|
+
return (matchesKittySequence(data, CODEPOINTS.backspace, MODIFIERS.alt) ||
|
|
735
|
+
matchesModifyOtherKeys(data, CODEPOINTS.backspace, MODIFIERS.alt));
|
|
736
|
+
}
|
|
737
|
+
if (modifier === MODIFIERS.ctrl) {
|
|
738
|
+
// Legacy raw 0x08 is ambiguous: it can be Ctrl+Backspace on Windows
|
|
739
|
+
// Terminal or plain Backspace on other terminals, while also
|
|
740
|
+
// overlapping with Ctrl+H.
|
|
741
|
+
if (matchesRawBackspace(data, MODIFIERS.ctrl))
|
|
742
|
+
return true;
|
|
743
|
+
return (matchesKittySequence(data, CODEPOINTS.backspace, MODIFIERS.ctrl) ||
|
|
744
|
+
matchesModifyOtherKeys(data, CODEPOINTS.backspace, MODIFIERS.ctrl));
|
|
621
745
|
}
|
|
622
746
|
if (modifier === 0) {
|
|
623
|
-
return
|
|
747
|
+
return (matchesRawBackspace(data, 0) ||
|
|
748
|
+
matchesKittySequence(data, CODEPOINTS.backspace, 0) ||
|
|
749
|
+
matchesModifyOtherKeys(data, CODEPOINTS.backspace, 0));
|
|
624
750
|
}
|
|
625
|
-
return matchesKittySequence(data, CODEPOINTS.backspace, modifier)
|
|
751
|
+
return (matchesKittySequence(data, CODEPOINTS.backspace, modifier) ||
|
|
752
|
+
matchesModifyOtherKeys(data, CODEPOINTS.backspace, modifier));
|
|
626
753
|
case "insert":
|
|
627
754
|
if (modifier === 0) {
|
|
628
755
|
return (matchesLegacySequence(data, LEGACY_KEY_SEQUENCES.insert) ||
|
|
@@ -683,7 +810,7 @@ export function matchesKey(data, keyId) {
|
|
|
683
810
|
}
|
|
684
811
|
return matchesKittySequence(data, FUNCTIONAL_CODEPOINTS.pageDown, modifier);
|
|
685
812
|
case "up":
|
|
686
|
-
if (
|
|
813
|
+
if (modifier === MODIFIERS.alt) {
|
|
687
814
|
return data === "\x1bp" || matchesKittySequence(data, ARROW_CODEPOINTS.up, MODIFIERS.alt);
|
|
688
815
|
}
|
|
689
816
|
if (modifier === 0) {
|
|
@@ -695,7 +822,7 @@ export function matchesKey(data, keyId) {
|
|
|
695
822
|
}
|
|
696
823
|
return matchesKittySequence(data, ARROW_CODEPOINTS.up, modifier);
|
|
697
824
|
case "down":
|
|
698
|
-
if (
|
|
825
|
+
if (modifier === MODIFIERS.alt) {
|
|
699
826
|
return data === "\x1bn" || matchesKittySequence(data, ARROW_CODEPOINTS.down, MODIFIERS.alt);
|
|
700
827
|
}
|
|
701
828
|
if (modifier === 0) {
|
|
@@ -707,13 +834,13 @@ export function matchesKey(data, keyId) {
|
|
|
707
834
|
}
|
|
708
835
|
return matchesKittySequence(data, ARROW_CODEPOINTS.down, modifier);
|
|
709
836
|
case "left":
|
|
710
|
-
if (
|
|
837
|
+
if (modifier === MODIFIERS.alt) {
|
|
711
838
|
return (data === "\x1b[1;3D" ||
|
|
712
839
|
(!_kittyProtocolActive && data === "\x1bB") ||
|
|
713
840
|
data === "\x1bb" ||
|
|
714
841
|
matchesKittySequence(data, ARROW_CODEPOINTS.left, MODIFIERS.alt));
|
|
715
842
|
}
|
|
716
|
-
if (
|
|
843
|
+
if (modifier === MODIFIERS.ctrl) {
|
|
717
844
|
return (data === "\x1b[1;5D" ||
|
|
718
845
|
matchesLegacyModifierSequence(data, "left", MODIFIERS.ctrl) ||
|
|
719
846
|
matchesKittySequence(data, ARROW_CODEPOINTS.left, MODIFIERS.ctrl));
|
|
@@ -727,13 +854,13 @@ export function matchesKey(data, keyId) {
|
|
|
727
854
|
}
|
|
728
855
|
return matchesKittySequence(data, ARROW_CODEPOINTS.left, modifier);
|
|
729
856
|
case "right":
|
|
730
|
-
if (
|
|
857
|
+
if (modifier === MODIFIERS.alt) {
|
|
731
858
|
return (data === "\x1b[1;3C" ||
|
|
732
859
|
(!_kittyProtocolActive && data === "\x1bF") ||
|
|
733
860
|
data === "\x1bf" ||
|
|
734
861
|
matchesKittySequence(data, ARROW_CODEPOINTS.right, MODIFIERS.alt));
|
|
735
862
|
}
|
|
736
|
-
if (
|
|
863
|
+
if (modifier === MODIFIERS.ctrl) {
|
|
737
864
|
return (data === "\x1b[1;5C" ||
|
|
738
865
|
matchesLegacyModifierSequence(data, "right", MODIFIERS.ctrl) ||
|
|
739
866
|
matchesKittySequence(data, ARROW_CODEPOINTS.right, MODIFIERS.ctrl));
|
|
@@ -765,36 +892,45 @@ export function matchesKey(data, keyId) {
|
|
|
765
892
|
return matchesLegacySequence(data, LEGACY_KEY_SEQUENCES[functionKey]);
|
|
766
893
|
}
|
|
767
894
|
}
|
|
768
|
-
// Handle single letter keys
|
|
769
|
-
if (key.length === 1 && ((key >= "a" && key <= "z") || SYMBOL_KEYS.has(key))) {
|
|
895
|
+
// Handle single letter/digit keys and symbols
|
|
896
|
+
if (key.length === 1 && ((key >= "a" && key <= "z") || isDigitKey(key) || SYMBOL_KEYS.has(key))) {
|
|
770
897
|
const codepoint = key.charCodeAt(0);
|
|
771
898
|
const rawCtrl = rawCtrlChar(key);
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
899
|
+
const isLetter = key >= "a" && key <= "z";
|
|
900
|
+
const isDigit = isDigitKey(key);
|
|
901
|
+
if (modifier === MODIFIERS.ctrl + MODIFIERS.alt && !_kittyProtocolActive && rawCtrl) {
|
|
902
|
+
// Legacy: ctrl+alt+key is ESC followed by the control character.
|
|
903
|
+
// If that legacy form does not match, continue so CSI-u and
|
|
904
|
+
// modifyOtherKeys sequences from tmux can still be recognized.
|
|
905
|
+
if (data === `\x1b${rawCtrl}`)
|
|
906
|
+
return true;
|
|
775
907
|
}
|
|
776
|
-
if (
|
|
777
|
-
// Legacy: alt+letter is ESC followed by the
|
|
908
|
+
if (modifier === MODIFIERS.alt && !_kittyProtocolActive && (isLetter || isDigit)) {
|
|
909
|
+
// Legacy: alt+letter/digit is ESC followed by the key
|
|
778
910
|
if (data === `\x1b${key}`)
|
|
779
911
|
return true;
|
|
780
912
|
}
|
|
781
|
-
if (
|
|
913
|
+
if (modifier === MODIFIERS.ctrl) {
|
|
782
914
|
// Legacy: ctrl+key sends the control character
|
|
783
915
|
if (rawCtrl && data === rawCtrl)
|
|
784
916
|
return true;
|
|
785
|
-
return matchesKittySequence(data, codepoint, MODIFIERS.ctrl)
|
|
917
|
+
return (matchesKittySequence(data, codepoint, MODIFIERS.ctrl) ||
|
|
918
|
+
matchesPrintableModifyOtherKeys(data, codepoint, MODIFIERS.ctrl));
|
|
786
919
|
}
|
|
787
|
-
if (
|
|
788
|
-
return matchesKittySequence(data, codepoint, MODIFIERS.shift + MODIFIERS.ctrl)
|
|
920
|
+
if (modifier === MODIFIERS.shift + MODIFIERS.ctrl) {
|
|
921
|
+
return (matchesKittySequence(data, codepoint, MODIFIERS.shift + MODIFIERS.ctrl) ||
|
|
922
|
+
matchesPrintableModifyOtherKeys(data, codepoint, MODIFIERS.shift + MODIFIERS.ctrl));
|
|
789
923
|
}
|
|
790
|
-
if (
|
|
924
|
+
if (modifier === MODIFIERS.shift) {
|
|
791
925
|
// Legacy: shift+letter produces uppercase
|
|
792
|
-
if (data === key.toUpperCase())
|
|
926
|
+
if (isLetter && data === key.toUpperCase())
|
|
793
927
|
return true;
|
|
794
|
-
return matchesKittySequence(data, codepoint, MODIFIERS.shift)
|
|
928
|
+
return (matchesKittySequence(data, codepoint, MODIFIERS.shift) ||
|
|
929
|
+
matchesPrintableModifyOtherKeys(data, codepoint, MODIFIERS.shift));
|
|
795
930
|
}
|
|
796
931
|
if (modifier !== 0) {
|
|
797
|
-
return matchesKittySequence(data, codepoint, modifier)
|
|
932
|
+
return (matchesKittySequence(data, codepoint, modifier) ||
|
|
933
|
+
matchesPrintableModifyOtherKeys(data, codepoint, modifier));
|
|
798
934
|
}
|
|
799
935
|
// Check both raw char and Kitty sequence (needed for release events)
|
|
800
936
|
return data === key || matchesKittySequence(data, codepoint, 0);
|
|
@@ -807,67 +943,67 @@ export function matchesKey(data, keyId) {
|
|
|
807
943
|
* @param data - Raw input data from terminal
|
|
808
944
|
* @returns Key identifier string (e.g., "ctrl+c") or undefined
|
|
809
945
|
*/
|
|
946
|
+
function formatParsedKey(codepoint, modifier, baseLayoutKey) {
|
|
947
|
+
const normalizedCodepoint = normalizeKittyFunctionalCodepoint(codepoint);
|
|
948
|
+
const identityCodepoint = normalizeShiftedLetterIdentityCodepoint(normalizedCodepoint, modifier);
|
|
949
|
+
// Use base layout key only when codepoint is not a recognized Latin
|
|
950
|
+
// letter (a-z), digit (0-9), or symbol (/, -, [, ;, etc.). For those,
|
|
951
|
+
// the codepoint is authoritative regardless of physical key position.
|
|
952
|
+
// This prevents remapped layouts (Dvorak, Colemak, xremap, etc.) from
|
|
953
|
+
// reporting the wrong key name based on the QWERTY physical position.
|
|
954
|
+
const isLatinLetter = identityCodepoint >= 97 && identityCodepoint <= 122; // a-z
|
|
955
|
+
const isDigit = identityCodepoint >= 48 && identityCodepoint <= 57; // 0-9
|
|
956
|
+
const isKnownSymbol = SYMBOL_KEYS.has(String.fromCharCode(identityCodepoint));
|
|
957
|
+
const effectiveCodepoint = isLatinLetter || isDigit || isKnownSymbol ? identityCodepoint : (baseLayoutKey ?? identityCodepoint);
|
|
958
|
+
let keyName;
|
|
959
|
+
if (effectiveCodepoint === CODEPOINTS.escape)
|
|
960
|
+
keyName = "escape";
|
|
961
|
+
else if (effectiveCodepoint === CODEPOINTS.tab)
|
|
962
|
+
keyName = "tab";
|
|
963
|
+
else if (effectiveCodepoint === CODEPOINTS.enter || effectiveCodepoint === CODEPOINTS.kpEnter)
|
|
964
|
+
keyName = "enter";
|
|
965
|
+
else if (effectiveCodepoint === CODEPOINTS.space)
|
|
966
|
+
keyName = "space";
|
|
967
|
+
else if (effectiveCodepoint === CODEPOINTS.backspace)
|
|
968
|
+
keyName = "backspace";
|
|
969
|
+
else if (effectiveCodepoint === FUNCTIONAL_CODEPOINTS.delete)
|
|
970
|
+
keyName = "delete";
|
|
971
|
+
else if (effectiveCodepoint === FUNCTIONAL_CODEPOINTS.insert)
|
|
972
|
+
keyName = "insert";
|
|
973
|
+
else if (effectiveCodepoint === FUNCTIONAL_CODEPOINTS.home)
|
|
974
|
+
keyName = "home";
|
|
975
|
+
else if (effectiveCodepoint === FUNCTIONAL_CODEPOINTS.end)
|
|
976
|
+
keyName = "end";
|
|
977
|
+
else if (effectiveCodepoint === FUNCTIONAL_CODEPOINTS.pageUp)
|
|
978
|
+
keyName = "pageUp";
|
|
979
|
+
else if (effectiveCodepoint === FUNCTIONAL_CODEPOINTS.pageDown)
|
|
980
|
+
keyName = "pageDown";
|
|
981
|
+
else if (effectiveCodepoint === ARROW_CODEPOINTS.up)
|
|
982
|
+
keyName = "up";
|
|
983
|
+
else if (effectiveCodepoint === ARROW_CODEPOINTS.down)
|
|
984
|
+
keyName = "down";
|
|
985
|
+
else if (effectiveCodepoint === ARROW_CODEPOINTS.left)
|
|
986
|
+
keyName = "left";
|
|
987
|
+
else if (effectiveCodepoint === ARROW_CODEPOINTS.right)
|
|
988
|
+
keyName = "right";
|
|
989
|
+
else if (effectiveCodepoint >= 48 && effectiveCodepoint <= 57)
|
|
990
|
+
keyName = String.fromCharCode(effectiveCodepoint);
|
|
991
|
+
else if (effectiveCodepoint >= 97 && effectiveCodepoint <= 122)
|
|
992
|
+
keyName = String.fromCharCode(effectiveCodepoint);
|
|
993
|
+
else if (SYMBOL_KEYS.has(String.fromCharCode(effectiveCodepoint)))
|
|
994
|
+
keyName = String.fromCharCode(effectiveCodepoint);
|
|
995
|
+
if (!keyName)
|
|
996
|
+
return undefined;
|
|
997
|
+
return formatKeyNameWithModifiers(keyName, modifier);
|
|
998
|
+
}
|
|
810
999
|
export function parseKey(data) {
|
|
811
1000
|
const kitty = parseKittySequence(data);
|
|
812
1001
|
if (kitty) {
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
return undefined;
|
|
819
|
-
if (effectiveMod & MODIFIERS.shift)
|
|
820
|
-
mods.push("shift");
|
|
821
|
-
if (effectiveMod & MODIFIERS.ctrl)
|
|
822
|
-
mods.push("ctrl");
|
|
823
|
-
if (effectiveMod & MODIFIERS.alt)
|
|
824
|
-
mods.push("alt");
|
|
825
|
-
// Use base layout key only when codepoint is not a recognized Latin
|
|
826
|
-
// letter (a-z) or symbol (/, -, [, ;, etc.). For those, the codepoint
|
|
827
|
-
// is authoritative regardless of physical key position. This prevents
|
|
828
|
-
// remapped layouts (Dvorak, Colemak, xremap, etc.) from reporting the
|
|
829
|
-
// wrong key name based on the QWERTY physical position.
|
|
830
|
-
const isLatinLetter = codepoint >= 97 && codepoint <= 122; // a-z
|
|
831
|
-
const isKnownSymbol = SYMBOL_KEYS.has(String.fromCharCode(codepoint));
|
|
832
|
-
const effectiveCodepoint = isLatinLetter || isKnownSymbol ? codepoint : (baseLayoutKey ?? codepoint);
|
|
833
|
-
let keyName;
|
|
834
|
-
if (effectiveCodepoint === CODEPOINTS.escape)
|
|
835
|
-
keyName = "escape";
|
|
836
|
-
else if (effectiveCodepoint === CODEPOINTS.tab)
|
|
837
|
-
keyName = "tab";
|
|
838
|
-
else if (effectiveCodepoint === CODEPOINTS.enter || effectiveCodepoint === CODEPOINTS.kpEnter)
|
|
839
|
-
keyName = "enter";
|
|
840
|
-
else if (effectiveCodepoint === CODEPOINTS.space)
|
|
841
|
-
keyName = "space";
|
|
842
|
-
else if (effectiveCodepoint === CODEPOINTS.backspace)
|
|
843
|
-
keyName = "backspace";
|
|
844
|
-
else if (effectiveCodepoint === FUNCTIONAL_CODEPOINTS.delete)
|
|
845
|
-
keyName = "delete";
|
|
846
|
-
else if (effectiveCodepoint === FUNCTIONAL_CODEPOINTS.insert)
|
|
847
|
-
keyName = "insert";
|
|
848
|
-
else if (effectiveCodepoint === FUNCTIONAL_CODEPOINTS.home)
|
|
849
|
-
keyName = "home";
|
|
850
|
-
else if (effectiveCodepoint === FUNCTIONAL_CODEPOINTS.end)
|
|
851
|
-
keyName = "end";
|
|
852
|
-
else if (effectiveCodepoint === FUNCTIONAL_CODEPOINTS.pageUp)
|
|
853
|
-
keyName = "pageUp";
|
|
854
|
-
else if (effectiveCodepoint === FUNCTIONAL_CODEPOINTS.pageDown)
|
|
855
|
-
keyName = "pageDown";
|
|
856
|
-
else if (effectiveCodepoint === ARROW_CODEPOINTS.up)
|
|
857
|
-
keyName = "up";
|
|
858
|
-
else if (effectiveCodepoint === ARROW_CODEPOINTS.down)
|
|
859
|
-
keyName = "down";
|
|
860
|
-
else if (effectiveCodepoint === ARROW_CODEPOINTS.left)
|
|
861
|
-
keyName = "left";
|
|
862
|
-
else if (effectiveCodepoint === ARROW_CODEPOINTS.right)
|
|
863
|
-
keyName = "right";
|
|
864
|
-
else if (effectiveCodepoint >= 97 && effectiveCodepoint <= 122)
|
|
865
|
-
keyName = String.fromCharCode(effectiveCodepoint);
|
|
866
|
-
else if (SYMBOL_KEYS.has(String.fromCharCode(effectiveCodepoint)))
|
|
867
|
-
keyName = String.fromCharCode(effectiveCodepoint);
|
|
868
|
-
if (keyName) {
|
|
869
|
-
return mods.length > 0 ? `${mods.join("+")}+${keyName}` : keyName;
|
|
870
|
-
}
|
|
1002
|
+
return formatParsedKey(kitty.codepoint, kitty.modifier, kitty.baseLayoutKey);
|
|
1003
|
+
}
|
|
1004
|
+
const modifyOtherKeys = parseModifyOtherKeysSequence(data);
|
|
1005
|
+
if (modifyOtherKeys) {
|
|
1006
|
+
return formatParsedKey(modifyOtherKeys.codepoint, modifyOtherKeys.modifier);
|
|
871
1007
|
}
|
|
872
1008
|
// Mode-aware legacy sequences
|
|
873
1009
|
// When Kitty protocol is active, ambiguous sequences are interpreted as custom terminal mappings:
|
|
@@ -905,8 +1041,10 @@ export function parseKey(data) {
|
|
|
905
1041
|
return "ctrl+space";
|
|
906
1042
|
if (data === " ")
|
|
907
1043
|
return "space";
|
|
908
|
-
if (data === "\x7f"
|
|
1044
|
+
if (data === "\x7f")
|
|
909
1045
|
return "backspace";
|
|
1046
|
+
if (data === "\x08")
|
|
1047
|
+
return isWindowsTerminalSession() ? "ctrl+backspace" : "backspace";
|
|
910
1048
|
if (data === "\x1b[Z")
|
|
911
1049
|
return "shift+tab";
|
|
912
1050
|
if (!_kittyProtocolActive && data === "\x1b\r")
|
|
@@ -924,8 +1062,8 @@ export function parseKey(data) {
|
|
|
924
1062
|
if (code >= 1 && code <= 26) {
|
|
925
1063
|
return `ctrl+alt+${String.fromCharCode(code + 96)}`;
|
|
926
1064
|
}
|
|
927
|
-
// Legacy alt+letter (ESC followed by
|
|
928
|
-
if (code >= 97 && code <= 122) {
|
|
1065
|
+
// Legacy alt+letter/digit (ESC followed by the key)
|
|
1066
|
+
if ((code >= 97 && code <= 122) || (code >= 48 && code <= 57)) {
|
|
929
1067
|
return `alt+${String.fromCharCode(code)}`;
|
|
930
1068
|
}
|
|
931
1069
|
}
|
|
@@ -1002,6 +1140,7 @@ export function decodeKittyPrintable(data) {
|
|
|
1002
1140
|
if (modifier & MODIFIERS.shift && typeof shiftedKey === "number") {
|
|
1003
1141
|
effectiveCodepoint = shiftedKey;
|
|
1004
1142
|
}
|
|
1143
|
+
effectiveCodepoint = normalizeKittyFunctionalCodepoint(effectiveCodepoint);
|
|
1005
1144
|
// Drop control characters or invalid codepoints.
|
|
1006
1145
|
if (!Number.isFinite(effectiveCodepoint) || effectiveCodepoint < 32)
|
|
1007
1146
|
return undefined;
|
|
@@ -1012,4 +1151,23 @@ export function decodeKittyPrintable(data) {
|
|
|
1012
1151
|
return undefined;
|
|
1013
1152
|
}
|
|
1014
1153
|
}
|
|
1154
|
+
function decodeModifyOtherKeysPrintable(data) {
|
|
1155
|
+
const parsed = parseModifyOtherKeysSequence(data);
|
|
1156
|
+
if (!parsed)
|
|
1157
|
+
return undefined;
|
|
1158
|
+
const modifier = parsed.modifier & ~LOCK_MASK;
|
|
1159
|
+
if ((modifier & ~MODIFIERS.shift) !== 0)
|
|
1160
|
+
return undefined;
|
|
1161
|
+
if (!Number.isFinite(parsed.codepoint) || parsed.codepoint < 32)
|
|
1162
|
+
return undefined;
|
|
1163
|
+
try {
|
|
1164
|
+
return String.fromCodePoint(parsed.codepoint);
|
|
1165
|
+
}
|
|
1166
|
+
catch {
|
|
1167
|
+
return undefined;
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1170
|
+
export function decodePrintableKey(data) {
|
|
1171
|
+
return decodeKittyPrintable(data) ?? decodeModifyOtherKeysPrintable(data);
|
|
1172
|
+
}
|
|
1015
1173
|
//# sourceMappingURL=keys.js.map
|