@google/gemini-cli 0.12.0-nightly.20251023.c4c0c0d1 → 0.12.0-preview.0
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/README.md +7 -5
- package/dist/package.json +3 -3
- package/dist/src/commands/extensions/disable.d.ts +1 -1
- package/dist/src/commands/extensions/disable.js +5 -4
- package/dist/src/commands/extensions/disable.js.map +1 -1
- package/dist/src/commands/extensions/enable.d.ts +1 -1
- package/dist/src/commands/extensions/enable.js +3 -2
- package/dist/src/commands/extensions/enable.js.map +1 -1
- package/dist/src/commands/extensions/install.js +2 -1
- package/dist/src/commands/extensions/install.js.map +1 -1
- package/dist/src/commands/extensions/install.test.js +1 -0
- package/dist/src/commands/extensions/install.test.js.map +1 -1
- package/dist/src/commands/extensions/link.js +2 -1
- package/dist/src/commands/extensions/link.js.map +1 -1
- package/dist/src/commands/extensions/list.js +2 -2
- package/dist/src/commands/extensions/list.js.map +1 -1
- package/dist/src/commands/extensions/uninstall.js +2 -1
- package/dist/src/commands/extensions/uninstall.js.map +1 -1
- package/dist/src/commands/extensions/update.js +2 -2
- package/dist/src/commands/extensions/update.js.map +1 -1
- package/dist/src/commands/mcp/list.js +2 -2
- package/dist/src/commands/mcp/list.js.map +1 -1
- package/dist/src/config/config.d.ts +6 -3
- package/dist/src/config/config.js +56 -11
- package/dist/src/config/config.js.map +1 -1
- package/dist/src/config/config.test.js +208 -175
- package/dist/src/config/config.test.js.map +1 -1
- package/dist/src/config/extension-manager.d.ts +23 -10
- package/dist/src/config/extension-manager.js +90 -64
- package/dist/src/config/extension-manager.js.map +1 -1
- package/dist/src/config/extension.test.js +183 -76
- package/dist/src/config/extension.test.js.map +1 -1
- package/dist/src/config/extensions/extensionEnablement.d.ts +1 -1
- package/dist/src/config/extensions/extensionEnablement.js +3 -2
- package/dist/src/config/extensions/extensionEnablement.js.map +1 -1
- package/dist/src/config/extensions/extensionEnablement.test.js +10 -10
- package/dist/src/config/extensions/extensionEnablement.test.js.map +1 -1
- package/dist/src/config/extensions/extensionSettings.d.ts +3 -3
- package/dist/src/config/extensions/extensionSettings.js +74 -24
- package/dist/src/config/extensions/extensionSettings.js.map +1 -1
- package/dist/src/config/extensions/extensionSettings.test.js +145 -24
- package/dist/src/config/extensions/extensionSettings.test.js.map +1 -1
- package/dist/src/config/extensions/github.js +3 -3
- package/dist/src/config/extensions/github.js.map +1 -1
- package/dist/src/config/extensions/github.test.js +1 -1
- package/dist/src/config/extensions/github.test.js.map +1 -1
- package/dist/src/config/extensions/github_fetch.d.ts +1 -1
- package/dist/src/config/extensions/github_fetch.js +13 -1
- package/dist/src/config/extensions/github_fetch.js.map +1 -1
- package/dist/src/config/extensions/github_fetch.test.d.ts +6 -0
- package/dist/src/config/extensions/github_fetch.test.js +169 -0
- package/dist/src/config/extensions/github_fetch.test.js.map +1 -0
- package/dist/src/config/extensions/update.js +7 -6
- package/dist/src/config/extensions/update.js.map +1 -1
- package/dist/src/config/extensions/update.test.js +54 -30
- package/dist/src/config/extensions/update.test.js.map +1 -1
- package/dist/src/config/keyBindings.js +1 -1
- package/dist/src/config/keyBindings.js.map +1 -1
- package/dist/src/config/policies/read-only.toml +56 -0
- package/dist/src/config/policies/write.toml +63 -0
- package/dist/src/config/policies/yolo.toml +31 -0
- package/dist/src/config/policy-engine.integration.test.js +41 -38
- package/dist/src/config/policy-engine.integration.test.js.map +1 -1
- package/dist/src/config/policy-toml-loader.d.ts +46 -0
- package/dist/src/config/policy-toml-loader.js +314 -0
- package/dist/src/config/policy-toml-loader.js.map +1 -0
- package/dist/src/config/policy-toml-loader.test.d.ts +6 -0
- package/dist/src/config/policy-toml-loader.test.js +626 -0
- package/dist/src/config/policy-toml-loader.test.js.map +1 -0
- package/dist/src/config/policy.d.ts +9 -2
- package/dist/src/config/policy.js +139 -110
- package/dist/src/config/policy.js.map +1 -1
- package/dist/src/config/policy.test.js +780 -82
- package/dist/src/config/policy.test.js.map +1 -1
- package/dist/src/config/settings.test.js +6 -6
- package/dist/src/config/settings.test.js.map +1 -1
- package/dist/src/core/initializer.js +2 -1
- package/dist/src/core/initializer.js.map +1 -1
- package/dist/src/gemini.js +6 -17
- package/dist/src/gemini.js.map +1 -1
- package/dist/src/gemini.test.js +27 -2
- package/dist/src/gemini.test.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/nonInteractiveCli.js +16 -4
- package/dist/src/nonInteractiveCli.js.map +1 -1
- package/dist/src/nonInteractiveCli.test.js +67 -12
- package/dist/src/nonInteractiveCli.test.js.map +1 -1
- package/dist/src/test-utils/render.d.ts +12 -0
- package/dist/src/test-utils/render.js +28 -1
- package/dist/src/test-utils/render.js.map +1 -1
- package/dist/src/test-utils/render.test.d.ts +6 -0
- package/dist/src/test-utils/render.test.js +54 -0
- package/dist/src/test-utils/render.test.js.map +1 -0
- package/dist/src/ui/AppContainer.js +29 -23
- package/dist/src/ui/AppContainer.js.map +1 -1
- package/dist/src/ui/AppContainer.test.js +8 -0
- package/dist/src/ui/AppContainer.test.js.map +1 -1
- package/dist/src/ui/commands/directoryCommand.js +1 -1
- package/dist/src/ui/commands/directoryCommand.js.map +1 -1
- package/dist/src/ui/commands/extensionsCommand.js +45 -1
- package/dist/src/ui/commands/extensionsCommand.js.map +1 -1
- package/dist/src/ui/commands/extensionsCommand.test.js +64 -1
- package/dist/src/ui/commands/extensionsCommand.test.js.map +1 -1
- package/dist/src/ui/commands/memoryCommand.js +1 -1
- package/dist/src/ui/commands/memoryCommand.js.map +1 -1
- package/dist/src/ui/commands/memoryCommand.test.js +3 -1
- package/dist/src/ui/commands/memoryCommand.test.js.map +1 -1
- package/dist/src/ui/components/ConsoleSummaryDisplay.js +1 -1
- package/dist/src/ui/components/ConsoleSummaryDisplay.js.map +1 -1
- package/dist/src/ui/components/DetailedMessagesDisplay.js +1 -1
- package/dist/src/ui/components/DetailedMessagesDisplay.js.map +1 -1
- package/dist/src/ui/components/FolderTrustDialog.test.js +4 -4
- package/dist/src/ui/components/FolderTrustDialog.test.js.map +1 -1
- package/dist/src/ui/components/Footer.js +1 -1
- package/dist/src/ui/components/Footer.js.map +1 -1
- package/dist/src/ui/components/Footer.test.js +24 -0
- package/dist/src/ui/components/Footer.test.js.map +1 -1
- package/dist/src/ui/components/Help.test.js +0 -1
- package/dist/src/ui/components/Help.test.js.map +1 -1
- package/dist/src/ui/components/InputPrompt.test.js +442 -342
- package/dist/src/ui/components/InputPrompt.test.js.map +1 -1
- package/dist/src/ui/components/ModelDialog.test.js +5 -5
- package/dist/src/ui/components/ModelDialog.test.js.map +1 -1
- package/dist/src/ui/components/PermissionsModifyTrustDialog.test.js +11 -12
- package/dist/src/ui/components/PermissionsModifyTrustDialog.test.js.map +1 -1
- package/dist/src/ui/components/SettingsDialog.test.js +13 -14
- package/dist/src/ui/components/SettingsDialog.test.js.map +1 -1
- package/dist/src/ui/components/ThemeDialog.test.js +1 -2
- package/dist/src/ui/components/ThemeDialog.test.js.map +1 -1
- package/dist/src/ui/components/shared/BaseSelectionList.test.js +11 -12
- package/dist/src/ui/components/shared/BaseSelectionList.test.js.map +1 -1
- package/dist/src/ui/components/shared/text-buffer.test.js +2 -1
- package/dist/src/ui/components/shared/text-buffer.test.js.map +1 -1
- package/dist/src/ui/components/views/ExtensionsList.d.ts +1 -1
- package/dist/src/ui/components/views/ExtensionsList.js +4 -1
- package/dist/src/ui/components/views/ExtensionsList.js.map +1 -1
- package/dist/src/ui/contexts/KeypressContext.d.ts +3 -2
- package/dist/src/ui/contexts/KeypressContext.js +114 -64
- package/dist/src/ui/contexts/KeypressContext.js.map +1 -1
- package/dist/src/ui/contexts/KeypressContext.test.js +166 -482
- package/dist/src/ui/contexts/KeypressContext.test.js.map +1 -1
- package/dist/src/ui/contexts/SessionContext.test.js +27 -13
- package/dist/src/ui/contexts/SessionContext.test.js.map +1 -1
- package/dist/src/ui/hooks/atCommandProcessor.js +2 -2
- package/dist/src/ui/hooks/atCommandProcessor.js.map +1 -1
- package/dist/src/ui/hooks/shellCommandProcessor.test.js +18 -2
- package/dist/src/ui/hooks/shellCommandProcessor.test.js.map +1 -1
- package/dist/src/ui/hooks/slashCommandProcessor.test.js +74 -80
- package/dist/src/ui/hooks/slashCommandProcessor.test.js.map +1 -1
- package/dist/src/ui/hooks/useAtCompletion.test.js +32 -23
- package/dist/src/ui/hooks/useAtCompletion.test.js.map +1 -1
- package/dist/src/ui/hooks/useAutoAcceptIndicator.test.js +2 -1
- package/dist/src/ui/hooks/useAutoAcceptIndicator.test.js.map +1 -1
- package/dist/src/ui/hooks/useCommandCompletion.test.js +79 -78
- package/dist/src/ui/hooks/useCommandCompletion.test.js.map +1 -1
- package/dist/src/ui/hooks/useConsoleMessages.test.js +26 -9
- package/dist/src/ui/hooks/useConsoleMessages.test.js.map +1 -1
- package/dist/src/ui/hooks/useEditorSettings.test.js +40 -34
- package/dist/src/ui/hooks/useEditorSettings.test.js.map +1 -1
- package/dist/src/ui/hooks/useExtensionUpdates.d.ts +1 -2
- package/dist/src/ui/hooks/useExtensionUpdates.js +4 -2
- package/dist/src/ui/hooks/useExtensionUpdates.js.map +1 -1
- package/dist/src/ui/hooks/useExtensionUpdates.test.js +37 -26
- package/dist/src/ui/hooks/useExtensionUpdates.test.js.map +1 -1
- package/dist/src/ui/hooks/useFlickerDetector.test.js +9 -5
- package/dist/src/ui/hooks/useFlickerDetector.test.js.map +1 -1
- package/dist/src/ui/hooks/useFocus.test.js +25 -9
- package/dist/src/ui/hooks/useFocus.test.js.map +1 -1
- package/dist/src/ui/hooks/useFolderTrust.test.js +45 -22
- package/dist/src/ui/hooks/useFolderTrust.test.js.map +1 -1
- package/dist/src/ui/hooks/useGeminiStream.js +56 -19
- package/dist/src/ui/hooks/useGeminiStream.js.map +1 -1
- package/dist/src/ui/hooks/useGeminiStream.test.js +155 -74
- package/dist/src/ui/hooks/useGeminiStream.test.js.map +1 -1
- package/dist/src/ui/hooks/useGitBranchName.test.js +29 -16
- package/dist/src/ui/hooks/useGitBranchName.test.js.map +1 -1
- package/dist/src/ui/hooks/useHistoryManager.test.js +2 -1
- package/dist/src/ui/hooks/useHistoryManager.test.js.map +1 -1
- package/dist/src/ui/hooks/useIdeTrustListener.test.js +24 -7
- package/dist/src/ui/hooks/useIdeTrustListener.test.js.map +1 -1
- package/dist/src/ui/hooks/useInputHistory.test.js +2 -1
- package/dist/src/ui/hooks/useInputHistory.test.js.map +1 -1
- package/dist/src/ui/hooks/useInputHistoryStore.test.js +2 -1
- package/dist/src/ui/hooks/useInputHistoryStore.test.js.map +1 -1
- package/dist/src/ui/hooks/useKeypress.test.js +94 -113
- package/dist/src/ui/hooks/useKeypress.test.js.map +1 -1
- package/dist/src/ui/hooks/useLoadingIndicator.test.js +24 -6
- package/dist/src/ui/hooks/useLoadingIndicator.test.js.map +1 -1
- package/dist/src/ui/hooks/useMemoryMonitor.test.js +10 -5
- package/dist/src/ui/hooks/useMemoryMonitor.test.js.map +1 -1
- package/dist/src/ui/hooks/useMessageQueue.test.js +61 -45
- package/dist/src/ui/hooks/useMessageQueue.test.js.map +1 -1
- package/dist/src/ui/hooks/useModelCommand.test.js +18 -11
- package/dist/src/ui/hooks/useModelCommand.test.js.map +1 -1
- package/dist/src/ui/hooks/usePermissionsModifyTrust.test.js +2 -2
- package/dist/src/ui/hooks/usePermissionsModifyTrust.test.js.map +1 -1
- package/dist/src/ui/hooks/usePhraseCycler.js +1 -1
- package/dist/src/ui/hooks/usePhraseCycler.js.map +1 -1
- package/dist/src/ui/hooks/usePhraseCycler.test.js +83 -110
- package/dist/src/ui/hooks/usePhraseCycler.test.js.map +1 -1
- package/dist/src/ui/hooks/usePrivacySettings.test.js +26 -10
- package/dist/src/ui/hooks/usePrivacySettings.test.js.map +1 -1
- package/dist/src/ui/hooks/useQuotaAndFallback.js +13 -14
- package/dist/src/ui/hooks/useQuotaAndFallback.js.map +1 -1
- package/dist/src/ui/hooks/useQuotaAndFallback.test.js +33 -40
- package/dist/src/ui/hooks/useQuotaAndFallback.test.js.map +1 -1
- package/dist/src/ui/hooks/useReactToolScheduler.d.ts +8 -1
- package/dist/src/ui/hooks/useReactToolScheduler.js +37 -26
- package/dist/src/ui/hooks/useReactToolScheduler.js.map +1 -1
- package/dist/src/ui/hooks/useReactToolScheduler.test.js +1 -1
- package/dist/src/ui/hooks/useReactToolScheduler.test.js.map +1 -1
- package/dist/src/ui/hooks/useReverseSearchCompletion.test.js +2 -2
- package/dist/src/ui/hooks/useReverseSearchCompletion.test.js.map +1 -1
- package/dist/src/ui/hooks/useSelectionList.test.js +193 -132
- package/dist/src/ui/hooks/useSelectionList.test.js.map +1 -1
- package/dist/src/ui/hooks/useShellHistory.test.js +40 -16
- package/dist/src/ui/hooks/useShellHistory.test.js.map +1 -1
- package/dist/src/ui/hooks/useSlashCompletion.test.js +54 -49
- package/dist/src/ui/hooks/useSlashCompletion.test.js.map +1 -1
- package/dist/src/ui/hooks/useTimer.test.js +43 -14
- package/dist/src/ui/hooks/useTimer.test.js.map +1 -1
- package/dist/src/ui/hooks/useToolScheduler.test.js +163 -74
- package/dist/src/ui/hooks/useToolScheduler.test.js.map +1 -1
- package/dist/src/ui/hooks/vim.test.js +251 -356
- package/dist/src/ui/hooks/vim.test.js.map +1 -1
- package/dist/src/ui/keyMatchers.test.js +3 -3
- package/dist/src/ui/keyMatchers.test.js.map +1 -1
- package/dist/src/ui/utils/textOutput.d.ts +25 -0
- package/dist/src/ui/utils/textOutput.js +49 -0
- package/dist/src/ui/utils/textOutput.js.map +1 -0
- package/dist/src/ui/utils/textOutput.test.d.ts +6 -0
- package/dist/src/ui/utils/textOutput.test.js +79 -0
- package/dist/src/ui/utils/textOutput.test.js.map +1 -0
- package/dist/src/ui/utils/updateCheck.d.ts +7 -1
- package/dist/src/ui/utils/updateCheck.js +27 -26
- package/dist/src/ui/utils/updateCheck.js.map +1 -1
- package/dist/src/ui/utils/updateCheck.test.js +19 -49
- package/dist/src/ui/utils/updateCheck.test.js.map +1 -1
- package/dist/src/utils/handleAutoUpdate.js +9 -3
- package/dist/src/utils/handleAutoUpdate.js.map +1 -1
- package/dist/src/zed-integration/zedIntegration.d.ts +2 -2
- package/dist/src/zed-integration/zedIntegration.js +9 -16
- package/dist/src/zed-integration/zedIntegration.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +4 -4
- package/dist/google-gemini-cli-0.12.0-nightly.20251022.0542de95.tgz +0 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
2
|
+
import { act } from 'react';
|
|
3
|
+
import { renderHook } from '../../test-utils/render.js';
|
|
3
4
|
import { vi } from 'vitest';
|
|
4
5
|
import { KeypressProvider, useKeypressContext, DRAG_COMPLETION_TIMEOUT_MS, KITTY_SEQUENCE_TIMEOUT_MS,
|
|
5
6
|
// CSI_END_O,
|
|
@@ -15,29 +16,20 @@ vi.mock('ink', async (importOriginal) => {
|
|
|
15
16
|
useStdin: vi.fn(),
|
|
16
17
|
};
|
|
17
18
|
});
|
|
19
|
+
const PASTE_START = '\x1B[200~';
|
|
20
|
+
const PASTE_END = '\x1B[201~';
|
|
21
|
+
// readline will not emit most incomplete kitty sequences but it will give
|
|
22
|
+
// up on sequences like this where the modifier (135) has more than two digits.
|
|
23
|
+
const INCOMPLETE_KITTY_SEQUENCE = '\x1b[97;135';
|
|
18
24
|
class MockStdin extends EventEmitter {
|
|
19
25
|
isTTY = true;
|
|
20
26
|
setRawMode = vi.fn();
|
|
21
27
|
on = this.addListener;
|
|
22
28
|
removeListener = super.removeListener;
|
|
23
|
-
write = vi.fn();
|
|
24
29
|
resume = vi.fn();
|
|
25
30
|
pause = vi.fn();
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
this.emit('keypress', null, key);
|
|
29
|
-
}
|
|
30
|
-
// Helper to simulate a kitty protocol sequence
|
|
31
|
-
sendKittySequence(sequence) {
|
|
32
|
-
this.emit('data', Buffer.from(sequence));
|
|
33
|
-
}
|
|
34
|
-
// Helper to simulate a paste event
|
|
35
|
-
sendPaste(text) {
|
|
36
|
-
const PASTE_MODE_PREFIX = `\x1b[200~`;
|
|
37
|
-
const PASTE_MODE_SUFFIX = `\x1b[201~`;
|
|
38
|
-
this.emit('data', Buffer.from(PASTE_MODE_PREFIX));
|
|
39
|
-
this.emit('data', Buffer.from(text));
|
|
40
|
-
this.emit('data', Buffer.from(PASTE_MODE_SUFFIX));
|
|
31
|
+
write(text) {
|
|
32
|
+
this.emit('data', text);
|
|
41
33
|
}
|
|
42
34
|
}
|
|
43
35
|
describe('KeypressContext - Kitty Protocol', () => {
|
|
@@ -58,12 +50,10 @@ describe('KeypressContext - Kitty Protocol', () => {
|
|
|
58
50
|
const { result } = renderHook(() => useKeypressContext(), {
|
|
59
51
|
wrapper: ({ children }) => wrapper({ children, kittyProtocolEnabled: true }),
|
|
60
52
|
});
|
|
61
|
-
act(() =>
|
|
62
|
-
result.current.subscribe(keyHandler);
|
|
63
|
-
});
|
|
53
|
+
act(() => result.current.subscribe(keyHandler));
|
|
64
54
|
// Send kitty protocol sequence for regular enter: ESC[13u
|
|
65
55
|
act(() => {
|
|
66
|
-
stdin.
|
|
56
|
+
stdin.write(`\x1b[13u`);
|
|
67
57
|
});
|
|
68
58
|
expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({
|
|
69
59
|
name: 'return',
|
|
@@ -78,12 +68,10 @@ describe('KeypressContext - Kitty Protocol', () => {
|
|
|
78
68
|
const { result } = renderHook(() => useKeypressContext(), {
|
|
79
69
|
wrapper: ({ children }) => wrapper({ children, kittyProtocolEnabled: true }),
|
|
80
70
|
});
|
|
81
|
-
act(() =>
|
|
82
|
-
result.current.subscribe(keyHandler);
|
|
83
|
-
});
|
|
71
|
+
act(() => result.current.subscribe(keyHandler));
|
|
84
72
|
// Send kitty protocol sequence for numpad enter: ESC[57414u
|
|
85
73
|
act(() => {
|
|
86
|
-
stdin.
|
|
74
|
+
stdin.write(`\x1b[57414u`);
|
|
87
75
|
});
|
|
88
76
|
expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({
|
|
89
77
|
name: 'return',
|
|
@@ -98,12 +86,10 @@ describe('KeypressContext - Kitty Protocol', () => {
|
|
|
98
86
|
const { result } = renderHook(() => useKeypressContext(), {
|
|
99
87
|
wrapper: ({ children }) => wrapper({ children, kittyProtocolEnabled: true }),
|
|
100
88
|
});
|
|
101
|
-
act(() =>
|
|
102
|
-
result.current.subscribe(keyHandler);
|
|
103
|
-
});
|
|
89
|
+
act(() => result.current.subscribe(keyHandler));
|
|
104
90
|
// Send kitty protocol sequence for numpad enter with Shift (modifier 2): ESC[57414;2u
|
|
105
91
|
act(() => {
|
|
106
|
-
stdin.
|
|
92
|
+
stdin.write(`\x1b[57414;2u`);
|
|
107
93
|
});
|
|
108
94
|
expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({
|
|
109
95
|
name: 'return',
|
|
@@ -118,13 +104,9 @@ describe('KeypressContext - Kitty Protocol', () => {
|
|
|
118
104
|
const { result } = renderHook(() => useKeypressContext(), {
|
|
119
105
|
wrapper: ({ children }) => wrapper({ children, kittyProtocolEnabled: true }),
|
|
120
106
|
});
|
|
121
|
-
act(() =>
|
|
122
|
-
result.current.subscribe(keyHandler);
|
|
123
|
-
});
|
|
107
|
+
act(() => result.current.subscribe(keyHandler));
|
|
124
108
|
// Send kitty protocol sequence for numpad enter with Ctrl (modifier 5): ESC[57414;5u
|
|
125
|
-
act(() =>
|
|
126
|
-
stdin.sendKittySequence(`\x1b[57414;5u`);
|
|
127
|
-
});
|
|
109
|
+
act(() => stdin.write(`\x1b[57414;5u`));
|
|
128
110
|
expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({
|
|
129
111
|
name: 'return',
|
|
130
112
|
kittyProtocol: true,
|
|
@@ -138,12 +120,10 @@ describe('KeypressContext - Kitty Protocol', () => {
|
|
|
138
120
|
const { result } = renderHook(() => useKeypressContext(), {
|
|
139
121
|
wrapper: ({ children }) => wrapper({ children, kittyProtocolEnabled: true }),
|
|
140
122
|
});
|
|
141
|
-
act(() =>
|
|
142
|
-
result.current.subscribe(keyHandler);
|
|
143
|
-
});
|
|
123
|
+
act(() => result.current.subscribe(keyHandler));
|
|
144
124
|
// Send kitty protocol sequence for numpad enter with Alt (modifier 3): ESC[57414;3u
|
|
145
125
|
act(() => {
|
|
146
|
-
stdin.
|
|
126
|
+
stdin.write(`\x1b[57414;3u`);
|
|
147
127
|
});
|
|
148
128
|
expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({
|
|
149
129
|
name: 'return',
|
|
@@ -158,12 +138,10 @@ describe('KeypressContext - Kitty Protocol', () => {
|
|
|
158
138
|
const { result } = renderHook(() => useKeypressContext(), {
|
|
159
139
|
wrapper: ({ children }) => wrapper({ children, kittyProtocolEnabled: false }),
|
|
160
140
|
});
|
|
161
|
-
act(() =>
|
|
162
|
-
result.current.subscribe(keyHandler);
|
|
163
|
-
});
|
|
141
|
+
act(() => result.current.subscribe(keyHandler));
|
|
164
142
|
// Send kitty protocol sequence for numpad enter
|
|
165
143
|
act(() => {
|
|
166
|
-
stdin.
|
|
144
|
+
stdin.write(`\x1b[57414u`);
|
|
167
145
|
});
|
|
168
146
|
// When kitty protocol is disabled, the sequence should be passed through
|
|
169
147
|
// as individual keypresses, not recognized as a single enter key
|
|
@@ -179,12 +157,10 @@ describe('KeypressContext - Kitty Protocol', () => {
|
|
|
179
157
|
const { result } = renderHook(() => useKeypressContext(), {
|
|
180
158
|
wrapper: ({ children }) => wrapper({ children, kittyProtocolEnabled: true }),
|
|
181
159
|
});
|
|
182
|
-
act(() =>
|
|
183
|
-
result.current.subscribe(keyHandler);
|
|
184
|
-
});
|
|
160
|
+
act(() => result.current.subscribe(keyHandler));
|
|
185
161
|
// Send kitty protocol sequence for escape: ESC[27u
|
|
186
162
|
act(() => {
|
|
187
|
-
stdin.
|
|
163
|
+
stdin.write('\x1b[27u');
|
|
188
164
|
});
|
|
189
165
|
expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({
|
|
190
166
|
name: 'escape',
|
|
@@ -198,7 +174,7 @@ describe('KeypressContext - Kitty Protocol', () => {
|
|
|
198
174
|
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
|
199
175
|
act(() => result.current.subscribe(keyHandler));
|
|
200
176
|
act(() => {
|
|
201
|
-
stdin.
|
|
177
|
+
stdin.write(`\x1b[9u`);
|
|
202
178
|
});
|
|
203
179
|
expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({
|
|
204
180
|
name: 'tab',
|
|
@@ -212,7 +188,7 @@ describe('KeypressContext - Kitty Protocol', () => {
|
|
|
212
188
|
act(() => result.current.subscribe(keyHandler));
|
|
213
189
|
// Modifier 2 is Shift
|
|
214
190
|
act(() => {
|
|
215
|
-
stdin.
|
|
191
|
+
stdin.write(`\x1b[9;2u`);
|
|
216
192
|
});
|
|
217
193
|
expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({
|
|
218
194
|
name: 'tab',
|
|
@@ -225,7 +201,7 @@ describe('KeypressContext - Kitty Protocol', () => {
|
|
|
225
201
|
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
|
226
202
|
act(() => result.current.subscribe(keyHandler));
|
|
227
203
|
act(() => {
|
|
228
|
-
stdin.
|
|
204
|
+
stdin.write(`\x1b[127u`);
|
|
229
205
|
});
|
|
230
206
|
expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({
|
|
231
207
|
name: 'backspace',
|
|
@@ -239,7 +215,7 @@ describe('KeypressContext - Kitty Protocol', () => {
|
|
|
239
215
|
act(() => result.current.subscribe(keyHandler));
|
|
240
216
|
// Modifier 3 is Alt/Option
|
|
241
217
|
act(() => {
|
|
242
|
-
stdin.
|
|
218
|
+
stdin.write(`\x1b[127;3u`);
|
|
243
219
|
});
|
|
244
220
|
expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({
|
|
245
221
|
name: 'backspace',
|
|
@@ -253,7 +229,7 @@ describe('KeypressContext - Kitty Protocol', () => {
|
|
|
253
229
|
act(() => result.current.subscribe(keyHandler));
|
|
254
230
|
// Modifier 5 is Ctrl
|
|
255
231
|
act(() => {
|
|
256
|
-
stdin.
|
|
232
|
+
stdin.write(`\x1b[127;5u`);
|
|
257
233
|
});
|
|
258
234
|
expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({
|
|
259
235
|
name: 'backspace',
|
|
@@ -269,14 +245,14 @@ describe('KeypressContext - Kitty Protocol', () => {
|
|
|
269
245
|
const { result } = renderHook(() => useKeypressContext(), {
|
|
270
246
|
wrapper,
|
|
271
247
|
});
|
|
272
|
-
act(() =>
|
|
273
|
-
result.current.subscribe(keyHandler);
|
|
274
|
-
});
|
|
248
|
+
act(() => result.current.subscribe(keyHandler));
|
|
275
249
|
// Simulate a bracketed paste event
|
|
276
250
|
act(() => {
|
|
277
|
-
stdin.
|
|
251
|
+
stdin.write(PASTE_START);
|
|
252
|
+
stdin.write(pastedText);
|
|
253
|
+
stdin.write(PASTE_END);
|
|
278
254
|
});
|
|
279
|
-
await waitFor(() => {
|
|
255
|
+
await vi.waitFor(() => {
|
|
280
256
|
// Expect the handler to be called exactly once for the entire paste
|
|
281
257
|
expect(keyHandler).toHaveBeenCalledTimes(1);
|
|
282
258
|
});
|
|
@@ -286,6 +262,46 @@ describe('KeypressContext - Kitty Protocol', () => {
|
|
|
286
262
|
sequence: pastedText,
|
|
287
263
|
}));
|
|
288
264
|
});
|
|
265
|
+
it('should paste start code split over multiple writes', async () => {
|
|
266
|
+
const keyHandler = vi.fn();
|
|
267
|
+
const pastedText = 'pasted content';
|
|
268
|
+
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
|
269
|
+
act(() => result.current.subscribe(keyHandler));
|
|
270
|
+
act(() => {
|
|
271
|
+
// Split PASTE_START into two parts
|
|
272
|
+
stdin.write(PASTE_START.slice(0, 3));
|
|
273
|
+
stdin.write(PASTE_START.slice(3));
|
|
274
|
+
stdin.write(pastedText);
|
|
275
|
+
stdin.write(PASTE_END);
|
|
276
|
+
});
|
|
277
|
+
await vi.waitFor(() => {
|
|
278
|
+
expect(keyHandler).toHaveBeenCalledTimes(1);
|
|
279
|
+
});
|
|
280
|
+
expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({
|
|
281
|
+
paste: true,
|
|
282
|
+
sequence: pastedText,
|
|
283
|
+
}));
|
|
284
|
+
});
|
|
285
|
+
it('should paste end code split over multiple writes', async () => {
|
|
286
|
+
const keyHandler = vi.fn();
|
|
287
|
+
const pastedText = 'pasted content';
|
|
288
|
+
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
|
289
|
+
act(() => result.current.subscribe(keyHandler));
|
|
290
|
+
act(() => {
|
|
291
|
+
stdin.write(PASTE_START);
|
|
292
|
+
stdin.write(pastedText);
|
|
293
|
+
// Split PASTE_END into two parts
|
|
294
|
+
stdin.write(PASTE_END.slice(0, 3));
|
|
295
|
+
stdin.write(PASTE_END.slice(3));
|
|
296
|
+
});
|
|
297
|
+
await vi.waitFor(() => {
|
|
298
|
+
expect(keyHandler).toHaveBeenCalledTimes(1);
|
|
299
|
+
});
|
|
300
|
+
expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({
|
|
301
|
+
paste: true,
|
|
302
|
+
sequence: pastedText,
|
|
303
|
+
}));
|
|
304
|
+
});
|
|
289
305
|
});
|
|
290
306
|
describe('debug keystroke logging', () => {
|
|
291
307
|
let consoleLogSpy;
|
|
@@ -302,12 +318,10 @@ describe('KeypressContext - Kitty Protocol', () => {
|
|
|
302
318
|
const keyHandler = vi.fn();
|
|
303
319
|
const wrapper = ({ children }) => (_jsx(KeypressProvider, { kittyProtocolEnabled: true, debugKeystrokeLogging: false, children: children }));
|
|
304
320
|
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
|
305
|
-
act(() =>
|
|
306
|
-
result.current.subscribe(keyHandler);
|
|
307
|
-
});
|
|
321
|
+
act(() => result.current.subscribe(keyHandler));
|
|
308
322
|
// Send a kitty sequence
|
|
309
323
|
act(() => {
|
|
310
|
-
stdin.
|
|
324
|
+
stdin.write('\x1b[27u');
|
|
311
325
|
});
|
|
312
326
|
expect(keyHandler).toHaveBeenCalled();
|
|
313
327
|
expect(consoleLogSpy).not.toHaveBeenCalledWith(expect.stringContaining('[DEBUG] Kitty'));
|
|
@@ -316,13 +330,9 @@ describe('KeypressContext - Kitty Protocol', () => {
|
|
|
316
330
|
const keyHandler = vi.fn();
|
|
317
331
|
const wrapper = ({ children }) => (_jsx(KeypressProvider, { kittyProtocolEnabled: true, debugKeystrokeLogging: true, children: children }));
|
|
318
332
|
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
|
319
|
-
act(() =>
|
|
320
|
-
result.current.subscribe(keyHandler);
|
|
321
|
-
});
|
|
333
|
+
act(() => result.current.subscribe(keyHandler));
|
|
322
334
|
// Send a complete kitty sequence for escape
|
|
323
|
-
act(() =>
|
|
324
|
-
stdin.sendKittySequence('\x1b[27u');
|
|
325
|
-
});
|
|
335
|
+
act(() => stdin.write('\x1b[27u'));
|
|
326
336
|
expect(consoleLogSpy).toHaveBeenCalledWith('[DEBUG] Kitty buffer accumulating:', expect.stringContaining('"\\u001b[27u"'));
|
|
327
337
|
const parsedCall = consoleLogSpy.mock.calls.find((args) => typeof args[0] === 'string' &&
|
|
328
338
|
args[0].includes('[DEBUG] Kitty sequence parsed successfully'));
|
|
@@ -333,44 +343,21 @@ describe('KeypressContext - Kitty Protocol', () => {
|
|
|
333
343
|
const keyHandler = vi.fn();
|
|
334
344
|
const wrapper = ({ children }) => (_jsx(KeypressProvider, { kittyProtocolEnabled: true, debugKeystrokeLogging: true, children: children }));
|
|
335
345
|
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
|
336
|
-
act(() =>
|
|
337
|
-
result.current.subscribe(keyHandler);
|
|
338
|
-
});
|
|
346
|
+
act(() => result.current.subscribe(keyHandler));
|
|
339
347
|
// Send a long sequence starting with a valid kitty prefix to trigger overflow
|
|
340
348
|
const longSequence = '\x1b[1;' + '1'.repeat(100);
|
|
341
|
-
act(() =>
|
|
342
|
-
stdin.sendKittySequence(longSequence);
|
|
343
|
-
});
|
|
349
|
+
act(() => stdin.write(longSequence));
|
|
344
350
|
expect(consoleLogSpy).toHaveBeenCalledWith('[DEBUG] Kitty buffer overflow, clearing:', expect.any(String));
|
|
345
351
|
});
|
|
346
352
|
it('should log kitty buffer clear on Ctrl+C when debugKeystrokeLogging is true', async () => {
|
|
347
353
|
const keyHandler = vi.fn();
|
|
348
354
|
const wrapper = ({ children }) => (_jsx(KeypressProvider, { kittyProtocolEnabled: true, debugKeystrokeLogging: true, children: children }));
|
|
349
355
|
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
|
350
|
-
act(() =>
|
|
351
|
-
|
|
352
|
-
});
|
|
353
|
-
// Send incomplete kitty sequence
|
|
354
|
-
act(() => {
|
|
355
|
-
stdin.pressKey({
|
|
356
|
-
name: undefined,
|
|
357
|
-
ctrl: false,
|
|
358
|
-
meta: false,
|
|
359
|
-
shift: false,
|
|
360
|
-
sequence: '\x1b[1',
|
|
361
|
-
});
|
|
362
|
-
});
|
|
356
|
+
act(() => result.current.subscribe(keyHandler));
|
|
357
|
+
act(() => stdin.write(INCOMPLETE_KITTY_SEQUENCE));
|
|
363
358
|
// Send Ctrl+C
|
|
364
|
-
act(() =>
|
|
365
|
-
|
|
366
|
-
name: 'c',
|
|
367
|
-
ctrl: true,
|
|
368
|
-
meta: false,
|
|
369
|
-
shift: false,
|
|
370
|
-
sequence: '\x03',
|
|
371
|
-
});
|
|
372
|
-
});
|
|
373
|
-
expect(consoleLogSpy).toHaveBeenCalledWith('[DEBUG] Kitty buffer cleared on Ctrl+C:', '\x1b[1');
|
|
359
|
+
act(() => stdin.write('\x03'));
|
|
360
|
+
expect(consoleLogSpy).toHaveBeenCalledWith('[DEBUG] Kitty buffer cleared on Ctrl+C:', INCOMPLETE_KITTY_SEQUENCE);
|
|
374
361
|
// Verify Ctrl+C was handled
|
|
375
362
|
expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({
|
|
376
363
|
name: 'c',
|
|
@@ -381,24 +368,13 @@ describe('KeypressContext - Kitty Protocol', () => {
|
|
|
381
368
|
const keyHandler = vi.fn();
|
|
382
369
|
const wrapper = ({ children }) => (_jsx(KeypressProvider, { kittyProtocolEnabled: true, debugKeystrokeLogging: true, children: children }));
|
|
383
370
|
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
|
384
|
-
act(() =>
|
|
385
|
-
result.current.subscribe(keyHandler);
|
|
386
|
-
});
|
|
371
|
+
act(() => result.current.subscribe(keyHandler));
|
|
387
372
|
// Send incomplete kitty sequence
|
|
388
|
-
|
|
389
|
-
act(() => {
|
|
390
|
-
stdin.pressKey({
|
|
391
|
-
name: undefined,
|
|
392
|
-
ctrl: false,
|
|
393
|
-
meta: false,
|
|
394
|
-
shift: false,
|
|
395
|
-
sequence,
|
|
396
|
-
});
|
|
397
|
-
});
|
|
373
|
+
act(() => stdin.write(INCOMPLETE_KITTY_SEQUENCE));
|
|
398
374
|
// Verify debug logging for accumulation
|
|
399
|
-
expect(consoleLogSpy).toHaveBeenCalledWith('[DEBUG] Kitty buffer accumulating:', JSON.stringify(
|
|
375
|
+
expect(consoleLogSpy).toHaveBeenCalledWith('[DEBUG] Kitty buffer accumulating:', JSON.stringify(INCOMPLETE_KITTY_SEQUENCE));
|
|
400
376
|
// Verify warning for char codes
|
|
401
|
-
expect(consoleWarnSpy).toHaveBeenCalledWith('Kitty sequence buffer has content:', JSON.stringify(
|
|
377
|
+
expect(consoleWarnSpy).toHaveBeenCalledWith('Kitty sequence buffer has content:', JSON.stringify(INCOMPLETE_KITTY_SEQUENCE));
|
|
402
378
|
});
|
|
403
379
|
});
|
|
404
380
|
describe('Parameterized functional keys', () => {
|
|
@@ -414,6 +390,9 @@ describe('KeypressContext - Kitty Protocol', () => {
|
|
|
414
390
|
{ sequence: `\x1b[1~`, expected: { name: 'home' } },
|
|
415
391
|
{ sequence: `\x1b[4~`, expected: { name: 'end' } },
|
|
416
392
|
{ sequence: `\x1b[2~`, expected: { name: 'insert' } },
|
|
393
|
+
// Reverse tabs
|
|
394
|
+
{ sequence: `\x1b[Z`, expected: { name: 'tab', shift: true } },
|
|
395
|
+
{ sequence: `\x1b[1;2Z`, expected: { name: 'tab', shift: true } },
|
|
417
396
|
// Legacy Arrows
|
|
418
397
|
{
|
|
419
398
|
sequence: `\x1b[A`,
|
|
@@ -444,29 +423,17 @@ describe('KeypressContext - Kitty Protocol', () => {
|
|
|
444
423
|
const keyHandler = vi.fn();
|
|
445
424
|
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
|
446
425
|
act(() => result.current.subscribe(keyHandler));
|
|
447
|
-
act(() => stdin.
|
|
426
|
+
act(() => stdin.write(sequence));
|
|
448
427
|
expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining(expected));
|
|
449
428
|
});
|
|
450
429
|
});
|
|
451
|
-
describe('Shift+Tab forms', () => {
|
|
452
|
-
it.each([
|
|
453
|
-
{ sequence: `\x1b[Z`, description: 'legacy reverse Tab' },
|
|
454
|
-
{ sequence: `\x1b[1;2Z`, description: 'parameterized reverse Tab' },
|
|
455
|
-
])('should recognize $description "$sequence" as Shift+Tab', ({ sequence }) => {
|
|
456
|
-
const keyHandler = vi.fn();
|
|
457
|
-
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
|
458
|
-
act(() => result.current.subscribe(keyHandler));
|
|
459
|
-
act(() => stdin.sendKittySequence(sequence));
|
|
460
|
-
expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({ name: 'tab', shift: true }));
|
|
461
|
-
});
|
|
462
|
-
});
|
|
463
430
|
describe('Double-tap and batching', () => {
|
|
464
431
|
it('should emit two delete events for double-tap CSI[3~', async () => {
|
|
465
432
|
const keyHandler = vi.fn();
|
|
466
433
|
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
|
467
434
|
act(() => result.current.subscribe(keyHandler));
|
|
468
|
-
act(() => stdin.
|
|
469
|
-
act(() => stdin.
|
|
435
|
+
act(() => stdin.write(`\x1b[3~`));
|
|
436
|
+
act(() => stdin.write(`\x1b[3~`));
|
|
470
437
|
expect(keyHandler).toHaveBeenNthCalledWith(1, expect.objectContaining({ name: 'delete' }));
|
|
471
438
|
expect(keyHandler).toHaveBeenNthCalledWith(2, expect.objectContaining({ name: 'delete' }));
|
|
472
439
|
});
|
|
@@ -474,7 +441,7 @@ describe('KeypressContext - Kitty Protocol', () => {
|
|
|
474
441
|
const keyHandler = vi.fn();
|
|
475
442
|
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
|
476
443
|
act(() => result.current.subscribe(keyHandler));
|
|
477
|
-
act(() => stdin.
|
|
444
|
+
act(() => stdin.write(`\x1b[3~\x1b[5~`));
|
|
478
445
|
expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({ name: 'delete' }));
|
|
479
446
|
expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({ name: 'pageup' }));
|
|
480
447
|
});
|
|
@@ -485,15 +452,9 @@ describe('KeypressContext - Kitty Protocol', () => {
|
|
|
485
452
|
// Incomplete ESC sequence then a complete Delete
|
|
486
453
|
act(() => {
|
|
487
454
|
// Provide an incomplete ESC sequence chunk with a real ESC character
|
|
488
|
-
stdin.
|
|
489
|
-
name: undefined,
|
|
490
|
-
ctrl: false,
|
|
491
|
-
meta: false,
|
|
492
|
-
shift: false,
|
|
493
|
-
sequence: '\x1b[1;',
|
|
494
|
-
});
|
|
455
|
+
stdin.write('\x1b[1;');
|
|
495
456
|
});
|
|
496
|
-
act(() => stdin.
|
|
457
|
+
act(() => stdin.write(`\x1b[3~`));
|
|
497
458
|
expect(keyHandler).toHaveBeenCalledTimes(1);
|
|
498
459
|
expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({ name: 'delete' }));
|
|
499
460
|
});
|
|
@@ -519,37 +480,15 @@ describe('Drag and Drop Handling', () => {
|
|
|
519
480
|
it('should start collecting when single quote arrives and not broadcast immediately', async () => {
|
|
520
481
|
const keyHandler = vi.fn();
|
|
521
482
|
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
|
522
|
-
act(() =>
|
|
523
|
-
|
|
524
|
-
});
|
|
525
|
-
act(() => {
|
|
526
|
-
stdin.pressKey({
|
|
527
|
-
name: undefined,
|
|
528
|
-
ctrl: false,
|
|
529
|
-
meta: false,
|
|
530
|
-
shift: false,
|
|
531
|
-
paste: false,
|
|
532
|
-
sequence: SINGLE_QUOTE,
|
|
533
|
-
});
|
|
534
|
-
});
|
|
483
|
+
act(() => result.current.subscribe(keyHandler));
|
|
484
|
+
act(() => stdin.write(SINGLE_QUOTE));
|
|
535
485
|
expect(keyHandler).not.toHaveBeenCalled();
|
|
536
486
|
});
|
|
537
487
|
it('should start collecting when double quote arrives and not broadcast immediately', async () => {
|
|
538
488
|
const keyHandler = vi.fn();
|
|
539
489
|
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
|
540
|
-
act(() =>
|
|
541
|
-
|
|
542
|
-
});
|
|
543
|
-
act(() => {
|
|
544
|
-
stdin.pressKey({
|
|
545
|
-
name: undefined,
|
|
546
|
-
ctrl: false,
|
|
547
|
-
meta: false,
|
|
548
|
-
shift: false,
|
|
549
|
-
paste: false,
|
|
550
|
-
sequence: DOUBLE_QUOTE,
|
|
551
|
-
});
|
|
552
|
-
});
|
|
490
|
+
act(() => result.current.subscribe(keyHandler));
|
|
491
|
+
act(() => stdin.write(DOUBLE_QUOTE));
|
|
553
492
|
expect(keyHandler).not.toHaveBeenCalled();
|
|
554
493
|
});
|
|
555
494
|
});
|
|
@@ -557,31 +496,11 @@ describe('Drag and Drop Handling', () => {
|
|
|
557
496
|
it('should collect single character inputs during drag mode', async () => {
|
|
558
497
|
const keyHandler = vi.fn();
|
|
559
498
|
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
|
560
|
-
act(() =>
|
|
561
|
-
result.current.subscribe(keyHandler);
|
|
562
|
-
});
|
|
499
|
+
act(() => result.current.subscribe(keyHandler));
|
|
563
500
|
// Start by single quote
|
|
564
|
-
act(() =>
|
|
565
|
-
stdin.pressKey({
|
|
566
|
-
name: undefined,
|
|
567
|
-
ctrl: false,
|
|
568
|
-
meta: false,
|
|
569
|
-
shift: false,
|
|
570
|
-
paste: false,
|
|
571
|
-
sequence: SINGLE_QUOTE,
|
|
572
|
-
});
|
|
573
|
-
});
|
|
501
|
+
act(() => stdin.write(SINGLE_QUOTE));
|
|
574
502
|
// Send single character
|
|
575
|
-
act(() =>
|
|
576
|
-
stdin.pressKey({
|
|
577
|
-
name: undefined,
|
|
578
|
-
ctrl: false,
|
|
579
|
-
meta: false,
|
|
580
|
-
shift: false,
|
|
581
|
-
paste: false,
|
|
582
|
-
sequence: 'a',
|
|
583
|
-
});
|
|
584
|
-
});
|
|
503
|
+
act(() => stdin.write('a'));
|
|
585
504
|
// Character should not be immediately broadcast
|
|
586
505
|
expect(keyHandler).not.toHaveBeenCalled();
|
|
587
506
|
// Fast-forward to completion timeout
|
|
@@ -598,61 +517,14 @@ describe('Drag and Drop Handling', () => {
|
|
|
598
517
|
it('should collect multiple characters and complete on timeout', async () => {
|
|
599
518
|
const keyHandler = vi.fn();
|
|
600
519
|
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
|
601
|
-
act(() =>
|
|
602
|
-
result.current.subscribe(keyHandler);
|
|
603
|
-
});
|
|
520
|
+
act(() => result.current.subscribe(keyHandler));
|
|
604
521
|
// Start by single quote
|
|
605
|
-
act(() =>
|
|
606
|
-
stdin.pressKey({
|
|
607
|
-
name: undefined,
|
|
608
|
-
ctrl: false,
|
|
609
|
-
meta: false,
|
|
610
|
-
shift: false,
|
|
611
|
-
paste: false,
|
|
612
|
-
sequence: SINGLE_QUOTE,
|
|
613
|
-
});
|
|
614
|
-
});
|
|
522
|
+
act(() => stdin.write(SINGLE_QUOTE));
|
|
615
523
|
// Send multiple characters
|
|
616
|
-
act(() =>
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
meta: false,
|
|
621
|
-
shift: false,
|
|
622
|
-
paste: false,
|
|
623
|
-
sequence: 'p',
|
|
624
|
-
});
|
|
625
|
-
});
|
|
626
|
-
act(() => {
|
|
627
|
-
stdin.pressKey({
|
|
628
|
-
name: undefined,
|
|
629
|
-
ctrl: false,
|
|
630
|
-
meta: false,
|
|
631
|
-
shift: false,
|
|
632
|
-
paste: false,
|
|
633
|
-
sequence: 'a',
|
|
634
|
-
});
|
|
635
|
-
});
|
|
636
|
-
act(() => {
|
|
637
|
-
stdin.pressKey({
|
|
638
|
-
name: undefined,
|
|
639
|
-
ctrl: false,
|
|
640
|
-
meta: false,
|
|
641
|
-
shift: false,
|
|
642
|
-
paste: false,
|
|
643
|
-
sequence: 't',
|
|
644
|
-
});
|
|
645
|
-
});
|
|
646
|
-
act(() => {
|
|
647
|
-
stdin.pressKey({
|
|
648
|
-
name: undefined,
|
|
649
|
-
ctrl: false,
|
|
650
|
-
meta: false,
|
|
651
|
-
shift: false,
|
|
652
|
-
paste: false,
|
|
653
|
-
sequence: 'h',
|
|
654
|
-
});
|
|
655
|
-
});
|
|
524
|
+
act(() => stdin.write('p'));
|
|
525
|
+
act(() => stdin.write('a'));
|
|
526
|
+
act(() => stdin.write('t'));
|
|
527
|
+
act(() => stdin.write('h'));
|
|
656
528
|
// Characters should not be immediately broadcast
|
|
657
529
|
expect(keyHandler).not.toHaveBeenCalled();
|
|
658
530
|
// Fast-forward to completion timeout
|
|
@@ -686,20 +558,19 @@ describe('Kitty Sequence Parsing', () => {
|
|
|
686
558
|
});
|
|
687
559
|
// Terminals to test
|
|
688
560
|
const terminals = ['iTerm2', 'Ghostty', 'MacTerminal', 'VSCodeTerminal'];
|
|
689
|
-
// Key mappings: letter -> [keycode, accented character
|
|
690
|
-
// Note: µ (mu) is sent with meta:false on iTerm2/VSCode
|
|
561
|
+
// Key mappings: letter -> [keycode, accented character]
|
|
691
562
|
const keys = {
|
|
692
|
-
a: [97, 'å'
|
|
693
|
-
o: [111, 'ø'
|
|
694
|
-
m: [109, 'µ'
|
|
563
|
+
a: [97, 'å'],
|
|
564
|
+
o: [111, 'ø'],
|
|
565
|
+
m: [109, 'µ'],
|
|
695
566
|
};
|
|
696
|
-
it.each(terminals.flatMap((terminal) => Object.entries(keys).map(([key, [keycode, accentedChar
|
|
567
|
+
it.each(terminals.flatMap((terminal) => Object.entries(keys).map(([key, [keycode, accentedChar]]) => {
|
|
697
568
|
if (terminal === 'Ghostty') {
|
|
698
569
|
// Ghostty uses kitty protocol sequences
|
|
699
570
|
return {
|
|
700
571
|
terminal,
|
|
701
572
|
key,
|
|
702
|
-
|
|
573
|
+
chunk: `\x1b[${keycode};3u`,
|
|
703
574
|
expected: {
|
|
704
575
|
name: key,
|
|
705
576
|
ctrl: false,
|
|
@@ -716,14 +587,7 @@ describe('Kitty Sequence Parsing', () => {
|
|
|
716
587
|
terminal,
|
|
717
588
|
key,
|
|
718
589
|
kitty: false,
|
|
719
|
-
|
|
720
|
-
sequence: `\x1b${key}`,
|
|
721
|
-
name: key,
|
|
722
|
-
ctrl: false,
|
|
723
|
-
meta: true,
|
|
724
|
-
shift: false,
|
|
725
|
-
paste: false,
|
|
726
|
-
},
|
|
590
|
+
chunk: `\x1b${key}`,
|
|
727
591
|
expected: {
|
|
728
592
|
sequence: `\x1b${key}`,
|
|
729
593
|
name: key,
|
|
@@ -736,18 +600,12 @@ describe('Kitty Sequence Parsing', () => {
|
|
|
736
600
|
}
|
|
737
601
|
else {
|
|
738
602
|
// iTerm2 and VSCode send accented characters (å, ø, µ)
|
|
739
|
-
// Note: µ
|
|
603
|
+
// Note: µ (mu) is sent with meta:false on iTerm2/VSCode but
|
|
604
|
+
// gets converted to m with meta:true
|
|
740
605
|
return {
|
|
741
606
|
terminal,
|
|
742
607
|
key,
|
|
743
|
-
|
|
744
|
-
name: key,
|
|
745
|
-
ctrl: false,
|
|
746
|
-
meta: shouldHaveMeta,
|
|
747
|
-
shift: false,
|
|
748
|
-
paste: false,
|
|
749
|
-
sequence: accentedChar,
|
|
750
|
-
},
|
|
608
|
+
chunk: accentedChar,
|
|
751
609
|
expected: {
|
|
752
610
|
name: key,
|
|
753
611
|
ctrl: false,
|
|
@@ -758,19 +616,14 @@ describe('Kitty Sequence Parsing', () => {
|
|
|
758
616
|
},
|
|
759
617
|
};
|
|
760
618
|
}
|
|
761
|
-
})))('should handle Alt+$key in $terminal', ({
|
|
619
|
+
})))('should handle Alt+$key in $terminal', ({ chunk, expected, kitty = true, }) => {
|
|
762
620
|
const keyHandler = vi.fn();
|
|
763
621
|
const testWrapper = ({ children }) => (_jsx(KeypressProvider, { kittyProtocolEnabled: kitty, children: children }));
|
|
764
622
|
const { result } = renderHook(() => useKeypressContext(), {
|
|
765
623
|
wrapper: testWrapper,
|
|
766
624
|
});
|
|
767
625
|
act(() => result.current.subscribe(keyHandler));
|
|
768
|
-
|
|
769
|
-
act(() => stdin.sendKittySequence(kittySequence));
|
|
770
|
-
}
|
|
771
|
-
else if (input) {
|
|
772
|
-
act(() => stdin.pressKey(input));
|
|
773
|
-
}
|
|
626
|
+
act(() => stdin.write(chunk));
|
|
774
627
|
expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining(expected));
|
|
775
628
|
});
|
|
776
629
|
describe('Backslash key handling', () => {
|
|
@@ -784,14 +637,7 @@ describe('Kitty Sequence Parsing', () => {
|
|
|
784
637
|
const keyHandler = vi.fn();
|
|
785
638
|
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
|
786
639
|
act(() => result.current.subscribe(keyHandler));
|
|
787
|
-
act(() => stdin.
|
|
788
|
-
name: undefined,
|
|
789
|
-
ctrl: false,
|
|
790
|
-
meta: false,
|
|
791
|
-
shift: false,
|
|
792
|
-
paste: false,
|
|
793
|
-
sequence: '\\',
|
|
794
|
-
}));
|
|
640
|
+
act(() => stdin.write('\\'));
|
|
795
641
|
// Advance timers to trigger the backslash timeout
|
|
796
642
|
act(() => {
|
|
797
643
|
vi.runAllTimers();
|
|
@@ -805,57 +651,30 @@ describe('Kitty Sequence Parsing', () => {
|
|
|
805
651
|
it('should timeout and flush incomplete kitty sequences after 50ms', async () => {
|
|
806
652
|
const keyHandler = vi.fn();
|
|
807
653
|
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
|
808
|
-
act(() =>
|
|
809
|
-
|
|
810
|
-
});
|
|
811
|
-
// Send incomplete kitty sequence
|
|
812
|
-
act(() => {
|
|
813
|
-
stdin.pressKey({
|
|
814
|
-
name: undefined,
|
|
815
|
-
ctrl: false,
|
|
816
|
-
meta: false,
|
|
817
|
-
shift: false,
|
|
818
|
-
paste: false,
|
|
819
|
-
sequence: '\x1b[1;',
|
|
820
|
-
});
|
|
821
|
-
});
|
|
654
|
+
act(() => result.current.subscribe(keyHandler));
|
|
655
|
+
act(() => stdin.write(INCOMPLETE_KITTY_SEQUENCE));
|
|
822
656
|
// Should not broadcast immediately
|
|
823
657
|
expect(keyHandler).not.toHaveBeenCalled();
|
|
824
658
|
// Advance time just before timeout
|
|
825
|
-
act(() =>
|
|
826
|
-
vi.advanceTimersByTime(KITTY_SEQUENCE_TIMEOUT_MS - 5);
|
|
827
|
-
});
|
|
659
|
+
act(() => vi.advanceTimersByTime(KITTY_SEQUENCE_TIMEOUT_MS - 5));
|
|
828
660
|
// Still shouldn't broadcast
|
|
829
661
|
expect(keyHandler).not.toHaveBeenCalled();
|
|
830
662
|
// Advance past timeout
|
|
831
|
-
act(() =>
|
|
832
|
-
vi.advanceTimersByTime(10);
|
|
833
|
-
});
|
|
663
|
+
act(() => vi.advanceTimersByTime(10));
|
|
834
664
|
// Should now broadcast the incomplete sequence as regular input
|
|
835
665
|
expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({
|
|
836
666
|
name: '',
|
|
837
|
-
sequence:
|
|
667
|
+
sequence: INCOMPLETE_KITTY_SEQUENCE,
|
|
838
668
|
paste: false,
|
|
839
669
|
}));
|
|
840
670
|
});
|
|
841
671
|
it('should immediately flush non-kitty CSI sequences', async () => {
|
|
842
672
|
const keyHandler = vi.fn();
|
|
843
673
|
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
|
844
|
-
act(() =>
|
|
845
|
-
result.current.subscribe(keyHandler);
|
|
846
|
-
});
|
|
674
|
+
act(() => result.current.subscribe(keyHandler));
|
|
847
675
|
// Send a CSI sequence that doesn't match kitty patterns
|
|
848
676
|
// ESC[m is SGR reset, not a kitty sequence
|
|
849
|
-
act(() =>
|
|
850
|
-
stdin.pressKey({
|
|
851
|
-
name: undefined,
|
|
852
|
-
ctrl: false,
|
|
853
|
-
meta: false,
|
|
854
|
-
shift: false,
|
|
855
|
-
paste: false,
|
|
856
|
-
sequence: '\x1b[m',
|
|
857
|
-
});
|
|
858
|
-
});
|
|
677
|
+
act(() => stdin.write('\x1b[m'));
|
|
859
678
|
// Should broadcast immediately as it's not a valid kitty pattern
|
|
860
679
|
expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({
|
|
861
680
|
name: '',
|
|
@@ -866,20 +685,9 @@ describe('Kitty Sequence Parsing', () => {
|
|
|
866
685
|
it('should parse valid kitty sequences immediately when complete', async () => {
|
|
867
686
|
const keyHandler = vi.fn();
|
|
868
687
|
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
|
869
|
-
act(() =>
|
|
870
|
-
result.current.subscribe(keyHandler);
|
|
871
|
-
});
|
|
688
|
+
act(() => result.current.subscribe(keyHandler));
|
|
872
689
|
// Send complete kitty sequence for Ctrl+A
|
|
873
|
-
act(() =>
|
|
874
|
-
stdin.pressKey({
|
|
875
|
-
name: undefined,
|
|
876
|
-
ctrl: false,
|
|
877
|
-
meta: false,
|
|
878
|
-
shift: false,
|
|
879
|
-
paste: false,
|
|
880
|
-
sequence: '\x1b[97;5u',
|
|
881
|
-
});
|
|
882
|
-
});
|
|
690
|
+
act(() => stdin.write('\x1b[97;5u'));
|
|
883
691
|
// Should parse and broadcast immediately
|
|
884
692
|
expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({
|
|
885
693
|
name: 'a',
|
|
@@ -890,20 +698,9 @@ describe('Kitty Sequence Parsing', () => {
|
|
|
890
698
|
it('should handle batched kitty sequences correctly', async () => {
|
|
891
699
|
const keyHandler = vi.fn();
|
|
892
700
|
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
|
893
|
-
act(() =>
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
// Send multiple kitty sequences at once
|
|
897
|
-
act(() => {
|
|
898
|
-
stdin.pressKey({
|
|
899
|
-
name: undefined,
|
|
900
|
-
ctrl: false,
|
|
901
|
-
meta: false,
|
|
902
|
-
shift: false,
|
|
903
|
-
paste: false,
|
|
904
|
-
sequence: '\x1b[97;5u\x1b[98;5u', // Ctrl+a followed by Ctrl+b
|
|
905
|
-
});
|
|
906
|
-
});
|
|
701
|
+
act(() => result.current.subscribe(keyHandler));
|
|
702
|
+
// Send Ctrl+a followed by Ctrl+b
|
|
703
|
+
act(() => stdin.write('\x1b[97;5u\x1b[98;5u'));
|
|
907
704
|
// Should parse both sequences
|
|
908
705
|
expect(keyHandler).toHaveBeenCalledTimes(2);
|
|
909
706
|
expect(keyHandler).toHaveBeenNthCalledWith(1, expect.objectContaining({
|
|
@@ -920,35 +717,12 @@ describe('Kitty Sequence Parsing', () => {
|
|
|
920
717
|
it('should clear kitty buffer and timeout on Ctrl+C', async () => {
|
|
921
718
|
const keyHandler = vi.fn();
|
|
922
719
|
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
|
923
|
-
act(() =>
|
|
924
|
-
|
|
925
|
-
});
|
|
926
|
-
// Send incomplete kitty sequence
|
|
927
|
-
act(() => {
|
|
928
|
-
stdin.pressKey({
|
|
929
|
-
name: undefined,
|
|
930
|
-
ctrl: false,
|
|
931
|
-
meta: false,
|
|
932
|
-
shift: false,
|
|
933
|
-
paste: false,
|
|
934
|
-
sequence: '\x1b[1;',
|
|
935
|
-
});
|
|
936
|
-
});
|
|
720
|
+
act(() => result.current.subscribe(keyHandler));
|
|
721
|
+
act(() => stdin.write(INCOMPLETE_KITTY_SEQUENCE));
|
|
937
722
|
// Press Ctrl+C
|
|
938
|
-
act(() =>
|
|
939
|
-
stdin.pressKey({
|
|
940
|
-
name: 'c',
|
|
941
|
-
ctrl: true,
|
|
942
|
-
meta: false,
|
|
943
|
-
shift: false,
|
|
944
|
-
paste: false,
|
|
945
|
-
sequence: '\x03',
|
|
946
|
-
});
|
|
947
|
-
});
|
|
723
|
+
act(() => stdin.write('\x03'));
|
|
948
724
|
// Advance past timeout
|
|
949
|
-
act(() =>
|
|
950
|
-
vi.advanceTimersByTime(KITTY_SEQUENCE_TIMEOUT_MS + 10);
|
|
951
|
-
});
|
|
725
|
+
act(() => vi.advanceTimersByTime(KITTY_SEQUENCE_TIMEOUT_MS + 10));
|
|
952
726
|
// Should only have received Ctrl+C, not the incomplete sequence
|
|
953
727
|
expect(keyHandler).toHaveBeenCalledTimes(1);
|
|
954
728
|
expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({
|
|
@@ -959,20 +733,10 @@ describe('Kitty Sequence Parsing', () => {
|
|
|
959
733
|
it('should handle mixed valid and invalid sequences', async () => {
|
|
960
734
|
const keyHandler = vi.fn();
|
|
961
735
|
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
|
962
|
-
act(() =>
|
|
963
|
-
result.current.subscribe(keyHandler);
|
|
964
|
-
});
|
|
736
|
+
act(() => result.current.subscribe(keyHandler));
|
|
965
737
|
// Send valid kitty sequence followed by invalid CSI
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
name: undefined,
|
|
969
|
-
ctrl: false,
|
|
970
|
-
meta: false,
|
|
971
|
-
shift: false,
|
|
972
|
-
paste: false,
|
|
973
|
-
sequence: '\x1b[13u\x1b[!', // Valid enter, then invalid sequence
|
|
974
|
-
});
|
|
975
|
-
});
|
|
738
|
+
// Valid enter, then invalid sequence
|
|
739
|
+
act(() => stdin.write('\x1b[13u\x1b[!'));
|
|
976
740
|
// Should parse valid sequence and flush invalid immediately
|
|
977
741
|
expect(keyHandler).toHaveBeenCalledTimes(2);
|
|
978
742
|
expect(keyHandler).toHaveBeenNthCalledWith(1, expect.objectContaining({
|
|
@@ -989,20 +753,9 @@ describe('Kitty Sequence Parsing', () => {
|
|
|
989
753
|
const { result } = renderHook(() => useKeypressContext(), {
|
|
990
754
|
wrapper: ({ children }) => wrapper({ children, kittyProtocolEnabled: false }),
|
|
991
755
|
});
|
|
992
|
-
act(() =>
|
|
993
|
-
result.current.subscribe(keyHandler);
|
|
994
|
-
});
|
|
756
|
+
act(() => result.current.subscribe(keyHandler));
|
|
995
757
|
// Send what would be a kitty sequence
|
|
996
|
-
act(() =>
|
|
997
|
-
stdin.pressKey({
|
|
998
|
-
name: undefined,
|
|
999
|
-
ctrl: false,
|
|
1000
|
-
meta: false,
|
|
1001
|
-
shift: false,
|
|
1002
|
-
paste: false,
|
|
1003
|
-
sequence: '\x1b[13u',
|
|
1004
|
-
});
|
|
1005
|
-
});
|
|
758
|
+
act(() => stdin.write('\x1b[13u'));
|
|
1006
759
|
// Should pass through without parsing
|
|
1007
760
|
expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({
|
|
1008
761
|
sequence: '\x1b[13u',
|
|
@@ -1028,7 +781,7 @@ describe('Kitty Sequence Parsing', () => {
|
|
|
1028
781
|
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
1029
782
|
}
|
|
1030
783
|
// Should parse once complete
|
|
1031
|
-
await waitFor(() => {
|
|
784
|
+
await vi.waitFor(() => {
|
|
1032
785
|
expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({
|
|
1033
786
|
name: 'escape',
|
|
1034
787
|
kittyProtocol: true,
|
|
@@ -1038,113 +791,56 @@ describe('Kitty Sequence Parsing', () => {
|
|
|
1038
791
|
it('should reset timeout when new input arrives', async () => {
|
|
1039
792
|
const keyHandler = vi.fn();
|
|
1040
793
|
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
|
1041
|
-
act(() =>
|
|
1042
|
-
result.current.subscribe(keyHandler);
|
|
1043
|
-
});
|
|
794
|
+
act(() => result.current.subscribe(keyHandler));
|
|
1044
795
|
// Start incomplete sequence
|
|
1045
|
-
act(() =>
|
|
1046
|
-
stdin.pressKey({
|
|
1047
|
-
name: undefined,
|
|
1048
|
-
ctrl: false,
|
|
1049
|
-
meta: false,
|
|
1050
|
-
shift: false,
|
|
1051
|
-
paste: false,
|
|
1052
|
-
sequence: '\x1b[1',
|
|
1053
|
-
});
|
|
1054
|
-
});
|
|
796
|
+
act(() => stdin.write('\x1b[97;13'));
|
|
1055
797
|
// Advance time partway
|
|
1056
|
-
act(() =>
|
|
1057
|
-
vi.advanceTimersByTime(30);
|
|
1058
|
-
});
|
|
798
|
+
act(() => vi.advanceTimersByTime(30));
|
|
1059
799
|
// Add more to sequence
|
|
1060
|
-
act(() =>
|
|
1061
|
-
stdin.pressKey({
|
|
1062
|
-
name: undefined,
|
|
1063
|
-
ctrl: false,
|
|
1064
|
-
meta: false,
|
|
1065
|
-
shift: false,
|
|
1066
|
-
paste: false,
|
|
1067
|
-
sequence: '3',
|
|
1068
|
-
});
|
|
1069
|
-
});
|
|
800
|
+
act(() => stdin.write('5'));
|
|
1070
801
|
// Advance time from the first timeout point
|
|
1071
|
-
act(() =>
|
|
1072
|
-
vi.advanceTimersByTime(25);
|
|
1073
|
-
});
|
|
802
|
+
act(() => vi.advanceTimersByTime(25));
|
|
1074
803
|
// Should not have timed out yet (timeout restarted)
|
|
1075
804
|
expect(keyHandler).not.toHaveBeenCalled();
|
|
1076
805
|
// Complete the sequence
|
|
1077
|
-
act(() =>
|
|
1078
|
-
stdin.pressKey({
|
|
1079
|
-
name: undefined,
|
|
1080
|
-
ctrl: false,
|
|
1081
|
-
meta: false,
|
|
1082
|
-
shift: false,
|
|
1083
|
-
paste: false,
|
|
1084
|
-
sequence: 'u',
|
|
1085
|
-
});
|
|
1086
|
-
});
|
|
806
|
+
act(() => stdin.write('u'));
|
|
1087
807
|
// Should now parse as complete enter key
|
|
1088
808
|
expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({
|
|
1089
|
-
name: '
|
|
809
|
+
name: 'a',
|
|
1090
810
|
kittyProtocol: true,
|
|
1091
811
|
}));
|
|
1092
812
|
});
|
|
1093
813
|
it('should flush incomplete kitty sequence on FOCUS_IN event', async () => {
|
|
1094
814
|
const keyHandler = vi.fn();
|
|
1095
815
|
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
|
1096
|
-
act(() =>
|
|
1097
|
-
|
|
1098
|
-
});
|
|
1099
|
-
// Send incomplete kitty sequence
|
|
1100
|
-
act(() => {
|
|
1101
|
-
stdin.pressKey({
|
|
1102
|
-
sequence: '\x1b[1;',
|
|
1103
|
-
});
|
|
1104
|
-
});
|
|
816
|
+
act(() => result.current.subscribe(keyHandler));
|
|
817
|
+
act(() => stdin.write(INCOMPLETE_KITTY_SEQUENCE));
|
|
1105
818
|
// Incomplete sequence should be buffered, not broadcast
|
|
1106
819
|
expect(keyHandler).not.toHaveBeenCalled();
|
|
1107
820
|
// Send FOCUS_IN event
|
|
1108
|
-
|
|
1109
|
-
act(() => {
|
|
1110
|
-
stdin.pressKey({
|
|
1111
|
-
sequence: FOCUS_IN,
|
|
1112
|
-
});
|
|
1113
|
-
});
|
|
821
|
+
act(() => stdin.write('\x1b[I'));
|
|
1114
822
|
// The buffered sequence should be flushed
|
|
1115
823
|
expect(keyHandler).toHaveBeenCalledTimes(1);
|
|
1116
824
|
expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({
|
|
1117
825
|
name: '',
|
|
1118
|
-
sequence:
|
|
826
|
+
sequence: INCOMPLETE_KITTY_SEQUENCE,
|
|
1119
827
|
paste: false,
|
|
1120
828
|
}));
|
|
1121
829
|
});
|
|
1122
830
|
it('should flush incomplete kitty sequence on FOCUS_OUT event', async () => {
|
|
1123
831
|
const keyHandler = vi.fn();
|
|
1124
832
|
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
|
1125
|
-
act(() =>
|
|
1126
|
-
|
|
1127
|
-
});
|
|
1128
|
-
// Send incomplete kitty sequence
|
|
1129
|
-
act(() => {
|
|
1130
|
-
stdin.pressKey({
|
|
1131
|
-
sequence: '\x1b[1;',
|
|
1132
|
-
});
|
|
1133
|
-
});
|
|
833
|
+
act(() => result.current.subscribe(keyHandler));
|
|
834
|
+
act(() => stdin.write(INCOMPLETE_KITTY_SEQUENCE));
|
|
1134
835
|
// Incomplete sequence should be buffered, not broadcast
|
|
1135
836
|
expect(keyHandler).not.toHaveBeenCalled();
|
|
1136
837
|
// Send FOCUS_OUT event
|
|
1137
|
-
|
|
1138
|
-
act(() => {
|
|
1139
|
-
stdin.pressKey({
|
|
1140
|
-
sequence: FOCUS_OUT,
|
|
1141
|
-
});
|
|
1142
|
-
});
|
|
838
|
+
act(() => stdin.write('\x1b[O'));
|
|
1143
839
|
// The buffered sequence should be flushed
|
|
1144
840
|
expect(keyHandler).toHaveBeenCalledTimes(1);
|
|
1145
841
|
expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({
|
|
1146
842
|
name: '',
|
|
1147
|
-
sequence:
|
|
843
|
+
sequence: INCOMPLETE_KITTY_SEQUENCE,
|
|
1148
844
|
paste: false,
|
|
1149
845
|
}));
|
|
1150
846
|
});
|
|
@@ -1152,39 +848,27 @@ describe('Kitty Sequence Parsing', () => {
|
|
|
1152
848
|
vi.useFakeTimers();
|
|
1153
849
|
const keyHandler = vi.fn();
|
|
1154
850
|
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
|
1155
|
-
act(() =>
|
|
1156
|
-
|
|
1157
|
-
});
|
|
1158
|
-
// Send incomplete kitty sequence
|
|
1159
|
-
act(() => {
|
|
1160
|
-
stdin.pressKey({
|
|
1161
|
-
sequence: '\x1b[1;',
|
|
1162
|
-
});
|
|
1163
|
-
});
|
|
851
|
+
act(() => result.current.subscribe(keyHandler));
|
|
852
|
+
act(() => stdin.write(INCOMPLETE_KITTY_SEQUENCE));
|
|
1164
853
|
// Incomplete sequence should be buffered, not broadcast
|
|
1165
854
|
expect(keyHandler).not.toHaveBeenCalled();
|
|
1166
855
|
// Send paste start sequence
|
|
1167
|
-
|
|
1168
|
-
act(() => {
|
|
1169
|
-
stdin.emit('data', Buffer.from(PASTE_MODE_PREFIX));
|
|
1170
|
-
});
|
|
856
|
+
act(() => stdin.write(`\x1b[200~`));
|
|
1171
857
|
// The buffered sequence should be flushed
|
|
1172
858
|
expect(keyHandler).toHaveBeenCalledTimes(1);
|
|
1173
859
|
expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({
|
|
1174
860
|
name: '',
|
|
1175
|
-
sequence:
|
|
861
|
+
sequence: INCOMPLETE_KITTY_SEQUENCE,
|
|
1176
862
|
paste: false,
|
|
1177
863
|
}));
|
|
1178
864
|
// Now send some paste content and end paste to make sure paste still works
|
|
1179
865
|
const pastedText = 'hello';
|
|
1180
866
|
const PASTE_MODE_SUFFIX = `\x1b[201~`;
|
|
1181
867
|
act(() => {
|
|
1182
|
-
stdin.
|
|
1183
|
-
stdin.
|
|
1184
|
-
});
|
|
1185
|
-
act(() => {
|
|
1186
|
-
vi.runAllTimers();
|
|
868
|
+
stdin.write(pastedText);
|
|
869
|
+
stdin.write(PASTE_MODE_SUFFIX);
|
|
1187
870
|
});
|
|
871
|
+
act(() => vi.runAllTimers());
|
|
1188
872
|
// The paste event should be broadcast
|
|
1189
873
|
expect(keyHandler).toHaveBeenCalledTimes(2);
|
|
1190
874
|
expect(keyHandler).toHaveBeenNthCalledWith(2, expect.objectContaining({
|