@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.
- package/dist/components/AssetPanel.d.ts.map +1 -1
- package/dist/components/AssetPanel.js +14 -14
- package/dist/components/AssetPanel.js.map +1 -1
- package/dist/components/Canvas.d.ts +4 -4
- package/dist/components/Canvas.d.ts.map +1 -1
- package/dist/components/Canvas.js +8 -8
- package/dist/components/Canvas.js.map +1 -1
- package/dist/components/CommandPalette.d.ts +7 -7
- package/dist/components/CommandPalette.d.ts.map +1 -1
- package/dist/components/CommandPalette.js +10 -10
- package/dist/components/CommandPalette.js.map +1 -1
- package/dist/components/CompareSlider.d.ts +6 -6
- package/dist/components/CompareSlider.d.ts.map +1 -1
- package/dist/components/CompareSlider.js +7 -7
- package/dist/components/CompareSlider.js.map +1 -1
- package/dist/components/DownloadPanel.d.ts +3 -3
- package/dist/components/DownloadPanel.d.ts.map +1 -1
- package/dist/components/DownloadPanel.js +9 -9
- package/dist/components/DownloadPanel.js.map +1 -1
- package/dist/components/ErrorBanner.d.ts +2 -2
- package/dist/components/ErrorBanner.d.ts.map +1 -1
- package/dist/components/ErrorBanner.js +3 -3
- package/dist/components/ErrorBanner.js.map +1 -1
- package/dist/components/ExifPanel.d.ts +1 -1
- package/dist/components/ExifPanel.d.ts.map +1 -1
- package/dist/components/ExifPanel.js +2 -2
- package/dist/components/ExifPanel.js.map +1 -1
- package/dist/components/GlobalDropzone.d.ts +5 -5
- package/dist/components/GlobalDropzone.d.ts.map +1 -1
- package/dist/components/GlobalDropzone.js +6 -6
- package/dist/components/GlobalDropzone.js.map +1 -1
- package/dist/components/HistoryPanel.d.ts +2 -2
- package/dist/components/HistoryPanel.d.ts.map +1 -1
- package/dist/components/HistoryPanel.js +23 -23
- package/dist/components/HistoryPanel.js.map +1 -1
- package/dist/components/Inspector.d.ts.map +1 -1
- package/dist/components/Inspector.js +6 -6
- package/dist/components/Inspector.js.map +1 -1
- package/dist/components/ParamForm.d.ts.map +1 -1
- package/dist/components/ParamForm.js +7 -7
- package/dist/components/ParamForm.js.map +1 -1
- package/dist/components/PipelineBar.d.ts.map +1 -1
- package/dist/components/PipelineBar.js +13 -13
- package/dist/components/PipelineBar.js.map +1 -1
- package/dist/components/ProgressBar.d.ts +4 -4
- package/dist/components/ProgressBar.d.ts.map +1 -1
- package/dist/components/ProgressBar.js +8 -8
- package/dist/components/ProgressBar.js.map +1 -1
- package/dist/components/StatusBar.d.ts +3 -3
- package/dist/components/StatusBar.d.ts.map +1 -1
- package/dist/components/StatusBar.js +11 -11
- package/dist/components/StatusBar.js.map +1 -1
- package/dist/components/ThemeToggle.d.ts +4 -4
- package/dist/components/ThemeToggle.d.ts.map +1 -1
- package/dist/components/ThemeToggle.js +7 -7
- package/dist/components/ThemeToggle.js.map +1 -1
- package/dist/components/Toolbar.d.ts.map +1 -1
- package/dist/components/Toolbar.js +2 -2
- package/dist/components/Toolbar.js.map +1 -1
- package/dist/components/WorkflowEditor.d.ts +6 -6
- package/dist/components/WorkflowEditor.d.ts.map +1 -1
- package/dist/components/WorkflowEditor.js +25 -25
- package/dist/components/WorkflowEditor.js.map +1 -1
- package/dist/components/WorkflowTemplates.d.ts +2 -2
- package/dist/components/WorkflowTemplates.d.ts.map +1 -1
- package/dist/components/WorkflowTemplates.js +3 -3
- package/dist/components/WorkflowTemplates.js.map +1 -1
- package/dist/components/Workspace.d.ts +22 -22
- package/dist/components/Workspace.d.ts.map +1 -1
- package/dist/components/Workspace.js +30 -30
- package/dist/components/Workspace.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/package.json +6 -6
- package/src/components/AssetPanel.tsx +263 -263
- package/src/components/Canvas.tsx +174 -174
- package/src/components/CommandPalette.tsx +242 -242
- package/src/components/CompareSlider.tsx +163 -163
- package/src/components/DownloadPanel.tsx +203 -203
- package/src/components/ErrorBanner.tsx +64 -64
- package/src/components/ExifPanel.tsx +154 -154
- package/src/components/GlobalDropzone.tsx +170 -170
- package/src/components/HistoryPanel.tsx +219 -219
- package/src/components/Inspector.tsx +136 -136
- package/src/components/ParamForm.tsx +136 -136
- package/src/components/PipelineBar.tsx +98 -98
- package/src/components/ProgressBar.tsx +78 -78
- package/src/components/StatusBar.tsx +148 -148
- package/src/components/ThemeToggle.tsx +98 -98
- package/src/components/Toolbar.tsx +65 -65
- package/src/components/WorkflowEditor.tsx +321 -321
- package/src/components/WorkflowTemplates.tsx +77 -77
- package/src/components/Workspace.tsx +223 -223
- package/src/css-modules.d.ts +3 -0
- package/src/index.ts +4 -0
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* 工作流节点链已移至独立的 PipelineBar 组件。
|
|
6
6
|
*
|
|
7
7
|
* W9.4: 当存在 lastOutputIds 时,可切换到 CompareSlider 模式,
|
|
8
|
-
*
|
|
8
|
+
* 并排展示 before/after。
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import * as React from 'react';
|
|
@@ -14,188 +14,188 @@ import { useWorkspaceStore } from '../store/index.js';
|
|
|
14
14
|
import { CompareSlider } from './CompareSlider.js';
|
|
15
15
|
|
|
16
16
|
export interface CanvasProps {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
17
|
+
className?: string;
|
|
18
|
+
/**
|
|
19
|
+
* 是否启用 before/after 对比模式(W9.4)。
|
|
20
|
+
* 默认 true;关闭后即使有 outputs 也只显示单图预览。
|
|
21
|
+
*/
|
|
22
|
+
enableCompare?: boolean;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
export function Canvas({ className = '', enableCompare = true }: CanvasProps) {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
26
|
+
const selectedAssetId = useWorkspaceStore((s) => s.selectedAssetId);
|
|
27
|
+
const assets = useWorkspaceStore((s) => s.assets);
|
|
28
|
+
const thumbnails = useWorkspaceStore((s) => s.thumbnails);
|
|
29
|
+
const importFiles = useWorkspaceStore((s) => s.importFiles);
|
|
30
|
+
const lastOutputIds = useWorkspaceStore((s) => s.lastOutputIds);
|
|
31
|
+
const selectedOutputId = useWorkspaceStore((s) => s.selectedOutputId);
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
const selected = assets.find((a) => a.id === selectedAssetId);
|
|
34
|
+
const preview = selected ? thumbnails[selected.id] : undefined;
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
36
|
+
// W9.4 是否有可对比的输出
|
|
37
|
+
const outputId = selectedOutputId ?? lastOutputIds[0] ?? null;
|
|
38
|
+
const outputThumbnail = outputId ? thumbnails[outputId] : undefined;
|
|
39
|
+
const canCompare = enableCompare && !!(preview && outputThumbnail);
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
41
|
+
// W9.4 默认开启 compare 模式:当 outputs 生成后,用户首次希望对比
|
|
42
|
+
// 切换状态由用户控制,直到下次 outputs 重新生成时再默认开启
|
|
43
|
+
const [compareMode, setCompareMode] = React.useState(false);
|
|
44
|
+
// 记录上次已自动切到 compare 模式的 outputId。
|
|
45
|
+
// 修复 review:原 effect 依赖 [lastOutputIds, preview, outputThumbnail],
|
|
46
|
+
// 当用户手动切到 Single 后再切换选中资产,preview 变化会触发 effect
|
|
47
|
+
// 把 compareMode 强制重置为 true,覆盖用户选择。
|
|
48
|
+
// 现在仅在 outputId 真正变化(新 run 完成 / 切换输出)且 preview/outputThumbnail
|
|
49
|
+
// 都就绪时才自动切换;同一 outputId 下 preview / outputThumbnail 的异步变化
|
|
50
|
+
// 不会覆盖用户已选择的 Single 模式。
|
|
51
|
+
const lastAutoSwitchedOutputId = React.useRef<string | null>(null);
|
|
52
|
+
React.useEffect(() => {
|
|
53
|
+
if (
|
|
54
|
+
outputId !== null &&
|
|
55
|
+
outputId !== lastAutoSwitchedOutputId.current &&
|
|
56
|
+
preview &&
|
|
57
|
+
outputThumbnail
|
|
58
|
+
) {
|
|
59
|
+
lastAutoSwitchedOutputId.current = outputId;
|
|
60
|
+
setCompareMode(true);
|
|
61
|
+
}
|
|
62
|
+
}, [outputId, preview, outputThumbnail]);
|
|
63
63
|
|
|
64
|
-
|
|
65
|
-
|
|
64
|
+
// 全局拖拽支持(直接拖到画布区)
|
|
65
|
+
const [dragOver, setDragOver] = React.useState(false);
|
|
66
66
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
67
|
+
return (
|
|
68
|
+
<main
|
|
69
|
+
className={`relative flex flex-1 flex-col overflow-hidden ${className}`}
|
|
70
|
+
onDragOver={(e) => { e.preventDefault(); setDragOver(true); }}
|
|
71
|
+
onDragLeave={() => setDragOver(false)}
|
|
72
|
+
onDrop={(e) => {
|
|
73
|
+
e.preventDefault();
|
|
74
|
+
// stopPropagation 防止事件冒泡到 window 的 GlobalDropzone 监听,
|
|
75
|
+
// 否则文件会被导入两次(Canvas 处理一次 + GlobalDropzone 再处理一次)
|
|
76
|
+
e.stopPropagation();
|
|
77
|
+
setDragOver(false);
|
|
78
|
+
if (e.dataTransfer.files.length > 0) {
|
|
79
|
+
void importFiles(Array.from(e.dataTransfer.files));
|
|
80
|
+
}
|
|
81
|
+
}}
|
|
82
|
+
>
|
|
83
|
+
{/* Preview area */}
|
|
84
|
+
<div className="relative flex flex-1 items-center justify-center bg-[var(--lokvis-surface)]">
|
|
85
|
+
{/* Subtle grid pattern */}
|
|
86
|
+
<div
|
|
87
|
+
className="absolute inset-0 opacity-[0.35]"
|
|
88
|
+
style={{
|
|
89
|
+
backgroundImage: 'radial-gradient(circle, #d4d4d8 1px, transparent 1px)',
|
|
90
|
+
backgroundSize: '20px 20px',
|
|
91
|
+
}}
|
|
92
|
+
/>
|
|
93
93
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
94
|
+
{/* W9.4 Compare 模式切换按钮(右上) */}
|
|
95
|
+
{canCompare && (
|
|
96
|
+
<div className="absolute top-2 right-2 z-20 flex items-center gap-1 rounded-md bg-[var(--lokvis-surface)]/90 p-0.5 shadow-sm backdrop-blur-sm">
|
|
97
|
+
<button
|
|
98
|
+
type="button"
|
|
99
|
+
onClick={() => setCompareMode(false)}
|
|
100
|
+
aria-pressed={!compareMode}
|
|
101
|
+
className={`rounded px-2 py-1 text-[10px] font-medium transition-colors ${
|
|
102
|
+
!compareMode
|
|
103
|
+
? 'bg-[var(--lokvis-primary)]/15 text-[var(--lokvis-primary)]'
|
|
104
|
+
: 'text-[var(--lokvis-fg-muted)] hover:bg-[var(--lokvis-surface-muted)]'
|
|
105
|
+
}`}
|
|
106
|
+
>
|
|
107
|
+
Single
|
|
108
|
+
</button>
|
|
109
|
+
<button
|
|
110
|
+
type="button"
|
|
111
|
+
onClick={() => setCompareMode(true)}
|
|
112
|
+
aria-pressed={compareMode}
|
|
113
|
+
className={`rounded px-2 py-1 text-[10px] font-medium transition-colors ${
|
|
114
|
+
compareMode
|
|
115
|
+
? 'bg-[var(--lokvis-primary)]/15 text-[var(--lokvis-primary)]'
|
|
116
|
+
: 'text-[var(--lokvis-fg-muted)] hover:bg-[var(--lokvis-surface-muted)]'
|
|
117
|
+
}`}
|
|
118
|
+
>
|
|
119
|
+
Compare
|
|
120
|
+
</button>
|
|
121
|
+
</div>
|
|
122
|
+
)}
|
|
123
123
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
124
|
+
{dragOver && (
|
|
125
|
+
<div className="absolute inset-4 z-10 rounded-xl border-2 border-dashed border-[var(--lokvis-primary)] bg-[var(--lokvis-primary)]/10/80 flex items-center justify-center">
|
|
126
|
+
<div className="text-center">
|
|
127
|
+
<Icon size={32} className="mx-auto text-[var(--lokvis-primary)]" strokeWidth={1.5}><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.5m-13.5-9L12 3m0 0 4.5 4.5M12 3v13.5" /></Icon>
|
|
128
|
+
<p className="mt-2 text-sm font-medium text-[var(--lokvis-primary)]">Drop files to import</p>
|
|
129
|
+
</div>
|
|
130
|
+
</div>
|
|
131
|
+
)}
|
|
132
132
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
133
|
+
{/* W9.4 Compare 模式:展示 before/after 滑块 */}
|
|
134
|
+
{compareMode && canCompare ? (
|
|
135
|
+
<div className="relative flex max-h-[calc(100%-4rem)] max-w-[calc(100%-4rem)] items-center justify-center">
|
|
136
|
+
<CompareSlider className="max-h-[calc(100vh-12rem)] max-w-full" />
|
|
137
|
+
</div>
|
|
138
|
+
) : selected ? (
|
|
139
|
+
preview ? (
|
|
140
|
+
<div className="relative">
|
|
141
|
+
<img
|
|
142
|
+
src={preview}
|
|
143
|
+
alt={selected.metadata.format}
|
|
144
|
+
className="max-h-[calc(100%-4rem)] max-w-[calc(100%-4rem)] rounded-lg object-contain shadow-2xl shadow-black/10 ring-1 ring-black/5"
|
|
145
|
+
/>
|
|
146
|
+
{/* Metadata overlay */}
|
|
147
|
+
<div className="absolute bottom-2 left-2 rounded-lg bg-[var(--lokvis-surface)]/90 px-2.5 py-1 text-[11px] text-[var(--lokvis-fg-muted)] shadow-sm backdrop-blur-sm">
|
|
148
|
+
<div className="flex items-center gap-2">
|
|
149
|
+
<span className="font-medium">{selected.metadata.format.toUpperCase()}</span>
|
|
150
|
+
{selected.metadata.dimensions && (
|
|
151
|
+
<>
|
|
152
|
+
<span className="text-[var(--lokvis-fg-subtle)]">·</span>
|
|
153
|
+
<span>{selected.metadata.dimensions.width} x {selected.metadata.dimensions.height}</span>
|
|
154
|
+
</>
|
|
155
|
+
)}
|
|
156
|
+
<span className="text-[var(--lokvis-fg-subtle)]">·</span>
|
|
157
|
+
<span>{(selected.metadata.size / 1024).toFixed(1)} KB</span>
|
|
158
|
+
</div>
|
|
159
|
+
</div>
|
|
160
|
+
</div>
|
|
161
|
+
) : (
|
|
162
|
+
<div className="flex flex-col items-center gap-3">
|
|
163
|
+
<div className="h-28 w-28 animate-pulse rounded-xl bg-[var(--lokvis-border)]" />
|
|
164
|
+
<span className="text-xs text-[var(--lokvis-fg-subtle)]">Loading preview...</span>
|
|
165
|
+
</div>
|
|
166
|
+
)
|
|
167
|
+
) : (
|
|
168
|
+
<div className="relative flex flex-col items-center gap-5 px-6 text-center">
|
|
169
|
+
<div className="flex h-20 w-20 items-center justify-center rounded-2xl bg-[var(--lokvis-surface)] shadow-sm ring-1 ring-[var(--lokvis-border)]/80">
|
|
170
|
+
<Icon size={36} className="text-[var(--lokvis-fg-subtle)]" strokeWidth={1.5}>
|
|
171
|
+
<path d="m2.25 15.75 5.159-5.159a2.25 2.25 0 0 1 3.182 0l5.159 5.159m-1.5-1.5 1.409-1.409a2.25 2.25 0 0 1 3.182 0l2.909 2.909m-18 3.75h16.5a1.5 1.5 0 0 0 1.5-1.5V6a1.5 1.5 0 0 0-1.5-1.5H3.75A1.5 1.5 0 0 0 2.25 6v12a1.5 1.5 0 0 0 1.5 1.5Zm10.5-11.25h.008v.008h-.008V8.25Zm.375 0a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0Z" />
|
|
172
|
+
</Icon>
|
|
173
|
+
</div>
|
|
174
|
+
<div>
|
|
175
|
+
<p className="text-sm font-medium text-[var(--lokvis-fg-muted)]">No asset selected</p>
|
|
176
|
+
<p className="mt-1.5 text-xs text-[var(--lokvis-fg-subtle)]">
|
|
177
|
+
{assets.length === 0
|
|
178
|
+
? 'Import files to get started, or drag them here'
|
|
179
|
+
: 'Select an asset from the left panel'}
|
|
180
|
+
</p>
|
|
181
|
+
</div>
|
|
182
|
+
{assets.length === 0 && (
|
|
183
|
+
<label className="mt-2 inline-flex items-center gap-1.5 rounded-lg bg-[var(--lokvis-primary-hover)] px-4 py-2 text-xs font-medium text-[var(--lokvis-primary-fg)] shadow-sm cursor-pointer transition-colors hover:bg-[var(--lokvis-primary)]">
|
|
184
|
+
<Icon size={14}><path d="M12 4.5v15m7.5-7.5h-15" /></Icon>
|
|
185
|
+
Import Files
|
|
186
|
+
<input
|
|
187
|
+
type="file"
|
|
188
|
+
multiple
|
|
189
|
+
className="hidden"
|
|
190
|
+
onChange={(e) => {
|
|
191
|
+
if (e.target.files) void importFiles(Array.from(e.target.files));
|
|
192
|
+
}}
|
|
193
|
+
/>
|
|
194
|
+
</label>
|
|
195
|
+
)}
|
|
196
|
+
</div>
|
|
197
|
+
)}
|
|
198
|
+
</div>
|
|
199
|
+
</main>
|
|
200
|
+
);
|
|
201
201
|
}
|