@lokvis/ui-react 0.2.1 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (165) hide show
  1. package/dist/components/AssetPanel.d.ts.map +1 -1
  2. package/dist/components/AssetPanel.js +18 -62
  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 +9 -9
  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 +9 -8
  13. package/dist/components/CompareSlider.d.ts.map +1 -1
  14. package/dist/components/CompareSlider.js +10 -9
  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 +10 -17
  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/ErrorBoundary.d.ts +40 -0
  25. package/dist/components/ErrorBoundary.d.ts.map +1 -0
  26. package/dist/components/ErrorBoundary.js +52 -0
  27. package/dist/components/ErrorBoundary.js.map +1 -0
  28. package/dist/components/ExifPanel.d.ts +1 -1
  29. package/dist/components/ExifPanel.d.ts.map +1 -1
  30. package/dist/components/ExifPanel.js +2 -2
  31. package/dist/components/ExifPanel.js.map +1 -1
  32. package/dist/components/GlobalDropzone.d.ts +5 -5
  33. package/dist/components/GlobalDropzone.d.ts.map +1 -1
  34. package/dist/components/GlobalDropzone.js +15 -8
  35. package/dist/components/GlobalDropzone.js.map +1 -1
  36. package/dist/components/HistoryPanel.d.ts +2 -2
  37. package/dist/components/HistoryPanel.d.ts.map +1 -1
  38. package/dist/components/HistoryPanel.js +23 -23
  39. package/dist/components/HistoryPanel.js.map +1 -1
  40. package/dist/components/Inspector.d.ts.map +1 -1
  41. package/dist/components/Inspector.js +13 -10
  42. package/dist/components/Inspector.js.map +1 -1
  43. package/dist/components/ParamForm.d.ts.map +1 -1
  44. package/dist/components/ParamForm.js +10 -7
  45. package/dist/components/ParamForm.js.map +1 -1
  46. package/dist/components/PipelineBar.d.ts.map +1 -1
  47. package/dist/components/PipelineBar.js +19 -28
  48. package/dist/components/PipelineBar.js.map +1 -1
  49. package/dist/components/ProgressBar.d.ts +4 -4
  50. package/dist/components/ProgressBar.d.ts.map +1 -1
  51. package/dist/components/ProgressBar.js +8 -8
  52. package/dist/components/ProgressBar.js.map +1 -1
  53. package/dist/components/StatusBar.d.ts +3 -3
  54. package/dist/components/StatusBar.d.ts.map +1 -1
  55. package/dist/components/StatusBar.js +12 -21
  56. package/dist/components/StatusBar.js.map +1 -1
  57. package/dist/components/StatusDot.d.ts +12 -0
  58. package/dist/components/StatusDot.d.ts.map +1 -0
  59. package/dist/components/StatusDot.js +15 -0
  60. package/dist/components/StatusDot.js.map +1 -0
  61. package/dist/components/ThemeToggle.d.ts +4 -4
  62. package/dist/components/ThemeToggle.d.ts.map +1 -1
  63. package/dist/components/ThemeToggle.js +7 -7
  64. package/dist/components/ThemeToggle.js.map +1 -1
  65. package/dist/components/ToolRunner.d.ts +53 -0
  66. package/dist/components/ToolRunner.d.ts.map +1 -0
  67. package/dist/components/ToolRunner.js +142 -0
  68. package/dist/components/ToolRunner.js.map +1 -0
  69. package/dist/components/Toolbar.d.ts.map +1 -1
  70. package/dist/components/Toolbar.js +3 -3
  71. package/dist/components/Toolbar.js.map +1 -1
  72. package/dist/components/WorkflowEditor.d.ts +6 -6
  73. package/dist/components/WorkflowEditor.d.ts.map +1 -1
  74. package/dist/components/WorkflowEditor.js +40 -49
  75. package/dist/components/WorkflowEditor.js.map +1 -1
  76. package/dist/components/WorkflowTemplates.d.ts +2 -2
  77. package/dist/components/WorkflowTemplates.d.ts.map +1 -1
  78. package/dist/components/WorkflowTemplates.js +17 -7
  79. package/dist/components/WorkflowTemplates.js.map +1 -1
  80. package/dist/components/Workspace.d.ts +45 -23
  81. package/dist/components/Workspace.d.ts.map +1 -1
  82. package/dist/components/Workspace.js +87 -40
  83. package/dist/components/Workspace.js.map +1 -1
  84. package/dist/hooks/useCustomPresets.d.ts +98 -0
  85. package/dist/hooks/useCustomPresets.d.ts.map +1 -0
  86. package/dist/hooks/useCustomPresets.js +210 -0
  87. package/dist/hooks/useCustomPresets.js.map +1 -0
  88. package/dist/hooks/useLokvis.d.ts +19 -1
  89. package/dist/hooks/useLokvis.d.ts.map +1 -1
  90. package/dist/hooks/useLokvis.js +30 -9
  91. package/dist/hooks/useLokvis.js.map +1 -1
  92. package/dist/index.d.ts +4 -0
  93. package/dist/index.d.ts.map +1 -1
  94. package/dist/index.js +6 -0
  95. package/dist/index.js.map +1 -1
  96. package/dist/store/assets-slice.d.ts +8 -1
  97. package/dist/store/assets-slice.d.ts.map +1 -1
  98. package/dist/store/assets-slice.js +101 -54
  99. package/dist/store/assets-slice.js.map +1 -1
  100. package/dist/store/capabilities-slice.d.ts +1 -1
  101. package/dist/store/capabilities-slice.d.ts.map +1 -1
  102. package/dist/store/capabilities-slice.js +8 -1
  103. package/dist/store/capabilities-slice.js.map +1 -1
  104. package/dist/store/index.d.ts +1 -1
  105. package/dist/store/index.d.ts.map +1 -1
  106. package/dist/store/index.js +1 -1
  107. package/dist/store/index.js.map +1 -1
  108. package/dist/store/runtime-slice.d.ts.map +1 -1
  109. package/dist/store/runtime-slice.js +22 -16
  110. package/dist/store/runtime-slice.js.map +1 -1
  111. package/dist/store/subscribe-utils.d.ts +45 -0
  112. package/dist/store/subscribe-utils.d.ts.map +1 -0
  113. package/dist/store/subscribe-utils.js +69 -0
  114. package/dist/store/subscribe-utils.js.map +1 -0
  115. package/dist/store/types.d.ts +19 -11
  116. package/dist/store/types.d.ts.map +1 -1
  117. package/dist/store/types.js +0 -10
  118. package/dist/store/types.js.map +1 -1
  119. package/dist/store/workflow-slice.d.ts +1 -1
  120. package/dist/store/workflow-slice.d.ts.map +1 -1
  121. package/dist/store/workflow-slice.js +19 -51
  122. package/dist/store/workflow-slice.js.map +1 -1
  123. package/dist/utils.d.ts +24 -0
  124. package/dist/utils.d.ts.map +1 -0
  125. package/dist/utils.js +31 -0
  126. package/dist/utils.js.map +1 -0
  127. package/package.json +7 -6
  128. package/src/components/AssetPanel.tsx +224 -264
  129. package/src/components/Canvas.tsx +178 -174
  130. package/src/components/CommandPalette.tsx +242 -242
  131. package/src/components/CompareSlider.tsx +168 -165
  132. package/src/components/DownloadPanel.tsx +201 -207
  133. package/src/components/ErrorBanner.tsx +64 -64
  134. package/src/components/ErrorBoundary.tsx +101 -0
  135. package/src/components/ExifPanel.tsx +154 -154
  136. package/src/components/GlobalDropzone.tsx +175 -170
  137. package/src/components/HistoryPanel.tsx +219 -219
  138. package/src/components/Inspector.tsx +145 -137
  139. package/src/components/ParamForm.tsx +140 -137
  140. package/src/components/PipelineBar.tsx +97 -104
  141. package/src/components/ProgressBar.tsx +78 -78
  142. package/src/components/StatusBar.tsx +145 -152
  143. package/src/components/StatusDot.tsx +27 -0
  144. package/src/components/ThemeToggle.tsx +98 -98
  145. package/src/components/ToolRunner.tsx +352 -0
  146. package/src/components/Toolbar.tsx +65 -65
  147. package/src/components/WorkflowEditor.tsx +333 -326
  148. package/src/components/WorkflowTemplates.tsx +101 -78
  149. package/src/components/Workspace.tsx +310 -223
  150. package/src/css-modules.d.ts +3 -0
  151. package/src/hooks/useCustomPresets.ts +284 -0
  152. package/src/hooks/useLokvis.ts +43 -10
  153. package/src/index.ts +7 -0
  154. package/src/store/assets-slice.ts +106 -52
  155. package/src/store/capabilities-slice.ts +10 -2
  156. package/src/store/index.ts +1 -1
  157. package/src/store/runtime-slice.ts +20 -15
  158. package/src/store/subscribe-utils.ts +82 -0
  159. package/src/store/types.ts +19 -12
  160. package/src/store/workflow-slice.ts +19 -53
  161. package/src/utils.ts +41 -0
  162. package/dist/store.d.ts +0 -76
  163. package/dist/store.d.ts.map +0 -1
  164. package/dist/store.js +0 -217
  165. package/dist/store.js.map +0 -1
@@ -6,121 +6,114 @@
6
6
  */
7
7
 
8
8
  import * as React from 'react';
9
- import { Icon } from '@lokvis/ui-core';
9
+ import { FOCUS_RING, Icon } from '@lokvis/ui-core';
10
10
  import { useWorkspaceStore } from '../store/index.js';
11
+ import { StatusDot } from './StatusDot.js';
11
12
 
12
13
  export interface PipelineBarProps {
13
- className?: string;
14
+ className?: string;
14
15
  }
15
16
 
16
17
  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);
18
+ const nodes = useWorkspaceStore((s) => s.nodes);
19
+ const selectedNodeId = useWorkspaceStore((s) => s.selectedNodeId);
20
+ const selectNode = useWorkspaceStore((s) => s.selectNode);
21
+ const removeNode = useWorkspaceStore((s) => s.removeNode);
21
22
 
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>
23
+ return (
24
+ <div className={`flex h-10 shrink-0 items-center gap-2 border-t border-[var(--lokvis-border)] bg-[var(--lokvis-surface)] px-3 ${className}`}>
25
+ <span className="text-[10px] font-semibold uppercase tracking-wider text-[var(--lokvis-fg-subtle)] shrink-0">
26
+ Pipeline
27
+ </span>
27
28
 
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>
29
+ {nodes.length === 0 ? (
30
+ <span className="text-[11px] text-[var(--lokvis-fg-subtle)]">
31
+ Empty &mdash; add capabilities from the right panel
32
+ </span>
33
+ ) : (
34
+ <div className="flex flex-1 items-center gap-1 overflow-x-auto min-w-0">
35
+ {/* Source indicator */}
36
+ <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">
37
+ <Icon size={12}><path d="M12 4.5v15m7.5-7.5h-15" /></Icon>
38
+ Source
39
+ </div>
39
40
 
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>
41
+ {/* Arrow */}
42
+ <Icon size={12} className="shrink-0 text-[var(--lokvis-fg-subtle)]">
43
+ <path d="m9 5 7 7-7 7" />
44
+ </Icon>
44
45
 
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>
46
+ {/* Nodes */}
47
+ {nodes.map((node, i) => {
48
+ const selected = node.id === selectedNodeId;
49
+ return (
50
+ <React.Fragment key={node.id}>
51
+ {/* D7: remove button 作为 node button 的兄弟而非子元素(HTML 规范禁止 button 嵌套) */}
52
+ <div className="group flex shrink-0 items-center gap-0.5">
53
+ <button
54
+ type="button"
55
+ onClick={() => selectNode(node.id)}
56
+ onKeyDown={(e) => {
57
+ // 键盘可达:Delete/Backspace 删除节点(与 WorkflowEditor 一致)
58
+ if (e.key === 'Delete' || e.key === 'Backspace') {
59
+ e.preventDefault();
60
+ removeNode(node.id);
61
+ }
62
+ }}
63
+ aria-label={`节点 ${node.capability},位置 ${i + 1},Delete 删除`}
64
+ aria-pressed={selected}
65
+ className={`flex shrink-0 items-center gap-1.5 rounded-md px-2.5 py-1 text-[11px] font-medium transition-all ${FOCUS_RING} ${
66
+ selected
67
+ ? 'bg-[var(--lokvis-primary)]/15 text-[var(--lokvis-primary)] ring-1 ring-[var(--lokvis-primary)]/50'
68
+ : node.status === 'running'
69
+ ? 'bg-[var(--lokvis-warning)]/15 text-[var(--lokvis-warning)]'
70
+ : node.status === 'success'
71
+ ? 'bg-[var(--lokvis-success)]/15 text-[var(--lokvis-success)]'
72
+ : node.status === 'failed'
73
+ ? 'bg-[var(--lokvis-danger)]/15 text-[var(--lokvis-danger)]'
74
+ : 'bg-[var(--lokvis-surface-muted)] text-[var(--lokvis-fg-muted)] hover:bg-[var(--lokvis-border)]'
75
+ }`}
76
+ >
77
+ <span className="font-mono">{node.capability}</span>
78
+ <StatusDot status={node.status} />
79
+ </button>
80
+ <button
81
+ type="button"
82
+ onClick={() => removeNode(node.id)}
83
+ className="rounded p-0.5 opacity-0 transition-opacity group-hover:opacity-100 focus-visible:opacity-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--lokvis-danger)] hover:bg-[var(--lokvis-danger)]/15 hover:text-[var(--lokvis-danger)]"
84
+ aria-label="Remove step"
85
+ >
86
+ <Icon size={10} strokeWidth={3}><path d="M6 18L18 6M6 6l12 12" /></Icon>
87
+ </button>
88
+ </div>
79
89
 
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
- })}
90
+ {/* Arrow between nodes */}
91
+ {i < nodes.length - 1 && (
92
+ <Icon size={12} className="shrink-0 text-[var(--lokvis-fg-subtle)]">
93
+ <path d="m9 5 7 7-7 7" />
94
+ </Icon>
95
+ )}
96
+ </React.Fragment>
97
+ );
98
+ })}
89
99
 
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
- )}
100
+ {/* Output indicator */}
101
+ <Icon size={12} className="shrink-0 text-[var(--lokvis-fg-subtle)]">
102
+ <path d="m9 5 7 7-7 7" />
103
+ </Icon>
104
+ <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">
105
+ <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>
106
+ Output
107
+ </div>
108
+ </div>
109
+ )}
100
110
 
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
- );
109
- }
110
-
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';
122
-
123
- const animate = status === 'running' ? 'animate-pulse' : '';
124
-
125
- return <span className={`h-1.5 w-1.5 rounded-full shrink-0 ${color} ${animate}`} />;
111
+ {/* Step count */}
112
+ {nodes.length > 0 && (
113
+ <span className="shrink-0 text-[10px] tabular-nums text-[var(--lokvis-fg-subtle)]">
114
+ {nodes.length} step{nodes.length !== 1 ? 's' : ''}
115
+ </span>
116
+ )}
117
+ </div>
118
+ );
126
119
  }
@@ -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
  }