@google/gemini-cli 0.11.0-preview.0 → 0.12.0-nightly.20251023.c4c0c0d1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (140) hide show
  1. package/dist/google-gemini-cli-0.12.0-nightly.20251022.0542de95.tgz +0 -0
  2. package/dist/package.json +4 -3
  3. package/dist/src/commands/extensions/disable.js +13 -6
  4. package/dist/src/commands/extensions/disable.js.map +1 -1
  5. package/dist/src/commands/extensions/enable.js +13 -6
  6. package/dist/src/commands/extensions/enable.js.map +1 -1
  7. package/dist/src/commands/extensions/install.js +12 -2
  8. package/dist/src/commands/extensions/install.js.map +1 -1
  9. package/dist/src/commands/extensions/install.test.js +11 -3
  10. package/dist/src/commands/extensions/install.test.js.map +1 -1
  11. package/dist/src/commands/extensions/link.js +12 -2
  12. package/dist/src/commands/extensions/link.js.map +1 -1
  13. package/dist/src/commands/extensions/list.js +13 -4
  14. package/dist/src/commands/extensions/list.js.map +1 -1
  15. package/dist/src/commands/extensions/uninstall.js +12 -2
  16. package/dist/src/commands/extensions/uninstall.js.map +1 -1
  17. package/dist/src/commands/extensions/update.js +17 -13
  18. package/dist/src/commands/extensions/update.js.map +1 -1
  19. package/dist/src/commands/extensions.js +1 -0
  20. package/dist/src/commands/extensions.js.map +1 -1
  21. package/dist/src/commands/mcp/list.js +10 -3
  22. package/dist/src/commands/mcp/list.js.map +1 -1
  23. package/dist/src/commands/mcp/list.test.js +12 -6
  24. package/dist/src/commands/mcp/list.test.js.map +1 -1
  25. package/dist/src/config/config.js +12 -0
  26. package/dist/src/config/config.js.map +1 -1
  27. package/dist/src/config/config.test.js +11 -0
  28. package/dist/src/config/config.test.js.map +1 -1
  29. package/dist/src/config/extension-manager.d.ts +38 -0
  30. package/dist/src/config/extension-manager.js +412 -0
  31. package/dist/src/config/extension-manager.js.map +1 -0
  32. package/dist/src/config/extension.d.ts +4 -51
  33. package/dist/src/config/extension.js +1 -535
  34. package/dist/src/config/extension.js.map +1 -1
  35. package/dist/src/config/extension.test.js +316 -159
  36. package/dist/src/config/extension.test.js.map +1 -1
  37. package/dist/src/config/extensions/consent.d.ts +38 -0
  38. package/dist/src/config/extensions/consent.js +123 -0
  39. package/dist/src/config/extensions/consent.js.map +1 -0
  40. package/dist/src/config/extensions/extensionEnablement.js +1 -1
  41. package/dist/src/config/extensions/extensionEnablement.js.map +1 -1
  42. package/dist/src/config/extensions/extensionSettings.d.ts +15 -0
  43. package/dist/src/config/extensions/extensionSettings.js +63 -0
  44. package/dist/src/config/extensions/extensionSettings.js.map +1 -0
  45. package/dist/src/config/extensions/extensionSettings.test.d.ts +6 -0
  46. package/dist/src/config/extensions/extensionSettings.test.js +137 -0
  47. package/dist/src/config/extensions/extensionSettings.test.js.map +1 -0
  48. package/dist/src/config/extensions/github.d.ts +2 -2
  49. package/dist/src/config/extensions/github.js +3 -8
  50. package/dist/src/config/extensions/github.js.map +1 -1
  51. package/dist/src/config/extensions/github.test.js +25 -7
  52. package/dist/src/config/extensions/github.test.js.map +1 -1
  53. package/dist/src/config/extensions/storage.d.ts +14 -0
  54. package/dist/src/config/extensions/storage.js +32 -0
  55. package/dist/src/config/extensions/storage.js.map +1 -0
  56. package/dist/src/config/extensions/update.d.ts +4 -4
  57. package/dist/src/config/extensions/update.js +11 -18
  58. package/dist/src/config/extensions/update.js.map +1 -1
  59. package/dist/src/config/extensions/update.test.js +32 -58
  60. package/dist/src/config/extensions/update.test.js.map +1 -1
  61. package/dist/src/config/extensions/variableSchema.d.ts +0 -6
  62. package/dist/src/config/extensions/variableSchema.js.map +1 -1
  63. package/dist/src/config/extensions/variables.d.ts +4 -0
  64. package/dist/src/config/extensions/variables.js +6 -0
  65. package/dist/src/config/extensions/variables.js.map +1 -1
  66. package/dist/src/config/settings.d.ts +2 -1
  67. package/dist/src/config/settings.js +4 -7
  68. package/dist/src/config/settings.js.map +1 -1
  69. package/dist/src/config/settings.test.js +113 -14
  70. package/dist/src/config/settings.test.js.map +1 -1
  71. package/dist/src/config/settingsSchema.d.ts +9 -0
  72. package/dist/src/config/settingsSchema.js +9 -0
  73. package/dist/src/config/settingsSchema.js.map +1 -1
  74. package/dist/src/gemini.js +22 -5
  75. package/dist/src/gemini.js.map +1 -1
  76. package/dist/src/generated/git-commit.d.ts +2 -2
  77. package/dist/src/generated/git-commit.js +2 -2
  78. package/dist/src/generated/git-commit.js.map +1 -1
  79. package/dist/src/nonInteractiveCli.js +14 -1
  80. package/dist/src/nonInteractiveCli.js.map +1 -1
  81. package/dist/src/nonInteractiveCli.test.js +84 -2
  82. package/dist/src/nonInteractiveCli.test.js.map +1 -1
  83. package/dist/src/test-utils/createExtension.d.ts +3 -1
  84. package/dist/src/test-utils/createExtension.js +3 -3
  85. package/dist/src/test-utils/createExtension.js.map +1 -1
  86. package/dist/src/ui/AppContainer.js +101 -47
  87. package/dist/src/ui/AppContainer.js.map +1 -1
  88. package/dist/src/ui/AppContainer.test.js +138 -79
  89. package/dist/src/ui/AppContainer.test.js.map +1 -1
  90. package/dist/src/ui/commands/extensionsCommand.js +19 -10
  91. package/dist/src/ui/commands/extensionsCommand.js.map +1 -1
  92. package/dist/src/ui/commands/extensionsCommand.test.js +8 -0
  93. package/dist/src/ui/commands/extensionsCommand.test.js.map +1 -1
  94. package/dist/src/ui/components/HistoryItemDisplay.js +1 -1
  95. package/dist/src/ui/components/HistoryItemDisplay.js.map +1 -1
  96. package/dist/src/ui/components/InputPrompt.js +5 -6
  97. package/dist/src/ui/components/InputPrompt.js.map +1 -1
  98. package/dist/src/ui/components/InputPrompt.test.js +249 -393
  99. package/dist/src/ui/components/InputPrompt.test.js.map +1 -1
  100. package/dist/src/ui/components/SettingsDialog.test.js +0 -29
  101. package/dist/src/ui/components/SettingsDialog.test.js.map +1 -1
  102. package/dist/src/ui/components/views/ExtensionsList.d.ts +7 -1
  103. package/dist/src/ui/components/views/ExtensionsList.js +4 -10
  104. package/dist/src/ui/components/views/ExtensionsList.js.map +1 -1
  105. package/dist/src/ui/components/views/ExtensionsList.test.js +34 -21
  106. package/dist/src/ui/components/views/ExtensionsList.test.js.map +1 -1
  107. package/dist/src/ui/contexts/KeypressContext.js +328 -335
  108. package/dist/src/ui/contexts/KeypressContext.js.map +1 -1
  109. package/dist/src/ui/hooks/useAutoAcceptIndicator.js +10 -0
  110. package/dist/src/ui/hooks/useAutoAcceptIndicator.js.map +1 -1
  111. package/dist/src/ui/hooks/useAutoAcceptIndicator.test.js +30 -0
  112. package/dist/src/ui/hooks/useAutoAcceptIndicator.test.js.map +1 -1
  113. package/dist/src/ui/hooks/useExtensionUpdates.d.ts +14 -4
  114. package/dist/src/ui/hooks/useExtensionUpdates.js +14 -17
  115. package/dist/src/ui/hooks/useExtensionUpdates.js.map +1 -1
  116. package/dist/src/ui/hooks/useExtensionUpdates.test.js +23 -30
  117. package/dist/src/ui/hooks/useExtensionUpdates.test.js.map +1 -1
  118. package/dist/src/ui/hooks/useGitBranchName.js +4 -0
  119. package/dist/src/ui/hooks/useGitBranchName.js.map +1 -1
  120. package/dist/src/ui/hooks/useGitBranchName.test.js +19 -21
  121. package/dist/src/ui/hooks/useGitBranchName.test.js.map +1 -1
  122. package/dist/src/ui/hooks/useReactToolScheduler.js +22 -8
  123. package/dist/src/ui/hooks/useReactToolScheduler.js.map +1 -1
  124. package/dist/src/ui/hooks/useReactToolScheduler.test.d.ts +6 -0
  125. package/dist/src/ui/hooks/useReactToolScheduler.test.js +65 -0
  126. package/dist/src/ui/hooks/useReactToolScheduler.test.js.map +1 -0
  127. package/dist/src/ui/hooks/useToolScheduler.test.js +30 -48
  128. package/dist/src/ui/hooks/useToolScheduler.test.js.map +1 -1
  129. package/dist/src/ui/types.d.ts +2 -1
  130. package/dist/src/ui/types.js.map +1 -1
  131. package/dist/src/ui/utils/CodeColorizer.js +2 -1
  132. package/dist/src/ui/utils/CodeColorizer.js.map +1 -1
  133. package/dist/src/utils/envVarResolver.d.ts +2 -2
  134. package/dist/src/utils/envVarResolver.js +10 -7
  135. package/dist/src/utils/envVarResolver.js.map +1 -1
  136. package/dist/src/zed-integration/schema.d.ts +4 -4
  137. package/dist/src/zed-integration/zedIntegration.js +3 -3
  138. package/dist/src/zed-integration/zedIntegration.js.map +1 -1
  139. package/dist/tsconfig.tsbuildinfo +1 -1
  140. package/package.json +4 -3
@@ -41,169 +41,186 @@ const ALT_KEY_CHARACTER_MAP = {
41
41
  '\u00A5': 'y',
42
42
  '\u03A9': 'z',
43
43
  };
44
- const KeypressContext = createContext(undefined);
45
- export function useKeypressContext() {
46
- const context = useContext(KeypressContext);
47
- if (!context) {
48
- throw new Error('useKeypressContext must be used within a KeypressProvider');
49
- }
50
- return context;
44
+ /**
45
+ * Check if a buffer could potentially be a valid kitty sequence or its prefix.
46
+ */
47
+ function couldBeKittySequence(buffer) {
48
+ // Kitty sequences always start with ESC[.
49
+ if (buffer.length === 0)
50
+ return true;
51
+ if (buffer === ESC || buffer === `${ESC}[`)
52
+ return true;
53
+ if (!buffer.startsWith(`${ESC}[`))
54
+ return false;
55
+ // Check for known kitty sequence patterns:
56
+ // 1. ESC[<digit> - could be CSI-u or tilde-coded
57
+ // 2. ESC[1;<digit> - parameterized functional
58
+ // 3. ESC[<letter> - legacy functional keys
59
+ // 4. ESC[Z - reverse tab
60
+ const afterCSI = buffer.slice(2);
61
+ // Check if it starts with a digit (could be CSI-u or parameterized)
62
+ if (/^\d/.test(afterCSI))
63
+ return true;
64
+ // Check for known single-letter sequences
65
+ if (/^[ABCDHFPQRSZ]/.test(afterCSI))
66
+ return true;
67
+ // Check for 1; pattern (parameterized sequences)
68
+ if (/^1;\d/.test(afterCSI))
69
+ return true;
70
+ // Anything else starting with ESC[ that doesn't match our patterns
71
+ // is likely not a kitty sequence we handle
72
+ return false;
51
73
  }
52
- export function KeypressProvider({ children, kittyProtocolEnabled, config, debugKeystrokeLogging, }) {
53
- const { stdin, setRawMode } = useStdin();
54
- const subscribers = useRef(new Set()).current;
55
- const isDraggingRef = useRef(false);
56
- const dragBufferRef = useRef('');
57
- const draggingTimerRef = useRef(null);
58
- const subscribe = useCallback((handler) => {
59
- subscribers.add(handler);
60
- }, [subscribers]);
61
- const unsubscribe = useCallback((handler) => {
62
- subscribers.delete(handler);
63
- }, [subscribers]);
64
- useEffect(() => {
65
- const clearDraggingTimer = () => {
66
- if (draggingTimerRef.current) {
67
- clearTimeout(draggingTimerRef.current);
68
- draggingTimerRef.current = null;
69
- }
74
+ /**
75
+ * Parses a single complete kitty/parameterized/legacy sequence from the start
76
+ * of the buffer.
77
+ *
78
+ * This enables peel-and-continue parsing for batched input, allowing us to
79
+ * "peel off" one complete event when multiple sequences arrive in a single
80
+ * chunk, preventing buffer overflow and fragmentation.
81
+ *
82
+ * @param buffer - The input buffer string to parse.
83
+ * @returns The parsed Key and the number of characters consumed, or null if
84
+ * no complete sequence is found at the start of the buffer.
85
+ */
86
+ function parseKittyPrefix(buffer) {
87
+ // In older terminals ESC [ Z was used as Cursor Backward Tabulation (CBT)
88
+ // In newer terminals the same functionality of key combination for moving
89
+ // backward through focusable elements is Shift+Tab, hence we will
90
+ // map ESC [ Z to Shift+Tab
91
+ // 0) Reverse Tab (legacy): ESC [ Z
92
+ // Treat as Shift+Tab for UI purposes.
93
+ // Regex parts:
94
+ // ^ - start of buffer
95
+ // ESC [ - CSI introducer
96
+ // Z - legacy reverse tab
97
+ const revTabLegacy = new RegExp(`^${ESC}\\[Z`);
98
+ let m = buffer.match(revTabLegacy);
99
+ if (m) {
100
+ return {
101
+ key: {
102
+ name: 'tab',
103
+ ctrl: false,
104
+ meta: false,
105
+ shift: true,
106
+ paste: false,
107
+ sequence: buffer.slice(0, m[0].length),
108
+ kittyProtocol: true,
109
+ },
110
+ length: m[0].length,
70
111
  };
71
- const wasRaw = stdin.isRaw;
72
- if (wasRaw === false) {
73
- setRawMode(true);
112
+ }
113
+ // 1) Reverse Tab (parameterized): ESC [ 1 ; <mods> Z
114
+ // Parameterized reverse Tab: ESC [ 1 ; <mods> Z
115
+ const revTabParam = new RegExp(`^${ESC}\\[1;(\\d+)Z`);
116
+ m = buffer.match(revTabParam);
117
+ if (m) {
118
+ let mods = parseInt(m[1], 10);
119
+ if (mods >= KITTY_MODIFIER_EVENT_TYPES_OFFSET) {
120
+ mods -= KITTY_MODIFIER_EVENT_TYPES_OFFSET;
74
121
  }
75
- const keypressStream = new PassThrough();
76
- let usePassthrough = false;
77
- const nodeMajorVersion = parseInt(process.versions.node.split('.')[0], 10);
78
- if (nodeMajorVersion < 20 ||
79
- process.env['PASTE_WORKAROUND'] === '1' ||
80
- process.env['PASTE_WORKAROUND'] === 'true') {
81
- usePassthrough = true;
122
+ const bits = mods - KITTY_MODIFIER_BASE;
123
+ const alt = (bits & MODIFIER_ALT_BIT) === MODIFIER_ALT_BIT;
124
+ const ctrl = (bits & MODIFIER_CTRL_BIT) === MODIFIER_CTRL_BIT;
125
+ return {
126
+ key: {
127
+ name: 'tab',
128
+ ctrl,
129
+ meta: alt,
130
+ // Reverse tab implies Shift behavior; force shift regardless of mods
131
+ shift: true,
132
+ paste: false,
133
+ sequence: buffer.slice(0, m[0].length),
134
+ kittyProtocol: true,
135
+ },
136
+ length: m[0].length,
137
+ };
138
+ }
139
+ // 2) Parameterized functional: ESC [ 1 ; <mods> (A|B|C|D|H|F|P|Q|R|S)
140
+ // 2) Parameterized functional: ESC [ 1 ; <mods> (A|B|C|D|H|F|P|Q|R|S)
141
+ // Arrows, Home/End, F1–F4 with modifiers encoded in <mods>.
142
+ const arrowPrefix = new RegExp(`^${ESC}\\[1;(\\d+)([ABCDHFPQSR])`);
143
+ m = buffer.match(arrowPrefix);
144
+ if (m) {
145
+ let mods = parseInt(m[1], 10);
146
+ if (mods >= KITTY_MODIFIER_EVENT_TYPES_OFFSET) {
147
+ mods -= KITTY_MODIFIER_EVENT_TYPES_OFFSET;
82
148
  }
83
- let isPaste = false;
84
- let pasteBuffer = Buffer.alloc(0);
85
- let kittySequenceBuffer = '';
86
- let kittySequenceTimeout = null;
87
- let backslashTimeout = null;
88
- let waitingForEnterAfterBackslash = false;
89
- // Check if a buffer could potentially be a valid kitty sequence or its prefix
90
- const couldBeKittySequence = (buffer) => {
91
- // Kitty sequences always start with ESC[.
92
- if (buffer.length === 0)
93
- return true;
94
- if (buffer === ESC || buffer === `${ESC}[`)
95
- return true;
96
- if (!buffer.startsWith(`${ESC}[`))
97
- return false;
98
- // Check for known kitty sequence patterns:
99
- // 1. ESC[<digit> - could be CSI-u or tilde-coded
100
- // 2. ESC[1;<digit> - parameterized functional
101
- // 3. ESC[<letter> - legacy functional keys
102
- // 4. ESC[Z - reverse tab
103
- const afterCSI = buffer.slice(2);
104
- // Check if it starts with a digit (could be CSI-u or parameterized)
105
- if (/^\d/.test(afterCSI))
106
- return true;
107
- // Check for known single-letter sequences
108
- if (/^[ABCDHFPQRSZ]/.test(afterCSI))
109
- return true;
110
- // Check for 1; pattern (parameterized sequences)
111
- if (/^1;\d/.test(afterCSI))
112
- return true;
113
- // Anything else starting with ESC[ that doesn't match our patterns
114
- // is likely not a kitty sequence we handle
115
- return false;
149
+ const bits = mods - KITTY_MODIFIER_BASE;
150
+ const shift = (bits & MODIFIER_SHIFT_BIT) === MODIFIER_SHIFT_BIT;
151
+ const alt = (bits & MODIFIER_ALT_BIT) === MODIFIER_ALT_BIT;
152
+ const ctrl = (bits & MODIFIER_CTRL_BIT) === MODIFIER_CTRL_BIT;
153
+ const sym = m[2];
154
+ const symbolToName = {
155
+ A: 'up',
156
+ B: 'down',
157
+ C: 'right',
158
+ D: 'left',
159
+ H: 'home',
160
+ F: 'end',
161
+ P: 'f1',
162
+ Q: 'f2',
163
+ R: 'f3',
164
+ S: 'f4',
116
165
  };
117
- // Parse a single complete kitty sequence from the start (prefix) of the
118
- // buffer and return both the Key and the number of characters consumed.
119
- // This lets us "peel off" one complete event when multiple sequences arrive
120
- // in a single chunk, preventing buffer overflow and fragmentation.
121
- // Parse a single complete kitty/parameterized/legacy sequence from the start
122
- // of the buffer and return both the parsed Key and the number of characters
123
- // consumed. This enables peel-and-continue parsing for batched input.
124
- const parseKittyPrefix = (buffer) => {
125
- // In older terminals ESC [ Z was used as Cursor Backward Tabulation (CBT)
126
- // In newer terminals the same functionality of key combination for moving
127
- // backward through focusable elements is Shift+Tab, hence we will
128
- // map ESC [ Z to Shift+Tab
129
- // 0) Reverse Tab (legacy): ESC [ Z
130
- // Treat as Shift+Tab for UI purposes.
131
- // Regex parts:
132
- // ^ - start of buffer
133
- // ESC [ - CSI introducer
134
- // Z - legacy reverse tab
135
- const revTabLegacy = new RegExp(`^${ESC}\\[Z`);
136
- let m = buffer.match(revTabLegacy);
137
- if (m) {
138
- return {
139
- key: {
140
- name: 'tab',
141
- ctrl: false,
142
- meta: false,
143
- shift: true,
144
- paste: false,
145
- sequence: buffer.slice(0, m[0].length),
146
- kittyProtocol: true,
147
- },
148
- length: m[0].length,
149
- };
150
- }
151
- // 1) Reverse Tab (parameterized): ESC [ 1 ; <mods> Z
152
- // Parameterized reverse Tab: ESC [ 1 ; <mods> Z
153
- const revTabParam = new RegExp(`^${ESC}\\[1;(\\d+)Z`);
154
- m = buffer.match(revTabParam);
155
- if (m) {
156
- let mods = parseInt(m[1], 10);
157
- if (mods >= KITTY_MODIFIER_EVENT_TYPES_OFFSET) {
158
- mods -= KITTY_MODIFIER_EVENT_TYPES_OFFSET;
159
- }
160
- const bits = mods - KITTY_MODIFIER_BASE;
161
- const alt = (bits & MODIFIER_ALT_BIT) === MODIFIER_ALT_BIT;
162
- const ctrl = (bits & MODIFIER_CTRL_BIT) === MODIFIER_CTRL_BIT;
163
- return {
164
- key: {
165
- name: 'tab',
166
- ctrl,
167
- meta: alt,
168
- // Reverse tab implies Shift behavior; force shift regardless of mods
169
- shift: true,
170
- paste: false,
171
- sequence: buffer.slice(0, m[0].length),
172
- kittyProtocol: true,
173
- },
174
- length: m[0].length,
175
- };
176
- }
177
- // 2) Parameterized functional: ESC [ 1 ; <mods> (A|B|C|D|H|F|P|Q|R|S)
178
- // 2) Parameterized functional: ESC [ 1 ; <mods> (A|B|C|D|H|F|P|Q|R|S)
179
- // Arrows, Home/End, F1–F4 with modifiers encoded in <mods>.
180
- const arrowPrefix = new RegExp(`^${ESC}\\[1;(\\d+)([ABCDHFPQSR])`);
181
- m = buffer.match(arrowPrefix);
182
- if (m) {
183
- let mods = parseInt(m[1], 10);
184
- if (mods >= KITTY_MODIFIER_EVENT_TYPES_OFFSET) {
185
- mods -= KITTY_MODIFIER_EVENT_TYPES_OFFSET;
186
- }
187
- const bits = mods - KITTY_MODIFIER_BASE;
188
- const shift = (bits & MODIFIER_SHIFT_BIT) === MODIFIER_SHIFT_BIT;
189
- const alt = (bits & MODIFIER_ALT_BIT) === MODIFIER_ALT_BIT;
190
- const ctrl = (bits & MODIFIER_CTRL_BIT) === MODIFIER_CTRL_BIT;
191
- const sym = m[2];
192
- const symbolToName = {
193
- A: 'up',
194
- B: 'down',
195
- C: 'right',
196
- D: 'left',
197
- H: 'home',
198
- F: 'end',
199
- P: 'f1',
200
- Q: 'f2',
201
- R: 'f3',
202
- S: 'f4',
203
- };
204
- const name = symbolToName[sym] || '';
205
- if (!name)
206
- return null;
166
+ const name = symbolToName[sym] || '';
167
+ if (!name)
168
+ return null;
169
+ return {
170
+ key: {
171
+ name,
172
+ ctrl,
173
+ meta: alt,
174
+ shift,
175
+ paste: false,
176
+ sequence: buffer.slice(0, m[0].length),
177
+ kittyProtocol: true,
178
+ },
179
+ length: m[0].length,
180
+ };
181
+ }
182
+ // 3) CSI-u form: ESC [ <code> ; <mods> (u|~)
183
+ // 3) CSI-u and tilde-coded functional keys: ESC [ <code> ; <mods> (u|~)
184
+ // 'u' terminator: Kitty CSI-u; '~' terminator: tilde-coded function keys.
185
+ const csiUPrefix = new RegExp(`^${ESC}\\[(\\d+)(;(\\d+))?([u~])`);
186
+ m = buffer.match(csiUPrefix);
187
+ if (m) {
188
+ const keyCode = parseInt(m[1], 10);
189
+ let modifiers = m[3] ? parseInt(m[3], 10) : KITTY_MODIFIER_BASE;
190
+ if (modifiers >= KITTY_MODIFIER_EVENT_TYPES_OFFSET) {
191
+ modifiers -= KITTY_MODIFIER_EVENT_TYPES_OFFSET;
192
+ }
193
+ const modifierBits = modifiers - KITTY_MODIFIER_BASE;
194
+ const shift = (modifierBits & MODIFIER_SHIFT_BIT) === MODIFIER_SHIFT_BIT;
195
+ const alt = (modifierBits & MODIFIER_ALT_BIT) === MODIFIER_ALT_BIT;
196
+ const ctrl = (modifierBits & MODIFIER_CTRL_BIT) === MODIFIER_CTRL_BIT;
197
+ const terminator = m[4];
198
+ // Tilde-coded functional keys (Delete, Insert, PageUp/Down, Home/End)
199
+ if (terminator === '~') {
200
+ let name = null;
201
+ switch (keyCode) {
202
+ case 1:
203
+ name = 'home';
204
+ break;
205
+ case 2:
206
+ name = 'insert';
207
+ break;
208
+ case 3:
209
+ name = 'delete';
210
+ break;
211
+ case 4:
212
+ name = 'end';
213
+ break;
214
+ case 5:
215
+ name = 'pageup';
216
+ break;
217
+ case 6:
218
+ name = 'pagedown';
219
+ break;
220
+ default:
221
+ break;
222
+ }
223
+ if (name) {
207
224
  return {
208
225
  key: {
209
226
  name,
@@ -217,136 +234,124 @@ export function KeypressProvider({ children, kittyProtocolEnabled, config, debug
217
234
  length: m[0].length,
218
235
  };
219
236
  }
220
- // 3) CSI-u form: ESC [ <code> ; <mods> (u|~)
221
- // 3) CSI-u and tilde-coded functional keys: ESC [ <code> ; <mods> (u|~)
222
- // 'u' terminator: Kitty CSI-u; '~' terminator: tilde-coded function keys.
223
- const csiUPrefix = new RegExp(`^${ESC}\\[(\\d+)(;(\\d+))?([u~])`);
224
- m = buffer.match(csiUPrefix);
225
- if (m) {
226
- const keyCode = parseInt(m[1], 10);
227
- let modifiers = m[3] ? parseInt(m[3], 10) : KITTY_MODIFIER_BASE;
228
- if (modifiers >= KITTY_MODIFIER_EVENT_TYPES_OFFSET) {
229
- modifiers -= KITTY_MODIFIER_EVENT_TYPES_OFFSET;
230
- }
231
- const modifierBits = modifiers - KITTY_MODIFIER_BASE;
232
- const shift = (modifierBits & MODIFIER_SHIFT_BIT) === MODIFIER_SHIFT_BIT;
233
- const alt = (modifierBits & MODIFIER_ALT_BIT) === MODIFIER_ALT_BIT;
234
- const ctrl = (modifierBits & MODIFIER_CTRL_BIT) === MODIFIER_CTRL_BIT;
235
- const terminator = m[4];
236
- // Tilde-coded functional keys (Delete, Insert, PageUp/Down, Home/End)
237
- if (terminator === '~') {
238
- let name = null;
239
- switch (keyCode) {
240
- case 1:
241
- name = 'home';
242
- break;
243
- case 2:
244
- name = 'insert';
245
- break;
246
- case 3:
247
- name = 'delete';
248
- break;
249
- case 4:
250
- name = 'end';
251
- break;
252
- case 5:
253
- name = 'pageup';
254
- break;
255
- case 6:
256
- name = 'pagedown';
257
- break;
258
- default:
259
- break;
260
- }
261
- if (name) {
262
- return {
263
- key: {
264
- name,
265
- ctrl,
266
- meta: alt,
267
- shift,
268
- paste: false,
269
- sequence: buffer.slice(0, m[0].length),
270
- kittyProtocol: true,
271
- },
272
- length: m[0].length,
273
- };
274
- }
275
- }
276
- const kittyKeyCodeToName = {
277
- [CHAR_CODE_ESC]: 'escape',
278
- [KITTY_KEYCODE_TAB]: 'tab',
279
- [KITTY_KEYCODE_BACKSPACE]: 'backspace',
280
- [KITTY_KEYCODE_ENTER]: 'return',
281
- [KITTY_KEYCODE_NUMPAD_ENTER]: 'return',
282
- };
283
- const name = kittyKeyCodeToName[keyCode];
284
- if (name) {
285
- return {
286
- key: {
287
- name,
288
- ctrl,
289
- meta: alt,
290
- shift,
291
- paste: false,
292
- sequence: buffer.slice(0, m[0].length),
293
- kittyProtocol: true,
294
- },
295
- length: m[0].length,
296
- };
297
- }
298
- // Ctrl+letters and Alt+letters
299
- if ((ctrl || alt) &&
300
- keyCode >= 'a'.charCodeAt(0) &&
301
- keyCode <= 'z'.charCodeAt(0)) {
302
- const letter = String.fromCharCode(keyCode);
303
- return {
304
- key: {
305
- name: letter,
306
- ctrl,
307
- meta: alt,
308
- shift,
309
- paste: false,
310
- sequence: buffer.slice(0, m[0].length),
311
- kittyProtocol: true,
312
- },
313
- length: m[0].length,
314
- };
315
- }
316
- }
317
- // 4) Legacy function keys (no parameters): ESC [ (A|B|C|D|H|F)
318
- // Arrows + Home/End without modifiers.
319
- const legacyFuncKey = new RegExp(`^${ESC}\\[([ABCDHF])`);
320
- m = buffer.match(legacyFuncKey);
321
- if (m) {
322
- const sym = m[1];
323
- const nameMap = {
324
- A: 'up',
325
- B: 'down',
326
- C: 'right',
327
- D: 'left',
328
- H: 'home',
329
- F: 'end',
330
- };
331
- const name = nameMap[sym];
332
- return {
333
- key: {
334
- name,
335
- ctrl: false,
336
- meta: false,
337
- shift: false,
338
- paste: false,
339
- sequence: buffer.slice(0, m[0].length),
340
- kittyProtocol: true,
341
- },
342
- length: m[0].length,
343
- };
344
- }
345
- return null;
237
+ }
238
+ const kittyKeyCodeToName = {
239
+ [CHAR_CODE_ESC]: 'escape',
240
+ [KITTY_KEYCODE_TAB]: 'tab',
241
+ [KITTY_KEYCODE_BACKSPACE]: 'backspace',
242
+ [KITTY_KEYCODE_ENTER]: 'return',
243
+ [KITTY_KEYCODE_NUMPAD_ENTER]: 'return',
244
+ };
245
+ const name = kittyKeyCodeToName[keyCode];
246
+ if (name) {
247
+ return {
248
+ key: {
249
+ name,
250
+ ctrl,
251
+ meta: alt,
252
+ shift,
253
+ paste: false,
254
+ sequence: buffer.slice(0, m[0].length),
255
+ kittyProtocol: true,
256
+ },
257
+ length: m[0].length,
258
+ };
259
+ }
260
+ // Ctrl+letters and Alt+letters
261
+ if ((ctrl || alt) &&
262
+ keyCode >= 'a'.charCodeAt(0) &&
263
+ keyCode <= 'z'.charCodeAt(0)) {
264
+ const letter = String.fromCharCode(keyCode);
265
+ return {
266
+ key: {
267
+ name: letter,
268
+ ctrl,
269
+ meta: alt,
270
+ shift,
271
+ paste: false,
272
+ sequence: buffer.slice(0, m[0].length),
273
+ kittyProtocol: true,
274
+ },
275
+ length: m[0].length,
276
+ };
277
+ }
278
+ }
279
+ // 4) Legacy function keys (no parameters): ESC [ (A|B|C|D|H|F)
280
+ // Arrows + Home/End without modifiers.
281
+ const legacyFuncKey = new RegExp(`^${ESC}\\[([ABCDHF])`);
282
+ m = buffer.match(legacyFuncKey);
283
+ if (m) {
284
+ const sym = m[1];
285
+ const nameMap = {
286
+ A: 'up',
287
+ B: 'down',
288
+ C: 'right',
289
+ D: 'left',
290
+ H: 'home',
291
+ F: 'end',
292
+ };
293
+ const name = nameMap[sym];
294
+ return {
295
+ key: {
296
+ name,
297
+ ctrl: false,
298
+ meta: false,
299
+ shift: false,
300
+ paste: false,
301
+ sequence: buffer.slice(0, m[0].length),
302
+ kittyProtocol: true,
303
+ },
304
+ length: m[0].length,
346
305
  };
347
- const broadcast = (key) => {
348
- for (const handler of subscribers) {
349
- handler(key);
306
+ }
307
+ return null;
308
+ }
309
+ const KeypressContext = createContext(undefined);
310
+ export function useKeypressContext() {
311
+ const context = useContext(KeypressContext);
312
+ if (!context) {
313
+ throw new Error('useKeypressContext must be used within a KeypressProvider');
314
+ }
315
+ return context;
316
+ }
317
+ /**
318
+ * Determines if the passthrough stream workaround should be used.
319
+ * This is necessary for Node.js versions older than 20 or when the
320
+ * PASTE_WORKAROUND environment variable is set, to correctly handle
321
+ * paste events.
322
+ */
323
+ function shouldUsePassthrough() {
324
+ const nodeMajorVersion = parseInt(process.versions.node.split('.')[0], 10);
325
+ return (nodeMajorVersion < 20 ||
326
+ process.env['PASTE_WORKAROUND'] === '1' ||
327
+ process.env['PASTE_WORKAROUND'] === 'true');
328
+ }
329
+ export function KeypressProvider({ children, kittyProtocolEnabled, config, debugKeystrokeLogging, }) {
330
+ const { stdin, setRawMode } = useStdin();
331
+ const subscribers = useRef(new Set()).current;
332
+ const subscribe = useCallback((handler) => subscribers.add(handler), [subscribers]);
333
+ const unsubscribe = useCallback((handler) => subscribers.delete(handler), [subscribers]);
334
+ const broadcast = useCallback((key) => subscribers.forEach((handler) => handler(key)), [subscribers]);
335
+ useEffect(() => {
336
+ const wasRaw = stdin.isRaw;
337
+ if (wasRaw === false) {
338
+ setRawMode(true);
339
+ }
340
+ const keypressStream = shouldUsePassthrough() ? new PassThrough() : null;
341
+ // If non-null that means we are in paste mode
342
+ let pasteBuffer = null;
343
+ // Used to turn "\" quickly followed by a "enter" into a shift enter
344
+ let backslashTimeout = null;
345
+ // Buffers incomplete Kitty sequences and timer to flush it
346
+ let kittySequenceBuffer = '';
347
+ let kittySequenceTimeout = null;
348
+ // Used to detect filename drag-and-drops.
349
+ let dragBuffer = '';
350
+ let draggingTimer = null;
351
+ const clearDraggingTimer = () => {
352
+ if (draggingTimer) {
353
+ clearTimeout(draggingTimer);
354
+ draggingTimer = null;
350
355
  }
351
356
  };
352
357
  const flushKittyBufferOnInterrupt = (reason) => {
@@ -376,36 +381,36 @@ export function KeypressProvider({ children, kittyProtocolEnabled, config, debug
376
381
  }
377
382
  if (key.name === 'paste-start') {
378
383
  flushKittyBufferOnInterrupt('paste start');
379
- isPaste = true;
384
+ pasteBuffer = Buffer.alloc(0);
380
385
  return;
381
386
  }
382
387
  if (key.name === 'paste-end') {
383
- isPaste = false;
384
- broadcast({
385
- name: '',
386
- ctrl: false,
387
- meta: false,
388
- shift: false,
389
- paste: true,
390
- sequence: pasteBuffer.toString(),
391
- });
392
- pasteBuffer = Buffer.alloc(0);
388
+ if (pasteBuffer !== null) {
389
+ broadcast({
390
+ name: '',
391
+ ctrl: false,
392
+ meta: false,
393
+ shift: false,
394
+ paste: true,
395
+ sequence: pasteBuffer.toString(),
396
+ });
397
+ }
398
+ pasteBuffer = null;
393
399
  return;
394
400
  }
395
- if (isPaste) {
401
+ if (pasteBuffer !== null) {
396
402
  pasteBuffer = Buffer.concat([pasteBuffer, Buffer.from(key.sequence)]);
397
403
  return;
398
404
  }
399
405
  if (key.sequence === SINGLE_QUOTE ||
400
406
  key.sequence === DOUBLE_QUOTE ||
401
- isDraggingRef.current) {
402
- isDraggingRef.current = true;
403
- dragBufferRef.current += key.sequence;
407
+ draggingTimer !== null) {
408
+ dragBuffer += key.sequence;
404
409
  clearDraggingTimer();
405
- draggingTimerRef.current = setTimeout(() => {
406
- isDraggingRef.current = false;
407
- const seq = dragBufferRef.current;
408
- dragBufferRef.current = '';
410
+ draggingTimer = setTimeout(() => {
411
+ draggingTimer = null;
412
+ const seq = dragBuffer;
413
+ dragBuffer = '';
409
414
  if (seq) {
410
415
  broadcast({ ...key, name: '', paste: true, sequence: seq });
411
416
  }
@@ -419,17 +424,14 @@ export function KeypressProvider({ children, kittyProtocolEnabled, config, debug
419
424
  ctrl: false,
420
425
  meta: true,
421
426
  shift: false,
422
- paste: isPaste,
427
+ paste: pasteBuffer !== null,
423
428
  sequence: key.sequence,
424
429
  });
425
430
  return;
426
431
  }
427
- if (key.name === 'return' && waitingForEnterAfterBackslash) {
428
- if (backslashTimeout) {
429
- clearTimeout(backslashTimeout);
430
- backslashTimeout = null;
431
- }
432
- waitingForEnterAfterBackslash = false;
432
+ if (key.name === 'return' && backslashTimeout !== null) {
433
+ clearTimeout(backslashTimeout);
434
+ backslashTimeout = null;
433
435
  broadcast({
434
436
  ...key,
435
437
  shift: true,
@@ -439,20 +441,15 @@ export function KeypressProvider({ children, kittyProtocolEnabled, config, debug
439
441
  }
440
442
  if (key.sequence === '\\' && !key.name) {
441
443
  // Corrected escaping for backslash
442
- waitingForEnterAfterBackslash = true;
443
444
  backslashTimeout = setTimeout(() => {
444
- waitingForEnterAfterBackslash = false;
445
445
  backslashTimeout = null;
446
446
  broadcast(key);
447
447
  }, BACKSLASH_ENTER_DETECTION_WINDOW_MS);
448
448
  return;
449
449
  }
450
- if (waitingForEnterAfterBackslash && key.name !== 'return') {
451
- if (backslashTimeout) {
452
- clearTimeout(backslashTimeout);
453
- backslashTimeout = null;
454
- }
455
- waitingForEnterAfterBackslash = false;
450
+ if (backslashTimeout !== null && key.name !== 'return') {
451
+ clearTimeout(backslashTimeout);
452
+ backslashTimeout = null;
456
453
  broadcast({
457
454
  name: '',
458
455
  sequence: '\\',
@@ -614,7 +611,7 @@ export function KeypressProvider({ children, kittyProtocolEnabled, config, debug
614
611
  if (key.name === 'return' && key.sequence === `${ESC}\r`) {
615
612
  key.meta = true;
616
613
  }
617
- broadcast({ ...key, paste: isPaste });
614
+ broadcast({ ...key, paste: pasteBuffer !== null });
618
615
  };
619
616
  const handleRawKeypress = (data) => {
620
617
  const pasteModePrefixBuffer = Buffer.from(PASTE_MODE_PREFIX);
@@ -660,7 +657,7 @@ export function KeypressProvider({ children, kittyProtocolEnabled, config, debug
660
657
  }
661
658
  };
662
659
  let rl;
663
- if (usePassthrough) {
660
+ if (keypressStream !== null) {
664
661
  rl = readline.createInterface({
665
662
  input: keypressStream,
666
663
  escapeCodeTimeout: 0,
@@ -675,7 +672,7 @@ export function KeypressProvider({ children, kittyProtocolEnabled, config, debug
675
672
  stdin.on('keypress', handleKeypress);
676
673
  }
677
674
  return () => {
678
- if (usePassthrough) {
675
+ if (keypressStream !== null) {
679
676
  keypressStream.removeListener('keypress', handleKeypress);
680
677
  stdin.removeListener('data', handleRawKeypress);
681
678
  }
@@ -708,7 +705,7 @@ export function KeypressProvider({ children, kittyProtocolEnabled, config, debug
708
705
  kittySequenceBuffer = '';
709
706
  }
710
707
  // Flush any pending paste data to avoid data loss on exit.
711
- if (isPaste) {
708
+ if (pasteBuffer !== null) {
712
709
  broadcast({
713
710
  name: '',
714
711
  ctrl: false,
@@ -717,23 +714,19 @@ export function KeypressProvider({ children, kittyProtocolEnabled, config, debug
717
714
  paste: true,
718
715
  sequence: pasteBuffer.toString(),
719
716
  });
720
- pasteBuffer = Buffer.alloc(0);
721
- }
722
- if (draggingTimerRef.current) {
723
- clearTimeout(draggingTimerRef.current);
724
- draggingTimerRef.current = null;
717
+ pasteBuffer = null;
725
718
  }
726
- if (isDraggingRef.current && dragBufferRef.current) {
719
+ clearDraggingTimer();
720
+ if (dragBuffer) {
727
721
  broadcast({
728
722
  name: '',
729
723
  ctrl: false,
730
724
  meta: false,
731
725
  shift: false,
732
726
  paste: true,
733
- sequence: dragBufferRef.current,
727
+ sequence: dragBuffer,
734
728
  });
735
- isDraggingRef.current = false;
736
- dragBufferRef.current = '';
729
+ dragBuffer = '';
737
730
  }
738
731
  };
739
732
  }, [
@@ -741,8 +734,8 @@ export function KeypressProvider({ children, kittyProtocolEnabled, config, debug
741
734
  setRawMode,
742
735
  kittyProtocolEnabled,
743
736
  config,
744
- subscribers,
745
737
  debugKeystrokeLogging,
738
+ broadcast,
746
739
  ]);
747
740
  return (_jsx(KeypressContext.Provider, { value: { subscribe, unsubscribe }, children: children }));
748
741
  }