@genfeedai/workflow-ui 0.1.3 → 0.1.4

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 (84) hide show
  1. package/dist/canvas.d.mts +16 -2
  2. package/dist/canvas.mjs +10 -8
  3. package/dist/chunk-6PSJTBNV.mjs +638 -0
  4. package/dist/chunk-7H3WJJYS.mjs +52 -0
  5. package/dist/{chunk-HCXI63ME.mjs → chunk-AUQGOJOQ.mjs} +27 -4
  6. package/dist/{chunk-AOTUCJMA.mjs → chunk-GWBGK3KL.mjs} +2 -2
  7. package/dist/chunk-JTPADIUO.mjs +130 -0
  8. package/dist/{chunk-SQK4JDYY.mjs → chunk-LT3ZJJL6.mjs} +9 -2
  9. package/dist/{chunk-7P2JWDC7.mjs → chunk-O5II6BOJ.mjs} +1198 -254
  10. package/dist/{chunk-AUZR6REQ.mjs → chunk-OQREHJXK.mjs} +1 -1
  11. package/dist/chunk-OY7BRSGG.mjs +60 -0
  12. package/dist/{chunk-E3YBVMYZ.mjs → chunk-PANZDSP6.mjs} +274 -305
  13. package/dist/chunk-PCIWWD37.mjs +90 -0
  14. package/dist/{chunk-RIGVIEYB.mjs → chunk-R727OFBR.mjs} +11 -1
  15. package/dist/chunk-ZD2BADZO.mjs +1294 -0
  16. package/dist/contextMenuStore-DMg0hJQ1.d.mts +22 -0
  17. package/dist/hooks.d.mts +53 -244
  18. package/dist/hooks.mjs +6 -6
  19. package/dist/index.d.mts +11 -7
  20. package/dist/index.mjs +13 -11
  21. package/dist/lib.d.mts +250 -4
  22. package/dist/lib.mjs +562 -2
  23. package/dist/nodes.d.mts +3 -1
  24. package/dist/nodes.mjs +6 -6
  25. package/dist/panels.mjs +3 -4
  26. package/dist/{promptLibraryStore-zqb59nsu.d.mts → promptLibraryStore-Bgw5LzvD.d.mts} +33 -5
  27. package/dist/provider.d.mts +2 -2
  28. package/dist/provider.mjs +0 -1
  29. package/dist/stores.d.mts +4 -3
  30. package/dist/stores.mjs +3 -40
  31. package/dist/toolbar.d.mts +3 -1
  32. package/dist/toolbar.mjs +5 -4
  33. package/dist/{types-ipAnBzAJ.d.mts → types-CF6DPx0P.d.mts} +8 -3
  34. package/dist/ui.d.mts +1 -1
  35. package/dist/ui.mjs +0 -1
  36. package/dist/{hooks.d.ts → useCommentNavigation-NzJjkaj2.d.mts} +15 -2
  37. package/dist/workflowStore-UAAKOOIK.mjs +2 -0
  38. package/package.json +30 -24
  39. package/dist/canvas.d.ts +0 -27
  40. package/dist/canvas.js +0 -45
  41. package/dist/chunk-3SPPKCWR.js +0 -458
  42. package/dist/chunk-3TMV3K34.js +0 -534
  43. package/dist/chunk-3YFFDHC5.js +0 -300
  44. package/dist/chunk-4MZ62VMF.js +0 -37
  45. package/dist/chunk-5HJFQVUR.js +0 -61
  46. package/dist/chunk-5LQ4QBR5.js +0 -2
  47. package/dist/chunk-6DOEUDD5.js +0 -254
  48. package/dist/chunk-AXFOCPPP.js +0 -998
  49. package/dist/chunk-BMFRA6GK.js +0 -1546
  50. package/dist/chunk-E323WAZG.mjs +0 -272
  51. package/dist/chunk-ECD5J2BA.js +0 -6022
  52. package/dist/chunk-EMGXUNBL.js +0 -120
  53. package/dist/chunk-EMUMKW5C.js +0 -107
  54. package/dist/chunk-FOMOOERN.js +0 -2
  55. package/dist/chunk-IASLG6IA.mjs +0 -118
  56. package/dist/chunk-IHF35QZD.js +0 -1095
  57. package/dist/chunk-JLWKW3G5.js +0 -2
  58. package/dist/chunk-KDIWRSYV.js +0 -375
  59. package/dist/chunk-L5TF4EHW.mjs +0 -1
  60. package/dist/chunk-RJ262NXS.js +0 -24
  61. package/dist/chunk-RXNEDWK2.js +0 -141
  62. package/dist/chunk-SEV2DWKF.js +0 -744
  63. package/dist/chunk-ZJWP5KGZ.mjs +0 -33
  64. package/dist/hooks.js +0 -56
  65. package/dist/index.d.ts +0 -29
  66. package/dist/index.js +0 -180
  67. package/dist/lib.d.ts +0 -164
  68. package/dist/lib.js +0 -144
  69. package/dist/nodes.d.ts +0 -128
  70. package/dist/nodes.js +0 -151
  71. package/dist/panels.d.ts +0 -22
  72. package/dist/panels.js +0 -21
  73. package/dist/promptLibraryStore-BZnfmEkc.d.ts +0 -464
  74. package/dist/provider.d.ts +0 -29
  75. package/dist/provider.js +0 -17
  76. package/dist/stores.d.ts +0 -96
  77. package/dist/stores.js +0 -113
  78. package/dist/toolbar.d.ts +0 -73
  79. package/dist/toolbar.js +0 -34
  80. package/dist/types-ipAnBzAJ.d.ts +0 -46
  81. package/dist/ui.d.ts +0 -67
  82. package/dist/ui.js +0 -84
  83. package/dist/workflowStore-7SDJC4UR.mjs +0 -3
  84. package/dist/workflowStore-LNJQ5RZG.js +0 -12
@@ -1,744 +0,0 @@
1
- 'use strict';
2
-
3
- var chunkAXFOCPPP_js = require('./chunk-AXFOCPPP.js');
4
- var chunkIHF35QZD_js = require('./chunk-IHF35QZD.js');
5
- var lucideReact = require('lucide-react');
6
- var react = require('react');
7
- var jsxRuntime = require('react/jsx-runtime');
8
-
9
- function SaveAsDialog({ isOpen, currentName, onSave, onClose }) {
10
- const [name, setName] = react.useState("");
11
- const inputRef = react.useRef(null);
12
- react.useEffect(() => {
13
- if (isOpen) {
14
- setName(`${currentName} (copy)`);
15
- setTimeout(() => inputRef.current?.select(), 0);
16
- }
17
- }, [isOpen, currentName]);
18
- const handleSubmit = react.useCallback(
19
- (e) => {
20
- e.preventDefault();
21
- const trimmed = name.trim();
22
- if (trimmed) {
23
- onSave(trimmed);
24
- }
25
- },
26
- [name, onSave]
27
- );
28
- const handleKeyDown = react.useCallback(
29
- (e) => {
30
- if (e.key === "Escape") {
31
- onClose();
32
- }
33
- },
34
- [onClose]
35
- );
36
- if (!isOpen) return null;
37
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "fixed inset-0 z-50 flex items-center justify-center", children: [
38
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 bg-black/60", onClick: onClose }),
39
- /* @__PURE__ */ jsxRuntime.jsxs(
40
- "div",
41
- {
42
- className: "relative z-10 w-full max-w-md rounded-lg border border-border bg-card p-6 shadow-xl",
43
- onKeyDown: handleKeyDown,
44
- children: [
45
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-4 flex items-center justify-between", children: [
46
- /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-lg font-semibold text-foreground", children: "Save As" }),
47
- /* @__PURE__ */ jsxRuntime.jsx(
48
- "button",
49
- {
50
- onClick: onClose,
51
- className: "flex h-7 w-7 items-center justify-center rounded-md text-muted-foreground transition hover:bg-secondary hover:text-foreground",
52
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "h-4 w-4" })
53
- }
54
- )
55
- ] }),
56
- /* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: handleSubmit, children: [
57
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-4", children: [
58
- /* @__PURE__ */ jsxRuntime.jsx(
59
- "label",
60
- {
61
- htmlFor: "workflow-name",
62
- className: "mb-2 block text-sm font-medium text-foreground",
63
- children: "Workflow Name"
64
- }
65
- ),
66
- /* @__PURE__ */ jsxRuntime.jsx(
67
- "input",
68
- {
69
- ref: inputRef,
70
- id: "workflow-name",
71
- type: "text",
72
- value: name,
73
- onChange: (e) => setName(e.target.value),
74
- placeholder: "Enter workflow name",
75
- autoFocus: true,
76
- className: "w-full rounded-md border border-border bg-secondary px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground outline-none focus:ring-1 focus:ring-ring"
77
- }
78
- )
79
- ] }),
80
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-end gap-2", children: [
81
- /* @__PURE__ */ jsxRuntime.jsx(
82
- "button",
83
- {
84
- type: "button",
85
- onClick: onClose,
86
- className: "rounded-md border border-border px-4 py-2 text-sm font-medium text-foreground transition hover:bg-secondary",
87
- children: "Cancel"
88
- }
89
- ),
90
- /* @__PURE__ */ jsxRuntime.jsx(
91
- "button",
92
- {
93
- type: "submit",
94
- disabled: !name.trim(),
95
- className: "rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground transition hover:bg-primary/90 disabled:opacity-50 disabled:cursor-not-allowed",
96
- children: "Save"
97
- }
98
- )
99
- ] })
100
- ] })
101
- ]
102
- }
103
- )
104
- ] });
105
- }
106
- function SaveIndicator() {
107
- const isDirty = chunkIHF35QZD_js.useWorkflowStore((state) => state.isDirty);
108
- const isSaving = chunkIHF35QZD_js.useWorkflowStore((state) => state.isSaving);
109
- const autoSaveEnabled = chunkAXFOCPPP_js.useSettingsStore((state) => state.autoSaveEnabled);
110
- const toggleAutoSave = chunkAXFOCPPP_js.useSettingsStore((state) => state.toggleAutoSave);
111
- if (!autoSaveEnabled) {
112
- return /* @__PURE__ */ jsxRuntime.jsxs(
113
- "button",
114
- {
115
- onClick: toggleAutoSave,
116
- title: "Click to enable auto-save",
117
- className: "flex items-center gap-1.5 text-muted-foreground text-xs hover:text-foreground transition-colors",
118
- children: [
119
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CloudOff, { className: "h-3.5 w-3.5" }),
120
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Auto-save off" })
121
- ]
122
- }
123
- );
124
- }
125
- if (isSaving) {
126
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5 text-blue-500 text-xs", children: [
127
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "h-3.5 w-3.5 animate-spin" }),
128
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Saving..." })
129
- ] });
130
- }
131
- if (isDirty) {
132
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5 text-muted-foreground text-xs", children: [
133
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Cloud, { className: "h-3.5 w-3.5" }),
134
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Unsaved" })
135
- ] });
136
- }
137
- return /* @__PURE__ */ jsxRuntime.jsxs(
138
- "button",
139
- {
140
- onClick: toggleAutoSave,
141
- title: "Click to disable auto-save",
142
- className: "flex items-center gap-1.5 text-green-500 text-xs hover:text-green-400 transition-colors",
143
- children: [
144
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { className: "h-3.5 w-3.5" }),
145
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Saved" })
146
- ]
147
- }
148
- );
149
- }
150
- function ToolbarDropdown({ label, items }) {
151
- const [isOpen, setIsOpen] = react.useState(false);
152
- const menuRef = react.useRef(null);
153
- react.useEffect(() => {
154
- if (!isOpen) return;
155
- const handleClickOutside = (event) => {
156
- if (menuRef.current && !menuRef.current.contains(event.target)) {
157
- setIsOpen(false);
158
- }
159
- };
160
- document.addEventListener("mousedown", handleClickOutside);
161
- return () => document.removeEventListener("mousedown", handleClickOutside);
162
- }, [isOpen]);
163
- react.useEffect(() => {
164
- if (!isOpen) return;
165
- const handleKeyDown = (event) => {
166
- if (event.key === "Escape") {
167
- setIsOpen(false);
168
- }
169
- };
170
- document.addEventListener("keydown", handleKeyDown);
171
- return () => document.removeEventListener("keydown", handleKeyDown);
172
- }, [isOpen]);
173
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { ref: menuRef, className: "relative", children: [
174
- /* @__PURE__ */ jsxRuntime.jsxs(
175
- "button",
176
- {
177
- onClick: () => setIsOpen(!isOpen),
178
- className: "flex items-center gap-1 rounded-md px-2.5 py-1.5 text-sm font-medium text-muted-foreground transition hover:bg-secondary hover:text-foreground",
179
- children: [
180
- label,
181
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { className: "h-3.5 w-3.5" })
182
- ]
183
- }
184
- ),
185
- isOpen && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute left-0 top-full z-50 mt-1 min-w-[180px] rounded-lg border border-border bg-card py-1 shadow-lg whitespace-nowrap", children: items.map((item) => {
186
- if (item.separator) {
187
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "my-1 h-px bg-border" }, item.id);
188
- }
189
- return /* @__PURE__ */ jsxRuntime.jsxs(
190
- "button",
191
- {
192
- onClick: () => {
193
- if (item.disabled || !item.onClick) return;
194
- item.onClick();
195
- setIsOpen(false);
196
- },
197
- disabled: item.disabled,
198
- className: "flex w-full items-center gap-2.5 px-3 py-2 text-sm text-foreground transition hover:bg-secondary disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:bg-transparent",
199
- children: [
200
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "h-4 w-4 shrink-0", children: item.icon }),
201
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: item.label }),
202
- item.external && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ml-auto text-xs text-muted-foreground", children: "\u2197" })
203
- ]
204
- },
205
- item.id
206
- );
207
- }) })
208
- ] });
209
- }
210
- function isValidWorkflow(data) {
211
- if (!data || typeof data !== "object") return false;
212
- const workflow = data;
213
- if (typeof workflow.name !== "string") return false;
214
- if (!Array.isArray(workflow.nodes)) return false;
215
- if (!Array.isArray(workflow.edges)) return false;
216
- for (const node of workflow.nodes) {
217
- if (!node || typeof node !== "object") return false;
218
- const n = node;
219
- if (typeof n.id !== "string") return false;
220
- if (typeof n.type !== "string") return false;
221
- if (!n.position || typeof n.position !== "object") return false;
222
- }
223
- for (const edge of workflow.edges) {
224
- if (!edge || typeof edge !== "object") return false;
225
- const e = edge;
226
- if (typeof e.id !== "string") return false;
227
- if (typeof e.source !== "string") return false;
228
- if (typeof e.target !== "string") return false;
229
- }
230
- return true;
231
- }
232
- function Toolbar({
233
- onAutoLayout,
234
- onSaveAs,
235
- fileMenuItemsPrepend,
236
- fileMenuItemsAppend,
237
- additionalMenus,
238
- logoHref = "/",
239
- logoSrc = "https://cdn.genfeed.ai/assets/branding/logo-white.png",
240
- showSettings = true,
241
- rightContent
242
- }) {
243
- const { exportWorkflow, workflowName } = chunkIHF35QZD_js.useWorkflowStore();
244
- const { undo, redo } = chunkIHF35QZD_js.useWorkflowStore.temporal.getState();
245
- const [canUndo, setCanUndo] = react.useState(false);
246
- const [canRedo, setCanRedo] = react.useState(false);
247
- const [showSaveAsDialog, setShowSaveAsDialog] = react.useState(false);
248
- const validationErrors = chunkAXFOCPPP_js.useExecutionStore((state) => state.validationErrors);
249
- const clearValidationErrors = chunkAXFOCPPP_js.useExecutionStore((state) => state.clearValidationErrors);
250
- const { openModal } = chunkAXFOCPPP_js.useUIStore();
251
- const debugMode = chunkAXFOCPPP_js.useSettingsStore((s) => s.debugMode);
252
- const uniqueErrorMessages = react.useMemo(() => {
253
- if (!validationErrors?.errors.length) return [];
254
- return [...new Set(validationErrors.errors.map((e) => e.message))];
255
- }, [validationErrors]);
256
- react.useEffect(() => {
257
- const unsubscribe = chunkIHF35QZD_js.useWorkflowStore.temporal.subscribe((state) => {
258
- setCanUndo(state.pastStates.length > 0);
259
- setCanRedo(state.futureStates.length > 0);
260
- });
261
- const temporal = chunkIHF35QZD_js.useWorkflowStore.temporal.getState();
262
- setCanUndo(temporal.pastStates.length > 0);
263
- setCanRedo(temporal.futureStates.length > 0);
264
- return unsubscribe;
265
- }, []);
266
- const handleExport = react.useCallback(() => {
267
- const workflow = exportWorkflow();
268
- const blob = new Blob([JSON.stringify(workflow, null, 2)], {
269
- type: "application/json"
270
- });
271
- const url = URL.createObjectURL(blob);
272
- const link = document.createElement("a");
273
- link.href = url;
274
- link.download = `${workflow.name.toLowerCase().replace(/\s+/g, "-")}.json`;
275
- document.body.appendChild(link);
276
- link.click();
277
- document.body.removeChild(link);
278
- URL.revokeObjectURL(url);
279
- }, [exportWorkflow]);
280
- const handleImport = react.useCallback(() => {
281
- const input = document.createElement("input");
282
- input.type = "file";
283
- input.accept = ".json";
284
- input.onchange = (e) => {
285
- const file = e.target.files?.[0];
286
- if (!file) return;
287
- const reader = new FileReader();
288
- reader.onload = (event) => {
289
- try {
290
- const data = JSON.parse(event.target?.result);
291
- if (!isValidWorkflow(data)) {
292
- console.warn("[Toolbar] Invalid workflow file structure");
293
- return;
294
- }
295
- chunkIHF35QZD_js.useWorkflowStore.getState().loadWorkflow(data);
296
- } catch {
297
- console.warn("[Toolbar] Failed to parse workflow file");
298
- }
299
- };
300
- reader.readAsText(file);
301
- };
302
- input.click();
303
- }, []);
304
- const handleSaveAs = react.useCallback(
305
- (newName) => {
306
- if (onSaveAs) {
307
- onSaveAs(newName);
308
- }
309
- setShowSaveAsDialog(false);
310
- },
311
- [onSaveAs]
312
- );
313
- const fileMenuItems = react.useMemo(() => {
314
- const items = [];
315
- if (fileMenuItemsPrepend?.length) {
316
- items.push(...fileMenuItemsPrepend);
317
- items.push({ id: "separator-prepend", separator: true });
318
- }
319
- if (onSaveAs) {
320
- items.push({
321
- id: "saveAs",
322
- label: "Save As...",
323
- icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.SaveAll, { className: "h-4 w-4" }),
324
- onClick: () => setShowSaveAsDialog(true)
325
- });
326
- items.push({ id: "separator-saveas", separator: true });
327
- }
328
- items.push(
329
- {
330
- id: "export",
331
- label: "Export Workflow",
332
- icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Save, { className: "h-4 w-4" }),
333
- onClick: handleExport
334
- },
335
- {
336
- id: "import",
337
- label: "Import Workflow",
338
- icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.FolderOpen, { className: "h-4 w-4" }),
339
- onClick: handleImport
340
- }
341
- );
342
- if (fileMenuItemsAppend?.length) {
343
- items.push({ id: "separator-append", separator: true });
344
- items.push(...fileMenuItemsAppend);
345
- }
346
- return items;
347
- }, [handleExport, handleImport, onSaveAs, fileMenuItemsPrepend, fileMenuItemsAppend]);
348
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex h-14 items-center gap-3 border-b border-border bg-card px-4", children: [
349
- /* @__PURE__ */ jsxRuntime.jsx(
350
- "a",
351
- {
352
- href: logoHref,
353
- title: "Go to Dashboard",
354
- className: "flex h-6 w-6 items-center justify-center hover:opacity-90 transition",
355
- children: /* @__PURE__ */ jsxRuntime.jsx(
356
- "img",
357
- {
358
- src: logoSrc,
359
- alt: "Genfeed",
360
- className: "h-6 w-6 object-contain"
361
- }
362
- )
363
- }
364
- ),
365
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-8 w-px bg-border" }),
366
- /* @__PURE__ */ jsxRuntime.jsx(ToolbarDropdown, { label: "File", items: fileMenuItems }),
367
- additionalMenus?.map((menu) => /* @__PURE__ */ jsxRuntime.jsx(ToolbarDropdown, { label: menu.label, items: menu.items }, menu.label)),
368
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-8 w-px bg-border" }),
369
- debugMode && /* @__PURE__ */ jsxRuntime.jsxs(
370
- "button",
371
- {
372
- onClick: () => openModal("settings"),
373
- title: "Debug mode active - API calls are mocked",
374
- className: "flex items-center gap-1.5 rounded-md border border-amber-500/30 bg-amber-500/10 px-2 py-1 text-sm text-amber-500 transition hover:bg-amber-500/20",
375
- children: [
376
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Bug, { className: "h-4 w-4" }),
377
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: "Debug" })
378
- ]
379
- }
380
- ),
381
- onAutoLayout && /* @__PURE__ */ jsxRuntime.jsx(
382
- "button",
383
- {
384
- onClick: () => onAutoLayout("LR"),
385
- title: "Auto-layout nodes",
386
- className: "flex h-8 w-8 items-center justify-center rounded-md text-muted-foreground transition hover:bg-secondary hover:text-foreground",
387
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.LayoutGrid, { className: "h-4 w-4" })
388
- }
389
- ),
390
- /* @__PURE__ */ jsxRuntime.jsx(
391
- "button",
392
- {
393
- onClick: () => undo(),
394
- disabled: !canUndo,
395
- title: "Undo (Ctrl+Z)",
396
- className: "flex h-8 w-8 items-center justify-center rounded-md text-muted-foreground transition hover:bg-secondary hover:text-foreground disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:bg-transparent",
397
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Undo2, { className: "h-4 w-4" })
398
- }
399
- ),
400
- /* @__PURE__ */ jsxRuntime.jsx(
401
- "button",
402
- {
403
- onClick: () => redo(),
404
- disabled: !canRedo,
405
- title: "Redo (Ctrl+Shift+Z)",
406
- className: "flex h-8 w-8 items-center justify-center rounded-md text-muted-foreground transition hover:bg-secondary hover:text-foreground disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:bg-transparent",
407
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Redo2, { className: "h-4 w-4" })
408
- }
409
- ),
410
- /* @__PURE__ */ jsxRuntime.jsx(SaveIndicator, {}),
411
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1" }),
412
- rightContent,
413
- showSettings && /* @__PURE__ */ jsxRuntime.jsx(
414
- "button",
415
- {
416
- onClick: () => openModal("settings"),
417
- title: "Settings",
418
- className: "flex h-8 w-8 items-center justify-center rounded-md text-muted-foreground transition hover:bg-secondary hover:text-foreground",
419
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Settings, { className: "h-4 w-4" })
420
- }
421
- ),
422
- uniqueErrorMessages.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "fixed right-4 top-20 z-50 max-w-sm rounded-lg border border-destructive/30 bg-destructive/10 p-4 shadow-xl", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start gap-3", children: [
423
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertCircle, { className: "mt-0.5 h-5 w-5 shrink-0 text-destructive" }),
424
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 flex-1", children: [
425
- /* @__PURE__ */ jsxRuntime.jsx("h4", { className: "mb-2 text-sm font-medium text-destructive", children: "Cannot run workflow" }),
426
- /* @__PURE__ */ jsxRuntime.jsxs("ul", { className: "space-y-1", children: [
427
- uniqueErrorMessages.slice(0, 5).map((message) => /* @__PURE__ */ jsxRuntime.jsx("li", { className: "text-xs text-destructive/80", children: message }, message)),
428
- uniqueErrorMessages.length > 5 && /* @__PURE__ */ jsxRuntime.jsxs("li", { className: "text-xs text-destructive/60", children: [
429
- "+",
430
- uniqueErrorMessages.length - 5,
431
- " more errors"
432
- ] })
433
- ] })
434
- ] }),
435
- /* @__PURE__ */ jsxRuntime.jsx(
436
- "button",
437
- {
438
- onClick: clearValidationErrors,
439
- className: "flex h-7 w-7 items-center justify-center rounded-md text-destructive transition hover:bg-destructive/20",
440
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "h-4 w-4" })
441
- }
442
- )
443
- ] }) }),
444
- /* @__PURE__ */ jsxRuntime.jsx(
445
- SaveAsDialog,
446
- {
447
- isOpen: showSaveAsDialog,
448
- currentName: workflowName,
449
- onSave: handleSaveAs,
450
- onClose: () => setShowSaveAsDialog(false)
451
- }
452
- )
453
- ] });
454
- }
455
- var MIN_BATCH = 1;
456
- var MAX_BATCH = 10;
457
- function BottomBar() {
458
- const [batchCount, setBatchCount] = react.useState(1);
459
- const [currentBatchRun, setCurrentBatchRun] = react.useState(0);
460
- const [isBatchRunning, setIsBatchRunning] = react.useState(false);
461
- const [dropdownOpen, setDropdownOpen] = react.useState(false);
462
- const batchCancelledRef = react.useRef(false);
463
- const dropdownRef = react.useRef(null);
464
- const isRunning = chunkAXFOCPPP_js.useExecutionStore((s) => s.isRunning);
465
- const executeWorkflow = chunkAXFOCPPP_js.useExecutionStore((s) => s.executeWorkflow);
466
- const executeSelectedNodes = chunkAXFOCPPP_js.useExecutionStore((s) => s.executeSelectedNodes);
467
- const resumeFromFailed = chunkAXFOCPPP_js.useExecutionStore((s) => s.resumeFromFailed);
468
- const canResumeFromFailed = chunkAXFOCPPP_js.useExecutionStore((s) => s.canResumeFromFailed);
469
- const stopExecution = chunkAXFOCPPP_js.useExecutionStore((s) => s.stopExecution);
470
- chunkAXFOCPPP_js.useExecutionStore((s) => s.lastFailedNodeId);
471
- const selectedNodeIds = chunkIHF35QZD_js.useWorkflowStore((s) => s.selectedNodeIds);
472
- const nodes = chunkIHF35QZD_js.useWorkflowStore((s) => s.nodes);
473
- const validateWorkflow = chunkIHF35QZD_js.useWorkflowStore((s) => s.validateWorkflow);
474
- const canRunWorkflow = react.useMemo(() => {
475
- if (nodes.length === 0) return false;
476
- const validation = validateWorkflow();
477
- return validation.isValid;
478
- }, [nodes, validateWorkflow]);
479
- const hasSelection = selectedNodeIds.length > 0;
480
- const showResume = canResumeFromFailed();
481
- const decrementBatch = react.useCallback(() => {
482
- setBatchCount((prev) => Math.max(MIN_BATCH, prev - 1));
483
- }, []);
484
- const incrementBatch = react.useCallback(() => {
485
- setBatchCount((prev) => Math.min(MAX_BATCH, prev + 1));
486
- }, []);
487
- const waitForExecutionEnd = react.useCallback(() => {
488
- return new Promise((resolve) => {
489
- if (!chunkAXFOCPPP_js.useExecutionStore.getState().isRunning) {
490
- resolve();
491
- return;
492
- }
493
- const unsubscribe = chunkAXFOCPPP_js.useExecutionStore.subscribe((state) => {
494
- if (!state.isRunning) {
495
- unsubscribe();
496
- resolve();
497
- }
498
- });
499
- });
500
- }, []);
501
- const runBatch = react.useCallback(async () => {
502
- batchCancelledRef.current = false;
503
- setIsBatchRunning(true);
504
- const accumulatedImages = /* @__PURE__ */ new Map();
505
- for (let i = 0; i < batchCount; i++) {
506
- if (batchCancelledRef.current) break;
507
- setCurrentBatchRun(i + 1);
508
- executeWorkflow();
509
- await new Promise((r) => setTimeout(r, 50));
510
- if (!chunkAXFOCPPP_js.useExecutionStore.getState().isRunning) break;
511
- await waitForExecutionEnd();
512
- if (chunkAXFOCPPP_js.useExecutionStore.getState().lastFailedNodeId) break;
513
- const { nodes: currentNodes, updateNodeData } = chunkIHF35QZD_js.useWorkflowStore.getState();
514
- for (const node of currentNodes) {
515
- if (node.type !== "imageGen") continue;
516
- const nodeData = node.data;
517
- const newImages = nodeData.outputImages;
518
- if (!newImages?.length) continue;
519
- const existing = accumulatedImages.get(node.id) || [];
520
- const merged = [...existing, ...newImages];
521
- accumulatedImages.set(node.id, merged);
522
- updateNodeData(node.id, { outputImages: merged });
523
- }
524
- }
525
- setIsBatchRunning(false);
526
- setCurrentBatchRun(0);
527
- }, [batchCount, executeWorkflow, waitForExecutionEnd]);
528
- const handlePrimaryClick = react.useCallback(() => {
529
- if (isRunning || isBatchRunning) {
530
- batchCancelledRef.current = true;
531
- stopExecution();
532
- return;
533
- }
534
- if (batchCount > 1) {
535
- runBatch();
536
- } else {
537
- executeWorkflow();
538
- }
539
- }, [isRunning, isBatchRunning, batchCount, runBatch, executeWorkflow, stopExecution]);
540
- const handleRunSelected = react.useCallback(() => {
541
- if (!isRunning && hasSelection) {
542
- executeSelectedNodes();
543
- setDropdownOpen(false);
544
- }
545
- }, [isRunning, hasSelection, executeSelectedNodes]);
546
- const handleResume = react.useCallback(() => {
547
- if (canResumeFromFailed()) {
548
- resumeFromFailed();
549
- setDropdownOpen(false);
550
- }
551
- }, [canResumeFromFailed, resumeFromFailed]);
552
- const isActive = isRunning || isBatchRunning;
553
- return /* @__PURE__ */ jsxRuntime.jsx(
554
- "div",
555
- {
556
- className: "fixed bottom-5 left-1/2 z-50 -translate-x-1/2",
557
- onMouseDown: (e) => e.stopPropagation(),
558
- children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1 rounded-md border border-neutral-700/80 bg-neutral-800/95 px-2 py-1 shadow-lg backdrop-blur-sm", children: [
559
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-0.5", children: [
560
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "mr-0.5 text-[11px] text-neutral-400", children: "Batch" }),
561
- /* @__PURE__ */ jsxRuntime.jsx(
562
- "button",
563
- {
564
- onClick: decrementBatch,
565
- disabled: batchCount <= MIN_BATCH || isActive,
566
- className: "flex h-5 w-5 items-center justify-center rounded text-neutral-400 transition hover:bg-neutral-700 hover:text-white disabled:opacity-40 disabled:hover:bg-transparent",
567
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Minus, { className: "h-2.5 w-2.5" })
568
- }
569
- ),
570
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "w-4 text-center text-xs font-medium tabular-nums text-white", children: batchCount }),
571
- /* @__PURE__ */ jsxRuntime.jsx(
572
- "button",
573
- {
574
- onClick: incrementBatch,
575
- disabled: batchCount >= MAX_BATCH || isActive,
576
- className: "flex h-5 w-5 items-center justify-center rounded text-neutral-400 transition hover:bg-neutral-700 hover:text-white disabled:opacity-40 disabled:hover:bg-transparent",
577
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, { className: "h-2.5 w-2.5" })
578
- }
579
- )
580
- ] }),
581
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mx-1 h-4 w-px bg-neutral-600" }),
582
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative flex items-center", children: [
583
- /* @__PURE__ */ jsxRuntime.jsx(
584
- "button",
585
- {
586
- onClick: (e) => {
587
- e.stopPropagation();
588
- handlePrimaryClick();
589
- },
590
- disabled: !isActive && !canRunWorkflow,
591
- className: `flex h-7 items-center gap-1.5 rounded-l px-3 text-xs font-medium transition ${isActive ? "bg-red-500/90 text-white hover:bg-red-500" : canRunWorkflow ? "bg-white text-black hover:bg-neutral-200" : "bg-neutral-600 text-neutral-400"} disabled:cursor-not-allowed`,
592
- children: isActive ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
593
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Square, { className: "h-3.5 w-3.5" }),
594
- "Stop"
595
- ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
596
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Play, { className: "h-3.5 w-3.5 fill-current" }),
597
- "Run"
598
- ] })
599
- }
600
- ),
601
- /* @__PURE__ */ jsxRuntime.jsx(
602
- "button",
603
- {
604
- onPointerDown: (e) => {
605
- e.stopPropagation();
606
- e.preventDefault();
607
- },
608
- onClick: (e) => {
609
- e.stopPropagation();
610
- setDropdownOpen((prev) => !prev);
611
- },
612
- disabled: isActive,
613
- className: `flex h-7 items-center rounded-r border-l px-1.5 transition ${isActive ? "border-red-400/30 bg-red-500/90 text-white" : canRunWorkflow ? "border-neutral-300 bg-white text-black hover:bg-neutral-200" : "border-neutral-500 bg-neutral-600 text-neutral-400"} disabled:cursor-not-allowed`,
614
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronUp, { className: "h-3.5 w-3.5" })
615
- }
616
- ),
617
- dropdownOpen && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
618
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "fixed inset-0 z-40", onClick: () => setDropdownOpen(false) }),
619
- /* @__PURE__ */ jsxRuntime.jsxs(
620
- "div",
621
- {
622
- ref: dropdownRef,
623
- onClick: (e) => e.stopPropagation(),
624
- onPointerDown: (e) => e.stopPropagation(),
625
- className: "absolute bottom-full left-0 z-50 mb-1.5 min-w-[180px] rounded-md border border-neutral-700 bg-neutral-800 py-0.5 shadow-xl",
626
- children: [
627
- /* @__PURE__ */ jsxRuntime.jsxs(
628
- "button",
629
- {
630
- onClick: () => {
631
- executeWorkflow();
632
- setDropdownOpen(false);
633
- },
634
- disabled: !canRunWorkflow,
635
- className: "flex w-full items-center gap-1.5 px-2.5 py-1.5 text-left text-xs text-neutral-200 transition hover:bg-neutral-700 disabled:text-neutral-500 disabled:hover:bg-transparent",
636
- children: [
637
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Play, { className: "h-3 w-3" }),
638
- "Run Workflow"
639
- ]
640
- }
641
- ),
642
- /* @__PURE__ */ jsxRuntime.jsxs(
643
- "button",
644
- {
645
- onClick: handleRunSelected,
646
- disabled: !hasSelection || isRunning,
647
- className: "flex w-full items-center gap-1.5 px-2.5 py-1.5 text-left text-xs text-neutral-200 transition hover:bg-neutral-700 disabled:text-neutral-500 disabled:hover:bg-transparent",
648
- children: [
649
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.PlayCircle, { className: "h-3 w-3" }),
650
- "Run Selected (",
651
- selectedNodeIds.length,
652
- ")"
653
- ]
654
- }
655
- ),
656
- showResume && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
657
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mx-2 my-0.5 h-px bg-neutral-700" }),
658
- /* @__PURE__ */ jsxRuntime.jsxs(
659
- "button",
660
- {
661
- onClick: handleResume,
662
- className: "flex w-full items-center gap-1.5 px-2.5 py-1.5 text-left text-xs text-neutral-200 transition hover:bg-neutral-700",
663
- children: [
664
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.RotateCcw, { className: "h-3 w-3" }),
665
- "Resume from Failed"
666
- ]
667
- }
668
- )
669
- ] })
670
- ]
671
- }
672
- )
673
- ] })
674
- ] }),
675
- isBatchRunning && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
676
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mx-1 h-4 w-px bg-neutral-600" }),
677
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-[11px] tabular-nums text-neutral-400", children: [
678
- currentBatchRun,
679
- "/",
680
- batchCount
681
- ] })
682
- ] })
683
- ] })
684
- }
685
- );
686
- }
687
- function OverflowMenu({ items }) {
688
- const [isOpen, setIsOpen] = react.useState(false);
689
- const menuRef = react.useRef(null);
690
- react.useEffect(() => {
691
- if (!isOpen) return;
692
- const handleClickOutside = (event) => {
693
- if (menuRef.current && !menuRef.current.contains(event.target)) {
694
- setIsOpen(false);
695
- }
696
- };
697
- document.addEventListener("mousedown", handleClickOutside);
698
- return () => document.removeEventListener("mousedown", handleClickOutside);
699
- }, [isOpen]);
700
- react.useEffect(() => {
701
- if (!isOpen) return;
702
- const handleKeyDown = (event) => {
703
- if (event.key === "Escape") {
704
- setIsOpen(false);
705
- }
706
- };
707
- document.addEventListener("keydown", handleKeyDown);
708
- return () => document.removeEventListener("keydown", handleKeyDown);
709
- }, [isOpen]);
710
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { ref: menuRef, className: "relative", children: [
711
- /* @__PURE__ */ jsxRuntime.jsx(
712
- "button",
713
- {
714
- onClick: () => setIsOpen(!isOpen),
715
- title: "More options",
716
- className: "flex h-8 w-8 items-center justify-center rounded-md text-muted-foreground transition hover:bg-secondary hover:text-foreground",
717
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.MoreVertical, { className: "h-4 w-4" })
718
- }
719
- ),
720
- isOpen && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute right-0 top-full z-50 mt-1 min-w-[180px] rounded-lg border border-border bg-card py-1 shadow-lg", children: items.map((item) => /* @__PURE__ */ jsxRuntime.jsxs(
721
- "button",
722
- {
723
- onClick: () => {
724
- item.onClick?.();
725
- setIsOpen(false);
726
- },
727
- className: "flex w-full items-center gap-2.5 px-3 py-2 text-sm text-foreground transition hover:bg-secondary",
728
- children: [
729
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "h-4 w-4 shrink-0", children: item.icon }),
730
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: item.label }),
731
- item.external && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ml-auto text-xs text-muted-foreground", children: "\u2197" })
732
- ]
733
- },
734
- item.id
735
- )) })
736
- ] });
737
- }
738
-
739
- exports.BottomBar = BottomBar;
740
- exports.OverflowMenu = OverflowMenu;
741
- exports.SaveAsDialog = SaveAsDialog;
742
- exports.SaveIndicator = SaveIndicator;
743
- exports.Toolbar = Toolbar;
744
- exports.ToolbarDropdown = ToolbarDropdown;