@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.
Files changed (109) hide show
  1. package/README.md +47 -7
  2. package/dist/autocomplete.d.ts +17 -23
  3. package/dist/autocomplete.d.ts.map +1 -1
  4. package/dist/autocomplete.js +129 -195
  5. package/dist/autocomplete.js.map +1 -1
  6. package/dist/components/alt-screen-flash.d.ts +13 -0
  7. package/dist/components/alt-screen-flash.d.ts.map +1 -0
  8. package/dist/components/alt-screen-flash.js +37 -0
  9. package/dist/components/alt-screen-flash.js.map +1 -0
  10. package/dist/components/editor.d.ts +47 -4
  11. package/dist/components/editor.d.ts.map +1 -1
  12. package/dist/components/editor.js +615 -585
  13. package/dist/components/editor.js.map +1 -1
  14. package/dist/components/h-stack.d.ts +7 -0
  15. package/dist/components/h-stack.d.ts.map +1 -0
  16. package/dist/components/h-stack.js +43 -0
  17. package/dist/components/h-stack.js.map +1 -0
  18. package/dist/components/image.d.ts +4 -0
  19. package/dist/components/image.d.ts.map +1 -1
  20. package/dist/components/image.js +38 -51
  21. package/dist/components/image.js.map +1 -1
  22. package/dist/components/input.d.ts +9 -0
  23. package/dist/components/input.d.ts.map +1 -1
  24. package/dist/components/input.js +121 -147
  25. package/dist/components/input.js.map +1 -1
  26. package/dist/components/markdown.d.ts +34 -0
  27. package/dist/components/markdown.d.ts.map +1 -1
  28. package/dist/components/markdown.js +491 -429
  29. package/dist/components/markdown.js.map +1 -1
  30. package/dist/components/scroll-view.d.ts +53 -0
  31. package/dist/components/scroll-view.d.ts.map +1 -0
  32. package/dist/components/scroll-view.js +173 -0
  33. package/dist/components/scroll-view.js.map +1 -0
  34. package/dist/components/select-list.d.ts.map +1 -1
  35. package/dist/components/select-list.js.map +1 -1
  36. package/dist/components/settings-list.d.ts +2 -0
  37. package/dist/components/settings-list.d.ts.map +1 -1
  38. package/dist/components/settings-list.js +31 -29
  39. package/dist/components/settings-list.js.map +1 -1
  40. package/dist/components/stack.d.ts +32 -0
  41. package/dist/components/stack.d.ts.map +1 -0
  42. package/dist/components/stack.js +122 -0
  43. package/dist/components/stack.js.map +1 -0
  44. package/dist/components/v-stack.d.ts +8 -0
  45. package/dist/components/v-stack.d.ts.map +1 -0
  46. package/dist/components/v-stack.js +27 -0
  47. package/dist/components/v-stack.js.map +1 -0
  48. package/dist/fuzzy.d.ts.map +1 -1
  49. package/dist/fuzzy.js +37 -46
  50. package/dist/fuzzy.js.map +1 -1
  51. package/dist/index.d.ts +10 -3
  52. package/dist/index.d.ts.map +1 -1
  53. package/dist/index.js +11 -2
  54. package/dist/index.js.map +1 -1
  55. package/dist/keybindings.d.ts +54 -4
  56. package/dist/keybindings.d.ts.map +1 -1
  57. package/dist/keybindings.js +39 -4
  58. package/dist/keybindings.js.map +1 -1
  59. package/dist/keys.d.ts.map +1 -1
  60. package/dist/keys.js +359 -479
  61. package/dist/keys.js.map +1 -1
  62. package/dist/kill-ring.d.ts +6 -4
  63. package/dist/kill-ring.d.ts.map +1 -1
  64. package/dist/kill-ring.js +0 -7
  65. package/dist/kill-ring.js.map +1 -1
  66. package/dist/latex.d.ts +10 -0
  67. package/dist/latex.d.ts.map +1 -0
  68. package/dist/latex.js +1406 -0
  69. package/dist/latex.js.map +1 -0
  70. package/dist/layout-node.d.ts +43 -0
  71. package/dist/layout-node.d.ts.map +1 -0
  72. package/dist/layout-node.js +6 -0
  73. package/dist/layout-node.js.map +1 -0
  74. package/dist/layout.d.ts +40 -0
  75. package/dist/layout.d.ts.map +1 -0
  76. package/dist/layout.js +386 -0
  77. package/dist/layout.js.map +1 -0
  78. package/dist/stdin-buffer.d.ts +8 -0
  79. package/dist/stdin-buffer.d.ts.map +1 -1
  80. package/dist/stdin-buffer.js +116 -159
  81. package/dist/stdin-buffer.js.map +1 -1
  82. package/dist/terminal-image.d.ts +41 -19
  83. package/dist/terminal-image.d.ts.map +1 -1
  84. package/dist/terminal-image.js +171 -32
  85. package/dist/terminal-image.js.map +1 -1
  86. package/dist/terminal.d.ts +3 -0
  87. package/dist/terminal.d.ts.map +1 -1
  88. package/dist/terminal.js +27 -23
  89. package/dist/terminal.js.map +1 -1
  90. package/dist/tui-alt-screen.d.ts +114 -0
  91. package/dist/tui-alt-screen.d.ts.map +1 -0
  92. package/dist/tui-alt-screen.js +950 -0
  93. package/dist/tui-alt-screen.js.map +1 -0
  94. package/dist/tui-main-screen.d.ts +52 -0
  95. package/dist/tui-main-screen.d.ts.map +1 -0
  96. package/dist/tui-main-screen.js +511 -0
  97. package/dist/tui-main-screen.js.map +1 -0
  98. package/dist/tui.d.ts +112 -44
  99. package/dist/tui.d.ts.map +1 -1
  100. package/dist/tui.js +489 -699
  101. package/dist/tui.js.map +1 -1
  102. package/dist/utils.d.ts +29 -17
  103. package/dist/utils.d.ts.map +1 -1
  104. package/dist/utils.js +605 -528
  105. package/dist/utils.js.map +1 -1
  106. package/dist/word-navigation.d.ts.map +1 -1
  107. package/dist/word-navigation.js +37 -39
  108. package/dist/word-navigation.js.map +1 -1
  109. 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 parseKittySequence(data) {
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
- // With flag 2, event type is appended after modifier colon: 1=press, 2=repeat, 3=release
427
- // With flag 4, alternate keys are appended after codepoint with colons
428
- const csiUMatch = data.match(/^\x1b\[(\d+)(?::(\d*))?(?::(\d+))?(?:;(\d+))?(?::(\d+))?u$/);
429
- if (csiUMatch) {
430
- const codepoint = parseInt(csiUMatch[1], 10);
431
- const shiftedKey = csiUMatch[2] && csiUMatch[2].length > 0 ? parseInt(csiUMatch[2], 10) : undefined;
432
- const baseLayoutKey = csiUMatch[3] ? parseInt(csiUMatch[3], 10) : undefined;
433
- const modValue = csiUMatch[4] ? parseInt(csiUMatch[4], 10) : 1;
434
- const eventType = parseEventType(csiUMatch[5]);
435
- _lastEventType = eventType;
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 arrowMatch = data.match(/^\x1b\[1;(\d+)(?::(\d+))?([ABCD])$/);
440
- if (arrowMatch) {
441
- const modValue = parseInt(arrowMatch[1], 10);
442
- const eventType = parseEventType(arrowMatch[2]);
443
- const arrowCodes = { A: -1, B: -2, C: -3, D: -4 };
444
- _lastEventType = eventType;
445
- return { codepoint: arrowCodes[arrowMatch[3]], modifier: modValue - 1, eventType };
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 funcMatch = data.match(/^\x1b\[(\d+)(?:;(\d+))?(?::(\d+))?~$/);
449
- if (funcMatch) {
450
- const keyNum = parseInt(funcMatch[1], 10);
451
- const modValue = funcMatch[2] ? parseInt(funcMatch[2], 10) : 1;
452
- const eventType = parseEventType(funcMatch[3]);
453
- const funcCodes = {
454
- 2: FUNCTIONAL_CODEPOINTS.insert,
455
- 3: FUNCTIONAL_CODEPOINTS.delete,
456
- 5: FUNCTIONAL_CODEPOINTS.pageUp,
457
- 6: FUNCTIONAL_CODEPOINTS.pageDown,
458
- 7: FUNCTIONAL_CODEPOINTS.home,
459
- 8: FUNCTIONAL_CODEPOINTS.end,
460
- };
461
- const codepoint = funcCodes[keyNum];
462
- if (codepoint !== undefined) {
463
- _lastEventType = eventType;
464
- return { codepoint, modifier: modValue - 1, eventType };
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 homeEndMatch = data.match(/^\x1b\[1;(\d+)(?::(\d+))?([HF])$/);
469
- if (homeEndMatch) {
470
- const modValue = parseInt(homeEndMatch[1], 10);
471
- const eventType = parseEventType(homeEndMatch[2]);
472
- const codepoint = homeEndMatch[3] === "H" ? FUNCTIONAL_CODEPOINTS.home : FUNCTIONAL_CODEPOINTS.end;
473
- _lastEventType = eventType;
474
- return { codepoint, modifier: modValue - 1, eventType };
475
- }
476
- return null;
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
- * Match input data against a key identifier string.
618
- *
619
- * Supported key identifiers:
620
- * - Single keys: "escape", "tab", "enter", "backspace", "delete", "home", "end", "space"
621
- * - Arrow keys: "up", "down", "left", "right"
622
- * - Ctrl combinations: "ctrl+c", "ctrl+z", etc.
623
- * - Shift combinations: "shift+tab", "shift+enter"
624
- * - Alt combinations: "alt+enter", "alt+backspace"
625
- * - Super combinations: "super+k", "super+enter"
626
- * - Combined modifiers: "shift+ctrl+p", "ctrl+alt+x", "ctrl+super+k"
627
- *
628
- * Use the Key helper for autocomplete: Key.ctrl("c"), Key.escape, Key.ctrlShift("p"), Key.super("k")
629
- *
630
- * @param data - Raw input data from terminal
631
- * @param keyId - Key identifier (e.g., "ctrl+c", "escape", Key.ctrl("c"))
632
- */
633
- export function matchesKey(data, keyId) {
634
- const parsed = parseKeyId(keyId);
635
- if (!parsed)
636
- return false;
637
- const { key, ctrl, shift, alt, super: superModifier } = parsed;
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 (superModifier)
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
- if (!_kittyProtocolActive) {
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
- if (modifier === MODIFIERS.shift) {
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
- if (modifier === MODIFIERS.shift) {
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
- if (modifier === MODIFIERS.alt) {
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
- if (modifier === 0) {
773
- return matchesLegacySequence(data, LEGACY_KEY_SEQUENCES.clear);
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 (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);
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
- export function parseKey(data) {
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 formatParsedKey(modifyOtherKeys.codepoint, modifyOtherKeys.modifier);
1007
- }
1008
- // Mode-aware legacy sequences
1009
- // When Kitty protocol is active, ambiguous sequences are interpreted as custom terminal mappings:
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 (data === "\x1b[Z")
1049
- return "shift+tab";
1050
- if (!_kittyProtocolActive && data === "\x1b\r")
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 (!_kittyProtocolActive && data === "\x1b ")
941
+ if (data === "\x1b ")
1053
942
  return "alt+space";
1054
- if (data === "\x1b\x7f" || data === "\x1b\b")
1055
- return "alt+backspace";
1056
- if (!_kittyProtocolActive && data === "\x1bB")
943
+ if (data === "\x1bB")
1057
944
  return "alt+left";
1058
- if (!_kittyProtocolActive && data === "\x1bF")
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
  // =============================================================================