@mmmbuto/gemini-cli-termux 0.30.3-termux → 0.30.5-termux
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +42 -193
- package/bundle/docs/cli/settings.md +8 -9
- package/bundle/docs/extensions/reference.md +14 -38
- package/bundle/docs/hooks/reference.md +0 -8
- package/bundle/docs/reference/configuration.md +0 -8
- package/bundle/gemini.js +10730 -11704
- package/bundle/node_modules/@google/gemini-cli-devtools/package.json +1 -1
- package/bundle/package.json +2 -1
- package/package.json +67 -5
- package/packages/cli/dist/index.js +0 -0
- package/packages/cli/dist/package.json +2 -2
- package/packages/cli/dist/src/commands/extensions/examples/custom-commands/gemini-extension.json +2 -2
- package/packages/cli/dist/src/commands/extensions/examples/exclude-tools/gemini-extension.json +3 -3
- package/packages/cli/dist/src/commands/extensions/examples/hooks/gemini-extension.json +2 -2
- package/packages/cli/dist/src/commands/extensions/examples/hooks/hooks/hooks.json +11 -11
- package/packages/cli/dist/src/commands/extensions/examples/mcp-server/example.js +1 -1
- package/packages/cli/dist/src/commands/extensions/examples/mcp-server/gemini-extension.json +8 -8
- package/packages/cli/dist/src/commands/extensions/examples/mcp-server/package.json +9 -9
- package/packages/cli/dist/src/commands/extensions/examples/skills/gemini-extension.json +2 -2
- package/packages/cli/dist/src/commands/extensions/examples/themes-example/README.md +8 -5
- package/packages/cli/dist/src/commands/extensions/examples/themes-example/gemini-extension.json +27 -27
- package/packages/cli/dist/src/config/config.js +4 -65
- package/packages/cli/dist/src/config/config.js.map +1 -1
- package/packages/cli/dist/src/config/settingsSchema.d.ts +1 -235
- package/packages/cli/dist/src/config/settingsSchema.js +1 -235
- package/packages/cli/dist/src/config/settingsSchema.js.map +1 -1
- package/packages/cli/dist/src/gemini.js +0 -2
- package/packages/cli/dist/src/gemini.js.map +1 -1
- package/packages/cli/dist/src/generated/git-commit.d.ts +2 -2
- package/packages/cli/dist/src/generated/git-commit.js +2 -2
- package/packages/cli/dist/src/patches/empty-module.d.ts +2 -0
- package/packages/cli/dist/src/patches/empty-module.js +2 -0
- package/packages/cli/dist/src/patches/empty-module.js.map +1 -0
- package/packages/cli/dist/src/services/McpPromptLoader.js +3 -6
- package/packages/cli/dist/src/services/McpPromptLoader.js.map +1 -1
- package/packages/cli/dist/src/ui/components/AgentConfigDialog.js +19 -5
- package/packages/cli/dist/src/ui/components/AgentConfigDialog.js.map +1 -1
- package/packages/cli/dist/src/ui/components/AgentConfigDialog.test.js +41 -19
- package/packages/cli/dist/src/ui/components/AgentConfigDialog.test.js.map +1 -1
- package/packages/cli/dist/src/ui/components/DialogManager.d.ts +1 -1
- package/packages/cli/dist/src/ui/components/DialogManager.js +34 -7
- package/packages/cli/dist/src/ui/components/DialogManager.js.map +1 -1
- package/packages/cli/dist/src/ui/components/InputPrompt.d.ts +9 -0
- package/packages/cli/dist/src/ui/components/InputPrompt.js +350 -125
- package/packages/cli/dist/src/ui/components/InputPrompt.js.map +1 -1
- package/packages/cli/dist/src/ui/components/InputPrompt.test.js +788 -35
- package/packages/cli/dist/src/ui/components/InputPrompt.test.js.map +1 -1
- package/packages/cli/dist/src/ui/components/messages/ShellToolMessage.js +2 -2
- package/packages/cli/dist/src/ui/components/messages/ShellToolMessage.js.map +1 -1
- package/packages/cli/dist/src/ui/components/messages/ToolConfirmationMessage.js +4 -77
- package/packages/cli/dist/src/ui/components/messages/ToolConfirmationMessage.js.map +1 -1
- package/packages/cli/dist/src/ui/components/messages/ToolConfirmationMessage.test.js +0 -51
- package/packages/cli/dist/src/ui/components/messages/ToolConfirmationMessage.test.js.map +1 -1
- package/packages/cli/dist/src/ui/components/messages/ToolMessage.js +2 -2
- package/packages/cli/dist/src/ui/components/messages/ToolMessage.js.map +1 -1
- package/packages/cli/dist/src/ui/components/messages/ToolShared.d.ts +0 -1
- package/packages/cli/dist/src/ui/components/messages/ToolShared.js +2 -2
- package/packages/cli/dist/src/ui/components/messages/ToolShared.js.map +1 -1
- package/packages/cli/dist/src/ui/components/shared/BaseSettingsDialog.js +4 -1
- package/packages/cli/dist/src/ui/components/shared/BaseSettingsDialog.js.map +1 -1
- package/packages/cli/dist/src/ui/components/shared/BaseSettingsDialog.test.js +139 -40
- package/packages/cli/dist/src/ui/components/shared/BaseSettingsDialog.test.js.map +1 -1
- package/packages/cli/dist/src/ui/components/shared/TextInput.js +2 -1
- package/packages/cli/dist/src/ui/components/shared/TextInput.js.map +1 -1
- package/packages/cli/dist/src/ui/hooks/toolMapping.js +0 -1
- package/packages/cli/dist/src/ui/hooks/toolMapping.js.map +1 -1
- package/packages/cli/dist/src/ui/hooks/toolMapping.test.js +0 -13
- package/packages/cli/dist/src/ui/hooks/toolMapping.test.js.map +1 -1
- package/packages/cli/dist/src/ui/hooks/useToolScheduler.js +2 -18
- package/packages/cli/dist/src/ui/hooks/useToolScheduler.js.map +1 -1
- package/packages/cli/dist/src/ui/hooks/useToolScheduler.test.js +0 -44
- package/packages/cli/dist/src/ui/hooks/useToolScheduler.test.js.map +1 -1
- package/packages/cli/dist/src/ui/keyMatchers.test.js.map +1 -1
- package/packages/cli/dist/src/ui/types.d.ts +0 -1
- package/packages/cli/dist/src/ui/types.js.map +1 -1
- package/packages/cli/dist/src/ui/utils/commandUtils.js +4 -1
- package/packages/cli/dist/src/ui/utils/commandUtils.js.map +1 -1
- package/packages/cli/dist/src/utils/activityLogger.js +23 -94
- package/packages/cli/dist/src/utils/activityLogger.js.map +1 -1
- package/packages/cli/dist/src/utils/handleAutoUpdate.d.ts +1 -1
- package/packages/cli/dist/src/utils/handleAutoUpdate.js +1 -1
- package/packages/cli/dist/src/utils/handleAutoUpdate.js.map +1 -1
- package/packages/cli/dist/src/utils/handleAutoUpdate.test.js +11 -11
- package/packages/cli/dist/src/utils/handleAutoUpdate.test.js.map +1 -1
- package/{bundle/sandbox-macos-restrictive-closed.sb → packages/cli/dist/src/utils/sandbox-macos-strict-open.sb} +42 -4
- package/packages/cli/dist/src/utils/{sandbox-macos-restrictive-closed.sb → sandbox-macos-strict-proxied.sb} +44 -4
- package/packages/cli/dist/tsconfig.tsbuildinfo +1 -1
- package/packages/core/dist/docs/admin/enterprise-controls.md +115 -0
- package/packages/core/dist/docs/changelogs/index.md +57 -1
- package/packages/core/dist/docs/changelogs/latest.md +357 -314
- package/packages/core/dist/docs/changelogs/preview.md +288 -411
- package/packages/core/dist/docs/cli/checkpointing.md +2 -3
- package/packages/core/dist/docs/cli/cli-reference.md +42 -28
- package/packages/core/dist/docs/cli/custom-commands.md +3 -0
- package/packages/core/dist/docs/cli/enterprise.md +28 -8
- package/packages/core/dist/docs/cli/gemini-md.md +21 -13
- package/packages/core/dist/docs/cli/headless.md +34 -372
- package/packages/core/dist/docs/cli/model.md +1 -1
- package/packages/core/dist/docs/cli/plan-mode.md +245 -0
- package/packages/core/dist/docs/cli/rewind.md +11 -11
- package/packages/core/dist/docs/cli/sandbox.md +6 -5
- package/packages/core/dist/docs/cli/session-management.md +61 -44
- package/packages/core/dist/docs/cli/settings.md +64 -49
- package/packages/core/dist/docs/cli/skills.md +29 -7
- package/packages/core/dist/docs/cli/telemetry.md +41 -5
- package/packages/core/dist/docs/cli/themes.md +88 -54
- package/packages/core/dist/docs/cli/trusted-folders.md +31 -0
- package/packages/core/dist/docs/cli/tutorials/automation.md +187 -0
- package/packages/core/dist/docs/cli/tutorials/file-management.md +142 -0
- package/packages/core/dist/docs/cli/tutorials/mcp-setup.md +105 -0
- package/packages/core/dist/docs/cli/tutorials/memory-management.md +126 -0
- package/packages/core/dist/docs/cli/tutorials/session-management.md +105 -0
- package/packages/core/dist/docs/cli/tutorials/shell-commands.md +107 -0
- package/packages/core/dist/docs/cli/tutorials/skills-getting-started.md +36 -31
- package/packages/core/dist/docs/cli/tutorials/task-planning.md +93 -0
- package/packages/core/dist/docs/cli/tutorials/web-tools.md +78 -0
- package/packages/core/dist/docs/core/index.md +7 -7
- package/packages/core/dist/docs/core/subagents.md +40 -40
- package/packages/core/dist/docs/extensions/best-practices.md +102 -53
- package/packages/core/dist/docs/extensions/index.md +37 -21
- package/packages/core/dist/docs/extensions/reference.md +148 -219
- package/packages/core/dist/docs/extensions/releasing.md +93 -122
- package/packages/core/dist/docs/extensions/writing-extensions.md +87 -76
- package/packages/core/dist/docs/get-started/authentication.md +4 -4
- package/packages/core/dist/docs/get-started/examples.md +39 -119
- package/packages/core/dist/docs/get-started/gemini-3.md +17 -3
- package/packages/core/dist/docs/get-started/index.md +16 -5
- package/packages/core/dist/docs/get-started/installation.md +110 -77
- package/packages/core/dist/docs/hooks/best-practices.md +1 -1
- package/packages/core/dist/docs/hooks/reference.md +2 -2
- package/packages/core/dist/docs/index.md +142 -149
- package/packages/core/dist/docs/redirects.json +19 -0
- package/packages/core/dist/docs/reference/commands.md +523 -0
- package/{bundle/docs/get-started → packages/core/dist/docs/reference}/configuration.md +180 -71
- package/packages/core/dist/docs/{cli → reference}/keyboard-shortcuts.md +49 -35
- package/packages/core/dist/docs/{core → reference}/policy-engine.md +76 -32
- package/packages/core/dist/docs/releases.md +2 -2
- package/{bundle/docs → packages/core/dist/docs/resources}/faq.md +1 -1
- package/packages/core/dist/docs/{quota-and-pricing.md → resources/quota-and-pricing.md} +12 -5
- package/{bundle/docs → packages/core/dist/docs/resources}/tos-privacy.md +3 -3
- package/packages/core/dist/docs/{troubleshooting.md → resources/troubleshooting.md} +1 -1
- package/packages/core/dist/docs/sidebar.json +194 -113
- package/packages/core/dist/docs/tools/activate-skill.md +43 -0
- package/packages/core/dist/docs/tools/ask-user.md +95 -0
- package/packages/core/dist/docs/tools/file-system.md +55 -143
- package/packages/core/dist/docs/tools/index.md +97 -93
- package/packages/core/dist/docs/tools/internal-docs.md +46 -0
- package/packages/core/dist/docs/tools/mcp-server.md +65 -16
- package/packages/core/dist/docs/tools/memory.md +21 -40
- package/packages/core/dist/docs/tools/planning.md +57 -0
- package/packages/core/dist/docs/tools/shell.md +44 -88
- package/packages/core/dist/docs/tools/todos.md +22 -44
- package/packages/core/dist/docs/tools/web-fetch.md +22 -46
- package/packages/core/dist/docs/tools/web-search.md +19 -29
- package/packages/core/dist/src/code_assist/types.d.ts +14 -14
- package/packages/core/dist/src/config/config.d.ts +1 -13
- package/packages/core/dist/src/config/config.js +6 -39
- package/packages/core/dist/src/config/config.js.map +1 -1
- package/packages/core/dist/src/confirmation-bus/types.d.ts +0 -3
- package/packages/core/dist/src/confirmation-bus/types.js.map +1 -1
- package/packages/core/dist/src/core/coreToolHookTriggers.d.ts +1 -1
- package/packages/core/dist/src/core/coreToolHookTriggers.js +3 -8
- package/packages/core/dist/src/core/coreToolHookTriggers.js.map +1 -1
- package/packages/core/dist/src/generated/git-commit.d.ts +2 -2
- package/packages/core/dist/src/generated/git-commit.js +2 -2
- package/packages/core/dist/src/hooks/hookEventHandler.d.ts +2 -2
- package/packages/core/dist/src/hooks/hookEventHandler.js +2 -8
- package/packages/core/dist/src/hooks/hookEventHandler.js.map +1 -1
- package/packages/core/dist/src/hooks/hookSystem.d.ts +2 -2
- package/packages/core/dist/src/hooks/hookSystem.js +4 -4
- package/packages/core/dist/src/hooks/hookSystem.js.map +1 -1
- package/packages/core/dist/src/hooks/types.d.ts +0 -18
- package/packages/core/dist/src/hooks/types.js +0 -17
- package/packages/core/dist/src/hooks/types.js.map +1 -1
- package/packages/core/dist/src/ide/ide-client.js +1 -1
- package/packages/core/dist/src/ide/ide-client.js.map +1 -1
- package/packages/core/dist/src/ide/types.d.ts +8 -8
- package/packages/core/dist/src/index.d.ts +2 -0
- package/packages/core/dist/src/index.js +2 -0
- package/packages/core/dist/src/index.js.map +1 -1
- package/packages/core/dist/src/policy/policies/plan.toml +29 -43
- package/packages/core/dist/src/policy/policies/read-only.toml +12 -11
- package/packages/core/dist/src/policy/policies/write.toml +11 -10
- package/packages/core/dist/src/policy/policies/yolo.toml +24 -12
- package/packages/core/dist/src/policy/policy-engine.js +1 -5
- package/packages/core/dist/src/policy/policy-engine.js.map +1 -1
- package/packages/core/dist/src/policy/types.d.ts +1 -2
- package/packages/core/dist/src/policy/types.js +0 -1
- package/packages/core/dist/src/policy/types.js.map +1 -1
- package/packages/core/dist/src/safety/context-builder.d.ts +3 -3
- package/packages/core/dist/src/safety/context-builder.js +4 -60
- package/packages/core/dist/src/safety/context-builder.js.map +1 -1
- package/packages/core/dist/src/safety/context-builder.test.js +18 -98
- package/packages/core/dist/src/safety/context-builder.test.js.map +1 -1
- package/packages/core/dist/src/safety/protocol.d.ts +0 -4
- package/packages/core/dist/src/safety/registry.d.ts +1 -2
- package/packages/core/dist/src/safety/registry.js +4 -14
- package/packages/core/dist/src/safety/registry.js.map +1 -1
- package/packages/core/dist/src/safety/registry.test.js +2 -5
- package/packages/core/dist/src/safety/registry.test.js.map +1 -1
- package/packages/core/dist/src/scheduler/scheduler.d.ts +1 -1
- package/packages/core/dist/src/scheduler/scheduler.js +4 -49
- package/packages/core/dist/src/scheduler/scheduler.js.map +1 -1
- package/packages/core/dist/src/scheduler/scheduler.test.js +0 -91
- package/packages/core/dist/src/scheduler/scheduler.test.js.map +1 -1
- package/packages/core/dist/src/scheduler/state-manager.d.ts +0 -6
- package/packages/core/dist/src/scheduler/state-manager.js +0 -12
- package/packages/core/dist/src/scheduler/state-manager.js.map +1 -1
- package/packages/core/dist/src/scheduler/tool-executor.js +7 -9
- package/packages/core/dist/src/scheduler/tool-executor.js.map +1 -1
- package/packages/core/dist/src/scheduler/tool-executor.test.js +1 -1
- package/packages/core/dist/src/scheduler/tool-executor.test.js.map +1 -1
- package/packages/core/dist/src/scheduler/types.d.ts +0 -13
- package/packages/core/dist/src/services/shellExecutionService.d.ts +1 -1
- package/packages/core/dist/src/services/shellExecutionService.js +7 -8
- package/packages/core/dist/src/services/shellExecutionService.js.map +1 -1
- package/packages/core/dist/src/services/shellExecutionService.test.js +5 -5
- package/packages/core/dist/src/services/shellExecutionService.test.js.map +1 -1
- package/packages/core/dist/src/services/test-data/resolved-aliases-retry.golden.json +251 -251
- package/packages/core/dist/src/services/test-data/resolved-aliases.golden.json +251 -251
- package/packages/core/dist/src/skills/builtin/skill-creator/scripts/init_skill.cjs +5 -1
- package/packages/core/dist/src/skills/builtin/skill-creator/scripts/package_skill.cjs +5 -1
- package/packages/core/dist/src/skills/builtin/skill-creator/scripts/validate_skill.cjs +5 -1
- package/packages/core/dist/src/telemetry/clearcut-logger/clearcut-logger.d.ts +1 -3
- package/packages/core/dist/src/telemetry/clearcut-logger/clearcut-logger.js +0 -2
- package/packages/core/dist/src/telemetry/clearcut-logger/clearcut-logger.js.map +1 -1
- package/packages/core/dist/src/telemetry/clearcut-logger/event-metadata-key.d.ts +1 -9
- package/packages/core/dist/src/telemetry/clearcut-logger/event-metadata-key.js +1 -19
- package/packages/core/dist/src/telemetry/clearcut-logger/event-metadata-key.js.map +1 -1
- package/packages/core/dist/src/telemetry/index.d.ts +1 -2
- package/packages/core/dist/src/telemetry/index.js +1 -2
- package/packages/core/dist/src/telemetry/index.js.map +1 -1
- package/packages/core/dist/src/telemetry/loggers.js +0 -4
- package/packages/core/dist/src/telemetry/loggers.js.map +1 -1
- package/packages/core/dist/src/telemetry/trace.js.map +1 -1
- package/packages/core/dist/src/telemetry/types.d.ts +0 -26
- package/packages/core/dist/src/telemetry/types.js +2 -76
- package/packages/core/dist/src/telemetry/types.js.map +1 -1
- package/packages/core/dist/src/tools/diffOptions.d.ts +2 -2
- package/packages/core/dist/src/tools/diffOptions.js.map +1 -1
- package/packages/core/dist/src/tools/mcp-client.test.js.map +1 -1
- package/packages/core/dist/src/tools/mcp-tool.d.ts +1 -3
- package/packages/core/dist/src/tools/mcp-tool.js +2 -9
- package/packages/core/dist/src/tools/mcp-tool.js.map +1 -1
- package/packages/core/dist/src/tools/memoryTool.d.ts +0 -10
- package/packages/core/dist/src/tools/memoryTool.js +30 -25
- package/packages/core/dist/src/tools/memoryTool.js.map +1 -1
- package/packages/core/dist/src/tools/tool-names.d.ts +6 -0
- package/packages/core/dist/src/tools/tool-names.js +16 -1
- package/packages/core/dist/src/tools/tool-names.js.map +1 -1
- package/packages/core/dist/src/tools/tools.d.ts +0 -11
- package/packages/core/dist/src/tools/tools.js.map +1 -1
- package/packages/core/dist/src/utils/getPty.d.ts +14 -1
- package/packages/core/dist/src/utils/getPty.js +67 -2
- package/packages/core/dist/src/utils/getPty.js.map +1 -1
- package/packages/core/dist/src/utils/memoryDiscovery.d.ts +11 -3
- package/packages/core/dist/src/utils/memoryDiscovery.js +56 -21
- package/packages/core/dist/src/utils/memoryDiscovery.js.map +1 -1
- package/packages/core/dist/src/utils/secure-browser-launcher.js +40 -29
- package/packages/core/dist/src/utils/secure-browser-launcher.js.map +1 -1
- package/packages/core/dist/src/utils/textUtils.d.ts +0 -9
- package/packages/core/dist/src/utils/textUtils.js +0 -15
- package/packages/core/dist/src/utils/textUtils.js.map +1 -1
- package/packages/core/dist/src/utils/textUtils.test.js +1 -42
- package/packages/core/dist/src/utils/textUtils.test.js.map +1 -1
- package/packages/core/dist/tsconfig.tsbuildinfo +1 -1
- package/packages/test-utils/dist/tsconfig.tsbuildinfo +1 -1
- package/bundle/docs/ROADMAP.md +0 -113
- package/bundle/docs/TERMUX.md +0 -95
- package/bundle/docs/architecture.md +0 -80
- package/bundle/docs/assets/hero.png +0 -0
- package/bundle/docs/cli/authentication.md +0 -3
- package/bundle/docs/cli/commands.md +0 -430
- package/bundle/docs/cli/context-memory.md +0 -69
- package/bundle/docs/cli/index.md +0 -65
- package/bundle/docs/cli/keyboard-shortcuts.md +0 -140
- package/bundle/docs/cli/tutorials.md +0 -87
- package/bundle/docs/core/policy-engine.md +0 -305
- package/bundle/docs/get-started/configuration-v1.md +0 -888
- package/bundle/docs/patches/README.md +0 -74
- package/bundle/docs/patches/mcp-sdk-typings-shim.md +0 -27
- package/bundle/docs/quota-and-pricing.md +0 -158
- package/bundle/docs/termux-api/COMMANDS.md +0 -592
- package/bundle/docs/termux-api/DISCOVERY_SETUP.md +0 -670
- package/bundle/docs/termux-api/EXECUTION_PLAN.md +0 -532
- package/bundle/docs/termux-api/MERGE_STRATEGY.md +0 -365
- package/bundle/docs/termux-api/PATCHES.md +0 -480
- package/bundle/docs/termux-api/README.md +0 -416
- package/bundle/docs/troubleshooting.md +0 -173
- package/bundle/policies/conseca.toml +0 -6
- package/bundle/sandbox-macos-permissive-closed.sb +0 -32
- package/packages/cli/README.md +0 -173
- package/packages/cli/dist/src/commands/extensions/examples/mcp-server/example.d.ts +0 -6
- package/packages/cli/dist/src/commands/extensions/examples/mcp-server/example.js.map +0 -1
- package/packages/cli/dist/src/commands/extensions/examples/mcp-server/example.test.d.ts +0 -6
- package/packages/cli/dist/src/commands/extensions/examples/mcp-server/example.test.js +0 -111
- package/packages/cli/dist/src/commands/extensions/examples/mcp-server/example.test.js.map +0 -1
- package/packages/cli/dist/src/ui/components/messages/ToolConfirmationMessageOverflow.test.d.ts +0 -6
- package/packages/cli/dist/src/ui/components/messages/ToolConfirmationMessageOverflow.test.js +0 -93
- package/packages/cli/dist/src/ui/components/messages/ToolConfirmationMessageOverflow.test.js.map +0 -1
- package/packages/cli/dist/src/ui/hooks/useReactToolScheduler.d.ts +0 -42
- package/packages/cli/dist/src/ui/hooks/useReactToolScheduler.js +0 -105
- package/packages/cli/dist/src/ui/hooks/useReactToolScheduler.js.map +0 -1
- package/packages/cli/dist/src/ui/hooks/useReactToolScheduler.test.d.ts +0 -6
- package/packages/cli/dist/src/ui/hooks/useReactToolScheduler.test.js +0 -58
- package/packages/cli/dist/src/ui/hooks/useReactToolScheduler.test.js.map +0 -1
- package/packages/cli/dist/src/ui/hooks/useRefreshMemoryCommand.d.ts +0 -6
- package/packages/cli/dist/src/ui/hooks/useRefreshMemoryCommand.js +0 -7
- package/packages/cli/dist/src/ui/hooks/useRefreshMemoryCommand.js.map +0 -1
- package/packages/cli/dist/src/ui/hooks/useShowMemoryCommand.d.ts +0 -9
- package/packages/cli/dist/src/ui/hooks/useShowMemoryCommand.js +0 -59
- package/packages/cli/dist/src/ui/hooks/useShowMemoryCommand.js.map +0 -1
- package/packages/cli/dist/src/ui/hooks/useToolExecutionScheduler.d.ts +0 -30
- package/packages/cli/dist/src/ui/hooks/useToolExecutionScheduler.js +0 -149
- package/packages/cli/dist/src/ui/hooks/useToolExecutionScheduler.js.map +0 -1
- package/packages/cli/dist/src/ui/hooks/useToolExecutionScheduler.test.d.ts +0 -6
- package/packages/cli/dist/src/ui/hooks/useToolExecutionScheduler.test.js +0 -376
- package/packages/cli/dist/src/ui/hooks/useToolExecutionScheduler.test.js.map +0 -1
- package/packages/cli/dist/src/ui/hooks/useToolSchedulerFacade.test.d.ts +0 -6
- package/packages/cli/dist/src/ui/hooks/useToolSchedulerFacade.test.js +0 -45
- package/packages/cli/dist/src/ui/hooks/useToolSchedulerFacade.test.js.map +0 -1
- package/packages/cli/dist/src/ui/utils/InlineMarkdownRenderer.test.d.ts +0 -6
- package/packages/cli/dist/src/ui/utils/InlineMarkdownRenderer.test.js +0 -21
- package/packages/cli/dist/src/ui/utils/InlineMarkdownRenderer.test.js.map +0 -1
- package/packages/cli/dist/src/ui/utils/terminalUtils.test.d.ts +0 -6
- package/packages/cli/dist/src/ui/utils/terminalUtils.test.js +0 -40
- package/packages/cli/dist/src/ui/utils/terminalUtils.test.js.map +0 -1
- package/packages/cli/dist/src/utils/checks.d.ts +0 -19
- package/packages/cli/dist/src/utils/checks.js +0 -24
- package/packages/cli/dist/src/utils/checks.js.map +0 -1
- package/packages/cli/dist/src/utils/checks.test.d.ts +0 -6
- package/packages/cli/dist/src/utils/checks.test.js +0 -29
- package/packages/cli/dist/src/utils/checks.test.js.map +0 -1
- package/packages/cli/dist/src/utils/sandbox-macos-permissive-closed.sb +0 -32
- package/packages/cli/index.js +0 -2
- package/packages/core/dist/docs/TERMUX.md +0 -95
- package/packages/core/dist/docs/architecture.md +0 -80
- package/packages/core/dist/docs/assets/hero.png +0 -0
- package/packages/core/dist/docs/cli/authentication.md +0 -3
- package/packages/core/dist/docs/cli/commands.md +0 -430
- package/packages/core/dist/docs/cli/context-memory.md +0 -69
- package/packages/core/dist/docs/cli/index.md +0 -65
- package/packages/core/dist/docs/cli/tutorials.md +0 -87
- package/packages/core/dist/docs/cli/uninstall.md +0 -65
- package/packages/core/dist/docs/core/memport.md +0 -246
- package/packages/core/dist/docs/core/tools-api.md +0 -131
- package/packages/core/dist/docs/faq.md +0 -154
- package/packages/core/dist/docs/get-started/configuration-v1.md +0 -888
- package/packages/core/dist/docs/get-started/configuration.md +0 -1585
- package/packages/core/dist/docs/patches/README.md +0 -74
- package/packages/core/dist/docs/patches/mcp-sdk-typings-shim.md +0 -27
- package/packages/core/dist/docs/termux-api/COMMANDS.md +0 -592
- package/packages/core/dist/docs/termux-api/DISCOVERY_SETUP.md +0 -670
- package/packages/core/dist/docs/termux-api/EXECUTION_PLAN.md +0 -532
- package/packages/core/dist/docs/termux-api/MERGE_STRATEGY.md +0 -365
- package/packages/core/dist/docs/termux-api/PATCHES.md +0 -480
- package/packages/core/dist/docs/termux-api/README.md +0 -416
- package/packages/core/dist/docs/tos-privacy.md +0 -96
- package/packages/core/dist/src/safety/conseca/conseca.d.ts +0 -31
- package/packages/core/dist/src/safety/conseca/conseca.js +0 -105
- package/packages/core/dist/src/safety/conseca/conseca.js.map +0 -1
- package/packages/core/dist/src/safety/conseca/conseca.test.d.ts +0 -6
- package/packages/core/dist/src/safety/conseca/conseca.test.js +0 -226
- package/packages/core/dist/src/safety/conseca/conseca.test.js.map +0 -1
- package/packages/core/dist/src/safety/conseca/integration.test.d.ts +0 -6
- package/packages/core/dist/src/safety/conseca/integration.test.js +0 -19
- package/packages/core/dist/src/safety/conseca/integration.test.js.map +0 -1
- package/packages/core/dist/src/safety/conseca/policy-enforcer.d.ts +0 -13
- package/packages/core/dist/src/safety/conseca/policy-enforcer.js +0 -135
- package/packages/core/dist/src/safety/conseca/policy-enforcer.js.map +0 -1
- package/packages/core/dist/src/safety/conseca/policy-enforcer.test.d.ts +0 -6
- package/packages/core/dist/src/safety/conseca/policy-enforcer.test.js +0 -141
- package/packages/core/dist/src/safety/conseca/policy-enforcer.test.js.map +0 -1
- package/packages/core/dist/src/safety/conseca/policy-generator.d.ts +0 -15
- package/packages/core/dist/src/safety/conseca/policy-generator.js +0 -144
- package/packages/core/dist/src/safety/conseca/policy-generator.js.map +0 -1
- package/packages/core/dist/src/safety/conseca/policy-generator.test.d.ts +0 -6
- package/packages/core/dist/src/safety/conseca/policy-generator.test.js +0 -84
- package/packages/core/dist/src/safety/conseca/policy-generator.test.js.map +0 -1
- package/packages/core/dist/src/safety/conseca/types.d.ts +0 -15
- package/packages/core/dist/src/safety/conseca/types.js +0 -7
- package/packages/core/dist/src/safety/conseca/types.js.map +0 -1
- package/packages/core/dist/src/telemetry/conseca-logger.d.ts +0 -9
- package/packages/core/dist/src/telemetry/conseca-logger.js +0 -91
- package/packages/core/dist/src/telemetry/conseca-logger.js.map +0 -1
- package/packages/core/dist/src/telemetry/conseca-logger.test.d.ts +0 -6
- package/packages/core/dist/src/telemetry/conseca-logger.test.js +0 -89
- package/packages/core/dist/src/telemetry/conseca-logger.test.js.map +0 -1
- package/packages/core/dist/src/tools/mcpImportTool.d.ts +0 -31
- package/packages/core/dist/src/tools/mcpImportTool.js +0 -143
- package/packages/core/dist/src/tools/mcpImportTool.js.map +0 -1
- package/packages/core/dist/src/utils/contextMemory.d.ts +0 -67
- package/packages/core/dist/src/utils/contextMemory.js +0 -493
- package/packages/core/dist/src/utils/contextMemory.js.map +0 -1
- package/packages/core/dist/src/utils/contextMemory.test.d.ts +0 -6
- package/packages/core/dist/src/utils/contextMemory.test.js +0 -183
- package/packages/core/dist/src/utils/contextMemory.test.js.map +0 -1
- /package/{bundle/docs/core → packages/core/dist/docs/reference}/memport.md +0 -0
- /package/{bundle/docs/core → packages/core/dist/docs/reference}/tools-api.md +0 -0
- /package/{bundle/docs/cli → packages/core/dist/docs/resources}/uninstall.md +0 -0
|
@@ -4,10 +4,11 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
4
4
|
* Copyright 2025 Google LLC
|
|
5
5
|
* SPDX-License-Identifier: Apache-2.0
|
|
6
6
|
*/
|
|
7
|
-
import { renderWithProviders
|
|
7
|
+
import { renderWithProviders } from '../../test-utils/render.js';
|
|
8
|
+
import { createMockSettings } from '../../test-utils/settings.js';
|
|
8
9
|
import { waitFor } from '../../test-utils/async.js';
|
|
9
10
|
import { act, useState } from 'react';
|
|
10
|
-
import { InputPrompt } from './InputPrompt.js';
|
|
11
|
+
import { InputPrompt, tryTogglePasteExpansion } from './InputPrompt.js';
|
|
11
12
|
import { calculateTransformationsForLine, calculateTransformedLine, } from './shared/text-buffer.js';
|
|
12
13
|
import { ApprovalMode, debugLogger } from '@google/gemini-cli-core';
|
|
13
14
|
import * as path from 'node:path';
|
|
@@ -27,7 +28,9 @@ import chalk from 'chalk';
|
|
|
27
28
|
import { StreamingState } from '../types.js';
|
|
28
29
|
import { terminalCapabilityManager } from '../utils/terminalCapabilityManager.js';
|
|
29
30
|
import { isLowColorDepth } from '../utils/terminalUtils.js';
|
|
31
|
+
import { cpLen } from '../utils/textUtils.js';
|
|
30
32
|
import { keyMatchers, Command } from '../keyMatchers.js';
|
|
33
|
+
import { appEvents, AppEvent, TransientMessageType, } from '../../utils/events.js';
|
|
31
34
|
vi.mock('../hooks/useShellHistory.js');
|
|
32
35
|
vi.mock('../hooks/useCommandCompletion.js');
|
|
33
36
|
vi.mock('../hooks/useInputHistory.js');
|
|
@@ -46,6 +49,9 @@ vi.mock('ink', async (importOriginal) => {
|
|
|
46
49
|
Text: vi.fn(({ children, ...props }) => (_jsx(actual.Text, { ...props, children: children }))),
|
|
47
50
|
};
|
|
48
51
|
});
|
|
52
|
+
afterEach(() => {
|
|
53
|
+
vi.restoreAllMocks();
|
|
54
|
+
});
|
|
49
55
|
const mockSlashCommands = [
|
|
50
56
|
{
|
|
51
57
|
name: 'clear',
|
|
@@ -113,8 +119,14 @@ describe('InputPrompt', () => {
|
|
|
113
119
|
const mockedUseReverseSearchCompletion = vi.mocked(useReverseSearchCompletion);
|
|
114
120
|
const mockedUseKittyKeyboardProtocol = vi.mocked(useKittyKeyboardProtocol);
|
|
115
121
|
const mockSetEmbeddedShellFocused = vi.fn();
|
|
122
|
+
const mockSetCleanUiDetailsVisible = vi.fn();
|
|
123
|
+
const mockToggleCleanUiDetailsVisible = vi.fn();
|
|
124
|
+
const mockRevealCleanUiDetailsTemporarily = vi.fn();
|
|
116
125
|
const uiActions = {
|
|
117
126
|
setEmbeddedShellFocused: mockSetEmbeddedShellFocused,
|
|
127
|
+
setCleanUiDetailsVisible: mockSetCleanUiDetailsVisible,
|
|
128
|
+
toggleCleanUiDetailsVisible: mockToggleCleanUiDetailsVisible,
|
|
129
|
+
revealCleanUiDetailsTemporarily: mockRevealCleanUiDetailsTemporarily,
|
|
118
130
|
};
|
|
119
131
|
beforeEach(() => {
|
|
120
132
|
vi.resetAllMocks();
|
|
@@ -124,13 +136,24 @@ describe('InputPrompt', () => {
|
|
|
124
136
|
text: '',
|
|
125
137
|
cursor: [0, 0],
|
|
126
138
|
lines: [''],
|
|
127
|
-
setText: vi.fn((newText) => {
|
|
139
|
+
setText: vi.fn((newText, cursorPosition) => {
|
|
128
140
|
mockBuffer.text = newText;
|
|
129
141
|
mockBuffer.lines = [newText];
|
|
130
|
-
|
|
142
|
+
let col = 0;
|
|
143
|
+
if (typeof cursorPosition === 'number') {
|
|
144
|
+
col = cursorPosition;
|
|
145
|
+
}
|
|
146
|
+
else if (cursorPosition === 'start') {
|
|
147
|
+
col = 0;
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
col = newText.length;
|
|
151
|
+
}
|
|
152
|
+
mockBuffer.cursor = [0, col];
|
|
131
153
|
mockBuffer.viewportVisualLines = [newText];
|
|
132
154
|
mockBuffer.allVisualLines = [newText];
|
|
133
155
|
mockBuffer.visualToLogicalMap = [[0, 0]];
|
|
156
|
+
mockBuffer.visualCursor = [0, col];
|
|
134
157
|
}),
|
|
135
158
|
replaceRangeByOffset: vi.fn(),
|
|
136
159
|
viewportVisualLines: [''],
|
|
@@ -147,7 +170,15 @@ describe('InputPrompt', () => {
|
|
|
147
170
|
}
|
|
148
171
|
return false;
|
|
149
172
|
}),
|
|
150
|
-
move: vi.fn()
|
|
173
|
+
move: vi.fn((dir) => {
|
|
174
|
+
if (dir === 'home') {
|
|
175
|
+
mockBuffer.visualCursor = [mockBuffer.visualCursor[0], 0];
|
|
176
|
+
}
|
|
177
|
+
else if (dir === 'end') {
|
|
178
|
+
const line = mockBuffer.allVisualLines[mockBuffer.visualCursor[0]] || '';
|
|
179
|
+
mockBuffer.visualCursor = [mockBuffer.visualCursor[0], cpLen(line)];
|
|
180
|
+
}
|
|
181
|
+
}),
|
|
151
182
|
moveToOffset: vi.fn((offset) => {
|
|
152
183
|
mockBuffer.cursor = [0, offset];
|
|
153
184
|
}),
|
|
@@ -191,7 +222,6 @@ describe('InputPrompt', () => {
|
|
|
191
222
|
navigateDown: vi.fn(),
|
|
192
223
|
resetCompletionState: vi.fn(),
|
|
193
224
|
setActiveSuggestionIndex: vi.fn(),
|
|
194
|
-
setShowSuggestions: vi.fn(),
|
|
195
225
|
handleAutocomplete: vi.fn(),
|
|
196
226
|
promptCompletion: {
|
|
197
227
|
text: '',
|
|
@@ -218,7 +248,10 @@ describe('InputPrompt', () => {
|
|
|
218
248
|
navigateDown: vi.fn(),
|
|
219
249
|
handleSubmit: vi.fn(),
|
|
220
250
|
};
|
|
221
|
-
mockedUseInputHistory.
|
|
251
|
+
mockedUseInputHistory.mockImplementation(({ onSubmit }) => {
|
|
252
|
+
mockInputHistory.handleSubmit = vi.fn((val) => onSubmit(val));
|
|
253
|
+
return mockInputHistory;
|
|
254
|
+
});
|
|
222
255
|
mockReverseSearchCompletion = {
|
|
223
256
|
suggestions: [],
|
|
224
257
|
activeSuggestionIndex: -1,
|
|
@@ -322,11 +355,11 @@ describe('InputPrompt', () => {
|
|
|
322
355
|
uiActions,
|
|
323
356
|
});
|
|
324
357
|
await act(async () => {
|
|
325
|
-
stdin.write('\
|
|
358
|
+
stdin.write('\u0010'); // Ctrl+P
|
|
326
359
|
});
|
|
327
360
|
await waitFor(() => expect(mockInputHistory.navigateUp).toHaveBeenCalled());
|
|
328
361
|
await act(async () => {
|
|
329
|
-
stdin.write('\
|
|
362
|
+
stdin.write('\u000E'); // Ctrl+N
|
|
330
363
|
});
|
|
331
364
|
await waitFor(() => expect(mockInputHistory.navigateDown).toHaveBeenCalled());
|
|
332
365
|
await act(async () => {
|
|
@@ -338,6 +371,72 @@ describe('InputPrompt', () => {
|
|
|
338
371
|
expect(mockShellHistory.addCommandToHistory).not.toHaveBeenCalled();
|
|
339
372
|
unmount();
|
|
340
373
|
});
|
|
374
|
+
describe('arrow key navigation', () => {
|
|
375
|
+
it('should move to start of line on Up arrow if on first line but not at start', async () => {
|
|
376
|
+
mockBuffer.allVisualLines = ['line 1', 'line 2'];
|
|
377
|
+
mockBuffer.visualCursor = [0, 5]; // First line, not at start
|
|
378
|
+
mockBuffer.visualScrollRow = 0;
|
|
379
|
+
const { stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }), {
|
|
380
|
+
uiActions,
|
|
381
|
+
});
|
|
382
|
+
await act(async () => {
|
|
383
|
+
stdin.write('\u001B[A'); // Up arrow
|
|
384
|
+
});
|
|
385
|
+
await waitFor(() => {
|
|
386
|
+
expect(mockBuffer.move).toHaveBeenCalledWith('home');
|
|
387
|
+
expect(mockInputHistory.navigateUp).not.toHaveBeenCalled();
|
|
388
|
+
});
|
|
389
|
+
unmount();
|
|
390
|
+
});
|
|
391
|
+
it('should navigate history on Up arrow if on first line and at start', async () => {
|
|
392
|
+
mockBuffer.allVisualLines = ['line 1', 'line 2'];
|
|
393
|
+
mockBuffer.visualCursor = [0, 0]; // First line, at start
|
|
394
|
+
mockBuffer.visualScrollRow = 0;
|
|
395
|
+
const { stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }), {
|
|
396
|
+
uiActions,
|
|
397
|
+
});
|
|
398
|
+
await act(async () => {
|
|
399
|
+
stdin.write('\u001B[A'); // Up arrow
|
|
400
|
+
});
|
|
401
|
+
await waitFor(() => {
|
|
402
|
+
expect(mockBuffer.move).not.toHaveBeenCalledWith('home');
|
|
403
|
+
expect(mockInputHistory.navigateUp).toHaveBeenCalled();
|
|
404
|
+
});
|
|
405
|
+
unmount();
|
|
406
|
+
});
|
|
407
|
+
it('should move to end of line on Down arrow if on last line but not at end', async () => {
|
|
408
|
+
mockBuffer.allVisualLines = ['line 1', 'line 2'];
|
|
409
|
+
mockBuffer.visualCursor = [1, 0]; // Last line, not at end
|
|
410
|
+
mockBuffer.visualScrollRow = 0;
|
|
411
|
+
const { stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }), {
|
|
412
|
+
uiActions,
|
|
413
|
+
});
|
|
414
|
+
await act(async () => {
|
|
415
|
+
stdin.write('\u001B[B'); // Down arrow
|
|
416
|
+
});
|
|
417
|
+
await waitFor(() => {
|
|
418
|
+
expect(mockBuffer.move).toHaveBeenCalledWith('end');
|
|
419
|
+
expect(mockInputHistory.navigateDown).not.toHaveBeenCalled();
|
|
420
|
+
});
|
|
421
|
+
unmount();
|
|
422
|
+
});
|
|
423
|
+
it('should navigate history on Down arrow if on last line and at end', async () => {
|
|
424
|
+
mockBuffer.allVisualLines = ['line 1', 'line 2'];
|
|
425
|
+
mockBuffer.visualCursor = [1, 6]; // Last line, at end ("line 2" is length 6)
|
|
426
|
+
mockBuffer.visualScrollRow = 0;
|
|
427
|
+
const { stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }), {
|
|
428
|
+
uiActions,
|
|
429
|
+
});
|
|
430
|
+
await act(async () => {
|
|
431
|
+
stdin.write('\u001B[B'); // Down arrow
|
|
432
|
+
});
|
|
433
|
+
await waitFor(() => {
|
|
434
|
+
expect(mockBuffer.move).not.toHaveBeenCalledWith('end');
|
|
435
|
+
expect(mockInputHistory.navigateDown).toHaveBeenCalled();
|
|
436
|
+
});
|
|
437
|
+
unmount();
|
|
438
|
+
});
|
|
439
|
+
});
|
|
341
440
|
it('should call completion.navigateUp for both up arrow and Ctrl+P when suggestions are showing', async () => {
|
|
342
441
|
mockedUseCommandCompletion.mockReturnValue({
|
|
343
442
|
...mockCommandCompletion,
|
|
@@ -398,11 +497,11 @@ describe('InputPrompt', () => {
|
|
|
398
497
|
uiActions,
|
|
399
498
|
});
|
|
400
499
|
await act(async () => {
|
|
401
|
-
stdin.write('\
|
|
500
|
+
stdin.write('\u0010'); // Ctrl+P
|
|
402
501
|
});
|
|
403
502
|
await waitFor(() => expect(mockInputHistory.navigateUp).toHaveBeenCalled());
|
|
404
503
|
await act(async () => {
|
|
405
|
-
stdin.write('\
|
|
504
|
+
stdin.write('\u000E'); // Ctrl+N
|
|
406
505
|
});
|
|
407
506
|
await waitFor(() => expect(mockInputHistory.navigateDown).toHaveBeenCalled());
|
|
408
507
|
await act(async () => {
|
|
@@ -741,6 +840,50 @@ describe('InputPrompt', () => {
|
|
|
741
840
|
await waitFor(() => expect(props.onSubmit).toHaveBeenCalledWith('/clear'));
|
|
742
841
|
unmount();
|
|
743
842
|
});
|
|
843
|
+
it('should submit on Enter when an @-path is a perfect match', async () => {
|
|
844
|
+
mockedUseCommandCompletion.mockReturnValue({
|
|
845
|
+
...mockCommandCompletion,
|
|
846
|
+
showSuggestions: true,
|
|
847
|
+
suggestions: [{ label: 'file.txt', value: 'file.txt' }],
|
|
848
|
+
activeSuggestionIndex: 0,
|
|
849
|
+
isPerfectMatch: true,
|
|
850
|
+
completionMode: CompletionMode.AT,
|
|
851
|
+
});
|
|
852
|
+
props.buffer.text = '@file.txt';
|
|
853
|
+
const { stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }), {
|
|
854
|
+
uiActions,
|
|
855
|
+
});
|
|
856
|
+
await act(async () => {
|
|
857
|
+
stdin.write('\r');
|
|
858
|
+
});
|
|
859
|
+
await waitFor(() => {
|
|
860
|
+
// Should submit directly
|
|
861
|
+
expect(props.onSubmit).toHaveBeenCalledWith('@file.txt');
|
|
862
|
+
});
|
|
863
|
+
unmount();
|
|
864
|
+
});
|
|
865
|
+
it('should NOT submit on Shift+Enter even if an @-path is a perfect match', async () => {
|
|
866
|
+
mockedUseCommandCompletion.mockReturnValue({
|
|
867
|
+
...mockCommandCompletion,
|
|
868
|
+
showSuggestions: true,
|
|
869
|
+
suggestions: [{ label: 'file.txt', value: 'file.txt' }],
|
|
870
|
+
activeSuggestionIndex: 0,
|
|
871
|
+
isPerfectMatch: true,
|
|
872
|
+
completionMode: CompletionMode.AT,
|
|
873
|
+
});
|
|
874
|
+
props.buffer.text = '@file.txt';
|
|
875
|
+
const { stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }), {
|
|
876
|
+
uiActions,
|
|
877
|
+
});
|
|
878
|
+
await act(async () => {
|
|
879
|
+
// Simulate Shift+Enter using CSI u sequence
|
|
880
|
+
stdin.write('\x1b[13;2u');
|
|
881
|
+
});
|
|
882
|
+
// Should NOT submit, should call newline instead
|
|
883
|
+
expect(props.onSubmit).not.toHaveBeenCalled();
|
|
884
|
+
expect(props.buffer.newline).toHaveBeenCalled();
|
|
885
|
+
unmount();
|
|
886
|
+
});
|
|
744
887
|
it('should auto-execute commands with autoExecute: true on Enter', async () => {
|
|
745
888
|
const aboutCommand = {
|
|
746
889
|
name: 'about',
|
|
@@ -849,6 +992,29 @@ describe('InputPrompt', () => {
|
|
|
849
992
|
});
|
|
850
993
|
unmount();
|
|
851
994
|
});
|
|
995
|
+
it('should NOT autocomplete on Shift+Tab', async () => {
|
|
996
|
+
const suggestion = { label: 'about', value: 'about' };
|
|
997
|
+
mockedUseCommandCompletion.mockReturnValue({
|
|
998
|
+
...mockCommandCompletion,
|
|
999
|
+
showSuggestions: true,
|
|
1000
|
+
suggestions: [suggestion],
|
|
1001
|
+
activeSuggestionIndex: 0,
|
|
1002
|
+
getCompletedText: vi.fn().mockReturnValue('/about'),
|
|
1003
|
+
});
|
|
1004
|
+
props.buffer.setText('/ab');
|
|
1005
|
+
props.buffer.lines = ['/ab'];
|
|
1006
|
+
props.buffer.cursor = [0, 3];
|
|
1007
|
+
const { stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }), {
|
|
1008
|
+
uiActions,
|
|
1009
|
+
});
|
|
1010
|
+
await act(async () => {
|
|
1011
|
+
stdin.write('\x1b[Z'); // Shift+Tab
|
|
1012
|
+
});
|
|
1013
|
+
// We need to wait a bit to ensure handleAutocomplete was NOT called
|
|
1014
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
1015
|
+
expect(mockCommandCompletion.handleAutocomplete).not.toHaveBeenCalled();
|
|
1016
|
+
unmount();
|
|
1017
|
+
});
|
|
852
1018
|
it('should autocomplete custom commands from .toml files on Enter', async () => {
|
|
853
1019
|
const customCommand = {
|
|
854
1020
|
name: 'find-capital',
|
|
@@ -1124,7 +1290,6 @@ describe('InputPrompt', () => {
|
|
|
1124
1290
|
{ color: 'black', name: 'black' },
|
|
1125
1291
|
{ color: '#000000', name: '#000000' },
|
|
1126
1292
|
{ color: '#000', name: '#000' },
|
|
1127
|
-
{ color: undefined, name: 'default (black)' },
|
|
1128
1293
|
{ color: 'white', name: 'white' },
|
|
1129
1294
|
{ color: '#ffffff', name: '#ffffff' },
|
|
1130
1295
|
{ color: '#fff', name: '#fff' },
|
|
@@ -1172,7 +1337,11 @@ describe('InputPrompt', () => {
|
|
|
1172
1337
|
});
|
|
1173
1338
|
it('should handle 4-bit color mode (16 colors) as low color depth', async () => {
|
|
1174
1339
|
vi.mocked(isLowColorDepth).mockReturnValue(true);
|
|
1175
|
-
const { stdout, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props })
|
|
1340
|
+
const { stdout, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }), {
|
|
1341
|
+
uiState: {
|
|
1342
|
+
terminalBackgroundColor: 'black',
|
|
1343
|
+
},
|
|
1344
|
+
});
|
|
1176
1345
|
await waitFor(() => {
|
|
1177
1346
|
const frame = stdout.lastFrame();
|
|
1178
1347
|
expect(frame).toContain('▀');
|
|
@@ -1308,7 +1477,16 @@ describe('InputPrompt', () => {
|
|
|
1308
1477
|
uiActions,
|
|
1309
1478
|
});
|
|
1310
1479
|
await waitFor(() => {
|
|
1311
|
-
expect(mockedUseCommandCompletion).toHaveBeenCalledWith(
|
|
1480
|
+
expect(mockedUseCommandCompletion).toHaveBeenCalledWith({
|
|
1481
|
+
buffer: mockBuffer,
|
|
1482
|
+
cwd: path.join('test', 'project', 'src'),
|
|
1483
|
+
slashCommands: mockSlashCommands,
|
|
1484
|
+
commandContext: mockCommandContext,
|
|
1485
|
+
reverseSearchActive: false,
|
|
1486
|
+
shellModeActive: false,
|
|
1487
|
+
config: expect.any(Object),
|
|
1488
|
+
active: expect.anything(),
|
|
1489
|
+
});
|
|
1312
1490
|
});
|
|
1313
1491
|
unmount();
|
|
1314
1492
|
});
|
|
@@ -1541,7 +1719,7 @@ describe('InputPrompt', () => {
|
|
|
1541
1719
|
// This implicitly tests that the Box wrapper provides height for the empty line.
|
|
1542
1720
|
expect(frame).toContain('hello');
|
|
1543
1721
|
expect(frame).toContain(`world${chalk.inverse(' ')}`);
|
|
1544
|
-
const outputLines = frame.split('\n');
|
|
1722
|
+
const outputLines = frame.trim().split('\n');
|
|
1545
1723
|
// The number of lines should be 2 for the border plus 3 for the content.
|
|
1546
1724
|
expect(outputLines.length).toBe(5);
|
|
1547
1725
|
});
|
|
@@ -1671,6 +1849,29 @@ describe('InputPrompt', () => {
|
|
|
1671
1849
|
expect(props.buffer.newline).toHaveBeenCalled();
|
|
1672
1850
|
unmount();
|
|
1673
1851
|
});
|
|
1852
|
+
it('should prevent perfect match auto-submission immediately after an unsafe paste', async () => {
|
|
1853
|
+
// isTerminalPasteTrusted will be false due to beforeEach setup.
|
|
1854
|
+
mockedUseCommandCompletion.mockReturnValue({
|
|
1855
|
+
...mockCommandCompletion,
|
|
1856
|
+
isPerfectMatch: true,
|
|
1857
|
+
completionMode: CompletionMode.AT,
|
|
1858
|
+
});
|
|
1859
|
+
props.buffer.text = '@file.txt';
|
|
1860
|
+
const { stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }));
|
|
1861
|
+
// Simulate an unsafe paste of a perfect match
|
|
1862
|
+
await act(async () => {
|
|
1863
|
+
stdin.write(`\x1b[200~@file.txt\x1b[201~`);
|
|
1864
|
+
});
|
|
1865
|
+
// Simulate an Enter key press immediately after paste
|
|
1866
|
+
await act(async () => {
|
|
1867
|
+
stdin.write('\r');
|
|
1868
|
+
});
|
|
1869
|
+
// Verify that onSubmit was NOT called due to recent paste protection
|
|
1870
|
+
expect(props.onSubmit).not.toHaveBeenCalled();
|
|
1871
|
+
// It should call newline() instead
|
|
1872
|
+
expect(props.buffer.newline).toHaveBeenCalled();
|
|
1873
|
+
unmount();
|
|
1874
|
+
});
|
|
1674
1875
|
it('should allow submission after unsafe paste protection timeout', async () => {
|
|
1675
1876
|
// isTerminalPasteTrusted will be false due to beforeEach setup.
|
|
1676
1877
|
props.buffer.text = 'pasted text';
|
|
@@ -1967,6 +2168,28 @@ describe('InputPrompt', () => {
|
|
|
1967
2168
|
});
|
|
1968
2169
|
unmount();
|
|
1969
2170
|
}, 15000);
|
|
2171
|
+
it('should NOT autocomplete on Shift+Tab in reverse search', async () => {
|
|
2172
|
+
const mockHandleAutocomplete = vi.fn();
|
|
2173
|
+
mockedUseReverseSearchCompletion.mockReturnValue({
|
|
2174
|
+
...mockReverseSearchCompletion,
|
|
2175
|
+
suggestions: [{ label: 'echo hello', value: 'echo hello' }],
|
|
2176
|
+
showSuggestions: true,
|
|
2177
|
+
activeSuggestionIndex: 0,
|
|
2178
|
+
handleAutocomplete: mockHandleAutocomplete,
|
|
2179
|
+
});
|
|
2180
|
+
const { stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }), {
|
|
2181
|
+
uiActions,
|
|
2182
|
+
});
|
|
2183
|
+
await act(async () => {
|
|
2184
|
+
stdin.write('\x12'); // Ctrl+R
|
|
2185
|
+
});
|
|
2186
|
+
await act(async () => {
|
|
2187
|
+
stdin.write('\x1b[Z'); // Shift+Tab
|
|
2188
|
+
});
|
|
2189
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
2190
|
+
expect(mockHandleAutocomplete).not.toHaveBeenCalled();
|
|
2191
|
+
unmount();
|
|
2192
|
+
});
|
|
1970
2193
|
it('submits the highlighted entry on Enter and exits reverse-search', async () => {
|
|
1971
2194
|
// Mock the reverse search completion to return suggestions
|
|
1972
2195
|
mockedUseReverseSearchCompletion.mockReturnValue({
|
|
@@ -2174,32 +2397,42 @@ describe('InputPrompt', () => {
|
|
|
2174
2397
|
});
|
|
2175
2398
|
unmount();
|
|
2176
2399
|
});
|
|
2400
|
+
it('ensures Ctrl+R search results are prioritized newest-to-oldest by reversing userMessages', async () => {
|
|
2401
|
+
props.shellModeActive = false;
|
|
2402
|
+
props.userMessages = ['oldest', 'middle', 'newest'];
|
|
2403
|
+
renderWithProviders(_jsx(InputPrompt, { ...props }));
|
|
2404
|
+
const calls = vi.mocked(useReverseSearchCompletion).mock.calls;
|
|
2405
|
+
const commandSearchCall = calls.find((call) => call[1] === props.userMessages ||
|
|
2406
|
+
(Array.isArray(call[1]) && call[1][0] === 'newest'));
|
|
2407
|
+
expect(commandSearchCall).toBeDefined();
|
|
2408
|
+
expect(commandSearchCall[1]).toEqual(['newest', 'middle', 'oldest']);
|
|
2409
|
+
});
|
|
2177
2410
|
});
|
|
2178
|
-
describe('Tab
|
|
2411
|
+
describe('Tab clean UI toggle', () => {
|
|
2179
2412
|
it.each([
|
|
2180
2413
|
{
|
|
2181
|
-
name: 'should toggle
|
|
2414
|
+
name: 'should toggle clean UI details on double-Tab when no suggestions or ghost text',
|
|
2182
2415
|
showSuggestions: false,
|
|
2183
2416
|
ghostText: '',
|
|
2184
2417
|
suggestions: [],
|
|
2185
|
-
|
|
2418
|
+
expectedUiToggle: true,
|
|
2186
2419
|
},
|
|
2187
2420
|
{
|
|
2188
|
-
name: 'should accept ghost text and NOT toggle
|
|
2421
|
+
name: 'should accept ghost text and NOT toggle clean UI details on Tab',
|
|
2189
2422
|
showSuggestions: false,
|
|
2190
2423
|
ghostText: 'ghost text',
|
|
2191
2424
|
suggestions: [],
|
|
2192
|
-
|
|
2425
|
+
expectedUiToggle: false,
|
|
2193
2426
|
expectedAcceptCall: true,
|
|
2194
2427
|
},
|
|
2195
2428
|
{
|
|
2196
|
-
name: 'should NOT toggle
|
|
2429
|
+
name: 'should NOT toggle clean UI details on Tab when suggestions are present',
|
|
2197
2430
|
showSuggestions: true,
|
|
2198
2431
|
ghostText: '',
|
|
2199
2432
|
suggestions: [{ label: 'test', value: 'test' }],
|
|
2200
|
-
|
|
2433
|
+
expectedUiToggle: false,
|
|
2201
2434
|
},
|
|
2202
|
-
])('$name', async ({ showSuggestions, ghostText, suggestions,
|
|
2435
|
+
])('$name', async ({ showSuggestions, ghostText, suggestions, expectedUiToggle, expectedAcceptCall, }) => {
|
|
2203
2436
|
const mockAccept = vi.fn();
|
|
2204
2437
|
mockedUseCommandCompletion.mockReturnValue({
|
|
2205
2438
|
...mockCommandCompletion,
|
|
@@ -2216,17 +2449,20 @@ describe('InputPrompt', () => {
|
|
|
2216
2449
|
});
|
|
2217
2450
|
const { stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }), {
|
|
2218
2451
|
uiActions,
|
|
2219
|
-
uiState: {
|
|
2452
|
+
uiState: {},
|
|
2220
2453
|
});
|
|
2221
2454
|
await act(async () => {
|
|
2222
2455
|
stdin.write('\t');
|
|
2456
|
+
if (expectedUiToggle) {
|
|
2457
|
+
stdin.write('\t');
|
|
2458
|
+
}
|
|
2223
2459
|
});
|
|
2224
2460
|
await waitFor(() => {
|
|
2225
|
-
if (
|
|
2226
|
-
expect(uiActions.
|
|
2461
|
+
if (expectedUiToggle) {
|
|
2462
|
+
expect(uiActions.toggleCleanUiDetailsVisible).toHaveBeenCalled();
|
|
2227
2463
|
}
|
|
2228
2464
|
else {
|
|
2229
|
-
expect(uiActions.
|
|
2465
|
+
expect(uiActions.toggleCleanUiDetailsVisible).not.toHaveBeenCalled();
|
|
2230
2466
|
}
|
|
2231
2467
|
if (expectedAcceptCall) {
|
|
2232
2468
|
expect(mockAccept).toHaveBeenCalled();
|
|
@@ -2234,6 +2470,84 @@ describe('InputPrompt', () => {
|
|
|
2234
2470
|
});
|
|
2235
2471
|
unmount();
|
|
2236
2472
|
});
|
|
2473
|
+
it('should NOT accept ghost text on Shift+Tab', async () => {
|
|
2474
|
+
const mockAccept = vi.fn();
|
|
2475
|
+
mockedUseCommandCompletion.mockReturnValue({
|
|
2476
|
+
...mockCommandCompletion,
|
|
2477
|
+
showSuggestions: false,
|
|
2478
|
+
suggestions: [],
|
|
2479
|
+
promptCompletion: {
|
|
2480
|
+
text: 'ghost text',
|
|
2481
|
+
accept: mockAccept,
|
|
2482
|
+
clear: vi.fn(),
|
|
2483
|
+
isLoading: false,
|
|
2484
|
+
isActive: true,
|
|
2485
|
+
markSelected: vi.fn(),
|
|
2486
|
+
},
|
|
2487
|
+
});
|
|
2488
|
+
const { stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }), {
|
|
2489
|
+
uiActions,
|
|
2490
|
+
});
|
|
2491
|
+
await act(async () => {
|
|
2492
|
+
stdin.write('\x1b[Z'); // Shift+Tab
|
|
2493
|
+
});
|
|
2494
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
2495
|
+
expect(mockAccept).not.toHaveBeenCalled();
|
|
2496
|
+
unmount();
|
|
2497
|
+
});
|
|
2498
|
+
it('should not reveal clean UI details on Shift+Tab when hidden', async () => {
|
|
2499
|
+
mockedUseCommandCompletion.mockReturnValue({
|
|
2500
|
+
...mockCommandCompletion,
|
|
2501
|
+
showSuggestions: false,
|
|
2502
|
+
suggestions: [],
|
|
2503
|
+
promptCompletion: {
|
|
2504
|
+
text: '',
|
|
2505
|
+
accept: vi.fn(),
|
|
2506
|
+
clear: vi.fn(),
|
|
2507
|
+
isLoading: false,
|
|
2508
|
+
isActive: false,
|
|
2509
|
+
markSelected: vi.fn(),
|
|
2510
|
+
},
|
|
2511
|
+
});
|
|
2512
|
+
const { stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }), {
|
|
2513
|
+
uiActions,
|
|
2514
|
+
uiState: { activePtyId: 1, cleanUiDetailsVisible: false },
|
|
2515
|
+
});
|
|
2516
|
+
await act(async () => {
|
|
2517
|
+
stdin.write('\x1b[Z');
|
|
2518
|
+
});
|
|
2519
|
+
await waitFor(() => {
|
|
2520
|
+
expect(uiActions.revealCleanUiDetailsTemporarily).not.toHaveBeenCalled();
|
|
2521
|
+
});
|
|
2522
|
+
unmount();
|
|
2523
|
+
});
|
|
2524
|
+
it('should toggle clean UI details on double-Tab by default', async () => {
|
|
2525
|
+
mockedUseCommandCompletion.mockReturnValue({
|
|
2526
|
+
...mockCommandCompletion,
|
|
2527
|
+
showSuggestions: false,
|
|
2528
|
+
suggestions: [],
|
|
2529
|
+
promptCompletion: {
|
|
2530
|
+
text: '',
|
|
2531
|
+
accept: vi.fn(),
|
|
2532
|
+
clear: vi.fn(),
|
|
2533
|
+
isLoading: false,
|
|
2534
|
+
isActive: false,
|
|
2535
|
+
markSelected: vi.fn(),
|
|
2536
|
+
},
|
|
2537
|
+
});
|
|
2538
|
+
const { stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }), {
|
|
2539
|
+
uiActions,
|
|
2540
|
+
uiState: {},
|
|
2541
|
+
});
|
|
2542
|
+
await act(async () => {
|
|
2543
|
+
stdin.write('\t');
|
|
2544
|
+
stdin.write('\t');
|
|
2545
|
+
});
|
|
2546
|
+
await waitFor(() => {
|
|
2547
|
+
expect(uiActions.toggleCleanUiDetailsVisible).toHaveBeenCalled();
|
|
2548
|
+
});
|
|
2549
|
+
unmount();
|
|
2550
|
+
});
|
|
2237
2551
|
});
|
|
2238
2552
|
describe('mouse interaction', () => {
|
|
2239
2553
|
it.each([
|
|
@@ -2354,7 +2668,7 @@ describe('InputPrompt', () => {
|
|
|
2354
2668
|
};
|
|
2355
2669
|
return _jsx(InputPrompt, { ...baseProps, buffer: buffer });
|
|
2356
2670
|
};
|
|
2357
|
-
const {
|
|
2671
|
+
const { stdout, unmount, simulateClick } = renderWithProviders(_jsx(TestWrapper, {}), {
|
|
2358
2672
|
mouseEventsEnabled: true,
|
|
2359
2673
|
useAlternateBuffer: true,
|
|
2360
2674
|
uiActions,
|
|
@@ -2364,15 +2678,15 @@ describe('InputPrompt', () => {
|
|
|
2364
2678
|
expect(stdout.lastFrame()).toMatchSnapshot();
|
|
2365
2679
|
});
|
|
2366
2680
|
// Simulate double-click to expand
|
|
2367
|
-
await simulateClick(
|
|
2368
|
-
await simulateClick(
|
|
2681
|
+
await simulateClick(5, 2);
|
|
2682
|
+
await simulateClick(5, 2);
|
|
2369
2683
|
// 2. Verify expanded content is visible
|
|
2370
2684
|
await waitFor(() => {
|
|
2371
2685
|
expect(stdout.lastFrame()).toMatchSnapshot();
|
|
2372
2686
|
});
|
|
2373
2687
|
// Simulate double-click to collapse
|
|
2374
|
-
await simulateClick(
|
|
2375
|
-
await simulateClick(
|
|
2688
|
+
await simulateClick(5, 2);
|
|
2689
|
+
await simulateClick(5, 2);
|
|
2376
2690
|
// 3. Verify placeholder is restored
|
|
2377
2691
|
await waitFor(() => {
|
|
2378
2692
|
expect(stdout.lastFrame()).toMatchSnapshot();
|
|
@@ -2424,7 +2738,7 @@ describe('InputPrompt', () => {
|
|
|
2424
2738
|
};
|
|
2425
2739
|
return _jsx(InputPrompt, { ...baseProps, buffer: buffer });
|
|
2426
2740
|
};
|
|
2427
|
-
const {
|
|
2741
|
+
const { stdout, unmount, simulateClick } = renderWithProviders(_jsx(TestWrapper, {}), {
|
|
2428
2742
|
mouseEventsEnabled: true,
|
|
2429
2743
|
useAlternateBuffer: true,
|
|
2430
2744
|
uiActions,
|
|
@@ -2434,8 +2748,8 @@ describe('InputPrompt', () => {
|
|
|
2434
2748
|
expect(stdout.lastFrame()).toContain('line1');
|
|
2435
2749
|
});
|
|
2436
2750
|
// Simulate double-click WAY to the right on the first line
|
|
2437
|
-
await simulateClick(
|
|
2438
|
-
await simulateClick(
|
|
2751
|
+
await simulateClick(90, 2);
|
|
2752
|
+
await simulateClick(90, 2);
|
|
2439
2753
|
// Verify it is NOW collapsed
|
|
2440
2754
|
await waitFor(() => {
|
|
2441
2755
|
expect(stdout.lastFrame()).toContain(id);
|
|
@@ -2833,6 +3147,445 @@ describe('InputPrompt', () => {
|
|
|
2833
3147
|
unmount();
|
|
2834
3148
|
});
|
|
2835
3149
|
});
|
|
3150
|
+
describe('Ctrl+O paste expansion', () => {
|
|
3151
|
+
const CTRL_O = '\x0f'; // Ctrl+O key sequence
|
|
3152
|
+
it('Ctrl+O triggers paste expansion via keybinding', async () => {
|
|
3153
|
+
const id = '[Pasted Text: 10 lines]';
|
|
3154
|
+
const toggleFn = vi.fn();
|
|
3155
|
+
const buffer = {
|
|
3156
|
+
...props.buffer,
|
|
3157
|
+
text: id,
|
|
3158
|
+
cursor: [0, 0],
|
|
3159
|
+
pastedContent: {
|
|
3160
|
+
[id]: 'line1\nline2\nline3\nline4\nline5\nline6\nline7\nline8\nline9\nline10',
|
|
3161
|
+
},
|
|
3162
|
+
transformationsByLine: [
|
|
3163
|
+
[
|
|
3164
|
+
{
|
|
3165
|
+
logStart: 0,
|
|
3166
|
+
logEnd: id.length,
|
|
3167
|
+
logicalText: id,
|
|
3168
|
+
collapsedText: id,
|
|
3169
|
+
type: 'paste',
|
|
3170
|
+
id,
|
|
3171
|
+
},
|
|
3172
|
+
],
|
|
3173
|
+
],
|
|
3174
|
+
expandedPaste: null,
|
|
3175
|
+
getExpandedPasteAtLine: vi.fn().mockReturnValue(null),
|
|
3176
|
+
togglePasteExpansion: toggleFn,
|
|
3177
|
+
};
|
|
3178
|
+
const { stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props, buffer: buffer }), { uiActions });
|
|
3179
|
+
await act(async () => {
|
|
3180
|
+
stdin.write(CTRL_O);
|
|
3181
|
+
});
|
|
3182
|
+
await waitFor(() => {
|
|
3183
|
+
expect(toggleFn).toHaveBeenCalledWith(id, 0, 0);
|
|
3184
|
+
});
|
|
3185
|
+
unmount();
|
|
3186
|
+
});
|
|
3187
|
+
it.each([
|
|
3188
|
+
{
|
|
3189
|
+
name: 'hint appears on large paste via Ctrl+V',
|
|
3190
|
+
text: 'line1\nline2\nline3\nline4\nline5\nline6',
|
|
3191
|
+
method: 'ctrl-v',
|
|
3192
|
+
expectHint: true,
|
|
3193
|
+
},
|
|
3194
|
+
{
|
|
3195
|
+
name: 'hint does not appear for small pastes via Ctrl+V',
|
|
3196
|
+
text: 'hello',
|
|
3197
|
+
method: 'ctrl-v',
|
|
3198
|
+
expectHint: false,
|
|
3199
|
+
},
|
|
3200
|
+
{
|
|
3201
|
+
name: 'hint appears on large terminal paste event',
|
|
3202
|
+
text: 'line1\nline2\nline3\nline4\nline5\nline6',
|
|
3203
|
+
method: 'terminal-paste',
|
|
3204
|
+
expectHint: true,
|
|
3205
|
+
},
|
|
3206
|
+
])('$name', async ({ text, method, expectHint }) => {
|
|
3207
|
+
vi.mocked(clipboardy.read).mockResolvedValue(text);
|
|
3208
|
+
vi.mocked(clipboardUtils.clipboardHasImage).mockResolvedValue(false);
|
|
3209
|
+
const emitSpy = vi.spyOn(appEvents, 'emit');
|
|
3210
|
+
const buffer = {
|
|
3211
|
+
...props.buffer,
|
|
3212
|
+
handleInput: vi.fn().mockReturnValue(true),
|
|
3213
|
+
};
|
|
3214
|
+
// Need kitty protocol enabled for terminal paste events
|
|
3215
|
+
if (method === 'terminal-paste') {
|
|
3216
|
+
mockedUseKittyKeyboardProtocol.mockReturnValue({
|
|
3217
|
+
enabled: true,
|
|
3218
|
+
checking: false,
|
|
3219
|
+
});
|
|
3220
|
+
}
|
|
3221
|
+
const { stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props, buffer: method === 'terminal-paste' ? buffer : props.buffer }));
|
|
3222
|
+
await act(async () => {
|
|
3223
|
+
if (method === 'ctrl-v') {
|
|
3224
|
+
stdin.write('\x16'); // Ctrl+V
|
|
3225
|
+
}
|
|
3226
|
+
else {
|
|
3227
|
+
stdin.write(`\x1b[200~${text}\x1b[201~`);
|
|
3228
|
+
}
|
|
3229
|
+
});
|
|
3230
|
+
await waitFor(() => {
|
|
3231
|
+
if (expectHint) {
|
|
3232
|
+
expect(emitSpy).toHaveBeenCalledWith(AppEvent.TransientMessage, {
|
|
3233
|
+
message: 'Press Ctrl+O to expand pasted text',
|
|
3234
|
+
type: TransientMessageType.Hint,
|
|
3235
|
+
});
|
|
3236
|
+
}
|
|
3237
|
+
else {
|
|
3238
|
+
// If no hint expected, verify buffer was still updated
|
|
3239
|
+
if (method === 'ctrl-v') {
|
|
3240
|
+
expect(mockBuffer.insert).toHaveBeenCalledWith(text, {
|
|
3241
|
+
paste: true,
|
|
3242
|
+
});
|
|
3243
|
+
}
|
|
3244
|
+
else {
|
|
3245
|
+
expect(buffer.handleInput).toHaveBeenCalled();
|
|
3246
|
+
}
|
|
3247
|
+
}
|
|
3248
|
+
});
|
|
3249
|
+
if (!expectHint) {
|
|
3250
|
+
expect(emitSpy).not.toHaveBeenCalledWith(AppEvent.TransientMessage, expect.any(Object));
|
|
3251
|
+
}
|
|
3252
|
+
emitSpy.mockRestore();
|
|
3253
|
+
unmount();
|
|
3254
|
+
});
|
|
3255
|
+
});
|
|
3256
|
+
describe('tryTogglePasteExpansion', () => {
|
|
3257
|
+
it.each([
|
|
3258
|
+
{
|
|
3259
|
+
name: 'returns false when no pasted content exists',
|
|
3260
|
+
cursor: [0, 0],
|
|
3261
|
+
pastedContent: {},
|
|
3262
|
+
getExpandedPasteAtLine: null,
|
|
3263
|
+
expected: false,
|
|
3264
|
+
},
|
|
3265
|
+
{
|
|
3266
|
+
name: 'expands placeholder under cursor',
|
|
3267
|
+
cursor: [0, 2],
|
|
3268
|
+
pastedContent: { '[Pasted Text: 6 lines]': 'content' },
|
|
3269
|
+
transformations: [
|
|
3270
|
+
{
|
|
3271
|
+
logStart: 0,
|
|
3272
|
+
logEnd: '[Pasted Text: 6 lines]'.length,
|
|
3273
|
+
id: '[Pasted Text: 6 lines]',
|
|
3274
|
+
},
|
|
3275
|
+
],
|
|
3276
|
+
expected: true,
|
|
3277
|
+
expectedToggle: ['[Pasted Text: 6 lines]', 0, 2],
|
|
3278
|
+
},
|
|
3279
|
+
{
|
|
3280
|
+
name: 'collapses expanded paste when cursor is inside',
|
|
3281
|
+
cursor: [1, 0],
|
|
3282
|
+
pastedContent: { '[Pasted Text: 6 lines]': 'a\nb\nc' },
|
|
3283
|
+
getExpandedPasteAtLine: '[Pasted Text: 6 lines]',
|
|
3284
|
+
expected: true,
|
|
3285
|
+
expectedToggle: ['[Pasted Text: 6 lines]', 1, 0],
|
|
3286
|
+
},
|
|
3287
|
+
{
|
|
3288
|
+
name: 'expands placeholder when cursor is immediately after it',
|
|
3289
|
+
cursor: [0, '[Pasted Text: 6 lines]'.length],
|
|
3290
|
+
pastedContent: { '[Pasted Text: 6 lines]': 'content' },
|
|
3291
|
+
transformations: [
|
|
3292
|
+
{
|
|
3293
|
+
logStart: 0,
|
|
3294
|
+
logEnd: '[Pasted Text: 6 lines]'.length,
|
|
3295
|
+
id: '[Pasted Text: 6 lines]',
|
|
3296
|
+
},
|
|
3297
|
+
],
|
|
3298
|
+
expected: true,
|
|
3299
|
+
expectedToggle: [
|
|
3300
|
+
'[Pasted Text: 6 lines]',
|
|
3301
|
+
0,
|
|
3302
|
+
'[Pasted Text: 6 lines]'.length,
|
|
3303
|
+
],
|
|
3304
|
+
},
|
|
3305
|
+
{
|
|
3306
|
+
name: 'shows hint when cursor is not on placeholder but placeholders exist',
|
|
3307
|
+
cursor: [0, 0],
|
|
3308
|
+
pastedContent: { '[Pasted Text: 6 lines]': 'content' },
|
|
3309
|
+
transformationsByLine: [
|
|
3310
|
+
[],
|
|
3311
|
+
[
|
|
3312
|
+
{
|
|
3313
|
+
logStart: 0,
|
|
3314
|
+
logEnd: '[Pasted Text: 6 lines]'.length,
|
|
3315
|
+
type: 'paste',
|
|
3316
|
+
id: '[Pasted Text: 6 lines]',
|
|
3317
|
+
},
|
|
3318
|
+
],
|
|
3319
|
+
],
|
|
3320
|
+
expected: true,
|
|
3321
|
+
expectedHint: 'Move cursor within placeholder to expand',
|
|
3322
|
+
},
|
|
3323
|
+
])('$name', ({ cursor, pastedContent, transformations, transformationsByLine, getExpandedPasteAtLine, expected, expectedToggle, expectedHint, }) => {
|
|
3324
|
+
const id = '[Pasted Text: 6 lines]';
|
|
3325
|
+
const buffer = {
|
|
3326
|
+
cursor,
|
|
3327
|
+
pastedContent,
|
|
3328
|
+
transformationsByLine: transformationsByLine || [
|
|
3329
|
+
transformations
|
|
3330
|
+
? transformations.map((t) => ({
|
|
3331
|
+
...t,
|
|
3332
|
+
logicalText: id,
|
|
3333
|
+
collapsedText: id,
|
|
3334
|
+
type: 'paste',
|
|
3335
|
+
}))
|
|
3336
|
+
: [],
|
|
3337
|
+
],
|
|
3338
|
+
getExpandedPasteAtLine: vi
|
|
3339
|
+
.fn()
|
|
3340
|
+
.mockReturnValue(getExpandedPasteAtLine),
|
|
3341
|
+
togglePasteExpansion: vi.fn(),
|
|
3342
|
+
};
|
|
3343
|
+
const emitSpy = vi.spyOn(appEvents, 'emit');
|
|
3344
|
+
expect(tryTogglePasteExpansion(buffer)).toBe(expected);
|
|
3345
|
+
if (expectedToggle) {
|
|
3346
|
+
expect(buffer.togglePasteExpansion).toHaveBeenCalledWith(...expectedToggle);
|
|
3347
|
+
}
|
|
3348
|
+
else {
|
|
3349
|
+
expect(buffer.togglePasteExpansion).not.toHaveBeenCalled();
|
|
3350
|
+
}
|
|
3351
|
+
if (expectedHint) {
|
|
3352
|
+
expect(emitSpy).toHaveBeenCalledWith(AppEvent.TransientMessage, {
|
|
3353
|
+
message: expectedHint,
|
|
3354
|
+
type: TransientMessageType.Hint,
|
|
3355
|
+
});
|
|
3356
|
+
}
|
|
3357
|
+
else {
|
|
3358
|
+
expect(emitSpy).not.toHaveBeenCalledWith(AppEvent.TransientMessage, expect.any(Object));
|
|
3359
|
+
}
|
|
3360
|
+
emitSpy.mockRestore();
|
|
3361
|
+
});
|
|
3362
|
+
});
|
|
3363
|
+
describe('History Navigation and Completion Suppression', () => {
|
|
3364
|
+
beforeEach(() => {
|
|
3365
|
+
props.userMessages = ['first message', 'second message'];
|
|
3366
|
+
// Mock useInputHistory to actually call onChange
|
|
3367
|
+
mockedUseInputHistory.mockImplementation(({ onChange, onSubmit }) => ({
|
|
3368
|
+
navigateUp: () => {
|
|
3369
|
+
onChange('second message', 'start');
|
|
3370
|
+
return true;
|
|
3371
|
+
},
|
|
3372
|
+
navigateDown: () => {
|
|
3373
|
+
onChange('first message', 'end');
|
|
3374
|
+
return true;
|
|
3375
|
+
},
|
|
3376
|
+
handleSubmit: vi.fn((val) => onSubmit(val)),
|
|
3377
|
+
}));
|
|
3378
|
+
});
|
|
3379
|
+
it.each([
|
|
3380
|
+
{ name: 'Up arrow', key: '\u001B[A', position: 'start' },
|
|
3381
|
+
{ name: 'Ctrl+P', key: '\u0010', position: 'start' },
|
|
3382
|
+
])('should move cursor to $position on $name (older history)', async ({ key, position }) => {
|
|
3383
|
+
const { stdin } = renderWithProviders(_jsx(InputPrompt, { ...props }), {
|
|
3384
|
+
uiActions,
|
|
3385
|
+
});
|
|
3386
|
+
await act(async () => {
|
|
3387
|
+
stdin.write(key);
|
|
3388
|
+
});
|
|
3389
|
+
await waitFor(() => {
|
|
3390
|
+
expect(mockBuffer.setText).toHaveBeenCalledWith('second message', position);
|
|
3391
|
+
});
|
|
3392
|
+
});
|
|
3393
|
+
it.each([
|
|
3394
|
+
{ name: 'Down arrow', key: '\u001B[B', position: 'end' },
|
|
3395
|
+
{ name: 'Ctrl+N', key: '\u000E', position: 'end' },
|
|
3396
|
+
])('should move cursor to $position on $name (newer history)', async ({ key, position }) => {
|
|
3397
|
+
const { stdin } = renderWithProviders(_jsx(InputPrompt, { ...props }), {
|
|
3398
|
+
uiActions,
|
|
3399
|
+
});
|
|
3400
|
+
// First go up
|
|
3401
|
+
await act(async () => {
|
|
3402
|
+
stdin.write('\u001B[A');
|
|
3403
|
+
});
|
|
3404
|
+
// Then go down
|
|
3405
|
+
await act(async () => {
|
|
3406
|
+
stdin.write(key);
|
|
3407
|
+
if (key === '\u001B[B') {
|
|
3408
|
+
// Second press to actually navigate history
|
|
3409
|
+
stdin.write(key);
|
|
3410
|
+
}
|
|
3411
|
+
});
|
|
3412
|
+
await waitFor(() => {
|
|
3413
|
+
expect(mockBuffer.setText).toHaveBeenCalledWith('first message', position);
|
|
3414
|
+
});
|
|
3415
|
+
});
|
|
3416
|
+
it('should suppress completion after history navigation', async () => {
|
|
3417
|
+
const { stdin } = renderWithProviders(_jsx(InputPrompt, { ...props }), {
|
|
3418
|
+
uiActions,
|
|
3419
|
+
});
|
|
3420
|
+
await act(async () => {
|
|
3421
|
+
stdin.write('\u001B[A'); // Up arrow
|
|
3422
|
+
});
|
|
3423
|
+
await waitFor(() => {
|
|
3424
|
+
expect(mockedUseCommandCompletion).toHaveBeenLastCalledWith({
|
|
3425
|
+
buffer: mockBuffer,
|
|
3426
|
+
cwd: expect.anything(),
|
|
3427
|
+
slashCommands: expect.anything(),
|
|
3428
|
+
commandContext: expect.anything(),
|
|
3429
|
+
reverseSearchActive: expect.anything(),
|
|
3430
|
+
shellModeActive: expect.anything(),
|
|
3431
|
+
config: expect.anything(),
|
|
3432
|
+
active: false,
|
|
3433
|
+
});
|
|
3434
|
+
});
|
|
3435
|
+
});
|
|
3436
|
+
it('should not render suggestions during history navigation', async () => {
|
|
3437
|
+
// 1. Set up a dynamic mock implementation BEFORE rendering
|
|
3438
|
+
mockedUseCommandCompletion.mockImplementation(({ active }) => ({
|
|
3439
|
+
...mockCommandCompletion,
|
|
3440
|
+
showSuggestions: active,
|
|
3441
|
+
suggestions: active
|
|
3442
|
+
? [{ value: 'suggestion', label: 'suggestion' }]
|
|
3443
|
+
: [],
|
|
3444
|
+
}));
|
|
3445
|
+
const { stdout, stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }), { uiActions });
|
|
3446
|
+
// 2. Verify suggestions ARE showing initially because active is true by default
|
|
3447
|
+
await waitFor(() => {
|
|
3448
|
+
expect(stdout.lastFrame()).toContain('suggestion');
|
|
3449
|
+
});
|
|
3450
|
+
// 3. Trigger history navigation which should set suppressCompletion to true
|
|
3451
|
+
await act(async () => {
|
|
3452
|
+
stdin.write('\u001B[A');
|
|
3453
|
+
});
|
|
3454
|
+
// 4. Verify that suggestions are NOT in the output frame after navigation
|
|
3455
|
+
await waitFor(() => {
|
|
3456
|
+
expect(stdout.lastFrame()).not.toContain('suggestion');
|
|
3457
|
+
});
|
|
3458
|
+
expect(stdout.lastFrame()).toMatchSnapshot();
|
|
3459
|
+
unmount();
|
|
3460
|
+
});
|
|
3461
|
+
it('should continue to suppress completion after manual cursor movement', async () => {
|
|
3462
|
+
const { stdin } = renderWithProviders(_jsx(InputPrompt, { ...props }), {
|
|
3463
|
+
uiActions,
|
|
3464
|
+
});
|
|
3465
|
+
// Navigate history (suppresses)
|
|
3466
|
+
await act(async () => {
|
|
3467
|
+
stdin.write('\u001B[A');
|
|
3468
|
+
});
|
|
3469
|
+
// Wait for it to be suppressed
|
|
3470
|
+
await waitFor(() => {
|
|
3471
|
+
expect(mockedUseCommandCompletion).toHaveBeenLastCalledWith({
|
|
3472
|
+
buffer: mockBuffer,
|
|
3473
|
+
cwd: expect.anything(),
|
|
3474
|
+
slashCommands: expect.anything(),
|
|
3475
|
+
commandContext: expect.anything(),
|
|
3476
|
+
reverseSearchActive: expect.anything(),
|
|
3477
|
+
shellModeActive: expect.anything(),
|
|
3478
|
+
config: expect.anything(),
|
|
3479
|
+
active: false,
|
|
3480
|
+
});
|
|
3481
|
+
});
|
|
3482
|
+
// Move cursor manually
|
|
3483
|
+
await act(async () => {
|
|
3484
|
+
stdin.write('\u001B[D'); // Left arrow
|
|
3485
|
+
});
|
|
3486
|
+
await waitFor(() => {
|
|
3487
|
+
expect(mockedUseCommandCompletion).toHaveBeenLastCalledWith({
|
|
3488
|
+
buffer: mockBuffer,
|
|
3489
|
+
cwd: expect.anything(),
|
|
3490
|
+
slashCommands: expect.anything(),
|
|
3491
|
+
commandContext: expect.anything(),
|
|
3492
|
+
reverseSearchActive: expect.anything(),
|
|
3493
|
+
shellModeActive: expect.anything(),
|
|
3494
|
+
config: expect.anything(),
|
|
3495
|
+
active: false,
|
|
3496
|
+
});
|
|
3497
|
+
});
|
|
3498
|
+
});
|
|
3499
|
+
it('should re-enable completion after typing', async () => {
|
|
3500
|
+
const { stdin } = renderWithProviders(_jsx(InputPrompt, { ...props }), {
|
|
3501
|
+
uiActions,
|
|
3502
|
+
});
|
|
3503
|
+
// Navigate history (suppresses)
|
|
3504
|
+
await act(async () => {
|
|
3505
|
+
stdin.write('\u001B[A');
|
|
3506
|
+
});
|
|
3507
|
+
// Wait for it to be suppressed
|
|
3508
|
+
await waitFor(() => {
|
|
3509
|
+
expect(mockedUseCommandCompletion).toHaveBeenLastCalledWith(expect.objectContaining({ active: false }));
|
|
3510
|
+
});
|
|
3511
|
+
// Type a character
|
|
3512
|
+
await act(async () => {
|
|
3513
|
+
stdin.write('a');
|
|
3514
|
+
});
|
|
3515
|
+
await waitFor(() => {
|
|
3516
|
+
expect(mockedUseCommandCompletion).toHaveBeenLastCalledWith(expect.objectContaining({ active: true }));
|
|
3517
|
+
});
|
|
3518
|
+
});
|
|
3519
|
+
});
|
|
3520
|
+
describe('shortcuts help visibility', () => {
|
|
3521
|
+
it('opens shortcuts help with ? on empty prompt even when showShortcutsHint is false', async () => {
|
|
3522
|
+
const setShortcutsHelpVisible = vi.fn();
|
|
3523
|
+
const settings = createMockSettings({
|
|
3524
|
+
ui: { showShortcutsHint: false },
|
|
3525
|
+
});
|
|
3526
|
+
const { stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }), {
|
|
3527
|
+
settings,
|
|
3528
|
+
uiActions: { setShortcutsHelpVisible },
|
|
3529
|
+
});
|
|
3530
|
+
await act(async () => {
|
|
3531
|
+
stdin.write('?');
|
|
3532
|
+
});
|
|
3533
|
+
await waitFor(() => {
|
|
3534
|
+
expect(setShortcutsHelpVisible).toHaveBeenCalledWith(true);
|
|
3535
|
+
});
|
|
3536
|
+
unmount();
|
|
3537
|
+
});
|
|
3538
|
+
it.each([
|
|
3539
|
+
{
|
|
3540
|
+
name: 'terminal paste event occurs',
|
|
3541
|
+
input: '\x1b[200~pasted text\x1b[201~',
|
|
3542
|
+
},
|
|
3543
|
+
{
|
|
3544
|
+
name: 'Ctrl+V (PASTE_CLIPBOARD) is pressed',
|
|
3545
|
+
input: '\x16',
|
|
3546
|
+
setupMocks: () => {
|
|
3547
|
+
vi.mocked(clipboardUtils.clipboardHasImage).mockResolvedValue(false);
|
|
3548
|
+
vi.mocked(clipboardy.read).mockResolvedValue('clipboard text');
|
|
3549
|
+
},
|
|
3550
|
+
},
|
|
3551
|
+
{
|
|
3552
|
+
name: 'mouse right-click paste occurs',
|
|
3553
|
+
input: '\x1b[<2;1;1m',
|
|
3554
|
+
mouseEventsEnabled: true,
|
|
3555
|
+
setupMocks: () => {
|
|
3556
|
+
vi.mocked(clipboardUtils.clipboardHasImage).mockResolvedValue(false);
|
|
3557
|
+
vi.mocked(clipboardy.read).mockResolvedValue('clipboard text');
|
|
3558
|
+
},
|
|
3559
|
+
},
|
|
3560
|
+
{
|
|
3561
|
+
name: 'Ctrl+R hotkey is pressed',
|
|
3562
|
+
input: '\x12',
|
|
3563
|
+
},
|
|
3564
|
+
{
|
|
3565
|
+
name: 'Ctrl+X hotkey is pressed',
|
|
3566
|
+
input: '\x18',
|
|
3567
|
+
},
|
|
3568
|
+
{
|
|
3569
|
+
name: 'F12 hotkey is pressed',
|
|
3570
|
+
input: '\x1b[24~',
|
|
3571
|
+
},
|
|
3572
|
+
])('should close shortcuts help when a $name', async ({ input, setupMocks, mouseEventsEnabled }) => {
|
|
3573
|
+
setupMocks?.();
|
|
3574
|
+
const setShortcutsHelpVisible = vi.fn();
|
|
3575
|
+
const { stdin, unmount } = renderWithProviders(_jsx(InputPrompt, { ...props }), {
|
|
3576
|
+
uiState: { shortcutsHelpVisible: true },
|
|
3577
|
+
uiActions: { setShortcutsHelpVisible },
|
|
3578
|
+
mouseEventsEnabled,
|
|
3579
|
+
});
|
|
3580
|
+
await act(async () => {
|
|
3581
|
+
stdin.write(input);
|
|
3582
|
+
});
|
|
3583
|
+
await waitFor(() => {
|
|
3584
|
+
expect(setShortcutsHelpVisible).toHaveBeenCalledWith(false);
|
|
3585
|
+
});
|
|
3586
|
+
unmount();
|
|
3587
|
+
});
|
|
3588
|
+
});
|
|
2836
3589
|
});
|
|
2837
3590
|
function clean(str) {
|
|
2838
3591
|
if (!str)
|