@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,106 @@
|
|
|
1
|
+
import { useState } from 'react'
|
|
2
|
+
import type { ContextMessageNode } from '@deepseek-ai/dsh-client-ui-conversation/client'
|
|
3
|
+
import type { TranslateNS } from '@deepseek-ai/dsh-client-ui-slots'
|
|
4
|
+
import { DisclosureRow, IconBrowseOutlineRegular } from '@deepseek-ai/dsh-client-ui-primitives'
|
|
5
|
+
import { ReferenceIcon } from './ReferenceIcon.js'
|
|
6
|
+
import { contextBody } from './ContextBody.js'
|
|
7
|
+
import { toolUpdateRows } from './tool-update.js'
|
|
8
|
+
import css from './ContextInjectionRow.module.css'
|
|
9
|
+
|
|
10
|
+
/** Props for the logged non-user message presentation. */
|
|
11
|
+
export interface ContextInjectionRowProps {
|
|
12
|
+
content: ContextMessageNode['content']
|
|
13
|
+
source: ContextMessageNode['source']
|
|
14
|
+
/**
|
|
15
|
+
* Role and producer name projected from the durable source.
|
|
16
|
+
*
|
|
17
|
+
* Host generations disagree on the field name: newer builds project
|
|
18
|
+
* `provenance`, the 0.1.6-alpha generations project `producer`. Both are
|
|
19
|
+
* optional here and resolved below, so the row renders on either host instead
|
|
20
|
+
* of throwing on `undefined.role` and degrading its whole block boundary.
|
|
21
|
+
*/
|
|
22
|
+
provenance?: ContextMessageNode['producer'] | null
|
|
23
|
+
/** Alpha-generation spelling of {@link provenance}. */
|
|
24
|
+
producer?: ContextMessageNode['producer'] | null
|
|
25
|
+
/** Producer-declared information form; null renders the opaque body. */
|
|
26
|
+
form: ContextMessageNode['form']
|
|
27
|
+
/** The owning view's locale seat, passed down as a plain prop. */
|
|
28
|
+
t: TranslateNS<'chat'>
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Render logged context with the Tool calls disclosure chrome from Figma.
|
|
33
|
+
*
|
|
34
|
+
* The header names the role the context plays and, beside it, the producer the
|
|
35
|
+
* durable source identifies, so a reader can tell an injected skill catalog
|
|
36
|
+
* from a workspace instruction file or a recalled session without expanding.
|
|
37
|
+
* The expanded body follows the producer-declared form; an absent or unknown
|
|
38
|
+
* form renders the opaque body.
|
|
39
|
+
* @param props - Durable content, its projected producer role/name and form, and the locale seat.
|
|
40
|
+
* @returns A collapsed context row with a bounded, form-specific body.
|
|
41
|
+
*/
|
|
42
|
+
export function ContextInjectionRow({ content, source, provenance, producer, form, t }: ContextInjectionRowProps) {
|
|
43
|
+
const [open, setOpen] = useState(false)
|
|
44
|
+
// Two host generations, one component. A row whose projection is absent
|
|
45
|
+
// entirely still renders: the marker falls back to a plain injection.
|
|
46
|
+
const view = provenance ?? producer ?? { role: 'inject' as const, label: null }
|
|
47
|
+
// Resolved rather than declared: a form whose fields are unreadable renders
|
|
48
|
+
// the opaque body, and the marker must say what the row actually shows.
|
|
49
|
+
const { rendered, summary, body } = contextBody(form, { content, source, t })
|
|
50
|
+
const tools = toolUpdateRows(content)
|
|
51
|
+
const toolSummary = tools === null || tools.single !== null ? null
|
|
52
|
+
: tools.added.length > 0 && tools.removed.length > 0
|
|
53
|
+
? t('message.toolsChanged', { added: tools.added.length, removed: tools.removed.length })
|
|
54
|
+
: tools.added.length > 0
|
|
55
|
+
? t('message.toolsAddedCount', { count: tools.added.length })
|
|
56
|
+
: t('message.toolsRemovedCount', { count: tools.removed.length })
|
|
57
|
+
|
|
58
|
+
return (
|
|
59
|
+
<DisclosureRow
|
|
60
|
+
className={css.root}
|
|
61
|
+
icon={view.role === 'recall' && tools === null
|
|
62
|
+
? <span data-context-recall-icon><ReferenceIcon kind="session" /></span>
|
|
63
|
+
: <IconBrowseOutlineRegular size={14} />}
|
|
64
|
+
chevronClassName={css.chevron}
|
|
65
|
+
title={tools?.single !== undefined && tools.single !== null
|
|
66
|
+
? t(tools.single.type === 'tool-addition' ? 'message.toolAdded' : 'message.toolRemoved', { name: tools.single.toolName })
|
|
67
|
+
: tools !== null
|
|
68
|
+
? t('message.toolsUpdated')
|
|
69
|
+
: t(view.role === 'recall' ? 'message.contextRecall' : 'message.contextInjection')}
|
|
70
|
+
collapsedContent={toolSummary !== null ? (
|
|
71
|
+
<>
|
|
72
|
+
<span className={css.sep} aria-hidden />
|
|
73
|
+
<span className={css.summary}>{toolSummary}</span>
|
|
74
|
+
</>
|
|
75
|
+
) : tools !== null || view.label === null ? undefined : (
|
|
76
|
+
/* ToolRow's separator shape: an aria-hidden dot, so the accessible name
|
|
77
|
+
stays the two readable parts and the two disclosure rows expose one
|
|
78
|
+
name shape. A source that names no producer drops the dot with it. */
|
|
79
|
+
<>
|
|
80
|
+
<span className={css.sep} aria-hidden />
|
|
81
|
+
<span className={css.source} data-context-source>{view.label}</span>
|
|
82
|
+
{summary !== null && (
|
|
83
|
+
<>
|
|
84
|
+
<span className={css.sep} aria-hidden />
|
|
85
|
+
<span className={css.summary} data-context-summary>{summary}</span>
|
|
86
|
+
</>
|
|
87
|
+
)}
|
|
88
|
+
</>
|
|
89
|
+
)}
|
|
90
|
+
keepContentWhenOpen
|
|
91
|
+
open={open && tools?.single == null}
|
|
92
|
+
expandable={tools?.single == null}
|
|
93
|
+
expandOnRowClick
|
|
94
|
+
onToggle={() => { setOpen(value => !value) }}
|
|
95
|
+
>
|
|
96
|
+
<div className={css.body} data-context-injection-body data-context-form={rendered ?? undefined}>
|
|
97
|
+
{tools === null ? body : (
|
|
98
|
+
<div className={css.toolChanges}>
|
|
99
|
+
{tools.added.length > 0 && <div>{t('message.toolsAdded', { names: tools.added.join(', ') })}</div>}
|
|
100
|
+
{tools.removed.length > 0 && <div>{t('message.toolsRemoved', { names: tools.removed.join(', ') })}</div>}
|
|
101
|
+
</div>
|
|
102
|
+
)}
|
|
103
|
+
</div>
|
|
104
|
+
</DisclosureRow>
|
|
105
|
+
)
|
|
106
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { ReactNode } from 'react'
|
|
2
|
+
import {
|
|
3
|
+
IconBrowseOutlineRegular, IconFolderCloseRegular,
|
|
4
|
+
} from '@deepseek-ai/dsh-client-ui-primitives'
|
|
5
|
+
|
|
6
|
+
/** Reference domains with distinct composer and transcript glyphs. */
|
|
7
|
+
export type ReferenceIconKind = 'session' | 'file' | 'folder'
|
|
8
|
+
|
|
9
|
+
/** Props shared by inline reference glyphs. */
|
|
10
|
+
export interface ReferenceIconProps {
|
|
11
|
+
kind: ReferenceIconKind
|
|
12
|
+
size?: number
|
|
13
|
+
className?: string | undefined
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Render the icon that identifies one inline reference domain.
|
|
18
|
+
* @param props - Reference kind, optional size, and optional CSS class.
|
|
19
|
+
* @returns The corresponding current-color SVG glyph.
|
|
20
|
+
*/
|
|
21
|
+
export function ReferenceIcon({ kind, size = 16, className }: ReferenceIconProps): ReactNode {
|
|
22
|
+
switch (kind) {
|
|
23
|
+
case 'session':
|
|
24
|
+
return (
|
|
25
|
+
<svg width={size} height={size} className={className} viewBox="0 0 16 16" fill="none" aria-hidden>
|
|
26
|
+
<path
|
|
27
|
+
d="M8 0.597656C3.91296 0.597656 0.599716 3.91103 0.599609 7.99805C0.599609 9.13171 0.854567 10.2079 1.31152 11.1699L1.59277 11.7607L2.77441 11.1992L2.49414 10.6084L2.36035 10.3076C2.06865 9.59612 1.90723 8.81645 1.90723 7.99805C1.90733 4.63362 4.63554 1.90625 8 1.90625C11.3644 1.90635 14.0917 4.63368 14.0918 7.99805C14.0918 11.3625 11.3644 14.0907 8 14.0908C7.311 14.0908 6.80642 14.0414 6.35938 13.918C5.919 13.7963 5.50105 13.5929 5.00098 13.2441C4.26805 12.7329 3.21756 12.5526 2.35156 13.0996L2.33789 13.1084L2.32422 13.1182L1.74805 13.5234L2.18164 14.8184L3.05957 14.2002C3.37505 14.0068 3.84248 14.0319 4.25195 14.3174C4.84447 14.7307 5.39718 15.009 6.01172 15.1787C6.61963 15.3465 7.25579 15.3984 8 15.3984C12.087 15.3983 15.4004 12.0851 15.4004 7.99805C15.4003 3.9111 12.087 0.59776 8 0.597656ZM4.56836 8.50977V9.80371H8.12402V8.50977H4.56836ZM4.56836 7.30078H11.4619V6.00684H4.56836V7.30078Z"
|
|
28
|
+
fill="currentColor"
|
|
29
|
+
/>
|
|
30
|
+
</svg>
|
|
31
|
+
)
|
|
32
|
+
case 'file': return <IconBrowseOutlineRegular size={size} className={className} />
|
|
33
|
+
case 'folder': return <IconFolderCloseRegular size={size} className={className} />
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure row-model derivation for tool summary rows: variant classification,
|
|
3
|
+
* one-line summary, expanded-body text, and flattened result output from the
|
|
4
|
+
* frozen call slice. Input material comes from the call ARGUMENTS; output and
|
|
5
|
+
* error material from the settled result node. A call whose render intent is
|
|
6
|
+
* a terminal card gets its expanded body from the views instead, through
|
|
7
|
+
* `terminalCardModel` in terminal-card-model.ts.
|
|
8
|
+
*/
|
|
9
|
+
// The block union's defining home is runtime (fold-product types); this
|
|
10
|
+
// contract only forwards it (type-definition authority stays with the layer
|
|
11
|
+
// that produces the values).
|
|
12
|
+
import { abbreviateHomePath } from '@deepseek-ai/dsh-util-workspace-path'
|
|
13
|
+
import type { ToolCallBlock, ToolResultNode } from '@deepseek-ai/dsh-client-ui-conversation/client'
|
|
14
|
+
|
|
15
|
+
export type { ToolCallBlock } from '@deepseek-ai/dsh-client-ui-conversation/client'
|
|
16
|
+
|
|
17
|
+
/** Tool-call row variants selected by the generic atomic renderer. */
|
|
18
|
+
export type ToolRowVariant = 'search' | 'read' | 'bash' | 'write' | 'edit' | 'code' | 'others'
|
|
19
|
+
|
|
20
|
+
/** Row state semantic; colors self-supplied via StateDot (design gives none). */
|
|
21
|
+
export type ToolRowState = 'running' | 'ok' | 'error' | 'stopped'
|
|
22
|
+
|
|
23
|
+
/** Figma row titles per variant (design literals, not translatable copy). */
|
|
24
|
+
export const VARIANT_TITLES: Record<ToolRowVariant, string> = {
|
|
25
|
+
search: 'Search', read: 'Read', bash: 'Bash',
|
|
26
|
+
write: 'Write', edit: 'Edit', code: 'Code', others: 'Tool call',
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Known tool name -> variant.
|
|
31
|
+
*
|
|
32
|
+
* `cordis_define` is deliberately absent: ui-cordis registers a keyed
|
|
33
|
+
* `tool.call.toolview` entry for it, and a keyed hit REPLACES the generic row
|
|
34
|
+
* (this table is only reached through GenericToolCard, the dispatch fallback in
|
|
35
|
+
* ToolCallTree). An entry here would be unreachable, and a second title for the
|
|
36
|
+
* same call would be a second answer to a question the card already owns.
|
|
37
|
+
*/
|
|
38
|
+
const TOOL_VARIANTS: Record<string, ToolRowVariant> = {
|
|
39
|
+
bash: 'bash',
|
|
40
|
+
// The PowerShell twin is a shell tool: the bash row family (icon, colors)
|
|
41
|
+
// with its own title from TOOL_TITLES, not the generic `others` row.
|
|
42
|
+
pwsh: 'bash',
|
|
43
|
+
read: 'read',
|
|
44
|
+
web_fetch: 'read',
|
|
45
|
+
web_search: 'search',
|
|
46
|
+
grep: 'search',
|
|
47
|
+
glob: 'search',
|
|
48
|
+
write: 'write',
|
|
49
|
+
edit: 'edit',
|
|
50
|
+
run_code: 'code',
|
|
51
|
+
cordis_package_inspect: 'read',
|
|
52
|
+
cordis_runtime_inspect: 'read',
|
|
53
|
+
// The three run-control verbs take one package id and produce a receipt, so
|
|
54
|
+
// the generic row is the decided intent, not an unclassified default: there is
|
|
55
|
+
// no program to show (that is `cordis_define`'s card) and no file to open. The
|
|
56
|
+
// id lands in the summary slot, and the titles below name the act.
|
|
57
|
+
cordis_run: 'others',
|
|
58
|
+
cordis_stop: 'others',
|
|
59
|
+
cordis_undefine: 'others',
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Tool-owned titles that refine a generic row variant without replacing it. */
|
|
63
|
+
const TOOL_TITLES: Record<string, string> = {
|
|
64
|
+
cordis_package_inspect: 'Inspect',
|
|
65
|
+
cordis_runtime_inspect: 'Inspect',
|
|
66
|
+
cordis_run: 'Run Cordis Plugin',
|
|
67
|
+
cordis_stop: 'Stop Cordis Plugin',
|
|
68
|
+
cordis_undefine: 'Remove Cordis Plugin',
|
|
69
|
+
pwsh: 'Pwsh',
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Classify a tool name into its row variant.
|
|
74
|
+
* @param toolName - wire tool name.
|
|
75
|
+
* @returns matching variant, others when unknown.
|
|
76
|
+
*/
|
|
77
|
+
export function classifyTool(toolName: string): ToolRowVariant {
|
|
78
|
+
return TOOL_VARIANTS[toolName] ?? 'others'
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** Everything ToolRow needs, derived once from the frozen slice. */
|
|
82
|
+
export interface ToolRowModel {
|
|
83
|
+
variant: ToolRowVariant
|
|
84
|
+
title: string
|
|
85
|
+
summary: string
|
|
86
|
+
/**
|
|
87
|
+
* Filesystem path from args (`path` / `file_path`) when the row is a file
|
|
88
|
+
* tool; absent for URL reads and non-file tools. The chat view resolves
|
|
89
|
+
* relative values against the session cwd before opening.
|
|
90
|
+
*/
|
|
91
|
+
filePath: string | undefined
|
|
92
|
+
/** Expanded-body input text (pretty args); null = no input section. */
|
|
93
|
+
body: string | null
|
|
94
|
+
/** Flattened result text ({@link resultText}); null while running or when the result carries no text. */
|
|
95
|
+
output: string | null
|
|
96
|
+
/** First line of the result text on an error row; null for every other state. */
|
|
97
|
+
errorSummary: string | null
|
|
98
|
+
state: ToolRowState
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Flatten a settled result's content blocks to display text: text blocks
|
|
103
|
+
* verbatim, other block shapes as pretty JSON. Empty content on a failed call
|
|
104
|
+
* falls back to the structured error's `name: code` line.
|
|
105
|
+
* @param node - the settled result node.
|
|
106
|
+
* @returns the flattened result text (may be empty).
|
|
107
|
+
*/
|
|
108
|
+
export function resultText(node: ToolResultNode): string {
|
|
109
|
+
const parts: string[] = []
|
|
110
|
+
for (const block of node.content) {
|
|
111
|
+
if (block.type === 'text') parts.push(block.text)
|
|
112
|
+
else parts.push(JSON.stringify(block, null, 2))
|
|
113
|
+
}
|
|
114
|
+
if (parts.length === 0 && node.error !== undefined) {
|
|
115
|
+
parts.push(`${node.error.name}: ${node.error.code}`)
|
|
116
|
+
}
|
|
117
|
+
return parts.join('\n')
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function parseArgs(argsRaw: string): unknown {
|
|
121
|
+
try {
|
|
122
|
+
return JSON.parse(argsRaw)
|
|
123
|
+
} catch {
|
|
124
|
+
// Non-JSON args (mid-stream truncation): summary/body fall back to the raw string.
|
|
125
|
+
return undefined
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function firstLine(text: string): string {
|
|
130
|
+
const nl = text.indexOf('\n')
|
|
131
|
+
return nl === -1 ? text : text.slice(0, nl)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function pickString(args: Record<string, unknown>, keys: readonly string[]): string | undefined {
|
|
135
|
+
for (const key of keys) {
|
|
136
|
+
const v = args[key]
|
|
137
|
+
if (typeof v === 'string' && v !== '') return v
|
|
138
|
+
}
|
|
139
|
+
return undefined
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/** Summary key preference per variant (args-derived; result-derived summaries are a ledger item). */
|
|
143
|
+
const SUMMARY_KEYS: Record<ToolRowVariant, readonly string[]> = {
|
|
144
|
+
bash: ['description', 'command'],
|
|
145
|
+
read: ['path', 'file_path', 'url'],
|
|
146
|
+
search: ['query', 'pattern', 'url'],
|
|
147
|
+
write: ['path', 'file_path'],
|
|
148
|
+
edit: ['path', 'file_path'],
|
|
149
|
+
code: ['description'],
|
|
150
|
+
others: [],
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Strip the workspace root from a workspace-rooted absolute path (display only).
|
|
155
|
+
* @param text - the path to shorten.
|
|
156
|
+
* @param cwd - session workspace root; absent or empty leaves the path unchanged.
|
|
157
|
+
* @returns the path relative to the workspace root, or unchanged when it is not rooted there.
|
|
158
|
+
*/
|
|
159
|
+
export function relativizeToCwd(text: string, cwd: string | undefined): string {
|
|
160
|
+
if (cwd === undefined || cwd === '') return text
|
|
161
|
+
const root = cwd.replace(/[/\\]+$/, '')
|
|
162
|
+
if (text.startsWith(`${root}/`) || text.startsWith(`${root}\\`)) return text.slice(root.length + 1)
|
|
163
|
+
return text
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function deriveSummary(variant: ToolRowVariant, argsRaw: string): string {
|
|
167
|
+
const parsed = parseArgs(argsRaw)
|
|
168
|
+
if (typeof parsed !== 'object' || parsed === null) return firstLine(argsRaw)
|
|
169
|
+
const args = parsed as Record<string, unknown>
|
|
170
|
+
if (variant === 'search' && Array.isArray(args.queries)) {
|
|
171
|
+
const queries = args.queries.filter((query): query is string => typeof query === 'string' && query !== '')
|
|
172
|
+
if (queries.length > 0) return queries.map(firstLine).join(', ')
|
|
173
|
+
}
|
|
174
|
+
const picked = pickString(args, SUMMARY_KEYS[variant])
|
|
175
|
+
if (picked !== undefined) return firstLine(picked)
|
|
176
|
+
for (const v of Object.values(args)) {
|
|
177
|
+
if (typeof v === 'string' && v !== '') return firstLine(v)
|
|
178
|
+
}
|
|
179
|
+
return firstLine(argsRaw)
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/** Path keys only — never `url` (web_fetch lands on the read variant). */
|
|
183
|
+
const FILE_PATH_KEYS = ['path', 'file_path'] as const
|
|
184
|
+
|
|
185
|
+
/** File-tool variants whose summary may be an openable workspace path. */
|
|
186
|
+
const FILE_PATH_VARIANTS: ReadonlySet<ToolRowVariant> = new Set(['read', 'write', 'edit'])
|
|
187
|
+
|
|
188
|
+
function deriveFilePath(variant: ToolRowVariant, argsRaw: string): string | undefined {
|
|
189
|
+
if (!FILE_PATH_VARIANTS.has(variant)) return undefined
|
|
190
|
+
const parsed = parseArgs(argsRaw)
|
|
191
|
+
if (typeof parsed !== 'object' || parsed === null) return undefined
|
|
192
|
+
const picked = pickString(parsed as Record<string, unknown>, FILE_PATH_KEYS)
|
|
193
|
+
return picked === undefined ? undefined : firstLine(picked)
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function deriveBody(variant: ToolRowVariant, argsRaw: string): string | null {
|
|
197
|
+
if (argsRaw === '') return null
|
|
198
|
+
const parsed = parseArgs(argsRaw)
|
|
199
|
+
if (parsed === undefined) return argsRaw
|
|
200
|
+
// The code row's expanded body IS the program (monospace via the row's
|
|
201
|
+
// variant styling), not the args JSON envelope around it.
|
|
202
|
+
if (variant === 'code' && typeof parsed === 'object' && parsed !== null) {
|
|
203
|
+
const code = (parsed as Record<string, unknown>).code
|
|
204
|
+
if (typeof code === 'string' && code !== '') return code
|
|
205
|
+
}
|
|
206
|
+
return JSON.stringify(parsed, null, 2)
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Derive the full row model from a frozen call slice.
|
|
211
|
+
* @param toolName - wire tool name (dispatch-supplied; survives windowless results).
|
|
212
|
+
* @param block - RunningToolCall or ToolResultNode off the snapshot caches.
|
|
213
|
+
* @param cwd - session workspace root; workspace-rooted path summaries display relative to it.
|
|
214
|
+
* @param home - host account home; a leftover POSIX home path displays as `~`.
|
|
215
|
+
* @returns the row model.
|
|
216
|
+
*/
|
|
217
|
+
export function toolRowModel(toolName: string, block: ToolCallBlock, cwd?: string, home?: string): ToolRowModel {
|
|
218
|
+
const variant = classifyTool(toolName)
|
|
219
|
+
const done = 'kind' in block
|
|
220
|
+
const argsRaw = (done ? block.call?.argsRaw : ('argsRaw' in block ? block.argsRaw : '')) ?? ''
|
|
221
|
+
const state: ToolRowState = !done ? 'running'
|
|
222
|
+
: block.error?.code === 'interrupted' ? 'stopped'
|
|
223
|
+
: block.isError ? 'error' : 'ok'
|
|
224
|
+
const base = argsRaw === ''
|
|
225
|
+
? block.callId
|
|
226
|
+
: abbreviateHomePath(relativizeToCwd(deriveSummary(variant, argsRaw), cwd), home)
|
|
227
|
+
const toolTitle = TOOL_TITLES[toolName]
|
|
228
|
+
// Others keeps the static "Tool call" title (figma literal); the real tool
|
|
229
|
+
// name rides the mutable summary slot unless the tool owns a specific title.
|
|
230
|
+
const summary = variant === 'others' && toolName !== '' && toolTitle === undefined
|
|
231
|
+
? `${toolName} · ${base}`
|
|
232
|
+
: base
|
|
233
|
+
// The empty string is "no text" for both derived result fields: a settled
|
|
234
|
+
// call with blank content has nothing to expand, and a blank first line
|
|
235
|
+
// would erase the collapsed error row's summary slot.
|
|
236
|
+
const output = done ? (resultText(block) || null) : null
|
|
237
|
+
const errorSummary = state === 'error' && output !== null ? firstLine(output) : null
|
|
238
|
+
return {
|
|
239
|
+
variant,
|
|
240
|
+
title: toolTitle ?? VARIANT_TITLES[variant],
|
|
241
|
+
summary,
|
|
242
|
+
filePath: deriveFilePath(variant, argsRaw),
|
|
243
|
+
body: deriveBody(variant, argsRaw),
|
|
244
|
+
output,
|
|
245
|
+
errorSummary,
|
|
246
|
+
state,
|
|
247
|
+
}
|
|
248
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/** One developer tool-change block the 0.1.7-rc.2 context row names in place. */
|
|
2
|
+
export interface ToolChangeBlock {
|
|
3
|
+
type: 'tool-addition' | 'tool-removal'
|
|
4
|
+
toolName: string
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
/** Names collected from a context whose every block is a tool change. */
|
|
8
|
+
export interface ToolUpdateRows {
|
|
9
|
+
added: readonly string[]
|
|
10
|
+
removed: readonly string[]
|
|
11
|
+
/** The only block, when the row is a single add or removal. */
|
|
12
|
+
single: ToolChangeBlock | null
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Classify a context as the rc.2 tool-update row.
|
|
17
|
+
* Mixed or empty content stays on the generic injection presentation.
|
|
18
|
+
* @param content - durable context blocks.
|
|
19
|
+
* @returns collected names, or null when any block is not a tool change.
|
|
20
|
+
*/
|
|
21
|
+
export function toolUpdateRows(content: readonly { readonly type: string; readonly toolName?: string }[]): ToolUpdateRows | null {
|
|
22
|
+
if (content.length === 0) return null
|
|
23
|
+
const rows: ToolChangeBlock[] = []
|
|
24
|
+
for (const block of content) {
|
|
25
|
+
if (block.type !== 'tool-addition' && block.type !== 'tool-removal') return null
|
|
26
|
+
if (typeof block.toolName !== 'string') return null
|
|
27
|
+
rows.push({ type: block.type, toolName: block.toolName })
|
|
28
|
+
}
|
|
29
|
+
const added = rows.flatMap(row => row.type === 'tool-addition' ? [row.toolName] : [])
|
|
30
|
+
const removed = rows.flatMap(row => row.type === 'tool-removal' ? [row.toolName] : [])
|
|
31
|
+
return { added, removed, single: rows.length === 1 ? rows[0]! : null }
|
|
32
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"repository": "DeepSeek Harness",
|
|
3
|
+
"commit": "b150a551b8d465e31e418e1b2eaf5e79bbb7d28e",
|
|
4
|
+
"adaptations": [
|
|
5
|
+
"Public type import in place of private type-only path",
|
|
6
|
+
"ESM .js import suffixes",
|
|
7
|
+
"Local ReferenceIcon location"
|
|
8
|
+
],
|
|
9
|
+
"files": [
|
|
10
|
+
{
|
|
11
|
+
"source": "packages/client/ui-conversation/src/client/chat/ContextInjectionRow.tsx",
|
|
12
|
+
"output": "ContextInjectionRow.tsx",
|
|
13
|
+
"sha256": "afb41e6285f2abaf0eb6a1f843dbc135c8480bd48848beed1f94e395ac0f68c8"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"source": "packages/client/ui-conversation/src/client/chat/ContextInjectionRow.module.css",
|
|
17
|
+
"output": "ContextInjectionRow.module.css",
|
|
18
|
+
"sha256": "af61636789223d2476ec818a76d795bc7c13ca1a433f834d78702a825bd000de"
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"source": "packages/client/ui-conversation/src/client/chat/ContextBody.tsx",
|
|
22
|
+
"output": "ContextBody.tsx",
|
|
23
|
+
"sha256": "94f41356d59bdcdae7225f3cb8cd9b0c0b1a32d24153d82512bfba7c40443cc8"
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"source": "packages/client/ui-conversation/src/client/chat/ContextBody.module.css",
|
|
27
|
+
"output": "ContextBody.module.css",
|
|
28
|
+
"sha256": "b3838bf9cf38af53b31fd6333b4766ad063024d9a59cd636a38e3e0e07d7bf61"
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"source": "packages/client/ui-conversation/src/client/reference/ReferenceIcon.tsx",
|
|
32
|
+
"output": "ReferenceIcon.tsx",
|
|
33
|
+
"sha256": "6315cb1ceafa830cd9e38ce296ddf5791ab93562e4f5a7bdbda1ef9ac6f7ed57"
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"source": "packages/client/ui-tool/src/client/tool/models/tool-call-model.ts",
|
|
37
|
+
"output": "tool-call-model.ts",
|
|
38
|
+
"sha256": "02536cb0be80168351857083d0f0b71554216cd3195bad8016b1e4bb567acc7f"
|
|
39
|
+
}
|
|
40
|
+
]
|
|
41
|
+
}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import { createElement, memo, useMemo, type ComponentType, type ReactNode } from 'react';
|
|
2
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
3
|
+
import type { SlotEntryDef, SlotMap, SlotSpec, StoredEntry } from '@deepseek-ai/dsh-client-ui-slots';
|
|
4
|
+
import type {} from '@deepseek-ai/dsh-client-ui-renderer/client';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* RC2 composition adapter. Only public registry operations are used. We lend
|
|
8
|
+
* registrations a Reader-owned seat, not a second implementation of their UI.
|
|
9
|
+
* The platform still binds injection, hooks, locale, stores and boundaries.
|
|
10
|
+
* Original entries, declarations and components are never changed.
|
|
11
|
+
*
|
|
12
|
+
* Child seats have unique names because RC2 declarations have one owner. The
|
|
13
|
+
* sole component wrapper translates those names before delegating to the
|
|
14
|
+
* framework-provided renderSlot. It does not implement a slot renderer.
|
|
15
|
+
*/
|
|
16
|
+
export const OFFICIAL_SLOTS = {
|
|
17
|
+
actions: 'conversation.chat.assistant-actions',
|
|
18
|
+
tools: 'tool.call.toolview',
|
|
19
|
+
tail: 'conversation.chat.turnTail',
|
|
20
|
+
nodes: 'conversation.chat.node',
|
|
21
|
+
images: 'conversation.message.images',
|
|
22
|
+
} as const;
|
|
23
|
+
export type OfficialFamily = keyof typeof OFFICIAL_SLOTS;
|
|
24
|
+
export const OFFICIAL_SEATS = {
|
|
25
|
+
actions: 'dsh-better-display.official.actions/conversation.chat.assistant-actions',
|
|
26
|
+
tools: 'dsh-better-display.official.tools/tool.call.toolview',
|
|
27
|
+
tail: 'dsh-better-display.official.tail/conversation.chat.turnTail',
|
|
28
|
+
nodes: 'dsh-better-display.official.nodes/conversation.chat.node',
|
|
29
|
+
images: 'dsh-better-display.official.images/conversation.message.images',
|
|
30
|
+
} as const;
|
|
31
|
+
export type OfficialSeat = typeof OFFICIAL_SEATS[OfficialFamily];
|
|
32
|
+
export const officialSeat = (family: OfficialFamily, source: string = OFFICIAL_SLOTS[family]) =>
|
|
33
|
+
`dsh-better-display.official.${family}/${source}`;
|
|
34
|
+
|
|
35
|
+
type Spec = SlotSpec<SlotEntryDef>;
|
|
36
|
+
type Render = (key: string, owner: object, options?: object) => ReactNode;
|
|
37
|
+
type RenderProps = { renderSlot: Render; renderSlotChain?: Render } & Record<string, unknown>;
|
|
38
|
+
type Registration = StoredEntry['options'] & Omit<StoredEntry, 'options' | 'component'> & { name: string };
|
|
39
|
+
|
|
40
|
+
/** The documented, type-erased registry inspection/registration boundary. */
|
|
41
|
+
export interface CompositionRegistry {
|
|
42
|
+
spec(key: string): Spec | undefined;
|
|
43
|
+
entriesOfSlot(key: string): readonly StoredEntry[];
|
|
44
|
+
subscribe(key: string, listener: () => void): () => void;
|
|
45
|
+
inject(key: string, effect: () => () => void): () => void;
|
|
46
|
+
register(options: Registration, component: unknown): () => void;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
declare module '@deepseek-ai/dsh-client-ui-slots' {
|
|
50
|
+
interface SlotMap {
|
|
51
|
+
'dsh-better-display.official.actions/conversation.chat.assistant-actions': SlotMap['conversation.chat.assistant-actions'];
|
|
52
|
+
'dsh-better-display.official.tools/tool.call.toolview': SlotMap['tool.call.toolview'];
|
|
53
|
+
'dsh-better-display.official.tail/conversation.chat.turnTail': SlotMap['conversation.chat.turnTail'];
|
|
54
|
+
'dsh-better-display.official.nodes/conversation.chat.node': SlotMap['conversation.chat.node'];
|
|
55
|
+
'dsh-better-display.official.images/conversation.message.images': SlotMap['conversation.message.images'];
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Existing Reader presentations own these rows. All other node kinds, including
|
|
60
|
+
// future registrations, reach the official renderer through the fallback seat.
|
|
61
|
+
const READER_NODES = new Set(['user', 'steering', 'assistant-step', 'tool-call', 'turn-tail', 'turn-process']);
|
|
62
|
+
const EXPECTED: Record<OfficialFamily, Spec> = {
|
|
63
|
+
actions: { kind: 'list', scope: 'session' },
|
|
64
|
+
tools: { kind: 'keyed', scope: 'session' },
|
|
65
|
+
tail: { kind: 'list', scope: 'session' },
|
|
66
|
+
nodes: { kind: 'keyed', scope: 'session' },
|
|
67
|
+
images: { kind: 'single', scope: 'session' },
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Reader already presents produced paths with its copy/reveal actions. RC2's
|
|
72
|
+
* official tail additionally owns explicit `present` artifacts. Leave those
|
|
73
|
+
* cards, actions and state intact, and omit only the duplicate produced chips.
|
|
74
|
+
* This changes a presentation prop, never the source match or session data.
|
|
75
|
+
* Unrecognized shapes pass through intact for safe forward degradation.
|
|
76
|
+
*/
|
|
77
|
+
export function readerTailMatch(value: unknown, displayedPaths?: readonly string[]): unknown {
|
|
78
|
+
if (!value || typeof value !== 'object') return value;
|
|
79
|
+
const match = value as Record<string, unknown>;
|
|
80
|
+
const displayed = new Set(displayedPaths ?? []);
|
|
81
|
+
return displayed.size > 0 && Array.isArray(match.produced) && Array.isArray(match.presented)
|
|
82
|
+
? { ...match, produced: match.produced.filter(path => !displayed.has(path)) }
|
|
83
|
+
: value;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function tailPresentation(component: unknown) {
|
|
87
|
+
const Original = component as ComponentType<Record<string, unknown>>;
|
|
88
|
+
return memo(function ReaderTailPresentation(props: Record<string, unknown>) {
|
|
89
|
+
const matched = useMemo(() => readerTailMatch(props.matched, props.readerProducedPaths as readonly string[] | undefined), [props.matched, props.readerProducedPaths]);
|
|
90
|
+
return createElement(Original, { ...props, matched });
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export function officialChildren(slots: Pick<CompositionRegistry, 'spec'>): { [K in OfficialSeat]: SlotSpec<SlotMap[K]> } {
|
|
95
|
+
return Object.fromEntries(Object.entries(OFFICIAL_SLOTS).map(([name, source]) => {
|
|
96
|
+
const family = name as OfficialFamily;
|
|
97
|
+
const spec = slots.spec(source);
|
|
98
|
+
if (spec && (spec.kind !== EXPECTED[family].kind || spec.scope !== EXPECTED[family].scope)) {
|
|
99
|
+
throw new Error(`Official slot contract changed: ${source} (${spec.kind}/${spec.scope})`);
|
|
100
|
+
}
|
|
101
|
+
// In particular, retain conversation.chat.node's contextual turn-data hook.
|
|
102
|
+
return [officialSeat(family), spec ?? EXPECTED[family]];
|
|
103
|
+
})) as { [K in OfficialSeat]: SlotSpec<SlotMap[K]> };
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function translatedComponent(entry: StoredEntry, names: ReadonlyMap<string, string>) {
|
|
107
|
+
if (names.size === 0) return entry.component;
|
|
108
|
+
const Original = entry.component as ComponentType<Record<string, unknown>>;
|
|
109
|
+
return memo(function OfficialSlotChildren(props: RenderProps) {
|
|
110
|
+
const renders = useMemo(() => {
|
|
111
|
+
const translate = (render: Render | undefined): Render => (key, owner, options) => {
|
|
112
|
+
const seat = names.get(key);
|
|
113
|
+
if (!seat || !render) throw new Error(`Undeclared official child slot: ${key}`);
|
|
114
|
+
return render(seat, owner, options);
|
|
115
|
+
};
|
|
116
|
+
return {
|
|
117
|
+
renderSlot: translate(props.renderSlot),
|
|
118
|
+
...(props.renderSlotChain ? { renderSlotChain: translate(props.renderSlotChain) } : {}),
|
|
119
|
+
};
|
|
120
|
+
}, [props.renderSlot, props.renderSlotChain]);
|
|
121
|
+
return createElement(Original, { ...props, ...renders });
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Mirror one elected contribution set incrementally. Unrelated additions do not
|
|
127
|
+
* remount existing cards or recreate their subscriptions. A source unload/HMR
|
|
128
|
+
* disposes the corresponding subtree and closures before its replacement.
|
|
129
|
+
*/
|
|
130
|
+
export function mirrorOfficialSlot(
|
|
131
|
+
slots: CompositionRegistry,
|
|
132
|
+
source: string,
|
|
133
|
+
target: string,
|
|
134
|
+
namespace: string,
|
|
135
|
+
accept: (entry: StoredEntry) => boolean = () => true,
|
|
136
|
+
): () => void {
|
|
137
|
+
const mounted = new Map<StoredEntry, () => void>();
|
|
138
|
+
let stopped = false;
|
|
139
|
+
const reconcile = () => {
|
|
140
|
+
if (stopped) return;
|
|
141
|
+
const entries = slots.entriesOfSlot(source).filter(accept);
|
|
142
|
+
const wanted = new Set(entries);
|
|
143
|
+
for (const [entry, dispose] of mounted) {
|
|
144
|
+
if (wanted.has(entry)) continue;
|
|
145
|
+
dispose();
|
|
146
|
+
mounted.delete(entry);
|
|
147
|
+
}
|
|
148
|
+
for (const entry of entries) {
|
|
149
|
+
if (mounted.has(entry)) continue;
|
|
150
|
+
const names = new Map(Object.keys(entry.children ?? {}).map(key => [key, `${namespace}/${key}`]));
|
|
151
|
+
const children = Object.fromEntries([...names].map(([key, seat]) => [seat, entry.children![key]]));
|
|
152
|
+
const disposers: (() => void)[] = [];
|
|
153
|
+
const dispose = () => { for (const stop of disposers.splice(0).reverse()) stop(); };
|
|
154
|
+
try {
|
|
155
|
+
const { component: _component, options, children: _children, ...metadata } = entry;
|
|
156
|
+
const presentation = source === OFFICIAL_SLOTS.tail && entry.locale === 'deliverables'
|
|
157
|
+
? { ...entry, component: tailPresentation(entry.component) }
|
|
158
|
+
: entry;
|
|
159
|
+
disposers.push(slots.register({
|
|
160
|
+
...options, ...metadata, name: target,
|
|
161
|
+
...(names.size ? { children } : {}),
|
|
162
|
+
registrant: `dsh-better-display → ${entry.registrant ?? source}`,
|
|
163
|
+
}, translatedComponent(presentation, names)));
|
|
164
|
+
for (const [key, seat] of names) {
|
|
165
|
+
disposers.push(mirrorOfficialSlot(slots, key, seat, namespace));
|
|
166
|
+
}
|
|
167
|
+
mounted.set(entry, dispose);
|
|
168
|
+
} catch (error) {
|
|
169
|
+
dispose();
|
|
170
|
+
throw error;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
const unsubscribe = slots.subscribe(source, reconcile);
|
|
175
|
+
const dispose = () => {
|
|
176
|
+
stopped = true;
|
|
177
|
+
unsubscribe();
|
|
178
|
+
for (const stop of [...mounted.values()].reverse()) stop();
|
|
179
|
+
mounted.clear();
|
|
180
|
+
};
|
|
181
|
+
try { reconcile(); } catch (error) { dispose(); throw error; }
|
|
182
|
+
return dispose;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
export function installOfficialSlots(ctx: Context): () => void {
|
|
186
|
+
// The SlotMap overloads are for statically declared keys. Aliases are a
|
|
187
|
+
// runtime composition, using exactly the public erased metadata contract.
|
|
188
|
+
const slots = ctx.slots as unknown as CompositionRegistry;
|
|
189
|
+
const disposers: (() => void)[] = [];
|
|
190
|
+
try {
|
|
191
|
+
for (const [name, source] of Object.entries(OFFICIAL_SLOTS)) {
|
|
192
|
+
const family = name as OfficialFamily;
|
|
193
|
+
disposers.push(slots.inject(source, () => {
|
|
194
|
+
const spec = slots.spec(source)!;
|
|
195
|
+
const target = officialSeat(family);
|
|
196
|
+
const declared = slots.spec(target);
|
|
197
|
+
if (spec.kind !== declared?.kind || spec.scope !== declared.scope) {
|
|
198
|
+
throw new Error(`Official slot contract changed: ${source}`);
|
|
199
|
+
}
|
|
200
|
+
return mirrorOfficialSlot(slots, source, target, `dsh-better-display.official.${family}`,
|
|
201
|
+
family === 'nodes' ? entry => !READER_NODES.has(entry.options.key ?? '') : undefined);
|
|
202
|
+
}));
|
|
203
|
+
}
|
|
204
|
+
} catch (error) {
|
|
205
|
+
for (const stop of disposers.reverse()) stop();
|
|
206
|
+
throw error;
|
|
207
|
+
}
|
|
208
|
+
return () => { for (const stop of disposers.splice(0).reverse()) stop(); };
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
export type OfficialSlotKey = typeof OFFICIAL_SLOTS[OfficialFamily] & keyof SlotMap;
|