@meiyukichan/dsh-sandore 0.1.0-rc.6
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/LICENSE +201 -0
- package/cordis.patch.yml +39 -0
- package/dsh.plugin.json +16 -0
- package/lib/client/CommandEditor.d.ts +19 -0
- package/lib/client/DagEditor.d.ts +22 -0
- package/lib/client/DefinitionDetail.d.ts +13 -0
- package/lib/client/DefinitionList.d.ts +19 -0
- package/lib/client/DiscussionInput.d.ts +63 -0
- package/lib/client/DragHandle.d.ts +16 -0
- package/lib/client/EditorPane.d.ts +13 -0
- package/lib/client/FlowEditor.d.ts +31 -0
- package/lib/client/InstanceList.d.ts +24 -0
- package/lib/client/NewInstanceDialog.d.ts +19 -0
- package/lib/client/PreviewPanel.d.ts +13 -0
- package/lib/client/RunView.d.ts +79 -0
- package/lib/client/StepChatPanel.d.ts +34 -0
- package/lib/client/TreeNav.d.ts +27 -0
- package/lib/client/UnifiedTimeline.d.ts +11 -0
- package/lib/client/WorkflowButton.d.ts +11 -0
- package/lib/client/WorkflowList.d.ts +18 -0
- package/lib/client/WorkflowOverlay.d.ts +6 -0
- package/lib/client/chat/AssistantMarkdown.d.ts +22 -0
- package/lib/client/chat/CompactionItem.d.ts +24 -0
- package/lib/client/chat/ContextBody.d.ts +136 -0
- package/lib/client/chat/ContextInjectionRow.d.ts +25 -0
- package/lib/client/chat/GenericCommandCard.d.ts +10 -0
- package/lib/client/chat/MessageIconActions.d.ts +41 -0
- package/lib/client/chat/MessageItem.d.ts +65 -0
- package/lib/client/chat/ReasoningRow.d.ts +13 -0
- package/lib/client/chat/TurnTailNodeView.d.ts +29 -0
- package/lib/client/chat/UserMessageRow.d.ts +18 -0
- package/lib/client/chat/attachment/ImageLightbox.d.ts +26 -0
- package/lib/client/chat/attachment/MessageImage.d.ts +56 -0
- package/lib/client/chat/attachment/image-attachment.d.ts +41 -0
- package/lib/client/chat/attachment/image-labels.d.ts +21 -0
- package/lib/client/chat/message-chrome.d.ts +51 -0
- package/lib/client/chat/reference/ReferenceIcon.d.ts +20 -0
- package/lib/client/chat/turn-assistant.d.ts +12 -0
- package/lib/client/chat/turn-metrics.d.ts +42 -0
- package/lib/client/chat/use-calendar-day.d.ts +5 -0
- package/lib/client/chat/use-throttled-visual-update.d.ts +7 -0
- package/lib/client/chat-flow.d.ts +110 -0
- package/lib/client/composer-selects.d.ts +17 -0
- package/lib/client/dag-editor-model.d.ts +141 -0
- package/lib/client/describe-error.d.ts +9 -0
- package/lib/client/index.d.ts +6 -0
- package/lib/client/locales.d.ts +777 -0
- package/lib/client/model-faces.d.ts +156 -0
- package/lib/client/open-discussion.d.ts +24 -0
- package/lib/client/panel-model.d.ts +34 -0
- package/lib/client/remote.d.ts +34 -0
- package/lib/client/run-view-model.d.ts +304 -0
- package/lib/client/sandore-save.d.ts +41 -0
- package/lib/client/sandore-sse.d.ts +15 -0
- package/lib/client/step-chat-retry.d.ts +44 -0
- package/lib/client/tool/GenericToolCard.d.ts +28 -0
- package/lib/client/tool/ToolRow.d.ts +95 -0
- package/lib/client/tool/diff-card-model.d.ts +63 -0
- package/lib/client/tool/read-card-model.d.ts +67 -0
- package/lib/client/tool/search-card-model.d.ts +94 -0
- package/lib/client/tool/terminal-card-model.d.ts +86 -0
- package/lib/client/tool/tool-call-model.d.ts +74 -0
- package/lib/client/tool/web-card-model.d.ts +44 -0
- package/lib/client/use-focus-refresh.d.ts +1 -0
- package/lib/client/workflow-face.d.ts +68 -0
- package/lib/client/workflow-store.d.ts +80 -0
- package/lib/client/workspace-path.d.ts +15 -0
- package/lib/client.js +38389 -0
- package/lib/index.js +8854 -0
- package/lib/microflow/branch-evaluator.d.ts +290 -0
- package/lib/microflow/concurrency-limiter.d.ts +39 -0
- package/lib/microflow/errors.d.ts +39 -0
- package/lib/microflow/file-utils.d.ts +210 -0
- package/lib/microflow/finalize-manager.d.ts +414 -0
- package/lib/microflow/fork-session.d.ts +151 -0
- package/lib/microflow/index.d.ts +17 -0
- package/lib/microflow/internal.d.ts +56 -0
- package/lib/microflow/loop-manager.d.ts +127 -0
- package/lib/microflow/rules-resolver.d.ts +28 -0
- package/lib/microflow/stage-executor.d.ts +362 -0
- package/lib/microflow/step-navigator.d.ts +108 -0
- package/lib/microflow/step-runner.d.ts +190 -0
- package/lib/microflow/subagents.d.ts +380 -0
- package/lib/tool/index.d.ts +65 -0
- package/lib/typert.host.d.ts +16 -0
- package/lib/typert.host.js +25 -0
- package/lib/typert.remote-client.d.ts +114 -0
- package/lib/typert.remote-client.js +399 -0
- package/lib/workflow/ask-tool.d.ts +78 -0
- package/lib/workflow/dag-navigator.d.ts +136 -0
- package/lib/workflow/domain.d.ts +304 -0
- package/lib/workflow/engine-surface.d.ts +70 -0
- package/lib/workflow/errors.d.ts +13 -0
- package/lib/workflow/host-agent.d.ts +53 -0
- package/lib/workflow/index.d.ts +23 -0
- package/lib/workflow/interaction-manager.d.ts +170 -0
- package/lib/workflow/lifecycle-manager.d.ts +111 -0
- package/lib/workflow/persistence.d.ts +35 -0
- package/lib/workflow/plugin.d.ts +27 -0
- package/lib/workflow/reactive-state.d.ts +27 -0
- package/lib/workflow/recovery-manager.d.ts +158 -0
- package/lib/workflow/remote.d.ts +139 -0
- package/lib/workflow/schema.d.ts +1917 -0
- package/lib/workflow/seam.d.ts +254 -0
- package/lib/workflow/shell.d.ts +127 -0
- package/lib/workflow/sse.d.ts +16 -0
- package/lib/workflow/typert/typert.host.d.ts +16 -0
- package/lib/workflow/typert/typert.remote-client.d.ts +114 -0
- package/lib/workflow/types.d.ts +187 -0
- package/lib/workflow/wire.d.ts +256 -0
- package/lib/workflow/workflow-engine.d.ts +373 -0
- package/lib/workflow/yaml-error.d.ts +22 -0
- package/lib/workflow/yaml-parser.d.ts +23 -0
- package/package.json +140 -0
- package/packages/client/CommandEditor.tsx +73 -0
- package/packages/client/DagEditor.module.css +711 -0
- package/packages/client/DagEditor.tsx +1203 -0
- package/packages/client/DefinitionDetail.module.css +110 -0
- package/packages/client/DefinitionDetail.tsx +126 -0
- package/packages/client/DefinitionList.tsx +173 -0
- package/packages/client/DiscussionInput.tsx +763 -0
- package/packages/client/DragHandle.tsx +92 -0
- package/packages/client/EditorPane.tsx +70 -0
- package/packages/client/FlowEditor.tsx +227 -0
- package/packages/client/InstanceList.tsx +250 -0
- package/packages/client/NewInstanceDialog.module.css +244 -0
- package/packages/client/NewInstanceDialog.tsx +478 -0
- package/packages/client/PreviewPanel.tsx +55 -0
- package/packages/client/RunView.module.css +246 -0
- package/packages/client/RunView.tsx +314 -0
- package/packages/client/StepChatPanel.tsx +486 -0
- package/packages/client/TreeNav.tsx +294 -0
- package/packages/client/UnifiedTimeline.tsx +484 -0
- package/packages/client/WorkflowButton.module.css +72 -0
- package/packages/client/WorkflowButton.tsx +50 -0
- package/packages/client/WorkflowList.tsx +158 -0
- package/packages/client/WorkflowOverlay.module.css +390 -0
- package/packages/client/WorkflowOverlay.tsx +136 -0
- package/packages/client/WorkflowPanel.module.css +247 -0
- package/packages/client/chat/AssistantMarkdown.module.css +38 -0
- package/packages/client/chat/AssistantMarkdown.tsx +120 -0
- package/packages/client/chat/ChatView.module.css +198 -0
- package/packages/client/chat/CompactionItem.tsx +85 -0
- package/packages/client/chat/ContextBody.module.css +162 -0
- package/packages/client/chat/ContextBody.tsx +598 -0
- package/packages/client/chat/ContextInjectionRow.module.css +63 -0
- package/packages/client/chat/ContextInjectionRow.tsx +78 -0
- package/packages/client/chat/GenericCommandCard.module.css +87 -0
- package/packages/client/chat/GenericCommandCard.tsx +76 -0
- package/packages/client/chat/MessageIconActions.module.css +84 -0
- package/packages/client/chat/MessageIconActions.tsx +147 -0
- package/packages/client/chat/MessageItem.module.css +293 -0
- package/packages/client/chat/MessageItem.tsx +384 -0
- package/packages/client/chat/ReasoningRow.module.css +82 -0
- package/packages/client/chat/ReasoningRow.tsx +70 -0
- package/packages/client/chat/TurnTailNodeView.module.css +10 -0
- package/packages/client/chat/TurnTailNodeView.tsx +63 -0
- package/packages/client/chat/UserMessageRow.module.css +75 -0
- package/packages/client/chat/UserMessageRow.tsx +92 -0
- package/packages/client/chat/accessibility.module.css +9 -0
- package/packages/client/chat/attachment/ImageLightbox.module.css +45 -0
- package/packages/client/chat/attachment/ImageLightbox.tsx +70 -0
- package/packages/client/chat/attachment/MessageImage.module.css +70 -0
- package/packages/client/chat/attachment/MessageImage.tsx +136 -0
- package/packages/client/chat/attachment/image-attachment.ts +98 -0
- package/packages/client/chat/attachment/image-labels.ts +34 -0
- package/packages/client/chat/message-chrome.ts +96 -0
- package/packages/client/chat/reference/ReferenceIcon.tsx +40 -0
- package/packages/client/chat/turn-assistant.ts +15 -0
- package/packages/client/chat/turn-metrics.ts +102 -0
- package/packages/client/chat/use-calendar-day.ts +27 -0
- package/packages/client/chat/use-throttled-visual-update.ts +44 -0
- package/packages/client/chat-flow.spec.ts +357 -0
- package/packages/client/chat-flow.ts +278 -0
- package/packages/client/composer-selects.module.css +376 -0
- package/packages/client/composer-selects.tsx +531 -0
- package/packages/client/dag-editor-model.spec.ts +173 -0
- package/packages/client/dag-editor-model.ts +359 -0
- package/packages/client/describe-error.spec.ts +18 -0
- package/packages/client/describe-error.ts +12 -0
- package/packages/client/index.ts +144 -0
- package/packages/client/input.module.css +854 -0
- package/packages/client/is-conditional-check.spec.ts +34 -0
- package/packages/client/locales.ts +789 -0
- package/packages/client/model-faces.ts +225 -0
- package/packages/client/open-discussion.spec.ts +39 -0
- package/packages/client/open-discussion.ts +32 -0
- package/packages/client/panel-model.spec.ts +152 -0
- package/packages/client/panel-model.ts +72 -0
- package/packages/client/preview.module.css +95 -0
- package/packages/client/react-dom.d.ts +11 -0
- package/packages/client/reactflow-theme.css +91 -0
- package/packages/client/remote.spec.ts +41 -0
- package/packages/client/remote.ts +46 -0
- package/packages/client/run-view-model.spec.ts +641 -0
- package/packages/client/run-view-model.ts +636 -0
- package/packages/client/sandore-save.spec.ts +146 -0
- package/packages/client/sandore-save.ts +97 -0
- package/packages/client/sandore-sse.ts +59 -0
- package/packages/client/step-chat-retry.spec.ts +70 -0
- package/packages/client/step-chat-retry.ts +65 -0
- package/packages/client/timeline.module.css +503 -0
- package/packages/client/tool/GenericToolCard.tsx +99 -0
- package/packages/client/tool/ToolRow.module.css +308 -0
- package/packages/client/tool/ToolRow.tsx +328 -0
- package/packages/client/tool/diff-card-model.ts +103 -0
- package/packages/client/tool/read-card-model.ts +86 -0
- package/packages/client/tool/search-card-model.ts +166 -0
- package/packages/client/tool/terminal-card-model.ts +225 -0
- package/packages/client/tool/tool-call-model.ts +267 -0
- package/packages/client/tool/web-card-model.ts +80 -0
- package/packages/client/tree-nav.module.css +282 -0
- package/packages/client/use-focus-refresh.ts +23 -0
- package/packages/client/workflow-face.ts +72 -0
- package/packages/client/workflow-store.spec.ts +49 -0
- package/packages/client/workflow-store.ts +161 -0
- package/packages/client/workspace-path.ts +27 -0
- package/packages/css-modules.d.ts +6 -0
- package/packages/microflow/branch-evaluator.spec.ts +745 -0
- package/packages/microflow/branch-evaluator.ts +1037 -0
- package/packages/microflow/concurrency-limiter.ts +70 -0
- package/packages/microflow/errors.ts +64 -0
- package/packages/microflow/file-utils.spec.ts +604 -0
- package/packages/microflow/file-utils.ts +521 -0
- package/packages/microflow/finalize-manager.spec.ts +1977 -0
- package/packages/microflow/finalize-manager.ts +1464 -0
- package/packages/microflow/fork-session.spec.ts +184 -0
- package/packages/microflow/fork-session.ts +316 -0
- package/packages/microflow/index.ts +17 -0
- package/packages/microflow/internal.ts +166 -0
- package/packages/microflow/loop-manager.spec.ts +294 -0
- package/packages/microflow/loop-manager.ts +255 -0
- package/packages/microflow/rules-resolver.spec.ts +147 -0
- package/packages/microflow/rules-resolver.ts +58 -0
- package/packages/microflow/stage-executor.spec.ts +1577 -0
- package/packages/microflow/stage-executor.ts +1478 -0
- package/packages/microflow/step-navigator.spec.ts +301 -0
- package/packages/microflow/step-navigator.ts +300 -0
- package/packages/microflow/step-runner.spec.ts +1132 -0
- package/packages/microflow/step-runner.ts +752 -0
- package/packages/microflow/subagents.spec.ts +215 -0
- package/packages/microflow/subagents.ts +422 -0
- package/packages/tests/fixtures/test-workflow.yaml +128 -0
- package/packages/tests/mocks/agents.ts +206 -0
- package/packages/tests/mocks/llm.ts +46 -0
- package/packages/tests/mocks/session-title.ts +31 -0
- package/packages/tests/mocks/sessions.ts +131 -0
- package/packages/tests/mocks/shell.ts +60 -0
- package/packages/tests/mocks/storage-domain.ts +101 -0
- package/packages/tests/mocks/subagents.ts +197 -0
- package/packages/tests/mocks/subprocess.ts +59 -0
- package/packages/tests/mocks/user-questions.ts +33 -0
- package/packages/tests/mocks/workspace.ts +52 -0
- package/packages/tool/index.spec.ts +287 -0
- package/packages/tool/index.ts +348 -0
- package/packages/workflow/ask-tool.ts +173 -0
- package/packages/workflow/dag-navigator.spec.ts +236 -0
- package/packages/workflow/dag-navigator.ts +425 -0
- package/packages/workflow/domain.ts +26 -0
- package/packages/workflow/engine-surface.ts +71 -0
- package/packages/workflow/errors.ts +13 -0
- package/packages/workflow/host-agent.spec.ts +92 -0
- package/packages/workflow/host-agent.ts +62 -0
- package/packages/workflow/index.ts +25 -0
- package/packages/workflow/interaction-manager.spec.ts +390 -0
- package/packages/workflow/interaction-manager.ts +397 -0
- package/packages/workflow/lifecycle-manager.spec.ts +683 -0
- package/packages/workflow/lifecycle-manager.ts +288 -0
- package/packages/workflow/persistence.spec.ts +109 -0
- package/packages/workflow/persistence.ts +64 -0
- package/packages/workflow/plugin.ts +39 -0
- package/packages/workflow/reactive-state.spec.ts +69 -0
- package/packages/workflow/reactive-state.ts +52 -0
- package/packages/workflow/recovery-manager.spec.ts +606 -0
- package/packages/workflow/recovery-manager.ts +471 -0
- package/packages/workflow/remote.spec.ts +1045 -0
- package/packages/workflow/remote.ts +479 -0
- package/packages/workflow/schema.spec.ts +303 -0
- package/packages/workflow/schema.ts +562 -0
- package/packages/workflow/seam.ts +293 -0
- package/packages/workflow/shell.ts +142 -0
- package/packages/workflow/sse.ts +110 -0
- package/packages/workflow/typert/typert.host.d.ts +16 -0
- package/packages/workflow/typert/typert.host.js +25 -0
- package/packages/workflow/typert/typert.remote-client.d.ts +114 -0
- package/packages/workflow/typert/typert.remote-client.js +399 -0
- package/packages/workflow/types.ts +258 -0
- package/packages/workflow/wire.ts +267 -0
- package/packages/workflow/workflow-engine-e2e.spec.ts +262 -0
- package/packages/workflow/workflow-engine-real-e2e.spec.ts +922 -0
- package/packages/workflow/workflow-engine.spec.ts +4389 -0
- package/packages/workflow/workflow-engine.ts +2133 -0
- package/packages/workflow/yaml-error.ts +29 -0
- package/packages/workflow/yaml-parser.spec.ts +757 -0
- package/packages/workflow/yaml-parser.ts +287 -0
|
@@ -0,0 +1,484 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UnifiedTimeline:执行详情对话流(按 execution-detail-prototype.html 原型)。
|
|
3
|
+
*
|
|
4
|
+
* multica Issue 评论流 × DSH 多 agent 对话流:用户输入 = 右侧气泡;每个
|
|
5
|
+
* executedStep = 一条 agent 消息(左对齐,头像 + 可点击标题 → openSession 打开
|
|
6
|
+
* DSH 现成子代理会话页 + 相对时间 + 状态图标)。正文按状态呈现:
|
|
7
|
+
* completed → md 渲染块(MarkdownText,原型 .md 样式);running/interacting 与
|
|
8
|
+
* failed/paused(真实子代理会话)→ 内嵌 DSH 对话流(StepChatPanel,最新 10 条,
|
|
9
|
+
* 无边框平铺,风格与运行中完全一致);failed/paused 无真实会话(shell 等合成
|
|
10
|
+
* 会话)→ 最近 5 条消息列表兜底(末条按失败/暂停色)。旧实例无时间戳时头部省略时间。
|
|
11
|
+
* @module @deepseek-ai/dsh-client-ui-sandore/src/client/UnifiedTimeline
|
|
12
|
+
*/
|
|
13
|
+
import { IconSparkle16, MarkdownText } from '@deepseek-ai/dsh-client-ui-primitives'
|
|
14
|
+
import type { TranslateNS } from '@deepseek-ai/dsh-client-ui-slots'
|
|
15
|
+
import { useState } from 'react'
|
|
16
|
+
|
|
17
|
+
import type { SandoreKey } from './locales.ts'
|
|
18
|
+
import {
|
|
19
|
+
relativeTimeOf, type ChatLine, type RelativeTime, type StepStatus, type TimelineItem, type TimelineStepItem,
|
|
20
|
+
} from './run-view-model.ts'
|
|
21
|
+
import { StepChatPanel } from './StepChatPanel.tsx'
|
|
22
|
+
import css from './timeline.module.css'
|
|
23
|
+
|
|
24
|
+
/** Step 执行状态 → 本地化标签键(7 态)。 */
|
|
25
|
+
const STEP_STATUS_KEYS: Record<StepStatus, SandoreKey> = {
|
|
26
|
+
blocked: 'run.step.status.blocked',
|
|
27
|
+
ready: 'run.step.status.ready',
|
|
28
|
+
interacting: 'run.step.status.interacting',
|
|
29
|
+
running: 'run.step.status.running',
|
|
30
|
+
completed: 'run.step.status.completed',
|
|
31
|
+
failed: 'run.step.status.failed',
|
|
32
|
+
paused: 'run.step.status.paused',
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** 收尾阶段(finalizeMeta.currentPhase)→ 本地化标签键。
|
|
36
|
+
* Phase A 显示「检测中」;completed 终态显示「已完成」(区别于「已收尾」= Phase B 完成态)。 */
|
|
37
|
+
function finalizePhaseKey(phase: string | undefined): SandoreKey | undefined {
|
|
38
|
+
switch (phase) {
|
|
39
|
+
case 'ask-detect':
|
|
40
|
+
return 'run.step.finalize.phase.detecting'
|
|
41
|
+
case 'finalizing':
|
|
42
|
+
return 'run.step.finalize.phase.finalizing'
|
|
43
|
+
case 'gating':
|
|
44
|
+
return 'run.step.finalize.phase.gating'
|
|
45
|
+
case 'routing':
|
|
46
|
+
return 'run.step.finalize.phase.routing'
|
|
47
|
+
case 'completed':
|
|
48
|
+
return 'run.step.finalize.phase.completed'
|
|
49
|
+
default:
|
|
50
|
+
return undefined
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** 状态 → 展示色类(原型 .st 的 4 色)。 */
|
|
55
|
+
function statusClass(status: StepStatus): string {
|
|
56
|
+
switch (status) {
|
|
57
|
+
case 'completed':
|
|
58
|
+
return css.stSuccess ?? ''
|
|
59
|
+
case 'failed':
|
|
60
|
+
return css.stDestructive ?? ''
|
|
61
|
+
case 'paused':
|
|
62
|
+
return css.stWarning ?? ''
|
|
63
|
+
case 'running':
|
|
64
|
+
case 'interacting':
|
|
65
|
+
default:
|
|
66
|
+
return css.stInfo ?? ''
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** 状态 SVG 图标(照原型 4 种:成功对勾 / 运行转圈 / 失败叉 / 暂停叹号)。 */
|
|
71
|
+
function StatusIcon({ status }: { readonly status: StepStatus }) {
|
|
72
|
+
switch (status) {
|
|
73
|
+
case 'completed':
|
|
74
|
+
return (
|
|
75
|
+
<svg viewBox="0 0 14 14" fill="none" aria-hidden>
|
|
76
|
+
<circle cx="7" cy="7" r="5.5" stroke="currentColor" strokeWidth="1.4" fill="currentColor" />
|
|
77
|
+
<path d="M4.6 7.2l1.6 1.6 3.2-3.4" stroke="#fff" strokeWidth="1.3" fill="none" strokeLinecap="round" />
|
|
78
|
+
</svg>
|
|
79
|
+
)
|
|
80
|
+
case 'failed':
|
|
81
|
+
return (
|
|
82
|
+
<svg viewBox="0 0 14 14" fill="none" aria-hidden>
|
|
83
|
+
<circle cx="7" cy="7" r="5.5" stroke="currentColor" strokeWidth="1.4" />
|
|
84
|
+
<path d="M4.6 4.6l4.8 4.8M9.4 4.6l-4.8 4.8" stroke="currentColor" strokeWidth="1.3" strokeLinecap="round" />
|
|
85
|
+
</svg>
|
|
86
|
+
)
|
|
87
|
+
case 'paused':
|
|
88
|
+
return (
|
|
89
|
+
<svg viewBox="0 0 14 14" fill="none" aria-hidden>
|
|
90
|
+
<circle cx="7" cy="7" r="5.5" stroke="currentColor" strokeWidth="1.4" />
|
|
91
|
+
<path d="M7 4v3.2" stroke="currentColor" strokeWidth="1.4" strokeLinecap="round" />
|
|
92
|
+
<circle cx="7" cy="10" r="0.7" fill="currentColor" />
|
|
93
|
+
</svg>
|
|
94
|
+
)
|
|
95
|
+
case 'running':
|
|
96
|
+
case 'interacting':
|
|
97
|
+
default:
|
|
98
|
+
return (
|
|
99
|
+
<svg viewBox="0 0 14 14" fill="none" aria-hidden>
|
|
100
|
+
<circle cx="7" cy="7" r="5.5" stroke="currentColor" strokeWidth="1.4" />
|
|
101
|
+
<path d="M7 1.5a5.5 5.5 0 1 1-5.5 5.5" stroke="currentColor" strokeWidth="1.4" strokeLinecap="round" />
|
|
102
|
+
</svg>
|
|
103
|
+
)
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** 相对时间 → 字典文案。 */
|
|
108
|
+
function formatTime(t: TranslateNS<'sandore'>, rel: RelativeTime | undefined): string {
|
|
109
|
+
if (rel === undefined) return ''
|
|
110
|
+
switch (rel.kind) {
|
|
111
|
+
case 'justNow':
|
|
112
|
+
return t('run.time.justNow')
|
|
113
|
+
case 'minutes':
|
|
114
|
+
return t('run.time.minutesAgo', { count: rel.count })
|
|
115
|
+
case 'hours':
|
|
116
|
+
return t('run.time.hoursAgo', { count: rel.count })
|
|
117
|
+
case 'days':
|
|
118
|
+
return t('run.time.daysAgo', { count: rel.count })
|
|
119
|
+
case 'date':
|
|
120
|
+
return rel.text
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/** 单行消息(agent 内嵌框 / 失败·暂停列表共用)。 */
|
|
125
|
+
function ChatLineRow({ line }: { readonly line: ChatLine }) {
|
|
126
|
+
return (
|
|
127
|
+
<div className={`${css.chatLine} ${line.kind === 'tool' ? css.chatLineTool : css.chatLineAssistant}`}>
|
|
128
|
+
<span className={css.chatWho}>{line.who}</span>
|
|
129
|
+
<span className={css.chatTxt}>{line.text}</span>
|
|
130
|
+
</div>
|
|
131
|
+
)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/** 兜底:失败/暂停且无真实子代理会话(shell 等合成会话)时的最近 N 条消息列表。 */
|
|
135
|
+
function LastMessages({ item, t }: {
|
|
136
|
+
readonly item: TimelineStepItem
|
|
137
|
+
readonly t: TranslateNS<'sandore'>
|
|
138
|
+
}) {
|
|
139
|
+
const lines = item.messages ?? []
|
|
140
|
+
const failed = item.status === 'failed'
|
|
141
|
+
const listClass = failed ? css.agentLastListFailed : css.agentLastListPaused
|
|
142
|
+
// failed 态错误行优先取收尾错误详情(门禁失败 finalizeMeta.errorDetail),其次段 error。
|
|
143
|
+
const fallback = failed
|
|
144
|
+
? (item.finalizeErrorDetail ?? item.error) !== undefined
|
|
145
|
+
? [{ who: `${item.name} · agent`, text: item.finalizeErrorDetail ?? item.error ?? '', kind: 'assistant' as const }]
|
|
146
|
+
: []
|
|
147
|
+
: [{ who: `${item.name} · agent`, text: t('step.paused.hint'), kind: 'assistant' as const }]
|
|
148
|
+
const shown = lines.length > 0 ? lines.slice(-5) : fallback
|
|
149
|
+
return (
|
|
150
|
+
<div className={`${css.agentLastList} ${listClass}`}>
|
|
151
|
+
<div className={css.agentLastScroll}>
|
|
152
|
+
{shown.map((line, index) => (
|
|
153
|
+
<ChatLineRow key={index} line={line} />
|
|
154
|
+
))}
|
|
155
|
+
</div>
|
|
156
|
+
</div>
|
|
157
|
+
)
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/** 收尾阶段 SVG 图标(语义化:finalizing=归档盒子 archive/ gating=盾牌/ routing=分支/ completed=对勾/ ask-detect=问号)。 */
|
|
161
|
+
function PhaseIcon({ phase }: { readonly phase: string }) {
|
|
162
|
+
switch (phase) {
|
|
163
|
+
case 'finalizing':
|
|
164
|
+
return (
|
|
165
|
+
<svg viewBox="0 0 14 14" fill="none" aria-hidden>
|
|
166
|
+
{/* 归档盒子:盒体 + 向下箭头(收尾归档语义) */}
|
|
167
|
+
<path d="M2.5 3.5h9l.5 2.5H2z" stroke="currentColor" strokeWidth="1.2" strokeLinejoin="round" />
|
|
168
|
+
<path d="M3 6v4.5h8V6" stroke="currentColor" strokeWidth="1.2" strokeLinejoin="round" />
|
|
169
|
+
<path d="M7 4v3M5.5 5.5L7 7l1.5-1.5" stroke="currentColor" strokeWidth="1.2" strokeLinecap="round" strokeLinejoin="round" />
|
|
170
|
+
</svg>
|
|
171
|
+
)
|
|
172
|
+
case 'gating':
|
|
173
|
+
return (
|
|
174
|
+
<svg viewBox="0 0 14 14" fill="none" aria-hidden>
|
|
175
|
+
<path d="M7 1.5L2.5 3.5v3c0 3.5 4.5 6 4.5 6s4.5-2.5 4.5-6v-3L7 1.5z" stroke="currentColor" strokeWidth="1.2" strokeLinecap="round" strokeLinejoin="round" />
|
|
176
|
+
</svg>
|
|
177
|
+
)
|
|
178
|
+
case 'routing':
|
|
179
|
+
return (
|
|
180
|
+
<svg viewBox="0 0 14 14" fill="none" aria-hidden>
|
|
181
|
+
<path d="M2 3h4m0 0L4 1m2 2L4 5M2 11h4m0 0l-2-2m2 2l-2 2M8 7h4m0 0l-2-2m2 2l-2 2" stroke="currentColor" strokeWidth="1.2" strokeLinecap="round" strokeLinejoin="round" />
|
|
182
|
+
</svg>
|
|
183
|
+
)
|
|
184
|
+
case 'completed':
|
|
185
|
+
return (
|
|
186
|
+
<svg viewBox="0 0 14 14" fill="none" aria-hidden>
|
|
187
|
+
<circle cx="7" cy="7" r="5.5" stroke="currentColor" strokeWidth="1.4" fill="currentColor" />
|
|
188
|
+
<path d="M4.6 7.2l1.6 1.6 3.2-3.4" stroke="#fff" strokeWidth="1.3" fill="none" strokeLinecap="round" />
|
|
189
|
+
</svg>
|
|
190
|
+
)
|
|
191
|
+
case 'ask-detect':
|
|
192
|
+
return (
|
|
193
|
+
<svg viewBox="0 0 14 14" fill="none" aria-hidden>
|
|
194
|
+
<circle cx="7" cy="7" r="5.5" stroke="currentColor" strokeWidth="1.2" />
|
|
195
|
+
<path d="M5.5 5.5a1.5 1.5 0 0 1 3 0c0 1-1.5 1-1.5 2" stroke="currentColor" strokeWidth="1.2" strokeLinecap="round" />
|
|
196
|
+
<circle cx="7" cy="10" r="0.5" fill="currentColor" />
|
|
197
|
+
</svg>
|
|
198
|
+
)
|
|
199
|
+
default:
|
|
200
|
+
return null
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* 收尾阶段徽标序列(最多五个过程状态位置同时展示):
|
|
206
|
+
* Round 17:纯 status 判断(删除 isPhaseDone/phaseHistory 依赖)。
|
|
207
|
+
* ① 运行状态(已完成)② Phase A【已检测/检测中】③ Phase B【已收尾/收尾中】
|
|
208
|
+
* ④ Phase C【已门禁/门禁失败/门禁中】⑤ Phase D【已判断/判断中】
|
|
209
|
+
* Phase B/C/D 可点击(进入对应子代理会话);Phase A 不可点击(临时会话,用完即删)。
|
|
210
|
+
* Round 20:completed 后 fork 会话已 dispose(live 移除),点击会报 unknown session——
|
|
211
|
+
* completed 态将 Phase B/C/D 的 target 置 undefined,徽标降级为纯文本(不可点击)。
|
|
212
|
+
*/
|
|
213
|
+
function PhaseBadge({ item, t, openSession }: {
|
|
214
|
+
readonly item: TimelineStepItem
|
|
215
|
+
readonly t: TranslateNS<'sandore'>
|
|
216
|
+
readonly openSession: (id: string) => void
|
|
217
|
+
}) {
|
|
218
|
+
// Round 20:completed 后 fork 会话已 dispose,badge 降级为纯文本(不可点击)。
|
|
219
|
+
const isCompleted = item.status === 'completed'
|
|
220
|
+
const badges: Array<{ key: string; labelKey: SandoreKey; phase: string; target: string | undefined; isActive: boolean; isFailed: boolean }> = []
|
|
221
|
+
|
|
222
|
+
// Phase A 状态(ask-detect:临时评估会话,用完即删,不可点击)
|
|
223
|
+
if (item.askStatus !== undefined) {
|
|
224
|
+
const done = item.askStatus === 'done'
|
|
225
|
+
badges.push({
|
|
226
|
+
key: 'phase-a',
|
|
227
|
+
labelKey: done ? 'run.step.finalize.phase.detected' : 'run.step.finalize.phase.detecting',
|
|
228
|
+
phase: 'ask-detect',
|
|
229
|
+
target: undefined, // Phase A 无持久化会话,不可点击
|
|
230
|
+
isActive: !done,
|
|
231
|
+
isFailed: false,
|
|
232
|
+
})
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// Phase B 状态
|
|
236
|
+
if (item.assessStatus !== undefined) {
|
|
237
|
+
const done = item.assessStatus === 'done'
|
|
238
|
+
badges.push({
|
|
239
|
+
key: 'phase-b',
|
|
240
|
+
labelKey: done ? 'run.step.finalize.phase.done' : 'run.step.finalize.phase.finalizing',
|
|
241
|
+
phase: 'finalizing',
|
|
242
|
+
// completed 后 fork 会话已 dispose → 不可点击
|
|
243
|
+
target: isCompleted ? undefined : item.finalizeAssessSessionId,
|
|
244
|
+
isActive: !done,
|
|
245
|
+
isFailed: false,
|
|
246
|
+
})
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Phase C 状态
|
|
250
|
+
if (item.gateStatus !== undefined) {
|
|
251
|
+
const done = item.gateStatus === 'done'
|
|
252
|
+
const failed = item.gateStatus === 'failed'
|
|
253
|
+
badges.push({
|
|
254
|
+
key: 'phase-c',
|
|
255
|
+
labelKey: failed ? 'run.step.finalize.phase.gate-failed'
|
|
256
|
+
: done ? 'run.step.finalize.phase.gated'
|
|
257
|
+
: 'run.step.finalize.phase.gating',
|
|
258
|
+
phase: 'gating',
|
|
259
|
+
// completed 后 fork 会话已 dispose → 不可点击
|
|
260
|
+
target: isCompleted ? undefined : item.finalizeGateSessionId,
|
|
261
|
+
isActive: !done && !failed,
|
|
262
|
+
isFailed: failed,
|
|
263
|
+
})
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Phase D 状态
|
|
267
|
+
if (item.routingStatus !== undefined) {
|
|
268
|
+
const done = item.routingStatus === 'done'
|
|
269
|
+
badges.push({
|
|
270
|
+
key: 'phase-d',
|
|
271
|
+
labelKey: done ? 'run.step.finalize.phase.routed' : 'run.step.finalize.phase.routing',
|
|
272
|
+
phase: 'routing',
|
|
273
|
+
// completed 后 fork 会话已 dispose → 不可点击
|
|
274
|
+
target: isCompleted ? undefined : item.finalizeRoutingSessionId,
|
|
275
|
+
isActive: !done,
|
|
276
|
+
isFailed: false,
|
|
277
|
+
})
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// 注意:不渲染 phase-completed badge——运行状态 badge(step.status === 'completed'
|
|
281
|
+
// → 「已完成」)已表达「全部完成」语义,避免重复。
|
|
282
|
+
|
|
283
|
+
if (badges.length === 0) return null
|
|
284
|
+
|
|
285
|
+
return (
|
|
286
|
+
<>
|
|
287
|
+
{badges.map(badge => (
|
|
288
|
+
badge.target !== undefined && badge.target !== '' ? (
|
|
289
|
+
<button
|
|
290
|
+
key={badge.key}
|
|
291
|
+
type="button"
|
|
292
|
+
className={`${css.phaseBtn} ${badge.isFailed ? css.phaseFailed : badge.isActive ? css.phaseActive : css.phaseDone}`}
|
|
293
|
+
title={t('step.open', { name: t(badge.labelKey) })}
|
|
294
|
+
onClick={(e) => { e.stopPropagation(); openSession(badge.target!) }}
|
|
295
|
+
>
|
|
296
|
+
<PhaseIcon phase={badge.phase} />
|
|
297
|
+
<span>{t(badge.labelKey)}</span>
|
|
298
|
+
</button>
|
|
299
|
+
) : (
|
|
300
|
+
<span key={badge.key} className={`${css.phaseLabel} ${badge.isFailed ? css.phaseFailed : badge.isActive ? css.phaseActive : css.phaseDone}`}>
|
|
301
|
+
<PhaseIcon phase={badge.phase} />
|
|
302
|
+
<span>{t(badge.labelKey)}</span>
|
|
303
|
+
</span>
|
|
304
|
+
)
|
|
305
|
+
))}
|
|
306
|
+
</>
|
|
307
|
+
)
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/** 单条 agent 消息(一个 executedStep)。 */
|
|
311
|
+
function AgentMsg({ item, t, nowIso, openSession }: {
|
|
312
|
+
readonly item: TimelineStepItem
|
|
313
|
+
readonly t: TranslateNS<'sandore'>
|
|
314
|
+
readonly nowIso: string
|
|
315
|
+
readonly openSession: (id: string) => void
|
|
316
|
+
}) {
|
|
317
|
+
const running = item.status === 'running' || item.status === 'interacting'
|
|
318
|
+
const terminalChat = item.status === 'failed' || item.status === 'paused'
|
|
319
|
+
const rel = relativeTimeOf(item.time, nowIso)
|
|
320
|
+
// 状态按钮:点击折叠/展开消息区(正文按状态渲染)。
|
|
321
|
+
const [collapsed, setCollapsed] = useState(false)
|
|
322
|
+
// 仅真实子代理步骤(prompt/agent/skill/mcp,非合成会话)才有真实会话:标题可点击跳
|
|
323
|
+
// DSH 子代理页、正文内嵌该会话的 DSH 对话流;shell 的 childId 是合成占位(无真实
|
|
324
|
+
// 会话),标题渲染为纯文本、正文走兜底。
|
|
325
|
+
const realSession = item.childId !== undefined && item.synthetic !== true
|
|
326
|
+
return (
|
|
327
|
+
<div className={css.agentMsg} data-tl-step>
|
|
328
|
+
<div className={css.agentHead}>
|
|
329
|
+
<span className={`${css.avatar} ${css.avatarAgent}`} aria-hidden><IconSparkle16 /></span>
|
|
330
|
+
{realSession ? (
|
|
331
|
+
<button
|
|
332
|
+
type="button"
|
|
333
|
+
className={css.authorLink}
|
|
334
|
+
title={t('step.open', { name: item.name })}
|
|
335
|
+
onClick={() => { openSession(item.childId as string) }}
|
|
336
|
+
>
|
|
337
|
+
{item.name}
|
|
338
|
+
</button>
|
|
339
|
+
) : (
|
|
340
|
+
<span className={css.author}>{item.name}</span>
|
|
341
|
+
)}
|
|
342
|
+
{item.loopLabel !== undefined && <span className={css.loopLabel}>{item.loopLabel}</span>}
|
|
343
|
+
{item.attemptIndex !== undefined && item.attemptIndex > 0 && (
|
|
344
|
+
<span className={css.loopLabel}>{t('run.step.attempt', { n: item.attemptIndex + 1 })}</span>
|
|
345
|
+
)}
|
|
346
|
+
{rel !== undefined && <span className={css.time}>{formatTime(t, rel)}</span>}
|
|
347
|
+
{/* 状态按钮:状态 + 收尾阶段徽标;点击折叠/展开消息区。 */}
|
|
348
|
+
<button
|
|
349
|
+
type="button"
|
|
350
|
+
className={`${css.st} ${css.stBtn} ${statusClass(item.status)}`}
|
|
351
|
+
title={t(STEP_STATUS_KEYS[item.status])}
|
|
352
|
+
aria-label={t(STEP_STATUS_KEYS[item.status])}
|
|
353
|
+
aria-expanded={!collapsed}
|
|
354
|
+
onClick={() => { setCollapsed(value => !value) }}
|
|
355
|
+
>
|
|
356
|
+
<StatusIcon status={item.status} />
|
|
357
|
+
<span className={css.stLbl}>{t(STEP_STATUS_KEYS[item.status])}</span>
|
|
358
|
+
<PhaseBadge item={item} t={t} openSession={openSession} />
|
|
359
|
+
</button>
|
|
360
|
+
</div>
|
|
361
|
+
|
|
362
|
+
{!collapsed && (
|
|
363
|
+
<>
|
|
364
|
+
{item.status === 'completed' && (
|
|
365
|
+
// Round 20:shell 步骤无收尾压缩(跳过 Phase A/B,compressedOutput 为空串会
|
|
366
|
+
// 渲染空白)——按 commandType 判定直接显示命令输出 outputText;
|
|
367
|
+
// 非 shell 优先 compressedOutput,降级(assessResult.errorDetail 非 undefined)
|
|
368
|
+
// 时回退原始 output。
|
|
369
|
+
item.commandType === 'shell' && item.outputText !== undefined
|
|
370
|
+
? <div className={css.md}><MarkdownText text={item.outputText} /></div>
|
|
371
|
+
// 非 shell:优先 compressedOutput,降级(有 errorDetail)时回退 output
|
|
372
|
+
: item.commandType !== 'shell' && (
|
|
373
|
+
item.finalizeAssessErrorDetail === undefined
|
|
374
|
+
&& item.finalizeAssessResult?.compressedOutput !== undefined
|
|
375
|
+
? <div className={css.md}><MarkdownText text={item.finalizeAssessResult.compressedOutput} /></div>
|
|
376
|
+
: item.outputText !== undefined
|
|
377
|
+
? <div className={css.md}><MarkdownText text={item.outputText} /></div>
|
|
378
|
+
: null
|
|
379
|
+
)
|
|
380
|
+
)}
|
|
381
|
+
|
|
382
|
+
{running && (
|
|
383
|
+
// 真实子代理步骤(有 childId 且非合成会话):内嵌子代理会话对话区
|
|
384
|
+
// (抄自 DSH ChatView 渲染栈,最新 10 条,step 运行中实时流式);
|
|
385
|
+
// 合成会话/无会话退化为原有的消息框或「运行中…」占位。
|
|
386
|
+
realSession
|
|
387
|
+
? <StepChatPanel sessionId={item.childId} t={t} />
|
|
388
|
+
: (
|
|
389
|
+
item.messages !== undefined && item.messages.length > 0
|
|
390
|
+
? (
|
|
391
|
+
<div className={css.agentChat}>
|
|
392
|
+
<div className={css.agentChatScroll}>
|
|
393
|
+
{item.messages.map((line, index) => (
|
|
394
|
+
<ChatLineRow key={index} line={line} />
|
|
395
|
+
))}
|
|
396
|
+
</div>
|
|
397
|
+
</div>
|
|
398
|
+
)
|
|
399
|
+
: <div className={css.agentBodyRun}>{t('run.step.status.running')}…</div>
|
|
400
|
+
)
|
|
401
|
+
)}
|
|
402
|
+
|
|
403
|
+
{terminalChat && (
|
|
404
|
+
<>
|
|
405
|
+
{/* Round 14:failed 态——errorDetail 恢复 errorBlock 样式,compressedOutput 用 .md 平铺。 */}
|
|
406
|
+
{item.status === 'failed' && (
|
|
407
|
+
<>
|
|
408
|
+
{/* errorDetail:恢复原 errorBlock 样式(浅红背景+红色边框+红色文字) */}
|
|
409
|
+
{(item.finalizeErrorDetail ?? item.error) && (
|
|
410
|
+
<div className={css.errorBlock}>
|
|
411
|
+
<span className={css.errorText}>{item.finalizeErrorDetail ?? item.error}</span>
|
|
412
|
+
</div>
|
|
413
|
+
)}
|
|
414
|
+
{/* compressedOutput:平铺 .md(与运行中消息同风格) */}
|
|
415
|
+
{item.finalizeAssessResult?.compressedOutput !== undefined && (
|
|
416
|
+
<div className={css.md}><MarkdownText text={item.finalizeAssessResult.compressedOutput} /></div>
|
|
417
|
+
)}
|
|
418
|
+
</>
|
|
419
|
+
)}
|
|
420
|
+
{/* paused 态:最新 10 条消息(真实会话 → DSH 对话流;合成会话 → 最近 N 条兜底)。 */}
|
|
421
|
+
{item.status === 'paused' && (
|
|
422
|
+
<>
|
|
423
|
+
{realSession
|
|
424
|
+
? <StepChatPanel sessionId={item.childId} t={t} />
|
|
425
|
+
: <LastMessages item={item} t={t} />}
|
|
426
|
+
</>
|
|
427
|
+
)}
|
|
428
|
+
</>
|
|
429
|
+
)}
|
|
430
|
+
</>
|
|
431
|
+
)}
|
|
432
|
+
</div>
|
|
433
|
+
)
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
/** 用户消息:右侧气泡 + kind 徽标 + meta。 */
|
|
437
|
+
function UserMsg({ item, t, nowIso }: {
|
|
438
|
+
readonly item: Extract<TimelineItem, { readonly kind: 'user' }>
|
|
439
|
+
readonly t: TranslateNS<'sandore'>
|
|
440
|
+
readonly nowIso: string
|
|
441
|
+
}) {
|
|
442
|
+
const rel = relativeTimeOf(item.time, nowIso)
|
|
443
|
+
// 消息种类徽标(input/resume/retry/answer/message);旧实例无 messageKind 时省略。
|
|
444
|
+
const kindKey = item.messageKind === undefined
|
|
445
|
+
? undefined
|
|
446
|
+
: item.messageKind === 'input' ? 'run.user.kind.input'
|
|
447
|
+
: item.messageKind === 'resume' ? 'run.user.kind.resume'
|
|
448
|
+
: item.messageKind === 'retry' ? 'run.user.kind.retry'
|
|
449
|
+
: item.messageKind === 'answer' ? 'run.user.kind.answer'
|
|
450
|
+
: item.messageKind === 'message' ? 'run.user.kind.message'
|
|
451
|
+
: undefined
|
|
452
|
+
return (
|
|
453
|
+
<div className={css.userMsg} data-tl-user>
|
|
454
|
+
<div className={css.bubble}>{item.content}</div>
|
|
455
|
+
<div className={css.userMeta}>
|
|
456
|
+
{kindKey !== undefined && <span className={css.loopLabel}>{t(kindKey)}</span>}
|
|
457
|
+
{rel !== undefined && <span className={css.time}>{formatTime(t, rel)} · {t('run.time.you')}</span>}
|
|
458
|
+
</div>
|
|
459
|
+
</div>
|
|
460
|
+
)
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
/** 渲染执行详情对话流。 */
|
|
464
|
+
export function UnifiedTimeline({ t, items, openSession }: {
|
|
465
|
+
/** 类型化翻译函数。 */
|
|
466
|
+
t: TranslateNS<'sandore'>
|
|
467
|
+
/** 时间线条目(buildTimeline 派生)。 */
|
|
468
|
+
items: readonly TimelineItem[]
|
|
469
|
+
/** 跳子代理会话(agent 标题点击)。 */
|
|
470
|
+
openSession: (id: string) => void
|
|
471
|
+
}) {
|
|
472
|
+
// 每次渲染取「现在」,运行中轮询会带动重渲染,保持相对时间新鲜。
|
|
473
|
+
const nowIso = new Date().toISOString()
|
|
474
|
+
return (
|
|
475
|
+
<div className={css.flow} data-sandore-timeline>
|
|
476
|
+
{items.length === 0 && <p className={css.empty}>{t('run.trajectory.empty')}</p>}
|
|
477
|
+
{items.map((item, index) => (
|
|
478
|
+
item.kind === 'user'
|
|
479
|
+
? <UserMsg key={`user-${index}`} item={item} t={t} nowIso={nowIso} />
|
|
480
|
+
: <AgentMsg key={`${item.stepId}-${index}`} item={item} t={t} nowIso={nowIso} openSession={openSession} />
|
|
481
|
+
))}
|
|
482
|
+
</div>
|
|
483
|
+
)
|
|
484
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/* sidebar 底部工作流按钮(宽态行 / 窄 rail 仅图标)。 */
|
|
2
|
+
|
|
3
|
+
.layer {
|
|
4
|
+
position: relative;
|
|
5
|
+
flex: none;
|
|
6
|
+
display: flex;
|
|
7
|
+
align-items: center;
|
|
8
|
+
width: 100%;
|
|
9
|
+
height: 49px;
|
|
10
|
+
margin: 8px 0 0;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.footerButtons {
|
|
14
|
+
display: flex;
|
|
15
|
+
align-items: center;
|
|
16
|
+
width: 100%;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.badge {
|
|
20
|
+
display: inline-flex;
|
|
21
|
+
align-items: center;
|
|
22
|
+
gap: 8px;
|
|
23
|
+
width: 100%;
|
|
24
|
+
height: 49px;
|
|
25
|
+
padding: 0 8px 0 6px;
|
|
26
|
+
border: none;
|
|
27
|
+
border-radius: 12px;
|
|
28
|
+
background: transparent;
|
|
29
|
+
color: var(--dsw-alias-label-primary);
|
|
30
|
+
font-family: inherit;
|
|
31
|
+
font-size: 14px;
|
|
32
|
+
cursor: pointer;
|
|
33
|
+
overflow: hidden;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.badge:hover {
|
|
37
|
+
background: var(--dsw-alias-interactive-bg-hover-solid);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/* overlay 打开时按钮高亮(accent 底)。 */
|
|
41
|
+
.badgeActive,
|
|
42
|
+
.badgeActive:hover {
|
|
43
|
+
background: var(--dsw-alias-accent-bg);
|
|
44
|
+
color: var(--dsw-alias-accent-primary);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.badgeLabel {
|
|
48
|
+
min-width: 0;
|
|
49
|
+
overflow: hidden;
|
|
50
|
+
text-overflow: ellipsis;
|
|
51
|
+
white-space: nowrap;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.layer.rail {
|
|
55
|
+
width: 36px;
|
|
56
|
+
height: 36px;
|
|
57
|
+
margin: 0;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.rail .badge {
|
|
61
|
+
justify-content: center;
|
|
62
|
+
gap: 0;
|
|
63
|
+
width: 36px;
|
|
64
|
+
height: 36px;
|
|
65
|
+
padding: 0;
|
|
66
|
+
border-radius: 50%;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.rail .footerButtons {
|
|
70
|
+
flex-direction: column;
|
|
71
|
+
gap: 2px;
|
|
72
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/** sidebar.footer.action 工作流入口:点击切换 shell.overlay 全局工作流 UI(设置上方)。 */
|
|
2
|
+
|
|
3
|
+
import { useSyncExternalStore } from 'react'
|
|
4
|
+
import type { InjectFace, PropsLocale, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots'
|
|
5
|
+
import type {} from '@deepseek-ai/dsh-client-ui-sidebar/client'
|
|
6
|
+
import { getWorkflowState, toggleWorkflowOverlay, subscribeWorkflowState } from './workflow-store.ts'
|
|
7
|
+
import css from './WorkflowButton.module.css'
|
|
8
|
+
|
|
9
|
+
/** 注册进 slot 的业务 face:切换工作流 overlay 开关的回调。 */
|
|
10
|
+
export interface WorkflowButtonFace {
|
|
11
|
+
/** 点击「工作流」:切换 shell.overlay 全局工作流 UI 开关(开→关、关→开)。 */
|
|
12
|
+
onToggleWorkflow(): void
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** sidebar.footer.action 的组件 props(owner + 注入 face + locale)。 */
|
|
16
|
+
export type WorkflowButtonProps =
|
|
17
|
+
PropsRuntime<'sidebar.footer.action'> & InjectFace<WorkflowButtonFace> & PropsLocale<'sandore'>
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* 工作流图标:Lucide "workflow" 官方形状——顶部左右两个节点 + 底部中间节点,
|
|
21
|
+
* 顶部水平连线 + 中间垂直连线(标准工作流符号)。手绘内联 SVG 16×16,
|
|
22
|
+
* stroke = currentColor。
|
|
23
|
+
*/
|
|
24
|
+
function WorkflowIcon() {
|
|
25
|
+
return (
|
|
26
|
+
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden>
|
|
27
|
+
<path d="M2 4.5C2 3.67157 2.67157 3 3.5 3C4.32843 3 5 3.67157 5 4.5C5 5.32843 4.32843 6 3.5 6C2.67157 6 2 5.32843 2 4.5ZM3.5 2C2.11929 2 1 3.11929 1 4.5C1 5.70948 1.85888 6.71836 3 6.94999V9.05001C1.85888 9.28164 1 10.2905 1 11.5C1 12.8807 2.11929 14 3.5 14C4.70948 14 5.71836 13.1411 5.94999 12H10.05C10.2816 13.1411 11.2905 14 12.5 14C13.8807 14 15 12.8807 15 11.5C15 10.2905 14.1411 9.28164 13 9.05001V6.94999C14.1411 6.71836 15 5.70948 15 4.5C15 3.11929 13.8807 2 12.5 2C11.2905 2 10.2816 2.85888 10.05 4H5.94999C5.71836 2.85888 4.70948 2 3.5 2ZM5.94999 5H10.05C10.2324 5.85811 10.8581 6.55625 11.6725 6.84607C11.7704 6.88046 11.8708 6.90979 11.9735 6.9336C11.9823 6.93561 11.9911 6.93751 12 6.93929V9.05001C10.8589 9.28164 10 10.2905 10 11.5C10 11.6712 10.0144 11.839 10.0418 12.0022C10.0446 12.0181 10.0475 12.0339 10.05 12.0497V12.05H5.94999C5.71836 10.9089 4.70948 10 3.5 10C3.32884 10 3.16098 10.0144 2.99779 10.0418C2.98193 10.0446 2.96613 10.0475 2.95039 10.05H2.94999V6.94999C4.09112 6.71836 4.94999 5.70948 4.94999 4.5C4.94999 4.32884 4.93564 4.16098 4.90821 3.99779C4.90541 3.98193 4.90248 3.96613 4.89943 3.95039V3.94999H5.94999C5.94999 3.9666 5.95007 3.98319 5.95023 3.99976C5.95007 4.16639 5.96055 4.33072 5.98089 4.49206C5.96055 4.33072 5.94999 4.16639 5.94999 4V5ZM11 4.5C11 3.67157 11.6716 3 12.5 3C13.3284 3 14 3.67157 14 4.5C14 5.32843 13.3284 6 12.5 6C11.6716 6 11 5.32843 11 4.5ZM2 11.5C2 10.6716 2.67157 10 3.5 10C4.32843 10 5 10.6716 5 11.5C5 12.3284 4.32843 13 3.5 13C2.67157 13 2 12.3284 2 11.5ZM11 11.5C11 10.6716 11.6716 10 12.5 10C13.3284 10 14 10.6716 14 11.5C14 12.3284 13.3284 13 12.5 13C11.6716 13 11 12.3284 11 11.5Z" fill="currentColor" fillRule="nonzero" />
|
|
28
|
+
</svg>
|
|
29
|
+
)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** 渲染工作流入口按钮,点击切换全局工作流 overlay 开关;overlay 打开时按钮高亮。 */
|
|
33
|
+
export function WorkflowButton({ wide, t, onToggleWorkflow }: WorkflowButtonProps) {
|
|
34
|
+
const open = useSyncExternalStore(subscribeWorkflowState, () => getWorkflowState().open)
|
|
35
|
+
return (
|
|
36
|
+
<div className={wide ? css.layer : `${css.layer} ${css.rail}`}>
|
|
37
|
+
<button
|
|
38
|
+
type="button"
|
|
39
|
+
className={`${css.badge} ${open ? css.badgeActive : ''}`}
|
|
40
|
+
data-sandore-btn
|
|
41
|
+
aria-label={t('button.aria')}
|
|
42
|
+
aria-expanded={open}
|
|
43
|
+
onClick={() => { onToggleWorkflow() }}
|
|
44
|
+
>
|
|
45
|
+
<WorkflowIcon />
|
|
46
|
+
{wide && <span className={css.badgeLabel}>{t('button.trigger')}</span>}
|
|
47
|
+
</button>
|
|
48
|
+
</div>
|
|
49
|
+
)
|
|
50
|
+
}
|