@genfeedai/workflow-ui 0.1.3 → 0.1.5

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 +31 -25
  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
@@ -0,0 +1,52 @@
1
+ import { useWorkflowStore } from './chunk-R727OFBR.mjs';
2
+ import { useMemo, useCallback } from 'react';
3
+
4
+ function useCommentNavigation(nodeId) {
5
+ const nodes = useWorkflowStore((state) => state.nodes);
6
+ const getNodesWithComments = useWorkflowStore((state) => state.getNodesWithComments);
7
+ const markCommentViewed = useWorkflowStore((state) => state.markCommentViewed);
8
+ const setNavigationTarget = useWorkflowStore((state) => state.setNavigationTarget);
9
+ const nodeComment = useMemo(() => {
10
+ const node = nodes.find((n) => n.id === nodeId);
11
+ const data = node?.data;
12
+ return data?.comment?.trim() || null;
13
+ }, [nodes, nodeId]);
14
+ const nodesWithComments = useMemo(() => {
15
+ return getNodesWithComments();
16
+ }, [getNodesWithComments]);
17
+ const currentIndex = useMemo(() => {
18
+ return nodesWithComments.findIndex((n) => n.id === nodeId);
19
+ }, [nodesWithComments, nodeId]);
20
+ const navigateTo = useCallback(
21
+ (targetIndex) => {
22
+ const targetNode = nodesWithComments[targetIndex];
23
+ if (targetNode) {
24
+ markCommentViewed(targetNode.id);
25
+ setNavigationTarget(targetNode.id);
26
+ }
27
+ },
28
+ [nodesWithComments, markCommentViewed, setNavigationTarget]
29
+ );
30
+ const onPrevious = useCallback(() => {
31
+ if (nodesWithComments.length === 0) return;
32
+ const newIndex = currentIndex <= 0 ? nodesWithComments.length - 1 : currentIndex - 1;
33
+ navigateTo(newIndex);
34
+ }, [currentIndex, nodesWithComments.length, navigateTo]);
35
+ const onNext = useCallback(() => {
36
+ if (nodesWithComments.length === 0) return;
37
+ const newIndex = (currentIndex + 1) % nodesWithComments.length;
38
+ navigateTo(newIndex);
39
+ }, [currentIndex, nodesWithComments.length, navigateTo]);
40
+ if (!nodeComment || currentIndex === -1) {
41
+ return null;
42
+ }
43
+ return {
44
+ currentIndex: currentIndex + 1,
45
+ // 1-based for display
46
+ totalCount: nodesWithComments.length,
47
+ onPrevious,
48
+ onNext
49
+ };
50
+ }
51
+
52
+ export { useCommentNavigation };
@@ -1,6 +1,7 @@
1
- import { useSettingsStore, useExecutionStore, useUIStore } from './chunk-SQK4JDYY.mjs';
2
- import { useWorkflowStore } from './chunk-RIGVIEYB.mjs';
3
- import { X, CloudOff, Loader2, Cloud, Check, ChevronDown, SaveAll, Save, FolderOpen, Bug, LayoutGrid, Undo2, Redo2, Settings, AlertCircle, Minus, Plus, Square, Play, ChevronUp, PlayCircle, RotateCcw, MoreVertical } from 'lucide-react';
1
+ import { calculateWorkflowCost, formatCost } from './chunk-JTPADIUO.mjs';
2
+ import { useSettingsStore, useExecutionStore, useUIStore } from './chunk-LT3ZJJL6.mjs';
3
+ import { useWorkflowStore } from './chunk-R727OFBR.mjs';
4
+ import { X, CloudOff, Loader2, Cloud, Check, ChevronDown, SaveAll, Save, FolderOpen, Bug, LayoutGrid, Undo2, Redo2, Settings, AlertCircle, Minus, Plus, Square, Play, ChevronUp, PlayCircle, RotateCcw, DollarSign, MoreVertical } from 'lucide-react';
4
5
  import { useState, useRef, useEffect, useCallback, useMemo } from 'react';
5
6
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
6
7
 
@@ -682,6 +683,28 @@ function BottomBar() {
682
683
  }
683
684
  );
684
685
  }
686
+ function CostIndicator() {
687
+ const nodes = useWorkflowStore((state) => state.nodes);
688
+ const isRunning = useExecutionStore((state) => state.isRunning);
689
+ const actualCost = useExecutionStore((state) => state.actualCost);
690
+ const { openModal } = useUIStore();
691
+ const breakdown = useMemo(() => calculateWorkflowCost(nodes), [nodes]);
692
+ const displayCost = isRunning && actualCost > 0 ? actualCost : breakdown.total;
693
+ if (breakdown.nodes.length === 0) return null;
694
+ return /* @__PURE__ */ jsxs(
695
+ "button",
696
+ {
697
+ onClick: () => openModal("cost"),
698
+ title: "View cost breakdown",
699
+ className: "flex items-center gap-1.5 rounded-md border border-[var(--border)] px-2 py-1 text-sm text-[var(--muted-foreground)] transition hover:bg-[var(--secondary)] hover:text-[var(--foreground)]",
700
+ children: [
701
+ /* @__PURE__ */ jsx(DollarSign, { className: "h-3.5 w-3.5" }),
702
+ /* @__PURE__ */ jsx("span", { className: "font-mono text-xs", children: formatCost(displayCost) }),
703
+ isRunning && actualCost > 0 && /* @__PURE__ */ jsx("span", { className: "h-1.5 w-1.5 rounded-full bg-green-500 animate-pulse" })
704
+ ]
705
+ }
706
+ );
707
+ }
685
708
  function OverflowMenu({ items }) {
686
709
  const [isOpen, setIsOpen] = useState(false);
687
710
  const menuRef = useRef(null);
@@ -734,4 +757,4 @@ function OverflowMenu({ items }) {
734
757
  ] });
735
758
  }
736
759
 
737
- export { BottomBar, OverflowMenu, SaveAsDialog, SaveIndicator, Toolbar, ToolbarDropdown };
760
+ export { BottomBar, CostIndicator, OverflowMenu, SaveAsDialog, SaveIndicator, Toolbar, ToolbarDropdown };
@@ -1,7 +1,7 @@
1
1
  import { Button } from './chunk-7SKSRSS7.mjs';
2
2
  import { extractEnumValues, supportsImageInput, validateRequiredSchemaFields, CONNECTION_FIELDS, getSchemaDefaults, getImageDimensions, getVideoMetadata } from './chunk-EFXQT23N.mjs';
3
- import { useExecutionStore } from './chunk-SQK4JDYY.mjs';
4
- import { useWorkflowStore } from './chunk-RIGVIEYB.mjs';
3
+ import { useExecutionStore } from './chunk-LT3ZJJL6.mjs';
4
+ import { useWorkflowStore } from './chunk-R727OFBR.mjs';
5
5
  import { useWorkflowUIConfig } from './chunk-FT33LFII.mjs';
6
6
  import { useMemo, useCallback, useRef, useEffect, useState } from 'react';
7
7
  import { ChevronDown, Expand, Square, Play } from 'lucide-react';
@@ -0,0 +1,130 @@
1
+ import { PRICING, DEFAULT_VIDEO_DURATION, IMAGE_NODE_TYPES, VIDEO_NODE_TYPES, LUMA_NODE_TYPES, TOPAZ_NODE_TYPES } from './chunk-OY7BRSGG.mjs';
2
+
3
+ // src/lib/costCalculator.ts
4
+ function isNodeType(type, list) {
5
+ return list.includes(type);
6
+ }
7
+ function getDataField(data, key, fallback) {
8
+ return data[key] ?? fallback;
9
+ }
10
+ function calculateWorkflowCost(nodes) {
11
+ const estimates = [];
12
+ for (const node of nodes) {
13
+ const data = node.data;
14
+ const type = node.type ?? "";
15
+ const label = getDataField(data, "label", type);
16
+ if (isNodeType(type, IMAGE_NODE_TYPES)) {
17
+ const model = getDataField(data, "model", "nano-banana");
18
+ const resolution = getDataField(data, "resolution", "2K");
19
+ let cost = 0;
20
+ const priceEntry = PRICING[model];
21
+ if (typeof priceEntry === "number") {
22
+ cost = priceEntry;
23
+ } else if (priceEntry && typeof priceEntry === "object" && !Array.isArray(priceEntry)) {
24
+ cost = priceEntry[resolution] ?? Object.values(priceEntry)[0] ?? 0;
25
+ }
26
+ estimates.push({
27
+ nodeId: node.id,
28
+ nodeLabel: label,
29
+ nodeType: type,
30
+ model,
31
+ unit: "per image",
32
+ cost
33
+ });
34
+ continue;
35
+ }
36
+ if (isNodeType(type, VIDEO_NODE_TYPES)) {
37
+ const model = getDataField(data, "model", "veo-3.1-fast");
38
+ const duration = getDataField(data, "duration", DEFAULT_VIDEO_DURATION);
39
+ const generateAudio = getDataField(data, "generateAudio", true);
40
+ const priceEntry = PRICING[model];
41
+ let perSecond = 0;
42
+ if (priceEntry && typeof priceEntry === "object" && !Array.isArray(priceEntry)) {
43
+ const entry = priceEntry;
44
+ perSecond = generateAudio ? entry.withAudio ?? 0 : entry.withoutAudio ?? 0;
45
+ }
46
+ estimates.push({
47
+ nodeId: node.id,
48
+ nodeLabel: label,
49
+ nodeType: type,
50
+ model,
51
+ unit: `${duration}s video`,
52
+ cost: perSecond * duration
53
+ });
54
+ continue;
55
+ }
56
+ if (isNodeType(type, LUMA_NODE_TYPES)) {
57
+ const model = getDataField(data, "model", "photon-flash-1");
58
+ const inputType = getDataField(data, "inputType", "image");
59
+ let cost = 0;
60
+ if (inputType === "video") {
61
+ cost = PRICING["luma-reframe-video"] * DEFAULT_VIDEO_DURATION;
62
+ } else {
63
+ const imageEntry = PRICING["luma-reframe-image"];
64
+ cost = imageEntry[model] ?? 0.01;
65
+ }
66
+ estimates.push({
67
+ nodeId: node.id,
68
+ nodeLabel: label,
69
+ nodeType: type,
70
+ model,
71
+ unit: inputType === "video" ? "per video" : "per image",
72
+ cost
73
+ });
74
+ continue;
75
+ }
76
+ if (isNodeType(type, TOPAZ_NODE_TYPES)) {
77
+ const inputType = getDataField(data, "inputType", "image");
78
+ if (inputType === "video") {
79
+ const resolution = getDataField(data, "targetResolution", "1080p");
80
+ const fps = getDataField(data, "targetFps", 30);
81
+ const key = `${resolution}-${fps}`;
82
+ const pricePerChunk = PRICING["topaz-video-upscale"][key] ?? 0.101;
83
+ const duration = DEFAULT_VIDEO_DURATION;
84
+ const chunks = Math.ceil(duration / 5);
85
+ estimates.push({
86
+ nodeId: node.id,
87
+ nodeLabel: label,
88
+ nodeType: type,
89
+ model: "topaz-video",
90
+ unit: `${duration}s video`,
91
+ cost: pricePerChunk * chunks
92
+ });
93
+ } else {
94
+ const tier = PRICING["topaz-image-upscale"].find((t) => 1 <= t.maxMP) ?? PRICING["topaz-image-upscale"][0];
95
+ estimates.push({
96
+ nodeId: node.id,
97
+ nodeLabel: label,
98
+ nodeType: type,
99
+ model: getDataField(data, "model", "topaz-standard-v2"),
100
+ unit: "per image",
101
+ cost: tier.price
102
+ });
103
+ }
104
+ continue;
105
+ }
106
+ if (type === "llm") {
107
+ const maxTokens = getDataField(data, "maxTokens", 1024);
108
+ const estimatedTokens = maxTokens * 3;
109
+ estimates.push({
110
+ nodeId: node.id,
111
+ nodeLabel: label,
112
+ nodeType: type,
113
+ model: getDataField(data, "model", "llama"),
114
+ unit: `~${estimatedTokens} tokens`,
115
+ cost: estimatedTokens * PRICING.llama
116
+ });
117
+ }
118
+ }
119
+ return {
120
+ total: estimates.reduce((sum, e) => sum + e.cost, 0),
121
+ nodes: estimates
122
+ };
123
+ }
124
+ function formatCost(amount) {
125
+ if (amount === 0) return "$0.00";
126
+ if (amount < 0.01) return `$${amount.toFixed(4)}`;
127
+ return `$${amount.toFixed(2)}`;
128
+ }
129
+
130
+ export { calculateWorkflowCost, formatCost };
@@ -1,4 +1,4 @@
1
- import { useWorkflowStore } from './chunk-RIGVIEYB.mjs';
1
+ import { useWorkflowStore } from './chunk-R727OFBR.mjs';
2
2
  import { create } from 'zustand';
3
3
  import { ProviderTypeEnum, NodeStatusEnum } from '@genfeedai/types';
4
4
 
@@ -12,6 +12,7 @@ var useUIStore = create((set) => ({
12
12
  selectedEdgeId: null,
13
13
  highlightedNodeIds: [],
14
14
  activeModal: null,
15
+ connectionDropMenu: null,
15
16
  nodeDetailNodeId: null,
16
17
  nodeDetailActiveTab: "preview",
17
18
  nodeDetailStartIndex: 0,
@@ -46,6 +47,12 @@ var useUIStore = create((set) => ({
46
47
  closeModal: () => {
47
48
  set({ activeModal: null });
48
49
  },
50
+ openConnectionDropMenu: (params) => {
51
+ set({ connectionDropMenu: params });
52
+ },
53
+ closeConnectionDropMenu: () => {
54
+ set({ connectionDropMenu: null });
55
+ },
49
56
  openNodeDetailModal: (nodeId, tab = "preview", startIndex = 0) => {
50
57
  set({
51
58
  activeModal: "nodeDetail",
@@ -211,7 +218,7 @@ var useSettingsStore = create((set, get) => {
211
218
  },
212
219
  setEdgeStyle: (style) => {
213
220
  setAndPersist(() => ({ edgeStyle: style }));
214
- import('./workflowStore-7SDJC4UR.mjs').then(({ useWorkflowStore: useWorkflowStore2 }) => {
221
+ import('./workflowStore-UAAKOOIK.mjs').then(({ useWorkflowStore: useWorkflowStore2 }) => {
215
222
  useWorkflowStore2.getState().setEdgeStyle(style);
216
223
  });
217
224
  },