@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
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StatusDot.js","sourceRoot":"","sources":["../../src/components/StatusDot.tsx"],"names":[],"mappings":";AAWA,MAAM,UAAU,SAAS,CAAC,EAAE,MAAM,EAAkB;IAClD,MAAM,KAAK,GACT,MAAM,KAAK,SAAS;QAClB,CAAC,CAAC,4BAA4B;QAC9B,CAAC,CAAC,MAAM,KAAK,SAAS;YACtB,CAAC,CAAC,4BAA4B;YAC9B,CAAC,CAAC,MAAM,KAAK,QAAQ;gBACrB,CAAC,CAAC,2BAA2B;gBAC7B,CAAC,CAAC,MAAM,KAAK,SAAS;oBACtB,CAAC,CAAC,8BAA8B;oBAChC,CAAC,CAAC,8BAA8B,CAAC;IAErC,MAAM,OAAO,GAAG,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,EAAE,CAAC;IAE5D,OAAO,eAAM,SAAS,EAAE,qCAAqC,KAAK,IAAI,OAAO,EAAE,GAAI,CAAC;AACtF,CAAC"}
|
|
@@ -5,10 +5,10 @@
|
|
|
5
5
|
* 与 `tokens.css` 的 `.dark` / `.light` 类双触发机制对齐。
|
|
6
6
|
*
|
|
7
7
|
* 交互:
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
8
|
+
* - 左键单击:在 light / dark 之间切换(基于当前 resolvedTheme 决定下一态)
|
|
9
|
+
* - 右键单击:弹出菜单(light / dark / system 三态)
|
|
10
|
+
* - ESC:关闭菜单
|
|
11
|
+
* - 点击菜单外部:关闭菜单
|
|
12
12
|
*/
|
|
13
13
|
import * as React from 'react';
|
|
14
14
|
export interface ThemeToggleProps {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ThemeToggle.d.ts","sourceRoot":"","sources":["../../src/components/ThemeToggle.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAI/B,MAAM,WAAW,gBAAgB;
|
|
1
|
+
{"version":3,"file":"ThemeToggle.d.ts","sourceRoot":"","sources":["../../src/components/ThemeToggle.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAI/B,MAAM,WAAW,gBAAgB;IAChC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,sBAAsB;IACtB,SAAS,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,wBAAgB,WAAW,CAAC,EAAE,SAAc,EAAE,SAAiB,EAAE,EAAE,gBAAgB,qBAkGlF"}
|
|
@@ -6,10 +6,10 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
6
6
|
* 与 `tokens.css` 的 `.dark` / `.light` 类双触发机制对齐。
|
|
7
7
|
*
|
|
8
8
|
* 交互:
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
9
|
+
* - 左键单击:在 light / dark 之间切换(基于当前 resolvedTheme 决定下一态)
|
|
10
|
+
* - 右键单击:弹出菜单(light / dark / system 三态)
|
|
11
|
+
* - ESC:关闭菜单
|
|
12
|
+
* - 点击菜单外部:关闭菜单
|
|
13
13
|
*/
|
|
14
14
|
import * as React from 'react';
|
|
15
15
|
import { Icon } from '@lokvis/ui-core';
|
|
@@ -46,15 +46,15 @@ export function ThemeToggle({ className = '', showLabel = false }) {
|
|
|
46
46
|
return (_jsxs("div", { className: `relative ${className}`, children: [_jsxs("button", { ref: btnRef, type: "button", "aria-haspopup": "menu", "aria-expanded": menuOpen, onClick: () => toggleTheme(), onContextMenu: (e) => {
|
|
47
47
|
e.preventDefault();
|
|
48
48
|
setMenuOpen((v) => !v);
|
|
49
|
-
}, "aria-label": `Theme: ${label}. Click to toggle, right-click for options.`, title: `Theme: ${label} (right-click for options)`, className: "flex h-8 w-8 items-center justify-center rounded-md text-
|
|
49
|
+
}, "aria-label": `Theme: ${label}. Click to toggle, right-click for options.`, title: `Theme: ${label} (right-click for options)`, className: "flex h-8 w-8 items-center justify-center rounded-md text-[var(--lokvis-fg-muted)] transition-colors hover:bg-[var(--lokvis-surface-muted)] hover:text-[var(--lokvis-fg-muted)]", children: [icon, showLabel && _jsx("span", { className: "ml-1 text-xs", children: label })] }), menuOpen && (_jsx("div", { role: "menu", className: "absolute right-0 top-full z-50 mt-1 w-32 overflow-hidden rounded-md border border-[var(--lokvis-border)] bg-[var(--lokvis-surface)] py-1 shadow-[var(--lokvis-elevation-2)]", children: ['light', 'dark', null].map((mode) => {
|
|
50
50
|
const isActive = theme === mode;
|
|
51
51
|
const text = mode === null ? 'System' : mode === 'dark' ? 'Dark' : 'Light';
|
|
52
52
|
return (_jsxs("button", { type: "button", role: "menuitemradio", "aria-checked": isActive, onClick: () => {
|
|
53
53
|
setTheme(mode);
|
|
54
54
|
setMenuOpen(false);
|
|
55
55
|
}, className: `flex w-full items-center justify-between px-3 py-1.5 text-left text-xs transition-colors ${isActive
|
|
56
|
-
? 'bg-
|
|
57
|
-
: 'text-
|
|
56
|
+
? 'bg-[var(--lokvis-primary)]/10 text-[var(--lokvis-primary)]'
|
|
57
|
+
: 'text-[var(--lokvis-fg-muted)] hover:bg-[var(--lokvis-surface-muted)]'}`, children: [_jsx("span", { children: text }), isActive && (_jsx(Icon, { size: 12, strokeWidth: 3, children: _jsx("path", { d: "M5 13l4 4L19 7" }) }))] }, mode ?? 'system'));
|
|
58
58
|
}) }))] }));
|
|
59
59
|
}
|
|
60
60
|
//# sourceMappingURL=ThemeToggle.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ThemeToggle.js","sourceRoot":"","sources":["../../src/components/ThemeToggle.tsx"],"names":[],"mappings":";AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AACvC,OAAO,EAAE,QAAQ,EAAkB,MAAM,sBAAsB,CAAC;AAQhE,MAAM,UAAU,WAAW,CAAC,EAAE,SAAS,GAAG,EAAE,EAAE,SAAS,GAAG,KAAK,EAAoB;
|
|
1
|
+
{"version":3,"file":"ThemeToggle.js","sourceRoot":"","sources":["../../src/components/ThemeToggle.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ToolRunner - 单工具页组合组件
|
|
3
|
+
*
|
|
4
|
+
* 为单工具操作场景(压缩、裁剪、格式转换等)提供一站式 UI:
|
|
5
|
+
* 上传 → 调参 → 处理 → 对比 → 下载。
|
|
6
|
+
*
|
|
7
|
+
* 不依赖 Workspace Store(zustand),通过 engine prop 注入处理函数,
|
|
8
|
+
* 可在任何 React 应用中独立使用。
|
|
9
|
+
*
|
|
10
|
+
* 五层架构:UI 层不直接依赖 Engine 层,engine 函数由消费方传入。
|
|
11
|
+
* 消费方需自行实现按 capability.name 路由到对应 Blob↔Blob 操作的 wrapper
|
|
12
|
+
* (engine-image 已不再提供 runTool 统一入口,各 operation 需直接组合调用)。
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```tsx
|
|
16
|
+
* import { ToolRunner } from '@lokvis/ui-react';
|
|
17
|
+
* import { IMAGE_COMPRESS, compress } from '@lokvis/engine-image';
|
|
18
|
+
*
|
|
19
|
+
* <ToolRunner
|
|
20
|
+
* capability={IMAGE_COMPRESS}
|
|
21
|
+
* engine={(name, input, params, opts) => compress(input, params, opts?.signal)}
|
|
22
|
+
* onOpenInWorkspace={(state) => { ... }}
|
|
23
|
+
* />
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
import * as React from 'react';
|
|
27
|
+
import type { Capability } from '@lokvis/schema';
|
|
28
|
+
type EngineFn = (toolName: string, input: Blob, params: Record<string, unknown>, options?: {
|
|
29
|
+
signal?: AbortSignal;
|
|
30
|
+
onProgress?: (p: number) => void;
|
|
31
|
+
}) => Promise<Blob>;
|
|
32
|
+
export interface ToolRunnerProps {
|
|
33
|
+
/** 工具能力定义(决定参数表单和输入类型) */
|
|
34
|
+
capability: Capability;
|
|
35
|
+
/** 处理函数(按 capability.name 路由到对应 Blob↔Blob 操作的 wrapper) */
|
|
36
|
+
engine: EngineFn;
|
|
37
|
+
/** 在 Workspace 中打开的回调(携带当前状态) */
|
|
38
|
+
onOpenInWorkspace?: (state: {
|
|
39
|
+
inputBlob: Blob;
|
|
40
|
+
outputBlob: Blob | null;
|
|
41
|
+
params: Record<string, unknown>;
|
|
42
|
+
}) => void;
|
|
43
|
+
className?: string;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* ToolRunner - 单工具操作组件。
|
|
47
|
+
*
|
|
48
|
+
* 内部组合:上传区 + 参数表单 + 预览/对比 + 进度 + 下载。
|
|
49
|
+
* 所有状态自管理,不依赖外部 store。
|
|
50
|
+
*/
|
|
51
|
+
export declare function ToolRunner({ capability, engine, onOpenInWorkspace, className, }: ToolRunnerProps): React.JSX.Element;
|
|
52
|
+
export {};
|
|
53
|
+
//# sourceMappingURL=ToolRunner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ToolRunner.d.ts","sourceRoot":"","sources":["../../src/components/ToolRunner.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAIjD,KAAK,QAAQ,GAAG,CACf,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,IAAI,EACX,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,OAAO,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,WAAW,CAAC;IAAC,UAAU,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;CAAE,KAChE,OAAO,CAAC,IAAI,CAAC,CAAC;AAEnB,MAAM,WAAW,eAAe;IAC/B,0BAA0B;IAC1B,UAAU,EAAE,UAAU,CAAC;IACvB,0DAA0D;IACzD,MAAM,EAAE,QAAQ,CAAC;IAClB,iCAAiC;IACjC,iBAAiB,CAAC,EAAE,CAAC,KAAK,EAAE;QAC3B,SAAS,EAAE,IAAI,CAAC;QAChB,UAAU,EAAE,IAAI,GAAG,IAAI,CAAC;QACxB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KAChC,KAAK,IAAI,CAAC;IACX,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,EAC1B,UAAU,EACV,MAAM,EACN,iBAAiB,EACjB,SAAc,GACd,EAAE,eAAe,qBAgSjB"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* ToolRunner - 单工具页组合组件
|
|
4
|
+
*
|
|
5
|
+
* 为单工具操作场景(压缩、裁剪、格式转换等)提供一站式 UI:
|
|
6
|
+
* 上传 → 调参 → 处理 → 对比 → 下载。
|
|
7
|
+
*
|
|
8
|
+
* 不依赖 Workspace Store(zustand),通过 engine prop 注入处理函数,
|
|
9
|
+
* 可在任何 React 应用中独立使用。
|
|
10
|
+
*
|
|
11
|
+
* 五层架构:UI 层不直接依赖 Engine 层,engine 函数由消费方传入。
|
|
12
|
+
* 消费方需自行实现按 capability.name 路由到对应 Blob↔Blob 操作的 wrapper
|
|
13
|
+
* (engine-image 已不再提供 runTool 统一入口,各 operation 需直接组合调用)。
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```tsx
|
|
17
|
+
* import { ToolRunner } from '@lokvis/ui-react';
|
|
18
|
+
* import { IMAGE_COMPRESS, compress } from '@lokvis/engine-image';
|
|
19
|
+
*
|
|
20
|
+
* <ToolRunner
|
|
21
|
+
* capability={IMAGE_COMPRESS}
|
|
22
|
+
* engine={(name, input, params, opts) => compress(input, params, opts?.signal)}
|
|
23
|
+
* onOpenInWorkspace={(state) => { ... }}
|
|
24
|
+
* />
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
import * as React from 'react';
|
|
28
|
+
import { Button, Card } from '@lokvis/ui-core';
|
|
29
|
+
import { ParamForm } from './ParamForm.js';
|
|
30
|
+
/**
|
|
31
|
+
* ToolRunner - 单工具操作组件。
|
|
32
|
+
*
|
|
33
|
+
* 内部组合:上传区 + 参数表单 + 预览/对比 + 进度 + 下载。
|
|
34
|
+
* 所有状态自管理,不依赖外部 store。
|
|
35
|
+
*/
|
|
36
|
+
export function ToolRunner({ capability, engine, onOpenInWorkspace, className = '', }) {
|
|
37
|
+
const [inputBlob, setInputBlob] = React.useState(null);
|
|
38
|
+
const [inputUrl, setInputUrl] = React.useState(null);
|
|
39
|
+
const [outputBlob, setOutputBlob] = React.useState(null);
|
|
40
|
+
const [outputUrl, setOutputUrl] = React.useState(null);
|
|
41
|
+
const [params, setParams] = React.useState(() => {
|
|
42
|
+
const defaults = {};
|
|
43
|
+
for (const p of capability.params) {
|
|
44
|
+
if (p.default !== undefined)
|
|
45
|
+
defaults[p.name] = p.default;
|
|
46
|
+
}
|
|
47
|
+
return defaults;
|
|
48
|
+
});
|
|
49
|
+
const [progress, setProgress] = React.useState(0);
|
|
50
|
+
const [processing, setProcessing] = React.useState(false);
|
|
51
|
+
const [error, setError] = React.useState(null);
|
|
52
|
+
const [dragging, setDragging] = React.useState(false);
|
|
53
|
+
const abortRef = React.useRef(null);
|
|
54
|
+
// 清理 ObjectURL
|
|
55
|
+
React.useEffect(() => {
|
|
56
|
+
return () => {
|
|
57
|
+
if (inputUrl)
|
|
58
|
+
URL.revokeObjectURL(inputUrl);
|
|
59
|
+
if (outputUrl)
|
|
60
|
+
URL.revokeObjectURL(outputUrl);
|
|
61
|
+
};
|
|
62
|
+
}, [inputUrl, outputUrl]);
|
|
63
|
+
// W21.6: unmount 时 abort 进行中的处理,防止后台泄漏
|
|
64
|
+
// (engine 函数持有 Worker/Canvas 资源,卸载后继续运行是浪费 + 潜在内存泄漏)
|
|
65
|
+
React.useEffect(() => {
|
|
66
|
+
return () => {
|
|
67
|
+
if (abortRef.current) {
|
|
68
|
+
abortRef.current.abort();
|
|
69
|
+
abortRef.current = null;
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
}, []);
|
|
73
|
+
const accept = capability.inputTypes.join(',');
|
|
74
|
+
function handleFiles(files) {
|
|
75
|
+
if (!files || files.length === 0)
|
|
76
|
+
return;
|
|
77
|
+
const file = files[0];
|
|
78
|
+
if (!file)
|
|
79
|
+
return;
|
|
80
|
+
setInputBlob(file);
|
|
81
|
+
if (inputUrl)
|
|
82
|
+
URL.revokeObjectURL(inputUrl);
|
|
83
|
+
setInputUrl(URL.createObjectURL(file));
|
|
84
|
+
// 重置输出
|
|
85
|
+
setOutputBlob(null);
|
|
86
|
+
if (outputUrl) {
|
|
87
|
+
URL.revokeObjectURL(outputUrl);
|
|
88
|
+
setOutputUrl(null);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
async function handleProcess() {
|
|
92
|
+
if (!inputBlob)
|
|
93
|
+
return;
|
|
94
|
+
setProcessing(true);
|
|
95
|
+
setProgress(0);
|
|
96
|
+
setError(null);
|
|
97
|
+
abortRef.current = new AbortController();
|
|
98
|
+
try {
|
|
99
|
+
const result = await engine(capability.name, inputBlob, params, {
|
|
100
|
+
signal: abortRef.current.signal,
|
|
101
|
+
onProgress: setProgress,
|
|
102
|
+
});
|
|
103
|
+
setOutputBlob(result);
|
|
104
|
+
if (outputUrl)
|
|
105
|
+
URL.revokeObjectURL(outputUrl);
|
|
106
|
+
setOutputUrl(URL.createObjectURL(result));
|
|
107
|
+
}
|
|
108
|
+
catch (err) {
|
|
109
|
+
if (err instanceof Error && err.name === 'AbortError')
|
|
110
|
+
return;
|
|
111
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
112
|
+
}
|
|
113
|
+
finally {
|
|
114
|
+
setProcessing(false);
|
|
115
|
+
abortRef.current = null;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
function handleCancel() {
|
|
119
|
+
abortRef.current?.abort();
|
|
120
|
+
}
|
|
121
|
+
const downloadName = outputBlob
|
|
122
|
+
? `output.${outputBlob.type.split('/')[1] ?? 'bin'}`
|
|
123
|
+
: 'output';
|
|
124
|
+
return (_jsxs("div", { className: `flex flex-col gap-6 ${className}`, children: [!inputBlob && (_jsxs("label", { className: `block cursor-pointer rounded-xl border-2 border-dashed p-10 text-center transition-all ${dragging
|
|
125
|
+
? 'border-[var(--lokvis-primary)] bg-[var(--lokvis-primary)]/10'
|
|
126
|
+
: 'border-[var(--lokvis-border)] hover:border-[var(--lokvis-primary)]/50 hover:bg-[var(--lokvis-surface-muted)]'}`, onDragOver: (e) => { e.preventDefault(); setDragging(true); }, onDragLeave: () => setDragging(false), onDrop: (e) => {
|
|
127
|
+
e.preventDefault();
|
|
128
|
+
setDragging(false);
|
|
129
|
+
handleFiles(e.dataTransfer.files);
|
|
130
|
+
}, children: [_jsx("input", { type: "file", accept: accept, className: "hidden", onChange: (e) => handleFiles(e.target.files) }), _jsxs("div", { className: "flex flex-col items-center gap-3", children: [_jsx("div", { className: "flex h-12 w-12 items-center justify-center rounded-xl bg-[var(--lokvis-surface-muted)]", children: _jsxs("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "text-[var(--lokvis-fg-subtle)]", children: [_jsx("path", { d: "M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4" }), _jsx("polyline", { points: "17 8 12 3 7 8" }), _jsx("line", { x1: "12", y1: "3", x2: "12", y2: "15" })] }) }), _jsxs("div", { children: [_jsx("p", { className: "text-sm font-medium text-[var(--lokvis-fg)]", children: "Drop file here or click to upload" }), _jsxs("p", { className: "mt-1 text-xs text-[var(--lokvis-fg-subtle)]", children: ["Accepted: ", accept || 'any file'] })] })] })] })), inputBlob && (_jsxs("div", { className: "grid gap-6 md:grid-cols-2", children: [_jsxs("div", { className: "space-y-4", children: [_jsxs(Card, { children: [_jsxs("div", { className: "mb-3 flex items-center justify-between", children: [_jsx("h3", { className: "text-sm font-semibold text-[var(--lokvis-fg)]", children: "Parameters" }), _jsx("span", { className: "rounded bg-[var(--lokvis-surface-muted)] px-2 py-0.5 font-mono text-xs text-[var(--lokvis-primary)]", children: capability.name })] }), _jsx(ParamForm, { capability: capability, values: params, onChange: setParams })] }), _jsxs("div", { className: "flex gap-3", children: [_jsx(Button, { variant: "primary", size: "md", onClick: handleProcess, loading: processing, disabled: !inputBlob, children: processing ? 'Processing...' : 'Process' }), _jsx(Button, { variant: "secondary", size: "md", onClick: () => {
|
|
131
|
+
setInputBlob(null);
|
|
132
|
+
if (inputUrl)
|
|
133
|
+
URL.revokeObjectURL(inputUrl);
|
|
134
|
+
setInputUrl(null);
|
|
135
|
+
setOutputBlob(null);
|
|
136
|
+
if (outputUrl) {
|
|
137
|
+
URL.revokeObjectURL(outputUrl);
|
|
138
|
+
setOutputUrl(null);
|
|
139
|
+
}
|
|
140
|
+
}, children: "Change file" }), onOpenInWorkspace && (_jsx(Button, { variant: "ghost", size: "md", onClick: () => onOpenInWorkspace({ inputBlob, outputBlob, params }), children: "Open in Workspace \u2192" }))] }), processing && (_jsxs("div", { className: "space-y-2", children: [_jsxs("div", { className: "flex items-center justify-between text-xs text-[var(--lokvis-fg-muted)]", children: [_jsx("span", { children: "Processing..." }), _jsxs("span", { className: "tabular-nums", children: [Math.round(progress), "%"] })] }), _jsx("div", { className: "h-1.5 overflow-hidden rounded-full bg-[var(--lokvis-border)]", children: _jsx("div", { className: "h-full rounded-full bg-[var(--lokvis-primary)] transition-all duration-200", style: { width: `${progress}%` } }) }), _jsx("button", { type: "button", onClick: handleCancel, className: "text-xs text-[var(--lokvis-danger)] hover:underline", children: "Cancel" })] })), error && (_jsx("div", { className: "rounded-lg border border-[var(--lokvis-danger)]/30 bg-[var(--lokvis-danger)]/10 px-4 py-3 text-sm text-[var(--lokvis-danger)]", children: error })), outputBlob && outputUrl && (_jsx(Card, { children: _jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { children: [_jsx("p", { className: "text-sm font-medium text-[var(--lokvis-fg)]", children: "Ready to download" }), _jsxs("p", { className: "mt-1 text-xs text-[var(--lokvis-fg-muted)]", children: [(outputBlob.size / 1024).toFixed(1), " KB \u00B7 ", outputBlob.type.split('/')[1] ?? 'file'] })] }), _jsxs("a", { href: outputUrl, download: downloadName, className: "inline-flex items-center gap-1.5 rounded-lg bg-[var(--lokvis-primary)] px-4 py-2 text-sm font-medium text-[var(--lokvis-primary-fg)] transition-colors hover:bg-[var(--lokvis-primary-hover)]", children: [_jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [_jsx("path", { d: "M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4" }), _jsx("polyline", { points: "7 10 12 15 17 10" }), _jsx("line", { x1: "12", y1: "15", x2: "12", y2: "3" })] }), "Download"] })] }) }))] }), _jsxs("div", { className: "space-y-4", children: [inputUrl && (_jsxs(Card, { children: [_jsx("h4", { className: "mb-2 text-xs font-semibold uppercase tracking-wider text-[var(--lokvis-fg-subtle)]", children: "Input" }), _jsx("img", { src: inputUrl, alt: "Input preview", className: "max-h-80 w-full rounded-lg object-contain" }), _jsxs("p", { className: "mt-2 text-xs text-[var(--lokvis-fg-muted)]", children: [(inputBlob.size / 1024).toFixed(1), " KB \u00B7 ", inputBlob.type.split('/')[1] ?? 'file'] })] })), outputUrl && (_jsxs(Card, { children: [_jsx("h4", { className: "mb-2 text-xs font-semibold uppercase tracking-wider text-[var(--lokvis-fg-subtle)]", children: "Output" }), _jsx("img", { src: outputUrl, alt: "Output preview", className: "max-h-80 w-full rounded-lg object-contain" }), _jsxs("p", { className: "mt-2 text-xs text-[var(--lokvis-fg-muted)]", children: [(outputBlob.size / 1024).toFixed(1), " KB \u00B7 ", outputBlob.type.split('/')[1] ?? 'file'] })] }))] })] }))] }));
|
|
141
|
+
}
|
|
142
|
+
//# sourceMappingURL=ToolRunner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ToolRunner.js","sourceRoot":"","sources":["../../src/components/ToolRunner.tsx"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Toolbar.d.ts","sourceRoot":"","sources":["../../src/components/Toolbar.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAI/B,MAAM,WAAW,YAAY;
|
|
1
|
+
{"version":3,"file":"Toolbar.d.ts","sourceRoot":"","sources":["../../src/components/Toolbar.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAI/B,MAAM,WAAW,YAAY;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC7B;AAED,wBAAgB,OAAO,CAAC,EAAE,KAA0B,EAAE,UAAU,EAAE,EAAE,YAAY,qBAqE/E"}
|
|
@@ -27,10 +27,10 @@ export function Toolbar({ title = 'Lokvis Workspace', rightExtra }) {
|
|
|
27
27
|
setError(err instanceof Error ? err.message : String(err));
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
|
-
return (_jsxs("header", { className: "flex h-
|
|
31
|
-
background: 'linear-gradient(135deg,
|
|
30
|
+
return (_jsxs("header", { className: "flex h-[var(--lokvis-toolbar-h)] shrink-0 items-center justify-between border-b border-[var(--lokvis-border)] bg-[var(--lokvis-surface)] px-4", children: [_jsxs("div", { className: "flex items-center gap-2.5 min-w-0", children: [_jsx("span", { className: "shrink-0 text-base font-bold leading-none", style: {
|
|
31
|
+
background: 'linear-gradient(135deg, var(--lokvis-primary), var(--lokvis-info))',
|
|
32
32
|
WebkitBackgroundClip: 'text',
|
|
33
33
|
WebkitTextFillColor: 'transparent',
|
|
34
|
-
}, children: "\u25C6" }), _jsx("span", { className: "truncate text-sm font-semibold tracking-tight", children: title })] }), _jsx("div", { className: "flex flex-1 items-center justify-center px-4 min-w-0", children: _jsx("span", { className: `truncate text-xs ${error ? 'text-
|
|
34
|
+
}, children: "\u25C6" }), _jsx("span", { className: "truncate text-sm font-semibold tracking-tight", children: title })] }), _jsx("div", { className: "flex flex-1 items-center justify-center px-4 min-w-0", children: _jsx("span", { className: `truncate text-xs ${error ? 'text-[var(--lokvis-danger)]' : 'text-[var(--lokvis-fg-subtle)]'}`, children: statusMessage }) }), _jsxs("div", { className: "flex items-center gap-1.5 shrink-0", children: [rightExtra, nodes.length > 0 && (_jsx(Button, { variant: "ghost", size: "sm", onClick: clearWorkflow, disabled: running, children: "Clear" })), _jsx(Button, { variant: "primary", size: "sm", onClick: handleRun, loading: running, disabled: !canRun, children: "Run" })] })] }));
|
|
35
35
|
}
|
|
36
36
|
//# sourceMappingURL=Toolbar.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Toolbar.js","sourceRoot":"","sources":["../../src/components/Toolbar.tsx"],"names":[],"mappings":";AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AACzC,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAOtD,MAAM,UAAU,OAAO,CAAC,EAAE,KAAK,GAAG,kBAAkB,EAAE,UAAU,EAAgB;
|
|
1
|
+
{"version":3,"file":"Toolbar.js","sourceRoot":"","sources":["../../src/components/Toolbar.tsx"],"names":[],"mappings":";AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AACzC,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAOtD,MAAM,UAAU,OAAO,CAAC,EAAE,KAAK,GAAG,kBAAkB,EAAE,UAAU,EAAgB;IAC/E,MAAM,OAAO,GAAG,iBAAiB,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;IACpD,MAAM,aAAa,GAAG,iBAAiB,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC;IAChE,MAAM,KAAK,GAAG,iBAAiB,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;IAChD,MAAM,KAAK,GAAG,iBAAiB,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;IAChD,MAAM,eAAe,GAAG,iBAAiB,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC;IACpE,MAAM,GAAG,GAAG,iBAAiB,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IAC5C,MAAM,aAAa,GAAG,iBAAiB,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC;IAChE,MAAM,QAAQ,GAAG,iBAAiB,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;IAEtD,MAAM,MAAM,GAAG,CAAC,OAAO,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,CAAC,eAAe,CAAC;IAEjE,KAAK,UAAU,SAAS;QACxB,IAAI,CAAC;YACL,MAAM,GAAG,EAAE,CAAC;QACZ,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACf,QAAQ,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;QAC3D,CAAC;IACD,CAAC;IAED,OAAO,CACP,kBAAQ,SAAS,EAAC,+IAA+I,aAEjK,eAAK,SAAS,EAAC,mCAAmC,aAClD,eACA,SAAS,EAAC,2CAA2C,EACrD,KAAK,EAAE;4BACP,UAAU,EAAE,oEAAoE;4BAChF,oBAAoB,EAAE,MAAM;4BAC5B,mBAAmB,EAAE,aAAa;yBACjC,uBAGM,EACP,eAAM,SAAS,EAAC,+CAA+C,YAAE,KAAK,GAAQ,IACxE,EAGN,cAAK,SAAS,EAAC,sDAAsD,YACrE,eAAM,SAAS,EAAE,oBAAoB,KAAK,CAAC,CAAC,CAAC,6BAA6B,CAAC,CAAC,CAAC,gCAAgC,EAAE,YAC9G,aAAa,GACP,GACD,EAGN,eAAK,SAAS,EAAC,oCAAoC,aAClD,UAAU,EACV,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,CACrB,KAAC,MAAM,IACP,OAAO,EAAC,OAAO,EACf,IAAI,EAAC,IAAI,EACT,OAAO,EAAE,aAAa,EACtB,QAAQ,EAAE,OAAO,sBAGR,CACR,EACD,KAAC,MAAM,IACP,OAAO,EAAC,SAAS,EACjB,IAAI,EAAC,IAAI,EACT,OAAO,EAAE,SAAS,EAClB,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,CAAC,MAAM,oBAGR,IACH,IACG,CACR,CAAC;AACH,CAAC"}
|
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
* WorkflowEditor - 拖拽式工作流编辑器(W10.4)
|
|
3
3
|
*
|
|
4
4
|
* 增强 PipelineBar,提供节点拖拽重排能力:
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
5
|
+
* - 拖拽节点到任意位置重排线性链
|
|
6
|
+
* - 拖拽时显示插入指示器
|
|
7
|
+
* - 键盘支持:选中节点后 ← → 移动(无障碍)
|
|
8
|
+
* - 节点数上限 MAX_WORKFLOW_STEPS=5(M1 MVP 约束,UI 层常量)
|
|
9
9
|
*
|
|
10
10
|
* 与 PipelineBar 区别:
|
|
11
|
-
*
|
|
12
|
-
*
|
|
11
|
+
* - PipelineBar:只读 + 选中/删除(快速预览)
|
|
12
|
+
* - WorkflowEditor:可编辑 + 拖拽重排(编辑模式)
|
|
13
13
|
*
|
|
14
14
|
* 使用原生 HTML5 Drag and Drop API,无额外依赖。
|
|
15
15
|
* 触摸设备降级为按钮上下移动(HTML5 DnD 在移动端支持不佳)。
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WorkflowEditor.d.ts","sourceRoot":"","sources":["../../src/components/WorkflowEditor.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"WorkflowEditor.d.ts","sourceRoot":"","sources":["../../src/components/WorkflowEditor.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAO/B,MAAM,WAAW,mBAAmB;IACnC,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,cAAc,CAAC,EAAE,SAAc,EAAE,EAAE,mBAAmB,qBAmOrE"}
|
|
@@ -3,14 +3,14 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
3
3
|
* WorkflowEditor - 拖拽式工作流编辑器(W10.4)
|
|
4
4
|
*
|
|
5
5
|
* 增强 PipelineBar,提供节点拖拽重排能力:
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
6
|
+
* - 拖拽节点到任意位置重排线性链
|
|
7
|
+
* - 拖拽时显示插入指示器
|
|
8
|
+
* - 键盘支持:选中节点后 ← → 移动(无障碍)
|
|
9
|
+
* - 节点数上限 MAX_WORKFLOW_STEPS=5(M1 MVP 约束,UI 层常量)
|
|
10
10
|
*
|
|
11
11
|
* 与 PipelineBar 区别:
|
|
12
|
-
*
|
|
13
|
-
*
|
|
12
|
+
* - PipelineBar:只读 + 选中/删除(快速预览)
|
|
13
|
+
* - WorkflowEditor:可编辑 + 拖拽重排(编辑模式)
|
|
14
14
|
*
|
|
15
15
|
* 使用原生 HTML5 Drag and Drop API,无额外依赖。
|
|
16
16
|
* 触摸设备降级为按钮上下移动(HTML5 DnD 在移动端支持不佳)。
|
|
@@ -18,8 +18,10 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
18
18
|
* @module WorkflowEditor
|
|
19
19
|
*/
|
|
20
20
|
import * as React from 'react';
|
|
21
|
-
import { Icon } from '@lokvis/ui-core';
|
|
21
|
+
import { ConfirmDialog, Icon, Input } from '@lokvis/ui-core';
|
|
22
22
|
import { useWorkspaceStore, MAX_WORKFLOW_STEPS } from '../store/index.js';
|
|
23
|
+
import { filterCapabilities } from '../utils.js';
|
|
24
|
+
import { StatusDot } from './StatusDot.js';
|
|
23
25
|
export function WorkflowEditor({ className = '' }) {
|
|
24
26
|
const nodes = useWorkspaceStore((s) => s.nodes);
|
|
25
27
|
const selectedNodeId = useWorkspaceStore((s) => s.selectedNodeId);
|
|
@@ -28,10 +30,13 @@ export function WorkflowEditor({ className = '' }) {
|
|
|
28
30
|
const moveNode = useWorkspaceStore((s) => s.moveNode);
|
|
29
31
|
const insertNodeAt = useWorkspaceStore((s) => s.insertNodeAt);
|
|
30
32
|
const capabilities = useWorkspaceStore((s) => s.capabilities);
|
|
33
|
+
const stubCapabilities = useWorkspaceStore((s) => s.stubCapabilities);
|
|
31
34
|
const clearWorkflow = useWorkspaceStore((s) => s.clearWorkflow);
|
|
32
35
|
// 拖拽状态:被拖拽的节点索引 + 当前 hover 的插入位置
|
|
33
36
|
const [dragIndex, setDragIndex] = React.useState(null);
|
|
34
37
|
const [hoverIndex, setHoverIndex] = React.useState(null);
|
|
38
|
+
// 清空确认对话框
|
|
39
|
+
const [clearOpen, setClearOpen] = React.useState(false);
|
|
35
40
|
const handleDragStart = (e, index) => {
|
|
36
41
|
setDragIndex(index);
|
|
37
42
|
e.dataTransfer.effectAllowed = 'move';
|
|
@@ -77,46 +82,29 @@ export function WorkflowEditor({ className = '' }) {
|
|
|
77
82
|
}
|
|
78
83
|
};
|
|
79
84
|
const canAddMore = nodes.length < MAX_WORKFLOW_STEPS;
|
|
80
|
-
return (_jsxs("div", { className: `flex flex-col border-b border-
|
|
81
|
-
? 'bg-
|
|
82
|
-
: 'bg-
|
|
83
|
-
if (window.confirm('清空当前工作流?所有节点将被移除。')) {
|
|
84
|
-
clearWorkflow();
|
|
85
|
-
}
|
|
86
|
-
}, className: "text-[10px] text-zinc-400 transition-colors hover:text-red-500", "aria-label": "\u6E05\u7A7A\u5DE5\u4F5C\u6D41", children: "Clear" }))] }), _jsx("div", { className: "flex min-h-[3rem] items-center gap-1 overflow-x-auto px-3 py-2", children: nodes.length === 0 ? (_jsx("div", { className: "flex flex-1 items-center justify-center py-2 text-center", children: _jsxs("p", { className: "text-[11px] text-zinc-400", children: ["\u7A7A\u5DE5\u4F5C\u6D41 \u2014 \u4ECE\u53F3\u4FA7 ", _jsx("span", { className: "font-mono", children: "Capabilities" }), " \u9762\u677F\u70B9\u51FB\u6DFB\u52A0\u8282\u70B9"] }) })) : (_jsxs(_Fragment, { children: [_jsxs("div", { className: "flex shrink-0 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", children: [_jsx(Icon, { size: 12, children: _jsx("path", { d: "M12 4.5v15m7.5-7.5h-15" }) }), "Source"] }), _jsx(InsertConnector, { index: 0, capabilities: capabilities, onInsert: insertNodeAt, disabled: nodes.length >= MAX_WORKFLOW_STEPS }), nodes.map((node, i) => {
|
|
85
|
+
return (_jsxs("div", { className: `flex flex-col border-b border-[var(--lokvis-border)] bg-[var(--lokvis-surface)] ${className}`, role: "region", "aria-label": "\u5DE5\u4F5C\u6D41\u7F16\u8F91\u5668", children: [_jsxs("div", { className: "flex h-9 shrink-0 items-center justify-between border-b border-[var(--lokvis-border)] px-3", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: "text-[10px] font-semibold uppercase tracking-wider text-[var(--lokvis-fg-subtle)]", children: "Workflow Editor" }), _jsxs("span", { className: `rounded px-1.5 py-0.5 text-[10px] tabular-nums ${canAddMore
|
|
86
|
+
? 'bg-[var(--lokvis-surface-muted)] text-[var(--lokvis-fg-muted)]'
|
|
87
|
+
: 'bg-[var(--lokvis-warning)]/15 text-[var(--lokvis-warning)]'}`, children: [nodes.length, "/", MAX_WORKFLOW_STEPS] })] }), nodes.length > 0 && (_jsx("button", { type: "button", onClick: () => setClearOpen(true), className: "text-[10px] text-[var(--lokvis-fg-subtle)] transition-colors hover:text-[var(--lokvis-danger)]", "aria-label": "\u6E05\u7A7A\u5DE5\u4F5C\u6D41", children: "Clear" }))] }), _jsx("div", { className: "flex min-h-[3rem] items-center gap-1 overflow-x-auto px-3 py-2", children: nodes.length === 0 ? (_jsx("div", { className: "flex flex-1 items-center justify-center py-2 text-center", children: _jsxs("p", { className: "text-[11px] text-[var(--lokvis-fg-subtle)]", children: ["\u7A7A\u5DE5\u4F5C\u6D41 \u2014 \u4ECE\u53F3\u4FA7 ", _jsx("span", { className: "font-mono", children: "Capabilities" }), " \u9762\u677F\u70B9\u51FB\u6DFB\u52A0\u8282\u70B9"] }) })) : (_jsxs(_Fragment, { children: [_jsxs("div", { className: "flex shrink-0 items-center gap-1 rounded-md bg-[var(--lokvis-surface-muted)] px-2 py-1 text-[10px] font-medium text-[var(--lokvis-fg-muted)]", children: [_jsx(Icon, { size: 12, children: _jsx("path", { d: "M12 4.5v15m7.5-7.5h-15" }) }), "Source"] }), _jsx(InsertConnector, { index: 0, capabilities: capabilities, stubCapabilities: stubCapabilities, onInsert: insertNodeAt, disabled: nodes.length >= MAX_WORKFLOW_STEPS }), nodes.map((node, i) => {
|
|
87
88
|
const selected = node.id === selectedNodeId;
|
|
88
89
|
const isDragging = dragIndex === i;
|
|
89
90
|
const isHoverTarget = hoverIndex === i && dragIndex !== null && dragIndex !== i;
|
|
90
|
-
return (_jsxs(React.Fragment, { children: [_jsxs("button", { type: "button", draggable: true, onDragStart: (e) => handleDragStart(e, i), onDragOver: (e) => handleDragOver(e, i), onDragLeave: handleDragLeave, onDrop: (e) => handleDrop(e, i), onDragEnd: handleDragEnd, onClick: () => selectNode(node.id), onKeyDown: (e) => handleKeyDown(e, node.id, i), "aria-label": `节点 ${node.capability},位置 ${i + 1},拖拽或方向键重排,Delete 删除`, className: `
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
}
|
|
108
|
-
function StatusDot({ status }) {
|
|
109
|
-
const color = status === 'running'
|
|
110
|
-
? 'bg-amber-500'
|
|
111
|
-
: status === 'success'
|
|
112
|
-
? 'bg-emerald-500'
|
|
113
|
-
: status === 'failed'
|
|
114
|
-
? 'bg-red-500'
|
|
115
|
-
: status === 'pending'
|
|
116
|
-
? 'bg-zinc-400 dark:bg-zinc-500'
|
|
117
|
-
: 'bg-zinc-300 dark:bg-zinc-600';
|
|
118
|
-
const animate = status === 'running' ? 'animate-pulse' : '';
|
|
119
|
-
return _jsx("span", { className: `h-1.5 w-1.5 rounded-full shrink-0 ${color} ${animate}` });
|
|
91
|
+
return (_jsxs(React.Fragment, { children: [_jsxs("div", { className: "group relative flex shrink-0 items-center", children: [_jsxs("button", { type: "button", draggable: true, onDragStart: (e) => handleDragStart(e, i), onDragOver: (e) => handleDragOver(e, i), onDragLeave: handleDragLeave, onDrop: (e) => handleDrop(e, i), onDragEnd: handleDragEnd, onClick: () => selectNode(node.id), onKeyDown: (e) => handleKeyDown(e, node.id, i), "aria-label": `节点 ${node.capability},位置 ${i + 1},拖拽或方向键重排,Delete 删除`, className: `flex shrink-0 items-center gap-1.5 rounded-md px-2.5 py-1.5 text-[11px] font-medium transition-all ${isDragging
|
|
92
|
+
? 'opacity-40'
|
|
93
|
+
: isHoverTarget
|
|
94
|
+
? 'ring-2 ring-[var(--lokvis-primary)] ring-offset-1'
|
|
95
|
+
: selected
|
|
96
|
+
? 'bg-[var(--lokvis-primary)]/15 text-[var(--lokvis-primary)] ring-1 ring-[var(--lokvis-primary)]/50'
|
|
97
|
+
: node.status === 'running'
|
|
98
|
+
? 'bg-[var(--lokvis-warning)]/15 text-[var(--lokvis-warning)]'
|
|
99
|
+
: node.status === 'success'
|
|
100
|
+
? 'bg-[var(--lokvis-success)]/15 text-[var(--lokvis-success)]'
|
|
101
|
+
: node.status === 'failed'
|
|
102
|
+
? 'bg-[var(--lokvis-danger)]/15 text-[var(--lokvis-danger)]'
|
|
103
|
+
: 'bg-[var(--lokvis-surface-muted)] text-[var(--lokvis-fg-muted)] hover:bg-[var(--lokvis-border)]'} cursor-grab active:cursor-grabbing`, children: [_jsx("span", { className: "flex h-4 w-4 items-center justify-center rounded-full bg-[var(--lokvis-fg)]/5 text-[9px] tabular-nums", children: i + 1 }), _jsx("span", { className: "font-mono", children: node.capability }), _jsx(StatusDot, { status: node.status })] }), _jsx("button", { type: "button", onClick: () => removeNode(node.id), "aria-label": "\u5220\u9664\u8282\u70B9", tabIndex: -1, className: "absolute -right-1 -top-1 rounded p-0.5 opacity-0 transition-opacity group-hover:opacity-100 hover:bg-[var(--lokvis-danger)]/15 hover:text-[var(--lokvis-danger)]", children: _jsx(Icon, { size: 10, strokeWidth: 3, children: _jsx("path", { d: "M6 18L18 6M6 6l12 12" }) }) })] }), _jsx(InsertConnector, { index: i + 1, capabilities: capabilities, stubCapabilities: stubCapabilities, onInsert: insertNodeAt, disabled: nodes.length >= MAX_WORKFLOW_STEPS })] }, node.id));
|
|
104
|
+
}), nodes.length > 0 && (_jsxs("div", { className: "flex shrink-0 items-center gap-1 rounded-md bg-[var(--lokvis-surface-muted)] px-2 py-1 text-[10px] font-medium text-[var(--lokvis-fg-muted)]", children: [_jsx(Icon, { size: 12, children: _jsx("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" }) }), "Output"] }))] })) }), nodes.length > 0 && (_jsxs("div", { className: "flex shrink-0 items-center gap-2 border-t border-[var(--lokvis-border)] px-3 py-1 text-[10px] text-[var(--lokvis-fg-subtle)]", children: [_jsx(Icon, { size: 10, children: _jsx("path", { d: "M13 5.5a1 1 0 1 1 2 0 1-1 0 0 0-.5.86L16.5 9l1.5-.5a1 1 0 1 1 0 2l-1.5-.5-1 1.5a1 1 0 1 1-2 0l1-1.5-1-1.5a1 1 0 0 1 0-2z" }) }), _jsx("span", { children: "\u62D6\u62FD\u91CD\u6392 \u00B7 \u65B9\u5411\u952E\u79FB\u52A8 \u00B7 Delete \u5220\u9664 \u00B7 hover \u7BAD\u5934\u5904\u63D2\u5165\u8282\u70B9" })] })), _jsx(ConfirmDialog, { open: clearOpen, title: "\u6E05\u7A7A\u5DE5\u4F5C\u6D41", message: "\u6E05\u7A7A\u5F53\u524D\u5DE5\u4F5C\u6D41?\u6240\u6709\u8282\u70B9\u5C06\u88AB\u79FB\u9664\u3002", confirmText: "\u6E05\u7A7A", variant: "danger", onConfirm: () => {
|
|
105
|
+
clearWorkflow();
|
|
106
|
+
setClearOpen(false);
|
|
107
|
+
}, onClose: () => setClearOpen(false) })] }));
|
|
120
108
|
}
|
|
121
109
|
/**
|
|
122
110
|
* InsertConnector - 节点之间的连接器 + 插入按钮(W11.1)
|
|
@@ -124,7 +112,7 @@ function StatusDot({ status }) {
|
|
|
124
112
|
* 默认显示箭头;hover 时变为 + 按钮,点击弹出 capability 选择菜单。
|
|
125
113
|
* 选择后调用 onInsert(index, capability)。
|
|
126
114
|
*/
|
|
127
|
-
function InsertConnector({ index, capabilities, onInsert, disabled, }) {
|
|
115
|
+
function InsertConnector({ index, capabilities, stubCapabilities, onInsert, disabled, }) {
|
|
128
116
|
const [open, setOpen] = React.useState(false);
|
|
129
117
|
const [filter, setFilter] = React.useState('');
|
|
130
118
|
const ref = React.useRef(null);
|
|
@@ -144,8 +132,7 @@ function InsertConnector({ index, capabilities, onInsert, disabled, }) {
|
|
|
144
132
|
document.addEventListener('mousedown', handler);
|
|
145
133
|
return () => document.removeEventListener('mousedown', handler);
|
|
146
134
|
}, [open]);
|
|
147
|
-
const filtered = React.useMemo(() => capabilities
|
|
148
|
-
c.description.toLowerCase().includes(filter.toLowerCase())), [capabilities, filter]);
|
|
135
|
+
const filtered = React.useMemo(() => filterCapabilities(capabilities, filter), [capabilities, filter]);
|
|
149
136
|
const handleSelect = (capName) => {
|
|
150
137
|
onInsert(index, capName);
|
|
151
138
|
setOpen(false);
|
|
@@ -153,8 +140,12 @@ function InsertConnector({ index, capabilities, onInsert, disabled, }) {
|
|
|
153
140
|
};
|
|
154
141
|
if (disabled) {
|
|
155
142
|
// 5 步上限时只显示箭头,不显示 +
|
|
156
|
-
return (_jsx(Icon, { size: 12, className: "shrink-0 text-
|
|
143
|
+
return (_jsx(Icon, { size: 12, className: "shrink-0 text-[var(--lokvis-fg-subtle)]", children: _jsx("path", { d: "m9 5 7 7-7 7" }) }));
|
|
157
144
|
}
|
|
158
|
-
return (_jsxs("div", { ref: ref, className: "relative flex shrink-0 items-center", children: [_jsxs("button", { type: "button", onClick: () => setOpen(!open), "aria-label": `在位置 ${index + 1} 插入节点`, title: "\u63D2\u5165\u8282\u70B9", className: "group flex h-5 w-5 items-center justify-center rounded text-
|
|
145
|
+
return (_jsxs("div", { ref: ref, className: "relative flex shrink-0 items-center", children: [_jsxs("button", { type: "button", onClick: () => setOpen(!open), "aria-label": `在位置 ${index + 1} 插入节点`, title: "\u63D2\u5165\u8282\u70B9", className: "group flex h-5 w-5 items-center justify-center rounded text-[var(--lokvis-fg-subtle)] transition-colors hover:bg-[var(--lokvis-primary)]/15 hover:text-[var(--lokvis-primary)]", children: [_jsx(Icon, { size: 12, className: "group-hover:hidden", children: _jsx("path", { d: "m9 5 7 7-7 7" }) }), _jsx(Icon, { size: 12, className: "hidden group-hover:block", strokeWidth: 2.5, children: _jsx("path", { d: "M12 5v14M5 12h14" }) })] }), open && (_jsxs("div", { className: "absolute top-full left-1/2 z-50 mt-1 w-56 -translate-x-1/2 rounded-lg border border-[var(--lokvis-border)] bg-[var(--lokvis-surface)] shadow-[var(--lokvis-elevation-2)]", children: [_jsx("div", { className: "border-b border-[var(--lokvis-border)] p-1.5", children: _jsx(Input, { type: "text", size: "sm", autoFocus: true, placeholder: "\u641C\u7D22 capability...", value: filter, onChange: (e) => setFilter(e.target.value) }) }), _jsx("div", { className: "max-h-48 overflow-y-auto p-1", children: filtered.length === 0 ? (_jsx("p", { className: "px-2 py-2 text-center text-[10px] text-[var(--lokvis-fg-subtle)]", children: "\u65E0\u5339\u914D capability" })) : (filtered.map((cap) => {
|
|
146
|
+
// A7: stub-only 能力显示 "Coming Soon" 标记
|
|
147
|
+
const isStubOnly = stubCapabilities.has(cap.name);
|
|
148
|
+
return (_jsxs("button", { type: "button", onClick: () => handleSelect(cap.name), title: isStubOnly ? 'Coming soon — no engine installed yet' : undefined, className: "block w-full rounded px-2 py-1 text-left transition-colors hover:bg-[var(--lokvis-primary)]/10", children: [_jsxs("span", { className: "flex items-center justify-between gap-1.5", children: [_jsx("span", { className: "block font-mono text-[10px] font-medium text-[var(--lokvis-fg-muted)]", children: cap.name }), isStubOnly && (_jsx("span", { className: "shrink-0 rounded px-1 py-0.5 text-[8px] font-medium uppercase bg-[var(--lokvis-warning)]/15 text-[var(--lokvis-warning)]", children: "Soon" }))] }), _jsx("span", { className: "block truncate text-[9px] text-[var(--lokvis-fg-muted)]", children: cap.description })] }, cap.name));
|
|
149
|
+
})) })] }))] }));
|
|
159
150
|
}
|
|
160
151
|
//# sourceMappingURL=WorkflowEditor.js.map
|