@vybestack/llxprt-code 0.8.0-nightly.260113.48db4b09b → 0.8.0-nightly.260114.3fcfeb2b0

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 (76) hide show
  1. package/README.md +64 -27
  2. package/dist/package.json +3 -3
  3. package/dist/src/commands/extensions.js +1 -0
  4. package/dist/src/commands/extensions.js.map +1 -1
  5. package/dist/src/config/config.d.ts +1 -1
  6. package/dist/src/config/config.js +35 -8
  7. package/dist/src/config/config.js.map +1 -1
  8. package/dist/src/config/extension.d.ts +1 -0
  9. package/dist/src/config/extension.js +14 -5
  10. package/dist/src/config/extension.js.map +1 -1
  11. package/dist/src/config/extensions/extensionEnablement.d.ts +1 -1
  12. package/dist/src/config/extensions/extensionEnablement.js +3 -2
  13. package/dist/src/config/extensions/extensionEnablement.js.map +1 -1
  14. package/dist/src/config/extensions/extensionEnablement.test.js +10 -9
  15. package/dist/src/config/extensions/extensionEnablement.test.js.map +1 -1
  16. package/dist/src/config/extensions/github.js +3 -2
  17. package/dist/src/config/extensions/github.js.map +1 -1
  18. package/dist/src/config/settings.js +2 -2
  19. package/dist/src/config/settings.js.map +1 -1
  20. package/dist/src/config/settingsSchema.d.ts +17 -4
  21. package/dist/src/config/settingsSchema.js +18 -4
  22. package/dist/src/config/settingsSchema.js.map +1 -1
  23. package/dist/src/generated/git-commit.d.ts +1 -1
  24. package/dist/src/generated/git-commit.js +1 -1
  25. package/dist/src/nonInteractiveCli.js +19 -1
  26. package/dist/src/nonInteractiveCli.js.map +1 -1
  27. package/dist/src/ui/AppContainer.js +28 -11
  28. package/dist/src/ui/AppContainer.js.map +1 -1
  29. package/dist/src/ui/commands/directoryCommand.js +1 -1
  30. package/dist/src/ui/commands/directoryCommand.js.map +1 -1
  31. package/dist/src/ui/commands/extensionsCommand.js +19 -10
  32. package/dist/src/ui/commands/extensionsCommand.js.map +1 -1
  33. package/dist/src/ui/commands/memoryCommand.js +1 -1
  34. package/dist/src/ui/commands/memoryCommand.js.map +1 -1
  35. package/dist/src/ui/commands/setCommand.js +21 -7
  36. package/dist/src/ui/commands/setCommand.js.map +1 -1
  37. package/dist/src/ui/components/HistoryItemDisplay.js +2 -1
  38. package/dist/src/ui/components/HistoryItemDisplay.js.map +1 -1
  39. package/dist/src/ui/components/InputPrompt.js +5 -6
  40. package/dist/src/ui/components/InputPrompt.js.map +1 -1
  41. package/dist/src/ui/components/messages/GeminiMessage.test.js +74 -31
  42. package/dist/src/ui/components/messages/GeminiMessage.test.js.map +1 -1
  43. package/dist/src/ui/components/messages/ToolMessage.test.js +13 -37
  44. package/dist/src/ui/components/messages/ToolMessage.test.js.map +1 -1
  45. package/dist/src/ui/components/views/ExtensionsList.d.ts +12 -0
  46. package/dist/src/ui/components/views/ExtensionsList.js +42 -0
  47. package/dist/src/ui/components/views/ExtensionsList.js.map +1 -0
  48. package/dist/src/ui/components/views/ExtensionsList.test.js +111 -0
  49. package/dist/src/ui/components/views/ExtensionsList.test.js.map +1 -0
  50. package/dist/src/ui/containers/SessionController.js +1 -1
  51. package/dist/src/ui/containers/SessionController.js.map +1 -1
  52. package/dist/src/ui/contexts/KeypressContext.test.js +131 -477
  53. package/dist/src/ui/contexts/KeypressContext.test.js.map +1 -1
  54. package/dist/src/ui/hooks/useAutoAcceptIndicator.js +10 -0
  55. package/dist/src/ui/hooks/useAutoAcceptIndicator.js.map +1 -1
  56. package/dist/src/ui/hooks/useAutoAcceptIndicator.test.js +1 -0
  57. package/dist/src/ui/hooks/useAutoAcceptIndicator.test.js.map +1 -1
  58. package/dist/src/ui/hooks/useGitBranchName.js +4 -0
  59. package/dist/src/ui/hooks/useGitBranchName.js.map +1 -1
  60. package/dist/src/ui/hooks/useGitBranchName.test.js +15 -17
  61. package/dist/src/ui/hooks/useGitBranchName.test.js.map +1 -1
  62. package/dist/src/ui/hooks/useSessionRestore.test.d.ts +6 -0
  63. package/dist/src/ui/hooks/useSessionRestore.test.js +103 -0
  64. package/dist/src/ui/hooks/useSessionRestore.test.js.map +1 -0
  65. package/dist/src/ui/hooks/useToolScheduler.test.js +6 -7
  66. package/dist/src/ui/hooks/useToolScheduler.test.js.map +1 -1
  67. package/dist/src/ui/types.d.ts +2 -1
  68. package/dist/src/ui/types.js.map +1 -1
  69. package/dist/src/utils/handleAutoUpdate.js +3 -3
  70. package/dist/src/zed-integration/zedIntegration.js +5 -10
  71. package/dist/src/zed-integration/zedIntegration.js.map +1 -1
  72. package/dist/tsconfig.build.tsbuildinfo +1 -1
  73. package/package.json +3 -3
  74. package/dist/src/ui/components/messages/ToolMessageRawMarkdown.test.js +0 -30
  75. package/dist/src/ui/components/messages/ToolMessageRawMarkdown.test.js.map +0 -1
  76. /package/dist/src/ui/components/{messages/ToolMessageRawMarkdown.test.d.ts → views/ExtensionsList.test.d.ts} +0 -0
@@ -15,30 +15,35 @@ vi.mock('ink', async (importOriginal) => {
15
15
  useStdin: vi.fn(),
16
16
  };
17
17
  });
18
+ const PASTE_START = '\x1B[200~';
19
+ const PASTE_END = '\x1B[201~';
20
+ // readline will not emit most incomplete kitty sequences but it will give
21
+ // up on sequences like this where the modifier (135) has more than two digits.
22
+ const INCOMPLETE_KITTY_SEQUENCE = '\x1b[97;135';
18
23
  class MockStdin extends EventEmitter {
19
24
  isTTY = true;
20
25
  isRaw = false;
21
26
  setRawMode = vi.fn();
22
27
  on = this.addListener;
23
28
  removeListener = super.removeListener;
24
- write = vi.fn();
25
29
  resume = vi.fn();
26
30
  pause = vi.fn();
27
- // Helper to simulate a keypress event
31
+ write(text) {
32
+ this.emit('data', Buffer.from(text));
33
+ }
34
+ /**
35
+ * Used to directly simulate keyPress events. Certain keypress events might
36
+ * be impossible to fire in certain versions of node. This allows us to
37
+ * sidestep readline entirely and just emit the keypress we want.
38
+ */
28
39
  pressKey(key) {
29
40
  this.emit('keypress', null, key);
30
41
  }
31
- // Helper to simulate a kitty protocol sequence
32
42
  sendKittySequence(sequence) {
33
- this.emit('data', Buffer.from(sequence));
43
+ this.write(sequence);
34
44
  }
35
- // Helper to simulate a paste event
36
45
  sendPaste(text) {
37
- const PASTE_MODE_PREFIX = `\x1b[200~`;
38
- const PASTE_MODE_SUFFIX = `\x1b[201~`;
39
- this.emit('data', Buffer.from(PASTE_MODE_PREFIX));
40
- this.emit('data', Buffer.from(text));
41
- this.emit('data', Buffer.from(PASTE_MODE_SUFFIX));
46
+ this.write(PASTE_START + text + PASTE_END);
42
47
  }
43
48
  }
44
49
  describe('KeypressContext - Kitty Protocol', () => {
@@ -59,12 +64,10 @@ describe('KeypressContext - Kitty Protocol', () => {
59
64
  const { result } = renderHook(() => useKeypressContext(), {
60
65
  wrapper: ({ children }) => wrapper({ children, kittyProtocolEnabled: true }),
61
66
  });
62
- act(() => {
63
- result.current.subscribe(keyHandler);
64
- });
67
+ act(() => result.current.subscribe(keyHandler));
65
68
  // Send kitty protocol sequence for regular enter: ESC[13u
66
69
  act(() => {
67
- stdin.sendKittySequence(`\x1b[13u`);
70
+ stdin.write(`\x1b[13u`);
68
71
  });
69
72
  expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({
70
73
  name: 'return',
@@ -79,12 +82,10 @@ describe('KeypressContext - Kitty Protocol', () => {
79
82
  const { result } = renderHook(() => useKeypressContext(), {
80
83
  wrapper: ({ children }) => wrapper({ children, kittyProtocolEnabled: true }),
81
84
  });
82
- act(() => {
83
- result.current.subscribe(keyHandler);
84
- });
85
+ act(() => result.current.subscribe(keyHandler));
85
86
  // Send kitty protocol sequence for numpad enter: ESC[57414u
86
87
  act(() => {
87
- stdin.sendKittySequence(`\x1b[57414u`);
88
+ stdin.write(`\x1b[57414u`);
88
89
  });
89
90
  expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({
90
91
  name: 'return',
@@ -99,12 +100,10 @@ describe('KeypressContext - Kitty Protocol', () => {
99
100
  const { result } = renderHook(() => useKeypressContext(), {
100
101
  wrapper: ({ children }) => wrapper({ children, kittyProtocolEnabled: true }),
101
102
  });
102
- act(() => {
103
- result.current.subscribe(keyHandler);
104
- });
103
+ act(() => result.current.subscribe(keyHandler));
105
104
  // Send kitty protocol sequence for numpad enter with Shift (modifier 2): ESC[57414;2u
106
105
  act(() => {
107
- stdin.sendKittySequence(`\x1b[57414;2u`);
106
+ stdin.write(`\x1b[57414;2u`);
108
107
  });
109
108
  expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({
110
109
  name: 'return',
@@ -119,13 +118,9 @@ describe('KeypressContext - Kitty Protocol', () => {
119
118
  const { result } = renderHook(() => useKeypressContext(), {
120
119
  wrapper: ({ children }) => wrapper({ children, kittyProtocolEnabled: true }),
121
120
  });
122
- act(() => {
123
- result.current.subscribe(keyHandler);
124
- });
121
+ act(() => result.current.subscribe(keyHandler));
125
122
  // Send kitty protocol sequence for numpad enter with Ctrl (modifier 5): ESC[57414;5u
126
- act(() => {
127
- stdin.sendKittySequence(`\x1b[57414;5u`);
128
- });
123
+ act(() => stdin.write(`\x1b[57414;5u`));
129
124
  expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({
130
125
  name: 'return',
131
126
  kittyProtocol: true,
@@ -139,12 +134,10 @@ describe('KeypressContext - Kitty Protocol', () => {
139
134
  const { result } = renderHook(() => useKeypressContext(), {
140
135
  wrapper: ({ children }) => wrapper({ children, kittyProtocolEnabled: true }),
141
136
  });
142
- act(() => {
143
- result.current.subscribe(keyHandler);
144
- });
137
+ act(() => result.current.subscribe(keyHandler));
145
138
  // Send kitty protocol sequence for numpad enter with Alt (modifier 3): ESC[57414;3u
146
139
  act(() => {
147
- stdin.sendKittySequence(`\x1b[57414;3u`);
140
+ stdin.write(`\x1b[57414;3u`);
148
141
  });
149
142
  expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({
150
143
  name: 'return',
@@ -159,12 +152,10 @@ describe('KeypressContext - Kitty Protocol', () => {
159
152
  const { result } = renderHook(() => useKeypressContext(), {
160
153
  wrapper: ({ children }) => wrapper({ children, kittyProtocolEnabled: false }),
161
154
  });
162
- act(() => {
163
- result.current.subscribe(keyHandler);
164
- });
155
+ act(() => result.current.subscribe(keyHandler));
165
156
  // Send kitty protocol sequence for numpad enter
166
157
  act(() => {
167
- stdin.sendKittySequence(`\x1b[57414u`);
158
+ stdin.write(`\x1b[57414u`);
168
159
  });
169
160
  // When kitty protocol is disabled, the sequence should be passed through
170
161
  // as individual keypresses, not recognized as a single enter key
@@ -180,12 +171,10 @@ describe('KeypressContext - Kitty Protocol', () => {
180
171
  const { result } = renderHook(() => useKeypressContext(), {
181
172
  wrapper: ({ children }) => wrapper({ children, kittyProtocolEnabled: true }),
182
173
  });
183
- act(() => {
184
- result.current.subscribe(keyHandler);
185
- });
174
+ act(() => result.current.subscribe(keyHandler));
186
175
  // Send kitty protocol sequence for escape: ESC[27u
187
176
  act(() => {
188
- stdin.sendKittySequence('\x1b[27u');
177
+ stdin.write('\x1b[27u');
189
178
  });
190
179
  expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({
191
180
  name: 'escape',
@@ -199,7 +188,7 @@ describe('KeypressContext - Kitty Protocol', () => {
199
188
  const { result } = renderHook(() => useKeypressContext(), { wrapper });
200
189
  act(() => result.current.subscribe(keyHandler));
201
190
  act(() => {
202
- stdin.sendKittySequence(`\x1b[9u`);
191
+ stdin.write(`\x1b[9u`);
203
192
  });
204
193
  expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({
205
194
  name: 'tab',
@@ -213,7 +202,7 @@ describe('KeypressContext - Kitty Protocol', () => {
213
202
  act(() => result.current.subscribe(keyHandler));
214
203
  // Modifier 2 is Shift
215
204
  act(() => {
216
- stdin.sendKittySequence(`\x1b[9;2u`);
205
+ stdin.write(`\x1b[9;2u`);
217
206
  });
218
207
  expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({
219
208
  name: 'tab',
@@ -226,7 +215,7 @@ describe('KeypressContext - Kitty Protocol', () => {
226
215
  const { result } = renderHook(() => useKeypressContext(), { wrapper });
227
216
  act(() => result.current.subscribe(keyHandler));
228
217
  act(() => {
229
- stdin.sendKittySequence(`\x1b[127u`);
218
+ stdin.write(`\x1b[127u`);
230
219
  });
231
220
  expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({
232
221
  name: 'backspace',
@@ -240,7 +229,7 @@ describe('KeypressContext - Kitty Protocol', () => {
240
229
  act(() => result.current.subscribe(keyHandler));
241
230
  // Modifier 3 is Alt/Option
242
231
  act(() => {
243
- stdin.sendKittySequence(`\x1b[127;3u`);
232
+ stdin.write(`\x1b[127;3u`);
244
233
  });
245
234
  expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({
246
235
  name: 'backspace',
@@ -254,7 +243,7 @@ describe('KeypressContext - Kitty Protocol', () => {
254
243
  act(() => result.current.subscribe(keyHandler));
255
244
  // Modifier 5 is Ctrl
256
245
  act(() => {
257
- stdin.sendKittySequence(`\x1b[127;5u`);
246
+ stdin.write(`\x1b[127;5u`);
258
247
  });
259
248
  expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({
260
249
  name: 'backspace',
@@ -289,12 +278,12 @@ describe('KeypressContext - Kitty Protocol', () => {
289
278
  const { result } = renderHook(() => useKeypressContext(), {
290
279
  wrapper,
291
280
  });
292
- act(() => {
293
- result.current.subscribe(keyHandler);
294
- });
281
+ act(() => result.current.subscribe(keyHandler));
295
282
  // Simulate a bracketed paste event
296
283
  act(() => {
297
- stdin.sendPaste(pastedText);
284
+ stdin.write(PASTE_START);
285
+ stdin.write(pastedText);
286
+ stdin.write(PASTE_END);
298
287
  });
299
288
  await waitFor(() => {
300
289
  // Expect the handler to be called exactly once for the entire paste
@@ -448,12 +437,10 @@ describe('KeypressContext - Kitty Protocol', () => {
448
437
  const keyHandler = vi.fn();
449
438
  const wrapper = ({ children }) => (_jsx(KeypressProvider, { kittyProtocolEnabled: true, debugKeystrokeLogging: false, children: children }));
450
439
  const { result } = renderHook(() => useKeypressContext(), { wrapper });
451
- act(() => {
452
- result.current.subscribe(keyHandler);
453
- });
440
+ act(() => result.current.subscribe(keyHandler));
454
441
  // Send a kitty sequence
455
442
  act(() => {
456
- stdin.sendKittySequence('\x1b[27u');
443
+ stdin.write('\x1b[27u');
457
444
  });
458
445
  expect(keyHandler).toHaveBeenCalled();
459
446
  expect(consoleLogSpy).not.toHaveBeenCalledWith(expect.stringContaining('[DEBUG] Kitty'));
@@ -462,13 +449,9 @@ describe('KeypressContext - Kitty Protocol', () => {
462
449
  const keyHandler = vi.fn();
463
450
  const wrapper = ({ children }) => (_jsx(KeypressProvider, { kittyProtocolEnabled: true, debugKeystrokeLogging: true, children: children }));
464
451
  const { result } = renderHook(() => useKeypressContext(), { wrapper });
465
- act(() => {
466
- result.current.subscribe(keyHandler);
467
- });
452
+ act(() => result.current.subscribe(keyHandler));
468
453
  // Send a complete kitty sequence for escape
469
- act(() => {
470
- stdin.sendKittySequence('\x1b[27u');
471
- });
454
+ act(() => stdin.write('\x1b[27u'));
472
455
  expect(consoleLogSpy).toHaveBeenCalledWith('[DEBUG] Kitty buffer accumulating:', expect.stringContaining('"\\u001b[27u"'));
473
456
  const parsedCall = consoleLogSpy.mock.calls.find((args) => typeof args[0] === 'string' &&
474
457
  args[0].includes('[DEBUG] Kitty sequence parsed successfully'));
@@ -479,44 +462,21 @@ describe('KeypressContext - Kitty Protocol', () => {
479
462
  const keyHandler = vi.fn();
480
463
  const wrapper = ({ children }) => (_jsx(KeypressProvider, { kittyProtocolEnabled: true, debugKeystrokeLogging: true, children: children }));
481
464
  const { result } = renderHook(() => useKeypressContext(), { wrapper });
482
- act(() => {
483
- result.current.subscribe(keyHandler);
484
- });
465
+ act(() => result.current.subscribe(keyHandler));
485
466
  // Send a long sequence starting with a valid kitty prefix to trigger overflow
486
467
  const longSequence = '\x1b[1;' + '1'.repeat(100);
487
- act(() => {
488
- stdin.sendKittySequence(longSequence);
489
- });
468
+ act(() => stdin.write(longSequence));
490
469
  expect(consoleLogSpy).toHaveBeenCalledWith('[DEBUG] Kitty buffer overflow, clearing:', expect.any(String));
491
470
  });
492
471
  it('should log kitty buffer clear on Ctrl+C when debugKeystrokeLogging is true', async () => {
493
472
  const keyHandler = vi.fn();
494
473
  const wrapper = ({ children }) => (_jsx(KeypressProvider, { kittyProtocolEnabled: true, debugKeystrokeLogging: true, children: children }));
495
474
  const { result } = renderHook(() => useKeypressContext(), { wrapper });
496
- act(() => {
497
- result.current.subscribe(keyHandler);
498
- });
499
- // Send incomplete kitty sequence
500
- act(() => {
501
- stdin.pressKey({
502
- name: undefined,
503
- ctrl: false,
504
- meta: false,
505
- shift: false,
506
- sequence: '\x1b[1',
507
- });
508
- });
475
+ act(() => result.current.subscribe(keyHandler));
476
+ act(() => stdin.write(INCOMPLETE_KITTY_SEQUENCE));
509
477
  // Send Ctrl+C
510
- act(() => {
511
- stdin.pressKey({
512
- name: 'c',
513
- ctrl: true,
514
- meta: false,
515
- shift: false,
516
- sequence: '\x03',
517
- });
518
- });
519
- expect(consoleLogSpy).toHaveBeenCalledWith('[DEBUG] Kitty buffer cleared on Ctrl+C:', '\x1b[1');
478
+ act(() => stdin.write('\x03'));
479
+ expect(consoleLogSpy).toHaveBeenCalledWith('[DEBUG] Kitty buffer cleared on Ctrl+C:', INCOMPLETE_KITTY_SEQUENCE);
520
480
  // Verify Ctrl+C was handled
521
481
  expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({
522
482
  name: 'c',
@@ -527,24 +487,13 @@ describe('KeypressContext - Kitty Protocol', () => {
527
487
  const keyHandler = vi.fn();
528
488
  const wrapper = ({ children }) => (_jsx(KeypressProvider, { kittyProtocolEnabled: true, debugKeystrokeLogging: true, children: children }));
529
489
  const { result } = renderHook(() => useKeypressContext(), { wrapper });
530
- act(() => {
531
- result.current.subscribe(keyHandler);
532
- });
490
+ act(() => result.current.subscribe(keyHandler));
533
491
  // Send incomplete kitty sequence
534
- const sequence = '\x1b[12';
535
- act(() => {
536
- stdin.pressKey({
537
- name: undefined,
538
- ctrl: false,
539
- meta: false,
540
- shift: false,
541
- sequence,
542
- });
543
- });
492
+ act(() => stdin.write(INCOMPLETE_KITTY_SEQUENCE));
544
493
  // Verify debug logging for accumulation
545
- expect(consoleLogSpy).toHaveBeenCalledWith('[DEBUG] Kitty buffer accumulating:', JSON.stringify(sequence));
494
+ expect(consoleLogSpy).toHaveBeenCalledWith('[DEBUG] Kitty buffer accumulating:', JSON.stringify(INCOMPLETE_KITTY_SEQUENCE));
546
495
  // Verify warning for char codes
547
- expect(consoleWarnSpy).toHaveBeenCalledWith('Kitty sequence buffer has content:', JSON.stringify(sequence));
496
+ expect(consoleWarnSpy).toHaveBeenCalledWith('Kitty sequence buffer has content:', JSON.stringify(INCOMPLETE_KITTY_SEQUENCE));
548
497
  });
549
498
  });
550
499
  describe('Parameterized functional keys', () => {
@@ -597,29 +546,17 @@ describe('KeypressContext - Kitty Protocol', () => {
597
546
  const keyHandler = vi.fn();
598
547
  const { result } = renderHook(() => useKeypressContext(), { wrapper });
599
548
  act(() => result.current.subscribe(keyHandler));
600
- act(() => stdin.sendKittySequence(sequence));
549
+ act(() => stdin.write(sequence));
601
550
  expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining(expected));
602
551
  });
603
552
  });
604
- describe('Shift+Tab forms', () => {
605
- it.each([
606
- { sequence: `\x1b[Z`, description: 'legacy reverse Tab' },
607
- { sequence: `\x1b[1;2Z`, description: 'parameterized reverse Tab' },
608
- ])('should recognize $description "$sequence" as Shift+Tab', ({ sequence }) => {
609
- const keyHandler = vi.fn();
610
- const { result } = renderHook(() => useKeypressContext(), { wrapper });
611
- act(() => result.current.subscribe(keyHandler));
612
- act(() => stdin.sendKittySequence(sequence));
613
- expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({ name: 'tab', shift: true }));
614
- });
615
- });
616
553
  describe('Double-tap and batching', () => {
617
554
  it('should emit two delete events for double-tap CSI[3~', async () => {
618
555
  const keyHandler = vi.fn();
619
556
  const { result } = renderHook(() => useKeypressContext(), { wrapper });
620
557
  act(() => result.current.subscribe(keyHandler));
621
- act(() => stdin.sendKittySequence(`\x1b[3~`));
622
- act(() => stdin.sendKittySequence(`\x1b[3~`));
558
+ act(() => stdin.write(`\x1b[3~`));
559
+ act(() => stdin.write(`\x1b[3~`));
623
560
  expect(keyHandler).toHaveBeenNthCalledWith(1, expect.objectContaining({ name: 'delete' }));
624
561
  expect(keyHandler).toHaveBeenNthCalledWith(2, expect.objectContaining({ name: 'delete' }));
625
562
  });
@@ -627,7 +564,7 @@ describe('KeypressContext - Kitty Protocol', () => {
627
564
  const keyHandler = vi.fn();
628
565
  const { result } = renderHook(() => useKeypressContext(), { wrapper });
629
566
  act(() => result.current.subscribe(keyHandler));
630
- act(() => stdin.sendKittySequence(`\x1b[3~\x1b[5~`));
567
+ act(() => stdin.write(`\x1b[3~\x1b[5~`));
631
568
  expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({ name: 'delete' }));
632
569
  expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({ name: 'pageup' }));
633
570
  });
@@ -638,15 +575,9 @@ describe('KeypressContext - Kitty Protocol', () => {
638
575
  // Incomplete ESC sequence then a complete Delete
639
576
  act(() => {
640
577
  // Provide an incomplete ESC sequence chunk with a real ESC character
641
- stdin.pressKey({
642
- name: undefined,
643
- ctrl: false,
644
- meta: false,
645
- shift: false,
646
- sequence: '\x1b[1;',
647
- });
578
+ stdin.write('\x1b[1;');
648
579
  });
649
- act(() => stdin.sendKittySequence(`\x1b[3~`));
580
+ act(() => stdin.write(`\x1b[3~`));
650
581
  expect(keyHandler).toHaveBeenCalledTimes(1);
651
582
  expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({ name: 'delete' }));
652
583
  });
@@ -672,37 +603,15 @@ describe('Drag and Drop Handling', () => {
672
603
  it('should start collecting when single quote arrives and not broadcast immediately', async () => {
673
604
  const keyHandler = vi.fn();
674
605
  const { result } = renderHook(() => useKeypressContext(), { wrapper });
675
- act(() => {
676
- result.current.subscribe(keyHandler);
677
- });
678
- act(() => {
679
- stdin.pressKey({
680
- name: undefined,
681
- ctrl: false,
682
- meta: false,
683
- shift: false,
684
- paste: false,
685
- sequence: SINGLE_QUOTE,
686
- });
687
- });
606
+ act(() => result.current.subscribe(keyHandler));
607
+ act(() => stdin.write(SINGLE_QUOTE));
688
608
  expect(keyHandler).not.toHaveBeenCalled();
689
609
  });
690
610
  it('should start collecting when double quote arrives and not broadcast immediately', async () => {
691
611
  const keyHandler = vi.fn();
692
612
  const { result } = renderHook(() => useKeypressContext(), { wrapper });
693
- act(() => {
694
- result.current.subscribe(keyHandler);
695
- });
696
- act(() => {
697
- stdin.pressKey({
698
- name: undefined,
699
- ctrl: false,
700
- meta: false,
701
- shift: false,
702
- paste: false,
703
- sequence: DOUBLE_QUOTE,
704
- });
705
- });
613
+ act(() => result.current.subscribe(keyHandler));
614
+ act(() => stdin.write(DOUBLE_QUOTE));
706
615
  expect(keyHandler).not.toHaveBeenCalled();
707
616
  });
708
617
  });
@@ -710,31 +619,11 @@ describe('Drag and Drop Handling', () => {
710
619
  it('should collect single character inputs during drag mode', async () => {
711
620
  const keyHandler = vi.fn();
712
621
  const { result } = renderHook(() => useKeypressContext(), { wrapper });
713
- act(() => {
714
- result.current.subscribe(keyHandler);
715
- });
622
+ act(() => result.current.subscribe(keyHandler));
716
623
  // Start by single quote
717
- act(() => {
718
- stdin.pressKey({
719
- name: undefined,
720
- ctrl: false,
721
- meta: false,
722
- shift: false,
723
- paste: false,
724
- sequence: SINGLE_QUOTE,
725
- });
726
- });
624
+ act(() => stdin.write(SINGLE_QUOTE));
727
625
  // Send single character
728
- act(() => {
729
- stdin.pressKey({
730
- name: undefined,
731
- ctrl: false,
732
- meta: false,
733
- shift: false,
734
- paste: false,
735
- sequence: 'a',
736
- });
737
- });
626
+ act(() => stdin.write('a'));
738
627
  // Character should not be immediately broadcast
739
628
  expect(keyHandler).not.toHaveBeenCalled();
740
629
  // Fast-forward to completion timeout
@@ -751,61 +640,14 @@ describe('Drag and Drop Handling', () => {
751
640
  it('should collect multiple characters and complete on timeout', async () => {
752
641
  const keyHandler = vi.fn();
753
642
  const { result } = renderHook(() => useKeypressContext(), { wrapper });
754
- act(() => {
755
- result.current.subscribe(keyHandler);
756
- });
643
+ act(() => result.current.subscribe(keyHandler));
757
644
  // Start by single quote
758
- act(() => {
759
- stdin.pressKey({
760
- name: undefined,
761
- ctrl: false,
762
- meta: false,
763
- shift: false,
764
- paste: false,
765
- sequence: SINGLE_QUOTE,
766
- });
767
- });
645
+ act(() => stdin.write(SINGLE_QUOTE));
768
646
  // Send multiple characters
769
- act(() => {
770
- stdin.pressKey({
771
- name: undefined,
772
- ctrl: false,
773
- meta: false,
774
- shift: false,
775
- paste: false,
776
- sequence: 'p',
777
- });
778
- });
779
- act(() => {
780
- stdin.pressKey({
781
- name: undefined,
782
- ctrl: false,
783
- meta: false,
784
- shift: false,
785
- paste: false,
786
- sequence: 'a',
787
- });
788
- });
789
- act(() => {
790
- stdin.pressKey({
791
- name: undefined,
792
- ctrl: false,
793
- meta: false,
794
- shift: false,
795
- paste: false,
796
- sequence: 't',
797
- });
798
- });
799
- act(() => {
800
- stdin.pressKey({
801
- name: undefined,
802
- ctrl: false,
803
- meta: false,
804
- shift: false,
805
- paste: false,
806
- sequence: 'h',
807
- });
808
- });
647
+ act(() => stdin.write('p'));
648
+ act(() => stdin.write('a'));
649
+ act(() => stdin.write('t'));
650
+ act(() => stdin.write('h'));
809
651
  // Characters should not be immediately broadcast
810
652
  expect(keyHandler).not.toHaveBeenCalled();
811
653
  // Fast-forward to completion timeout
@@ -839,20 +681,19 @@ describe('Kitty Sequence Parsing', () => {
839
681
  });
840
682
  // Terminals to test
841
683
  const terminals = ['iTerm2', 'Ghostty', 'MacTerminal', 'VSCodeTerminal'];
842
- // Key mappings: letter -> [keycode, accented character, shouldHaveMeta]
843
- // Note: µ (mu) is sent with meta:false on iTerm2/VSCode
684
+ // Key mappings: letter -> [keycode, accented character]
844
685
  const keys = {
845
- a: [97, 'å', true],
846
- o: [111, 'ø', true],
847
- m: [109, 'µ', false],
686
+ a: [97, 'å'],
687
+ o: [111, 'ø'],
688
+ m: [109, 'µ'],
848
689
  };
849
- it.each(terminals.flatMap((terminal) => Object.entries(keys).map(([key, [keycode, accentedChar, shouldHaveMeta]]) => {
690
+ it.each(terminals.flatMap((terminal) => Object.entries(keys).map(([key, [keycode, accentedChar]]) => {
850
691
  if (terminal === 'Ghostty') {
851
692
  // Ghostty uses kitty protocol sequences
852
693
  return {
853
694
  terminal,
854
695
  key,
855
- kittySequence: `\x1b[${keycode};3u`,
696
+ chunk: `\x1b[${keycode};3u`,
856
697
  expected: {
857
698
  name: key,
858
699
  ctrl: false,
@@ -869,14 +710,7 @@ describe('Kitty Sequence Parsing', () => {
869
710
  terminal,
870
711
  key,
871
712
  kitty: false,
872
- input: {
873
- sequence: `\x1b${key}`,
874
- name: key,
875
- ctrl: false,
876
- meta: true,
877
- shift: false,
878
- paste: false,
879
- },
713
+ chunk: `\x1b${key}`,
880
714
  expected: {
881
715
  sequence: `\x1b${key}`,
882
716
  name: key,
@@ -889,18 +723,12 @@ describe('Kitty Sequence Parsing', () => {
889
723
  }
890
724
  else {
891
725
  // iTerm2 and VSCode send accented characters (å, ø, µ)
892
- // Note: µ comes with meta:false but gets converted to m with meta:true
726
+ // Note: µ (mu) is sent with meta:false on iTerm2/VSCode but
727
+ // gets converted to m with meta:true
893
728
  return {
894
729
  terminal,
895
730
  key,
896
- input: {
897
- name: key,
898
- ctrl: false,
899
- meta: shouldHaveMeta,
900
- shift: false,
901
- paste: false,
902
- sequence: accentedChar,
903
- },
731
+ chunk: accentedChar,
904
732
  expected: {
905
733
  name: key,
906
734
  ctrl: false,
@@ -911,19 +739,14 @@ describe('Kitty Sequence Parsing', () => {
911
739
  },
912
740
  };
913
741
  }
914
- })))('should handle Alt+$key in $terminal', ({ kittySequence, input, expected, kitty = true, }) => {
742
+ })))('should handle Alt+$key in $terminal', ({ chunk, expected, kitty = true, }) => {
915
743
  const keyHandler = vi.fn();
916
744
  const testWrapper = ({ children }) => (_jsx(KeypressProvider, { kittyProtocolEnabled: kitty, children: children }));
917
745
  const { result } = renderHook(() => useKeypressContext(), {
918
746
  wrapper: testWrapper,
919
747
  });
920
748
  act(() => result.current.subscribe(keyHandler));
921
- if (kittySequence) {
922
- act(() => stdin.sendKittySequence(kittySequence));
923
- }
924
- else if (input) {
925
- act(() => stdin.pressKey(input));
926
- }
749
+ act(() => stdin.write(chunk));
927
750
  expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining(expected));
928
751
  });
929
752
  describe('Backslash key handling', () => {
@@ -937,14 +760,7 @@ describe('Kitty Sequence Parsing', () => {
937
760
  const keyHandler = vi.fn();
938
761
  const { result } = renderHook(() => useKeypressContext(), { wrapper });
939
762
  act(() => result.current.subscribe(keyHandler));
940
- act(() => stdin.pressKey({
941
- name: undefined,
942
- ctrl: false,
943
- meta: false,
944
- shift: false,
945
- paste: false,
946
- sequence: '\\',
947
- }));
763
+ act(() => stdin.write('\\'));
948
764
  // Advance timers to trigger the backslash timeout
949
765
  act(() => {
950
766
  vi.runAllTimers();
@@ -958,81 +774,43 @@ describe('Kitty Sequence Parsing', () => {
958
774
  it('should timeout and flush incomplete kitty sequences after 50ms', async () => {
959
775
  const keyHandler = vi.fn();
960
776
  const { result } = renderHook(() => useKeypressContext(), { wrapper });
961
- act(() => {
962
- result.current.subscribe(keyHandler);
963
- });
964
- // Send incomplete kitty sequence
965
- act(() => {
966
- stdin.pressKey({
967
- name: undefined,
968
- ctrl: false,
969
- meta: false,
970
- shift: false,
971
- paste: false,
972
- sequence: '\x1b[1;',
973
- });
974
- });
777
+ act(() => result.current.subscribe(keyHandler));
778
+ act(() => stdin.write(INCOMPLETE_KITTY_SEQUENCE));
975
779
  // Should not broadcast immediately
976
780
  expect(keyHandler).not.toHaveBeenCalled();
977
781
  // Advance time just before timeout
978
- act(() => {
979
- vi.advanceTimersByTime(KITTY_SEQUENCE_TIMEOUT_MS - 5);
980
- });
782
+ act(() => vi.advanceTimersByTime(KITTY_SEQUENCE_TIMEOUT_MS - 5));
981
783
  // Still shouldn't broadcast
982
784
  expect(keyHandler).not.toHaveBeenCalled();
983
785
  // Advance past timeout
984
- act(() => {
985
- vi.advanceTimersByTime(10);
986
- });
786
+ act(() => vi.advanceTimersByTime(10));
987
787
  // Should now broadcast the incomplete sequence as regular input
988
788
  expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({
989
789
  name: '',
990
- sequence: '\x1b[1;',
790
+ sequence: INCOMPLETE_KITTY_SEQUENCE,
991
791
  paste: false,
992
792
  }));
993
793
  });
994
794
  it('should immediately flush non-kitty CSI sequences', async () => {
995
795
  const keyHandler = vi.fn();
996
796
  const { result } = renderHook(() => useKeypressContext(), { wrapper });
997
- act(() => {
998
- result.current.subscribe(keyHandler);
999
- });
797
+ act(() => result.current.subscribe(keyHandler));
1000
798
  // Send a CSI sequence that doesn't match kitty patterns
1001
799
  // ESC[m is SGR reset, not a kitty sequence
1002
- act(() => {
1003
- stdin.pressKey({
1004
- name: undefined,
1005
- ctrl: false,
1006
- meta: false,
1007
- shift: false,
1008
- paste: false,
1009
- sequence: '\x1b[m',
1010
- });
1011
- });
800
+ act(() => stdin.write('\x1b[m'));
1012
801
  // Should broadcast immediately as it's not a valid kitty pattern
1013
802
  expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({
1014
- name: undefined,
1015
- sequence: '\x1b[m',
803
+ name: 'undefined',
804
+ code: '[m',
1016
805
  paste: false,
1017
806
  }));
1018
807
  });
1019
808
  it('should parse valid kitty sequences immediately when complete', async () => {
1020
809
  const keyHandler = vi.fn();
1021
810
  const { result } = renderHook(() => useKeypressContext(), { wrapper });
1022
- act(() => {
1023
- result.current.subscribe(keyHandler);
1024
- });
811
+ act(() => result.current.subscribe(keyHandler));
1025
812
  // Send complete kitty sequence for Ctrl+A
1026
- act(() => {
1027
- stdin.pressKey({
1028
- name: undefined,
1029
- ctrl: false,
1030
- meta: false,
1031
- shift: false,
1032
- paste: false,
1033
- sequence: '\x1b[97;5u',
1034
- });
1035
- });
813
+ act(() => stdin.write('\x1b[97;5u'));
1036
814
  // Should parse and broadcast immediately
1037
815
  expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({
1038
816
  name: 'a',
@@ -1043,20 +821,9 @@ describe('Kitty Sequence Parsing', () => {
1043
821
  it('should handle batched kitty sequences correctly', async () => {
1044
822
  const keyHandler = vi.fn();
1045
823
  const { result } = renderHook(() => useKeypressContext(), { wrapper });
1046
- act(() => {
1047
- result.current.subscribe(keyHandler);
1048
- });
1049
- // Send multiple kitty sequences at once
1050
- act(() => {
1051
- stdin.pressKey({
1052
- name: undefined,
1053
- ctrl: false,
1054
- meta: false,
1055
- shift: false,
1056
- paste: false,
1057
- sequence: '\x1b[97;5u\x1b[98;5u', // Ctrl+a followed by Ctrl+b
1058
- });
1059
- });
824
+ act(() => result.current.subscribe(keyHandler));
825
+ // Send Ctrl+a followed by Ctrl+b
826
+ act(() => stdin.write('\x1b[97;5u\x1b[98;5u'));
1060
827
  // Should parse both sequences
1061
828
  expect(keyHandler).toHaveBeenCalledTimes(2);
1062
829
  expect(keyHandler).toHaveBeenNthCalledWith(1, expect.objectContaining({
@@ -1073,35 +840,12 @@ describe('Kitty Sequence Parsing', () => {
1073
840
  it('should clear kitty buffer and timeout on Ctrl+C', async () => {
1074
841
  const keyHandler = vi.fn();
1075
842
  const { result } = renderHook(() => useKeypressContext(), { wrapper });
1076
- act(() => {
1077
- result.current.subscribe(keyHandler);
1078
- });
1079
- // Send incomplete kitty sequence
1080
- act(() => {
1081
- stdin.pressKey({
1082
- name: undefined,
1083
- ctrl: false,
1084
- meta: false,
1085
- shift: false,
1086
- paste: false,
1087
- sequence: '\x1b[1;',
1088
- });
1089
- });
843
+ act(() => result.current.subscribe(keyHandler));
844
+ act(() => stdin.write(INCOMPLETE_KITTY_SEQUENCE));
1090
845
  // Press Ctrl+C
1091
- act(() => {
1092
- stdin.pressKey({
1093
- name: 'c',
1094
- ctrl: true,
1095
- meta: false,
1096
- shift: false,
1097
- paste: false,
1098
- sequence: '\x03',
1099
- });
1100
- });
846
+ act(() => stdin.write('\x03'));
1101
847
  // Advance past timeout
1102
- act(() => {
1103
- vi.advanceTimersByTime(KITTY_SEQUENCE_TIMEOUT_MS + 10);
1104
- });
848
+ act(() => vi.advanceTimersByTime(KITTY_SEQUENCE_TIMEOUT_MS + 10));
1105
849
  // Should only have received Ctrl+C, not the incomplete sequence
1106
850
  expect(keyHandler).toHaveBeenCalledTimes(1);
1107
851
  expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({
@@ -1112,20 +856,10 @@ describe('Kitty Sequence Parsing', () => {
1112
856
  it('should handle mixed valid and invalid sequences', async () => {
1113
857
  const keyHandler = vi.fn();
1114
858
  const { result } = renderHook(() => useKeypressContext(), { wrapper });
1115
- act(() => {
1116
- result.current.subscribe(keyHandler);
1117
- });
859
+ act(() => result.current.subscribe(keyHandler));
1118
860
  // Send valid kitty sequence followed by invalid CSI
1119
- act(() => {
1120
- stdin.pressKey({
1121
- name: undefined,
1122
- ctrl: false,
1123
- meta: false,
1124
- shift: false,
1125
- paste: false,
1126
- sequence: '\x1b[13u\x1b[!', // Valid enter, then invalid sequence
1127
- });
1128
- });
861
+ // Valid enter, then invalid sequence
862
+ act(() => stdin.write('\x1b[13u\x1b[!'));
1129
863
  // Should parse valid sequence and flush invalid immediately
1130
864
  expect(keyHandler).toHaveBeenCalledTimes(2);
1131
865
  expect(keyHandler).toHaveBeenNthCalledWith(1, expect.objectContaining({
@@ -1133,8 +867,8 @@ describe('Kitty Sequence Parsing', () => {
1133
867
  kittyProtocol: true,
1134
868
  }));
1135
869
  expect(keyHandler).toHaveBeenNthCalledWith(2, expect.objectContaining({
1136
- name: '',
1137
- sequence: '\x1b[!',
870
+ name: 'undefined',
871
+ code: '[!',
1138
872
  }));
1139
873
  });
1140
874
  it('should not buffer sequences when kitty protocol is disabled', async () => {
@@ -1142,20 +876,9 @@ describe('Kitty Sequence Parsing', () => {
1142
876
  const { result } = renderHook(() => useKeypressContext(), {
1143
877
  wrapper: ({ children }) => wrapper({ children, kittyProtocolEnabled: false }),
1144
878
  });
1145
- act(() => {
1146
- result.current.subscribe(keyHandler);
1147
- });
879
+ act(() => result.current.subscribe(keyHandler));
1148
880
  // Send what would be a kitty sequence
1149
- act(() => {
1150
- stdin.pressKey({
1151
- name: undefined,
1152
- ctrl: false,
1153
- meta: false,
1154
- shift: false,
1155
- paste: false,
1156
- sequence: '\x1b[13u',
1157
- });
1158
- });
881
+ act(() => stdin.write('\x1b[13u'));
1159
882
  // Should pass through without parsing
1160
883
  expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({
1161
884
  sequence: '\x1b[13u',
@@ -1191,113 +914,56 @@ describe('Kitty Sequence Parsing', () => {
1191
914
  it('should reset timeout when new input arrives', async () => {
1192
915
  const keyHandler = vi.fn();
1193
916
  const { result } = renderHook(() => useKeypressContext(), { wrapper });
1194
- act(() => {
1195
- result.current.subscribe(keyHandler);
1196
- });
917
+ act(() => result.current.subscribe(keyHandler));
1197
918
  // Start incomplete sequence
1198
- act(() => {
1199
- stdin.pressKey({
1200
- name: undefined,
1201
- ctrl: false,
1202
- meta: false,
1203
- shift: false,
1204
- paste: false,
1205
- sequence: '\x1b[1',
1206
- });
1207
- });
919
+ act(() => stdin.write('\x1b[97;13'));
1208
920
  // Advance time partway
1209
- act(() => {
1210
- vi.advanceTimersByTime(30);
1211
- });
921
+ act(() => vi.advanceTimersByTime(30));
1212
922
  // Add more to sequence
1213
- act(() => {
1214
- stdin.pressKey({
1215
- name: undefined,
1216
- ctrl: false,
1217
- meta: false,
1218
- shift: false,
1219
- paste: false,
1220
- sequence: '3',
1221
- });
1222
- });
923
+ act(() => stdin.write('5'));
1223
924
  // Advance time from the first timeout point
1224
- act(() => {
1225
- vi.advanceTimersByTime(25);
1226
- });
925
+ act(() => vi.advanceTimersByTime(25));
1227
926
  // Should not have timed out yet (timeout restarted)
1228
927
  expect(keyHandler).not.toHaveBeenCalled();
1229
928
  // Complete the sequence
1230
- act(() => {
1231
- stdin.pressKey({
1232
- name: undefined,
1233
- ctrl: false,
1234
- meta: false,
1235
- shift: false,
1236
- paste: false,
1237
- sequence: 'u',
1238
- });
1239
- });
929
+ act(() => stdin.write('u'));
1240
930
  // Should now parse as complete enter key
1241
931
  expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({
1242
- name: 'return',
932
+ name: 'a',
1243
933
  kittyProtocol: true,
1244
934
  }));
1245
935
  });
1246
936
  it('should flush incomplete kitty sequence on FOCUS_IN event', async () => {
1247
937
  const keyHandler = vi.fn();
1248
938
  const { result } = renderHook(() => useKeypressContext(), { wrapper });
1249
- act(() => {
1250
- result.current.subscribe(keyHandler);
1251
- });
1252
- // Send incomplete kitty sequence
1253
- act(() => {
1254
- stdin.pressKey({
1255
- sequence: '\x1b[1;',
1256
- });
1257
- });
939
+ act(() => result.current.subscribe(keyHandler));
940
+ act(() => stdin.write(INCOMPLETE_KITTY_SEQUENCE));
1258
941
  // Incomplete sequence should be buffered, not broadcast
1259
942
  expect(keyHandler).not.toHaveBeenCalled();
1260
943
  // Send FOCUS_IN event
1261
- const FOCUS_IN = '\x1b[I';
1262
- act(() => {
1263
- stdin.pressKey({
1264
- sequence: FOCUS_IN,
1265
- });
1266
- });
944
+ act(() => stdin.write('\x1b[I'));
1267
945
  // The buffered sequence should be flushed
1268
946
  expect(keyHandler).toHaveBeenCalledTimes(1);
1269
947
  expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({
1270
948
  name: '',
1271
- sequence: '\x1b[1;',
949
+ sequence: INCOMPLETE_KITTY_SEQUENCE,
1272
950
  paste: false,
1273
951
  }));
1274
952
  });
1275
953
  it('should flush incomplete kitty sequence on FOCUS_OUT event', async () => {
1276
954
  const keyHandler = vi.fn();
1277
955
  const { result } = renderHook(() => useKeypressContext(), { wrapper });
1278
- act(() => {
1279
- result.current.subscribe(keyHandler);
1280
- });
1281
- // Send incomplete kitty sequence
1282
- act(() => {
1283
- stdin.pressKey({
1284
- sequence: '\x1b[1;',
1285
- });
1286
- });
956
+ act(() => result.current.subscribe(keyHandler));
957
+ act(() => stdin.write(INCOMPLETE_KITTY_SEQUENCE));
1287
958
  // Incomplete sequence should be buffered, not broadcast
1288
959
  expect(keyHandler).not.toHaveBeenCalled();
1289
960
  // Send FOCUS_OUT event
1290
- const FOCUS_OUT = '\x1b[O';
1291
- act(() => {
1292
- stdin.pressKey({
1293
- sequence: FOCUS_OUT,
1294
- });
1295
- });
961
+ act(() => stdin.write('\x1b[O'));
1296
962
  // The buffered sequence should be flushed
1297
963
  expect(keyHandler).toHaveBeenCalledTimes(1);
1298
964
  expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({
1299
965
  name: '',
1300
- sequence: '\x1b[1;',
966
+ sequence: INCOMPLETE_KITTY_SEQUENCE,
1301
967
  paste: false,
1302
968
  }));
1303
969
  });
@@ -1305,39 +971,27 @@ describe('Kitty Sequence Parsing', () => {
1305
971
  vi.useFakeTimers();
1306
972
  const keyHandler = vi.fn();
1307
973
  const { result } = renderHook(() => useKeypressContext(), { wrapper });
1308
- act(() => {
1309
- result.current.subscribe(keyHandler);
1310
- });
1311
- // Send incomplete kitty sequence
1312
- act(() => {
1313
- stdin.pressKey({
1314
- sequence: '\x1b[1;',
1315
- });
1316
- });
974
+ act(() => result.current.subscribe(keyHandler));
975
+ act(() => stdin.write(INCOMPLETE_KITTY_SEQUENCE));
1317
976
  // Incomplete sequence should be buffered, not broadcast
1318
977
  expect(keyHandler).not.toHaveBeenCalled();
1319
978
  // Send paste start sequence
1320
- const PASTE_MODE_PREFIX = `\x1b[200~`;
1321
- act(() => {
1322
- stdin.emit('data', Buffer.from(PASTE_MODE_PREFIX));
1323
- });
979
+ act(() => stdin.write(`\x1b[200~`));
1324
980
  // The buffered sequence should be flushed
1325
981
  expect(keyHandler).toHaveBeenCalledTimes(1);
1326
982
  expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({
1327
983
  name: '',
1328
- sequence: '\x1b[1;',
984
+ sequence: INCOMPLETE_KITTY_SEQUENCE,
1329
985
  paste: false,
1330
986
  }));
1331
987
  // Now send some paste content and end paste to make sure paste still works
1332
988
  const pastedText = 'hello';
1333
989
  const PASTE_MODE_SUFFIX = `\x1b[201~`;
1334
990
  act(() => {
1335
- stdin.emit('data', Buffer.from(pastedText));
1336
- stdin.emit('data', Buffer.from(PASTE_MODE_SUFFIX));
1337
- });
1338
- act(() => {
1339
- vi.runAllTimers();
991
+ stdin.write(pastedText);
992
+ stdin.write(PASTE_MODE_SUFFIX);
1340
993
  });
994
+ act(() => vi.runAllTimers());
1341
995
  // The paste event should be broadcast
1342
996
  expect(keyHandler).toHaveBeenCalledTimes(2);
1343
997
  expect(keyHandler).toHaveBeenNthCalledWith(2, expect.objectContaining({