openclaw-agent-dashboard 1.0.21 → 1.0.23

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.
Files changed (135) hide show
  1. package/README.md +55 -321
  2. package/frontend-dist/assets/index-B7XqKAxm.css +1 -0
  3. package/frontend-dist/assets/index-CxJaSYyo.js +24 -0
  4. package/{frontend → frontend-dist}/index.html +2 -1
  5. package/{plugin/openclaw.plugin.json → openclaw.plugin.json} +2 -2
  6. package/package.json +21 -13
  7. package/.github/workflows/release.yml +0 -56
  8. package/VERSION_DISPLAY_delivery.md +0 -242
  9. package/VERSION_DISPLAY_implementation_summary.md +0 -315
  10. package/design_manifest.md +0 -100
  11. package/docs/CHANGELOG_AGENT_MODIFICATIONS.md +0 -132
  12. package/docs/MAINTAINER_RELEASE_WORKFLOW.md +0 -211
  13. package/docs/Openclaw-Agent-Dashboard/345/217/221/345/270/203/344/270/216/346/233/264/346/226/260.md +0 -147
  14. package/docs/RELEASE-LATEST.md +0 -189
  15. package/docs/RELEASE-MODEL-CONFIG.md +0 -95
  16. package/docs/WINDOWS_INSTALL_TROUBLESHOOTING.md +0 -171
  17. package/docs/design/.gitkeep +0 -0
  18. package/docs/design/VERSION_DISPLAY_design.md +0 -1236
  19. package/docs/release-guide.md +0 -259
  20. package/docs/release-operations-manual.md +0 -167
  21. package/docs/reviews/.gitkeep +0 -0
  22. package/docs/reviews/approval_history.json +0 -14
  23. package/docs/reviews/cr_VERSION_DISPLAY.md +0 -397
  24. package/docs/reviews/traceability_manifest.json +0 -279
  25. package/docs/specs/VERSION_DISPLAY_spec.md +0 -371
  26. package/docs/specs/tr3-install-system.md +0 -580
  27. package/docs/windows-collaboration-model-paths-troubleshooting.md +0 -0
  28. package/frontend/package-lock.json +0 -1240
  29. package/frontend/package.json +0 -19
  30. package/frontend/src/App.vue +0 -355
  31. package/frontend/src/components/AgentCard.vue +0 -796
  32. package/frontend/src/components/AgentConfigPanel.vue +0 -539
  33. package/frontend/src/components/AgentDetailPanel.vue +0 -738
  34. package/frontend/src/components/ErrorAnalysisView.vue +0 -546
  35. package/frontend/src/components/ErrorCenterPanel.vue +0 -844
  36. package/frontend/src/components/PerformanceMonitor.vue +0 -515
  37. package/frontend/src/components/SettingsPanel.vue +0 -236
  38. package/frontend/src/components/TokenAnalysisPanel.vue +0 -683
  39. package/frontend/src/components/chain/ChainEdge.vue +0 -85
  40. package/frontend/src/components/chain/ChainNode.vue +0 -166
  41. package/frontend/src/components/chain/TaskChainView.vue +0 -425
  42. package/frontend/src/components/chain/index.ts +0 -3
  43. package/frontend/src/components/chain/types.ts +0 -70
  44. package/frontend/src/components/collaboration/CollaborationFlowSection.vue +0 -1032
  45. package/frontend/src/components/collaboration/CollaborationFlowWrapper.vue +0 -113
  46. package/frontend/src/components/common/VersionDisplay.vue +0 -187
  47. package/frontend/src/components/performance/PerformancePanel.vue +0 -119
  48. package/frontend/src/components/performance/PerformanceSection.vue +0 -1137
  49. package/frontend/src/components/tasks/TaskStatusSection.vue +0 -973
  50. package/frontend/src/components/timeline/TimelineConnector.vue +0 -31
  51. package/frontend/src/components/timeline/TimelineRound.vue +0 -135
  52. package/frontend/src/components/timeline/TimelineStep.vue +0 -691
  53. package/frontend/src/components/timeline/TimelineToolLink.vue +0 -109
  54. package/frontend/src/components/timeline/TimelineView.vue +0 -540
  55. package/frontend/src/components/timeline/index.ts +0 -5
  56. package/frontend/src/components/timeline/types.ts +0 -120
  57. package/frontend/src/composables/index.ts +0 -7
  58. package/frontend/src/composables/useDebounce.ts +0 -48
  59. package/frontend/src/composables/useRealtime.ts +0 -52
  60. package/frontend/src/composables/useState.ts +0 -52
  61. package/frontend/src/composables/useThrottle.ts +0 -46
  62. package/frontend/src/composables/useVirtualScroll.ts +0 -106
  63. package/frontend/src/main.ts +0 -4
  64. package/frontend/src/managers/EventDispatcher.ts +0 -127
  65. package/frontend/src/managers/RealtimeDataManager.ts +0 -302
  66. package/frontend/src/managers/StateManager.ts +0 -128
  67. package/frontend/src/managers/index.ts +0 -5
  68. package/frontend/src/types/collaboration.ts +0 -135
  69. package/frontend/src/types/index.ts +0 -20
  70. package/frontend/src/types/performance.ts +0 -105
  71. package/frontend/src/types/task.ts +0 -38
  72. package/frontend/vite.config.ts +0 -18
  73. package/legacy_code_anatomy.md +0 -518
  74. package/plugin/README.md +0 -99
  75. package/plugin/config.json.example +0 -1
  76. package/plugin/package.json +0 -26
  77. package/scripts/build-plugin.js +0 -81
  78. package/scripts/bundle.sh +0 -62
  79. package/scripts/install-plugin.sh +0 -162
  80. package/scripts/install-python-deps.sh +0 -226
  81. package/scripts/install.js +0 -684
  82. package/scripts/install.sh +0 -367
  83. package/scripts/lib/common.sh +0 -137
  84. package/scripts/release-pack.sh +0 -110
  85. package/scripts/start.js +0 -50
  86. package/scripts/test_available_models.py +0 -284
  87. package/scripts/test_version_display.sh +0 -128
  88. package/scripts/test_websocket_ping.py +0 -44
  89. package/session_registry.json +0 -58
  90. package/tests/.gitkeep +0 -0
  91. package/tests/qa_regression_report.md +0 -359
  92. package/tests/qa_version_display_report.md +0 -598
  93. /package/{src/backend → dashboard}/agents.py +0 -0
  94. /package/{src/backend → dashboard}/api/__init__.py +0 -0
  95. /package/{src/backend → dashboard}/api/agent_config_api.py +0 -0
  96. /package/{src/backend → dashboard}/api/agents.py +0 -0
  97. /package/{src/backend → dashboard}/api/agents_config.py +0 -0
  98. /package/{src/backend → dashboard}/api/chains.py +0 -0
  99. /package/{src/backend → dashboard}/api/collaboration.py +0 -0
  100. /package/{src/backend → dashboard}/api/debug_paths.py +0 -0
  101. /package/{src/backend → dashboard}/api/error_analysis.py +0 -0
  102. /package/{src/backend → dashboard}/api/errors.py +0 -0
  103. /package/{src/backend → dashboard}/api/performance.py +0 -0
  104. /package/{src/backend → dashboard}/api/subagents.py +0 -0
  105. /package/{src/backend → dashboard}/api/timeline.py +0 -0
  106. /package/{src/backend → dashboard}/api/version.py +0 -0
  107. /package/{src/backend → dashboard}/api/websocket.py +0 -0
  108. /package/{src/backend → dashboard}/collaboration.py +0 -0
  109. /package/{src/backend → dashboard}/data/__init__.py +0 -0
  110. /package/{src/backend → dashboard}/data/agent_config_manager.py +0 -0
  111. /package/{src/backend → dashboard}/data/chain_reader.py +0 -0
  112. /package/{src/backend → dashboard}/data/config_reader.py +0 -0
  113. /package/{src/backend → dashboard}/data/error_analyzer.py +0 -0
  114. /package/{src/backend → dashboard}/data/session_reader.py +0 -0
  115. /package/{src/backend → dashboard}/data/subagent_reader.py +0 -0
  116. /package/{src/backend → dashboard}/data/task_history.py +0 -0
  117. /package/{src/backend → dashboard}/data/timeline_reader.py +0 -0
  118. /package/{src/backend → dashboard}/data/version_info_reader.py +0 -0
  119. /package/{src/backend → dashboard}/errors.py +0 -0
  120. /package/{src/backend → dashboard}/main.py +0 -0
  121. /package/{src/backend → dashboard}/mechanism_reader.py +0 -0
  122. /package/{src/backend → dashboard}/mechanisms.py +0 -0
  123. /package/{src/backend → dashboard}/performance.py +0 -0
  124. /package/{src/backend → dashboard}/requirements.txt +0 -0
  125. /package/{src/backend → dashboard}/session_reader.py +0 -0
  126. /package/{src/backend → dashboard}/status/__init__.py +0 -0
  127. /package/{src/backend → dashboard}/status/change_tracker.py +0 -0
  128. /package/{src/backend → dashboard}/status/error_detector.py +0 -0
  129. /package/{src/backend → dashboard}/status/status_cache.py +0 -0
  130. /package/{src/backend → dashboard}/status/status_calculator.py +0 -0
  131. /package/{src/backend → dashboard}/status_calculator.py +0 -0
  132. /package/{src/backend → dashboard}/subagent_reader.py +0 -0
  133. /package/{src/backend → dashboard}/watchers/__init__.py +0 -0
  134. /package/{src/backend → dashboard}/watchers/file_watcher.py +0 -0
  135. /package/{plugin/index.js → index.js} +0 -0
@@ -1,109 +0,0 @@
1
- <template>
2
- <div class="tool-link" :class="{ 'link-error': isError, 'link-active': isActive }">
3
- <div class="link-line">
4
- <span class="link-dot top"></span>
5
- <span class="link-connector"></span>
6
- <span class="link-dot bottom"></span>
7
- </div>
8
- <span class="link-time" v-if="executionTime">{{ formatDuration(executionTime) }}</span>
9
- </div>
10
- </template>
11
-
12
- <script setup lang="ts">
13
- const props = defineProps<{
14
- isError?: boolean
15
- isActive?: boolean
16
- executionTime?: number
17
- }>()
18
-
19
- function formatDuration(ms: number): string {
20
- if (!ms) return ''
21
- if (ms < 1000) return `${ms}ms`
22
- if (ms < 60000) return `${(ms / 1000).toFixed(1)}s`
23
- return `${(ms / 60000).toFixed(1)}min`
24
- }
25
- </script>
26
-
27
- <style scoped>
28
- .tool-link {
29
- display: flex;
30
- align-items: center;
31
- padding: 4px 0 4px 28px;
32
- position: relative;
33
- }
34
-
35
- .link-line {
36
- display: flex;
37
- flex-direction: column;
38
- align-items: center;
39
- position: absolute;
40
- left: 12px;
41
- top: 0;
42
- bottom: 0;
43
- }
44
-
45
- .link-connector {
46
- width: 2px;
47
- flex: 1;
48
- background: linear-gradient(to bottom, #94a3b8 0%, #94a3b8 50%, transparent 50%);
49
- background-size: 2px 8px;
50
- min-height: 20px;
51
- }
52
-
53
- .link-dot {
54
- width: 8px;
55
- height: 8px;
56
- border-radius: 50%;
57
- background: #94a3b8;
58
- border: 2px solid #fff;
59
- box-shadow: 0 0 0 1px #e5e7eb;
60
- }
61
-
62
- .link-dot.bottom {
63
- position: absolute;
64
- bottom: 0;
65
- }
66
-
67
- .link-time {
68
- font-size: 10px;
69
- color: #64748b;
70
- background: #f8fafc;
71
- padding: 1px 6px;
72
- border-radius: 3px;
73
- margin-left: 8px;
74
- border: 1px solid #e5e7eb;
75
- }
76
-
77
- /* 错误状态 */
78
- .link-error .link-connector {
79
- background: linear-gradient(to bottom, #ef4444 0%, #ef4444 50%, transparent 50%);
80
- background-size: 2px 8px;
81
- }
82
-
83
- .link-error .link-dot {
84
- background: #ef4444;
85
- }
86
-
87
- .link-error .link-time {
88
- color: #dc2626;
89
- background: #fef2f2;
90
- border-color: #fecaca;
91
- }
92
-
93
- /* 高亮状态 */
94
- .link-active .link-connector {
95
- background: linear-gradient(to bottom, #3b82f6 0%, #3b82f6 50%, transparent 50%);
96
- background-size: 2px 8px;
97
- }
98
-
99
- .link-active .link-dot {
100
- background: #3b82f6;
101
- box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.3);
102
- }
103
-
104
- .link-active .link-time {
105
- color: #2563eb;
106
- background: #eff6ff;
107
- border-color: #bfdbfe;
108
- }
109
- </style>
@@ -1,540 +0,0 @@
1
- <template>
2
- <div class="timeline-view">
3
- <!-- 头部 -->
4
- <div class="timeline-header">
5
- <div class="header-left">
6
- <span class="title">📊 实时执行时序</span>
7
- <span class="agent-info" v-if="data">
8
- {{ data.agentName || data.agentId }}
9
- <span class="model" v-if="data.model">({{ data.model }})</span>
10
- </span>
11
- </div>
12
- <div class="header-right">
13
- <span class="status-badge" :class="`status-${statusClass}`">
14
- {{ statusLabel }}
15
- </span>
16
- <button class="refresh-btn" @click="refresh" :disabled="loading">
17
- {{ loading ? '加载中...' : '🔄 刷新' }}
18
- </button>
19
- </div>
20
- </div>
21
-
22
- <!-- 加载状态 -->
23
- <div v-if="loading && !data" class="loading-state">
24
- <div class="spinner"></div>
25
- <span>加载时序数据...</span>
26
- </div>
27
-
28
- <!-- 空状态 -->
29
- <div v-else-if="!data || data.steps.length === 0" class="empty-state">
30
- <span class="empty-icon">📭</span>
31
- <span v-if="data?.status === 'no_sessions'" class="empty-message">
32
- 该 Agent 是子代理,暂无独立会话记录
33
- </span>
34
- <span v-else>暂无会话记录</span>
35
- <div v-if="data?.message" class="empty-hint">
36
- {{ data.message }}
37
- </div>
38
- </div>
39
-
40
- <!-- 时序内容 -->
41
- <div v-else class="timeline-content">
42
- <!-- 图例 -->
43
- <div class="timeline-legend">
44
- <span class="legend-item"><span class="legend-icon">👤</span> 用户/回传</span>
45
- <span class="legend-item"><span class="legend-icon">🧠</span> LLM 思考</span>
46
- <span class="legend-item"><span class="legend-icon">🤖</span> LLM 回复</span>
47
- <span class="legend-item"><span class="legend-icon">🔧</span> 工具调用</span>
48
- <span class="legend-item"><span class="legend-icon">✅</span> 工具成功</span>
49
- <span class="legend-item"><span class="legend-icon">❌</span> 工具失败</span>
50
- <span class="legend-item"><span class="legend-icon">⚠️</span> 错误</span>
51
- </div>
52
- <!-- Session 信息 -->
53
- <div class="session-info" v-if="data.sessionId">
54
- <span class="session-id">Session: {{ data.sessionId.slice(0, 8) }}...</span>
55
- <span class="started-at" v-if="data.startedAt">
56
- 开始: {{ formatTime(data.startedAt) }}
57
- </span>
58
- </div>
59
-
60
- <!-- 步骤列表 -->
61
- <div class="steps-list">
62
- <!-- 轮次分组模式 -->
63
- <template v-if="useRoundMode">
64
- <template v-for="(item, index) in renderItems" :key="item.type === 'round' ? (item.data as any).id : (item.data as any).id">
65
- <TimelineConnector v-if="index > 0" />
66
- <!-- 轮次 -->
67
- <TimelineRound
68
- v-if="item.type === 'round'"
69
- :round="(item.data as any)"
70
- :steps="data.steps"
71
- :highlightedPair="highlightedPair"
72
- @highlight-pair="handleHighlightPair"
73
- />
74
- <!-- 独立步骤(如 toolResult) -->
75
- <template v-else>
76
- <!-- 工具执行分隔标签 -->
77
- <div class="tool-execution-label" v-if="(item.data as any).type === 'toolResult'">
78
- <span class="label-line"></span>
79
- <span class="label-text">⚡ 工具执行</span>
80
- <span class="label-line"></span>
81
- </div>
82
- <!-- 连接线(如果 toolResult 有配对的 toolCall) -->
83
- <TimelineToolLink
84
- v-if="(item.data as any).type === 'toolResult' && (item.data as any).pairedToolCallId"
85
- :isError="(item.data as any).toolResultStatus === 'error'"
86
- :isActive="highlightedPair?.resultId === (item.data as any).id"
87
- :executionTime="(item.data as any).executionTime"
88
- />
89
- <TimelineStepItem
90
- :step="(item.data as any)"
91
- :highlightedPair="highlightedPair"
92
- @toggle-collapse="toggleStepCollapse((item.data as any).id)"
93
- @highlight-pair="handleHighlightPair"
94
- />
95
- </template>
96
- </template>
97
- </template>
98
-
99
- <!-- 传统列表模式(无轮次分组时回退) -->
100
- <template v-else>
101
- <template v-for="(step, index) in data.steps" :key="step.id">
102
- <TimelineConnector v-if="index > 0" />
103
- <TimelineToolLink
104
- v-if="step.type === 'toolResult' && step.pairedToolCallId"
105
- :isError="step.toolResultStatus === 'error'"
106
- :isActive="highlightedPair?.resultId === step.id"
107
- :executionTime="step.executionTime"
108
- />
109
- <TimelineStepItem
110
- :step="step"
111
- :prevStep="data.steps[index - 1]"
112
- :highlightedPair="highlightedPair"
113
- @toggle-collapse="toggleCollapse(index)"
114
- @highlight-pair="handleHighlightPair"
115
- />
116
- </template>
117
- </template>
118
- </div>
119
-
120
- <!-- 统计汇总 -->
121
- <div class="timeline-footer">
122
- <div class="stats-grid">
123
- <div class="stat-item">
124
- <span class="stat-label">总耗时</span>
125
- <span class="stat-value">{{ formatDuration(data.stats.totalDuration) }}</span>
126
- </div>
127
- <div class="stat-item">
128
- <span class="stat-label">Token</span>
129
- <span class="stat-value">
130
- {{ formatNumber(data.stats.totalInputTokens) }} / {{ formatNumber(data.stats.totalOutputTokens) }}
131
- </span>
132
- </div>
133
- <div class="stat-item">
134
- <span class="stat-label">工具调用</span>
135
- <span class="stat-value">{{ data.stats.toolCallCount }} 次</span>
136
- </div>
137
- <div class="stat-item">
138
- <span class="stat-label">步骤数</span>
139
- <span class="stat-value">{{ data.stats.stepCount }}</span>
140
- </div>
141
- </div>
142
- </div>
143
- </div>
144
- </div>
145
- </template>
146
-
147
- <script setup lang="ts">
148
- import { ref, computed, onMounted, watch } from 'vue'
149
- import type { TimelineResponse, TimelineStep } from './types'
150
- import TimelineStepItem from './TimelineStep.vue'
151
- import TimelineConnector from './TimelineConnector.vue'
152
- import TimelineRound from './TimelineRound.vue'
153
- import TimelineToolLink from './TimelineToolLink.vue'
154
-
155
- const props = defineProps<{
156
- agentId: string
157
- sessionKey?: string
158
- autoRefresh?: boolean
159
- refreshInterval?: number
160
- }>()
161
-
162
- const data = ref<TimelineResponse | null>(null)
163
- const loading = ref(false)
164
- const error = ref<string | null>(null)
165
-
166
- // 高亮配对状态
167
- const highlightedPair = ref<{ callId: string; resultId: string } | null>(null)
168
-
169
- const statusClass = computed(() => {
170
- if (!data.value) return 'empty'
171
- return data.value.status
172
- })
173
-
174
- const statusLabel = computed(() => {
175
- const labels: Record<string, string> = {
176
- running: '🔄 进行中',
177
- completed: '✅ 已完成',
178
- error: '❌ 出错',
179
- empty: '空',
180
- no_sessions: '无会话'
181
- }
182
- return labels[statusClass.value] || '未知'
183
- })
184
-
185
- // 是否启用轮次分组模式
186
- const useRoundMode = computed(() => {
187
- return data.value?.roundMode && data.value.rounds && data.value.rounds.length > 0
188
- })
189
-
190
- // 获取不属于任何轮次的步骤(如 toolResult)
191
- const standaloneSteps = computed(() => {
192
- if (!data.value || !useRoundMode.value) return []
193
-
194
- const roundStepIds = new Set<string>()
195
- data.value.rounds.forEach(round => {
196
- round.stepIds.forEach(id => roundStepIds.add(id))
197
- })
198
-
199
- return data.value.steps.filter(step => !roundStepIds.has(step.id))
200
- })
201
-
202
- // 构建渲染项列表(轮次或独立步骤)
203
- const renderItems = computed(() => {
204
- if (!data.value || !useRoundMode.value) return []
205
-
206
- const items: Array<{ type: 'round' | 'step', data: unknown }> = []
207
- const renderedStepIds = new Set<string>()
208
-
209
- // 遍历所有步骤,按顺序构建渲染项
210
- for (const step of data.value.steps) {
211
- // 如果已经在某个轮次中渲染过,跳过
212
- if (renderedStepIds.has(step.id)) continue
213
-
214
- // 查找包含此步骤的轮次
215
- const round = data.value.rounds?.find(r => r.stepIds.includes(step.id))
216
-
217
- if (round) {
218
- // 渲染整个轮次
219
- items.push({ type: 'round', data: round })
220
- round.stepIds.forEach(id => renderedStepIds.add(id))
221
- } else {
222
- // 独立步骤(如 toolResult)
223
- items.push({ type: 'step', data: step })
224
- renderedStepIds.add(step.id)
225
- }
226
- }
227
-
228
- return items
229
- })
230
-
231
- async function refresh() {
232
- if (!props.agentId) return
233
- loading.value = true
234
- error.value = null
235
-
236
- try {
237
- let url = `/api/timeline/${props.agentId}?limit=100`
238
- if (props.sessionKey) {
239
- url += `&session_key=${encodeURIComponent(props.sessionKey)}`
240
- }
241
-
242
- const res = await fetch(url)
243
- if (!res.ok) {
244
- throw new Error(`HTTP ${res.status}`)
245
- }
246
- data.value = await res.json()
247
- } catch (e) {
248
- error.value = e instanceof Error ? e.message : '加载失败'
249
- console.error('Timeline load error:', e)
250
- } finally {
251
- loading.value = false
252
- }
253
- }
254
-
255
- function toggleCollapse(index: number) {
256
- if (data.value && data.value.steps[index]) {
257
- data.value.steps[index].collapsed = !data.value.steps[index].collapsed
258
- }
259
- }
260
-
261
- function toggleStepCollapse(stepId: string) {
262
- if (data.value) {
263
- const step = data.value.steps.find(s => s.id === stepId)
264
- if (step) {
265
- step.collapsed = !step.collapsed
266
- }
267
- }
268
- }
269
-
270
- function handleHighlightPair(pair: { callId: string; resultId: string }) {
271
- highlightedPair.value = pair
272
- // 3秒后取消高亮
273
- setTimeout(() => {
274
- highlightedPair.value = null
275
- }, 3000)
276
- }
277
-
278
- function formatTime(ts: number): string {
279
- if (!ts) return ''
280
- const d = new Date(ts)
281
- return d.toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit', second: '2-digit' })
282
- }
283
-
284
- function formatDuration(ms: number): string {
285
- if (!ms) return '0ms'
286
- if (ms < 1000) return `${ms}ms`
287
- if (ms < 60000) return `${(ms / 1000).toFixed(1)}s`
288
- return `${(ms / 60000).toFixed(1)}min`
289
- }
290
-
291
- function formatNumber(n: number): string {
292
- if (n >= 1000) return `${(n / 1000).toFixed(1)}k`
293
- return String(n)
294
- }
295
-
296
- // 初始加载
297
- onMounted(refresh)
298
-
299
- // 监听 agentId 变化
300
- watch(() => props.agentId, refresh)
301
-
302
- // 自动刷新
303
- let refreshTimer: ReturnType<typeof setInterval> | null = null
304
- watch([() => props.autoRefresh, () => props.refreshInterval], ([auto, interval]) => {
305
- if (refreshTimer) {
306
- clearInterval(refreshTimer)
307
- refreshTimer = null
308
- }
309
- if (auto && interval && interval > 0) {
310
- refreshTimer = setInterval(refresh, interval * 1000)
311
- }
312
- }, { immediate: true })
313
- </script>
314
-
315
- <style scoped>
316
- .timeline-view {
317
- background: #fff;
318
- border-radius: 8px;
319
- border: 1px solid #e5e7eb;
320
- overflow: hidden;
321
- }
322
-
323
- .timeline-header {
324
- display: flex;
325
- justify-content: space-between;
326
- align-items: center;
327
- padding: 12px 16px;
328
- background: #f9fafb;
329
- border-bottom: 1px solid #e5e7eb;
330
- }
331
-
332
- .header-left {
333
- display: flex;
334
- align-items: center;
335
- gap: 12px;
336
- }
337
-
338
- .title {
339
- font-weight: 600;
340
- font-size: 14px;
341
- color: #374151;
342
- }
343
-
344
- .agent-info {
345
- font-size: 13px;
346
- color: #6b7280;
347
- }
348
-
349
- .agent-info .model {
350
- color: #9ca3af;
351
- }
352
-
353
- .header-right {
354
- display: flex;
355
- align-items: center;
356
- gap: 12px;
357
- }
358
-
359
- .status-badge {
360
- font-size: 12px;
361
- padding: 4px 8px;
362
- border-radius: 4px;
363
- }
364
-
365
- .status-badge.status-running {
366
- background: #fef3c7;
367
- color: #92400e;
368
- }
369
-
370
- .status-badge.status-completed {
371
- background: #d1fae5;
372
- color: #065f46;
373
- }
374
-
375
- .status-badge.status-error {
376
- background: #fee2e2;
377
- color: #991b1b;
378
- }
379
-
380
- .status-badge.status-no_sessions {
381
- background: #f3f4f6;
382
- color: #6b7280;
383
- }
384
-
385
- .refresh-btn {
386
- font-size: 12px;
387
- padding: 4px 10px;
388
- border: 1px solid #e5e7eb;
389
- border-radius: 4px;
390
- background: #fff;
391
- cursor: pointer;
392
- color: #374151;
393
- }
394
-
395
- .refresh-btn:hover:not(:disabled) {
396
- background: #f3f4f6;
397
- }
398
-
399
- .refresh-btn:disabled {
400
- opacity: 0.5;
401
- cursor: not-allowed;
402
- }
403
-
404
- .loading-state,
405
- .empty-state {
406
- display: flex;
407
- flex-direction: column;
408
- align-items: center;
409
- justify-content: center;
410
- padding: 48px;
411
- color: #6b7280;
412
- gap: 12px;
413
- }
414
-
415
- .spinner {
416
- width: 24px;
417
- height: 24px;
418
- border: 3px solid #e5e7eb;
419
- border-top-color: #3b82f6;
420
- border-radius: 50%;
421
- animation: spin 1s linear infinite;
422
- }
423
-
424
- @keyframes spin {
425
- to { transform: rotate(360deg); }
426
- }
427
-
428
- .empty-icon {
429
- font-size: 32px;
430
- }
431
-
432
- .empty-message {
433
- font-size: 14px;
434
- color: #374151;
435
- font-weight: 500;
436
- }
437
-
438
- .empty-hint {
439
- margin-top: 8px;
440
- font-size: 12px;
441
- color: #9ca3af;
442
- text-align: center;
443
- max-width: 280px;
444
- line-height: 1.5;
445
- }
446
-
447
- .timeline-content {
448
- padding: 16px;
449
- }
450
-
451
- .timeline-legend {
452
- display: flex;
453
- flex-wrap: wrap;
454
- gap: 12px 20px;
455
- font-size: 11px;
456
- color: #6b7280;
457
- margin-bottom: 12px;
458
- padding: 8px 10px;
459
- background: #f9fafb;
460
- border-radius: 6px;
461
- }
462
-
463
- .legend-item {
464
- display: inline-flex;
465
- align-items: center;
466
- gap: 4px;
467
- }
468
-
469
- .legend-icon {
470
- font-size: 12px;
471
- }
472
-
473
- .session-info {
474
- display: flex;
475
- gap: 16px;
476
- font-size: 12px;
477
- color: #9ca3af;
478
- margin-bottom: 16px;
479
- padding-bottom: 12px;
480
- border-bottom: 1px dashed #e5e7eb;
481
- }
482
-
483
- .steps-list {
484
- display: flex;
485
- flex-direction: column;
486
- }
487
-
488
- .tool-execution-label {
489
- display: flex;
490
- align-items: center;
491
- gap: 12px;
492
- margin: 12px 0;
493
- padding: 0 4px;
494
- }
495
-
496
- .tool-execution-label .label-line {
497
- flex: 1;
498
- height: 1px;
499
- background: #e5e7eb;
500
- }
501
-
502
- .tool-execution-label .label-text {
503
- font-size: 11px;
504
- color: #9ca3af;
505
- white-space: nowrap;
506
- }
507
-
508
- .timeline-footer {
509
- margin-top: 16px;
510
- padding-top: 16px;
511
- border-top: 1px solid #e5e7eb;
512
- }
513
-
514
- .stats-grid {
515
- display: grid;
516
- grid-template-columns: repeat(4, 1fr);
517
- gap: 12px;
518
- }
519
-
520
- .stat-item {
521
- display: flex;
522
- flex-direction: column;
523
- align-items: center;
524
- padding: 8px;
525
- background: #f9fafb;
526
- border-radius: 6px;
527
- }
528
-
529
- .stat-label {
530
- font-size: 11px;
531
- color: #9ca3af;
532
- margin-bottom: 4px;
533
- }
534
-
535
- .stat-value {
536
- font-size: 14px;
537
- font-weight: 600;
538
- color: #374151;
539
- }
540
- </style>
@@ -1,5 +0,0 @@
1
- // Timeline 组件导出
2
- export { default as TimelineView } from './TimelineView.vue'
3
- export { default as TimelineStep } from './TimelineStep.vue'
4
- export { default as TimelineConnector } from './TimelineConnector.vue'
5
- export * from './types'