@genfeedai/workflow-ui 0.1.0

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 (86) hide show
  1. package/dist/canvas.d.mts +27 -0
  2. package/dist/canvas.d.ts +27 -0
  3. package/dist/canvas.js +45 -0
  4. package/dist/canvas.mjs +16 -0
  5. package/dist/chunk-22PDGHNQ.mjs +737 -0
  6. package/dist/chunk-3SPPKCWR.js +458 -0
  7. package/dist/chunk-3YFFDHC5.js +300 -0
  8. package/dist/chunk-5HJFQVUR.js +61 -0
  9. package/dist/chunk-5LQ4QBR5.js +2 -0
  10. package/dist/chunk-6DOEUDD5.js +254 -0
  11. package/dist/chunk-7SKSRSS7.mjs +57 -0
  12. package/dist/chunk-AC6TWLRT.mjs +27 -0
  13. package/dist/chunk-ADWNF7V3.js +120 -0
  14. package/dist/chunk-BJ3R5R32.mjs +2163 -0
  15. package/dist/chunk-CETJJ73S.js +1555 -0
  16. package/dist/chunk-CSUBLSKZ.mjs +1002 -0
  17. package/dist/chunk-CV4M7CNU.mjs +251 -0
  18. package/dist/chunk-E323WAZG.mjs +272 -0
  19. package/dist/chunk-E544XUBL.js +378 -0
  20. package/dist/chunk-EC2ZIWOK.js +1007 -0
  21. package/dist/chunk-EFXQT23N.mjs +99 -0
  22. package/dist/chunk-EMUMKW5C.js +107 -0
  23. package/dist/chunk-FOMOOERN.js +2 -0
  24. package/dist/chunk-FT33LFII.mjs +21 -0
  25. package/dist/chunk-FT64PCUP.mjs +533 -0
  26. package/dist/chunk-H6LZKSLY.js +5678 -0
  27. package/dist/chunk-HPQT36RR.js +543 -0
  28. package/dist/chunk-JLWKW3G5.js +2 -0
  29. package/dist/chunk-L5TF4EHW.mjs +1 -0
  30. package/dist/chunk-LAJ34AH2.mjs +374 -0
  31. package/dist/chunk-LDN7IX4Y.mjs +1 -0
  32. package/dist/chunk-MLJJBBTB.mjs +1 -0
  33. package/dist/chunk-NSDLGLAQ.js +2166 -0
  34. package/dist/chunk-RJ262NXS.js +24 -0
  35. package/dist/chunk-RXNEDWK2.js +141 -0
  36. package/dist/chunk-SW7QNEZU.js +744 -0
  37. package/dist/chunk-UQQUWGHW.mjs +118 -0
  38. package/dist/chunk-VOGL2WCE.mjs +1542 -0
  39. package/dist/chunk-VRN3UWE5.mjs +138 -0
  40. package/dist/chunk-XV5Z5XYR.mjs +5640 -0
  41. package/dist/chunk-Z7PWFZG5.js +30 -0
  42. package/dist/chunk-ZJD5WMR3.mjs +418 -0
  43. package/dist/hooks.d.mts +255 -0
  44. package/dist/hooks.d.ts +255 -0
  45. package/dist/hooks.js +56 -0
  46. package/dist/hooks.mjs +11 -0
  47. package/dist/index.d.mts +29 -0
  48. package/dist/index.d.ts +29 -0
  49. package/dist/index.js +180 -0
  50. package/dist/index.mjs +19 -0
  51. package/dist/lib.d.mts +164 -0
  52. package/dist/lib.d.ts +164 -0
  53. package/dist/lib.js +144 -0
  54. package/dist/lib.mjs +3 -0
  55. package/dist/nodes.d.mts +128 -0
  56. package/dist/nodes.d.ts +128 -0
  57. package/dist/nodes.js +151 -0
  58. package/dist/nodes.mjs +14 -0
  59. package/dist/panels.d.mts +22 -0
  60. package/dist/panels.d.ts +22 -0
  61. package/dist/panels.js +21 -0
  62. package/dist/panels.mjs +4 -0
  63. package/dist/promptLibraryStore-BZnfmEkc.d.ts +464 -0
  64. package/dist/promptLibraryStore-zqb59nsu.d.mts +464 -0
  65. package/dist/provider.d.mts +29 -0
  66. package/dist/provider.d.ts +29 -0
  67. package/dist/provider.js +17 -0
  68. package/dist/provider.mjs +4 -0
  69. package/dist/stores.d.mts +96 -0
  70. package/dist/stores.d.ts +96 -0
  71. package/dist/stores.js +113 -0
  72. package/dist/stores.mjs +43 -0
  73. package/dist/toolbar.d.mts +73 -0
  74. package/dist/toolbar.d.ts +73 -0
  75. package/dist/toolbar.js +34 -0
  76. package/dist/toolbar.mjs +5 -0
  77. package/dist/types-ipAnBzAJ.d.mts +46 -0
  78. package/dist/types-ipAnBzAJ.d.ts +46 -0
  79. package/dist/ui.d.mts +67 -0
  80. package/dist/ui.d.ts +67 -0
  81. package/dist/ui.js +84 -0
  82. package/dist/ui.mjs +3 -0
  83. package/dist/workflowStore-4EGKJLYK.mjs +3 -0
  84. package/dist/workflowStore-KM32FDL7.js +12 -0
  85. package/package.json +117 -0
  86. package/src/styles/workflow-ui.css +186 -0
@@ -0,0 +1,1002 @@
1
+ import { require_dist, useWorkflowStore } from './chunk-BJ3R5R32.mjs';
2
+ import { __toESM } from './chunk-AC6TWLRT.mjs';
3
+ import { create } from 'zustand';
4
+
5
+ var notificationId = 0;
6
+ var useUIStore = create((set) => ({
7
+ showPalette: true,
8
+ showMinimap: true,
9
+ showAIGenerator: false,
10
+ showDebugPanel: false,
11
+ selectedNodeId: null,
12
+ selectedEdgeId: null,
13
+ highlightedNodeIds: [],
14
+ activeModal: null,
15
+ nodeDetailNodeId: null,
16
+ nodeDetailActiveTab: "preview",
17
+ nodeDetailStartIndex: 0,
18
+ notifications: [],
19
+ togglePalette: () => {
20
+ set((state) => ({ showPalette: !state.showPalette }));
21
+ },
22
+ toggleMinimap: () => {
23
+ set((state) => ({ showMinimap: !state.showMinimap }));
24
+ },
25
+ toggleAIGenerator: () => {
26
+ set((state) => ({ showAIGenerator: !state.showAIGenerator }));
27
+ },
28
+ toggleDebugPanel: () => {
29
+ set((state) => ({ showDebugPanel: !state.showDebugPanel }));
30
+ },
31
+ setShowDebugPanel: (show) => {
32
+ set({ showDebugPanel: show });
33
+ },
34
+ selectNode: (nodeId) => {
35
+ set({ selectedNodeId: nodeId, selectedEdgeId: null });
36
+ },
37
+ selectEdge: (edgeId) => {
38
+ set({ selectedEdgeId: edgeId, selectedNodeId: null });
39
+ },
40
+ setHighlightedNodeIds: (ids) => {
41
+ set({ highlightedNodeIds: ids });
42
+ },
43
+ openModal: (modal) => {
44
+ set({ activeModal: modal });
45
+ },
46
+ closeModal: () => {
47
+ set({ activeModal: null });
48
+ },
49
+ openNodeDetailModal: (nodeId, tab = "preview", startIndex = 0) => {
50
+ set({
51
+ activeModal: "nodeDetail",
52
+ nodeDetailNodeId: nodeId,
53
+ nodeDetailActiveTab: tab,
54
+ nodeDetailStartIndex: startIndex
55
+ });
56
+ },
57
+ closeNodeDetailModal: () => {
58
+ set({
59
+ activeModal: null,
60
+ nodeDetailNodeId: null,
61
+ nodeDetailActiveTab: "preview",
62
+ nodeDetailStartIndex: 0
63
+ });
64
+ },
65
+ setNodeDetailTab: (tab) => {
66
+ set({ nodeDetailActiveTab: tab });
67
+ },
68
+ addNotification: (notification) => {
69
+ const id = `notification-${++notificationId}`;
70
+ set((state) => ({
71
+ notifications: [...state.notifications, { ...notification, id }]
72
+ }));
73
+ if (notification.duration !== 0) {
74
+ setTimeout(() => {
75
+ set((state) => ({
76
+ notifications: state.notifications.filter((n) => n.id !== id)
77
+ }));
78
+ }, notification.duration ?? 5e3);
79
+ }
80
+ },
81
+ removeNotification: (id) => {
82
+ set((state) => ({
83
+ notifications: state.notifications.filter((n) => n.id !== id)
84
+ }));
85
+ }
86
+ }));
87
+
88
+ // src/stores/settingsStore.ts
89
+ var import_types = __toESM(require_dist());
90
+ var STORAGE_KEY = "genfeed-settings";
91
+ var MAX_RECENT_MODELS = 8;
92
+ var DEFAULT_SETTINGS = {
93
+ providers: {
94
+ replicate: { apiKey: null, enabled: true },
95
+ fal: { apiKey: null, enabled: false },
96
+ huggingface: { apiKey: null, enabled: false },
97
+ "genfeed-ai": { apiKey: null, enabled: true }
98
+ },
99
+ defaults: {
100
+ imageModel: "nano-banana-pro",
101
+ imageProvider: "replicate",
102
+ videoModel: "veo-3.1",
103
+ videoProvider: "replicate"
104
+ },
105
+ edgeStyle: "default",
106
+ showMinimap: true,
107
+ autoSaveEnabled: true,
108
+ recentModels: [],
109
+ hasSeenWelcome: false,
110
+ debugMode: false
111
+ };
112
+ function loadFromStorage() {
113
+ if (typeof window === "undefined") return {};
114
+ try {
115
+ const stored = localStorage.getItem(STORAGE_KEY);
116
+ if (stored) {
117
+ const parsed = JSON.parse(stored);
118
+ return {
119
+ providers: { ...DEFAULT_SETTINGS.providers, ...parsed.providers },
120
+ defaults: { ...DEFAULT_SETTINGS.defaults, ...parsed.defaults },
121
+ edgeStyle: parsed.edgeStyle === "bezier" ? "default" : parsed.edgeStyle ?? DEFAULT_SETTINGS.edgeStyle,
122
+ showMinimap: parsed.showMinimap ?? DEFAULT_SETTINGS.showMinimap,
123
+ autoSaveEnabled: parsed.autoSaveEnabled ?? true,
124
+ recentModels: parsed.recentModels ?? [],
125
+ hasSeenWelcome: parsed.hasSeenWelcome ?? false,
126
+ debugMode: parsed.debugMode ?? false
127
+ };
128
+ }
129
+ } catch {
130
+ }
131
+ return {};
132
+ }
133
+ function saveToStorage(state) {
134
+ if (typeof window === "undefined") return;
135
+ try {
136
+ const toSave = {
137
+ providers: {
138
+ replicate: {
139
+ apiKey: state.providers.replicate.apiKey,
140
+ enabled: state.providers.replicate.enabled
141
+ },
142
+ fal: {
143
+ apiKey: state.providers.fal.apiKey,
144
+ enabled: state.providers.fal.enabled
145
+ },
146
+ huggingface: {
147
+ apiKey: state.providers.huggingface.apiKey,
148
+ enabled: state.providers.huggingface.enabled
149
+ }
150
+ },
151
+ defaults: state.defaults,
152
+ edgeStyle: state.edgeStyle,
153
+ showMinimap: state.showMinimap,
154
+ autoSaveEnabled: state.autoSaveEnabled,
155
+ recentModels: state.recentModels.slice(0, MAX_RECENT_MODELS),
156
+ hasSeenWelcome: state.hasSeenWelcome,
157
+ debugMode: state.debugMode
158
+ };
159
+ localStorage.setItem(STORAGE_KEY, JSON.stringify(toSave));
160
+ } catch {
161
+ }
162
+ }
163
+ var initialState = { ...DEFAULT_SETTINGS, ...loadFromStorage() };
164
+ var useSettingsStore = create((set, get) => {
165
+ const setAndPersist = (updater) => {
166
+ set((state) => {
167
+ const newState = updater(state);
168
+ saveToStorage({ ...state, ...newState });
169
+ return newState;
170
+ });
171
+ };
172
+ return {
173
+ providers: initialState.providers,
174
+ defaults: initialState.defaults,
175
+ edgeStyle: initialState.edgeStyle,
176
+ showMinimap: initialState.showMinimap,
177
+ autoSaveEnabled: initialState.autoSaveEnabled,
178
+ recentModels: initialState.recentModels,
179
+ hasSeenWelcome: initialState.hasSeenWelcome,
180
+ debugMode: initialState.debugMode,
181
+ toggleAutoSave: () => {
182
+ setAndPersist((state) => ({ autoSaveEnabled: !state.autoSaveEnabled }));
183
+ },
184
+ setProviderKey: (provider, key) => {
185
+ setAndPersist((state) => ({
186
+ providers: {
187
+ ...state.providers,
188
+ [provider]: {
189
+ ...state.providers[provider],
190
+ apiKey: key,
191
+ enabled: key ? true : state.providers[provider].enabled
192
+ }
193
+ }
194
+ }));
195
+ },
196
+ setProviderEnabled: (provider, enabled) => {
197
+ setAndPersist((state) => ({
198
+ providers: {
199
+ ...state.providers,
200
+ [provider]: {
201
+ ...state.providers[provider],
202
+ enabled
203
+ }
204
+ }
205
+ }));
206
+ },
207
+ setDefaultModel: (type, model, provider) => {
208
+ setAndPersist((state) => ({
209
+ defaults: {
210
+ ...state.defaults,
211
+ ...type === "image" ? { imageModel: model, imageProvider: provider } : { videoModel: model, videoProvider: provider }
212
+ }
213
+ }));
214
+ },
215
+ setEdgeStyle: (style) => {
216
+ setAndPersist(() => ({ edgeStyle: style }));
217
+ import('./workflowStore-4EGKJLYK.mjs').then(({ useWorkflowStore: useWorkflowStore2 }) => {
218
+ useWorkflowStore2.getState().setEdgeStyle(style);
219
+ });
220
+ },
221
+ setShowMinimap: (show) => {
222
+ setAndPersist(() => ({ showMinimap: show }));
223
+ },
224
+ addRecentModel: (model) => {
225
+ setAndPersist((state) => {
226
+ const filtered = state.recentModels.filter(
227
+ (m) => !(m.id === model.id && m.provider === model.provider)
228
+ );
229
+ const newRecentModels = [{ ...model, timestamp: Date.now() }, ...filtered].slice(
230
+ 0,
231
+ MAX_RECENT_MODELS
232
+ );
233
+ return { recentModels: newRecentModels };
234
+ });
235
+ },
236
+ clearProviderKey: (provider) => {
237
+ setAndPersist((state) => ({
238
+ providers: {
239
+ ...state.providers,
240
+ [provider]: {
241
+ ...state.providers[provider],
242
+ apiKey: null
243
+ }
244
+ }
245
+ }));
246
+ },
247
+ clearAllKeys: () => {
248
+ setAndPersist((state) => ({
249
+ providers: {
250
+ replicate: { ...state.providers.replicate, apiKey: null },
251
+ fal: { ...state.providers.fal, apiKey: null },
252
+ huggingface: { ...state.providers.huggingface, apiKey: null },
253
+ "genfeed-ai": { ...state.providers["genfeed-ai"], apiKey: null }
254
+ }
255
+ }));
256
+ },
257
+ setHasSeenWelcome: (seen) => {
258
+ setAndPersist(() => ({ hasSeenWelcome: seen }));
259
+ },
260
+ setDebugMode: (enabled) => {
261
+ setAndPersist(() => ({ debugMode: enabled }));
262
+ },
263
+ isProviderConfigured: (provider) => {
264
+ const state = get();
265
+ return !!state.providers[provider].apiKey;
266
+ },
267
+ getProviderHeader: (provider) => {
268
+ const state = get();
269
+ const key = state.providers[provider].apiKey;
270
+ if (!key) return {};
271
+ const headerMap = {
272
+ [import_types.ProviderTypeEnum.REPLICATE]: "X-Replicate-Key",
273
+ [import_types.ProviderTypeEnum.FAL]: "X-Fal-Key",
274
+ [import_types.ProviderTypeEnum.HUGGINGFACE]: "X-HF-Key",
275
+ [import_types.ProviderTypeEnum.GENFEED_AI]: "X-Genfeed-Key"
276
+ };
277
+ return { [headerMap[provider]]: key };
278
+ },
279
+ // API Sync - stubbed as no-ops (consuming app provides real implementations)
280
+ isSyncing: false,
281
+ syncFromServer: async () => {
282
+ },
283
+ syncToServer: async () => {
284
+ }
285
+ };
286
+ });
287
+ var PROVIDER_INFO = {
288
+ [import_types.ProviderTypeEnum.REPLICATE]: {
289
+ name: "Replicate",
290
+ description: "Access thousands of open-source AI models",
291
+ docsUrl: "https://replicate.com/docs"
292
+ },
293
+ [import_types.ProviderTypeEnum.FAL]: {
294
+ name: "fal.ai",
295
+ description: "Fast inference for image and video generation",
296
+ docsUrl: "https://fal.ai/docs"
297
+ },
298
+ [import_types.ProviderTypeEnum.HUGGINGFACE]: {
299
+ name: "Hugging Face",
300
+ description: "The AI community platform with 500k+ models",
301
+ docsUrl: "https://huggingface.co/docs/api-inference"
302
+ },
303
+ [import_types.ProviderTypeEnum.GENFEED_AI]: {
304
+ name: "Genfeed AI",
305
+ description: "Built-in models powered by Genfeed",
306
+ docsUrl: "https://genfeed.ai/docs"
307
+ }
308
+ };
309
+
310
+ // src/stores/execution/slices/executionSlice.ts
311
+ var import_types3 = __toESM(require_dist());
312
+
313
+ // src/stores/execution/helpers/sseSubscription.ts
314
+ var import_types2 = __toESM(require_dist());
315
+
316
+ // src/stores/execution/helpers/outputHelpers.ts
317
+ function extractOutputValue(output) {
318
+ if (!output) return null;
319
+ if (typeof output === "string") {
320
+ return output;
321
+ }
322
+ if (Array.isArray(output) && output.length > 0) {
323
+ const first = output[0];
324
+ if (typeof first === "string") return first;
325
+ if (first && typeof first === "object" && "url" in first) {
326
+ return String(first.url);
327
+ }
328
+ }
329
+ if (typeof output === "object" && output !== null) {
330
+ const obj = output;
331
+ if ("url" in obj) return String(obj.url);
332
+ if ("image" in obj) return String(obj.image);
333
+ if ("video" in obj) return String(obj.video);
334
+ if ("audio" in obj) return String(obj.audio);
335
+ }
336
+ return null;
337
+ }
338
+ function extractAllOutputValues(output) {
339
+ if (!output) return [];
340
+ if (typeof output === "string") {
341
+ return [output];
342
+ }
343
+ if (Array.isArray(output)) {
344
+ return output.filter((item) => typeof item === "string");
345
+ }
346
+ if (typeof output === "object" && output !== null) {
347
+ const obj = output;
348
+ if ("images" in obj && Array.isArray(obj.images)) {
349
+ return obj.images.filter((item) => typeof item === "string");
350
+ }
351
+ if ("image" in obj && typeof obj.image === "string") {
352
+ return [obj.image];
353
+ }
354
+ }
355
+ return [];
356
+ }
357
+ function getOutputUpdate(nodeId, output, workflowStore) {
358
+ const node = workflowStore.getNodeById(nodeId);
359
+ if (!node) return {};
360
+ const nodeType = node.type;
361
+ const outputValue = extractOutputValue(output);
362
+ if (["imageGen"].includes(nodeType)) {
363
+ const allImages = extractAllOutputValues(output);
364
+ return {
365
+ outputImage: allImages[0] ?? null,
366
+ outputImages: allImages
367
+ };
368
+ }
369
+ if (["reframe", "upscale"].includes(nodeType)) {
370
+ const inputType = node.data.inputType;
371
+ if (inputType === "video") {
372
+ return { outputVideo: outputValue };
373
+ }
374
+ return { outputImage: outputValue };
375
+ }
376
+ if (["videoGen", "animation", "videoStitch", "lipSync", "voiceChange", "motionControl"].includes(
377
+ nodeType
378
+ )) {
379
+ return { outputVideo: outputValue };
380
+ }
381
+ if (nodeType === "textToSpeech") {
382
+ return { outputAudio: outputValue };
383
+ }
384
+ if (nodeType === "llm") {
385
+ const textOutput = Array.isArray(output) ? output.join("") : outputValue;
386
+ return { outputText: textOutput };
387
+ }
388
+ if (nodeType === "resize") {
389
+ return { outputMedia: outputValue };
390
+ }
391
+ return { output: outputValue };
392
+ }
393
+
394
+ // src/stores/execution/helpers/sseSubscription.ts
395
+ var API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || "http://local.genfeed.ai:3001/api";
396
+ var statusMap = {
397
+ pending: import_types2.NodeStatusEnum.IDLE,
398
+ processing: import_types2.NodeStatusEnum.PROCESSING,
399
+ complete: import_types2.NodeStatusEnum.COMPLETE,
400
+ succeeded: import_types2.NodeStatusEnum.COMPLETE,
401
+ error: import_types2.NodeStatusEnum.ERROR
402
+ };
403
+ function applyJobUpdates(jobs, workflowStore, debugMode, set, filterNodeId) {
404
+ if (!jobs || jobs.length === 0) return;
405
+ set((state) => {
406
+ let didChange = false;
407
+ const newJobs = new Map(state.jobs);
408
+ const newDebugPayloads = [];
409
+ for (const job of jobs) {
410
+ if (filterNodeId && job.nodeId !== filterNodeId) continue;
411
+ const status = job.status;
412
+ const output = job.output ?? null;
413
+ const error = job.error ?? null;
414
+ const existing = state.jobs.get(job.predictionId);
415
+ if (!existing) {
416
+ didChange = true;
417
+ newJobs.set(job.predictionId, {
418
+ nodeId: job.nodeId,
419
+ predictionId: job.predictionId,
420
+ status,
421
+ progress: 0,
422
+ output,
423
+ error,
424
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
425
+ });
426
+ } else if (existing.status !== status || existing.output !== output || existing.error !== error) {
427
+ didChange = true;
428
+ newJobs.set(job.predictionId, {
429
+ ...existing,
430
+ status,
431
+ output,
432
+ error
433
+ });
434
+ }
435
+ if (job.result?.debugPayload) {
436
+ const node = workflowStore.getNodeById(job.nodeId);
437
+ newDebugPayloads.push({
438
+ nodeId: job.nodeId,
439
+ nodeName: String(node?.data?.label || node?.data?.name || job.nodeId),
440
+ nodeType: node?.type || "unknown",
441
+ model: job.result.debugPayload.model,
442
+ input: job.result.debugPayload.input,
443
+ timestamp: job.result.debugPayload.timestamp
444
+ });
445
+ }
446
+ }
447
+ if (!didChange && newDebugPayloads.length === 0) return state;
448
+ if (newDebugPayloads.length > 0 && debugMode) {
449
+ useUIStore.getState().setShowDebugPanel(true);
450
+ }
451
+ return {
452
+ jobs: didChange ? newJobs : state.jobs,
453
+ debugPayloads: newDebugPayloads.length > 0 ? [
454
+ ...state.debugPayloads.filter(
455
+ (existing) => !newDebugPayloads.some((newP) => newP.nodeId === existing.nodeId)
456
+ ),
457
+ ...newDebugPayloads
458
+ ] : state.debugPayloads
459
+ };
460
+ });
461
+ }
462
+ async function reconcileNodeStatuses(executionId) {
463
+ try {
464
+ const response = await fetch(`${API_BASE_URL}/executions/${executionId}`);
465
+ if (!response.ok) return;
466
+ const execution = await response.json();
467
+ const workflowStore = useWorkflowStore.getState();
468
+ for (const nodeResult of execution.nodeResults || []) {
469
+ const nodeStatus = statusMap[nodeResult.status] ?? import_types2.NodeStatusEnum.IDLE;
470
+ const isSuccess = nodeResult.status === "complete" || nodeResult.status === "succeeded";
471
+ workflowStore.updateNodeData(nodeResult.nodeId, {
472
+ status: nodeStatus,
473
+ error: isSuccess ? void 0 : nodeResult.error,
474
+ ...nodeResult.output && getOutputUpdate(nodeResult.nodeId, nodeResult.output, workflowStore)
475
+ });
476
+ if (isSuccess && nodeResult.output) {
477
+ workflowStore.propagateOutputsDownstream(nodeResult.nodeId);
478
+ }
479
+ }
480
+ } catch {
481
+ }
482
+ }
483
+ function createExecutionSubscription(executionId, set) {
484
+ const eventSource = new EventSource(`${API_BASE_URL}/executions/${executionId}/stream`);
485
+ const propagatedNodeIds = /* @__PURE__ */ new Set();
486
+ set({ eventSource });
487
+ eventSource.onmessage = (event) => {
488
+ void (async () => {
489
+ try {
490
+ const data = JSON.parse(event.data);
491
+ const workflowStore = useWorkflowStore.getState();
492
+ const nodeResults = data.nodeResults || [];
493
+ for (const nodeResult of nodeResults) {
494
+ const nodeStatus = statusMap[nodeResult.status] ?? import_types2.NodeStatusEnum.IDLE;
495
+ const isSuccess = nodeResult.status === "complete" || nodeResult.status === "succeeded";
496
+ workflowStore.updateNodeData(nodeResult.nodeId, {
497
+ status: nodeStatus,
498
+ error: isSuccess ? void 0 : nodeResult.error,
499
+ ...nodeResult.output && getOutputUpdate(nodeResult.nodeId, nodeResult.output, workflowStore)
500
+ });
501
+ if ((nodeResult.status === "complete" || nodeResult.status === "succeeded") && nodeResult.output && !propagatedNodeIds.has(nodeResult.nodeId)) {
502
+ propagatedNodeIds.add(nodeResult.nodeId);
503
+ workflowStore.propagateOutputsDownstream(nodeResult.nodeId);
504
+ }
505
+ if (nodeResult.status === "error") {
506
+ set({ lastFailedNodeId: nodeResult.nodeId });
507
+ }
508
+ }
509
+ applyJobUpdates(data.jobs, workflowStore, data.debugMode, set);
510
+ const isComplete = ["completed", "failed", "cancelled", "error"].includes(data.status);
511
+ const hasFailedNode = (data.nodeResults || []).some((r) => r.status === "error");
512
+ const hasPendingNodes = (data.pendingNodes || []).length > 0;
513
+ const hasProcessingNodes = (data.nodeResults || []).some((r) => r.status === "processing");
514
+ const isDone = isComplete || hasFailedNode && !hasPendingNodes && !hasProcessingNodes;
515
+ if (isDone) {
516
+ propagatedNodeIds.clear();
517
+ eventSource.close();
518
+ await reconcileNodeStatuses(executionId);
519
+ set({ isRunning: false, eventSource: null, currentNodeId: null, jobs: /* @__PURE__ */ new Map() });
520
+ if (data.status === "failed" || hasFailedNode) {
521
+ }
522
+ }
523
+ } catch {
524
+ }
525
+ })();
526
+ };
527
+ eventSource.onerror = () => {
528
+ eventSource.close();
529
+ void reconcileNodeStatuses(executionId).then(() => {
530
+ set({ isRunning: false, eventSource: null });
531
+ });
532
+ };
533
+ return eventSource;
534
+ }
535
+ function createNodeExecutionSubscription(executionId, nodeId, set, _get) {
536
+ const eventSource = new EventSource(`${API_BASE_URL}/executions/${executionId}/stream`);
537
+ const propagatedNodeIds = /* @__PURE__ */ new Set();
538
+ eventSource.onmessage = (event) => {
539
+ void (async () => {
540
+ try {
541
+ const data = JSON.parse(event.data);
542
+ const workflowStore = useWorkflowStore.getState();
543
+ const nodeResults = data.nodeResults || [];
544
+ for (const nodeResult of nodeResults) {
545
+ const nodeStatus = statusMap[nodeResult.status] ?? import_types2.NodeStatusEnum.IDLE;
546
+ const isSuccess = nodeResult.status === "complete" || nodeResult.status === "succeeded";
547
+ workflowStore.updateNodeData(nodeResult.nodeId, {
548
+ status: nodeStatus,
549
+ error: isSuccess ? void 0 : nodeResult.error,
550
+ ...nodeResult.output && getOutputUpdate(nodeResult.nodeId, nodeResult.output, workflowStore)
551
+ });
552
+ if ((nodeResult.status === "complete" || nodeResult.status === "succeeded") && nodeResult.output && !propagatedNodeIds.has(nodeResult.nodeId)) {
553
+ propagatedNodeIds.add(nodeResult.nodeId);
554
+ workflowStore.propagateOutputsDownstream(nodeResult.nodeId);
555
+ }
556
+ if (nodeResult.status === "error") {
557
+ set({ lastFailedNodeId: nodeResult.nodeId });
558
+ }
559
+ }
560
+ applyJobUpdates(data.jobs, workflowStore, data.debugMode, set, nodeId);
561
+ const isComplete = ["completed", "failed", "cancelled", "error"].includes(data.status);
562
+ const hasFailedNode = (data.nodeResults || []).some((r) => r.status === "error");
563
+ const hasPendingNodes = (data.pendingNodes || []).length > 0;
564
+ const hasProcessingNodes = (data.nodeResults || []).some((r) => r.status === "processing");
565
+ const isDone = isComplete || hasFailedNode && !hasPendingNodes && !hasProcessingNodes;
566
+ if (isDone) {
567
+ propagatedNodeIds.clear();
568
+ eventSource.close();
569
+ await reconcileNodeStatuses(executionId);
570
+ set((state) => {
571
+ const newMap = new Map(state.activeNodeExecutions);
572
+ newMap.delete(nodeId);
573
+ return { activeNodeExecutions: newMap };
574
+ });
575
+ }
576
+ } catch {
577
+ }
578
+ })();
579
+ };
580
+ eventSource.onerror = () => {
581
+ eventSource.close();
582
+ void reconcileNodeStatuses(executionId).then(() => {
583
+ set((state) => {
584
+ const newMap = new Map(state.activeNodeExecutions);
585
+ newMap.delete(nodeId);
586
+ return { activeNodeExecutions: newMap };
587
+ });
588
+ });
589
+ };
590
+ return eventSource;
591
+ }
592
+
593
+ // src/stores/execution/slices/executionSlice.ts
594
+ var API_BASE_URL2 = process.env.NEXT_PUBLIC_API_URL || "http://local.genfeed.ai:3001/api";
595
+ async function apiPost(path, body) {
596
+ const response = await fetch(`${API_BASE_URL2}${path}`, {
597
+ method: "POST",
598
+ headers: { "Content-Type": "application/json" },
599
+ ...body && { body: JSON.stringify(body) }
600
+ });
601
+ if (!response.ok) {
602
+ const errorData = await response.json().catch(() => ({}));
603
+ throw new Error(errorData.message || `API error: ${response.status}`);
604
+ }
605
+ return response.json();
606
+ }
607
+ var createExecutionSlice = (set, get) => ({
608
+ executeWorkflow: async () => {
609
+ const { isRunning, resetExecution } = get();
610
+ if (isRunning) return;
611
+ const workflowStore = useWorkflowStore.getState();
612
+ const debugMode = useSettingsStore.getState().debugMode;
613
+ const validation = workflowStore.validateWorkflow();
614
+ if (!validation.isValid) {
615
+ set({ validationErrors: validation });
616
+ return;
617
+ }
618
+ set({ validationErrors: null });
619
+ resetExecution();
620
+ if (debugMode) {
621
+ useUIStore.getState().setShowDebugPanel(true);
622
+ }
623
+ if (workflowStore.isDirty || !workflowStore.workflowId) {
624
+ try {
625
+ await workflowStore.saveWorkflow();
626
+ } catch {
627
+ set({
628
+ validationErrors: {
629
+ isValid: false,
630
+ errors: [{ nodeId: "", message: "Failed to save workflow", severity: "error" }],
631
+ warnings: []
632
+ }
633
+ });
634
+ return;
635
+ }
636
+ }
637
+ const workflowId = workflowStore.workflowId;
638
+ if (!workflowId) {
639
+ set({
640
+ validationErrors: {
641
+ isValid: false,
642
+ errors: [{ nodeId: "", message: "Workflow must be saved first", severity: "error" }],
643
+ warnings: []
644
+ }
645
+ });
646
+ return;
647
+ }
648
+ set({ isRunning: true });
649
+ for (const node of workflowStore.nodes) {
650
+ workflowStore.updateNodeData(node.id, {
651
+ status: import_types3.NodeStatusEnum.IDLE,
652
+ error: void 0,
653
+ progress: void 0
654
+ });
655
+ }
656
+ try {
657
+ const execution = await apiPost(`/workflows/${workflowId}/execute`, {
658
+ debugMode
659
+ });
660
+ const executionId = execution._id;
661
+ set({ executionId });
662
+ createExecutionSubscription(executionId, set);
663
+ } catch (error) {
664
+ set({
665
+ isRunning: false,
666
+ validationErrors: {
667
+ isValid: false,
668
+ errors: [
669
+ {
670
+ nodeId: "",
671
+ message: error instanceof Error ? error.message : "Execution failed",
672
+ severity: "error"
673
+ }
674
+ ],
675
+ warnings: []
676
+ }
677
+ });
678
+ }
679
+ },
680
+ executeNode: async (nodeId) => {
681
+ const workflowStore = useWorkflowStore.getState();
682
+ const debugMode = useSettingsStore.getState().debugMode;
683
+ if (workflowStore.isDirty || !workflowStore.workflowId) {
684
+ try {
685
+ await workflowStore.saveWorkflow();
686
+ } catch {
687
+ workflowStore.updateNodeData(nodeId, {
688
+ status: import_types3.NodeStatusEnum.ERROR,
689
+ error: "Failed to save workflow"
690
+ });
691
+ return;
692
+ }
693
+ }
694
+ const workflowId = workflowStore.workflowId;
695
+ if (!workflowId) {
696
+ workflowStore.updateNodeData(nodeId, {
697
+ status: import_types3.NodeStatusEnum.ERROR,
698
+ error: "Workflow must be saved first"
699
+ });
700
+ return;
701
+ }
702
+ if (debugMode) {
703
+ useUIStore.getState().setShowDebugPanel(true);
704
+ }
705
+ try {
706
+ const execution = await apiPost(`/workflows/${workflowId}/execute`, {
707
+ debugMode,
708
+ selectedNodeIds: [nodeId]
709
+ });
710
+ const executionId = execution._id;
711
+ const eventSource = createNodeExecutionSubscription(executionId, nodeId, set, get);
712
+ const nodeExecution = {
713
+ executionId,
714
+ nodeIds: [nodeId],
715
+ eventSource
716
+ };
717
+ set((state) => {
718
+ const newMap = new Map(state.activeNodeExecutions);
719
+ newMap.set(nodeId, nodeExecution);
720
+ return { activeNodeExecutions: newMap };
721
+ });
722
+ } catch (error) {
723
+ workflowStore.updateNodeData(nodeId, {
724
+ status: import_types3.NodeStatusEnum.ERROR,
725
+ error: error instanceof Error ? error.message : "Node execution failed"
726
+ });
727
+ }
728
+ },
729
+ executeSelectedNodes: async () => {
730
+ const { isRunning, resetExecution } = get();
731
+ if (isRunning) return;
732
+ const workflowStore = useWorkflowStore.getState();
733
+ const debugMode = useSettingsStore.getState().debugMode;
734
+ const { selectedNodeIds } = workflowStore;
735
+ if (selectedNodeIds.length === 0) {
736
+ set({
737
+ validationErrors: {
738
+ isValid: false,
739
+ errors: [{ nodeId: "", message: "No nodes selected", severity: "error" }],
740
+ warnings: []
741
+ }
742
+ });
743
+ return;
744
+ }
745
+ set({ validationErrors: null });
746
+ resetExecution();
747
+ if (workflowStore.isDirty || !workflowStore.workflowId) {
748
+ try {
749
+ await workflowStore.saveWorkflow();
750
+ } catch {
751
+ set({
752
+ validationErrors: {
753
+ isValid: false,
754
+ errors: [{ nodeId: "", message: "Failed to save workflow", severity: "error" }],
755
+ warnings: []
756
+ }
757
+ });
758
+ return;
759
+ }
760
+ }
761
+ const workflowId = workflowStore.workflowId;
762
+ if (!workflowId) {
763
+ set({
764
+ validationErrors: {
765
+ isValid: false,
766
+ errors: [{ nodeId: "", message: "Workflow must be saved first", severity: "error" }],
767
+ warnings: []
768
+ }
769
+ });
770
+ return;
771
+ }
772
+ set({ isRunning: true, executingNodeIds: selectedNodeIds });
773
+ if (debugMode) {
774
+ useUIStore.getState().setShowDebugPanel(true);
775
+ }
776
+ for (const nodeId of selectedNodeIds) {
777
+ workflowStore.updateNodeData(nodeId, {
778
+ status: import_types3.NodeStatusEnum.IDLE,
779
+ error: void 0,
780
+ progress: void 0
781
+ });
782
+ }
783
+ try {
784
+ const execution = await apiPost(`/workflows/${workflowId}/execute`, {
785
+ debugMode,
786
+ selectedNodeIds
787
+ });
788
+ const executionId = execution._id;
789
+ set({ executionId });
790
+ createExecutionSubscription(executionId, set);
791
+ } catch (error) {
792
+ set({
793
+ isRunning: false,
794
+ validationErrors: {
795
+ isValid: false,
796
+ errors: [
797
+ {
798
+ nodeId: "",
799
+ message: error instanceof Error ? error.message : "Partial execution failed",
800
+ severity: "error"
801
+ }
802
+ ],
803
+ warnings: []
804
+ }
805
+ });
806
+ }
807
+ },
808
+ resumeFromFailed: async () => {
809
+ const { isRunning, executionId, lastFailedNodeId } = get();
810
+ if (isRunning || !executionId || !lastFailedNodeId) return;
811
+ const workflowStore = useWorkflowStore.getState();
812
+ const debugMode = useSettingsStore.getState().debugMode;
813
+ const workflowId = workflowStore.workflowId;
814
+ if (!workflowId) {
815
+ set({
816
+ validationErrors: {
817
+ isValid: false,
818
+ errors: [{ nodeId: "", message: "Workflow must be saved first", severity: "error" }],
819
+ warnings: []
820
+ }
821
+ });
822
+ return;
823
+ }
824
+ set({ isRunning: true, validationErrors: null });
825
+ if (debugMode) {
826
+ useUIStore.getState().setShowDebugPanel(true);
827
+ }
828
+ workflowStore.updateNodeData(lastFailedNodeId, {
829
+ status: import_types3.NodeStatusEnum.IDLE,
830
+ error: void 0,
831
+ progress: void 0
832
+ });
833
+ try {
834
+ const execution = await apiPost(`/workflows/${workflowId}/execute`, {
835
+ debugMode
836
+ });
837
+ const newExecutionId = execution._id;
838
+ set({ executionId: newExecutionId, lastFailedNodeId: null });
839
+ createExecutionSubscription(newExecutionId, set);
840
+ } catch (error) {
841
+ set({
842
+ isRunning: false,
843
+ validationErrors: {
844
+ isValid: false,
845
+ errors: [
846
+ {
847
+ nodeId: "",
848
+ message: error instanceof Error ? error.message : "Resume failed",
849
+ severity: "error"
850
+ }
851
+ ],
852
+ warnings: []
853
+ }
854
+ });
855
+ }
856
+ },
857
+ stopExecution: () => {
858
+ const { eventSource, executionId } = get();
859
+ if (eventSource) {
860
+ eventSource.close();
861
+ }
862
+ if (executionId) {
863
+ apiPost(`/executions/${executionId}/stop`).catch(() => {
864
+ });
865
+ }
866
+ set({
867
+ isRunning: false,
868
+ eventSource: null,
869
+ currentNodeId: null
870
+ });
871
+ },
872
+ stopNodeExecution: (nodeId) => {
873
+ const { activeNodeExecutions } = get();
874
+ const nodeExecution = activeNodeExecutions.get(nodeId);
875
+ if (nodeExecution) {
876
+ nodeExecution.eventSource.close();
877
+ apiPost(`/executions/${nodeExecution.executionId}/stop`).catch(() => {
878
+ });
879
+ set((state) => {
880
+ const newMap = new Map(state.activeNodeExecutions);
881
+ newMap.delete(nodeId);
882
+ return { activeNodeExecutions: newMap };
883
+ });
884
+ }
885
+ const workflowStore = useWorkflowStore.getState();
886
+ workflowStore.updateNodeData(nodeId, { status: import_types3.NodeStatusEnum.IDLE, error: void 0 });
887
+ },
888
+ isNodeExecuting: (nodeId) => {
889
+ const { activeNodeExecutions } = get();
890
+ return activeNodeExecutions.has(nodeId);
891
+ },
892
+ clearValidationErrors: () => {
893
+ set({ validationErrors: null });
894
+ },
895
+ resetExecution: () => {
896
+ const { eventSource, activeNodeExecutions } = get();
897
+ if (eventSource) {
898
+ eventSource.close();
899
+ }
900
+ for (const nodeExecution of activeNodeExecutions.values()) {
901
+ nodeExecution.eventSource.close();
902
+ }
903
+ set({
904
+ isRunning: false,
905
+ jobs: /* @__PURE__ */ new Map(),
906
+ executionId: null,
907
+ currentNodeId: null,
908
+ executingNodeIds: [],
909
+ eventSource: null,
910
+ actualCost: 0,
911
+ lastFailedNodeId: null,
912
+ debugPayloads: [],
913
+ activeNodeExecutions: /* @__PURE__ */ new Map()
914
+ });
915
+ const workflowStore = useWorkflowStore.getState();
916
+ for (const node of workflowStore.nodes) {
917
+ workflowStore.updateNodeData(node.id, {
918
+ status: import_types3.NodeStatusEnum.IDLE,
919
+ error: void 0,
920
+ progress: void 0
921
+ });
922
+ }
923
+ },
924
+ canResumeFromFailed: () => {
925
+ const { executionId, lastFailedNodeId, isRunning } = get();
926
+ return !isRunning && Boolean(executionId) && Boolean(lastFailedNodeId);
927
+ },
928
+ setEstimatedCost: (cost) => {
929
+ set({ estimatedCost: cost });
930
+ }
931
+ });
932
+
933
+ // src/stores/execution/slices/jobSlice.ts
934
+ var createJobSlice = (set, get) => ({
935
+ addJob: (nodeId, predictionId) => {
936
+ set((state) => {
937
+ const newJobs = new Map(state.jobs);
938
+ newJobs.set(predictionId, {
939
+ nodeId,
940
+ predictionId,
941
+ status: "pending",
942
+ progress: 0,
943
+ output: null,
944
+ error: null,
945
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
946
+ });
947
+ return { jobs: newJobs };
948
+ });
949
+ },
950
+ updateJob: (predictionId, updates) => {
951
+ set((state) => {
952
+ const newJobs = new Map(state.jobs);
953
+ const job = newJobs.get(predictionId);
954
+ if (job) {
955
+ newJobs.set(predictionId, { ...job, ...updates });
956
+ }
957
+ return { jobs: newJobs };
958
+ });
959
+ },
960
+ getJobByNodeId: (nodeId) => {
961
+ const { jobs } = get();
962
+ for (const job of jobs.values()) {
963
+ if (job.nodeId === nodeId) return job;
964
+ }
965
+ return void 0;
966
+ }
967
+ });
968
+
969
+ // src/stores/execution/executionStore.ts
970
+ var useExecutionStore = create()((...args) => {
971
+ const [set] = args;
972
+ return {
973
+ // Initial state
974
+ isRunning: false,
975
+ executionId: null,
976
+ currentNodeId: null,
977
+ executingNodeIds: [],
978
+ validationErrors: null,
979
+ eventSource: null,
980
+ lastFailedNodeId: null,
981
+ pausedAtNodeId: null,
982
+ jobs: /* @__PURE__ */ new Map(),
983
+ estimatedCost: 0,
984
+ actualCost: 0,
985
+ debugPayloads: [],
986
+ activeNodeExecutions: /* @__PURE__ */ new Map(),
987
+ // Debug payload actions
988
+ addDebugPayload: (payload) => {
989
+ set((state) => ({
990
+ debugPayloads: [...state.debugPayloads, payload]
991
+ }));
992
+ },
993
+ clearDebugPayloads: () => {
994
+ set({ debugPayloads: [] });
995
+ },
996
+ // Compose slices
997
+ ...createJobSlice(...args),
998
+ ...createExecutionSlice(...args)
999
+ };
1000
+ });
1001
+
1002
+ export { PROVIDER_INFO, useExecutionStore, useSettingsStore, useUIStore };