@deepseek-harness-tui/dsh-tui 0.9.0 → 0.9.2
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 +69 -256
- package/bin/dsh-tui.js +172 -5
- package/cordis.patch.yml +16 -4
- package/dsh-ecosystem-spec/protocols/profile-definitions.d.ts +13 -13
- package/dsh-ecosystem-spec/protocols/profile-definitions.js +59 -59
- package/dsh-ecosystem-spec/protocols/tui-channel-http.d.ts +15 -15
- package/dsh-ecosystem-spec/protocols/tui-channel-http.js +32 -32
- package/dsh-ecosystem-spec/protocols/tui-channel.d.ts +59 -59
- package/dsh-ecosystem-spec/protocols/tui-channel.js +166 -166
- package/dsh-ecosystem-spec/protocols/tui-contributions.d.ts +42 -42
- package/dsh-ecosystem-spec/protocols/tui-contributions.js +143 -143
- package/dsh-ecosystem-spec/registry/README.md +14 -14
- package/dsh-ecosystem-spec/registry/host-descriptor.tui.example.json +56 -56
- package/dsh-ecosystem-spec/registry/permissions-0.1.json +13 -13
- package/dsh-ecosystem-spec/registry/registry-0.15.json +149 -149
- package/lib/types/cc/hyperlink.d.ts +7 -1
- package/lib/types/cc/hyperlink.d.ts.map +1 -1
- package/lib/types/cc/hyperlink.js +3 -2
- package/lib/types/cc/markdown.d.ts.map +1 -1
- package/lib/types/cc/markdown.js +33 -3
- package/lib/types/cc/sessionColors.d.ts +27 -0
- package/lib/types/cc/sessionColors.d.ts.map +1 -0
- package/lib/types/cc/sessionColors.js +39 -0
- package/lib/types/commands.d.ts.map +1 -1
- package/lib/types/commands.js +6 -0
- package/lib/types/components/AutoRecapRow.d.ts +23 -0
- package/lib/types/components/AutoRecapRow.d.ts.map +1 -0
- package/lib/types/components/AutoRecapRow.js +23 -0
- package/lib/types/components/ColorPicker.d.ts +17 -0
- package/lib/types/components/ColorPicker.d.ts.map +1 -0
- package/lib/types/components/ColorPicker.js +20 -0
- package/lib/types/components/CommandSuggestions.d.ts +3 -1
- package/lib/types/components/CommandSuggestions.d.ts.map +1 -1
- package/lib/types/components/ContextBarView.d.ts +33 -0
- package/lib/types/components/ContextBarView.d.ts.map +1 -0
- package/lib/types/components/ContextBarView.js +46 -0
- package/lib/types/components/EffortInputBorder.d.ts +12 -1
- package/lib/types/components/EffortInputBorder.d.ts.map +1 -1
- package/lib/types/components/EffortInputBorder.js +22 -5
- package/lib/types/components/FileActionsPanel.d.ts +22 -0
- package/lib/types/components/FileActionsPanel.d.ts.map +1 -0
- package/lib/types/components/FileActionsPanel.js +28 -0
- package/lib/types/components/FileSuggestions.d.ts +3 -1
- package/lib/types/components/FileSuggestions.d.ts.map +1 -1
- package/lib/types/components/MessageList.d.ts +3 -1
- package/lib/types/components/MessageList.d.ts.map +1 -1
- package/lib/types/components/MessageList.js +13 -6
- package/lib/types/components/ModelPicker.d.ts +26 -4
- package/lib/types/components/ModelPicker.d.ts.map +1 -1
- package/lib/types/components/ModelPicker.js +26 -6
- package/lib/types/components/PromptInput.d.ts.map +1 -1
- package/lib/types/components/PromptInput.js +365 -57
- package/lib/types/components/RecapPanel.d.ts +25 -0
- package/lib/types/components/RecapPanel.d.ts.map +1 -0
- package/lib/types/components/RecapPanel.js +55 -0
- package/lib/types/components/ScrollbarGutter.d.ts.map +1 -1
- package/lib/types/components/ScrollbarGutter.js +55 -4
- package/lib/types/components/SubagentCard.d.ts.map +1 -1
- package/lib/types/components/SubagentCard.js +6 -4
- package/lib/types/components/SuggestionCard.d.ts +4 -2
- package/lib/types/components/SuggestionCard.d.ts.map +1 -1
- package/lib/types/components/TimelineRail.d.ts.map +1 -1
- package/lib/types/components/TimelineRail.js +9 -2
- package/lib/types/components/design-system/ThemeProvider.d.ts +7 -0
- package/lib/types/components/design-system/ThemeProvider.d.ts.map +1 -1
- package/lib/types/components/design-system/ThemeProvider.js +2 -1
- package/lib/types/components/messages/AssistantToolUseMessage.d.ts +8 -1
- package/lib/types/components/messages/AssistantToolUseMessage.d.ts.map +1 -1
- package/lib/types/components/messages/AssistantToolUseMessage.js +36 -8
- package/lib/types/components/questions/AskUserQuestionPanel.d.ts +7 -3
- package/lib/types/components/questions/AskUserQuestionPanel.d.ts.map +1 -1
- package/lib/types/components/questions/AskUserQuestionPanel.js +41 -9
- package/lib/types/dsh-adapter/channel.d.ts +101 -2
- package/lib/types/dsh-adapter/channel.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel.js +969 -93
- package/lib/types/dsh-adapter/compat/index.d.ts +8 -6
- package/lib/types/dsh-adapter/compat/index.d.ts.map +1 -1
- package/lib/types/dsh-adapter/compat/index.js +8 -6
- package/lib/types/dsh-adapter/compat/sessionLog.d.ts +150 -0
- package/lib/types/dsh-adapter/compat/sessionLog.d.ts.map +1 -1
- package/lib/types/dsh-adapter/compat/sessionLog.js +518 -41
- package/lib/types/dsh-adapter/contract.d.ts +1 -1
- package/lib/types/dsh-adapter/contract.d.ts.map +1 -1
- package/lib/types/dsh-adapter/contract.js +1 -0
- package/lib/types/dsh-adapter/extension-events.d.ts +1 -1
- package/lib/types/dsh-adapter/extension-events.d.ts.map +1 -1
- package/lib/types/dsh-adapter/index.d.ts +10 -0
- package/lib/types/dsh-adapter/index.d.ts.map +1 -1
- package/lib/types/dsh-adapter/index.js +5 -0
- package/lib/types/dsh-adapter/plugin.d.ts.map +1 -1
- package/lib/types/dsh-adapter/plugin.js +317 -8
- package/lib/types/dsh-adapter/presets.d.ts +18 -0
- package/lib/types/dsh-adapter/presets.d.ts.map +1 -1
- package/lib/types/dsh-adapter/presets.js +31 -0
- package/lib/types/dsh-adapter/providerWizard.d.ts +29 -1
- package/lib/types/dsh-adapter/providerWizard.d.ts.map +1 -1
- package/lib/types/dsh-adapter/providerWizard.js +89 -5
- package/lib/types/dsh-adapter/questions.d.ts +19 -2
- package/lib/types/dsh-adapter/questions.d.ts.map +1 -1
- package/lib/types/dsh-adapter/questions.js +53 -10
- package/lib/types/dsh-adapter/recap.d.ts +49 -0
- package/lib/types/dsh-adapter/recap.d.ts.map +1 -0
- package/lib/types/dsh-adapter/recap.js +121 -0
- package/lib/types/dsh-adapter/sessionTree.d.ts +314 -0
- package/lib/types/dsh-adapter/sessionTree.d.ts.map +1 -0
- package/lib/types/dsh-adapter/sessionTree.js +954 -0
- package/lib/types/dsh-adapter/shortcuts.d.ts +15 -44
- package/lib/types/dsh-adapter/shortcuts.d.ts.map +1 -1
- package/lib/types/dsh-adapter/shortcuts.js +28 -165
- package/lib/types/dsh-adapter/types.d.ts +1 -1
- package/lib/types/dsh-adapter/types.d.ts.map +1 -1
- package/lib/types/herdr.js +1 -1
- package/lib/types/i18n.d.ts +604 -176
- package/lib/types/i18n.d.ts.map +1 -1
- package/lib/types/i18n.js +206 -72
- package/lib/types/ink/log-update.d.ts.map +1 -1
- package/lib/types/ink/log-update.js +21 -0
- package/lib/types/migrationPrefs.d.ts +60 -0
- package/lib/types/migrationPrefs.d.ts.map +1 -0
- package/lib/types/migrationPrefs.js +97 -0
- package/lib/types/modelGroups.d.ts +72 -0
- package/lib/types/modelGroups.d.ts.map +1 -0
- package/lib/types/modelGroups.js +97 -0
- package/lib/types/modelRecents.d.ts +39 -0
- package/lib/types/modelRecents.d.ts.map +1 -0
- package/lib/types/modelRecents.js +97 -0
- package/lib/types/native-ts/yoga-layout/index.d.ts +11 -0
- package/lib/types/native-ts/yoga-layout/index.d.ts.map +1 -1
- package/lib/types/native-ts/yoga-layout/index.js +31 -1
- package/lib/types/oauth.d.ts +15 -0
- package/lib/types/oauth.d.ts.map +1 -0
- package/lib/types/oauth.js +14 -0
- package/lib/types/plugin-spec/registry.d.ts +1 -1
- package/lib/types/plugin-spec/registry.js +1 -1
- package/lib/types/reload.d.ts +109 -0
- package/lib/types/reload.d.ts.map +1 -0
- package/lib/types/reload.js +118 -0
- package/lib/types/screens/Chat.d.ts +3 -1
- package/lib/types/screens/Chat.d.ts.map +1 -1
- package/lib/types/screens/Chat.js +588 -64
- package/lib/types/screens/SessionTree.d.ts +27 -0
- package/lib/types/screens/SessionTree.d.ts.map +1 -0
- package/lib/types/screens/SessionTree.js +555 -0
- package/lib/types/screens/StatusLine.d.ts +0 -9
- package/lib/types/screens/StatusLine.d.ts.map +1 -1
- package/lib/types/screens/StatusLine.js +215 -52
- package/lib/types/screens/StatusMetrics.d.ts +35 -3
- package/lib/types/screens/StatusMetrics.d.ts.map +1 -1
- package/lib/types/screens/StatusMetrics.js +63 -8
- package/lib/types/screens/chatOverlay.d.ts +17 -1
- package/lib/types/screens/chatOverlay.d.ts.map +1 -1
- package/lib/types/screens/chatOverlay.js +3 -1
- package/lib/types/tips.d.ts.map +1 -1
- package/lib/types/tips.js +22 -4
- package/lib/types/update.d.ts +159 -0
- package/lib/types/update.d.ts.map +1 -1
- package/lib/types/update.js +496 -30
- package/lib/types/utils/debug.d.ts +1 -1
- package/lib/types/utils/debug.js +3 -3
- package/lib/types/utils/fileTarget.d.ts +46 -0
- package/lib/types/utils/fileTarget.d.ts.map +1 -0
- package/lib/types/utils/fileTarget.js +178 -0
- package/lib/types/utils/keymap.d.ts +134 -0
- package/lib/types/utils/keymap.d.ts.map +1 -0
- package/lib/types/utils/keymap.js +311 -0
- package/lib/types/utils/mentions.d.ts +25 -2
- package/lib/types/utils/mentions.d.ts.map +1 -1
- package/lib/types/utils/mentions.js +69 -10
- package/lib/types/utils/openExternal.d.ts +50 -0
- package/lib/types/utils/openExternal.d.ts.map +1 -0
- package/lib/types/utils/openExternal.js +170 -0
- package/node_modules/@deepseek-harness-tui/dsh-auth/LICENSE +21 -0
- package/node_modules/@deepseek-harness-tui/dsh-auth/README.md +140 -0
- package/node_modules/@deepseek-harness-tui/dsh-auth/cordis.patch.yml +11 -0
- package/node_modules/@deepseek-harness-tui/dsh-auth/dsh-plugin.json +44 -0
- package/node_modules/@deepseek-harness-tui/dsh-auth/lib/command.d.ts +14 -0
- package/node_modules/@deepseek-harness-tui/dsh-auth/lib/command.js +67 -0
- package/node_modules/@deepseek-harness-tui/dsh-auth/lib/credentials.d.ts +64 -0
- package/node_modules/@deepseek-harness-tui/dsh-auth/lib/credentials.js +170 -0
- package/node_modules/@deepseek-harness-tui/dsh-auth/lib/index.d.ts +79 -0
- package/node_modules/@deepseek-harness-tui/dsh-auth/lib/index.js +196 -0
- package/node_modules/@deepseek-harness-tui/dsh-auth/lib/interaction.d.ts +68 -0
- package/node_modules/@deepseek-harness-tui/dsh-auth/lib/interaction.js +193 -0
- package/node_modules/@deepseek-harness-tui/dsh-auth/lib/opener.d.ts +32 -0
- package/node_modules/@deepseek-harness-tui/dsh-auth/lib/opener.js +101 -0
- package/node_modules/@deepseek-harness-tui/dsh-auth/lib/profiles.d.ts +31 -0
- package/node_modules/@deepseek-harness-tui/dsh-auth/lib/profiles.js +68 -0
- package/node_modules/@deepseek-harness-tui/dsh-auth/lib/service.d.ts +69 -0
- package/node_modules/@deepseek-harness-tui/dsh-auth/lib/service.js +129 -0
- package/node_modules/@deepseek-harness-tui/dsh-auth/package.json +70 -0
- package/node_modules/@dsh-std/command/CHANGELOG.md +8 -8
- package/node_modules/@dsh-std/command/LICENSE +21 -21
- package/node_modules/@dsh-std/command/README.md +26 -26
- package/node_modules/@dsh-std/command/README.zh.md +28 -28
- package/node_modules/@dsh-std/command/lib/index.js.map +1 -1
- package/node_modules/@dsh-std/command/package.json +22 -22
- package/node_modules/@dsh-std/connection/CHANGELOG.md +8 -8
- package/node_modules/@dsh-std/connection/LICENSE +21 -21
- package/node_modules/@dsh-std/connection/README.md +42 -42
- package/node_modules/@dsh-std/connection/README.zh.md +42 -42
- package/node_modules/@dsh-std/connection/lib/index.js.map +1 -1
- package/node_modules/@dsh-std/connection/lib/memory.js.map +1 -1
- package/node_modules/@dsh-std/connection/lib/resolve-3X6ausgz.js.map +1 -1
- package/node_modules/@dsh-std/connection/package.json +20 -20
- package/node_modules/@dsh-std/core/CHANGELOG.md +8 -8
- package/node_modules/@dsh-std/core/LICENSE +21 -21
- package/node_modules/@dsh-std/core/README.md +13 -13
- package/node_modules/@dsh-std/core/README.zh.md +13 -13
- package/node_modules/@dsh-std/core/lib/index.js.map +1 -1
- package/node_modules/@dsh-std/core/package.json +21 -21
- package/node_modules/@dsh-std/manifest/CHANGELOG.md +9 -9
- package/node_modules/@dsh-std/manifest/LICENSE +21 -21
- package/node_modules/@dsh-std/manifest/README.md +5 -5
- package/node_modules/@dsh-std/manifest/README.zh.md +5 -5
- package/node_modules/@dsh-std/manifest/lib/index.js.map +1 -1
- package/node_modules/@dsh-std/manifest/package.json +20 -20
- package/node_modules/@dsh-std/manifest/schema/dsh-plugin-0.15.schema.json +144 -144
- package/node_modules/@dsh-std/messages/CHANGELOG.md +8 -8
- package/node_modules/@dsh-std/messages/LICENSE +21 -21
- package/node_modules/@dsh-std/messages/README.md +5 -5
- package/node_modules/@dsh-std/messages/README.zh.md +7 -7
- package/node_modules/@dsh-std/messages/lib/index.js.map +1 -1
- package/node_modules/@dsh-std/messages/package.json +20 -20
- package/node_modules/@dsh-std/messages/schema/message-observer-envelope-0.15.schema.json +54 -54
- package/node_modules/@dsh-std/presentation/CHANGELOG.md +9 -9
- package/node_modules/@dsh-std/presentation/LICENSE +21 -21
- package/node_modules/@dsh-std/presentation/README.md +27 -27
- package/node_modules/@dsh-std/presentation/README.zh.md +29 -29
- package/node_modules/@dsh-std/presentation/lib/callback.js.map +1 -1
- package/node_modules/@dsh-std/presentation/lib/index.js.map +1 -1
- package/node_modules/@dsh-std/presentation/package.json +25 -25
- package/node_modules/@dsh-std/storage/CHANGELOG.md +8 -8
- package/node_modules/@dsh-std/storage/LICENSE +21 -21
- package/node_modules/@dsh-std/storage/README.md +5 -5
- package/node_modules/@dsh-std/storage/README.zh.md +7 -7
- package/node_modules/@dsh-std/storage/lib/index.js.map +1 -1
- package/node_modules/@dsh-std/storage/package.json +20 -20
- package/node_modules/@dsh-std/storage/schema/local-storage-0.15.schema.json +56 -56
- package/package.json +17 -5
- package/presets/liangshen/.dsh-tui-managed.json +1 -1
- package/presets/liangshen/instruction-hint.mjs +8 -0
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/CHANGELOG.md +8 -8
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/LICENSE +21 -21
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/README.md +42 -42
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/README.zh.md +42 -42
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/lib/index.js.map +1 -1
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/lib/memory.js.map +1 -1
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/lib/resolve-3X6ausgz.js.map +1 -1
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/package.json +20 -20
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/core/CHANGELOG.md +8 -8
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/core/LICENSE +21 -21
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/core/README.md +13 -13
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/core/README.zh.md +13 -13
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/core/lib/index.js.map +1 -1
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/core/package.json +21 -21
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/manifest/CHANGELOG.md +9 -9
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/manifest/LICENSE +21 -21
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/manifest/README.md +5 -5
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/manifest/README.zh.md +5 -5
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/manifest/lib/index.js.map +1 -1
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/manifest/package.json +20 -20
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/manifest/schema/dsh-plugin-0.15.schema.json +144 -144
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/CHANGELOG.md +9 -9
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/LICENSE +21 -21
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/README.md +27 -27
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/README.zh.md +29 -29
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/lib/callback.js.map +1 -1
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/lib/index.js.map +1 -1
- package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/package.json +25 -25
- package/vendor/dsh-std/packages/connection/node_modules/@dsh-std/core/CHANGELOG.md +8 -8
- package/vendor/dsh-std/packages/connection/node_modules/@dsh-std/core/LICENSE +21 -21
- package/vendor/dsh-std/packages/connection/node_modules/@dsh-std/core/README.md +13 -13
- package/vendor/dsh-std/packages/connection/node_modules/@dsh-std/core/README.zh.md +13 -13
- package/vendor/dsh-std/packages/connection/node_modules/@dsh-std/core/lib/index.js.map +1 -1
- package/vendor/dsh-std/packages/connection/node_modules/@dsh-std/core/package.json +21 -21
- package/vendor/dsh-std/packages/manifest/node_modules/@dsh-std/core/CHANGELOG.md +8 -8
- package/vendor/dsh-std/packages/manifest/node_modules/@dsh-std/core/LICENSE +21 -21
- package/vendor/dsh-std/packages/manifest/node_modules/@dsh-std/core/README.md +13 -13
- package/vendor/dsh-std/packages/manifest/node_modules/@dsh-std/core/README.zh.md +13 -13
- package/vendor/dsh-std/packages/manifest/node_modules/@dsh-std/core/lib/index.js.map +1 -1
- package/vendor/dsh-std/packages/manifest/node_modules/@dsh-std/core/package.json +21 -21
- package/vendor/dsh-std/packages/messages/node_modules/@dsh-std/core/CHANGELOG.md +8 -8
- package/vendor/dsh-std/packages/messages/node_modules/@dsh-std/core/LICENSE +21 -21
- package/vendor/dsh-std/packages/messages/node_modules/@dsh-std/core/README.md +13 -13
- package/vendor/dsh-std/packages/messages/node_modules/@dsh-std/core/README.zh.md +13 -13
- package/vendor/dsh-std/packages/messages/node_modules/@dsh-std/core/lib/index.js.map +1 -1
- package/vendor/dsh-std/packages/messages/node_modules/@dsh-std/core/package.json +21 -21
- package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/CHANGELOG.md +8 -8
- package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/LICENSE +21 -21
- package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/README.md +42 -42
- package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/README.zh.md +42 -42
- package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/lib/index.js.map +1 -1
- package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/lib/memory.js.map +1 -1
- package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/lib/resolve-3X6ausgz.js.map +1 -1
- package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/package.json +20 -20
- package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/core/CHANGELOG.md +8 -8
- package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/core/LICENSE +21 -21
- package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/core/README.md +13 -13
- package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/core/README.zh.md +13 -13
- package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/core/lib/index.js.map +1 -1
- package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/core/package.json +21 -21
- package/vendor/dsh-std/packages/storage/node_modules/@dsh-std/core/CHANGELOG.md +8 -8
- package/vendor/dsh-std/packages/storage/node_modules/@dsh-std/core/LICENSE +21 -21
- package/vendor/dsh-std/packages/storage/node_modules/@dsh-std/core/README.md +13 -13
- package/vendor/dsh-std/packages/storage/node_modules/@dsh-std/core/README.zh.md +13 -13
- package/vendor/dsh-std/packages/storage/node_modules/@dsh-std/core/lib/index.js.map +1 -1
- package/vendor/dsh-std/packages/storage/node_modules/@dsh-std/core/package.json +21 -21
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/** Our custom OSC 8 scheme carrying a raw display path (URI-encoded). */
|
|
2
|
+
export declare const FILE_LINK_SCHEME = "dsh-file:";
|
|
3
|
+
/** Encode a display path into a `dsh-file:` link URL. */
|
|
4
|
+
export declare function fileLinkUrl(path: string): string;
|
|
5
|
+
/**
|
|
6
|
+
* Decode a `dsh-file:` link URL back to the raw display path. Returns
|
|
7
|
+
* undefined for anything that is not a well-formed dsh-file URL.
|
|
8
|
+
*/
|
|
9
|
+
export declare function parseFileLinkUrl(url: string): string | undefined;
|
|
10
|
+
/**
|
|
11
|
+
* Resolve a target (raw display path) against the session's base directory:
|
|
12
|
+
* absolute paths and `~` pass through (with ~ expanded), everything else is
|
|
13
|
+
* joined onto the base. Pure, so it is unit-testable without a workspace.
|
|
14
|
+
*/
|
|
15
|
+
export declare function resolveTargetPath(path: string, baseDir: string): string;
|
|
16
|
+
/**
|
|
17
|
+
* Convert a `file://` URL to a local path. Wraps node:url.fileURLToPath —
|
|
18
|
+
* undefined when the URL is not a valid file URL.
|
|
19
|
+
*/
|
|
20
|
+
export declare function fileUrlToPath(url: string): string | undefined;
|
|
21
|
+
/**
|
|
22
|
+
* Conservative heuristic: does this text look like a file path worth
|
|
23
|
+
* making clickable? Requires a slash separator, no whitespace, no URL/email
|
|
24
|
+
* shape, and either a path anchor (`./`, `../`, `/`, `~/`, drive letter)
|
|
25
|
+
* with a real path shape (another separator or an extension — a bare
|
|
26
|
+
* anchored word like `/a` or `x.com/a` is too ambiguous), or a plausible
|
|
27
|
+
* file extension. Length-capped so huge tokens (base64, logs) never get
|
|
28
|
+
* linked.
|
|
29
|
+
*/
|
|
30
|
+
export declare function looksLikeFilePath(text: string): boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Best-effort existence hint: does `path` exist on disk? Used to decide
|
|
33
|
+
* whether to render a path as clickable in contexts where the raw text is
|
|
34
|
+
* ambiguous; never throws.
|
|
35
|
+
*/
|
|
36
|
+
export declare function pathExistsOnDisk(path: string): boolean;
|
|
37
|
+
/**
|
|
38
|
+
* Replace every path-looking span in `text` via `wrap(path, display)`.
|
|
39
|
+
* Trailing sentence punctuation stays OUTSIDE the wrapped display (so
|
|
40
|
+
* "src/foo.ts." links `src/foo.ts` and keeps the period). Spans that fail
|
|
41
|
+
* `looksLikeFilePath` are returned unchanged.
|
|
42
|
+
* @param text - plain text to scan (no ANSI).
|
|
43
|
+
* @param wrap - builds the clickable rendering for one path.
|
|
44
|
+
*/
|
|
45
|
+
export declare function linkifyFilePaths(text: string, wrap: (path: string, display: string) => string): string;
|
|
46
|
+
//# sourceMappingURL=fileTarget.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fileTarget.d.ts","sourceRoot":"","sources":["../../../src/utils/fileTarget.ts"],"names":[],"mappings":"AAuBA,yEAAyE;AACzE,eAAO,MAAM,gBAAgB,cAAc,CAAA;AAE3C,yDAAyD;AACzD,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEhD;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAOhE;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAWvE;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAM7D;AA0BD;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAoBvD;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAMtD;AAWD;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,MAAM,GAC9C,MAAM,CAcR"}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Clickable file targets: turning path-like text in AI output into
|
|
3
|
+
* clickable targets, and resolving those targets back to absolute paths at
|
|
4
|
+
* click time.
|
|
5
|
+
*
|
|
6
|
+
* Two URL shapes are produced / accepted:
|
|
7
|
+
* - `dsh-file:<encodeURIComponent(path)>` — our own scheme. The payload is
|
|
8
|
+
* the RAW displayed path (possibly relative, possibly Windows-style), so
|
|
9
|
+
* nothing is lost in transit and it resolves against the CURRENT
|
|
10
|
+
* workspace at click time (paths in a long-lived session may outlive the
|
|
11
|
+
* cwd they were written under).
|
|
12
|
+
* - `file://…` — real file URLs the model may emit in markdown; decoded
|
|
13
|
+
* with node:url and treated the same way.
|
|
14
|
+
*
|
|
15
|
+
* `looksLikeFilePath` is deliberately conservative: it must be obviously a
|
|
16
|
+
* path (slash + no whitespace + anchor or extension), otherwise prose like
|
|
17
|
+
* "and/or" or "2024/01/15" would light up as clickable everywhere.
|
|
18
|
+
*/
|
|
19
|
+
import { resolve } from 'node:path';
|
|
20
|
+
import { homedir } from 'node:os';
|
|
21
|
+
import { fileURLToPath } from 'node:url';
|
|
22
|
+
import { existsSync } from 'node:fs';
|
|
23
|
+
/** Our custom OSC 8 scheme carrying a raw display path (URI-encoded). */
|
|
24
|
+
export const FILE_LINK_SCHEME = 'dsh-file:';
|
|
25
|
+
/** Encode a display path into a `dsh-file:` link URL. */
|
|
26
|
+
export function fileLinkUrl(path) {
|
|
27
|
+
return FILE_LINK_SCHEME + encodeURIComponent(path);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Decode a `dsh-file:` link URL back to the raw display path. Returns
|
|
31
|
+
* undefined for anything that is not a well-formed dsh-file URL.
|
|
32
|
+
*/
|
|
33
|
+
export function parseFileLinkUrl(url) {
|
|
34
|
+
if (!url.startsWith(FILE_LINK_SCHEME))
|
|
35
|
+
return undefined;
|
|
36
|
+
try {
|
|
37
|
+
return decodeURIComponent(url.slice(FILE_LINK_SCHEME.length));
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Resolve a target (raw display path) against the session's base directory:
|
|
45
|
+
* absolute paths and `~` pass through (with ~ expanded), everything else is
|
|
46
|
+
* joined onto the base. Pure, so it is unit-testable without a workspace.
|
|
47
|
+
*/
|
|
48
|
+
export function resolveTargetPath(path, baseDir) {
|
|
49
|
+
if (path === '')
|
|
50
|
+
return path;
|
|
51
|
+
if (path.startsWith('~/'))
|
|
52
|
+
return resolve(homedir(), path.slice(2));
|
|
53
|
+
if (path === '~')
|
|
54
|
+
return homedir();
|
|
55
|
+
// Windows drive-absolute (`C:\…`, `C:/…`) is absolute regardless of
|
|
56
|
+
// platform; node:path.isAbsolute handles the current platform only, so
|
|
57
|
+
// probe both separators explicitly.
|
|
58
|
+
if (/^[A-Za-z]:[\\/]/.test(path) || path.startsWith('/') || path.startsWith('\\')) {
|
|
59
|
+
return path;
|
|
60
|
+
}
|
|
61
|
+
return resolve(baseDir, path);
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Convert a `file://` URL to a local path. Wraps node:url.fileURLToPath —
|
|
65
|
+
* undefined when the URL is not a valid file URL.
|
|
66
|
+
*/
|
|
67
|
+
export function fileUrlToPath(url) {
|
|
68
|
+
try {
|
|
69
|
+
return fileURLToPath(url);
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
return undefined;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
/** Known source/config/data file extensions — the conservative allowlist. */
|
|
76
|
+
const FILE_EXTENSION_RE = /\.([A-Za-z0-9]{1,6})$/u;
|
|
77
|
+
/** A path is worth linking when it carries one of these anchors, or has a
|
|
78
|
+
* known-extension tail. */
|
|
79
|
+
const PATH_ANCHOR_RE = /^(\.{1,2}[\\/]|[\\/]|~[\\/]|[A-Za-z]:[\\/])/u;
|
|
80
|
+
/** Stoplist for extension-like tails that are TLDs / prose words, not
|
|
81
|
+
* file extensions (`github.com/foo` must not link as a file). */
|
|
82
|
+
const NON_EXTENSION_TAILS = new Set([
|
|
83
|
+
'com', 'org', 'net', 'io', 'dev', 'app', 'edu', 'gov', 'mil', 'co', 'uk', 'cn',
|
|
84
|
+
]);
|
|
85
|
+
/** A dot-suffix of up to 6 alphanumerics is a plausible extension when at
|
|
86
|
+
* least 4 chars precede it (`a/b.x`-style prose fragments and short
|
|
87
|
+
* `x/y.ts` guesses must not false-positive). */
|
|
88
|
+
function hasPlausibleExtension(stripped) {
|
|
89
|
+
const extMatch = FILE_EXTENSION_RE.exec(stripped);
|
|
90
|
+
if (extMatch === null)
|
|
91
|
+
return false;
|
|
92
|
+
const ext = extMatch[1].toLowerCase();
|
|
93
|
+
if (NON_EXTENSION_TAILS.has(ext))
|
|
94
|
+
return false;
|
|
95
|
+
return stripped.slice(0, extMatch.index).length >= 4;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Conservative heuristic: does this text look like a file path worth
|
|
99
|
+
* making clickable? Requires a slash separator, no whitespace, no URL/email
|
|
100
|
+
* shape, and either a path anchor (`./`, `../`, `/`, `~/`, drive letter)
|
|
101
|
+
* with a real path shape (another separator or an extension — a bare
|
|
102
|
+
* anchored word like `/a` or `x.com/a` is too ambiguous), or a plausible
|
|
103
|
+
* file extension. Length-capped so huge tokens (base64, logs) never get
|
|
104
|
+
* linked.
|
|
105
|
+
*/
|
|
106
|
+
export function looksLikeFilePath(text) {
|
|
107
|
+
const t = text.trim();
|
|
108
|
+
if (t.length < 2 || t.length > 200)
|
|
109
|
+
return false;
|
|
110
|
+
if (!t.includes('/') && !t.includes('\\'))
|
|
111
|
+
return false;
|
|
112
|
+
if (/\s/u.test(t))
|
|
113
|
+
return false;
|
|
114
|
+
if (/[<>"'`|?*]/.test(t))
|
|
115
|
+
return false;
|
|
116
|
+
// URLs (any scheme://, incl. file://) and emails are handled by their
|
|
117
|
+
// own linkifiers.
|
|
118
|
+
if (/^[a-z][a-z0-9+.-]*:\/\//iu.test(t) || /^www\./iu.test(t))
|
|
119
|
+
return false;
|
|
120
|
+
if (t.includes('@'))
|
|
121
|
+
return false;
|
|
122
|
+
if (/[#\u0000-\u001f]/u.test(t))
|
|
123
|
+
return false;
|
|
124
|
+
// Strip one layer of trailing punctuation so "foo/bar.ts." still links.
|
|
125
|
+
const stripped = t.replace(/[.,;:!?]+$/u, '');
|
|
126
|
+
if (stripped === '')
|
|
127
|
+
return false;
|
|
128
|
+
const separators = (stripped.match(/[\\/]/gu) ?? []).length;
|
|
129
|
+
if (PATH_ANCHOR_RE.test(stripped)) {
|
|
130
|
+
return separators >= 2 || hasPlausibleExtension(stripped);
|
|
131
|
+
}
|
|
132
|
+
return hasPlausibleExtension(stripped);
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Best-effort existence hint: does `path` exist on disk? Used to decide
|
|
136
|
+
* whether to render a path as clickable in contexts where the raw text is
|
|
137
|
+
* ambiguous; never throws.
|
|
138
|
+
*/
|
|
139
|
+
export function pathExistsOnDisk(path) {
|
|
140
|
+
try {
|
|
141
|
+
return existsSync(path);
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Candidate-span scanner: anchored paths (`./`, `../`, `/`, `~/`, drive)
|
|
149
|
+
* run to the end of the word; everything else must contain a slash AND a
|
|
150
|
+
* dot-extension tail. This is only a pre-filter — every span is then
|
|
151
|
+
* verified by `looksLikeFilePath` before it becomes clickable.
|
|
152
|
+
*/
|
|
153
|
+
const PATH_SPAN_RE = /(?:\.{1,2}[\\/]|[\\/]|~[\\/]|[A-Za-z]:[\\/])[^\s<>"'`|?#]*|(?:[^\s<>"'`|?#]*[\\/][^\s<>"'`|?#]*\.[A-Za-z0-9]{1,6})/gu;
|
|
154
|
+
/**
|
|
155
|
+
* Replace every path-looking span in `text` via `wrap(path, display)`.
|
|
156
|
+
* Trailing sentence punctuation stays OUTSIDE the wrapped display (so
|
|
157
|
+
* "src/foo.ts." links `src/foo.ts` and keeps the period). Spans that fail
|
|
158
|
+
* `looksLikeFilePath` are returned unchanged.
|
|
159
|
+
* @param text - plain text to scan (no ANSI).
|
|
160
|
+
* @param wrap - builds the clickable rendering for one path.
|
|
161
|
+
*/
|
|
162
|
+
export function linkifyFilePaths(text, wrap) {
|
|
163
|
+
return text.replace(PATH_SPAN_RE, (match, offset, full) => {
|
|
164
|
+
// The slash-anchor branch would otherwise match inside URLs
|
|
165
|
+
// (`https:/…` matches `/…`): a span glued to a scheme colon or a `//`
|
|
166
|
+
// run is part of a URL, never a path. offset is available because the
|
|
167
|
+
// regex has no capture groups.
|
|
168
|
+
if (offset > 0) {
|
|
169
|
+
const prev = full[offset - 1];
|
|
170
|
+
if (prev === ':' || prev === '/')
|
|
171
|
+
return match;
|
|
172
|
+
}
|
|
173
|
+
const display = match.replace(/[.,;:!?]+$/u, '');
|
|
174
|
+
if (display === '' || !looksLikeFilePath(display))
|
|
175
|
+
return match;
|
|
176
|
+
return wrap(display, display) + match.slice(display.length);
|
|
177
|
+
});
|
|
178
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Built-in keyboard-shortcut keymap: the shared combo grammar, the action
|
|
3
|
+
* registry, and the live override cache behind `/settings` customization.
|
|
4
|
+
*
|
|
5
|
+
* Split of concerns across the input stack:
|
|
6
|
+
* - `dsh-adapter/shortcuts.ts` owns the PLUGIN registry (`ctx.tuiShortcuts`)
|
|
7
|
+
* and re-exports the grammar from here so both sides parse identically.
|
|
8
|
+
* - This module owns what the TUI itself binds: a fixed set of named actions
|
|
9
|
+
* (paste, history, editor, …) whose combos resolve as
|
|
10
|
+
* settings.yaml user layer > cordis.yml `shortcuts` > the registry default.
|
|
11
|
+
* `Chat.tsx` / `PromptInput.tsx` match keypresses against the EFFECTIVE
|
|
12
|
+
* combos via {@link actionMatches} — never against a hardcoded string.
|
|
13
|
+
*
|
|
14
|
+
* The macOS modifier alias mirrors `isMod`: a combo spelled with `ctrl`
|
|
15
|
+
* matches Cmd (`super`) on the mac too, so `ctrl+v` keeps meaning "paste
|
|
16
|
+
* with the platform primary modifier" everywhere. `alt` always means the
|
|
17
|
+
* ink `meta` flag. Shift must match exactly (ctrl+shift+v is the terminal's
|
|
18
|
+
* native paste, never the app's clipboard read).
|
|
19
|
+
*/
|
|
20
|
+
/** Minimal structural shape of the ink Key flags the matchers read. */
|
|
21
|
+
export interface ComboKeyFlags {
|
|
22
|
+
ctrl?: boolean;
|
|
23
|
+
meta?: boolean;
|
|
24
|
+
super?: boolean;
|
|
25
|
+
shift?: boolean;
|
|
26
|
+
return?: boolean;
|
|
27
|
+
escape?: boolean;
|
|
28
|
+
tab?: boolean;
|
|
29
|
+
backspace?: boolean;
|
|
30
|
+
delete?: boolean;
|
|
31
|
+
upArrow?: boolean;
|
|
32
|
+
downArrow?: boolean;
|
|
33
|
+
leftArrow?: boolean;
|
|
34
|
+
rightArrow?: boolean;
|
|
35
|
+
home?: boolean;
|
|
36
|
+
end?: boolean;
|
|
37
|
+
pageUp?: boolean;
|
|
38
|
+
pageDown?: boolean;
|
|
39
|
+
}
|
|
40
|
+
/** A parsed `ctrl+shift+p` style combo. */
|
|
41
|
+
export interface ParsedCombo {
|
|
42
|
+
readonly raw: string;
|
|
43
|
+
readonly ctrl: boolean;
|
|
44
|
+
readonly meta: boolean;
|
|
45
|
+
readonly shift: boolean;
|
|
46
|
+
/** Named key flag on the Key object, or undefined for a character key. */
|
|
47
|
+
readonly named?: keyof ComboKeyFlags;
|
|
48
|
+
/** Character to match against the ink `input` string (lowercased). */
|
|
49
|
+
readonly char?: string;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Parse `ctrl+shift+p` style combos. Returns undefined on anything
|
|
53
|
+
* malformed or disallowed (no ctrl/alt modifier, unknown key name, escape
|
|
54
|
+
* combos — the same grammar the plugin shortcut registry enforces).
|
|
55
|
+
*/
|
|
56
|
+
export declare function parseCombo(raw: string): ParsedCombo | undefined;
|
|
57
|
+
/** Canonical form for dedupe/reserved checks: modifiers sorted, key last. */
|
|
58
|
+
export declare function canonicalCombo(combo: ParsedCombo): string;
|
|
59
|
+
/** Canonicalize a user-spelled combo string (`ctrl+c`); unparseable input
|
|
60
|
+
* comes back lowercased as-is so reserved sets can still carry entries the
|
|
61
|
+
* grammar itself refuses (bare escape, tab…). */
|
|
62
|
+
export declare function canonicalComboString(raw: string): string;
|
|
63
|
+
/**
|
|
64
|
+
* Strict matcher (plugin registry semantics): every modifier flag must equal
|
|
65
|
+
* the combo's. `ctrl` means the ctrl flag only — no platform alias.
|
|
66
|
+
*/
|
|
67
|
+
export declare function comboMatchesStrict(combo: ParsedCombo, input: string, key: ComboKeyFlags): boolean;
|
|
68
|
+
/** Identifiers of every customizable built-in action. */
|
|
69
|
+
export type ShortcutActionId = 'paste' | 'history' | 'editor' | 'transcript' | 'trajectory' | 'dashboard' | 'contextPanel' | 'showAll' | 'redraw' | 'todoFold';
|
|
70
|
+
export interface ShortcutActionSpec {
|
|
71
|
+
readonly id: ShortcutActionId;
|
|
72
|
+
/** Default combos; the FIRST entry is the canonical display form. */
|
|
73
|
+
readonly defaults: readonly string[];
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* The registry. Defaults encode today's hard-wired bindings — `paste` also
|
|
77
|
+
* carries the `alt+v` alias for terminals that intercept Ctrl+V before the
|
|
78
|
+
* app ever sees the byte (some terminals/IMEs reserve Ctrl+V for their own
|
|
79
|
+
* paste and never forward it in raw mode).
|
|
80
|
+
*/
|
|
81
|
+
export declare const SHORTCUT_ACTIONS: readonly ShortcutActionSpec[];
|
|
82
|
+
/**
|
|
83
|
+
* Replace the whole override map. Invalid or blank entries are DROPPED
|
|
84
|
+
* (the action keeps its default) — a typo in cordis.yml must never disable
|
|
85
|
+
* a built-in, and the settings field's parse already refuses invalid
|
|
86
|
+
* drafts before anything is written.
|
|
87
|
+
*/
|
|
88
|
+
export declare function setKeymapOverrides(overrides: Partial<Record<ShortcutActionId, string | readonly string[] | undefined>>): void;
|
|
89
|
+
/** Test seam: drop every override. */
|
|
90
|
+
export declare function resetKeymapOverrides(): void;
|
|
91
|
+
/** Effective combos for one action (overrides win over defaults). */
|
|
92
|
+
export declare function effectiveCombos(action: ShortcutActionId): readonly ParsedCombo[];
|
|
93
|
+
/** Effective combos as display strings (`ctrl+v, alt+v`). */
|
|
94
|
+
export declare function effectiveComboString(action: ShortcutActionId): string;
|
|
95
|
+
/** Match a keypress against an action's EFFECTIVE combos (platform alias
|
|
96
|
+
* included). This is the one call site Chat/PromptInput should need. */
|
|
97
|
+
export declare function actionMatches(action: ShortcutActionId, input: string, key: ComboKeyFlags): boolean;
|
|
98
|
+
/**
|
|
99
|
+
* Parse user draft text for a settings field: one or more combos separated
|
|
100
|
+
* by commas/semicolons (spaces around separators tolerated). Empty/blank
|
|
101
|
+
* text means "restore the default" ({ combos: [] }); a malformed token makes
|
|
102
|
+
* the whole draft undefined (invalid — the settings screen blocks the save).
|
|
103
|
+
*/
|
|
104
|
+
export declare function parseComboDraft(text: string): {
|
|
105
|
+
combos: string[];
|
|
106
|
+
} | undefined;
|
|
107
|
+
/**
|
|
108
|
+
* Canonical combos the TUI binds but users canNOT remap — interrupt/exit,
|
|
109
|
+
* the readline-style editor keys, the newline variants, and the navigation
|
|
110
|
+
* keys plugins must never shadow. The customizable action combos (see
|
|
111
|
+
* {@link SHORTCUT_ACTIONS}) join this set dynamically wherever both are
|
|
112
|
+
* consulted. Entries the combo grammar itself refuses (bare escape, tab,
|
|
113
|
+
* shift+tab) stay listed verbatim for the lowercase fallback canonical.
|
|
114
|
+
*/
|
|
115
|
+
export declare const FIXED_RESERVED_COMBOS: readonly string[];
|
|
116
|
+
/** The fixed reserved set in canonical form (shared with the adapter registry). */
|
|
117
|
+
export declare function fixedReservedCombos(): ReadonlySet<string>;
|
|
118
|
+
/** Whether a user-spelled combo hits a binding that no remap can free. */
|
|
119
|
+
export declare function isFixedReserved(raw: string): boolean;
|
|
120
|
+
/**
|
|
121
|
+
* Draft-time conflict check for the settings fields: a combo is unusable
|
|
122
|
+
* when it is fixed-reserved, or when some OTHER action currently binds it
|
|
123
|
+
* (its own action's defaults are fine to restate). Without this a remap
|
|
124
|
+
* like history → ctrl+v would silently shadow paste at match time.
|
|
125
|
+
*/
|
|
126
|
+
export declare function draftComboConflicts(action: ShortcutActionId, combos: readonly string[]): boolean;
|
|
127
|
+
/**
|
|
128
|
+
* Canonical combos every customizable action currently binds (defaults ∪
|
|
129
|
+
* overrides) — the `dsh-adapter` plugin registry folds these into its
|
|
130
|
+
* reserved set so a plugin can never shadow a built-in, however the user
|
|
131
|
+
* remapped it.
|
|
132
|
+
*/
|
|
133
|
+
export declare function reservedActionCombos(): ReadonlySet<string>;
|
|
134
|
+
//# sourceMappingURL=keymap.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keymap.d.ts","sourceRoot":"","sources":["../../../src/utils/keymap.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Built-in keyboard-shortcut keymap: the shared combo grammar, the action
|
|
3
|
+
* registry, and the live override cache behind `/settings` customization.
|
|
4
|
+
*
|
|
5
|
+
* Split of concerns across the input stack:
|
|
6
|
+
* - `dsh-adapter/shortcuts.ts` owns the PLUGIN registry (`ctx.tuiShortcuts`)
|
|
7
|
+
* and re-exports the grammar from here so both sides parse identically.
|
|
8
|
+
* - This module owns what the TUI itself binds: a fixed set of named actions
|
|
9
|
+
* (paste, history, editor, …) whose combos resolve as
|
|
10
|
+
* settings.yaml user layer > cordis.yml `shortcuts` > the registry default.
|
|
11
|
+
* `Chat.tsx` / `PromptInput.tsx` match keypresses against the EFFECTIVE
|
|
12
|
+
* combos via {@link actionMatches} — never against a hardcoded string.
|
|
13
|
+
*
|
|
14
|
+
* The macOS modifier alias mirrors `isMod`: a combo spelled with `ctrl`
|
|
15
|
+
* matches Cmd (`super`) on the mac too, so `ctrl+v` keeps meaning "paste
|
|
16
|
+
* with the platform primary modifier" everywhere. `alt` always means the
|
|
17
|
+
* ink `meta` flag. Shift must match exactly (ctrl+shift+v is the terminal's
|
|
18
|
+
* native paste, never the app's clipboard read).
|
|
19
|
+
*/
|
|
20
|
+
import { isMac } from './modifiers.js';
|
|
21
|
+
const NAMED_KEYS = {
|
|
22
|
+
enter: 'return',
|
|
23
|
+
return: 'return',
|
|
24
|
+
esc: 'escape',
|
|
25
|
+
escape: 'escape',
|
|
26
|
+
tab: 'tab',
|
|
27
|
+
backspace: 'backspace',
|
|
28
|
+
delete: 'delete',
|
|
29
|
+
up: 'upArrow',
|
|
30
|
+
down: 'downArrow',
|
|
31
|
+
left: 'leftArrow',
|
|
32
|
+
right: 'rightArrow',
|
|
33
|
+
home: 'home',
|
|
34
|
+
end: 'end',
|
|
35
|
+
pageup: 'pageUp',
|
|
36
|
+
pagedown: 'pageDown',
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* Parse `ctrl+shift+p` style combos. Returns undefined on anything
|
|
40
|
+
* malformed or disallowed (no ctrl/alt modifier, unknown key name, escape
|
|
41
|
+
* combos — the same grammar the plugin shortcut registry enforces).
|
|
42
|
+
*/
|
|
43
|
+
export function parseCombo(raw) {
|
|
44
|
+
const parts = String(raw ?? '')
|
|
45
|
+
.toLowerCase()
|
|
46
|
+
.split('+')
|
|
47
|
+
.map(part => part.trim())
|
|
48
|
+
.filter(part => part !== '');
|
|
49
|
+
if (parts.length === 0)
|
|
50
|
+
return undefined;
|
|
51
|
+
let ctrl = false;
|
|
52
|
+
let meta = false;
|
|
53
|
+
let shift = false;
|
|
54
|
+
let named;
|
|
55
|
+
let char;
|
|
56
|
+
for (const part of parts) {
|
|
57
|
+
if (part === 'ctrl' || part === 'control') {
|
|
58
|
+
if (ctrl)
|
|
59
|
+
return undefined;
|
|
60
|
+
ctrl = true;
|
|
61
|
+
}
|
|
62
|
+
else if (part === 'alt' || part === 'meta' || part === 'option') {
|
|
63
|
+
if (meta)
|
|
64
|
+
return undefined;
|
|
65
|
+
meta = true;
|
|
66
|
+
}
|
|
67
|
+
else if (part === 'shift') {
|
|
68
|
+
if (shift)
|
|
69
|
+
return undefined;
|
|
70
|
+
shift = true;
|
|
71
|
+
}
|
|
72
|
+
else if (part === 'space') {
|
|
73
|
+
if (char !== undefined || named !== undefined)
|
|
74
|
+
return undefined;
|
|
75
|
+
char = ' ';
|
|
76
|
+
}
|
|
77
|
+
else if (part in NAMED_KEYS) {
|
|
78
|
+
if (char !== undefined || named !== undefined)
|
|
79
|
+
return undefined;
|
|
80
|
+
named = NAMED_KEYS[part];
|
|
81
|
+
}
|
|
82
|
+
else if ([...part].length === 1) {
|
|
83
|
+
if (char !== undefined || named !== undefined)
|
|
84
|
+
return undefined;
|
|
85
|
+
char = part;
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
return undefined;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
if (char === undefined && named === undefined)
|
|
92
|
+
return undefined;
|
|
93
|
+
// Bare keys are typing/navigation; a modifier is what makes a shortcut.
|
|
94
|
+
if (!ctrl && !meta)
|
|
95
|
+
return undefined;
|
|
96
|
+
// Escape is fully owned by the TUI (every Escape arrives with meta set on
|
|
97
|
+
// the input layer), so escape combos can never be unambiguous.
|
|
98
|
+
if (named === 'escape')
|
|
99
|
+
return undefined;
|
|
100
|
+
return { raw: parts.join('+'), ctrl, meta, shift, ...(named === undefined ? {} : { named }), ...(char === undefined ? {} : { char }) };
|
|
101
|
+
}
|
|
102
|
+
/** Canonical form for dedupe/reserved checks: modifiers sorted, key last. */
|
|
103
|
+
export function canonicalCombo(combo) {
|
|
104
|
+
const mods = [combo.ctrl ? 'ctrl' : '', combo.meta ? 'alt' : '', combo.shift ? 'shift' : '']
|
|
105
|
+
.filter(part => part !== '')
|
|
106
|
+
.sort();
|
|
107
|
+
return [...mods, combo.named === undefined ? (combo.char ?? '') : String(combo.named)].join('+');
|
|
108
|
+
}
|
|
109
|
+
/** Canonicalize a user-spelled combo string (`ctrl+c`); unparseable input
|
|
110
|
+
* comes back lowercased as-is so reserved sets can still carry entries the
|
|
111
|
+
* grammar itself refuses (bare escape, tab…). */
|
|
112
|
+
export function canonicalComboString(raw) {
|
|
113
|
+
const combo = parseCombo(raw);
|
|
114
|
+
return combo === undefined ? String(raw ?? '').toLowerCase() : canonicalCombo(combo);
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Strict matcher (plugin registry semantics): every modifier flag must equal
|
|
118
|
+
* the combo's. `ctrl` means the ctrl flag only — no platform alias.
|
|
119
|
+
*/
|
|
120
|
+
export function comboMatchesStrict(combo, input, key) {
|
|
121
|
+
if (Boolean(key.ctrl) !== combo.ctrl)
|
|
122
|
+
return false;
|
|
123
|
+
if (Boolean(key.meta) !== combo.meta)
|
|
124
|
+
return false;
|
|
125
|
+
if (key.super)
|
|
126
|
+
return false;
|
|
127
|
+
if (Boolean(key.shift) !== combo.shift)
|
|
128
|
+
return false;
|
|
129
|
+
if (combo.named !== undefined) {
|
|
130
|
+
return key[combo.named] === true;
|
|
131
|
+
}
|
|
132
|
+
if (combo.char === undefined)
|
|
133
|
+
return false;
|
|
134
|
+
return input.toLowerCase() === combo.char;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Built-in matcher with the platform primary-modifier alias: `ctrl` combos
|
|
138
|
+
* also accept Cmd (`super`) on macOS (isMod semantics), `alt` maps to the
|
|
139
|
+
* ink `meta` flag. Shift stays exact — ctrl+shift+v must keep meaning the
|
|
140
|
+
* terminal's native paste, never the app's clipboard read.
|
|
141
|
+
*/
|
|
142
|
+
function comboMatchesBuiltin(combo, input, key) {
|
|
143
|
+
const primary = key.ctrl === true || (isMac && key.super === true);
|
|
144
|
+
if (combo.ctrl !== primary)
|
|
145
|
+
return false;
|
|
146
|
+
if (Boolean(key.meta) !== combo.meta)
|
|
147
|
+
return false;
|
|
148
|
+
if (Boolean(key.shift) !== combo.shift)
|
|
149
|
+
return false;
|
|
150
|
+
if (combo.named !== undefined) {
|
|
151
|
+
return key[combo.named] === true;
|
|
152
|
+
}
|
|
153
|
+
if (combo.char === undefined)
|
|
154
|
+
return false;
|
|
155
|
+
return input.toLowerCase() === combo.char;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* The registry. Defaults encode today's hard-wired bindings — `paste` also
|
|
159
|
+
* carries the `alt+v` alias for terminals that intercept Ctrl+V before the
|
|
160
|
+
* app ever sees the byte (some terminals/IMEs reserve Ctrl+V for their own
|
|
161
|
+
* paste and never forward it in raw mode).
|
|
162
|
+
*/
|
|
163
|
+
export const SHORTCUT_ACTIONS = [
|
|
164
|
+
{ id: 'paste', defaults: ['ctrl+v', 'alt+v'] },
|
|
165
|
+
{ id: 'history', defaults: ['ctrl+r'] },
|
|
166
|
+
{ id: 'editor', defaults: ['ctrl+g'] },
|
|
167
|
+
{ id: 'transcript', defaults: ['ctrl+o'] },
|
|
168
|
+
{ id: 'trajectory', defaults: ['ctrl+t'] },
|
|
169
|
+
{ id: 'dashboard', defaults: ['ctrl+a'] },
|
|
170
|
+
{ id: 'contextPanel', defaults: ['ctrl+p'] },
|
|
171
|
+
{ id: 'showAll', defaults: ['ctrl+e'] },
|
|
172
|
+
{ id: 'redraw', defaults: ['ctrl+l'] },
|
|
173
|
+
{ id: 'todoFold', defaults: ['ctrl+q'] },
|
|
174
|
+
];
|
|
175
|
+
const DEFAULT_COMBO_MAP = new Map(SHORTCUT_ACTIONS.map(action => [action.id, action.defaults.map(parseCombo).filter((combo) => combo !== undefined)]));
|
|
176
|
+
/** Live overrides (settings user layer + cordis.yml merged by plugin.ts). */
|
|
177
|
+
let overrideMap = new Map();
|
|
178
|
+
/**
|
|
179
|
+
* Replace the whole override map. Invalid or blank entries are DROPPED
|
|
180
|
+
* (the action keeps its default) — a typo in cordis.yml must never disable
|
|
181
|
+
* a built-in, and the settings field's parse already refuses invalid
|
|
182
|
+
* drafts before anything is written.
|
|
183
|
+
*/
|
|
184
|
+
export function setKeymapOverrides(overrides) {
|
|
185
|
+
const next = new Map();
|
|
186
|
+
for (const action of SHORTCUT_ACTIONS) {
|
|
187
|
+
const raw = overrides[action.id];
|
|
188
|
+
if (raw === undefined)
|
|
189
|
+
continue;
|
|
190
|
+
const list = (Array.isArray(raw) ? raw : [raw])
|
|
191
|
+
.flatMap(entry => String(entry).split(/[,;]/))
|
|
192
|
+
.map(entry => entry.trim())
|
|
193
|
+
.filter(entry => entry !== '');
|
|
194
|
+
const parsed = list.map(parseCombo).filter((combo) => combo !== undefined);
|
|
195
|
+
if (parsed.length === 0)
|
|
196
|
+
continue;
|
|
197
|
+
next.set(action.id, parsed);
|
|
198
|
+
}
|
|
199
|
+
overrideMap = next;
|
|
200
|
+
}
|
|
201
|
+
/** Test seam: drop every override. */
|
|
202
|
+
export function resetKeymapOverrides() {
|
|
203
|
+
overrideMap = new Map();
|
|
204
|
+
}
|
|
205
|
+
/** Effective combos for one action (overrides win over defaults). */
|
|
206
|
+
export function effectiveCombos(action) {
|
|
207
|
+
return overrideMap.get(action) ?? DEFAULT_COMBO_MAP.get(action) ?? [];
|
|
208
|
+
}
|
|
209
|
+
/** Effective combos as display strings (`ctrl+v, alt+v`). */
|
|
210
|
+
export function effectiveComboString(action) {
|
|
211
|
+
return effectiveCombos(action).map(combo => combo.raw).join(', ');
|
|
212
|
+
}
|
|
213
|
+
/** Match a keypress against an action's EFFECTIVE combos (platform alias
|
|
214
|
+
* included). This is the one call site Chat/PromptInput should need. */
|
|
215
|
+
export function actionMatches(action, input, key) {
|
|
216
|
+
const combos = effectiveCombos(action);
|
|
217
|
+
for (let index = 0; index < combos.length; index += 1) {
|
|
218
|
+
if (comboMatchesBuiltin(combos[index], input, key))
|
|
219
|
+
return true;
|
|
220
|
+
}
|
|
221
|
+
return false;
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Parse user draft text for a settings field: one or more combos separated
|
|
225
|
+
* by commas/semicolons (spaces around separators tolerated). Empty/blank
|
|
226
|
+
* text means "restore the default" ({ combos: [] }); a malformed token makes
|
|
227
|
+
* the whole draft undefined (invalid — the settings screen blocks the save).
|
|
228
|
+
*/
|
|
229
|
+
export function parseComboDraft(text) {
|
|
230
|
+
const trimmed = String(text ?? '').trim();
|
|
231
|
+
if (trimmed === '')
|
|
232
|
+
return { combos: [] };
|
|
233
|
+
const tokens = trimmed
|
|
234
|
+
.split(/[,;]/)
|
|
235
|
+
.map(token => token.trim())
|
|
236
|
+
.filter(token => token !== '');
|
|
237
|
+
if (tokens.length === 0)
|
|
238
|
+
return { combos: [] };
|
|
239
|
+
const combos = [];
|
|
240
|
+
for (const token of tokens) {
|
|
241
|
+
if (parseCombo(token) === undefined)
|
|
242
|
+
return undefined;
|
|
243
|
+
combos.push(token.toLowerCase());
|
|
244
|
+
}
|
|
245
|
+
return { combos };
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Canonical combos the TUI binds but users canNOT remap — interrupt/exit,
|
|
249
|
+
* the readline-style editor keys, the newline variants, and the navigation
|
|
250
|
+
* keys plugins must never shadow. The customizable action combos (see
|
|
251
|
+
* {@link SHORTCUT_ACTIONS}) join this set dynamically wherever both are
|
|
252
|
+
* consulted. Entries the combo grammar itself refuses (bare escape, tab,
|
|
253
|
+
* shift+tab) stay listed verbatim for the lowercase fallback canonical.
|
|
254
|
+
*/
|
|
255
|
+
export const FIXED_RESERVED_COMBOS = [
|
|
256
|
+
'ctrl+c', // interrupt / clear
|
|
257
|
+
'ctrl+d', // exit on empty input
|
|
258
|
+
'ctrl+e', // editor line end (also the showAll action's default)
|
|
259
|
+
'ctrl+a', // editor line start (also the dashboard action's default)
|
|
260
|
+
'ctrl+u', // kill line
|
|
261
|
+
'ctrl+k', // kill to end
|
|
262
|
+
'ctrl+w', // kill word
|
|
263
|
+
'ctrl+left', // word jump
|
|
264
|
+
'ctrl+right', // word jump
|
|
265
|
+
'ctrl+return', // newline (multi-line input)
|
|
266
|
+
'ctrl+shift+return', // shift+Enter newline (CSI 13;6u) — same editor binding
|
|
267
|
+
'alt+return', // newline fallback on terminals without shift reporting
|
|
268
|
+
'alt+up', // pull the last pending message back for editing
|
|
269
|
+
'escape', // pickers / interrupt / rewind double-tap
|
|
270
|
+
'tab', // command completion
|
|
271
|
+
'shift+tab', // session-mode cycle
|
|
272
|
+
];
|
|
273
|
+
const FIXED_RESERVED_CANONICAL = new Set(FIXED_RESERVED_COMBOS.map(canonicalComboString));
|
|
274
|
+
/** The fixed reserved set in canonical form (shared with the adapter registry). */
|
|
275
|
+
export function fixedReservedCombos() {
|
|
276
|
+
return FIXED_RESERVED_CANONICAL;
|
|
277
|
+
}
|
|
278
|
+
/** Whether a user-spelled combo hits a binding that no remap can free. */
|
|
279
|
+
export function isFixedReserved(raw) {
|
|
280
|
+
return FIXED_RESERVED_CANONICAL.has(canonicalComboString(raw));
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Draft-time conflict check for the settings fields: a combo is unusable
|
|
284
|
+
* when it is fixed-reserved, or when some OTHER action currently binds it
|
|
285
|
+
* (its own action's defaults are fine to restate). Without this a remap
|
|
286
|
+
* like history → ctrl+v would silently shadow paste at match time.
|
|
287
|
+
*/
|
|
288
|
+
export function draftComboConflicts(action, combos) {
|
|
289
|
+
const others = new Set();
|
|
290
|
+
for (const spec of SHORTCUT_ACTIONS) {
|
|
291
|
+
if (spec.id === action)
|
|
292
|
+
continue;
|
|
293
|
+
for (const combo of effectiveCombos(spec.id))
|
|
294
|
+
others.add(canonicalCombo(combo));
|
|
295
|
+
}
|
|
296
|
+
return combos.some(combo => isFixedReserved(combo) || others.has(canonicalComboString(combo)));
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Canonical combos every customizable action currently binds (defaults ∪
|
|
300
|
+
* overrides) — the `dsh-adapter` plugin registry folds these into its
|
|
301
|
+
* reserved set so a plugin can never shadow a built-in, however the user
|
|
302
|
+
* remapped it.
|
|
303
|
+
*/
|
|
304
|
+
export function reservedActionCombos() {
|
|
305
|
+
const reserved = new Set();
|
|
306
|
+
for (const action of SHORTCUT_ACTIONS) {
|
|
307
|
+
for (const combo of effectiveCombos(action.id))
|
|
308
|
+
reserved.add(canonicalCombo(combo));
|
|
309
|
+
}
|
|
310
|
+
return reserved;
|
|
311
|
+
}
|