@deepseek-harness-tui/dsh-tui 0.11.1 → 0.11.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 +50 -20
- package/assets/whale-girl/whale-girl-dark.png +0 -0
- package/assets/whale-girl/whale-girl-happy.png +0 -0
- package/assets/whale-girl/whale-girl.png +0 -0
- package/bin/dsh-tui.js +63 -3
- package/cordis.patch.yml +39 -17
- package/cordis.yml +3 -2
- package/lib/settings.json +1272 -0
- package/lib/types/adapter/channel/ui-policy.d.ts +9 -2
- package/lib/types/adapter/channel/ui-policy.d.ts.map +1 -1
- package/lib/types/adapter/channel/ui-policy.js +13 -0
- package/lib/types/adapter/kernel/replay.d.ts.map +1 -1
- package/lib/types/adapter/kernel/replay.js +2 -0
- package/lib/types/adapter/ports/channel-display.d.ts +12 -0
- package/lib/types/adapter/ports/channel-display.d.ts.map +1 -1
- package/lib/types/adapter/ports/channel-session.d.ts +38 -0
- package/lib/types/adapter/ports/channel-session.d.ts.map +1 -1
- package/lib/types/adapter/ports/channel-settings.d.ts +12 -0
- package/lib/types/adapter/ports/channel-settings.d.ts.map +1 -1
- package/lib/types/adapter/ports/channel-ui.d.ts +46 -4
- package/lib/types/adapter/ports/channel-ui.d.ts.map +1 -1
- package/lib/types/adapter/ports/channel-view.d.ts +43 -0
- package/lib/types/adapter/ports/channel-view.d.ts.map +1 -1
- package/lib/types/commands.d.ts.map +1 -1
- package/lib/types/commands.js +2 -0
- package/lib/types/components/BalanceReportRow.d.ts +10 -2
- package/lib/types/components/BalanceReportRow.d.ts.map +1 -1
- package/lib/types/components/BalanceReportRow.js +18 -4
- package/lib/types/components/CompactionStatusRow.d.ts +24 -0
- package/lib/types/components/CompactionStatusRow.d.ts.map +1 -0
- package/lib/types/components/CompactionStatusRow.js +72 -0
- package/lib/types/components/GoalTodoPanel.js +2 -2
- package/lib/types/components/HelpMenu.d.ts +2 -2
- package/lib/types/components/HelpMenu.d.ts.map +1 -1
- package/lib/types/components/HelpMenu.js +5 -4
- package/lib/types/components/InlineMathParagraph.d.ts +22 -0
- package/lib/types/components/InlineMathParagraph.d.ts.map +1 -0
- package/lib/types/components/InlineMathParagraph.js +178 -0
- package/lib/types/components/LogoV2.d.ts +41 -3
- package/lib/types/components/LogoV2.d.ts.map +1 -1
- package/lib/types/components/LogoV2.js +148 -24
- package/lib/types/components/Markdown.d.ts +10 -3
- package/lib/types/components/Markdown.d.ts.map +1 -1
- package/lib/types/components/Markdown.js +47 -14
- package/lib/types/components/MathBlock.d.ts +13 -0
- package/lib/types/components/MathBlock.d.ts.map +1 -0
- package/lib/types/components/MathBlock.js +136 -0
- package/lib/types/components/MessageList.d.ts +10 -1
- package/lib/types/components/MessageList.d.ts.map +1 -1
- package/lib/types/components/MessageList.js +13 -4
- package/lib/types/components/MigratePicker.d.ts +30 -0
- package/lib/types/components/MigratePicker.d.ts.map +1 -0
- package/lib/types/components/MigratePicker.js +38 -0
- package/lib/types/components/PromptInput.d.ts +17 -0
- package/lib/types/components/PromptInput.d.ts.map +1 -1
- package/lib/types/components/PromptInput.js +48 -7
- package/lib/types/components/SplitDiffView.d.ts.map +1 -1
- package/lib/types/components/SplitDiffView.js +2 -1
- package/lib/types/components/StarPrompt.d.ts +53 -0
- package/lib/types/components/StarPrompt.d.ts.map +1 -0
- package/lib/types/components/StarPrompt.js +281 -0
- package/lib/types/components/StreamingMarkdown.d.ts.map +1 -1
- package/lib/types/components/StreamingMarkdown.js +20 -3
- package/lib/types/components/TimelineRail.d.ts.map +1 -1
- package/lib/types/components/TimelineRail.js +9 -1
- package/lib/types/components/Whale.d.ts +17 -8
- package/lib/types/components/Whale.d.ts.map +1 -1
- package/lib/types/components/Whale.js +25 -15
- package/lib/types/components/WhaleGirl.d.ts +29 -0
- package/lib/types/components/WhaleGirl.d.ts.map +1 -0
- package/lib/types/components/WhaleGirl.js +32 -0
- package/lib/types/components/WorkingSpinner.d.ts +4 -1
- package/lib/types/components/WorkingSpinner.d.ts.map +1 -1
- package/lib/types/components/WorkingSpinner.js +2 -2
- package/lib/types/components/bigfont.d.ts +47 -18
- package/lib/types/components/bigfont.d.ts.map +1 -1
- package/lib/types/components/bigfont.js +55 -40
- package/lib/types/components/design-system/ThemeProvider.d.ts +6 -0
- package/lib/types/components/design-system/ThemeProvider.d.ts.map +1 -1
- package/lib/types/components/design-system/ThemeProvider.js +40 -1
- package/lib/types/components/maidPortrait.d.ts +44 -0
- package/lib/types/components/maidPortrait.d.ts.map +1 -0
- package/lib/types/components/maidPortrait.js +178 -0
- package/lib/types/components/mathPreview.d.ts +25 -0
- package/lib/types/components/mathPreview.d.ts.map +1 -0
- package/lib/types/components/mathPreview.js +48 -0
- 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.map +1 -1
- package/lib/types/components/messages/AssistantToolUseMessage.js +3 -2
- package/lib/types/components/messages/TranscriptImages.d.ts.map +1 -1
- package/lib/types/components/messages/TranscriptImages.js +11 -1
- package/lib/types/components/questions/AskUserQuestionPanel.d.ts +14 -2
- package/lib/types/components/questions/AskUserQuestionPanel.d.ts.map +1 -1
- package/lib/types/components/questions/AskUserQuestionPanel.js +80 -38
- package/lib/types/components/questions/PlanReviewPanel.d.ts.map +1 -1
- package/lib/types/components/questions/PlanReviewPanel.js +3 -3
- package/lib/types/components/sessions/ForeignSessionRow.d.ts +34 -0
- package/lib/types/components/sessions/ForeignSessionRow.d.ts.map +1 -0
- package/lib/types/components/sessions/ForeignSessionRow.js +43 -0
- package/lib/types/components/sessions/SourceTabs.d.ts +52 -0
- package/lib/types/components/sessions/SourceTabs.d.ts.map +1 -0
- package/lib/types/components/sessions/SourceTabs.js +70 -0
- package/lib/types/components/splashEggs.d.ts +56 -0
- package/lib/types/components/splashEggs.d.ts.map +1 -0
- package/lib/types/components/splashEggs.js +67 -0
- package/lib/types/components/splashFonts.d.ts +119 -0
- package/lib/types/components/splashFonts.d.ts.map +1 -0
- package/lib/types/components/splashFonts.js +267 -0
- package/lib/types/components/splashLayout.d.ts +41 -0
- package/lib/types/components/splashLayout.d.ts.map +1 -0
- package/lib/types/components/splashLayout.js +40 -0
- package/lib/types/components/whaleGirlSprite.d.ts +17 -0
- package/lib/types/components/whaleGirlSprite.d.ts.map +1 -0
- package/lib/types/components/whaleGirlSprite.js +47 -0
- package/lib/types/deepseekPricing.d.ts +74 -0
- package/lib/types/deepseekPricing.d.ts.map +1 -1
- package/lib/types/deepseekPricing.js +110 -11
- 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 +6 -1
- package/lib/types/dsh-adapter/channel/background-action.d.ts +1 -0
- package/lib/types/dsh-adapter/channel/background-action.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/background-action.js +1 -0
- package/lib/types/dsh-adapter/channel/binding-events.d.ts +8 -9
- package/lib/types/dsh-adapter/channel/binding-events.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/binding-events.js +69 -21
- package/lib/types/dsh-adapter/channel/compaction.d.ts +2 -1
- package/lib/types/dsh-adapter/channel/compaction.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/compaction.js +35 -2
- package/lib/types/dsh-adapter/channel/local-actions.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/local-actions.js +6 -1
- package/lib/types/dsh-adapter/channel/model-switch.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/model-switch.js +5 -1
- package/lib/types/dsh-adapter/channel/paths.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/paths.js +7 -1
- package/lib/types/dsh-adapter/channel/preferences.d.ts +1 -1
- package/lib/types/dsh-adapter/channel/preferences.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/preferences.js +16 -0
- package/lib/types/dsh-adapter/channel/projection.d.ts +1 -2
- package/lib/types/dsh-adapter/channel/projection.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/projection.js +127 -17
- package/lib/types/dsh-adapter/channel/question-record.d.ts +76 -0
- package/lib/types/dsh-adapter/channel/question-record.d.ts.map +1 -0
- package/lib/types/dsh-adapter/channel/question-record.js +107 -0
- package/lib/types/dsh-adapter/channel/session-lineage.d.ts +42 -0
- package/lib/types/dsh-adapter/channel/session-lineage.d.ts.map +1 -0
- package/lib/types/dsh-adapter/channel/session-lineage.js +51 -0
- package/lib/types/dsh-adapter/channel/session-live-adoption.d.ts +2 -0
- package/lib/types/dsh-adapter/channel/session-live-adoption.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/session-live-adoption.js +9 -3
- package/lib/types/dsh-adapter/channel/session-metadata.d.ts +2 -1
- package/lib/types/dsh-adapter/channel/session-metadata.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/session-metadata.js +39 -7
- 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 +7 -0
- package/lib/types/dsh-adapter/channel/session-resume.d.ts +1 -0
- package/lib/types/dsh-adapter/channel/session-resume.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/session-resume.js +4 -2
- package/lib/types/dsh-adapter/channel/settings-host.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/settings-host.js +8 -0
- package/lib/types/dsh-adapter/channel/state.d.ts +14 -1
- package/lib/types/dsh-adapter/channel/state.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/state.js +6 -1
- package/lib/types/dsh-adapter/channel/subagent-projection.d.ts +30 -35
- package/lib/types/dsh-adapter/channel/subagent-projection.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/subagent-projection.js +138 -4
- package/lib/types/dsh-adapter/channel/types.d.ts +1 -1
- package/lib/types/dsh-adapter/channel/types.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel.js +19 -4
- package/lib/types/dsh-adapter/compat/messages.d.ts +6 -0
- package/lib/types/dsh-adapter/compat/messages.d.ts.map +1 -1
- package/lib/types/dsh-adapter/compat/messages.js +9 -0
- package/lib/types/dsh-adapter/contract.d.ts +5 -5
- package/lib/types/dsh-adapter/contract.d.ts.map +1 -1
- package/lib/types/dsh-adapter/contract.js +8 -6
- package/lib/types/dsh-adapter/index.d.ts +36 -1
- package/lib/types/dsh-adapter/index.d.ts.map +1 -1
- package/lib/types/dsh-adapter/index.js +22 -6
- package/lib/types/dsh-adapter/migrate/adapters/claude-code.d.ts +3 -0
- package/lib/types/dsh-adapter/migrate/adapters/claude-code.d.ts.map +1 -0
- package/lib/types/dsh-adapter/migrate/adapters/claude-code.js +131 -0
- package/lib/types/dsh-adapter/migrate/adapters/claude-code.parse.d.ts +17 -0
- package/lib/types/dsh-adapter/migrate/adapters/claude-code.parse.d.ts.map +1 -0
- package/lib/types/dsh-adapter/migrate/adapters/claude-code.parse.js +329 -0
- package/lib/types/dsh-adapter/migrate/adapters/codex.d.ts +3 -0
- package/lib/types/dsh-adapter/migrate/adapters/codex.d.ts.map +1 -0
- package/lib/types/dsh-adapter/migrate/adapters/codex.js +102 -0
- package/lib/types/dsh-adapter/migrate/adapters/codex.parse.d.ts +16 -0
- package/lib/types/dsh-adapter/migrate/adapters/codex.parse.d.ts.map +1 -0
- package/lib/types/dsh-adapter/migrate/adapters/codex.parse.js +289 -0
- package/lib/types/dsh-adapter/migrate/adapters/grok-build.d.ts +3 -0
- package/lib/types/dsh-adapter/migrate/adapters/grok-build.d.ts.map +1 -0
- package/lib/types/dsh-adapter/migrate/adapters/grok-build.js +117 -0
- package/lib/types/dsh-adapter/migrate/adapters/grok-build.parse.d.ts +13 -0
- package/lib/types/dsh-adapter/migrate/adapters/grok-build.parse.d.ts.map +1 -0
- package/lib/types/dsh-adapter/migrate/adapters/grok-build.parse.js +178 -0
- package/lib/types/dsh-adapter/migrate/adapters/omp.d.ts +3 -0
- package/lib/types/dsh-adapter/migrate/adapters/omp.d.ts.map +1 -0
- package/lib/types/dsh-adapter/migrate/adapters/omp.js +133 -0
- package/lib/types/dsh-adapter/migrate/adapters/scan-fs.d.ts +60 -0
- package/lib/types/dsh-adapter/migrate/adapters/scan-fs.d.ts.map +1 -0
- package/lib/types/dsh-adapter/migrate/adapters/scan-fs.js +178 -0
- package/lib/types/dsh-adapter/migrate/adapters/scan.d.ts +8 -0
- package/lib/types/dsh-adapter/migrate/adapters/scan.d.ts.map +1 -0
- package/lib/types/dsh-adapter/migrate/adapters/scan.js +40 -0
- package/lib/types/dsh-adapter/migrate/adapters/zcode.d.ts +3 -0
- package/lib/types/dsh-adapter/migrate/adapters/zcode.d.ts.map +1 -0
- package/lib/types/dsh-adapter/migrate/adapters/zcode.js +93 -0
- package/lib/types/dsh-adapter/migrate/adapters/zcode.parse.d.ts +9 -0
- package/lib/types/dsh-adapter/migrate/adapters/zcode.parse.d.ts.map +1 -0
- package/lib/types/dsh-adapter/migrate/adapters/zcode.parse.js +89 -0
- package/lib/types/dsh-adapter/migrate/bin-path.d.ts +6 -0
- package/lib/types/dsh-adapter/migrate/bin-path.d.ts.map +1 -0
- package/lib/types/dsh-adapter/migrate/bin-path.js +30 -0
- package/lib/types/dsh-adapter/migrate/browse.d.ts +42 -0
- package/lib/types/dsh-adapter/migrate/browse.d.ts.map +1 -0
- package/lib/types/dsh-adapter/migrate/browse.js +90 -0
- package/lib/types/dsh-adapter/migrate/cli.d.ts +9 -0
- package/lib/types/dsh-adapter/migrate/cli.d.ts.map +1 -0
- package/lib/types/dsh-adapter/migrate/cli.js +106 -0
- package/lib/types/dsh-adapter/migrate/import-one.d.ts +89 -0
- package/lib/types/dsh-adapter/migrate/import-one.d.ts.map +1 -0
- package/lib/types/dsh-adapter/migrate/import-one.js +133 -0
- package/lib/types/dsh-adapter/migrate/index.d.ts +35 -0
- package/lib/types/dsh-adapter/migrate/index.d.ts.map +1 -0
- package/lib/types/dsh-adapter/migrate/index.js +107 -0
- package/lib/types/dsh-adapter/migrate/parse/injection.d.ts +40 -0
- package/lib/types/dsh-adapter/migrate/parse/injection.d.ts.map +1 -0
- package/lib/types/dsh-adapter/migrate/parse/injection.js +127 -0
- package/lib/types/dsh-adapter/migrate/parse/jsonl.d.ts +28 -0
- package/lib/types/dsh-adapter/migrate/parse/jsonl.d.ts.map +1 -0
- package/lib/types/dsh-adapter/migrate/parse/jsonl.js +40 -0
- package/lib/types/dsh-adapter/migrate/parse/role-turns.d.ts +28 -0
- package/lib/types/dsh-adapter/migrate/parse/role-turns.d.ts.map +1 -0
- package/lib/types/dsh-adapter/migrate/parse/role-turns.js +50 -0
- package/lib/types/dsh-adapter/migrate/parse/title.d.ts +18 -0
- package/lib/types/dsh-adapter/migrate/parse/title.d.ts.map +1 -0
- package/lib/types/dsh-adapter/migrate/parse/title.js +29 -0
- package/lib/types/dsh-adapter/migrate/parse/tools.d.ts +62 -0
- package/lib/types/dsh-adapter/migrate/parse/tools.d.ts.map +1 -0
- package/lib/types/dsh-adapter/migrate/parse/tools.js +110 -0
- package/lib/types/dsh-adapter/migrate/picker.d.ts +64 -0
- package/lib/types/dsh-adapter/migrate/picker.d.ts.map +1 -0
- package/lib/types/dsh-adapter/migrate/picker.js +86 -0
- package/lib/types/dsh-adapter/migrate/recent-agents.d.ts +40 -0
- package/lib/types/dsh-adapter/migrate/recent-agents.d.ts.map +1 -0
- package/lib/types/dsh-adapter/migrate/recent-agents.js +100 -0
- package/lib/types/dsh-adapter/migrate/sessionize.d.ts +39 -0
- package/lib/types/dsh-adapter/migrate/sessionize.d.ts.map +1 -0
- package/lib/types/dsh-adapter/migrate/sessionize.js +271 -0
- package/lib/types/dsh-adapter/migrate/types.d.ts +176 -0
- package/lib/types/dsh-adapter/migrate/types.d.ts.map +1 -0
- package/lib/types/dsh-adapter/migrate/types.js +11 -0
- package/lib/types/dsh-adapter/migrate/uuid.d.ts +7 -0
- package/lib/types/dsh-adapter/migrate/uuid.d.ts.map +1 -0
- package/lib/types/dsh-adapter/migrate/uuid.js +25 -0
- package/lib/types/dsh-adapter/plugin.d.ts +4 -2
- package/lib/types/dsh-adapter/plugin.d.ts.map +1 -1
- package/lib/types/dsh-adapter/plugin.js +143 -364
- package/lib/types/dsh-adapter/providerWizard.d.ts +17 -0
- package/lib/types/dsh-adapter/providerWizard.d.ts.map +1 -1
- package/lib/types/dsh-adapter/providerWizard.js +254 -66
- package/lib/types/dsh-adapter/questions.d.ts +22 -15
- package/lib/types/dsh-adapter/questions.d.ts.map +1 -1
- package/lib/types/dsh-adapter/questions.js +47 -45
- package/lib/types/dsh-adapter/sessions/digest.d.ts +11 -6
- package/lib/types/dsh-adapter/sessions/digest.d.ts.map +1 -1
- package/lib/types/dsh-adapter/sessions/digest.js +20 -13
- package/lib/types/dsh-adapter/sessions/frames.d.ts +3 -1
- package/lib/types/dsh-adapter/sessions/frames.d.ts.map +1 -1
- package/lib/types/dsh-adapter/sessions/frames.js +5 -4
- package/lib/types/dsh-adapter/sessions/list.d.ts +6 -1
- package/lib/types/dsh-adapter/sessions/list.d.ts.map +1 -1
- package/lib/types/dsh-adapter/sessions/list.js +188 -164
- package/lib/types/dsh-adapter/sessions/recovery.d.ts +16 -0
- package/lib/types/dsh-adapter/sessions/recovery.d.ts.map +1 -0
- package/lib/types/dsh-adapter/sessions/recovery.js +108 -0
- package/lib/types/dsh-adapter/sessions/snapshot.d.ts +7 -0
- package/lib/types/dsh-adapter/sessions/snapshot.d.ts.map +1 -0
- package/lib/types/dsh-adapter/sessions/snapshot.js +90 -0
- package/lib/types/dsh-adapter/sessions/store.d.ts +10 -4
- package/lib/types/dsh-adapter/sessions/store.d.ts.map +1 -1
- package/lib/types/dsh-adapter/sessions/store.js +62 -25
- package/lib/types/dsh-adapter/sideQuestion.d.ts +41 -2
- package/lib/types/dsh-adapter/sideQuestion.d.ts.map +1 -1
- package/lib/types/dsh-adapter/sideQuestion.js +98 -2
- package/lib/types/dsh-adapter/subagents.d.ts +28 -3
- package/lib/types/dsh-adapter/subagents.d.ts.map +1 -1
- package/lib/types/dsh-adapter/subagents.js +77 -2
- package/lib/types/herdr.d.ts +2 -0
- package/lib/types/herdr.d.ts.map +1 -1
- package/lib/types/herdr.js +9 -3
- package/lib/types/i18n.d.ts +424 -42
- package/lib/types/i18n.d.ts.map +1 -1
- package/lib/types/i18n.js +145 -20
- package/lib/types/ink/components/App.d.ts +2 -2
- package/lib/types/ink/components/App.d.ts.map +1 -1
- package/lib/types/ink/components/App.js +5 -1
- package/lib/types/ink/components/Image.d.ts +14 -1
- package/lib/types/ink/components/Image.d.ts.map +1 -1
- package/lib/types/ink/components/Image.js +2 -2
- package/lib/types/ink/events/input-event.js +3 -3
- package/lib/types/ink/hooks/use-terminal-images.d.ts +12 -0
- package/lib/types/ink/hooks/use-terminal-images.d.ts.map +1 -1
- package/lib/types/ink/hooks/use-terminal-images.js +13 -0
- package/lib/types/ink/ink.d.ts +6 -0
- package/lib/types/ink/ink.d.ts.map +1 -1
- package/lib/types/ink/ink.js +16 -1
- package/lib/types/ink/kitty-graphics.d.ts +44 -3
- package/lib/types/ink/kitty-graphics.d.ts.map +1 -1
- package/lib/types/ink/kitty-graphics.js +184 -23
- package/lib/types/ink/output.d.ts +24 -2
- package/lib/types/ink/output.d.ts.map +1 -1
- package/lib/types/ink/output.js +83 -5
- package/lib/types/ink/parse-keypress.d.ts +26 -1
- package/lib/types/ink/parse-keypress.d.ts.map +1 -1
- package/lib/types/ink/parse-keypress.js +156 -13
- package/lib/types/ink/render-node-to-output.d.ts.map +1 -1
- package/lib/types/ink/render-node-to-output.js +8 -1
- package/lib/types/ink/screen.d.ts +24 -0
- package/lib/types/ink/screen.d.ts.map +1 -1
- package/lib/types/ink/screen.js +89 -0
- package/lib/types/ink/selection.d.ts +40 -18
- package/lib/types/ink/selection.d.ts.map +1 -1
- package/lib/types/ink/selection.js +142 -30
- package/lib/types/ink/sixel-codec.d.ts +12 -1
- package/lib/types/ink/sixel-codec.d.ts.map +1 -1
- package/lib/types/ink/sixel-codec.js +29 -5
- package/lib/types/ink/sixel-graphics.d.ts.map +1 -1
- package/lib/types/ink/sixel-graphics.js +5 -3
- package/lib/types/ink/styles.d.ts +9 -0
- package/lib/types/ink/styles.d.ts.map +1 -1
- package/lib/types/ink/terminal-image.d.ts +8 -0
- package/lib/types/ink/terminal-image.d.ts.map +1 -1
- package/lib/types/ink/terminal-querier.d.ts +7 -1
- package/lib/types/ink/terminal-querier.d.ts.map +1 -1
- package/lib/types/ink/terminal-querier.js +12 -2
- package/lib/types/ink/timeline-rail.d.ts +5 -0
- package/lib/types/ink/timeline-rail.d.ts.map +1 -1
- package/lib/types/math/inline-layout.d.ts +29 -0
- package/lib/types/math/inline-layout.d.ts.map +1 -0
- package/lib/types/math/inline-layout.js +106 -0
- package/lib/types/math/layout.d.ts +58 -0
- package/lib/types/math/layout.d.ts.map +1 -0
- package/lib/types/math/layout.js +59 -0
- package/lib/types/math/renderer.d.ts +64 -0
- package/lib/types/math/renderer.d.ts.map +1 -0
- package/lib/types/math/renderer.js +256 -0
- package/lib/types/screens/Chat.d.ts +14 -1
- package/lib/types/screens/Chat.d.ts.map +1 -1
- package/lib/types/screens/Chat.js +606 -53
- package/lib/types/screens/SessionSupervisor.d.ts +2 -1
- package/lib/types/screens/SessionSupervisor.d.ts.map +1 -1
- package/lib/types/screens/SessionSupervisor.js +156 -19
- package/lib/types/screens/Settings.d.ts.map +1 -1
- package/lib/types/screens/Settings.js +49 -11
- package/lib/types/screens/StatusLine.d.ts.map +1 -1
- package/lib/types/screens/StatusLine.js +45 -15
- package/lib/types/screens/chatOverlay.d.ts +5 -0
- package/lib/types/screens/chatOverlay.d.ts.map +1 -1
- package/lib/types/screens/chatOverlay.js +1 -0
- package/lib/types/screens/sessionSupervisor/ForeignSessionPanes.d.ts +47 -0
- package/lib/types/screens/sessionSupervisor/ForeignSessionPanes.d.ts.map +1 -0
- package/lib/types/screens/sessionSupervisor/ForeignSessionPanes.js +65 -0
- package/lib/types/screens/sessionSupervisor/model.d.ts +12 -0
- package/lib/types/screens/sessionSupervisor/model.d.ts.map +1 -1
- package/lib/types/screens/sessionSupervisor/model.js +26 -0
- package/lib/types/screens/sessionSupervisor/useForeignSessions.d.ts +107 -0
- package/lib/types/screens/sessionSupervisor/useForeignSessions.d.ts.map +1 -0
- package/lib/types/screens/sessionSupervisor/useForeignSessions.js +312 -0
- package/lib/types/screens/sessionSupervisor/useSessionSupervisor.d.ts +4 -1
- package/lib/types/screens/sessionSupervisor/useSessionSupervisor.d.ts.map +1 -1
- package/lib/types/screens/sessionSupervisor/useSessionSupervisor.js +97 -14
- package/lib/types/sessionHistory.d.ts +2 -2
- package/lib/types/sessionHistory.d.ts.map +1 -1
- package/lib/types/sessionHistory.js +31 -5
- package/lib/types/settings/definitions.d.ts +681 -0
- package/lib/types/settings/definitions.d.ts.map +1 -0
- package/lib/types/settings/definitions.js +511 -0
- package/lib/types/starAction.d.ts +44 -0
- package/lib/types/starAction.d.ts.map +1 -0
- package/lib/types/starAction.js +52 -0
- package/lib/types/terminal-utils/latex.d.ts +10 -0
- package/lib/types/terminal-utils/latex.d.ts.map +1 -0
- package/lib/types/terminal-utils/latex.js +1417 -0
- package/lib/types/terminal-utils/markdown.d.ts +3 -1
- package/lib/types/terminal-utils/markdown.d.ts.map +1 -1
- package/lib/types/terminal-utils/markdown.js +26 -1
- package/lib/types/terminal-utils/math.d.ts +66 -0
- package/lib/types/terminal-utils/math.d.ts.map +1 -0
- package/lib/types/terminal-utils/math.js +337 -0
- package/lib/types/tips.d.ts.map +1 -1
- package/lib/types/tips.js +6 -0
- package/lib/types/tuiDisplayPrefs.d.ts +67 -0
- package/lib/types/tuiDisplayPrefs.d.ts.map +1 -1
- package/lib/types/tuiDisplayPrefs.js +59 -0
- package/lib/types/ui.d.ts +1 -1
- package/lib/types/ui.d.ts.map +1 -1
- package/lib/types/ui.js +1 -1
- package/lib/types/usageStats.d.ts +98 -0
- package/lib/types/usageStats.d.ts.map +1 -0
- package/lib/types/usageStats.js +169 -0
- package/lib/types/utils/bmp.d.ts +17 -0
- package/lib/types/utils/bmp.d.ts.map +1 -0
- package/lib/types/utils/bmp.js +154 -0
- package/lib/types/utils/clipboard.d.ts +14 -2
- package/lib/types/utils/clipboard.d.ts.map +1 -1
- package/lib/types/utils/clipboard.js +143 -8
- package/lib/types/utils/fold-long-lines.d.ts.map +1 -1
- package/lib/types/utils/fold-long-lines.js +2 -1
- package/lib/types/utils/keymap.d.ts +4 -1
- package/lib/types/utils/keymap.d.ts.map +1 -1
- package/lib/types/utils/keymap.js +8 -0
- package/lib/types/utils/modifiers.d.ts +4 -3
- package/lib/types/utils/modifiers.d.ts.map +1 -1
- package/lib/types/utils/modifiers.js +4 -3
- package/node_modules/@dsh-tui-vendor/mathjax-tex-svg/LICENSE +202 -0
- package/node_modules/@dsh-tui-vendor/mathjax-tex-svg/NOTICE +20 -0
- package/node_modules/@dsh-tui-vendor/mathjax-tex-svg/index.d.ts +16 -0
- package/node_modules/@dsh-tui-vendor/mathjax-tex-svg/lib/bundled-packages.json +4 -0
- package/node_modules/@dsh-tui-vendor/mathjax-tex-svg/lib/index.mjs +20 -0
- package/node_modules/@dsh-tui-vendor/mathjax-tex-svg/lib/index.mjs.LEGAL.txt +35 -0
- package/node_modules/@dsh-tui-vendor/mathjax-tex-svg/package.json +29 -0
- package/package.json +111 -97
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Text } from '../../ui.js';
|
|
3
|
+
import { t } from '../../i18n.js';
|
|
4
|
+
import { HintLine } from '../../components/design-system/HintLine.js';
|
|
5
|
+
import { SearchBox } from '../../components/SearchBox.js';
|
|
6
|
+
import { HomeWorkspaceRow } from '../../components/workspaces/HomeWorkspaceRow.js';
|
|
7
|
+
import { ForeignSessionRow } from '../../components/sessions/ForeignSessionRow.js';
|
|
8
|
+
import { truncateWidth } from '../../sessions/format.js';
|
|
9
|
+
import { SESSION_ROW_LINES } from './model.js';
|
|
10
|
+
/**
|
|
11
|
+
* Chrome the right pane of a source tab spends outside its rows: the screen
|
|
12
|
+
* header and divider, the pane banner, the filter, the notice and the hints.
|
|
13
|
+
* Two rows fewer than the DSH pane, which also carries the new-session card.
|
|
14
|
+
*/
|
|
15
|
+
export const FOREIGN_PANE_CHROME_ROWS = 6;
|
|
16
|
+
/**
|
|
17
|
+
* First visible row of a scroll window that keeps `focus` on screen, the same
|
|
18
|
+
* anchoring the DSH list uses.
|
|
19
|
+
* @param focus - Cursor index.
|
|
20
|
+
* @param total - Rows in the list.
|
|
21
|
+
* @param capacity - Rows the window shows.
|
|
22
|
+
* @returns Index of the first row to draw.
|
|
23
|
+
*/
|
|
24
|
+
export function windowTop(focus, total, capacity) {
|
|
25
|
+
const size = Math.max(1, capacity);
|
|
26
|
+
const top = Math.max(0, focus - size + 1);
|
|
27
|
+
return Math.min(top, Math.max(0, total - size));
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* The two panes of a source tab: the source's directories on the left, the
|
|
31
|
+
* conversations of the selected one on the right.
|
|
32
|
+
*
|
|
33
|
+
* Presentational only; every value and action comes from
|
|
34
|
+
* {@link ForeignSessionsModel}. The shape follows the DSH panes on purpose so
|
|
35
|
+
* the tabs read as one screen, with what a foreign source cannot do removed:
|
|
36
|
+
* no workspace menu (the rail is derived, not a ledger), no new-session card,
|
|
37
|
+
* no live state and no pins.
|
|
38
|
+
*/
|
|
39
|
+
export function ForeignSessionPanes({ model, sourceLabel, home, now, query, notice, railVisible, railWidth, railEntryCapacity, sessionWidth, rows, isTerminalFocused, }) {
|
|
40
|
+
const { groups, selected, railFocus, visibleRows, rowIndex, pane, loading } = model;
|
|
41
|
+
const filtered = query.trim().length > 0;
|
|
42
|
+
const railTop = windowTop(railFocus, groups.length, railEntryCapacity);
|
|
43
|
+
const railRows = groups.slice(railTop, railTop + railEntryCapacity);
|
|
44
|
+
const capacity = Math.max(1, Math.floor(Math.max(SESSION_ROW_LINES, rows - FOREIGN_PANE_CHROME_ROWS) / SESSION_ROW_LINES));
|
|
45
|
+
const listTop = windowTop(rowIndex, visibleRows.length, capacity);
|
|
46
|
+
const listRows = visibleRows.slice(listTop, listTop + capacity);
|
|
47
|
+
const groupName = selected?.title ?? sourceLabel;
|
|
48
|
+
// The rail carries its own hint; this one is always the list's, as on DSH.
|
|
49
|
+
const hint = filtered ? t('supervisor-foreign-hint-filter') : t('supervisor-foreign-hint-list');
|
|
50
|
+
return (_jsxs(Box, { flexDirection: "row", flexGrow: 1, flexShrink: 1, overflow: "hidden", children: [railVisible && (_jsxs("ink-box", { style: { flexDirection: 'column', width: railWidth, height: '100%', flexShrink: 0, overflow: 'hidden' }, onClick: model.activateRail, onMouseEnter: model.activateRail, onWheel: (event) => {
|
|
51
|
+
model.moveRail(event.deltaY >= 0 ? 1 : -1);
|
|
52
|
+
}, children: [_jsx(Box, { height: 1, flexShrink: 0, overflow: "hidden", paddingX: 1, children: _jsx(Text, { dimColor: true, children: truncateWidth(t('home-section-workspaces', { n: groups.length }), railWidth - 2) }) }), !loading && groups.length === 0 && (_jsx(Box, { paddingX: 1, children: _jsx(Text, { dimColor: true, italic: true, wrap: "truncate-end", children: truncateWidth(t('supervisor-foreign-empty', { source: sourceLabel }), railWidth - 2) }) })), railRows.map((group) => {
|
|
53
|
+
const absolute = groups.indexOf(group);
|
|
54
|
+
return (_jsx(HomeWorkspaceRow, { title: group.title, path: group.path, home: home, sessionCount: group.rows.length, present: group.present, selected: selected?.key === group.key, focused: pane === 'rail' && railFocus === absolute, width: railWidth, onSelect: (event) => {
|
|
55
|
+
event.stopImmediatePropagation();
|
|
56
|
+
model.selectGroup(group);
|
|
57
|
+
} }, group.key));
|
|
58
|
+
}), _jsx(Box, { flexGrow: 1 }), _jsx(Box, { flexShrink: 0, paddingX: 1, children: _jsx(Text, { dimColor: true, italic: true, children: _jsx(HintLine, { text: t('supervisor-foreign-hint-rail') }) }) })] })), railVisible && (_jsx(Box, { width: 1, flexShrink: 0, flexDirection: "column", children: _jsx(Text, { dimColor: true, children: '│' }) })), _jsxs(Box, { flexDirection: "column", width: sessionWidth, height: "100%", flexShrink: 0, overflow: "hidden", onClick: model.activateList, onMouseEnter: model.activateList, children: [_jsx(Box, { height: 1, flexShrink: 0, overflow: "hidden", children: _jsxs(Box, { flexShrink: 1, overflow: "hidden", children: [_jsx(Text, { color: "remember", bold: true, children: truncateWidth(` ${t('supervisor-foreign-sessions-title', { source: sourceLabel, name: groupName })}`, Math.max(4, sessionWidth - 3)) }), _jsx(Text, { dimColor: true, children: ` ${truncateWidth(t('supervisor-foreign-count', { total: visibleRows.length }), Math.max(4, sessionWidth - 3))}` })] }) }), _jsx(Box, { height: 1, flexShrink: 0, paddingX: 1, children: _jsx(SearchBox, { query: query, isFocused: pane === 'list', isTerminalFocused: isTerminalFocused, placeholder: truncateWidth(t('supervisor-foreign-filter-placeholder'), Math.max(8, sessionWidth - 6)), prefix: "/", borderless: true, width: Math.max(8, sessionWidth - 2) }) }), _jsxs("ink-box", { style: { flexDirection: 'column', flexGrow: 1, flexShrink: 1, overflow: 'hidden' }, onWheel: (event) => {
|
|
59
|
+
model.moveList(event.deltaY >= 0 ? 1 : -1);
|
|
60
|
+
}, children: [loading && _jsx(Text, { dimColor: true, italic: true, children: ` ${truncateWidth(t('supervisor-foreign-loading', { source: sourceLabel }), sessionWidth - 2)}` }), !loading && visibleRows.length === 0 && (_jsx(Text, { dimColor: true, italic: true, children: ` ${truncateWidth(filtered ? t('supervisor-no-matches') : t('supervisor-foreign-empty', { source: sourceLabel }), sessionWidth - 2)}` })), listRows.map((row, index) => (_jsx(ForeignSessionRow, { session: row, width: sessionWidth, focused: pane === 'list' && listTop + index === rowIndex, home: home, now: now, onClick: (event) => {
|
|
61
|
+
event.stopImmediatePropagation();
|
|
62
|
+
model.focusRow(row);
|
|
63
|
+
model.openRow(row);
|
|
64
|
+
} }, row.key)))] }), _jsx(Box, { flexShrink: 0, height: 1, overflow: "hidden", children: _jsx(Text, { color: notice?.tone === 'error' ? 'error' : 'success', children: notice === undefined ? ' ' : ` ${truncateWidth(notice.text, Math.max(0, sessionWidth - 3))}` }) }), _jsx(Box, { flexShrink: 0, children: _jsx(Text, { dimColor: true, italic: true, children: _jsx(HintLine, { text: hint }) }) })] })] }));
|
|
65
|
+
}
|
|
@@ -27,6 +27,18 @@ export declare const RAIL_WIDTH_MAX = 38;
|
|
|
27
27
|
export declare const SESSION_ROW_LINES = 2;
|
|
28
28
|
/** Chrome the right pane spends on banner, filter, new-session card, notice and hints. */
|
|
29
29
|
export declare const SESSION_PANE_CHROME_ROWS = 8;
|
|
30
|
+
/**
|
|
31
|
+
* Most rows the notice may grow to. A mount refusal carries the adapter's own
|
|
32
|
+
* error text, which is routinely longer than one row; three rows show it in
|
|
33
|
+
* full on ordinary widths without letting it push the list off the screen.
|
|
34
|
+
*/
|
|
35
|
+
export declare const NOTICE_MAX_LINES = 3;
|
|
36
|
+
/**
|
|
37
|
+
* The notice wrapped to `width` display columns, at most
|
|
38
|
+
* {@link NOTICE_MAX_LINES} rows, the last one ellipsised when text remains.
|
|
39
|
+
* Always at least one row, so an empty notice still holds its place.
|
|
40
|
+
*/
|
|
41
|
+
export declare function noticeLines(text: string | undefined, width: number): string[];
|
|
30
42
|
export type MenuAction = 'edit' | 'new' | 'rename' | 'remove';
|
|
31
43
|
export declare const MENU_ACTIONS: readonly MenuAction[];
|
|
32
44
|
export declare const MENU_WIDTH = 30;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../../../../src/screens/sessionSupervisor/model.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qCAAqC,CAAA;AACzE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAA;
|
|
1
|
+
{"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../../../../src/screens/sessionSupervisor/model.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qCAAqC,CAAA;AACzE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAA;AAM5D;;;;;;;GAOG;AACH,eAAO,MAAM,gBAAgB,IAAI,CAAA;AACjC,2EAA2E;AAC3E,eAAO,MAAM,mBAAmB,IAAI,CAAA;AACpC,+EAA+E;AAC/E,eAAO,MAAM,sBAAsB,KAAK,CAAA;AACxC,eAAO,MAAM,cAAc,KAAK,CAAA;AAChC,eAAO,MAAM,cAAc,KAAK,CAAA;AAChC,kFAAkF;AAClF,eAAO,MAAM,iBAAiB,IAAI,CAAA;AAClC,0FAA0F;AAC1F,eAAO,MAAM,wBAAwB,IAAI,CAAA;AACzC;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,IAAI,CAAA;AAEjC;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAS7E;AAED,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,KAAK,GAAG,QAAQ,GAAG,QAAQ,CAAA;AAC7D,eAAO,MAAM,YAAY,EAAE,SAAS,UAAU,EAAwC,CAAA;AACtF,eAAO,MAAM,UAAU,KAAK,CAAA;AAC5B,0CAA0C;AAC1C,eAAO,MAAM,WAAW,QAA0B,CAAA;AAElD,eAAO,MAAM,eAAe;;;;;CAKlB,CAAA;AAEV;;;GAGG;AACH,MAAM,WAAW,mBAAmB;IAClC,kEAAkE;IAClE,QAAQ,CAAC,MAAM,EAAE,SAAS,GAAG,aAAa,GAAG,MAAM,GAAG,WAAW,GAAG,QAAQ,GAAG,SAAS,CAAA;IACxF,oEAAoE;IACpE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAA;IACtB,iEAAiE;IACjE,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;IACzB,iCAAiC;IACjC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CACzB;AAED;;;;;;;;GAQG;AACH,MAAM,MAAM,SAAS,GAAG,iBAAiB,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,UAAU,GAAG,cAAc,CAAA;CAAE,CAAA;AAE1F,iEAAiE;AACjE,eAAO,MAAM,oBAAoB,sBAAsB,CAAA;AAEvD,wBAAgB,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAE9C;AAED,gFAAgF;AAChF,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAE7D;AAED;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAUpF"}
|
|
@@ -6,7 +6,10 @@
|
|
|
6
6
|
* importing each other, and so a regression can drive them directly. Anything
|
|
7
7
|
* that needs a channel, the filesystem or a render belongs to the hook instead.
|
|
8
8
|
*/
|
|
9
|
+
import wrapAnsi from 'wrap-ansi';
|
|
9
10
|
import { normalizeWorkspaceCwd } from '../../sessions/view.js';
|
|
11
|
+
import { truncateWidth } from '../../sessions/format.js';
|
|
12
|
+
import { stringWidth } from '../../ink/stringWidth.js';
|
|
10
13
|
/**
|
|
11
14
|
* Rows the left rail always keeps: the section header, the hint line, and the
|
|
12
15
|
* blank rows around them.
|
|
@@ -26,6 +29,29 @@ export const RAIL_WIDTH_MAX = 38;
|
|
|
26
29
|
export const SESSION_ROW_LINES = 2;
|
|
27
30
|
/** Chrome the right pane spends on banner, filter, new-session card, notice and hints. */
|
|
28
31
|
export const SESSION_PANE_CHROME_ROWS = 8;
|
|
32
|
+
/**
|
|
33
|
+
* Most rows the notice may grow to. A mount refusal carries the adapter's own
|
|
34
|
+
* error text, which is routinely longer than one row; three rows show it in
|
|
35
|
+
* full on ordinary widths without letting it push the list off the screen.
|
|
36
|
+
*/
|
|
37
|
+
export const NOTICE_MAX_LINES = 3;
|
|
38
|
+
/**
|
|
39
|
+
* The notice wrapped to `width` display columns, at most
|
|
40
|
+
* {@link NOTICE_MAX_LINES} rows, the last one ellipsised when text remains.
|
|
41
|
+
* Always at least one row, so an empty notice still holds its place.
|
|
42
|
+
*/
|
|
43
|
+
export function noticeLines(text, width) {
|
|
44
|
+
if (text === undefined || width <= 0)
|
|
45
|
+
return [''];
|
|
46
|
+
const rows = wrapAnsi(text, width, { hard: true, trim: true }).split('\n');
|
|
47
|
+
if (rows.length <= NOTICE_MAX_LINES)
|
|
48
|
+
return rows;
|
|
49
|
+
const kept = rows.slice(0, NOTICE_MAX_LINES);
|
|
50
|
+
const last = kept[NOTICE_MAX_LINES - 1] ?? '';
|
|
51
|
+
// The ellipsis marks the cut even when the kept row is short of `width`.
|
|
52
|
+
kept[NOTICE_MAX_LINES - 1] = stringWidth(last) < width ? `${last}…` : truncateWidth(`${last}…`, width);
|
|
53
|
+
return kept;
|
|
54
|
+
}
|
|
29
55
|
export const MENU_ACTIONS = ['edit', 'new', 'rename', 'remove'];
|
|
30
56
|
export const MENU_WIDTH = 30;
|
|
31
57
|
/** One confirm line + its explanation. */
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Data, derived state and actions for the session screen's foreign-source
|
|
3
|
+
* tabs: another coding agent's conversations, grouped by directory, imported
|
|
4
|
+
* on selection.
|
|
5
|
+
*
|
|
6
|
+
* It mirrors the DSH half's ownership rules rather than sharing its code: the
|
|
7
|
+
* two lists carry different data (a DSH summary has live state, pins and a
|
|
8
|
+
* registry; a foreign summary has none of those), and pretending one is the
|
|
9
|
+
* other is exactly what the design ruled out. What is kept identical is the
|
|
10
|
+
* cursor model — the cursor is ONE fact, the key of the row it stands on, and
|
|
11
|
+
* every index is derived from it — because that is what makes Enter act on
|
|
12
|
+
* the row under `❯` after a filter or a streamed row moved the rows.
|
|
13
|
+
*
|
|
14
|
+
* State is only what the visible tab needs: its rows and its cursor. Switching
|
|
15
|
+
* tabs starts the new one fresh, and nothing outlives the screen — reopening a
|
|
16
|
+
* source relists it, which the channel keeps cheap for unchanged ones.
|
|
17
|
+
*
|
|
18
|
+
* Every call is optional on the channel: a host (or a regression stub) that
|
|
19
|
+
* predates the foreign-session facade simply has no source tabs.
|
|
20
|
+
*/
|
|
21
|
+
import type { ForeignSessionRow, ForeignSource } from '../../adapter/ports/channel-session.js';
|
|
22
|
+
import type { ResumeResult } from '../../adapter/ports/channel-view.js';
|
|
23
|
+
import type { ChannelUi as Channel } from '../../adapter/channel/ui-policy.js';
|
|
24
|
+
import { type RailEntry } from './model.js';
|
|
25
|
+
/** The tab id of the screen's own sessions; every other tab is an agent id. */
|
|
26
|
+
export declare const DSH_TAB = "dsh";
|
|
27
|
+
/** Rail key of the group holding conversations with no recorded directory. */
|
|
28
|
+
export declare const FOREIGN_UNKNOWN_GROUP = "foreign:unknown";
|
|
29
|
+
/** One rail row of a source tab: every conversation recorded in one directory. */
|
|
30
|
+
export interface ForeignGroup {
|
|
31
|
+
/** Normalised directory, or {@link FOREIGN_UNKNOWN_GROUP}. */
|
|
32
|
+
readonly key: string;
|
|
33
|
+
/** The directory as recorded ('' for the unknown group). */
|
|
34
|
+
readonly path: string;
|
|
35
|
+
/** The DSH workspace title for a registered directory, else its basename. */
|
|
36
|
+
readonly title: string;
|
|
37
|
+
/** False when the recorded directory no longer exists. */
|
|
38
|
+
readonly present: boolean;
|
|
39
|
+
/** Newest first. */
|
|
40
|
+
readonly rows: readonly ForeignSessionRow[];
|
|
41
|
+
readonly newest: number;
|
|
42
|
+
}
|
|
43
|
+
type Notice = {
|
|
44
|
+
text: string;
|
|
45
|
+
tone: 'info' | 'error';
|
|
46
|
+
} | undefined;
|
|
47
|
+
export interface ForeignSessionsInput {
|
|
48
|
+
readonly channel: Channel;
|
|
49
|
+
/** The screen's active tab: {@link DSH_TAB} or a foreign agent id. */
|
|
50
|
+
readonly tab: string;
|
|
51
|
+
/** The DSH workspace registry, so a known directory keeps its DSH title. */
|
|
52
|
+
readonly registry: readonly RailEntry[];
|
|
53
|
+
/** The screen's live query (shared with the DSH half, cleared on tab switch). */
|
|
54
|
+
readonly query: string;
|
|
55
|
+
setNotice(next: Notice): void;
|
|
56
|
+
/** Mount a persisted session (the channel's unified resume path). */
|
|
57
|
+
onOpenSession(sessionId: string): Promise<ResumeResult>;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Case-insensitive match over what a person searches a foreign row by: its
|
|
61
|
+
* title and the directory it ran in.
|
|
62
|
+
* @param row - The conversation to test.
|
|
63
|
+
* @param needle - Lower-cased query; empty matches everything.
|
|
64
|
+
* @returns True when the row stays visible.
|
|
65
|
+
*/
|
|
66
|
+
export declare function foreignRowMatchesQuery(row: ForeignSessionRow, needle: string): boolean;
|
|
67
|
+
/**
|
|
68
|
+
* Group a source's conversations by directory, newest group first.
|
|
69
|
+
*
|
|
70
|
+
* A directory the DSH registry knows keeps the title the user gave it there,
|
|
71
|
+
* so "the same project" reads the same in every tab; anything else is named by
|
|
72
|
+
* its basename. Conversations with no recorded directory share one group.
|
|
73
|
+
* @param rows - The source's conversations, any order.
|
|
74
|
+
* @param registry - DSH workspace registry rows.
|
|
75
|
+
* @param exists - Directory probe (injected for the regression).
|
|
76
|
+
* @returns Groups sorted by their newest conversation, rows newest first.
|
|
77
|
+
*/
|
|
78
|
+
export declare function groupForeignRows(rows: readonly ForeignSessionRow[], registry: readonly RailEntry[], exists?: (path: string) => boolean): readonly ForeignGroup[];
|
|
79
|
+
/**
|
|
80
|
+
* Derive the foreign-source half of the screen.
|
|
81
|
+
* @param input - Channel, active tab, registry, shared query and notice, and
|
|
82
|
+
* the host's open action.
|
|
83
|
+
* @returns The tab list, the active source's panes, and their actions.
|
|
84
|
+
*/
|
|
85
|
+
export declare function useForeignSessions(input: ForeignSessionsInput): {
|
|
86
|
+
sources: readonly ForeignSource[];
|
|
87
|
+
rows: readonly ForeignSessionRow[];
|
|
88
|
+
loading: boolean;
|
|
89
|
+
groups: readonly ForeignGroup[];
|
|
90
|
+
selected: ForeignGroup | undefined;
|
|
91
|
+
railFocus: number;
|
|
92
|
+
visibleRows: ForeignSessionRow[];
|
|
93
|
+
rowIndex: number;
|
|
94
|
+
focusedRow: ForeignSessionRow;
|
|
95
|
+
pane: "list" | "rail";
|
|
96
|
+
selectGroup: (group: ForeignGroup) => void;
|
|
97
|
+
moveRail: (by: 1 | -1) => void;
|
|
98
|
+
moveList: (by: 1 | -1) => void;
|
|
99
|
+
activateRail: () => void;
|
|
100
|
+
activateList: () => void;
|
|
101
|
+
focusRow: (row: ForeignSessionRow) => void;
|
|
102
|
+
openRow: (row: ForeignSessionRow) => void;
|
|
103
|
+
rescan: () => void;
|
|
104
|
+
};
|
|
105
|
+
export type ForeignSessionsModel = ReturnType<typeof useForeignSessions>;
|
|
106
|
+
export {};
|
|
107
|
+
//# sourceMappingURL=useForeignSessions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useForeignSessions.d.ts","sourceRoot":"","sources":["../../../../src/screens/sessionSupervisor/useForeignSessions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAOH,OAAO,KAAK,EAAwB,iBAAiB,EAAE,aAAa,EAAE,MAAM,wCAAwC,CAAA;AACpH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qCAAqC,CAAA;AAEvE,OAAO,KAAK,EAAE,SAAS,IAAI,OAAO,EAAE,MAAM,oCAAoC,CAAA;AAC9E,OAAO,EAAqB,KAAK,SAAS,EAAE,MAAM,YAAY,CAAA;AAE9D,+EAA+E;AAC/E,eAAO,MAAM,OAAO,QAAQ,CAAA;AAE5B,8EAA8E;AAC9E,eAAO,MAAM,qBAAqB,oBAAoB,CAAA;AAKtD,kFAAkF;AAClF,MAAM,WAAW,YAAY;IAC3B,8DAA8D;IAC9D,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,4DAA4D;IAC5D,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,6EAA6E;IAC7E,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,0DAA0D;IAC1D,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;IACzB,oBAAoB;IACpB,QAAQ,CAAC,IAAI,EAAE,SAAS,iBAAiB,EAAE,CAAA;IAC3C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CACxB;AAmBD,KAAK,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAA;CAAE,GAAG,SAAS,CAAA;AAElE,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;IACzB,sEAAsE;IACtE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,4EAA4E;IAC5E,QAAQ,CAAC,QAAQ,EAAE,SAAS,SAAS,EAAE,CAAA;IACvC,iFAAiF;IACjF,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,qEAAqE;IACrE,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAAA;CACxD;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,iBAAiB,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAGtF;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,SAAS,iBAAiB,EAAE,EAClC,QAAQ,EAAE,SAAS,SAAS,EAAE,EAC9B,MAAM,GAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAoB,GAC7C,SAAS,YAAY,EAAE,CA0BzB;AAoBD;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,oBAAoB;;;;;;;;;;;yBAsHpB,YAAY,KAAG,IAAI;mBAIzB,CAAC,GAAG,CAAC,CAAC,KAAG,IAAI;mBAMb,CAAC,GAAG,CAAC,CAAC,KAAG,IAAI;wBAMV,IAAI;wBACJ,IAAI;oBACN,iBAAiB,KAAG,IAAI;mBAYzB,iBAAiB,KAAG,IAAI;kBA4B3B,IAAI;EAwBpC;AAED,MAAM,MAAM,oBAAoB,GAAG,UAAU,CAAC,OAAO,kBAAkB,CAAC,CAAA"}
|
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Data, derived state and actions for the session screen's foreign-source
|
|
3
|
+
* tabs: another coding agent's conversations, grouped by directory, imported
|
|
4
|
+
* on selection.
|
|
5
|
+
*
|
|
6
|
+
* It mirrors the DSH half's ownership rules rather than sharing its code: the
|
|
7
|
+
* two lists carry different data (a DSH summary has live state, pins and a
|
|
8
|
+
* registry; a foreign summary has none of those), and pretending one is the
|
|
9
|
+
* other is exactly what the design ruled out. What is kept identical is the
|
|
10
|
+
* cursor model — the cursor is ONE fact, the key of the row it stands on, and
|
|
11
|
+
* every index is derived from it — because that is what makes Enter act on
|
|
12
|
+
* the row under `❯` after a filter or a streamed row moved the rows.
|
|
13
|
+
*
|
|
14
|
+
* State is only what the visible tab needs: its rows and its cursor. Switching
|
|
15
|
+
* tabs starts the new one fresh, and nothing outlives the screen — reopening a
|
|
16
|
+
* source relists it, which the channel keeps cheap for unchanged ones.
|
|
17
|
+
*
|
|
18
|
+
* Every call is optional on the channel: a host (or a regression stub) that
|
|
19
|
+
* predates the foreign-session facade simply has no source tabs.
|
|
20
|
+
*/
|
|
21
|
+
import React, { useCallback, useMemo, useRef, useState } from 'react';
|
|
22
|
+
import { basename } from 'node:path';
|
|
23
|
+
import { existsSync } from 'node:fs';
|
|
24
|
+
import { t } from '../../i18n.js';
|
|
25
|
+
import { normalizeWorkspaceCwd } from '../../sessions/view.js';
|
|
26
|
+
import { resumeFailureText } from '../../sessions/resumeFailure.js';
|
|
27
|
+
import { message, samePath } from './model.js';
|
|
28
|
+
/** The tab id of the screen's own sessions; every other tab is an agent id. */
|
|
29
|
+
export const DSH_TAB = 'dsh';
|
|
30
|
+
/** Rail key of the group holding conversations with no recorded directory. */
|
|
31
|
+
export const FOREIGN_UNKNOWN_GROUP = 'foreign:unknown';
|
|
32
|
+
/** Streamed rows are applied at most this often, not once per conversation. */
|
|
33
|
+
const STREAM_FLUSH_MS = 100;
|
|
34
|
+
/**
|
|
35
|
+
* Case-insensitive match over what a person searches a foreign row by: its
|
|
36
|
+
* title and the directory it ran in.
|
|
37
|
+
* @param row - The conversation to test.
|
|
38
|
+
* @param needle - Lower-cased query; empty matches everything.
|
|
39
|
+
* @returns True when the row stays visible.
|
|
40
|
+
*/
|
|
41
|
+
export function foreignRowMatchesQuery(row, needle) {
|
|
42
|
+
if (needle.length === 0)
|
|
43
|
+
return true;
|
|
44
|
+
return `${row.title}\n${row.cwd}`.toLowerCase().includes(needle);
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Group a source's conversations by directory, newest group first.
|
|
48
|
+
*
|
|
49
|
+
* A directory the DSH registry knows keeps the title the user gave it there,
|
|
50
|
+
* so "the same project" reads the same in every tab; anything else is named by
|
|
51
|
+
* its basename. Conversations with no recorded directory share one group.
|
|
52
|
+
* @param rows - The source's conversations, any order.
|
|
53
|
+
* @param registry - DSH workspace registry rows.
|
|
54
|
+
* @param exists - Directory probe (injected for the regression).
|
|
55
|
+
* @returns Groups sorted by their newest conversation, rows newest first.
|
|
56
|
+
*/
|
|
57
|
+
export function groupForeignRows(rows, registry, exists = existsSync) {
|
|
58
|
+
const buckets = new Map();
|
|
59
|
+
for (const row of rows) {
|
|
60
|
+
const key = row.cwd === '' ? FOREIGN_UNKNOWN_GROUP : normalizeWorkspaceCwd(row.cwd);
|
|
61
|
+
const bucket = buckets.get(key);
|
|
62
|
+
if (bucket === undefined)
|
|
63
|
+
buckets.set(key, { path: row.cwd, rows: [row] });
|
|
64
|
+
else
|
|
65
|
+
bucket.rows.push(row);
|
|
66
|
+
}
|
|
67
|
+
const groups = [...buckets.entries()].map(([key, bucket]) => {
|
|
68
|
+
const sorted = bucket.rows.slice().sort((left, right) => right.updatedAt - left.updatedAt);
|
|
69
|
+
const unknown = key === FOREIGN_UNKNOWN_GROUP;
|
|
70
|
+
const registered = unknown
|
|
71
|
+
? undefined
|
|
72
|
+
: registry.find(entry => entry.from === 'registry' && samePath(entry.path, bucket.path));
|
|
73
|
+
return {
|
|
74
|
+
key,
|
|
75
|
+
path: unknown ? '' : bucket.path,
|
|
76
|
+
title: unknown
|
|
77
|
+
? t('supervisor-foreign-unknown-cwd')
|
|
78
|
+
: registered?.title ?? (basename(bucket.path) || bucket.path),
|
|
79
|
+
present: unknown || exists(bucket.path),
|
|
80
|
+
rows: sorted,
|
|
81
|
+
newest: sorted[0]?.updatedAt ?? 0,
|
|
82
|
+
};
|
|
83
|
+
});
|
|
84
|
+
return groups.sort((left, right) => right.newest - left.newest);
|
|
85
|
+
}
|
|
86
|
+
/** Why an import failed, in words. */
|
|
87
|
+
const FAILURE_KEYS = {
|
|
88
|
+
missing: 'supervisor-foreign-failed-missing',
|
|
89
|
+
'too-large': 'supervisor-foreign-failed-too-large',
|
|
90
|
+
'not-a-session': 'supervisor-foreign-failed-not-a-session',
|
|
91
|
+
'write-failed': 'supervisor-foreign-failed-write-failed',
|
|
92
|
+
'unknown-source': 'supervisor-foreign-failed-unknown-source',
|
|
93
|
+
};
|
|
94
|
+
/** The notice for an import that did not end in a session to open. */
|
|
95
|
+
function importFailureText(outcome) {
|
|
96
|
+
if (outcome.kind === 'cwd-missing')
|
|
97
|
+
return t('supervisor-foreign-cwd-missing', { cwd: outcome.cwd });
|
|
98
|
+
const reason = t(FAILURE_KEYS[outcome.reason]);
|
|
99
|
+
return outcome.detail === undefined || outcome.detail === ''
|
|
100
|
+
? t('supervisor-foreign-import-failed', { err: reason })
|
|
101
|
+
: t('supervisor-foreign-import-failed', { err: `${reason} · ${outcome.detail}` });
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Derive the foreign-source half of the screen.
|
|
105
|
+
* @param input - Channel, active tab, registry, shared query and notice, and
|
|
106
|
+
* the host's open action.
|
|
107
|
+
* @returns The tab list, the active source's panes, and their actions.
|
|
108
|
+
*/
|
|
109
|
+
export function useForeignSessions(input) {
|
|
110
|
+
const { channel, tab, registry, query, setNotice, onOpenSession } = input;
|
|
111
|
+
const supported = typeof channel.listForeignSources === 'function';
|
|
112
|
+
const agentId = tab === DSH_TAB ? undefined : tab;
|
|
113
|
+
const [sources, setSources] = useState([]);
|
|
114
|
+
const [listing, setListing] = useState(undefined);
|
|
115
|
+
const [storedView, setView] = useState(undefined);
|
|
116
|
+
const mounted = useRef(true);
|
|
117
|
+
/** The tab on screen now, read by async work that must not act on another. */
|
|
118
|
+
const tabRef = useRef(tab);
|
|
119
|
+
tabRef.current = tab;
|
|
120
|
+
/** Only the newest listing may publish. */
|
|
121
|
+
const generation = useRef(0);
|
|
122
|
+
const importing = useRef(new Set());
|
|
123
|
+
React.useEffect(() => () => {
|
|
124
|
+
mounted.current = false;
|
|
125
|
+
generation.current++;
|
|
126
|
+
}, []);
|
|
127
|
+
// The one read an open pays for: which sources have any conversation.
|
|
128
|
+
React.useEffect(() => {
|
|
129
|
+
if (!supported)
|
|
130
|
+
return;
|
|
131
|
+
void channel.listForeignSources()
|
|
132
|
+
.then((found) => {
|
|
133
|
+
if (mounted.current)
|
|
134
|
+
setSources(found);
|
|
135
|
+
})
|
|
136
|
+
.catch(() => {
|
|
137
|
+
// No tabs is the honest answer to a failed probe; the DSH half is
|
|
138
|
+
// unaffected.
|
|
139
|
+
});
|
|
140
|
+
}, [channel, supported]);
|
|
141
|
+
const list = useCallback(async (id) => {
|
|
142
|
+
if (!supported)
|
|
143
|
+
return;
|
|
144
|
+
const current = ++generation.current;
|
|
145
|
+
const live = () => generation.current === current;
|
|
146
|
+
setListing(previous => ({ tab: id, rows: previous?.tab === id ? previous.rows : [], loading: true }));
|
|
147
|
+
// Rows stream in as the scan finds them, batched so a large source does
|
|
148
|
+
// not render once per conversation.
|
|
149
|
+
let pending = [];
|
|
150
|
+
let timer;
|
|
151
|
+
const flush = () => {
|
|
152
|
+
timer = undefined;
|
|
153
|
+
if (!live() || pending.length === 0)
|
|
154
|
+
return;
|
|
155
|
+
const batch = pending;
|
|
156
|
+
pending = [];
|
|
157
|
+
setListing((previous) => {
|
|
158
|
+
const byKey = new Map((previous?.tab === id ? previous.rows : []).map(row => [row.key, row]));
|
|
159
|
+
for (const row of batch)
|
|
160
|
+
byKey.set(row.key, row);
|
|
161
|
+
return { tab: id, rows: [...byKey.values()], loading: true };
|
|
162
|
+
});
|
|
163
|
+
};
|
|
164
|
+
try {
|
|
165
|
+
const rows = await channel.listForeignSessions(id, (row) => {
|
|
166
|
+
if (!live())
|
|
167
|
+
return;
|
|
168
|
+
pending.push(row);
|
|
169
|
+
timer ??= setTimeout(flush, STREAM_FLUSH_MS);
|
|
170
|
+
});
|
|
171
|
+
if (live())
|
|
172
|
+
setListing({ tab: id, rows, loading: false });
|
|
173
|
+
}
|
|
174
|
+
catch (error) {
|
|
175
|
+
if (!live())
|
|
176
|
+
return;
|
|
177
|
+
setListing(previous => ({ tab: id, rows: previous?.tab === id ? previous.rows : [], loading: false }));
|
|
178
|
+
setNotice({ text: t('supervisor-foreign-scan-failed', { err: message(error) }), tone: 'error' });
|
|
179
|
+
}
|
|
180
|
+
finally {
|
|
181
|
+
if (timer !== undefined)
|
|
182
|
+
clearTimeout(timer);
|
|
183
|
+
}
|
|
184
|
+
}, [channel, supported, setNotice]);
|
|
185
|
+
// Entering a source tab lists it.
|
|
186
|
+
React.useEffect(() => {
|
|
187
|
+
if (agentId !== undefined)
|
|
188
|
+
void list(agentId);
|
|
189
|
+
else
|
|
190
|
+
generation.current++;
|
|
191
|
+
}, [agentId, list]);
|
|
192
|
+
const rows = listing !== undefined && listing.tab === tab ? listing.rows : [];
|
|
193
|
+
const loading = agentId !== undefined && (listing === undefined || listing.tab !== tab || (listing.loading && rows.length === 0));
|
|
194
|
+
const view = storedView !== undefined && storedView.tab === tab ? storedView : { tab, pane: 'rail' };
|
|
195
|
+
const patchView = useCallback((patch) => {
|
|
196
|
+
setView(previous => ({ ...(previous !== undefined && previous.tab === tab ? previous : { tab, pane: 'rail' }), ...patch }));
|
|
197
|
+
}, [tab]);
|
|
198
|
+
const groups = useMemo(() => groupForeignRows(rows, registry), [rows, registry]);
|
|
199
|
+
/**
|
|
200
|
+
* The group the list shows: the one picked by hand while it still exists,
|
|
201
|
+
* else the directory this terminal is in, else the newest.
|
|
202
|
+
*/
|
|
203
|
+
const selected = useMemo(() => {
|
|
204
|
+
if (view.group !== undefined) {
|
|
205
|
+
const picked = groups.find(group => group.key === view.group);
|
|
206
|
+
if (picked !== undefined)
|
|
207
|
+
return picked;
|
|
208
|
+
}
|
|
209
|
+
return groups.find(group => group.path !== '' && samePath(group.path, channel.cwd)) ?? groups[0];
|
|
210
|
+
}, [groups, view.group, channel.cwd]);
|
|
211
|
+
/** The rail cursor IS the selection, as on the DSH rail. */
|
|
212
|
+
const railFocus = selected === undefined ? 0 : Math.max(0, groups.indexOf(selected));
|
|
213
|
+
const visibleRows = useMemo(() => {
|
|
214
|
+
const needle = query.trim().toLowerCase();
|
|
215
|
+
return (selected?.rows ?? []).filter(row => foreignRowMatchesQuery(row, needle));
|
|
216
|
+
}, [selected, query]);
|
|
217
|
+
/**
|
|
218
|
+
* The list cursor, derived from the one stored fact (the row's key). A key
|
|
219
|
+
* the filter removed puts the cursor on the first REAL row — there is no
|
|
220
|
+
* new-session card in a source tab, so the cursor space starts at 0.
|
|
221
|
+
*/
|
|
222
|
+
const rowIndex = useMemo(() => {
|
|
223
|
+
if (view.cursor === undefined)
|
|
224
|
+
return 0;
|
|
225
|
+
return Math.max(0, visibleRows.findIndex(row => row.key === view.cursor));
|
|
226
|
+
}, [visibleRows, view.cursor]);
|
|
227
|
+
const focusedRow = visibleRows[rowIndex];
|
|
228
|
+
const selectGroup = useCallback((group) => {
|
|
229
|
+
patchView({ group: group.key, cursor: undefined });
|
|
230
|
+
}, [patchView]);
|
|
231
|
+
const moveRail = useCallback((by) => {
|
|
232
|
+
if (groups.length === 0)
|
|
233
|
+
return;
|
|
234
|
+
const next = groups[(railFocus + by + groups.length) % groups.length];
|
|
235
|
+
patchView({ group: next.key, cursor: undefined });
|
|
236
|
+
}, [groups, railFocus, patchView]);
|
|
237
|
+
const moveList = useCallback((by) => {
|
|
238
|
+
if (visibleRows.length === 0)
|
|
239
|
+
return;
|
|
240
|
+
const next = Math.min(visibleRows.length - 1, Math.max(0, rowIndex + by));
|
|
241
|
+
patchView({ cursor: visibleRows[next].key });
|
|
242
|
+
}, [visibleRows, rowIndex, patchView]);
|
|
243
|
+
const activateRail = useCallback(() => patchView({ pane: 'rail' }), [patchView]);
|
|
244
|
+
const activateList = useCallback(() => patchView({ pane: 'list' }), [patchView]);
|
|
245
|
+
const focusRow = useCallback((row) => patchView({ cursor: row.key, pane: 'list' }), [patchView]);
|
|
246
|
+
/**
|
|
247
|
+
* Import a conversation (or find the copy an earlier selection made) and
|
|
248
|
+
* open it.
|
|
249
|
+
*
|
|
250
|
+
* The import can take seconds, and the screen may be gone or on another tab
|
|
251
|
+
* when it lands. It opens the session only while the screen is still up —
|
|
252
|
+
* a user who pressed Esc meanwhile must not be pulled into it — and it
|
|
253
|
+
* reports only to the tab that asked, so one source's notice never shows
|
|
254
|
+
* under another.
|
|
255
|
+
*/
|
|
256
|
+
const openRow = useCallback((row) => {
|
|
257
|
+
if (!supported)
|
|
258
|
+
return;
|
|
259
|
+
const key = `${row.agentId}|${row.key}`;
|
|
260
|
+
if (importing.current.has(key))
|
|
261
|
+
return;
|
|
262
|
+
importing.current.add(key);
|
|
263
|
+
const report = (next) => {
|
|
264
|
+
if (mounted.current && tabRef.current === row.agentId)
|
|
265
|
+
setNotice(next);
|
|
266
|
+
};
|
|
267
|
+
report({ text: t('supervisor-foreign-importing', { name: row.title }), tone: 'info' });
|
|
268
|
+
void channel.importForeignSession(row.agentId, row.key)
|
|
269
|
+
.then(async (outcome) => {
|
|
270
|
+
if (outcome.kind !== 'ready') {
|
|
271
|
+
report({ text: importFailureText(outcome), tone: 'error' });
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
if (!mounted.current)
|
|
275
|
+
return;
|
|
276
|
+
report(undefined);
|
|
277
|
+
// The host owns the refusal's reason (same contract as the DSH half):
|
|
278
|
+
// `cancelled` stays silent, a plain failure shows the bare error.
|
|
279
|
+
const result = await onOpenSession(outcome.sessionId);
|
|
280
|
+
const reason = !result.ok && result.reason === 'failed' ? result.error : resumeFailureText(result);
|
|
281
|
+
if (reason !== undefined)
|
|
282
|
+
report({ text: t('supervisor-open-failed', { name: row.title, reason }), tone: 'error' });
|
|
283
|
+
})
|
|
284
|
+
.catch(error => report({ text: t('supervisor-foreign-import-failed', { err: message(error) }), tone: 'error' }))
|
|
285
|
+
.finally(() => importing.current.delete(key));
|
|
286
|
+
}, [channel, supported, onOpenSession, setNotice]);
|
|
287
|
+
/** Ctrl+L on a source tab: list it again. */
|
|
288
|
+
const rescan = useCallback(() => {
|
|
289
|
+
if (agentId !== undefined)
|
|
290
|
+
void list(agentId);
|
|
291
|
+
}, [agentId, list]);
|
|
292
|
+
return {
|
|
293
|
+
sources,
|
|
294
|
+
rows,
|
|
295
|
+
loading,
|
|
296
|
+
groups,
|
|
297
|
+
selected,
|
|
298
|
+
railFocus,
|
|
299
|
+
visibleRows,
|
|
300
|
+
rowIndex,
|
|
301
|
+
focusedRow,
|
|
302
|
+
pane: view.pane,
|
|
303
|
+
selectGroup,
|
|
304
|
+
moveRail,
|
|
305
|
+
moveList,
|
|
306
|
+
activateRail,
|
|
307
|
+
activateList,
|
|
308
|
+
focusRow,
|
|
309
|
+
openRow,
|
|
310
|
+
rescan,
|
|
311
|
+
};
|
|
312
|
+
}
|
|
@@ -15,6 +15,7 @@ import React from 'react';
|
|
|
15
15
|
import type { SessionSummary } from '../../dsh-adapter/sessions/index.js';
|
|
16
16
|
import type { TuiWorkspaceEntry, TuiWorkspaceTarget } from '../../workspaces.js';
|
|
17
17
|
import type { ChannelUi as Channel } from '../../adapter/channel/ui-policy.js';
|
|
18
|
+
import type { ResumeResult } from '../../adapter/ports/channel-view.js';
|
|
18
19
|
import { SupervisorLiveState, RailEntry } from './model.js';
|
|
19
20
|
/** Everything the screen owns that the model needs to read. */
|
|
20
21
|
export interface SessionSupervisorInput {
|
|
@@ -22,7 +23,7 @@ export interface SessionSupervisorInput {
|
|
|
22
23
|
/** Home directory, for collapsing paths to `~`. */
|
|
23
24
|
readonly home: string;
|
|
24
25
|
/** Mount a persisted session (the channel unified resume path). */
|
|
25
|
-
onOpenSession(sessionId: string): Promise<
|
|
26
|
+
onOpenSession(sessionId: string): Promise<ResumeResult>;
|
|
26
27
|
/** Start a fresh session in the workspace at `path`. */
|
|
27
28
|
onNewSession(target: TuiWorkspaceTarget): Promise<boolean>;
|
|
28
29
|
/** Stop a background session of this terminal; false when it is not ours. */
|
|
@@ -41,6 +42,7 @@ export declare function useSessionSupervisor(input: SessionSupervisorInput): {
|
|
|
41
42
|
entries: readonly RailEntry[];
|
|
42
43
|
sessions: readonly SessionSummary[];
|
|
43
44
|
loading: boolean;
|
|
45
|
+
refreshing: boolean;
|
|
44
46
|
notice: {
|
|
45
47
|
text: string;
|
|
46
48
|
tone: "info" | "error";
|
|
@@ -104,6 +106,7 @@ export declare function useSessionSupervisor(input: SessionSupervisorInput): {
|
|
|
104
106
|
sessionWidth: number;
|
|
105
107
|
railEntryCapacity: number;
|
|
106
108
|
sessionListHeight: number;
|
|
109
|
+
noticeRows: string[];
|
|
107
110
|
persistPin: (id: string, pinned: boolean) => void;
|
|
108
111
|
selectEntry: (entry: RailEntry) => void;
|
|
109
112
|
openSession: (session: SessionSummary) => void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useSessionSupervisor.d.ts","sourceRoot":"","sources":["../../../../src/screens/sessionSupervisor/useSessionSupervisor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAiD,MAAM,OAAO,CAAA;AAOrE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qCAAqC,CAAA;AACzE,OAAO,KAAK,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAA;AAChF,OAAO,KAAK,EAAE,SAAS,IAAI,OAAO,EAAE,MAAM,oCAAoC,CAAA;AAC9E,OAAO,
|
|
1
|
+
{"version":3,"file":"useSessionSupervisor.d.ts","sourceRoot":"","sources":["../../../../src/screens/sessionSupervisor/useSessionSupervisor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAiD,MAAM,OAAO,CAAA;AAOrE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qCAAqC,CAAA;AACzE,OAAO,KAAK,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAA;AAChF,OAAO,KAAK,EAAE,SAAS,IAAI,OAAO,EAAE,MAAM,oCAAoC,CAAA;AAC9E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qCAAqC,CAAA;AAEvE,OAAO,EAA+N,mBAAmB,EAAE,SAAS,EAAgE,MAAM,YAAY,CAAA;AAiDtV,+DAA+D;AAC/D,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;IACzB,mDAAmD;IACnD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,mEAAmE;IACnE,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAAA;IACvD,wDAAwD;IACxD,YAAY,CAAC,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IAC1D,6EAA6E;IAC7E,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IAClD,6EAA6E;IAC7E,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,mBAAmB,GAAG,SAAS,CAAA;IAC/D,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CACtB;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,sBAAsB;;;;;;cAmBnB,MAAM;cAAQ,MAAM,GAAG,OAAO;;;cAA9B,MAAM;cAAQ,MAAM,GAAG,OAAO;;;;0BAwB7D,MAAM,KAAG,MAAM,GAAG,SAAS;;;qBA6F/B,SAAS,KAAG,MAAM;;;;;;;cA6Ca,MAAM;aAAO,MAAM;aAAO,MAAM;cAAQ,MAAM;;;cAA9C,MAAM;aAAO,MAAM;aAAO,MAAM;cAAQ,MAAM;;;cAC1C,MAAM;eAAS,MAAM;;;cAArB,MAAM;eAAS,MAAM;;;;;;cADzB,MAAM;aAAO,MAAM;aAAO,MAAM;cAAQ,MAAM;;;;kBA0ClD,OAAO,CAAC,IAAI,CAAC;;;;;uBAgLxC,MAAM,KAAG,cAAc,GAAG,SAAS;wBAWR,IAAI;wBAOJ,IAAI;;;;;;;qBAoCL,MAAM,UAAU,OAAO,KAAG,IAAI;yBAS1B,SAAS,KAAG,IAAI;2BAed,cAAc,KAAG,IAAI;0BAqBtB,iBAAiB,KAAG,IAAI;wBAgB1B,MAAM,SAAS,MAAM,KAAG,IAAI;wBAe5B,MAAM,KAAG,IAAI;2BAWV,cAAc,KAAG,IAAI;qBAiB7B,IAAI;0BAKG,SAAS,QAAQ,MAAM,KAAG,IAAI;mBAarC,CAAC,GAAG,CAAC,CAAC,KAAG,IAAI;sBAWV,CAAC,GAAG,CAAC,CAAC,KAAG,IAAI;;EAuEnD"}
|