@google/gemini-cli 0.14.0-preview.0 → 0.15.0-nightly.20251110.c0b766ad
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/dist/google-gemini-cli-0.15.0-nightly.20251107.b8eeb553.tgz +0 -0
- package/dist/package.json +3 -3
- package/dist/src/config/config.d.ts +1 -8
- package/dist/src/config/config.js +3 -23
- package/dist/src/config/config.js.map +1 -1
- package/dist/src/config/config.test.js +11 -11
- package/dist/src/config/config.test.js.map +1 -1
- package/dist/src/config/extension-manager.js +8 -7
- package/dist/src/config/extension-manager.js.map +1 -1
- package/dist/src/config/extensions/github.d.ts +1 -1
- package/dist/src/config/extensions/github.js +8 -2
- package/dist/src/config/extensions/github.js.map +1 -1
- package/dist/src/config/extensions/github.test.js +8 -10
- package/dist/src/config/extensions/github.test.js.map +1 -1
- package/dist/src/gemini.js +2 -2
- package/dist/src/gemini.js.map +1 -1
- package/dist/src/generated/git-commit.d.ts +2 -2
- package/dist/src/generated/git-commit.js +2 -2
- package/dist/src/generated/git-commit.js.map +1 -1
- package/dist/src/services/FileCommandLoader.js +4 -2
- package/dist/src/services/FileCommandLoader.js.map +1 -1
- package/dist/src/services/FileCommandLoader.test.js +37 -0
- package/dist/src/services/FileCommandLoader.test.js.map +1 -1
- package/dist/src/test-utils/render.d.ts +1 -2
- package/dist/src/test-utils/render.js +2 -2
- package/dist/src/test-utils/render.js.map +1 -1
- package/dist/src/ui/AppContainer.js +14 -13
- package/dist/src/ui/AppContainer.js.map +1 -1
- package/dist/src/ui/commands/directoryCommand.js +2 -10
- package/dist/src/ui/commands/directoryCommand.js.map +1 -1
- package/dist/src/ui/commands/extensionsCommand.js +91 -2
- package/dist/src/ui/commands/extensionsCommand.js.map +1 -1
- package/dist/src/ui/commands/extensionsCommand.test.js +125 -1
- package/dist/src/ui/commands/extensionsCommand.test.js.map +1 -1
- package/dist/src/ui/commands/memoryCommand.js +2 -10
- package/dist/src/ui/commands/memoryCommand.js.map +1 -1
- package/dist/src/ui/commands/memoryCommand.test.js +11 -28
- package/dist/src/ui/commands/memoryCommand.test.js.map +1 -1
- package/dist/src/ui/commands/types.d.ts +0 -1
- package/dist/src/ui/commands/types.js.map +1 -1
- package/dist/src/ui/components/HistoryItemDisplay.js +1 -1
- package/dist/src/ui/components/HistoryItemDisplay.js.map +1 -1
- package/dist/src/ui/components/InputPrompt.test.js +35 -26
- package/dist/src/ui/components/InputPrompt.test.js.map +1 -1
- package/dist/src/ui/components/SettingsDialog.test.js +4 -4
- package/dist/src/ui/components/SettingsDialog.test.js.map +1 -1
- package/dist/src/ui/components/ThemeDialog.test.js +3 -3
- package/dist/src/ui/components/ThemeDialog.test.js.map +1 -1
- package/dist/src/ui/components/messages/InfoMessage.d.ts +2 -0
- package/dist/src/ui/components/messages/InfoMessage.js +4 -3
- package/dist/src/ui/components/messages/InfoMessage.js.map +1 -1
- package/dist/src/ui/components/shared/Scrollable.js +9 -4
- package/dist/src/ui/components/shared/Scrollable.js.map +1 -1
- package/dist/src/ui/components/shared/Scrollable.test.js +39 -1
- package/dist/src/ui/components/shared/Scrollable.test.js.map +1 -1
- package/dist/src/ui/components/shared/ScrollableList.test.js +1 -1
- package/dist/src/ui/components/shared/ScrollableList.test.js.map +1 -1
- package/dist/src/ui/components/shared/VirtualizedList.js +10 -3
- package/dist/src/ui/components/shared/VirtualizedList.js.map +1 -1
- package/dist/src/ui/components/shared/VirtualizedList.test.js +23 -0
- package/dist/src/ui/components/shared/VirtualizedList.test.js.map +1 -1
- package/dist/src/ui/contexts/KeypressContext.d.ts +5 -11
- package/dist/src/ui/contexts/KeypressContext.js +446 -755
- package/dist/src/ui/contexts/KeypressContext.js.map +1 -1
- package/dist/src/ui/contexts/KeypressContext.test.js +103 -334
- package/dist/src/ui/contexts/KeypressContext.test.js.map +1 -1
- package/dist/src/ui/contexts/ScrollProvider.js +25 -4
- package/dist/src/ui/contexts/ScrollProvider.js.map +1 -1
- package/dist/src/ui/contexts/ScrollProvider.test.d.ts +6 -0
- package/dist/src/ui/contexts/ScrollProvider.test.js +173 -0
- package/dist/src/ui/contexts/ScrollProvider.test.js.map +1 -0
- package/dist/src/ui/hooks/atCommandProcessor.test.js +1 -0
- package/dist/src/ui/hooks/atCommandProcessor.test.js.map +1 -1
- package/dist/src/ui/hooks/slashCommandProcessor.d.ts +1 -1
- package/dist/src/ui/hooks/slashCommandProcessor.js +1 -3
- package/dist/src/ui/hooks/slashCommandProcessor.js.map +1 -1
- package/dist/src/ui/hooks/slashCommandProcessor.test.js +1 -2
- package/dist/src/ui/hooks/slashCommandProcessor.test.js.map +1 -1
- package/dist/src/ui/hooks/useBatchedScroll.d.ts +14 -0
- package/dist/src/ui/hooks/useBatchedScroll.js +27 -0
- package/dist/src/ui/hooks/useBatchedScroll.js.map +1 -0
- package/dist/src/ui/hooks/useBatchedScroll.test.d.ts +6 -0
- package/dist/src/ui/hooks/useBatchedScroll.test.js +62 -0
- package/dist/src/ui/hooks/useBatchedScroll.test.js.map +1 -0
- package/dist/src/ui/hooks/useFocus.test.js +10 -10
- package/dist/src/ui/hooks/useFocus.test.js.map +1 -1
- package/dist/src/ui/hooks/useKeypress.test.js +8 -14
- package/dist/src/ui/hooks/useKeypress.test.js.map +1 -1
- package/dist/src/ui/noninteractive/nonInteractiveUi.js +0 -1
- package/dist/src/ui/noninteractive/nonInteractiveUi.js.map +1 -1
- package/dist/src/ui/state/extensions.d.ts +5 -0
- package/dist/src/ui/state/extensions.js +12 -0
- package/dist/src/ui/state/extensions.js.map +1 -1
- package/dist/src/ui/state/extensions.test.d.ts +6 -0
- package/dist/src/ui/state/extensions.test.js +62 -0
- package/dist/src/ui/state/extensions.test.js.map +1 -0
- package/dist/src/ui/types.d.ts +6 -0
- package/dist/src/ui/types.js +4 -0
- package/dist/src/ui/types.js.map +1 -1
- package/dist/src/ui/utils/terminalSetup.d.ts +1 -0
- package/dist/src/ui/utils/terminalSetup.js +1 -1
- package/dist/src/ui/utils/terminalSetup.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +4 -4
- package/dist/src/ui/utils/platformConstants.d.ts +0 -75
- package/dist/src/ui/utils/platformConstants.js +0 -78
- package/dist/src/ui/utils/platformConstants.js.map +0 -1
|
@@ -1,382 +1,461 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
|
|
2
|
+
/**
|
|
3
|
+
* @license
|
|
4
|
+
* Copyright 2025 Google LLC
|
|
5
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
6
|
+
*/
|
|
7
|
+
import { debugLogger } from '@google/gemini-cli-core';
|
|
3
8
|
import { useStdin } from 'ink';
|
|
4
9
|
import { createContext, useCallback, useContext, useEffect, useRef, } from 'react';
|
|
5
|
-
import
|
|
6
|
-
import {
|
|
7
|
-
import { BACKSLASH_ENTER_DETECTION_WINDOW_MS, CHAR_CODE_ESC, KITTY_CTRL_C, KITTY_KEYCODE_BACKSPACE, KITTY_KEYCODE_ENTER, KITTY_KEYCODE_NUMPAD_ENTER, KITTY_KEYCODE_TAB, MAX_KITTY_SEQUENCE_LENGTH, KITTY_MODIFIER_BASE, KITTY_MODIFIER_EVENT_TYPES_OFFSET, MODIFIER_SHIFT_BIT, MODIFIER_ALT_BIT, MODIFIER_CTRL_BIT, } from '../utils/platformConstants.js';
|
|
8
|
-
import { ESC, couldBeMouseSequence } from '../utils/input.js';
|
|
10
|
+
import { ESC } from '../utils/input.js';
|
|
11
|
+
import { parseMouseEvent } from '../utils/mouse.js';
|
|
9
12
|
import { FOCUS_IN, FOCUS_OUT } from '../hooks/useFocus.js';
|
|
10
|
-
|
|
11
|
-
export const
|
|
12
|
-
export const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
13
|
+
export const BACKSLASH_ENTER_TIMEOUT = 5;
|
|
14
|
+
export const ESC_TIMEOUT = 50;
|
|
15
|
+
export const PASTE_TIMEOUT = 50;
|
|
16
|
+
// Parse the key itself
|
|
17
|
+
const KEY_INFO_MAP = {
|
|
18
|
+
'[200~': { name: 'paste-start' },
|
|
19
|
+
'[201~': { name: 'paste-end' },
|
|
20
|
+
'[[A': { name: 'f1' },
|
|
21
|
+
'[[B': { name: 'f2' },
|
|
22
|
+
'[[C': { name: 'f3' },
|
|
23
|
+
'[[D': { name: 'f4' },
|
|
24
|
+
'[[E': { name: 'f5' },
|
|
25
|
+
'[1~': { name: 'home' },
|
|
26
|
+
'[2~': { name: 'insert' },
|
|
27
|
+
'[3~': { name: 'delete' },
|
|
28
|
+
'[4~': { name: 'end' },
|
|
29
|
+
'[5~': { name: 'pageup' },
|
|
30
|
+
'[6~': { name: 'pagedown' },
|
|
31
|
+
'[7~': { name: 'home' },
|
|
32
|
+
'[8~': { name: 'end' },
|
|
33
|
+
'[11~': { name: 'f1' },
|
|
34
|
+
'[12~': { name: 'f2' },
|
|
35
|
+
'[13~': { name: 'f3' },
|
|
36
|
+
'[14~': { name: 'f4' },
|
|
37
|
+
'[15~': { name: 'f5' },
|
|
38
|
+
'[17~': { name: 'f6' },
|
|
39
|
+
'[18~': { name: 'f7' },
|
|
40
|
+
'[19~': { name: 'f8' },
|
|
41
|
+
'[20~': { name: 'f9' },
|
|
42
|
+
'[21~': { name: 'f10' },
|
|
43
|
+
'[23~': { name: 'f11' },
|
|
44
|
+
'[24~': { name: 'f12' },
|
|
45
|
+
'[A': { name: 'up' },
|
|
46
|
+
'[B': { name: 'down' },
|
|
47
|
+
'[C': { name: 'right' },
|
|
48
|
+
'[D': { name: 'left' },
|
|
49
|
+
'[E': { name: 'clear' },
|
|
50
|
+
'[F': { name: 'end' },
|
|
51
|
+
'[H': { name: 'home' },
|
|
52
|
+
'[P': { name: 'f1' },
|
|
53
|
+
'[Q': { name: 'f2' },
|
|
54
|
+
'[R': { name: 'f3' },
|
|
55
|
+
'[S': { name: 'f4' },
|
|
56
|
+
OA: { name: 'up' },
|
|
57
|
+
OB: { name: 'down' },
|
|
58
|
+
OC: { name: 'right' },
|
|
59
|
+
OD: { name: 'left' },
|
|
60
|
+
OE: { name: 'clear' },
|
|
61
|
+
OF: { name: 'end' },
|
|
62
|
+
OH: { name: 'home' },
|
|
63
|
+
OP: { name: 'f1' },
|
|
64
|
+
OQ: { name: 'f2' },
|
|
65
|
+
OR: { name: 'f3' },
|
|
66
|
+
OS: { name: 'f4' },
|
|
67
|
+
'[[5~': { name: 'pageup' },
|
|
68
|
+
'[[6~': { name: 'pagedown' },
|
|
69
|
+
'[9u': { name: 'tab' },
|
|
70
|
+
'[13u': { name: 'return' },
|
|
71
|
+
'[27u': { name: 'escape' },
|
|
72
|
+
'[127u': { name: 'backspace' },
|
|
73
|
+
'[57414u': { name: 'return' }, // Numpad Enter
|
|
74
|
+
'[a': { name: 'up', shift: true },
|
|
75
|
+
'[b': { name: 'down', shift: true },
|
|
76
|
+
'[c': { name: 'right', shift: true },
|
|
77
|
+
'[d': { name: 'left', shift: true },
|
|
78
|
+
'[e': { name: 'clear', shift: true },
|
|
79
|
+
'[2$': { name: 'insert', shift: true },
|
|
80
|
+
'[3$': { name: 'delete', shift: true },
|
|
81
|
+
'[5$': { name: 'pageup', shift: true },
|
|
82
|
+
'[6$': { name: 'pagedown', shift: true },
|
|
83
|
+
'[7$': { name: 'home', shift: true },
|
|
84
|
+
'[8$': { name: 'end', shift: true },
|
|
85
|
+
'[Z': { name: 'tab', shift: true },
|
|
86
|
+
Oa: { name: 'up', ctrl: true },
|
|
87
|
+
Ob: { name: 'down', ctrl: true },
|
|
88
|
+
Oc: { name: 'right', ctrl: true },
|
|
89
|
+
Od: { name: 'left', ctrl: true },
|
|
90
|
+
Oe: { name: 'clear', ctrl: true },
|
|
91
|
+
'[2^': { name: 'insert', ctrl: true },
|
|
92
|
+
'[3^': { name: 'delete', ctrl: true },
|
|
93
|
+
'[5^': { name: 'pageup', ctrl: true },
|
|
94
|
+
'[6^': { name: 'pagedown', ctrl: true },
|
|
95
|
+
'[7^': { name: 'home', ctrl: true },
|
|
96
|
+
'[8^': { name: 'end', ctrl: true },
|
|
97
|
+
};
|
|
98
|
+
const kUTF16SurrogateThreshold = 0x10000; // 2 ** 16
|
|
99
|
+
function charLengthAt(str, i) {
|
|
100
|
+
if (str.length <= i) {
|
|
101
|
+
// Pretend to move to the right. This is necessary to autocomplete while
|
|
102
|
+
// moving to the right.
|
|
103
|
+
return 1;
|
|
104
|
+
}
|
|
105
|
+
const code = str.codePointAt(i);
|
|
106
|
+
return code !== undefined && code >= kUTF16SurrogateThreshold ? 2 : 1;
|
|
107
|
+
}
|
|
20
108
|
const MAC_ALT_KEY_CHARACTER_MAP = {
|
|
21
109
|
'\u222B': 'b', // "∫" back one word
|
|
22
110
|
'\u0192': 'f', // "ƒ" forward one word
|
|
23
111
|
'\u00B5': 'm', // "µ" toggle markup view
|
|
24
112
|
};
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
B: 'down',
|
|
32
|
-
C: 'right',
|
|
33
|
-
D: 'left',
|
|
34
|
-
H: 'home',
|
|
35
|
-
F: 'end',
|
|
36
|
-
P: 'f1',
|
|
37
|
-
Q: 'f2',
|
|
38
|
-
R: 'f3',
|
|
39
|
-
S: 'f4',
|
|
40
|
-
};
|
|
41
|
-
/**
|
|
42
|
-
* Maps key codes from tilde-coded functional keys `\x1b[<code>~`
|
|
43
|
-
* to their corresponding key names.
|
|
44
|
-
*/
|
|
45
|
-
const TILDE_KEYCODE_TO_NAME = {
|
|
46
|
-
1: 'home',
|
|
47
|
-
2: 'insert',
|
|
48
|
-
3: 'delete',
|
|
49
|
-
4: 'end',
|
|
50
|
-
5: 'pageup',
|
|
51
|
-
6: 'pagedown',
|
|
52
|
-
11: 'f1',
|
|
53
|
-
12: 'f2',
|
|
54
|
-
13: 'f3',
|
|
55
|
-
14: 'f4',
|
|
56
|
-
15: 'f5',
|
|
57
|
-
17: 'f6', // skipping 16 is intentional
|
|
58
|
-
18: 'f7',
|
|
59
|
-
19: 'f8',
|
|
60
|
-
20: 'f9',
|
|
61
|
-
21: 'f10',
|
|
62
|
-
23: 'f11', // skipping 22 is intentional
|
|
63
|
-
24: 'f12',
|
|
64
|
-
};
|
|
65
|
-
/**
|
|
66
|
-
* Check if a buffer could potentially be a valid kitty sequence or its prefix.
|
|
67
|
-
*/
|
|
68
|
-
function couldBeKittySequence(buffer) {
|
|
69
|
-
// Kitty sequences always start with ESC[.
|
|
70
|
-
if (buffer.length === 0)
|
|
71
|
-
return true;
|
|
72
|
-
if (buffer === ESC || buffer === `${ESC}[`)
|
|
73
|
-
return true;
|
|
74
|
-
if (!buffer.startsWith(`${ESC}[`))
|
|
75
|
-
return false;
|
|
76
|
-
if (couldBeMouseSequence(buffer))
|
|
77
|
-
return true;
|
|
78
|
-
// Check for known kitty sequence patterns:
|
|
79
|
-
// 1. ESC[<digit> - could be CSI-u or tilde-coded
|
|
80
|
-
// 2. ESC[1;<digit> - parameterized functional
|
|
81
|
-
// 3. ESC[<letter> - legacy functional keys
|
|
82
|
-
// 4. ESC[Z - reverse tab
|
|
83
|
-
const afterCSI = buffer.slice(2);
|
|
84
|
-
// Check if it starts with a digit (could be CSI-u or parameterized)
|
|
85
|
-
if (/^\d/.test(afterCSI))
|
|
86
|
-
return true;
|
|
87
|
-
// Check for known single-letter sequences
|
|
88
|
-
if (/^[ABCDHFPQRSZ]/.test(afterCSI))
|
|
89
|
-
return true;
|
|
90
|
-
// Check for 1; pattern (parameterized sequences)
|
|
91
|
-
if (/^1;\d/.test(afterCSI))
|
|
92
|
-
return true;
|
|
93
|
-
// Anything else starting with ESC[ that doesn't match our patterns
|
|
94
|
-
// is likely not a kitty sequence we handle
|
|
95
|
-
return false;
|
|
96
|
-
}
|
|
97
|
-
/**
|
|
98
|
-
* Parses a single complete kitty/parameterized/legacy sequence from the start
|
|
99
|
-
* of the buffer.
|
|
100
|
-
*
|
|
101
|
-
* This enables peel-and-continue parsing for batched input, allowing us to
|
|
102
|
-
* "peel off" one complete event when multiple sequences arrive in a single
|
|
103
|
-
* chunk, preventing buffer overflow and fragmentation.
|
|
104
|
-
*
|
|
105
|
-
* @param buffer - The input buffer string to parse.
|
|
106
|
-
* @returns The parsed Key and the number of characters consumed, or null if
|
|
107
|
-
* no complete sequence is found at the start of the buffer.
|
|
108
|
-
*/
|
|
109
|
-
function parseKittyPrefix(buffer) {
|
|
110
|
-
// In older terminals ESC [ Z was used as Cursor Backward Tabulation (CBT)
|
|
111
|
-
// In newer terminals the same functionality of key combination for moving
|
|
112
|
-
// backward through focusable elements is Shift+Tab, hence we will
|
|
113
|
-
// map ESC [ Z to Shift+Tab
|
|
114
|
-
// 0) Reverse Tab (legacy): ESC [ Z
|
|
115
|
-
// Treat as Shift+Tab for UI purposes.
|
|
116
|
-
// Regex parts:
|
|
117
|
-
// ^ - start of buffer
|
|
118
|
-
// ESC [ - CSI introducer
|
|
119
|
-
// Z - legacy reverse tab
|
|
120
|
-
const revTabLegacy = new RegExp(`^${ESC}\\[Z`);
|
|
121
|
-
let m = buffer.match(revTabLegacy);
|
|
122
|
-
if (m) {
|
|
123
|
-
return {
|
|
124
|
-
key: {
|
|
125
|
-
name: 'tab',
|
|
126
|
-
ctrl: false,
|
|
127
|
-
meta: false,
|
|
128
|
-
shift: true,
|
|
129
|
-
paste: false,
|
|
130
|
-
sequence: buffer.slice(0, m[0].length),
|
|
131
|
-
kittyProtocol: false,
|
|
132
|
-
},
|
|
133
|
-
length: m[0].length,
|
|
134
|
-
};
|
|
135
|
-
}
|
|
136
|
-
// 1) Reverse Tab (parameterized): ESC [ 1 ; <mods> Z
|
|
137
|
-
// Parameterized reverse Tab: ESC [ 1 ; <mods> Z
|
|
138
|
-
const revTabParam = new RegExp(`^${ESC}\\[1;(\\d+)Z`);
|
|
139
|
-
m = buffer.match(revTabParam);
|
|
140
|
-
if (m) {
|
|
141
|
-
let mods = parseInt(m[1], 10);
|
|
142
|
-
if (mods >= KITTY_MODIFIER_EVENT_TYPES_OFFSET) {
|
|
143
|
-
mods -= KITTY_MODIFIER_EVENT_TYPES_OFFSET;
|
|
144
|
-
}
|
|
145
|
-
const bits = mods - KITTY_MODIFIER_BASE;
|
|
146
|
-
const alt = (bits & MODIFIER_ALT_BIT) === MODIFIER_ALT_BIT;
|
|
147
|
-
const ctrl = (bits & MODIFIER_CTRL_BIT) === MODIFIER_CTRL_BIT;
|
|
148
|
-
return {
|
|
149
|
-
key: {
|
|
150
|
-
name: 'tab',
|
|
151
|
-
ctrl,
|
|
152
|
-
meta: alt,
|
|
153
|
-
// Reverse tab implies Shift behavior; force shift regardless of mods
|
|
154
|
-
shift: true,
|
|
155
|
-
paste: false,
|
|
156
|
-
sequence: buffer.slice(0, m[0].length),
|
|
157
|
-
kittyProtocol: true,
|
|
158
|
-
},
|
|
159
|
-
length: m[0].length,
|
|
160
|
-
};
|
|
161
|
-
}
|
|
162
|
-
// 2) Parameterized functional: ESC [ 1 ; <mods> (A|B|C|D|H|F|P|Q|R|S)
|
|
163
|
-
// 2) Parameterized functional: ESC [ 1 ; <mods> (A|B|C|D|H|F|P|Q|R|S)
|
|
164
|
-
// Arrows, Home/End, F1–F4 with modifiers encoded in <mods>.
|
|
165
|
-
const arrowPrefix = new RegExp(`^${ESC}\\[1;(\\d+)([ABCDHFPQSR])`);
|
|
166
|
-
m = buffer.match(arrowPrefix);
|
|
167
|
-
if (m) {
|
|
168
|
-
let mods = parseInt(m[1], 10);
|
|
169
|
-
if (mods >= KITTY_MODIFIER_EVENT_TYPES_OFFSET) {
|
|
170
|
-
mods -= KITTY_MODIFIER_EVENT_TYPES_OFFSET;
|
|
113
|
+
function nonKeyboardEventFilter(keypressHandler) {
|
|
114
|
+
return (key) => {
|
|
115
|
+
if (!parseMouseEvent(key.sequence) &&
|
|
116
|
+
key.sequence !== FOCUS_IN &&
|
|
117
|
+
key.sequence !== FOCUS_OUT) {
|
|
118
|
+
keypressHandler(key);
|
|
171
119
|
}
|
|
172
|
-
|
|
173
|
-
const shift = (bits & MODIFIER_SHIFT_BIT) === MODIFIER_SHIFT_BIT;
|
|
174
|
-
const alt = (bits & MODIFIER_ALT_BIT) === MODIFIER_ALT_BIT;
|
|
175
|
-
const ctrl = (bits & MODIFIER_CTRL_BIT) === MODIFIER_CTRL_BIT;
|
|
176
|
-
const sym = m[2];
|
|
177
|
-
const name = LEGACY_FUNC_TO_NAME[sym] || '';
|
|
178
|
-
if (!name)
|
|
179
|
-
return null;
|
|
180
|
-
return {
|
|
181
|
-
key: {
|
|
182
|
-
name,
|
|
183
|
-
ctrl,
|
|
184
|
-
meta: alt,
|
|
185
|
-
shift,
|
|
186
|
-
paste: false,
|
|
187
|
-
sequence: buffer.slice(0, m[0].length),
|
|
188
|
-
kittyProtocol: true,
|
|
189
|
-
},
|
|
190
|
-
length: m[0].length,
|
|
191
|
-
};
|
|
192
|
-
}
|
|
193
|
-
// 3) CSI-u form: ESC [ <code> ; <mods> (u|~)
|
|
194
|
-
// 3) CSI-u and tilde-coded functional keys: ESC [ <code> ; <mods> (u|~)
|
|
195
|
-
// 'u' terminator: Kitty CSI-u; '~' terminator: tilde-coded function keys.
|
|
196
|
-
const csiUPrefix = new RegExp(`^${ESC}\\[(\\d+)(;(\\d+))?([u~])`);
|
|
197
|
-
m = buffer.match(csiUPrefix);
|
|
198
|
-
if (m) {
|
|
199
|
-
const keyCode = parseInt(m[1], 10);
|
|
200
|
-
let modifiers = m[3] ? parseInt(m[3], 10) : KITTY_MODIFIER_BASE;
|
|
201
|
-
if (modifiers >= KITTY_MODIFIER_EVENT_TYPES_OFFSET) {
|
|
202
|
-
modifiers -= KITTY_MODIFIER_EVENT_TYPES_OFFSET;
|
|
203
|
-
}
|
|
204
|
-
const modifierBits = modifiers - KITTY_MODIFIER_BASE;
|
|
205
|
-
const shift = (modifierBits & MODIFIER_SHIFT_BIT) === MODIFIER_SHIFT_BIT;
|
|
206
|
-
const alt = (modifierBits & MODIFIER_ALT_BIT) === MODIFIER_ALT_BIT;
|
|
207
|
-
const ctrl = (modifierBits & MODIFIER_CTRL_BIT) === MODIFIER_CTRL_BIT;
|
|
208
|
-
const terminator = m[4];
|
|
209
|
-
// Tilde-coded functional keys (Delete, Insert, PageUp/Down, Home/End)
|
|
210
|
-
if (terminator === '~') {
|
|
211
|
-
const name = TILDE_KEYCODE_TO_NAME[keyCode];
|
|
212
|
-
if (name) {
|
|
213
|
-
return {
|
|
214
|
-
key: {
|
|
215
|
-
name,
|
|
216
|
-
ctrl,
|
|
217
|
-
meta: alt,
|
|
218
|
-
shift,
|
|
219
|
-
paste: false,
|
|
220
|
-
sequence: buffer.slice(0, m[0].length),
|
|
221
|
-
kittyProtocol: false,
|
|
222
|
-
},
|
|
223
|
-
length: m[0].length,
|
|
224
|
-
};
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
const kittyKeyCodeToName = {
|
|
228
|
-
[CHAR_CODE_ESC]: 'escape',
|
|
229
|
-
[KITTY_KEYCODE_TAB]: 'tab',
|
|
230
|
-
[KITTY_KEYCODE_BACKSPACE]: 'backspace',
|
|
231
|
-
[KITTY_KEYCODE_ENTER]: 'return',
|
|
232
|
-
[KITTY_KEYCODE_NUMPAD_ENTER]: 'return',
|
|
233
|
-
};
|
|
234
|
-
const name = kittyKeyCodeToName[keyCode];
|
|
235
|
-
if (name) {
|
|
236
|
-
return {
|
|
237
|
-
key: {
|
|
238
|
-
name,
|
|
239
|
-
ctrl,
|
|
240
|
-
meta: alt,
|
|
241
|
-
shift,
|
|
242
|
-
paste: false,
|
|
243
|
-
sequence: buffer.slice(0, m[0].length),
|
|
244
|
-
kittyProtocol: true,
|
|
245
|
-
},
|
|
246
|
-
length: m[0].length,
|
|
247
|
-
};
|
|
248
|
-
}
|
|
249
|
-
// Ctrl+letters and Alt+letters
|
|
250
|
-
if ((ctrl || alt) &&
|
|
251
|
-
keyCode >= 'a'.charCodeAt(0) &&
|
|
252
|
-
keyCode <= 'z'.charCodeAt(0)) {
|
|
253
|
-
const letter = String.fromCharCode(keyCode);
|
|
254
|
-
return {
|
|
255
|
-
key: {
|
|
256
|
-
name: letter,
|
|
257
|
-
ctrl,
|
|
258
|
-
meta: alt,
|
|
259
|
-
shift,
|
|
260
|
-
paste: false,
|
|
261
|
-
sequence: buffer.slice(0, m[0].length),
|
|
262
|
-
kittyProtocol: true,
|
|
263
|
-
},
|
|
264
|
-
length: m[0].length,
|
|
265
|
-
};
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
// 4) Legacy function keys (no parameters): ESC [ (A|B|C|D|H|F)
|
|
269
|
-
// Arrows + Home/End without modifiers.
|
|
270
|
-
const legacyFuncKey = new RegExp(`^${ESC}\\[([ABCDHF])`);
|
|
271
|
-
m = buffer.match(legacyFuncKey);
|
|
272
|
-
if (m) {
|
|
273
|
-
const sym = m[1];
|
|
274
|
-
const name = LEGACY_FUNC_TO_NAME[sym];
|
|
275
|
-
return {
|
|
276
|
-
key: {
|
|
277
|
-
name,
|
|
278
|
-
ctrl: false,
|
|
279
|
-
meta: false,
|
|
280
|
-
shift: false,
|
|
281
|
-
paste: false,
|
|
282
|
-
sequence: buffer.slice(0, m[0].length),
|
|
283
|
-
kittyProtocol: false,
|
|
284
|
-
},
|
|
285
|
-
length: m[0].length,
|
|
286
|
-
};
|
|
287
|
-
}
|
|
288
|
-
return null;
|
|
120
|
+
};
|
|
289
121
|
}
|
|
290
122
|
/**
|
|
291
|
-
*
|
|
123
|
+
* Buffers "/" keys to see if they are followed return.
|
|
124
|
+
* Will flush the buffer if no data is received for DRAG_COMPLETION_TIMEOUT_MS
|
|
125
|
+
* or when a null key is received.
|
|
292
126
|
*/
|
|
293
|
-
function
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
127
|
+
function bufferBackslashEnter(keypressHandler) {
|
|
128
|
+
const bufferer = (function* () {
|
|
129
|
+
while (true) {
|
|
130
|
+
const key = yield;
|
|
131
|
+
if (key == null) {
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
else if (key.sequence !== '\\') {
|
|
135
|
+
keypressHandler(key);
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
const timeoutId = setTimeout(() => bufferer.next(null), BACKSLASH_ENTER_TIMEOUT);
|
|
139
|
+
const nextKey = yield;
|
|
140
|
+
clearTimeout(timeoutId);
|
|
141
|
+
if (nextKey === null) {
|
|
142
|
+
keypressHandler(key);
|
|
143
|
+
}
|
|
144
|
+
else if (nextKey.name === 'return') {
|
|
145
|
+
keypressHandler({
|
|
146
|
+
...nextKey,
|
|
147
|
+
shift: true,
|
|
148
|
+
sequence: '\r', // Corrected escaping for newline
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
keypressHandler(key);
|
|
153
|
+
keypressHandler(nextKey);
|
|
154
|
+
}
|
|
314
155
|
}
|
|
315
|
-
}
|
|
316
|
-
|
|
156
|
+
})();
|
|
157
|
+
bufferer.next(); // prime the generator so it starts listening.
|
|
158
|
+
return (key) => bufferer.next(key);
|
|
317
159
|
}
|
|
318
160
|
/**
|
|
319
|
-
*
|
|
320
|
-
*
|
|
161
|
+
* Buffers paste events between paste-start and paste-end sequences.
|
|
162
|
+
* Will flush the buffer if no data is received for PASTE_TIMEOUT ms or
|
|
163
|
+
* when a null key is received.
|
|
321
164
|
*/
|
|
322
|
-
function
|
|
323
|
-
|
|
324
|
-
let data = yield;
|
|
325
|
-
if (data.length === 0) {
|
|
326
|
-
continue; // we timed out
|
|
327
|
-
}
|
|
165
|
+
function bufferPaste(keypressHandler) {
|
|
166
|
+
const bufferer = (function* () {
|
|
328
167
|
while (true) {
|
|
329
|
-
|
|
330
|
-
if (
|
|
331
|
-
|
|
332
|
-
passthrough.write(data);
|
|
333
|
-
break;
|
|
168
|
+
let key = yield;
|
|
169
|
+
if (key === null) {
|
|
170
|
+
continue;
|
|
334
171
|
}
|
|
335
|
-
if (
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
data = data.slice(index);
|
|
172
|
+
else if (key.name !== 'paste-start') {
|
|
173
|
+
keypressHandler(key);
|
|
174
|
+
continue;
|
|
339
175
|
}
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
if (
|
|
346
|
-
// we timed out. Just dump what we have and start over.
|
|
347
|
-
passthrough.write(data);
|
|
176
|
+
let buffer = '';
|
|
177
|
+
while (true) {
|
|
178
|
+
const timeoutId = setTimeout(() => bufferer.next(null), PASTE_TIMEOUT);
|
|
179
|
+
key = yield;
|
|
180
|
+
clearTimeout(timeoutId);
|
|
181
|
+
if (key === null || key.name === 'paste-end') {
|
|
348
182
|
break;
|
|
349
183
|
}
|
|
350
|
-
|
|
184
|
+
buffer += key.sequence;
|
|
351
185
|
}
|
|
352
|
-
|
|
353
|
-
keypressHandler(
|
|
354
|
-
name: '
|
|
186
|
+
if (buffer.length > 0) {
|
|
187
|
+
keypressHandler({
|
|
188
|
+
name: '',
|
|
355
189
|
ctrl: false,
|
|
356
190
|
meta: false,
|
|
357
191
|
shift: false,
|
|
358
|
-
paste:
|
|
359
|
-
sequence:
|
|
192
|
+
paste: true,
|
|
193
|
+
sequence: buffer,
|
|
360
194
|
});
|
|
361
|
-
data = data.slice(PASTE_MODE_START.length);
|
|
362
195
|
}
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
196
|
+
}
|
|
197
|
+
})();
|
|
198
|
+
bufferer.next(); // prime the generator so it starts listening.
|
|
199
|
+
return (key) => bufferer.next(key);
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Turns raw data strings into keypress events sent to the provided handler.
|
|
203
|
+
* Buffers escape sequences until a full sequence is received or
|
|
204
|
+
* until a timeout occurs.
|
|
205
|
+
*/
|
|
206
|
+
function createDataListener(keypressHandler) {
|
|
207
|
+
const parser = emitKeys(keypressHandler);
|
|
208
|
+
parser.next(); // prime the generator so it starts listening.
|
|
209
|
+
let timeoutId;
|
|
210
|
+
return (data) => {
|
|
211
|
+
clearTimeout(timeoutId);
|
|
212
|
+
for (const char of data) {
|
|
213
|
+
parser.next(char);
|
|
214
|
+
}
|
|
215
|
+
if (data.length !== 0) {
|
|
216
|
+
timeoutId = setTimeout(() => parser.next(''), ESC_TIMEOUT);
|
|
217
|
+
}
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Translates raw keypress characters into key events.
|
|
222
|
+
* Buffers escape sequences until a full sequence is received or
|
|
223
|
+
* until an empty string is sent to indicate a timeout.
|
|
224
|
+
*/
|
|
225
|
+
function* emitKeys(keypressHandler) {
|
|
226
|
+
while (true) {
|
|
227
|
+
let ch = yield;
|
|
228
|
+
let sequence = ch;
|
|
229
|
+
let escaped = false;
|
|
230
|
+
let name = undefined;
|
|
231
|
+
let ctrl = false;
|
|
232
|
+
let meta = false;
|
|
233
|
+
let shift = false;
|
|
234
|
+
let code = undefined;
|
|
235
|
+
if (ch === ESC) {
|
|
236
|
+
escaped = true;
|
|
237
|
+
ch = yield;
|
|
238
|
+
sequence += ch;
|
|
239
|
+
if (ch === ESC) {
|
|
240
|
+
ch = yield;
|
|
241
|
+
sequence += ch;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
if (escaped && (ch === 'O' || ch === '[')) {
|
|
245
|
+
// ANSI escape sequence
|
|
246
|
+
code = ch;
|
|
247
|
+
let modifier = 0;
|
|
248
|
+
if (ch === 'O') {
|
|
249
|
+
// ESC O letter
|
|
250
|
+
// ESC O modifier letter
|
|
251
|
+
ch = yield;
|
|
252
|
+
sequence += ch;
|
|
253
|
+
if (ch >= '0' && ch <= '9') {
|
|
254
|
+
modifier = parseInt(ch, 10) - 1;
|
|
255
|
+
ch = yield;
|
|
256
|
+
sequence += ch;
|
|
257
|
+
}
|
|
258
|
+
code += ch;
|
|
259
|
+
}
|
|
260
|
+
else if (ch === '[') {
|
|
261
|
+
// ESC [ letter
|
|
262
|
+
// ESC [ modifier letter
|
|
263
|
+
// ESC [ [ modifier letter
|
|
264
|
+
// ESC [ [ num char
|
|
265
|
+
ch = yield;
|
|
266
|
+
sequence += ch;
|
|
267
|
+
if (ch === '[') {
|
|
268
|
+
// \x1b[[A
|
|
269
|
+
// ^--- escape codes might have a second bracket
|
|
270
|
+
code += ch;
|
|
271
|
+
ch = yield;
|
|
272
|
+
sequence += ch;
|
|
273
|
+
}
|
|
274
|
+
/*
|
|
275
|
+
* Here and later we try to buffer just enough data to get
|
|
276
|
+
* a complete ascii sequence.
|
|
277
|
+
*
|
|
278
|
+
* We have basically two classes of ascii characters to process:
|
|
279
|
+
*
|
|
280
|
+
*
|
|
281
|
+
* 1. `\x1b[24;5~` should be parsed as { code: '[24~', modifier: 5 }
|
|
282
|
+
*
|
|
283
|
+
* This particular example is featuring Ctrl+F12 in xterm.
|
|
284
|
+
*
|
|
285
|
+
* - `;5` part is optional, e.g. it could be `\x1b[24~`
|
|
286
|
+
* - first part can contain one or two digits
|
|
287
|
+
* - there is also special case when there can be 3 digits
|
|
288
|
+
* but without modifier. They are the case of paste bracket mode
|
|
289
|
+
*
|
|
290
|
+
* So the generic regexp is like /^(?:\d\d?(;\d)?[~^$]|\d{3}~)$/
|
|
291
|
+
*
|
|
292
|
+
*
|
|
293
|
+
* 2. `\x1b[1;5H` should be parsed as { code: '[H', modifier: 5 }
|
|
294
|
+
*
|
|
295
|
+
* This particular example is featuring Ctrl+Home in xterm.
|
|
296
|
+
*
|
|
297
|
+
* - `1;5` part is optional, e.g. it could be `\x1b[H`
|
|
298
|
+
* - `1;` part is optional, e.g. it could be `\x1b[5H`
|
|
299
|
+
*
|
|
300
|
+
* So the generic regexp is like /^((\d;)?\d)?[A-Za-z]$/
|
|
301
|
+
*
|
|
302
|
+
*/
|
|
303
|
+
const cmdStart = sequence.length - 1;
|
|
304
|
+
// collect as many digits as possible
|
|
305
|
+
while (ch >= '0' && ch <= '9') {
|
|
306
|
+
ch = yield;
|
|
307
|
+
sequence += ch;
|
|
308
|
+
}
|
|
309
|
+
// skip modifier
|
|
310
|
+
if (ch === ';') {
|
|
311
|
+
ch = yield;
|
|
312
|
+
sequence += ch;
|
|
313
|
+
// collect as many digits as possible
|
|
314
|
+
while (ch >= '0' && ch <= '9') {
|
|
315
|
+
ch = yield;
|
|
316
|
+
sequence += ch;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
else if (ch === '<') {
|
|
320
|
+
// SGR mouse mode
|
|
321
|
+
ch = yield;
|
|
322
|
+
sequence += ch;
|
|
323
|
+
// Don't skip on empty string here to avoid timeouts on slow events.
|
|
324
|
+
while (ch === '' || ch === ';' || (ch >= '0' && ch <= '9')) {
|
|
325
|
+
ch = yield;
|
|
326
|
+
sequence += ch;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
else if (ch === 'M') {
|
|
330
|
+
// X11 mouse mode
|
|
331
|
+
// three characters after 'M'
|
|
332
|
+
ch = yield;
|
|
333
|
+
sequence += ch;
|
|
334
|
+
ch = yield;
|
|
335
|
+
sequence += ch;
|
|
336
|
+
ch = yield;
|
|
337
|
+
sequence += ch;
|
|
338
|
+
}
|
|
339
|
+
/*
|
|
340
|
+
* We buffered enough data, now trying to extract code
|
|
341
|
+
* and modifier from it
|
|
342
|
+
*/
|
|
343
|
+
const cmd = sequence.slice(cmdStart);
|
|
344
|
+
let match;
|
|
345
|
+
if ((match = /^(\d+)(?:;(\d+))?([~^$u])$/.exec(cmd))) {
|
|
346
|
+
code += match[1] + match[3];
|
|
347
|
+
// Defaults to '1' if no modifier exists, resulting in a 0 modifier value
|
|
348
|
+
modifier = parseInt(match[2] ?? '1', 10) - 1;
|
|
349
|
+
}
|
|
350
|
+
else if ((match = /^((\d;)?(\d))?([A-Za-z])$/.exec(cmd))) {
|
|
351
|
+
code += match[4];
|
|
352
|
+
modifier = parseInt(match[3] ?? '1', 10) - 1;
|
|
353
|
+
}
|
|
354
|
+
else {
|
|
355
|
+
code += cmd;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
// Parse the key modifier
|
|
359
|
+
ctrl = !!(modifier & 4);
|
|
360
|
+
meta = !!(modifier & 10); // use 10 to catch both alt (2) and meta (8).
|
|
361
|
+
shift = !!(modifier & 1);
|
|
362
|
+
const keyInfo = KEY_INFO_MAP[code];
|
|
363
|
+
if (keyInfo) {
|
|
364
|
+
name = keyInfo.name;
|
|
365
|
+
if (keyInfo.shift) {
|
|
366
|
+
shift = true;
|
|
367
|
+
}
|
|
368
|
+
if (keyInfo.ctrl) {
|
|
369
|
+
ctrl = true;
|
|
370
|
+
}
|
|
373
371
|
}
|
|
374
372
|
else {
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
373
|
+
name = 'undefined';
|
|
374
|
+
if ((ctrl || meta) && (code.endsWith('u') || code.endsWith('~'))) {
|
|
375
|
+
// CSI-u or tilde-coded functional keys: ESC [ <code> ; <mods> (u|~)
|
|
376
|
+
const codeNumber = parseInt(code.slice(1, -1), 10);
|
|
377
|
+
if (codeNumber >= 'a'.charCodeAt(0) &&
|
|
378
|
+
codeNumber <= 'z'.charCodeAt(0)) {
|
|
379
|
+
name = String.fromCharCode(codeNumber);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
378
382
|
}
|
|
379
383
|
}
|
|
384
|
+
else if (ch === '\r') {
|
|
385
|
+
// carriage return
|
|
386
|
+
name = 'return';
|
|
387
|
+
meta = escaped;
|
|
388
|
+
}
|
|
389
|
+
else if (ch === '\n') {
|
|
390
|
+
// Enter, should have been called linefeed
|
|
391
|
+
name = 'enter';
|
|
392
|
+
meta = escaped;
|
|
393
|
+
}
|
|
394
|
+
else if (ch === '\t') {
|
|
395
|
+
// tab
|
|
396
|
+
name = 'tab';
|
|
397
|
+
meta = escaped;
|
|
398
|
+
}
|
|
399
|
+
else if (ch === '\b' || ch === '\x7f') {
|
|
400
|
+
// backspace or ctrl+h
|
|
401
|
+
name = 'backspace';
|
|
402
|
+
meta = escaped;
|
|
403
|
+
}
|
|
404
|
+
else if (ch === ESC) {
|
|
405
|
+
// escape key
|
|
406
|
+
name = 'escape';
|
|
407
|
+
meta = escaped;
|
|
408
|
+
}
|
|
409
|
+
else if (ch === ' ') {
|
|
410
|
+
name = 'space';
|
|
411
|
+
meta = escaped;
|
|
412
|
+
}
|
|
413
|
+
else if (!escaped && ch <= '\x1a') {
|
|
414
|
+
// ctrl+letter
|
|
415
|
+
name = String.fromCharCode(ch.charCodeAt(0) + 'a'.charCodeAt(0) - 1);
|
|
416
|
+
ctrl = true;
|
|
417
|
+
}
|
|
418
|
+
else if (/^[0-9A-Za-z]$/.exec(ch) !== null) {
|
|
419
|
+
// Letter, number, shift+letter
|
|
420
|
+
name = ch.toLowerCase();
|
|
421
|
+
shift = /^[A-Z]$/.exec(ch) !== null;
|
|
422
|
+
meta = escaped;
|
|
423
|
+
}
|
|
424
|
+
else if (MAC_ALT_KEY_CHARACTER_MAP[ch] && process.platform === 'darwin') {
|
|
425
|
+
name = MAC_ALT_KEY_CHARACTER_MAP[ch];
|
|
426
|
+
meta = true;
|
|
427
|
+
}
|
|
428
|
+
else if (sequence === `${ESC}${ESC}`) {
|
|
429
|
+
// Double escape
|
|
430
|
+
name = 'escape';
|
|
431
|
+
meta = true;
|
|
432
|
+
// Emit first escape key here, then continue processing
|
|
433
|
+
keypressHandler({
|
|
434
|
+
name: 'escape',
|
|
435
|
+
ctrl,
|
|
436
|
+
meta,
|
|
437
|
+
shift,
|
|
438
|
+
paste: false,
|
|
439
|
+
sequence: ESC,
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
else if (escaped) {
|
|
443
|
+
// Escape sequence timeout
|
|
444
|
+
name = ch.length ? undefined : 'escape';
|
|
445
|
+
meta = true;
|
|
446
|
+
}
|
|
447
|
+
if ((sequence.length !== 0 && (name !== undefined || escaped)) ||
|
|
448
|
+
charLengthAt(sequence, 0) === sequence.length) {
|
|
449
|
+
keypressHandler({
|
|
450
|
+
name: name || '',
|
|
451
|
+
ctrl,
|
|
452
|
+
meta,
|
|
453
|
+
shift,
|
|
454
|
+
paste: false,
|
|
455
|
+
sequence,
|
|
456
|
+
});
|
|
457
|
+
}
|
|
458
|
+
// Unrecognized or broken escape sequence, don't emit anything
|
|
380
459
|
}
|
|
381
460
|
}
|
|
382
461
|
const KeypressContext = createContext(undefined);
|
|
@@ -387,10 +466,7 @@ export function useKeypressContext() {
|
|
|
387
466
|
}
|
|
388
467
|
return context;
|
|
389
468
|
}
|
|
390
|
-
function
|
|
391
|
-
return process.env['PASTE_WORKAROUND'] !== 'false';
|
|
392
|
-
}
|
|
393
|
-
export function KeypressProvider({ children, kittyProtocolEnabled, config, debugKeystrokeLogging, }) {
|
|
469
|
+
export function KeypressProvider({ children, config, debugKeystrokeLogging, }) {
|
|
394
470
|
const { stdin, setRawMode } = useStdin();
|
|
395
471
|
const subscribers = useRef(new Set()).current;
|
|
396
472
|
const subscribe = useCallback((handler) => subscribers.add(handler), [subscribers]);
|
|
@@ -401,419 +477,34 @@ export function KeypressProvider({ children, kittyProtocolEnabled, config, debug
|
|
|
401
477
|
if (wasRaw === false) {
|
|
402
478
|
setRawMode(true);
|
|
403
479
|
}
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
let
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
let draggingTimer = null;
|
|
415
|
-
const clearDraggingTimer = () => {
|
|
416
|
-
if (draggingTimer) {
|
|
417
|
-
clearTimeout(draggingTimer);
|
|
418
|
-
draggingTimer = null;
|
|
419
|
-
}
|
|
420
|
-
};
|
|
421
|
-
const flushInputBufferOnInterrupt = (reason) => {
|
|
422
|
-
if (inputBuffer) {
|
|
423
|
-
if (debugKeystrokeLogging) {
|
|
424
|
-
debugLogger.log(`[DEBUG] Input sequence flushed due to ${reason}:`, JSON.stringify(inputBuffer));
|
|
425
|
-
}
|
|
426
|
-
broadcast({
|
|
427
|
-
name: '',
|
|
428
|
-
ctrl: false,
|
|
429
|
-
meta: false,
|
|
430
|
-
shift: false,
|
|
431
|
-
paste: false,
|
|
432
|
-
sequence: inputBuffer,
|
|
433
|
-
});
|
|
434
|
-
inputBuffer = '';
|
|
435
|
-
}
|
|
436
|
-
if (inputTimeout) {
|
|
437
|
-
clearTimeout(inputTimeout);
|
|
438
|
-
inputTimeout = null;
|
|
439
|
-
}
|
|
440
|
-
};
|
|
441
|
-
const handleKeypress = (_, key) => {
|
|
442
|
-
if (key.sequence === FOCUS_IN || key.sequence === FOCUS_OUT) {
|
|
443
|
-
flushInputBufferOnInterrupt('focus event');
|
|
444
|
-
return;
|
|
445
|
-
}
|
|
446
|
-
if (key.name === 'paste-start') {
|
|
447
|
-
flushInputBufferOnInterrupt('paste start');
|
|
448
|
-
pasteBuffer = Buffer.alloc(0);
|
|
449
|
-
return;
|
|
450
|
-
}
|
|
451
|
-
if (key.name === 'paste-end') {
|
|
452
|
-
if (pasteBuffer !== null) {
|
|
453
|
-
broadcast({
|
|
454
|
-
name: '',
|
|
455
|
-
ctrl: false,
|
|
456
|
-
meta: false,
|
|
457
|
-
shift: false,
|
|
458
|
-
paste: true,
|
|
459
|
-
sequence: pasteBuffer.toString(),
|
|
460
|
-
});
|
|
461
|
-
}
|
|
462
|
-
pasteBuffer = null;
|
|
463
|
-
return;
|
|
464
|
-
}
|
|
465
|
-
if (pasteBuffer !== null) {
|
|
466
|
-
pasteBuffer = Buffer.concat([pasteBuffer, Buffer.from(key.sequence)]);
|
|
467
|
-
return;
|
|
468
|
-
}
|
|
469
|
-
if (key.sequence === SINGLE_QUOTE ||
|
|
470
|
-
key.sequence === DOUBLE_QUOTE ||
|
|
471
|
-
draggingTimer !== null) {
|
|
472
|
-
dragBuffer += key.sequence;
|
|
473
|
-
clearDraggingTimer();
|
|
474
|
-
draggingTimer = setTimeout(() => {
|
|
475
|
-
draggingTimer = null;
|
|
476
|
-
const seq = dragBuffer;
|
|
477
|
-
dragBuffer = '';
|
|
478
|
-
if (seq) {
|
|
479
|
-
broadcast({ ...key, name: '', paste: true, sequence: seq });
|
|
480
|
-
}
|
|
481
|
-
}, DRAG_COMPLETION_TIMEOUT_MS);
|
|
482
|
-
return;
|
|
483
|
-
}
|
|
484
|
-
const mappedLetter = MAC_ALT_KEY_CHARACTER_MAP[key.sequence];
|
|
485
|
-
if (process.platform === 'darwin' && mappedLetter && !key.meta) {
|
|
486
|
-
broadcast({
|
|
487
|
-
name: mappedLetter,
|
|
488
|
-
ctrl: false,
|
|
489
|
-
meta: true,
|
|
490
|
-
shift: false,
|
|
491
|
-
paste: pasteBuffer !== null,
|
|
492
|
-
sequence: key.sequence,
|
|
493
|
-
});
|
|
494
|
-
return;
|
|
495
|
-
}
|
|
496
|
-
if (key.name === 'return' && backslashTimeout !== null) {
|
|
497
|
-
clearTimeout(backslashTimeout);
|
|
498
|
-
backslashTimeout = null;
|
|
499
|
-
broadcast({
|
|
500
|
-
...key,
|
|
501
|
-
shift: true,
|
|
502
|
-
sequence: '\r', // Corrected escaping for newline
|
|
503
|
-
});
|
|
504
|
-
return;
|
|
505
|
-
}
|
|
506
|
-
if (key.sequence === '\\' && !key.name) {
|
|
507
|
-
// Corrected escaping for backslash
|
|
508
|
-
backslashTimeout = setTimeout(() => {
|
|
509
|
-
backslashTimeout = null;
|
|
510
|
-
broadcast(key);
|
|
511
|
-
}, BACKSLASH_ENTER_DETECTION_WINDOW_MS);
|
|
512
|
-
return;
|
|
513
|
-
}
|
|
514
|
-
if (backslashTimeout !== null && key.name !== 'return') {
|
|
515
|
-
clearTimeout(backslashTimeout);
|
|
516
|
-
backslashTimeout = null;
|
|
517
|
-
broadcast({
|
|
518
|
-
name: '',
|
|
519
|
-
sequence: '\\',
|
|
520
|
-
ctrl: false,
|
|
521
|
-
meta: false,
|
|
522
|
-
shift: false,
|
|
523
|
-
paste: false,
|
|
524
|
-
});
|
|
525
|
-
}
|
|
526
|
-
if (['up', 'down', 'left', 'right'].includes(key.name)) {
|
|
527
|
-
broadcast(key);
|
|
528
|
-
return;
|
|
529
|
-
}
|
|
530
|
-
if ((key.ctrl && key.name === 'c') ||
|
|
531
|
-
key.sequence === `${ESC}${KITTY_CTRL_C}`) {
|
|
532
|
-
if (inputBuffer && debugKeystrokeLogging) {
|
|
533
|
-
debugLogger.log('[DEBUG] Input buffer cleared on Ctrl+C:', inputBuffer);
|
|
534
|
-
}
|
|
535
|
-
inputBuffer = '';
|
|
536
|
-
if (inputTimeout) {
|
|
537
|
-
clearTimeout(inputTimeout);
|
|
538
|
-
inputTimeout = null;
|
|
539
|
-
}
|
|
540
|
-
if (key.sequence === `${ESC}${KITTY_CTRL_C}`) {
|
|
541
|
-
broadcast({
|
|
542
|
-
name: 'c',
|
|
543
|
-
ctrl: true,
|
|
544
|
-
meta: false,
|
|
545
|
-
shift: false,
|
|
546
|
-
paste: false,
|
|
547
|
-
sequence: key.sequence,
|
|
548
|
-
kittyProtocol: true,
|
|
549
|
-
});
|
|
550
|
-
}
|
|
551
|
-
else {
|
|
552
|
-
broadcast(key);
|
|
553
|
-
}
|
|
554
|
-
return;
|
|
555
|
-
}
|
|
556
|
-
// Clear any pending timeout when new input arrives
|
|
557
|
-
if (inputTimeout) {
|
|
558
|
-
clearTimeout(inputTimeout);
|
|
559
|
-
inputTimeout = null;
|
|
560
|
-
}
|
|
561
|
-
// Always check if this could start a sequence we need to buffer (Kitty or Mouse)
|
|
562
|
-
// Other ESC sequences (like Alt+Key which is ESC+Key) should be let through if readline parsed them.
|
|
563
|
-
const shouldBuffer = couldBeKittySequence(key.sequence);
|
|
564
|
-
const isExcluded = [
|
|
565
|
-
PASTE_MODE_START,
|
|
566
|
-
PASTE_MODE_END,
|
|
567
|
-
FOCUS_IN,
|
|
568
|
-
FOCUS_OUT,
|
|
569
|
-
].some((prefix) => key.sequence.startsWith(prefix));
|
|
570
|
-
if (inputBuffer || (shouldBuffer && !isExcluded)) {
|
|
571
|
-
inputBuffer += key.sequence;
|
|
572
|
-
if (debugKeystrokeLogging && !couldBeMouseSequence(inputBuffer)) {
|
|
573
|
-
debugLogger.log('[DEBUG] Input buffer accumulating:', JSON.stringify(inputBuffer));
|
|
480
|
+
process.stdin.setEncoding('utf8'); // Make data events emit strings
|
|
481
|
+
const mouseFilterer = nonKeyboardEventFilter(broadcast);
|
|
482
|
+
const backslashBufferer = bufferBackslashEnter(mouseFilterer);
|
|
483
|
+
const pasteBufferer = bufferPaste(backslashBufferer);
|
|
484
|
+
let dataListener = createDataListener(pasteBufferer);
|
|
485
|
+
if (debugKeystrokeLogging) {
|
|
486
|
+
const old = dataListener;
|
|
487
|
+
dataListener = (data) => {
|
|
488
|
+
if (data.length > 0) {
|
|
489
|
+
debugLogger.log(`[DEBUG] Raw StdIn: ${JSON.stringify(data)}`);
|
|
574
490
|
}
|
|
575
|
-
|
|
576
|
-
let remainingBuffer = inputBuffer;
|
|
577
|
-
let parsedAny = false;
|
|
578
|
-
while (remainingBuffer) {
|
|
579
|
-
const parsed = parseKittyPrefix(remainingBuffer);
|
|
580
|
-
if (parsed) {
|
|
581
|
-
// If kitty protocol is disabled, only allow legacy/standard sequences.
|
|
582
|
-
// parseKittyPrefix returns true for kittyProtocol if it's a modern kitty sequence.
|
|
583
|
-
if (kittyProtocolEnabled || !parsed.key.kittyProtocol) {
|
|
584
|
-
if (debugKeystrokeLogging) {
|
|
585
|
-
const parsedSequence = remainingBuffer.slice(0, parsed.length);
|
|
586
|
-
debugLogger.log('[DEBUG] Sequence parsed successfully:', JSON.stringify(parsedSequence));
|
|
587
|
-
}
|
|
588
|
-
broadcast(parsed.key);
|
|
589
|
-
remainingBuffer = remainingBuffer.slice(parsed.length);
|
|
590
|
-
parsedAny = true;
|
|
591
|
-
continue;
|
|
592
|
-
}
|
|
593
|
-
}
|
|
594
|
-
const mouseParsed = parseMouseEvent(remainingBuffer);
|
|
595
|
-
if (mouseParsed) {
|
|
596
|
-
// These are handled by the separate mouse sequence parser.
|
|
597
|
-
// All we need to do is make sure we don't get confused by these
|
|
598
|
-
// sequences.
|
|
599
|
-
remainingBuffer = remainingBuffer.slice(mouseParsed.length);
|
|
600
|
-
parsedAny = true;
|
|
601
|
-
continue;
|
|
602
|
-
}
|
|
603
|
-
// If we can't parse a sequence at the start, check if there's
|
|
604
|
-
// another ESC later in the buffer. If so, the data before it
|
|
605
|
-
// is garbage/incomplete and should be dropped so we can
|
|
606
|
-
// process the next sequence.
|
|
607
|
-
const nextEscIndex = remainingBuffer.indexOf(ESC, 1);
|
|
608
|
-
if (nextEscIndex !== -1) {
|
|
609
|
-
const garbage = remainingBuffer.slice(0, nextEscIndex);
|
|
610
|
-
// Special case: if garbage is exactly ESC, it's likely a rapid ESC press.
|
|
611
|
-
if (garbage === ESC) {
|
|
612
|
-
if (debugKeystrokeLogging) {
|
|
613
|
-
debugLogger.log('[DEBUG] Flushing rapid ESC before next ESC:', JSON.stringify(garbage));
|
|
614
|
-
}
|
|
615
|
-
broadcast({
|
|
616
|
-
name: 'escape',
|
|
617
|
-
ctrl: false,
|
|
618
|
-
meta: true,
|
|
619
|
-
shift: false,
|
|
620
|
-
paste: false,
|
|
621
|
-
sequence: garbage,
|
|
622
|
-
});
|
|
623
|
-
}
|
|
624
|
-
else {
|
|
625
|
-
if (debugKeystrokeLogging) {
|
|
626
|
-
debugLogger.log('[DEBUG] Dropping incomplete sequence before next ESC:', JSON.stringify(garbage));
|
|
627
|
-
}
|
|
628
|
-
}
|
|
629
|
-
// Continue parsing from next ESC
|
|
630
|
-
remainingBuffer = remainingBuffer.slice(nextEscIndex);
|
|
631
|
-
// We made progress, so we can continue the loop to parse the next sequence
|
|
632
|
-
continue;
|
|
633
|
-
}
|
|
634
|
-
// Check if buffer could become a valid sequence
|
|
635
|
-
const couldBeValidKitty = kittyProtocolEnabled && couldBeKittySequence(remainingBuffer);
|
|
636
|
-
const isMouse = isIncompleteMouseSequence(remainingBuffer);
|
|
637
|
-
const couldBeValid = couldBeValidKitty || isMouse;
|
|
638
|
-
if (!couldBeValid) {
|
|
639
|
-
// Not a valid sequence - flush as regular input immediately
|
|
640
|
-
if (debugKeystrokeLogging) {
|
|
641
|
-
debugLogger.log('[DEBUG] Not a valid sequence, flushing:', JSON.stringify(remainingBuffer));
|
|
642
|
-
}
|
|
643
|
-
broadcast({
|
|
644
|
-
name: '',
|
|
645
|
-
ctrl: false,
|
|
646
|
-
meta: false,
|
|
647
|
-
shift: false,
|
|
648
|
-
paste: false,
|
|
649
|
-
sequence: remainingBuffer,
|
|
650
|
-
});
|
|
651
|
-
remainingBuffer = '';
|
|
652
|
-
parsedAny = true;
|
|
653
|
-
}
|
|
654
|
-
else if (remainingBuffer.length > MAX_KITTY_SEQUENCE_LENGTH) {
|
|
655
|
-
// Buffer overflow - log and clear
|
|
656
|
-
if (debugKeystrokeLogging) {
|
|
657
|
-
debugLogger.log('[DEBUG] Input buffer overflow, clearing:', JSON.stringify(remainingBuffer));
|
|
658
|
-
}
|
|
659
|
-
if (config && kittyProtocolEnabled) {
|
|
660
|
-
const event = new KittySequenceOverflowEvent(remainingBuffer.length, remainingBuffer);
|
|
661
|
-
logKittySequenceOverflow(config, event);
|
|
662
|
-
}
|
|
663
|
-
// Flush as regular input
|
|
664
|
-
broadcast({
|
|
665
|
-
name: '',
|
|
666
|
-
ctrl: false,
|
|
667
|
-
meta: false,
|
|
668
|
-
shift: false,
|
|
669
|
-
paste: false,
|
|
670
|
-
sequence: remainingBuffer,
|
|
671
|
-
});
|
|
672
|
-
remainingBuffer = '';
|
|
673
|
-
parsedAny = true;
|
|
674
|
-
}
|
|
675
|
-
else {
|
|
676
|
-
if ((config?.getDebugMode() || debugKeystrokeLogging) &&
|
|
677
|
-
!couldBeMouseSequence(inputBuffer)) {
|
|
678
|
-
debugLogger.warn('Input sequence buffer has content:', JSON.stringify(inputBuffer));
|
|
679
|
-
}
|
|
680
|
-
// Could be valid but incomplete - set timeout
|
|
681
|
-
// Only set timeout if it's NOT a mouse sequence.
|
|
682
|
-
// Mouse sequences might be slow (e.g. over network) and we don't want to
|
|
683
|
-
// flush them as garbage keypresses.
|
|
684
|
-
// However, if it's just ESC or ESC[, it might be a user typing slowly,
|
|
685
|
-
// so we should still timeout in that case.
|
|
686
|
-
const isAmbiguousPrefix = remainingBuffer === ESC || remainingBuffer === `${ESC}[`;
|
|
687
|
-
if (!isMouse || isAmbiguousPrefix) {
|
|
688
|
-
inputTimeout = setTimeout(() => {
|
|
689
|
-
if (inputBuffer) {
|
|
690
|
-
if (debugKeystrokeLogging) {
|
|
691
|
-
debugLogger.log('[DEBUG] Input sequence timeout, flushing:', JSON.stringify(inputBuffer));
|
|
692
|
-
}
|
|
693
|
-
const isEscape = inputBuffer === ESC;
|
|
694
|
-
broadcast({
|
|
695
|
-
name: isEscape ? 'escape' : '',
|
|
696
|
-
ctrl: false,
|
|
697
|
-
meta: isEscape,
|
|
698
|
-
shift: false,
|
|
699
|
-
paste: false,
|
|
700
|
-
sequence: inputBuffer,
|
|
701
|
-
});
|
|
702
|
-
inputBuffer = '';
|
|
703
|
-
}
|
|
704
|
-
inputTimeout = null;
|
|
705
|
-
}, KITTY_SEQUENCE_TIMEOUT_MS);
|
|
706
|
-
}
|
|
707
|
-
else {
|
|
708
|
-
// It IS a mouse sequence and it's long enough to be unambiguously NOT just a user hitting ESC slowly.
|
|
709
|
-
// We just wait for more data.
|
|
710
|
-
if (inputTimeout) {
|
|
711
|
-
clearTimeout(inputTimeout);
|
|
712
|
-
inputTimeout = null;
|
|
713
|
-
}
|
|
714
|
-
}
|
|
715
|
-
break;
|
|
716
|
-
}
|
|
717
|
-
}
|
|
718
|
-
inputBuffer = remainingBuffer;
|
|
719
|
-
if (parsedAny || inputBuffer)
|
|
720
|
-
return;
|
|
721
|
-
}
|
|
722
|
-
if (key.name === 'return' && key.sequence === `${ESC}\r`) {
|
|
723
|
-
key.meta = true;
|
|
724
|
-
}
|
|
725
|
-
broadcast({ ...key, paste: pasteBuffer !== null });
|
|
726
|
-
};
|
|
727
|
-
let cleanup = () => { };
|
|
728
|
-
let rl;
|
|
729
|
-
if (keypressStream !== null) {
|
|
730
|
-
rl = readline.createInterface({
|
|
731
|
-
input: keypressStream,
|
|
732
|
-
escapeCodeTimeout: 0,
|
|
733
|
-
});
|
|
734
|
-
readline.emitKeypressEvents(keypressStream, rl);
|
|
735
|
-
const parser = pasteMarkerParser(keypressStream, handleKeypress);
|
|
736
|
-
parser.next(); // prime the generator so it starts listening.
|
|
737
|
-
let timeoutId;
|
|
738
|
-
const handleRawKeypress = (data) => {
|
|
739
|
-
clearTimeout(timeoutId);
|
|
740
|
-
parser.next(data);
|
|
741
|
-
timeoutId = setTimeout(() => parser.next(''), PASTE_CODE_TIMEOUT_MS);
|
|
491
|
+
old(data);
|
|
742
492
|
};
|
|
743
|
-
keypressStream.on('keypress', handleKeypress);
|
|
744
|
-
process.stdin.setEncoding('utf8'); // so handleRawKeypress gets strings
|
|
745
|
-
stdin.on('data', handleRawKeypress);
|
|
746
|
-
cleanup = () => {
|
|
747
|
-
keypressStream.removeListener('keypress', handleKeypress);
|
|
748
|
-
stdin.removeListener('data', handleRawKeypress);
|
|
749
|
-
};
|
|
750
|
-
}
|
|
751
|
-
else {
|
|
752
|
-
rl = readline.createInterface({ input: stdin, escapeCodeTimeout: 0 });
|
|
753
|
-
readline.emitKeypressEvents(stdin, rl);
|
|
754
|
-
stdin.on('keypress', handleKeypress);
|
|
755
|
-
cleanup = () => stdin.removeListener('keypress', handleKeypress);
|
|
756
493
|
}
|
|
494
|
+
stdin.on('data', dataListener);
|
|
757
495
|
return () => {
|
|
758
|
-
|
|
759
|
-
|
|
496
|
+
// flush buffers by sending null key
|
|
497
|
+
backslashBufferer(null);
|
|
498
|
+
pasteBufferer(null);
|
|
499
|
+
// flush by sending empty string to the data listener
|
|
500
|
+
dataListener('');
|
|
501
|
+
stdin.removeListener('data', dataListener);
|
|
760
502
|
// Restore the terminal to its original state.
|
|
761
503
|
if (wasRaw === false) {
|
|
762
504
|
setRawMode(false);
|
|
763
505
|
}
|
|
764
|
-
if (backslashTimeout) {
|
|
765
|
-
clearTimeout(backslashTimeout);
|
|
766
|
-
backslashTimeout = null;
|
|
767
|
-
}
|
|
768
|
-
if (inputTimeout) {
|
|
769
|
-
clearTimeout(inputTimeout);
|
|
770
|
-
inputTimeout = null;
|
|
771
|
-
}
|
|
772
|
-
// Flush any pending kitty sequence data to avoid data loss on exit.
|
|
773
|
-
if (inputBuffer) {
|
|
774
|
-
broadcast({
|
|
775
|
-
name: '',
|
|
776
|
-
ctrl: false,
|
|
777
|
-
meta: false,
|
|
778
|
-
shift: false,
|
|
779
|
-
paste: false,
|
|
780
|
-
sequence: inputBuffer,
|
|
781
|
-
});
|
|
782
|
-
inputBuffer = '';
|
|
783
|
-
}
|
|
784
|
-
// Flush any pending paste data to avoid data loss on exit.
|
|
785
|
-
if (pasteBuffer !== null) {
|
|
786
|
-
broadcast({
|
|
787
|
-
name: '',
|
|
788
|
-
ctrl: false,
|
|
789
|
-
meta: false,
|
|
790
|
-
shift: false,
|
|
791
|
-
paste: true,
|
|
792
|
-
sequence: pasteBuffer.toString(),
|
|
793
|
-
});
|
|
794
|
-
pasteBuffer = null;
|
|
795
|
-
}
|
|
796
|
-
clearDraggingTimer();
|
|
797
|
-
if (dragBuffer) {
|
|
798
|
-
broadcast({
|
|
799
|
-
name: '',
|
|
800
|
-
ctrl: false,
|
|
801
|
-
meta: false,
|
|
802
|
-
shift: false,
|
|
803
|
-
paste: true,
|
|
804
|
-
sequence: dragBuffer,
|
|
805
|
-
});
|
|
806
|
-
dragBuffer = '';
|
|
807
|
-
}
|
|
808
506
|
};
|
|
809
|
-
}, [
|
|
810
|
-
stdin,
|
|
811
|
-
setRawMode,
|
|
812
|
-
kittyProtocolEnabled,
|
|
813
|
-
config,
|
|
814
|
-
debugKeystrokeLogging,
|
|
815
|
-
broadcast,
|
|
816
|
-
]);
|
|
507
|
+
}, [stdin, setRawMode, config, debugKeystrokeLogging, broadcast]);
|
|
817
508
|
return (_jsx(KeypressContext.Provider, { value: { subscribe, unsubscribe }, children: children }));
|
|
818
509
|
}
|
|
819
510
|
//# sourceMappingURL=KeypressContext.js.map
|