@lokvis/ui-react 0.5.0 → 0.5.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.
|
@@ -3,14 +3,13 @@
|
|
|
3
3
|
*
|
|
4
4
|
* 展示上次工作流执行的输出资产,提供:
|
|
5
5
|
* - 单项下载(从 runtime.exportAsset 取 blob,触发浏览器下载)
|
|
6
|
-
* - 全部下载(
|
|
6
|
+
* - 全部下载(JSZip 打包为单个 ZIP,一次用户手势完成)
|
|
7
7
|
* - 清空输出列表
|
|
8
8
|
*
|
|
9
9
|
* 与 apps/playground 的 DownloadManager(独立工具页)不同:本组件是 Workspace
|
|
10
10
|
* 内嵌的轻量面板,数据来自 store.lastOutputIds(由 workflow-slice.run 写入)。
|
|
11
11
|
*
|
|
12
12
|
* 下载命名:`lokvis-output-{assetId前8位}.{ext}`,简洁且避免覆盖。
|
|
13
|
-
* 不引入 JSZip(需新增依赖),用浏览器原生 downloadBlob 实现。
|
|
14
13
|
*/
|
|
15
14
|
import * as React from 'react';
|
|
16
15
|
export interface DownloadPanelProps {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DownloadPanel.d.ts","sourceRoot":"","sources":["../../src/components/DownloadPanel.tsx"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"DownloadPanel.d.ts","sourceRoot":"","sources":["../../src/components/DownloadPanel.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAM/B,MAAM,WAAW,kBAAkB;IAClC,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAqBD,wBAAgB,aAAa,CAAC,EAAE,SAAc,EAAE,EAAE,kBAAkB,4BA8LnE"}
|
|
@@ -4,33 +4,19 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
4
4
|
*
|
|
5
5
|
* 展示上次工作流执行的输出资产,提供:
|
|
6
6
|
* - 单项下载(从 runtime.exportAsset 取 blob,触发浏览器下载)
|
|
7
|
-
* - 全部下载(
|
|
7
|
+
* - 全部下载(JSZip 打包为单个 ZIP,一次用户手势完成)
|
|
8
8
|
* - 清空输出列表
|
|
9
9
|
*
|
|
10
10
|
* 与 apps/playground 的 DownloadManager(独立工具页)不同:本组件是 Workspace
|
|
11
11
|
* 内嵌的轻量面板,数据来自 store.lastOutputIds(由 workflow-slice.run 写入)。
|
|
12
12
|
*
|
|
13
13
|
* 下载命名:`lokvis-output-{assetId前8位}.{ext}`,简洁且避免覆盖。
|
|
14
|
-
* 不引入 JSZip(需新增依赖),用浏览器原生 downloadBlob 实现。
|
|
15
14
|
*/
|
|
16
15
|
import * as React from 'react';
|
|
16
|
+
import JSZip from 'jszip';
|
|
17
17
|
import { Icon } from '@lokvis/ui-core';
|
|
18
18
|
import { useWorkspaceStore } from '../store/index.js';
|
|
19
19
|
import { formatBytes } from '../utils.js';
|
|
20
|
-
/**
|
|
21
|
-
* 批量下载时每项之间的延迟毫秒数。
|
|
22
|
-
*
|
|
23
|
-
* 短期方案:浏览器(尤其 Chrome)会拦截同一 tick 内触发的多个 programmatic
|
|
24
|
-
* `<a download>` 点击,把第一个之后的视为"非用户手势"弹窗拦截。两次下载之间
|
|
25
|
-
* 插入小延迟可绕过该启发式判定。该值是经验值,不是规范要求 —— 不同浏览器
|
|
26
|
-
* 版本行为可能变化。
|
|
27
|
-
*
|
|
28
|
-
* 长期方案:用 JSZip 将多个输出打包成单个 ZIP 下载(只需一次用户手势)。
|
|
29
|
-
* 本组件暂未引入 JSZip 依赖(见文件头注释),待 M2 引入 archive capability 后
|
|
30
|
-
* 一并迁移,届时可移除此常量与 sleep 调用。
|
|
31
|
-
*/
|
|
32
|
-
const BATCH_DOWNLOAD_DELAY_MS = 200;
|
|
33
|
-
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
34
20
|
/** 从扩展名或 mime 推断下载扩展名 */
|
|
35
21
|
function extFromMime(mime) {
|
|
36
22
|
const sub = mime.split('/')[1] ?? 'bin';
|
|
@@ -103,42 +89,47 @@ export function DownloadPanel({ className = '' }) {
|
|
|
103
89
|
async function handleDownloadAll() {
|
|
104
90
|
if (!runtime || batchDownloading)
|
|
105
91
|
return;
|
|
92
|
+
// 单文件时直接下载,无需 ZIP 开销
|
|
93
|
+
if (outputAssets.length === 1) {
|
|
94
|
+
void handleDownloadOne(outputAssets[0].id);
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
106
97
|
setBatchDownloading(true);
|
|
107
98
|
setError(null);
|
|
108
|
-
setStatus('
|
|
109
|
-
// 用本地 Set 跟踪本次循环已下载 id,避免 downloaded 闭包快照与 state 不同步
|
|
110
|
-
// (review 反馈:循环中 setDownloaded 更新 state 但闭包 downloaded 不变,
|
|
111
|
-
// 虽有 batchDownloading guard 阻止并发,逻辑上仍应读取最新已下载集合)
|
|
112
|
-
const localDownloaded = new Set(downloaded);
|
|
99
|
+
setStatus('Packaging outputs...');
|
|
113
100
|
try {
|
|
101
|
+
const zip = new JSZip();
|
|
114
102
|
for (const asset of outputAssets) {
|
|
115
|
-
// 卸载后立即停止循环(避免对已卸载组件 setState)
|
|
116
103
|
if (!mountedRef.current)
|
|
117
104
|
return;
|
|
118
|
-
if (localDownloaded.has(asset.id))
|
|
119
|
-
continue;
|
|
120
105
|
try {
|
|
121
106
|
const blob = await runtime.exportAsset(asset.id);
|
|
122
107
|
const ext = extFromMime(asset.metadata.mimeType);
|
|
123
108
|
const filename = `lokvis-output-${asset.id.slice(0, 8)}.${ext}`;
|
|
124
|
-
|
|
125
|
-
localDownloaded.add(asset.id);
|
|
126
|
-
if (mountedRef.current) {
|
|
127
|
-
setDownloaded((prev) => new Set(prev).add(asset.id));
|
|
128
|
-
}
|
|
129
|
-
await sleep(BATCH_DOWNLOAD_DELAY_MS);
|
|
109
|
+
zip.file(filename, blob);
|
|
130
110
|
}
|
|
131
111
|
catch (err) {
|
|
112
|
+
// 单项失败不阻断打包,记录错误继续
|
|
132
113
|
if (mountedRef.current) {
|
|
133
114
|
setError(err instanceof Error ? err.message : String(err));
|
|
134
115
|
}
|
|
135
|
-
// 单项失败不阻断后续
|
|
136
116
|
}
|
|
137
117
|
}
|
|
118
|
+
if (!mountedRef.current)
|
|
119
|
+
return;
|
|
120
|
+
const zipBlob = await zip.generateAsync({ type: 'blob' });
|
|
121
|
+
downloadBlob(zipBlob, 'lokvis-outputs.zip');
|
|
122
|
+
// 标记全部已下载
|
|
138
123
|
if (mountedRef.current) {
|
|
124
|
+
setDownloaded(new Set(outputAssets.map((a) => a.id)));
|
|
139
125
|
setStatus('Downloads complete');
|
|
140
126
|
}
|
|
141
127
|
}
|
|
128
|
+
catch (err) {
|
|
129
|
+
if (mountedRef.current) {
|
|
130
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
131
|
+
}
|
|
132
|
+
}
|
|
142
133
|
finally {
|
|
143
134
|
if (mountedRef.current) {
|
|
144
135
|
setBatchDownloading(false);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DownloadPanel.js","sourceRoot":"","sources":["../../src/components/DownloadPanel.tsx"],"names":[],"mappings":";AAAA
|
|
1
|
+
{"version":3,"file":"DownloadPanel.js","sourceRoot":"","sources":["../../src/components/DownloadPanel.tsx"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lokvis/ui-react",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"description": "Lokvis Workspace UI - React components for Image/Video/PDF Workspace",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -30,13 +30,14 @@
|
|
|
30
30
|
"react-dom": "^19.0.0"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
+
"jszip": "^3.10.0",
|
|
33
34
|
"zustand": "^5.0.0",
|
|
34
|
-
"@lokvis/schema": "0.5.
|
|
35
|
-
"@lokvis/runtime": "0.5.
|
|
36
|
-
"@lokvis/sdk": "0.5.
|
|
37
|
-
"@lokvis/ui-core": "0.5.
|
|
38
|
-
"@lokvis/capability": "0.5.
|
|
39
|
-
"@lokvis/workflow": "0.5.
|
|
35
|
+
"@lokvis/schema": "0.5.1",
|
|
36
|
+
"@lokvis/runtime": "0.5.1",
|
|
37
|
+
"@lokvis/sdk": "0.5.1",
|
|
38
|
+
"@lokvis/ui-core": "0.5.1",
|
|
39
|
+
"@lokvis/capability": "0.5.1",
|
|
40
|
+
"@lokvis/workflow": "0.5.1"
|
|
40
41
|
},
|
|
41
42
|
"devDependencies": {
|
|
42
43
|
"@types/react": "^19.0.0",
|
|
@@ -3,17 +3,17 @@
|
|
|
3
3
|
*
|
|
4
4
|
* 展示上次工作流执行的输出资产,提供:
|
|
5
5
|
* - 单项下载(从 runtime.exportAsset 取 blob,触发浏览器下载)
|
|
6
|
-
* - 全部下载(
|
|
6
|
+
* - 全部下载(JSZip 打包为单个 ZIP,一次用户手势完成)
|
|
7
7
|
* - 清空输出列表
|
|
8
8
|
*
|
|
9
9
|
* 与 apps/playground 的 DownloadManager(独立工具页)不同:本组件是 Workspace
|
|
10
10
|
* 内嵌的轻量面板,数据来自 store.lastOutputIds(由 workflow-slice.run 写入)。
|
|
11
11
|
*
|
|
12
12
|
* 下载命名:`lokvis-output-{assetId前8位}.{ext}`,简洁且避免覆盖。
|
|
13
|
-
* 不引入 JSZip(需新增依赖),用浏览器原生 downloadBlob 实现。
|
|
14
13
|
*/
|
|
15
14
|
|
|
16
15
|
import * as React from 'react';
|
|
16
|
+
import JSZip from 'jszip';
|
|
17
17
|
import { Icon } from '@lokvis/ui-core';
|
|
18
18
|
import { useWorkspaceStore } from '../store/index.js';
|
|
19
19
|
import { formatBytes } from '../utils.js';
|
|
@@ -22,22 +22,6 @@ export interface DownloadPanelProps {
|
|
|
22
22
|
className?: string;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
/**
|
|
26
|
-
* 批量下载时每项之间的延迟毫秒数。
|
|
27
|
-
*
|
|
28
|
-
* 短期方案:浏览器(尤其 Chrome)会拦截同一 tick 内触发的多个 programmatic
|
|
29
|
-
* `<a download>` 点击,把第一个之后的视为"非用户手势"弹窗拦截。两次下载之间
|
|
30
|
-
* 插入小延迟可绕过该启发式判定。该值是经验值,不是规范要求 —— 不同浏览器
|
|
31
|
-
* 版本行为可能变化。
|
|
32
|
-
*
|
|
33
|
-
* 长期方案:用 JSZip 将多个输出打包成单个 ZIP 下载(只需一次用户手势)。
|
|
34
|
-
* 本组件暂未引入 JSZip 依赖(见文件头注释),待 M2 引入 archive capability 后
|
|
35
|
-
* 一并迁移,届时可移除此常量与 sleep 调用。
|
|
36
|
-
*/
|
|
37
|
-
const BATCH_DOWNLOAD_DELAY_MS = 200;
|
|
38
|
-
|
|
39
|
-
const sleep = (ms: number): Promise<void> => new Promise((r) => setTimeout(r, ms));
|
|
40
|
-
|
|
41
25
|
/** 从扩展名或 mime 推断下载扩展名 */
|
|
42
26
|
function extFromMime(mime: string): string {
|
|
43
27
|
const sub = mime.split('/')[1] ?? 'bin';
|
|
@@ -115,38 +99,42 @@ export function DownloadPanel({ className = '' }: DownloadPanelProps) {
|
|
|
115
99
|
|
|
116
100
|
async function handleDownloadAll() {
|
|
117
101
|
if (!runtime || batchDownloading) return;
|
|
102
|
+
// 单文件时直接下载,无需 ZIP 开销
|
|
103
|
+
if (outputAssets.length === 1) {
|
|
104
|
+
void handleDownloadOne(outputAssets[0]!.id);
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
118
107
|
setBatchDownloading(true);
|
|
119
108
|
setError(null);
|
|
120
|
-
setStatus('
|
|
121
|
-
// 用本地 Set 跟踪本次循环已下载 id,避免 downloaded 闭包快照与 state 不同步
|
|
122
|
-
// (review 反馈:循环中 setDownloaded 更新 state 但闭包 downloaded 不变,
|
|
123
|
-
// 虽有 batchDownloading guard 阻止并发,逻辑上仍应读取最新已下载集合)
|
|
124
|
-
const localDownloaded = new Set(downloaded);
|
|
109
|
+
setStatus('Packaging outputs...');
|
|
125
110
|
try {
|
|
111
|
+
const zip = new JSZip();
|
|
126
112
|
for (const asset of outputAssets) {
|
|
127
|
-
// 卸载后立即停止循环(避免对已卸载组件 setState)
|
|
128
113
|
if (!mountedRef.current) return;
|
|
129
|
-
if (localDownloaded.has(asset.id)) continue;
|
|
130
114
|
try {
|
|
131
115
|
const blob = await runtime.exportAsset(asset.id);
|
|
132
116
|
const ext = extFromMime(asset.metadata.mimeType);
|
|
133
117
|
const filename = `lokvis-output-${asset.id.slice(0, 8)}.${ext}`;
|
|
134
|
-
|
|
135
|
-
localDownloaded.add(asset.id);
|
|
136
|
-
if (mountedRef.current) {
|
|
137
|
-
setDownloaded((prev) => new Set(prev).add(asset.id));
|
|
138
|
-
}
|
|
139
|
-
await sleep(BATCH_DOWNLOAD_DELAY_MS);
|
|
118
|
+
zip.file(filename, blob);
|
|
140
119
|
} catch (err) {
|
|
120
|
+
// 单项失败不阻断打包,记录错误继续
|
|
141
121
|
if (mountedRef.current) {
|
|
142
122
|
setError(err instanceof Error ? err.message : String(err));
|
|
143
123
|
}
|
|
144
|
-
// 单项失败不阻断后续
|
|
145
124
|
}
|
|
146
125
|
}
|
|
126
|
+
if (!mountedRef.current) return;
|
|
127
|
+
const zipBlob = await zip.generateAsync({ type: 'blob' });
|
|
128
|
+
downloadBlob(zipBlob, 'lokvis-outputs.zip');
|
|
129
|
+
// 标记全部已下载
|
|
147
130
|
if (mountedRef.current) {
|
|
131
|
+
setDownloaded(new Set(outputAssets.map((a) => a.id)));
|
|
148
132
|
setStatus('Downloads complete');
|
|
149
133
|
}
|
|
134
|
+
} catch (err) {
|
|
135
|
+
if (mountedRef.current) {
|
|
136
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
137
|
+
}
|
|
150
138
|
} finally {
|
|
151
139
|
if (mountedRef.current) {
|
|
152
140
|
setBatchDownloading(false);
|