@lokvis/ui-react 0.2.0 → 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.
Files changed (97) hide show
  1. package/dist/components/AssetPanel.d.ts.map +1 -1
  2. package/dist/components/AssetPanel.js +14 -14
  3. package/dist/components/AssetPanel.js.map +1 -1
  4. package/dist/components/Canvas.d.ts +4 -4
  5. package/dist/components/Canvas.d.ts.map +1 -1
  6. package/dist/components/Canvas.js +8 -8
  7. package/dist/components/Canvas.js.map +1 -1
  8. package/dist/components/CommandPalette.d.ts +7 -7
  9. package/dist/components/CommandPalette.d.ts.map +1 -1
  10. package/dist/components/CommandPalette.js +10 -10
  11. package/dist/components/CommandPalette.js.map +1 -1
  12. package/dist/components/CompareSlider.d.ts +6 -6
  13. package/dist/components/CompareSlider.d.ts.map +1 -1
  14. package/dist/components/CompareSlider.js +7 -7
  15. package/dist/components/CompareSlider.js.map +1 -1
  16. package/dist/components/DownloadPanel.d.ts +3 -3
  17. package/dist/components/DownloadPanel.d.ts.map +1 -1
  18. package/dist/components/DownloadPanel.js +9 -9
  19. package/dist/components/DownloadPanel.js.map +1 -1
  20. package/dist/components/ErrorBanner.d.ts +2 -2
  21. package/dist/components/ErrorBanner.d.ts.map +1 -1
  22. package/dist/components/ErrorBanner.js +3 -3
  23. package/dist/components/ErrorBanner.js.map +1 -1
  24. package/dist/components/ExifPanel.d.ts +1 -1
  25. package/dist/components/ExifPanel.d.ts.map +1 -1
  26. package/dist/components/ExifPanel.js +2 -2
  27. package/dist/components/ExifPanel.js.map +1 -1
  28. package/dist/components/GlobalDropzone.d.ts +5 -5
  29. package/dist/components/GlobalDropzone.d.ts.map +1 -1
  30. package/dist/components/GlobalDropzone.js +6 -6
  31. package/dist/components/GlobalDropzone.js.map +1 -1
  32. package/dist/components/HistoryPanel.d.ts +2 -2
  33. package/dist/components/HistoryPanel.d.ts.map +1 -1
  34. package/dist/components/HistoryPanel.js +23 -23
  35. package/dist/components/HistoryPanel.js.map +1 -1
  36. package/dist/components/Inspector.d.ts.map +1 -1
  37. package/dist/components/Inspector.js +6 -6
  38. package/dist/components/Inspector.js.map +1 -1
  39. package/dist/components/ParamForm.d.ts.map +1 -1
  40. package/dist/components/ParamForm.js +7 -7
  41. package/dist/components/ParamForm.js.map +1 -1
  42. package/dist/components/PipelineBar.d.ts.map +1 -1
  43. package/dist/components/PipelineBar.js +13 -13
  44. package/dist/components/PipelineBar.js.map +1 -1
  45. package/dist/components/ProgressBar.d.ts +4 -4
  46. package/dist/components/ProgressBar.d.ts.map +1 -1
  47. package/dist/components/ProgressBar.js +8 -8
  48. package/dist/components/ProgressBar.js.map +1 -1
  49. package/dist/components/StatusBar.d.ts +3 -3
  50. package/dist/components/StatusBar.d.ts.map +1 -1
  51. package/dist/components/StatusBar.js +11 -11
  52. package/dist/components/StatusBar.js.map +1 -1
  53. package/dist/components/ThemeToggle.d.ts +4 -4
  54. package/dist/components/ThemeToggle.d.ts.map +1 -1
  55. package/dist/components/ThemeToggle.js +7 -7
  56. package/dist/components/ThemeToggle.js.map +1 -1
  57. package/dist/components/Toolbar.d.ts.map +1 -1
  58. package/dist/components/Toolbar.js +2 -2
  59. package/dist/components/Toolbar.js.map +1 -1
  60. package/dist/components/WorkflowEditor.d.ts +6 -6
  61. package/dist/components/WorkflowEditor.d.ts.map +1 -1
  62. package/dist/components/WorkflowEditor.js +25 -25
  63. package/dist/components/WorkflowEditor.js.map +1 -1
  64. package/dist/components/WorkflowTemplates.d.ts +2 -2
  65. package/dist/components/WorkflowTemplates.d.ts.map +1 -1
  66. package/dist/components/WorkflowTemplates.js +3 -3
  67. package/dist/components/WorkflowTemplates.js.map +1 -1
  68. package/dist/components/Workspace.d.ts +22 -22
  69. package/dist/components/Workspace.d.ts.map +1 -1
  70. package/dist/components/Workspace.js +30 -30
  71. package/dist/components/Workspace.js.map +1 -1
  72. package/dist/index.d.ts +1 -0
  73. package/dist/index.d.ts.map +1 -1
  74. package/dist/index.js +3 -0
  75. package/dist/index.js.map +1 -1
  76. package/package.json +6 -6
  77. package/src/components/AssetPanel.tsx +263 -263
  78. package/src/components/Canvas.tsx +174 -174
  79. package/src/components/CommandPalette.tsx +242 -242
  80. package/src/components/CompareSlider.tsx +163 -163
  81. package/src/components/DownloadPanel.tsx +203 -203
  82. package/src/components/ErrorBanner.tsx +64 -64
  83. package/src/components/ExifPanel.tsx +154 -154
  84. package/src/components/GlobalDropzone.tsx +170 -170
  85. package/src/components/HistoryPanel.tsx +219 -219
  86. package/src/components/Inspector.tsx +136 -136
  87. package/src/components/ParamForm.tsx +136 -136
  88. package/src/components/PipelineBar.tsx +98 -98
  89. package/src/components/ProgressBar.tsx +78 -78
  90. package/src/components/StatusBar.tsx +148 -148
  91. package/src/components/ThemeToggle.tsx +98 -98
  92. package/src/components/Toolbar.tsx +65 -65
  93. package/src/components/WorkflowEditor.tsx +321 -321
  94. package/src/components/WorkflowTemplates.tsx +77 -77
  95. package/src/components/Workspace.tsx +223 -223
  96. package/src/css-modules.d.ts +3 -0
  97. package/src/index.ts +4 -0
@@ -5,13 +5,13 @@
5
5
  * 通过 `accept` 白名单做 MIME 类型校验,拒绝类型时给出视觉反馈 + 警告。
6
6
  *
7
7
  * 工作机制:
8
- * 1. 监听 window dragenter / dragover / dragleave / drop 事件
9
- * 2. dragenter 显示遮罩,dragleave(离开 window)隐藏
10
- * 3. drop 时校验文件类型 → 合法则 importFiles,非法则提示拒绝原因
8
+ * 1. 监听 window dragenter / dragover / dragleave / drop 事件
9
+ * 2. dragenter 显示遮罩,dragleave(离开 window)隐藏
10
+ * 3. drop 时校验文件类型 → 合法则 importFiles,非法则提示拒绝原因
11
11
  *
12
12
  * MIME 校验:
13
- * - 默认接受所有 image/* 类型(以及 video/audio/pdf 等,与 workspace 多媒体定位对齐)
14
- * - 拒绝类型在遮罩中央显示红色提示,并阻止导入
13
+ * - 默认接受所有 image/* 类型(以及 video/audio/pdf 等,与 workspace 多媒体定位对齐)
14
+ * - 拒绝类型在遮罩中央显示红色提示,并阻止导入
15
15
  *
16
16
  * 与 AssetPanel / Canvas 内置的局部 dropzone 互不冲突:它们各自处理自己的
17
17
  * drop 事件并 `stopPropagation()`,本组件的 window drop 监听不会触发。
@@ -33,9 +33,9 @@ import { Icon } from '@lokvis/ui-core';
33
33
  import { useWorkspaceStore } from '../store/index.js';
34
34
 
35
35
  export interface GlobalDropzoneProps {
36
- /** 接受的 MIME 类型(逗号分隔),默认接受图片/视频/音频/PDF */
37
- accept?: string;
38
- className?: string;
36
+ /** 接受的 MIME 类型(逗号分隔),默认接受图片/视频/音频/PDF */
37
+ accept?: string;
38
+ className?: string;
39
39
  }
40
40
 
41
41
  /** 默认接受的类型:与 Workspace 多媒体定位对齐 */
@@ -43,178 +43,178 @@ const DEFAULT_ACCEPT = 'image/*,video/*,audio/*,application/pdf';
43
43
 
44
44
  /** 检查单个文件是否符合 accept 白名单 */
45
45
  function isFileAccepted(file: File, accept: string): boolean {
46
- if (!accept) return true;
47
- const patterns = accept.split(',').map((p) => p.trim().toLowerCase()).filter(Boolean);
48
- if (patterns.length === 0) return true;
49
- const mime = file.type.toLowerCase();
50
- const name = file.name.toLowerCase();
51
- return patterns.some((p) => {
52
- // image/* → image/(prefix match)
53
- if (p.endsWith('/*')) {
54
- return mime.startsWith(p.slice(0, -1));
55
- }
56
- // image/jpeg → exact mime match
57
- if (p.includes('/')) {
58
- return mime === p;
59
- }
60
- // .png → extension match
61
- if (p.startsWith('.')) {
62
- return name.endsWith(p);
63
- }
64
- return false;
65
- });
46
+ if (!accept) return true;
47
+ const patterns = accept.split(',').map((p) => p.trim().toLowerCase()).filter(Boolean);
48
+ if (patterns.length === 0) return true;
49
+ const mime = file.type.toLowerCase();
50
+ const name = file.name.toLowerCase();
51
+ return patterns.some((p) => {
52
+ // image/* → image/(prefix match)
53
+ if (p.endsWith('/*')) {
54
+ return mime.startsWith(p.slice(0, -1));
55
+ }
56
+ // image/jpeg → exact mime match
57
+ if (p.includes('/')) {
58
+ return mime === p;
59
+ }
60
+ // .png → extension match
61
+ if (p.startsWith('.')) {
62
+ return name.endsWith(p);
63
+ }
64
+ return false;
65
+ });
66
66
  }
67
67
 
68
68
  export function GlobalDropzone({
69
- accept = DEFAULT_ACCEPT,
70
- className = '',
69
+ accept = DEFAULT_ACCEPT,
70
+ className = '',
71
71
  }: GlobalDropzoneProps) {
72
- const importFiles = useWorkspaceStore((s) => s.importFiles);
73
- const setStatus = useWorkspaceStore((s) => s.setStatus);
74
- const setError = useWorkspaceStore((s) => s.setError);
72
+ const importFiles = useWorkspaceStore((s) => s.importFiles);
73
+ const setStatus = useWorkspaceStore((s) => s.setStatus);
74
+ const setError = useWorkspaceStore((s) => s.setError);
75
75
 
76
- // dragCounter:用计数器而非布尔,避免子元素 dragenter/dragleave 触发抖动
77
- const [dragCounter, setDragCounter] = React.useState(0);
78
- const [rejectedFiles, setRejectedFiles] = React.useState<string[]>([]);
76
+ // dragCounter:用计数器而非布尔,避免子元素 dragenter/dragleave 触发抖动
77
+ const [dragCounter, setDragCounter] = React.useState(0);
78
+ const [rejectedFiles, setRejectedFiles] = React.useState<string[]>([]);
79
79
 
80
- React.useEffect(() => {
81
- // 阻止浏览器默认行为(打开文件 / 下载),否则 drop 不会触发
82
- const preventDefault = (e: DragEvent) => {
83
- e.preventDefault();
84
- e.stopPropagation();
85
- };
86
- // dragenter:进入窗口,计数 +1
87
- const onDragEnter = (e: DragEvent) => {
88
- preventDefault(e);
89
- if (e.dataTransfer?.types?.includes('Files')) {
90
- setDragCounter((c) => c + 1);
91
- setRejectedFiles([]);
92
- }
93
- };
94
- // dragover:必须 preventDefault 才能触发 drop
95
- const onDragOver = (e: DragEvent) => {
96
- preventDefault(e);
97
- if (e.dataTransfer) {
98
- // 显示 drop 效果
99
- e.dataTransfer.dropEffect = 'copy';
100
- }
101
- };
102
- // dragleave:离开窗口,计数 -1
103
- const onDragLeave = (e: DragEvent) => {
104
- preventDefault(e);
105
- // 只在离开 window 时(relatedTarget 为 null 或 document.documentElement)减计数
106
- // relatedTarget 为 null 表示离开文档边界
107
- if (e.relatedTarget === null) {
108
- setDragCounter(0);
109
- setRejectedFiles([]);
110
- }
111
- };
112
- // drop:校验 + 导入
113
- const onDrop = (e: DragEvent) => {
114
- // 兜底:子组件(Canvas / AssetPanel)若已处理 drop 并 preventDefault(),
115
- // 表明文件已导入,本组件跳过避免双重导入。子组件仍需 stopPropagation()
116
- // 防止事件冒泡到 window,这里 defaultPrevented 是双保险。
117
- if (e.defaultPrevented) {
118
- setDragCounter(0);
119
- return;
120
- }
121
- preventDefault(e);
122
- setDragCounter(0);
123
- const files = e.dataTransfer?.files ? Array.from(e.dataTransfer.files) : [];
124
- if (files.length === 0) return;
80
+ React.useEffect(() => {
81
+ // 阻止浏览器默认行为(打开文件 / 下载),否则 drop 不会触发
82
+ const preventDefault = (e: DragEvent) => {
83
+ e.preventDefault();
84
+ e.stopPropagation();
85
+ };
86
+ // dragenter:进入窗口,计数 +1
87
+ const onDragEnter = (e: DragEvent) => {
88
+ preventDefault(e);
89
+ if (e.dataTransfer?.types?.includes('Files')) {
90
+ setDragCounter((c) => c + 1);
91
+ setRejectedFiles([]);
92
+ }
93
+ };
94
+ // dragover:必须 preventDefault 才能触发 drop
95
+ const onDragOver = (e: DragEvent) => {
96
+ preventDefault(e);
97
+ if (e.dataTransfer) {
98
+ // 显示 drop 效果
99
+ e.dataTransfer.dropEffect = 'copy';
100
+ }
101
+ };
102
+ // dragleave:离开窗口,计数 -1
103
+ const onDragLeave = (e: DragEvent) => {
104
+ preventDefault(e);
105
+ // 只在离开 window 时(relatedTarget 为 null 或 document.documentElement)减计数
106
+ // relatedTarget 为 null 表示离开文档边界
107
+ if (e.relatedTarget === null) {
108
+ setDragCounter(0);
109
+ setRejectedFiles([]);
110
+ }
111
+ };
112
+ // drop:校验 + 导入
113
+ const onDrop = (e: DragEvent) => {
114
+ // 兜底:子组件(Canvas / AssetPanel)若已处理 drop 并 preventDefault(),
115
+ // 表明文件已导入,本组件跳过避免双重导入。子组件仍需 stopPropagation()
116
+ // 防止事件冒泡到 window,这里 defaultPrevented 是双保险。
117
+ if (e.defaultPrevented) {
118
+ setDragCounter(0);
119
+ return;
120
+ }
121
+ preventDefault(e);
122
+ setDragCounter(0);
123
+ const files = e.dataTransfer?.files ? Array.from(e.dataTransfer.files) : [];
124
+ if (files.length === 0) return;
125
125
 
126
- const accepted: File[] = [];
127
- const rejected: string[] = [];
128
- for (const file of files) {
129
- if (isFileAccepted(file, accept)) {
130
- accepted.push(file);
131
- } else {
132
- rejected.push(`${file.name} (${file.type || 'unknown'})`);
133
- }
134
- }
135
- if (rejected.length > 0) {
136
- setRejectedFiles(rejected);
137
- // 5 秒后自动清空拒绝提示
138
- setTimeout(() => setRejectedFiles([]), 5000);
139
- }
140
- if (accepted.length > 0) {
141
- void importFiles(accepted);
142
- } else {
143
- setStatus(`Rejected ${rejected.length} file(s) — unsupported type`);
144
- setError(null);
145
- }
146
- };
126
+ const accepted: File[] = [];
127
+ const rejected: string[] = [];
128
+ for (const file of files) {
129
+ if (isFileAccepted(file, accept)) {
130
+ accepted.push(file);
131
+ } else {
132
+ rejected.push(`${file.name} (${file.type || 'unknown'})`);
133
+ }
134
+ }
135
+ if (rejected.length > 0) {
136
+ setRejectedFiles(rejected);
137
+ // 5 秒后自动清空拒绝提示
138
+ setTimeout(() => setRejectedFiles([]), 5000);
139
+ }
140
+ if (accepted.length > 0) {
141
+ void importFiles(accepted);
142
+ } else {
143
+ setStatus(`Rejected ${rejected.length} file(s) — unsupported type`);
144
+ setError(null);
145
+ }
146
+ };
147
147
 
148
- window.addEventListener('dragenter', onDragEnter);
149
- window.addEventListener('dragover', onDragOver);
150
- window.addEventListener('dragleave', onDragLeave);
151
- window.addEventListener('drop', onDrop);
152
- return () => {
153
- window.removeEventListener('dragenter', onDragEnter);
154
- window.removeEventListener('dragover', onDragOver);
155
- window.removeEventListener('dragleave', onDragLeave);
156
- window.removeEventListener('drop', onDrop);
157
- };
158
- }, [accept, importFiles, setStatus, setError]);
148
+ window.addEventListener('dragenter', onDragEnter);
149
+ window.addEventListener('dragover', onDragOver);
150
+ window.addEventListener('dragleave', onDragLeave);
151
+ window.addEventListener('drop', onDrop);
152
+ return () => {
153
+ window.removeEventListener('dragenter', onDragEnter);
154
+ window.removeEventListener('dragover', onDragOver);
155
+ window.removeEventListener('dragleave', onDragLeave);
156
+ window.removeEventListener('drop', onDrop);
157
+ };
158
+ }, [accept, importFiles, setStatus, setError]);
159
159
 
160
- const isDragging = dragCounter > 0;
161
- const hasRejections = rejectedFiles.length > 0;
160
+ const isDragging = dragCounter > 0;
161
+ const hasRejections = rejectedFiles.length > 0;
162
162
 
163
- if (!isDragging && !hasRejections) return null;
163
+ if (!isDragging && !hasRejections) return null;
164
164
 
165
- return (
166
- <div
167
- className={`pointer-events-none fixed inset-0 z-40 flex items-center justify-center p-8 ${className}`}
168
- role="alertdialog"
169
- aria-live="polite"
170
- >
171
- {/* 遮罩 */}
172
- <div className="absolute inset-0 bg-indigo-50/80 backdrop-blur-sm dark:bg-indigo-950/60" />
165
+ return (
166
+ <div
167
+ className={`pointer-events-none fixed inset-0 z-40 flex items-center justify-center p-8 ${className}`}
168
+ role="alertdialog"
169
+ aria-live="polite"
170
+ >
171
+ {/* 遮罩 */}
172
+ <div className="absolute inset-0 bg-[var(--lokvis-primary)]/10/80 backdrop-blur-sm" />
173
173
 
174
- {/* 内容 */}
175
- <div
176
- className={`relative w-full max-w-md rounded-2xl border-2 border-dashed p-8 text-center bg-white shadow-2xl dark:bg-zinc-900 ${
177
- hasRejections ? 'border-red-400 dark:border-red-600' : 'border-indigo-500 dark:border-indigo-400'
178
- }`}
179
- >
180
- {hasRejections ? (
181
- <>
182
- <div className="mx-auto flex h-12 w-12 items-center justify-center rounded-full bg-red-100 dark:bg-red-900/40">
183
- <Icon size={24} className="text-red-500" strokeWidth={2}>
184
- <circle cx="12" cy="12" r="10" />
185
- <line x1="15" y1="9" x2="9" y2="15" />
186
- <line x1="9" y1="9" x2="15" y2="15" />
187
- </Icon>
188
- </div>
189
- <p className="mt-3 text-sm font-semibold text-red-700 dark:text-red-400">
190
- Unsupported file type
191
- </p>
192
- <p className="mt-1.5 text-xs text-red-600/80 dark:text-red-400/70">
193
- {rejectedFiles.join(', ')}
194
- </p>
195
- <p className="mt-2 text-[10px] text-zinc-500">
196
- Accepted: {accept || 'all files'}
197
- </p>
198
- </>
199
- ) : (
200
- <>
201
- <div className="mx-auto flex h-12 w-12 items-center justify-center rounded-full bg-indigo-100 dark:bg-indigo-900/40">
202
- <Icon size={24} className="text-indigo-500" strokeWidth={1.5}>
203
- <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" />
204
- </Icon>
205
- </div>
206
- <p className="mt-3 text-sm font-semibold text-indigo-700 dark:text-indigo-300">
207
- Drop files to import
208
- </p>
209
- <p className="mt-1 text-xs text-zinc-500 dark:text-zinc-400">
210
- Files are processed locally in your browser
211
- </p>
212
- <p className="mt-2 text-[10px] text-zinc-400">
213
- Accepted: {accept || 'all files'}
214
- </p>
215
- </>
216
- )}
217
- </div>
218
- </div>
219
- );
174
+ {/* 内容 */}
175
+ <div
176
+ className={`relative w-full max-w-md rounded-2xl border-2 border-dashed p-8 text-center bg-[var(--lokvis-surface)] shadow-2xl ${
177
+ hasRejections ? 'border-[var(--lokvis-danger)]' : 'border-[var(--lokvis-primary)]'
178
+ }`}
179
+ >
180
+ {hasRejections ? (
181
+ <>
182
+ <div className="mx-auto flex h-12 w-12 items-center justify-center rounded-full bg-[var(--lokvis-danger)]/15">
183
+ <Icon size={24} className="text-[var(--lokvis-danger)]" strokeWidth={2}>
184
+ <circle cx="12" cy="12" r="10" />
185
+ <line x1="15" y1="9" x2="9" y2="15" />
186
+ <line x1="9" y1="9" x2="15" y2="15" />
187
+ </Icon>
188
+ </div>
189
+ <p className="mt-3 text-sm font-semibold text-[var(--lokvis-danger)]">
190
+ Unsupported file type
191
+ </p>
192
+ <p className="mt-1.5 text-xs text-[var(--lokvis-danger)]/80">
193
+ {rejectedFiles.join(', ')}
194
+ </p>
195
+ <p className="mt-2 text-[10px] text-[var(--lokvis-fg-muted)]">
196
+ Accepted: {accept || 'all files'}
197
+ </p>
198
+ </>
199
+ ) : (
200
+ <>
201
+ <div className="mx-auto flex h-12 w-12 items-center justify-center rounded-full bg-[var(--lokvis-primary)]/15">
202
+ <Icon size={24} className="text-[var(--lokvis-primary)]" strokeWidth={1.5}>
203
+ <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" />
204
+ </Icon>
205
+ </div>
206
+ <p className="mt-3 text-sm font-semibold text-[var(--lokvis-primary)]">
207
+ Drop files to import
208
+ </p>
209
+ <p className="mt-1 text-xs text-[var(--lokvis-fg-muted)]">
210
+ Files are processed locally in your browser
211
+ </p>
212
+ <p className="mt-2 text-[10px] text-[var(--lokvis-fg-subtle)]">
213
+ Accepted: {accept || 'all files'}
214
+ </p>
215
+ </>
216
+ )}
217
+ </div>
218
+ </div>
219
+ );
220
220
  }