@machina.ai/cell-cli 1.11.0-rc1 → 1.13.0-rc2
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 +16 -10
- package/dist/src/commands/extensions/disable.d.ts +1 -1
- package/dist/src/commands/extensions/disable.js +15 -7
- 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 +15 -7
- package/dist/src/commands/extensions/enable.js.map +1 -1
- package/dist/src/commands/extensions/install.js +14 -3
- package/dist/src/commands/extensions/install.js.map +1 -1
- package/dist/src/commands/extensions/install.test.js +39 -19
- package/dist/src/commands/extensions/install.test.js.map +1 -1
- package/dist/src/commands/extensions/link.js +14 -3
- 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 +13 -2
- package/dist/src/commands/extensions/uninstall.js.map +1 -1
- package/dist/src/commands/extensions/update.js +18 -13
- package/dist/src/commands/extensions/update.js.map +1 -1
- package/dist/src/commands/extensions/validate.d.ts +12 -0
- package/dist/src/commands/extensions/validate.js +83 -0
- package/dist/src/commands/extensions/validate.js.map +1 -0
- package/dist/src/commands/extensions/validate.test.js +93 -0
- package/dist/src/commands/extensions/validate.test.js.map +1 -0
- package/dist/src/commands/extensions.js +3 -0
- package/dist/src/commands/extensions.js.map +1 -1
- package/dist/src/commands/mcp/add.test.js +3 -0
- package/dist/src/commands/mcp/add.test.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 +37 -27
- package/dist/src/commands/mcp/list.test.js.map +1 -1
- package/dist/src/config/auth.js +0 -5
- package/dist/src/config/auth.js.map +1 -1
- package/dist/src/config/config.d.ts +6 -3
- package/dist/src/config/config.js +65 -80
- package/dist/src/config/config.js.map +1 -1
- package/dist/src/config/config.test.js +235 -212
- package/dist/src/config/config.test.js.map +1 -1
- package/dist/src/config/extension-manager.d.ts +63 -0
- package/dist/src/config/extension-manager.js +450 -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 +525 -201
- 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 +113 -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 +254 -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 +5 -10
- package/dist/src/config/extensions/github.js.map +1 -1
- package/dist/src/config/extensions/github.test.js +153 -167
- 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 +39 -39
- package/dist/src/config/extensions/update.js.map +1 -1
- package/dist/src/config/extensions/update.test.js +72 -74
- 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/keyBindings.d.ts +3 -0
- package/dist/src/config/keyBindings.js +30 -8
- package/dist/src/config/keyBindings.js.map +1 -1
- package/dist/src/config/keyBindings.test.js +17 -0
- package/dist/src/config/keyBindings.test.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.d.ts +2 -2
- package/dist/src/config/policy.js +10 -148
- package/dist/src/config/policy.js.map +1 -1
- package/dist/src/config/sandboxConfig.d.ts +1 -1
- package/dist/src/config/sandboxConfig.js +6 -3
- package/dist/src/config/sandboxConfig.js.map +1 -1
- package/dist/src/config/settings.d.ts +2 -1
- package/dist/src/config/settings.js +58 -18
- package/dist/src/config/settings.js.map +1 -1
- package/dist/src/config/settings.test.js +128 -69
- package/dist/src/config/settings.test.js.map +1 -1
- package/dist/src/config/settingsSchema.d.ts +170 -28
- package/dist/src/config/settingsSchema.js +418 -27
- package/dist/src/config/settingsSchema.js.map +1 -1
- package/dist/src/config/settingsSchema.test.js +42 -1
- package/dist/src/config/settingsSchema.test.js.map +1 -1
- package/dist/src/config/trustedFolders.d.ts +1 -1
- package/dist/src/config/trustedFolders.js +4 -2
- package/dist/src/config/trustedFolders.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.d.ts +1 -1
- package/dist/src/gemini.js +46 -16
- package/dist/src/gemini.js.map +1 -1
- package/dist/src/gemini.test.js +88 -30
- 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.d.ts +9 -1
- package/dist/src/nonInteractiveCli.js +114 -7
- package/dist/src/nonInteractiveCli.js.map +1 -1
- package/dist/src/nonInteractiveCli.test.js +355 -112
- package/dist/src/nonInteractiveCli.test.js.map +1 -1
- package/dist/src/services/BuiltinCommandLoader.js +4 -0
- package/dist/src/services/BuiltinCommandLoader.js.map +1 -1
- package/dist/src/services/BuiltinCommandLoader.test.js +22 -0
- package/dist/src/services/BuiltinCommandLoader.test.js.map +1 -1
- package/dist/src/services/FeedbackService.js +2 -2
- package/dist/src/services/FeedbackService.js.map +1 -1
- package/dist/src/services/McpPromptLoader.js +2 -2
- package/dist/src/services/McpPromptLoader.js.map +1 -1
- package/dist/src/services/McpPromptLoader.test.js +4 -2
- package/dist/src/services/McpPromptLoader.test.js.map +1 -1
- package/dist/src/test-utils/async.d.ts +9 -0
- package/dist/src/test-utils/async.js +29 -0
- package/dist/src/test-utils/async.js.map +1 -0
- 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/test-utils/render.d.ts +16 -2
- package/dist/src/test-utils/render.js +66 -4
- 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 +79 -0
- package/dist/src/test-utils/render.test.js.map +1 -0
- package/dist/src/ui/App.test.js +1 -1
- package/dist/src/ui/App.test.js.map +1 -1
- package/dist/src/ui/AppContainer.js +181 -65
- package/dist/src/ui/AppContainer.js.map +1 -1
- package/dist/src/ui/AppContainer.test.js +505 -147
- package/dist/src/ui/AppContainer.test.js.map +1 -1
- package/dist/src/ui/IdeIntegrationNudge.js +1 -1
- package/dist/src/ui/IdeIntegrationNudge.js.map +1 -1
- package/dist/src/ui/auth/ApiAuthDialog.d.ts +14 -0
- package/dist/src/ui/auth/ApiAuthDialog.js +26 -0
- package/dist/src/ui/auth/ApiAuthDialog.js.map +1 -0
- package/dist/src/ui/auth/ApiAuthDialog.test.d.ts +6 -0
- package/dist/src/ui/auth/ApiAuthDialog.test.js +91 -0
- package/dist/src/ui/auth/ApiAuthDialog.test.js.map +1 -0
- package/dist/src/ui/auth/AuthDialog.js +7 -3
- package/dist/src/ui/auth/AuthDialog.js.map +1 -1
- package/dist/src/ui/auth/useAuth.d.ts +2 -0
- package/dist/src/ui/auth/useAuth.js +31 -2
- package/dist/src/ui/auth/useAuth.js.map +1 -1
- package/dist/src/ui/colors.js +3 -0
- package/dist/src/ui/colors.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 +64 -11
- package/dist/src/ui/commands/extensionsCommand.js.map +1 -1
- package/dist/src/ui/commands/extensionsCommand.test.js +72 -1
- package/dist/src/ui/commands/extensionsCommand.test.js.map +1 -1
- package/dist/src/ui/commands/mcpCommand.js +14 -14
- package/dist/src/ui/commands/mcpCommand.js.map +1 -1
- package/dist/src/ui/commands/mcpCommand.test.js +4 -0
- package/dist/src/ui/commands/mcpCommand.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/commands/policiesCommand.d.ts +7 -0
- package/dist/src/ui/commands/policiesCommand.js +59 -0
- package/dist/src/ui/commands/policiesCommand.js.map +1 -0
- package/dist/src/ui/commands/policiesCommand.test.d.ts +6 -0
- package/dist/src/ui/commands/policiesCommand.test.js +83 -0
- package/dist/src/ui/commands/policiesCommand.test.js.map +1 -0
- package/dist/src/ui/components/AnsiOutput.test.js +1 -1
- package/dist/src/ui/components/AnsiOutput.test.js.map +1 -1
- package/dist/src/ui/components/AsciiArt.d.ts +3 -3
- package/dist/src/ui/components/AsciiArt.js +3 -3
- package/dist/src/ui/components/Composer.js +1 -1
- package/dist/src/ui/components/Composer.js.map +1 -1
- package/dist/src/ui/components/Composer.test.js +5 -2
- package/dist/src/ui/components/Composer.test.js.map +1 -1
- package/dist/src/ui/components/ConfigInitDisplay.js +4 -6
- package/dist/src/ui/components/ConfigInitDisplay.js.map +1 -1
- package/dist/src/ui/components/ConsentPrompt.test.js +18 -8
- package/dist/src/ui/components/ConsentPrompt.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/ContextSummaryDisplay.test.js +11 -6
- package/dist/src/ui/components/ContextSummaryDisplay.test.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/DialogManager.js +4 -0
- package/dist/src/ui/components/DialogManager.js.map +1 -1
- package/dist/src/ui/components/FolderTrustDialog.test.js +2 -1
- package/dist/src/ui/components/FolderTrustDialog.test.js.map +1 -1
- package/dist/src/ui/components/Footer.js +4 -3
- package/dist/src/ui/components/Footer.js.map +1 -1
- package/dist/src/ui/components/Footer.test.js +83 -0
- package/dist/src/ui/components/Footer.test.js.map +1 -1
- package/dist/src/ui/components/Header.test.js +13 -5
- package/dist/src/ui/components/Header.test.js.map +1 -1
- package/dist/src/ui/components/Help.test.js +5 -4
- package/dist/src/ui/components/Help.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 +27 -8
- package/dist/src/ui/components/InputPrompt.js.map +1 -1
- package/dist/src/ui/components/InputPrompt.test.js +776 -727
- package/dist/src/ui/components/InputPrompt.test.js.map +1 -1
- package/dist/src/ui/components/LoadingIndicator.js +2 -2
- package/dist/src/ui/components/LoadingIndicator.js.map +1 -1
- package/dist/src/ui/components/LoadingIndicator.test.js +28 -15
- package/dist/src/ui/components/LoadingIndicator.test.js.map +1 -1
- package/dist/src/ui/components/LoopDetectionConfirmation.js +1 -1
- package/dist/src/ui/components/LoopDetectionConfirmation.js.map +1 -1
- package/dist/src/ui/components/LoopDetectionConfirmation.test.js +2 -2
- package/dist/src/ui/components/LoopDetectionConfirmation.test.js.map +1 -1
- package/dist/src/ui/components/MainContent.js +15 -4
- package/dist/src/ui/components/MainContent.js.map +1 -1
- package/dist/src/ui/components/ModelDialog.js +1 -1
- package/dist/src/ui/components/ModelDialog.js.map +1 -1
- package/dist/src/ui/components/ModelDialog.test.js +23 -13
- package/dist/src/ui/components/ModelDialog.test.js.map +1 -1
- package/dist/src/ui/components/ModelStatsDisplay.test.js +1 -1
- package/dist/src/ui/components/ModelStatsDisplay.test.js.map +1 -1
- package/dist/src/ui/components/Notifications.js +38 -5
- package/dist/src/ui/components/Notifications.js.map +1 -1
- package/dist/src/ui/components/PermissionsModifyTrustDialog.test.js +2 -2
- package/dist/src/ui/components/PermissionsModifyTrustDialog.test.js.map +1 -1
- package/dist/src/ui/components/PrepareLabel.test.js +14 -8
- package/dist/src/ui/components/PrepareLabel.test.js.map +1 -1
- package/dist/src/ui/components/ProQuotaDialog.test.js +14 -6
- package/dist/src/ui/components/ProQuotaDialog.test.js.map +1 -1
- package/dist/src/ui/components/QueuedMessageDisplay.test.js +11 -6
- package/dist/src/ui/components/QueuedMessageDisplay.test.js.map +1 -1
- package/dist/src/ui/components/SessionSummaryDisplay.test.js +1 -1
- package/dist/src/ui/components/SessionSummaryDisplay.test.js.map +1 -1
- package/dist/src/ui/components/SettingsDialog.js +32 -25
- package/dist/src/ui/components/SettingsDialog.js.map +1 -1
- package/dist/src/ui/components/SettingsDialog.test.js +428 -532
- package/dist/src/ui/components/SettingsDialog.test.js.map +1 -1
- package/dist/src/ui/components/ShellConfirmationDialog.js +1 -1
- package/dist/src/ui/components/ShellConfirmationDialog.js.map +1 -1
- package/dist/src/ui/components/ShellConfirmationDialog.test.js +2 -2
- package/dist/src/ui/components/ShellConfirmationDialog.test.js.map +1 -1
- package/dist/src/ui/components/StatsDisplay.test.js +1 -1
- package/dist/src/ui/components/StatsDisplay.test.js.map +1 -1
- package/dist/src/ui/components/SuggestionsDisplay.js +1 -1
- package/dist/src/ui/components/SuggestionsDisplay.js.map +1 -1
- package/dist/src/ui/components/ThemeDialog.test.js +2 -2
- package/dist/src/ui/components/ThemeDialog.test.js.map +1 -1
- package/dist/src/ui/components/ToolStatsDisplay.test.js +1 -1
- package/dist/src/ui/components/ToolStatsDisplay.test.js.map +1 -1
- package/dist/src/ui/components/messages/CompressionMessage.test.js +25 -17
- package/dist/src/ui/components/messages/CompressionMessage.test.js.map +1 -1
- package/dist/src/ui/components/messages/DiffRenderer.test.js +1 -1
- package/dist/src/ui/components/messages/DiffRenderer.test.js.map +1 -1
- package/dist/src/ui/components/messages/InfoMessage.js +1 -1
- package/dist/src/ui/components/messages/InfoMessage.js.map +1 -1
- package/dist/src/ui/components/messages/Todo.js +27 -5
- package/dist/src/ui/components/messages/Todo.js.map +1 -1
- package/dist/src/ui/components/messages/Todo.test.js +20 -8
- package/dist/src/ui/components/messages/Todo.test.js.map +1 -1
- package/dist/src/ui/components/messages/ToolConfirmationMessage.js +1 -1
- package/dist/src/ui/components/messages/ToolConfirmationMessage.js.map +1 -1
- package/dist/src/ui/components/messages/ToolGroupMessage.test.js +29 -15
- package/dist/src/ui/components/messages/ToolGroupMessage.test.js.map +1 -1
- package/dist/src/ui/components/messages/WarningMessage.js +2 -2
- package/dist/src/ui/components/messages/WarningMessage.js.map +1 -1
- package/dist/src/ui/components/shared/BaseSelectionList.test.js +1 -1
- package/dist/src/ui/components/shared/BaseSelectionList.test.js.map +1 -1
- package/dist/src/ui/components/shared/MaxSizedBox.test.js +43 -22
- package/dist/src/ui/components/shared/MaxSizedBox.test.js.map +1 -1
- package/dist/src/ui/components/shared/TextInput.d.ts +15 -0
- package/dist/src/ui/components/shared/TextInput.js +38 -0
- package/dist/src/ui/components/shared/TextInput.js.map +1 -0
- package/dist/src/ui/components/shared/TextInput.test.d.ts +6 -0
- package/dist/src/ui/components/shared/TextInput.test.js +242 -0
- package/dist/src/ui/components/shared/TextInput.test.js.map +1 -0
- package/dist/src/ui/components/shared/text-buffer.d.ts +9 -2
- package/dist/src/ui/components/shared/text-buffer.js +51 -13
- package/dist/src/ui/components/shared/text-buffer.js.map +1 -1
- package/dist/src/ui/components/shared/text-buffer.test.js +385 -202
- package/dist/src/ui/components/shared/text-buffer.test.js.map +1 -1
- package/dist/src/ui/components/views/ChatList.test.js +7 -4
- package/dist/src/ui/components/views/ChatList.test.js.map +1 -1
- package/dist/src/ui/components/views/ExtensionsList.d.ts +7 -1
- package/dist/src/ui/components/views/ExtensionsList.js +9 -11
- package/dist/src/ui/components/views/ExtensionsList.js.map +1 -1
- package/dist/src/ui/components/views/ExtensionsList.test.js +43 -22
- package/dist/src/ui/components/views/ExtensionsList.test.js.map +1 -1
- package/dist/src/ui/components/views/McpStatus.test.js +23 -12
- package/dist/src/ui/components/views/McpStatus.test.js.map +1 -1
- package/dist/src/ui/contexts/KeypressContext.d.ts +3 -2
- package/dist/src/ui/contexts/KeypressContext.js +610 -540
- package/dist/src/ui/contexts/KeypressContext.js.map +1 -1
- package/dist/src/ui/contexts/KeypressContext.test.js +438 -718
- package/dist/src/ui/contexts/KeypressContext.test.js.map +1 -1
- package/dist/src/ui/contexts/MouseContext.d.ts +21 -0
- package/dist/src/ui/contexts/MouseContext.js +89 -0
- package/dist/src/ui/contexts/MouseContext.js.map +1 -0
- package/dist/src/ui/contexts/MouseContext.test.d.ts +6 -0
- package/dist/src/ui/contexts/MouseContext.test.js +164 -0
- package/dist/src/ui/contexts/MouseContext.test.js.map +1 -0
- package/dist/src/ui/contexts/SessionContext.test.js +35 -17
- package/dist/src/ui/contexts/SessionContext.test.js.map +1 -1
- package/dist/src/ui/contexts/UIActionsContext.d.ts +2 -0
- package/dist/src/ui/contexts/UIActionsContext.js.map +1 -1
- package/dist/src/ui/contexts/UIStateContext.d.ts +2 -0
- package/dist/src/ui/contexts/UIStateContext.js.map +1 -1
- package/dist/src/ui/hooks/atCommandProcessor.js +31 -9
- package/dist/src/ui/hooks/atCommandProcessor.js.map +1 -1
- package/dist/src/ui/hooks/atCommandProcessor.test.js +163 -64
- package/dist/src/ui/hooks/atCommandProcessor.test.js.map +1 -1
- package/dist/src/ui/hooks/shellCommandProcessor.test.js +64 -35
- package/dist/src/ui/hooks/shellCommandProcessor.test.js.map +1 -1
- package/dist/src/ui/hooks/slashCommandProcessor.test.js +193 -165
- package/dist/src/ui/hooks/slashCommandProcessor.test.js.map +1 -1
- package/dist/src/ui/hooks/useAtCompletion.test.js +16 -5
- package/dist/src/ui/hooks/useAtCompletion.test.js.map +1 -1
- package/dist/src/ui/hooks/useAutoAcceptIndicator.js +10 -0
- package/dist/src/ui/hooks/useAutoAcceptIndicator.js.map +1 -1
- package/dist/src/ui/hooks/useAutoAcceptIndicator.test.js +32 -1
- package/dist/src/ui/hooks/useAutoAcceptIndicator.test.js.map +1 -1
- package/dist/src/ui/hooks/useCommandCompletion.test.js +66 -64
- 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 +14 -5
- package/dist/src/ui/hooks/useExtensionUpdates.js +18 -13
- package/dist/src/ui/hooks/useExtensionUpdates.js.map +1 -1
- package/dist/src/ui/hooks/useExtensionUpdates.test.js +49 -44
- 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 +46 -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 +260 -411
- 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 -34
- 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 +40 -9
- 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 +103 -114
- 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 +62 -45
- package/dist/src/ui/hooks/useMessageQueue.test.js.map +1 -1
- package/dist/src/ui/hooks/useModelCommand.test.js +21 -11
- package/dist/src/ui/hooks/useModelCommand.test.js.map +1 -1
- package/dist/src/ui/hooks/useMouse.d.ts +17 -0
- package/dist/src/ui/hooks/useMouse.js +27 -0
- package/dist/src/ui/hooks/useMouse.js.map +1 -0
- package/dist/src/ui/hooks/useMouse.test.d.ts +6 -0
- package/dist/src/ui/hooks/useMouse.test.js +57 -0
- package/dist/src/ui/hooks/useMouse.test.js.map +1 -0
- 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 +109 -106
- package/dist/src/ui/hooks/usePhraseCycler.test.js.map +1 -1
- package/dist/src/ui/hooks/usePrivacySettings.test.js +26 -6
- package/dist/src/ui/hooks/usePrivacySettings.test.js.map +1 -1
- package/dist/src/ui/hooks/usePromptCompletion.js +2 -2
- package/dist/src/ui/hooks/usePromptCompletion.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 +55 -48
- 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 +65 -0
- package/dist/src/ui/hooks/useReactToolScheduler.test.js.map +1 -0
- 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.js +5 -4
- package/dist/src/ui/hooks/useSelectionList.js.map +1 -1
- package/dist/src/ui/hooks/useSelectionList.test.js +272 -183
- package/dist/src/ui/hooks/useSelectionList.test.js.map +1 -1
- package/dist/src/ui/hooks/useShellHistory.test.js +52 -20
- package/dist/src/ui/hooks/useShellHistory.test.js.map +1 -1
- package/dist/src/ui/hooks/useSlashCompletion.js +18 -7
- package/dist/src/ui/hooks/useSlashCompletion.js.map +1 -1
- package/dist/src/ui/hooks/useSlashCompletion.test.js +275 -137
- 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 +226 -242
- package/dist/src/ui/hooks/useToolScheduler.test.js.map +1 -1
- package/dist/src/ui/hooks/vim.test.js +235 -355
- package/dist/src/ui/hooks/vim.test.js.map +1 -1
- package/dist/src/ui/keyMatchers.test.js +30 -3
- package/dist/src/ui/keyMatchers.test.js.map +1 -1
- package/dist/src/ui/state/extensions.d.ts +1 -0
- package/dist/src/ui/state/extensions.js +1 -0
- package/dist/src/ui/state/extensions.js.map +1 -1
- package/dist/src/ui/themes/ansi-light.js +1 -0
- package/dist/src/ui/themes/ansi-light.js.map +1 -1
- package/dist/src/ui/themes/ansi.js +1 -0
- package/dist/src/ui/themes/ansi.js.map +1 -1
- package/dist/src/ui/themes/atom-one-dark.js +2 -0
- package/dist/src/ui/themes/atom-one-dark.js.map +1 -1
- package/dist/src/ui/themes/ayu-light.js +2 -0
- package/dist/src/ui/themes/ayu-light.js.map +1 -1
- package/dist/src/ui/themes/ayu.js +2 -0
- package/dist/src/ui/themes/ayu.js.map +1 -1
- package/dist/src/ui/themes/color-utils.d.ts +1 -0
- package/dist/src/ui/themes/color-utils.js +6 -0
- package/dist/src/ui/themes/color-utils.js.map +1 -1
- package/dist/src/ui/themes/color-utils.test.js +13 -1
- package/dist/src/ui/themes/color-utils.test.js.map +1 -1
- package/dist/src/ui/themes/dracula.js +2 -0
- package/dist/src/ui/themes/dracula.js.map +1 -1
- package/dist/src/ui/themes/github-dark.js +2 -0
- package/dist/src/ui/themes/github-dark.js.map +1 -1
- package/dist/src/ui/themes/github-light.js +2 -0
- package/dist/src/ui/themes/github-light.js.map +1 -1
- package/dist/src/ui/themes/googlecode.js +2 -0
- package/dist/src/ui/themes/googlecode.js.map +1 -1
- package/dist/src/ui/themes/no-color.js +3 -0
- package/dist/src/ui/themes/no-color.js.map +1 -1
- package/dist/src/ui/themes/semantic-tokens.d.ts +2 -0
- package/dist/src/ui/themes/semantic-tokens.js +6 -0
- package/dist/src/ui/themes/semantic-tokens.js.map +1 -1
- package/dist/src/ui/themes/shades-of-purple.js +2 -0
- package/dist/src/ui/themes/shades-of-purple.js.map +1 -1
- package/dist/src/ui/themes/theme.d.ts +3 -0
- package/dist/src/ui/themes/theme.js +14 -3
- package/dist/src/ui/themes/theme.js.map +1 -1
- package/dist/src/ui/themes/theme.test.js +67 -1
- package/dist/src/ui/themes/theme.test.js.map +1 -1
- package/dist/src/ui/themes/xcode.js +2 -0
- package/dist/src/ui/themes/xcode.js.map +1 -1
- package/dist/src/ui/types.d.ts +3 -1
- package/dist/src/ui/types.js +2 -0
- 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/InlineMarkdownRenderer.d.ts +1 -0
- package/dist/src/ui/utils/InlineMarkdownRenderer.js +11 -10
- package/dist/src/ui/utils/InlineMarkdownRenderer.js.map +1 -1
- package/dist/src/ui/utils/MarkdownDisplay.js +11 -9
- package/dist/src/ui/utils/MarkdownDisplay.js.map +1 -1
- package/dist/src/ui/utils/clipboardUtils.js +2 -2
- package/dist/src/ui/utils/clipboardUtils.js.map +1 -1
- package/dist/src/ui/utils/input.d.ts +17 -0
- package/dist/src/ui/utils/input.js +51 -0
- package/dist/src/ui/utils/input.js.map +1 -0
- package/dist/src/ui/utils/input.test.d.ts +6 -0
- package/dist/src/ui/utils/input.test.js +44 -0
- package/dist/src/ui/utils/input.test.js.map +1 -0
- package/dist/src/ui/utils/kittyProtocolDetector.js +13 -4
- package/dist/src/ui/utils/kittyProtocolDetector.js.map +1 -1
- package/dist/src/ui/utils/mouse.d.ts +31 -0
- package/dist/src/ui/utils/mouse.js +164 -0
- package/dist/src/ui/utils/mouse.js.map +1 -0
- package/dist/src/ui/utils/mouse.test.d.ts +6 -0
- package/dist/src/ui/utils/mouse.test.js +131 -0
- package/dist/src/ui/utils/mouse.test.js.map +1 -0
- 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 +33 -29
- package/dist/src/ui/utils/updateCheck.js.map +1 -1
- package/dist/src/ui/utils/updateCheck.test.js +24 -50
- package/dist/src/ui/utils/updateCheck.test.js.map +1 -1
- package/dist/src/utils/commentJson.js +2 -2
- package/dist/src/utils/commentJson.js.map +1 -1
- package/dist/src/utils/commentJson.test.js +7 -6
- package/dist/src/utils/commentJson.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/events.d.ts +11 -2
- package/dist/src/utils/events.js +1 -0
- package/dist/src/utils/events.js.map +1 -1
- package/dist/src/utils/handleAutoUpdate.js +9 -3
- package/dist/src/utils/handleAutoUpdate.js.map +1 -1
- package/dist/src/utils/sandbox.js +16 -18
- package/dist/src/utils/sandbox.js.map +1 -1
- package/dist/src/utils/version.js +6 -2
- package/dist/src/utils/version.js.map +1 -1
- package/dist/src/zed-integration/acp.js +2 -1
- package/dist/src/zed-integration/acp.js.map +1 -1
- package/dist/src/zed-integration/schema.d.ts +4 -4
- package/dist/src/zed-integration/zedIntegration.d.ts +2 -2
- package/dist/src/zed-integration/zedIntegration.js +12 -19
- package/dist/src/zed-integration/zedIntegration.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +18 -14
- package/dist/src/config/policy.test.js +0 -360
- package/dist/src/config/policy.test.js.map +0 -1
- package/dist/src/utils/package.d.ts +0 -12
- package/dist/src/utils/package.js +0 -24
- package/dist/src/utils/package.js.map +0 -1
- /package/dist/src/{config/policy.test.d.ts → commands/extensions/validate.test.d.ts} +0 -0
|
@@ -1,5 +1,7 @@
|
|
|
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';
|
|
4
|
+
import { waitFor } from '../../test-utils/async.js';
|
|
3
5
|
import { vi } from 'vitest';
|
|
4
6
|
import { KeypressProvider, useKeypressContext, DRAG_COMPLETION_TIMEOUT_MS, KITTY_SEQUENCE_TIMEOUT_MS,
|
|
5
7
|
// CSI_END_O,
|
|
@@ -15,31 +17,30 @@ vi.mock('ink', async (importOriginal) => {
|
|
|
15
17
|
useStdin: vi.fn(),
|
|
16
18
|
};
|
|
17
19
|
});
|
|
20
|
+
const PASTE_START = '\x1B[200~';
|
|
21
|
+
const PASTE_END = '\x1B[201~';
|
|
22
|
+
// readline will not emit most incomplete kitty sequences but it will give
|
|
23
|
+
// up on sequences like this where the modifier (135) has more than two digits.
|
|
24
|
+
const INCOMPLETE_KITTY_SEQUENCE = '\x1b[97;135';
|
|
18
25
|
class MockStdin extends EventEmitter {
|
|
19
26
|
isTTY = true;
|
|
20
27
|
setRawMode = vi.fn();
|
|
21
28
|
on = this.addListener;
|
|
22
29
|
removeListener = super.removeListener;
|
|
23
|
-
write = vi.fn();
|
|
24
30
|
resume = vi.fn();
|
|
25
31
|
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));
|
|
32
|
+
write(text) {
|
|
33
|
+
this.emit('data', text);
|
|
41
34
|
}
|
|
42
35
|
}
|
|
36
|
+
// Helper function to setup keypress test with standard configuration
|
|
37
|
+
const setupKeypressTest = (kittyProtocolEnabled = true) => {
|
|
38
|
+
const keyHandler = vi.fn();
|
|
39
|
+
const wrapper = ({ children }) => (_jsx(KeypressProvider, { kittyProtocolEnabled: kittyProtocolEnabled, children: children }));
|
|
40
|
+
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
|
41
|
+
act(() => result.current.subscribe(keyHandler));
|
|
42
|
+
return { result, keyHandler };
|
|
43
|
+
};
|
|
43
44
|
describe('KeypressContext - Kitty Protocol', () => {
|
|
44
45
|
let stdin;
|
|
45
46
|
const mockSetRawMode = vi.fn();
|
|
@@ -53,117 +54,58 @@ describe('KeypressContext - Kitty Protocol', () => {
|
|
|
53
54
|
});
|
|
54
55
|
});
|
|
55
56
|
describe('Enter key handling', () => {
|
|
56
|
-
it(
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
});
|
|
68
|
-
expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({
|
|
69
|
-
name: 'return',
|
|
70
|
-
kittyProtocol: true,
|
|
71
|
-
ctrl: false,
|
|
72
|
-
meta: false,
|
|
73
|
-
shift: false,
|
|
74
|
-
}));
|
|
75
|
-
});
|
|
76
|
-
it('should recognize numpad enter key (keycode 57414) in kitty protocol', async () => {
|
|
77
|
-
const keyHandler = vi.fn();
|
|
78
|
-
const { result } = renderHook(() => useKeypressContext(), {
|
|
79
|
-
wrapper: ({ children }) => wrapper({ children, kittyProtocolEnabled: true }),
|
|
80
|
-
});
|
|
81
|
-
act(() => {
|
|
82
|
-
result.current.subscribe(keyHandler);
|
|
83
|
-
});
|
|
84
|
-
// Send kitty protocol sequence for numpad enter: ESC[57414u
|
|
85
|
-
act(() => {
|
|
86
|
-
stdin.sendKittySequence(`\x1b[57414u`);
|
|
87
|
-
});
|
|
88
|
-
expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({
|
|
89
|
-
name: 'return',
|
|
90
|
-
kittyProtocol: true,
|
|
91
|
-
ctrl: false,
|
|
92
|
-
meta: false,
|
|
93
|
-
shift: false,
|
|
94
|
-
}));
|
|
95
|
-
});
|
|
96
|
-
it('should handle numpad enter with modifiers', async () => {
|
|
97
|
-
const keyHandler = vi.fn();
|
|
98
|
-
const { result } = renderHook(() => useKeypressContext(), {
|
|
99
|
-
wrapper: ({ children }) => wrapper({ children, kittyProtocolEnabled: true }),
|
|
100
|
-
});
|
|
101
|
-
act(() => {
|
|
102
|
-
result.current.subscribe(keyHandler);
|
|
103
|
-
});
|
|
104
|
-
// Send kitty protocol sequence for numpad enter with Shift (modifier 2): ESC[57414;2u
|
|
57
|
+
it.each([
|
|
58
|
+
{
|
|
59
|
+
name: 'regular enter key (keycode 13)',
|
|
60
|
+
sequence: '\x1b[13u',
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
name: 'numpad enter key (keycode 57414)',
|
|
64
|
+
sequence: '\x1b[57414u',
|
|
65
|
+
},
|
|
66
|
+
])('should recognize $name in kitty protocol', async ({ sequence }) => {
|
|
67
|
+
const { keyHandler } = setupKeypressTest(true);
|
|
105
68
|
act(() => {
|
|
106
|
-
stdin.
|
|
69
|
+
stdin.write(sequence);
|
|
107
70
|
});
|
|
108
71
|
expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({
|
|
109
72
|
name: 'return',
|
|
110
73
|
kittyProtocol: true,
|
|
111
74
|
ctrl: false,
|
|
112
75
|
meta: false,
|
|
113
|
-
shift: true,
|
|
114
|
-
}));
|
|
115
|
-
});
|
|
116
|
-
it('should handle numpad enter with Ctrl modifier', async () => {
|
|
117
|
-
const keyHandler = vi.fn();
|
|
118
|
-
const { result } = renderHook(() => useKeypressContext(), {
|
|
119
|
-
wrapper: ({ children }) => wrapper({ children, kittyProtocolEnabled: true }),
|
|
120
|
-
});
|
|
121
|
-
act(() => {
|
|
122
|
-
result.current.subscribe(keyHandler);
|
|
123
|
-
});
|
|
124
|
-
// Send kitty protocol sequence for numpad enter with Ctrl (modifier 5): ESC[57414;5u
|
|
125
|
-
act(() => {
|
|
126
|
-
stdin.sendKittySequence(`\x1b[57414;5u`);
|
|
127
|
-
});
|
|
128
|
-
expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({
|
|
129
|
-
name: 'return',
|
|
130
|
-
kittyProtocol: true,
|
|
131
|
-
ctrl: true,
|
|
132
|
-
meta: false,
|
|
133
76
|
shift: false,
|
|
134
77
|
}));
|
|
135
78
|
});
|
|
136
|
-
it(
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
79
|
+
it.each([
|
|
80
|
+
{
|
|
81
|
+
modifier: 'Shift',
|
|
82
|
+
sequence: '\x1b[57414;2u',
|
|
83
|
+
expected: { ctrl: false, meta: false, shift: true },
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
modifier: 'Ctrl',
|
|
87
|
+
sequence: '\x1b[57414;5u',
|
|
88
|
+
expected: { ctrl: true, meta: false, shift: false },
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
modifier: 'Alt',
|
|
92
|
+
sequence: '\x1b[57414;3u',
|
|
93
|
+
expected: { ctrl: false, meta: true, shift: false },
|
|
94
|
+
},
|
|
95
|
+
])('should handle numpad enter with $modifier modifier', async ({ sequence, expected }) => {
|
|
96
|
+
const { keyHandler } = setupKeypressTest(true);
|
|
97
|
+
act(() => stdin.write(sequence));
|
|
148
98
|
expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({
|
|
149
99
|
name: 'return',
|
|
150
100
|
kittyProtocol: true,
|
|
151
|
-
|
|
152
|
-
meta: true,
|
|
153
|
-
shift: false,
|
|
101
|
+
...expected,
|
|
154
102
|
}));
|
|
155
103
|
});
|
|
156
104
|
it('should not process kitty sequences when kitty protocol is disabled', async () => {
|
|
157
|
-
const keyHandler =
|
|
158
|
-
const { result } = renderHook(() => useKeypressContext(), {
|
|
159
|
-
wrapper: ({ children }) => wrapper({ children, kittyProtocolEnabled: false }),
|
|
160
|
-
});
|
|
161
|
-
act(() => {
|
|
162
|
-
result.current.subscribe(keyHandler);
|
|
163
|
-
});
|
|
105
|
+
const { keyHandler } = setupKeypressTest(false);
|
|
164
106
|
// Send kitty protocol sequence for numpad enter
|
|
165
107
|
act(() => {
|
|
166
|
-
stdin.
|
|
108
|
+
stdin.write(`\x1b[57414u`);
|
|
167
109
|
});
|
|
168
110
|
// When kitty protocol is disabled, the sequence should be passed through
|
|
169
111
|
// as individual keypresses, not recognized as a single enter key
|
|
@@ -175,112 +117,115 @@ describe('KeypressContext - Kitty Protocol', () => {
|
|
|
175
117
|
});
|
|
176
118
|
describe('Escape key handling', () => {
|
|
177
119
|
it('should recognize escape key (keycode 27) in kitty protocol', async () => {
|
|
178
|
-
const keyHandler =
|
|
179
|
-
const { result } = renderHook(() => useKeypressContext(), {
|
|
180
|
-
wrapper: ({ children }) => wrapper({ children, kittyProtocolEnabled: true }),
|
|
181
|
-
});
|
|
182
|
-
act(() => {
|
|
183
|
-
result.current.subscribe(keyHandler);
|
|
184
|
-
});
|
|
120
|
+
const { keyHandler } = setupKeypressTest(true);
|
|
185
121
|
// Send kitty protocol sequence for escape: ESC[27u
|
|
186
122
|
act(() => {
|
|
187
|
-
stdin.
|
|
123
|
+
stdin.write('\x1b[27u');
|
|
188
124
|
});
|
|
189
125
|
expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({
|
|
190
126
|
name: 'escape',
|
|
191
127
|
kittyProtocol: true,
|
|
192
128
|
}));
|
|
193
129
|
});
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
const keyHandler = vi.fn();
|
|
198
|
-
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
|
199
|
-
act(() => result.current.subscribe(keyHandler));
|
|
200
|
-
act(() => {
|
|
201
|
-
stdin.sendKittySequence(`\x1b[9u`);
|
|
202
|
-
});
|
|
203
|
-
expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({
|
|
204
|
-
name: 'tab',
|
|
205
|
-
kittyProtocol: true,
|
|
206
|
-
shift: false,
|
|
207
|
-
}));
|
|
208
|
-
});
|
|
209
|
-
it('should recognize Shift+Tab in kitty protocol', async () => {
|
|
210
|
-
const keyHandler = vi.fn();
|
|
211
|
-
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
|
212
|
-
act(() => result.current.subscribe(keyHandler));
|
|
213
|
-
// Modifier 2 is Shift
|
|
214
|
-
act(() => {
|
|
215
|
-
stdin.sendKittySequence(`\x1b[9;2u`);
|
|
216
|
-
});
|
|
217
|
-
expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({
|
|
218
|
-
name: 'tab',
|
|
219
|
-
kittyProtocol: true,
|
|
220
|
-
shift: true,
|
|
221
|
-
}));
|
|
222
|
-
});
|
|
223
|
-
it('should recognize Backspace key in kitty protocol', async () => {
|
|
224
|
-
const keyHandler = vi.fn();
|
|
225
|
-
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
|
226
|
-
act(() => result.current.subscribe(keyHandler));
|
|
227
|
-
act(() => {
|
|
228
|
-
stdin.sendKittySequence(`\x1b[127u`);
|
|
229
|
-
});
|
|
230
|
-
expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({
|
|
231
|
-
name: 'backspace',
|
|
232
|
-
kittyProtocol: true,
|
|
233
|
-
meta: false,
|
|
234
|
-
}));
|
|
235
|
-
});
|
|
236
|
-
it('should recognize Option+Backspace in kitty protocol', async () => {
|
|
130
|
+
it('should handle lone Escape key (keycode 27) with timeout when kitty protocol is enabled', async () => {
|
|
131
|
+
// Use real timers for this test to avoid issues with stream/buffer timing
|
|
132
|
+
vi.useRealTimers();
|
|
237
133
|
const keyHandler = vi.fn();
|
|
134
|
+
const wrapper = ({ children }) => (_jsx(KeypressProvider, { kittyProtocolEnabled: true, children: children }));
|
|
238
135
|
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
|
239
136
|
act(() => result.current.subscribe(keyHandler));
|
|
240
|
-
//
|
|
137
|
+
// Send just ESC
|
|
241
138
|
act(() => {
|
|
242
|
-
stdin.
|
|
139
|
+
stdin.write('\x1b');
|
|
243
140
|
});
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
141
|
+
// Should be buffered initially
|
|
142
|
+
expect(keyHandler).not.toHaveBeenCalled();
|
|
143
|
+
// Wait for timeout
|
|
144
|
+
await waitFor(() => {
|
|
145
|
+
expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({
|
|
146
|
+
name: 'escape',
|
|
147
|
+
meta: true,
|
|
148
|
+
}));
|
|
149
|
+
}, { timeout: 500 });
|
|
249
150
|
});
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
151
|
+
});
|
|
152
|
+
describe('Tab and Backspace handling', () => {
|
|
153
|
+
it.each([
|
|
154
|
+
{
|
|
155
|
+
name: 'Tab key',
|
|
156
|
+
sequence: '\x1b[9u',
|
|
157
|
+
expected: { name: 'tab', shift: false },
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
name: 'Shift+Tab',
|
|
161
|
+
sequence: '\x1b[9;2u',
|
|
162
|
+
expected: { name: 'tab', shift: true },
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
name: 'Backspace',
|
|
166
|
+
sequence: '\x1b[127u',
|
|
167
|
+
expected: { name: 'backspace', meta: false },
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
name: 'Option+Backspace',
|
|
171
|
+
sequence: '\x1b[127;3u',
|
|
172
|
+
expected: { name: 'backspace', meta: true },
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
name: 'Ctrl+Backspace',
|
|
176
|
+
sequence: '\x1b[127;5u',
|
|
177
|
+
expected: { name: 'backspace', ctrl: true },
|
|
178
|
+
},
|
|
179
|
+
])('should recognize $name in kitty protocol', async ({ sequence, expected }) => {
|
|
180
|
+
const { keyHandler } = setupKeypressTest(true);
|
|
255
181
|
act(() => {
|
|
256
|
-
stdin.
|
|
182
|
+
stdin.write(sequence);
|
|
257
183
|
});
|
|
258
184
|
expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({
|
|
259
|
-
|
|
185
|
+
...expected,
|
|
260
186
|
kittyProtocol: true,
|
|
261
|
-
ctrl: true,
|
|
262
187
|
}));
|
|
263
188
|
});
|
|
264
189
|
});
|
|
265
190
|
describe('paste mode', () => {
|
|
266
|
-
it(
|
|
191
|
+
it.each([
|
|
192
|
+
{
|
|
193
|
+
name: 'handle multiline paste as a single event',
|
|
194
|
+
pastedText: 'This \n is \n a \n multiline \n paste.',
|
|
195
|
+
writeSequence: (text) => {
|
|
196
|
+
stdin.write(PASTE_START);
|
|
197
|
+
stdin.write(text);
|
|
198
|
+
stdin.write(PASTE_END);
|
|
199
|
+
},
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
name: 'handle paste start code split over multiple writes',
|
|
203
|
+
pastedText: 'pasted content',
|
|
204
|
+
writeSequence: (text) => {
|
|
205
|
+
stdin.write(PASTE_START.slice(0, 3));
|
|
206
|
+
stdin.write(PASTE_START.slice(3));
|
|
207
|
+
stdin.write(text);
|
|
208
|
+
stdin.write(PASTE_END);
|
|
209
|
+
},
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
name: 'handle paste end code split over multiple writes',
|
|
213
|
+
pastedText: 'pasted content',
|
|
214
|
+
writeSequence: (text) => {
|
|
215
|
+
stdin.write(PASTE_START);
|
|
216
|
+
stdin.write(text);
|
|
217
|
+
stdin.write(PASTE_END.slice(0, 3));
|
|
218
|
+
stdin.write(PASTE_END.slice(3));
|
|
219
|
+
},
|
|
220
|
+
},
|
|
221
|
+
])('should $name', async ({ pastedText, writeSequence }) => {
|
|
267
222
|
const keyHandler = vi.fn();
|
|
268
|
-
const
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
});
|
|
272
|
-
act(() => {
|
|
273
|
-
result.current.subscribe(keyHandler);
|
|
274
|
-
});
|
|
275
|
-
// Simulate a bracketed paste event
|
|
276
|
-
act(() => {
|
|
277
|
-
stdin.sendPaste(pastedText);
|
|
278
|
-
});
|
|
223
|
+
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
|
224
|
+
act(() => result.current.subscribe(keyHandler));
|
|
225
|
+
act(() => writeSequence(pastedText));
|
|
279
226
|
await waitFor(() => {
|
|
280
|
-
// Expect the handler to be called exactly once for the entire paste
|
|
281
227
|
expect(keyHandler).toHaveBeenCalledTimes(1);
|
|
282
228
|
});
|
|
283
|
-
// Verify the single event contains the full pasted text
|
|
284
229
|
expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({
|
|
285
230
|
paste: true,
|
|
286
231
|
sequence: pastedText,
|
|
@@ -302,12 +247,10 @@ describe('KeypressContext - Kitty Protocol', () => {
|
|
|
302
247
|
const keyHandler = vi.fn();
|
|
303
248
|
const wrapper = ({ children }) => (_jsx(KeypressProvider, { kittyProtocolEnabled: true, debugKeystrokeLogging: false, children: children }));
|
|
304
249
|
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
|
305
|
-
act(() =>
|
|
306
|
-
result.current.subscribe(keyHandler);
|
|
307
|
-
});
|
|
250
|
+
act(() => result.current.subscribe(keyHandler));
|
|
308
251
|
// Send a kitty sequence
|
|
309
252
|
act(() => {
|
|
310
|
-
stdin.
|
|
253
|
+
stdin.write('\x1b[27u');
|
|
311
254
|
});
|
|
312
255
|
expect(keyHandler).toHaveBeenCalled();
|
|
313
256
|
expect(consoleLogSpy).not.toHaveBeenCalledWith(expect.stringContaining('[DEBUG] Kitty'));
|
|
@@ -316,16 +259,12 @@ describe('KeypressContext - Kitty Protocol', () => {
|
|
|
316
259
|
const keyHandler = vi.fn();
|
|
317
260
|
const wrapper = ({ children }) => (_jsx(KeypressProvider, { kittyProtocolEnabled: true, debugKeystrokeLogging: true, children: children }));
|
|
318
261
|
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
|
319
|
-
act(() =>
|
|
320
|
-
result.current.subscribe(keyHandler);
|
|
321
|
-
});
|
|
262
|
+
act(() => result.current.subscribe(keyHandler));
|
|
322
263
|
// Send a complete kitty sequence for escape
|
|
323
|
-
act(() =>
|
|
324
|
-
|
|
325
|
-
});
|
|
326
|
-
expect(consoleLogSpy).toHaveBeenCalledWith('[DEBUG] Kitty buffer accumulating:', expect.stringContaining('"\\u001b[27u"'));
|
|
264
|
+
act(() => stdin.write('\x1b[27u'));
|
|
265
|
+
expect(consoleLogSpy).toHaveBeenCalledWith('[DEBUG] Input buffer accumulating:', expect.stringContaining('"\\u001b[27u"'));
|
|
327
266
|
const parsedCall = consoleLogSpy.mock.calls.find((args) => typeof args[0] === 'string' &&
|
|
328
|
-
args[0].includes('[DEBUG]
|
|
267
|
+
args[0].includes('[DEBUG] Sequence parsed successfully'));
|
|
329
268
|
expect(parsedCall).toBeTruthy();
|
|
330
269
|
expect(parsedCall?.[1]).toEqual(expect.stringContaining('\\u001b[27u'));
|
|
331
270
|
});
|
|
@@ -333,44 +272,21 @@ describe('KeypressContext - Kitty Protocol', () => {
|
|
|
333
272
|
const keyHandler = vi.fn();
|
|
334
273
|
const wrapper = ({ children }) => (_jsx(KeypressProvider, { kittyProtocolEnabled: true, debugKeystrokeLogging: true, children: children }));
|
|
335
274
|
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
|
336
|
-
act(() =>
|
|
337
|
-
result.current.subscribe(keyHandler);
|
|
338
|
-
});
|
|
275
|
+
act(() => result.current.subscribe(keyHandler));
|
|
339
276
|
// Send a long sequence starting with a valid kitty prefix to trigger overflow
|
|
340
277
|
const longSequence = '\x1b[1;' + '1'.repeat(100);
|
|
341
|
-
act(() =>
|
|
342
|
-
|
|
343
|
-
});
|
|
344
|
-
expect(consoleLogSpy).toHaveBeenCalledWith('[DEBUG] Kitty buffer overflow, clearing:', expect.any(String));
|
|
278
|
+
act(() => stdin.write(longSequence));
|
|
279
|
+
expect(consoleLogSpy).toHaveBeenCalledWith('[DEBUG] Input buffer overflow, clearing:', expect.any(String));
|
|
345
280
|
});
|
|
346
281
|
it('should log kitty buffer clear on Ctrl+C when debugKeystrokeLogging is true', async () => {
|
|
347
282
|
const keyHandler = vi.fn();
|
|
348
283
|
const wrapper = ({ children }) => (_jsx(KeypressProvider, { kittyProtocolEnabled: true, debugKeystrokeLogging: true, children: children }));
|
|
349
284
|
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
|
-
});
|
|
285
|
+
act(() => result.current.subscribe(keyHandler));
|
|
286
|
+
act(() => stdin.write(INCOMPLETE_KITTY_SEQUENCE));
|
|
363
287
|
// 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');
|
|
288
|
+
act(() => stdin.write('\x03'));
|
|
289
|
+
expect(consoleLogSpy).toHaveBeenCalledWith('[DEBUG] Input buffer cleared on Ctrl+C:', INCOMPLETE_KITTY_SEQUENCE);
|
|
374
290
|
// Verify Ctrl+C was handled
|
|
375
291
|
expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({
|
|
376
292
|
name: 'c',
|
|
@@ -381,24 +297,13 @@ describe('KeypressContext - Kitty Protocol', () => {
|
|
|
381
297
|
const keyHandler = vi.fn();
|
|
382
298
|
const wrapper = ({ children }) => (_jsx(KeypressProvider, { kittyProtocolEnabled: true, debugKeystrokeLogging: true, children: children }));
|
|
383
299
|
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
|
384
|
-
act(() =>
|
|
385
|
-
result.current.subscribe(keyHandler);
|
|
386
|
-
});
|
|
300
|
+
act(() => result.current.subscribe(keyHandler));
|
|
387
301
|
// 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
|
-
});
|
|
302
|
+
act(() => stdin.write(INCOMPLETE_KITTY_SEQUENCE));
|
|
398
303
|
// Verify debug logging for accumulation
|
|
399
|
-
expect(consoleLogSpy).toHaveBeenCalledWith('[DEBUG]
|
|
304
|
+
expect(consoleLogSpy).toHaveBeenCalledWith('[DEBUG] Input buffer accumulating:', JSON.stringify(INCOMPLETE_KITTY_SEQUENCE));
|
|
400
305
|
// Verify warning for char codes
|
|
401
|
-
expect(consoleWarnSpy).toHaveBeenCalledWith('
|
|
306
|
+
expect(consoleWarnSpy).toHaveBeenCalledWith('Input sequence buffer has content:', JSON.stringify(INCOMPLETE_KITTY_SEQUENCE));
|
|
402
307
|
});
|
|
403
308
|
});
|
|
404
309
|
describe('Parameterized functional keys', () => {
|
|
@@ -414,6 +319,13 @@ describe('KeypressContext - Kitty Protocol', () => {
|
|
|
414
319
|
{ sequence: `\x1b[1~`, expected: { name: 'home' } },
|
|
415
320
|
{ sequence: `\x1b[4~`, expected: { name: 'end' } },
|
|
416
321
|
{ sequence: `\x1b[2~`, expected: { name: 'insert' } },
|
|
322
|
+
{ sequence: `\x1b[11~`, expected: { name: 'f1' } },
|
|
323
|
+
{ sequence: `\x1b[17~`, expected: { name: 'f6' } },
|
|
324
|
+
{ sequence: `\x1b[23~`, expected: { name: 'f11' } },
|
|
325
|
+
{ sequence: `\x1b[24~`, expected: { name: 'f12' } },
|
|
326
|
+
// Reverse tabs
|
|
327
|
+
{ sequence: `\x1b[Z`, expected: { name: 'tab', shift: true } },
|
|
328
|
+
{ sequence: `\x1b[1;2Z`, expected: { name: 'tab', shift: true } },
|
|
417
329
|
// Legacy Arrows
|
|
418
330
|
{
|
|
419
331
|
sequence: `\x1b[A`,
|
|
@@ -444,56 +356,32 @@ describe('KeypressContext - Kitty Protocol', () => {
|
|
|
444
356
|
const keyHandler = vi.fn();
|
|
445
357
|
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
|
446
358
|
act(() => result.current.subscribe(keyHandler));
|
|
447
|
-
act(() => stdin.
|
|
359
|
+
act(() => stdin.write(sequence));
|
|
448
360
|
expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining(expected));
|
|
449
361
|
});
|
|
450
362
|
});
|
|
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
363
|
describe('Double-tap and batching', () => {
|
|
464
364
|
it('should emit two delete events for double-tap CSI[3~', async () => {
|
|
465
|
-
const keyHandler =
|
|
466
|
-
|
|
467
|
-
act(() =>
|
|
468
|
-
act(() => stdin.sendKittySequence(`\x1b[3~`));
|
|
469
|
-
act(() => stdin.sendKittySequence(`\x1b[3~`));
|
|
365
|
+
const { keyHandler } = setupKeypressTest(true);
|
|
366
|
+
act(() => stdin.write(`\x1b[3~`));
|
|
367
|
+
act(() => stdin.write(`\x1b[3~`));
|
|
470
368
|
expect(keyHandler).toHaveBeenNthCalledWith(1, expect.objectContaining({ name: 'delete' }));
|
|
471
369
|
expect(keyHandler).toHaveBeenNthCalledWith(2, expect.objectContaining({ name: 'delete' }));
|
|
472
370
|
});
|
|
473
371
|
it('should parse two concatenated tilde-coded sequences in one chunk', async () => {
|
|
474
|
-
const keyHandler =
|
|
475
|
-
|
|
476
|
-
act(() => result.current.subscribe(keyHandler));
|
|
477
|
-
act(() => stdin.sendKittySequence(`\x1b[3~\x1b[5~`));
|
|
372
|
+
const { keyHandler } = setupKeypressTest(true);
|
|
373
|
+
act(() => stdin.write(`\x1b[3~\x1b[5~`));
|
|
478
374
|
expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({ name: 'delete' }));
|
|
479
375
|
expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({ name: 'pageup' }));
|
|
480
376
|
});
|
|
481
377
|
it('should ignore incomplete CSI then parse the next complete sequence', async () => {
|
|
482
|
-
const keyHandler =
|
|
483
|
-
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
|
484
|
-
act(() => result.current.subscribe(keyHandler));
|
|
378
|
+
const { keyHandler } = setupKeypressTest(true);
|
|
485
379
|
// Incomplete ESC sequence then a complete Delete
|
|
486
380
|
act(() => {
|
|
487
381
|
// 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
|
-
});
|
|
382
|
+
stdin.write('\x1b[1;');
|
|
495
383
|
});
|
|
496
|
-
act(() => stdin.
|
|
384
|
+
act(() => stdin.write(`\x1b[3~`));
|
|
497
385
|
expect(keyHandler).toHaveBeenCalledTimes(1);
|
|
498
386
|
expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({ name: 'delete' }));
|
|
499
387
|
});
|
|
@@ -516,154 +404,45 @@ describe('Drag and Drop Handling', () => {
|
|
|
516
404
|
vi.useRealTimers();
|
|
517
405
|
});
|
|
518
406
|
describe('drag start by quotes', () => {
|
|
519
|
-
it(
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
result.current.subscribe(keyHandler);
|
|
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
|
-
});
|
|
535
|
-
expect(keyHandler).not.toHaveBeenCalled();
|
|
536
|
-
});
|
|
537
|
-
it('should start collecting when double quote arrives and not broadcast immediately', async () => {
|
|
407
|
+
it.each([
|
|
408
|
+
{ name: 'single quote', quote: SINGLE_QUOTE },
|
|
409
|
+
{ name: 'double quote', quote: DOUBLE_QUOTE },
|
|
410
|
+
])('should start collecting when $name arrives and not broadcast immediately', async ({ quote }) => {
|
|
538
411
|
const keyHandler = vi.fn();
|
|
539
412
|
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
|
-
});
|
|
413
|
+
act(() => result.current.subscribe(keyHandler));
|
|
414
|
+
act(() => stdin.write(quote));
|
|
553
415
|
expect(keyHandler).not.toHaveBeenCalled();
|
|
554
416
|
});
|
|
555
417
|
});
|
|
556
418
|
describe('drag collection and completion', () => {
|
|
557
|
-
it(
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
shift: false,
|
|
570
|
-
paste: false,
|
|
571
|
-
sequence: SINGLE_QUOTE,
|
|
572
|
-
});
|
|
573
|
-
});
|
|
574
|
-
// 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
|
-
});
|
|
585
|
-
// Character should not be immediately broadcast
|
|
586
|
-
expect(keyHandler).not.toHaveBeenCalled();
|
|
587
|
-
// Fast-forward to completion timeout
|
|
588
|
-
act(() => {
|
|
589
|
-
vi.advanceTimersByTime(DRAG_COMPLETION_TIMEOUT_MS + 10);
|
|
590
|
-
});
|
|
591
|
-
// Should broadcast the collected path as paste (includes starting quote)
|
|
592
|
-
expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({
|
|
593
|
-
name: '',
|
|
594
|
-
paste: true,
|
|
595
|
-
sequence: `${SINGLE_QUOTE}a`,
|
|
596
|
-
}));
|
|
597
|
-
});
|
|
598
|
-
it('should collect multiple characters and complete on timeout', async () => {
|
|
419
|
+
it.each([
|
|
420
|
+
{
|
|
421
|
+
name: 'collect single character inputs during drag mode',
|
|
422
|
+
characters: ['a'],
|
|
423
|
+
expectedText: 'a',
|
|
424
|
+
},
|
|
425
|
+
{
|
|
426
|
+
name: 'collect multiple characters and complete on timeout',
|
|
427
|
+
characters: ['p', 'a', 't', 'h'],
|
|
428
|
+
expectedText: 'path',
|
|
429
|
+
},
|
|
430
|
+
])('should $name', async ({ characters, expectedText }) => {
|
|
599
431
|
const keyHandler = vi.fn();
|
|
600
432
|
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
|
601
|
-
act(() =>
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
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
|
-
});
|
|
615
|
-
// Send multiple characters
|
|
616
|
-
act(() => {
|
|
617
|
-
stdin.pressKey({
|
|
618
|
-
name: undefined,
|
|
619
|
-
ctrl: false,
|
|
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
|
-
});
|
|
433
|
+
act(() => result.current.subscribe(keyHandler));
|
|
434
|
+
act(() => stdin.write(SINGLE_QUOTE));
|
|
435
|
+
characters.forEach((char) => {
|
|
436
|
+
act(() => stdin.write(char));
|
|
655
437
|
});
|
|
656
|
-
// Characters should not be immediately broadcast
|
|
657
438
|
expect(keyHandler).not.toHaveBeenCalled();
|
|
658
|
-
// Fast-forward to completion timeout
|
|
659
439
|
act(() => {
|
|
660
440
|
vi.advanceTimersByTime(DRAG_COMPLETION_TIMEOUT_MS + 10);
|
|
661
441
|
});
|
|
662
|
-
// Should broadcast the collected path as paste (includes starting quote)
|
|
663
442
|
expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({
|
|
664
443
|
name: '',
|
|
665
444
|
paste: true,
|
|
666
|
-
sequence: `${SINGLE_QUOTE}
|
|
445
|
+
sequence: `${SINGLE_QUOTE}${expectedText}`,
|
|
667
446
|
}));
|
|
668
447
|
});
|
|
669
448
|
});
|
|
@@ -684,94 +463,91 @@ describe('Kitty Sequence Parsing', () => {
|
|
|
684
463
|
afterEach(() => {
|
|
685
464
|
vi.useRealTimers();
|
|
686
465
|
});
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
466
|
+
describe('Cross-terminal Alt key handling (simulating macOS)', () => {
|
|
467
|
+
let originalPlatform;
|
|
468
|
+
beforeEach(() => {
|
|
469
|
+
originalPlatform = process.platform;
|
|
470
|
+
Object.defineProperty(process, 'platform', {
|
|
471
|
+
value: 'darwin',
|
|
472
|
+
configurable: true,
|
|
473
|
+
});
|
|
474
|
+
});
|
|
475
|
+
afterEach(() => {
|
|
476
|
+
Object.defineProperty(process, 'platform', {
|
|
477
|
+
value: originalPlatform,
|
|
478
|
+
configurable: true,
|
|
479
|
+
});
|
|
480
|
+
});
|
|
481
|
+
// Terminals to test
|
|
482
|
+
const terminals = ['iTerm2', 'Ghostty', 'MacTerminal', 'VSCodeTerminal'];
|
|
483
|
+
// Key mappings: letter -> [keycode, accented character]
|
|
484
|
+
const keys = {
|
|
485
|
+
b: [98, '\u222B'],
|
|
486
|
+
f: [102, '\u0192'],
|
|
487
|
+
m: [109, '\u00B5'],
|
|
488
|
+
};
|
|
489
|
+
it.each(terminals.flatMap((terminal) => Object.entries(keys).map(([key, [keycode, accentedChar]]) => {
|
|
490
|
+
if (terminal === 'Ghostty') {
|
|
491
|
+
// Ghostty uses kitty protocol sequences
|
|
492
|
+
return {
|
|
493
|
+
terminal,
|
|
494
|
+
key,
|
|
495
|
+
chunk: `\x1b[${keycode};3u`,
|
|
496
|
+
expected: {
|
|
497
|
+
name: key,
|
|
498
|
+
ctrl: false,
|
|
499
|
+
meta: true,
|
|
500
|
+
shift: false,
|
|
501
|
+
paste: false,
|
|
502
|
+
kittyProtocol: true,
|
|
503
|
+
},
|
|
504
|
+
};
|
|
505
|
+
}
|
|
506
|
+
else if (terminal === 'MacTerminal') {
|
|
507
|
+
// Mac Terminal sends ESC + letter
|
|
508
|
+
return {
|
|
509
|
+
terminal,
|
|
510
|
+
key,
|
|
511
|
+
kitty: false,
|
|
512
|
+
chunk: `\x1b${key}`,
|
|
513
|
+
expected: {
|
|
514
|
+
sequence: `\x1b${key}`,
|
|
515
|
+
name: key,
|
|
516
|
+
ctrl: false,
|
|
517
|
+
meta: true,
|
|
518
|
+
shift: false,
|
|
519
|
+
paste: false,
|
|
520
|
+
},
|
|
521
|
+
};
|
|
522
|
+
}
|
|
523
|
+
else {
|
|
524
|
+
// iTerm2 and VSCode send accented characters (å, ø, µ)
|
|
525
|
+
// Note: µ (mu) is sent with meta:false on iTerm2/VSCode but
|
|
526
|
+
// gets converted to m with meta:true
|
|
527
|
+
return {
|
|
528
|
+
terminal,
|
|
529
|
+
key,
|
|
530
|
+
chunk: accentedChar,
|
|
531
|
+
expected: {
|
|
532
|
+
name: key,
|
|
533
|
+
ctrl: false,
|
|
534
|
+
meta: true, // Always expect meta:true after conversion
|
|
535
|
+
shift: false,
|
|
536
|
+
paste: false,
|
|
537
|
+
sequence: accentedChar,
|
|
538
|
+
},
|
|
539
|
+
};
|
|
540
|
+
}
|
|
541
|
+
})))('should handle Alt+$key in $terminal', ({ chunk, expected, kitty = true, }) => {
|
|
542
|
+
const keyHandler = vi.fn();
|
|
543
|
+
const testWrapper = ({ children }) => (_jsx(KeypressProvider, { kittyProtocolEnabled: kitty, children: children }));
|
|
544
|
+
const { result } = renderHook(() => useKeypressContext(), {
|
|
545
|
+
wrapper: testWrapper,
|
|
546
|
+
});
|
|
547
|
+
act(() => result.current.subscribe(keyHandler));
|
|
548
|
+
act(() => stdin.write(chunk));
|
|
549
|
+
expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining(expected));
|
|
766
550
|
});
|
|
767
|
-
act(() => result.current.subscribe(keyHandler));
|
|
768
|
-
if (kittySequence) {
|
|
769
|
-
act(() => stdin.sendKittySequence(kittySequence));
|
|
770
|
-
}
|
|
771
|
-
else if (input) {
|
|
772
|
-
act(() => stdin.pressKey(input));
|
|
773
|
-
}
|
|
774
|
-
expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining(expected));
|
|
775
551
|
});
|
|
776
552
|
describe('Backslash key handling', () => {
|
|
777
553
|
beforeEach(() => {
|
|
@@ -781,17 +557,8 @@ describe('Kitty Sequence Parsing', () => {
|
|
|
781
557
|
vi.useRealTimers();
|
|
782
558
|
});
|
|
783
559
|
it('should treat backslash as a regular keystroke', () => {
|
|
784
|
-
const keyHandler =
|
|
785
|
-
|
|
786
|
-
act(() => result.current.subscribe(keyHandler));
|
|
787
|
-
act(() => stdin.pressKey({
|
|
788
|
-
name: undefined,
|
|
789
|
-
ctrl: false,
|
|
790
|
-
meta: false,
|
|
791
|
-
shift: false,
|
|
792
|
-
paste: false,
|
|
793
|
-
sequence: '\\',
|
|
794
|
-
}));
|
|
560
|
+
const { keyHandler } = setupKeypressTest(true);
|
|
561
|
+
act(() => stdin.write('\\'));
|
|
795
562
|
// Advance timers to trigger the backslash timeout
|
|
796
563
|
act(() => {
|
|
797
564
|
vi.runAllTimers();
|
|
@@ -805,60 +572,32 @@ describe('Kitty Sequence Parsing', () => {
|
|
|
805
572
|
it('should timeout and flush incomplete kitty sequences after 50ms', async () => {
|
|
806
573
|
const keyHandler = vi.fn();
|
|
807
574
|
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
|
-
});
|
|
575
|
+
act(() => result.current.subscribe(keyHandler));
|
|
576
|
+
act(() => stdin.write(INCOMPLETE_KITTY_SEQUENCE));
|
|
822
577
|
// Should not broadcast immediately
|
|
823
578
|
expect(keyHandler).not.toHaveBeenCalled();
|
|
824
579
|
// Advance time just before timeout
|
|
825
|
-
act(() =>
|
|
826
|
-
vi.advanceTimersByTime(KITTY_SEQUENCE_TIMEOUT_MS - 5);
|
|
827
|
-
});
|
|
580
|
+
act(() => vi.advanceTimersByTime(KITTY_SEQUENCE_TIMEOUT_MS - 5));
|
|
828
581
|
// Still shouldn't broadcast
|
|
829
582
|
expect(keyHandler).not.toHaveBeenCalled();
|
|
830
583
|
// Advance past timeout
|
|
831
|
-
act(() =>
|
|
832
|
-
vi.advanceTimersByTime(10);
|
|
833
|
-
});
|
|
584
|
+
act(() => vi.advanceTimersByTime(10));
|
|
834
585
|
// Should now broadcast the incomplete sequence as regular input
|
|
835
586
|
expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({
|
|
836
587
|
name: '',
|
|
837
|
-
sequence:
|
|
588
|
+
sequence: INCOMPLETE_KITTY_SEQUENCE,
|
|
838
589
|
paste: false,
|
|
839
590
|
}));
|
|
840
591
|
});
|
|
841
592
|
it('should immediately flush non-kitty CSI sequences', async () => {
|
|
842
593
|
const keyHandler = vi.fn();
|
|
843
594
|
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
|
844
|
-
act(() =>
|
|
845
|
-
result.current.subscribe(keyHandler);
|
|
846
|
-
});
|
|
595
|
+
act(() => result.current.subscribe(keyHandler));
|
|
847
596
|
// Send a CSI sequence that doesn't match kitty patterns
|
|
848
597
|
// 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
|
-
});
|
|
598
|
+
act(() => stdin.write('\x1b[m'));
|
|
859
599
|
// Should broadcast immediately as it's not a valid kitty pattern
|
|
860
600
|
expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({
|
|
861
|
-
name: '',
|
|
862
601
|
sequence: '\x1b[m',
|
|
863
602
|
paste: false,
|
|
864
603
|
}));
|
|
@@ -866,20 +605,9 @@ describe('Kitty Sequence Parsing', () => {
|
|
|
866
605
|
it('should parse valid kitty sequences immediately when complete', async () => {
|
|
867
606
|
const keyHandler = vi.fn();
|
|
868
607
|
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
|
869
|
-
act(() =>
|
|
870
|
-
result.current.subscribe(keyHandler);
|
|
871
|
-
});
|
|
608
|
+
act(() => result.current.subscribe(keyHandler));
|
|
872
609
|
// 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
|
-
});
|
|
610
|
+
act(() => stdin.write('\x1b[97;5u'));
|
|
883
611
|
// Should parse and broadcast immediately
|
|
884
612
|
expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({
|
|
885
613
|
name: 'a',
|
|
@@ -890,20 +618,9 @@ describe('Kitty Sequence Parsing', () => {
|
|
|
890
618
|
it('should handle batched kitty sequences correctly', async () => {
|
|
891
619
|
const keyHandler = vi.fn();
|
|
892
620
|
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
|
-
});
|
|
621
|
+
act(() => result.current.subscribe(keyHandler));
|
|
622
|
+
// Send Ctrl+a followed by Ctrl+b
|
|
623
|
+
act(() => stdin.write('\x1b[97;5u\x1b[98;5u'));
|
|
907
624
|
// Should parse both sequences
|
|
908
625
|
expect(keyHandler).toHaveBeenCalledTimes(2);
|
|
909
626
|
expect(keyHandler).toHaveBeenNthCalledWith(1, expect.objectContaining({
|
|
@@ -920,35 +637,12 @@ describe('Kitty Sequence Parsing', () => {
|
|
|
920
637
|
it('should clear kitty buffer and timeout on Ctrl+C', async () => {
|
|
921
638
|
const keyHandler = vi.fn();
|
|
922
639
|
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
|
-
});
|
|
640
|
+
act(() => result.current.subscribe(keyHandler));
|
|
641
|
+
act(() => stdin.write(INCOMPLETE_KITTY_SEQUENCE));
|
|
937
642
|
// 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
|
-
});
|
|
643
|
+
act(() => stdin.write('\x03'));
|
|
948
644
|
// Advance past timeout
|
|
949
|
-
act(() =>
|
|
950
|
-
vi.advanceTimersByTime(KITTY_SEQUENCE_TIMEOUT_MS + 10);
|
|
951
|
-
});
|
|
645
|
+
act(() => vi.advanceTimersByTime(KITTY_SEQUENCE_TIMEOUT_MS + 10));
|
|
952
646
|
// Should only have received Ctrl+C, not the incomplete sequence
|
|
953
647
|
expect(keyHandler).toHaveBeenCalledTimes(1);
|
|
954
648
|
expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({
|
|
@@ -959,20 +653,10 @@ describe('Kitty Sequence Parsing', () => {
|
|
|
959
653
|
it('should handle mixed valid and invalid sequences', async () => {
|
|
960
654
|
const keyHandler = vi.fn();
|
|
961
655
|
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
|
962
|
-
act(() =>
|
|
963
|
-
result.current.subscribe(keyHandler);
|
|
964
|
-
});
|
|
656
|
+
act(() => result.current.subscribe(keyHandler));
|
|
965
657
|
// 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
|
-
});
|
|
658
|
+
// Valid enter, then invalid sequence
|
|
659
|
+
act(() => stdin.write('\x1b[13u\x1b[!'));
|
|
976
660
|
// Should parse valid sequence and flush invalid immediately
|
|
977
661
|
expect(keyHandler).toHaveBeenCalledTimes(2);
|
|
978
662
|
expect(keyHandler).toHaveBeenNthCalledWith(1, expect.objectContaining({
|
|
@@ -980,7 +664,6 @@ describe('Kitty Sequence Parsing', () => {
|
|
|
980
664
|
kittyProtocol: true,
|
|
981
665
|
}));
|
|
982
666
|
expect(keyHandler).toHaveBeenNthCalledWith(2, expect.objectContaining({
|
|
983
|
-
name: '',
|
|
984
667
|
sequence: '\x1b[!',
|
|
985
668
|
}));
|
|
986
669
|
});
|
|
@@ -989,20 +672,9 @@ describe('Kitty Sequence Parsing', () => {
|
|
|
989
672
|
const { result } = renderHook(() => useKeypressContext(), {
|
|
990
673
|
wrapper: ({ children }) => wrapper({ children, kittyProtocolEnabled: false }),
|
|
991
674
|
});
|
|
992
|
-
act(() =>
|
|
993
|
-
result.current.subscribe(keyHandler);
|
|
994
|
-
});
|
|
675
|
+
act(() => result.current.subscribe(keyHandler));
|
|
995
676
|
// 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
|
-
});
|
|
677
|
+
act(() => stdin.write('\x1b[13u'));
|
|
1006
678
|
// Should pass through without parsing
|
|
1007
679
|
expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({
|
|
1008
680
|
sequence: '\x1b[13u',
|
|
@@ -1025,7 +697,7 @@ describe('Kitty Sequence Parsing', () => {
|
|
|
1025
697
|
act(() => {
|
|
1026
698
|
stdin.emit('data', Buffer.from(char));
|
|
1027
699
|
});
|
|
1028
|
-
await new Promise((resolve) =>
|
|
700
|
+
await new Promise((resolve) => setImmediate(resolve));
|
|
1029
701
|
}
|
|
1030
702
|
// Should parse once complete
|
|
1031
703
|
await waitFor(() => {
|
|
@@ -1038,113 +710,56 @@ describe('Kitty Sequence Parsing', () => {
|
|
|
1038
710
|
it('should reset timeout when new input arrives', async () => {
|
|
1039
711
|
const keyHandler = vi.fn();
|
|
1040
712
|
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
|
1041
|
-
act(() =>
|
|
1042
|
-
result.current.subscribe(keyHandler);
|
|
1043
|
-
});
|
|
713
|
+
act(() => result.current.subscribe(keyHandler));
|
|
1044
714
|
// 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
|
-
});
|
|
715
|
+
act(() => stdin.write('\x1b[97;13'));
|
|
1055
716
|
// Advance time partway
|
|
1056
|
-
act(() =>
|
|
1057
|
-
vi.advanceTimersByTime(30);
|
|
1058
|
-
});
|
|
717
|
+
act(() => vi.advanceTimersByTime(30));
|
|
1059
718
|
// 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
|
-
});
|
|
719
|
+
act(() => stdin.write('5'));
|
|
1070
720
|
// Advance time from the first timeout point
|
|
1071
|
-
act(() =>
|
|
1072
|
-
vi.advanceTimersByTime(25);
|
|
1073
|
-
});
|
|
721
|
+
act(() => vi.advanceTimersByTime(25));
|
|
1074
722
|
// Should not have timed out yet (timeout restarted)
|
|
1075
723
|
expect(keyHandler).not.toHaveBeenCalled();
|
|
1076
724
|
// 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
|
-
});
|
|
725
|
+
act(() => stdin.write('u'));
|
|
1087
726
|
// Should now parse as complete enter key
|
|
1088
727
|
expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({
|
|
1089
|
-
name: '
|
|
728
|
+
name: 'a',
|
|
1090
729
|
kittyProtocol: true,
|
|
1091
730
|
}));
|
|
1092
731
|
});
|
|
1093
732
|
it('should flush incomplete kitty sequence on FOCUS_IN event', async () => {
|
|
1094
733
|
const keyHandler = vi.fn();
|
|
1095
734
|
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
|
-
});
|
|
735
|
+
act(() => result.current.subscribe(keyHandler));
|
|
736
|
+
act(() => stdin.write(INCOMPLETE_KITTY_SEQUENCE));
|
|
1105
737
|
// Incomplete sequence should be buffered, not broadcast
|
|
1106
738
|
expect(keyHandler).not.toHaveBeenCalled();
|
|
1107
739
|
// Send FOCUS_IN event
|
|
1108
|
-
|
|
1109
|
-
act(() => {
|
|
1110
|
-
stdin.pressKey({
|
|
1111
|
-
sequence: FOCUS_IN,
|
|
1112
|
-
});
|
|
1113
|
-
});
|
|
740
|
+
act(() => stdin.write('\x1b[I'));
|
|
1114
741
|
// The buffered sequence should be flushed
|
|
1115
742
|
expect(keyHandler).toHaveBeenCalledTimes(1);
|
|
1116
743
|
expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({
|
|
1117
744
|
name: '',
|
|
1118
|
-
sequence:
|
|
745
|
+
sequence: INCOMPLETE_KITTY_SEQUENCE,
|
|
1119
746
|
paste: false,
|
|
1120
747
|
}));
|
|
1121
748
|
});
|
|
1122
749
|
it('should flush incomplete kitty sequence on FOCUS_OUT event', async () => {
|
|
1123
750
|
const keyHandler = vi.fn();
|
|
1124
751
|
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
|
-
});
|
|
752
|
+
act(() => result.current.subscribe(keyHandler));
|
|
753
|
+
act(() => stdin.write(INCOMPLETE_KITTY_SEQUENCE));
|
|
1134
754
|
// Incomplete sequence should be buffered, not broadcast
|
|
1135
755
|
expect(keyHandler).not.toHaveBeenCalled();
|
|
1136
756
|
// Send FOCUS_OUT event
|
|
1137
|
-
|
|
1138
|
-
act(() => {
|
|
1139
|
-
stdin.pressKey({
|
|
1140
|
-
sequence: FOCUS_OUT,
|
|
1141
|
-
});
|
|
1142
|
-
});
|
|
757
|
+
act(() => stdin.write('\x1b[O'));
|
|
1143
758
|
// The buffered sequence should be flushed
|
|
1144
759
|
expect(keyHandler).toHaveBeenCalledTimes(1);
|
|
1145
760
|
expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({
|
|
1146
761
|
name: '',
|
|
1147
|
-
sequence:
|
|
762
|
+
sequence: INCOMPLETE_KITTY_SEQUENCE,
|
|
1148
763
|
paste: false,
|
|
1149
764
|
}));
|
|
1150
765
|
});
|
|
@@ -1152,39 +767,27 @@ describe('Kitty Sequence Parsing', () => {
|
|
|
1152
767
|
vi.useFakeTimers();
|
|
1153
768
|
const keyHandler = vi.fn();
|
|
1154
769
|
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
|
-
});
|
|
770
|
+
act(() => result.current.subscribe(keyHandler));
|
|
771
|
+
act(() => stdin.write(INCOMPLETE_KITTY_SEQUENCE));
|
|
1164
772
|
// Incomplete sequence should be buffered, not broadcast
|
|
1165
773
|
expect(keyHandler).not.toHaveBeenCalled();
|
|
1166
774
|
// Send paste start sequence
|
|
1167
|
-
|
|
1168
|
-
act(() => {
|
|
1169
|
-
stdin.emit('data', Buffer.from(PASTE_MODE_PREFIX));
|
|
1170
|
-
});
|
|
775
|
+
act(() => stdin.write(`\x1b[200~`));
|
|
1171
776
|
// The buffered sequence should be flushed
|
|
1172
777
|
expect(keyHandler).toHaveBeenCalledTimes(1);
|
|
1173
778
|
expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({
|
|
1174
779
|
name: '',
|
|
1175
|
-
sequence:
|
|
780
|
+
sequence: INCOMPLETE_KITTY_SEQUENCE,
|
|
1176
781
|
paste: false,
|
|
1177
782
|
}));
|
|
1178
783
|
// Now send some paste content and end paste to make sure paste still works
|
|
1179
784
|
const pastedText = 'hello';
|
|
1180
785
|
const PASTE_MODE_SUFFIX = `\x1b[201~`;
|
|
1181
786
|
act(() => {
|
|
1182
|
-
stdin.
|
|
1183
|
-
stdin.
|
|
1184
|
-
});
|
|
1185
|
-
act(() => {
|
|
1186
|
-
vi.runAllTimers();
|
|
787
|
+
stdin.write(pastedText);
|
|
788
|
+
stdin.write(PASTE_MODE_SUFFIX);
|
|
1187
789
|
});
|
|
790
|
+
act(() => vi.runAllTimers());
|
|
1188
791
|
// The paste event should be broadcast
|
|
1189
792
|
expect(keyHandler).toHaveBeenCalledTimes(2);
|
|
1190
793
|
expect(keyHandler).toHaveBeenNthCalledWith(2, expect.objectContaining({
|
|
@@ -1193,5 +796,122 @@ describe('Kitty Sequence Parsing', () => {
|
|
|
1193
796
|
}));
|
|
1194
797
|
vi.useRealTimers();
|
|
1195
798
|
});
|
|
799
|
+
describe('SGR Mouse Handling', () => {
|
|
800
|
+
it('should ignore SGR mouse sequences', async () => {
|
|
801
|
+
const keyHandler = vi.fn();
|
|
802
|
+
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
|
803
|
+
act(() => result.current.subscribe(keyHandler));
|
|
804
|
+
// Send various SGR mouse sequences
|
|
805
|
+
act(() => {
|
|
806
|
+
stdin.write('\x1b[<0;10;20M'); // Mouse press
|
|
807
|
+
stdin.write('\x1b[<0;10;20m'); // Mouse release
|
|
808
|
+
stdin.write('\x1b[<32;30;40M'); // Mouse drag
|
|
809
|
+
stdin.write('\x1b[<64;5;5M'); // Scroll up
|
|
810
|
+
});
|
|
811
|
+
// Should not broadcast any of these as keystrokes
|
|
812
|
+
expect(keyHandler).not.toHaveBeenCalled();
|
|
813
|
+
});
|
|
814
|
+
it('should handle mixed SGR mouse and key sequences', async () => {
|
|
815
|
+
const keyHandler = vi.fn();
|
|
816
|
+
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
|
817
|
+
act(() => result.current.subscribe(keyHandler));
|
|
818
|
+
// Send mouse event then a key press
|
|
819
|
+
act(() => {
|
|
820
|
+
stdin.write('\x1b[<0;10;20M');
|
|
821
|
+
stdin.write('a');
|
|
822
|
+
});
|
|
823
|
+
// Should only broadcast 'a'
|
|
824
|
+
expect(keyHandler).toHaveBeenCalledTimes(1);
|
|
825
|
+
expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({
|
|
826
|
+
name: 'a',
|
|
827
|
+
sequence: 'a',
|
|
828
|
+
}));
|
|
829
|
+
});
|
|
830
|
+
it('should ignore X11 mouse sequences', async () => {
|
|
831
|
+
const keyHandler = vi.fn();
|
|
832
|
+
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
|
833
|
+
act(() => result.current.subscribe(keyHandler));
|
|
834
|
+
// Send X11 mouse sequence: ESC [ M followed by 3 bytes
|
|
835
|
+
// Space is 32. 32+0=32 (button 0), 32+33=65 ('A', col 33), 32+34=66 ('B', row 34)
|
|
836
|
+
const x11Seq = '\x1b[M AB';
|
|
837
|
+
act(() => {
|
|
838
|
+
stdin.write(x11Seq);
|
|
839
|
+
});
|
|
840
|
+
// Should not broadcast as keystrokes
|
|
841
|
+
expect(keyHandler).not.toHaveBeenCalled();
|
|
842
|
+
});
|
|
843
|
+
it('should not flush slow SGR mouse sequences as garbage', async () => {
|
|
844
|
+
vi.useFakeTimers();
|
|
845
|
+
const keyHandler = vi.fn();
|
|
846
|
+
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
|
847
|
+
act(() => result.current.subscribe(keyHandler));
|
|
848
|
+
// Send start of SGR sequence
|
|
849
|
+
act(() => stdin.write('\x1b[<'));
|
|
850
|
+
// Advance time past the normal kitty timeout (50ms)
|
|
851
|
+
act(() => vi.advanceTimersByTime(KITTY_SEQUENCE_TIMEOUT_MS + 10));
|
|
852
|
+
// Send the rest
|
|
853
|
+
act(() => stdin.write('0;37;25M'));
|
|
854
|
+
// Should NOT have flushed the prefix as garbage, and should have consumed the whole thing
|
|
855
|
+
expect(keyHandler).not.toHaveBeenCalled();
|
|
856
|
+
vi.useRealTimers();
|
|
857
|
+
});
|
|
858
|
+
it('should ignore specific SGR mouse sequence sandwiched between keystrokes', async () => {
|
|
859
|
+
const keyHandler = vi.fn();
|
|
860
|
+
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
|
861
|
+
act(() => result.current.subscribe(keyHandler));
|
|
862
|
+
act(() => {
|
|
863
|
+
stdin.write('H');
|
|
864
|
+
stdin.write('\x1b[<64;96;8M');
|
|
865
|
+
stdin.write('I');
|
|
866
|
+
});
|
|
867
|
+
expect(keyHandler).toHaveBeenCalledTimes(2);
|
|
868
|
+
expect(keyHandler).toHaveBeenNthCalledWith(1, expect.objectContaining({ name: 'h', sequence: 'H', shift: true }));
|
|
869
|
+
expect(keyHandler).toHaveBeenNthCalledWith(2, expect.objectContaining({ name: 'i', sequence: 'I', shift: true }));
|
|
870
|
+
});
|
|
871
|
+
});
|
|
872
|
+
describe('Ignored Sequences', () => {
|
|
873
|
+
describe.each([true, false])('with kittyProtocolEnabled = %s', (kittyEnabled) => {
|
|
874
|
+
it.each([
|
|
875
|
+
{ name: 'Focus In', sequence: '\x1b[I' },
|
|
876
|
+
{ name: 'Focus Out', sequence: '\x1b[O' },
|
|
877
|
+
{ name: 'SGR Mouse Release', sequence: '\u001b[<0;44;18m' },
|
|
878
|
+
{ name: 'something mouse', sequence: '\u001b[<0;53;19M' },
|
|
879
|
+
{ name: 'another mouse', sequence: '\u001b[<0;29;19m' },
|
|
880
|
+
])('should ignore $name sequence', async ({ sequence }) => {
|
|
881
|
+
vi.useFakeTimers();
|
|
882
|
+
const keyHandler = vi.fn();
|
|
883
|
+
const wrapper = ({ children }) => (_jsx(KeypressProvider, { kittyProtocolEnabled: kittyEnabled, children: children }));
|
|
884
|
+
const { result } = renderHook(() => useKeypressContext(), {
|
|
885
|
+
wrapper,
|
|
886
|
+
});
|
|
887
|
+
act(() => result.current.subscribe(keyHandler));
|
|
888
|
+
for (const char of sequence) {
|
|
889
|
+
act(() => {
|
|
890
|
+
stdin.write(char);
|
|
891
|
+
});
|
|
892
|
+
await act(async () => {
|
|
893
|
+
vi.advanceTimersByTime(0);
|
|
894
|
+
});
|
|
895
|
+
}
|
|
896
|
+
act(() => {
|
|
897
|
+
stdin.write('HI');
|
|
898
|
+
});
|
|
899
|
+
expect(keyHandler).toHaveBeenCalledTimes(2);
|
|
900
|
+
expect(keyHandler).toHaveBeenNthCalledWith(1, expect.objectContaining({ name: 'h', sequence: 'H', shift: true }));
|
|
901
|
+
expect(keyHandler).toHaveBeenNthCalledWith(2, expect.objectContaining({ name: 'i', sequence: 'I', shift: true }));
|
|
902
|
+
vi.useRealTimers();
|
|
903
|
+
});
|
|
904
|
+
});
|
|
905
|
+
it('should handle F12 when kittyProtocolEnabled is false', async () => {
|
|
906
|
+
const keyHandler = vi.fn();
|
|
907
|
+
const wrapper = ({ children }) => (_jsx(KeypressProvider, { kittyProtocolEnabled: false, children: children }));
|
|
908
|
+
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
|
909
|
+
act(() => result.current.subscribe(keyHandler));
|
|
910
|
+
act(() => {
|
|
911
|
+
stdin.write('\u001b[24~');
|
|
912
|
+
});
|
|
913
|
+
expect(keyHandler).toHaveBeenCalledWith(expect.objectContaining({ name: 'f12', sequence: '\u001b[24~' }));
|
|
914
|
+
});
|
|
915
|
+
});
|
|
1196
916
|
});
|
|
1197
917
|
//# sourceMappingURL=KeypressContext.test.js.map
|