@oh-my-pi/pi-tui 5.5.0 → 5.6.70
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/package.json +1 -1
- package/src/components/cancellable-loader.ts +2 -2
- package/src/components/editor.ts +314 -91
- package/src/components/input.ts +9 -3
- package/src/components/select-list.ts +5 -5
- package/src/components/settings-list.ts +5 -5
- package/src/components/tab-bar.ts +9 -6
- package/src/index.ts +1 -43
- package/src/keybindings.ts +30 -2
- package/src/keys.ts +416 -237
- package/src/terminal.ts +2 -1
- package/src/tui.ts +159 -69
package/src/keys.ts
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Supports both legacy terminal sequences and Kitty keyboard protocol.
|
|
5
5
|
* See: https://sw.kovidgoyal.net/kitty/keyboard-protocol/
|
|
6
|
+
* Reference: https://github.com/sst/opentui/blob/7da92b4088aebfe27b9f691c04163a48821e49fd/packages/core/src/lib/parse.keypress.ts
|
|
6
7
|
*
|
|
7
8
|
* Symbol keys are also supported, however some ctrl+symbol combos
|
|
8
9
|
* overlap with ASCII codes, e.g. ctrl+[ = ESC.
|
|
@@ -21,21 +22,21 @@
|
|
|
21
22
|
// Global Kitty Protocol State
|
|
22
23
|
// =============================================================================
|
|
23
24
|
|
|
24
|
-
let
|
|
25
|
+
let _kittyProtocolActive = false;
|
|
25
26
|
|
|
26
27
|
/**
|
|
27
28
|
* Set the global Kitty keyboard protocol state.
|
|
28
29
|
* Called by ProcessTerminal after detecting protocol support.
|
|
29
30
|
*/
|
|
30
31
|
export function setKittyProtocolActive(active: boolean): void {
|
|
31
|
-
|
|
32
|
+
_kittyProtocolActive = active;
|
|
32
33
|
}
|
|
33
34
|
|
|
34
35
|
/**
|
|
35
36
|
* Query whether Kitty keyboard protocol is currently active.
|
|
36
37
|
*/
|
|
37
38
|
export function isKittyProtocolActive(): boolean {
|
|
38
|
-
return
|
|
39
|
+
return _kittyProtocolActive;
|
|
39
40
|
}
|
|
40
41
|
|
|
41
42
|
// =============================================================================
|
|
@@ -112,6 +113,8 @@ type SpecialKey =
|
|
|
112
113
|
| "space"
|
|
113
114
|
| "backspace"
|
|
114
115
|
| "delete"
|
|
116
|
+
| "insert"
|
|
117
|
+
| "clear"
|
|
115
118
|
| "home"
|
|
116
119
|
| "end"
|
|
117
120
|
| "pageUp"
|
|
@@ -119,7 +122,19 @@ type SpecialKey =
|
|
|
119
122
|
| "up"
|
|
120
123
|
| "down"
|
|
121
124
|
| "left"
|
|
122
|
-
| "right"
|
|
125
|
+
| "right"
|
|
126
|
+
| "f1"
|
|
127
|
+
| "f2"
|
|
128
|
+
| "f3"
|
|
129
|
+
| "f4"
|
|
130
|
+
| "f5"
|
|
131
|
+
| "f6"
|
|
132
|
+
| "f7"
|
|
133
|
+
| "f8"
|
|
134
|
+
| "f9"
|
|
135
|
+
| "f10"
|
|
136
|
+
| "f11"
|
|
137
|
+
| "f12";
|
|
123
138
|
|
|
124
139
|
type BaseKey = Letter | SymbolKey | SpecialKey;
|
|
125
140
|
|
|
@@ -164,6 +179,8 @@ export const Key = {
|
|
|
164
179
|
space: "space" as const,
|
|
165
180
|
backspace: "backspace" as const,
|
|
166
181
|
delete: "delete" as const,
|
|
182
|
+
insert: "insert" as const,
|
|
183
|
+
clear: "clear" as const,
|
|
167
184
|
home: "home" as const,
|
|
168
185
|
end: "end" as const,
|
|
169
186
|
pageUp: "pageUp" as const,
|
|
@@ -172,6 +189,18 @@ export const Key = {
|
|
|
172
189
|
down: "down" as const,
|
|
173
190
|
left: "left" as const,
|
|
174
191
|
right: "right" as const,
|
|
192
|
+
f1: "f1" as const,
|
|
193
|
+
f2: "f2" as const,
|
|
194
|
+
f3: "f3" as const,
|
|
195
|
+
f4: "f4" as const,
|
|
196
|
+
f5: "f5" as const,
|
|
197
|
+
f6: "f6" as const,
|
|
198
|
+
f7: "f7" as const,
|
|
199
|
+
f8: "f8" as const,
|
|
200
|
+
f9: "f9" as const,
|
|
201
|
+
f10: "f10" as const,
|
|
202
|
+
f11: "f11" as const,
|
|
203
|
+
f12: "f12" as const,
|
|
175
204
|
|
|
176
205
|
// Symbol keys
|
|
177
206
|
backtick: "`" as const,
|
|
@@ -294,6 +323,135 @@ const FUNCTIONAL_CODEPOINTS = {
|
|
|
294
323
|
end: -15,
|
|
295
324
|
} as const;
|
|
296
325
|
|
|
326
|
+
const LEGACY_KEY_SEQUENCES = {
|
|
327
|
+
up: ["\x1b[A", "\x1bOA"],
|
|
328
|
+
down: ["\x1b[B", "\x1bOB"],
|
|
329
|
+
right: ["\x1b[C", "\x1bOC"],
|
|
330
|
+
left: ["\x1b[D", "\x1bOD"],
|
|
331
|
+
home: ["\x1b[H", "\x1bOH", "\x1b[1~", "\x1b[7~"],
|
|
332
|
+
end: ["\x1b[F", "\x1bOF", "\x1b[4~", "\x1b[8~"],
|
|
333
|
+
insert: ["\x1b[2~"],
|
|
334
|
+
delete: ["\x1b[3~"],
|
|
335
|
+
pageUp: ["\x1b[5~", "\x1b[[5~"],
|
|
336
|
+
pageDown: ["\x1b[6~", "\x1b[[6~"],
|
|
337
|
+
clear: ["\x1b[E", "\x1bOE"],
|
|
338
|
+
f1: ["\x1bOP", "\x1b[11~", "\x1b[[A"],
|
|
339
|
+
f2: ["\x1bOQ", "\x1b[12~", "\x1b[[B"],
|
|
340
|
+
f3: ["\x1bOR", "\x1b[13~", "\x1b[[C"],
|
|
341
|
+
f4: ["\x1bOS", "\x1b[14~", "\x1b[[D"],
|
|
342
|
+
f5: ["\x1b[15~", "\x1b[[E"],
|
|
343
|
+
f6: ["\x1b[17~"],
|
|
344
|
+
f7: ["\x1b[18~"],
|
|
345
|
+
f8: ["\x1b[19~"],
|
|
346
|
+
f9: ["\x1b[20~"],
|
|
347
|
+
f10: ["\x1b[21~"],
|
|
348
|
+
f11: ["\x1b[23~"],
|
|
349
|
+
f12: ["\x1b[24~"],
|
|
350
|
+
} as const;
|
|
351
|
+
|
|
352
|
+
const LEGACY_SHIFT_SEQUENCES = {
|
|
353
|
+
up: ["\x1b[a"],
|
|
354
|
+
down: ["\x1b[b"],
|
|
355
|
+
right: ["\x1b[c"],
|
|
356
|
+
left: ["\x1b[d"],
|
|
357
|
+
clear: ["\x1b[e"],
|
|
358
|
+
insert: ["\x1b[2$"],
|
|
359
|
+
delete: ["\x1b[3$"],
|
|
360
|
+
pageUp: ["\x1b[5$"],
|
|
361
|
+
pageDown: ["\x1b[6$"],
|
|
362
|
+
home: ["\x1b[7$"],
|
|
363
|
+
end: ["\x1b[8$"],
|
|
364
|
+
} as const;
|
|
365
|
+
|
|
366
|
+
const LEGACY_CTRL_SEQUENCES = {
|
|
367
|
+
up: ["\x1bOa"],
|
|
368
|
+
down: ["\x1bOb"],
|
|
369
|
+
right: ["\x1bOc"],
|
|
370
|
+
left: ["\x1bOd"],
|
|
371
|
+
clear: ["\x1bOe"],
|
|
372
|
+
insert: ["\x1b[2^"],
|
|
373
|
+
delete: ["\x1b[3^"],
|
|
374
|
+
pageUp: ["\x1b[5^"],
|
|
375
|
+
pageDown: ["\x1b[6^"],
|
|
376
|
+
home: ["\x1b[7^"],
|
|
377
|
+
end: ["\x1b[8^"],
|
|
378
|
+
} as const;
|
|
379
|
+
|
|
380
|
+
const LEGACY_SEQUENCE_KEY_IDS: Record<string, KeyId> = {
|
|
381
|
+
"\x1bOA": "up",
|
|
382
|
+
"\x1bOB": "down",
|
|
383
|
+
"\x1bOC": "right",
|
|
384
|
+
"\x1bOD": "left",
|
|
385
|
+
"\x1bOH": "home",
|
|
386
|
+
"\x1bOF": "end",
|
|
387
|
+
"\x1b[E": "clear",
|
|
388
|
+
"\x1bOE": "clear",
|
|
389
|
+
"\x1bOe": "ctrl+clear",
|
|
390
|
+
"\x1b[e": "shift+clear",
|
|
391
|
+
"\x1b[2~": "insert",
|
|
392
|
+
"\x1b[2$": "shift+insert",
|
|
393
|
+
"\x1b[2^": "ctrl+insert",
|
|
394
|
+
"\x1b[3$": "shift+delete",
|
|
395
|
+
"\x1b[3^": "ctrl+delete",
|
|
396
|
+
"\x1b[[5~": "pageUp",
|
|
397
|
+
"\x1b[[6~": "pageDown",
|
|
398
|
+
"\x1b[a": "shift+up",
|
|
399
|
+
"\x1b[b": "shift+down",
|
|
400
|
+
"\x1b[c": "shift+right",
|
|
401
|
+
"\x1b[d": "shift+left",
|
|
402
|
+
"\x1bOa": "ctrl+up",
|
|
403
|
+
"\x1bOb": "ctrl+down",
|
|
404
|
+
"\x1bOc": "ctrl+right",
|
|
405
|
+
"\x1bOd": "ctrl+left",
|
|
406
|
+
"\x1b[5$": "shift+pageUp",
|
|
407
|
+
"\x1b[6$": "shift+pageDown",
|
|
408
|
+
"\x1b[7$": "shift+home",
|
|
409
|
+
"\x1b[8$": "shift+end",
|
|
410
|
+
"\x1b[5^": "ctrl+pageUp",
|
|
411
|
+
"\x1b[6^": "ctrl+pageDown",
|
|
412
|
+
"\x1b[7^": "ctrl+home",
|
|
413
|
+
"\x1b[8^": "ctrl+end",
|
|
414
|
+
"\x1bOP": "f1",
|
|
415
|
+
"\x1bOQ": "f2",
|
|
416
|
+
"\x1bOR": "f3",
|
|
417
|
+
"\x1bOS": "f4",
|
|
418
|
+
"\x1b[11~": "f1",
|
|
419
|
+
"\x1b[12~": "f2",
|
|
420
|
+
"\x1b[13~": "f3",
|
|
421
|
+
"\x1b[14~": "f4",
|
|
422
|
+
"\x1b[[A": "f1",
|
|
423
|
+
"\x1b[[B": "f2",
|
|
424
|
+
"\x1b[[C": "f3",
|
|
425
|
+
"\x1b[[D": "f4",
|
|
426
|
+
"\x1b[[E": "f5",
|
|
427
|
+
"\x1b[15~": "f5",
|
|
428
|
+
"\x1b[17~": "f6",
|
|
429
|
+
"\x1b[18~": "f7",
|
|
430
|
+
"\x1b[19~": "f8",
|
|
431
|
+
"\x1b[20~": "f9",
|
|
432
|
+
"\x1b[21~": "f10",
|
|
433
|
+
"\x1b[23~": "f11",
|
|
434
|
+
"\x1b[24~": "f12",
|
|
435
|
+
"\x1bb": "alt+left",
|
|
436
|
+
"\x1bf": "alt+right",
|
|
437
|
+
"\x1bp": "alt+up",
|
|
438
|
+
"\x1bn": "alt+down",
|
|
439
|
+
} as const;
|
|
440
|
+
|
|
441
|
+
type LegacyModifierKey = keyof typeof LEGACY_SHIFT_SEQUENCES;
|
|
442
|
+
|
|
443
|
+
const matchesLegacySequence = (data: string, sequences: readonly string[]): boolean => sequences.includes(data);
|
|
444
|
+
|
|
445
|
+
const matchesLegacyModifierSequence = (data: string, key: LegacyModifierKey, modifier: number): boolean => {
|
|
446
|
+
if (modifier === MODIFIERS.shift) {
|
|
447
|
+
return matchesLegacySequence(data, LEGACY_SHIFT_SEQUENCES[key]);
|
|
448
|
+
}
|
|
449
|
+
if (modifier === MODIFIERS.ctrl) {
|
|
450
|
+
return matchesLegacySequence(data, LEGACY_CTRL_SEQUENCES[key]);
|
|
451
|
+
}
|
|
452
|
+
return false;
|
|
453
|
+
};
|
|
454
|
+
|
|
297
455
|
// =============================================================================
|
|
298
456
|
// Kitty Protocol Parsing
|
|
299
457
|
// =============================================================================
|
|
@@ -306,10 +464,15 @@ export type KeyEventType = "press" | "repeat" | "release";
|
|
|
306
464
|
|
|
307
465
|
interface ParsedKittySequence {
|
|
308
466
|
codepoint: number;
|
|
467
|
+
shiftedKey?: number; // Shifted version of the key (when shift is pressed)
|
|
468
|
+
baseLayoutKey?: number; // Key in standard PC-101 layout (for non-Latin layouts)
|
|
309
469
|
modifier: number;
|
|
310
470
|
eventType: KeyEventType;
|
|
311
471
|
}
|
|
312
472
|
|
|
473
|
+
// Store the last parsed event type for isKeyRelease() to query
|
|
474
|
+
let _lastEventType: KeyEventType = "press";
|
|
475
|
+
|
|
313
476
|
/**
|
|
314
477
|
* Check if the last parsed key event was a key release.
|
|
315
478
|
* Only meaningful when Kitty keyboard protocol with flag 2 is active.
|
|
@@ -374,14 +537,26 @@ function parseEventType(eventTypeStr: string | undefined): KeyEventType {
|
|
|
374
537
|
return "press";
|
|
375
538
|
}
|
|
376
539
|
|
|
377
|
-
function parseKittySequence(data: string): ParsedKittySequence | null {
|
|
378
|
-
// CSI u format
|
|
379
|
-
|
|
540
|
+
export function parseKittySequence(data: string): ParsedKittySequence | null {
|
|
541
|
+
// CSI u format with alternate keys (flag 4):
|
|
542
|
+
// \x1b[<codepoint>u
|
|
543
|
+
// \x1b[<codepoint>;<mod>u
|
|
544
|
+
// \x1b[<codepoint>;<mod>:<event>u
|
|
545
|
+
// \x1b[<codepoint>:<shifted>;<mod>u
|
|
546
|
+
// \x1b[<codepoint>:<shifted>:<base>;<mod>u
|
|
547
|
+
// \x1b[<codepoint>::<base>;<mod>u (no shifted key, only base)
|
|
548
|
+
//
|
|
549
|
+
// With flag 2, event type is appended after modifier colon: 1=press, 2=repeat, 3=release
|
|
550
|
+
// With flag 4, alternate keys are appended after codepoint with colons
|
|
551
|
+
const csiUMatch = data.match(/^\x1b\[(\d+)(?::(\d*))?(?::(\d+))?(?:;(\d+))?(?::(\d+))?u$/);
|
|
380
552
|
if (csiUMatch) {
|
|
381
553
|
const codepoint = parseInt(csiUMatch[1]!, 10);
|
|
382
|
-
const
|
|
383
|
-
const
|
|
384
|
-
|
|
554
|
+
const shiftedKey = csiUMatch[2] && csiUMatch[2].length > 0 ? parseInt(csiUMatch[2], 10) : undefined;
|
|
555
|
+
const baseLayoutKey = csiUMatch[3] ? parseInt(csiUMatch[3], 10) : undefined;
|
|
556
|
+
const modValue = csiUMatch[4] ? parseInt(csiUMatch[4], 10) : 1;
|
|
557
|
+
const eventType = parseEventType(csiUMatch[5]);
|
|
558
|
+
_lastEventType = eventType;
|
|
559
|
+
return { codepoint, shiftedKey, baseLayoutKey, modifier: modValue - 1, eventType };
|
|
385
560
|
}
|
|
386
561
|
|
|
387
562
|
// Arrow keys with modifier: \x1b[1;<mod>A/B/C/D or \x1b[1;<mod>:<event>A/B/C/D
|
|
@@ -390,6 +565,7 @@ function parseKittySequence(data: string): ParsedKittySequence | null {
|
|
|
390
565
|
const modValue = parseInt(arrowMatch[1]!, 10);
|
|
391
566
|
const eventType = parseEventType(arrowMatch[2]);
|
|
392
567
|
const arrowCodes: Record<string, number> = { A: -1, B: -2, C: -3, D: -4 };
|
|
568
|
+
_lastEventType = eventType;
|
|
393
569
|
return { codepoint: arrowCodes[arrowMatch[3]!]!, modifier: modValue - 1, eventType };
|
|
394
570
|
}
|
|
395
571
|
|
|
@@ -409,6 +585,7 @@ function parseKittySequence(data: string): ParsedKittySequence | null {
|
|
|
409
585
|
};
|
|
410
586
|
const codepoint = funcCodes[keyNum];
|
|
411
587
|
if (codepoint !== undefined) {
|
|
588
|
+
_lastEventType = eventType;
|
|
412
589
|
return { codepoint, modifier: modValue - 1, eventType };
|
|
413
590
|
}
|
|
414
591
|
}
|
|
@@ -419,6 +596,7 @@ function parseKittySequence(data: string): ParsedKittySequence | null {
|
|
|
419
596
|
const modValue = parseInt(homeEndMatch[1]!, 10);
|
|
420
597
|
const eventType = parseEventType(homeEndMatch[2]);
|
|
421
598
|
const codepoint = homeEndMatch[3] === "H" ? FUNCTIONAL_CODEPOINTS.home : FUNCTIONAL_CODEPOINTS.end;
|
|
599
|
+
_lastEventType = eventType;
|
|
422
600
|
return { codepoint, modifier: modValue - 1, eventType };
|
|
423
601
|
}
|
|
424
602
|
|
|
@@ -430,7 +608,34 @@ function matchesKittySequence(data: string, expectedCodepoint: number, expectedM
|
|
|
430
608
|
if (!parsed) return false;
|
|
431
609
|
const actualMod = parsed.modifier & ~LOCK_MASK;
|
|
432
610
|
const expectedMod = expectedModifier & ~LOCK_MASK;
|
|
433
|
-
|
|
611
|
+
|
|
612
|
+
// Check if modifiers match
|
|
613
|
+
if (actualMod !== expectedMod) return false;
|
|
614
|
+
|
|
615
|
+
// Primary match: codepoint matches directly
|
|
616
|
+
if (parsed.codepoint === expectedCodepoint) return true;
|
|
617
|
+
|
|
618
|
+
// Alternate match: use base layout key for non-Latin keyboard layouts
|
|
619
|
+
// This allows Ctrl+С (Cyrillic) to match Ctrl+c (Latin) when terminal reports
|
|
620
|
+
// the base layout key (the key in standard PC-101 layout)
|
|
621
|
+
if (parsed.baseLayoutKey !== undefined && parsed.baseLayoutKey === expectedCodepoint) return true;
|
|
622
|
+
|
|
623
|
+
return false;
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
/**
|
|
627
|
+
* Match xterm modifyOtherKeys format: CSI 27 ; modifiers ; keycode ~
|
|
628
|
+
* This is used by terminals when Kitty protocol is not enabled.
|
|
629
|
+
* Modifier values are 1-indexed: 2=shift, 3=alt, 5=ctrl, etc.
|
|
630
|
+
*/
|
|
631
|
+
function matchesModifyOtherKeys(data: string, expectedKeycode: number, expectedModifier: number): boolean {
|
|
632
|
+
const match = data.match(/^\x1b\[27;(\d+);(\d+)~$/);
|
|
633
|
+
if (!match) return false;
|
|
634
|
+
const modValue = parseInt(match[1]!, 10);
|
|
635
|
+
const keycode = parseInt(match[2]!, 10);
|
|
636
|
+
// Convert from 1-indexed xterm format to our 0-indexed format
|
|
637
|
+
const actualMod = modValue - 1;
|
|
638
|
+
return keycode === expectedKeycode && actualMod === expectedModifier;
|
|
434
639
|
}
|
|
435
640
|
|
|
436
641
|
// =============================================================================
|
|
@@ -487,6 +692,14 @@ export function matchesKey(data: string, keyId: KeyId): boolean {
|
|
|
487
692
|
return data === "\x1b" || matchesKittySequence(data, CODEPOINTS.escape, 0);
|
|
488
693
|
|
|
489
694
|
case "space":
|
|
695
|
+
if (!_kittyProtocolActive) {
|
|
696
|
+
if (ctrl && !alt && !shift && data === "\x00") {
|
|
697
|
+
return true;
|
|
698
|
+
}
|
|
699
|
+
if (alt && !ctrl && !shift && data === "\x1b ") {
|
|
700
|
+
return true;
|
|
701
|
+
}
|
|
702
|
+
}
|
|
490
703
|
if (modifier === 0) {
|
|
491
704
|
return data === " " || matchesKittySequence(data, CODEPOINTS.space, 0);
|
|
492
705
|
}
|
|
@@ -504,25 +717,40 @@ export function matchesKey(data: string, keyId: KeyId): boolean {
|
|
|
504
717
|
case "enter":
|
|
505
718
|
case "return":
|
|
506
719
|
if (shift && !ctrl && !alt) {
|
|
720
|
+
// CSI u sequences (standard Kitty protocol)
|
|
507
721
|
if (
|
|
508
722
|
matchesKittySequence(data, CODEPOINTS.enter, MODIFIERS.shift) ||
|
|
509
723
|
matchesKittySequence(data, CODEPOINTS.kpEnter, MODIFIERS.shift)
|
|
510
724
|
) {
|
|
511
725
|
return true;
|
|
512
726
|
}
|
|
513
|
-
|
|
727
|
+
// xterm modifyOtherKeys format (fallback when Kitty protocol not enabled)
|
|
728
|
+
if (matchesModifyOtherKeys(data, CODEPOINTS.enter, MODIFIERS.shift)) {
|
|
729
|
+
return true;
|
|
730
|
+
}
|
|
731
|
+
// When Kitty protocol is active, legacy sequences are custom terminal mappings
|
|
732
|
+
// \x1b\r = Kitty's "map shift+enter send_text all \e\r"
|
|
733
|
+
// \n = Ghostty's "keybind = shift+enter=text:\n"
|
|
734
|
+
if (_kittyProtocolActive) {
|
|
514
735
|
return data === "\x1b\r" || data === "\n";
|
|
515
736
|
}
|
|
516
737
|
return false;
|
|
517
738
|
}
|
|
518
739
|
if (alt && !ctrl && !shift) {
|
|
740
|
+
// CSI u sequences (standard Kitty protocol)
|
|
519
741
|
if (
|
|
520
742
|
matchesKittySequence(data, CODEPOINTS.enter, MODIFIERS.alt) ||
|
|
521
743
|
matchesKittySequence(data, CODEPOINTS.kpEnter, MODIFIERS.alt)
|
|
522
744
|
) {
|
|
523
745
|
return true;
|
|
524
746
|
}
|
|
525
|
-
|
|
747
|
+
// xterm modifyOtherKeys format (fallback when Kitty protocol not enabled)
|
|
748
|
+
if (matchesModifyOtherKeys(data, CODEPOINTS.enter, MODIFIERS.alt)) {
|
|
749
|
+
return true;
|
|
750
|
+
}
|
|
751
|
+
// \x1b\r is alt+enter only in legacy mode (no Kitty protocol)
|
|
752
|
+
// When Kitty protocol is active, alt+enter comes as CSI u sequence
|
|
753
|
+
if (!_kittyProtocolActive) {
|
|
526
754
|
return data === "\x1b\r";
|
|
527
755
|
}
|
|
528
756
|
return false;
|
|
@@ -530,6 +758,7 @@ export function matchesKey(data: string, keyId: KeyId): boolean {
|
|
|
530
758
|
if (modifier === 0) {
|
|
531
759
|
return (
|
|
532
760
|
data === "\r" ||
|
|
761
|
+
(!_kittyProtocolActive && data === "\n") ||
|
|
533
762
|
data === "\x1bOM" || // SS3 M (numpad enter in some terminals)
|
|
534
763
|
matchesKittySequence(data, CODEPOINTS.enter, 0) ||
|
|
535
764
|
matchesKittySequence(data, CODEPOINTS.kpEnter, 0)
|
|
@@ -542,62 +771,121 @@ export function matchesKey(data: string, keyId: KeyId): boolean {
|
|
|
542
771
|
|
|
543
772
|
case "backspace":
|
|
544
773
|
if (alt && !ctrl && !shift) {
|
|
545
|
-
|
|
774
|
+
if (data === "\x1b\x7f" || data === "\x1b\b") {
|
|
775
|
+
return true;
|
|
776
|
+
}
|
|
777
|
+
return matchesKittySequence(data, CODEPOINTS.backspace, MODIFIERS.alt);
|
|
546
778
|
}
|
|
547
779
|
if (modifier === 0) {
|
|
548
780
|
return data === "\x7f" || data === "\x08" || matchesKittySequence(data, CODEPOINTS.backspace, 0);
|
|
549
781
|
}
|
|
550
782
|
return matchesKittySequence(data, CODEPOINTS.backspace, modifier);
|
|
551
783
|
|
|
784
|
+
case "insert":
|
|
785
|
+
if (modifier === 0) {
|
|
786
|
+
return (
|
|
787
|
+
matchesLegacySequence(data, LEGACY_KEY_SEQUENCES.insert) ||
|
|
788
|
+
matchesKittySequence(data, FUNCTIONAL_CODEPOINTS.insert, 0)
|
|
789
|
+
);
|
|
790
|
+
}
|
|
791
|
+
if (matchesLegacyModifierSequence(data, "insert", modifier)) {
|
|
792
|
+
return true;
|
|
793
|
+
}
|
|
794
|
+
return matchesKittySequence(data, FUNCTIONAL_CODEPOINTS.insert, modifier);
|
|
795
|
+
|
|
552
796
|
case "delete":
|
|
553
797
|
if (modifier === 0) {
|
|
554
|
-
return
|
|
798
|
+
return (
|
|
799
|
+
matchesLegacySequence(data, LEGACY_KEY_SEQUENCES.delete) ||
|
|
800
|
+
matchesKittySequence(data, FUNCTIONAL_CODEPOINTS.delete, 0)
|
|
801
|
+
);
|
|
802
|
+
}
|
|
803
|
+
if (matchesLegacyModifierSequence(data, "delete", modifier)) {
|
|
804
|
+
return true;
|
|
555
805
|
}
|
|
556
806
|
return matchesKittySequence(data, FUNCTIONAL_CODEPOINTS.delete, modifier);
|
|
557
807
|
|
|
808
|
+
case "clear":
|
|
809
|
+
if (modifier === 0) {
|
|
810
|
+
return matchesLegacySequence(data, LEGACY_KEY_SEQUENCES.clear);
|
|
811
|
+
}
|
|
812
|
+
return matchesLegacyModifierSequence(data, "clear", modifier);
|
|
813
|
+
|
|
558
814
|
case "home":
|
|
559
815
|
if (modifier === 0) {
|
|
560
816
|
return (
|
|
561
|
-
data
|
|
562
|
-
data === "\x1b[1~" ||
|
|
563
|
-
data === "\x1b[7~" ||
|
|
817
|
+
matchesLegacySequence(data, LEGACY_KEY_SEQUENCES.home) ||
|
|
564
818
|
matchesKittySequence(data, FUNCTIONAL_CODEPOINTS.home, 0)
|
|
565
819
|
);
|
|
566
820
|
}
|
|
821
|
+
if (matchesLegacyModifierSequence(data, "home", modifier)) {
|
|
822
|
+
return true;
|
|
823
|
+
}
|
|
567
824
|
return matchesKittySequence(data, FUNCTIONAL_CODEPOINTS.home, modifier);
|
|
568
825
|
|
|
569
826
|
case "end":
|
|
570
827
|
if (modifier === 0) {
|
|
571
828
|
return (
|
|
572
|
-
data
|
|
573
|
-
data === "\x1b[4~" ||
|
|
574
|
-
data === "\x1b[8~" ||
|
|
829
|
+
matchesLegacySequence(data, LEGACY_KEY_SEQUENCES.end) ||
|
|
575
830
|
matchesKittySequence(data, FUNCTIONAL_CODEPOINTS.end, 0)
|
|
576
831
|
);
|
|
577
832
|
}
|
|
833
|
+
if (matchesLegacyModifierSequence(data, "end", modifier)) {
|
|
834
|
+
return true;
|
|
835
|
+
}
|
|
578
836
|
return matchesKittySequence(data, FUNCTIONAL_CODEPOINTS.end, modifier);
|
|
579
837
|
|
|
580
|
-
case "
|
|
838
|
+
case "pageup":
|
|
581
839
|
if (modifier === 0) {
|
|
582
|
-
return
|
|
840
|
+
return (
|
|
841
|
+
matchesLegacySequence(data, LEGACY_KEY_SEQUENCES.pageUp) ||
|
|
842
|
+
matchesKittySequence(data, FUNCTIONAL_CODEPOINTS.pageUp, 0)
|
|
843
|
+
);
|
|
844
|
+
}
|
|
845
|
+
if (matchesLegacyModifierSequence(data, "pageUp", modifier)) {
|
|
846
|
+
return true;
|
|
583
847
|
}
|
|
584
848
|
return matchesKittySequence(data, FUNCTIONAL_CODEPOINTS.pageUp, modifier);
|
|
585
849
|
|
|
586
|
-
case "
|
|
850
|
+
case "pagedown":
|
|
587
851
|
if (modifier === 0) {
|
|
588
|
-
return
|
|
852
|
+
return (
|
|
853
|
+
matchesLegacySequence(data, LEGACY_KEY_SEQUENCES.pageDown) ||
|
|
854
|
+
matchesKittySequence(data, FUNCTIONAL_CODEPOINTS.pageDown, 0)
|
|
855
|
+
);
|
|
856
|
+
}
|
|
857
|
+
if (matchesLegacyModifierSequence(data, "pageDown", modifier)) {
|
|
858
|
+
return true;
|
|
589
859
|
}
|
|
590
860
|
return matchesKittySequence(data, FUNCTIONAL_CODEPOINTS.pageDown, modifier);
|
|
591
861
|
|
|
592
862
|
case "up":
|
|
863
|
+
if (alt && !ctrl && !shift) {
|
|
864
|
+
return data === "\x1bp" || matchesKittySequence(data, ARROW_CODEPOINTS.up, MODIFIERS.alt);
|
|
865
|
+
}
|
|
593
866
|
if (modifier === 0) {
|
|
594
|
-
return
|
|
867
|
+
return (
|
|
868
|
+
matchesLegacySequence(data, LEGACY_KEY_SEQUENCES.up) ||
|
|
869
|
+
matchesKittySequence(data, ARROW_CODEPOINTS.up, 0)
|
|
870
|
+
);
|
|
871
|
+
}
|
|
872
|
+
if (matchesLegacyModifierSequence(data, "up", modifier)) {
|
|
873
|
+
return true;
|
|
595
874
|
}
|
|
596
875
|
return matchesKittySequence(data, ARROW_CODEPOINTS.up, modifier);
|
|
597
876
|
|
|
598
877
|
case "down":
|
|
878
|
+
if (alt && !ctrl && !shift) {
|
|
879
|
+
return data === "\x1bn" || matchesKittySequence(data, ARROW_CODEPOINTS.down, MODIFIERS.alt);
|
|
880
|
+
}
|
|
599
881
|
if (modifier === 0) {
|
|
600
|
-
return
|
|
882
|
+
return (
|
|
883
|
+
matchesLegacySequence(data, LEGACY_KEY_SEQUENCES.down) ||
|
|
884
|
+
matchesKittySequence(data, ARROW_CODEPOINTS.down, 0)
|
|
885
|
+
);
|
|
886
|
+
}
|
|
887
|
+
if (matchesLegacyModifierSequence(data, "down", modifier)) {
|
|
888
|
+
return true;
|
|
601
889
|
}
|
|
602
890
|
return matchesKittySequence(data, ARROW_CODEPOINTS.down, modifier);
|
|
603
891
|
|
|
@@ -605,15 +893,26 @@ export function matchesKey(data: string, keyId: KeyId): boolean {
|
|
|
605
893
|
if (alt && !ctrl && !shift) {
|
|
606
894
|
return (
|
|
607
895
|
data === "\x1b[1;3D" ||
|
|
896
|
+
(!_kittyProtocolActive && data === "\x1bB") ||
|
|
608
897
|
data === "\x1bb" ||
|
|
609
898
|
matchesKittySequence(data, ARROW_CODEPOINTS.left, MODIFIERS.alt)
|
|
610
899
|
);
|
|
611
900
|
}
|
|
612
901
|
if (ctrl && !alt && !shift) {
|
|
613
|
-
return
|
|
902
|
+
return (
|
|
903
|
+
data === "\x1b[1;5D" ||
|
|
904
|
+
matchesLegacyModifierSequence(data, "left", MODIFIERS.ctrl) ||
|
|
905
|
+
matchesKittySequence(data, ARROW_CODEPOINTS.left, MODIFIERS.ctrl)
|
|
906
|
+
);
|
|
614
907
|
}
|
|
615
908
|
if (modifier === 0) {
|
|
616
|
-
return
|
|
909
|
+
return (
|
|
910
|
+
matchesLegacySequence(data, LEGACY_KEY_SEQUENCES.left) ||
|
|
911
|
+
matchesKittySequence(data, ARROW_CODEPOINTS.left, 0)
|
|
912
|
+
);
|
|
913
|
+
}
|
|
914
|
+
if (matchesLegacyModifierSequence(data, "left", modifier)) {
|
|
915
|
+
return true;
|
|
617
916
|
}
|
|
618
917
|
return matchesKittySequence(data, ARROW_CODEPOINTS.left, modifier);
|
|
619
918
|
|
|
@@ -621,23 +920,62 @@ export function matchesKey(data: string, keyId: KeyId): boolean {
|
|
|
621
920
|
if (alt && !ctrl && !shift) {
|
|
622
921
|
return (
|
|
623
922
|
data === "\x1b[1;3C" ||
|
|
923
|
+
(!_kittyProtocolActive && data === "\x1bF") ||
|
|
624
924
|
data === "\x1bf" ||
|
|
625
925
|
matchesKittySequence(data, ARROW_CODEPOINTS.right, MODIFIERS.alt)
|
|
626
926
|
);
|
|
627
927
|
}
|
|
628
928
|
if (ctrl && !alt && !shift) {
|
|
629
|
-
return
|
|
929
|
+
return (
|
|
930
|
+
data === "\x1b[1;5C" ||
|
|
931
|
+
matchesLegacyModifierSequence(data, "right", MODIFIERS.ctrl) ||
|
|
932
|
+
matchesKittySequence(data, ARROW_CODEPOINTS.right, MODIFIERS.ctrl)
|
|
933
|
+
);
|
|
630
934
|
}
|
|
631
935
|
if (modifier === 0) {
|
|
632
|
-
return
|
|
936
|
+
return (
|
|
937
|
+
matchesLegacySequence(data, LEGACY_KEY_SEQUENCES.right) ||
|
|
938
|
+
matchesKittySequence(data, ARROW_CODEPOINTS.right, 0)
|
|
939
|
+
);
|
|
940
|
+
}
|
|
941
|
+
if (matchesLegacyModifierSequence(data, "right", modifier)) {
|
|
942
|
+
return true;
|
|
633
943
|
}
|
|
634
944
|
return matchesKittySequence(data, ARROW_CODEPOINTS.right, modifier);
|
|
945
|
+
|
|
946
|
+
case "f1":
|
|
947
|
+
case "f2":
|
|
948
|
+
case "f3":
|
|
949
|
+
case "f4":
|
|
950
|
+
case "f5":
|
|
951
|
+
case "f6":
|
|
952
|
+
case "f7":
|
|
953
|
+
case "f8":
|
|
954
|
+
case "f9":
|
|
955
|
+
case "f10":
|
|
956
|
+
case "f11":
|
|
957
|
+
case "f12": {
|
|
958
|
+
if (modifier !== 0) {
|
|
959
|
+
return false;
|
|
960
|
+
}
|
|
961
|
+
const functionKey = key as keyof typeof LEGACY_KEY_SEQUENCES;
|
|
962
|
+
return matchesLegacySequence(data, LEGACY_KEY_SEQUENCES[functionKey]);
|
|
963
|
+
}
|
|
635
964
|
}
|
|
636
965
|
|
|
637
966
|
// Handle single letter keys (a-z) and some symbols
|
|
638
967
|
if (key.length === 1 && ((key >= "a" && key <= "z") || SYMBOL_KEYS.has(key))) {
|
|
639
968
|
const codepoint = key.charCodeAt(0);
|
|
640
969
|
|
|
970
|
+
if (ctrl && alt && !shift && !_kittyProtocolActive && key >= "a" && key <= "z") {
|
|
971
|
+
return data === `\x1b${rawCtrlChar(key)}`;
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
if (alt && !ctrl && !shift && !_kittyProtocolActive && key >= "a" && key <= "z") {
|
|
975
|
+
// Legacy: alt+letter is ESC followed by the letter
|
|
976
|
+
if (data === `\x1b${key}`) return true;
|
|
977
|
+
}
|
|
978
|
+
|
|
641
979
|
if (ctrl && !shift && !alt) {
|
|
642
980
|
const raw = rawCtrlChar(key);
|
|
643
981
|
if (data === raw) return true;
|
|
@@ -659,6 +997,7 @@ export function matchesKey(data: string, keyId: KeyId): boolean {
|
|
|
659
997
|
return matchesKittySequence(data, codepoint, modifier);
|
|
660
998
|
}
|
|
661
999
|
|
|
1000
|
+
// Check both raw char and Kitty sequence (needed for release events)
|
|
662
1001
|
return data === key || matchesKittySequence(data, codepoint, 0);
|
|
663
1002
|
}
|
|
664
1003
|
|
|
@@ -674,30 +1013,36 @@ export function matchesKey(data: string, keyId: KeyId): boolean {
|
|
|
674
1013
|
export function parseKey(data: string): string | undefined {
|
|
675
1014
|
const kitty = parseKittySequence(data);
|
|
676
1015
|
if (kitty) {
|
|
677
|
-
const { codepoint, modifier } = kitty;
|
|
1016
|
+
const { codepoint, baseLayoutKey, modifier } = kitty;
|
|
678
1017
|
const mods: string[] = [];
|
|
679
1018
|
const effectiveMod = modifier & ~LOCK_MASK;
|
|
680
1019
|
if (effectiveMod & MODIFIERS.shift) mods.push("shift");
|
|
681
1020
|
if (effectiveMod & MODIFIERS.ctrl) mods.push("ctrl");
|
|
682
1021
|
if (effectiveMod & MODIFIERS.alt) mods.push("alt");
|
|
683
1022
|
|
|
1023
|
+
// Prefer base layout key for consistent shortcut naming across keyboard layouts
|
|
1024
|
+
// This ensures Ctrl+С (Cyrillic) is reported as "ctrl+c" (Latin)
|
|
1025
|
+
const effectiveCodepoint = baseLayoutKey ?? codepoint;
|
|
1026
|
+
|
|
684
1027
|
let keyName: string | undefined;
|
|
685
|
-
if (
|
|
686
|
-
else if (
|
|
687
|
-
else if (
|
|
688
|
-
else if (
|
|
689
|
-
else if (
|
|
690
|
-
else if (
|
|
691
|
-
else if (
|
|
692
|
-
else if (
|
|
693
|
-
else if (
|
|
694
|
-
else if (
|
|
695
|
-
else if (
|
|
696
|
-
else if (
|
|
697
|
-
else if (
|
|
698
|
-
else if (
|
|
699
|
-
else if (
|
|
700
|
-
else if (
|
|
1028
|
+
if (effectiveCodepoint === CODEPOINTS.escape) keyName = "escape";
|
|
1029
|
+
else if (effectiveCodepoint === CODEPOINTS.tab) keyName = "tab";
|
|
1030
|
+
else if (effectiveCodepoint === CODEPOINTS.enter || effectiveCodepoint === CODEPOINTS.kpEnter) keyName = "enter";
|
|
1031
|
+
else if (effectiveCodepoint === CODEPOINTS.space) keyName = "space";
|
|
1032
|
+
else if (effectiveCodepoint === CODEPOINTS.backspace) keyName = "backspace";
|
|
1033
|
+
else if (effectiveCodepoint === FUNCTIONAL_CODEPOINTS.delete) keyName = "delete";
|
|
1034
|
+
else if (effectiveCodepoint === FUNCTIONAL_CODEPOINTS.insert) keyName = "insert";
|
|
1035
|
+
else if (effectiveCodepoint === FUNCTIONAL_CODEPOINTS.home) keyName = "home";
|
|
1036
|
+
else if (effectiveCodepoint === FUNCTIONAL_CODEPOINTS.end) keyName = "end";
|
|
1037
|
+
else if (effectiveCodepoint === FUNCTIONAL_CODEPOINTS.pageUp) keyName = "pageUp";
|
|
1038
|
+
else if (effectiveCodepoint === FUNCTIONAL_CODEPOINTS.pageDown) keyName = "pageDown";
|
|
1039
|
+
else if (effectiveCodepoint === ARROW_CODEPOINTS.up) keyName = "up";
|
|
1040
|
+
else if (effectiveCodepoint === ARROW_CODEPOINTS.down) keyName = "down";
|
|
1041
|
+
else if (effectiveCodepoint === ARROW_CODEPOINTS.left) keyName = "left";
|
|
1042
|
+
else if (effectiveCodepoint === ARROW_CODEPOINTS.right) keyName = "right";
|
|
1043
|
+
else if (effectiveCodepoint >= 97 && effectiveCodepoint <= 122) keyName = String.fromCharCode(effectiveCodepoint);
|
|
1044
|
+
else if (SYMBOL_KEYS.has(String.fromCharCode(effectiveCodepoint)))
|
|
1045
|
+
keyName = String.fromCharCode(effectiveCodepoint);
|
|
701
1046
|
|
|
702
1047
|
if (keyName) {
|
|
703
1048
|
return mods.length > 0 ? `${mods.join("+")}+${keyName}` : keyName;
|
|
@@ -708,25 +1053,42 @@ export function parseKey(data: string): string | undefined {
|
|
|
708
1053
|
// When Kitty protocol is active, ambiguous sequences are interpreted as custom terminal mappings:
|
|
709
1054
|
// - \x1b\r = shift+enter (Kitty mapping), not alt+enter
|
|
710
1055
|
// - \n = shift+enter (Ghostty mapping)
|
|
711
|
-
if (
|
|
1056
|
+
if (_kittyProtocolActive) {
|
|
712
1057
|
if (data === "\x1b\r" || data === "\n") return "shift+enter";
|
|
713
1058
|
}
|
|
714
1059
|
|
|
1060
|
+
const legacySequenceKeyId = LEGACY_SEQUENCE_KEY_IDS[data];
|
|
1061
|
+
if (legacySequenceKeyId) return legacySequenceKeyId;
|
|
1062
|
+
|
|
715
1063
|
// Legacy sequences (used when Kitty protocol is not active, or for unambiguous sequences)
|
|
716
1064
|
if (data === "\x1b") return "escape";
|
|
717
1065
|
if (data === "\t") return "tab";
|
|
718
|
-
if (data === "\r" || data === "\x1bOM") return "enter";
|
|
1066
|
+
if (data === "\r" || (!_kittyProtocolActive && data === "\n") || data === "\x1bOM") return "enter";
|
|
1067
|
+
if (data === "\x00") return "ctrl+space";
|
|
719
1068
|
if (data === " ") return "space";
|
|
720
1069
|
if (data === "\x7f" || data === "\x08") return "backspace";
|
|
721
1070
|
if (data === "\x1b[Z") return "shift+tab";
|
|
722
|
-
if (!
|
|
723
|
-
if (data === "\x1b
|
|
1071
|
+
if (!_kittyProtocolActive && data === "\x1b\r") return "alt+enter";
|
|
1072
|
+
if (!_kittyProtocolActive && data === "\x1b ") return "alt+space";
|
|
1073
|
+
if (data === "\x1b\x7f" || data === "\x1b\b") return "alt+backspace";
|
|
1074
|
+
if (!_kittyProtocolActive && data === "\x1bB") return "alt+left";
|
|
1075
|
+
if (!_kittyProtocolActive && data === "\x1bF") return "alt+right";
|
|
1076
|
+
if (!_kittyProtocolActive && data.length === 2 && data[0] === "\x1b") {
|
|
1077
|
+
const code = data.charCodeAt(1);
|
|
1078
|
+
if (code >= 1 && code <= 26) {
|
|
1079
|
+
return `ctrl+alt+${String.fromCharCode(code + 96)}`;
|
|
1080
|
+
}
|
|
1081
|
+
// Legacy alt+letter (ESC followed by letter a-z)
|
|
1082
|
+
if (code >= 97 && code <= 122) {
|
|
1083
|
+
return `alt+${String.fromCharCode(code)}`;
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
724
1086
|
if (data === "\x1b[A") return "up";
|
|
725
1087
|
if (data === "\x1b[B") return "down";
|
|
726
1088
|
if (data === "\x1b[C") return "right";
|
|
727
1089
|
if (data === "\x1b[D") return "left";
|
|
728
|
-
if (data === "\x1b[H") return "home";
|
|
729
|
-
if (data === "\x1b[F") return "end";
|
|
1090
|
+
if (data === "\x1b[H" || data === "\x1bOH") return "home";
|
|
1091
|
+
if (data === "\x1b[F" || data === "\x1bOF") return "end";
|
|
730
1092
|
if (data === "\x1b[3~") return "delete";
|
|
731
1093
|
if (data === "\x1b[5~") return "pageUp";
|
|
732
1094
|
if (data === "\x1b[6~") return "pageDown";
|
|
@@ -744,186 +1106,3 @@ export function parseKey(data: string): string | undefined {
|
|
|
744
1106
|
|
|
745
1107
|
return undefined;
|
|
746
1108
|
}
|
|
747
|
-
|
|
748
|
-
// =============================================================================
|
|
749
|
-
// Legacy helper wrappers (for compatibility)
|
|
750
|
-
// =============================================================================
|
|
751
|
-
|
|
752
|
-
export function isArrowUp(data: string): boolean {
|
|
753
|
-
return matchesKey(data, "up");
|
|
754
|
-
}
|
|
755
|
-
|
|
756
|
-
export function isArrowDown(data: string): boolean {
|
|
757
|
-
return matchesKey(data, "down");
|
|
758
|
-
}
|
|
759
|
-
|
|
760
|
-
export function isArrowLeft(data: string): boolean {
|
|
761
|
-
return matchesKey(data, "left");
|
|
762
|
-
}
|
|
763
|
-
|
|
764
|
-
export function isArrowRight(data: string): boolean {
|
|
765
|
-
return matchesKey(data, "right");
|
|
766
|
-
}
|
|
767
|
-
|
|
768
|
-
export function isPageUp(data: string): boolean {
|
|
769
|
-
return matchesKey(data, "pageUp");
|
|
770
|
-
}
|
|
771
|
-
|
|
772
|
-
export function isPageDown(data: string): boolean {
|
|
773
|
-
return matchesKey(data, "pageDown");
|
|
774
|
-
}
|
|
775
|
-
|
|
776
|
-
export function isEscape(data: string): boolean {
|
|
777
|
-
return matchesKey(data, "escape") || matchesKey(data, "esc");
|
|
778
|
-
}
|
|
779
|
-
|
|
780
|
-
export function isEnter(data: string): boolean {
|
|
781
|
-
return matchesKey(data, "enter") || matchesKey(data, "return") || data === "\n";
|
|
782
|
-
}
|
|
783
|
-
|
|
784
|
-
export function isTab(data: string): boolean {
|
|
785
|
-
return matchesKey(data, "tab");
|
|
786
|
-
}
|
|
787
|
-
|
|
788
|
-
export function isShiftTab(data: string): boolean {
|
|
789
|
-
return matchesKey(data, "shift+tab");
|
|
790
|
-
}
|
|
791
|
-
|
|
792
|
-
export function isShiftEnter(data: string): boolean {
|
|
793
|
-
return matchesKey(data, "shift+enter");
|
|
794
|
-
}
|
|
795
|
-
|
|
796
|
-
export function isAltEnter(data: string): boolean {
|
|
797
|
-
return matchesKey(data, "alt+enter");
|
|
798
|
-
}
|
|
799
|
-
|
|
800
|
-
export function isAltBackspace(data: string): boolean {
|
|
801
|
-
return matchesKey(data, "alt+backspace");
|
|
802
|
-
}
|
|
803
|
-
|
|
804
|
-
export function isBackspace(data: string): boolean {
|
|
805
|
-
return matchesKey(data, "backspace");
|
|
806
|
-
}
|
|
807
|
-
|
|
808
|
-
export function isDelete(data: string): boolean {
|
|
809
|
-
return matchesKey(data, "delete");
|
|
810
|
-
}
|
|
811
|
-
|
|
812
|
-
export function isHome(data: string): boolean {
|
|
813
|
-
return matchesKey(data, "home");
|
|
814
|
-
}
|
|
815
|
-
|
|
816
|
-
export function isEnd(data: string): boolean {
|
|
817
|
-
return matchesKey(data, "end");
|
|
818
|
-
}
|
|
819
|
-
|
|
820
|
-
export function isCtrlA(data: string): boolean {
|
|
821
|
-
return matchesKey(data, "ctrl+a");
|
|
822
|
-
}
|
|
823
|
-
|
|
824
|
-
export function isCtrlC(data: string): boolean {
|
|
825
|
-
return matchesKey(data, "ctrl+c");
|
|
826
|
-
}
|
|
827
|
-
|
|
828
|
-
export function isCtrlD(data: string): boolean {
|
|
829
|
-
return matchesKey(data, "ctrl+d");
|
|
830
|
-
}
|
|
831
|
-
|
|
832
|
-
export function isCtrlE(data: string): boolean {
|
|
833
|
-
return matchesKey(data, "ctrl+e");
|
|
834
|
-
}
|
|
835
|
-
|
|
836
|
-
export function isCtrlG(data: string): boolean {
|
|
837
|
-
return matchesKey(data, "ctrl+g");
|
|
838
|
-
}
|
|
839
|
-
|
|
840
|
-
export function isCtrlK(data: string): boolean {
|
|
841
|
-
return matchesKey(data, "ctrl+k");
|
|
842
|
-
}
|
|
843
|
-
|
|
844
|
-
export function isCtrlL(data: string): boolean {
|
|
845
|
-
return matchesKey(data, "ctrl+l");
|
|
846
|
-
}
|
|
847
|
-
|
|
848
|
-
export function isCtrlO(data: string): boolean {
|
|
849
|
-
return matchesKey(data, "ctrl+o");
|
|
850
|
-
}
|
|
851
|
-
|
|
852
|
-
export function isCtrlP(data: string): boolean {
|
|
853
|
-
return matchesKey(data, "ctrl+p");
|
|
854
|
-
}
|
|
855
|
-
|
|
856
|
-
export function isCtrlT(data: string): boolean {
|
|
857
|
-
return matchesKey(data, "ctrl+t");
|
|
858
|
-
}
|
|
859
|
-
|
|
860
|
-
export function isCtrlU(data: string): boolean {
|
|
861
|
-
return matchesKey(data, "ctrl+u");
|
|
862
|
-
}
|
|
863
|
-
|
|
864
|
-
export function isCtrlV(data: string): boolean {
|
|
865
|
-
return matchesKey(data, "ctrl+v");
|
|
866
|
-
}
|
|
867
|
-
|
|
868
|
-
export function isCtrlW(data: string): boolean {
|
|
869
|
-
return matchesKey(data, "ctrl+w");
|
|
870
|
-
}
|
|
871
|
-
|
|
872
|
-
export function isCtrlY(data: string): boolean {
|
|
873
|
-
return matchesKey(data, "ctrl+y");
|
|
874
|
-
}
|
|
875
|
-
|
|
876
|
-
export function isCtrlZ(data: string): boolean {
|
|
877
|
-
return matchesKey(data, "ctrl+z");
|
|
878
|
-
}
|
|
879
|
-
|
|
880
|
-
export function isCtrlLeft(data: string): boolean {
|
|
881
|
-
return matchesKey(data, "ctrl+left");
|
|
882
|
-
}
|
|
883
|
-
|
|
884
|
-
export function isCtrlRight(data: string): boolean {
|
|
885
|
-
return matchesKey(data, "ctrl+right");
|
|
886
|
-
}
|
|
887
|
-
|
|
888
|
-
export function isAltLeft(data: string): boolean {
|
|
889
|
-
return matchesKey(data, "alt+left");
|
|
890
|
-
}
|
|
891
|
-
|
|
892
|
-
export function isAltRight(data: string): boolean {
|
|
893
|
-
return matchesKey(data, "alt+right");
|
|
894
|
-
}
|
|
895
|
-
|
|
896
|
-
export function isShiftCtrlD(data: string): boolean {
|
|
897
|
-
return matchesKey(data, "shift+ctrl+d") || matchesKey(data, "ctrl+shift+d");
|
|
898
|
-
}
|
|
899
|
-
|
|
900
|
-
export function isShiftCtrlO(data: string): boolean {
|
|
901
|
-
return matchesKey(data, "shift+ctrl+o") || matchesKey(data, "ctrl+shift+o");
|
|
902
|
-
}
|
|
903
|
-
|
|
904
|
-
export function isShiftCtrlP(data: string): boolean {
|
|
905
|
-
return matchesKey(data, "shift+ctrl+p") || matchesKey(data, "ctrl+shift+p");
|
|
906
|
-
}
|
|
907
|
-
|
|
908
|
-
export function isShiftBackspace(data: string): boolean {
|
|
909
|
-
return matchesKey(data, "shift+backspace");
|
|
910
|
-
}
|
|
911
|
-
|
|
912
|
-
export function isShiftDelete(data: string): boolean {
|
|
913
|
-
return matchesKey(data, "shift+delete");
|
|
914
|
-
}
|
|
915
|
-
|
|
916
|
-
export function isShiftSpace(data: string): boolean {
|
|
917
|
-
return matchesKey(data, "shift+space");
|
|
918
|
-
}
|
|
919
|
-
|
|
920
|
-
/**
|
|
921
|
-
* Check if input indicates Caps Lock state change.
|
|
922
|
-
* Kitty protocol reports Caps Lock via modifier bit 64.
|
|
923
|
-
*/
|
|
924
|
-
export function isCapsLock(data: string): boolean {
|
|
925
|
-
const parsed = parseKittySequence(data);
|
|
926
|
-
if (!parsed) return false;
|
|
927
|
-
// Caps Lock is modifier bit 64
|
|
928
|
-
return (parsed.modifier & 64) !== 0;
|
|
929
|
-
}
|