@fleetagent/pi-tui 0.1.9 → 0.2.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 +47 -7
- package/dist/autocomplete.d.ts +17 -23
- package/dist/autocomplete.d.ts.map +1 -1
- package/dist/autocomplete.js +129 -195
- package/dist/autocomplete.js.map +1 -1
- package/dist/components/alt-screen-flash.d.ts +13 -0
- package/dist/components/alt-screen-flash.d.ts.map +1 -0
- package/dist/components/alt-screen-flash.js +37 -0
- package/dist/components/alt-screen-flash.js.map +1 -0
- package/dist/components/editor.d.ts +47 -4
- package/dist/components/editor.d.ts.map +1 -1
- package/dist/components/editor.js +615 -585
- package/dist/components/editor.js.map +1 -1
- package/dist/components/h-stack.d.ts +7 -0
- package/dist/components/h-stack.d.ts.map +1 -0
- package/dist/components/h-stack.js +43 -0
- package/dist/components/h-stack.js.map +1 -0
- package/dist/components/image.d.ts +4 -0
- package/dist/components/image.d.ts.map +1 -1
- package/dist/components/image.js +38 -51
- package/dist/components/image.js.map +1 -1
- package/dist/components/input.d.ts +9 -0
- package/dist/components/input.d.ts.map +1 -1
- package/dist/components/input.js +121 -147
- package/dist/components/input.js.map +1 -1
- package/dist/components/markdown.d.ts +34 -0
- package/dist/components/markdown.d.ts.map +1 -1
- package/dist/components/markdown.js +491 -429
- package/dist/components/markdown.js.map +1 -1
- package/dist/components/scroll-view.d.ts +53 -0
- package/dist/components/scroll-view.d.ts.map +1 -0
- package/dist/components/scroll-view.js +173 -0
- package/dist/components/scroll-view.js.map +1 -0
- package/dist/components/select-list.d.ts.map +1 -1
- package/dist/components/select-list.js.map +1 -1
- package/dist/components/settings-list.d.ts +2 -0
- package/dist/components/settings-list.d.ts.map +1 -1
- package/dist/components/settings-list.js +31 -29
- package/dist/components/settings-list.js.map +1 -1
- package/dist/components/stack.d.ts +32 -0
- package/dist/components/stack.d.ts.map +1 -0
- package/dist/components/stack.js +122 -0
- package/dist/components/stack.js.map +1 -0
- package/dist/components/v-stack.d.ts +8 -0
- package/dist/components/v-stack.d.ts.map +1 -0
- package/dist/components/v-stack.js +27 -0
- package/dist/components/v-stack.js.map +1 -0
- package/dist/fuzzy.d.ts.map +1 -1
- package/dist/fuzzy.js +37 -46
- package/dist/fuzzy.js.map +1 -1
- package/dist/index.d.ts +10 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -2
- package/dist/index.js.map +1 -1
- package/dist/keybindings.d.ts +54 -4
- package/dist/keybindings.d.ts.map +1 -1
- package/dist/keybindings.js +39 -4
- package/dist/keybindings.js.map +1 -1
- package/dist/keys.d.ts.map +1 -1
- package/dist/keys.js +359 -479
- package/dist/keys.js.map +1 -1
- package/dist/kill-ring.d.ts +6 -4
- package/dist/kill-ring.d.ts.map +1 -1
- package/dist/kill-ring.js +0 -7
- package/dist/kill-ring.js.map +1 -1
- package/dist/latex.d.ts +10 -0
- package/dist/latex.d.ts.map +1 -0
- package/dist/latex.js +1406 -0
- package/dist/latex.js.map +1 -0
- package/dist/layout-node.d.ts +43 -0
- package/dist/layout-node.d.ts.map +1 -0
- package/dist/layout-node.js +6 -0
- package/dist/layout-node.js.map +1 -0
- package/dist/layout.d.ts +40 -0
- package/dist/layout.d.ts.map +1 -0
- package/dist/layout.js +386 -0
- package/dist/layout.js.map +1 -0
- package/dist/stdin-buffer.d.ts +8 -0
- package/dist/stdin-buffer.d.ts.map +1 -1
- package/dist/stdin-buffer.js +116 -159
- package/dist/stdin-buffer.js.map +1 -1
- package/dist/terminal-image.d.ts +41 -19
- package/dist/terminal-image.d.ts.map +1 -1
- package/dist/terminal-image.js +171 -32
- package/dist/terminal-image.js.map +1 -1
- package/dist/terminal.d.ts +3 -0
- package/dist/terminal.d.ts.map +1 -1
- package/dist/terminal.js +27 -23
- package/dist/terminal.js.map +1 -1
- package/dist/tui-alt-screen.d.ts +114 -0
- package/dist/tui-alt-screen.d.ts.map +1 -0
- package/dist/tui-alt-screen.js +950 -0
- package/dist/tui-alt-screen.js.map +1 -0
- package/dist/tui-main-screen.d.ts +52 -0
- package/dist/tui-main-screen.d.ts.map +1 -0
- package/dist/tui-main-screen.js +511 -0
- package/dist/tui-main-screen.js.map +1 -0
- package/dist/tui.d.ts +112 -44
- package/dist/tui.d.ts.map +1 -1
- package/dist/tui.js +489 -699
- package/dist/tui.js.map +1 -1
- package/dist/utils.d.ts +29 -17
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +605 -528
- package/dist/utils.js.map +1 -1
- package/dist/word-navigation.d.ts.map +1 -1
- package/dist/word-navigation.js +37 -39
- package/dist/word-navigation.js.map +1 -1
- package/package.json +1 -1
package/dist/keys.js
CHANGED
|
@@ -194,6 +194,24 @@ const FUNCTIONAL_CODEPOINTS = {
|
|
|
194
194
|
home: -14,
|
|
195
195
|
end: -15,
|
|
196
196
|
};
|
|
197
|
+
const PARSED_KEY_NAMES_BY_CODEPOINT = new Map([
|
|
198
|
+
[CODEPOINTS.escape, "escape"],
|
|
199
|
+
[CODEPOINTS.tab, "tab"],
|
|
200
|
+
[CODEPOINTS.enter, "enter"],
|
|
201
|
+
[CODEPOINTS.kpEnter, "enter"],
|
|
202
|
+
[CODEPOINTS.space, "space"],
|
|
203
|
+
[CODEPOINTS.backspace, "backspace"],
|
|
204
|
+
[FUNCTIONAL_CODEPOINTS.delete, "delete"],
|
|
205
|
+
[FUNCTIONAL_CODEPOINTS.insert, "insert"],
|
|
206
|
+
[FUNCTIONAL_CODEPOINTS.home, "home"],
|
|
207
|
+
[FUNCTIONAL_CODEPOINTS.end, "end"],
|
|
208
|
+
[FUNCTIONAL_CODEPOINTS.pageUp, "pageUp"],
|
|
209
|
+
[FUNCTIONAL_CODEPOINTS.pageDown, "pageDown"],
|
|
210
|
+
[ARROW_CODEPOINTS.up, "up"],
|
|
211
|
+
[ARROW_CODEPOINTS.down, "down"],
|
|
212
|
+
[ARROW_CODEPOINTS.left, "left"],
|
|
213
|
+
[ARROW_CODEPOINTS.right, "right"],
|
|
214
|
+
]);
|
|
197
215
|
const KITTY_FUNCTIONAL_KEY_EQUIVALENTS = new Map([
|
|
198
216
|
[57399, 48], // KP_0 -> 0
|
|
199
217
|
[57400, 49], // KP_1 -> 1
|
|
@@ -285,6 +303,32 @@ const LEGACY_CTRL_SEQUENCES = {
|
|
|
285
303
|
end: ["\x1b[8^"],
|
|
286
304
|
};
|
|
287
305
|
const LEGACY_SEQUENCE_KEY_IDS = {
|
|
306
|
+
"\x1b": "escape",
|
|
307
|
+
"\x1c": "ctrl+\\",
|
|
308
|
+
"\x1d": "ctrl+]",
|
|
309
|
+
"\x1f": "ctrl+-",
|
|
310
|
+
"\x1b\x1b": "ctrl+alt+[",
|
|
311
|
+
"\x1b\x1c": "ctrl+alt+\\",
|
|
312
|
+
"\x1b\x1d": "ctrl+alt+]",
|
|
313
|
+
"\x1b\x1f": "ctrl+alt+-",
|
|
314
|
+
"\t": "tab",
|
|
315
|
+
"\r": "enter",
|
|
316
|
+
"\x1bOM": "enter",
|
|
317
|
+
"\x00": "ctrl+space",
|
|
318
|
+
" ": "space",
|
|
319
|
+
"\x7f": "backspace",
|
|
320
|
+
"\x1b[Z": "shift+tab",
|
|
321
|
+
"\x1b\x7f": "alt+backspace",
|
|
322
|
+
"\x1b\b": "alt+backspace",
|
|
323
|
+
"\x1b[A": "up",
|
|
324
|
+
"\x1b[B": "down",
|
|
325
|
+
"\x1b[C": "right",
|
|
326
|
+
"\x1b[D": "left",
|
|
327
|
+
"\x1b[H": "home",
|
|
328
|
+
"\x1b[F": "end",
|
|
329
|
+
"\x1b[3~": "delete",
|
|
330
|
+
"\x1b[5~": "pageUp",
|
|
331
|
+
"\x1b[6~": "pageDown",
|
|
288
332
|
"\x1bOA": "up",
|
|
289
333
|
"\x1bOB": "down",
|
|
290
334
|
"\x1bOC": "right",
|
|
@@ -414,7 +458,11 @@ function parseEventType(eventTypeStr) {
|
|
|
414
458
|
return "release";
|
|
415
459
|
return "press";
|
|
416
460
|
}
|
|
417
|
-
function
|
|
461
|
+
function recordParsedKittySequence(sequence) {
|
|
462
|
+
_lastEventType = sequence.eventType;
|
|
463
|
+
return sequence;
|
|
464
|
+
}
|
|
465
|
+
function parseKittyCsiUSequence(data) {
|
|
418
466
|
// CSI u format with alternate keys (flag 4):
|
|
419
467
|
// \x1b[<codepoint>u
|
|
420
468
|
// \x1b[<codepoint>;<mod>u
|
|
@@ -422,58 +470,64 @@ function parseKittySequence(data) {
|
|
|
422
470
|
// \x1b[<codepoint>:<shifted>;<mod>u
|
|
423
471
|
// \x1b[<codepoint>:<shifted>:<base>;<mod>u
|
|
424
472
|
// \x1b[<codepoint>::<base>;<mod>u (no shifted key, only base)
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
const
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
return { codepoint, shiftedKey, baseLayoutKey, modifier: modValue - 1, eventType };
|
|
437
|
-
}
|
|
473
|
+
const match = data.match(/^\x1b\[(\d+)(?::(\d*))?(?::(\d+))?(?:;(\d+))?(?::(\d+))?u$/);
|
|
474
|
+
if (!match)
|
|
475
|
+
return null;
|
|
476
|
+
const codepoint = parseInt(match[1], 10);
|
|
477
|
+
const shiftedKey = match[2] && match[2].length > 0 ? parseInt(match[2], 10) : undefined;
|
|
478
|
+
const baseLayoutKey = match[3] ? parseInt(match[3], 10) : undefined;
|
|
479
|
+
const modValue = match[4] ? parseInt(match[4], 10) : 1;
|
|
480
|
+
const eventType = parseEventType(match[5]);
|
|
481
|
+
return recordParsedKittySequence({ codepoint, shiftedKey, baseLayoutKey, modifier: modValue - 1, eventType });
|
|
482
|
+
}
|
|
483
|
+
function parseKittyArrowSequence(data) {
|
|
438
484
|
// Arrow keys with modifier: \x1b[1;<mod>A/B/C/D or \x1b[1;<mod>:<event>A/B/C/D
|
|
439
|
-
const
|
|
440
|
-
if (
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
485
|
+
const match = data.match(/^\x1b\[1;(\d+)(?::(\d+))?([ABCD])$/);
|
|
486
|
+
if (!match)
|
|
487
|
+
return null;
|
|
488
|
+
const modValue = parseInt(match[1], 10);
|
|
489
|
+
const eventType = parseEventType(match[2]);
|
|
490
|
+
const arrowCodes = { A: -1, B: -2, C: -3, D: -4 };
|
|
491
|
+
return recordParsedKittySequence({
|
|
492
|
+
codepoint: arrowCodes[match[3]],
|
|
493
|
+
modifier: modValue - 1,
|
|
494
|
+
eventType,
|
|
495
|
+
});
|
|
496
|
+
}
|
|
497
|
+
function parseKittyFunctionalSequence(data) {
|
|
447
498
|
// Functional keys: \x1b[<num>~ or \x1b[<num>;<mod>~ or \x1b[<num>;<mod>:<event>~
|
|
448
|
-
const
|
|
449
|
-
if (
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
}
|
|
499
|
+
const match = data.match(/^\x1b\[(\d+)(?:;(\d+))?(?::(\d+))?~$/);
|
|
500
|
+
if (!match)
|
|
501
|
+
return null;
|
|
502
|
+
const keyNum = parseInt(match[1], 10);
|
|
503
|
+
const modValue = match[2] ? parseInt(match[2], 10) : 1;
|
|
504
|
+
const eventType = parseEventType(match[3]);
|
|
505
|
+
const funcCodes = {
|
|
506
|
+
2: FUNCTIONAL_CODEPOINTS.insert,
|
|
507
|
+
3: FUNCTIONAL_CODEPOINTS.delete,
|
|
508
|
+
5: FUNCTIONAL_CODEPOINTS.pageUp,
|
|
509
|
+
6: FUNCTIONAL_CODEPOINTS.pageDown,
|
|
510
|
+
7: FUNCTIONAL_CODEPOINTS.home,
|
|
511
|
+
8: FUNCTIONAL_CODEPOINTS.end,
|
|
512
|
+
};
|
|
513
|
+
const codepoint = funcCodes[keyNum];
|
|
514
|
+
return codepoint === undefined ? null : recordParsedKittySequence({ codepoint, modifier: modValue - 1, eventType });
|
|
515
|
+
}
|
|
516
|
+
function parseKittyHomeEndSequence(data) {
|
|
467
517
|
// Home/End with modifier: \x1b[1;<mod>H/F or \x1b[1;<mod>:<event>H/F
|
|
468
|
-
const
|
|
469
|
-
if (
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
518
|
+
const match = data.match(/^\x1b\[1;(\d+)(?::(\d+))?([HF])$/);
|
|
519
|
+
if (!match)
|
|
520
|
+
return null;
|
|
521
|
+
const modValue = parseInt(match[1], 10);
|
|
522
|
+
const eventType = parseEventType(match[2]);
|
|
523
|
+
const codepoint = match[3] === "H" ? FUNCTIONAL_CODEPOINTS.home : FUNCTIONAL_CODEPOINTS.end;
|
|
524
|
+
return recordParsedKittySequence({ codepoint, modifier: modValue - 1, eventType });
|
|
525
|
+
}
|
|
526
|
+
function parseKittySequence(data) {
|
|
527
|
+
return (parseKittyCsiUSequence(data) ??
|
|
528
|
+
parseKittyArrowSequence(data) ??
|
|
529
|
+
parseKittyFunctionalSequence(data) ??
|
|
530
|
+
parseKittyHomeEndSequence(data));
|
|
477
531
|
}
|
|
478
532
|
function matchesKittySequence(data, expectedCodepoint, expectedModifier) {
|
|
479
533
|
const parsed = parseKittySequence(data);
|
|
@@ -613,37 +667,139 @@ function parseKeyId(keyId) {
|
|
|
613
667
|
super: parts.includes("super"),
|
|
614
668
|
};
|
|
615
669
|
}
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
670
|
+
const LEGACY_FUNCTIONAL_KEY_MATCHES = {
|
|
671
|
+
insert: { legacyKey: "insert", codepoint: FUNCTIONAL_CODEPOINTS.insert },
|
|
672
|
+
delete: { legacyKey: "delete", codepoint: FUNCTIONAL_CODEPOINTS.delete },
|
|
673
|
+
home: { legacyKey: "home", codepoint: FUNCTIONAL_CODEPOINTS.home },
|
|
674
|
+
end: { legacyKey: "end", codepoint: FUNCTIONAL_CODEPOINTS.end },
|
|
675
|
+
pageup: { legacyKey: "pageUp", codepoint: FUNCTIONAL_CODEPOINTS.pageUp },
|
|
676
|
+
pagedown: { legacyKey: "pageDown", codepoint: FUNCTIONAL_CODEPOINTS.pageDown },
|
|
677
|
+
};
|
|
678
|
+
const ARROW_KEY_MATCHES = {
|
|
679
|
+
up: { legacyKey: "up", codepoint: ARROW_CODEPOINTS.up },
|
|
680
|
+
down: { legacyKey: "down", codepoint: ARROW_CODEPOINTS.down },
|
|
681
|
+
left: { legacyKey: "left", codepoint: ARROW_CODEPOINTS.left },
|
|
682
|
+
right: { legacyKey: "right", codepoint: ARROW_CODEPOINTS.right },
|
|
683
|
+
};
|
|
684
|
+
const ALT_ARROW_SEQUENCES = {
|
|
685
|
+
up: ["\x1bp"],
|
|
686
|
+
down: ["\x1bn"],
|
|
687
|
+
left: ["\x1b[1;3D", "\x1bb"],
|
|
688
|
+
right: ["\x1b[1;3C", "\x1bf"],
|
|
689
|
+
};
|
|
690
|
+
const LEGACY_ALT_ARROW_SEQUENCES = {
|
|
691
|
+
left: "\x1bB",
|
|
692
|
+
right: "\x1bF",
|
|
693
|
+
};
|
|
694
|
+
const FUNCTION_KEY_NAMES = new Set(["f1", "f2", "f3", "f4", "f5", "f6", "f7", "f8", "f9", "f10", "f11", "f12"]);
|
|
695
|
+
function getModifierMask(parsed) {
|
|
638
696
|
let modifier = 0;
|
|
639
|
-
if (shift)
|
|
697
|
+
if (parsed.shift)
|
|
640
698
|
modifier |= MODIFIERS.shift;
|
|
641
|
-
if (alt)
|
|
699
|
+
if (parsed.alt)
|
|
642
700
|
modifier |= MODIFIERS.alt;
|
|
643
|
-
if (ctrl)
|
|
701
|
+
if (parsed.ctrl)
|
|
644
702
|
modifier |= MODIFIERS.ctrl;
|
|
645
|
-
if (
|
|
703
|
+
if (parsed.super)
|
|
646
704
|
modifier |= MODIFIERS.super;
|
|
705
|
+
return modifier;
|
|
706
|
+
}
|
|
707
|
+
function matchesEncodedKey(data, codepoint, modifier) {
|
|
708
|
+
return matchesKittySequence(data, codepoint, modifier) || matchesModifyOtherKeys(data, codepoint, modifier);
|
|
709
|
+
}
|
|
710
|
+
function matchesSpaceKey(data, modifier) {
|
|
711
|
+
if (!_kittyProtocolActive && modifier === MODIFIERS.ctrl && data === "\x00")
|
|
712
|
+
return true;
|
|
713
|
+
if (!_kittyProtocolActive && modifier === MODIFIERS.alt && data === "\x1b ")
|
|
714
|
+
return true;
|
|
715
|
+
if (modifier === 0)
|
|
716
|
+
return data === " " || matchesEncodedKey(data, CODEPOINTS.space, 0);
|
|
717
|
+
return matchesEncodedKey(data, CODEPOINTS.space, modifier);
|
|
718
|
+
}
|
|
719
|
+
function matchesTabKey(data, modifier) {
|
|
720
|
+
if (modifier === MODIFIERS.shift) {
|
|
721
|
+
return data === "\x1b[Z" || matchesEncodedKey(data, CODEPOINTS.tab, MODIFIERS.shift);
|
|
722
|
+
}
|
|
723
|
+
if (modifier === 0)
|
|
724
|
+
return data === "\t" || matchesKittySequence(data, CODEPOINTS.tab, 0);
|
|
725
|
+
return matchesEncodedKey(data, CODEPOINTS.tab, modifier);
|
|
726
|
+
}
|
|
727
|
+
function matchesKittyEnter(data, modifier) {
|
|
728
|
+
return (matchesKittySequence(data, CODEPOINTS.enter, modifier) || matchesKittySequence(data, CODEPOINTS.kpEnter, modifier));
|
|
729
|
+
}
|
|
730
|
+
function matchesEnterKey(data, modifier) {
|
|
731
|
+
if (modifier === MODIFIERS.shift) {
|
|
732
|
+
if (matchesKittyEnter(data, modifier) || matchesModifyOtherKeys(data, CODEPOINTS.enter, modifier))
|
|
733
|
+
return true;
|
|
734
|
+
// Kitty's custom mapping and Ghostty's text mapping for Shift+Enter.
|
|
735
|
+
return _kittyProtocolActive && (data === "\x1b\r" || data === "\n");
|
|
736
|
+
}
|
|
737
|
+
if (modifier === MODIFIERS.alt) {
|
|
738
|
+
if (matchesKittyEnter(data, modifier) || matchesModifyOtherKeys(data, CODEPOINTS.enter, modifier))
|
|
739
|
+
return true;
|
|
740
|
+
return !_kittyProtocolActive && data === "\x1b\r";
|
|
741
|
+
}
|
|
742
|
+
if (modifier === 0) {
|
|
743
|
+
return (data === "\r" || (!_kittyProtocolActive && data === "\n") || data === "\x1bOM" || matchesKittyEnter(data, 0));
|
|
744
|
+
}
|
|
745
|
+
return matchesKittyEnter(data, modifier) || matchesModifyOtherKeys(data, CODEPOINTS.enter, modifier);
|
|
746
|
+
}
|
|
747
|
+
function matchesBackspaceKey(data, modifier) {
|
|
748
|
+
if (modifier === MODIFIERS.alt) {
|
|
749
|
+
if (data === "\x1b\x7f" || data === "\x1b\b")
|
|
750
|
+
return true;
|
|
751
|
+
return matchesEncodedKey(data, CODEPOINTS.backspace, modifier);
|
|
752
|
+
}
|
|
753
|
+
if (modifier === MODIFIERS.ctrl) {
|
|
754
|
+
// Raw BS is ambiguous between Ctrl+Backspace and plain Backspace.
|
|
755
|
+
if (matchesRawBackspace(data, modifier))
|
|
756
|
+
return true;
|
|
757
|
+
return matchesEncodedKey(data, CODEPOINTS.backspace, modifier);
|
|
758
|
+
}
|
|
759
|
+
if (modifier === 0) {
|
|
760
|
+
return matchesRawBackspace(data, 0) || matchesEncodedKey(data, CODEPOINTS.backspace, 0);
|
|
761
|
+
}
|
|
762
|
+
return matchesEncodedKey(data, CODEPOINTS.backspace, modifier);
|
|
763
|
+
}
|
|
764
|
+
function matchesLegacyFunctionalKey(data, modifier, match) {
|
|
765
|
+
if (modifier === 0) {
|
|
766
|
+
return (matchesLegacySequence(data, LEGACY_KEY_SEQUENCES[match.legacyKey]) ||
|
|
767
|
+
matchesKittySequence(data, match.codepoint, 0));
|
|
768
|
+
}
|
|
769
|
+
return (matchesLegacyModifierSequence(data, match.legacyKey, modifier) ||
|
|
770
|
+
matchesKittySequence(data, match.codepoint, modifier));
|
|
771
|
+
}
|
|
772
|
+
function matchesClearKey(data, modifier) {
|
|
773
|
+
if (modifier === 0)
|
|
774
|
+
return matchesLegacySequence(data, LEGACY_KEY_SEQUENCES.clear);
|
|
775
|
+
return matchesLegacyModifierSequence(data, "clear", modifier);
|
|
776
|
+
}
|
|
777
|
+
function matchesLegacyAltArrow(data, key) {
|
|
778
|
+
if (ALT_ARROW_SEQUENCES[key].includes(data))
|
|
779
|
+
return true;
|
|
780
|
+
return !_kittyProtocolActive && LEGACY_ALT_ARROW_SEQUENCES[key] === data;
|
|
781
|
+
}
|
|
782
|
+
function matchesArrowKey(data, key, modifier) {
|
|
783
|
+
const match = ARROW_KEY_MATCHES[key];
|
|
784
|
+
if (modifier === MODIFIERS.alt && matchesLegacyAltArrow(data, key))
|
|
785
|
+
return true;
|
|
786
|
+
return matchesLegacyFunctionalKey(data, modifier, match);
|
|
787
|
+
}
|
|
788
|
+
function matchesFunctionKey(data, key, modifier) {
|
|
789
|
+
if (modifier !== 0)
|
|
790
|
+
return false;
|
|
791
|
+
const functionKey = key;
|
|
792
|
+
return matchesLegacySequence(data, LEGACY_KEY_SEQUENCES[functionKey]);
|
|
793
|
+
}
|
|
794
|
+
function matchesNamedKey(data, key, modifier) {
|
|
795
|
+
const functionalMatch = LEGACY_FUNCTIONAL_KEY_MATCHES[key];
|
|
796
|
+
if (functionalMatch)
|
|
797
|
+
return matchesLegacyFunctionalKey(data, modifier, functionalMatch);
|
|
798
|
+
const arrowMatch = ARROW_KEY_MATCHES[key];
|
|
799
|
+
if (arrowMatch)
|
|
800
|
+
return matchesArrowKey(data, key, modifier);
|
|
801
|
+
if (FUNCTION_KEY_NAMES.has(key))
|
|
802
|
+
return matchesFunctionKey(data, key, modifier);
|
|
647
803
|
switch (key) {
|
|
648
804
|
case "escape":
|
|
649
805
|
case "esc":
|
|
@@ -653,290 +809,87 @@ export function matchesKey(data, keyId) {
|
|
|
653
809
|
matchesKittySequence(data, CODEPOINTS.escape, 0) ||
|
|
654
810
|
matchesModifyOtherKeys(data, CODEPOINTS.escape, 0));
|
|
655
811
|
case "space":
|
|
656
|
-
|
|
657
|
-
if (modifier === MODIFIERS.ctrl && data === "\x00") {
|
|
658
|
-
return true;
|
|
659
|
-
}
|
|
660
|
-
if (modifier === MODIFIERS.alt && data === "\x1b ") {
|
|
661
|
-
return true;
|
|
662
|
-
}
|
|
663
|
-
}
|
|
664
|
-
if (modifier === 0) {
|
|
665
|
-
return (data === " " ||
|
|
666
|
-
matchesKittySequence(data, CODEPOINTS.space, 0) ||
|
|
667
|
-
matchesModifyOtherKeys(data, CODEPOINTS.space, 0));
|
|
668
|
-
}
|
|
669
|
-
return (matchesKittySequence(data, CODEPOINTS.space, modifier) ||
|
|
670
|
-
matchesModifyOtherKeys(data, CODEPOINTS.space, modifier));
|
|
812
|
+
return matchesSpaceKey(data, modifier);
|
|
671
813
|
case "tab":
|
|
672
|
-
|
|
673
|
-
return (data === "\x1b[Z" ||
|
|
674
|
-
matchesKittySequence(data, CODEPOINTS.tab, MODIFIERS.shift) ||
|
|
675
|
-
matchesModifyOtherKeys(data, CODEPOINTS.tab, MODIFIERS.shift));
|
|
676
|
-
}
|
|
677
|
-
if (modifier === 0) {
|
|
678
|
-
return data === "\t" || matchesKittySequence(data, CODEPOINTS.tab, 0);
|
|
679
|
-
}
|
|
680
|
-
return (matchesKittySequence(data, CODEPOINTS.tab, modifier) ||
|
|
681
|
-
matchesModifyOtherKeys(data, CODEPOINTS.tab, modifier));
|
|
814
|
+
return matchesTabKey(data, modifier);
|
|
682
815
|
case "enter":
|
|
683
816
|
case "return":
|
|
684
|
-
|
|
685
|
-
// CSI u sequences (standard Kitty protocol)
|
|
686
|
-
if (matchesKittySequence(data, CODEPOINTS.enter, MODIFIERS.shift) ||
|
|
687
|
-
matchesKittySequence(data, CODEPOINTS.kpEnter, MODIFIERS.shift)) {
|
|
688
|
-
return true;
|
|
689
|
-
}
|
|
690
|
-
// xterm modifyOtherKeys format (fallback when Kitty protocol not enabled)
|
|
691
|
-
if (matchesModifyOtherKeys(data, CODEPOINTS.enter, MODIFIERS.shift)) {
|
|
692
|
-
return true;
|
|
693
|
-
}
|
|
694
|
-
// When Kitty protocol is active, legacy sequences are custom terminal mappings
|
|
695
|
-
// \x1b\r = Kitty's "map shift+enter send_text all \e\r"
|
|
696
|
-
// \n = Ghostty's "keybind = shift+enter=text:\n"
|
|
697
|
-
if (_kittyProtocolActive) {
|
|
698
|
-
return data === "\x1b\r" || data === "\n";
|
|
699
|
-
}
|
|
700
|
-
return false;
|
|
701
|
-
}
|
|
702
|
-
if (modifier === MODIFIERS.alt) {
|
|
703
|
-
// CSI u sequences (standard Kitty protocol)
|
|
704
|
-
if (matchesKittySequence(data, CODEPOINTS.enter, MODIFIERS.alt) ||
|
|
705
|
-
matchesKittySequence(data, CODEPOINTS.kpEnter, MODIFIERS.alt)) {
|
|
706
|
-
return true;
|
|
707
|
-
}
|
|
708
|
-
// xterm modifyOtherKeys format (fallback when Kitty protocol not enabled)
|
|
709
|
-
if (matchesModifyOtherKeys(data, CODEPOINTS.enter, MODIFIERS.alt)) {
|
|
710
|
-
return true;
|
|
711
|
-
}
|
|
712
|
-
// \x1b\r is alt+enter only in legacy mode (no Kitty protocol)
|
|
713
|
-
// When Kitty protocol is active, alt+enter comes as CSI u sequence
|
|
714
|
-
if (!_kittyProtocolActive) {
|
|
715
|
-
return data === "\x1b\r";
|
|
716
|
-
}
|
|
717
|
-
return false;
|
|
718
|
-
}
|
|
719
|
-
if (modifier === 0) {
|
|
720
|
-
return (data === "\r" ||
|
|
721
|
-
(!_kittyProtocolActive && data === "\n") ||
|
|
722
|
-
data === "\x1bOM" || // SS3 M (numpad enter in some terminals)
|
|
723
|
-
matchesKittySequence(data, CODEPOINTS.enter, 0) ||
|
|
724
|
-
matchesKittySequence(data, CODEPOINTS.kpEnter, 0));
|
|
725
|
-
}
|
|
726
|
-
return (matchesKittySequence(data, CODEPOINTS.enter, modifier) ||
|
|
727
|
-
matchesKittySequence(data, CODEPOINTS.kpEnter, modifier) ||
|
|
728
|
-
matchesModifyOtherKeys(data, CODEPOINTS.enter, modifier));
|
|
817
|
+
return matchesEnterKey(data, modifier);
|
|
729
818
|
case "backspace":
|
|
730
|
-
|
|
731
|
-
if (data === "\x1b\x7f" || data === "\x1b\b") {
|
|
732
|
-
return true;
|
|
733
|
-
}
|
|
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));
|
|
745
|
-
}
|
|
746
|
-
if (modifier === 0) {
|
|
747
|
-
return (matchesRawBackspace(data, 0) ||
|
|
748
|
-
matchesKittySequence(data, CODEPOINTS.backspace, 0) ||
|
|
749
|
-
matchesModifyOtherKeys(data, CODEPOINTS.backspace, 0));
|
|
750
|
-
}
|
|
751
|
-
return (matchesKittySequence(data, CODEPOINTS.backspace, modifier) ||
|
|
752
|
-
matchesModifyOtherKeys(data, CODEPOINTS.backspace, modifier));
|
|
753
|
-
case "insert":
|
|
754
|
-
if (modifier === 0) {
|
|
755
|
-
return (matchesLegacySequence(data, LEGACY_KEY_SEQUENCES.insert) ||
|
|
756
|
-
matchesKittySequence(data, FUNCTIONAL_CODEPOINTS.insert, 0));
|
|
757
|
-
}
|
|
758
|
-
if (matchesLegacyModifierSequence(data, "insert", modifier)) {
|
|
759
|
-
return true;
|
|
760
|
-
}
|
|
761
|
-
return matchesKittySequence(data, FUNCTIONAL_CODEPOINTS.insert, modifier);
|
|
762
|
-
case "delete":
|
|
763
|
-
if (modifier === 0) {
|
|
764
|
-
return (matchesLegacySequence(data, LEGACY_KEY_SEQUENCES.delete) ||
|
|
765
|
-
matchesKittySequence(data, FUNCTIONAL_CODEPOINTS.delete, 0));
|
|
766
|
-
}
|
|
767
|
-
if (matchesLegacyModifierSequence(data, "delete", modifier)) {
|
|
768
|
-
return true;
|
|
769
|
-
}
|
|
770
|
-
return matchesKittySequence(data, FUNCTIONAL_CODEPOINTS.delete, modifier);
|
|
819
|
+
return matchesBackspaceKey(data, modifier);
|
|
771
820
|
case "clear":
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
return matchesLegacyModifierSequence(data, "clear", modifier);
|
|
776
|
-
case "home":
|
|
777
|
-
if (modifier === 0) {
|
|
778
|
-
return (matchesLegacySequence(data, LEGACY_KEY_SEQUENCES.home) ||
|
|
779
|
-
matchesKittySequence(data, FUNCTIONAL_CODEPOINTS.home, 0));
|
|
780
|
-
}
|
|
781
|
-
if (matchesLegacyModifierSequence(data, "home", modifier)) {
|
|
782
|
-
return true;
|
|
783
|
-
}
|
|
784
|
-
return matchesKittySequence(data, FUNCTIONAL_CODEPOINTS.home, modifier);
|
|
785
|
-
case "end":
|
|
786
|
-
if (modifier === 0) {
|
|
787
|
-
return (matchesLegacySequence(data, LEGACY_KEY_SEQUENCES.end) ||
|
|
788
|
-
matchesKittySequence(data, FUNCTIONAL_CODEPOINTS.end, 0));
|
|
789
|
-
}
|
|
790
|
-
if (matchesLegacyModifierSequence(data, "end", modifier)) {
|
|
791
|
-
return true;
|
|
792
|
-
}
|
|
793
|
-
return matchesKittySequence(data, FUNCTIONAL_CODEPOINTS.end, modifier);
|
|
794
|
-
case "pageup":
|
|
795
|
-
if (modifier === 0) {
|
|
796
|
-
return (matchesLegacySequence(data, LEGACY_KEY_SEQUENCES.pageUp) ||
|
|
797
|
-
matchesKittySequence(data, FUNCTIONAL_CODEPOINTS.pageUp, 0));
|
|
798
|
-
}
|
|
799
|
-
if (matchesLegacyModifierSequence(data, "pageUp", modifier)) {
|
|
800
|
-
return true;
|
|
801
|
-
}
|
|
802
|
-
return matchesKittySequence(data, FUNCTIONAL_CODEPOINTS.pageUp, modifier);
|
|
803
|
-
case "pagedown":
|
|
804
|
-
if (modifier === 0) {
|
|
805
|
-
return (matchesLegacySequence(data, LEGACY_KEY_SEQUENCES.pageDown) ||
|
|
806
|
-
matchesKittySequence(data, FUNCTIONAL_CODEPOINTS.pageDown, 0));
|
|
807
|
-
}
|
|
808
|
-
if (matchesLegacyModifierSequence(data, "pageDown", modifier)) {
|
|
809
|
-
return true;
|
|
810
|
-
}
|
|
811
|
-
return matchesKittySequence(data, FUNCTIONAL_CODEPOINTS.pageDown, modifier);
|
|
812
|
-
case "up":
|
|
813
|
-
if (modifier === MODIFIERS.alt) {
|
|
814
|
-
return data === "\x1bp" || matchesKittySequence(data, ARROW_CODEPOINTS.up, MODIFIERS.alt);
|
|
815
|
-
}
|
|
816
|
-
if (modifier === 0) {
|
|
817
|
-
return (matchesLegacySequence(data, LEGACY_KEY_SEQUENCES.up) ||
|
|
818
|
-
matchesKittySequence(data, ARROW_CODEPOINTS.up, 0));
|
|
819
|
-
}
|
|
820
|
-
if (matchesLegacyModifierSequence(data, "up", modifier)) {
|
|
821
|
-
return true;
|
|
822
|
-
}
|
|
823
|
-
return matchesKittySequence(data, ARROW_CODEPOINTS.up, modifier);
|
|
824
|
-
case "down":
|
|
825
|
-
if (modifier === MODIFIERS.alt) {
|
|
826
|
-
return data === "\x1bn" || matchesKittySequence(data, ARROW_CODEPOINTS.down, MODIFIERS.alt);
|
|
827
|
-
}
|
|
828
|
-
if (modifier === 0) {
|
|
829
|
-
return (matchesLegacySequence(data, LEGACY_KEY_SEQUENCES.down) ||
|
|
830
|
-
matchesKittySequence(data, ARROW_CODEPOINTS.down, 0));
|
|
831
|
-
}
|
|
832
|
-
if (matchesLegacyModifierSequence(data, "down", modifier)) {
|
|
833
|
-
return true;
|
|
834
|
-
}
|
|
835
|
-
return matchesKittySequence(data, ARROW_CODEPOINTS.down, modifier);
|
|
836
|
-
case "left":
|
|
837
|
-
if (modifier === MODIFIERS.alt) {
|
|
838
|
-
return (data === "\x1b[1;3D" ||
|
|
839
|
-
(!_kittyProtocolActive && data === "\x1bB") ||
|
|
840
|
-
data === "\x1bb" ||
|
|
841
|
-
matchesKittySequence(data, ARROW_CODEPOINTS.left, MODIFIERS.alt));
|
|
842
|
-
}
|
|
843
|
-
if (modifier === MODIFIERS.ctrl) {
|
|
844
|
-
return (data === "\x1b[1;5D" ||
|
|
845
|
-
matchesLegacyModifierSequence(data, "left", MODIFIERS.ctrl) ||
|
|
846
|
-
matchesKittySequence(data, ARROW_CODEPOINTS.left, MODIFIERS.ctrl));
|
|
847
|
-
}
|
|
848
|
-
if (modifier === 0) {
|
|
849
|
-
return (matchesLegacySequence(data, LEGACY_KEY_SEQUENCES.left) ||
|
|
850
|
-
matchesKittySequence(data, ARROW_CODEPOINTS.left, 0));
|
|
851
|
-
}
|
|
852
|
-
if (matchesLegacyModifierSequence(data, "left", modifier)) {
|
|
853
|
-
return true;
|
|
854
|
-
}
|
|
855
|
-
return matchesKittySequence(data, ARROW_CODEPOINTS.left, modifier);
|
|
856
|
-
case "right":
|
|
857
|
-
if (modifier === MODIFIERS.alt) {
|
|
858
|
-
return (data === "\x1b[1;3C" ||
|
|
859
|
-
(!_kittyProtocolActive && data === "\x1bF") ||
|
|
860
|
-
data === "\x1bf" ||
|
|
861
|
-
matchesKittySequence(data, ARROW_CODEPOINTS.right, MODIFIERS.alt));
|
|
862
|
-
}
|
|
863
|
-
if (modifier === MODIFIERS.ctrl) {
|
|
864
|
-
return (data === "\x1b[1;5C" ||
|
|
865
|
-
matchesLegacyModifierSequence(data, "right", MODIFIERS.ctrl) ||
|
|
866
|
-
matchesKittySequence(data, ARROW_CODEPOINTS.right, MODIFIERS.ctrl));
|
|
867
|
-
}
|
|
868
|
-
if (modifier === 0) {
|
|
869
|
-
return (matchesLegacySequence(data, LEGACY_KEY_SEQUENCES.right) ||
|
|
870
|
-
matchesKittySequence(data, ARROW_CODEPOINTS.right, 0));
|
|
871
|
-
}
|
|
872
|
-
if (matchesLegacyModifierSequence(data, "right", modifier)) {
|
|
873
|
-
return true;
|
|
874
|
-
}
|
|
875
|
-
return matchesKittySequence(data, ARROW_CODEPOINTS.right, modifier);
|
|
876
|
-
case "f1":
|
|
877
|
-
case "f2":
|
|
878
|
-
case "f3":
|
|
879
|
-
case "f4":
|
|
880
|
-
case "f5":
|
|
881
|
-
case "f6":
|
|
882
|
-
case "f7":
|
|
883
|
-
case "f8":
|
|
884
|
-
case "f9":
|
|
885
|
-
case "f10":
|
|
886
|
-
case "f11":
|
|
887
|
-
case "f12": {
|
|
888
|
-
if (modifier !== 0) {
|
|
889
|
-
return false;
|
|
890
|
-
}
|
|
891
|
-
const functionKey = key;
|
|
892
|
-
return matchesLegacySequence(data, LEGACY_KEY_SEQUENCES[functionKey]);
|
|
893
|
-
}
|
|
894
|
-
}
|
|
895
|
-
// Handle single letter/digit keys and symbols
|
|
896
|
-
if (key.length === 1 && ((key >= "a" && key <= "z") || isDigitKey(key) || SYMBOL_KEYS.has(key))) {
|
|
897
|
-
const codepoint = key.charCodeAt(0);
|
|
898
|
-
const rawCtrl = rawCtrlChar(key);
|
|
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;
|
|
907
|
-
}
|
|
908
|
-
if (modifier === MODIFIERS.alt && !_kittyProtocolActive && (isLetter || isDigit)) {
|
|
909
|
-
// Legacy: alt+letter/digit is ESC followed by the key
|
|
910
|
-
if (data === `\x1b${key}`)
|
|
911
|
-
return true;
|
|
912
|
-
}
|
|
913
|
-
if (modifier === MODIFIERS.ctrl) {
|
|
914
|
-
// Legacy: ctrl+key sends the control character
|
|
915
|
-
if (rawCtrl && data === rawCtrl)
|
|
916
|
-
return true;
|
|
917
|
-
return (matchesKittySequence(data, codepoint, MODIFIERS.ctrl) ||
|
|
918
|
-
matchesPrintableModifyOtherKeys(data, codepoint, MODIFIERS.ctrl));
|
|
919
|
-
}
|
|
920
|
-
if (modifier === MODIFIERS.shift + MODIFIERS.ctrl) {
|
|
921
|
-
return (matchesKittySequence(data, codepoint, MODIFIERS.shift + MODIFIERS.ctrl) ||
|
|
922
|
-
matchesPrintableModifyOtherKeys(data, codepoint, MODIFIERS.shift + MODIFIERS.ctrl));
|
|
923
|
-
}
|
|
924
|
-
if (modifier === MODIFIERS.shift) {
|
|
925
|
-
// Legacy: shift+letter produces uppercase
|
|
926
|
-
if (isLetter && data === key.toUpperCase())
|
|
927
|
-
return true;
|
|
928
|
-
return (matchesKittySequence(data, codepoint, MODIFIERS.shift) ||
|
|
929
|
-
matchesPrintableModifyOtherKeys(data, codepoint, MODIFIERS.shift));
|
|
930
|
-
}
|
|
931
|
-
if (modifier !== 0) {
|
|
932
|
-
return (matchesKittySequence(data, codepoint, modifier) ||
|
|
933
|
-
matchesPrintableModifyOtherKeys(data, codepoint, modifier));
|
|
934
|
-
}
|
|
935
|
-
// Check both raw char and Kitty sequence (needed for release events)
|
|
936
|
-
return data === key || matchesKittySequence(data, codepoint, 0);
|
|
821
|
+
return matchesClearKey(data, modifier);
|
|
822
|
+
default:
|
|
823
|
+
return undefined;
|
|
937
824
|
}
|
|
825
|
+
}
|
|
826
|
+
function isPrintableKeyName(key) {
|
|
827
|
+
return key.length === 1 && ((key >= "a" && key <= "z") || isDigitKey(key) || SYMBOL_KEYS.has(key));
|
|
828
|
+
}
|
|
829
|
+
function matchesLegacyPrintable(data, key, modifier, rawCtrl) {
|
|
830
|
+
if (_kittyProtocolActive)
|
|
831
|
+
return false;
|
|
832
|
+
if (modifier === MODIFIERS.ctrl + MODIFIERS.alt)
|
|
833
|
+
return rawCtrl !== null && data === `\x1b${rawCtrl}`;
|
|
834
|
+
if (modifier === MODIFIERS.alt)
|
|
835
|
+
return data === `\x1b${key}`;
|
|
938
836
|
return false;
|
|
939
837
|
}
|
|
838
|
+
function matchesPrintableEncoding(data, codepoint, modifier) {
|
|
839
|
+
return matchesKittySequence(data, codepoint, modifier) || matchesPrintableModifyOtherKeys(data, codepoint, modifier);
|
|
840
|
+
}
|
|
841
|
+
function matchesPrintableKey(data, key, modifier) {
|
|
842
|
+
if (!isPrintableKeyName(key))
|
|
843
|
+
return false;
|
|
844
|
+
const codepoint = key.charCodeAt(0);
|
|
845
|
+
const rawCtrl = rawCtrlChar(key);
|
|
846
|
+
const isLetter = key >= "a" && key <= "z";
|
|
847
|
+
if (matchesLegacyPrintable(data, key, modifier, rawCtrl))
|
|
848
|
+
return true;
|
|
849
|
+
if (modifier === MODIFIERS.ctrl) {
|
|
850
|
+
if (rawCtrl && data === rawCtrl)
|
|
851
|
+
return true;
|
|
852
|
+
return matchesPrintableEncoding(data, codepoint, modifier);
|
|
853
|
+
}
|
|
854
|
+
if (modifier === MODIFIERS.shift + MODIFIERS.ctrl) {
|
|
855
|
+
return matchesPrintableEncoding(data, codepoint, modifier);
|
|
856
|
+
}
|
|
857
|
+
if (modifier === MODIFIERS.shift) {
|
|
858
|
+
if (isLetter && data === key.toUpperCase())
|
|
859
|
+
return true;
|
|
860
|
+
return matchesPrintableEncoding(data, codepoint, modifier);
|
|
861
|
+
}
|
|
862
|
+
if (modifier !== 0)
|
|
863
|
+
return matchesPrintableEncoding(data, codepoint, modifier);
|
|
864
|
+
return data === key || matchesKittySequence(data, codepoint, 0);
|
|
865
|
+
}
|
|
866
|
+
/**
|
|
867
|
+
* Match input data against a key identifier string.
|
|
868
|
+
*
|
|
869
|
+
* Supported key identifiers:
|
|
870
|
+
* - Single keys: "escape", "tab", "enter", "backspace", "delete", "home", "end", "space"
|
|
871
|
+
* - Arrow keys: "up", "down", "left", "right"
|
|
872
|
+
* - Ctrl combinations: "ctrl+c", "ctrl+z", etc.
|
|
873
|
+
* - Shift combinations: "shift+tab", "shift+enter"
|
|
874
|
+
* - Alt combinations: "alt+enter", "alt+backspace"
|
|
875
|
+
* - Super combinations: "super+k", "super+enter"
|
|
876
|
+
* - Combined modifiers: "shift+ctrl+p", "ctrl+alt+x", "ctrl+super+k"
|
|
877
|
+
*
|
|
878
|
+
* Use the Key helper for autocomplete: Key.ctrl("c"), Key.escape, Key.ctrlShift("p"), Key.super("k")
|
|
879
|
+
*
|
|
880
|
+
* @param data - Raw input data from terminal
|
|
881
|
+
* @param keyId - Key identifier (e.g., "ctrl+c", "escape", Key.ctrl("c"))
|
|
882
|
+
*/
|
|
883
|
+
export function matchesKey(data, keyId) {
|
|
884
|
+
const parsed = parseKeyId(keyId);
|
|
885
|
+
if (!parsed)
|
|
886
|
+
return false;
|
|
887
|
+
const modifier = getModifierMask(parsed);
|
|
888
|
+
const namedMatch = matchesNamedKey(data, parsed.key, modifier);
|
|
889
|
+
if (namedMatch !== undefined)
|
|
890
|
+
return namedMatch;
|
|
891
|
+
return matchesPrintableKey(data, parsed.key, modifier);
|
|
892
|
+
}
|
|
940
893
|
/**
|
|
941
894
|
* Parse input data and return the key identifier if recognized.
|
|
942
895
|
*
|
|
@@ -955,148 +908,75 @@ function formatParsedKey(codepoint, modifier, baseLayoutKey) {
|
|
|
955
908
|
const isDigit = identityCodepoint >= 48 && identityCodepoint <= 57; // 0-9
|
|
956
909
|
const isKnownSymbol = SYMBOL_KEYS.has(String.fromCharCode(identityCodepoint));
|
|
957
910
|
const effectiveCodepoint = isLatinLetter || isDigit || isKnownSymbol ? identityCodepoint : (baseLayoutKey ?? identityCodepoint);
|
|
958
|
-
let keyName;
|
|
959
|
-
if (
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
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);
|
|
911
|
+
let keyName = PARSED_KEY_NAMES_BY_CODEPOINT.get(effectiveCodepoint);
|
|
912
|
+
if (!keyName) {
|
|
913
|
+
const character = String.fromCharCode(effectiveCodepoint);
|
|
914
|
+
const isDigitCharacter = effectiveCodepoint >= 48 && effectiveCodepoint <= 57;
|
|
915
|
+
const isLowercaseLetter = effectiveCodepoint >= 97 && effectiveCodepoint <= 122;
|
|
916
|
+
if (isDigitCharacter || isLowercaseLetter || SYMBOL_KEYS.has(character))
|
|
917
|
+
keyName = character;
|
|
918
|
+
}
|
|
995
919
|
if (!keyName)
|
|
996
920
|
return undefined;
|
|
997
921
|
return formatKeyNameWithModifiers(keyName, modifier);
|
|
998
922
|
}
|
|
999
|
-
|
|
923
|
+
function parseTerminalEncodedKey(data) {
|
|
1000
924
|
const kitty = parseKittySequence(data);
|
|
1001
|
-
if (kitty)
|
|
925
|
+
if (kitty)
|
|
1002
926
|
return formatParsedKey(kitty.codepoint, kitty.modifier, kitty.baseLayoutKey);
|
|
1003
|
-
}
|
|
1004
927
|
const modifyOtherKeys = parseModifyOtherKeysSequence(data);
|
|
1005
|
-
if (modifyOtherKeys)
|
|
1006
|
-
return
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
// - \x1b\r = shift+enter (Kitty mapping), not alt+enter
|
|
1011
|
-
// - \n = shift+enter (Ghostty mapping)
|
|
1012
|
-
if (_kittyProtocolActive) {
|
|
1013
|
-
if (data === "\x1b\r" || data === "\n")
|
|
1014
|
-
return "shift+enter";
|
|
1015
|
-
}
|
|
1016
|
-
const legacySequenceKeyId = LEGACY_SEQUENCE_KEY_IDS[data];
|
|
1017
|
-
if (legacySequenceKeyId)
|
|
1018
|
-
return legacySequenceKeyId;
|
|
1019
|
-
// Legacy sequences (used when Kitty protocol is not active, or for unambiguous sequences)
|
|
1020
|
-
if (data === "\x1b")
|
|
1021
|
-
return "escape";
|
|
1022
|
-
if (data === "\x1c")
|
|
1023
|
-
return "ctrl+\\";
|
|
1024
|
-
if (data === "\x1d")
|
|
1025
|
-
return "ctrl+]";
|
|
1026
|
-
if (data === "\x1f")
|
|
1027
|
-
return "ctrl+-";
|
|
1028
|
-
if (data === "\x1b\x1b")
|
|
1029
|
-
return "ctrl+alt+[";
|
|
1030
|
-
if (data === "\x1b\x1c")
|
|
1031
|
-
return "ctrl+alt+\\";
|
|
1032
|
-
if (data === "\x1b\x1d")
|
|
1033
|
-
return "ctrl+alt+]";
|
|
1034
|
-
if (data === "\x1b\x1f")
|
|
1035
|
-
return "ctrl+alt+-";
|
|
1036
|
-
if (data === "\t")
|
|
1037
|
-
return "tab";
|
|
1038
|
-
if (data === "\r" || (!_kittyProtocolActive && data === "\n") || data === "\x1bOM")
|
|
1039
|
-
return "enter";
|
|
1040
|
-
if (data === "\x00")
|
|
1041
|
-
return "ctrl+space";
|
|
1042
|
-
if (data === " ")
|
|
1043
|
-
return "space";
|
|
1044
|
-
if (data === "\x7f")
|
|
1045
|
-
return "backspace";
|
|
928
|
+
if (!modifyOtherKeys)
|
|
929
|
+
return undefined;
|
|
930
|
+
return formatParsedKey(modifyOtherKeys.codepoint, modifyOtherKeys.modifier);
|
|
931
|
+
}
|
|
932
|
+
function parseModeDependentLegacyKey(data) {
|
|
1046
933
|
if (data === "\x08")
|
|
1047
934
|
return isWindowsTerminalSession() ? "ctrl+backspace" : "backspace";
|
|
1048
|
-
if (
|
|
1049
|
-
return "shift+
|
|
1050
|
-
if (
|
|
935
|
+
if (_kittyProtocolActive)
|
|
936
|
+
return data === "\x1b\r" || data === "\n" ? "shift+enter" : undefined;
|
|
937
|
+
if (data === "\n")
|
|
938
|
+
return "enter";
|
|
939
|
+
if (data === "\x1b\r")
|
|
1051
940
|
return "alt+enter";
|
|
1052
|
-
if (
|
|
941
|
+
if (data === "\x1b ")
|
|
1053
942
|
return "alt+space";
|
|
1054
|
-
if (data === "\
|
|
1055
|
-
return "alt+backspace";
|
|
1056
|
-
if (!_kittyProtocolActive && data === "\x1bB")
|
|
943
|
+
if (data === "\x1bB")
|
|
1057
944
|
return "alt+left";
|
|
1058
|
-
if (
|
|
945
|
+
if (data === "\x1bF")
|
|
1059
946
|
return "alt+right";
|
|
1060
|
-
if (!_kittyProtocolActive && data.length === 2 && data[0] === "\x1b") {
|
|
1061
|
-
const code = data.charCodeAt(1);
|
|
1062
|
-
if (code >= 1 && code <= 26) {
|
|
1063
|
-
return `ctrl+alt+${String.fromCharCode(code + 96)}`;
|
|
1064
|
-
}
|
|
1065
|
-
// Legacy alt+letter/digit (ESC followed by the key)
|
|
1066
|
-
if ((code >= 97 && code <= 122) || (code >= 48 && code <= 57)) {
|
|
1067
|
-
return `alt+${String.fromCharCode(code)}`;
|
|
1068
|
-
}
|
|
1069
|
-
}
|
|
1070
|
-
if (data === "\x1b[A")
|
|
1071
|
-
return "up";
|
|
1072
|
-
if (data === "\x1b[B")
|
|
1073
|
-
return "down";
|
|
1074
|
-
if (data === "\x1b[C")
|
|
1075
|
-
return "right";
|
|
1076
|
-
if (data === "\x1b[D")
|
|
1077
|
-
return "left";
|
|
1078
|
-
if (data === "\x1b[H" || data === "\x1bOH")
|
|
1079
|
-
return "home";
|
|
1080
|
-
if (data === "\x1b[F" || data === "\x1bOF")
|
|
1081
|
-
return "end";
|
|
1082
|
-
if (data === "\x1b[3~")
|
|
1083
|
-
return "delete";
|
|
1084
|
-
if (data === "\x1b[5~")
|
|
1085
|
-
return "pageUp";
|
|
1086
|
-
if (data === "\x1b[6~")
|
|
1087
|
-
return "pageDown";
|
|
1088
|
-
// Raw Ctrl+letter
|
|
1089
|
-
if (data.length === 1) {
|
|
1090
|
-
const code = data.charCodeAt(0);
|
|
1091
|
-
if (code >= 1 && code <= 26) {
|
|
1092
|
-
return `ctrl+${String.fromCharCode(code + 96)}`;
|
|
1093
|
-
}
|
|
1094
|
-
if (code >= 32 && code <= 126) {
|
|
1095
|
-
return data;
|
|
1096
|
-
}
|
|
1097
|
-
}
|
|
1098
947
|
return undefined;
|
|
1099
948
|
}
|
|
949
|
+
function parseLegacyAltCombination(data) {
|
|
950
|
+
if (_kittyProtocolActive || data.length !== 2 || data[0] !== "\x1b")
|
|
951
|
+
return undefined;
|
|
952
|
+
const code = data.charCodeAt(1);
|
|
953
|
+
if (code >= 1 && code <= 26)
|
|
954
|
+
return `ctrl+alt+${String.fromCharCode(code + 96)}`;
|
|
955
|
+
const key = String.fromCharCode(code);
|
|
956
|
+
if ((code >= 97 && code <= 122) || (code >= 48 && code <= 57) || SYMBOL_KEYS.has(key))
|
|
957
|
+
return `alt+${key}`;
|
|
958
|
+
return undefined;
|
|
959
|
+
}
|
|
960
|
+
function parseSingleCharacterKey(data) {
|
|
961
|
+
if (data.length !== 1)
|
|
962
|
+
return undefined;
|
|
963
|
+
const code = data.charCodeAt(0);
|
|
964
|
+
if (code >= 1 && code <= 26)
|
|
965
|
+
return `ctrl+${String.fromCharCode(code + 96)}`;
|
|
966
|
+
return code >= 32 && code <= 126 ? data : undefined;
|
|
967
|
+
}
|
|
968
|
+
export function parseKey(data) {
|
|
969
|
+
const encodedKey = parseTerminalEncodedKey(data);
|
|
970
|
+
if (encodedKey)
|
|
971
|
+
return encodedKey;
|
|
972
|
+
const modeDependentKey = parseModeDependentLegacyKey(data);
|
|
973
|
+
if (modeDependentKey)
|
|
974
|
+
return modeDependentKey;
|
|
975
|
+
const legacySequenceKey = LEGACY_SEQUENCE_KEY_IDS[data];
|
|
976
|
+
if (legacySequenceKey)
|
|
977
|
+
return legacySequenceKey;
|
|
978
|
+
return parseLegacyAltCombination(data) ?? parseSingleCharacterKey(data);
|
|
979
|
+
}
|
|
1100
980
|
// =============================================================================
|
|
1101
981
|
// Kitty CSI-u Printable Decoding
|
|
1102
982
|
// =============================================================================
|