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,738 +0,0 @@
1
- <template>
2
- <div class="panel-overlay" @click="$emit('close')">
3
- <div class="panel" @click.stop>
4
- <div class="header">
5
- <h2>{{ agent.name }}</h2>
6
- <button class="close-btn" @click="$emit('close')">×</button>
7
- </div>
8
-
9
- <div class="content">
10
- <div class="section">
11
- <h3>状态</h3>
12
- <div class="status-info">
13
- <span class="status-dot" :class="`status-${agent.status}`"></span>
14
- <span class="status-text">{{ statusText }}</span>
15
- </div>
16
- </div>
17
-
18
- <div v-if="agent.currentTask" class="section">
19
- <h3>当前任务</h3>
20
- <p>{{ agent.currentTask }}</p>
21
- </div>
22
-
23
- <div v-if="agent.lastActiveFormatted" class="section">
24
- <h3>最后活跃</h3>
25
- <p>{{ agent.lastActiveFormatted }}</p>
26
- </div>
27
-
28
- <div v-if="agent.error" class="section">
29
- <h3 class="error-title">错误信息</h3>
30
- <div class="error-info">
31
- <div class="error-type">{{ agent.error.type }}</div>
32
- <div class="error-message">{{ agent.error.message }}</div>
33
- </div>
34
- </div>
35
-
36
- <!-- 卡顿诊断面板 -->
37
- <div v-if="stuckInfo" class="section">
38
- <div class="diagnostic-panel" :class="`severity-${stuckInfo.severity}`">
39
- <div class="diagnostic-header">
40
- <span class="diagnostic-icon">{{ stuckInfo.severity === 'critical' ? '🚨' : '⚠️' }}</span>
41
- <span class="diagnostic-title">{{ stuckInfo.label }}检测</span>
42
- </div>
43
- <div class="diagnostic-content">
44
- <div class="diagnostic-item">
45
- <span class="item-label">无响应时间:</span>
46
- <span class="item-value highlight">{{ stuckInfo.idleMinutes }} 分钟</span>
47
- </div>
48
- <div v-if="lastToolResultDisplay" class="diagnostic-item">
49
- <span class="item-label">最后操作:</span>
50
- <span class="item-value">{{ lastToolResultDisplay.tool }} → {{ lastToolResultDisplay.result }}</span>
51
- </div>
52
- <div v-if="timeoutCountdown" class="diagnostic-item">
53
- <span class="item-label">自动超时:</span>
54
- <span class="item-value">{{ timeoutCountdown }}</span>
55
- </div>
56
- <div class="diagnostic-hint">
57
- <div class="hint-title">可能原因:</div>
58
- <ul>
59
- <li>LLM API 响应超时</li>
60
- <li>网络连接问题</li>
61
- <li>API 配额限制</li>
62
- </ul>
63
- </div>
64
- <div class="diagnostic-actions">
65
- <button class="action-btn primary" @click="cancelRun" v-if="subagentRun">
66
- 取消任务
67
- </button>
68
- <button class="action-btn" @click="refreshStatus">
69
- 刷新状态
70
- </button>
71
- </div>
72
- </div>
73
- </div>
74
- </div>
75
-
76
- <div class="section">
77
- <h3>最近活动</h3>
78
- <div class="activity-list">
79
- <div v-if="agent.status === 'working'" class="activity-item working">
80
- 💼 正在执行任务...
81
- </div>
82
- <div v-else-if="agent.status === 'idle'" class="activity-item idle">
83
- 😴 空闲中
84
- </div>
85
- <div v-else-if="agent.status === 'down'" class="activity-item down">
86
- ⚠️ 检测到错误
87
- </div>
88
- </div>
89
- </div>
90
-
91
- <!-- 视图切换 Tab -->
92
- <div class="section">
93
- <div class="view-tabs">
94
- <button
95
- class="tab-btn"
96
- :class="{ active: activeView === 'timeline' }"
97
- @click="activeView = 'timeline'"
98
- >
99
- 📊 时序视图
100
- </button>
101
- <button
102
- class="tab-btn"
103
- :class="{ active: activeView === 'chain' }"
104
- @click="activeView = 'chain'"
105
- >
106
- 🔗 链路视图
107
- </button>
108
- <button
109
- class="tab-btn"
110
- :class="{ active: activeView === 'advanced' }"
111
- @click="activeView = 'advanced'"
112
- >
113
- ⚙️ 高级
114
- </button>
115
- </div>
116
-
117
- <!-- 时序视图 -->
118
- <div v-if="activeView === 'timeline'" class="timeline-container">
119
- <TimelineView
120
- :agentId="agent.id"
121
- :autoRefresh="false"
122
- />
123
- </div>
124
-
125
- <!-- 链路视图 -->
126
- <div v-else-if="activeView === 'chain'" class="chain-container">
127
- <TaskChainView :autoRefresh="true" :refreshInterval="10" />
128
- </div>
129
-
130
- <!-- 高级视图(配置 + 错误分析) -->
131
- <div v-else-if="activeView === 'advanced'" class="advanced-container">
132
- <div class="advanced-section">
133
- <h4>⚙️ 配置</h4>
134
- <AgentConfigPanel :agentId="agent.id" />
135
- </div>
136
- <div class="advanced-section">
137
- <h4>🔍 错误分析</h4>
138
- <ErrorAnalysisView :agentId="agent.id" />
139
- </div>
140
- </div>
141
- </div>
142
- </div>
143
- </div>
144
- </div>
145
- </template>
146
-
147
- <script setup lang="ts">
148
- import { computed, ref, watch, onMounted, onUnmounted } from 'vue'
149
- import { TimelineView } from './timeline'
150
- import { TaskChainView } from './chain'
151
- import AgentConfigPanel from './AgentConfigPanel.vue'
152
- import ErrorAnalysisView from './ErrorAnalysisView.vue'
153
-
154
- interface Agent {
155
- id: string
156
- name: string
157
- status: 'idle' | 'working' | 'down'
158
- currentTask?: string
159
- lastActiveFormatted?: string
160
- lastActiveAt?: number // 后端返回的字段名
161
- error?: {
162
- type: string
163
- message: string
164
- timestamp: number
165
- }
166
- }
167
-
168
- interface SubagentRun {
169
- runId: string
170
- status: string
171
- startedAt?: number
172
- archiveAtMs?: number
173
- lastToolResult?: string
174
- }
175
-
176
- const props = defineProps<{
177
- agent: Agent
178
- }>()
179
-
180
- defineEmits<{
181
- close: []
182
- }>()
183
-
184
- const activeView = ref<'timeline' | 'chain' | 'advanced'>('timeline')
185
- const subagentRun = ref<SubagentRun | null>(null)
186
- const currentTime = ref(Date.now())
187
- let timeUpdateInterval: ReturnType<typeof setInterval> | null = null
188
-
189
- // 卡顿检测阈值
190
- const STUCK_WARNING_MS = 5 * 60 * 1000 // 5 分钟警告
191
- const STUCK_CRITICAL_MS = 15 * 60 * 1000 // 15 分钟严重
192
-
193
- const statusText = computed(() => {
194
- const statusMap = {
195
- 'idle': '空闲',
196
- 'working': '工作中',
197
- 'down': '异常'
198
- }
199
- return statusMap[props.agent.status] || '未知'
200
- })
201
-
202
- // 卡顿检测
203
- const stuckInfo = computed(() => {
204
- if (props.agent.status !== 'working') return null
205
-
206
- const lastActive = props.agent.lastActiveAt || 0
207
- const idleTime = currentTime.value - lastActive
208
-
209
- if (idleTime > STUCK_CRITICAL_MS) {
210
- return {
211
- isStuck: true,
212
- idleMinutes: Math.floor(idleTime / 60000),
213
- severity: 'critical' as const,
214
- label: '严重卡顿'
215
- }
216
- } else if (idleTime > STUCK_WARNING_MS) {
217
- return {
218
- isStuck: true,
219
- idleMinutes: Math.floor(idleTime / 60000),
220
- severity: 'warning' as const,
221
- label: '可能卡顿'
222
- }
223
- }
224
- return null
225
- })
226
-
227
- // 超时倒计时
228
- const timeoutCountdown = computed(() => {
229
- if (!subagentRun.value?.archiveAtMs) return null
230
- const remaining = subagentRun.value.archiveAtMs - currentTime.value
231
- if (remaining <= 0) return '即将超时'
232
- const minutes = Math.floor(remaining / 60000)
233
- const seconds = Math.floor((remaining % 60000) / 1000)
234
- return `${minutes}分${seconds}秒`
235
- })
236
-
237
- // 最后工具结果
238
- const lastToolResultDisplay = computed(() => {
239
- if (!turns.value.length) return null
240
- const lastTurn = turns.value[turns.value.length - 1]
241
- if (lastTurn.role === 'toolResult') {
242
- const content = lastTurn.content[0]
243
- if (content) {
244
- const text = content.text || content.content || ''
245
- return {
246
- tool: lastTurn.toolName || '未知工具',
247
- status: content.status || 'completed',
248
- result: text.slice(0, 100) || '(无输出)'
249
- }
250
- }
251
- }
252
- return null
253
- })
254
-
255
- const turns = ref<Array<{
256
- turnIndex: number
257
- role: string
258
- content: Array<{ type: string; text?: string; content?: string; status?: string; error?: string }>
259
- usage?: { input: number; output: number; cacheRead?: number }
260
- toolCalls?: Array<{ name: string; id?: string; arguments?: unknown }>
261
- toolName?: string
262
- stopReason?: string
263
- }>>([])
264
- const loadingTurns = ref(false)
265
-
266
- function roleLabel(role: string) {
267
- const map: Record<string, string> = { user: '用户', assistant: '助手', toolResult: '工具结果' }
268
- return map[role] || role
269
- }
270
-
271
- function truncate(s: string, max: number) {
272
- if (!s || s.length <= max) return s
273
- return s.slice(0, max) + '...'
274
- }
275
-
276
- async function loadTurns() {
277
- if (!props.agent?.id) return
278
- loadingTurns.value = true
279
- try {
280
- const res = await fetch(`/api/agents/${props.agent.id}/output?limit=30`)
281
- if (res.ok) {
282
- const data = await res.json()
283
- turns.value = data.turns || []
284
- } else {
285
- turns.value = []
286
- }
287
- } catch {
288
- turns.value = []
289
- } finally {
290
- loadingTurns.value = false
291
- }
292
- }
293
-
294
- // 加载子 agent 运行状态
295
- async function loadSubagentRun() {
296
- if (props.agent.status !== 'working') {
297
- subagentRun.value = null
298
- return
299
- }
300
- try {
301
- const res = await fetch('/api/chains?limit=10')
302
- if (res.ok) {
303
- const data = await res.json()
304
- // 查找当前 agent 的运行
305
- const activeChain = data.activeChain
306
- if (activeChain?.nodes) {
307
- const node = activeChain.nodes.find((n: { agentId: string }) => n.agentId === props.agent.id)
308
- if (node && node.status === 'running') {
309
- subagentRun.value = {
310
- runId: activeChain.chainId,
311
- status: 'running',
312
- startedAt: node.startedAt,
313
- archiveAtMs: activeChain.archiveAtMs
314
- }
315
- }
316
- }
317
- }
318
- } catch {
319
- // ignore
320
- }
321
- }
322
-
323
- // 取消运行
324
- async function cancelRun() {
325
- if (!subagentRun.value?.runId) return
326
- if (!confirm('确定要取消这个任务吗?')) return
327
- try {
328
- // 这里需要 OpenClaw 提供取消 API
329
- alert('取消功能需要 OpenClaw 支持,请使用命令行: openclaw subagents cancel ' + subagentRun.value.runId)
330
- } catch (e) {
331
- console.error('Cancel failed:', e)
332
- }
333
- }
334
-
335
- // 刷新状态
336
- function refreshStatus() {
337
- loadTurns()
338
- loadSubagentRun()
339
- }
340
-
341
- watch(() => props.agent?.id, () => {
342
- loadTurns()
343
- loadSubagentRun()
344
- }, { immediate: true })
345
-
346
- // 监听 agent 状态变化
347
- watch(() => props.agent.status, loadSubagentRun)
348
-
349
- // 更新当前时间
350
- onMounted(() => {
351
- timeUpdateInterval = setInterval(() => {
352
- currentTime.value = Date.now()
353
- }, 1000)
354
- })
355
-
356
- onUnmounted(() => {
357
- if (timeUpdateInterval) {
358
- clearInterval(timeUpdateInterval)
359
- timeUpdateInterval = null
360
- }
361
- })
362
- </script>
363
-
364
- <style scoped>
365
- .panel-overlay {
366
- position: fixed;
367
- top: 0;
368
- left: 0;
369
- right: 0;
370
- bottom: 0;
371
- background: rgba(0, 0, 0, 0.5);
372
- display: flex;
373
- align-items: center;
374
- justify-content: center;
375
- z-index: 1000;
376
- }
377
-
378
- .panel {
379
- width: 800px;
380
- max-width: 92vw;
381
- max-height: 90vh;
382
- background: white;
383
- border-radius: 12px;
384
- box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
385
- overflow: hidden;
386
- display: flex;
387
- flex-direction: column;
388
- }
389
-
390
- .header {
391
- display: flex;
392
- justify-content: space-between;
393
- align-items: center;
394
- padding: 1.5rem;
395
- border-bottom: 1px solid #e5e7eb;
396
- }
397
-
398
- .header h2 {
399
- margin: 0;
400
- font-size: 1.5rem;
401
- color: #333;
402
- }
403
-
404
- .close-btn {
405
- font-size: 2rem;
406
- line-height: 1;
407
- background: none;
408
- border: none;
409
- cursor: pointer;
410
- color: #999;
411
- padding: 0;
412
- width: 32px;
413
- height: 32px;
414
- display: flex;
415
- align-items: center;
416
- justify-content: center;
417
- }
418
-
419
- .close-btn:hover {
420
- color: #333;
421
- }
422
-
423
- .content {
424
- padding: 1.5rem;
425
- overflow-y: auto;
426
- }
427
-
428
- .section {
429
- margin-bottom: 1.5rem;
430
- }
431
-
432
- .section:last-child {
433
- margin-bottom: 0;
434
- }
435
-
436
- .section h3 {
437
- margin: 0 0 0.75rem 0;
438
- font-size: 1rem;
439
- color: #666;
440
- }
441
-
442
- .status-info {
443
- display: flex;
444
- align-items: center;
445
- gap: 0.5rem;
446
- }
447
-
448
- .status-dot {
449
- width: 12px;
450
- height: 12px;
451
- border-radius: 50%;
452
- }
453
-
454
- .status-dot.status-idle {
455
- background: #4ade80;
456
- }
457
-
458
- .status-dot.status-working {
459
- background: #fbbf24;
460
- }
461
-
462
- .status-dot.status-down {
463
- background: #ef4444;
464
- }
465
-
466
- .status-text {
467
- font-size: 1.1rem;
468
- color: #333;
469
- font-weight: 500;
470
- }
471
-
472
- .error-title {
473
- color: #dc2626;
474
- }
475
-
476
- .error-info {
477
- padding: 1rem;
478
- background: #fef2f2;
479
- border-radius: 6px;
480
- border-left: 4px solid #dc2626;
481
- }
482
-
483
- .error-type {
484
- font-weight: 600;
485
- color: #dc2626;
486
- margin-bottom: 0.5rem;
487
- }
488
-
489
- .error-message {
490
- font-size: 0.9rem;
491
- color: #666;
492
- }
493
-
494
- .activity-list {
495
- display: flex;
496
- flex-direction: column;
497
- gap: 0.75rem;
498
- }
499
-
500
- .activity-item {
501
- padding: 0.75rem;
502
- border-radius: 6px;
503
- font-size: 0.9rem;
504
- }
505
-
506
- .activity-item.working {
507
- background: #fef3c7;
508
- color: #92400e;
509
- }
510
-
511
- .activity-item.idle {
512
- background: #d1fae5;
513
- color: #065f46;
514
- }
515
-
516
- .activity-item.down {
517
- background: #fee2e2;
518
- color: #991b1b;
519
- }
520
-
521
- /* 视图切换 Tab */
522
- .view-tabs {
523
- display: flex;
524
- flex-wrap: wrap;
525
- gap: 6px 10px;
526
- margin-bottom: 12px;
527
- }
528
-
529
- .tab-btn {
530
- padding: 6px 12px;
531
- font-size: 12px;
532
- border: 1px solid #e5e7eb;
533
- border-radius: 6px;
534
- background: #fff;
535
- cursor: pointer;
536
- color: #6b7280;
537
- transition: all 0.2s;
538
- flex-shrink: 0;
539
- }
540
-
541
- .tab-btn:hover {
542
- background: #f9fafb;
543
- }
544
-
545
- .tab-btn.active {
546
- background: #3b82f6;
547
- color: #fff;
548
- border-color: #3b82f6;
549
- }
550
-
551
- .timeline-container,
552
- .chain-container {
553
- border: 1px solid #e5e7eb;
554
- border-radius: 8px;
555
- overflow: hidden;
556
- }
557
-
558
- /* 高级视图容器 */
559
- .advanced-container {
560
- display: flex;
561
- flex-direction: column;
562
- gap: 16px;
563
- }
564
-
565
- .advanced-section {
566
- border: 1px solid #e5e7eb;
567
- border-radius: 8px;
568
- overflow: hidden;
569
- }
570
-
571
- .advanced-section h4 {
572
- margin: 0;
573
- padding: 10px 14px;
574
- background: #f9fafb;
575
- border-bottom: 1px solid #e5e7eb;
576
- font-size: 13px;
577
- font-weight: 600;
578
- color: #374151;
579
- }
580
-
581
- /* 诊断面板样式 */
582
- .diagnostic-panel {
583
- border-radius: 8px;
584
- overflow: hidden;
585
- border: 1px solid;
586
- }
587
-
588
- .diagnostic-panel.severity-warning {
589
- border-color: #fbbf24;
590
- background: #fffbeb;
591
- }
592
-
593
- .diagnostic-panel.severity-critical {
594
- border-color: #ef4444;
595
- background: #fef2f2;
596
- }
597
-
598
- .diagnostic-header {
599
- display: flex;
600
- align-items: center;
601
- gap: 8px;
602
- padding: 12px 16px;
603
- font-weight: 600;
604
- }
605
-
606
- .severity-warning .diagnostic-header {
607
- background: #fef3c7;
608
- color: #92400e;
609
- }
610
-
611
- .severity-critical .diagnostic-header {
612
- background: #fee2e2;
613
- color: #991b1b;
614
- }
615
-
616
- .diagnostic-icon {
617
- font-size: 18px;
618
- }
619
-
620
- .diagnostic-title {
621
- font-size: 14px;
622
- }
623
-
624
- .diagnostic-content {
625
- padding: 12px 16px;
626
- }
627
-
628
- .diagnostic-item {
629
- display: flex;
630
- gap: 8px;
631
- margin-bottom: 8px;
632
- font-size: 13px;
633
- }
634
-
635
- .diagnostic-item .item-label {
636
- color: #6b7280;
637
- min-width: 80px;
638
- }
639
-
640
- .diagnostic-item .item-value {
641
- color: #374151;
642
- }
643
-
644
- .diagnostic-item .item-value.highlight {
645
- font-weight: 600;
646
- color: #dc2626;
647
- }
648
-
649
- .diagnostic-hint {
650
- margin-top: 12px;
651
- padding: 10px;
652
- background: rgba(0, 0, 0, 0.03);
653
- border-radius: 6px;
654
- font-size: 12px;
655
- }
656
-
657
- .hint-title {
658
- font-weight: 600;
659
- color: #374151;
660
- margin-bottom: 6px;
661
- }
662
-
663
- .diagnostic-hint ul {
664
- margin: 0;
665
- padding-left: 18px;
666
- color: #6b7280;
667
- }
668
-
669
- .diagnostic-hint li {
670
- margin: 4px 0;
671
- }
672
-
673
- .diagnostic-actions {
674
- margin-top: 12px;
675
- display: flex;
676
- gap: 8px;
677
- }
678
-
679
- .action-btn {
680
- padding: 6px 14px;
681
- font-size: 13px;
682
- border-radius: 6px;
683
- border: 1px solid #d1d5db;
684
- background: #fff;
685
- cursor: pointer;
686
- color: #374151;
687
- transition: all 0.2s;
688
- }
689
-
690
- .action-btn:hover {
691
- background: #f3f4f6;
692
- }
693
-
694
- .action-btn.primary {
695
- background: #ef4444;
696
- border-color: #ef4444;
697
- color: #fff;
698
- }
699
-
700
- .action-btn.primary:hover {
701
- background: #dc2626;
702
- }
703
-
704
- /* 响应式适配 */
705
- @media (max-width: 1280px) {
706
- .panel {
707
- width: 700px;
708
- }
709
- }
710
-
711
- @media (max-width: 768px) {
712
- .panel {
713
- width: 95vw;
714
- max-height: 95vh;
715
- }
716
-
717
- .header {
718
- padding: 1rem;
719
- }
720
-
721
- .header h2 {
722
- font-size: 1.2rem;
723
- }
724
-
725
- .content {
726
- padding: 1rem;
727
- }
728
-
729
- .view-tabs {
730
- gap: 4px 8px;
731
- }
732
-
733
- .tab-btn {
734
- padding: 5px 10px;
735
- font-size: 11px;
736
- }
737
- }
738
- </style>