@deepseek-harness-tui/dsh-tui 0.10.2 → 0.11.1
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 +175 -123
- package/bin/dsh-tui.js +748 -22
- package/cordis.patch.yml +42 -13
- package/cordis.yml +5 -2
- package/lib/types/activityPrefs.d.ts +24 -0
- package/lib/types/activityPrefs.d.ts.map +1 -1
- package/lib/types/activityPrefs.js +50 -1
- package/lib/types/adapter/channel/session-projection.d.ts.map +1 -1
- package/lib/types/adapter/channel/session-projection.js +2 -0
- package/lib/types/adapter/channel/state.d.ts.map +1 -1
- package/lib/types/adapter/channel/state.js +3 -1
- package/lib/types/adapter/channel/ui-policy.d.ts +4 -1
- package/lib/types/adapter/channel/ui-policy.d.ts.map +1 -1
- package/lib/types/adapter/channel/ui-policy.js +6 -2
- package/lib/types/adapter/kernel/host-facade.d.ts.map +1 -1
- package/lib/types/adapter/kernel/host-facade.js +1 -0
- package/lib/types/adapter/kernel/replay.d.ts.map +1 -1
- package/lib/types/adapter/kernel/replay.js +3 -0
- package/lib/types/adapter/kernel/runtime.d.ts.map +1 -1
- package/lib/types/adapter/kernel/runtime.js +2 -0
- package/lib/types/adapter/kernel/slices/workspace.d.ts.map +1 -1
- package/lib/types/adapter/kernel/slices/workspace.js +1 -0
- package/lib/types/adapter/ports/channel-ui.d.ts +29 -5
- package/lib/types/adapter/ports/channel-ui.d.ts.map +1 -1
- package/lib/types/adapter/ports/channel-view.d.ts +66 -12
- package/lib/types/adapter/ports/channel-view.d.ts.map +1 -1
- package/lib/types/adapter/ports/channel-workspace.d.ts +23 -0
- package/lib/types/adapter/ports/channel-workspace.d.ts.map +1 -1
- package/lib/types/adapter/ports/channel.d.ts +4 -1
- package/lib/types/adapter/ports/channel.d.ts.map +1 -1
- package/lib/types/adapter/ports/workspace.d.ts +2 -0
- package/lib/types/adapter/ports/workspace.d.ts.map +1 -1
- package/lib/types/adapter/upstream/workspace-driver.d.ts.map +1 -1
- package/lib/types/adapter/upstream/workspace-driver.js +10 -2
- package/lib/types/commands.d.ts.map +1 -1
- package/lib/types/commands.js +1 -0
- package/lib/types/components/ActivityLine.d.ts +7 -2
- package/lib/types/components/ActivityLine.d.ts.map +1 -1
- package/lib/types/components/ActivityLine.js +0 -8
- package/lib/types/components/ContextBarView.d.ts +15 -7
- package/lib/types/components/ContextBarView.d.ts.map +1 -1
- package/lib/types/components/ContextBarView.js +26 -22
- package/lib/types/components/ImagePreviewOverlay.d.ts.map +1 -1
- package/lib/types/components/ImagePreviewOverlay.js +11 -5
- package/lib/types/components/Markdown.d.ts +11 -1
- package/lib/types/components/Markdown.d.ts.map +1 -1
- package/lib/types/components/Markdown.js +20 -2
- package/lib/types/components/MermaidDiagram.d.ts +13 -0
- package/lib/types/components/MermaidDiagram.d.ts.map +1 -0
- package/lib/types/components/MermaidDiagram.js +61 -0
- package/lib/types/components/MessageList.d.ts +26 -1
- package/lib/types/components/MessageList.d.ts.map +1 -1
- package/lib/types/components/MessageList.js +74 -8
- package/lib/types/components/PageMargin.d.ts +1 -1
- package/lib/types/components/PromptInput.d.ts +37 -1
- package/lib/types/components/PromptInput.d.ts.map +1 -1
- package/lib/types/components/PromptInput.js +291 -30
- package/lib/types/components/ScrollbarGutter.d.ts +20 -5
- package/lib/types/components/ScrollbarGutter.d.ts.map +1 -1
- package/lib/types/components/ScrollbarGutter.js +88 -10
- package/lib/types/components/SplitDiffView.d.ts.map +1 -1
- package/lib/types/components/SplitDiffView.js +2 -1
- package/lib/types/components/StreamingMarkdown.d.ts.map +1 -1
- package/lib/types/components/StreamingMarkdown.js +30 -28
- package/lib/types/components/SubagentCard.d.ts.map +1 -1
- package/lib/types/components/SubagentCard.js +12 -6
- package/lib/types/components/SubagentDetailScene.d.ts.map +1 -1
- package/lib/types/components/SubagentDetailScene.js +8 -3
- package/lib/types/components/Tooltip.d.ts.map +1 -1
- package/lib/types/components/Tooltip.js +45 -7
- package/lib/types/components/design-system/HintLine.d.ts +1 -2
- package/lib/types/components/design-system/HintLine.d.ts.map +1 -1
- package/lib/types/components/design-system/HintLine.js +1 -2
- package/lib/types/components/messages/AssistantThinkingMessage.d.ts +2 -1
- package/lib/types/components/messages/AssistantThinkingMessage.d.ts.map +1 -1
- package/lib/types/components/messages/AssistantThinkingMessage.js +2 -1
- package/lib/types/components/messages/AssistantToolUseMessage.d.ts +2 -2
- package/lib/types/components/messages/AssistantToolUseMessage.d.ts.map +1 -1
- package/lib/types/components/messages/AssistantToolUseMessage.js +30 -26
- package/lib/types/components/messages/UserPromptMessage.d.ts.map +1 -1
- package/lib/types/components/messages/UserPromptMessage.js +5 -7
- package/lib/types/components/messages/useImageInspection.d.ts.map +1 -1
- package/lib/types/components/messages/useImageInspection.js +19 -9
- package/lib/types/components/promptDraftCache.d.ts +102 -0
- package/lib/types/components/promptDraftCache.d.ts.map +1 -0
- package/lib/types/components/promptDraftCache.js +76 -0
- package/lib/types/components/questions/AskUserQuestionPanel.d.ts +17 -1
- package/lib/types/components/questions/AskUserQuestionPanel.d.ts.map +1 -1
- package/lib/types/components/questions/AskUserQuestionPanel.js +40 -7
- package/lib/types/components/questions/QuestionMinimizedBar.d.ts +12 -0
- package/lib/types/components/questions/QuestionMinimizedBar.d.ts.map +1 -0
- package/lib/types/components/questions/QuestionMinimizedBar.js +43 -0
- package/lib/types/components/sessions/SessionListRow.d.ts +23 -1
- package/lib/types/components/sessions/SessionListRow.d.ts.map +1 -1
- package/lib/types/components/sessions/SessionListRow.js +52 -5
- package/lib/types/components/workspaces/HomeWorkspaceRow.d.ts +49 -0
- package/lib/types/components/workspaces/HomeWorkspaceRow.d.ts.map +1 -0
- package/lib/types/components/workspaces/HomeWorkspaceRow.js +50 -0
- package/lib/types/dsh-adapter/activity-store.d.ts +221 -0
- package/lib/types/dsh-adapter/activity-store.d.ts.map +1 -0
- package/lib/types/dsh-adapter/activity-store.js +359 -0
- package/lib/types/dsh-adapter/bundled-presets.d.ts +10 -0
- package/lib/types/dsh-adapter/bundled-presets.d.ts.map +1 -0
- package/lib/types/dsh-adapter/bundled-presets.js +80 -0
- package/lib/types/dsh-adapter/channel/action-readiness.d.ts +1 -1
- package/lib/types/dsh-adapter/channel/action-readiness.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/action-readiness.js +3 -0
- package/lib/types/dsh-adapter/channel/agent-view-projection.d.ts +1 -1
- package/lib/types/dsh-adapter/channel/agent-view-projection.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/agent-view-projection.js +19 -2
- package/lib/types/dsh-adapter/channel/background-action.d.ts +1 -1
- package/lib/types/dsh-adapter/channel/background-action.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/background-action.js +53 -29
- package/lib/types/dsh-adapter/channel/binding-events.d.ts +8 -6
- package/lib/types/dsh-adapter/channel/binding-events.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/binding-events.js +5 -4
- package/lib/types/dsh-adapter/channel/binding.d.ts +19 -2
- package/lib/types/dsh-adapter/channel/binding.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/binding.js +78 -18
- package/lib/types/dsh-adapter/channel/compaction.d.ts +0 -1
- package/lib/types/dsh-adapter/channel/compaction.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/compaction.js +0 -2
- package/lib/types/dsh-adapter/channel/composer-images.d.ts +5 -0
- package/lib/types/dsh-adapter/channel/composer-images.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/composer-images.js +105 -6
- package/lib/types/dsh-adapter/channel/context-bookkeeping.d.ts +8 -0
- package/lib/types/dsh-adapter/channel/context-bookkeeping.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/context-bookkeeping.js +3 -2
- package/lib/types/dsh-adapter/channel/ide-selection.d.ts +58 -0
- package/lib/types/dsh-adapter/channel/ide-selection.d.ts.map +1 -0
- package/lib/types/dsh-adapter/channel/ide-selection.js +226 -0
- package/lib/types/dsh-adapter/channel/input-delivery.d.ts +6 -1
- package/lib/types/dsh-adapter/channel/input-delivery.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/input-delivery.js +9 -1
- package/lib/types/dsh-adapter/channel/local-actions.d.ts +3 -19
- package/lib/types/dsh-adapter/channel/local-actions.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/local-actions.js +2 -1
- package/lib/types/dsh-adapter/channel/model-actions.d.ts +4 -1
- package/lib/types/dsh-adapter/channel/model-actions.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/model-actions.js +82 -6
- package/lib/types/dsh-adapter/channel/model-switch.d.ts +1 -2
- package/lib/types/dsh-adapter/channel/model-switch.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/model-switch.js +49 -31
- package/lib/types/dsh-adapter/channel/projection.d.ts +4 -0
- package/lib/types/dsh-adapter/channel/projection.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/projection.js +29 -19
- package/lib/types/dsh-adapter/channel/reports.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/reports.js +4 -12
- package/lib/types/dsh-adapter/channel/session-adoption.d.ts +1 -1
- package/lib/types/dsh-adapter/channel/session-adoption.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/session-adoption.js +1 -0
- package/lib/types/dsh-adapter/channel/session-fork.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/session-fork.js +26 -2
- package/lib/types/dsh-adapter/channel/session-live-adoption.d.ts +4 -1
- package/lib/types/dsh-adapter/channel/session-live-adoption.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/session-live-adoption.js +2 -0
- package/lib/types/dsh-adapter/channel/session-metadata.d.ts +10 -0
- package/lib/types/dsh-adapter/channel/session-metadata.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/session-metadata.js +6 -4
- package/lib/types/dsh-adapter/channel/session-reset.d.ts +1 -1
- package/lib/types/dsh-adapter/channel/session-reset.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/session-reset.js +0 -1
- package/lib/types/dsh-adapter/channel/session-resume.d.ts +12 -3
- package/lib/types/dsh-adapter/channel/session-resume.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/session-resume.js +292 -94
- package/lib/types/dsh-adapter/channel/session-rewind.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/session-rewind.js +34 -16
- package/lib/types/dsh-adapter/channel/session-tree-actions.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/session-tree-actions.js +19 -3
- package/lib/types/dsh-adapter/channel/settings-host.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/settings-host.js +4 -3
- package/lib/types/dsh-adapter/channel/state.d.ts +6 -1
- package/lib/types/dsh-adapter/channel/state.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/state.js +3 -3
- package/lib/types/dsh-adapter/channel/subagent-projection.d.ts +21 -1
- package/lib/types/dsh-adapter/channel/subagent-projection.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/subagent-projection.js +173 -6
- package/lib/types/dsh-adapter/channel/transcript.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/transcript.js +6 -12
- package/lib/types/dsh-adapter/channel/types.d.ts +6 -1
- package/lib/types/dsh-adapter/channel/types.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/workspace-actions.d.ts +4 -1
- package/lib/types/dsh-adapter/channel/workspace-actions.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/workspace-actions.js +41 -1
- package/lib/types/dsh-adapter/channel.d.ts +1 -1
- package/lib/types/dsh-adapter/channel.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel.js +82 -20
- package/lib/types/dsh-adapter/compat/index.d.ts +1 -1
- package/lib/types/dsh-adapter/compat/index.d.ts.map +1 -1
- package/lib/types/dsh-adapter/compat/index.js +1 -1
- package/lib/types/dsh-adapter/compat/messages.d.ts +15 -0
- package/lib/types/dsh-adapter/compat/messages.d.ts.map +1 -0
- package/lib/types/dsh-adapter/compat/messages.js +15 -0
- package/lib/types/dsh-adapter/compat/sessionLog.d.ts +18 -0
- package/lib/types/dsh-adapter/compat/sessionLog.d.ts.map +1 -1
- package/lib/types/dsh-adapter/compat/sessionLog.js +18 -3
- package/lib/types/dsh-adapter/compat/settings.d.ts +35 -0
- package/lib/types/dsh-adapter/compat/settings.d.ts.map +1 -0
- package/lib/types/dsh-adapter/compat/settings.js +57 -0
- package/lib/types/dsh-adapter/compat/shell.d.ts +25 -0
- package/lib/types/dsh-adapter/compat/shell.d.ts.map +1 -0
- package/lib/types/dsh-adapter/compat/shell.js +11 -0
- package/lib/types/dsh-adapter/contract.d.ts +5 -4
- package/lib/types/dsh-adapter/contract.d.ts.map +1 -1
- package/lib/types/dsh-adapter/contract.js +17 -6
- package/lib/types/dsh-adapter/ide-channel.d.ts +224 -0
- package/lib/types/dsh-adapter/ide-channel.d.ts.map +1 -0
- package/lib/types/dsh-adapter/ide-channel.js +556 -0
- package/lib/types/dsh-adapter/index.d.ts +18 -3
- package/lib/types/dsh-adapter/index.d.ts.map +1 -1
- package/lib/types/dsh-adapter/index.js +32 -5
- package/lib/types/dsh-adapter/plugin.d.ts +4 -1
- package/lib/types/dsh-adapter/plugin.d.ts.map +1 -1
- package/lib/types/dsh-adapter/plugin.js +197 -49
- package/lib/types/dsh-adapter/preset-resolution.d.ts +8 -0
- package/lib/types/dsh-adapter/preset-resolution.d.ts.map +1 -1
- package/lib/types/dsh-adapter/preset-resolution.js +10 -0
- package/lib/types/dsh-adapter/session-mount-heartbeat.d.ts +53 -0
- package/lib/types/dsh-adapter/session-mount-heartbeat.d.ts.map +1 -0
- package/lib/types/dsh-adapter/session-mount-heartbeat.js +100 -0
- package/lib/types/dsh-adapter/sessionTree.d.ts.map +1 -1
- package/lib/types/dsh-adapter/sessionTree.js +3 -2
- package/lib/types/dsh-adapter/settings-sections.d.ts.map +1 -1
- package/lib/types/dsh-adapter/settings-sections.js +7 -4
- package/lib/types/dsh-adapter/subagents.d.ts +16 -3
- package/lib/types/dsh-adapter/subagents.d.ts.map +1 -1
- package/lib/types/dsh-adapter/subagents.js +92 -29
- package/lib/types/dsh-adapter/workspaces.d.ts +46 -3
- package/lib/types/dsh-adapter/workspaces.d.ts.map +1 -1
- package/lib/types/dsh-adapter/workspaces.js +116 -0
- package/lib/types/effortPrefs.d.ts +15 -3
- package/lib/types/effortPrefs.d.ts.map +1 -1
- package/lib/types/effortPrefs.js +37 -3
- package/lib/types/history.d.ts +13 -1
- package/lib/types/history.d.ts.map +1 -1
- package/lib/types/history.js +15 -2
- package/lib/types/homePrefs.d.ts +15 -0
- package/lib/types/homePrefs.d.ts.map +1 -0
- package/lib/types/homePrefs.js +53 -0
- package/lib/types/i18n.d.ts +347 -297
- package/lib/types/i18n.d.ts.map +1 -1
- package/lib/types/i18n.js +133 -85
- package/lib/types/ink/colorize.d.ts +10 -0
- package/lib/types/ink/colorize.d.ts.map +1 -1
- package/lib/types/ink/colorize.js +21 -0
- package/lib/types/ink/components/ScrollBox.d.ts.map +1 -1
- package/lib/types/ink/components/ScrollBox.js +12 -1
- package/lib/types/ink/dom.d.ts +1 -0
- package/lib/types/ink/dom.d.ts.map +1 -1
- package/lib/types/ink/hooks/use-copy-on-select.d.ts +5 -1
- package/lib/types/ink/hooks/use-copy-on-select.d.ts.map +1 -1
- package/lib/types/ink/hooks/use-copy-on-select.js +15 -1
- package/lib/types/ink/ink.d.ts.map +1 -1
- package/lib/types/ink/ink.js +36 -1
- package/lib/types/ink/log-update.d.ts.map +1 -1
- package/lib/types/ink/log-update.js +19 -1
- package/lib/types/ink/output.d.ts +10 -1
- package/lib/types/ink/output.d.ts.map +1 -1
- package/lib/types/ink/output.js +109 -17
- package/lib/types/ink/render-border.d.ts +6 -1
- package/lib/types/ink/render-border.d.ts.map +1 -1
- package/lib/types/ink/render-border.js +25 -9
- package/lib/types/ink/render-node-to-output.d.ts.map +1 -1
- package/lib/types/ink/render-node-to-output.js +48 -6
- package/lib/types/ink/renderer.d.ts +6 -0
- package/lib/types/ink/renderer.d.ts.map +1 -1
- package/lib/types/ink/renderer.js +2 -1
- package/lib/types/ink/selection.d.ts +88 -0
- package/lib/types/ink/selection.d.ts.map +1 -1
- package/lib/types/ink/selection.js +164 -0
- package/lib/types/ink/sixel-graphics.d.ts +19 -1
- package/lib/types/ink/sixel-graphics.d.ts.map +1 -1
- package/lib/types/ink/sixel-graphics.js +124 -12
- package/lib/types/ink/terminal-image.d.ts +18 -1
- package/lib/types/ink/terminal-image.d.ts.map +1 -1
- package/lib/types/ink/terminal.d.ts +17 -0
- package/lib/types/ink/terminal.d.ts.map +1 -1
- package/lib/types/ink/terminal.js +20 -1
- package/lib/types/screens/Chat.d.ts +20 -1
- package/lib/types/screens/Chat.d.ts.map +1 -1
- package/lib/types/screens/Chat.js +296 -76
- package/lib/types/screens/SessionSupervisor.d.ts +84 -0
- package/lib/types/screens/SessionSupervisor.d.ts.map +1 -0
- package/lib/types/screens/SessionSupervisor.js +350 -0
- package/lib/types/screens/SessionTree.js +2 -2
- package/lib/types/screens/Settings.d.ts.map +1 -1
- package/lib/types/screens/Settings.js +32 -9
- package/lib/types/screens/StatusLine.d.ts +18 -1
- package/lib/types/screens/StatusLine.d.ts.map +1 -1
- package/lib/types/screens/StatusLine.js +38 -19
- package/lib/types/screens/StatusMetrics.d.ts +79 -22
- package/lib/types/screens/StatusMetrics.d.ts.map +1 -1
- package/lib/types/screens/StatusMetrics.js +153 -88
- package/lib/types/screens/sessionSupervisor/model.d.ts +83 -0
- package/lib/types/screens/sessionSupervisor/model.d.ts.map +1 -0
- package/lib/types/screens/sessionSupervisor/model.js +69 -0
- package/lib/types/screens/sessionSupervisor/useSessionSupervisor.d.ts +120 -0
- package/lib/types/screens/sessionSupervisor/useSessionSupervisor.d.ts.map +1 -0
- package/lib/types/screens/sessionSupervisor/useSessionSupervisor.js +593 -0
- package/lib/types/sessionMounts.d.ts +272 -0
- package/lib/types/sessionMounts.d.ts.map +1 -0
- package/lib/types/sessionMounts.js +706 -0
- package/lib/types/sessions/format.d.ts.map +1 -1
- package/lib/types/sessions/format.js +4 -1
- package/lib/types/sessions/resumeFailure.d.ts +38 -0
- package/lib/types/sessions/resumeFailure.d.ts.map +1 -0
- package/lib/types/sessions/resumeFailure.js +58 -0
- package/lib/types/terminal-utils/mermaid.d.ts +44 -0
- package/lib/types/terminal-utils/mermaid.d.ts.map +1 -0
- package/lib/types/terminal-utils/mermaid.js +83 -0
- package/lib/types/tips.d.ts.map +1 -1
- package/lib/types/tips.js +175 -19
- package/lib/types/tuiDisplayPrefs.d.ts +7 -9
- package/lib/types/tuiDisplayPrefs.d.ts.map +1 -1
- package/lib/types/tuiDisplayPrefs.js +31 -28
- package/lib/types/update.d.ts +61 -0
- package/lib/types/update.d.ts.map +1 -1
- package/lib/types/update.js +194 -0
- package/lib/types/utils/fold-long-lines.d.ts +1 -1
- package/lib/types/utils/fold-long-lines.js +1 -1
- package/lib/types/utils/imageResize.d.ts +90 -0
- package/lib/types/utils/imageResize.d.ts.map +1 -0
- package/lib/types/utils/imageResize.js +272 -0
- package/lib/types/utils/keymap.d.ts +5 -1
- package/lib/types/utils/keymap.d.ts.map +1 -1
- package/lib/types/utils/keymap.js +23 -0
- package/lib/types/working-activity.d.ts +10 -9
- package/lib/types/working-activity.d.ts.map +1 -1
- package/lib/types/working-activity.js +17 -10
- package/lib/types/workspaces.d.ts +2 -1
- package/lib/types/workspaces.d.ts.map +1 -1
- package/lib/types/workspaces.js +1 -1
- package/node_modules/@deepseek-harness-tui/dsh-auth/package.json +10 -10
- package/package.json +126 -105
- package/presets/liangshen/.dsh-tui-managed.json +1 -1
- package/presets/liangshen/agent.cordis.yml +15 -0
- package/lib/types/components/design-system/KeyboardShortcutHint.d.ts +0 -19
- package/lib/types/components/design-system/KeyboardShortcutHint.d.ts.map +0 -1
- package/lib/types/components/design-system/KeyboardShortcutHint.js +0 -14
- package/lib/types/dsh-adapter/channel/activity.d.ts +0 -20
- package/lib/types/dsh-adapter/channel/activity.d.ts.map +0 -1
- package/lib/types/dsh-adapter/channel/activity.js +0 -113
- package/lib/types/screens/AgentView.d.ts +0 -23
- package/lib/types/screens/AgentView.d.ts.map +0 -1
- package/lib/types/screens/AgentView.js +0 -546
- package/lib/types/screens/SessionBrowser.d.ts +0 -42
- package/lib/types/screens/SessionBrowser.d.ts.map +0 -1
- package/lib/types/screens/SessionBrowser.js +0 -911
- package/lib/types/terminal-utils/terminal.d.ts +0 -18
- package/lib/types/terminal-utils/terminal.d.ts.map +0 -1
- package/lib/types/terminal-utils/terminal.js +0 -96
|
@@ -24,7 +24,7 @@ import { imagePathMediaType, parsePastedImagePath, stageClipboardFilePaths } fro
|
|
|
24
24
|
import { editInExternalEditor } from '../utils/externalEditor.js';
|
|
25
25
|
import { setPromptEditorNode, EditorButton } from './PromptEditor.js';
|
|
26
26
|
import { isHiddenCommandName, parseCommandName } from '../commands.js';
|
|
27
|
-
import { appendHistory } from '../history.js';
|
|
27
|
+
import { appendHistory, HISTORY_LIMIT, loadHistoryOldestFirst } from '../history.js';
|
|
28
28
|
import { mentionAtCaret } from '../utils/mentions.js';
|
|
29
29
|
import { preserveSelection } from '../utils/fileSuggestions.js';
|
|
30
30
|
import { isMod } from '../utils/modifiers.js';
|
|
@@ -34,7 +34,25 @@ import { FileSuggestions } from './FileSuggestions.js';
|
|
|
34
34
|
import { HelpMenu } from './HelpMenu.js';
|
|
35
35
|
import { OverlayAbove } from './OverlayAbove.js';
|
|
36
36
|
import { SuggestionCard, cardContentWidth } from './SuggestionCard.js';
|
|
37
|
-
|
|
37
|
+
import { filterLiveImageBindings, isUsableDraftSnapshot, resolveBindingGeneration, } from './promptDraftCache.js';
|
|
38
|
+
/**
|
|
39
|
+
* Visible text of the session-entry control at the head of the input row:
|
|
40
|
+
* U+2338 APL FUNCTIONAL SYMBOL QUAD COLON plus a separator space.
|
|
41
|
+
*
|
|
42
|
+
* The glyph is chosen for ONE property above all others: the layout model and
|
|
43
|
+
* the terminal cell table must agree on how many columns it occupies. `\u2338`
|
|
44
|
+
* is a plain single-cell symbol with no emoji presentation, so
|
|
45
|
+
* `stringWidth` and the terminal both say 1 + 1 = 2 and
|
|
46
|
+
* {@link HOME_BUTTON_COLS} can be derived rather than guessed.
|
|
47
|
+
*
|
|
48
|
+
* The emoji house (`U+1F3E0`) was tried and rejected on this exact ground: it
|
|
49
|
+
* costs the terminal ONE cell while `stringWidth` charges the row TWO, and
|
|
50
|
+
* appending VARIATION SELECTOR-15 (text presentation) does not change either
|
|
51
|
+
* number. That standing one-column disagreement put every caret-relative
|
|
52
|
+
* column in the composer off by one, so no offset compensation made it work.
|
|
53
|
+
*/
|
|
54
|
+
const HOME_BUTTON_TEXT = '⌸ ';
|
|
55
|
+
const HOME_BUTTON_COLS = stringWidth(HOME_BUTTON_TEXT);
|
|
38
56
|
/**
|
|
39
57
|
* Paste fold with a visible preview and no black box:
|
|
40
58
|
* a paste that leaves the input this big folds into a one-line chip
|
|
@@ -67,6 +85,9 @@ function sanitizeEditableText(text) {
|
|
|
67
85
|
.replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/gu, '');
|
|
68
86
|
}
|
|
69
87
|
const COMPOSER_IMAGE_TOKEN = /\[Image #\d+\]/gu;
|
|
88
|
+
/** Format label for one image media type, matching the image preview card's
|
|
89
|
+
* title (`JPEG`, `PNG`, `WEBP`, `GIF`). */
|
|
90
|
+
const mediaTypeLabel = (mediaType) => mediaType.replace(/^image\//u, '').replace(/\+xml$/u, '').toUpperCase();
|
|
70
91
|
/** Every `[Image #N]` in `text`, in order. */
|
|
71
92
|
function imageTokenSpans(text) {
|
|
72
93
|
const spans = [];
|
|
@@ -352,13 +373,70 @@ const DOUBLE_CLICK_MS = 500;
|
|
|
352
373
|
* working) interrupts the turn and delivers them right away; Ctrl+Enter
|
|
353
374
|
* aborts the turn and sends the current input immediately.
|
|
354
375
|
*/
|
|
355
|
-
export function PromptInput({ channel, suspended = false, helpOpen, onToggleHelp, onRunCommand, selectionActive, fillText, onFillConsumed, onRewindRequest, onBackgroundRequest, backgroundAgentsNeedingInput, controllerRef, onCaretImage, caretPreviewOpen = false, onDismissCaretPreview, }) {
|
|
376
|
+
export function PromptInput({ channel, suspended = false, draftCache, helpOpen, onToggleHelp, onRunCommand, selectionActive, fillText, onFillConsumed, onRewindRequest, onBackgroundRequest, onOpenSessions, backgroundAgentsNeedingInput, controllerRef, onCaretImage, caretPreviewOpen = false, onDismissCaretPreview, }) {
|
|
356
377
|
const [themeName] = useTheme();
|
|
357
378
|
// Raw stdout writer for OSC 52 clipboard writes (selection copy) — must
|
|
358
379
|
// bypass the frame pipeline; null outside a mounted Ink App.
|
|
359
380
|
const writeRaw = React.useContext(TerminalWriteContext);
|
|
381
|
+
// The composer owns its text in local state, so it starts EMPTY and adopts
|
|
382
|
+
// whatever draft the owner stored in a MOUNT-TIME effect below. Reading the
|
|
383
|
+
// store in this initializer instead looks equivalent and is not: a non-empty
|
|
384
|
+
// first frame leaves `repro-resume-position` red — resuming a session parks
|
|
385
|
+
// the transcript mid-history instead of pinning it to the newest message.
|
|
386
|
+
// Adopting after mount keeps the first frame identical to a fresh composer
|
|
387
|
+
// while still handing the draft back.
|
|
360
388
|
const [value, setValue] = React.useState('');
|
|
361
389
|
const [cursor, setCursor] = React.useState(0);
|
|
390
|
+
/**
|
|
391
|
+
* Adopt the owner's draft ONCE, after mount.
|
|
392
|
+
*
|
|
393
|
+
* This is where a screen swap gives the draft back: the slot outlives this
|
|
394
|
+
* component, so a remount picks up what the user had written — text, caret,
|
|
395
|
+
* the image bindings behind the visible `[Image #N]` tokens, and the edit
|
|
396
|
+
* state around them (fold chip, fullscreen editor, vim mode).
|
|
397
|
+
*
|
|
398
|
+
* It runs as an effect rather than as the `useState` initial value on
|
|
399
|
+
* purpose. A composer whose FIRST frame is already non-empty moves the
|
|
400
|
+
* transcript's restored scroll position (`repro-resume-position`); adopting
|
|
401
|
+
* after the first commit keeps that frame identical to a fresh composer. The
|
|
402
|
+
* slot is consumed here, and the composer never writes to it while mounted,
|
|
403
|
+
* so the empty first value cannot overwrite the draft before this reads it.
|
|
404
|
+
*/
|
|
405
|
+
const adoptDraft = React.useRef(true);
|
|
406
|
+
React.useEffect(() => {
|
|
407
|
+
if (draftCache === undefined || !adoptDraft.current)
|
|
408
|
+
return;
|
|
409
|
+
adoptDraft.current = false;
|
|
410
|
+
const snapshot = draftCache.current;
|
|
411
|
+
draftCache.current = null;
|
|
412
|
+
if (!isUsableDraftSnapshot(snapshot, String(channel.agentId), resolveBindingGeneration(channel)))
|
|
413
|
+
return;
|
|
414
|
+
const text = snapshot.value;
|
|
415
|
+
// Carried edit state, restored ahead of the text: the chip, the
|
|
416
|
+
// fullscreen editor and the vim mode/submode come back with the draft —
|
|
417
|
+
// and they come back even with NO text, because they are modes rather
|
|
418
|
+
// than content. Fold ranges are safe by the CAPTURE invariant, not by
|
|
419
|
+
// restore order: a snapshot's block, when set, always sits inside the
|
|
420
|
+
// snapshot's own text (setInput keeps or drops it atomically), and at
|
|
421
|
+
// mount the caret is 0 so updateFoldBlock's caret-drag clamp cannot fire
|
|
422
|
+
// here. The transient state around them — editor scroll, vim undo stack,
|
|
423
|
+
// selection — does not come back.
|
|
424
|
+
updateFoldBlock(snapshot.foldBlock);
|
|
425
|
+
expandedRef.current = snapshot.expanded;
|
|
426
|
+
setExpanded(snapshot.expanded);
|
|
427
|
+
vimEnabledRef.current = snapshot.vimEnabled;
|
|
428
|
+
setVimEnabled(snapshot.vimEnabled);
|
|
429
|
+
vimInsertRef.current = snapshot.vimInsert;
|
|
430
|
+
setVimInsert(snapshot.vimInsert);
|
|
431
|
+
if (text === '')
|
|
432
|
+
return;
|
|
433
|
+
const restoredCursor = normalizeCursorOffset(text, snapshot.cursor);
|
|
434
|
+
replaceDraftImages(filterLiveImageBindings(snapshot.images, stageId => channel.hasStagedImage?.(stageId) === true).map(([token, stageId]) => ({ token, stageId })));
|
|
435
|
+
valueRef.current = text;
|
|
436
|
+
cursorRef.current = restoredCursor;
|
|
437
|
+
setValue(text);
|
|
438
|
+
setCursor(restoredCursor);
|
|
439
|
+
}, [draftCache]);
|
|
362
440
|
/**
|
|
363
441
|
* Mouse text selection: UTF-16 offsets [start, end) in `value`, snapped
|
|
364
442
|
* to grapheme boundaries, start ≤ end. Null = no selection. Created by
|
|
@@ -403,7 +481,7 @@ export function PromptInput({ channel, suspended = false, helpOpen, onToggleHelp
|
|
|
403
481
|
const foldBlockRef = React.useRef(null);
|
|
404
482
|
/**
|
|
405
483
|
* Fullscreen draft editor (`expandEditor`, default Ctrl+Shift+E, or the
|
|
406
|
-
*
|
|
484
|
+
* ✎ affordance at the end of the input row). While expanded the SAME
|
|
407
485
|
* editing state renders into the PromptEditorLayer cover (published via
|
|
408
486
|
* setPromptEditorNode each render): Enter inserts a newline, Ctrl+Enter
|
|
409
487
|
* submits, Esc collapses. The fold chip is bypassed (full text shown).
|
|
@@ -424,14 +502,16 @@ export function PromptInput({ channel, suspended = false, helpOpen, onToggleHelp
|
|
|
424
502
|
const prevExpandedRef = React.useRef(false);
|
|
425
503
|
/**
|
|
426
504
|
* Feature gate (settings `dsh-tui.expandEditor`, on by default; a mock
|
|
427
|
-
* channel without the field also reads as on). Off hides the
|
|
505
|
+
* channel without the field also reads as on). Off hides the ✎
|
|
428
506
|
* affordance and refuses the shortcut — the editor cannot open.
|
|
429
507
|
*/
|
|
430
508
|
const expandEnabled = channel.expandEditor !== false;
|
|
431
509
|
/** Latest expanded viewport metrics for the useInput wheel branch. */
|
|
432
510
|
const editorViewportRef = React.useRef(null);
|
|
433
|
-
/** Hover state of the
|
|
511
|
+
/** Hover state of the ⤢/✎ expand affordance in the input row. */
|
|
434
512
|
const [expandHovered, setExpandHovered] = React.useState(false);
|
|
513
|
+
/** Hover state of the ⌂ session-list affordance at the head of the row. */
|
|
514
|
+
const [homeHovered, setHomeHovered] = React.useState(false);
|
|
435
515
|
/** Pointer over the input box (drives the hover peek card). */
|
|
436
516
|
const [hovered, setHovered] = React.useState(false);
|
|
437
517
|
/** 120ms grace so the pointer crossing the input border row from the
|
|
@@ -454,6 +534,8 @@ export function PromptInput({ channel, suspended = false, helpOpen, onToggleHelp
|
|
|
454
534
|
const history = React.useRef([]);
|
|
455
535
|
const historyIndex = React.useRef(-1);
|
|
456
536
|
const historyDraft = React.useRef({ text: '', images: [] });
|
|
537
|
+
/** The persisted history is read lazily, once per mount (see seedHistory). */
|
|
538
|
+
const historySeeded = React.useRef(false);
|
|
457
539
|
/** Visible `[Image #N]` labels are presentation only; this sidecar carries
|
|
458
540
|
* the non-reusable capability for the current draft. History/rewind text
|
|
459
541
|
* restored without this map can never bind to a later image by accident. */
|
|
@@ -550,6 +632,10 @@ export function PromptInput({ channel, suspended = false, helpOpen, onToggleHelp
|
|
|
550
632
|
syncImageGeneration();
|
|
551
633
|
valueRef.current = value;
|
|
552
634
|
cursorRef.current = cursor;
|
|
635
|
+
// Nothing is written to the owner's draft slot here — see the unmount
|
|
636
|
+
// hand-off below. A commit-time write would run BEFORE the restore effect
|
|
637
|
+
// has consumed the slot, so the composer's own first (empty) value would
|
|
638
|
+
// erase the draft it just came back for.
|
|
553
639
|
// Publish the live controller (fresh closure over `value` every render).
|
|
554
640
|
// A prompt-slot panel withdraws the handle in the same commit: external
|
|
555
641
|
// injection must not append/submit a hidden command draft while it waits
|
|
@@ -563,6 +649,7 @@ export function PromptInput({ channel, suspended = false, helpOpen, onToggleHelp
|
|
|
563
649
|
}
|
|
564
650
|
controllerRef.current = {
|
|
565
651
|
hasText: () => value.length > 0,
|
|
652
|
+
text: () => valueRef.current,
|
|
566
653
|
previewImages: () => composerImageRefsForText(valueRef.current, draftImagesRef.current).flatMap(ref => {
|
|
567
654
|
const image = channel.stagedImage(ref.stageId);
|
|
568
655
|
return image === undefined ? [] : [{ image, title: ref.token.slice(1, -1) }];
|
|
@@ -660,9 +747,56 @@ export function PromptInput({ channel, suspended = false, helpOpen, onToggleHelp
|
|
|
660
747
|
clearTimeout(escTimerRef.current);
|
|
661
748
|
if (hoverLeaveTimerRef.current)
|
|
662
749
|
clearTimeout(hoverLeaveTimerRef.current);
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
750
|
+
/**
|
|
751
|
+
* Hand the draft to the owner's slot as this component goes away.
|
|
752
|
+
*
|
|
753
|
+
* This is the ONLY write to that slot, and it belongs here: it happens at
|
|
754
|
+
* the instant a screen replaces the composer, so it can neither race the
|
|
755
|
+
* restore effect nor change the transcript's first frame.
|
|
756
|
+
*
|
|
757
|
+
* `advanceDraftRevision` invalidates any image read/stage still in
|
|
758
|
+
* flight, so a late continuation cannot bind a capability into a composer
|
|
759
|
+
* that no longer exists. `clearVimUndo` releases the capabilities that
|
|
760
|
+
* only the undo stack was holding: they are not part of the draft, and
|
|
761
|
+
* nothing will ever restore them once this composer is gone.
|
|
762
|
+
*
|
|
763
|
+
* The stageIds the DRAFT holds are deliberately NOT revoked — they are
|
|
764
|
+
* part of the snapshot the slot now keeps, and the restore filters out
|
|
765
|
+
* whatever the channel revoked in the meantime. Revoking them here is why
|
|
766
|
+
* an image draft used to come back as inert text.
|
|
767
|
+
*/
|
|
768
|
+
advanceDraftRevision();
|
|
769
|
+
clearVimUndo();
|
|
770
|
+
const images = [...draftImagesRef.current.entries()]
|
|
771
|
+
.map(([token, stageId]) => [token, stageId]);
|
|
772
|
+
draftImagesRef.current.clear();
|
|
773
|
+
if (draftCache === undefined) {
|
|
774
|
+
discardUnretainedImages(images.map(image => image[1]));
|
|
775
|
+
return;
|
|
776
|
+
}
|
|
777
|
+
const text = valueRef.current;
|
|
778
|
+
// The edit state rides along even with nothing typed: vim mode and the
|
|
779
|
+
// fullscreen editor are MODES the user turned on, not content, and an
|
|
780
|
+
// empty composer must not drop them (`text === ''` alone used to skip
|
|
781
|
+
// the snapshot entirely, losing the vim badge on a round trip with an
|
|
782
|
+
// empty composer).
|
|
783
|
+
const editState = foldBlockRef.current !== null || expandedRef.current
|
|
784
|
+
|| vimEnabledRef.current;
|
|
785
|
+
if (text === '' && images.length === 0 && !editState) {
|
|
786
|
+
draftCache.current = null;
|
|
787
|
+
return;
|
|
788
|
+
}
|
|
789
|
+
draftCache.current = {
|
|
790
|
+
ownerAgentId: String(channel.agentId),
|
|
791
|
+
bindingGeneration: resolveBindingGeneration(channel),
|
|
792
|
+
value: text,
|
|
793
|
+
cursor: cursorRef.current,
|
|
794
|
+
foldBlock: foldBlockRef.current,
|
|
795
|
+
expanded: expandedRef.current,
|
|
796
|
+
vimEnabled: vimEnabledRef.current,
|
|
797
|
+
vimInsert: vimInsertRef.current,
|
|
798
|
+
images,
|
|
799
|
+
};
|
|
666
800
|
};
|
|
667
801
|
}, []);
|
|
668
802
|
const { columns, rows: terminalRows } = useTerminalSize();
|
|
@@ -768,14 +902,17 @@ export function PromptInput({ channel, suspended = false, helpOpen, onToggleHelp
|
|
|
768
902
|
onCaretImageRef.current = onCaretImage;
|
|
769
903
|
const lastCaretImageRef = React.useRef({ image: undefined, title: undefined });
|
|
770
904
|
/** Tell the caller which staged image the caret is on. `'caret'` reports
|
|
771
|
-
* only changes; `'click'` always reports (see onCaretImage).
|
|
905
|
+
* only changes; `'click'` always reports (see onCaretImage). "Same image"
|
|
906
|
+
* is the attachment id plus the token, not facade identity: this runs
|
|
907
|
+
* after every commit and the caller stores what it reports, so an
|
|
908
|
+
* identity-only difference would re-render the caller forever (#885). */
|
|
772
909
|
const reportCaretImage = (reason) => {
|
|
773
910
|
const report = onCaretImageRef.current;
|
|
774
911
|
if (report === undefined)
|
|
775
912
|
return;
|
|
776
913
|
const found = suspended ? undefined : caretImageAt(valueRef.current, cursorRef.current);
|
|
777
914
|
const last = lastCaretImageRef.current;
|
|
778
|
-
if (reason === 'caret' && last.image === found?.image && last.title === found?.title)
|
|
915
|
+
if (reason === 'caret' && last.image?.id === found?.image.id && last.title === found?.title)
|
|
779
916
|
return;
|
|
780
917
|
lastCaretImageRef.current = { image: found?.image, title: found?.title };
|
|
781
918
|
report(found?.image, found?.title, reason);
|
|
@@ -960,7 +1097,26 @@ export function PromptInput({ channel, suspended = false, helpOpen, onToggleHelp
|
|
|
960
1097
|
return;
|
|
961
1098
|
}
|
|
962
1099
|
};
|
|
1100
|
+
/**
|
|
1101
|
+
* Seed the walk with the persisted history (issue #986). `↑`/`↓` used to
|
|
1102
|
+
* see only what this process submitted, so a restart lost every earlier
|
|
1103
|
+
* entry. Seeding before the first push keeps ONE chronological list —
|
|
1104
|
+
* persisted entries first, this run's submits behind them — instead of two
|
|
1105
|
+
* lists to merge at recall time. Restored text carries no image capability
|
|
1106
|
+
* (the file stores text only), which is also what keeps a recalled entry
|
|
1107
|
+
* from binding to a later staged image by accident.
|
|
1108
|
+
*/
|
|
1109
|
+
const seedHistory = () => {
|
|
1110
|
+
if (historySeeded.current)
|
|
1111
|
+
return;
|
|
1112
|
+
historySeeded.current = true;
|
|
1113
|
+
history.current = loadHistoryOldestFirst().map(entry => ({ text: entry.text, images: [] }));
|
|
1114
|
+
historyIndex.current = -1;
|
|
1115
|
+
};
|
|
963
1116
|
const rememberHistory = (text, images) => {
|
|
1117
|
+
// Both entries into the walk (a submit and ↑) must see the persisted
|
|
1118
|
+
// prefix, so seed here rather than merging two lists later.
|
|
1119
|
+
seedHistory();
|
|
964
1120
|
history.current.push({
|
|
965
1121
|
text,
|
|
966
1122
|
images: images.map(image => ({ ...image })),
|
|
@@ -1067,6 +1223,30 @@ export function PromptInput({ channel, suspended = false, helpOpen, onToggleHelp
|
|
|
1067
1223
|
setFileSelected(0);
|
|
1068
1224
|
channel.notify(t('input-retracted'), { timeoutMs: 2000 });
|
|
1069
1225
|
};
|
|
1226
|
+
/**
|
|
1227
|
+
* Withdraw the queued copy of the text `↑` just recalled (issue #986): the
|
|
1228
|
+
* message is still parked in the inbox, so editing it and sending again
|
|
1229
|
+
* would run the same text twice. Alt+Up withdraws explicitly; walking the
|
|
1230
|
+
* history to the same text has to land in the same place. The newest match
|
|
1231
|
+
* wins — `↑` walks newest-first and the queue is FIFO. A message the
|
|
1232
|
+
* running turn already claimed cannot be withdrawn, and saying so beats
|
|
1233
|
+
* pretending it was.
|
|
1234
|
+
*/
|
|
1235
|
+
const retractRecalledCopy = (text) => {
|
|
1236
|
+
let target;
|
|
1237
|
+
for (const item of channel.pending) {
|
|
1238
|
+
if (item.text === text)
|
|
1239
|
+
target = item;
|
|
1240
|
+
}
|
|
1241
|
+
if (target === undefined)
|
|
1242
|
+
return;
|
|
1243
|
+
if (channel.removePending(target.id)) {
|
|
1244
|
+
channel.notify(t('input-retracted'), { timeoutMs: 2000 });
|
|
1245
|
+
}
|
|
1246
|
+
else {
|
|
1247
|
+
channel.notify(t('input-cannot-retract'), { color: 'warning', timeoutMs: 2500 });
|
|
1248
|
+
}
|
|
1249
|
+
};
|
|
1070
1250
|
/**
|
|
1071
1251
|
* Ctrl+Enter: abort the running turn and send the input immediately — the
|
|
1072
1252
|
* model stops what it is doing and starts on this message right away.
|
|
@@ -1231,7 +1411,7 @@ export function PromptInput({ channel, suspended = false, helpOpen, onToggleHelp
|
|
|
1231
1411
|
if (!tryRunCommand(value))
|
|
1232
1412
|
submitText(value);
|
|
1233
1413
|
};
|
|
1234
|
-
/** Expand/collapse the fullscreen editor (shortcut +
|
|
1414
|
+
/** Expand/collapse the fullscreen editor (shortcut + ✎ affordance).
|
|
1235
1415
|
* Expanding also DROPS any fold block: the fullscreen view shows and
|
|
1236
1416
|
* edits the full text, and the block's atomic-clamp semantics (caret
|
|
1237
1417
|
* pushed to its edges, selection clamped to one side) would contradict
|
|
@@ -1324,6 +1504,35 @@ export function PromptInput({ channel, suspended = false, helpOpen, onToggleHelp
|
|
|
1324
1504
|
imageStageChainRef.current = queued.then(() => undefined, () => undefined);
|
|
1325
1505
|
return queued;
|
|
1326
1506
|
};
|
|
1507
|
+
/** Success notice for one staged image. An adapted paste says what the user
|
|
1508
|
+
* actually got (stored dimensions, stored format, filled alpha) — naming it
|
|
1509
|
+
* is the difference between a reported adaptation and a silently rewritten
|
|
1510
|
+
* image. */
|
|
1511
|
+
const stagedImageNotice = (token, handle) => {
|
|
1512
|
+
const adjustment = handle.adjustment;
|
|
1513
|
+
if (adjustment === undefined)
|
|
1514
|
+
return t('input-image-pasted', { token });
|
|
1515
|
+
const details = [];
|
|
1516
|
+
if (adjustment.resized) {
|
|
1517
|
+
details.push(t('input-image-detail-resized', {
|
|
1518
|
+
width: adjustment.width,
|
|
1519
|
+
height: adjustment.height,
|
|
1520
|
+
}));
|
|
1521
|
+
}
|
|
1522
|
+
if (adjustment.mediaType !== adjustment.sourceMediaType) {
|
|
1523
|
+
const from = mediaTypeLabel(adjustment.sourceMediaType);
|
|
1524
|
+
const to = mediaTypeLabel(adjustment.mediaType);
|
|
1525
|
+
details.push(adjustment.flattened
|
|
1526
|
+
? t('input-image-detail-converted-flattened', { from, to })
|
|
1527
|
+
: t('input-image-detail-converted', { from, to }));
|
|
1528
|
+
}
|
|
1529
|
+
// An adjustment always describes a resize or a conversion; anything else
|
|
1530
|
+
// keeps the plain notice rather than an empty parenthetical. ' · ' joins
|
|
1531
|
+
// the clauses in both shipped languages.
|
|
1532
|
+
if (details.length === 0)
|
|
1533
|
+
return t('input-image-pasted', { token });
|
|
1534
|
+
return t('input-image-pasted-adjusted', { token, detail: details.join(' · ') });
|
|
1535
|
+
};
|
|
1327
1536
|
const discardStagedHandles = (handles) => {
|
|
1328
1537
|
for (const stageId of new Set(handles.map(handle => handle.stageId))) {
|
|
1329
1538
|
channel.discardStagedImage(stageId);
|
|
@@ -1411,7 +1620,7 @@ export function PromptInput({ channel, suspended = false, helpOpen, onToggleHelp
|
|
|
1411
1620
|
setFileSelected(0);
|
|
1412
1621
|
return;
|
|
1413
1622
|
}
|
|
1414
|
-
// ── 全屏草稿编辑(expandEditor,默认 Ctrl+Shift+E / 输入行
|
|
1623
|
+
// ── 全屏草稿编辑(expandEditor,默认 Ctrl+Shift+E / 输入行 ✎)─────
|
|
1415
1624
|
// 展开态拥有屏幕;Esc 收起(有选区时上面的 selection 分支已先行只清
|
|
1416
1625
|
// 选区)。滚轮不经此——编辑区的 onWheel 位置路由直接驱动滚动窗口。
|
|
1417
1626
|
if (key.escape && expandedRef.current) {
|
|
@@ -1497,7 +1706,7 @@ export function PromptInput({ channel, suspended = false, helpOpen, onToggleHelp
|
|
|
1497
1706
|
// Bind and insert share this synchronous continuation: setInput's
|
|
1498
1707
|
// sidecar pruning can never observe a bound-but-not-visible token.
|
|
1499
1708
|
insertClipboardAtCaret(`${token} `);
|
|
1500
|
-
channel.notify(
|
|
1709
|
+
channel.notify(stagedImageNotice(token, handle), { timeoutMs: 2500 });
|
|
1501
1710
|
})
|
|
1502
1711
|
.catch(() => {
|
|
1503
1712
|
if (!draftImageLeaseIsCurrent(lease))
|
|
@@ -1561,7 +1770,7 @@ export function PromptInput({ channel, suspended = false, helpOpen, onToggleHelp
|
|
|
1561
1770
|
const handle = await stageImagePath(content.path, lease);
|
|
1562
1771
|
const token = bindStagedImage(handle, lease);
|
|
1563
1772
|
insertClipboardAtCaret(`${token} `);
|
|
1564
|
-
channel.notify(
|
|
1773
|
+
channel.notify(stagedImageNotice(token, handle), { timeoutMs: 2500 });
|
|
1565
1774
|
});
|
|
1566
1775
|
}
|
|
1567
1776
|
catch (error) {
|
|
@@ -1605,9 +1814,14 @@ export function PromptInput({ channel, suspended = false, helpOpen, onToggleHelp
|
|
|
1605
1814
|
// All bindings and their visible labels enter together;
|
|
1606
1815
|
// typing while an earlier file saves cannot prune one.
|
|
1607
1816
|
insertClipboardAtCaret(`${rendered.join(' ')} `);
|
|
1817
|
+
// A batch cannot itemise every image in one line, but it must
|
|
1818
|
+
// still say that some of them were not stored as pasted.
|
|
1819
|
+
const adapted = staged.filter(handle => handle.adjustment !== undefined).length;
|
|
1608
1820
|
channel.notify(boundTokens.length === 1
|
|
1609
|
-
?
|
|
1610
|
-
:
|
|
1821
|
+
? stagedImageNotice(boundTokens[0], staged[0])
|
|
1822
|
+
: adapted > 0
|
|
1823
|
+
? t('input-images-staged-adapted', { count: boundTokens.length, adapted })
|
|
1824
|
+
: t('input-images-staged', { count: boundTokens.length }), { timeoutMs: 2500 });
|
|
1611
1825
|
return true;
|
|
1612
1826
|
}
|
|
1613
1827
|
catch (error) {
|
|
@@ -1916,6 +2130,7 @@ export function PromptInput({ channel, suspended = false, helpOpen, onToggleHelp
|
|
|
1916
2130
|
setSelectedCommand(index => index <= 0 ? suggestions.length - 1 : index - 1);
|
|
1917
2131
|
return;
|
|
1918
2132
|
}
|
|
2133
|
+
seedHistory();
|
|
1919
2134
|
if (history.current.length === 0)
|
|
1920
2135
|
return;
|
|
1921
2136
|
if (historyIndex.current < 0) {
|
|
@@ -1931,6 +2146,7 @@ export function PromptInput({ channel, suspended = false, helpOpen, onToggleHelp
|
|
|
1931
2146
|
const entry = history.current[historyIndex.current];
|
|
1932
2147
|
if (entry === undefined)
|
|
1933
2148
|
return;
|
|
2149
|
+
retractRecalledCopy(entry.text);
|
|
1934
2150
|
updateFoldBlock(null);
|
|
1935
2151
|
restoreDraftImages(entry);
|
|
1936
2152
|
setInput(entry.text);
|
|
@@ -2463,6 +2679,11 @@ export function PromptInput({ channel, suspended = false, helpOpen, onToggleHelp
|
|
|
2463
2679
|
// the same row, so the wrap budget must shrink by its width or long lines
|
|
2464
2680
|
// would be clipped at the value box's right edge.
|
|
2465
2681
|
const vimBadgeCols = vimEnabled ? 7 : 0;
|
|
2682
|
+
// 行首会话入口占列:未传 onOpenSessions 时按钮整体不渲染,预算同步归还
|
|
2683
|
+
// (既有调用方的可用列数逐列不变)。用实测常量而不是 stringWidth:该字符
|
|
2684
|
+
// 是 U+1F3E0+VS15,stringWidth 按码点算 2(含 VS15 仍是 2),终端只给 1,
|
|
2685
|
+
// 见 HOME_BUTTON_COLS 的说明。
|
|
2686
|
+
const homeButtonCols = onOpenSessions === undefined ? 0 : HOME_BUTTON_COLS;
|
|
2466
2687
|
// 补全卡片边框与输入框 idle 边框同色(plan 模式下整套面板一起变 sage
|
|
2467
2688
|
// 绿)。`/color` 会话强调色优先于主题 promptBorder(plan 模式仍整体走
|
|
2468
2689
|
// sage 绿)。`?? ''` 防御最小 mock channel(只声明用到的字段的回归脚
|
|
@@ -2470,14 +2691,15 @@ export function PromptInput({ channel, suspended = false, helpOpen, onToggleHelp
|
|
|
2470
2691
|
const sessionAccent = sessionColorHex(channel.sessionColor ?? '');
|
|
2471
2692
|
const promptAccent = channel.mode.plan === true ? 'planMode' : (sessionAccent ?? 'promptBorder');
|
|
2472
2693
|
// 展开态布局参数:编辑器独占整屏 —— 行号槽(宽度随逻辑行数伸缩)+
|
|
2473
|
-
// 圆角边框 2 + 两侧 padding 各 1
|
|
2474
|
-
//
|
|
2694
|
+
// 圆角边框 2 + 两侧 padding 各 1 占列,⌸ 入口 / ❯ 前缀 / vim 徽标 /
|
|
2695
|
+
// ⛶ 按钮全部让位;收起态额外扣掉行首 ⌸ 入口(未渲染时 0 列)与行尾
|
|
2696
|
+
// ⛶ 按钮的 2 列。
|
|
2475
2697
|
const editorLogicalLines = expanded ? value.split('\n').length : 1;
|
|
2476
2698
|
const editorNoWidth = Math.max(2, String(editorLogicalLines).length);
|
|
2477
2699
|
const editorGutterCols = editorNoWidth + 3;
|
|
2478
2700
|
const inputWidth = expanded
|
|
2479
2701
|
? Math.max(1, columns - 4 - editorGutterCols)
|
|
2480
|
-
: Math.max(1, columns - 3 - vimBadgeCols - (expandEnabled ? 2 : 0));
|
|
2702
|
+
: Math.max(1, columns - 3 - vimBadgeCols - homeButtonCols - (expandEnabled ? 2 : 0));
|
|
2481
2703
|
// 展开态无视折叠块:全屏编辑就是为了看全文(foldBlock 状态保留,
|
|
2482
2704
|
// 收起后折叠显示恢复)。
|
|
2483
2705
|
const block = expanded ? null : foldBlock;
|
|
@@ -2546,22 +2768,50 @@ export function PromptInput({ channel, suspended = false, helpOpen, onToggleHelp
|
|
|
2546
2768
|
visibleCount = MAX_VISIBLE_LINES;
|
|
2547
2769
|
}
|
|
2548
2770
|
const visibleLines = visualLines.slice(windowStart, windowStart + visibleCount);
|
|
2771
|
+
// Caret-window jump: when the caret leaves MAX_VISIBLE_LINES (typing past
|
|
2772
|
+
// it, arrow-key walks through a long prompt) the whole visible band is
|
|
2773
|
+
// replaced in place — same-height rows, completely different text. The
|
|
2774
|
+
// per-cell diff that repaints that band is exactly the path that goes
|
|
2775
|
+
// haywire when the inline viewport is even one row out of sync with the
|
|
2776
|
+
// terminal's scrollback ("重叠变花": old rows bleeding through the new
|
|
2777
|
+
// ones). Same one-shot reanchor family as the shrink/floaters/editor
|
|
2778
|
+
// patches above — the window jump is a viewport-level discontinuity, not
|
|
2779
|
+
// an ordinary content edit.
|
|
2780
|
+
const prevWindowStartRef = React.useRef(windowStart);
|
|
2781
|
+
React.useLayoutEffect(() => {
|
|
2782
|
+
if (windowStart !== prevWindowStartRef.current) {
|
|
2783
|
+
prevWindowStartRef.current = windowStart;
|
|
2784
|
+
const ink = instances.get(process.stdout) ?? instances.values().next().value;
|
|
2785
|
+
ink?.invalidatePrevFrame();
|
|
2786
|
+
ink?.reanchorViewport();
|
|
2787
|
+
}
|
|
2788
|
+
}, [windowStart]);
|
|
2549
2789
|
// useInput 的滚轮分支需要这份几何(它在这些派生之前注册)。
|
|
2550
2790
|
if (expanded) {
|
|
2551
2791
|
editorViewportRef.current = { maxRows: editorMaxRows, total: visualLines.length };
|
|
2552
2792
|
}
|
|
2553
2793
|
// Folded chip content: block stats + first-line preview + hover hint,
|
|
2554
2794
|
// all pre-truncated to the input width (the row is one line, always).
|
|
2795
|
+
// `·` separators are U+30FB KATAKANA MIDDLE DOT, NOT U+00B7: the latter
|
|
2796
|
+
// is East-Asian-ambiguous (model 1 cell, CJK terminal fonts paint 2) and
|
|
2797
|
+
// this row's stats→preview→hint truncation arithmetic all runs through
|
|
2798
|
+
// stringWidth — a painted-wide separator shifts every segment right by
|
|
2799
|
+
// one column per separator and the row reads as overlapping text.
|
|
2800
|
+
// U+30FB is officially Ambiguous too, but get-east-asian-width hardcodes
|
|
2801
|
+
// it Wide and mainstream terminals (Western included) paint it 2 cells —
|
|
2802
|
+
// the model and the painted width agree in practice. A wcwidth-strict
|
|
2803
|
+
// Western terminal painting it 1 cell would leave a spare column, which
|
|
2804
|
+
// the `- 6` slack below absorbs.
|
|
2555
2805
|
const foldBadge = `▸ ${stats}`;
|
|
2556
2806
|
const foldHint = t('input-fold-hover');
|
|
2557
|
-
const foldPreviewWidth = inputWidth - stringWidth(foldBadge) - stringWidth(
|
|
2807
|
+
const foldPreviewWidth = inputWidth - stringWidth(foldBadge) - stringWidth(`・${foldHint}`) - 6;
|
|
2558
2808
|
const foldPreview = foldPreviewWidth >= 8
|
|
2559
2809
|
? truncateToWidth(foldText.split('\n')[0] ?? '', foldPreviewWidth)
|
|
2560
2810
|
: '';
|
|
2561
2811
|
// Expanded-state fold affordance: a `▾` prefix at the start of the FIRST
|
|
2562
2812
|
// row (only while the window is at the top and no block exists); its
|
|
2563
2813
|
// cells fold the whole input into a block again on click.
|
|
2564
|
-
const prefixLabel = `▾ ${stats}
|
|
2814
|
+
const prefixLabel = `▾ ${stats}・`;
|
|
2565
2815
|
const prefixCols = !block && !expanded && big && windowStart === 0 ? stringWidth(prefixLabel) : 0;
|
|
2566
2816
|
// Offset range [start, end) of every visual row — the SAME break rule as
|
|
2567
2817
|
// wrapToWidth — so the selection highlight can intersect each row. With a
|
|
@@ -2639,7 +2889,7 @@ export function PromptInput({ channel, suspended = false, helpOpen, onToggleHelp
|
|
|
2639
2889
|
return (_jsxs(Box, { flexDirection: "row", onClick: (event) => {
|
|
2640
2890
|
event.stopImmediatePropagation();
|
|
2641
2891
|
updateFoldBlock(null);
|
|
2642
|
-
}, onMouseEnter: hoverEnter, onMouseLeave: hoverLeave, children: [_jsx(Text, { dimColor: true, children: foldBadge }), foldPreview !== '' && _jsx(Text, { dimColor: true, children: "
|
|
2892
|
+
}, onMouseEnter: hoverEnter, onMouseLeave: hoverLeave, children: [_jsx(Text, { dimColor: true, children: foldBadge }), foldPreview !== '' && _jsx(Text, { dimColor: true, children: "\u30FB" }), foldPreview !== '' && _jsx(Text, { wrap: "truncate-end", children: foldPreview }), _jsx(Text, { dimColor: true, children: `・${foldHint}` })] }, `fold-${absoluteLine}`));
|
|
2643
2893
|
}
|
|
2644
2894
|
const withPrefix = absoluteLine === 0 && prefixCols > 0;
|
|
2645
2895
|
const text = withPrefix ? truncateToWidth(line, inputWidth - prefixCols) : line;
|
|
@@ -2723,12 +2973,16 @@ export function PromptInput({ channel, suspended = false, helpOpen, onToggleHelp
|
|
|
2723
2973
|
// In the expanded editor the declared box starts at its outer edge
|
|
2724
2974
|
// (padding + gutter included), so those columns ride along and the
|
|
2725
2975
|
// clamp grows with them.
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2976
|
+
//
|
|
2977
|
+
// The declared column is relative to the VALUE BOX, and `caretVisualCol`
|
|
2978
|
+
// is already measured in that box's cell space: the text box IS the text
|
|
2979
|
+
// run, so nothing that renders before it — the session entry, the `❯ `
|
|
2980
|
+
// glyph, the fold prefix — shifts a cell inside it. Adding any of those
|
|
2981
|
+
// back double-counts them and parks the hardware cursor to the RIGHT of
|
|
2982
|
+
// the inverted caret cell (a 2-column miss shows up as a displaced IME
|
|
2983
|
+
// preedit target). Only the expanded editor declares against a wider box
|
|
2984
|
+
// (gutter + padding), so only that branch adds columns.
|
|
2985
|
+
column: Math.min(caretVisualCol + (expanded ? editorGutterCols + 1 : 0), expanded ? editorGutterCols + 1 + inputWidth : inputWidth),
|
|
2732
2986
|
active: !suspended && !selectionActive,
|
|
2733
2987
|
});
|
|
2734
2988
|
/**
|
|
@@ -3063,7 +3317,14 @@ export function PromptInput({ channel, suspended = false, helpOpen, onToggleHelp
|
|
|
3063
3317
|
: undefined, rows: peekVisualLines.map((row, index) => (_jsx(Text, { wrap: "truncate-end", children: row }, index))), onRowPick: () => {
|
|
3064
3318
|
updateFoldBlock(null);
|
|
3065
3319
|
setHovered(false);
|
|
3066
|
-
} }) }))] })), lastNotification && (_jsx(Box, { position: "absolute", marginTop: -1, height: 1, width: "100%", paddingLeft: 2, paddingRight: 1, flexDirection: "column", justifyContent: "flex-end", overflow: "hidden", children: _jsx(Box, { justifyContent: "flex-end", children: _jsx(Text, { color: lastNotification.color, dimColor: !lastNotification.color, wrap: "truncate", children: lastNotification.text }) }) })), _jsx(EffortInputBorder, { effort: channel.reasoningEffort, levels: channel.effortLevels, columns: columns, onLight: isLightThemeActive(themeName), idleColor: promptAccent, topRightLabel: topRightLabel, children: _jsxs(Box, { flexDirection: "row", alignItems: "flex-start", width: "100%", children: [
|
|
3320
|
+
} }) }))] })), lastNotification && (_jsx(Box, { position: "absolute", marginTop: -1, height: 1, width: "100%", paddingLeft: 2, paddingRight: 1, flexDirection: "column", justifyContent: "flex-end", overflow: "hidden", children: _jsx(Box, { justifyContent: "flex-end", children: _jsx(Text, { color: lastNotification.color, dimColor: !lastNotification.color, wrap: "truncate", children: lastNotification.text }) }) })), _jsx(EffortInputBorder, { effort: channel.reasoningEffort, levels: channel.effortLevels, columns: columns, onLight: isLightThemeActive(themeName), idleColor: promptAccent, topRightLabel: topRightLabel, children: _jsxs(Box, { flexDirection: "row", alignItems: "flex-start", width: "100%", children: [onOpenSessions !== undefined && (_jsx(Box, { flexShrink: 0, onClick: (event) => {
|
|
3321
|
+
event.stopImmediatePropagation();
|
|
3322
|
+
onOpenSessions?.();
|
|
3323
|
+
}, onMouseEnter: () => {
|
|
3324
|
+
setHomeHovered(true);
|
|
3325
|
+
}, onMouseLeave: () => {
|
|
3326
|
+
setHomeHovered(false);
|
|
3327
|
+
}, children: _jsx(Text, { dimColor: !homeHovered, bold: homeHovered, color: homeHovered ? promptAccent : undefined, children: HOME_BUTTON_TEXT }) })), _jsx(EffortChargeGlyph, { effort: channel.reasoningEffort, levels: channel.effortLevels, working: channel.working }), vimEnabled && (_jsxs(Text, { bold: true, color: vimInsert ? 'success' : 'warning', children: [vimInsert ? 'INSERT' : 'NORMAL', " "] })), _jsx(Box, { ref: expanded ? undefined : valueBoxRef, flexGrow: 1, flexShrink: 1, onClick: handleValueClick, onDragStart: handleDragStart, onDragMove: handleDragMove, onDragEnd: handleDragEnd, children: value.length === 0 ? (_jsxs(_Fragment, { children: [_jsx(Text, { inverse: true, children: " " }), _jsx(EffortTierBadge, { effort: channel.reasoningEffort, levels: channel.effortLevels, onLight: isLightThemeActive(themeName), columns: columns, leadingColumns: 3 + homeButtonCols })] })) : (_jsx(Box, { flexDirection: "column", children: rendered })) }), expandEnabled && (_jsx(Box, { flexShrink: 0, onClick: (event) => {
|
|
3067
3328
|
event.stopImmediatePropagation();
|
|
3068
3329
|
toggleExpand();
|
|
3069
3330
|
}, onMouseEnter: () => {
|
|
@@ -10,16 +10,31 @@ import { type ScrollBoxHandle } from '../ui.js';
|
|
|
10
10
|
* - clicking the track scrolls the clicked position to the viewport top
|
|
11
11
|
* (classic scrollbar semantics — the thumb centers under the click
|
|
12
12
|
* through the follow-up renders);
|
|
13
|
+
* - dragging the track scrubs the transcript: the drag protocol's
|
|
14
|
+
* per-target localRow maps through the SAME trackScrollTop as a click
|
|
15
|
+
* (absolute mapping — drag to point, no grabbed thumb offset), so a
|
|
16
|
+
* drag is a continuous run of click-to-position jumps. The mapping only
|
|
17
|
+
* reads the render-time maxScroll/trackH and never re-reads scrollTop
|
|
18
|
+
* mid-gesture, so a throttled/clamped scrollTo cannot feed back into the
|
|
19
|
+
* pointer mapping (no jitter). Dragging the thumb therefore lands the
|
|
20
|
+
* pointer's row at the viewport top, exactly like clicking that row;
|
|
21
|
+
* - a landing (drag or the back-to-bottom affordance) breaks and re-pins
|
|
22
|
+
* sticky, which unmounts the chrome that had shrunk the transcript row;
|
|
23
|
+
* the re-pinning Ink pass follows the commit and fires no subscriber
|
|
24
|
+
* notify, so the gutter re-reads the handle once that frame lands (see
|
|
25
|
+
* the subscribe effect). Without it the thumb stays painted from the
|
|
26
|
+
* shrunken viewport and floats a few rows above the bottom until an
|
|
27
|
+
* unrelated render (hover) re-reads the handle;
|
|
13
28
|
* - the gutter is permanent while scrollable (Qwen's rule: an
|
|
14
29
|
* auto-hiding gutter that changes content width rewraps everything);
|
|
15
30
|
* hidden below 60 terminal columns or when the content fits (inline
|
|
16
31
|
* mode keeps the terminal's native scrollback);
|
|
17
32
|
* - NoSelect fences the glyphs out of click-drag text selection; the
|
|
18
|
-
* wheel over the gutter scrolls the transcript.
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
33
|
+
* wheel over the gutter scrolls the transcript. Because the track is a
|
|
34
|
+
* drag target, an unmodified left drag scrubs instead of selecting, and
|
|
35
|
+
* multi-clicks on the track no longer feed the select-line chain;
|
|
36
|
+
* Shift/Alt/Ctrl drags never open a drag session and keep the
|
|
37
|
+
* selection path.
|
|
23
38
|
*/
|
|
24
39
|
export declare function ScrollbarGutter({ handle, terminalWidth, }: {
|
|
25
40
|
handle: ScrollBoxHandle | null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ScrollbarGutter.d.ts","sourceRoot":"","sources":["../../../src/components/ScrollbarGutter.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AACzB,OAAO,EAAuB,KAAK,eAAe,EAAE,MAAM,UAAU,CAAA;
|
|
1
|
+
{"version":3,"file":"ScrollbarGutter.d.ts","sourceRoot":"","sources":["../../../src/components/ScrollbarGutter.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AACzB,OAAO,EAAuB,KAAK,eAAe,EAAE,MAAM,UAAU,CAAA;AAqBpE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,wBAAgB,eAAe,CAAC,EAC9B,MAAM,EACN,aAAa,GACd,EAAE;IACD,MAAM,EAAE,eAAe,GAAG,IAAI,CAAA;IAC9B,aAAa,EAAE,MAAM,CAAA;CACtB,GAAG,KAAK,CAAC,SAAS,CA6LlB"}
|