@google/gemini-cli 0.12.0-nightly.20251023.a7faa208 → 0.12.0-nightly.20251027.cb0947c5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/package.json +5 -4
- package/dist/src/commands/extensions/disable.js +13 -6
- package/dist/src/commands/extensions/disable.js.map +1 -1
- package/dist/src/commands/extensions/enable.js +13 -6
- package/dist/src/commands/extensions/enable.js.map +1 -1
- package/dist/src/commands/extensions/install.js +12 -2
- package/dist/src/commands/extensions/install.js.map +1 -1
- package/dist/src/commands/extensions/install.test.js +11 -3
- package/dist/src/commands/extensions/install.test.js.map +1 -1
- package/dist/src/commands/extensions/link.js +12 -2
- package/dist/src/commands/extensions/link.js.map +1 -1
- package/dist/src/commands/extensions/list.js +13 -4
- package/dist/src/commands/extensions/list.js.map +1 -1
- package/dist/src/commands/extensions/uninstall.js +12 -2
- package/dist/src/commands/extensions/uninstall.js.map +1 -1
- package/dist/src/commands/extensions/update.js +17 -13
- package/dist/src/commands/extensions/update.js.map +1 -1
- package/dist/src/commands/mcp/list.js +10 -3
- package/dist/src/commands/mcp/list.js.map +1 -1
- package/dist/src/commands/mcp/list.test.js +12 -6
- package/dist/src/commands/mcp/list.test.js.map +1 -1
- package/dist/src/config/config.d.ts +1 -0
- package/dist/src/config/config.js +15 -3
- package/dist/src/config/config.js.map +1 -1
- package/dist/src/config/config.test.js +23 -15
- package/dist/src/config/config.test.js.map +1 -1
- package/dist/src/config/extension-manager.d.ts +38 -0
- package/dist/src/config/extension-manager.js +411 -0
- package/dist/src/config/extension-manager.js.map +1 -0
- package/dist/src/config/extension.d.ts +4 -51
- package/dist/src/config/extension.js +1 -535
- package/dist/src/config/extension.js.map +1 -1
- package/dist/src/config/extension.test.js +343 -163
- package/dist/src/config/extension.test.js.map +1 -1
- package/dist/src/config/extensions/consent.d.ts +38 -0
- package/dist/src/config/extensions/consent.js +123 -0
- package/dist/src/config/extensions/consent.js.map +1 -0
- package/dist/src/config/extensions/extensionEnablement.d.ts +1 -1
- package/dist/src/config/extensions/extensionEnablement.js +4 -3
- 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 +15 -0
- package/dist/src/config/extensions/extensionSettings.js +63 -0
- package/dist/src/config/extensions/extensionSettings.js.map +1 -0
- package/dist/src/config/extensions/extensionSettings.test.d.ts +6 -0
- package/dist/src/config/extensions/extensionSettings.test.js +137 -0
- package/dist/src/config/extensions/extensionSettings.test.js.map +1 -0
- package/dist/src/config/extensions/github.d.ts +2 -2
- package/dist/src/config/extensions/github.js +3 -8
- package/dist/src/config/extensions/github.js.map +1 -1
- package/dist/src/config/extensions/github.test.js +25 -7
- 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/storage.d.ts +14 -0
- package/dist/src/config/extensions/storage.js +32 -0
- package/dist/src/config/extensions/storage.js.map +1 -0
- package/dist/src/config/extensions/update.d.ts +4 -4
- package/dist/src/config/extensions/update.js +11 -18
- package/dist/src/config/extensions/update.js.map +1 -1
- package/dist/src/config/extensions/update.test.js +33 -58
- package/dist/src/config/extensions/update.test.js.map +1 -1
- package/dist/src/config/extensions/variableSchema.d.ts +0 -6
- package/dist/src/config/extensions/variableSchema.js.map +1 -1
- package/dist/src/config/extensions/variables.d.ts +4 -0
- package/dist/src/config/extensions/variables.js +6 -0
- package/dist/src/config/extensions/variables.js.map +1 -1
- package/dist/src/config/settings.d.ts +2 -1
- package/dist/src/config/settings.js +4 -7
- package/dist/src/config/settings.js.map +1 -1
- package/dist/src/config/settings.test.js +86 -16
- 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 +22 -5
- package/dist/src/gemini.js.map +1 -1
- package/dist/src/gemini.test.js +26 -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/nonInteractiveCli.js +30 -5
- package/dist/src/nonInteractiveCli.js.map +1 -1
- package/dist/src/nonInteractiveCli.test.js +151 -14
- package/dist/src/nonInteractiveCli.test.js.map +1 -1
- package/dist/src/test-utils/createExtension.d.ts +3 -1
- package/dist/src/test-utils/createExtension.js +3 -3
- package/dist/src/test-utils/createExtension.js.map +1 -1
- package/dist/src/ui/AppContainer.js +102 -48
- package/dist/src/ui/AppContainer.js.map +1 -1
- package/dist/src/ui/AppContainer.test.js +138 -79
- package/dist/src/ui/AppContainer.test.js.map +1 -1
- package/dist/src/ui/commands/extensionsCommand.js +19 -10
- package/dist/src/ui/commands/extensionsCommand.js.map +1 -1
- package/dist/src/ui/commands/extensionsCommand.test.js +8 -0
- package/dist/src/ui/commands/extensionsCommand.test.js.map +1 -1
- package/dist/src/ui/components/FolderTrustDialog.test.js +1 -0
- package/dist/src/ui/components/FolderTrustDialog.test.js.map +1 -1
- package/dist/src/ui/components/HistoryItemDisplay.js +1 -1
- package/dist/src/ui/components/HistoryItemDisplay.js.map +1 -1
- package/dist/src/ui/components/InputPrompt.js +5 -6
- package/dist/src/ui/components/InputPrompt.js.map +1 -1
- package/dist/src/ui/components/InputPrompt.test.js +668 -712
- package/dist/src/ui/components/InputPrompt.test.js.map +1 -1
- package/dist/src/ui/components/ModelDialog.test.js +1 -0
- package/dist/src/ui/components/ModelDialog.test.js.map +1 -1
- package/dist/src/ui/components/PermissionsModifyTrustDialog.test.js +1 -0
- package/dist/src/ui/components/PermissionsModifyTrustDialog.test.js.map +1 -1
- package/dist/src/ui/components/SettingsDialog.test.js +2 -30
- 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 +1 -0
- package/dist/src/ui/components/shared/BaseSelectionList.test.js.map +1 -1
- package/dist/src/ui/components/shared/text-buffer.test.js +1 -0
- package/dist/src/ui/components/shared/text-buffer.test.js.map +1 -1
- package/dist/src/ui/components/views/ExtensionsList.d.ts +7 -1
- package/dist/src/ui/components/views/ExtensionsList.js +8 -11
- package/dist/src/ui/components/views/ExtensionsList.js.map +1 -1
- package/dist/src/ui/components/views/ExtensionsList.test.js +34 -21
- package/dist/src/ui/components/views/ExtensionsList.test.js.map +1 -1
- package/dist/src/ui/contexts/KeypressContext.d.ts +3 -2
- package/dist/src/ui/contexts/KeypressContext.js +429 -386
- package/dist/src/ui/contexts/KeypressContext.js.map +1 -1
- package/dist/src/ui/contexts/KeypressContext.test.js +162 -479
- package/dist/src/ui/contexts/KeypressContext.test.js.map +1 -1
- package/dist/src/ui/contexts/SessionContext.test.js +1 -0
- package/dist/src/ui/contexts/SessionContext.test.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/useAutoAcceptIndicator.test.js +1 -0
- 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 +15 -5
- package/dist/src/ui/hooks/useExtensionUpdates.js +16 -18
- package/dist/src/ui/hooks/useExtensionUpdates.js.map +1 -1
- package/dist/src/ui/hooks/useExtensionUpdates.test.js +46 -36
- package/dist/src/ui/hooks/useExtensionUpdates.test.js.map +1 -1
- package/dist/src/ui/hooks/useFlickerDetector.test.js +1 -0
- 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 +1 -0
- package/dist/src/ui/hooks/useFolderTrust.test.js.map +1 -1
- package/dist/src/ui/hooks/useGeminiStream.js +49 -14
- package/dist/src/ui/hooks/useGeminiStream.js.map +1 -1
- package/dist/src/ui/hooks/useGeminiStream.test.js +114 -34
- package/dist/src/ui/hooks/useGeminiStream.test.js.map +1 -1
- package/dist/src/ui/hooks/useGitBranchName.js +4 -0
- package/dist/src/ui/hooks/useGitBranchName.js.map +1 -1
- package/dist/src/ui/hooks/useGitBranchName.test.js +46 -35
- package/dist/src/ui/hooks/useGitBranchName.test.js.map +1 -1
- package/dist/src/ui/hooks/useHistoryManager.test.js +1 -0
- 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 +1 -0
- package/dist/src/ui/hooks/useInputHistory.test.js.map +1 -1
- package/dist/src/ui/hooks/useInputHistoryStore.test.js +1 -0
- 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 +1 -0
- package/dist/src/ui/hooks/usePermissionsModifyTrust.test.js.map +1 -1
- package/dist/src/ui/hooks/usePhraseCycler.test.js +1 -0
- 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 +32 -39
- 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 +59 -34
- package/dist/src/ui/hooks/useReactToolScheduler.js.map +1 -1
- package/dist/src/ui/hooks/useReactToolScheduler.test.d.ts +6 -0
- package/dist/src/ui/hooks/useReactToolScheduler.test.js +66 -0
- package/dist/src/ui/hooks/useReactToolScheduler.test.js.map +1 -0
- 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 +1 -0
- package/dist/src/ui/hooks/useShellHistory.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 +122 -39
- 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/types.d.ts +2 -1
- package/dist/src/ui/types.js.map +1 -1
- package/dist/src/ui/utils/CodeColorizer.js +2 -1
- package/dist/src/ui/utils/CodeColorizer.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/envVarResolver.d.ts +2 -2
- package/dist/src/utils/envVarResolver.js +10 -7
- package/dist/src/utils/envVarResolver.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/schema.d.ts +4 -4
- package/dist/src/zed-integration/zedIntegration.js +8 -13
- package/dist/src/zed-integration/zedIntegration.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +5 -4
- package/dist/google-gemini-cli-0.12.0-nightly.20251022.0542de95.tgz +0 -0
|
@@ -7,10 +7,11 @@ import { PassThrough } from 'node:stream';
|
|
|
7
7
|
import { BACKSLASH_ENTER_DETECTION_WINDOW_MS, CHAR_CODE_ESC, KITTY_CTRL_C, KITTY_KEYCODE_BACKSPACE, KITTY_KEYCODE_ENTER, KITTY_KEYCODE_NUMPAD_ENTER, KITTY_KEYCODE_TAB, MAX_KITTY_SEQUENCE_LENGTH, KITTY_MODIFIER_BASE, KITTY_MODIFIER_EVENT_TYPES_OFFSET, MODIFIER_SHIFT_BIT, MODIFIER_ALT_BIT, MODIFIER_CTRL_BIT, } from '../utils/platformConstants.js';
|
|
8
8
|
import { FOCUS_IN, FOCUS_OUT } from '../hooks/useFocus.js';
|
|
9
9
|
const ESC = '\u001B';
|
|
10
|
-
export const
|
|
11
|
-
export const
|
|
10
|
+
export const PASTE_MODE_START = `${ESC}[200~`;
|
|
11
|
+
export const PASTE_MODE_END = `${ESC}[201~`;
|
|
12
12
|
export const DRAG_COMPLETION_TIMEOUT_MS = 100; // Broadcast full path after 100ms if no more input
|
|
13
13
|
export const KITTY_SEQUENCE_TIMEOUT_MS = 50; // Flush incomplete kitty sequences after 50ms
|
|
14
|
+
export const PASTE_CODE_TIMEOUT_MS = 50; // Flush incomplete paste code after 50ms
|
|
14
15
|
export const SINGLE_QUOTE = "'";
|
|
15
16
|
export const DOUBLE_QUOTE = '"';
|
|
16
17
|
const ALT_KEY_CHARACTER_MAP = {
|
|
@@ -41,169 +42,186 @@ const ALT_KEY_CHARACTER_MAP = {
|
|
|
41
42
|
'\u00A5': 'y',
|
|
42
43
|
'\u03A9': 'z',
|
|
43
44
|
};
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
45
|
+
/**
|
|
46
|
+
* Check if a buffer could potentially be a valid kitty sequence or its prefix.
|
|
47
|
+
*/
|
|
48
|
+
function couldBeKittySequence(buffer) {
|
|
49
|
+
// Kitty sequences always start with ESC[.
|
|
50
|
+
if (buffer.length === 0)
|
|
51
|
+
return true;
|
|
52
|
+
if (buffer === ESC || buffer === `${ESC}[`)
|
|
53
|
+
return true;
|
|
54
|
+
if (!buffer.startsWith(`${ESC}[`))
|
|
55
|
+
return false;
|
|
56
|
+
// Check for known kitty sequence patterns:
|
|
57
|
+
// 1. ESC[<digit> - could be CSI-u or tilde-coded
|
|
58
|
+
// 2. ESC[1;<digit> - parameterized functional
|
|
59
|
+
// 3. ESC[<letter> - legacy functional keys
|
|
60
|
+
// 4. ESC[Z - reverse tab
|
|
61
|
+
const afterCSI = buffer.slice(2);
|
|
62
|
+
// Check if it starts with a digit (could be CSI-u or parameterized)
|
|
63
|
+
if (/^\d/.test(afterCSI))
|
|
64
|
+
return true;
|
|
65
|
+
// Check for known single-letter sequences
|
|
66
|
+
if (/^[ABCDHFPQRSZ]/.test(afterCSI))
|
|
67
|
+
return true;
|
|
68
|
+
// Check for 1; pattern (parameterized sequences)
|
|
69
|
+
if (/^1;\d/.test(afterCSI))
|
|
70
|
+
return true;
|
|
71
|
+
// Anything else starting with ESC[ that doesn't match our patterns
|
|
72
|
+
// is likely not a kitty sequence we handle
|
|
73
|
+
return false;
|
|
51
74
|
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
75
|
+
/**
|
|
76
|
+
* Parses a single complete kitty/parameterized/legacy sequence from the start
|
|
77
|
+
* of the buffer.
|
|
78
|
+
*
|
|
79
|
+
* This enables peel-and-continue parsing for batched input, allowing us to
|
|
80
|
+
* "peel off" one complete event when multiple sequences arrive in a single
|
|
81
|
+
* chunk, preventing buffer overflow and fragmentation.
|
|
82
|
+
*
|
|
83
|
+
* @param buffer - The input buffer string to parse.
|
|
84
|
+
* @returns The parsed Key and the number of characters consumed, or null if
|
|
85
|
+
* no complete sequence is found at the start of the buffer.
|
|
86
|
+
*/
|
|
87
|
+
function parseKittyPrefix(buffer) {
|
|
88
|
+
// In older terminals ESC [ Z was used as Cursor Backward Tabulation (CBT)
|
|
89
|
+
// In newer terminals the same functionality of key combination for moving
|
|
90
|
+
// backward through focusable elements is Shift+Tab, hence we will
|
|
91
|
+
// map ESC [ Z to Shift+Tab
|
|
92
|
+
// 0) Reverse Tab (legacy): ESC [ Z
|
|
93
|
+
// Treat as Shift+Tab for UI purposes.
|
|
94
|
+
// Regex parts:
|
|
95
|
+
// ^ - start of buffer
|
|
96
|
+
// ESC [ - CSI introducer
|
|
97
|
+
// Z - legacy reverse tab
|
|
98
|
+
const revTabLegacy = new RegExp(`^${ESC}\\[Z`);
|
|
99
|
+
let m = buffer.match(revTabLegacy);
|
|
100
|
+
if (m) {
|
|
101
|
+
return {
|
|
102
|
+
key: {
|
|
103
|
+
name: 'tab',
|
|
104
|
+
ctrl: false,
|
|
105
|
+
meta: false,
|
|
106
|
+
shift: true,
|
|
107
|
+
paste: false,
|
|
108
|
+
sequence: buffer.slice(0, m[0].length),
|
|
109
|
+
kittyProtocol: true,
|
|
110
|
+
},
|
|
111
|
+
length: m[0].length,
|
|
70
112
|
};
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
113
|
+
}
|
|
114
|
+
// 1) Reverse Tab (parameterized): ESC [ 1 ; <mods> Z
|
|
115
|
+
// Parameterized reverse Tab: ESC [ 1 ; <mods> Z
|
|
116
|
+
const revTabParam = new RegExp(`^${ESC}\\[1;(\\d+)Z`);
|
|
117
|
+
m = buffer.match(revTabParam);
|
|
118
|
+
if (m) {
|
|
119
|
+
let mods = parseInt(m[1], 10);
|
|
120
|
+
if (mods >= KITTY_MODIFIER_EVENT_TYPES_OFFSET) {
|
|
121
|
+
mods -= KITTY_MODIFIER_EVENT_TYPES_OFFSET;
|
|
74
122
|
}
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
123
|
+
const bits = mods - KITTY_MODIFIER_BASE;
|
|
124
|
+
const alt = (bits & MODIFIER_ALT_BIT) === MODIFIER_ALT_BIT;
|
|
125
|
+
const ctrl = (bits & MODIFIER_CTRL_BIT) === MODIFIER_CTRL_BIT;
|
|
126
|
+
return {
|
|
127
|
+
key: {
|
|
128
|
+
name: 'tab',
|
|
129
|
+
ctrl,
|
|
130
|
+
meta: alt,
|
|
131
|
+
// Reverse tab implies Shift behavior; force shift regardless of mods
|
|
132
|
+
shift: true,
|
|
133
|
+
paste: false,
|
|
134
|
+
sequence: buffer.slice(0, m[0].length),
|
|
135
|
+
kittyProtocol: true,
|
|
136
|
+
},
|
|
137
|
+
length: m[0].length,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
// 2) Parameterized functional: ESC [ 1 ; <mods> (A|B|C|D|H|F|P|Q|R|S)
|
|
141
|
+
// 2) Parameterized functional: ESC [ 1 ; <mods> (A|B|C|D|H|F|P|Q|R|S)
|
|
142
|
+
// Arrows, Home/End, F1–F4 with modifiers encoded in <mods>.
|
|
143
|
+
const arrowPrefix = new RegExp(`^${ESC}\\[1;(\\d+)([ABCDHFPQSR])`);
|
|
144
|
+
m = buffer.match(arrowPrefix);
|
|
145
|
+
if (m) {
|
|
146
|
+
let mods = parseInt(m[1], 10);
|
|
147
|
+
if (mods >= KITTY_MODIFIER_EVENT_TYPES_OFFSET) {
|
|
148
|
+
mods -= KITTY_MODIFIER_EVENT_TYPES_OFFSET;
|
|
82
149
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
// 1. ESC[<digit> - could be CSI-u or tilde-coded
|
|
100
|
-
// 2. ESC[1;<digit> - parameterized functional
|
|
101
|
-
// 3. ESC[<letter> - legacy functional keys
|
|
102
|
-
// 4. ESC[Z - reverse tab
|
|
103
|
-
const afterCSI = buffer.slice(2);
|
|
104
|
-
// Check if it starts with a digit (could be CSI-u or parameterized)
|
|
105
|
-
if (/^\d/.test(afterCSI))
|
|
106
|
-
return true;
|
|
107
|
-
// Check for known single-letter sequences
|
|
108
|
-
if (/^[ABCDHFPQRSZ]/.test(afterCSI))
|
|
109
|
-
return true;
|
|
110
|
-
// Check for 1; pattern (parameterized sequences)
|
|
111
|
-
if (/^1;\d/.test(afterCSI))
|
|
112
|
-
return true;
|
|
113
|
-
// Anything else starting with ESC[ that doesn't match our patterns
|
|
114
|
-
// is likely not a kitty sequence we handle
|
|
115
|
-
return false;
|
|
150
|
+
const bits = mods - KITTY_MODIFIER_BASE;
|
|
151
|
+
const shift = (bits & MODIFIER_SHIFT_BIT) === MODIFIER_SHIFT_BIT;
|
|
152
|
+
const alt = (bits & MODIFIER_ALT_BIT) === MODIFIER_ALT_BIT;
|
|
153
|
+
const ctrl = (bits & MODIFIER_CTRL_BIT) === MODIFIER_CTRL_BIT;
|
|
154
|
+
const sym = m[2];
|
|
155
|
+
const symbolToName = {
|
|
156
|
+
A: 'up',
|
|
157
|
+
B: 'down',
|
|
158
|
+
C: 'right',
|
|
159
|
+
D: 'left',
|
|
160
|
+
H: 'home',
|
|
161
|
+
F: 'end',
|
|
162
|
+
P: 'f1',
|
|
163
|
+
Q: 'f2',
|
|
164
|
+
R: 'f3',
|
|
165
|
+
S: 'f4',
|
|
116
166
|
};
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
};
|
|
176
|
-
}
|
|
177
|
-
// 2) Parameterized functional: ESC [ 1 ; <mods> (A|B|C|D|H|F|P|Q|R|S)
|
|
178
|
-
// 2) Parameterized functional: ESC [ 1 ; <mods> (A|B|C|D|H|F|P|Q|R|S)
|
|
179
|
-
// Arrows, Home/End, F1–F4 with modifiers encoded in <mods>.
|
|
180
|
-
const arrowPrefix = new RegExp(`^${ESC}\\[1;(\\d+)([ABCDHFPQSR])`);
|
|
181
|
-
m = buffer.match(arrowPrefix);
|
|
182
|
-
if (m) {
|
|
183
|
-
let mods = parseInt(m[1], 10);
|
|
184
|
-
if (mods >= KITTY_MODIFIER_EVENT_TYPES_OFFSET) {
|
|
185
|
-
mods -= KITTY_MODIFIER_EVENT_TYPES_OFFSET;
|
|
186
|
-
}
|
|
187
|
-
const bits = mods - KITTY_MODIFIER_BASE;
|
|
188
|
-
const shift = (bits & MODIFIER_SHIFT_BIT) === MODIFIER_SHIFT_BIT;
|
|
189
|
-
const alt = (bits & MODIFIER_ALT_BIT) === MODIFIER_ALT_BIT;
|
|
190
|
-
const ctrl = (bits & MODIFIER_CTRL_BIT) === MODIFIER_CTRL_BIT;
|
|
191
|
-
const sym = m[2];
|
|
192
|
-
const symbolToName = {
|
|
193
|
-
A: 'up',
|
|
194
|
-
B: 'down',
|
|
195
|
-
C: 'right',
|
|
196
|
-
D: 'left',
|
|
197
|
-
H: 'home',
|
|
198
|
-
F: 'end',
|
|
199
|
-
P: 'f1',
|
|
200
|
-
Q: 'f2',
|
|
201
|
-
R: 'f3',
|
|
202
|
-
S: 'f4',
|
|
203
|
-
};
|
|
204
|
-
const name = symbolToName[sym] || '';
|
|
205
|
-
if (!name)
|
|
206
|
-
return null;
|
|
167
|
+
const name = symbolToName[sym] || '';
|
|
168
|
+
if (!name)
|
|
169
|
+
return null;
|
|
170
|
+
return {
|
|
171
|
+
key: {
|
|
172
|
+
name,
|
|
173
|
+
ctrl,
|
|
174
|
+
meta: alt,
|
|
175
|
+
shift,
|
|
176
|
+
paste: false,
|
|
177
|
+
sequence: buffer.slice(0, m[0].length),
|
|
178
|
+
kittyProtocol: true,
|
|
179
|
+
},
|
|
180
|
+
length: m[0].length,
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
// 3) CSI-u form: ESC [ <code> ; <mods> (u|~)
|
|
184
|
+
// 3) CSI-u and tilde-coded functional keys: ESC [ <code> ; <mods> (u|~)
|
|
185
|
+
// 'u' terminator: Kitty CSI-u; '~' terminator: tilde-coded function keys.
|
|
186
|
+
const csiUPrefix = new RegExp(`^${ESC}\\[(\\d+)(;(\\d+))?([u~])`);
|
|
187
|
+
m = buffer.match(csiUPrefix);
|
|
188
|
+
if (m) {
|
|
189
|
+
const keyCode = parseInt(m[1], 10);
|
|
190
|
+
let modifiers = m[3] ? parseInt(m[3], 10) : KITTY_MODIFIER_BASE;
|
|
191
|
+
if (modifiers >= KITTY_MODIFIER_EVENT_TYPES_OFFSET) {
|
|
192
|
+
modifiers -= KITTY_MODIFIER_EVENT_TYPES_OFFSET;
|
|
193
|
+
}
|
|
194
|
+
const modifierBits = modifiers - KITTY_MODIFIER_BASE;
|
|
195
|
+
const shift = (modifierBits & MODIFIER_SHIFT_BIT) === MODIFIER_SHIFT_BIT;
|
|
196
|
+
const alt = (modifierBits & MODIFIER_ALT_BIT) === MODIFIER_ALT_BIT;
|
|
197
|
+
const ctrl = (modifierBits & MODIFIER_CTRL_BIT) === MODIFIER_CTRL_BIT;
|
|
198
|
+
const terminator = m[4];
|
|
199
|
+
// Tilde-coded functional keys (Delete, Insert, PageUp/Down, Home/End)
|
|
200
|
+
if (terminator === '~') {
|
|
201
|
+
let name = null;
|
|
202
|
+
switch (keyCode) {
|
|
203
|
+
case 1:
|
|
204
|
+
name = 'home';
|
|
205
|
+
break;
|
|
206
|
+
case 2:
|
|
207
|
+
name = 'insert';
|
|
208
|
+
break;
|
|
209
|
+
case 3:
|
|
210
|
+
name = 'delete';
|
|
211
|
+
break;
|
|
212
|
+
case 4:
|
|
213
|
+
name = 'end';
|
|
214
|
+
break;
|
|
215
|
+
case 5:
|
|
216
|
+
name = 'pageup';
|
|
217
|
+
break;
|
|
218
|
+
case 6:
|
|
219
|
+
name = 'pagedown';
|
|
220
|
+
break;
|
|
221
|
+
default:
|
|
222
|
+
break;
|
|
223
|
+
}
|
|
224
|
+
if (name) {
|
|
207
225
|
return {
|
|
208
226
|
key: {
|
|
209
227
|
name,
|
|
@@ -217,136 +235,207 @@ export function KeypressProvider({ children, kittyProtocolEnabled, config, debug
|
|
|
217
235
|
length: m[0].length,
|
|
218
236
|
};
|
|
219
237
|
}
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
238
|
+
}
|
|
239
|
+
const kittyKeyCodeToName = {
|
|
240
|
+
[CHAR_CODE_ESC]: 'escape',
|
|
241
|
+
[KITTY_KEYCODE_TAB]: 'tab',
|
|
242
|
+
[KITTY_KEYCODE_BACKSPACE]: 'backspace',
|
|
243
|
+
[KITTY_KEYCODE_ENTER]: 'return',
|
|
244
|
+
[KITTY_KEYCODE_NUMPAD_ENTER]: 'return',
|
|
245
|
+
};
|
|
246
|
+
const name = kittyKeyCodeToName[keyCode];
|
|
247
|
+
if (name) {
|
|
248
|
+
return {
|
|
249
|
+
key: {
|
|
250
|
+
name,
|
|
251
|
+
ctrl,
|
|
252
|
+
meta: alt,
|
|
253
|
+
shift,
|
|
254
|
+
paste: false,
|
|
255
|
+
sequence: buffer.slice(0, m[0].length),
|
|
256
|
+
kittyProtocol: true,
|
|
257
|
+
},
|
|
258
|
+
length: m[0].length,
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
// Ctrl+letters and Alt+letters
|
|
262
|
+
if ((ctrl || alt) &&
|
|
263
|
+
keyCode >= 'a'.charCodeAt(0) &&
|
|
264
|
+
keyCode <= 'z'.charCodeAt(0)) {
|
|
265
|
+
const letter = String.fromCharCode(keyCode);
|
|
266
|
+
return {
|
|
267
|
+
key: {
|
|
268
|
+
name: letter,
|
|
269
|
+
ctrl,
|
|
270
|
+
meta: alt,
|
|
271
|
+
shift,
|
|
272
|
+
paste: false,
|
|
273
|
+
sequence: buffer.slice(0, m[0].length),
|
|
274
|
+
kittyProtocol: true,
|
|
275
|
+
},
|
|
276
|
+
length: m[0].length,
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
// 4) Legacy function keys (no parameters): ESC [ (A|B|C|D|H|F)
|
|
281
|
+
// Arrows + Home/End without modifiers.
|
|
282
|
+
const legacyFuncKey = new RegExp(`^${ESC}\\[([ABCDHF])`);
|
|
283
|
+
m = buffer.match(legacyFuncKey);
|
|
284
|
+
if (m) {
|
|
285
|
+
const sym = m[1];
|
|
286
|
+
const nameMap = {
|
|
287
|
+
A: 'up',
|
|
288
|
+
B: 'down',
|
|
289
|
+
C: 'right',
|
|
290
|
+
D: 'left',
|
|
291
|
+
H: 'home',
|
|
292
|
+
F: 'end',
|
|
293
|
+
};
|
|
294
|
+
const name = nameMap[sym];
|
|
295
|
+
return {
|
|
296
|
+
key: {
|
|
297
|
+
name,
|
|
298
|
+
ctrl: false,
|
|
299
|
+
meta: false,
|
|
300
|
+
shift: false,
|
|
301
|
+
paste: false,
|
|
302
|
+
sequence: buffer.slice(0, m[0].length),
|
|
303
|
+
kittyProtocol: true,
|
|
304
|
+
},
|
|
305
|
+
length: m[0].length,
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
return null;
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* Returns the first index before which we are certain there is no paste marker.
|
|
312
|
+
*/
|
|
313
|
+
function earliestPossiblePasteMarker(data) {
|
|
314
|
+
// Check data for full start-paste or end-paste markers.
|
|
315
|
+
const startIndex = data.indexOf(PASTE_MODE_START);
|
|
316
|
+
const endIndex = data.indexOf(PASTE_MODE_END);
|
|
317
|
+
if (startIndex !== -1 && endIndex !== -1) {
|
|
318
|
+
return Math.min(startIndex, endIndex);
|
|
319
|
+
}
|
|
320
|
+
else if (startIndex !== -1) {
|
|
321
|
+
return startIndex;
|
|
322
|
+
}
|
|
323
|
+
else if (endIndex !== -1) {
|
|
324
|
+
return endIndex;
|
|
325
|
+
}
|
|
326
|
+
// data contains no full start-paste or end-paste.
|
|
327
|
+
// Check if data ends with a prefix of start-paste or end-paste.
|
|
328
|
+
const codeLength = PASTE_MODE_START.length;
|
|
329
|
+
for (let i = Math.min(data.length, codeLength - 1); i > 0; i--) {
|
|
330
|
+
const candidate = data.slice(data.length - i);
|
|
331
|
+
if (PASTE_MODE_START.indexOf(candidate) === 0 ||
|
|
332
|
+
PASTE_MODE_END.indexOf(candidate) === 0) {
|
|
333
|
+
return data.length - i;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
return data.length;
|
|
337
|
+
}
|
|
338
|
+
/**
|
|
339
|
+
* A generator that takes in data chunks and spits out paste-start and
|
|
340
|
+
* paste-end keypresses. All non-paste marker data is passed to passthrough.
|
|
341
|
+
*/
|
|
342
|
+
function* pasteMarkerParser(passthrough, keypressHandler) {
|
|
343
|
+
while (true) {
|
|
344
|
+
let data = yield;
|
|
345
|
+
if (data.length === 0) {
|
|
346
|
+
continue; // we timed out
|
|
347
|
+
}
|
|
348
|
+
while (true) {
|
|
349
|
+
const index = earliestPossiblePasteMarker(data);
|
|
350
|
+
if (index === data.length) {
|
|
351
|
+
// no possible paste markers were found
|
|
352
|
+
passthrough.write(data);
|
|
353
|
+
break;
|
|
354
|
+
}
|
|
355
|
+
if (index > 0) {
|
|
356
|
+
// snip off and send the part that doesn't have a paste marker
|
|
357
|
+
passthrough.write(data.slice(0, index));
|
|
358
|
+
data = data.slice(index);
|
|
359
|
+
}
|
|
360
|
+
// data starts with a possible paste marker
|
|
361
|
+
const codeLength = PASTE_MODE_START.length;
|
|
362
|
+
if (data.length < codeLength) {
|
|
363
|
+
// we have a prefix. Concat the next data and try again.
|
|
364
|
+
const newData = yield;
|
|
365
|
+
if (newData.length === 0) {
|
|
366
|
+
// we timed out. Just dump what we have and start over.
|
|
367
|
+
passthrough.write(data);
|
|
368
|
+
break;
|
|
315
369
|
}
|
|
370
|
+
data += newData;
|
|
316
371
|
}
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
D: 'left',
|
|
328
|
-
H: 'home',
|
|
329
|
-
F: 'end',
|
|
330
|
-
};
|
|
331
|
-
const name = nameMap[sym];
|
|
332
|
-
return {
|
|
333
|
-
key: {
|
|
334
|
-
name,
|
|
335
|
-
ctrl: false,
|
|
336
|
-
meta: false,
|
|
337
|
-
shift: false,
|
|
338
|
-
paste: false,
|
|
339
|
-
sequence: buffer.slice(0, m[0].length),
|
|
340
|
-
kittyProtocol: true,
|
|
341
|
-
},
|
|
342
|
-
length: m[0].length,
|
|
343
|
-
};
|
|
372
|
+
else if (data.startsWith(PASTE_MODE_START)) {
|
|
373
|
+
keypressHandler(undefined, {
|
|
374
|
+
name: 'paste-start',
|
|
375
|
+
ctrl: false,
|
|
376
|
+
meta: false,
|
|
377
|
+
shift: false,
|
|
378
|
+
paste: false,
|
|
379
|
+
sequence: '',
|
|
380
|
+
});
|
|
381
|
+
data = data.slice(PASTE_MODE_START.length);
|
|
344
382
|
}
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
383
|
+
else if (data.startsWith(PASTE_MODE_END)) {
|
|
384
|
+
keypressHandler(undefined, {
|
|
385
|
+
name: 'paste-end',
|
|
386
|
+
ctrl: false,
|
|
387
|
+
meta: false,
|
|
388
|
+
shift: false,
|
|
389
|
+
paste: false,
|
|
390
|
+
sequence: '',
|
|
391
|
+
});
|
|
392
|
+
data = data.slice(PASTE_MODE_END.length);
|
|
393
|
+
}
|
|
394
|
+
else {
|
|
395
|
+
// This should never happen.
|
|
396
|
+
passthrough.write(data);
|
|
397
|
+
break;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
const KeypressContext = createContext(undefined);
|
|
403
|
+
export function useKeypressContext() {
|
|
404
|
+
const context = useContext(KeypressContext);
|
|
405
|
+
if (!context) {
|
|
406
|
+
throw new Error('useKeypressContext must be used within a KeypressProvider');
|
|
407
|
+
}
|
|
408
|
+
return context;
|
|
409
|
+
}
|
|
410
|
+
function shouldUsePassthrough() {
|
|
411
|
+
return process.env['PASTE_WORKAROUND'] !== 'false';
|
|
412
|
+
}
|
|
413
|
+
export function KeypressProvider({ children, kittyProtocolEnabled, config, debugKeystrokeLogging, }) {
|
|
414
|
+
const { stdin, setRawMode } = useStdin();
|
|
415
|
+
const subscribers = useRef(new Set()).current;
|
|
416
|
+
const subscribe = useCallback((handler) => subscribers.add(handler), [subscribers]);
|
|
417
|
+
const unsubscribe = useCallback((handler) => subscribers.delete(handler), [subscribers]);
|
|
418
|
+
const broadcast = useCallback((key) => subscribers.forEach((handler) => handler(key)), [subscribers]);
|
|
419
|
+
useEffect(() => {
|
|
420
|
+
const wasRaw = stdin.isRaw;
|
|
421
|
+
if (wasRaw === false) {
|
|
422
|
+
setRawMode(true);
|
|
423
|
+
}
|
|
424
|
+
const keypressStream = shouldUsePassthrough() ? new PassThrough() : null;
|
|
425
|
+
// If non-null that means we are in paste mode
|
|
426
|
+
let pasteBuffer = null;
|
|
427
|
+
// Used to turn "\" quickly followed by a "enter" into a shift enter
|
|
428
|
+
let backslashTimeout = null;
|
|
429
|
+
// Buffers incomplete Kitty sequences and timer to flush it
|
|
430
|
+
let kittySequenceBuffer = '';
|
|
431
|
+
let kittySequenceTimeout = null;
|
|
432
|
+
// Used to detect filename drag-and-drops.
|
|
433
|
+
let dragBuffer = '';
|
|
434
|
+
let draggingTimer = null;
|
|
435
|
+
const clearDraggingTimer = () => {
|
|
436
|
+
if (draggingTimer) {
|
|
437
|
+
clearTimeout(draggingTimer);
|
|
438
|
+
draggingTimer = null;
|
|
350
439
|
}
|
|
351
440
|
};
|
|
352
441
|
const flushKittyBufferOnInterrupt = (reason) => {
|
|
@@ -376,36 +465,36 @@ export function KeypressProvider({ children, kittyProtocolEnabled, config, debug
|
|
|
376
465
|
}
|
|
377
466
|
if (key.name === 'paste-start') {
|
|
378
467
|
flushKittyBufferOnInterrupt('paste start');
|
|
379
|
-
|
|
468
|
+
pasteBuffer = Buffer.alloc(0);
|
|
380
469
|
return;
|
|
381
470
|
}
|
|
382
471
|
if (key.name === 'paste-end') {
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
472
|
+
if (pasteBuffer !== null) {
|
|
473
|
+
broadcast({
|
|
474
|
+
name: '',
|
|
475
|
+
ctrl: false,
|
|
476
|
+
meta: false,
|
|
477
|
+
shift: false,
|
|
478
|
+
paste: true,
|
|
479
|
+
sequence: pasteBuffer.toString(),
|
|
480
|
+
});
|
|
481
|
+
}
|
|
482
|
+
pasteBuffer = null;
|
|
393
483
|
return;
|
|
394
484
|
}
|
|
395
|
-
if (
|
|
485
|
+
if (pasteBuffer !== null) {
|
|
396
486
|
pasteBuffer = Buffer.concat([pasteBuffer, Buffer.from(key.sequence)]);
|
|
397
487
|
return;
|
|
398
488
|
}
|
|
399
489
|
if (key.sequence === SINGLE_QUOTE ||
|
|
400
490
|
key.sequence === DOUBLE_QUOTE ||
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
dragBufferRef.current += key.sequence;
|
|
491
|
+
draggingTimer !== null) {
|
|
492
|
+
dragBuffer += key.sequence;
|
|
404
493
|
clearDraggingTimer();
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
const seq =
|
|
408
|
-
|
|
494
|
+
draggingTimer = setTimeout(() => {
|
|
495
|
+
draggingTimer = null;
|
|
496
|
+
const seq = dragBuffer;
|
|
497
|
+
dragBuffer = '';
|
|
409
498
|
if (seq) {
|
|
410
499
|
broadcast({ ...key, name: '', paste: true, sequence: seq });
|
|
411
500
|
}
|
|
@@ -419,17 +508,14 @@ export function KeypressProvider({ children, kittyProtocolEnabled, config, debug
|
|
|
419
508
|
ctrl: false,
|
|
420
509
|
meta: true,
|
|
421
510
|
shift: false,
|
|
422
|
-
paste:
|
|
511
|
+
paste: pasteBuffer !== null,
|
|
423
512
|
sequence: key.sequence,
|
|
424
513
|
});
|
|
425
514
|
return;
|
|
426
515
|
}
|
|
427
|
-
if (key.name === 'return' &&
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
backslashTimeout = null;
|
|
431
|
-
}
|
|
432
|
-
waitingForEnterAfterBackslash = false;
|
|
516
|
+
if (key.name === 'return' && backslashTimeout !== null) {
|
|
517
|
+
clearTimeout(backslashTimeout);
|
|
518
|
+
backslashTimeout = null;
|
|
433
519
|
broadcast({
|
|
434
520
|
...key,
|
|
435
521
|
shift: true,
|
|
@@ -439,20 +525,15 @@ export function KeypressProvider({ children, kittyProtocolEnabled, config, debug
|
|
|
439
525
|
}
|
|
440
526
|
if (key.sequence === '\\' && !key.name) {
|
|
441
527
|
// Corrected escaping for backslash
|
|
442
|
-
waitingForEnterAfterBackslash = true;
|
|
443
528
|
backslashTimeout = setTimeout(() => {
|
|
444
|
-
waitingForEnterAfterBackslash = false;
|
|
445
529
|
backslashTimeout = null;
|
|
446
530
|
broadcast(key);
|
|
447
531
|
}, BACKSLASH_ENTER_DETECTION_WINDOW_MS);
|
|
448
532
|
return;
|
|
449
533
|
}
|
|
450
|
-
if (
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
backslashTimeout = null;
|
|
454
|
-
}
|
|
455
|
-
waitingForEnterAfterBackslash = false;
|
|
534
|
+
if (backslashTimeout !== null && key.name !== 'return') {
|
|
535
|
+
clearTimeout(backslashTimeout);
|
|
536
|
+
backslashTimeout = null;
|
|
456
537
|
broadcast({
|
|
457
538
|
name: '',
|
|
458
539
|
sequence: '\\',
|
|
@@ -501,8 +582,8 @@ export function KeypressProvider({ children, kittyProtocolEnabled, config, debug
|
|
|
501
582
|
// Check if this could start a kitty sequence
|
|
502
583
|
const startsWithEsc = key.sequence.startsWith(ESC);
|
|
503
584
|
const isExcluded = [
|
|
504
|
-
|
|
505
|
-
|
|
585
|
+
PASTE_MODE_START,
|
|
586
|
+
PASTE_MODE_END,
|
|
506
587
|
FOCUS_IN,
|
|
507
588
|
FOCUS_OUT,
|
|
508
589
|
].some((prefix) => key.sequence.startsWith(prefix));
|
|
@@ -614,74 +695,40 @@ export function KeypressProvider({ children, kittyProtocolEnabled, config, debug
|
|
|
614
695
|
if (key.name === 'return' && key.sequence === `${ESC}\r`) {
|
|
615
696
|
key.meta = true;
|
|
616
697
|
}
|
|
617
|
-
broadcast({ ...key, paste:
|
|
618
|
-
};
|
|
619
|
-
const handleRawKeypress = (data) => {
|
|
620
|
-
const pasteModePrefixBuffer = Buffer.from(PASTE_MODE_PREFIX);
|
|
621
|
-
const pasteModeSuffixBuffer = Buffer.from(PASTE_MODE_SUFFIX);
|
|
622
|
-
let pos = 0;
|
|
623
|
-
while (pos < data.length) {
|
|
624
|
-
const prefixPos = data.indexOf(pasteModePrefixBuffer, pos);
|
|
625
|
-
const suffixPos = data.indexOf(pasteModeSuffixBuffer, pos);
|
|
626
|
-
const isPrefixNext = prefixPos !== -1 && (suffixPos === -1 || prefixPos < suffixPos);
|
|
627
|
-
const isSuffixNext = suffixPos !== -1 && (prefixPos === -1 || suffixPos < prefixPos);
|
|
628
|
-
let nextMarkerPos = -1;
|
|
629
|
-
let markerLength = 0;
|
|
630
|
-
if (isPrefixNext) {
|
|
631
|
-
nextMarkerPos = prefixPos;
|
|
632
|
-
}
|
|
633
|
-
else if (isSuffixNext) {
|
|
634
|
-
nextMarkerPos = suffixPos;
|
|
635
|
-
}
|
|
636
|
-
markerLength = pasteModeSuffixBuffer.length;
|
|
637
|
-
if (nextMarkerPos === -1) {
|
|
638
|
-
keypressStream.write(data.slice(pos));
|
|
639
|
-
return;
|
|
640
|
-
}
|
|
641
|
-
const nextData = data.slice(pos, nextMarkerPos);
|
|
642
|
-
if (nextData.length > 0) {
|
|
643
|
-
keypressStream.write(nextData);
|
|
644
|
-
}
|
|
645
|
-
const createPasteKeyEvent = (name) => ({
|
|
646
|
-
name,
|
|
647
|
-
ctrl: false,
|
|
648
|
-
meta: false,
|
|
649
|
-
shift: false,
|
|
650
|
-
paste: false,
|
|
651
|
-
sequence: '',
|
|
652
|
-
});
|
|
653
|
-
if (isPrefixNext) {
|
|
654
|
-
handleKeypress(undefined, createPasteKeyEvent('paste-start'));
|
|
655
|
-
}
|
|
656
|
-
else if (isSuffixNext) {
|
|
657
|
-
handleKeypress(undefined, createPasteKeyEvent('paste-end'));
|
|
658
|
-
}
|
|
659
|
-
pos = nextMarkerPos + markerLength;
|
|
660
|
-
}
|
|
698
|
+
broadcast({ ...key, paste: pasteBuffer !== null });
|
|
661
699
|
};
|
|
700
|
+
let cleanup = () => { };
|
|
662
701
|
let rl;
|
|
663
|
-
if (
|
|
702
|
+
if (keypressStream !== null) {
|
|
664
703
|
rl = readline.createInterface({
|
|
665
704
|
input: keypressStream,
|
|
666
705
|
escapeCodeTimeout: 0,
|
|
667
706
|
});
|
|
668
707
|
readline.emitKeypressEvents(keypressStream, rl);
|
|
708
|
+
const parser = pasteMarkerParser(keypressStream, handleKeypress);
|
|
709
|
+
parser.next(); // prime the generator so it starts listening.
|
|
710
|
+
let timeoutId;
|
|
711
|
+
const handleRawKeypress = (data) => {
|
|
712
|
+
clearTimeout(timeoutId);
|
|
713
|
+
parser.next(data);
|
|
714
|
+
timeoutId = setTimeout(() => parser.next(''), PASTE_CODE_TIMEOUT_MS);
|
|
715
|
+
};
|
|
669
716
|
keypressStream.on('keypress', handleKeypress);
|
|
717
|
+
process.stdin.setEncoding('utf8'); // so handleRawKeypress gets strings
|
|
670
718
|
stdin.on('data', handleRawKeypress);
|
|
719
|
+
cleanup = () => {
|
|
720
|
+
keypressStream.removeListener('keypress', handleKeypress);
|
|
721
|
+
stdin.removeListener('data', handleRawKeypress);
|
|
722
|
+
};
|
|
671
723
|
}
|
|
672
724
|
else {
|
|
673
725
|
rl = readline.createInterface({ input: stdin, escapeCodeTimeout: 0 });
|
|
674
726
|
readline.emitKeypressEvents(stdin, rl);
|
|
675
727
|
stdin.on('keypress', handleKeypress);
|
|
728
|
+
cleanup = () => stdin.removeListener('keypress', handleKeypress);
|
|
676
729
|
}
|
|
677
730
|
return () => {
|
|
678
|
-
|
|
679
|
-
keypressStream.removeListener('keypress', handleKeypress);
|
|
680
|
-
stdin.removeListener('data', handleRawKeypress);
|
|
681
|
-
}
|
|
682
|
-
else {
|
|
683
|
-
stdin.removeListener('keypress', handleKeypress);
|
|
684
|
-
}
|
|
731
|
+
cleanup();
|
|
685
732
|
rl.close();
|
|
686
733
|
// Restore the terminal to its original state.
|
|
687
734
|
if (wasRaw === false) {
|
|
@@ -708,7 +755,7 @@ export function KeypressProvider({ children, kittyProtocolEnabled, config, debug
|
|
|
708
755
|
kittySequenceBuffer = '';
|
|
709
756
|
}
|
|
710
757
|
// Flush any pending paste data to avoid data loss on exit.
|
|
711
|
-
if (
|
|
758
|
+
if (pasteBuffer !== null) {
|
|
712
759
|
broadcast({
|
|
713
760
|
name: '',
|
|
714
761
|
ctrl: false,
|
|
@@ -717,23 +764,19 @@ export function KeypressProvider({ children, kittyProtocolEnabled, config, debug
|
|
|
717
764
|
paste: true,
|
|
718
765
|
sequence: pasteBuffer.toString(),
|
|
719
766
|
});
|
|
720
|
-
pasteBuffer =
|
|
721
|
-
}
|
|
722
|
-
if (draggingTimerRef.current) {
|
|
723
|
-
clearTimeout(draggingTimerRef.current);
|
|
724
|
-
draggingTimerRef.current = null;
|
|
767
|
+
pasteBuffer = null;
|
|
725
768
|
}
|
|
726
|
-
|
|
769
|
+
clearDraggingTimer();
|
|
770
|
+
if (dragBuffer) {
|
|
727
771
|
broadcast({
|
|
728
772
|
name: '',
|
|
729
773
|
ctrl: false,
|
|
730
774
|
meta: false,
|
|
731
775
|
shift: false,
|
|
732
776
|
paste: true,
|
|
733
|
-
sequence:
|
|
777
|
+
sequence: dragBuffer,
|
|
734
778
|
});
|
|
735
|
-
|
|
736
|
-
dragBufferRef.current = '';
|
|
779
|
+
dragBuffer = '';
|
|
737
780
|
}
|
|
738
781
|
};
|
|
739
782
|
}, [
|
|
@@ -741,8 +784,8 @@ export function KeypressProvider({ children, kittyProtocolEnabled, config, debug
|
|
|
741
784
|
setRawMode,
|
|
742
785
|
kittyProtocolEnabled,
|
|
743
786
|
config,
|
|
744
|
-
subscribers,
|
|
745
787
|
debugKeystrokeLogging,
|
|
788
|
+
broadcast,
|
|
746
789
|
]);
|
|
747
790
|
return (_jsx(KeypressContext.Provider, { value: { subscribe, unsubscribe }, children: children }));
|
|
748
791
|
}
|