@drscrewdriver/dsh-better-display 0.3.3-fork.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/CHANGELOG.md +259 -0
- package/DESIGN.md +72 -0
- package/LICENSE +24 -0
- package/README.en.md +55 -0
- package/README.md +55 -0
- package/THIRD_PARTY_NOTICES.md +65 -0
- package/compat/harness-rc2.json +99 -0
- package/cordis.patch.yml +5 -0
- package/cordis.yml +4 -0
- package/docs/official-rendering-bridge.md +65 -0
- package/dshx.yml +5 -0
- package/lib/client.js +49292 -0
- package/lib/client.js.map +1 -0
- package/lib/dsh-better-display.js +101 -0
- package/lib/types/client/Blocks.d.ts +49 -0
- package/lib/types/client/Blocks.d.ts.map +1 -0
- package/lib/types/client/Blocks.js +143 -0
- package/lib/types/client/Blocks.js.map +1 -0
- package/lib/types/client/ChoreographedFlow.d.ts +22 -0
- package/lib/types/client/ChoreographedFlow.d.ts.map +1 -0
- package/lib/types/client/ChoreographedFlow.js +218 -0
- package/lib/types/client/ChoreographedFlow.js.map +1 -0
- package/lib/types/client/ClosedProcessSummary.d.ts +9 -0
- package/lib/types/client/ClosedProcessSummary.d.ts.map +1 -0
- package/lib/types/client/ClosedProcessSummary.js +16 -0
- package/lib/types/client/ClosedProcessSummary.js.map +1 -0
- package/lib/types/client/DiffPanel.d.ts +13 -0
- package/lib/types/client/DiffPanel.d.ts.map +1 -0
- package/lib/types/client/DiffPanel.js +115 -0
- package/lib/types/client/DiffPanel.js.map +1 -0
- package/lib/types/client/LiveFold.d.ts +10 -0
- package/lib/types/client/LiveFold.d.ts.map +1 -0
- package/lib/types/client/LiveFold.js +39 -0
- package/lib/types/client/LiveFold.js.map +1 -0
- package/lib/types/client/McpAppFrame.d.ts +32 -0
- package/lib/types/client/McpAppFrame.d.ts.map +1 -0
- package/lib/types/client/McpAppFrame.js +193 -0
- package/lib/types/client/McpAppFrame.js.map +1 -0
- package/lib/types/client/OfficialContent.d.ts +25 -0
- package/lib/types/client/OfficialContent.d.ts.map +1 -0
- package/lib/types/client/OfficialContent.js +53 -0
- package/lib/types/client/OfficialContent.js.map +1 -0
- package/lib/types/client/Reader.d.ts +3 -0
- package/lib/types/client/Reader.d.ts.map +1 -0
- package/lib/types/client/Reader.js +603 -0
- package/lib/types/client/Reader.js.map +1 -0
- package/lib/types/client/ReasoningCard.d.ts +11 -0
- package/lib/types/client/ReasoningCard.d.ts.map +1 -0
- package/lib/types/client/ReasoningCard.js +322 -0
- package/lib/types/client/ReasoningCard.js.map +1 -0
- package/lib/types/client/SettingsSection.d.ts +37 -0
- package/lib/types/client/SettingsSection.d.ts.map +1 -0
- package/lib/types/client/SettingsSection.js +63 -0
- package/lib/types/client/SettingsSection.js.map +1 -0
- package/lib/types/client/StickyLane.d.ts +8 -0
- package/lib/types/client/StickyLane.d.ts.map +1 -0
- package/lib/types/client/StickyLane.js +39 -0
- package/lib/types/client/StickyLane.js.map +1 -0
- package/lib/types/client/TimelineRail.d.ts +10 -0
- package/lib/types/client/TimelineRail.d.ts.map +1 -0
- package/lib/types/client/TimelineRail.js +168 -0
- package/lib/types/client/TimelineRail.js.map +1 -0
- package/lib/types/client/ToolActivity.d.ts +17 -0
- package/lib/types/client/ToolActivity.d.ts.map +1 -0
- package/lib/types/client/ToolActivity.js +244 -0
- package/lib/types/client/ToolActivity.js.map +1 -0
- package/lib/types/client/TurnMetrics.d.ts +11 -0
- package/lib/types/client/TurnMetrics.d.ts.map +1 -0
- package/lib/types/client/TurnMetrics.js +44 -0
- package/lib/types/client/TurnMetrics.js.map +1 -0
- package/lib/types/client/WaitClock.d.ts +16 -0
- package/lib/types/client/WaitClock.d.ts.map +1 -0
- package/lib/types/client/WaitClock.js +29 -0
- package/lib/types/client/WaitClock.js.map +1 -0
- package/lib/types/client/WaitingClock.d.ts +15 -0
- package/lib/types/client/WaitingClock.d.ts.map +1 -0
- package/lib/types/client/WaitingClock.js +30 -0
- package/lib/types/client/WaitingClock.js.map +1 -0
- package/lib/types/client/WaitingStatus.d.ts +6 -0
- package/lib/types/client/WaitingStatus.d.ts.map +1 -0
- package/lib/types/client/WaitingStatus.js +9 -0
- package/lib/types/client/WaitingStatus.js.map +1 -0
- package/lib/types/client/conversation-scroll.d.ts +19 -0
- package/lib/types/client/conversation-scroll.d.ts.map +1 -0
- package/lib/types/client/conversation-scroll.js +25 -0
- package/lib/types/client/conversation-scroll.js.map +1 -0
- package/lib/types/client/deliverables.d.ts +24 -0
- package/lib/types/client/deliverables.d.ts.map +1 -0
- package/lib/types/client/deliverables.js +104 -0
- package/lib/types/client/deliverables.js.map +1 -0
- package/lib/types/client/entry-policy.d.ts +10 -0
- package/lib/types/client/entry-policy.d.ts.map +1 -0
- package/lib/types/client/entry-policy.js +24 -0
- package/lib/types/client/entry-policy.js.map +1 -0
- package/lib/types/client/entry.d.ts +4 -0
- package/lib/types/client/entry.d.ts.map +1 -0
- package/lib/types/client/entry.js +34 -0
- package/lib/types/client/entry.js.map +1 -0
- package/lib/types/client/fold-choreography.d.ts +40 -0
- package/lib/types/client/fold-choreography.d.ts.map +1 -0
- package/lib/types/client/fold-choreography.js +48 -0
- package/lib/types/client/fold-choreography.js.map +1 -0
- package/lib/types/client/fold-intensity.d.ts +11 -0
- package/lib/types/client/fold-intensity.d.ts.map +1 -0
- package/lib/types/client/fold-intensity.js +32 -0
- package/lib/types/client/fold-intensity.js.map +1 -0
- package/lib/types/client/index.d.ts +7 -0
- package/lib/types/client/index.d.ts.map +1 -0
- package/lib/types/client/index.js +195 -0
- package/lib/types/client/index.js.map +1 -0
- package/lib/types/client/live-turn.d.ts +58 -0
- package/lib/types/client/live-turn.d.ts.map +1 -0
- package/lib/types/client/live-turn.js +190 -0
- package/lib/types/client/live-turn.js.map +1 -0
- package/lib/types/client/markdown/MarkdownText.d.ts +41 -0
- package/lib/types/client/markdown/MarkdownText.d.ts.map +1 -0
- package/lib/types/client/markdown/MarkdownText.js +178 -0
- package/lib/types/client/markdown/MarkdownText.js.map +1 -0
- package/lib/types/client/markdown/cjkFriendlyStrong.d.ts +9 -0
- package/lib/types/client/markdown/cjkFriendlyStrong.d.ts.map +1 -0
- package/lib/types/client/markdown/cjkFriendlyStrong.js +73 -0
- package/lib/types/client/markdown/cjkFriendlyStrong.js.map +1 -0
- package/lib/types/client/markdown/incremental.d.ts +69 -0
- package/lib/types/client/markdown/incremental.d.ts.map +1 -0
- package/lib/types/client/markdown/incremental.js +108 -0
- package/lib/types/client/markdown/incremental.js.map +1 -0
- package/lib/types/client/markdown/katex.d.ts +27 -0
- package/lib/types/client/markdown/katex.d.ts.map +1 -0
- package/lib/types/client/markdown/katex.js +88 -0
- package/lib/types/client/markdown/katex.js.map +1 -0
- package/lib/types/client/markdown/mathCompatibility.d.ts +11 -0
- package/lib/types/client/markdown/mathCompatibility.d.ts.map +1 -0
- package/lib/types/client/markdown/mathCompatibility.js +293 -0
- package/lib/types/client/markdown/mathCompatibility.js.map +1 -0
- package/lib/types/client/markdown/parse.d.ts +25 -0
- package/lib/types/client/markdown/parse.d.ts.map +1 -0
- package/lib/types/client/markdown/parse.js +42 -0
- package/lib/types/client/markdown/parse.js.map +1 -0
- package/lib/types/client/markdown/render.d.ts +121 -0
- package/lib/types/client/markdown/render.d.ts.map +1 -0
- package/lib/types/client/markdown/render.js +452 -0
- package/lib/types/client/markdown/render.js.map +1 -0
- package/lib/types/client/mcp-app.d.ts +50 -0
- package/lib/types/client/mcp-app.d.ts.map +1 -0
- package/lib/types/client/mcp-app.js +328 -0
- package/lib/types/client/mcp-app.js.map +1 -0
- package/lib/types/client/message-chrome.d.ts +19 -0
- package/lib/types/client/message-chrome.d.ts.map +1 -0
- package/lib/types/client/message-chrome.js +42 -0
- package/lib/types/client/message-chrome.js.map +1 -0
- package/lib/types/client/motion.d.ts +38 -0
- package/lib/types/client/motion.d.ts.map +1 -0
- package/lib/types/client/motion.js +453 -0
- package/lib/types/client/motion.js.map +1 -0
- package/lib/types/client/native/ContextBody.d.ts +130 -0
- package/lib/types/client/native/ContextBody.d.ts.map +1 -0
- package/lib/types/client/native/ContextBody.js +398 -0
- package/lib/types/client/native/ContextBody.js.map +1 -0
- package/lib/types/client/native/ContextInjectionRow.d.ts +35 -0
- package/lib/types/client/native/ContextInjectionRow.d.ts.map +1 -0
- package/lib/types/client/native/ContextInjectionRow.js +42 -0
- package/lib/types/client/native/ContextInjectionRow.js.map +1 -0
- package/lib/types/client/native/ReferenceIcon.d.ts +16 -0
- package/lib/types/client/native/ReferenceIcon.d.ts.map +1 -0
- package/lib/types/client/native/ReferenceIcon.js +16 -0
- package/lib/types/client/native/ReferenceIcon.js.map +1 -0
- package/lib/types/client/native/tool-call-model.d.ts +58 -0
- package/lib/types/client/native/tool-call-model.d.ts.map +1 -0
- package/lib/types/client/native/tool-call-model.js +219 -0
- package/lib/types/client/native/tool-call-model.js.map +1 -0
- package/lib/types/client/native/tool-update.d.ts +23 -0
- package/lib/types/client/native/tool-update.d.ts.map +1 -0
- package/lib/types/client/native/tool-update.js +22 -0
- package/lib/types/client/native/tool-update.js.map +1 -0
- package/lib/types/client/official-slots.d.ts +71 -0
- package/lib/types/client/official-slots.d.ts.map +1 -0
- package/lib/types/client/official-slots.js +183 -0
- package/lib/types/client/official-slots.js.map +1 -0
- package/lib/types/client/open-file.d.ts +29 -0
- package/lib/types/client/open-file.d.ts.map +1 -0
- package/lib/types/client/open-file.js +71 -0
- package/lib/types/client/open-file.js.map +1 -0
- package/lib/types/client/pending-submission.d.ts +28 -0
- package/lib/types/client/pending-submission.d.ts.map +1 -0
- package/lib/types/client/pending-submission.js +28 -0
- package/lib/types/client/pending-submission.js.map +1 -0
- package/lib/types/client/platform-media.d.ts +6 -0
- package/lib/types/client/platform-media.d.ts.map +1 -0
- package/lib/types/client/platform-media.js +18 -0
- package/lib/types/client/platform-media.js.map +1 -0
- package/lib/types/client/primitive-labels.d.ts +10 -0
- package/lib/types/client/primitive-labels.d.ts.map +1 -0
- package/lib/types/client/primitive-labels.js +38 -0
- package/lib/types/client/primitive-labels.js.map +1 -0
- package/lib/types/client/projection.d.ts +41 -0
- package/lib/types/client/projection.d.ts.map +1 -0
- package/lib/types/client/projection.js +112 -0
- package/lib/types/client/projection.js.map +1 -0
- package/lib/types/client/reasoning-follow.d.ts +10 -0
- package/lib/types/client/reasoning-follow.d.ts.map +1 -0
- package/lib/types/client/reasoning-follow.js +21 -0
- package/lib/types/client/reasoning-follow.js.map +1 -0
- package/lib/types/client/resource-address.d.ts +3 -0
- package/lib/types/client/resource-address.d.ts.map +1 -0
- package/lib/types/client/resource-address.js +33 -0
- package/lib/types/client/resource-address.js.map +1 -0
- package/lib/types/client/settings-copy.d.ts +7 -0
- package/lib/types/client/settings-copy.d.ts.map +1 -0
- package/lib/types/client/settings-copy.js +53 -0
- package/lib/types/client/settings-copy.js.map +1 -0
- package/lib/types/client/settings-slots.d.ts +23 -0
- package/lib/types/client/settings-slots.d.ts.map +1 -0
- package/lib/types/client/settings-slots.js +2 -0
- package/lib/types/client/settings-slots.js.map +1 -0
- package/lib/types/client/settings.d.ts +4 -0
- package/lib/types/client/settings.d.ts.map +1 -0
- package/lib/types/client/settings.js +60 -0
- package/lib/types/client/settings.js.map +1 -0
- package/lib/types/client/skill-status.d.ts +25 -0
- package/lib/types/client/skill-status.d.ts.map +1 -0
- package/lib/types/client/skill-status.js +44 -0
- package/lib/types/client/skill-status.js.map +1 -0
- package/lib/types/client/store.d.ts +35 -0
- package/lib/types/client/store.d.ts.map +1 -0
- package/lib/types/client/store.js +37 -0
- package/lib/types/client/store.js.map +1 -0
- package/lib/types/client/stream-buffer.d.ts +37 -0
- package/lib/types/client/stream-buffer.d.ts.map +1 -0
- package/lib/types/client/stream-buffer.js +150 -0
- package/lib/types/client/stream-buffer.js.map +1 -0
- package/lib/types/client/streaming.d.ts +18 -0
- package/lib/types/client/streaming.d.ts.map +1 -0
- package/lib/types/client/streaming.js +84 -0
- package/lib/types/client/streaming.js.map +1 -0
- package/lib/types/client/timeline.d.ts +26 -0
- package/lib/types/client/timeline.d.ts.map +1 -0
- package/lib/types/client/timeline.js +48 -0
- package/lib/types/client/timeline.js.map +1 -0
- package/lib/types/client/tool-activity.d.ts +98 -0
- package/lib/types/client/tool-activity.d.ts.map +1 -0
- package/lib/types/client/tool-activity.js +301 -0
- package/lib/types/client/tool-activity.js.map +1 -0
- package/lib/types/client/types.d.ts +91 -0
- package/lib/types/client/types.d.ts.map +1 -0
- package/lib/types/client/types.js +2 -0
- package/lib/types/client/types.js.map +1 -0
- package/lib/types/client/waiting-clock.d.ts +49 -0
- package/lib/types/client/waiting-clock.d.ts.map +1 -0
- package/lib/types/client/waiting-clock.js +80 -0
- package/lib/types/client/waiting-clock.js.map +1 -0
- package/lib/types/client/word-motion.d.ts +16 -0
- package/lib/types/client/word-motion.d.ts.map +1 -0
- package/lib/types/client/word-motion.js +90 -0
- package/lib/types/client/word-motion.js.map +1 -0
- package/lib/types/client/word-timeline.d.ts +32 -0
- package/lib/types/client/word-timeline.d.ts.map +1 -0
- package/lib/types/client/word-timeline.js +119 -0
- package/lib/types/client/word-timeline.js.map +1 -0
- package/lib/types/dsh-better-display.d.ts +17 -0
- package/lib/types/dsh-better-display.d.ts.map +1 -0
- package/lib/types/dsh-better-display.js +93 -0
- package/lib/types/dsh-better-display.js.map +1 -0
- package/lib/types/skill-roots.d.ts +22 -0
- package/lib/types/skill-roots.d.ts.map +1 -0
- package/lib/types/skill-roots.js +126 -0
- package/lib/types/skill-roots.js.map +1 -0
- package/lib/types/skill-status.d.ts +36 -0
- package/lib/types/skill-status.d.ts.map +1 -0
- package/lib/types/skill-status.js +51 -0
- package/lib/types/skill-status.js.map +1 -0
- package/package.json +133 -0
- package/scripts/build-motion-fixture.mjs +26 -0
- package/scripts/check-harness-compat.mjs +71 -0
- package/scripts/link-harness-dependencies.mjs +54 -0
- package/scripts/verify-auto-fold.mjs +114 -0
- package/scripts/verify-official-bridge.mjs +121 -0
- package/scripts/verify-tool-shimmer.mjs +62 -0
- package/skills/generative-mcpapps/SKILL.md +47 -0
- package/skills/generative-mcpapps/examples/interactive_quiz.html +153 -0
- package/skills/generative-mcpapps/references/html_boilerplate.md +122 -0
- package/skills/generative-mcpapps/references/sep1865_protocol.md +54 -0
- package/src/client/Blocks.tsx +213 -0
- package/src/client/ChoreographedFlow.tsx +232 -0
- package/src/client/ClosedProcessSummary.tsx +23 -0
- package/src/client/DiffPanel.tsx +124 -0
- package/src/client/LiveFold.tsx +43 -0
- package/src/client/McpAppFrame.module.css +210 -0
- package/src/client/McpAppFrame.tsx +317 -0
- package/src/client/OfficialContent.tsx +69 -0
- package/src/client/Reader.module.css +692 -0
- package/src/client/Reader.tsx +878 -0
- package/src/client/ReasoningCard.tsx +306 -0
- package/src/client/SettingsSection.module.css +184 -0
- package/src/client/SettingsSection.tsx +194 -0
- package/src/client/StickyLane.tsx +34 -0
- package/src/client/TimelineRail.module.css +170 -0
- package/src/client/TimelineRail.tsx +262 -0
- package/src/client/ToolActivity.tsx +274 -0
- package/src/client/TurnMetrics.module.css +139 -0
- package/src/client/TurnMetrics.tsx +160 -0
- package/src/client/WaitClock.tsx +33 -0
- package/src/client/WaitingStatus.tsx +13 -0
- package/src/client/conversation-scroll.ts +28 -0
- package/src/client/deliverables.ts +122 -0
- package/src/client/entry-policy.ts +20 -0
- package/src/client/entry.tsx +40 -0
- package/src/client/fold-choreography.ts +55 -0
- package/src/client/fold-intensity.ts +36 -0
- package/src/client/index.tsx +222 -0
- package/src/client/live-turn.ts +198 -0
- package/src/client/markdown/MarkdownText.module.css +304 -0
- package/src/client/markdown/MarkdownText.tsx +195 -0
- package/src/client/markdown/cjkFriendlyStrong.ts +84 -0
- package/src/client/markdown/incremental.ts +131 -0
- package/src/client/markdown/katex.tsx +91 -0
- package/src/client/markdown/mathCompatibility.ts +350 -0
- package/src/client/markdown/parse.ts +45 -0
- package/src/client/markdown/render.tsx +647 -0
- package/src/client/markdown/upstream.json +14 -0
- package/src/client/mcp-app.ts +323 -0
- package/src/client/message-chrome.ts +47 -0
- package/src/client/motion.tsx +410 -0
- package/src/client/native/ContextBody.module.css +161 -0
- package/src/client/native/ContextBody.tsx +591 -0
- package/src/client/native/ContextInjectionRow.module.css +66 -0
- package/src/client/native/ContextInjectionRow.tsx +106 -0
- package/src/client/native/ReferenceIcon.tsx +35 -0
- package/src/client/native/tool-call-model.ts +248 -0
- package/src/client/native/tool-update.ts +32 -0
- package/src/client/native/upstream.json +41 -0
- package/src/client/official-slots.tsx +211 -0
- package/src/client/open-file.ts +94 -0
- package/src/client/pending-submission.ts +49 -0
- package/src/client/platform-media.ts +17 -0
- package/src/client/primitive-labels.ts +54 -0
- package/src/client/projection.ts +118 -0
- package/src/client/reasoning-follow.ts +28 -0
- package/src/client/settings-copy.ts +80 -0
- package/src/client/settings-slots.ts +20 -0
- package/src/client/settings.tsx +73 -0
- package/src/client/skill-status.ts +78 -0
- package/src/client/store.ts +75 -0
- package/src/client/stream-buffer.ts +136 -0
- package/src/client/streaming.tsx +70 -0
- package/src/client/style.d.ts +4 -0
- package/src/client/timeline.ts +77 -0
- package/src/client/tool-activity.ts +298 -0
- package/src/client/types.ts +89 -0
- package/src/client/waiting-clock.ts +83 -0
- package/src/client/word-motion.tsx +92 -0
- package/src/client/word-timeline.ts +113 -0
- package/src/dsh-better-display.ts +108 -0
- package/src/skill-roots.ts +138 -0
- package/src/skill-status.ts +81 -0
- package/tests/browser-auto-fold.tsx +64 -0
- package/tests/browser-closed-gap.tsx +20 -0
- package/tests/browser-motion.tsx +65 -0
- package/tests/browser-official-bridge.tsx +152 -0
- package/tests/browser-sticky-lanes.tsx +22 -0
- package/tests/browser-waiting-clock.tsx +14 -0
- package/tests/chat-flow-hook.test.ts +66 -0
- package/tests/cloud-gate.test.ts +34 -0
- package/tests/context-tool-update.test.ts +33 -0
- package/tests/conversation-scroll.test.ts +47 -0
- package/tests/deliverables.test.ts +128 -0
- package/tests/entry.test.ts +42 -0
- package/tests/fixtures/maid-composer-hide.html +31 -0
- package/tests/fold-choreography.test.ts +54 -0
- package/tests/fold-intensity.test.ts +94 -0
- package/tests/footer-precedence.test.ts +129 -0
- package/tests/history-word-regression.test.ts +16 -0
- package/tests/live-turn.test.ts +218 -0
- package/tests/mcp-apps.test.ts +157 -0
- package/tests/message-chrome.test.ts +29 -0
- package/tests/official-slots.test.ts +112 -0
- package/tests/open-file.test.ts +114 -0
- package/tests/pending-submission.test.ts +45 -0
- package/tests/platform-media.test.ts +23 -0
- package/tests/projection.test.ts +184 -0
- package/tests/reasoning-follow.test.ts +34 -0
- package/tests/settings-copy.test.ts +63 -0
- package/tests/skill-status.test.ts +137 -0
- package/tests/stock-install.test.ts +58 -0
- package/tests/stream-buffer.test.ts +102 -0
- package/tests/timeline.test.ts +59 -0
- package/tests/tool-diff.test.ts +57 -0
- package/tests/waiting-clock.test.ts +83 -0
- package/tests/word-timeline.test.ts +146 -0
- package/tsconfig.json +11 -0
- package/tsdown.config.ts +51 -0
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import type { TurnLocation } from '@deepseek-ai/dsh-client-ui-conversation/client';
|
|
2
|
+
import type { MarkdownFileMentions } from '@deepseek-ai/dsh-client-ui-primitives';
|
|
3
|
+
import type { ReaderFlowEntry } from './tool-activity.js';
|
|
4
|
+
import { inputFields, stringValue, toolIdentity } from './tool-activity.js';
|
|
5
|
+
|
|
6
|
+
interface ProducedEntry {
|
|
7
|
+
readonly seq?: number;
|
|
8
|
+
readonly path: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
interface DeliverablesData {
|
|
12
|
+
readonly produced: readonly ProducedEntry[];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** Extract standard filename without directory components. */
|
|
16
|
+
export function basename(path: string): string {
|
|
17
|
+
const normalized = path.replace(/[/\\]+$/, '');
|
|
18
|
+
const at = Math.max(normalized.lastIndexOf('/'), normalized.lastIndexOf('\\'));
|
|
19
|
+
return at === -1 ? normalized : normalized.slice(at + 1);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** Extract parent directory of a path (or '.' if top-level). */
|
|
23
|
+
export function dirname(path: string): string {
|
|
24
|
+
const normalized = path.replace(/[/\\]+$/, '');
|
|
25
|
+
const at = Math.max(normalized.lastIndexOf('/'), normalized.lastIndexOf('\\'));
|
|
26
|
+
return at === -1 ? '.' : normalized.slice(0, at) || '.';
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* The produced-files chip row waits for turn close. Live writes still
|
|
31
|
+
* accumulate, but the row must not interrupt an in-progress process.
|
|
32
|
+
*/
|
|
33
|
+
export function showDeliverablesRow(status: 'open' | 'closed' | 'unknown', paths: readonly string[]): boolean {
|
|
34
|
+
return status === 'closed' && paths.length > 0
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Extract all unique file paths produced/modified in one turn.
|
|
39
|
+
* Respects official deliverables data when available, and falls back to
|
|
40
|
+
* inspecting successful write/edit tool invocations in the turn flow.
|
|
41
|
+
*/
|
|
42
|
+
export function getTurnDeliverables(turn: TurnLocation | undefined, flow?: readonly ReaderFlowEntry[]): readonly string[] {
|
|
43
|
+
const paths: string[] = [];
|
|
44
|
+
const seen = new Set<string>();
|
|
45
|
+
|
|
46
|
+
// 1. Check official deliverables turn data from @deepseek-ai/dsh-client-ui-deliverables
|
|
47
|
+
const deliverables = (turn?.data as { get(key: string): unknown } | undefined)?.get('deliverables') as DeliverablesData | undefined;
|
|
48
|
+
if (deliverables?.produced && Array.isArray(deliverables.produced)) {
|
|
49
|
+
for (const item of deliverables.produced) {
|
|
50
|
+
if (typeof item?.path === 'string' && item.path.trim().length > 0) {
|
|
51
|
+
const clean = item.path.trim();
|
|
52
|
+
if (!seen.has(clean)) {
|
|
53
|
+
seen.add(clean);
|
|
54
|
+
paths.push(clean);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (paths.length > 0) return paths;
|
|
61
|
+
|
|
62
|
+
// 2. Fallback: inspect successful file modification tool calls in the flow
|
|
63
|
+
if (flow) {
|
|
64
|
+
for (const item of flow) {
|
|
65
|
+
if (item.kind !== 'tool' || !item.block) continue;
|
|
66
|
+
// Skip failed tool results
|
|
67
|
+
if ('isError' in item.block && item.block.isError) continue;
|
|
68
|
+
const { name, raw } = toolIdentity(item);
|
|
69
|
+
const toolName = name !== '工具调用' ? name : ((item.block as unknown as { name?: string }).name || (item.block as unknown as { call?: { name?: string } }).call?.name);
|
|
70
|
+
const toolRaw = raw || ((item.block as unknown as { argsRaw?: string }).argsRaw || (item.block as unknown as { call?: { argsRaw?: string } }).call?.argsRaw || '');
|
|
71
|
+
if (toolName === 'write' || toolName === 'edit' || toolName === 'apply_patch') {
|
|
72
|
+
const args = inputFields(toolRaw);
|
|
73
|
+
const target = stringValue(args, 'file_path', 'path', 'filename', 'filePath');
|
|
74
|
+
if (target && !seen.has(target)) {
|
|
75
|
+
seen.add(target);
|
|
76
|
+
paths.push(target);
|
|
77
|
+
}
|
|
78
|
+
} else if (toolName === 'str_replace_editor') {
|
|
79
|
+
const args = inputFields(toolRaw);
|
|
80
|
+
const cmd = stringValue(args, 'command');
|
|
81
|
+
if (cmd === 'create' || cmd === 'str_replace' || cmd === 'insert') {
|
|
82
|
+
const target = stringValue(args, 'path');
|
|
83
|
+
if (target && !seen.has(target)) {
|
|
84
|
+
seen.add(target);
|
|
85
|
+
paths.push(target);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return paths;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** The single produced path whose basename is exactly value, or undefined. */
|
|
96
|
+
function onlyPathWithBasename(paths: readonly string[], value: string): string | undefined {
|
|
97
|
+
const matches = paths.filter(path => basename(path) === value);
|
|
98
|
+
return matches.length === 1 ? matches[0] : undefined;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* File-mention resolver: converts inline-code tokens matching produced paths
|
|
103
|
+
* into clickable file references that open the corresponding file on the host.
|
|
104
|
+
*/
|
|
105
|
+
export function createProducedFileMentions(
|
|
106
|
+
paths: readonly string[],
|
|
107
|
+
openFile: (path: string) => void,
|
|
108
|
+
): MarkdownFileMentions {
|
|
109
|
+
return {
|
|
110
|
+
resolve(value: string) {
|
|
111
|
+
if (!value || value.includes('\n')) return undefined;
|
|
112
|
+
const clean = value.trim();
|
|
113
|
+
const path = paths.includes(clean) ? clean : onlyPathWithBasename(paths, clean);
|
|
114
|
+
if (path === undefined) return undefined;
|
|
115
|
+
return {
|
|
116
|
+
open: () => { openFile(path); },
|
|
117
|
+
label: `打开 ${path}`,
|
|
118
|
+
title: path,
|
|
119
|
+
};
|
|
120
|
+
},
|
|
121
|
+
};
|
|
122
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/** Select the reading view on entry, without fighting a later explicit tab choice. */
|
|
2
|
+
export class ReaderEntryPolicy {
|
|
3
|
+
private entered = false;
|
|
4
|
+
|
|
5
|
+
constructor(private readonly requested: boolean, private readonly consumeRequest: () => void = () => {}) {}
|
|
6
|
+
|
|
7
|
+
select(view: string | null | undefined): 'reader' | null {
|
|
8
|
+
const requested = !this.entered && this.requested;
|
|
9
|
+
if (!this.entered) {
|
|
10
|
+
this.entered = true;
|
|
11
|
+
if (this.requested) this.consumeRequest();
|
|
12
|
+
}
|
|
13
|
+
return (requested || view == null) && view !== 'reader' ? 'reader' : null;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function readerEntryRequested(search: string): boolean {
|
|
18
|
+
const value = new URLSearchParams(search).get('reader');
|
|
19
|
+
return value === '1' || /^0\.1\.0-trial\.\d+$/.test(value ?? '');
|
|
20
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { useLayoutEffect } from 'react';
|
|
2
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
3
|
+
import type { ConversationStore } from '@deepseek-ai/dsh-client-ui-conversation/client';
|
|
4
|
+
import type { PropsRuntime, PropsStore, StoreDecl } from '@deepseek-ai/dsh-client-ui-slots';
|
|
5
|
+
import type {} from '@deepseek-ai/dsh-client-ui-renderer/client';
|
|
6
|
+
import { ReaderEntryPolicy, readerEntryRequested } from './entry-policy.js';
|
|
7
|
+
|
|
8
|
+
function isConversationStore(store: StoreDecl | undefined): store is ConversationStore {
|
|
9
|
+
return typeof store === 'object' && store !== null
|
|
10
|
+
&& store.spec.persist === 'dsh.conversation'
|
|
11
|
+
&& typeof store.spec.actions.setView === 'function';
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
type EntryProps = PropsRuntime<'conversation.input.dock'> & PropsStore<ConversationStore> & { policy: ReaderEntryPolicy };
|
|
15
|
+
|
|
16
|
+
function ReaderEntry({ useStore, actions, policy }: EntryProps) {
|
|
17
|
+
const view = useStore(state => state.view);
|
|
18
|
+
useLayoutEffect(() => {
|
|
19
|
+
const next = policy.select(view);
|
|
20
|
+
if (next) actions.setView(next);
|
|
21
|
+
}, [view, actions, policy]);
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** Reuse the native store handle; its framework-owned instance preserves drafts. */
|
|
26
|
+
export function installReaderEntry(ctx: Context): () => void {
|
|
27
|
+
const native = ctx.slots.entriesOfSlot('conversation.session')[0]?.store;
|
|
28
|
+
if (!isConversationStore(native)) throw new Error('DSH Reader cannot bind the native conversation view store.');
|
|
29
|
+
const policy = new ReaderEntryPolicy(readerEntryRequested(location.search), () => {
|
|
30
|
+
const url = new URL(location.href);
|
|
31
|
+
url.searchParams.delete('reader');
|
|
32
|
+
history.replaceState(history.state, '', `${url.pathname}${url.search}${url.hash}`);
|
|
33
|
+
});
|
|
34
|
+
return ctx.slots.register({
|
|
35
|
+
name: 'conversation.input.dock',
|
|
36
|
+
id: 'dsh-better-display-entry',
|
|
37
|
+
store: native,
|
|
38
|
+
inject: () => ({ policy }),
|
|
39
|
+
}, ReaderEntry);
|
|
40
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { LiveStep, LiveTurnItem } from './live-turn.js';
|
|
2
|
+
|
|
3
|
+
/** Presentation clock, not model/network time. */
|
|
4
|
+
export const FOLD_TIMING = {
|
|
5
|
+
collapse: 320,
|
|
6
|
+
count: 160,
|
|
7
|
+
settle: 80,
|
|
8
|
+
reveal: 180,
|
|
9
|
+
/**
|
|
10
|
+
* Slack added to every phase deadline before the watchdog forces the next phase.
|
|
11
|
+
*
|
|
12
|
+
* The choreography advances on animation promises and rendered frames. Both are
|
|
13
|
+
* best-effort: a cancelled animation *rejects* and a compositor that never
|
|
14
|
+
* finishes one leaves its promise pending, while a backgrounded tab stops
|
|
15
|
+
* delivering frames. Without a deadline the machine would sit in a non-idle
|
|
16
|
+
* phase forever, and a non-idle phase freezes both the frame source and the text
|
|
17
|
+
* reveal, so the turn would look stuck until a remount. This is the liveness
|
|
18
|
+
* guarantee that does not depend on any of them.
|
|
19
|
+
*/
|
|
20
|
+
watchdogSlack: 240,
|
|
21
|
+
} as const;
|
|
22
|
+
export type FoldPhase = 'idle' | 'collapse' | 'count' | 'settle' | 'reveal';
|
|
23
|
+
export type FlowRow =
|
|
24
|
+
| { kind: 'summary'; key: string; item: Extract<LiveTurnItem, { kind: 'fold' }> }
|
|
25
|
+
| { kind: 'step'; key: string; step: LiveStep; foldKey?: string };
|
|
26
|
+
|
|
27
|
+
/** A step always has the same parent and React key, even when its membership changes. */
|
|
28
|
+
export function flowRows(items: readonly LiveTurnItem[]): FlowRow[] {
|
|
29
|
+
return items.flatMap((item): FlowRow[] => item.kind === 'fold'
|
|
30
|
+
? [{ kind: 'summary', key: item.key, item }, ...item.steps.map(step => ({ kind: 'step' as const, key: step.key, step, foldKey: item.key }))]
|
|
31
|
+
: [{ kind: 'step', key: item.key, step: item.step }]);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function retiringKeys(before: readonly LiveTurnItem[], after: readonly LiveTurnItem[], open: Readonly<Record<string, boolean>>): string[] {
|
|
35
|
+
const folded = new Set(flowRows(after).filter(row => row.kind === 'step' && row.foldKey && !open[row.foldKey]).map(row => row.key));
|
|
36
|
+
return flowRows(before).filter(row => row.kind === 'step' && !row.foldKey && folded.has(row.key)).map(row => row.key);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Insert only new summary slots. Never admit incoming content during shrink. */
|
|
40
|
+
export function collapseRows(before: readonly LiveTurnItem[], target: readonly LiveTurnItem[]): FlowRow[] {
|
|
41
|
+
const rows = flowRows(before);
|
|
42
|
+
const headers = new Set(rows.filter(row => row.kind === 'summary').map(row => row.key));
|
|
43
|
+
for (const item of target) {
|
|
44
|
+
if (item.kind !== 'fold' || headers.has(item.key)) continue;
|
|
45
|
+
const members = new Set(item.steps.map(step => step.key));
|
|
46
|
+
const at = rows.findIndex(row => members.has(row.key));
|
|
47
|
+
if (at >= 0) rows.splice(at, 0, { kind: 'summary', key: item.key, item: { ...item, summary: item.summary.replace(/\d+/g, '0') } });
|
|
48
|
+
}
|
|
49
|
+
return rows;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function containsNewUser(before: readonly LiveTurnItem[], after: readonly LiveTurnItem[]): boolean {
|
|
53
|
+
const users = new Set(before.filter(item => item.kind === 'user').map(item => item.key));
|
|
54
|
+
return after.some(item => item.kind === 'user' && !users.has(item.key));
|
|
55
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/** Three-stop fold intensity persisted on `dsh.reader.v1`. Default is standard. */
|
|
2
|
+
export type FoldIntensity = 0 | 1 | 2;
|
|
3
|
+
|
|
4
|
+
export const FOLD_INTENSITY_DEFAULT: FoldIntensity = 1;
|
|
5
|
+
|
|
6
|
+
/** Level 0: nothing auto-folds. Level 1: current-main choreography. Level 2: process summaries. */
|
|
7
|
+
export function foldIntensityOf(state: unknown): FoldIntensity {
|
|
8
|
+
const rec = state && typeof state === 'object' ? state as Record<string, unknown> : {};
|
|
9
|
+
if (rec.foldIntensity === 0 || rec.foldIntensity === 1 || rec.foldIntensity === 2) return rec.foldIntensity;
|
|
10
|
+
if (rec.processOnly === true) return 2;
|
|
11
|
+
if (rec.autoFold === false) return 0;
|
|
12
|
+
return FOLD_INTENSITY_DEFAULT;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function frostedGlassOf(state: unknown): boolean {
|
|
16
|
+
if (state && typeof state === 'object' && 'frostedGlass' in state) {
|
|
17
|
+
return (state as { frostedGlass?: unknown }).frostedGlass === true;
|
|
18
|
+
}
|
|
19
|
+
return state === true;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** Bubbles default on: snapshots persisted before the preference read as on. */
|
|
23
|
+
export function bubblesOf(state: unknown): boolean {
|
|
24
|
+
if (state && typeof state === 'object' && 'bubbles' in state) {
|
|
25
|
+
return (state as { bubbles?: unknown }).bubbles !== false;
|
|
26
|
+
}
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function autoFoldFromIntensity(intensity: FoldIntensity): boolean {
|
|
31
|
+
return intensity !== 0;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function processOnlyFromIntensity(intensity: FoldIntensity): boolean {
|
|
35
|
+
return intensity === 2;
|
|
36
|
+
}
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
2
|
+
import type {} from '@deepseek-ai/dsh-api-remotes/client';
|
|
3
|
+
import type {} from '@deepseek-ai/dsh-api-session-controller/client';
|
|
4
|
+
import type {} from '@deepseek-ai/dsh-client-connection/client';
|
|
5
|
+
import type { SessionId } from '@deepseek-ai/dsh-session/types';
|
|
6
|
+
import { resolveWorkspacePath } from '@deepseek-ai/dsh-util-workspace-path';
|
|
7
|
+
import * as workspacePathPkg from '@deepseek-ai/dsh-util-workspace-path';
|
|
8
|
+
import { dirname } from './deliverables.js';
|
|
9
|
+
import type {} from '@deepseek-ai/dsh-client-ui-conversation/client';
|
|
10
|
+
import type {} from '@deepseek-ai/dsh-client-ui-renderer/client';
|
|
11
|
+
import { Reader } from './Reader.js';
|
|
12
|
+
import { createReaderStore } from './store.js';
|
|
13
|
+
import { installReaderEntry } from './entry.js';
|
|
14
|
+
import { installBetterDisplaySettings } from './settings.js';
|
|
15
|
+
import { fillComposerDom } from './mcp-app.js';
|
|
16
|
+
import { fileAddressFor, modeFromSnapshot, openDeliverableFile } from './open-file.js';
|
|
17
|
+
import type { ReaderInjected } from './types.js';
|
|
18
|
+
import type { ImageAttachmentRef } from '@deepseek-ai/dsh-attachment';
|
|
19
|
+
import { installOfficialSlots, officialChildren, type CompositionRegistry } from './official-slots.js';
|
|
20
|
+
|
|
21
|
+
/** Structural face of the sanctioned per-session composer writer. */
|
|
22
|
+
interface ComposerShell {
|
|
23
|
+
setDraft: (text: string) => void;
|
|
24
|
+
}
|
|
25
|
+
interface ConversationFace {
|
|
26
|
+
input?: { shell?: (id: SessionId) => ComposerShell };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
interface SidebarRightFace {
|
|
30
|
+
openResource?: (address: string, options?: { params?: { line?: number } }) => void;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const officialFileAddressFor = (workspacePathPkg as {
|
|
34
|
+
fileAddressFor?: typeof fileAddressFor;
|
|
35
|
+
}).fileAddressFor;
|
|
36
|
+
|
|
37
|
+
async function openWorkspacePath(
|
|
38
|
+
ctx: Context,
|
|
39
|
+
path: string,
|
|
40
|
+
): Promise<void> {
|
|
41
|
+
const remote = ctx.remote as unknown as { session?: { openWorkspacePath: (arg: { path: string }) => Promise<{ ok: boolean; error?: { message: string } }> } } | undefined;
|
|
42
|
+
const remoteSession = remote?.session
|
|
43
|
+
?? (ctx.get?.('remote.session') as unknown as { openWorkspacePath: (arg: { path: string }) => Promise<{ ok: boolean; error?: { message: string } }> } | undefined)
|
|
44
|
+
?? ((ctx.get?.('remote') as unknown as { session?: { openWorkspacePath: (arg: { path: string }) => Promise<{ ok: boolean; error?: { message: string } }> } })?.session);
|
|
45
|
+
if (remoteSession?.openWorkspacePath) {
|
|
46
|
+
const result = await remoteSession.openWorkspacePath({ path });
|
|
47
|
+
if (!result?.ok) {
|
|
48
|
+
console.warn('[dsh-better-display] openWorkspacePath failed:', result?.error?.message);
|
|
49
|
+
}
|
|
50
|
+
} else {
|
|
51
|
+
console.warn('[dsh-better-display] remote.session is not available');
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export type { ReaderBlockOwner } from './types.js';
|
|
56
|
+
export { McpAppFrame } from './McpAppFrame.js';
|
|
57
|
+
export const name = 'dsh-better-display-client';
|
|
58
|
+
export const inject = ['slots', 'sessions', 'conversation', 'uiConversation', 'remote', 'remote.session'];
|
|
59
|
+
|
|
60
|
+
export function apply(ctx: Context): void {
|
|
61
|
+
const store = createReaderStore();
|
|
62
|
+
// conversation.view is session-scoped, so session persist keys are
|
|
63
|
+
// `dsh.reader.v1.<sessionId>`. One root instance keeps glass, fold
|
|
64
|
+
// intensity, and open-mode on the unsuffixed `dsh.reader.v1` key.
|
|
65
|
+
const prefs = store.create();
|
|
66
|
+
installBetterDisplaySettings(ctx, prefs);
|
|
67
|
+
ctx.slots.inject('conversation.chat.node', function* () {
|
|
68
|
+
yield ctx.slots.register({
|
|
69
|
+
name: 'conversation.view',
|
|
70
|
+
id: 'reader',
|
|
71
|
+
order: -5,
|
|
72
|
+
label: () => '阅读',
|
|
73
|
+
locale: 'chat',
|
|
74
|
+
children: {
|
|
75
|
+
'dsh-better-display.block': { kind: 'chain', scope: 'session' },
|
|
76
|
+
...officialChildren(ctx.slots as unknown as CompositionRegistry),
|
|
77
|
+
},
|
|
78
|
+
store,
|
|
79
|
+
inject: (sessionId: SessionId): ReaderInjected => {
|
|
80
|
+
const session = () => {
|
|
81
|
+
const current = ctx.sessions.binding(sessionId)?.session;
|
|
82
|
+
if (!current) throw new Error('阅读页对应的会话已关闭。');
|
|
83
|
+
return current;
|
|
84
|
+
};
|
|
85
|
+
return {
|
|
86
|
+
openPrefs: prefs,
|
|
87
|
+
officialImageLoader: Object.assign(
|
|
88
|
+
(attachment: ImageAttachmentRef) => ctx.uiConversation.imageUrl(sessionId, attachment),
|
|
89
|
+
{ peek: (attachment: ImageAttachmentRef) => ctx.uiConversation.peekImageUrl(sessionId, attachment) },
|
|
90
|
+
),
|
|
91
|
+
officialFileMentions: owner => ctx.get('chatFileMentions')?.forClosing(owner, sessionId),
|
|
92
|
+
officialPreviewFile: path => {
|
|
93
|
+
const cwd = ctx.sessions.list.getSnapshot().byId[sessionId]?.cwd;
|
|
94
|
+
const sidebar = ctx.get('sidebarRight') as SidebarRightFace | undefined;
|
|
95
|
+
if (!sidebar?.openResource) throw new Error('文件预览面板不可用。');
|
|
96
|
+
sidebar.openResource((officialFileAddressFor ?? fileAddressFor)(sessionId, cwd, path));
|
|
97
|
+
},
|
|
98
|
+
officialHost: {
|
|
99
|
+
getSnapshot: () => ctx.remote.$host,
|
|
100
|
+
subscribe: listener => ctx.on('connection/reset', listener),
|
|
101
|
+
},
|
|
102
|
+
loadOlder: async () => { await session().loadOlder(); },
|
|
103
|
+
loadImage: async attachment => {
|
|
104
|
+
const receipt = await session().readAttachment(attachment.attachmentId);
|
|
105
|
+
if (!receipt.ok) throw new Error(receipt.error.message);
|
|
106
|
+
return { data: Uint8Array.from(receipt.value.data), mediaType: receipt.value.attachment.mediaType };
|
|
107
|
+
},
|
|
108
|
+
openFile: async (path, options) => {
|
|
109
|
+
try {
|
|
110
|
+
const cwd = ctx.sessions?.list?.getSnapshot?.()?.byId[sessionId]?.cwd;
|
|
111
|
+
const sidebar = (
|
|
112
|
+
ctx.get?.('sidebarRight')
|
|
113
|
+
?? (ctx as unknown as { sidebarRight?: SidebarRightFace }).sidebarRight
|
|
114
|
+
) as SidebarRightFace | undefined;
|
|
115
|
+
await openDeliverableFile({
|
|
116
|
+
path,
|
|
117
|
+
mode: modeFromSnapshot(prefs),
|
|
118
|
+
sessionId,
|
|
119
|
+
cwd,
|
|
120
|
+
resolveWorkspacePath,
|
|
121
|
+
openExternal: async (absolutePath) => { await openWorkspacePath(ctx, absolutePath); },
|
|
122
|
+
openSidebar: typeof sidebar?.openResource === 'function'
|
|
123
|
+
? (address) => { sidebar.openResource!(address, options?.line === undefined ? undefined : { params: { line: options.line } }); }
|
|
124
|
+
: undefined,
|
|
125
|
+
fileAddressFor: officialFileAddressFor ?? fileAddressFor,
|
|
126
|
+
warn: (message, extra) => { console.warn(message, extra); },
|
|
127
|
+
});
|
|
128
|
+
} catch (error) {
|
|
129
|
+
console.warn('[dsh-better-display] openFile error:', error);
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
revealFile: async (path: string) => {
|
|
133
|
+
try {
|
|
134
|
+
const cwd = ctx.sessions?.list?.getSnapshot?.()?.byId[sessionId]?.cwd;
|
|
135
|
+
const targetPath = resolveWorkspacePath(cwd, path);
|
|
136
|
+
// 1. Try dedicated host endpoint for native file highlighting (open -R / explorer /select)
|
|
137
|
+
try {
|
|
138
|
+
const res = await fetch('/better-display/reveal', {
|
|
139
|
+
method: 'POST',
|
|
140
|
+
headers: { 'Content-Type': 'application/json' },
|
|
141
|
+
body: JSON.stringify({ path: targetPath }),
|
|
142
|
+
});
|
|
143
|
+
if (res.ok) {
|
|
144
|
+
const data = await res.json();
|
|
145
|
+
if (data.ok) return;
|
|
146
|
+
}
|
|
147
|
+
} catch {
|
|
148
|
+
// Server endpoint not yet available, fallback to directory open
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// 2. Fallback to official opener with parent directory
|
|
152
|
+
const parentDir = dirname(targetPath);
|
|
153
|
+
const remote = ctx.remote as unknown as { session?: { openWorkspacePath: (arg: { path: string }) => Promise<{ ok: boolean; error?: { message: string } }> } } | undefined;
|
|
154
|
+
const remoteSession = remote?.session
|
|
155
|
+
?? (ctx.get?.('remote.session') as unknown as { openWorkspacePath: (arg: { path: string }) => Promise<{ ok: boolean; error?: { message: string } }> } | undefined);
|
|
156
|
+
if (remoteSession?.openWorkspacePath) {
|
|
157
|
+
await remoteSession.openWorkspacePath({ path: parentDir });
|
|
158
|
+
}
|
|
159
|
+
} catch (error) {
|
|
160
|
+
console.warn('[dsh-better-display] revealFile error:', error);
|
|
161
|
+
}
|
|
162
|
+
},
|
|
163
|
+
forkAt: (seq: number) => {
|
|
164
|
+
// A missing anchor would silently fork the whole session instead of
|
|
165
|
+
// the intended turn prefix, so refuse it loudly rather than guessing.
|
|
166
|
+
if (typeof seq !== 'number' || !Number.isFinite(seq)) {
|
|
167
|
+
console.warn('[dsh-better-display] fork refused: missing anchor seq');
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
try {
|
|
171
|
+
const sessionsApi = ctx.sessions as unknown as {
|
|
172
|
+
fork: (arg: { sessionId: string; atSeq: number; increaseTitle: boolean }) => Promise<string>;
|
|
173
|
+
open: (sessionId: string) => void;
|
|
174
|
+
} | undefined;
|
|
175
|
+
if (sessionsApi?.fork) {
|
|
176
|
+
sessionsApi.fork({ sessionId, atSeq: seq, increaseTitle: true })
|
|
177
|
+
.then(childId => { sessionsApi.open?.(childId); })
|
|
178
|
+
.catch(err => { console.warn('[dsh-better-display] fork failed:', err); });
|
|
179
|
+
}
|
|
180
|
+
} catch (error) {
|
|
181
|
+
console.warn('[dsh-better-display] forkAt error:', error);
|
|
182
|
+
}
|
|
183
|
+
},
|
|
184
|
+
loadThrough: async (seq: unknown) => {
|
|
185
|
+
try {
|
|
186
|
+
const current = session() as unknown as { loadThrough?: (seq: unknown) => Promise<void> };
|
|
187
|
+
if (typeof current?.loadThrough === 'function') {
|
|
188
|
+
await current.loadThrough(seq);
|
|
189
|
+
} else {
|
|
190
|
+
await session().loadOlder();
|
|
191
|
+
}
|
|
192
|
+
} catch (error) {
|
|
193
|
+
console.warn('[dsh-better-display] loadThrough error:', error);
|
|
194
|
+
}
|
|
195
|
+
},
|
|
196
|
+
fillComposer: (text: string) => {
|
|
197
|
+
// Sanctioned path: the conversation input shell owns the Lexical
|
|
198
|
+
// editor, so setDraft lands in the draft store deterministically.
|
|
199
|
+
try {
|
|
200
|
+
const conversation = (ctx as unknown as { conversation?: ConversationFace }).conversation;
|
|
201
|
+
const shell = conversation?.input?.shell?.(sessionId);
|
|
202
|
+
if (shell && typeof shell.setDraft === 'function') {
|
|
203
|
+
shell.setDraft(text);
|
|
204
|
+
return true;
|
|
205
|
+
}
|
|
206
|
+
} catch {
|
|
207
|
+
// Fall through to the DOM path below.
|
|
208
|
+
}
|
|
209
|
+
try {
|
|
210
|
+
return fillComposerDom(text);
|
|
211
|
+
} catch {
|
|
212
|
+
return false;
|
|
213
|
+
}
|
|
214
|
+
},
|
|
215
|
+
|
|
216
|
+
};
|
|
217
|
+
},
|
|
218
|
+
}, Reader);
|
|
219
|
+
yield installOfficialSlots(ctx);
|
|
220
|
+
yield installReaderEntry(ctx);
|
|
221
|
+
});
|
|
222
|
+
}
|