|
1 | | -import { Editor, Key, matchesKey } from "@earendil-works/pi-tui"; |
| 1 | +import { Editor, isKeyRelease, Key, matchesKey } from "@earendil-works/pi-tui"; |
| 2 | + |
| 3 | +const KITTY_CSI_U_SUFFIX_REGEX = /^(\d+)(?::(\d*))?(?::(\d+))?(?:;(\d+))?(?::(\d+))?u$/u; |
| 4 | +const KITTY_MODIFIERS = { |
| 5 | +alt: 2, |
| 6 | +ctrl: 4, |
| 7 | +}; |
| 8 | +const LOCK_MODIFIER_MASK = 64 + 128; |
| 9 | + |
| 10 | +function decodeAltGrPrintable(data: string): string | undefined { |
| 11 | +if (!data.startsWith("\u001b[")) { |
| 12 | +return undefined; |
| 13 | +} |
| 14 | + |
| 15 | +const match = data.slice(2).match(KITTY_CSI_U_SUFFIX_REGEX); |
| 16 | +if (!match) { |
| 17 | +return undefined; |
| 18 | +} |
| 19 | + |
| 20 | +const codepoint = Number.parseInt(match[1] ?? "", 10); |
| 21 | +const baseLayoutKey = match[3] ? Number.parseInt(match[3], 10) : undefined; |
| 22 | +const modifierValue = match[4] ? Number.parseInt(match[4], 10) : 1; |
| 23 | +const modifier = (Number.isFinite(modifierValue) ? modifierValue - 1 : 0) & ~LOCK_MODIFIER_MASK; |
| 24 | + |
| 25 | +if (modifier !== (KITTY_MODIFIERS.alt | KITTY_MODIFIERS.ctrl)) { |
| 26 | +return undefined; |
| 27 | +} |
| 28 | +if (typeof baseLayoutKey !== "number" || baseLayoutKey === codepoint) { |
| 29 | +return undefined; |
| 30 | +} |
| 31 | +if (!Number.isFinite(codepoint) || codepoint < 32) { |
| 32 | +return undefined; |
| 33 | +} |
| 34 | + |
| 35 | +try { |
| 36 | +return String.fromCodePoint(codepoint); |
| 37 | +} catch { |
| 38 | +return undefined; |
| 39 | +} |
| 40 | +} |
2 | 41 | |
3 | 42 | export class CustomEditor extends Editor { |
4 | 43 | onEscape?: () => void; |
@@ -14,6 +53,10 @@ export class CustomEditor extends Editor {
|
14 | 53 | onAltUp?: () => void; |
15 | 54 | |
16 | 55 | override handleInput(data: string): void { |
| 56 | +if (isKeyRelease(data)) { |
| 57 | +return; |
| 58 | +} |
| 59 | + |
17 | 60 | if (matchesKey(data, Key.alt("enter")) && this.onAltEnter) { |
18 | 61 | this.onAltEnter(); |
19 | 62 | return; |
@@ -60,6 +103,13 @@ export class CustomEditor extends Editor {
|
60 | 103 | } |
61 | 104 | return; |
62 | 105 | } |
| 106 | + |
| 107 | +const altGrPrintable = decodeAltGrPrintable(data); |
| 108 | +if (altGrPrintable !== undefined) { |
| 109 | +super.handleInput(altGrPrintable); |
| 110 | +return; |
| 111 | +} |
| 112 | + |
63 | 113 | super.handleInput(data); |
64 | 114 | } |
65 | 115 | } |