@lokvis/ui-react 0.2.1 → 0.2.2

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 (97) hide show
  1. package/dist/components/AssetPanel.d.ts.map +1 -1
  2. package/dist/components/AssetPanel.js +14 -14
  3. package/dist/components/AssetPanel.js.map +1 -1
  4. package/dist/components/Canvas.d.ts +4 -4
  5. package/dist/components/Canvas.d.ts.map +1 -1
  6. package/dist/components/Canvas.js +8 -8
  7. package/dist/components/Canvas.js.map +1 -1
  8. package/dist/components/CommandPalette.d.ts +7 -7
  9. package/dist/components/CommandPalette.d.ts.map +1 -1
  10. package/dist/components/CommandPalette.js +10 -10
  11. package/dist/components/CommandPalette.js.map +1 -1
  12. package/dist/components/CompareSlider.d.ts +6 -6
  13. package/dist/components/CompareSlider.d.ts.map +1 -1
  14. package/dist/components/CompareSlider.js +7 -7
  15. package/dist/components/CompareSlider.js.map +1 -1
  16. package/dist/components/DownloadPanel.d.ts +3 -3
  17. package/dist/components/DownloadPanel.d.ts.map +1 -1
  18. package/dist/components/DownloadPanel.js +9 -9
  19. package/dist/components/DownloadPanel.js.map +1 -1
  20. package/dist/components/ErrorBanner.d.ts +2 -2
  21. package/dist/components/ErrorBanner.d.ts.map +1 -1
  22. package/dist/components/ErrorBanner.js +3 -3
  23. package/dist/components/ErrorBanner.js.map +1 -1
  24. package/dist/components/ExifPanel.d.ts +1 -1
  25. package/dist/components/ExifPanel.d.ts.map +1 -1
  26. package/dist/components/ExifPanel.js +2 -2
  27. package/dist/components/ExifPanel.js.map +1 -1
  28. package/dist/components/GlobalDropzone.d.ts +5 -5
  29. package/dist/components/GlobalDropzone.d.ts.map +1 -1
  30. package/dist/components/GlobalDropzone.js +6 -6
  31. package/dist/components/GlobalDropzone.js.map +1 -1
  32. package/dist/components/HistoryPanel.d.ts +2 -2
  33. package/dist/components/HistoryPanel.d.ts.map +1 -1
  34. package/dist/components/HistoryPanel.js +23 -23
  35. package/dist/components/HistoryPanel.js.map +1 -1
  36. package/dist/components/Inspector.d.ts.map +1 -1
  37. package/dist/components/Inspector.js +6 -6
  38. package/dist/components/Inspector.js.map +1 -1
  39. package/dist/components/ParamForm.d.ts.map +1 -1
  40. package/dist/components/ParamForm.js +7 -7
  41. package/dist/components/ParamForm.js.map +1 -1
  42. package/dist/components/PipelineBar.d.ts.map +1 -1
  43. package/dist/components/PipelineBar.js +13 -13
  44. package/dist/components/PipelineBar.js.map +1 -1
  45. package/dist/components/ProgressBar.d.ts +4 -4
  46. package/dist/components/ProgressBar.d.ts.map +1 -1
  47. package/dist/components/ProgressBar.js +8 -8
  48. package/dist/components/ProgressBar.js.map +1 -1
  49. package/dist/components/StatusBar.d.ts +3 -3
  50. package/dist/components/StatusBar.d.ts.map +1 -1
  51. package/dist/components/StatusBar.js +11 -11
  52. package/dist/components/StatusBar.js.map +1 -1
  53. package/dist/components/ThemeToggle.d.ts +4 -4
  54. package/dist/components/ThemeToggle.d.ts.map +1 -1
  55. package/dist/components/ThemeToggle.js +7 -7
  56. package/dist/components/ThemeToggle.js.map +1 -1
  57. package/dist/components/Toolbar.d.ts.map +1 -1
  58. package/dist/components/Toolbar.js +2 -2
  59. package/dist/components/Toolbar.js.map +1 -1
  60. package/dist/components/WorkflowEditor.d.ts +6 -6
  61. package/dist/components/WorkflowEditor.d.ts.map +1 -1
  62. package/dist/components/WorkflowEditor.js +25 -25
  63. package/dist/components/WorkflowEditor.js.map +1 -1
  64. package/dist/components/WorkflowTemplates.d.ts +2 -2
  65. package/dist/components/WorkflowTemplates.d.ts.map +1 -1
  66. package/dist/components/WorkflowTemplates.js +3 -3
  67. package/dist/components/WorkflowTemplates.js.map +1 -1
  68. package/dist/components/Workspace.d.ts +22 -22
  69. package/dist/components/Workspace.d.ts.map +1 -1
  70. package/dist/components/Workspace.js +30 -30
  71. package/dist/components/Workspace.js.map +1 -1
  72. package/dist/index.d.ts +1 -0
  73. package/dist/index.d.ts.map +1 -1
  74. package/dist/index.js +3 -0
  75. package/dist/index.js.map +1 -1
  76. package/package.json +6 -6
  77. package/src/components/AssetPanel.tsx +263 -263
  78. package/src/components/Canvas.tsx +174 -174
  79. package/src/components/CommandPalette.tsx +242 -242
  80. package/src/components/CompareSlider.tsx +163 -163
  81. package/src/components/DownloadPanel.tsx +203 -203
  82. package/src/components/ErrorBanner.tsx +64 -64
  83. package/src/components/ExifPanel.tsx +154 -154
  84. package/src/components/GlobalDropzone.tsx +170 -170
  85. package/src/components/HistoryPanel.tsx +219 -219
  86. package/src/components/Inspector.tsx +136 -136
  87. package/src/components/ParamForm.tsx +136 -136
  88. package/src/components/PipelineBar.tsx +98 -98
  89. package/src/components/ProgressBar.tsx +78 -78
  90. package/src/components/StatusBar.tsx +148 -148
  91. package/src/components/ThemeToggle.tsx +98 -98
  92. package/src/components/Toolbar.tsx +65 -65
  93. package/src/components/WorkflowEditor.tsx +321 -321
  94. package/src/components/WorkflowTemplates.tsx +77 -77
  95. package/src/components/Workspace.tsx +223 -223
  96. package/src/css-modules.d.ts +3 -0
  97. package/src/index.ts +4 -0
@@ -10,117 +10,117 @@ import { Icon } from '@lokvis/ui-core';
10
10
  import { useWorkspaceStore } from '../store/index.js';
11
11
 
12
12
  export interface PipelineBarProps {
13
- className?: string;
13
+ className?: string;
14
14
  }
15
15
 
16
16
  export function PipelineBar({ className = '' }: PipelineBarProps) {
17
- const nodes = useWorkspaceStore((s) => s.nodes);
18
- const selectedNodeId = useWorkspaceStore((s) => s.selectedNodeId);
19
- const selectNode = useWorkspaceStore((s) => s.selectNode);
20
- const removeNode = useWorkspaceStore((s) => s.removeNode);
17
+ const nodes = useWorkspaceStore((s) => s.nodes);
18
+ const selectedNodeId = useWorkspaceStore((s) => s.selectedNodeId);
19
+ const selectNode = useWorkspaceStore((s) => s.selectNode);
20
+ const removeNode = useWorkspaceStore((s) => s.removeNode);
21
21
 
22
- return (
23
- <div className={`flex h-10 shrink-0 items-center gap-2 border-t border-zinc-200 bg-white px-3 dark:border-zinc-800 dark:bg-zinc-900 ${className}`}>
24
- <span className="text-[10px] font-semibold uppercase tracking-wider text-zinc-400 shrink-0">
25
- Pipeline
26
- </span>
22
+ return (
23
+ <div className={`flex h-10 shrink-0 items-center gap-2 border-t border-[var(--lokvis-border)] bg-[var(--lokvis-surface)] px-3 ${className}`}>
24
+ <span className="text-[10px] font-semibold uppercase tracking-wider text-[var(--lokvis-fg-subtle)] shrink-0">
25
+ Pipeline
26
+ </span>
27
27
 
28
- {nodes.length === 0 ? (
29
- <span className="text-[11px] text-zinc-400">
30
- Empty &mdash; add capabilities from the right panel
31
- </span>
32
- ) : (
33
- <div className="flex flex-1 items-center gap-1 overflow-x-auto min-w-0">
34
- {/* Source indicator */}
35
- <div className="flex items-center gap-1 rounded-md bg-zinc-100 px-2 py-1 text-[10px] font-medium text-zinc-500 dark:bg-zinc-800 dark:text-zinc-400 shrink-0">
36
- <Icon size={12}><path d="M12 4.5v15m7.5-7.5h-15" /></Icon>
37
- Source
38
- </div>
28
+ {nodes.length === 0 ? (
29
+ <span className="text-[11px] text-[var(--lokvis-fg-subtle)]">
30
+ Empty &mdash; add capabilities from the right panel
31
+ </span>
32
+ ) : (
33
+ <div className="flex flex-1 items-center gap-1 overflow-x-auto min-w-0">
34
+ {/* Source indicator */}
35
+ <div className="flex items-center gap-1 rounded-md bg-[var(--lokvis-surface-muted)] px-2 py-1 text-[10px] font-medium text-[var(--lokvis-fg-muted)] shrink-0">
36
+ <Icon size={12}><path d="M12 4.5v15m7.5-7.5h-15" /></Icon>
37
+ Source
38
+ </div>
39
39
 
40
- {/* Arrow */}
41
- <Icon size={12} className="shrink-0 text-zinc-300 dark:text-zinc-600">
42
- <path d="m9 5 7 7-7 7" />
43
- </Icon>
40
+ {/* Arrow */}
41
+ <Icon size={12} className="shrink-0 text-[var(--lokvis-fg-subtle)]">
42
+ <path d="m9 5 7 7-7 7" />
43
+ </Icon>
44
44
 
45
- {/* Nodes */}
46
- {nodes.map((node, i) => {
47
- const selected = node.id === selectedNodeId;
48
- return (
49
- <React.Fragment key={node.id}>
50
- <button
51
- type="button"
52
- onClick={() => selectNode(node.id)}
53
- className={`group flex shrink-0 items-center gap-1.5 rounded-md px-2.5 py-1 text-[11px] font-medium transition-all ${
54
- selected
55
- ? 'bg-indigo-100 text-indigo-700 ring-1 ring-indigo-300 dark:bg-indigo-950/50 dark:text-indigo-300 dark:ring-indigo-700'
56
- : node.status === 'running'
57
- ? 'bg-amber-100 text-amber-700 dark:bg-amber-950/30 dark:text-amber-400'
58
- : node.status === 'success'
59
- ? 'bg-emerald-100 text-emerald-700 dark:bg-emerald-950/30 dark:text-emerald-400'
60
- : node.status === 'failed'
61
- ? 'bg-red-100 text-red-700 dark:bg-red-950/30 dark:text-red-400'
62
- : 'bg-zinc-100 text-zinc-700 hover:bg-zinc-200 dark:bg-zinc-800 dark:text-zinc-300 dark:hover:bg-zinc-700'
63
- }`}
64
- >
65
- <span className="font-mono">{node.capability}</span>
66
- <StatusDot status={node.status} />
67
- <span
68
- onClick={(e) => {
69
- e.stopPropagation();
70
- removeNode(node.id);
71
- }}
72
- className="ml-0.5 rounded p-0.5 opacity-0 transition-opacity group-hover:opacity-100 hover:bg-red-100 hover:text-red-600 dark:hover:bg-red-900/40"
73
- role="button"
74
- aria-label="Remove step"
75
- >
76
- <Icon size={10} strokeWidth={3}><path d="M6 18L18 6M6 6l12 12" /></Icon>
77
- </span>
78
- </button>
45
+ {/* Nodes */}
46
+ {nodes.map((node, i) => {
47
+ const selected = node.id === selectedNodeId;
48
+ return (
49
+ <React.Fragment key={node.id}>
50
+ <button
51
+ type="button"
52
+ onClick={() => selectNode(node.id)}
53
+ className={`group flex shrink-0 items-center gap-1.5 rounded-md px-2.5 py-1 text-[11px] font-medium transition-all ${
54
+ selected
55
+ ? 'bg-[var(--lokvis-primary)]/15 text-[var(--lokvis-primary)] ring-1 ring-[var(--lokvis-primary)]/50'
56
+ : node.status === 'running'
57
+ ? 'bg-[var(--lokvis-warning)]/15 text-[var(--lokvis-warning)]'
58
+ : node.status === 'success'
59
+ ? 'bg-[var(--lokvis-success)]/15 text-[var(--lokvis-success)]'
60
+ : node.status === 'failed'
61
+ ? 'bg-[var(--lokvis-danger)]/15 text-[var(--lokvis-danger)]'
62
+ : 'bg-[var(--lokvis-surface-muted)] text-[var(--lokvis-fg-muted)] hover:bg-[var(--lokvis-border)]'
63
+ }`}
64
+ >
65
+ <span className="font-mono">{node.capability}</span>
66
+ <StatusDot status={node.status} />
67
+ <span
68
+ onClick={(e) => {
69
+ e.stopPropagation();
70
+ removeNode(node.id);
71
+ }}
72
+ className="ml-0.5 rounded p-0.5 opacity-0 transition-opacity group-hover:opacity-100 hover:bg-[var(--lokvis-danger)]/15 hover:text-[var(--lokvis-danger)]"
73
+ role="button"
74
+ aria-label="Remove step"
75
+ >
76
+ <Icon size={10} strokeWidth={3}><path d="M6 18L18 6M6 6l12 12" /></Icon>
77
+ </span>
78
+ </button>
79
79
 
80
- {/* Arrow between nodes */}
81
- {i < nodes.length - 1 && (
82
- <Icon size={12} className="shrink-0 text-zinc-300 dark:text-zinc-600">
83
- <path d="m9 5 7 7-7 7" />
84
- </Icon>
85
- )}
86
- </React.Fragment>
87
- );
88
- })}
80
+ {/* Arrow between nodes */}
81
+ {i < nodes.length - 1 && (
82
+ <Icon size={12} className="shrink-0 text-[var(--lokvis-fg-subtle)]">
83
+ <path d="m9 5 7 7-7 7" />
84
+ </Icon>
85
+ )}
86
+ </React.Fragment>
87
+ );
88
+ })}
89
89
 
90
- {/* Output indicator */}
91
- <Icon size={12} className="shrink-0 text-zinc-300 dark:text-zinc-600">
92
- <path d="m9 5 7 7-7 7" />
93
- </Icon>
94
- <div className="flex items-center gap-1 rounded-md bg-zinc-100 px-2 py-1 text-[10px] font-medium text-zinc-500 dark:bg-zinc-800 dark:text-zinc-400 shrink-0">
95
- <Icon size={12}><path d="M3 16.5v2.25A2.25 2.25 0 0 0 5.25 21h13.5A2.25 2.25 0 0 0 21 18.75V16.5M16.5 12 12 16.5m0 0L7.5 12m4.5 4.5V3" /></Icon>
96
- Output
97
- </div>
98
- </div>
99
- )}
90
+ {/* Output indicator */}
91
+ <Icon size={12} className="shrink-0 text-[var(--lokvis-fg-subtle)]">
92
+ <path d="m9 5 7 7-7 7" />
93
+ </Icon>
94
+ <div className="flex items-center gap-1 rounded-md bg-[var(--lokvis-surface-muted)] px-2 py-1 text-[10px] font-medium text-[var(--lokvis-fg-muted)] shrink-0">
95
+ <Icon size={12}><path d="M3 16.5v2.25A2.25 2.25 0 0 0 5.25 21h13.5A2.25 2.25 0 0 0 21 18.75V16.5M16.5 12 12 16.5m0 0L7.5 12m4.5 4.5V3" /></Icon>
96
+ Output
97
+ </div>
98
+ </div>
99
+ )}
100
100
 
101
- {/* Step count */}
102
- {nodes.length > 0 && (
103
- <span className="shrink-0 text-[10px] tabular-nums text-zinc-400">
104
- {nodes.length} step{nodes.length !== 1 ? 's' : ''}
105
- </span>
106
- )}
107
- </div>
108
- );
101
+ {/* Step count */}
102
+ {nodes.length > 0 && (
103
+ <span className="shrink-0 text-[10px] tabular-nums text-[var(--lokvis-fg-subtle)]">
104
+ {nodes.length} step{nodes.length !== 1 ? 's' : ''}
105
+ </span>
106
+ )}
107
+ </div>
108
+ );
109
109
  }
110
110
 
111
111
  function StatusDot({ status }: { status: string }) {
112
- const color =
113
- status === 'running'
114
- ? 'bg-amber-500'
115
- : status === 'success'
116
- ? 'bg-emerald-500'
117
- : status === 'failed'
118
- ? 'bg-red-500'
119
- : status === 'pending'
120
- ? 'bg-zinc-400 dark:bg-zinc-500'
121
- : 'bg-zinc-300 dark:bg-zinc-600';
112
+ const color =
113
+ status === 'running'
114
+ ? 'bg-[var(--lokvis-warning)]'
115
+ : status === 'success'
116
+ ? 'bg-[var(--lokvis-success)]'
117
+ : status === 'failed'
118
+ ? 'bg-[var(--lokvis-danger)]'
119
+ : status === 'pending'
120
+ ? 'bg-[var(--lokvis-fg-subtle)]'
121
+ : 'bg-[var(--lokvis-fg-subtle)]';
122
122
 
123
- const animate = status === 'running' ? 'animate-pulse' : '';
123
+ const animate = status === 'running' ? 'animate-pulse' : '';
124
124
 
125
- return <span className={`h-1.5 w-1.5 rounded-full shrink-0 ${color} ${animate}`} />;
125
+ return <span className={`h-1.5 w-1.5 rounded-full shrink-0 ${color} ${animate}`} />;
126
126
  }
@@ -2,10 +2,10 @@
2
2
  * ProgressBar - 工作流执行进度条 + 取消按钮(W11.6)
3
3
  *
4
4
  * 展示:
5
- * - 节点完成进度(成功数 / 总数)
6
- * - 横向进度条(running 时动画)
7
- * - Cancel 按钮(调用 cancelRun)
8
- * - 状态消息(statusMessage)
5
+ * - 节点完成进度(成功数 / 总数)
6
+ * - 横向进度条(running 时动画)
7
+ * - Cancel 按钮(调用 cancelRun)
8
+ * - 状态消息(statusMessage)
9
9
  *
10
10
  * 仅在 running 或最近一次执行有结果时显示。
11
11
  *
@@ -17,88 +17,88 @@ import { useWorkspaceStore } from '../store/index.js';
17
17
  import { useWorkflowProgress } from '../hooks/useWorkflowProgress.js';
18
18
 
19
19
  export interface ProgressBarProps {
20
- className?: string;
20
+ className?: string;
21
21
  }
22
22
 
23
23
  export function ProgressBar({ className = '' }: ProgressBarProps) {
24
- const running = useWorkspaceStore((s) => s.running);
25
- const statusMessage = useWorkspaceStore((s) => s.statusMessage);
26
- const cancelRun = useWorkspaceStore((s) => s.cancelRun);
24
+ const running = useWorkspaceStore((s) => s.running);
25
+ const statusMessage = useWorkspaceStore((s) => s.statusMessage);
26
+ const cancelRun = useWorkspaceStore((s) => s.cancelRun);
27
27
 
28
- // 进度计算抽取到共享 hook(与 StatusBar 共用,避免重复逻辑)
29
- const { total, done, failedCount, pct, hasFailure } = useWorkflowProgress();
28
+ // 进度计算抽取到共享 hook(与 StatusBar 共用,避免重复逻辑)
29
+ const { total, done, failedCount, pct, hasFailure } = useWorkflowProgress();
30
30
 
31
- // 没有节点或未运行且无最近结果时不渲染
32
- if (total === 0) return null;
33
- if (!running && done === 0) return null;
31
+ // 没有节点或未运行且无最近结果时不渲染
32
+ if (total === 0) return null;
33
+ if (!running && done === 0) return null;
34
34
 
35
- return (
36
- <div
37
- className={`flex items-center gap-2 border-b border-zinc-200 bg-white px-3 py-1.5 dark:border-zinc-800 dark:bg-zinc-900 ${className}`}
38
- role="status"
39
- aria-live="polite"
40
- aria-label={`工作流进度:${done} / ${total}`}
41
- >
42
- {/* 进度条 */}
43
- <div className="relative h-1.5 flex-1 overflow-hidden rounded-full bg-zinc-200 dark:bg-zinc-800">
44
- <div
45
- className={`absolute inset-y-0 left-0 transition-all duration-300 ${
46
- hasFailure
47
- ? 'bg-red-500'
48
- : running
49
- ? 'bg-indigo-500'
50
- : 'bg-emerald-500'
51
- } ${running ? 'animate-pulse' : ''}`}
52
- style={{ width: `${pct}%` }}
53
- />
54
- </div>
35
+ return (
36
+ <div
37
+ className={`flex items-center gap-2 border-b border-[var(--lokvis-border)] bg-[var(--lokvis-surface)] px-3 py-1.5 ${className}`}
38
+ role="status"
39
+ aria-live="polite"
40
+ aria-label={`工作流进度:${done} / ${total}`}
41
+ >
42
+ {/* 进度条 */}
43
+ <div className="relative h-1.5 flex-1 overflow-hidden rounded-full bg-[var(--lokvis-border)]">
44
+ <div
45
+ className={`absolute inset-y-0 left-0 transition-all duration-300 ${
46
+ hasFailure
47
+ ? 'bg-[var(--lokvis-danger)]'
48
+ : running
49
+ ? 'bg-[var(--lokvis-primary)]'
50
+ : 'bg-[var(--lokvis-success)]'
51
+ } ${running ? 'animate-pulse' : ''}`}
52
+ style={{ width: `${pct}%` }}
53
+ />
54
+ </div>
55
55
 
56
- {/* 计数 */}
57
- <span className="shrink-0 text-[10px] font-medium tabular-nums text-zinc-500 dark:text-zinc-400">
58
- {done}/{total}
59
- {failedCount > 0 && (
60
- <span className="ml-1 text-red-500">({failedCount} failed)</span>
61
- )}
62
- </span>
56
+ {/* 计数 */}
57
+ <span className="shrink-0 text-[10px] font-medium tabular-nums text-[var(--lokvis-fg-muted)]">
58
+ {done}/{total}
59
+ {failedCount > 0 && (
60
+ <span className="ml-1 text-[var(--lokvis-danger)]">({failedCount} failed)</span>
61
+ )}
62
+ </span>
63
63
 
64
- {/* 状态消息(running 时显示) */}
65
- {running && statusMessage && (
66
- <span className="hidden shrink-0 truncate text-[10px] text-zinc-400 sm:inline">
67
- {statusMessage}
68
- </span>
69
- )}
64
+ {/* 状态消息(running 时显示) */}
65
+ {running && statusMessage && (
66
+ <span className="hidden shrink-0 truncate text-[10px] text-[var(--lokvis-fg-subtle)] sm:inline">
67
+ {statusMessage}
68
+ </span>
69
+ )}
70
70
 
71
- {/* Cancel 按钮(仅 running 时) */}
72
- {running && (
73
- <button
74
- type="button"
75
- onClick={() => void cancelRun()}
76
- aria-label="取消运行"
77
- title="取消运行"
78
- className="flex shrink-0 items-center gap-1 rounded-md bg-red-50 px-2 py-0.5 text-[10px] font-medium text-red-600 transition-colors hover:bg-red-100 dark:bg-red-950/30 dark:text-red-400 dark:hover:bg-red-900/40"
79
- >
80
- <Icon size={10} strokeWidth={3}><path d="M6 6l12 12M6 18L18 6" /></Icon>
81
- Cancel
82
- </button>
83
- )}
71
+ {/* Cancel 按钮(仅 running 时) */}
72
+ {running && (
73
+ <button
74
+ type="button"
75
+ onClick={() => void cancelRun()}
76
+ aria-label="取消运行"
77
+ title="取消运行"
78
+ className="flex shrink-0 items-center gap-1 rounded-md bg-[var(--lokvis-danger)]/10 px-2 py-0.5 text-[10px] font-medium text-[var(--lokvis-danger)] transition-colors hover:bg-[var(--lokvis-danger)]/15"
79
+ >
80
+ <Icon size={10} strokeWidth={3}><path d="M6 6l12 12M6 18L18 6" /></Icon>
81
+ Cancel
82
+ </button>
83
+ )}
84
84
 
85
- {/* 完成图标(非 running 且有结果) */}
86
- {!running && done === total && (
87
- <span
88
- className={`flex shrink-0 items-center gap-1 text-[10px] font-medium ${
89
- hasFailure ? 'text-red-500' : 'text-emerald-500'
90
- }`}
91
- >
92
- <Icon size={10} strokeWidth={3}>
93
- {hasFailure ? (
94
- <path d="M12 2L2 22h20L12 2zM12 9v5M12 17v.01" />
95
- ) : (
96
- <path d="M20 6L9 17l-5-5" />
97
- )}
98
- </Icon>
99
- {hasFailure ? 'Failed' : 'Done'}
100
- </span>
101
- )}
102
- </div>
103
- );
85
+ {/* 完成图标(非 running 且有结果) */}
86
+ {!running && done === total && (
87
+ <span
88
+ className={`flex shrink-0 items-center gap-1 text-[10px] font-medium ${
89
+ hasFailure ? 'text-[var(--lokvis-danger)]' : 'text-[var(--lokvis-success)]'
90
+ }`}
91
+ >
92
+ <Icon size={10} strokeWidth={3}>
93
+ {hasFailure ? (
94
+ <path d="M12 2L2 22h20L12 2zM12 9v5M12 17v.01" />
95
+ ) : (
96
+ <path d="M20 6L9 17l-5-5" />
97
+ )}
98
+ </Icon>
99
+ {hasFailure ? 'Failed' : 'Done'}
100
+ </span>
101
+ )}
102
+ </div>
103
+ );
104
104
  }