@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
|
@@ -2,11 +2,8 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
2
2
|
import { act } from 'react';
|
|
3
3
|
import { renderHook } from '../../test-utils/render.js';
|
|
4
4
|
import { waitFor } from '../../test-utils/async.js';
|
|
5
|
-
import { vi } from 'vitest';
|
|
6
|
-
import { KeypressProvider, useKeypressContext,
|
|
7
|
-
// CSI_END_O,
|
|
8
|
-
// SS3_END,
|
|
9
|
-
SINGLE_QUOTE, DOUBLE_QUOTE, } from './KeypressContext.js';
|
|
5
|
+
import { vi, afterAll, beforeAll } from 'vitest';
|
|
6
|
+
import { KeypressProvider, useKeypressContext, ESC_TIMEOUT, } from './KeypressContext.js';
|
|
10
7
|
import { useStdin } from 'ink';
|
|
11
8
|
import { EventEmitter } from 'node:events';
|
|
12
9
|
// Mock the 'ink' module to control stdin
|
|
@@ -34,17 +31,19 @@ class MockStdin extends EventEmitter {
|
|
|
34
31
|
}
|
|
35
32
|
}
|
|
36
33
|
// Helper function to setup keypress test with standard configuration
|
|
37
|
-
const setupKeypressTest = (
|
|
34
|
+
const setupKeypressTest = () => {
|
|
38
35
|
const keyHandler = vi.fn();
|
|
39
|
-
const wrapper = ({ children }) => (_jsx(KeypressProvider, {
|
|
36
|
+
const wrapper = ({ children }) => (_jsx(KeypressProvider, { children: children }));
|
|
40
37
|
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
|
41
38
|
act(() => result.current.subscribe(keyHandler));
|
|
42
39
|
return { result, keyHandler };
|
|
43
40
|
};
|
|
44
|
-
describe('KeypressContext
|
|
41
|
+
describe('KeypressContext', () => {
|
|
45
42
|
let stdin;
|
|
46
43
|
const mockSetRawMode = vi.fn();
|
|
47
|
-
const wrapper = ({ children
|
|
44
|
+
const wrapper = ({ children }) => (_jsx(KeypressProvider, { children: children }));
|
|
45
|
+
beforeAll(() => vi.useFakeTimers());
|
|
46
|
+
afterAll(() => vi.useRealTimers());
|
|
48
47
|
beforeEach(() => {
|
|
49
48
|
vi.clearAllMocks();
|
|
50
49
|
stdin = new MockStdin();
|
|
@@ -64,18 +63,25 @@ describe('KeypressContext - Kitty Protocol', () => {
|
|
|
64
63
|
sequence: '\x1b[57414u',
|
|
65
64
|
},
|
|
66
65
|
])('should recognize $name in kitty protocol', async ({ sequence }) => {
|
|
67
|
-
const { keyHandler } = setupKeypressTest(
|
|
68
|
-
act(() =>
|
|
69
|
-
stdin.write(sequence);
|
|
70
|
-
});
|
|
66
|
+
const { keyHandler } = setupKeypressTest();
|
|
67
|
+
act(() => stdin.write(sequence));
|
|
71
68
|
expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({
|
|
72
69
|
name: 'return',
|
|
73
|
-
kittyProtocol: true,
|
|
74
70
|
ctrl: false,
|
|
75
71
|
meta: false,
|
|
76
72
|
shift: false,
|
|
77
73
|
}));
|
|
78
74
|
});
|
|
75
|
+
it('should handle backslash return', async () => {
|
|
76
|
+
const { keyHandler } = setupKeypressTest();
|
|
77
|
+
act(() => stdin.write('\\\r'));
|
|
78
|
+
expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({
|
|
79
|
+
name: 'return',
|
|
80
|
+
ctrl: false,
|
|
81
|
+
meta: false,
|
|
82
|
+
shift: true,
|
|
83
|
+
}));
|
|
84
|
+
});
|
|
79
85
|
it.each([
|
|
80
86
|
{
|
|
81
87
|
modifier: 'Shift',
|
|
@@ -93,60 +99,57 @@ describe('KeypressContext - Kitty Protocol', () => {
|
|
|
93
99
|
expected: { ctrl: false, meta: true, shift: false },
|
|
94
100
|
},
|
|
95
101
|
])('should handle numpad enter with $modifier modifier', async ({ sequence, expected }) => {
|
|
96
|
-
const { keyHandler } = setupKeypressTest(
|
|
102
|
+
const { keyHandler } = setupKeypressTest();
|
|
97
103
|
act(() => stdin.write(sequence));
|
|
98
104
|
expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({
|
|
99
105
|
name: 'return',
|
|
100
|
-
kittyProtocol: true,
|
|
101
106
|
...expected,
|
|
102
107
|
}));
|
|
103
108
|
});
|
|
104
|
-
it('should not process kitty sequences when kitty protocol is disabled', async () => {
|
|
105
|
-
const { keyHandler } = setupKeypressTest(false);
|
|
106
|
-
// Send kitty protocol sequence for numpad enter
|
|
107
|
-
act(() => {
|
|
108
|
-
stdin.write(`\x1b[57414u`);
|
|
109
|
-
});
|
|
110
|
-
// When kitty protocol is disabled, the sequence should be passed through
|
|
111
|
-
// as individual keypresses, not recognized as a single enter key
|
|
112
|
-
expect(keyHandler).not.toHaveBeenCalledWith(expect.objectContaining({
|
|
113
|
-
name: 'return',
|
|
114
|
-
kittyProtocol: true,
|
|
115
|
-
}));
|
|
116
|
-
});
|
|
117
109
|
});
|
|
118
110
|
describe('Escape key handling', () => {
|
|
119
111
|
it('should recognize escape key (keycode 27) in kitty protocol', async () => {
|
|
120
|
-
const { keyHandler } = setupKeypressTest(
|
|
112
|
+
const { keyHandler } = setupKeypressTest();
|
|
121
113
|
// Send kitty protocol sequence for escape: ESC[27u
|
|
122
114
|
act(() => {
|
|
123
115
|
stdin.write('\x1b[27u');
|
|
124
116
|
});
|
|
125
117
|
expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({
|
|
126
118
|
name: 'escape',
|
|
127
|
-
kittyProtocol: true,
|
|
128
119
|
}));
|
|
129
120
|
});
|
|
121
|
+
it('should handle double Escape', async () => {
|
|
122
|
+
const keyHandler = vi.fn();
|
|
123
|
+
const wrapper = ({ children }) => (_jsx(KeypressProvider, { children: children }));
|
|
124
|
+
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
|
125
|
+
act(() => result.current.subscribe(keyHandler));
|
|
126
|
+
act(() => {
|
|
127
|
+
stdin.write('\x1b');
|
|
128
|
+
vi.advanceTimersByTime(10);
|
|
129
|
+
stdin.write('\x1b');
|
|
130
|
+
expect(keyHandler).not.toHaveBeenCalled();
|
|
131
|
+
vi.advanceTimersByTime(ESC_TIMEOUT);
|
|
132
|
+
expect(keyHandler).toHaveBeenNthCalledWith(1, expect.objectContaining({ name: 'escape', meta: true }));
|
|
133
|
+
expect(keyHandler).toHaveBeenNthCalledWith(2, expect.objectContaining({ name: 'escape', meta: true }));
|
|
134
|
+
});
|
|
135
|
+
});
|
|
130
136
|
it('should handle lone Escape key (keycode 27) with timeout when kitty protocol is enabled', async () => {
|
|
131
137
|
// Use real timers for this test to avoid issues with stream/buffer timing
|
|
132
|
-
vi.useRealTimers();
|
|
133
138
|
const keyHandler = vi.fn();
|
|
134
|
-
const wrapper = ({ children }) => (_jsx(KeypressProvider, {
|
|
139
|
+
const wrapper = ({ children }) => (_jsx(KeypressProvider, { children: children }));
|
|
135
140
|
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
|
136
141
|
act(() => result.current.subscribe(keyHandler));
|
|
137
142
|
// Send just ESC
|
|
138
143
|
act(() => {
|
|
139
144
|
stdin.write('\x1b');
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
// Wait for timeout
|
|
144
|
-
await waitFor(() => {
|
|
145
|
+
// Should be buffered initially
|
|
146
|
+
expect(keyHandler).not.toHaveBeenCalled();
|
|
147
|
+
vi.advanceTimersByTime(ESC_TIMEOUT + 10);
|
|
145
148
|
expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({
|
|
146
149
|
name: 'escape',
|
|
147
150
|
meta: true,
|
|
148
151
|
}));
|
|
149
|
-
}
|
|
152
|
+
});
|
|
150
153
|
});
|
|
151
154
|
});
|
|
152
155
|
describe('Tab and Backspace handling', () => {
|
|
@@ -177,13 +180,12 @@ describe('KeypressContext - Kitty Protocol', () => {
|
|
|
177
180
|
expected: { name: 'backspace', ctrl: true },
|
|
178
181
|
},
|
|
179
182
|
])('should recognize $name in kitty protocol', async ({ sequence, expected }) => {
|
|
180
|
-
const { keyHandler } = setupKeypressTest(
|
|
183
|
+
const { keyHandler } = setupKeypressTest();
|
|
181
184
|
act(() => {
|
|
182
185
|
stdin.write(sequence);
|
|
183
186
|
});
|
|
184
187
|
expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({
|
|
185
188
|
...expected,
|
|
186
|
-
kittyProtocol: true,
|
|
187
189
|
}));
|
|
188
190
|
});
|
|
189
191
|
});
|
|
@@ -245,7 +247,7 @@ describe('KeypressContext - Kitty Protocol', () => {
|
|
|
245
247
|
});
|
|
246
248
|
it('should not log keystrokes when debugKeystrokeLogging is false', async () => {
|
|
247
249
|
const keyHandler = vi.fn();
|
|
248
|
-
const wrapper = ({ children }) => (_jsx(KeypressProvider, {
|
|
250
|
+
const wrapper = ({ children }) => (_jsx(KeypressProvider, { debugKeystrokeLogging: false, children: children }));
|
|
249
251
|
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
|
250
252
|
act(() => result.current.subscribe(keyHandler));
|
|
251
253
|
// Send a kitty sequence
|
|
@@ -257,53 +259,22 @@ describe('KeypressContext - Kitty Protocol', () => {
|
|
|
257
259
|
});
|
|
258
260
|
it('should log kitty buffer accumulation when debugKeystrokeLogging is true', async () => {
|
|
259
261
|
const keyHandler = vi.fn();
|
|
260
|
-
const wrapper = ({ children }) => (_jsx(KeypressProvider, {
|
|
262
|
+
const wrapper = ({ children }) => (_jsx(KeypressProvider, { debugKeystrokeLogging: true, children: children }));
|
|
261
263
|
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
|
262
264
|
act(() => result.current.subscribe(keyHandler));
|
|
263
265
|
// Send a complete kitty sequence for escape
|
|
264
266
|
act(() => stdin.write('\x1b[27u'));
|
|
265
|
-
expect(consoleLogSpy).toHaveBeenCalledWith(
|
|
266
|
-
const parsedCall = consoleLogSpy.mock.calls.find((args) => typeof args[0] === 'string' &&
|
|
267
|
-
args[0].includes('[DEBUG] Sequence parsed successfully'));
|
|
268
|
-
expect(parsedCall).toBeTruthy();
|
|
269
|
-
expect(parsedCall?.[1]).toEqual(expect.stringContaining('\\u001b[27u'));
|
|
270
|
-
});
|
|
271
|
-
it('should log kitty buffer overflow when debugKeystrokeLogging is true', async () => {
|
|
272
|
-
const keyHandler = vi.fn();
|
|
273
|
-
const wrapper = ({ children }) => (_jsx(KeypressProvider, { kittyProtocolEnabled: true, debugKeystrokeLogging: true, children: children }));
|
|
274
|
-
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
|
275
|
-
act(() => result.current.subscribe(keyHandler));
|
|
276
|
-
// Send a long sequence starting with a valid kitty prefix to trigger overflow
|
|
277
|
-
const longSequence = '\x1b[1;' + '1'.repeat(100);
|
|
278
|
-
act(() => stdin.write(longSequence));
|
|
279
|
-
expect(consoleLogSpy).toHaveBeenCalledWith('[DEBUG] Input buffer overflow, clearing:', expect.any(String));
|
|
280
|
-
});
|
|
281
|
-
it('should log kitty buffer clear on Ctrl+C when debugKeystrokeLogging is true', async () => {
|
|
282
|
-
const keyHandler = vi.fn();
|
|
283
|
-
const wrapper = ({ children }) => (_jsx(KeypressProvider, { kittyProtocolEnabled: true, debugKeystrokeLogging: true, children: children }));
|
|
284
|
-
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
|
285
|
-
act(() => result.current.subscribe(keyHandler));
|
|
286
|
-
act(() => stdin.write(INCOMPLETE_KITTY_SEQUENCE));
|
|
287
|
-
// Send Ctrl+C
|
|
288
|
-
act(() => stdin.write('\x03'));
|
|
289
|
-
expect(consoleLogSpy).toHaveBeenCalledWith('[DEBUG] Input buffer cleared on Ctrl+C:', INCOMPLETE_KITTY_SEQUENCE);
|
|
290
|
-
// Verify Ctrl+C was handled
|
|
291
|
-
expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({
|
|
292
|
-
name: 'c',
|
|
293
|
-
ctrl: true,
|
|
294
|
-
}));
|
|
267
|
+
expect(consoleLogSpy).toHaveBeenCalledWith(`[DEBUG] Raw StdIn: ${JSON.stringify('\x1b[27u')}`);
|
|
295
268
|
});
|
|
296
269
|
it('should show char codes when debugKeystrokeLogging is true even without debug mode', async () => {
|
|
297
270
|
const keyHandler = vi.fn();
|
|
298
|
-
const wrapper = ({ children }) => (_jsx(KeypressProvider, {
|
|
271
|
+
const wrapper = ({ children }) => (_jsx(KeypressProvider, { debugKeystrokeLogging: true, children: children }));
|
|
299
272
|
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
|
300
273
|
act(() => result.current.subscribe(keyHandler));
|
|
301
274
|
// Send incomplete kitty sequence
|
|
302
275
|
act(() => stdin.write(INCOMPLETE_KITTY_SEQUENCE));
|
|
303
276
|
// Verify debug logging for accumulation
|
|
304
|
-
expect(consoleLogSpy).toHaveBeenCalledWith(
|
|
305
|
-
// Verify warning for char codes
|
|
306
|
-
expect(consoleWarnSpy).toHaveBeenCalledWith('Input sequence buffer has content:', JSON.stringify(INCOMPLETE_KITTY_SEQUENCE));
|
|
277
|
+
expect(consoleLogSpy).toHaveBeenCalledWith(`[DEBUG] Raw StdIn: ${JSON.stringify(INCOMPLETE_KITTY_SEQUENCE)}`);
|
|
307
278
|
});
|
|
308
279
|
});
|
|
309
280
|
describe('Parameterized functional keys', () => {
|
|
@@ -362,106 +333,18 @@ describe('KeypressContext - Kitty Protocol', () => {
|
|
|
362
333
|
});
|
|
363
334
|
describe('Double-tap and batching', () => {
|
|
364
335
|
it('should emit two delete events for double-tap CSI[3~', async () => {
|
|
365
|
-
const { keyHandler } = setupKeypressTest(
|
|
336
|
+
const { keyHandler } = setupKeypressTest();
|
|
366
337
|
act(() => stdin.write(`\x1b[3~`));
|
|
367
338
|
act(() => stdin.write(`\x1b[3~`));
|
|
368
339
|
expect(keyHandler).toHaveBeenNthCalledWith(1, expect.objectContaining({ name: 'delete' }));
|
|
369
340
|
expect(keyHandler).toHaveBeenNthCalledWith(2, expect.objectContaining({ name: 'delete' }));
|
|
370
341
|
});
|
|
371
342
|
it('should parse two concatenated tilde-coded sequences in one chunk', async () => {
|
|
372
|
-
const { keyHandler } = setupKeypressTest(
|
|
343
|
+
const { keyHandler } = setupKeypressTest();
|
|
373
344
|
act(() => stdin.write(`\x1b[3~\x1b[5~`));
|
|
374
345
|
expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({ name: 'delete' }));
|
|
375
346
|
expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({ name: 'pageup' }));
|
|
376
347
|
});
|
|
377
|
-
it('should ignore incomplete CSI then parse the next complete sequence', async () => {
|
|
378
|
-
const { keyHandler } = setupKeypressTest(true);
|
|
379
|
-
// Incomplete ESC sequence then a complete Delete
|
|
380
|
-
act(() => {
|
|
381
|
-
// Provide an incomplete ESC sequence chunk with a real ESC character
|
|
382
|
-
stdin.write('\x1b[1;');
|
|
383
|
-
});
|
|
384
|
-
act(() => stdin.write(`\x1b[3~`));
|
|
385
|
-
expect(keyHandler).toHaveBeenCalledTimes(1);
|
|
386
|
-
expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({ name: 'delete' }));
|
|
387
|
-
});
|
|
388
|
-
});
|
|
389
|
-
});
|
|
390
|
-
describe('Drag and Drop Handling', () => {
|
|
391
|
-
let stdin;
|
|
392
|
-
const mockSetRawMode = vi.fn();
|
|
393
|
-
const wrapper = ({ children, kittyProtocolEnabled = true, }) => (_jsx(KeypressProvider, { kittyProtocolEnabled: kittyProtocolEnabled, children: children }));
|
|
394
|
-
beforeEach(() => {
|
|
395
|
-
vi.clearAllMocks();
|
|
396
|
-
vi.useFakeTimers();
|
|
397
|
-
stdin = new MockStdin();
|
|
398
|
-
useStdin.mockReturnValue({
|
|
399
|
-
stdin,
|
|
400
|
-
setRawMode: mockSetRawMode,
|
|
401
|
-
});
|
|
402
|
-
});
|
|
403
|
-
afterEach(() => {
|
|
404
|
-
vi.useRealTimers();
|
|
405
|
-
});
|
|
406
|
-
describe('drag start by quotes', () => {
|
|
407
|
-
it.each([
|
|
408
|
-
{ name: 'single quote', quote: SINGLE_QUOTE },
|
|
409
|
-
{ name: 'double quote', quote: DOUBLE_QUOTE },
|
|
410
|
-
])('should start collecting when $name arrives and not broadcast immediately', async ({ quote }) => {
|
|
411
|
-
const keyHandler = vi.fn();
|
|
412
|
-
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
|
413
|
-
act(() => result.current.subscribe(keyHandler));
|
|
414
|
-
act(() => stdin.write(quote));
|
|
415
|
-
expect(keyHandler).not.toHaveBeenCalled();
|
|
416
|
-
});
|
|
417
|
-
});
|
|
418
|
-
describe('drag collection and completion', () => {
|
|
419
|
-
it.each([
|
|
420
|
-
{
|
|
421
|
-
name: 'collect single character inputs during drag mode',
|
|
422
|
-
characters: ['a'],
|
|
423
|
-
expectedText: 'a',
|
|
424
|
-
},
|
|
425
|
-
{
|
|
426
|
-
name: 'collect multiple characters and complete on timeout',
|
|
427
|
-
characters: ['p', 'a', 't', 'h'],
|
|
428
|
-
expectedText: 'path',
|
|
429
|
-
},
|
|
430
|
-
])('should $name', async ({ characters, expectedText }) => {
|
|
431
|
-
const keyHandler = vi.fn();
|
|
432
|
-
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
|
433
|
-
act(() => result.current.subscribe(keyHandler));
|
|
434
|
-
act(() => stdin.write(SINGLE_QUOTE));
|
|
435
|
-
characters.forEach((char) => {
|
|
436
|
-
act(() => stdin.write(char));
|
|
437
|
-
});
|
|
438
|
-
expect(keyHandler).not.toHaveBeenCalled();
|
|
439
|
-
act(() => {
|
|
440
|
-
vi.advanceTimersByTime(DRAG_COMPLETION_TIMEOUT_MS + 10);
|
|
441
|
-
});
|
|
442
|
-
expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({
|
|
443
|
-
name: '',
|
|
444
|
-
paste: true,
|
|
445
|
-
sequence: `${SINGLE_QUOTE}${expectedText}`,
|
|
446
|
-
}));
|
|
447
|
-
});
|
|
448
|
-
});
|
|
449
|
-
});
|
|
450
|
-
describe('Kitty Sequence Parsing', () => {
|
|
451
|
-
let stdin;
|
|
452
|
-
const mockSetRawMode = vi.fn();
|
|
453
|
-
const wrapper = ({ children, kittyProtocolEnabled = true, }) => (_jsx(KeypressProvider, { kittyProtocolEnabled: kittyProtocolEnabled, children: children }));
|
|
454
|
-
beforeEach(() => {
|
|
455
|
-
vi.clearAllMocks();
|
|
456
|
-
vi.useFakeTimers();
|
|
457
|
-
stdin = new MockStdin();
|
|
458
|
-
useStdin.mockReturnValue({
|
|
459
|
-
stdin,
|
|
460
|
-
setRawMode: mockSetRawMode,
|
|
461
|
-
});
|
|
462
|
-
});
|
|
463
|
-
afterEach(() => {
|
|
464
|
-
vi.useRealTimers();
|
|
465
348
|
});
|
|
466
349
|
describe('Cross-terminal Alt key handling (simulating macOS)', () => {
|
|
467
350
|
let originalPlatform;
|
|
@@ -499,7 +382,6 @@ describe('Kitty Sequence Parsing', () => {
|
|
|
499
382
|
meta: true,
|
|
500
383
|
shift: false,
|
|
501
384
|
paste: false,
|
|
502
|
-
kittyProtocol: true,
|
|
503
385
|
},
|
|
504
386
|
};
|
|
505
387
|
}
|
|
@@ -538,9 +420,9 @@ describe('Kitty Sequence Parsing', () => {
|
|
|
538
420
|
},
|
|
539
421
|
};
|
|
540
422
|
}
|
|
541
|
-
})))('should handle Alt+$key in $terminal', ({ chunk, expected
|
|
423
|
+
})))('should handle Alt+$key in $terminal', ({ chunk, expected }) => {
|
|
542
424
|
const keyHandler = vi.fn();
|
|
543
|
-
const testWrapper = ({ children }) => (_jsx(KeypressProvider, {
|
|
425
|
+
const testWrapper = ({ children }) => (_jsx(KeypressProvider, { children: children }));
|
|
544
426
|
const { result } = renderHook(() => useKeypressContext(), {
|
|
545
427
|
wrapper: testWrapper,
|
|
546
428
|
});
|
|
@@ -550,14 +432,8 @@ describe('Kitty Sequence Parsing', () => {
|
|
|
550
432
|
});
|
|
551
433
|
});
|
|
552
434
|
describe('Backslash key handling', () => {
|
|
553
|
-
beforeEach(() => {
|
|
554
|
-
vi.useFakeTimers();
|
|
555
|
-
});
|
|
556
|
-
afterEach(() => {
|
|
557
|
-
vi.useRealTimers();
|
|
558
|
-
});
|
|
559
435
|
it('should treat backslash as a regular keystroke', () => {
|
|
560
|
-
const { keyHandler } = setupKeypressTest(
|
|
436
|
+
const { keyHandler } = setupKeypressTest();
|
|
561
437
|
act(() => stdin.write('\\'));
|
|
562
438
|
// Advance timers to trigger the backslash timeout
|
|
563
439
|
act(() => {
|
|
@@ -577,14 +453,14 @@ describe('Kitty Sequence Parsing', () => {
|
|
|
577
453
|
// Should not broadcast immediately
|
|
578
454
|
expect(keyHandler).not.toHaveBeenCalled();
|
|
579
455
|
// Advance time just before timeout
|
|
580
|
-
act(() => vi.advanceTimersByTime(
|
|
456
|
+
act(() => vi.advanceTimersByTime(ESC_TIMEOUT - 5));
|
|
581
457
|
// Still shouldn't broadcast
|
|
582
458
|
expect(keyHandler).not.toHaveBeenCalled();
|
|
583
459
|
// Advance past timeout
|
|
584
460
|
act(() => vi.advanceTimersByTime(10));
|
|
585
461
|
// Should now broadcast the incomplete sequence as regular input
|
|
586
462
|
expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({
|
|
587
|
-
name: '',
|
|
463
|
+
name: 'undefined',
|
|
588
464
|
sequence: INCOMPLETE_KITTY_SEQUENCE,
|
|
589
465
|
paste: false,
|
|
590
466
|
}));
|
|
@@ -612,7 +488,6 @@ describe('Kitty Sequence Parsing', () => {
|
|
|
612
488
|
expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({
|
|
613
489
|
name: 'a',
|
|
614
490
|
ctrl: true,
|
|
615
|
-
kittyProtocol: true,
|
|
616
491
|
}));
|
|
617
492
|
});
|
|
618
493
|
it('should handle batched kitty sequences correctly', async () => {
|
|
@@ -626,28 +501,10 @@ describe('Kitty Sequence Parsing', () => {
|
|
|
626
501
|
expect(keyHandler).toHaveBeenNthCalledWith(1, expect.objectContaining({
|
|
627
502
|
name: 'a',
|
|
628
503
|
ctrl: true,
|
|
629
|
-
kittyProtocol: true,
|
|
630
504
|
}));
|
|
631
505
|
expect(keyHandler).toHaveBeenNthCalledWith(2, expect.objectContaining({
|
|
632
506
|
name: 'b',
|
|
633
507
|
ctrl: true,
|
|
634
|
-
kittyProtocol: true,
|
|
635
|
-
}));
|
|
636
|
-
});
|
|
637
|
-
it('should clear kitty buffer and timeout on Ctrl+C', async () => {
|
|
638
|
-
const keyHandler = vi.fn();
|
|
639
|
-
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
|
640
|
-
act(() => result.current.subscribe(keyHandler));
|
|
641
|
-
act(() => stdin.write(INCOMPLETE_KITTY_SEQUENCE));
|
|
642
|
-
// Press Ctrl+C
|
|
643
|
-
act(() => stdin.write('\x03'));
|
|
644
|
-
// Advance past timeout
|
|
645
|
-
act(() => vi.advanceTimersByTime(KITTY_SEQUENCE_TIMEOUT_MS + 10));
|
|
646
|
-
// Should only have received Ctrl+C, not the incomplete sequence
|
|
647
|
-
expect(keyHandler).toHaveBeenCalledTimes(1);
|
|
648
|
-
expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({
|
|
649
|
-
name: 'c',
|
|
650
|
-
ctrl: true,
|
|
651
508
|
}));
|
|
652
509
|
});
|
|
653
510
|
it('should handle mixed valid and invalid sequences', async () => {
|
|
@@ -661,49 +518,25 @@ describe('Kitty Sequence Parsing', () => {
|
|
|
661
518
|
expect(keyHandler).toHaveBeenCalledTimes(2);
|
|
662
519
|
expect(keyHandler).toHaveBeenNthCalledWith(1, expect.objectContaining({
|
|
663
520
|
name: 'return',
|
|
664
|
-
kittyProtocol: true,
|
|
665
521
|
}));
|
|
666
522
|
expect(keyHandler).toHaveBeenNthCalledWith(2, expect.objectContaining({
|
|
667
523
|
sequence: '\x1b[!',
|
|
668
524
|
}));
|
|
669
525
|
});
|
|
670
|
-
it('should
|
|
671
|
-
const keyHandler = vi.fn();
|
|
672
|
-
const { result } = renderHook(() => useKeypressContext(), {
|
|
673
|
-
wrapper: ({ children }) => wrapper({ children, kittyProtocolEnabled: false }),
|
|
674
|
-
});
|
|
675
|
-
act(() => result.current.subscribe(keyHandler));
|
|
676
|
-
// Send what would be a kitty sequence
|
|
677
|
-
act(() => stdin.write('\x1b[13u'));
|
|
678
|
-
// Should pass through without parsing
|
|
679
|
-
expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({
|
|
680
|
-
sequence: '\x1b[13u',
|
|
681
|
-
}));
|
|
682
|
-
expect(keyHandler).not.toHaveBeenCalledWith(expect.objectContaining({
|
|
683
|
-
name: 'return',
|
|
684
|
-
kittyProtocol: true,
|
|
685
|
-
}));
|
|
686
|
-
});
|
|
687
|
-
it('should handle sequences arriving character by character', async () => {
|
|
688
|
-
vi.useRealTimers(); // Required for correct buffering timing.
|
|
526
|
+
it.each([1, ESC_TIMEOUT - 1])('should handle sequences arriving character by character with %s ms delay', async (delay) => {
|
|
689
527
|
const keyHandler = vi.fn();
|
|
690
528
|
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
|
691
|
-
act(() =>
|
|
692
|
-
result.current.subscribe(keyHandler);
|
|
693
|
-
});
|
|
529
|
+
act(() => result.current.subscribe(keyHandler));
|
|
694
530
|
// Send kitty sequence character by character
|
|
695
|
-
const
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
});
|
|
700
|
-
await new Promise((resolve) => setImmediate(resolve));
|
|
531
|
+
for (const char of '\x1b[27u') {
|
|
532
|
+
act(() => stdin.write(char));
|
|
533
|
+
// Advance time but not enough to timeout
|
|
534
|
+
vi.advanceTimersByTime(delay);
|
|
701
535
|
}
|
|
702
536
|
// Should parse once complete
|
|
703
537
|
await waitFor(() => {
|
|
704
538
|
expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({
|
|
705
539
|
name: 'escape',
|
|
706
|
-
kittyProtocol: true,
|
|
707
540
|
}));
|
|
708
541
|
});
|
|
709
542
|
});
|
|
@@ -726,76 +559,8 @@ describe('Kitty Sequence Parsing', () => {
|
|
|
726
559
|
// Should now parse as complete enter key
|
|
727
560
|
expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({
|
|
728
561
|
name: 'a',
|
|
729
|
-
kittyProtocol: true,
|
|
730
|
-
}));
|
|
731
|
-
});
|
|
732
|
-
it('should flush incomplete kitty sequence on FOCUS_IN event', async () => {
|
|
733
|
-
const keyHandler = vi.fn();
|
|
734
|
-
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
|
735
|
-
act(() => result.current.subscribe(keyHandler));
|
|
736
|
-
act(() => stdin.write(INCOMPLETE_KITTY_SEQUENCE));
|
|
737
|
-
// Incomplete sequence should be buffered, not broadcast
|
|
738
|
-
expect(keyHandler).not.toHaveBeenCalled();
|
|
739
|
-
// Send FOCUS_IN event
|
|
740
|
-
act(() => stdin.write('\x1b[I'));
|
|
741
|
-
// The buffered sequence should be flushed
|
|
742
|
-
expect(keyHandler).toHaveBeenCalledTimes(1);
|
|
743
|
-
expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({
|
|
744
|
-
name: '',
|
|
745
|
-
sequence: INCOMPLETE_KITTY_SEQUENCE,
|
|
746
|
-
paste: false,
|
|
747
562
|
}));
|
|
748
563
|
});
|
|
749
|
-
it('should flush incomplete kitty sequence on FOCUS_OUT event', async () => {
|
|
750
|
-
const keyHandler = vi.fn();
|
|
751
|
-
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
|
752
|
-
act(() => result.current.subscribe(keyHandler));
|
|
753
|
-
act(() => stdin.write(INCOMPLETE_KITTY_SEQUENCE));
|
|
754
|
-
// Incomplete sequence should be buffered, not broadcast
|
|
755
|
-
expect(keyHandler).not.toHaveBeenCalled();
|
|
756
|
-
// Send FOCUS_OUT event
|
|
757
|
-
act(() => stdin.write('\x1b[O'));
|
|
758
|
-
// The buffered sequence should be flushed
|
|
759
|
-
expect(keyHandler).toHaveBeenCalledTimes(1);
|
|
760
|
-
expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({
|
|
761
|
-
name: '',
|
|
762
|
-
sequence: INCOMPLETE_KITTY_SEQUENCE,
|
|
763
|
-
paste: false,
|
|
764
|
-
}));
|
|
765
|
-
});
|
|
766
|
-
it('should flush incomplete kitty sequence on paste event', async () => {
|
|
767
|
-
vi.useFakeTimers();
|
|
768
|
-
const keyHandler = vi.fn();
|
|
769
|
-
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
|
770
|
-
act(() => result.current.subscribe(keyHandler));
|
|
771
|
-
act(() => stdin.write(INCOMPLETE_KITTY_SEQUENCE));
|
|
772
|
-
// Incomplete sequence should be buffered, not broadcast
|
|
773
|
-
expect(keyHandler).not.toHaveBeenCalled();
|
|
774
|
-
// Send paste start sequence
|
|
775
|
-
act(() => stdin.write(`\x1b[200~`));
|
|
776
|
-
// The buffered sequence should be flushed
|
|
777
|
-
expect(keyHandler).toHaveBeenCalledTimes(1);
|
|
778
|
-
expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({
|
|
779
|
-
name: '',
|
|
780
|
-
sequence: INCOMPLETE_KITTY_SEQUENCE,
|
|
781
|
-
paste: false,
|
|
782
|
-
}));
|
|
783
|
-
// Now send some paste content and end paste to make sure paste still works
|
|
784
|
-
const pastedText = 'hello';
|
|
785
|
-
const PASTE_MODE_SUFFIX = `\x1b[201~`;
|
|
786
|
-
act(() => {
|
|
787
|
-
stdin.write(pastedText);
|
|
788
|
-
stdin.write(PASTE_MODE_SUFFIX);
|
|
789
|
-
});
|
|
790
|
-
act(() => vi.runAllTimers());
|
|
791
|
-
// The paste event should be broadcast
|
|
792
|
-
expect(keyHandler).toHaveBeenCalledTimes(2);
|
|
793
|
-
expect(keyHandler).toHaveBeenNthCalledWith(2, expect.objectContaining({
|
|
794
|
-
paste: true,
|
|
795
|
-
sequence: pastedText,
|
|
796
|
-
}));
|
|
797
|
-
vi.useRealTimers();
|
|
798
|
-
});
|
|
799
564
|
describe('SGR Mouse Handling', () => {
|
|
800
565
|
it('should ignore SGR mouse sequences', async () => {
|
|
801
566
|
const keyHandler = vi.fn();
|
|
@@ -834,26 +599,22 @@ describe('Kitty Sequence Parsing', () => {
|
|
|
834
599
|
// Send X11 mouse sequence: ESC [ M followed by 3 bytes
|
|
835
600
|
// Space is 32. 32+0=32 (button 0), 32+33=65 ('A', col 33), 32+34=66 ('B', row 34)
|
|
836
601
|
const x11Seq = '\x1b[M AB';
|
|
837
|
-
act(() =>
|
|
838
|
-
stdin.write(x11Seq);
|
|
839
|
-
});
|
|
602
|
+
act(() => stdin.write(x11Seq));
|
|
840
603
|
// Should not broadcast as keystrokes
|
|
841
604
|
expect(keyHandler).not.toHaveBeenCalled();
|
|
842
605
|
});
|
|
843
606
|
it('should not flush slow SGR mouse sequences as garbage', async () => {
|
|
844
|
-
vi.useFakeTimers();
|
|
845
607
|
const keyHandler = vi.fn();
|
|
846
608
|
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
|
847
609
|
act(() => result.current.subscribe(keyHandler));
|
|
848
610
|
// Send start of SGR sequence
|
|
849
611
|
act(() => stdin.write('\x1b[<'));
|
|
850
612
|
// Advance time past the normal kitty timeout (50ms)
|
|
851
|
-
act(() => vi.advanceTimersByTime(
|
|
613
|
+
act(() => vi.advanceTimersByTime(ESC_TIMEOUT + 10));
|
|
852
614
|
// Send the rest
|
|
853
615
|
act(() => stdin.write('0;37;25M'));
|
|
854
616
|
// Should NOT have flushed the prefix as garbage, and should have consumed the whole thing
|
|
855
617
|
expect(keyHandler).not.toHaveBeenCalled();
|
|
856
|
-
vi.useRealTimers();
|
|
857
618
|
});
|
|
858
619
|
it('should ignore specific SGR mouse sequence sandwiched between keystrokes', async () => {
|
|
859
620
|
const keyHandler = vi.fn();
|
|
@@ -870,41 +631,31 @@ describe('Kitty Sequence Parsing', () => {
|
|
|
870
631
|
});
|
|
871
632
|
});
|
|
872
633
|
describe('Ignored Sequences', () => {
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
const { result } = renderHook(() => useKeypressContext(), {
|
|
885
|
-
wrapper,
|
|
886
|
-
});
|
|
887
|
-
act(() => result.current.subscribe(keyHandler));
|
|
888
|
-
for (const char of sequence) {
|
|
889
|
-
act(() => {
|
|
890
|
-
stdin.write(char);
|
|
891
|
-
});
|
|
892
|
-
await act(async () => {
|
|
893
|
-
vi.advanceTimersByTime(0);
|
|
894
|
-
});
|
|
895
|
-
}
|
|
896
|
-
act(() => {
|
|
897
|
-
stdin.write('HI');
|
|
898
|
-
});
|
|
899
|
-
expect(keyHandler).toHaveBeenCalledTimes(2);
|
|
900
|
-
expect(keyHandler).toHaveBeenNthCalledWith(1, expect.objectContaining({ name: 'h', sequence: 'H', shift: true }));
|
|
901
|
-
expect(keyHandler).toHaveBeenNthCalledWith(2, expect.objectContaining({ name: 'i', sequence: 'I', shift: true }));
|
|
902
|
-
vi.useRealTimers();
|
|
634
|
+
it.each([
|
|
635
|
+
{ name: 'Focus In', sequence: '\x1b[I' },
|
|
636
|
+
{ name: 'Focus Out', sequence: '\x1b[O' },
|
|
637
|
+
{ name: 'SGR Mouse Release', sequence: '\u001b[<0;44;18m' },
|
|
638
|
+
{ name: 'something mouse', sequence: '\u001b[<0;53;19M' },
|
|
639
|
+
{ name: 'another mouse', sequence: '\u001b[<0;29;19m' },
|
|
640
|
+
])('should ignore $name sequence', async ({ sequence }) => {
|
|
641
|
+
const keyHandler = vi.fn();
|
|
642
|
+
const wrapper = ({ children }) => (_jsx(KeypressProvider, { children: children }));
|
|
643
|
+
const { result } = renderHook(() => useKeypressContext(), {
|
|
644
|
+
wrapper,
|
|
903
645
|
});
|
|
646
|
+
act(() => result.current.subscribe(keyHandler));
|
|
647
|
+
for (const char of sequence) {
|
|
648
|
+
act(() => stdin.write(char));
|
|
649
|
+
act(() => vi.advanceTimersByTime(0));
|
|
650
|
+
}
|
|
651
|
+
act(() => stdin.write('HI'));
|
|
652
|
+
expect(keyHandler).toHaveBeenCalledTimes(2);
|
|
653
|
+
expect(keyHandler).toHaveBeenNthCalledWith(1, expect.objectContaining({ name: 'h', sequence: 'H', shift: true }));
|
|
654
|
+
expect(keyHandler).toHaveBeenNthCalledWith(2, expect.objectContaining({ name: 'i', sequence: 'I', shift: true }));
|
|
904
655
|
});
|
|
905
|
-
it('should handle F12
|
|
656
|
+
it('should handle F12', async () => {
|
|
906
657
|
const keyHandler = vi.fn();
|
|
907
|
-
const wrapper = ({ children }) => (_jsx(KeypressProvider, {
|
|
658
|
+
const wrapper = ({ children }) => (_jsx(KeypressProvider, { children: children }));
|
|
908
659
|
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
|
909
660
|
act(() => result.current.subscribe(keyHandler));
|
|
910
661
|
act(() => {
|
|
@@ -913,5 +664,23 @@ describe('Kitty Sequence Parsing', () => {
|
|
|
913
664
|
expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({ name: 'f12', sequence: '\u001b[24~' }));
|
|
914
665
|
});
|
|
915
666
|
});
|
|
667
|
+
describe('Individual Character Input', () => {
|
|
668
|
+
it.each([
|
|
669
|
+
'abc', // ASCII character
|
|
670
|
+
'你好', // Chinese characters
|
|
671
|
+
'こんにちは', // Japanese characters
|
|
672
|
+
'안녕하세요', // Korean characters
|
|
673
|
+
'A你B好C', // Mixed characters
|
|
674
|
+
])('should correctly handle string "%s"', async (inputString) => {
|
|
675
|
+
const keyHandler = vi.fn();
|
|
676
|
+
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
|
677
|
+
act(() => result.current.subscribe(keyHandler));
|
|
678
|
+
act(() => stdin.write(inputString));
|
|
679
|
+
expect(keyHandler).toHaveBeenCalledTimes(inputString.length);
|
|
680
|
+
for (const char of inputString) {
|
|
681
|
+
expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({ sequence: char }));
|
|
682
|
+
}
|
|
683
|
+
});
|
|
684
|
+
});
|
|
916
685
|
});
|
|
917
686
|
//# sourceMappingURL=KeypressContext.test.js.map
|