@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.
- package/dist/components/AssetPanel.d.ts.map +1 -1
- package/dist/components/AssetPanel.js +18 -62
- 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 +9 -9
- 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 +9 -8
- package/dist/components/CompareSlider.d.ts.map +1 -1
- package/dist/components/CompareSlider.js +10 -9
- 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 +10 -17
- 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/ErrorBoundary.d.ts +40 -0
- package/dist/components/ErrorBoundary.d.ts.map +1 -0
- package/dist/components/ErrorBoundary.js +52 -0
- package/dist/components/ErrorBoundary.js.map +1 -0
- 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 +15 -8
- 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 +13 -10
- package/dist/components/Inspector.js.map +1 -1
- package/dist/components/ParamForm.d.ts.map +1 -1
- package/dist/components/ParamForm.js +10 -7
- package/dist/components/ParamForm.js.map +1 -1
- package/dist/components/PipelineBar.d.ts.map +1 -1
- package/dist/components/PipelineBar.js +19 -28
- 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 +12 -21
- package/dist/components/StatusBar.js.map +1 -1
- package/dist/components/StatusDot.d.ts +12 -0
- package/dist/components/StatusDot.d.ts.map +1 -0
- package/dist/components/StatusDot.js +15 -0
- package/dist/components/StatusDot.js.map +1 -0
- 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/ToolRunner.d.ts +53 -0
- package/dist/components/ToolRunner.d.ts.map +1 -0
- package/dist/components/ToolRunner.js +142 -0
- package/dist/components/ToolRunner.js.map +1 -0
- package/dist/components/Toolbar.d.ts.map +1 -1
- package/dist/components/Toolbar.js +3 -3
- 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 +40 -49
- 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 +17 -7
- package/dist/components/WorkflowTemplates.js.map +1 -1
- package/dist/components/Workspace.d.ts +45 -23
- package/dist/components/Workspace.d.ts.map +1 -1
- package/dist/components/Workspace.js +87 -40
- package/dist/components/Workspace.js.map +1 -1
- package/dist/hooks/useCustomPresets.d.ts +98 -0
- package/dist/hooks/useCustomPresets.d.ts.map +1 -0
- package/dist/hooks/useCustomPresets.js +210 -0
- package/dist/hooks/useCustomPresets.js.map +1 -0
- package/dist/hooks/useLokvis.d.ts +19 -1
- package/dist/hooks/useLokvis.d.ts.map +1 -1
- package/dist/hooks/useLokvis.js +30 -9
- package/dist/hooks/useLokvis.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/store/assets-slice.d.ts +8 -1
- package/dist/store/assets-slice.d.ts.map +1 -1
- package/dist/store/assets-slice.js +101 -54
- package/dist/store/assets-slice.js.map +1 -1
- package/dist/store/capabilities-slice.d.ts +1 -1
- package/dist/store/capabilities-slice.d.ts.map +1 -1
- package/dist/store/capabilities-slice.js +8 -1
- package/dist/store/capabilities-slice.js.map +1 -1
- package/dist/store/index.d.ts +1 -1
- package/dist/store/index.d.ts.map +1 -1
- package/dist/store/index.js +1 -1
- package/dist/store/index.js.map +1 -1
- package/dist/store/runtime-slice.d.ts.map +1 -1
- package/dist/store/runtime-slice.js +22 -16
- package/dist/store/runtime-slice.js.map +1 -1
- package/dist/store/subscribe-utils.d.ts +45 -0
- package/dist/store/subscribe-utils.d.ts.map +1 -0
- package/dist/store/subscribe-utils.js +69 -0
- package/dist/store/subscribe-utils.js.map +1 -0
- package/dist/store/types.d.ts +19 -11
- package/dist/store/types.d.ts.map +1 -1
- package/dist/store/types.js +0 -10
- package/dist/store/types.js.map +1 -1
- package/dist/store/workflow-slice.d.ts +1 -1
- package/dist/store/workflow-slice.d.ts.map +1 -1
- package/dist/store/workflow-slice.js +19 -51
- package/dist/store/workflow-slice.js.map +1 -1
- package/dist/utils.d.ts +24 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +31 -0
- package/dist/utils.js.map +1 -0
- package/package.json +7 -6
- package/src/components/AssetPanel.tsx +224 -264
- package/src/components/Canvas.tsx +178 -174
- package/src/components/CommandPalette.tsx +242 -242
- package/src/components/CompareSlider.tsx +168 -165
- package/src/components/DownloadPanel.tsx +201 -207
- package/src/components/ErrorBanner.tsx +64 -64
- package/src/components/ErrorBoundary.tsx +101 -0
- package/src/components/ExifPanel.tsx +154 -154
- package/src/components/GlobalDropzone.tsx +175 -170
- package/src/components/HistoryPanel.tsx +219 -219
- package/src/components/Inspector.tsx +145 -137
- package/src/components/ParamForm.tsx +140 -137
- package/src/components/PipelineBar.tsx +97 -104
- package/src/components/ProgressBar.tsx +78 -78
- package/src/components/StatusBar.tsx +145 -152
- package/src/components/StatusDot.tsx +27 -0
- package/src/components/ThemeToggle.tsx +98 -98
- package/src/components/ToolRunner.tsx +352 -0
- package/src/components/Toolbar.tsx +65 -65
- package/src/components/WorkflowEditor.tsx +333 -326
- package/src/components/WorkflowTemplates.tsx +101 -78
- package/src/components/Workspace.tsx +310 -223
- package/src/css-modules.d.ts +3 -0
- package/src/hooks/useCustomPresets.ts +284 -0
- package/src/hooks/useLokvis.ts +43 -10
- package/src/index.ts +7 -0
- package/src/store/assets-slice.ts +106 -52
- package/src/store/capabilities-slice.ts +10 -2
- package/src/store/index.ts +1 -1
- package/src/store/runtime-slice.ts +20 -15
- package/src/store/subscribe-utils.ts +82 -0
- package/src/store/types.ts +19 -12
- package/src/store/workflow-slice.ts +19 -53
- package/src/utils.ts +41 -0
- package/dist/store.d.ts +0 -76
- package/dist/store.d.ts.map +0 -1
- package/dist/store.js +0 -217
- package/dist/store.js.map +0 -1
|
@@ -2,30 +2,30 @@
|
|
|
2
2
|
* Workspace - 完整工作台组件
|
|
3
3
|
*
|
|
4
4
|
* W9 布局:
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
5
|
+
* ┌──────────────────────────────────────────────┐
|
|
6
|
+
* │ Toolbar │
|
|
7
|
+
* ├──────┬──────────────┬───────────────────────┤
|
|
8
|
+
* │Asset │ Canvas │ Inspector │
|
|
9
|
+
* │Panel │ (center) │ (right) │
|
|
10
|
+
* ├──────┴──────────────┴───────────────────────┤
|
|
11
|
+
* │ PipelineBar │
|
|
12
|
+
* ├──────────────────────────────────────────────┤
|
|
13
|
+
* │ HistoryPanel (horizontal, W9.1) │
|
|
14
|
+
* ├──────────────────────────────────────────────┤
|
|
15
|
+
* │ DownloadPanel (W9.5,条件渲染) │
|
|
16
|
+
* ├──────────────────────────────────────────────┤
|
|
17
|
+
* │ StatusBar │
|
|
18
|
+
* └──────────────────────────────────────────────┘
|
|
19
19
|
*
|
|
20
20
|
* W9 集成:
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
21
|
+
* - 9.1 HistoryPanel 移到底部(horizontal variant)
|
|
22
|
+
* - 9.2 CommandPalette(⌘K)挂载在 Workspace 内
|
|
23
|
+
* - 9.3 GlobalDropzone 全屏拖拽 + MIME 校验
|
|
24
|
+
* - 9.4 Canvas 内置 CompareSlider(before/after)
|
|
25
|
+
* - 9.5 DownloadPanel 在 HistoryPanel 下方,有 outputs 时显示
|
|
26
|
+
* - 9.6 StatusBar 已增强(当前工具/进度/在线)
|
|
27
|
+
* - 9.7 ThemeToggle 通过 Toolbar rightExtra 挂载
|
|
28
|
+
* - 9.8 响应式:移动端折叠为 drawer 模式
|
|
29
29
|
*
|
|
30
30
|
* 通过 useLokvis 自动初始化 Runtime 与插件。
|
|
31
31
|
*
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
*/
|
|
40
40
|
|
|
41
41
|
import * as React from 'react';
|
|
42
|
-
import { Icon } from '@lokvis/ui-core';
|
|
42
|
+
import { ConfirmDialog, Icon } from '@lokvis/ui-core';
|
|
43
43
|
import { useLokvis, type UseLokvisOptions } from '../hooks/useLokvis.js';
|
|
44
44
|
import { useBreakpoints } from '../hooks/useMediaQuery.js';
|
|
45
45
|
import { Toolbar } from './Toolbar.js';
|
|
@@ -56,233 +56,320 @@ import { DownloadPanel } from './DownloadPanel.js';
|
|
|
56
56
|
import { CommandPalette, useCommandPalette } from './CommandPalette.js';
|
|
57
57
|
import { GlobalDropzone } from './GlobalDropzone.js';
|
|
58
58
|
import { ThemeToggle } from './ThemeToggle.js';
|
|
59
|
+
import { ErrorBoundary } from './ErrorBoundary.js';
|
|
59
60
|
import { useShareLink } from '../hooks/useShareLink.js';
|
|
60
61
|
import { useWorkspaceStore } from '../store/index.js';
|
|
61
62
|
|
|
62
63
|
export interface WorkspaceProps extends UseLokvisOptions {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
64
|
+
/** 顶部标题 */
|
|
65
|
+
title?: string;
|
|
66
|
+
/** 是否显示状态栏(默认 true) */
|
|
67
|
+
showStatusBar?: boolean;
|
|
68
|
+
/** 是否显示历史面板(默认 true,W7.1) */
|
|
69
|
+
showHistoryPanel?: boolean;
|
|
70
|
+
/** 是否启用全屏拖拽导入(默认 true,W9.3) */
|
|
71
|
+
enableGlobalDropzone?: boolean;
|
|
72
|
+
/** 是否启用 Command Palette ⌘K(默认 true,W9.2) */
|
|
73
|
+
enableCommandPalette?: boolean;
|
|
74
|
+
/** 是否启用 ThemeToggle(默认 true,W9.7) */
|
|
75
|
+
enableThemeToggle?: boolean;
|
|
76
|
+
/** 是否启用 CompareSlider(默认 true,W9.4) */
|
|
77
|
+
enableCompare?: boolean;
|
|
78
|
+
/** 是否启用 DownloadPanel(默认 true,W9.5) */
|
|
79
|
+
enableDownloadPanel?: boolean;
|
|
80
|
+
/** 是否启用拖拽式 WorkflowEditor(默认 false,W10.4;关闭则用只读 PipelineBar) */
|
|
81
|
+
enableWorkflowEditor?: boolean;
|
|
82
|
+
/** 是否启用 ProgressBar + Cancel 按钮(默认 true,W11.6) */
|
|
83
|
+
enableProgressBar?: boolean;
|
|
84
|
+
/** 是否启用 ErrorBanner 错误信息横幅(默认 true,W11.3) */
|
|
85
|
+
enableErrorBanner?: boolean;
|
|
86
|
+
/** 是否从 URL ?workflow= 参数加载分享工作流(默认 true,W11.5) */
|
|
87
|
+
enableShareLink?: boolean;
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* 初始化时自动导入的资产列表。
|
|
91
|
+
* Runtime 就绪后,通过 runtime.importAsset() 逐个导入。
|
|
92
|
+
*/
|
|
93
|
+
initialAssets?: Array<{ blob: Blob; name: string }>;
|
|
94
|
+
/**
|
|
95
|
+
* 初始化时预选的能力名称(自动添加到工作流节点)。
|
|
96
|
+
*/
|
|
97
|
+
initialCapability?: string;
|
|
98
|
+
/**
|
|
99
|
+
* 初始化时预填的参数(与 initialCapability 配合使用)。
|
|
100
|
+
*/
|
|
101
|
+
initialParams?: Record<string, unknown>;
|
|
102
|
+
/**
|
|
103
|
+
* 布局模式:
|
|
104
|
+
* - 'full': 完整三栏布局(默认)
|
|
105
|
+
* - 'focused': 精简模式,隐藏 AssetPanel 和 PipelineBar,适合单工具页跳转进入
|
|
106
|
+
*/
|
|
107
|
+
mode?: 'full' | 'focused';
|
|
108
|
+
|
|
109
|
+
className?: string;
|
|
88
110
|
}
|
|
89
111
|
|
|
90
112
|
/** W9.8 移动端抽屉切换状态 */
|
|
91
113
|
type MobilePanel = 'asset' | 'inspector' | null;
|
|
92
114
|
|
|
93
115
|
export function Workspace({
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
116
|
+
title,
|
|
117
|
+
showStatusBar = true,
|
|
118
|
+
showHistoryPanel = true,
|
|
119
|
+
enableGlobalDropzone = true,
|
|
120
|
+
enableCommandPalette = true,
|
|
121
|
+
enableThemeToggle = true,
|
|
122
|
+
enableCompare = true,
|
|
123
|
+
enableDownloadPanel = true,
|
|
124
|
+
enableWorkflowEditor = false,
|
|
125
|
+
enableProgressBar = true,
|
|
126
|
+
enableErrorBanner = true,
|
|
127
|
+
enableShareLink = true,
|
|
128
|
+
initialAssets,
|
|
129
|
+
initialCapability,
|
|
130
|
+
initialParams,
|
|
131
|
+
mode = 'full',
|
|
132
|
+
className = '',
|
|
133
|
+
...lokvisOptions
|
|
108
134
|
}: WorkspaceProps) {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
135
|
+
const { status, error } = useLokvis(lokvisOptions);
|
|
136
|
+
const { isMobile } = useBreakpoints();
|
|
137
|
+
const [paletteOpen, setPaletteOpen] = useCommandPalette({ enabled: enableCommandPalette });
|
|
138
|
+
// W9.8 移动端抽屉:Asset / Inspector 切换显示
|
|
139
|
+
const [mobilePanel, setMobilePanel] = React.useState<MobilePanel>(null);
|
|
140
|
+
|
|
141
|
+
// W11.5: 从 URL ?workflow= 参数加载分享工作流(runtime 就绪后执行一次)
|
|
142
|
+
// 保护:若用户已通过其它路径(如 localStorage 恢复或手动添加)有 nodes,
|
|
143
|
+
// 不静默替换 —— 用 ConfirmDialog 让用户显式选择,避免丢失未保存工作。
|
|
144
|
+
// 注:不用 useWorkspaceStore 订阅 nodes.length 做 effect 依赖
|
|
145
|
+
// (review 反馈:那样 nodesLength 变化会触发 effect 重跑,而 ref 已 true
|
|
146
|
+
// 时重跑无意义,且语义上 effect 只应在 status 切换时跑一次)。
|
|
147
|
+
// 改用 getState() 在体内读取最新值,既不订阅也不进 deps。
|
|
148
|
+
const { loadFromCurrentUrl } = useShareLink();
|
|
149
|
+
const shareLoadedRef = React.useRef(false);
|
|
150
|
+
// 待确认的分享链接替换(nodes>0 时需用户确认才替换)
|
|
151
|
+
const [shareConfirmOpen, setShareConfirmOpen] = React.useState(false);
|
|
152
|
+
const [shareConfirmNodes, setShareConfirmNodes] = React.useState(0);
|
|
153
|
+
React.useEffect(() => {
|
|
154
|
+
if (enableShareLink && !shareLoadedRef.current && status !== 'initializing' && status !== 'error') {
|
|
155
|
+
shareLoadedRef.current = true;
|
|
156
|
+
// 仅在 URL 含 ?workflow= 参数时才提示(避免无谓弹窗)
|
|
157
|
+
const hasShareParam = typeof window !== 'undefined'
|
|
158
|
+
&& new URLSearchParams(window.location.search).has('workflow');
|
|
159
|
+
if (!hasShareParam) return;
|
|
160
|
+
// 读取当前 nodes 长度(最新值,非订阅快照)
|
|
161
|
+
const currentNodesLength = useWorkspaceStore.getState().nodes.length;
|
|
162
|
+
if (currentNodesLength > 0) {
|
|
163
|
+
setShareConfirmNodes(currentNodesLength);
|
|
164
|
+
setShareConfirmOpen(true);
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
loadFromCurrentUrl();
|
|
168
|
+
}
|
|
169
|
+
}, [enableShareLink, status, loadFromCurrentUrl]);
|
|
114
170
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
`检测到分享工作流链接,但当前已有 ${currentNodesLength} 个节点。是否替换为分享的工作流?`
|
|
136
|
-
);
|
|
137
|
-
if (!ok) return;
|
|
138
|
-
}
|
|
139
|
-
loadFromCurrentUrl();
|
|
171
|
+
// 初始化 props:runtime 就绪后导入资产、添加节点、填充参数
|
|
172
|
+
const initDoneRef = React.useRef(false);
|
|
173
|
+
React.useEffect(() => {
|
|
174
|
+
if (initDoneRef.current || status !== 'ready') return;
|
|
175
|
+
const store = useWorkspaceStore.getState();
|
|
176
|
+
if (!store.runtime) return;
|
|
177
|
+
initDoneRef.current = true;
|
|
178
|
+
|
|
179
|
+
(async () => {
|
|
180
|
+
// 导入初始资产
|
|
181
|
+
if (initialAssets?.length) {
|
|
182
|
+
for (const { blob, name } of initialAssets) {
|
|
183
|
+
const file = new File([blob], name, { type: blob.type });
|
|
184
|
+
await store.runtime!.importAsset({ kind: 'file', file });
|
|
185
|
+
}
|
|
186
|
+
await store.refreshAssets();
|
|
187
|
+
// 自动选中第一个资产
|
|
188
|
+
const assets = store.assets;
|
|
189
|
+
if (assets.length > 0) {
|
|
190
|
+
store.selectAsset(assets[0]!.id);
|
|
140
191
|
}
|
|
141
|
-
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// 添加初始能力节点并填充参数
|
|
195
|
+
if (initialCapability) {
|
|
196
|
+
store.addNode(initialCapability);
|
|
197
|
+
if (initialParams) {
|
|
198
|
+
const nodes = useWorkspaceStore.getState().nodes;
|
|
199
|
+
const lastNode = nodes[nodes.length - 1];
|
|
200
|
+
if (lastNode) {
|
|
201
|
+
store.updateNodeParams(lastNode.id, initialParams);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
})();
|
|
206
|
+
}, [status, initialAssets, initialCapability, initialParams]);
|
|
207
|
+
|
|
208
|
+
const isFocused = mode === 'focused';
|
|
209
|
+
|
|
210
|
+
if (status === 'initializing') {
|
|
211
|
+
return (
|
|
212
|
+
<div className="flex h-full items-center justify-center">
|
|
213
|
+
<div className="flex flex-col items-center gap-4">
|
|
214
|
+
<div className="relative flex h-12 w-12 items-center justify-center">
|
|
215
|
+
<div className="absolute inset-0 rounded-full border-2 border-[var(--lokvis-primary)]/40" />
|
|
216
|
+
<div className="h-8 w-8 animate-spin rounded-full border-2 border-transparent border-t-[var(--lokvis-primary)]" />
|
|
217
|
+
</div>
|
|
218
|
+
<div className="text-center">
|
|
219
|
+
<p className="text-sm font-medium text-[var(--lokvis-fg-muted)]">Initializing Runtime</p>
|
|
220
|
+
<p className="mt-1 text-xs text-[var(--lokvis-fg-subtle)]">Loading plugins and capabilities...</p>
|
|
221
|
+
</div>
|
|
222
|
+
</div>
|
|
223
|
+
</div>
|
|
224
|
+
);
|
|
225
|
+
}
|
|
142
226
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
</div>
|
|
157
|
-
);
|
|
158
|
-
}
|
|
227
|
+
if (status === 'error') {
|
|
228
|
+
return (
|
|
229
|
+
<div className="flex h-full items-center justify-center px-6">
|
|
230
|
+
<div className="max-w-md rounded-xl border border-[var(--lokvis-danger)]/30 bg-[var(--lokvis-danger)]/10 p-6 text-center">
|
|
231
|
+
<div className="mb-3 flex h-10 w-10 mx-auto items-center justify-center rounded-full bg-[var(--lokvis-danger)]/15">
|
|
232
|
+
<Icon size={20} className="text-[var(--lokvis-danger)]"><circle cx="12" cy="12" r="10" /><line x1="12" y1="8" x2="12" y2="12" /><line x1="12" y1="16" x2="12.01" y2="16" /></Icon>
|
|
233
|
+
</div>
|
|
234
|
+
<p className="font-semibold text-[var(--lokvis-danger)]">Failed to initialize</p>
|
|
235
|
+
<p className="mt-1.5 text-sm text-[var(--lokvis-danger)]/80">{error}</p>
|
|
236
|
+
</div>
|
|
237
|
+
</div>
|
|
238
|
+
);
|
|
239
|
+
}
|
|
159
240
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
241
|
+
// W9.7 Toolbar rightExtra: ThemeToggle + ⌘K hint
|
|
242
|
+
const toolbarRight = (
|
|
243
|
+
<>
|
|
244
|
+
{enableCommandPalette && (
|
|
245
|
+
<button
|
|
246
|
+
type="button"
|
|
247
|
+
onClick={() => setPaletteOpen(true)}
|
|
248
|
+
aria-label="Open command palette"
|
|
249
|
+
title="Command palette (⌘K)"
|
|
250
|
+
className="flex h-7 items-center gap-1 rounded-md border border-[var(--lokvis-border)] px-1.5 text-[10px] text-[var(--lokvis-fg-muted)] transition-colors hover:bg-[var(--lokvis-surface-muted)] hover:text-[var(--lokvis-fg-muted)]"
|
|
251
|
+
>
|
|
252
|
+
<Icon size={11}><circle cx="11" cy="11" r="8" /><path d="m21 21-4.3-4.3" /></Icon>
|
|
253
|
+
<kbd className="font-sans">⌘K</kbd>
|
|
254
|
+
</button>
|
|
255
|
+
)}
|
|
256
|
+
{enableThemeToggle && <ThemeToggle />}
|
|
257
|
+
{/* W9.8 移动端面板切换按钮 */}
|
|
258
|
+
{isMobile && (
|
|
259
|
+
<>
|
|
260
|
+
<button
|
|
261
|
+
type="button"
|
|
262
|
+
onClick={() => setMobilePanel(mobilePanel === 'asset' ? null : 'asset')}
|
|
263
|
+
aria-label="Toggle asset panel"
|
|
264
|
+
aria-pressed={mobilePanel === 'asset'}
|
|
265
|
+
className="flex h-7 w-7 items-center justify-center rounded-md text-[var(--lokvis-fg-muted)] hover:bg-[var(--lokvis-surface-muted)] hover:text-[var(--lokvis-fg-muted)]"
|
|
266
|
+
>
|
|
267
|
+
<Icon size={14}><rect x="3" y="3" width="7" height="18" rx="1" /><path d="M14 3h7v18h-7z" opacity="0.3" /></Icon>
|
|
268
|
+
</button>
|
|
269
|
+
<button
|
|
270
|
+
type="button"
|
|
271
|
+
onClick={() => setMobilePanel(mobilePanel === 'inspector' ? null : 'inspector')}
|
|
272
|
+
aria-label="Toggle inspector panel"
|
|
273
|
+
aria-pressed={mobilePanel === 'inspector'}
|
|
274
|
+
className="flex h-7 w-7 items-center justify-center rounded-md text-[var(--lokvis-fg-muted)] hover:bg-[var(--lokvis-surface-muted)] hover:text-[var(--lokvis-fg-muted)]"
|
|
275
|
+
>
|
|
276
|
+
<Icon size={14}><path d="M3 3h7v18H3z" opacity="0.3" /><rect x="14" y="3" width="7" height="18" rx="1" /></Icon>
|
|
277
|
+
</button>
|
|
278
|
+
</>
|
|
279
|
+
)}
|
|
280
|
+
</>
|
|
281
|
+
);
|
|
173
282
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
aria-label="Open command palette"
|
|
182
|
-
title="Command palette (⌘K)"
|
|
183
|
-
className="flex h-7 items-center gap-1 rounded-md border border-zinc-200 px-1.5 text-[10px] text-zinc-500 transition-colors hover:bg-zinc-100 hover:text-zinc-700 dark:border-zinc-700 dark:hover:bg-zinc-800 dark:hover:text-zinc-200"
|
|
184
|
-
>
|
|
185
|
-
<Icon size={11}><circle cx="11" cy="11" r="8" /><path d="m21 21-4.3-4.3" /></Icon>
|
|
186
|
-
<kbd className="font-sans">⌘K</kbd>
|
|
187
|
-
</button>
|
|
188
|
-
)}
|
|
189
|
-
{enableThemeToggle && <ThemeToggle />}
|
|
190
|
-
{/* W9.8 移动端面板切换按钮 */}
|
|
191
|
-
{isMobile && (
|
|
192
|
-
<>
|
|
193
|
-
<button
|
|
194
|
-
type="button"
|
|
195
|
-
onClick={() => setMobilePanel(mobilePanel === 'asset' ? null : 'asset')}
|
|
196
|
-
aria-label="Toggle asset panel"
|
|
197
|
-
aria-pressed={mobilePanel === 'asset'}
|
|
198
|
-
className="flex h-7 w-7 items-center justify-center rounded-md text-zinc-500 hover:bg-zinc-100 hover:text-zinc-700 dark:hover:bg-zinc-800 dark:hover:text-zinc-200"
|
|
199
|
-
>
|
|
200
|
-
<Icon size={14}><rect x="3" y="3" width="7" height="18" rx="1" /><path d="M14 3h7v18h-7z" opacity="0.3" /></Icon>
|
|
201
|
-
</button>
|
|
202
|
-
<button
|
|
203
|
-
type="button"
|
|
204
|
-
onClick={() => setMobilePanel(mobilePanel === 'inspector' ? null : 'inspector')}
|
|
205
|
-
aria-label="Toggle inspector panel"
|
|
206
|
-
aria-pressed={mobilePanel === 'inspector'}
|
|
207
|
-
className="flex h-7 w-7 items-center justify-center rounded-md text-zinc-500 hover:bg-zinc-100 hover:text-zinc-700 dark:hover:bg-zinc-800 dark:hover:text-zinc-200"
|
|
208
|
-
>
|
|
209
|
-
<Icon size={14}><path d="M3 3h7v18H3z" opacity="0.3" /><rect x="14" y="3" width="7" height="18" rx="1" /></Icon>
|
|
210
|
-
</button>
|
|
211
|
-
</>
|
|
212
|
-
)}
|
|
213
|
-
</>
|
|
214
|
-
);
|
|
283
|
+
return (
|
|
284
|
+
// D6: ErrorBoundary 捕获子组件渲染异常,避免整个 workspace 白屏。
|
|
285
|
+
// useLokvis 异常不在本组件树内(早于本 return),由消费方在外层包裹处理。
|
|
286
|
+
<ErrorBoundary>
|
|
287
|
+
<div className={`flex h-full flex-col bg-[var(--lokvis-surface)] ${className}`}>
|
|
288
|
+
{/* Top: Toolbar */}
|
|
289
|
+
<Toolbar title={title} rightExtra={toolbarRight} />
|
|
215
290
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
{/* Top: Toolbar */}
|
|
219
|
-
<Toolbar title={title} rightExtra={toolbarRight} />
|
|
291
|
+
{/* W11.3 ErrorBanner (有 error 时显示) */}
|
|
292
|
+
{enableErrorBanner && <ErrorBanner />}
|
|
220
293
|
|
|
221
|
-
|
|
222
|
-
|
|
294
|
+
{/* W9.3 全屏拖拽 */}
|
|
295
|
+
{enableGlobalDropzone && <GlobalDropzone />}
|
|
223
296
|
|
|
224
|
-
|
|
225
|
-
|
|
297
|
+
{/* W9.2 Command Palette */}
|
|
298
|
+
{enableCommandPalette && (
|
|
299
|
+
<CommandPalette open={paletteOpen} onClose={() => setPaletteOpen(false)} />
|
|
300
|
+
)}
|
|
226
301
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
302
|
+
{/* Middle: Panel area */}
|
|
303
|
+
<div className="flex flex-1 overflow-hidden relative">
|
|
304
|
+
{/* W9.8 桌面:三栏并列;移动:Canvas 单独,其他为 overlay drawer */}
|
|
305
|
+
{!isFocused && (
|
|
306
|
+
<div
|
|
307
|
+
className={`${
|
|
308
|
+
isMobile
|
|
309
|
+
? `absolute inset-y-0 left-0 z-20 w-64 max-w-[80vw] transition-transform duration-200 ${
|
|
310
|
+
mobilePanel === 'asset' ? 'translate-x-0' : '-translate-x-full'
|
|
311
|
+
}`
|
|
312
|
+
: 'relative'
|
|
313
|
+
}`}
|
|
314
|
+
>
|
|
315
|
+
<AssetPanel className="h-full" />
|
|
316
|
+
</div>
|
|
317
|
+
)}
|
|
231
318
|
|
|
232
|
-
|
|
233
|
-
<div className="flex flex-1 overflow-hidden relative">
|
|
234
|
-
{/* W9.8 桌面:三栏并列;移动:Canvas 单独,其他为 overlay drawer */}
|
|
235
|
-
<div
|
|
236
|
-
className={`${
|
|
237
|
-
isMobile
|
|
238
|
-
? `absolute inset-y-0 left-0 z-20 w-64 max-w-[80vw] transition-transform duration-200 ${
|
|
239
|
-
mobilePanel === 'asset' ? 'translate-x-0' : '-translate-x-full'
|
|
240
|
-
}`
|
|
241
|
-
: 'relative'
|
|
242
|
-
}`}
|
|
243
|
-
>
|
|
244
|
-
<AssetPanel className="h-full" />
|
|
245
|
-
</div>
|
|
319
|
+
<Canvas enableCompare={enableCompare} />
|
|
246
320
|
|
|
247
|
-
|
|
321
|
+
<div
|
|
322
|
+
className={`${
|
|
323
|
+
isMobile
|
|
324
|
+
? `absolute inset-y-0 right-0 z-20 w-72 max-w-[80vw] transition-transform duration-200 ${
|
|
325
|
+
mobilePanel === 'inspector' ? 'translate-x-0' : 'translate-x-full'
|
|
326
|
+
}`
|
|
327
|
+
: 'relative'
|
|
328
|
+
}`}
|
|
329
|
+
>
|
|
330
|
+
<Inspector className="h-full" />
|
|
331
|
+
</div>
|
|
248
332
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
</div>
|
|
333
|
+
{/* W9.8 移动端遮罩:点击关闭抽屉 */}
|
|
334
|
+
{isMobile && mobilePanel !== null && !isFocused && (
|
|
335
|
+
<button
|
|
336
|
+
type="button"
|
|
337
|
+
aria-label="Close panel"
|
|
338
|
+
onClick={() => setMobilePanel(null)}
|
|
339
|
+
className="absolute inset-0 z-10 bg-[var(--lokvis-overlay)]"
|
|
340
|
+
/>
|
|
341
|
+
)}
|
|
342
|
+
</div>
|
|
260
343
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
<button
|
|
264
|
-
type="button"
|
|
265
|
-
aria-label="Close panel"
|
|
266
|
-
onClick={() => setMobilePanel(null)}
|
|
267
|
-
className="absolute inset-0 z-10 bg-black/30"
|
|
268
|
-
/>
|
|
269
|
-
)}
|
|
270
|
-
</div>
|
|
344
|
+
{/* Pipeline bar */}
|
|
345
|
+
{!isFocused && (enableWorkflowEditor ? <WorkflowEditor /> : <PipelineBar />)}
|
|
271
346
|
|
|
272
|
-
|
|
273
|
-
|
|
347
|
+
{/* W11.6 ProgressBar + Cancel */}
|
|
348
|
+
{enableProgressBar && <ProgressBar />}
|
|
274
349
|
|
|
275
|
-
|
|
276
|
-
|
|
350
|
+
{/* W9.1 History panel (horizontal at bottom) */}
|
|
351
|
+
{showHistoryPanel && <HistoryPanel variant="horizontal" />}
|
|
277
352
|
|
|
278
|
-
|
|
279
|
-
|
|
353
|
+
{/* W9.5 Download panel (条件渲染:有 outputs 时显示) */}
|
|
354
|
+
{enableDownloadPanel && <DownloadPanel />}
|
|
280
355
|
|
|
281
|
-
|
|
282
|
-
|
|
356
|
+
{/* Bottom: StatusBar */}
|
|
357
|
+
{showStatusBar && <StatusBar />}
|
|
283
358
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
359
|
+
{/* W11.5 分享链接替换确认(替代 window.confirm) */}
|
|
360
|
+
<ConfirmDialog
|
|
361
|
+
open={shareConfirmOpen}
|
|
362
|
+
title="加载分享工作流"
|
|
363
|
+
message={`检测到分享工作流链接,但当前已有 ${shareConfirmNodes} 个节点。是否替换为分享的工作流?`}
|
|
364
|
+
confirmText="替换"
|
|
365
|
+
variant="danger"
|
|
366
|
+
onConfirm={() => {
|
|
367
|
+
setShareConfirmOpen(false);
|
|
368
|
+
loadFromCurrentUrl();
|
|
369
|
+
}}
|
|
370
|
+
onClose={() => setShareConfirmOpen(false)}
|
|
371
|
+
/>
|
|
372
|
+
</div>
|
|
373
|
+
</ErrorBoundary>
|
|
374
|
+
);
|
|
288
375
|
}
|