@genfeedai/workflow-ui 0.2.3 → 0.2.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.
- package/dist/canvas.d.ts +22 -22
- package/dist/canvas.mjs +16 -16
- package/dist/{chunk-XPZAHIWY.mjs → chunk-2FUPL67V.mjs} +1592 -1044
- package/dist/{chunk-HWVTD2LC.mjs → chunk-53XDE62A.mjs} +818 -623
- package/dist/{chunk-PCIWWD37.mjs → chunk-7LV4UAUS.mjs} +19 -19
- package/dist/{chunk-7SKSRSS7.mjs → chunk-B4EAAKYF.mjs} +16 -16
- package/dist/{chunk-ZJD5WMR3.mjs → chunk-C6MQBJFC.mjs} +45 -13
- package/dist/{chunk-7H3WJJYS.mjs → chunk-ESVULCFY.mjs} +12 -6
- package/dist/{chunk-GWBGK3KL.mjs → chunk-FWJIAW2E.mjs} +82 -47
- package/dist/{chunk-R727OFBR.mjs → chunk-GPYIIWD5.mjs} +404 -350
- package/dist/{chunk-OQREHJXK.mjs → chunk-IYFWAJBB.mjs} +208 -203
- package/dist/{chunk-N5NJZTK4.mjs → chunk-MGLAKMDP.mjs} +23 -21
- package/dist/{chunk-LT3ZJJL6.mjs → chunk-OJWVEEMM.mjs} +497 -399
- package/dist/{chunk-ZD2BADZO.mjs → chunk-ORVDYXDP.mjs} +221 -175
- package/dist/{chunk-CV4M7CNU.mjs → chunk-QQVHGJ2G.mjs} +149 -142
- package/dist/{chunk-6PSJTBNV.mjs → chunk-U4QPE4CY.mjs} +387 -347
- package/dist/{chunk-EFXQT23N.mjs → chunk-VVQ4CH77.mjs} +5 -5
- package/dist/{chunk-VRN3UWE5.mjs → chunk-XRC3O5GK.mjs} +73 -73
- package/dist/{chunk-FT33LFII.mjs → chunk-YUIK4AHM.mjs} +1 -1
- package/dist/{chunk-FMJPFB6W.mjs → chunk-ZSITTZ4S.mjs} +630 -569
- package/dist/hooks.d.ts +37 -37
- package/dist/hooks.mjs +10 -10
- package/dist/index.d.ts +26 -11
- package/dist/index.mjs +105 -19
- package/dist/lib.d.ts +203 -203
- package/dist/lib.mjs +228 -198
- package/dist/nodes.d.ts +2 -2
- package/dist/nodes.mjs +12 -12
- package/dist/panels.d.ts +2 -3
- package/dist/panels.mjs +3 -3
- package/dist/provider.d.ts +2 -2
- package/dist/provider.mjs +2 -2
- package/dist/stores.d.ts +5 -5
- package/dist/stores.mjs +5 -5
- package/dist/toolbar.d.ts +42 -24
- package/dist/toolbar.mjs +4 -4
- package/dist/ui.d.ts +2 -2
- package/dist/ui.mjs +2 -2
- package/dist/{useCommentNavigation-BakbiiIc.d.ts → useRequiredInputs-ByoIS-fT.d.ts} +160 -160
- package/dist/{promptLibraryStore-Dl3Q3cP6.d.ts → workflowStore-Bsz0nd5c.d.ts} +368 -368
- package/dist/workflowStore-N2F7WIG3.mjs +2 -0
- package/package.json +77 -75
- package/src/styles/workflow-ui.css +56 -19
- package/dist/workflowStore-UAAKOOIK.mjs +0 -2
- package/dist/{types-IEKYuYhu.d.ts → types-CRXJnajq.d.ts} +1 -1
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
import { Checkbox, Select, SelectTrigger, SelectValue, SelectContent, SelectItem, Slider, Label, Input, GridPositionSelector, ComparisonSlider } from './chunk-
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { getImageDimensions, getVideoMetadata } from './chunk-
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import { usePromptLibraryStore } from './chunk-
|
|
1
|
+
import { Checkbox, Select, SelectTrigger, SelectValue, SelectContent, SelectItem, Slider, Label, Input, GridPositionSelector, ComparisonSlider } from './chunk-C6MQBJFC.mjs';
|
|
2
|
+
import { useNodeExecution, useCanGenerate, useModelSelection, useAutoLoadModelSchema, useAIGenNode, useAIGenNodeHeader, useMediaUpload, usePromptAutocomplete, useRequiredInputs } from './chunk-FWJIAW2E.mjs';
|
|
3
|
+
import { Button } from './chunk-B4EAAKYF.mjs';
|
|
4
|
+
import { generateHandlesFromSchema, IMAGE_MODEL_MAP, DEFAULT_IMAGE_MODEL, IMAGE_MODEL_ID_MAP, IMAGE_MODELS, LIPSYNC_MODELS, LIPSYNC_SYNC_MODES, LLM_MODEL_MAP, DEFAULT_LLM_MODEL, LLM_MODEL_ID_MAP, LLM_MODELS, VIDEO_MODEL_MAP, DEFAULT_VIDEO_MODEL, VIDEO_MODEL_ID_MAP, VIDEO_MODELS, EASING_PRESETS, CubicBezierEditor, getMediaFromNode } from './chunk-U4QPE4CY.mjs';
|
|
5
|
+
import { getImageDimensions, getVideoMetadata } from './chunk-VVQ4CH77.mjs';
|
|
6
|
+
import { useWorkflowUIConfig } from './chunk-YUIK4AHM.mjs';
|
|
7
|
+
import { usePromptEditorStore, useAnnotationStore } from './chunk-QQVHGJ2G.mjs';
|
|
8
|
+
import { useUIStore, useExecutionStore } from './chunk-OJWVEEMM.mjs';
|
|
9
|
+
import { useWorkflowStore } from './chunk-GPYIIWD5.mjs';
|
|
10
|
+
import { usePromptLibraryStore } from './chunk-XRC3O5GK.mjs';
|
|
11
|
+
import { Wand2, Volume2, Video, Subtitles, Sparkles, Share2, Scissors, RefreshCw, Puzzle, Pencil, Navigation, Mic, MessageSquare, Maximize2, Maximize, LayoutGrid, Layers, Image as Image$1, Grid3X3, GitBranch, Film, FileVideo, FileText, Eye, Download, Crop, Columns2, CheckCircle, Brain, AudioLines, AtSign, ArrowRightToLine, ArrowLeftFromLine, Square, Lock, Unlock, Copy, RotateCcw, Loader2, ChevronDown, ChevronRight, AlertCircle, ImageIcon, Expand, Play, AlertTriangle, CheckCircle2, Upload, Link, X, Music, Save, Clock, Shapes, ChevronLeft, SplitSquareHorizontal, Zap, ZoomOut, ZoomIn } from 'lucide-react';
|
|
12
|
+
import Image4 from 'next/image';
|
|
13
|
+
import { memo, forwardRef, useRef, useState, useCallback, useEffect, useImperativeHandle, useMemo, Component, useLayoutEffect } from 'react';
|
|
11
14
|
import { NODE_DEFINITIONS, NodeStatusEnum } from '@genfeedai/types';
|
|
12
15
|
import { useUpdateNodeInternals, NodeResizer, Handle, Position } from '@xyflow/react';
|
|
13
16
|
import { clsx } from 'clsx';
|
|
14
|
-
import { GitBranch, ArrowLeftFromLine, ArrowRightToLine, Subtitles, Pencil, Columns2, LayoutGrid, Grid3X3, Puzzle, Navigation, Volume2, AudioLines, Mic, Maximize, Crop, RefreshCw, AtSign, Download, Eye, CheckCircle, Share2, Scissors, Layers, Wand2, Maximize2, Brain, Video, Sparkles, Film, FileVideo, FileText, MessageSquare, Image as Image$1, Square, Lock, Unlock, Copy, RotateCcw, Loader2, ChevronDown, ChevronRight, AlertCircle, ImageIcon, Expand, Play, AlertTriangle, CheckCircle2, Upload, Link, X, Music, Save, Clock, Shapes, ChevronLeft, SplitSquareHorizontal, Zap, ZoomOut, ZoomIn } from 'lucide-react';
|
|
15
|
-
import { memo, forwardRef, useRef, useState, useCallback, useEffect, useImperativeHandle, useMemo, Component, useLayoutEffect } from 'react';
|
|
16
17
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
17
|
-
import Image4 from 'next/image';
|
|
18
18
|
import { createPortal } from 'react-dom';
|
|
19
19
|
import { useShallow } from 'zustand/react/shallow';
|
|
20
20
|
import { LUMA_ASPECT_RATIOS } from '@genfeedai/core';
|
|
@@ -22,16 +22,20 @@ import { LUMA_ASPECT_RATIOS } from '@genfeedai/core';
|
|
|
22
22
|
var NodeErrorBoundary = class extends Component {
|
|
23
23
|
constructor(props) {
|
|
24
24
|
super(props);
|
|
25
|
-
this.state = {
|
|
25
|
+
this.state = { error: null, hasError: false };
|
|
26
26
|
}
|
|
27
27
|
static getDerivedStateFromError(error) {
|
|
28
|
-
return { hasError: true
|
|
28
|
+
return { error, hasError: true };
|
|
29
29
|
}
|
|
30
30
|
componentDidCatch(error, errorInfo) {
|
|
31
|
-
console.error(
|
|
31
|
+
console.error(
|
|
32
|
+
`Node ${this.props.nodeId} (${this.props.nodeType}) crashed:`,
|
|
33
|
+
error,
|
|
34
|
+
errorInfo
|
|
35
|
+
);
|
|
32
36
|
}
|
|
33
37
|
handleRetry = () => {
|
|
34
|
-
this.setState({
|
|
38
|
+
this.setState({ error: null, hasError: false });
|
|
35
39
|
};
|
|
36
40
|
render() {
|
|
37
41
|
if (this.state.hasError) {
|
|
@@ -41,10 +45,19 @@ var NodeErrorBoundary = class extends Component {
|
|
|
41
45
|
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-destructive", children: "Node Error" }),
|
|
42
46
|
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground max-w-[200px] truncate", children: this.state.error?.message ?? "An unexpected error occurred" })
|
|
43
47
|
] }),
|
|
44
|
-
/* @__PURE__ */ jsxs(
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
+
/* @__PURE__ */ jsxs(
|
|
49
|
+
Button,
|
|
50
|
+
{
|
|
51
|
+
variant: "outline",
|
|
52
|
+
size: "sm",
|
|
53
|
+
onClick: this.handleRetry,
|
|
54
|
+
className: "gap-1.5",
|
|
55
|
+
children: [
|
|
56
|
+
/* @__PURE__ */ jsx(RefreshCw, { className: "h-3 w-3" }),
|
|
57
|
+
"Retry"
|
|
58
|
+
]
|
|
59
|
+
}
|
|
60
|
+
)
|
|
48
61
|
] });
|
|
49
62
|
}
|
|
50
63
|
return this.props.children;
|
|
@@ -60,10 +73,18 @@ function calculatePosition(anchorRect) {
|
|
|
60
73
|
const placement = spaceAbove >= tooltipHeight + offset ? "top" : "bottom";
|
|
61
74
|
const top = placement === "top" ? anchorRect.top - tooltipHeight - offset : anchorRect.bottom + offset;
|
|
62
75
|
let left = anchorRect.left + anchorRect.width / 2 - tooltipWidth / 2;
|
|
63
|
-
left = Math.max(
|
|
64
|
-
|
|
76
|
+
left = Math.max(
|
|
77
|
+
padding,
|
|
78
|
+
Math.min(left, viewportWidth - tooltipWidth - padding)
|
|
79
|
+
);
|
|
80
|
+
return { left, placement, top };
|
|
65
81
|
}
|
|
66
|
-
function PreviewTooltip({
|
|
82
|
+
function PreviewTooltip({
|
|
83
|
+
nodeType,
|
|
84
|
+
nodeData,
|
|
85
|
+
anchorRect,
|
|
86
|
+
isVisible
|
|
87
|
+
}) {
|
|
67
88
|
const [mounted, setMounted] = useState(false);
|
|
68
89
|
const tooltipRef = useRef(null);
|
|
69
90
|
const mediaInfo = useMemo(() => {
|
|
@@ -86,15 +107,24 @@ function PreviewTooltip({ nodeType, nodeData, anchorRect, isVisible }) {
|
|
|
86
107
|
ref: tooltipRef,
|
|
87
108
|
className: "fixed z-[100] pointer-events-none",
|
|
88
109
|
style: {
|
|
89
|
-
top: position.top,
|
|
90
110
|
left: position.left,
|
|
91
111
|
opacity: isVisible ? 1 : 0,
|
|
112
|
+
top: position.top,
|
|
92
113
|
transform: `scale(${isVisible ? 1 : 0.95})`,
|
|
93
114
|
transition: "opacity 150ms ease-out, transform 150ms ease-out"
|
|
94
115
|
},
|
|
95
116
|
children: /* @__PURE__ */ jsxs("div", { className: "w-[280px] bg-card border border-border rounded-lg shadow-xl overflow-hidden", children: [
|
|
96
117
|
/* @__PURE__ */ jsxs("div", { className: "relative aspect-video bg-background", children: [
|
|
97
|
-
mediaInfo.type === "image" && /* @__PURE__ */ jsx(
|
|
118
|
+
mediaInfo.type === "image" && /* @__PURE__ */ jsx(
|
|
119
|
+
Image4,
|
|
120
|
+
{
|
|
121
|
+
src: mediaInfo.url,
|
|
122
|
+
alt: "Preview",
|
|
123
|
+
fill: true,
|
|
124
|
+
className: "object-contain",
|
|
125
|
+
unoptimized: true
|
|
126
|
+
}
|
|
127
|
+
),
|
|
98
128
|
mediaInfo.type === "video" && /* @__PURE__ */ jsx(
|
|
99
129
|
"video",
|
|
100
130
|
{
|
|
@@ -120,47 +150,47 @@ function PreviewTooltip({ nodeType, nodeData, anchorRect, isVisible }) {
|
|
|
120
150
|
return createPortal(content, document.body);
|
|
121
151
|
}
|
|
122
152
|
var ICON_MAP = {
|
|
123
|
-
|
|
124
|
-
|
|
153
|
+
ArrowLeftFromLine,
|
|
154
|
+
// Composition icons
|
|
155
|
+
ArrowRightToLine,
|
|
156
|
+
AtSign,
|
|
157
|
+
AudioLines,
|
|
158
|
+
Brain,
|
|
159
|
+
CheckCircle,
|
|
160
|
+
Columns2,
|
|
161
|
+
Crop,
|
|
162
|
+
Download,
|
|
163
|
+
Eye,
|
|
125
164
|
FileText,
|
|
126
165
|
FileVideo,
|
|
127
166
|
Film,
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
Maximize2,
|
|
132
|
-
Wand2,
|
|
167
|
+
GitBranch,
|
|
168
|
+
Grid3X3,
|
|
169
|
+
Image: Image$1,
|
|
133
170
|
Layers,
|
|
134
|
-
|
|
135
|
-
Share2,
|
|
136
|
-
CheckCircle,
|
|
137
|
-
Eye,
|
|
138
|
-
Download,
|
|
139
|
-
AtSign,
|
|
140
|
-
RefreshCw: RefreshCw,
|
|
141
|
-
Crop,
|
|
171
|
+
LayoutGrid,
|
|
142
172
|
Maximize,
|
|
173
|
+
Maximize2,
|
|
174
|
+
MessageSquare,
|
|
143
175
|
Mic,
|
|
144
|
-
AudioLines,
|
|
145
|
-
Volume2,
|
|
146
176
|
Navigation,
|
|
147
|
-
Puzzle,
|
|
148
|
-
Grid3X3,
|
|
149
|
-
LayoutGrid,
|
|
150
|
-
Columns2,
|
|
151
177
|
Pencil,
|
|
178
|
+
Puzzle,
|
|
179
|
+
RefreshCw: RefreshCw,
|
|
180
|
+
Scissors,
|
|
181
|
+
Share2,
|
|
182
|
+
Sparkles,
|
|
152
183
|
Subtitles,
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
GitBranch
|
|
184
|
+
Video,
|
|
185
|
+
Volume2,
|
|
186
|
+
Wand2
|
|
157
187
|
};
|
|
158
188
|
var HANDLE_COLORS = {
|
|
189
|
+
audio: "var(--handle-audio)",
|
|
159
190
|
image: "var(--handle-image)",
|
|
160
|
-
video: "var(--handle-video)",
|
|
161
|
-
text: "var(--handle-text)",
|
|
162
191
|
number: "var(--handle-number)",
|
|
163
|
-
|
|
192
|
+
text: "var(--handle-text)",
|
|
193
|
+
video: "var(--handle-video)"
|
|
164
194
|
};
|
|
165
195
|
function StatusIndicator({ status }) {
|
|
166
196
|
switch (status) {
|
|
@@ -204,7 +234,9 @@ function BaseNodeComponent({
|
|
|
204
234
|
const executeNode = useExecutionStore((state) => state.executeNode);
|
|
205
235
|
const isRunning = useExecutionStore((state) => state.isRunning);
|
|
206
236
|
const stopExecution = useExecutionStore((state) => state.stopExecution);
|
|
207
|
-
const stopNodeExecution = useExecutionStore(
|
|
237
|
+
const stopNodeExecution = useExecutionStore(
|
|
238
|
+
(state) => state.stopNodeExecution
|
|
239
|
+
);
|
|
208
240
|
const updateNodeInternals = useUpdateNodeInternals();
|
|
209
241
|
const nodeDef = NODE_DEFINITIONS[type];
|
|
210
242
|
const nodeData = data;
|
|
@@ -231,12 +263,17 @@ function BaseNodeComponent({
|
|
|
231
263
|
return () => cancelAnimationFrame(rafId);
|
|
232
264
|
}, [id, updateNodeInternals, handlesKey]);
|
|
233
265
|
const isHighlighted = highlightedNodeIds.length === 0 || highlightedNodeIds.includes(id);
|
|
234
|
-
const nodeExecuting = useExecutionStore(
|
|
266
|
+
const nodeExecuting = useExecutionStore(
|
|
267
|
+
(state) => state.activeNodeExecutions.has(id)
|
|
268
|
+
);
|
|
235
269
|
const handleRetry = useCallback(
|
|
236
270
|
(e) => {
|
|
237
271
|
e.stopPropagation();
|
|
238
272
|
if (!nodeExecuting) {
|
|
239
|
-
updateNodeData(id, {
|
|
273
|
+
updateNodeData(id, {
|
|
274
|
+
error: void 0,
|
|
275
|
+
status: NodeStatusEnum.PROCESSING
|
|
276
|
+
});
|
|
240
277
|
executeNode(id);
|
|
241
278
|
}
|
|
242
279
|
},
|
|
@@ -250,10 +287,17 @@ function BaseNodeComponent({
|
|
|
250
287
|
} else if (nodeExecuting) {
|
|
251
288
|
stopNodeExecution(id);
|
|
252
289
|
} else {
|
|
253
|
-
updateNodeData(id, { status: NodeStatusEnum.IDLE
|
|
290
|
+
updateNodeData(id, { error: void 0, status: NodeStatusEnum.IDLE });
|
|
254
291
|
}
|
|
255
292
|
},
|
|
256
|
-
[
|
|
293
|
+
[
|
|
294
|
+
id,
|
|
295
|
+
isRunning,
|
|
296
|
+
nodeExecuting,
|
|
297
|
+
stopExecution,
|
|
298
|
+
stopNodeExecution,
|
|
299
|
+
updateNodeData
|
|
300
|
+
]
|
|
257
301
|
);
|
|
258
302
|
const handleCopyError = useCallback(
|
|
259
303
|
async (e) => {
|
|
@@ -306,11 +350,11 @@ function BaseNodeComponent({
|
|
|
306
350
|
toggleNodeLock(id);
|
|
307
351
|
};
|
|
308
352
|
const categoryCssVars = {
|
|
309
|
-
input: "var(--category-input)",
|
|
310
353
|
ai: "var(--category-ai)",
|
|
311
|
-
|
|
354
|
+
composition: "var(--category-composition)",
|
|
355
|
+
input: "var(--category-input)",
|
|
312
356
|
output: "var(--category-output)",
|
|
313
|
-
|
|
357
|
+
processing: "var(--category-processing)"
|
|
314
358
|
};
|
|
315
359
|
const categoryColor = categoryCssVars[nodeDef.category] ?? categoryCssVars.input;
|
|
316
360
|
const customColor = nodeData.color;
|
|
@@ -355,8 +399,8 @@ function BaseNodeComponent({
|
|
|
355
399
|
...customColor && { borderColor: customColor },
|
|
356
400
|
// When resized, use explicit dimensions
|
|
357
401
|
...isResized && {
|
|
358
|
-
|
|
359
|
-
|
|
402
|
+
height: height ? `${height}px` : void 0,
|
|
403
|
+
width: width ? `${width}px` : void 0
|
|
360
404
|
}
|
|
361
405
|
},
|
|
362
406
|
onClick: () => selectNode(id),
|
|
@@ -374,8 +418,8 @@ function BaseNodeComponent({
|
|
|
374
418
|
isConnectableEnd: !isDisabled,
|
|
375
419
|
className: clsx("!w-3 !h-3", isDisabled && "opacity-30"),
|
|
376
420
|
style: {
|
|
377
|
-
|
|
378
|
-
|
|
421
|
+
background: HANDLE_COLORS[input.type],
|
|
422
|
+
top: `${(index + 1) / (sortedInputs.length + 1) * 100}%`
|
|
379
423
|
}
|
|
380
424
|
},
|
|
381
425
|
input.id
|
|
@@ -388,85 +432,96 @@ function BaseNodeComponent({
|
|
|
388
432
|
position: Position.Right,
|
|
389
433
|
id: output.id,
|
|
390
434
|
className: "!w-3 !h-3 handle-output",
|
|
391
|
-
style: {
|
|
435
|
+
style: {
|
|
436
|
+
top: `${(index + 1) / (nodeDef.outputs.length + 1) * 100}%`
|
|
437
|
+
}
|
|
392
438
|
},
|
|
393
439
|
output.id
|
|
394
440
|
)),
|
|
395
|
-
/* @__PURE__ */ jsxs(
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
{
|
|
402
|
-
variant: "ghost",
|
|
403
|
-
size: "icon-sm",
|
|
404
|
-
onClick: handleStopNode,
|
|
405
|
-
className: "text-destructive hover:bg-destructive/20 hover:text-destructive",
|
|
406
|
-
title: isRunning ? "Stop execution" : nodeExecuting ? "Stop node" : "Reset node",
|
|
407
|
-
children: /* @__PURE__ */ jsx(Square, { className: "h-3.5 w-3.5 fill-current" })
|
|
408
|
-
}
|
|
409
|
-
) : /* @__PURE__ */ jsx(StatusIndicator, { status: nodeData.status })),
|
|
410
|
-
/* @__PURE__ */ jsx(
|
|
411
|
-
Button,
|
|
412
|
-
{
|
|
413
|
-
variant: "ghost",
|
|
414
|
-
size: "icon-sm",
|
|
415
|
-
onClick: handleLockToggle,
|
|
416
|
-
className: isLocked ? "text-chart-3" : "text-muted-foreground",
|
|
417
|
-
title: isLocked ? "Unlock node (L)" : "Lock node (L)",
|
|
418
|
-
children: isLocked ? /* @__PURE__ */ jsx(Lock, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx(Unlock, { className: "h-4 w-4" })
|
|
419
|
-
}
|
|
441
|
+
/* @__PURE__ */ jsxs(
|
|
442
|
+
"div",
|
|
443
|
+
{
|
|
444
|
+
className: clsx(
|
|
445
|
+
"flex flex-col",
|
|
446
|
+
isResized && "flex-1 min-h-0 overflow-auto"
|
|
420
447
|
),
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
448
|
+
children: [
|
|
449
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 px-3 py-2.5 border-b border-border", children: [
|
|
450
|
+
/* @__PURE__ */ jsx(Icon, { className: "h-4 w-4 text-foreground" }),
|
|
451
|
+
titleElement ?? /* @__PURE__ */ jsx("span", { className: "flex-1 truncate text-sm font-medium text-left text-foreground", children: title ?? nodeData.label }),
|
|
452
|
+
!hideStatusIndicator && (isProcessing ? /* @__PURE__ */ jsx(
|
|
453
|
+
Button,
|
|
454
|
+
{
|
|
455
|
+
variant: "ghost",
|
|
456
|
+
size: "icon-sm",
|
|
457
|
+
onClick: handleStopNode,
|
|
458
|
+
className: "text-destructive hover:bg-destructive/20 hover:text-destructive",
|
|
459
|
+
title: isRunning ? "Stop execution" : nodeExecuting ? "Stop node" : "Reset node",
|
|
460
|
+
children: /* @__PURE__ */ jsx(Square, { className: "h-3.5 w-3.5 fill-current" })
|
|
461
|
+
}
|
|
462
|
+
) : /* @__PURE__ */ jsx(StatusIndicator, { status: nodeData.status })),
|
|
427
463
|
/* @__PURE__ */ jsx(
|
|
428
464
|
Button,
|
|
429
465
|
{
|
|
430
466
|
variant: "ghost",
|
|
431
467
|
size: "icon-sm",
|
|
432
|
-
onClick:
|
|
433
|
-
className:
|
|
434
|
-
title: "
|
|
435
|
-
children: /* @__PURE__ */ jsx(
|
|
468
|
+
onClick: handleLockToggle,
|
|
469
|
+
className: isLocked ? "text-chart-3" : "text-muted-foreground",
|
|
470
|
+
title: isLocked ? "Unlock node (L)" : "Lock node (L)",
|
|
471
|
+
children: isLocked ? /* @__PURE__ */ jsx(Lock, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx(Unlock, { className: "h-4 w-4" })
|
|
436
472
|
}
|
|
437
|
-
)
|
|
473
|
+
),
|
|
474
|
+
headerActions
|
|
438
475
|
] }),
|
|
439
|
-
/* @__PURE__ */ jsxs(
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
476
|
+
/* @__PURE__ */ jsx("div", { className: "flex-1 flex flex-col p-3 min-h-0", children: /* @__PURE__ */ jsxs(NodeErrorBoundary, { nodeId: id, nodeType: type, children: [
|
|
477
|
+
nodeData.error && /* @__PURE__ */ jsxs("div", { className: "mb-3 rounded-md border border-destructive/30 bg-destructive/10 p-2", children: [
|
|
478
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-start gap-1.5", children: [
|
|
479
|
+
/* @__PURE__ */ jsx("p", { className: "flex-1 text-xs text-destructive break-all", children: nodeData.error }),
|
|
480
|
+
/* @__PURE__ */ jsx(
|
|
481
|
+
Button,
|
|
482
|
+
{
|
|
483
|
+
variant: "ghost",
|
|
484
|
+
size: "icon-sm",
|
|
485
|
+
onClick: handleCopyError,
|
|
486
|
+
className: "flex-shrink-0 h-5 w-5 text-destructive/70 hover:bg-destructive/20 hover:text-destructive",
|
|
487
|
+
title: "Copy error",
|
|
488
|
+
children: /* @__PURE__ */ jsx(Copy, { className: "h-3 w-3" })
|
|
489
|
+
}
|
|
490
|
+
)
|
|
491
|
+
] }),
|
|
492
|
+
/* @__PURE__ */ jsxs(
|
|
493
|
+
Button,
|
|
494
|
+
{
|
|
495
|
+
variant: "destructive",
|
|
496
|
+
size: "sm",
|
|
497
|
+
onClick: handleRetry,
|
|
498
|
+
disabled: nodeExecuting,
|
|
499
|
+
className: "mt-2 w-full",
|
|
500
|
+
children: [
|
|
501
|
+
/* @__PURE__ */ jsx(RotateCcw, { className: "h-3 w-3" }),
|
|
502
|
+
"Retry"
|
|
503
|
+
]
|
|
504
|
+
}
|
|
505
|
+
)
|
|
506
|
+
] }),
|
|
507
|
+
children,
|
|
508
|
+
nodeData.status === "processing" && nodeData.progress !== void 0 && /* @__PURE__ */ jsxs("div", { className: "mt-3", children: [
|
|
509
|
+
/* @__PURE__ */ jsx("div", { className: "h-1.5 overflow-hidden rounded-full bg-secondary", children: /* @__PURE__ */ jsx(
|
|
510
|
+
"div",
|
|
511
|
+
{
|
|
512
|
+
className: "h-full bg-primary transition-all duration-300",
|
|
513
|
+
style: { width: `${nodeData.progress}%` }
|
|
514
|
+
}
|
|
515
|
+
) }),
|
|
516
|
+
/* @__PURE__ */ jsxs("span", { className: "mt-1 text-xs text-muted-foreground", children: [
|
|
517
|
+
nodeData.progress,
|
|
518
|
+
"%"
|
|
519
|
+
] })
|
|
520
|
+
] })
|
|
521
|
+
] }) })
|
|
522
|
+
]
|
|
523
|
+
}
|
|
524
|
+
),
|
|
470
525
|
isLocked && /* @__PURE__ */ jsx("div", { className: "absolute -right-2 -top-2 rounded bg-chart-3 px-1.5 py-0.5 text-[10px] font-bold text-background", children: "LOCKED" })
|
|
471
526
|
]
|
|
472
527
|
}
|
|
@@ -508,7 +563,10 @@ function arePropsEqual(prev, next) {
|
|
|
508
563
|
return true;
|
|
509
564
|
}
|
|
510
565
|
var BaseNode = memo(BaseNodeComponent, arePropsEqual);
|
|
511
|
-
function ProcessingOverlayComponent({
|
|
566
|
+
function ProcessingOverlayComponent({
|
|
567
|
+
label = "Generating...",
|
|
568
|
+
onStop
|
|
569
|
+
}) {
|
|
512
570
|
return /* @__PURE__ */ jsx("div", { className: "absolute inset-0 flex items-center justify-center bg-black/60 backdrop-blur-sm rounded-md", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-2", children: [
|
|
513
571
|
/* @__PURE__ */ jsx(Loader2, { className: "h-8 w-8 animate-spin text-primary" }),
|
|
514
572
|
/* @__PURE__ */ jsx("span", { className: "text-xs text-white/80", children: label }),
|
|
@@ -520,14 +578,14 @@ function ProcessingOverlayComponent({ label = "Generating...", onStop }) {
|
|
|
520
578
|
}
|
|
521
579
|
var ProcessingOverlay = memo(ProcessingOverlayComponent);
|
|
522
580
|
var NEGATIVE_PROMPT_OPTIONS = [
|
|
523
|
-
{
|
|
524
|
-
{
|
|
525
|
-
{
|
|
526
|
-
{
|
|
527
|
-
{
|
|
528
|
-
{
|
|
529
|
-
{
|
|
530
|
-
{
|
|
581
|
+
{ label: "Blurry", value: "blurry" },
|
|
582
|
+
{ label: "Distorted", value: "distorted" },
|
|
583
|
+
{ label: "Low Quality", value: "low quality" },
|
|
584
|
+
{ label: "Watermark", value: "watermark" },
|
|
585
|
+
{ label: "Text/Logos", value: "text" },
|
|
586
|
+
{ label: "Artifacts", value: "artifacts" },
|
|
587
|
+
{ label: "Grainy/Noisy", value: "grainy" },
|
|
588
|
+
{ label: "Oversaturated", value: "oversaturated" }
|
|
531
589
|
];
|
|
532
590
|
function parseNegativePrompt(value) {
|
|
533
591
|
if (!value) return /* @__PURE__ */ new Set();
|
|
@@ -537,7 +595,9 @@ function parseNegativePrompt(value) {
|
|
|
537
595
|
}
|
|
538
596
|
function isKnownTerm(term) {
|
|
539
597
|
const lowerTerm = term.toLowerCase();
|
|
540
|
-
return NEGATIVE_PROMPT_OPTIONS.some(
|
|
598
|
+
return NEGATIVE_PROMPT_OPTIONS.some(
|
|
599
|
+
(opt) => opt.value.toLowerCase() === lowerTerm
|
|
600
|
+
);
|
|
541
601
|
}
|
|
542
602
|
function extractCustomTerms(value) {
|
|
543
603
|
if (!value) return "";
|
|
@@ -558,7 +618,10 @@ function combineTerms(checkedValues, customText) {
|
|
|
558
618
|
}
|
|
559
619
|
return parts.join(", ");
|
|
560
620
|
}
|
|
561
|
-
function NegativePromptSelectorComponent({
|
|
621
|
+
function NegativePromptSelectorComponent({
|
|
622
|
+
value,
|
|
623
|
+
onChange
|
|
624
|
+
}) {
|
|
562
625
|
const [isExpanded, setIsExpanded] = useState(false);
|
|
563
626
|
const checkedTerms = useMemo(() => parseNegativePrompt(value), [value]);
|
|
564
627
|
const [customText, setCustomText] = useState(() => extractCustomTerms(value));
|
|
@@ -606,29 +669,36 @@ function NegativePromptSelectorComponent({ value, onChange }) {
|
|
|
606
669
|
/* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 gap-x-3 gap-y-1.5", children: NEGATIVE_PROMPT_OPTIONS.map((option) => {
|
|
607
670
|
const isChecked = checkedTerms.has(option.value.toLowerCase());
|
|
608
671
|
const checkboxId = `negative-prompt-${option.value}`;
|
|
609
|
-
return /* @__PURE__ */ jsxs(
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
672
|
+
return /* @__PURE__ */ jsxs(
|
|
673
|
+
"div",
|
|
674
|
+
{
|
|
675
|
+
className: "flex items-center gap-1.5 nodrag",
|
|
676
|
+
children: [
|
|
677
|
+
/* @__PURE__ */ jsx(
|
|
678
|
+
Checkbox,
|
|
679
|
+
{
|
|
680
|
+
id: checkboxId,
|
|
681
|
+
checked: isChecked,
|
|
682
|
+
onCheckedChange: (checked) => {
|
|
683
|
+
if (typeof checked === "boolean") {
|
|
684
|
+
handleCheckboxChange(option.value, checked);
|
|
685
|
+
}
|
|
686
|
+
},
|
|
687
|
+
className: "w-3.5 h-3.5"
|
|
618
688
|
}
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
689
|
+
),
|
|
690
|
+
/* @__PURE__ */ jsx(
|
|
691
|
+
"label",
|
|
692
|
+
{
|
|
693
|
+
htmlFor: checkboxId,
|
|
694
|
+
className: "text-sm text-foreground truncate cursor-pointer",
|
|
695
|
+
children: option.label
|
|
696
|
+
}
|
|
697
|
+
)
|
|
698
|
+
]
|
|
699
|
+
},
|
|
700
|
+
option.value
|
|
701
|
+
);
|
|
632
702
|
}) }),
|
|
633
703
|
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1 mt-1", children: [
|
|
634
704
|
/* @__PURE__ */ jsx("label", { className: "text-xs text-muted-foreground", children: "Custom" }),
|
|
@@ -685,28 +755,32 @@ var DEFAULT_ENUM_VALUES = {
|
|
|
685
755
|
"4:5",
|
|
686
756
|
"5:4"
|
|
687
757
|
],
|
|
688
|
-
//
|
|
689
|
-
|
|
690
|
-
// Resolution options
|
|
691
|
-
resolution: ["1K", "2K", "4K", "720p", "1080p"],
|
|
758
|
+
// Character orientation
|
|
759
|
+
character_orientation: ["image", "video"],
|
|
692
760
|
// Video duration
|
|
693
761
|
duration: ["4", "5", "6", "8", "10"],
|
|
694
762
|
// Megapixels
|
|
695
763
|
megapixels: ["0.25", "1", "2"],
|
|
764
|
+
// Video model mode
|
|
765
|
+
mode: ["std", "pro"],
|
|
766
|
+
// Luma models
|
|
767
|
+
model: ["photon-flash-1", "photon-1"],
|
|
768
|
+
// Output formats
|
|
769
|
+
output_format: ["jpg", "png", "webp"],
|
|
770
|
+
// Refine options
|
|
771
|
+
refine: ["no_refiner", "expert_ensemble_refiner", "base_image_refiner"],
|
|
772
|
+
// Resolution options
|
|
773
|
+
resolution: ["1K", "2K", "4K", "720p", "1080p"],
|
|
696
774
|
// Safety filter level
|
|
697
|
-
safety_filter_level: [
|
|
775
|
+
safety_filter_level: [
|
|
776
|
+
"block_only_high",
|
|
777
|
+
"block_medium_and_above",
|
|
778
|
+
"block_low_and_above"
|
|
779
|
+
],
|
|
698
780
|
// Scheduler options
|
|
699
781
|
scheduler: ["K_EULER", "K_EULER_ANCESTRAL", "DDIM", "DPM_SOLVER", "PNDM"],
|
|
700
|
-
// Refine options
|
|
701
|
-
refine: ["no_refiner", "expert_ensemble_refiner", "base_image_refiner"],
|
|
702
|
-
// Video model mode
|
|
703
|
-
mode: ["std", "pro"],
|
|
704
|
-
// Character orientation
|
|
705
|
-
character_orientation: ["image", "video"],
|
|
706
782
|
// Sync mode
|
|
707
|
-
sync_mode: ["loop", "bounce", "cut_off", "silence", "remap"]
|
|
708
|
-
// Luma models
|
|
709
|
-
model: ["photon-flash-1", "photon-1"]
|
|
783
|
+
sync_mode: ["loop", "bounce", "cut_off", "silence", "remap"]
|
|
710
784
|
};
|
|
711
785
|
function getEnumKey(refPath) {
|
|
712
786
|
const parts = refPath.split("/");
|
|
@@ -725,10 +799,17 @@ function EnumSelect({
|
|
|
725
799
|
}) {
|
|
726
800
|
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
|
|
727
801
|
/* @__PURE__ */ jsx("label", { className: "text-xs text-muted-foreground", children: formatLabel(propertyKey, property.title) }),
|
|
728
|
-
/* @__PURE__ */ jsxs(
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
802
|
+
/* @__PURE__ */ jsxs(
|
|
803
|
+
Select,
|
|
804
|
+
{
|
|
805
|
+
value: String(value ?? property.default ?? options[0]),
|
|
806
|
+
onValueChange: onChange,
|
|
807
|
+
children: [
|
|
808
|
+
/* @__PURE__ */ jsx(SelectTrigger, { className: "nodrag h-8 w-full", children: /* @__PURE__ */ jsx(SelectValue, {}) }),
|
|
809
|
+
/* @__PURE__ */ jsx(SelectContent, { children: options.map((opt) => /* @__PURE__ */ jsx(SelectItem, { value: opt, children: opt }, opt)) })
|
|
810
|
+
]
|
|
811
|
+
}
|
|
812
|
+
)
|
|
732
813
|
] });
|
|
733
814
|
}
|
|
734
815
|
function SliderInput({
|
|
@@ -791,7 +872,14 @@ function CheckboxInput({
|
|
|
791
872
|
}
|
|
792
873
|
}
|
|
793
874
|
),
|
|
794
|
-
/* @__PURE__ */ jsx(
|
|
875
|
+
/* @__PURE__ */ jsx(
|
|
876
|
+
"label",
|
|
877
|
+
{
|
|
878
|
+
htmlFor: inputId,
|
|
879
|
+
className: "text-sm text-foreground cursor-pointer",
|
|
880
|
+
children: formatLabel(propertyKey, property.title)
|
|
881
|
+
}
|
|
882
|
+
)
|
|
795
883
|
] });
|
|
796
884
|
}
|
|
797
885
|
function NumberInput({
|
|
@@ -843,96 +931,102 @@ function SchemaInputsComponent({
|
|
|
843
931
|
if (!schema || sortedProperties.length === 0) {
|
|
844
932
|
return null;
|
|
845
933
|
}
|
|
846
|
-
return /* @__PURE__ */ jsx(
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
934
|
+
return /* @__PURE__ */ jsx(
|
|
935
|
+
"div",
|
|
936
|
+
{
|
|
937
|
+
className: `flex flex-col gap-2${disabled ? " pointer-events-none opacity-50" : ""}`,
|
|
938
|
+
children: sortedProperties.map(([key, property]) => {
|
|
939
|
+
const value = values[key];
|
|
940
|
+
if (key === "negative_prompt" && property.type === "string") {
|
|
941
|
+
return /* @__PURE__ */ jsx(
|
|
942
|
+
NegativePromptSelector,
|
|
943
|
+
{
|
|
944
|
+
value: value ?? "",
|
|
945
|
+
onChange: (v) => handleChange(key, v)
|
|
946
|
+
},
|
|
947
|
+
key
|
|
948
|
+
);
|
|
949
|
+
}
|
|
950
|
+
if (property.allOf && property.allOf.length > 0) {
|
|
951
|
+
const enumKey = getEnumKey(property.allOf[0].$ref);
|
|
952
|
+
const options = enumValues?.[enumKey] ?? DEFAULT_ENUM_VALUES[enumKey] ?? [];
|
|
953
|
+
if (options.length > 0) {
|
|
954
|
+
const componentSchema = componentSchemas?.[enumKey];
|
|
955
|
+
const enumType = componentSchema?.type;
|
|
956
|
+
return /* @__PURE__ */ jsx(
|
|
957
|
+
EnumSelect,
|
|
958
|
+
{
|
|
959
|
+
propertyKey: key,
|
|
960
|
+
property,
|
|
961
|
+
value,
|
|
962
|
+
options,
|
|
963
|
+
onChange: (v) => {
|
|
964
|
+
if (enumType === "integer") {
|
|
965
|
+
handleChange(key, Number.parseInt(v, 10));
|
|
966
|
+
} else if (enumType === "number") {
|
|
967
|
+
handleChange(key, Number.parseFloat(v));
|
|
968
|
+
} else {
|
|
969
|
+
handleChange(key, v);
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
},
|
|
973
|
+
key
|
|
974
|
+
);
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
if (property.enum) {
|
|
978
|
+
return /* @__PURE__ */ jsx(
|
|
979
|
+
EnumSelect,
|
|
980
|
+
{
|
|
981
|
+
propertyKey: key,
|
|
982
|
+
property,
|
|
983
|
+
value,
|
|
984
|
+
options: property.enum,
|
|
985
|
+
onChange: (v) => handleChange(key, v)
|
|
986
|
+
},
|
|
987
|
+
key
|
|
988
|
+
);
|
|
989
|
+
}
|
|
990
|
+
if (property.type === "boolean") {
|
|
991
|
+
return /* @__PURE__ */ jsx(
|
|
992
|
+
CheckboxInput,
|
|
993
|
+
{
|
|
994
|
+
propertyKey: key,
|
|
995
|
+
property,
|
|
996
|
+
value,
|
|
997
|
+
onChange: (v) => handleChange(key, v)
|
|
998
|
+
},
|
|
999
|
+
key
|
|
1000
|
+
);
|
|
1001
|
+
}
|
|
1002
|
+
if ((property.type === "integer" || property.type === "number") && property.minimum !== void 0 && property.maximum !== void 0) {
|
|
1003
|
+
return /* @__PURE__ */ jsx(
|
|
1004
|
+
SliderInput,
|
|
1005
|
+
{
|
|
1006
|
+
propertyKey: key,
|
|
1007
|
+
property,
|
|
1008
|
+
value,
|
|
1009
|
+
onChange: (v) => handleChange(key, v)
|
|
1010
|
+
},
|
|
1011
|
+
key
|
|
1012
|
+
);
|
|
1013
|
+
}
|
|
1014
|
+
if (property.type === "integer" || property.type === "number") {
|
|
1015
|
+
return /* @__PURE__ */ jsx(
|
|
1016
|
+
NumberInput,
|
|
1017
|
+
{
|
|
1018
|
+
propertyKey: key,
|
|
1019
|
+
property,
|
|
1020
|
+
value,
|
|
1021
|
+
onChange: (v) => handleChange(key, v)
|
|
1022
|
+
},
|
|
1023
|
+
key
|
|
1024
|
+
);
|
|
1025
|
+
}
|
|
1026
|
+
return null;
|
|
1027
|
+
})
|
|
933
1028
|
}
|
|
934
|
-
|
|
935
|
-
}) });
|
|
1029
|
+
);
|
|
936
1030
|
}
|
|
937
1031
|
var SchemaInputs = memo(SchemaInputsComponent);
|
|
938
1032
|
function ImageGenNodeComponent(props) {
|
|
@@ -942,9 +1036,9 @@ function ImageGenNodeComponent(props) {
|
|
|
942
1036
|
const openNodeDetailModal = useUIStore((state) => state.openNodeDetailModal);
|
|
943
1037
|
const { handleGenerate, handleStop } = useNodeExecution(id);
|
|
944
1038
|
const { canGenerate } = useCanGenerate({
|
|
1039
|
+
inputSchema: nodeData.selectedModel?.inputSchema,
|
|
945
1040
|
nodeId: id,
|
|
946
1041
|
nodeType: type,
|
|
947
|
-
inputSchema: nodeData.selectedModel?.inputSchema,
|
|
948
1042
|
schemaParams: nodeData.schemaParams
|
|
949
1043
|
});
|
|
950
1044
|
const [isModelBrowserOpen, setIsModelBrowserOpen] = useState(false);
|
|
@@ -969,16 +1063,18 @@ function ImageGenNodeComponent(props) {
|
|
|
969
1063
|
setTimeout(() => handleDownload(index), index * 100);
|
|
970
1064
|
});
|
|
971
1065
|
}, [nodeData.outputImages, handleDownload]);
|
|
972
|
-
const { handleModelSelect } = useModelSelection(
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
1066
|
+
const { handleModelSelect } = useModelSelection(
|
|
1067
|
+
{
|
|
1068
|
+
fallbackModel: DEFAULT_IMAGE_MODEL,
|
|
1069
|
+
modelMap: IMAGE_MODEL_MAP,
|
|
1070
|
+
nodeId: id
|
|
1071
|
+
}
|
|
1072
|
+
);
|
|
977
1073
|
useAutoLoadModelSchema({
|
|
978
1074
|
currentModel: nodeData.model,
|
|
979
|
-
selectedModel: nodeData.selectedModel,
|
|
980
1075
|
modelIdMap: IMAGE_MODEL_ID_MAP,
|
|
981
|
-
onModelSelect: handleModelSelect
|
|
1076
|
+
onModelSelect: handleModelSelect,
|
|
1077
|
+
selectedModel: nodeData.selectedModel
|
|
982
1078
|
});
|
|
983
1079
|
const {
|
|
984
1080
|
schemaProperties,
|
|
@@ -988,8 +1084,8 @@ function ImageGenNodeComponent(props) {
|
|
|
988
1084
|
componentSchemas
|
|
989
1085
|
} = useAIGenNode({
|
|
990
1086
|
nodeId: id,
|
|
991
|
-
|
|
992
|
-
|
|
1087
|
+
schemaParams: nodeData.schemaParams,
|
|
1088
|
+
selectedModel: nodeData.selectedModel
|
|
993
1089
|
});
|
|
994
1090
|
const handleExpand = useCallback(() => {
|
|
995
1091
|
openNodeDetailModal(id, "preview", selectedPreview ?? 0);
|
|
@@ -1001,14 +1097,14 @@ function ImageGenNodeComponent(props) {
|
|
|
1001
1097
|
const isProcessing = nodeData.status === "processing";
|
|
1002
1098
|
const handleModelBrowse = useCallback(() => setIsModelBrowserOpen(true), []);
|
|
1003
1099
|
const { titleElement, headerActions } = useAIGenNodeHeader({
|
|
1004
|
-
modelDisplayName,
|
|
1005
|
-
isProcessing,
|
|
1006
1100
|
canGenerate,
|
|
1007
1101
|
hasOutput: !!nodeData.outputImage,
|
|
1008
|
-
|
|
1102
|
+
isProcessing,
|
|
1103
|
+
modelDisplayName,
|
|
1104
|
+
onExpand: handleExpand,
|
|
1009
1105
|
onGenerate: handleGenerate,
|
|
1010
|
-
|
|
1011
|
-
|
|
1106
|
+
onModelBrowse: handleModelBrowse,
|
|
1107
|
+
onStop: handleStop
|
|
1012
1108
|
});
|
|
1013
1109
|
return /* @__PURE__ */ jsxs(
|
|
1014
1110
|
BaseNode,
|
|
@@ -1042,10 +1138,19 @@ function ImageGenNodeComponent(props) {
|
|
|
1042
1138
|
nodeData.outputImages.length,
|
|
1043
1139
|
" images)"
|
|
1044
1140
|
] }),
|
|
1045
|
-
/* @__PURE__ */ jsxs(
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1141
|
+
/* @__PURE__ */ jsxs(
|
|
1142
|
+
Button,
|
|
1143
|
+
{
|
|
1144
|
+
variant: "link",
|
|
1145
|
+
size: "sm",
|
|
1146
|
+
onClick: handleDownloadAll,
|
|
1147
|
+
className: "h-auto p-0",
|
|
1148
|
+
children: [
|
|
1149
|
+
/* @__PURE__ */ jsx(Download, { className: "w-3 h-3" }),
|
|
1150
|
+
"Download All"
|
|
1151
|
+
]
|
|
1152
|
+
}
|
|
1153
|
+
)
|
|
1049
1154
|
] }),
|
|
1050
1155
|
/* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 gap-1", children: nodeData.outputImages.map((img, i) => /* @__PURE__ */ jsxs(
|
|
1051
1156
|
"div",
|
|
@@ -1186,8 +1291,17 @@ function LipSyncNodeComponent(props) {
|
|
|
1186
1291
|
const isSyncModel = nodeData.model.startsWith("sync/");
|
|
1187
1292
|
const supportsImage = currentModel?.supportsImage ?? false;
|
|
1188
1293
|
const headerActions = useMemo(
|
|
1189
|
-
() => nodeData.outputVideo ? /* @__PURE__ */ jsx(
|
|
1190
|
-
|
|
1294
|
+
() => nodeData.outputVideo ? /* @__PURE__ */ jsx(
|
|
1295
|
+
Button,
|
|
1296
|
+
{
|
|
1297
|
+
variant: "ghost",
|
|
1298
|
+
size: "icon-sm",
|
|
1299
|
+
onClick: handleExpand,
|
|
1300
|
+
title: "Expand preview",
|
|
1301
|
+
children: /* @__PURE__ */ jsx(Expand, { className: "h-3 w-3" })
|
|
1302
|
+
}
|
|
1303
|
+
) : null,
|
|
1304
|
+
[nodeData.outputVideo, handleExpand]
|
|
1191
1305
|
);
|
|
1192
1306
|
return /* @__PURE__ */ jsx(BaseNode, { ...props, headerActions, children: /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
1193
1307
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
@@ -1199,10 +1313,17 @@ function LipSyncNodeComponent(props) {
|
|
|
1199
1313
|
] }),
|
|
1200
1314
|
isSyncModel && /* @__PURE__ */ jsxs("div", { children: [
|
|
1201
1315
|
/* @__PURE__ */ jsx("label", { className: "text-xs text-[var(--muted-foreground)]", children: "Sync Mode" }),
|
|
1202
|
-
/* @__PURE__ */ jsxs(
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1316
|
+
/* @__PURE__ */ jsxs(
|
|
1317
|
+
Select,
|
|
1318
|
+
{
|
|
1319
|
+
value: nodeData.syncMode,
|
|
1320
|
+
onValueChange: handleSyncModeChange,
|
|
1321
|
+
children: [
|
|
1322
|
+
/* @__PURE__ */ jsx(SelectTrigger, { className: "nodrag h-8 w-full", children: /* @__PURE__ */ jsx(SelectValue, {}) }),
|
|
1323
|
+
/* @__PURE__ */ jsx(SelectContent, { children: LIPSYNC_SYNC_MODES.map((mode) => /* @__PURE__ */ jsx(SelectItem, { value: mode.value, children: mode.label }, mode.value)) })
|
|
1324
|
+
]
|
|
1325
|
+
}
|
|
1326
|
+
)
|
|
1206
1327
|
] }),
|
|
1207
1328
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
1208
1329
|
/* @__PURE__ */ jsxs("label", { className: "text-xs text-[var(--muted-foreground)]", children: [
|
|
@@ -1297,15 +1418,15 @@ function LLMNodeComponent(props) {
|
|
|
1297
1418
|
const canGenerate = hasRequiredConnections && hasSystemPrompt && hasInputPrompt;
|
|
1298
1419
|
const [isModelBrowserOpen, setIsModelBrowserOpen] = useState(false);
|
|
1299
1420
|
const { handleModelSelect } = useModelSelection({
|
|
1300
|
-
|
|
1421
|
+
fallbackModel: DEFAULT_LLM_MODEL,
|
|
1301
1422
|
modelMap: LLM_MODEL_MAP,
|
|
1302
|
-
|
|
1423
|
+
nodeId: id
|
|
1303
1424
|
});
|
|
1304
1425
|
useAutoLoadModelSchema({
|
|
1305
1426
|
currentModel: nodeData.model,
|
|
1306
|
-
selectedModel: nodeData.selectedModel,
|
|
1307
1427
|
modelIdMap: LLM_MODEL_ID_MAP,
|
|
1308
|
-
onModelSelect: handleModelSelect
|
|
1428
|
+
onModelSelect: handleModelSelect,
|
|
1429
|
+
selectedModel: nodeData.selectedModel
|
|
1309
1430
|
});
|
|
1310
1431
|
const handleSystemPromptChange = useCallback(
|
|
1311
1432
|
(e) => {
|
|
@@ -1321,7 +1442,9 @@ function LLMNodeComponent(props) {
|
|
|
1321
1442
|
);
|
|
1322
1443
|
const handleMaxTokensChange = useCallback(
|
|
1323
1444
|
(e) => {
|
|
1324
|
-
updateNodeData(id, {
|
|
1445
|
+
updateNodeData(id, {
|
|
1446
|
+
maxTokens: parseInt(e.target.value, 10)
|
|
1447
|
+
});
|
|
1325
1448
|
},
|
|
1326
1449
|
[id, updateNodeData]
|
|
1327
1450
|
);
|
|
@@ -1335,14 +1458,14 @@ function LLMNodeComponent(props) {
|
|
|
1335
1458
|
const isProcessing = nodeData.status === "processing";
|
|
1336
1459
|
const handleModelBrowse = useCallback(() => setIsModelBrowserOpen(true), []);
|
|
1337
1460
|
const { titleElement, headerActions } = useAIGenNodeHeader({
|
|
1338
|
-
modelDisplayName,
|
|
1339
|
-
isProcessing,
|
|
1340
1461
|
canGenerate,
|
|
1341
1462
|
hasOutput: !!nodeData.outputText,
|
|
1342
|
-
|
|
1463
|
+
isProcessing,
|
|
1464
|
+
modelDisplayName,
|
|
1465
|
+
onExpand: handleExpand,
|
|
1343
1466
|
onGenerate: handleGenerate,
|
|
1344
|
-
|
|
1345
|
-
|
|
1467
|
+
onModelBrowse: handleModelBrowse,
|
|
1468
|
+
onStop: handleStop
|
|
1346
1469
|
});
|
|
1347
1470
|
return /* @__PURE__ */ jsxs(
|
|
1348
1471
|
BaseNode,
|
|
@@ -1461,31 +1584,35 @@ function LLMNodeComponent(props) {
|
|
|
1461
1584
|
}
|
|
1462
1585
|
var LLMNode = memo(LLMNodeComponent);
|
|
1463
1586
|
var QUALITY_MODES = [
|
|
1464
|
-
{
|
|
1465
|
-
{
|
|
1587
|
+
{ description: "Faster processing", label: "Standard", value: "std" },
|
|
1588
|
+
{ description: "Higher quality", label: "Pro", value: "pro" }
|
|
1466
1589
|
];
|
|
1467
1590
|
var CHARACTER_ORIENTATIONS = [
|
|
1468
|
-
{
|
|
1469
|
-
{
|
|
1591
|
+
{ label: "From Image", value: "image" },
|
|
1592
|
+
{ label: "From Video", value: "video" }
|
|
1470
1593
|
];
|
|
1471
1594
|
var MOTION_MODES = [
|
|
1472
1595
|
{
|
|
1473
|
-
|
|
1596
|
+
description: "Apply motion from reference video",
|
|
1474
1597
|
label: "Video Transfer",
|
|
1475
|
-
|
|
1598
|
+
value: "video_transfer"
|
|
1599
|
+
},
|
|
1600
|
+
{ description: "Apply camera movements", label: "Camera", value: "camera" },
|
|
1601
|
+
{
|
|
1602
|
+
description: "Define motion path",
|
|
1603
|
+
label: "Trajectory",
|
|
1604
|
+
value: "trajectory"
|
|
1476
1605
|
},
|
|
1477
|
-
{
|
|
1478
|
-
{ value: "trajectory", label: "Trajectory", description: "Define motion path" },
|
|
1479
|
-
{ value: "combined", label: "Combined", description: "Camera + Trajectory" }
|
|
1606
|
+
{ description: "Camera + Trajectory", label: "Combined", value: "combined" }
|
|
1480
1607
|
];
|
|
1481
1608
|
var ASPECT_RATIOS = [
|
|
1482
|
-
{
|
|
1483
|
-
{
|
|
1484
|
-
{
|
|
1609
|
+
{ label: "16:9 (Landscape)", value: "16:9" },
|
|
1610
|
+
{ label: "9:16 (Portrait)", value: "9:16" },
|
|
1611
|
+
{ label: "1:1 (Square)", value: "1:1" }
|
|
1485
1612
|
];
|
|
1486
1613
|
var DURATIONS = [
|
|
1487
|
-
{
|
|
1488
|
-
{
|
|
1614
|
+
{ label: "5 seconds", value: 5 },
|
|
1615
|
+
{ label: "10 seconds", value: 10 }
|
|
1489
1616
|
];
|
|
1490
1617
|
function MotionControlNodeComponent(props) {
|
|
1491
1618
|
const { id, type, data } = props;
|
|
@@ -1499,7 +1626,9 @@ function MotionControlNodeComponent(props) {
|
|
|
1499
1626
|
});
|
|
1500
1627
|
const handleModeChange = useCallback(
|
|
1501
1628
|
(value) => {
|
|
1502
|
-
updateNodeData(id, {
|
|
1629
|
+
updateNodeData(id, {
|
|
1630
|
+
mode: value
|
|
1631
|
+
});
|
|
1503
1632
|
},
|
|
1504
1633
|
[id, updateNodeData]
|
|
1505
1634
|
);
|
|
@@ -1538,7 +1667,9 @@ function MotionControlNodeComponent(props) {
|
|
|
1538
1667
|
const handleKeepOriginalSoundToggle = useCallback(
|
|
1539
1668
|
(checked) => {
|
|
1540
1669
|
if (typeof checked === "boolean") {
|
|
1541
|
-
updateNodeData(id, {
|
|
1670
|
+
updateNodeData(id, {
|
|
1671
|
+
keepOriginalSound: checked
|
|
1672
|
+
});
|
|
1542
1673
|
}
|
|
1543
1674
|
},
|
|
1544
1675
|
[id, updateNodeData]
|
|
@@ -1555,7 +1686,16 @@ function MotionControlNodeComponent(props) {
|
|
|
1555
1686
|
const isVideoTransferMode = nodeData.mode === "video_transfer";
|
|
1556
1687
|
const headerActions = useMemo(
|
|
1557
1688
|
() => /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1558
|
-
nodeData.outputVideo && /* @__PURE__ */ jsx(
|
|
1689
|
+
nodeData.outputVideo && /* @__PURE__ */ jsx(
|
|
1690
|
+
Button,
|
|
1691
|
+
{
|
|
1692
|
+
variant: "ghost",
|
|
1693
|
+
size: "icon-sm",
|
|
1694
|
+
onClick: handleExpand,
|
|
1695
|
+
title: "Expand preview",
|
|
1696
|
+
children: /* @__PURE__ */ jsx(Expand, { className: "h-3 w-3" })
|
|
1697
|
+
}
|
|
1698
|
+
),
|
|
1559
1699
|
nodeData.status === "processing" ? /* @__PURE__ */ jsxs(Button, { variant: "destructive", size: "sm", onClick: handleStop, children: [
|
|
1560
1700
|
/* @__PURE__ */ jsx(Square, { className: "h-4 w-4 fill-current" }),
|
|
1561
1701
|
"Generating"
|
|
@@ -1573,7 +1713,14 @@ function MotionControlNodeComponent(props) {
|
|
|
1573
1713
|
}
|
|
1574
1714
|
)
|
|
1575
1715
|
] }),
|
|
1576
|
-
[
|
|
1716
|
+
[
|
|
1717
|
+
nodeData.outputVideo,
|
|
1718
|
+
nodeData.status,
|
|
1719
|
+
handleGenerate,
|
|
1720
|
+
handleStop,
|
|
1721
|
+
handleExpand,
|
|
1722
|
+
canGenerate
|
|
1723
|
+
]
|
|
1577
1724
|
);
|
|
1578
1725
|
return /* @__PURE__ */ jsx(BaseNode, { ...props, headerActions, hideStatusIndicator: true, children: /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
1579
1726
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
@@ -1586,14 +1733,21 @@ function MotionControlNodeComponent(props) {
|
|
|
1586
1733
|
isVideoTransferMode && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1587
1734
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
1588
1735
|
/* @__PURE__ */ jsx("label", { className: "text-xs text-[var(--muted-foreground)]", children: "Quality" }),
|
|
1589
|
-
/* @__PURE__ */ jsxs(
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1736
|
+
/* @__PURE__ */ jsxs(
|
|
1737
|
+
Select,
|
|
1738
|
+
{
|
|
1739
|
+
value: nodeData.qualityMode,
|
|
1740
|
+
onValueChange: handleQualityModeChange,
|
|
1741
|
+
children: [
|
|
1742
|
+
/* @__PURE__ */ jsx(SelectTrigger, { className: "nodrag h-8 w-full", children: /* @__PURE__ */ jsx(SelectValue, {}) }),
|
|
1743
|
+
/* @__PURE__ */ jsx(SelectContent, { children: QUALITY_MODES.map((mode) => /* @__PURE__ */ jsxs(SelectItem, { value: mode.value, children: [
|
|
1744
|
+
mode.label,
|
|
1745
|
+
" - ",
|
|
1746
|
+
mode.description
|
|
1747
|
+
] }, mode.value)) })
|
|
1748
|
+
]
|
|
1749
|
+
}
|
|
1750
|
+
)
|
|
1597
1751
|
] }),
|
|
1598
1752
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
1599
1753
|
/* @__PURE__ */ jsx("label", { className: "text-xs text-[var(--muted-foreground)]", children: "Character Orientation" }),
|
|
@@ -1604,7 +1758,14 @@ function MotionControlNodeComponent(props) {
|
|
|
1604
1758
|
onValueChange: handleCharacterOrientationChange,
|
|
1605
1759
|
children: [
|
|
1606
1760
|
/* @__PURE__ */ jsx(SelectTrigger, { className: "nodrag h-8 w-full", children: /* @__PURE__ */ jsx(SelectValue, {}) }),
|
|
1607
|
-
/* @__PURE__ */ jsx(SelectContent, { children: CHARACTER_ORIENTATIONS.map((orientation) => /* @__PURE__ */ jsx(
|
|
1761
|
+
/* @__PURE__ */ jsx(SelectContent, { children: CHARACTER_ORIENTATIONS.map((orientation) => /* @__PURE__ */ jsx(
|
|
1762
|
+
SelectItem,
|
|
1763
|
+
{
|
|
1764
|
+
value: orientation.value,
|
|
1765
|
+
children: orientation.label
|
|
1766
|
+
},
|
|
1767
|
+
orientation.value
|
|
1768
|
+
)) })
|
|
1608
1769
|
]
|
|
1609
1770
|
}
|
|
1610
1771
|
)
|
|
@@ -1632,17 +1793,31 @@ function MotionControlNodeComponent(props) {
|
|
|
1632
1793
|
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-2", children: [
|
|
1633
1794
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
1634
1795
|
/* @__PURE__ */ jsx("label", { className: "text-xs text-[var(--muted-foreground)]", children: "Duration" }),
|
|
1635
|
-
/* @__PURE__ */ jsxs(
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1796
|
+
/* @__PURE__ */ jsxs(
|
|
1797
|
+
Select,
|
|
1798
|
+
{
|
|
1799
|
+
value: String(nodeData.duration),
|
|
1800
|
+
onValueChange: handleDurationChange,
|
|
1801
|
+
children: [
|
|
1802
|
+
/* @__PURE__ */ jsx(SelectTrigger, { className: "nodrag h-8 w-full", children: /* @__PURE__ */ jsx(SelectValue, {}) }),
|
|
1803
|
+
/* @__PURE__ */ jsx(SelectContent, { children: DURATIONS.map((d) => /* @__PURE__ */ jsx(SelectItem, { value: String(d.value), children: d.label }, d.value)) })
|
|
1804
|
+
]
|
|
1805
|
+
}
|
|
1806
|
+
)
|
|
1639
1807
|
] }),
|
|
1640
1808
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
1641
1809
|
/* @__PURE__ */ jsx("label", { className: "text-xs text-[var(--muted-foreground)]", children: "Aspect Ratio" }),
|
|
1642
|
-
/* @__PURE__ */ jsxs(
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1810
|
+
/* @__PURE__ */ jsxs(
|
|
1811
|
+
Select,
|
|
1812
|
+
{
|
|
1813
|
+
value: nodeData.aspectRatio,
|
|
1814
|
+
onValueChange: handleAspectRatioChange,
|
|
1815
|
+
children: [
|
|
1816
|
+
/* @__PURE__ */ jsx(SelectTrigger, { className: "nodrag h-8 w-full", children: /* @__PURE__ */ jsx(SelectValue, {}) }),
|
|
1817
|
+
/* @__PURE__ */ jsx(SelectContent, { children: ASPECT_RATIOS.map((ratio) => /* @__PURE__ */ jsx(SelectItem, { value: ratio.value, children: ratio.label }, ratio.value)) })
|
|
1818
|
+
]
|
|
1819
|
+
}
|
|
1820
|
+
)
|
|
1646
1821
|
] })
|
|
1647
1822
|
] }),
|
|
1648
1823
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
@@ -1702,27 +1877,35 @@ function MotionControlNodeComponent(props) {
|
|
|
1702
1877
|
var MotionControlNode = memo(MotionControlNodeComponent);
|
|
1703
1878
|
var TTS_ENABLED = process.env.NEXT_PUBLIC_TTS_ENABLED === "true";
|
|
1704
1879
|
var PROVIDERS = [
|
|
1705
|
-
{
|
|
1706
|
-
{
|
|
1880
|
+
{ label: "ElevenLabs", value: "elevenlabs" },
|
|
1881
|
+
{ label: "OpenAI", value: "openai" }
|
|
1707
1882
|
];
|
|
1708
1883
|
var VOICES = [
|
|
1709
|
-
{
|
|
1710
|
-
{
|
|
1711
|
-
{
|
|
1712
|
-
{
|
|
1713
|
-
{
|
|
1714
|
-
{
|
|
1715
|
-
{
|
|
1716
|
-
{
|
|
1717
|
-
{
|
|
1718
|
-
{
|
|
1719
|
-
{
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
{
|
|
1725
|
-
{
|
|
1884
|
+
{ description: "American female, calm", label: "Rachel", value: "rachel" },
|
|
1885
|
+
{ description: "American male, confident", label: "Drew", value: "drew" },
|
|
1886
|
+
{ description: "American male, war veteran", label: "Clyde", value: "clyde" },
|
|
1887
|
+
{ description: "American male, narrative", label: "Paul", value: "paul" },
|
|
1888
|
+
{ description: "American female, assertive", label: "Domi", value: "domi" },
|
|
1889
|
+
{ description: "British male, conversational", label: "Dave", value: "dave" },
|
|
1890
|
+
{ description: "Irish male, sailor", label: "Fin", value: "fin" },
|
|
1891
|
+
{ description: "American female, soft", label: "Sarah", value: "sarah" },
|
|
1892
|
+
{ description: "American male, friendly", label: "Antoni", value: "antoni" },
|
|
1893
|
+
{ description: "American male, calm", label: "Thomas", value: "thomas" },
|
|
1894
|
+
{
|
|
1895
|
+
description: "Australian male, casual",
|
|
1896
|
+
label: "Charlie",
|
|
1897
|
+
value: "charlie"
|
|
1898
|
+
},
|
|
1899
|
+
{ description: "American female, calm", label: "Emily", value: "emily" },
|
|
1900
|
+
{
|
|
1901
|
+
description: "British female, pleasant",
|
|
1902
|
+
label: "Dorothy",
|
|
1903
|
+
value: "dorothy"
|
|
1904
|
+
},
|
|
1905
|
+
{ description: "American male, deep", label: "Josh", value: "josh" },
|
|
1906
|
+
{ description: "American male, narrator", label: "Arnold", value: "arnold" },
|
|
1907
|
+
{ description: "American male, deep", label: "Adam", value: "adam" },
|
|
1908
|
+
{ description: "American male, raspy", label: "Sam", value: "sam" }
|
|
1726
1909
|
];
|
|
1727
1910
|
function TextToSpeechNodeComponent(props) {
|
|
1728
1911
|
const { id, type, data } = props;
|
|
@@ -1735,7 +1918,9 @@ function TextToSpeechNodeComponent(props) {
|
|
|
1735
1918
|
});
|
|
1736
1919
|
const handleProviderChange = useCallback(
|
|
1737
1920
|
(value) => {
|
|
1738
|
-
updateNodeData(id, {
|
|
1921
|
+
updateNodeData(id, {
|
|
1922
|
+
provider: value
|
|
1923
|
+
});
|
|
1739
1924
|
},
|
|
1740
1925
|
[id, updateNodeData]
|
|
1741
1926
|
);
|
|
@@ -1783,9 +1968,11 @@ function TextToSpeechNodeComponent(props) {
|
|
|
1783
1968
|
/* @__PURE__ */ jsxs("div", { className: "text-amber-500", children: [
|
|
1784
1969
|
/* @__PURE__ */ jsx("p", { className: "font-medium", children: "ElevenLabs not configured" }),
|
|
1785
1970
|
/* @__PURE__ */ jsxs("p", { className: "text-amber-500/80 mt-0.5", children: [
|
|
1786
|
-
"Set
|
|
1971
|
+
"Set",
|
|
1972
|
+
" ",
|
|
1787
1973
|
/* @__PURE__ */ jsx("code", { className: "bg-amber-500/20 px-1 rounded", children: "ELEVENLABS_API_KEY" }),
|
|
1788
|
-
"
|
|
1974
|
+
" ",
|
|
1975
|
+
"in API and",
|
|
1789
1976
|
" ",
|
|
1790
1977
|
/* @__PURE__ */ jsx("code", { className: "bg-amber-500/20 px-1 rounded", children: "NEXT_PUBLIC_TTS_ENABLED=true" }),
|
|
1791
1978
|
" ",
|
|
@@ -1795,10 +1982,17 @@ function TextToSpeechNodeComponent(props) {
|
|
|
1795
1982
|
] }),
|
|
1796
1983
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
1797
1984
|
/* @__PURE__ */ jsx("label", { className: "text-xs text-[var(--muted-foreground)]", children: "Provider" }),
|
|
1798
|
-
/* @__PURE__ */ jsxs(
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1985
|
+
/* @__PURE__ */ jsxs(
|
|
1986
|
+
Select,
|
|
1987
|
+
{
|
|
1988
|
+
value: nodeData.provider,
|
|
1989
|
+
onValueChange: handleProviderChange,
|
|
1990
|
+
children: [
|
|
1991
|
+
/* @__PURE__ */ jsx(SelectTrigger, { className: "nodrag h-8 w-full", children: /* @__PURE__ */ jsx(SelectValue, {}) }),
|
|
1992
|
+
/* @__PURE__ */ jsx(SelectContent, { children: PROVIDERS.map((provider) => /* @__PURE__ */ jsx(SelectItem, { value: provider.value, children: provider.label }, provider.value)) })
|
|
1993
|
+
]
|
|
1994
|
+
}
|
|
1995
|
+
)
|
|
1802
1996
|
] }),
|
|
1803
1997
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
1804
1998
|
/* @__PURE__ */ jsx("label", { className: "text-xs text-[var(--muted-foreground)]", children: "Voice" }),
|
|
@@ -1896,15 +2090,15 @@ function TextToSpeechNodeComponent(props) {
|
|
|
1896
2090
|
}
|
|
1897
2091
|
var TextToSpeechNode = memo(TextToSpeechNodeComponent);
|
|
1898
2092
|
var LANGUAGES = [
|
|
1899
|
-
{
|
|
1900
|
-
{
|
|
1901
|
-
{
|
|
1902
|
-
{
|
|
1903
|
-
{
|
|
1904
|
-
{
|
|
1905
|
-
{
|
|
1906
|
-
{
|
|
1907
|
-
{
|
|
2093
|
+
{ label: "Auto-detect", value: "auto" },
|
|
2094
|
+
{ label: "English", value: "en" },
|
|
2095
|
+
{ label: "Spanish", value: "es" },
|
|
2096
|
+
{ label: "French", value: "fr" },
|
|
2097
|
+
{ label: "German", value: "de" },
|
|
2098
|
+
{ label: "Japanese", value: "ja" },
|
|
2099
|
+
{ label: "Chinese", value: "zh" },
|
|
2100
|
+
{ label: "Korean", value: "ko" },
|
|
2101
|
+
{ label: "Portuguese", value: "pt" }
|
|
1908
2102
|
];
|
|
1909
2103
|
function TranscribeNodeComponent(props) {
|
|
1910
2104
|
const { id, type, data } = props;
|
|
@@ -1918,7 +2112,9 @@ function TranscribeNodeComponent(props) {
|
|
|
1918
2112
|
});
|
|
1919
2113
|
const handleLanguageChange = useCallback(
|
|
1920
2114
|
(value) => {
|
|
1921
|
-
updateNodeData(id, {
|
|
2115
|
+
updateNodeData(id, {
|
|
2116
|
+
language: value
|
|
2117
|
+
});
|
|
1922
2118
|
},
|
|
1923
2119
|
[id, updateNodeData]
|
|
1924
2120
|
);
|
|
@@ -1938,17 +2134,33 @@ function TranscribeNodeComponent(props) {
|
|
|
1938
2134
|
openNodeDetailModal(id, "preview");
|
|
1939
2135
|
}, [id, openNodeDetailModal]);
|
|
1940
2136
|
const headerActions = useMemo(
|
|
1941
|
-
() => nodeData.outputText ? /* @__PURE__ */ jsx(
|
|
2137
|
+
() => nodeData.outputText ? /* @__PURE__ */ jsx(
|
|
2138
|
+
Button,
|
|
2139
|
+
{
|
|
2140
|
+
variant: "ghost",
|
|
2141
|
+
size: "icon-sm",
|
|
2142
|
+
onClick: handleExpand,
|
|
2143
|
+
title: "Expand preview",
|
|
2144
|
+
children: /* @__PURE__ */ jsx(Expand, { className: "h-3 w-3" })
|
|
2145
|
+
}
|
|
2146
|
+
) : null,
|
|
1942
2147
|
[nodeData.outputText, handleExpand]
|
|
1943
2148
|
);
|
|
1944
2149
|
return /* @__PURE__ */ jsx(BaseNode, { ...props, headerActions, children: /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
1945
2150
|
/* @__PURE__ */ jsx("div", { className: "text-xs text-[var(--muted-foreground)]", children: "Using: Whisper Large V3" }),
|
|
1946
2151
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
1947
2152
|
/* @__PURE__ */ jsx("label", { className: "text-xs text-[var(--muted-foreground)]", children: "Language" }),
|
|
1948
|
-
/* @__PURE__ */ jsxs(
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
2153
|
+
/* @__PURE__ */ jsxs(
|
|
2154
|
+
Select,
|
|
2155
|
+
{
|
|
2156
|
+
value: nodeData.language,
|
|
2157
|
+
onValueChange: handleLanguageChange,
|
|
2158
|
+
children: [
|
|
2159
|
+
/* @__PURE__ */ jsx(SelectTrigger, { className: "nodrag h-8 w-full", children: /* @__PURE__ */ jsx(SelectValue, {}) }),
|
|
2160
|
+
/* @__PURE__ */ jsx(SelectContent, { children: LANGUAGES.map((lang) => /* @__PURE__ */ jsx(SelectItem, { value: lang.value, children: lang.label }, lang.value)) })
|
|
2161
|
+
]
|
|
2162
|
+
}
|
|
2163
|
+
)
|
|
1952
2164
|
] }),
|
|
1953
2165
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 nodrag", children: [
|
|
1954
2166
|
/* @__PURE__ */ jsx(
|
|
@@ -2010,22 +2222,24 @@ function VideoGenNodeComponent(props) {
|
|
|
2010
2222
|
const openNodeDetailModal = useUIStore((state) => state.openNodeDetailModal);
|
|
2011
2223
|
const { handleGenerate, handleStop } = useNodeExecution(id);
|
|
2012
2224
|
const { canGenerate } = useCanGenerate({
|
|
2225
|
+
inputSchema: nodeData.selectedModel?.inputSchema,
|
|
2013
2226
|
nodeId: id,
|
|
2014
2227
|
nodeType: type,
|
|
2015
|
-
inputSchema: nodeData.selectedModel?.inputSchema,
|
|
2016
2228
|
schemaParams: nodeData.schemaParams
|
|
2017
2229
|
});
|
|
2018
2230
|
const [isModelBrowserOpen, setIsModelBrowserOpen] = useState(false);
|
|
2019
|
-
const { handleModelSelect } = useModelSelection(
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2231
|
+
const { handleModelSelect } = useModelSelection(
|
|
2232
|
+
{
|
|
2233
|
+
fallbackModel: DEFAULT_VIDEO_MODEL,
|
|
2234
|
+
modelMap: VIDEO_MODEL_MAP,
|
|
2235
|
+
nodeId: id
|
|
2236
|
+
}
|
|
2237
|
+
);
|
|
2024
2238
|
useAutoLoadModelSchema({
|
|
2025
2239
|
currentModel: nodeData.model,
|
|
2026
|
-
selectedModel: nodeData.selectedModel,
|
|
2027
2240
|
modelIdMap: VIDEO_MODEL_ID_MAP,
|
|
2028
|
-
onModelSelect: handleModelSelect
|
|
2241
|
+
onModelSelect: handleModelSelect,
|
|
2242
|
+
selectedModel: nodeData.selectedModel
|
|
2029
2243
|
});
|
|
2030
2244
|
const {
|
|
2031
2245
|
schemaProperties,
|
|
@@ -2035,8 +2249,8 @@ function VideoGenNodeComponent(props) {
|
|
|
2035
2249
|
componentSchemas
|
|
2036
2250
|
} = useAIGenNode({
|
|
2037
2251
|
nodeId: id,
|
|
2038
|
-
|
|
2039
|
-
|
|
2252
|
+
schemaParams: nodeData.schemaParams,
|
|
2253
|
+
selectedModel: nodeData.selectedModel
|
|
2040
2254
|
});
|
|
2041
2255
|
const handleExpand = useCallback(() => {
|
|
2042
2256
|
openNodeDetailModal(id, "preview");
|
|
@@ -2048,14 +2262,14 @@ function VideoGenNodeComponent(props) {
|
|
|
2048
2262
|
const isProcessing = nodeData.status === "processing";
|
|
2049
2263
|
const handleModelBrowse = useCallback(() => setIsModelBrowserOpen(true), []);
|
|
2050
2264
|
const { titleElement, headerActions } = useAIGenNodeHeader({
|
|
2051
|
-
modelDisplayName,
|
|
2052
|
-
isProcessing,
|
|
2053
2265
|
canGenerate,
|
|
2054
2266
|
hasOutput: !!nodeData.outputVideo,
|
|
2055
|
-
|
|
2267
|
+
isProcessing,
|
|
2268
|
+
modelDisplayName,
|
|
2269
|
+
onExpand: handleExpand,
|
|
2056
2270
|
onGenerate: handleGenerate,
|
|
2057
|
-
|
|
2058
|
-
|
|
2271
|
+
onModelBrowse: handleModelBrowse,
|
|
2272
|
+
onStop: handleStop
|
|
2059
2273
|
});
|
|
2060
2274
|
const disabledInputs = useMemo(() => {
|
|
2061
2275
|
if (modelSupportsImageInput) return void 0;
|
|
@@ -2133,7 +2347,9 @@ function VoiceChangeNodeComponent(props) {
|
|
|
2133
2347
|
const handlePreserveOriginalChange = useCallback(
|
|
2134
2348
|
(checked) => {
|
|
2135
2349
|
if (typeof checked === "boolean") {
|
|
2136
|
-
updateNodeData(id, {
|
|
2350
|
+
updateNodeData(id, {
|
|
2351
|
+
preserveOriginalAudio: checked
|
|
2352
|
+
});
|
|
2137
2353
|
}
|
|
2138
2354
|
},
|
|
2139
2355
|
[id, updateNodeData]
|
|
@@ -2152,7 +2368,16 @@ function VoiceChangeNodeComponent(props) {
|
|
|
2152
2368
|
openNodeDetailModal(id, "preview");
|
|
2153
2369
|
}, [id, openNodeDetailModal]);
|
|
2154
2370
|
const headerActions = useMemo(
|
|
2155
|
-
() => nodeData.outputVideo ? /* @__PURE__ */ jsx(
|
|
2371
|
+
() => nodeData.outputVideo ? /* @__PURE__ */ jsx(
|
|
2372
|
+
Button,
|
|
2373
|
+
{
|
|
2374
|
+
variant: "ghost",
|
|
2375
|
+
size: "icon-sm",
|
|
2376
|
+
onClick: handleExpand,
|
|
2377
|
+
title: "Expand preview",
|
|
2378
|
+
children: /* @__PURE__ */ jsx(Expand, { className: "h-3 w-3" })
|
|
2379
|
+
}
|
|
2380
|
+
) : null,
|
|
2156
2381
|
[nodeData.outputVideo, handleExpand]
|
|
2157
2382
|
);
|
|
2158
2383
|
return /* @__PURE__ */ jsx(BaseNode, { ...props, headerActions, children: /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
@@ -2238,291 +2463,43 @@ function VoiceChangeNodeComponent(props) {
|
|
|
2238
2463
|
] }) });
|
|
2239
2464
|
}
|
|
2240
2465
|
var VoiceChangeNode = memo(VoiceChangeNodeComponent);
|
|
2241
|
-
var
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
const
|
|
2250
|
-
const
|
|
2251
|
-
const
|
|
2252
|
-
|
|
2253
|
-
return getNodeById(nodeDetailNodeId);
|
|
2254
|
-
}, [nodeDetailNodeId, getNodeById]);
|
|
2255
|
-
useEffect(() => {
|
|
2256
|
-
if (activeModal !== "nodeDetail" || !node) return;
|
|
2257
|
-
if (PROMPT_NODE_TYPES.includes(node.type)) {
|
|
2258
|
-
const promptData = node.data;
|
|
2259
|
-
closeNodeDetailModal();
|
|
2260
|
-
openEditor(node.id, promptData.prompt ?? "");
|
|
2261
|
-
}
|
|
2262
|
-
}, [activeModal, node, closeNodeDetailModal, openEditor]);
|
|
2263
|
-
const mediaInfo = useMemo(() => {
|
|
2264
|
-
if (!node) return { url: null, type: null };
|
|
2265
|
-
return getMediaFromNode(node.type, node.data);
|
|
2266
|
-
}, [node]);
|
|
2267
|
-
const nodeDef = useMemo(() => {
|
|
2268
|
-
if (!node) return null;
|
|
2269
|
-
return NODE_DEFINITIONS[node.type];
|
|
2270
|
-
}, [node]);
|
|
2271
|
-
const imageUrls = mediaInfo.urls ?? [];
|
|
2272
|
-
const hasMultipleImages = imageUrls.length > 1;
|
|
2273
|
-
const displayUrl = hasMultipleImages ? imageUrls[currentIndex] ?? mediaInfo.url : mediaInfo.url;
|
|
2274
|
-
const goToPrevious = useCallback(() => {
|
|
2275
|
-
setCurrentIndex((prev) => Math.max(prev - 1, 0));
|
|
2276
|
-
setZoomLevel(1);
|
|
2277
|
-
setPanOffset({ x: 0, y: 0 });
|
|
2278
|
-
}, []);
|
|
2279
|
-
const goToNext = useCallback(() => {
|
|
2280
|
-
setCurrentIndex((prev) => Math.min(prev + 1, imageUrls.length - 1));
|
|
2281
|
-
setZoomLevel(1);
|
|
2282
|
-
setPanOffset({ x: 0, y: 0 });
|
|
2283
|
-
}, [imageUrls.length]);
|
|
2284
|
-
useEffect(() => {
|
|
2285
|
-
setZoomLevel(1);
|
|
2286
|
-
setPanOffset({ x: 0, y: 0 });
|
|
2287
|
-
setCurrentIndex(nodeDetailStartIndex);
|
|
2288
|
-
}, [nodeDetailNodeId, nodeDetailStartIndex]);
|
|
2289
|
-
useEffect(() => {
|
|
2290
|
-
const handleKeyDown = (e) => {
|
|
2291
|
-
if (activeModal !== "nodeDetail") return;
|
|
2292
|
-
if (e.key === "Escape") {
|
|
2293
|
-
closeNodeDetailModal();
|
|
2294
|
-
}
|
|
2295
|
-
if (e.key === "+" || e.key === "=") {
|
|
2296
|
-
setZoomLevel((prev) => Math.min(prev + 0.25, 4));
|
|
2297
|
-
}
|
|
2298
|
-
if (e.key === "-") {
|
|
2299
|
-
setZoomLevel((prev) => Math.max(prev - 0.25, 0.25));
|
|
2300
|
-
}
|
|
2301
|
-
if (e.key === "0") {
|
|
2302
|
-
setZoomLevel(1);
|
|
2303
|
-
setPanOffset({ x: 0, y: 0 });
|
|
2304
|
-
}
|
|
2305
|
-
if (e.key === "ArrowLeft") {
|
|
2306
|
-
goToPrevious();
|
|
2307
|
-
}
|
|
2308
|
-
if (e.key === "ArrowRight") {
|
|
2309
|
-
goToNext();
|
|
2310
|
-
}
|
|
2311
|
-
};
|
|
2312
|
-
window.addEventListener("keydown", handleKeyDown);
|
|
2313
|
-
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
2314
|
-
}, [activeModal, closeNodeDetailModal, goToPrevious, goToNext]);
|
|
2315
|
-
const handleMouseDown = useCallback(
|
|
2466
|
+
var INPUT_TYPES = [
|
|
2467
|
+
{ label: "Image", value: "image" },
|
|
2468
|
+
{ label: "Video", value: "video" },
|
|
2469
|
+
{ label: "Text", value: "text" },
|
|
2470
|
+
{ label: "Audio", value: "audio" },
|
|
2471
|
+
{ label: "Number", value: "number" }
|
|
2472
|
+
];
|
|
2473
|
+
function WorkflowInputNodeComponent(props) {
|
|
2474
|
+
const { id, data } = props;
|
|
2475
|
+
const nodeData = data;
|
|
2476
|
+
const updateNodeData = useWorkflowStore((state) => state.updateNodeData);
|
|
2477
|
+
const handleNameChange = useCallback(
|
|
2316
2478
|
(e) => {
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
}
|
|
2479
|
+
updateNodeData(id, {
|
|
2480
|
+
inputName: e.target.value,
|
|
2481
|
+
label: `Input: ${e.target.value}`
|
|
2482
|
+
});
|
|
2321
2483
|
},
|
|
2322
|
-
[
|
|
2484
|
+
[id, updateNodeData]
|
|
2323
2485
|
);
|
|
2324
|
-
const
|
|
2325
|
-
(
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2486
|
+
const handleTypeChange = useCallback(
|
|
2487
|
+
(value) => {
|
|
2488
|
+
updateNodeData(id, {
|
|
2489
|
+
inputType: value
|
|
2490
|
+
});
|
|
2491
|
+
},
|
|
2492
|
+
[id, updateNodeData]
|
|
2493
|
+
);
|
|
2494
|
+
const handleRequiredChange = useCallback(
|
|
2495
|
+
(checked) => {
|
|
2496
|
+
if (typeof checked === "boolean") {
|
|
2497
|
+
updateNodeData(id, {
|
|
2498
|
+
required: checked
|
|
2330
2499
|
});
|
|
2331
2500
|
}
|
|
2332
2501
|
},
|
|
2333
|
-
[
|
|
2334
|
-
);
|
|
2335
|
-
const handleMouseUp = useCallback(() => {
|
|
2336
|
-
setIsPanning(false);
|
|
2337
|
-
}, []);
|
|
2338
|
-
const handleDownload = useCallback(() => {
|
|
2339
|
-
const url = displayUrl ?? mediaInfo.url;
|
|
2340
|
-
if (!url) return;
|
|
2341
|
-
const link = document.createElement("a");
|
|
2342
|
-
link.href = url;
|
|
2343
|
-
const suffix = hasMultipleImages ? `_${currentIndex + 1}` : "";
|
|
2344
|
-
link.download = `${node?.data.label || "output"}${suffix}.${mediaInfo.type === "video" ? "mp4" : "png"}`;
|
|
2345
|
-
document.body.appendChild(link);
|
|
2346
|
-
link.click();
|
|
2347
|
-
document.body.removeChild(link);
|
|
2348
|
-
}, [displayUrl, mediaInfo, node, hasMultipleImages, currentIndex]);
|
|
2349
|
-
if (activeModal !== "nodeDetail" || !node || !nodeDef) {
|
|
2350
|
-
return null;
|
|
2351
|
-
}
|
|
2352
|
-
if (PROMPT_NODE_TYPES.includes(node.type)) {
|
|
2353
|
-
return null;
|
|
2354
|
-
}
|
|
2355
|
-
const nodeData = node.data;
|
|
2356
|
-
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2357
|
-
/* @__PURE__ */ jsx("div", { className: "fixed inset-0 z-50 bg-black/80", onClick: closeNodeDetailModal }),
|
|
2358
|
-
/* @__PURE__ */ jsxs("div", { className: "fixed inset-4 z-50 flex flex-col bg-card rounded-lg border border-border shadow-xl overflow-hidden", children: [
|
|
2359
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-4 py-3 border-b border-border", children: [
|
|
2360
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
2361
|
-
/* @__PURE__ */ jsx("h2", { className: "text-lg font-medium text-foreground", children: nodeData.label }),
|
|
2362
|
-
/* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground bg-secondary px-2 py-0.5 rounded", children: nodeDef.label })
|
|
2363
|
-
] }),
|
|
2364
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
2365
|
-
displayUrl && /* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", onClick: handleDownload, children: [
|
|
2366
|
-
/* @__PURE__ */ jsx(Download, { className: "h-4 w-4 mr-1" }),
|
|
2367
|
-
"Download"
|
|
2368
|
-
] }),
|
|
2369
|
-
/* @__PURE__ */ jsx(Button, { variant: "ghost", size: "icon-sm", onClick: closeNodeDetailModal, children: /* @__PURE__ */ jsx(X, { className: "w-5 h-5" }) })
|
|
2370
|
-
] })
|
|
2371
|
-
] }),
|
|
2372
|
-
/* @__PURE__ */ jsx("div", { className: "flex-1 overflow-hidden", children: /* @__PURE__ */ jsxs(
|
|
2373
|
-
"div",
|
|
2374
|
-
{
|
|
2375
|
-
className: "relative w-full h-full flex items-center justify-center bg-background overflow-hidden",
|
|
2376
|
-
onMouseDown: handleMouseDown,
|
|
2377
|
-
onMouseMove: handleMouseMove,
|
|
2378
|
-
onMouseUp: handleMouseUp,
|
|
2379
|
-
onMouseLeave: handleMouseUp,
|
|
2380
|
-
style: { cursor: zoomLevel > 1 ? isPanning ? "grabbing" : "grab" : "default" },
|
|
2381
|
-
children: [
|
|
2382
|
-
displayUrl ? /* @__PURE__ */ jsxs(
|
|
2383
|
-
"div",
|
|
2384
|
-
{
|
|
2385
|
-
className: "transition-transform duration-100",
|
|
2386
|
-
style: {
|
|
2387
|
-
transform: `scale(${zoomLevel}) translate(${panOffset.x / zoomLevel}px, ${panOffset.y / zoomLevel}px)`
|
|
2388
|
-
},
|
|
2389
|
-
children: [
|
|
2390
|
-
mediaInfo.type === "image" && /* @__PURE__ */ jsx(
|
|
2391
|
-
Image4,
|
|
2392
|
-
{
|
|
2393
|
-
src: displayUrl,
|
|
2394
|
-
alt: nodeData.label,
|
|
2395
|
-
width: 800,
|
|
2396
|
-
height: 600,
|
|
2397
|
-
className: "max-h-[calc(100vh-200px)] max-w-[calc(100vw-100px)] object-contain rounded-lg",
|
|
2398
|
-
unoptimized: true
|
|
2399
|
-
}
|
|
2400
|
-
),
|
|
2401
|
-
mediaInfo.type === "video" && /* @__PURE__ */ jsx(
|
|
2402
|
-
"video",
|
|
2403
|
-
{
|
|
2404
|
-
src: displayUrl,
|
|
2405
|
-
controls: true,
|
|
2406
|
-
autoPlay: true,
|
|
2407
|
-
loop: true,
|
|
2408
|
-
className: "max-h-[calc(100vh-200px)] max-w-[calc(100vw-100px)] rounded-lg"
|
|
2409
|
-
}
|
|
2410
|
-
)
|
|
2411
|
-
]
|
|
2412
|
-
}
|
|
2413
|
-
) : /* @__PURE__ */ jsxs("div", { className: "text-muted-foreground text-center", children: [
|
|
2414
|
-
/* @__PURE__ */ jsx("p", { className: "text-lg", children: "No preview available" }),
|
|
2415
|
-
/* @__PURE__ */ jsx("p", { className: "text-sm mt-2", children: "Generate content to see the preview" })
|
|
2416
|
-
] }),
|
|
2417
|
-
hasMultipleImages && currentIndex > 0 && /* @__PURE__ */ jsx(
|
|
2418
|
-
Button,
|
|
2419
|
-
{
|
|
2420
|
-
variant: "ghost",
|
|
2421
|
-
size: "icon-sm",
|
|
2422
|
-
onClick: goToPrevious,
|
|
2423
|
-
className: "absolute left-4 top-1/2 -translate-y-1/2 bg-card/80 hover:bg-card border border-border shadow-md",
|
|
2424
|
-
title: "Previous image (\u2190)",
|
|
2425
|
-
children: /* @__PURE__ */ jsx(ChevronLeft, { className: "w-5 h-5" })
|
|
2426
|
-
}
|
|
2427
|
-
),
|
|
2428
|
-
hasMultipleImages && currentIndex < imageUrls.length - 1 && /* @__PURE__ */ jsx(
|
|
2429
|
-
Button,
|
|
2430
|
-
{
|
|
2431
|
-
variant: "ghost",
|
|
2432
|
-
size: "icon-sm",
|
|
2433
|
-
onClick: goToNext,
|
|
2434
|
-
className: "absolute right-4 top-1/2 -translate-y-1/2 bg-card/80 hover:bg-card border border-border shadow-md",
|
|
2435
|
-
title: "Next image (\u2192)",
|
|
2436
|
-
children: /* @__PURE__ */ jsx(ChevronRight, { className: "w-5 h-5" })
|
|
2437
|
-
}
|
|
2438
|
-
),
|
|
2439
|
-
hasMultipleImages && /* @__PURE__ */ jsxs("div", { className: "absolute bottom-4 left-1/2 -translate-x-1/2 bg-card/80 border border-border rounded-full px-3 py-1 text-xs text-muted-foreground shadow-md", children: [
|
|
2440
|
-
currentIndex + 1,
|
|
2441
|
-
" / ",
|
|
2442
|
-
imageUrls.length
|
|
2443
|
-
] }),
|
|
2444
|
-
displayUrl && mediaInfo.type === "image" && /* @__PURE__ */ jsxs("div", { className: "absolute bottom-4 right-4 flex items-center gap-2 bg-card border border-border rounded-lg p-1", children: [
|
|
2445
|
-
/* @__PURE__ */ jsx(
|
|
2446
|
-
Button,
|
|
2447
|
-
{
|
|
2448
|
-
variant: "ghost",
|
|
2449
|
-
size: "icon-sm",
|
|
2450
|
-
onClick: () => setZoomLevel((prev) => Math.max(prev - 0.25, 0.25)),
|
|
2451
|
-
title: "Zoom out (-)",
|
|
2452
|
-
children: /* @__PURE__ */ jsx(ZoomOut, { className: "w-4 h-4" })
|
|
2453
|
-
}
|
|
2454
|
-
),
|
|
2455
|
-
/* @__PURE__ */ jsxs("span", { className: "text-xs text-muted-foreground w-12 text-center", children: [
|
|
2456
|
-
Math.round(zoomLevel * 100),
|
|
2457
|
-
"%"
|
|
2458
|
-
] }),
|
|
2459
|
-
/* @__PURE__ */ jsx(
|
|
2460
|
-
Button,
|
|
2461
|
-
{
|
|
2462
|
-
variant: "ghost",
|
|
2463
|
-
size: "icon-sm",
|
|
2464
|
-
onClick: () => setZoomLevel((prev) => Math.min(prev + 0.25, 4)),
|
|
2465
|
-
title: "Zoom in (+)",
|
|
2466
|
-
children: /* @__PURE__ */ jsx(ZoomIn, { className: "w-4 h-4" })
|
|
2467
|
-
}
|
|
2468
|
-
),
|
|
2469
|
-
/* @__PURE__ */ jsx(
|
|
2470
|
-
Button,
|
|
2471
|
-
{
|
|
2472
|
-
variant: "ghost",
|
|
2473
|
-
size: "sm",
|
|
2474
|
-
onClick: () => {
|
|
2475
|
-
setZoomLevel(1);
|
|
2476
|
-
setPanOffset({ x: 0, y: 0 });
|
|
2477
|
-
},
|
|
2478
|
-
title: "Reset zoom (0)",
|
|
2479
|
-
children: "Reset"
|
|
2480
|
-
}
|
|
2481
|
-
)
|
|
2482
|
-
] })
|
|
2483
|
-
]
|
|
2484
|
-
}
|
|
2485
|
-
) })
|
|
2486
|
-
] })
|
|
2487
|
-
] });
|
|
2488
|
-
}
|
|
2489
|
-
var INPUT_TYPES = [
|
|
2490
|
-
{ value: "image", label: "Image" },
|
|
2491
|
-
{ value: "video", label: "Video" },
|
|
2492
|
-
{ value: "text", label: "Text" },
|
|
2493
|
-
{ value: "audio", label: "Audio" },
|
|
2494
|
-
{ value: "number", label: "Number" }
|
|
2495
|
-
];
|
|
2496
|
-
function WorkflowInputNodeComponent(props) {
|
|
2497
|
-
const { id, data } = props;
|
|
2498
|
-
const nodeData = data;
|
|
2499
|
-
const updateNodeData = useWorkflowStore((state) => state.updateNodeData);
|
|
2500
|
-
const handleNameChange = useCallback(
|
|
2501
|
-
(e) => {
|
|
2502
|
-
updateNodeData(id, {
|
|
2503
|
-
inputName: e.target.value,
|
|
2504
|
-
label: `Input: ${e.target.value}`
|
|
2505
|
-
});
|
|
2506
|
-
},
|
|
2507
|
-
[id, updateNodeData]
|
|
2508
|
-
);
|
|
2509
|
-
const handleTypeChange = useCallback(
|
|
2510
|
-
(value) => {
|
|
2511
|
-
updateNodeData(id, {
|
|
2512
|
-
inputType: value
|
|
2513
|
-
});
|
|
2514
|
-
},
|
|
2515
|
-
[id, updateNodeData]
|
|
2516
|
-
);
|
|
2517
|
-
const handleRequiredChange = useCallback(
|
|
2518
|
-
(checked) => {
|
|
2519
|
-
if (typeof checked === "boolean") {
|
|
2520
|
-
updateNodeData(id, {
|
|
2521
|
-
required: checked
|
|
2522
|
-
});
|
|
2523
|
-
}
|
|
2524
|
-
},
|
|
2525
|
-
[id, updateNodeData]
|
|
2502
|
+
[id, updateNodeData]
|
|
2526
2503
|
);
|
|
2527
2504
|
const handleDescriptionChange = useCallback(
|
|
2528
2505
|
(e) => {
|
|
@@ -2549,10 +2526,17 @@ function WorkflowInputNodeComponent(props) {
|
|
|
2549
2526
|
] }),
|
|
2550
2527
|
/* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
|
|
2551
2528
|
/* @__PURE__ */ jsx(Label, { className: "text-xs", children: "Data Type" }),
|
|
2552
|
-
/* @__PURE__ */ jsxs(
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2529
|
+
/* @__PURE__ */ jsxs(
|
|
2530
|
+
Select,
|
|
2531
|
+
{
|
|
2532
|
+
value: nodeData.inputType || "image",
|
|
2533
|
+
onValueChange: handleTypeChange,
|
|
2534
|
+
children: [
|
|
2535
|
+
/* @__PURE__ */ jsx(SelectTrigger, { className: "nodrag h-9 w-full", children: /* @__PURE__ */ jsx(SelectValue, {}) }),
|
|
2536
|
+
/* @__PURE__ */ jsx(SelectContent, { children: INPUT_TYPES.map((type) => /* @__PURE__ */ jsx(SelectItem, { value: type.value, children: type.label }, type.value)) })
|
|
2537
|
+
]
|
|
2538
|
+
}
|
|
2539
|
+
)
|
|
2556
2540
|
] }),
|
|
2557
2541
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 nodrag", children: [
|
|
2558
2542
|
/* @__PURE__ */ jsx(
|
|
@@ -2587,11 +2571,11 @@ function WorkflowInputNodeComponent(props) {
|
|
|
2587
2571
|
}
|
|
2588
2572
|
var WorkflowInputNode = memo(WorkflowInputNodeComponent);
|
|
2589
2573
|
var OUTPUT_TYPES = [
|
|
2590
|
-
{
|
|
2591
|
-
{
|
|
2592
|
-
{
|
|
2593
|
-
{
|
|
2594
|
-
{
|
|
2574
|
+
{ label: "Image", value: "image" },
|
|
2575
|
+
{ label: "Video", value: "video" },
|
|
2576
|
+
{ label: "Text", value: "text" },
|
|
2577
|
+
{ label: "Audio", value: "audio" },
|
|
2578
|
+
{ label: "Number", value: "number" }
|
|
2595
2579
|
];
|
|
2596
2580
|
function WorkflowOutputNodeComponent(props) {
|
|
2597
2581
|
const { id, data } = props;
|
|
@@ -2602,8 +2586,8 @@ function WorkflowOutputNodeComponent(props) {
|
|
|
2602
2586
|
const handleNameChange = useCallback(
|
|
2603
2587
|
(e) => {
|
|
2604
2588
|
updateNodeData(id, {
|
|
2605
|
-
|
|
2606
|
-
|
|
2589
|
+
label: `Output: ${e.target.value}`,
|
|
2590
|
+
outputName: e.target.value
|
|
2607
2591
|
});
|
|
2608
2592
|
},
|
|
2609
2593
|
[id, updateNodeData]
|
|
@@ -2699,35 +2683,49 @@ function WorkflowOutputNodeComponent(props) {
|
|
|
2699
2683
|
String(nodeData.inputValue).substring(0, 200),
|
|
2700
2684
|
String(nodeData.inputValue).length > 200 && "..."
|
|
2701
2685
|
] }),
|
|
2702
|
-
outputType === "audio" && /* @__PURE__ */ jsx(
|
|
2686
|
+
outputType === "audio" && /* @__PURE__ */ jsx(
|
|
2687
|
+
"audio",
|
|
2688
|
+
{
|
|
2689
|
+
src: nodeData.inputValue,
|
|
2690
|
+
controls: true,
|
|
2691
|
+
className: "mt-1 w-full h-8"
|
|
2692
|
+
}
|
|
2693
|
+
),
|
|
2703
2694
|
outputType === "number" && /* @__PURE__ */ jsx("div", { className: "mt-1 p-2 bg-secondary/50 rounded text-sm font-mono", children: String(nodeData.inputValue) })
|
|
2704
2695
|
] }),
|
|
2705
2696
|
!hasValue && /* @__PURE__ */ jsx("div", { className: "mt-1 text-[10px] text-muted-foreground text-center", children: "Connect an input to define the workflow output" }),
|
|
2706
2697
|
/* @__PURE__ */ jsxs("div", { className: "text-[10px] text-muted-foreground text-center", children: [
|
|
2707
|
-
"Input type:
|
|
2698
|
+
"Input type:",
|
|
2699
|
+
" ",
|
|
2708
2700
|
/* @__PURE__ */ jsx("span", { className: "font-medium capitalize", children: outputType })
|
|
2709
2701
|
] })
|
|
2710
2702
|
] }) });
|
|
2711
2703
|
}
|
|
2712
2704
|
var WorkflowOutputNode = memo(WorkflowOutputNodeComponent);
|
|
2713
2705
|
var HANDLE_COLORS2 = {
|
|
2706
|
+
audio: "var(--handle-audio)",
|
|
2714
2707
|
image: "var(--handle-image)",
|
|
2715
|
-
video: "var(--handle-video)",
|
|
2716
|
-
text: "var(--handle-text)",
|
|
2717
2708
|
number: "var(--handle-number)",
|
|
2718
|
-
|
|
2709
|
+
text: "var(--handle-text)",
|
|
2710
|
+
video: "var(--handle-video)"
|
|
2719
2711
|
};
|
|
2720
2712
|
var noopApi = {
|
|
2721
2713
|
fetchReferencableWorkflows: async () => {
|
|
2722
|
-
console.warn(
|
|
2714
|
+
console.warn(
|
|
2715
|
+
"[workflow-ui] WorkflowRefApi not configured: fetchReferencableWorkflows"
|
|
2716
|
+
);
|
|
2723
2717
|
return [];
|
|
2724
2718
|
},
|
|
2725
|
-
validateReference: async () => {
|
|
2726
|
-
console.warn("[workflow-ui] WorkflowRefApi not configured: validateReference");
|
|
2727
|
-
},
|
|
2728
2719
|
fetchWorkflowInterface: async () => {
|
|
2729
|
-
console.warn(
|
|
2720
|
+
console.warn(
|
|
2721
|
+
"[workflow-ui] WorkflowRefApi not configured: fetchWorkflowInterface"
|
|
2722
|
+
);
|
|
2730
2723
|
return { inputs: [], outputs: [] };
|
|
2724
|
+
},
|
|
2725
|
+
validateReference: async () => {
|
|
2726
|
+
console.warn(
|
|
2727
|
+
"[workflow-ui] WorkflowRefApi not configured: validateReference"
|
|
2728
|
+
);
|
|
2731
2729
|
}
|
|
2732
2730
|
};
|
|
2733
2731
|
var workflowRefApi = noopApi;
|
|
@@ -2767,11 +2765,11 @@ function WorkflowRefNodeComponent(props) {
|
|
|
2767
2765
|
async (selectedId) => {
|
|
2768
2766
|
if (!selectedId || selectedId === "none") {
|
|
2769
2767
|
updateNodeData(id, {
|
|
2770
|
-
referencedWorkflowId: null,
|
|
2771
|
-
referencedWorkflowName: null,
|
|
2772
2768
|
cachedInterface: null,
|
|
2773
2769
|
inputMappings: {},
|
|
2774
|
-
outputMappings: {}
|
|
2770
|
+
outputMappings: {},
|
|
2771
|
+
referencedWorkflowId: null,
|
|
2772
|
+
referencedWorkflowName: null
|
|
2775
2773
|
});
|
|
2776
2774
|
return;
|
|
2777
2775
|
}
|
|
@@ -2794,12 +2792,12 @@ function WorkflowRefNodeComponent(props) {
|
|
|
2794
2792
|
outputMappings[output.name] = null;
|
|
2795
2793
|
}
|
|
2796
2794
|
updateNodeData(id, {
|
|
2797
|
-
referencedWorkflowId: selectedId,
|
|
2798
|
-
referencedWorkflowName: selectedWorkflow.name,
|
|
2799
2795
|
cachedInterface: selectedWorkflow.interface,
|
|
2800
2796
|
inputMappings,
|
|
2797
|
+
label: `Subworkflow: ${selectedWorkflow.name}`,
|
|
2801
2798
|
outputMappings,
|
|
2802
|
-
|
|
2799
|
+
referencedWorkflowId: selectedId,
|
|
2800
|
+
referencedWorkflowName: selectedWorkflow.name
|
|
2803
2801
|
});
|
|
2804
2802
|
setError(null);
|
|
2805
2803
|
},
|
|
@@ -2812,13 +2810,17 @@ function WorkflowRefNodeComponent(props) {
|
|
|
2812
2810
|
const response = await workflowRefApi.fetchWorkflowInterface(
|
|
2813
2811
|
nodeData.referencedWorkflowId
|
|
2814
2812
|
);
|
|
2815
|
-
const inputMappings = {
|
|
2813
|
+
const inputMappings = {
|
|
2814
|
+
...nodeData.inputMappings
|
|
2815
|
+
};
|
|
2816
2816
|
for (const input of response.inputs) {
|
|
2817
2817
|
if (!(input.name in inputMappings)) {
|
|
2818
2818
|
inputMappings[input.name] = null;
|
|
2819
2819
|
}
|
|
2820
2820
|
}
|
|
2821
|
-
const outputMappings = {
|
|
2821
|
+
const outputMappings = {
|
|
2822
|
+
...nodeData.outputMappings
|
|
2823
|
+
};
|
|
2822
2824
|
for (const output of response.outputs) {
|
|
2823
2825
|
if (!(output.name in outputMappings)) {
|
|
2824
2826
|
outputMappings[output.name] = null;
|
|
@@ -2841,21 +2843,17 @@ function WorkflowRefNodeComponent(props) {
|
|
|
2841
2843
|
nodeData.outputMappings,
|
|
2842
2844
|
updateNodeData
|
|
2843
2845
|
]);
|
|
2844
|
-
const inputHandles = (nodeData.cachedInterface?.inputs ?? []).map(
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
type: output.type,
|
|
2856
|
-
label: output.name
|
|
2857
|
-
})
|
|
2858
|
-
);
|
|
2846
|
+
const inputHandles = (nodeData.cachedInterface?.inputs ?? []).map((input) => ({
|
|
2847
|
+
id: input.name,
|
|
2848
|
+
label: input.name,
|
|
2849
|
+
required: input.required,
|
|
2850
|
+
type: input.type
|
|
2851
|
+
}));
|
|
2852
|
+
const outputHandles = (nodeData.cachedInterface?.outputs ?? []).map((output) => ({
|
|
2853
|
+
id: output.name,
|
|
2854
|
+
label: output.name,
|
|
2855
|
+
type: output.type
|
|
2856
|
+
}));
|
|
2859
2857
|
const isSelected = selected;
|
|
2860
2858
|
const hasWorkflow = !!nodeData.referencedWorkflowId;
|
|
2861
2859
|
const isProcessing = nodeData.status === "processing";
|
|
@@ -2870,7 +2868,9 @@ function WorkflowRefNodeComponent(props) {
|
|
|
2870
2868
|
),
|
|
2871
2869
|
style: {
|
|
2872
2870
|
"--node-color": "var(--category-composition)",
|
|
2873
|
-
...isSelected && {
|
|
2871
|
+
...isSelected && {
|
|
2872
|
+
"--tw-ring-color": "var(--category-composition)"
|
|
2873
|
+
}
|
|
2874
2874
|
},
|
|
2875
2875
|
children: [
|
|
2876
2876
|
inputHandles.map((input, index) => /* @__PURE__ */ jsx(
|
|
@@ -2881,8 +2881,8 @@ function WorkflowRefNodeComponent(props) {
|
|
|
2881
2881
|
id: input.id,
|
|
2882
2882
|
className: "!w-3 !h-3",
|
|
2883
2883
|
style: {
|
|
2884
|
-
|
|
2885
|
-
|
|
2884
|
+
background: HANDLE_COLORS2[input.type],
|
|
2885
|
+
top: `${(index + 1) / (inputHandles.length + 1) * 100}%`
|
|
2886
2886
|
},
|
|
2887
2887
|
title: `${input.label} (${input.type})${input.required ? " - required" : ""}`
|
|
2888
2888
|
},
|
|
@@ -2930,7 +2930,12 @@ function WorkflowRefNodeComponent(props) {
|
|
|
2930
2930
|
onClick: handleRefreshInterface,
|
|
2931
2931
|
disabled: isLoading,
|
|
2932
2932
|
title: "Refresh interface",
|
|
2933
|
-
children: /* @__PURE__ */ jsx(
|
|
2933
|
+
children: /* @__PURE__ */ jsx(
|
|
2934
|
+
RefreshCw,
|
|
2935
|
+
{
|
|
2936
|
+
className: clsx("h-3 w-3", isLoading && "animate-spin")
|
|
2937
|
+
}
|
|
2938
|
+
)
|
|
2934
2939
|
}
|
|
2935
2940
|
)
|
|
2936
2941
|
] }),
|
|
@@ -2978,8 +2983,8 @@ function WorkflowRefNodeComponent(props) {
|
|
|
2978
2983
|
id: output.id,
|
|
2979
2984
|
className: "!w-3 !h-3",
|
|
2980
2985
|
style: {
|
|
2981
|
-
|
|
2982
|
-
|
|
2986
|
+
background: HANDLE_COLORS2[output.type],
|
|
2987
|
+
top: `${(index + 1) / (outputHandles.length + 1) * 100}%`
|
|
2983
2988
|
},
|
|
2984
2989
|
title: `${output.label} (${output.type})`
|
|
2985
2990
|
},
|
|
@@ -3008,8 +3013,8 @@ function AudioInputNodeComponent(props) {
|
|
|
3008
3013
|
audio.onloadedmetadata = () => {
|
|
3009
3014
|
updateNodeData(id, {
|
|
3010
3015
|
audio: event.target?.result,
|
|
3011
|
-
filename: file.name,
|
|
3012
3016
|
duration: audio.duration,
|
|
3017
|
+
filename: file.name,
|
|
3013
3018
|
source: "upload"
|
|
3014
3019
|
});
|
|
3015
3020
|
};
|
|
@@ -3022,8 +3027,8 @@ function AudioInputNodeComponent(props) {
|
|
|
3022
3027
|
const handleRemoveAudio = useCallback(() => {
|
|
3023
3028
|
updateNodeData(id, {
|
|
3024
3029
|
audio: null,
|
|
3025
|
-
filename: null,
|
|
3026
3030
|
duration: null,
|
|
3031
|
+
filename: null,
|
|
3027
3032
|
url: void 0
|
|
3028
3033
|
});
|
|
3029
3034
|
setUrlValue("");
|
|
@@ -3035,8 +3040,8 @@ function AudioInputNodeComponent(props) {
|
|
|
3035
3040
|
audio.onloadedmetadata = () => {
|
|
3036
3041
|
updateNodeData(id, {
|
|
3037
3042
|
audio: urlValue,
|
|
3038
|
-
filename: urlValue.split("/").pop() || "url-audio",
|
|
3039
3043
|
duration: audio.duration,
|
|
3044
|
+
filename: urlValue.split("/").pop() || "url-audio",
|
|
3040
3045
|
source: "url",
|
|
3041
3046
|
url: urlValue
|
|
3042
3047
|
});
|
|
@@ -3045,8 +3050,8 @@ function AudioInputNodeComponent(props) {
|
|
|
3045
3050
|
audio.onerror = () => {
|
|
3046
3051
|
updateNodeData(id, {
|
|
3047
3052
|
audio: urlValue,
|
|
3048
|
-
filename: urlValue.split("/").pop() || "url-audio",
|
|
3049
3053
|
duration: null,
|
|
3054
|
+
filename: urlValue.split("/").pop() || "url-audio",
|
|
3050
3055
|
source: "url",
|
|
3051
3056
|
url: urlValue
|
|
3052
3057
|
});
|
|
@@ -3096,7 +3101,16 @@ function AudioInputNodeComponent(props) {
|
|
|
3096
3101
|
children: /* @__PURE__ */ jsx(Link, { className: "h-3.5 w-3.5" })
|
|
3097
3102
|
}
|
|
3098
3103
|
),
|
|
3099
|
-
nodeData.audio && /* @__PURE__ */ jsx(
|
|
3104
|
+
nodeData.audio && /* @__PURE__ */ jsx(
|
|
3105
|
+
Button,
|
|
3106
|
+
{
|
|
3107
|
+
variant: "ghost",
|
|
3108
|
+
size: "icon-sm",
|
|
3109
|
+
onClick: handleExpand,
|
|
3110
|
+
title: "Expand preview",
|
|
3111
|
+
children: /* @__PURE__ */ jsx(Expand, { className: "h-3.5 w-3.5" })
|
|
3112
|
+
}
|
|
3113
|
+
)
|
|
3100
3114
|
] }),
|
|
3101
3115
|
[showUrlInput, nodeData.audio, handleExpand]
|
|
3102
3116
|
);
|
|
@@ -3183,39 +3197,48 @@ function ImageInputNodeComponent(props) {
|
|
|
3183
3197
|
handleUrlSubmit,
|
|
3184
3198
|
handleUrlKeyDown
|
|
3185
3199
|
} = useMediaUpload({
|
|
3186
|
-
|
|
3187
|
-
|
|
3188
|
-
|
|
3189
|
-
|
|
3190
|
-
|
|
3191
|
-
|
|
3192
|
-
},
|
|
3200
|
+
buildRemoveUpdate: () => ({
|
|
3201
|
+
dimensions: null,
|
|
3202
|
+
filename: null,
|
|
3203
|
+
image: null,
|
|
3204
|
+
url: void 0
|
|
3205
|
+
}),
|
|
3193
3206
|
buildUploadUpdate: (url, filename, metadata) => ({
|
|
3194
|
-
image: url,
|
|
3195
|
-
filename,
|
|
3196
3207
|
dimensions: metadata,
|
|
3208
|
+
filename,
|
|
3209
|
+
image: url,
|
|
3197
3210
|
source: "upload"
|
|
3198
3211
|
}),
|
|
3199
3212
|
buildUrlUpdate: (url, metadata) => ({
|
|
3200
|
-
image: url,
|
|
3201
|
-
filename: url.split("/").pop() || "url-image",
|
|
3202
3213
|
dimensions: metadata,
|
|
3214
|
+
filename: url.split("/").pop() || "url-image",
|
|
3215
|
+
image: url,
|
|
3203
3216
|
source: "url",
|
|
3204
3217
|
url
|
|
3205
3218
|
}),
|
|
3206
|
-
|
|
3207
|
-
|
|
3208
|
-
|
|
3209
|
-
|
|
3210
|
-
|
|
3211
|
-
|
|
3219
|
+
getMetadata: async (src) => {
|
|
3220
|
+
const dims = await getImageDimensions(src);
|
|
3221
|
+
return dims;
|
|
3222
|
+
},
|
|
3223
|
+
initialUrl: nodeData.url || "",
|
|
3224
|
+
mediaType: "image",
|
|
3225
|
+
nodeId: id
|
|
3212
3226
|
});
|
|
3213
3227
|
const handleExpand = useCallback(() => {
|
|
3214
3228
|
openNodeDetailModal(id, "preview");
|
|
3215
3229
|
}, [id, openNodeDetailModal]);
|
|
3216
3230
|
const headerActions = useMemo(
|
|
3217
3231
|
() => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
|
|
3218
|
-
nodeData.image && /* @__PURE__ */ jsx(
|
|
3232
|
+
nodeData.image && /* @__PURE__ */ jsx(
|
|
3233
|
+
Button,
|
|
3234
|
+
{
|
|
3235
|
+
variant: "ghost",
|
|
3236
|
+
size: "icon-sm",
|
|
3237
|
+
onClick: handleExpand,
|
|
3238
|
+
title: "Expand preview",
|
|
3239
|
+
children: /* @__PURE__ */ jsx(Expand, { className: "h-3.5 w-3.5" })
|
|
3240
|
+
}
|
|
3241
|
+
),
|
|
3219
3242
|
/* @__PURE__ */ jsx(
|
|
3220
3243
|
Button,
|
|
3221
3244
|
{
|
|
@@ -3334,7 +3357,9 @@ function PromptConstructorNodeComponent(props) {
|
|
|
3334
3357
|
}
|
|
3335
3358
|
}, [nodeData.template, isEditing]);
|
|
3336
3359
|
const availableVariables = useMemo(() => {
|
|
3337
|
-
const connectedPromptNodes = edges.filter((e) => e.target === id && e.targetHandle === "text").map((e) => nodes.find((n) => n.id === e.source)).filter(
|
|
3360
|
+
const connectedPromptNodes = edges.filter((e) => e.target === id && e.targetHandle === "text").map((e) => nodes.find((n) => n.id === e.source)).filter(
|
|
3361
|
+
(n) => n !== void 0 && n.type === "prompt"
|
|
3362
|
+
);
|
|
3338
3363
|
const vars = [];
|
|
3339
3364
|
connectedPromptNodes.forEach((promptNode) => {
|
|
3340
3365
|
const promptData = promptNode.data;
|
|
@@ -3342,8 +3367,8 @@ function PromptConstructorNodeComponent(props) {
|
|
|
3342
3367
|
if (variableName) {
|
|
3343
3368
|
vars.push({
|
|
3344
3369
|
name: variableName,
|
|
3345
|
-
|
|
3346
|
-
|
|
3370
|
+
nodeId: promptNode.id,
|
|
3371
|
+
value: promptData.prompt || ""
|
|
3347
3372
|
});
|
|
3348
3373
|
}
|
|
3349
3374
|
});
|
|
@@ -3360,10 +3385,10 @@ function PromptConstructorNodeComponent(props) {
|
|
|
3360
3385
|
closeAutocomplete
|
|
3361
3386
|
} = usePromptAutocomplete({
|
|
3362
3387
|
availableVariables,
|
|
3363
|
-
textareaRef,
|
|
3364
3388
|
localTemplate,
|
|
3389
|
+
onTemplateCommit: (newTemplate) => updateNodeData(id, { template: newTemplate }),
|
|
3365
3390
|
setLocalTemplate,
|
|
3366
|
-
|
|
3391
|
+
textareaRef
|
|
3367
3392
|
});
|
|
3368
3393
|
const unresolvedVars = useMemo(() => {
|
|
3369
3394
|
const varPattern = /@(\w+)/g;
|
|
@@ -3392,16 +3417,26 @@ function PromptConstructorNodeComponent(props) {
|
|
|
3392
3417
|
});
|
|
3393
3418
|
const outputValue = resolved || null;
|
|
3394
3419
|
if (outputValue !== nodeData.outputText) {
|
|
3395
|
-
updateNodeData(id, {
|
|
3420
|
+
updateNodeData(id, {
|
|
3421
|
+
outputText: outputValue
|
|
3422
|
+
});
|
|
3396
3423
|
}
|
|
3397
|
-
}, [
|
|
3424
|
+
}, [
|
|
3425
|
+
nodeData.template,
|
|
3426
|
+
availableVariables,
|
|
3427
|
+
id,
|
|
3428
|
+
updateNodeData,
|
|
3429
|
+
nodeData.outputText
|
|
3430
|
+
]);
|
|
3398
3431
|
const handleFocus = useCallback(() => {
|
|
3399
3432
|
setIsEditing(true);
|
|
3400
3433
|
}, []);
|
|
3401
3434
|
const handleBlur = useCallback(() => {
|
|
3402
3435
|
setIsEditing(false);
|
|
3403
3436
|
if (localTemplate !== nodeData.template) {
|
|
3404
|
-
updateNodeData(id, {
|
|
3437
|
+
updateNodeData(id, {
|
|
3438
|
+
template: localTemplate
|
|
3439
|
+
});
|
|
3405
3440
|
}
|
|
3406
3441
|
setTimeout(() => closeAutocomplete(), 200);
|
|
3407
3442
|
}, [id, localTemplate, nodeData.template, updateNodeData, closeAutocomplete]);
|
|
@@ -3432,8 +3467,8 @@ function PromptConstructorNodeComponent(props) {
|
|
|
3432
3467
|
{
|
|
3433
3468
|
className: "absolute z-10 bg-popover border border-border rounded shadow-xl max-h-40 overflow-y-auto",
|
|
3434
3469
|
style: {
|
|
3435
|
-
|
|
3436
|
-
|
|
3470
|
+
left: autocompletePosition.left,
|
|
3471
|
+
top: autocompletePosition.top
|
|
3437
3472
|
},
|
|
3438
3473
|
children: filteredAutocompleteVars.map((variable, index) => /* @__PURE__ */ jsxs(
|
|
3439
3474
|
"button",
|
|
@@ -3486,18 +3521,18 @@ function PromptNodeComponent(props) {
|
|
|
3486
3521
|
if (!nodeData.prompt) return;
|
|
3487
3522
|
openCreateModal({
|
|
3488
3523
|
_id: "",
|
|
3489
|
-
name: "",
|
|
3490
|
-
description: "",
|
|
3491
|
-
promptText: nodeData.prompt,
|
|
3492
|
-
styleSettings: {},
|
|
3493
3524
|
category: "custom",
|
|
3494
|
-
tags: [],
|
|
3495
|
-
useCount: 0,
|
|
3496
|
-
isFeatured: false,
|
|
3497
|
-
isSystem: false,
|
|
3498
|
-
isDeleted: false,
|
|
3499
3525
|
createdAt: "",
|
|
3500
|
-
|
|
3526
|
+
description: "",
|
|
3527
|
+
isDeleted: false,
|
|
3528
|
+
isFeatured: false,
|
|
3529
|
+
isSystem: false,
|
|
3530
|
+
name: "",
|
|
3531
|
+
promptText: nodeData.prompt,
|
|
3532
|
+
styleSettings: {},
|
|
3533
|
+
tags: [],
|
|
3534
|
+
updatedAt: "",
|
|
3535
|
+
useCount: 0
|
|
3501
3536
|
});
|
|
3502
3537
|
}, [nodeData.prompt, openCreateModal]);
|
|
3503
3538
|
const handleExpand = useCallback(() => {
|
|
@@ -3505,7 +3540,16 @@ function PromptNodeComponent(props) {
|
|
|
3505
3540
|
}, [id, nodeData.prompt, openEditor]);
|
|
3506
3541
|
const titleElement = PromptPicker ? /* @__PURE__ */ jsx(PromptPicker, { onSelect: handleSelectFromLibrary, label: "Prompt" }) : /* @__PURE__ */ jsx("span", { className: "text-xs font-medium", children: "Prompt" });
|
|
3507
3542
|
const headerActions = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3508
|
-
/* @__PURE__ */ jsx(
|
|
3543
|
+
/* @__PURE__ */ jsx(
|
|
3544
|
+
Button,
|
|
3545
|
+
{
|
|
3546
|
+
variant: "ghost",
|
|
3547
|
+
size: "icon-sm",
|
|
3548
|
+
onClick: handleExpand,
|
|
3549
|
+
title: "Expand editor",
|
|
3550
|
+
children: /* @__PURE__ */ jsx(Expand, { className: "w-3.5 h-3.5" })
|
|
3551
|
+
}
|
|
3552
|
+
),
|
|
3509
3553
|
/* @__PURE__ */ jsx(
|
|
3510
3554
|
Button,
|
|
3511
3555
|
{
|
|
@@ -3518,15 +3562,23 @@ function PromptNodeComponent(props) {
|
|
|
3518
3562
|
}
|
|
3519
3563
|
)
|
|
3520
3564
|
] });
|
|
3521
|
-
return /* @__PURE__ */ jsx(
|
|
3522
|
-
|
|
3565
|
+
return /* @__PURE__ */ jsx(
|
|
3566
|
+
BaseNode,
|
|
3523
3567
|
{
|
|
3524
|
-
|
|
3525
|
-
|
|
3526
|
-
|
|
3527
|
-
|
|
3568
|
+
...props,
|
|
3569
|
+
titleElement,
|
|
3570
|
+
headerActions,
|
|
3571
|
+
children: /* @__PURE__ */ jsx(
|
|
3572
|
+
"textarea",
|
|
3573
|
+
{
|
|
3574
|
+
value: nodeData.prompt || "",
|
|
3575
|
+
onChange: handlePromptChange,
|
|
3576
|
+
placeholder: "Enter your prompt...",
|
|
3577
|
+
className: "nodrag nopan w-full flex-1 min-h-[80px] px-2 py-1.5 text-sm bg-background border border-border rounded resize-none focus:outline-none focus:ring-1 focus:ring-primary"
|
|
3578
|
+
}
|
|
3579
|
+
)
|
|
3528
3580
|
}
|
|
3529
|
-
)
|
|
3581
|
+
);
|
|
3530
3582
|
}
|
|
3531
3583
|
var PromptNode = memo(PromptNodeComponent);
|
|
3532
3584
|
function VideoInputNodeComponent(props) {
|
|
@@ -3545,51 +3597,51 @@ function VideoInputNodeComponent(props) {
|
|
|
3545
3597
|
handleUrlSubmit,
|
|
3546
3598
|
handleUrlKeyDown
|
|
3547
3599
|
} = useMediaUpload({
|
|
3548
|
-
|
|
3549
|
-
|
|
3550
|
-
|
|
3551
|
-
|
|
3552
|
-
|
|
3553
|
-
|
|
3554
|
-
},
|
|
3600
|
+
buildRemoveUpdate: () => ({
|
|
3601
|
+
dimensions: null,
|
|
3602
|
+
duration: null,
|
|
3603
|
+
filename: null,
|
|
3604
|
+
url: void 0,
|
|
3605
|
+
video: null
|
|
3606
|
+
}),
|
|
3555
3607
|
buildUploadUpdate: (url, filename, metadata) => {
|
|
3556
3608
|
const meta = metadata;
|
|
3557
3609
|
return {
|
|
3558
|
-
video: url,
|
|
3559
|
-
filename,
|
|
3560
|
-
duration: meta.duration,
|
|
3561
3610
|
dimensions: meta.dimensions,
|
|
3562
|
-
|
|
3611
|
+
duration: meta.duration,
|
|
3612
|
+
filename,
|
|
3613
|
+
source: "upload",
|
|
3614
|
+
video: url
|
|
3563
3615
|
};
|
|
3564
3616
|
},
|
|
3565
3617
|
buildUrlUpdate: (url, metadata) => {
|
|
3566
3618
|
if (metadata) {
|
|
3567
3619
|
const meta = metadata;
|
|
3568
3620
|
return {
|
|
3569
|
-
|
|
3570
|
-
filename: url.split("/").pop() || "url-video",
|
|
3621
|
+
dimensions: { height: meta.height, width: meta.width },
|
|
3571
3622
|
duration: meta.duration,
|
|
3572
|
-
|
|
3623
|
+
filename: url.split("/").pop() || "url-video",
|
|
3573
3624
|
source: "url",
|
|
3574
|
-
url
|
|
3625
|
+
url,
|
|
3626
|
+
video: url
|
|
3575
3627
|
};
|
|
3576
3628
|
}
|
|
3577
3629
|
return {
|
|
3578
|
-
video: url,
|
|
3579
|
-
filename: url.split("/").pop() || "url-video",
|
|
3580
|
-
duration: null,
|
|
3581
3630
|
dimensions: null,
|
|
3631
|
+
duration: null,
|
|
3632
|
+
filename: url.split("/").pop() || "url-video",
|
|
3582
3633
|
source: "url",
|
|
3583
|
-
url
|
|
3634
|
+
url,
|
|
3635
|
+
video: url
|
|
3584
3636
|
};
|
|
3585
3637
|
},
|
|
3586
|
-
|
|
3587
|
-
|
|
3588
|
-
|
|
3589
|
-
|
|
3590
|
-
|
|
3591
|
-
|
|
3592
|
-
|
|
3638
|
+
getMetadata: async (src) => {
|
|
3639
|
+
const meta = await getVideoMetadata(src);
|
|
3640
|
+
return meta;
|
|
3641
|
+
},
|
|
3642
|
+
initialUrl: nodeData.url || "",
|
|
3643
|
+
mediaType: "video",
|
|
3644
|
+
nodeId: id
|
|
3593
3645
|
});
|
|
3594
3646
|
const formatDuration = (seconds) => {
|
|
3595
3647
|
if (!seconds) return "";
|
|
@@ -3602,7 +3654,16 @@ function VideoInputNodeComponent(props) {
|
|
|
3602
3654
|
}, [id, openNodeDetailModal]);
|
|
3603
3655
|
const headerActions = useMemo(
|
|
3604
3656
|
() => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
|
|
3605
|
-
nodeData.video && /* @__PURE__ */ jsx(
|
|
3657
|
+
nodeData.video && /* @__PURE__ */ jsx(
|
|
3658
|
+
Button,
|
|
3659
|
+
{
|
|
3660
|
+
variant: "ghost",
|
|
3661
|
+
size: "icon-sm",
|
|
3662
|
+
onClick: handleExpand,
|
|
3663
|
+
title: "Expand preview",
|
|
3664
|
+
children: /* @__PURE__ */ jsx(Expand, { className: "h-3.5 w-3.5" })
|
|
3665
|
+
}
|
|
3666
|
+
),
|
|
3606
3667
|
/* @__PURE__ */ jsx(
|
|
3607
3668
|
Button,
|
|
3608
3669
|
{
|
|
@@ -3702,6 +3763,278 @@ function VideoInputNodeComponent(props) {
|
|
|
3702
3763
|
] });
|
|
3703
3764
|
}
|
|
3704
3765
|
var VideoInputNode = memo(VideoInputNodeComponent);
|
|
3766
|
+
var PROMPT_NODE_TYPES = ["prompt"];
|
|
3767
|
+
function NodeDetailModal() {
|
|
3768
|
+
const {
|
|
3769
|
+
activeModal,
|
|
3770
|
+
nodeDetailNodeId,
|
|
3771
|
+
nodeDetailStartIndex,
|
|
3772
|
+
closeNodeDetailModal
|
|
3773
|
+
} = useUIStore();
|
|
3774
|
+
const { getNodeById } = useWorkflowStore();
|
|
3775
|
+
const { openEditor } = usePromptEditorStore();
|
|
3776
|
+
const [zoomLevel, setZoomLevel] = useState(1);
|
|
3777
|
+
const [panOffset, setPanOffset] = useState({ x: 0, y: 0 });
|
|
3778
|
+
const [isPanning, setIsPanning] = useState(false);
|
|
3779
|
+
const [panStart, setPanStart] = useState({ x: 0, y: 0 });
|
|
3780
|
+
const [currentIndex, setCurrentIndex] = useState(0);
|
|
3781
|
+
const node = useMemo(() => {
|
|
3782
|
+
if (!nodeDetailNodeId) return null;
|
|
3783
|
+
return getNodeById(nodeDetailNodeId);
|
|
3784
|
+
}, [nodeDetailNodeId, getNodeById]);
|
|
3785
|
+
useEffect(() => {
|
|
3786
|
+
if (activeModal !== "nodeDetail" || !node) return;
|
|
3787
|
+
if (PROMPT_NODE_TYPES.includes(node.type)) {
|
|
3788
|
+
const promptData = node.data;
|
|
3789
|
+
closeNodeDetailModal();
|
|
3790
|
+
openEditor(node.id, promptData.prompt ?? "");
|
|
3791
|
+
}
|
|
3792
|
+
}, [activeModal, node, closeNodeDetailModal, openEditor]);
|
|
3793
|
+
const mediaInfo = useMemo(() => {
|
|
3794
|
+
if (!node) return { type: null, url: null };
|
|
3795
|
+
return getMediaFromNode(
|
|
3796
|
+
node.type,
|
|
3797
|
+
node.data
|
|
3798
|
+
);
|
|
3799
|
+
}, [node]);
|
|
3800
|
+
const nodeDef = useMemo(() => {
|
|
3801
|
+
if (!node) return null;
|
|
3802
|
+
return NODE_DEFINITIONS[node.type];
|
|
3803
|
+
}, [node]);
|
|
3804
|
+
const imageUrls = mediaInfo.urls ?? [];
|
|
3805
|
+
const hasMultipleImages = imageUrls.length > 1;
|
|
3806
|
+
const displayUrl = hasMultipleImages ? imageUrls[currentIndex] ?? mediaInfo.url : mediaInfo.url;
|
|
3807
|
+
const goToPrevious = useCallback(() => {
|
|
3808
|
+
setCurrentIndex((prev) => Math.max(prev - 1, 0));
|
|
3809
|
+
setZoomLevel(1);
|
|
3810
|
+
setPanOffset({ x: 0, y: 0 });
|
|
3811
|
+
}, []);
|
|
3812
|
+
const goToNext = useCallback(() => {
|
|
3813
|
+
setCurrentIndex((prev) => Math.min(prev + 1, imageUrls.length - 1));
|
|
3814
|
+
setZoomLevel(1);
|
|
3815
|
+
setPanOffset({ x: 0, y: 0 });
|
|
3816
|
+
}, [imageUrls.length]);
|
|
3817
|
+
useEffect(() => {
|
|
3818
|
+
setZoomLevel(1);
|
|
3819
|
+
setPanOffset({ x: 0, y: 0 });
|
|
3820
|
+
setCurrentIndex(nodeDetailStartIndex);
|
|
3821
|
+
}, [nodeDetailNodeId, nodeDetailStartIndex]);
|
|
3822
|
+
useEffect(() => {
|
|
3823
|
+
const handleKeyDown = (e) => {
|
|
3824
|
+
if (activeModal !== "nodeDetail") return;
|
|
3825
|
+
if (e.key === "Escape") {
|
|
3826
|
+
closeNodeDetailModal();
|
|
3827
|
+
}
|
|
3828
|
+
if (e.key === "+" || e.key === "=") {
|
|
3829
|
+
setZoomLevel((prev) => Math.min(prev + 0.25, 4));
|
|
3830
|
+
}
|
|
3831
|
+
if (e.key === "-") {
|
|
3832
|
+
setZoomLevel((prev) => Math.max(prev - 0.25, 0.25));
|
|
3833
|
+
}
|
|
3834
|
+
if (e.key === "0") {
|
|
3835
|
+
setZoomLevel(1);
|
|
3836
|
+
setPanOffset({ x: 0, y: 0 });
|
|
3837
|
+
}
|
|
3838
|
+
if (e.key === "ArrowLeft") {
|
|
3839
|
+
goToPrevious();
|
|
3840
|
+
}
|
|
3841
|
+
if (e.key === "ArrowRight") {
|
|
3842
|
+
goToNext();
|
|
3843
|
+
}
|
|
3844
|
+
};
|
|
3845
|
+
window.addEventListener("keydown", handleKeyDown);
|
|
3846
|
+
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
3847
|
+
}, [activeModal, closeNodeDetailModal, goToPrevious, goToNext]);
|
|
3848
|
+
const handleMouseDown = useCallback(
|
|
3849
|
+
(e) => {
|
|
3850
|
+
if (zoomLevel > 1) {
|
|
3851
|
+
setIsPanning(true);
|
|
3852
|
+
setPanStart({ x: e.clientX - panOffset.x, y: e.clientY - panOffset.y });
|
|
3853
|
+
}
|
|
3854
|
+
},
|
|
3855
|
+
[zoomLevel, panOffset]
|
|
3856
|
+
);
|
|
3857
|
+
const handleMouseMove = useCallback(
|
|
3858
|
+
(e) => {
|
|
3859
|
+
if (isPanning) {
|
|
3860
|
+
setPanOffset({
|
|
3861
|
+
x: e.clientX - panStart.x,
|
|
3862
|
+
y: e.clientY - panStart.y
|
|
3863
|
+
});
|
|
3864
|
+
}
|
|
3865
|
+
},
|
|
3866
|
+
[isPanning, panStart]
|
|
3867
|
+
);
|
|
3868
|
+
const handleMouseUp = useCallback(() => {
|
|
3869
|
+
setIsPanning(false);
|
|
3870
|
+
}, []);
|
|
3871
|
+
const handleDownload = useCallback(() => {
|
|
3872
|
+
const url = displayUrl ?? mediaInfo.url;
|
|
3873
|
+
if (!url) return;
|
|
3874
|
+
const link = document.createElement("a");
|
|
3875
|
+
link.href = url;
|
|
3876
|
+
const suffix = hasMultipleImages ? `_${currentIndex + 1}` : "";
|
|
3877
|
+
link.download = `${node?.data.label || "output"}${suffix}.${mediaInfo.type === "video" ? "mp4" : "png"}`;
|
|
3878
|
+
document.body.appendChild(link);
|
|
3879
|
+
link.click();
|
|
3880
|
+
document.body.removeChild(link);
|
|
3881
|
+
}, [displayUrl, mediaInfo, node, hasMultipleImages, currentIndex]);
|
|
3882
|
+
if (activeModal !== "nodeDetail" || !node || !nodeDef) {
|
|
3883
|
+
return null;
|
|
3884
|
+
}
|
|
3885
|
+
if (PROMPT_NODE_TYPES.includes(node.type)) {
|
|
3886
|
+
return null;
|
|
3887
|
+
}
|
|
3888
|
+
const nodeData = node.data;
|
|
3889
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3890
|
+
/* @__PURE__ */ jsx(
|
|
3891
|
+
"div",
|
|
3892
|
+
{
|
|
3893
|
+
className: "fixed inset-0 z-50 bg-black/80",
|
|
3894
|
+
onClick: closeNodeDetailModal
|
|
3895
|
+
}
|
|
3896
|
+
),
|
|
3897
|
+
/* @__PURE__ */ jsxs("div", { className: "fixed inset-4 z-50 flex flex-col bg-card rounded-lg border border-border shadow-xl overflow-hidden", children: [
|
|
3898
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-4 py-3 border-b border-border", children: [
|
|
3899
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
3900
|
+
/* @__PURE__ */ jsx("h2", { className: "text-lg font-medium text-foreground", children: nodeData.label }),
|
|
3901
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground bg-secondary px-2 py-0.5 rounded", children: nodeDef.label })
|
|
3902
|
+
] }),
|
|
3903
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
3904
|
+
displayUrl && /* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", onClick: handleDownload, children: [
|
|
3905
|
+
/* @__PURE__ */ jsx(Download, { className: "h-4 w-4 mr-1" }),
|
|
3906
|
+
"Download"
|
|
3907
|
+
] }),
|
|
3908
|
+
/* @__PURE__ */ jsx(
|
|
3909
|
+
Button,
|
|
3910
|
+
{
|
|
3911
|
+
variant: "ghost",
|
|
3912
|
+
size: "icon-sm",
|
|
3913
|
+
onClick: closeNodeDetailModal,
|
|
3914
|
+
children: /* @__PURE__ */ jsx(X, { className: "w-5 h-5" })
|
|
3915
|
+
}
|
|
3916
|
+
)
|
|
3917
|
+
] })
|
|
3918
|
+
] }),
|
|
3919
|
+
/* @__PURE__ */ jsx("div", { className: "flex-1 overflow-hidden", children: /* @__PURE__ */ jsxs(
|
|
3920
|
+
"div",
|
|
3921
|
+
{
|
|
3922
|
+
className: "relative w-full h-full flex items-center justify-center bg-background overflow-hidden",
|
|
3923
|
+
onMouseDown: handleMouseDown,
|
|
3924
|
+
onMouseMove: handleMouseMove,
|
|
3925
|
+
onMouseUp: handleMouseUp,
|
|
3926
|
+
onMouseLeave: handleMouseUp,
|
|
3927
|
+
style: {
|
|
3928
|
+
cursor: zoomLevel > 1 ? isPanning ? "grabbing" : "grab" : "default"
|
|
3929
|
+
},
|
|
3930
|
+
children: [
|
|
3931
|
+
displayUrl ? /* @__PURE__ */ jsxs(
|
|
3932
|
+
"div",
|
|
3933
|
+
{
|
|
3934
|
+
className: "transition-transform duration-100",
|
|
3935
|
+
style: {
|
|
3936
|
+
transform: `scale(${zoomLevel}) translate(${panOffset.x / zoomLevel}px, ${panOffset.y / zoomLevel}px)`
|
|
3937
|
+
},
|
|
3938
|
+
children: [
|
|
3939
|
+
mediaInfo.type === "image" && /* @__PURE__ */ jsx(
|
|
3940
|
+
Image4,
|
|
3941
|
+
{
|
|
3942
|
+
src: displayUrl,
|
|
3943
|
+
alt: nodeData.label,
|
|
3944
|
+
width: 800,
|
|
3945
|
+
height: 600,
|
|
3946
|
+
className: "max-h-[calc(100vh-200px)] max-w-[calc(100vw-100px)] object-contain rounded-lg",
|
|
3947
|
+
unoptimized: true
|
|
3948
|
+
}
|
|
3949
|
+
),
|
|
3950
|
+
mediaInfo.type === "video" && /* @__PURE__ */ jsx(
|
|
3951
|
+
"video",
|
|
3952
|
+
{
|
|
3953
|
+
src: displayUrl,
|
|
3954
|
+
controls: true,
|
|
3955
|
+
autoPlay: true,
|
|
3956
|
+
loop: true,
|
|
3957
|
+
className: "max-h-[calc(100vh-200px)] max-w-[calc(100vw-100px)] rounded-lg"
|
|
3958
|
+
}
|
|
3959
|
+
)
|
|
3960
|
+
]
|
|
3961
|
+
}
|
|
3962
|
+
) : /* @__PURE__ */ jsxs("div", { className: "text-muted-foreground text-center", children: [
|
|
3963
|
+
/* @__PURE__ */ jsx("p", { className: "text-lg", children: "No preview available" }),
|
|
3964
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm mt-2", children: "Generate content to see the preview" })
|
|
3965
|
+
] }),
|
|
3966
|
+
hasMultipleImages && currentIndex > 0 && /* @__PURE__ */ jsx(
|
|
3967
|
+
Button,
|
|
3968
|
+
{
|
|
3969
|
+
variant: "ghost",
|
|
3970
|
+
size: "icon-sm",
|
|
3971
|
+
onClick: goToPrevious,
|
|
3972
|
+
className: "absolute left-4 top-1/2 -translate-y-1/2 bg-card/80 hover:bg-card border border-border shadow-md",
|
|
3973
|
+
title: "Previous image (\u2190)",
|
|
3974
|
+
children: /* @__PURE__ */ jsx(ChevronLeft, { className: "w-5 h-5" })
|
|
3975
|
+
}
|
|
3976
|
+
),
|
|
3977
|
+
hasMultipleImages && currentIndex < imageUrls.length - 1 && /* @__PURE__ */ jsx(
|
|
3978
|
+
Button,
|
|
3979
|
+
{
|
|
3980
|
+
variant: "ghost",
|
|
3981
|
+
size: "icon-sm",
|
|
3982
|
+
onClick: goToNext,
|
|
3983
|
+
className: "absolute right-4 top-1/2 -translate-y-1/2 bg-card/80 hover:bg-card border border-border shadow-md",
|
|
3984
|
+
title: "Next image (\u2192)",
|
|
3985
|
+
children: /* @__PURE__ */ jsx(ChevronRight, { className: "w-5 h-5" })
|
|
3986
|
+
}
|
|
3987
|
+
),
|
|
3988
|
+
hasMultipleImages && /* @__PURE__ */ jsxs("div", { className: "absolute bottom-4 left-1/2 -translate-x-1/2 bg-card/80 border border-border rounded-full px-3 py-1 text-xs text-muted-foreground shadow-md", children: [
|
|
3989
|
+
currentIndex + 1,
|
|
3990
|
+
" / ",
|
|
3991
|
+
imageUrls.length
|
|
3992
|
+
] }),
|
|
3993
|
+
displayUrl && mediaInfo.type === "image" && /* @__PURE__ */ jsxs("div", { className: "absolute bottom-4 right-4 flex items-center gap-2 bg-card border border-border rounded-lg p-1", children: [
|
|
3994
|
+
/* @__PURE__ */ jsx(
|
|
3995
|
+
Button,
|
|
3996
|
+
{
|
|
3997
|
+
variant: "ghost",
|
|
3998
|
+
size: "icon-sm",
|
|
3999
|
+
onClick: () => setZoomLevel((prev) => Math.max(prev - 0.25, 0.25)),
|
|
4000
|
+
title: "Zoom out (-)",
|
|
4001
|
+
children: /* @__PURE__ */ jsx(ZoomOut, { className: "w-4 h-4" })
|
|
4002
|
+
}
|
|
4003
|
+
),
|
|
4004
|
+
/* @__PURE__ */ jsxs("span", { className: "text-xs text-muted-foreground w-12 text-center", children: [
|
|
4005
|
+
Math.round(zoomLevel * 100),
|
|
4006
|
+
"%"
|
|
4007
|
+
] }),
|
|
4008
|
+
/* @__PURE__ */ jsx(
|
|
4009
|
+
Button,
|
|
4010
|
+
{
|
|
4011
|
+
variant: "ghost",
|
|
4012
|
+
size: "icon-sm",
|
|
4013
|
+
onClick: () => setZoomLevel((prev) => Math.min(prev + 0.25, 4)),
|
|
4014
|
+
title: "Zoom in (+)",
|
|
4015
|
+
children: /* @__PURE__ */ jsx(ZoomIn, { className: "w-4 h-4" })
|
|
4016
|
+
}
|
|
4017
|
+
),
|
|
4018
|
+
/* @__PURE__ */ jsx(
|
|
4019
|
+
Button,
|
|
4020
|
+
{
|
|
4021
|
+
variant: "ghost",
|
|
4022
|
+
size: "sm",
|
|
4023
|
+
onClick: () => {
|
|
4024
|
+
setZoomLevel(1);
|
|
4025
|
+
setPanOffset({ x: 0, y: 0 });
|
|
4026
|
+
},
|
|
4027
|
+
title: "Reset zoom (0)",
|
|
4028
|
+
children: "Reset"
|
|
4029
|
+
}
|
|
4030
|
+
)
|
|
4031
|
+
] })
|
|
4032
|
+
]
|
|
4033
|
+
}
|
|
4034
|
+
) })
|
|
4035
|
+
] })
|
|
4036
|
+
] });
|
|
4037
|
+
}
|
|
3705
4038
|
function getExtensionFromUrl(url, inputType) {
|
|
3706
4039
|
try {
|
|
3707
4040
|
const urlPath = new URL(url).pathname;
|
|
@@ -3805,10 +4138,18 @@ function DownloadNodeComponent(props) {
|
|
|
3805
4138
|
fileExtension
|
|
3806
4139
|
] })
|
|
3807
4140
|
] }),
|
|
3808
|
-
/* @__PURE__ */ jsxs(
|
|
3809
|
-
|
|
3810
|
-
|
|
3811
|
-
|
|
4141
|
+
/* @__PURE__ */ jsxs(
|
|
4142
|
+
Button,
|
|
4143
|
+
{
|
|
4144
|
+
className: "w-full",
|
|
4145
|
+
onClick: handleDownload,
|
|
4146
|
+
disabled: isDownloading,
|
|
4147
|
+
children: [
|
|
4148
|
+
isDownloading ? /* @__PURE__ */ jsx(Loader2, { className: "h-4 w-4 animate-spin" }) : /* @__PURE__ */ jsx(Download, { className: "h-4 w-4" }),
|
|
4149
|
+
isDownloading ? "Downloading..." : "Download"
|
|
4150
|
+
]
|
|
4151
|
+
}
|
|
4152
|
+
)
|
|
3812
4153
|
] }) : isConnected && isRunning ? /* @__PURE__ */ jsx("div", { className: "relative flex h-20 flex-col items-center justify-center rounded-md bg-black/20", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-2", children: [
|
|
3813
4154
|
/* @__PURE__ */ jsx(Loader2, { className: "h-8 w-8 animate-spin text-primary" }),
|
|
3814
4155
|
/* @__PURE__ */ jsx("span", { className: "text-xs text-white/80", children: "Generating..." })
|
|
@@ -3824,19 +4165,19 @@ function DownloadNodeComponent(props) {
|
|
|
3824
4165
|
var DownloadNode = memo(DownloadNodeComponent);
|
|
3825
4166
|
var OutputNode = DownloadNode;
|
|
3826
4167
|
var PRESET_OPTIONS = [
|
|
3827
|
-
{
|
|
3828
|
-
{
|
|
3829
|
-
{
|
|
3830
|
-
{
|
|
3831
|
-
{
|
|
3832
|
-
{
|
|
3833
|
-
{
|
|
3834
|
-
{
|
|
3835
|
-
{
|
|
3836
|
-
{
|
|
3837
|
-
{
|
|
3838
|
-
{
|
|
3839
|
-
{
|
|
4168
|
+
{ label: "Linear", value: "linear" },
|
|
4169
|
+
{ label: "Ease In", value: "easeIn" },
|
|
4170
|
+
{ label: "Ease Out", value: "easeOut" },
|
|
4171
|
+
{ label: "Ease In Out", value: "easeInOut" },
|
|
4172
|
+
{ label: "Ease In Quad", value: "easeInQuad" },
|
|
4173
|
+
{ label: "Ease Out Quad", value: "easeOutQuad" },
|
|
4174
|
+
{ label: "Ease In Out Quad", value: "easeInOutQuad" },
|
|
4175
|
+
{ label: "Ease In Cubic", value: "easeInCubic" },
|
|
4176
|
+
{ label: "Ease Out Cubic", value: "easeOutCubic" },
|
|
4177
|
+
{ label: "Ease In Out Cubic", value: "easeInOutCubic" },
|
|
4178
|
+
{ label: "Ease In Expo", value: "easeInExpo" },
|
|
4179
|
+
{ label: "Ease Out Expo", value: "easeOutExpo" },
|
|
4180
|
+
{ label: "Ease In Out Expo", value: "easeInOutExpo" }
|
|
3840
4181
|
];
|
|
3841
4182
|
function AnimationNodeComponent(props) {
|
|
3842
4183
|
const { id, type, data } = props;
|
|
@@ -3855,8 +4196,8 @@ function AnimationNodeComponent(props) {
|
|
|
3855
4196
|
(value) => {
|
|
3856
4197
|
const preset = value;
|
|
3857
4198
|
updateNodeData(id, {
|
|
3858
|
-
preset,
|
|
3859
|
-
|
|
4199
|
+
customCurve: EASING_PRESETS[preset],
|
|
4200
|
+
preset
|
|
3860
4201
|
});
|
|
3861
4202
|
},
|
|
3862
4203
|
[id, updateNodeData]
|
|
@@ -3877,7 +4218,16 @@ function AnimationNodeComponent(props) {
|
|
|
3877
4218
|
openNodeDetailModal(id, "preview");
|
|
3878
4219
|
}, [id, openNodeDetailModal]);
|
|
3879
4220
|
const headerActions = useMemo(
|
|
3880
|
-
() => nodeData.outputVideo ? /* @__PURE__ */ jsx(
|
|
4221
|
+
() => nodeData.outputVideo ? /* @__PURE__ */ jsx(
|
|
4222
|
+
Button,
|
|
4223
|
+
{
|
|
4224
|
+
variant: "ghost",
|
|
4225
|
+
size: "icon-sm",
|
|
4226
|
+
onClick: handleExpand,
|
|
4227
|
+
title: "Expand preview",
|
|
4228
|
+
children: /* @__PURE__ */ jsx(Expand, { className: "h-3 w-3" })
|
|
4229
|
+
}
|
|
4230
|
+
) : null,
|
|
3881
4231
|
[nodeData.outputVideo, handleExpand]
|
|
3882
4232
|
);
|
|
3883
4233
|
const curve = nodeData.customCurve ?? [0.645, 0.045, 0.355, 1];
|
|
@@ -3917,11 +4267,35 @@ function AnimationNodeComponent(props) {
|
|
|
3917
4267
|
}
|
|
3918
4268
|
),
|
|
3919
4269
|
nodeData.curveType === "preset" && /* @__PURE__ */ jsx("div", { className: "h-20 bg-[var(--background)] rounded border border-[var(--border)] p-2", children: /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 100 100", className: "w-full h-full", children: [
|
|
3920
|
-
/* @__PURE__ */ jsx(
|
|
4270
|
+
/* @__PURE__ */ jsx(
|
|
4271
|
+
"path",
|
|
4272
|
+
{
|
|
4273
|
+
d: pathD,
|
|
4274
|
+
fill: "none",
|
|
4275
|
+
stroke: "var(--primary)",
|
|
4276
|
+
strokeWidth: "2"
|
|
4277
|
+
}
|
|
4278
|
+
),
|
|
3921
4279
|
/* @__PURE__ */ jsx("circle", { cx: "0", cy: "100", r: "3", fill: "var(--foreground)" }),
|
|
3922
4280
|
/* @__PURE__ */ jsx("circle", { cx: "100", cy: "0", r: "3", fill: "var(--foreground)" }),
|
|
3923
|
-
/* @__PURE__ */ jsx(
|
|
3924
|
-
|
|
4281
|
+
/* @__PURE__ */ jsx(
|
|
4282
|
+
"circle",
|
|
4283
|
+
{
|
|
4284
|
+
cx: curve[0] * 100,
|
|
4285
|
+
cy: 100 - curve[1] * 100,
|
|
4286
|
+
r: "2",
|
|
4287
|
+
fill: "var(--accent)"
|
|
4288
|
+
}
|
|
4289
|
+
),
|
|
4290
|
+
/* @__PURE__ */ jsx(
|
|
4291
|
+
"circle",
|
|
4292
|
+
{
|
|
4293
|
+
cx: curve[2] * 100,
|
|
4294
|
+
cy: 100 - curve[3] * 100,
|
|
4295
|
+
r: "2",
|
|
4296
|
+
fill: "var(--accent)"
|
|
4297
|
+
}
|
|
4298
|
+
)
|
|
3925
4299
|
] }) }),
|
|
3926
4300
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
3927
4301
|
/* @__PURE__ */ jsxs("label", { className: "text-xs text-[var(--muted-foreground)]", children: [
|
|
@@ -3992,26 +4366,41 @@ function AnnotationNodeComponent(props) {
|
|
|
3992
4366
|
const nodeData = data;
|
|
3993
4367
|
const openAnnotation = useAnnotationStore((state) => state.openAnnotation);
|
|
3994
4368
|
const openNodeDetailModal = useUIStore((state) => state.openNodeDetailModal);
|
|
3995
|
-
const getConnectedInputs = useWorkflowStore(
|
|
4369
|
+
const getConnectedInputs = useWorkflowStore(
|
|
4370
|
+
(state) => state.getConnectedInputs
|
|
4371
|
+
);
|
|
3996
4372
|
const connectedInputs = getConnectedInputs(id);
|
|
3997
4373
|
const inputImage = connectedInputs.get("image") ?? nodeData.inputImage;
|
|
3998
4374
|
const handleEditAnnotations = useCallback(() => {
|
|
3999
4375
|
if (!inputImage) return;
|
|
4000
4376
|
const shapes = nodeData.annotations?.map((ann) => ({
|
|
4377
|
+
fillColor: ann.fillColor,
|
|
4001
4378
|
id: ann.id,
|
|
4002
|
-
type: ann.type,
|
|
4003
4379
|
strokeColor: ann.strokeColor,
|
|
4004
4380
|
strokeWidth: ann.strokeWidth,
|
|
4005
|
-
|
|
4381
|
+
type: ann.type,
|
|
4006
4382
|
...ann.props
|
|
4007
4383
|
})) ?? [];
|
|
4008
|
-
openAnnotation(
|
|
4384
|
+
openAnnotation(
|
|
4385
|
+
id,
|
|
4386
|
+
inputImage,
|
|
4387
|
+
shapes
|
|
4388
|
+
);
|
|
4009
4389
|
}, [id, inputImage, nodeData.annotations, openAnnotation]);
|
|
4010
4390
|
const handleExpand = useCallback(() => {
|
|
4011
4391
|
openNodeDetailModal(id, "preview");
|
|
4012
4392
|
}, [id, openNodeDetailModal]);
|
|
4013
4393
|
const headerActions = useMemo(
|
|
4014
|
-
() => inputImage ? /* @__PURE__ */ jsx(
|
|
4394
|
+
() => inputImage ? /* @__PURE__ */ jsx(
|
|
4395
|
+
Button,
|
|
4396
|
+
{
|
|
4397
|
+
variant: "ghost",
|
|
4398
|
+
size: "icon-sm",
|
|
4399
|
+
onClick: handleExpand,
|
|
4400
|
+
title: "Expand preview",
|
|
4401
|
+
children: /* @__PURE__ */ jsx(Expand, { className: "h-3 w-3" })
|
|
4402
|
+
}
|
|
4403
|
+
) : null,
|
|
4015
4404
|
[inputImage, handleExpand]
|
|
4016
4405
|
);
|
|
4017
4406
|
return /* @__PURE__ */ jsx(BaseNode, { ...props, headerActions, children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3", children: [
|
|
@@ -4208,7 +4597,7 @@ function ImageCompareNodeComponent(props) {
|
|
|
4208
4597
|
}
|
|
4209
4598
|
),
|
|
4210
4599
|
portrait: false,
|
|
4211
|
-
style: {
|
|
4600
|
+
style: { height: "100%", width: "100%" }
|
|
4212
4601
|
}
|
|
4213
4602
|
),
|
|
4214
4603
|
/* @__PURE__ */ jsx("div", { className: "absolute top-2 left-2 bg-black/50 text-white text-[10px] font-medium px-2 py-1 rounded pointer-events-none", children: "A" }),
|
|
@@ -4221,9 +4610,9 @@ function ImageCompareNodeComponent(props) {
|
|
|
4221
4610
|
}
|
|
4222
4611
|
var ImageCompareNode = memo(ImageCompareNodeComponent);
|
|
4223
4612
|
var OUTPUT_FORMATS = [
|
|
4224
|
-
{
|
|
4225
|
-
{
|
|
4226
|
-
{
|
|
4613
|
+
{ label: "JPEG", value: "jpg" },
|
|
4614
|
+
{ label: "PNG", value: "png" },
|
|
4615
|
+
{ label: "WebP", value: "webp" }
|
|
4227
4616
|
];
|
|
4228
4617
|
function ImageGridSplitNodeComponent(props) {
|
|
4229
4618
|
const { id, data } = props;
|
|
@@ -4233,14 +4622,20 @@ function ImageGridSplitNodeComponent(props) {
|
|
|
4233
4622
|
const [selectedPreview, setSelectedPreview] = useState(null);
|
|
4234
4623
|
const handleRowsChange = useCallback(
|
|
4235
4624
|
(e) => {
|
|
4236
|
-
const value = Math.min(
|
|
4625
|
+
const value = Math.min(
|
|
4626
|
+
10,
|
|
4627
|
+
Math.max(1, Number.parseInt(e.target.value, 10) || 1)
|
|
4628
|
+
);
|
|
4237
4629
|
updateNodeData(id, { gridRows: value });
|
|
4238
4630
|
},
|
|
4239
4631
|
[id, updateNodeData]
|
|
4240
4632
|
);
|
|
4241
4633
|
const handleColsChange = useCallback(
|
|
4242
4634
|
(e) => {
|
|
4243
|
-
const value = Math.min(
|
|
4635
|
+
const value = Math.min(
|
|
4636
|
+
10,
|
|
4637
|
+
Math.max(1, Number.parseInt(e.target.value, 10) || 1)
|
|
4638
|
+
);
|
|
4244
4639
|
updateNodeData(id, { gridCols: value });
|
|
4245
4640
|
},
|
|
4246
4641
|
[id, updateNodeData]
|
|
@@ -4331,15 +4726,16 @@ function ImageGridSplitNodeComponent(props) {
|
|
|
4331
4726
|
nodeData.gridRows,
|
|
4332
4727
|
"\xD7",
|
|
4333
4728
|
nodeData.gridCols,
|
|
4334
|
-
"
|
|
4729
|
+
" ",
|
|
4730
|
+
"grid)"
|
|
4335
4731
|
] }),
|
|
4336
4732
|
/* @__PURE__ */ jsx(
|
|
4337
4733
|
"div",
|
|
4338
4734
|
{
|
|
4339
4735
|
className: "mt-2 grid gap-0.5 mx-auto w-20 aspect-square bg-[var(--border)] rounded overflow-hidden",
|
|
4340
4736
|
style: {
|
|
4341
|
-
|
|
4342
|
-
|
|
4737
|
+
gridTemplateColumns: `repeat(${nodeData.gridCols}, 1fr)`,
|
|
4738
|
+
gridTemplateRows: `repeat(${nodeData.gridRows}, 1fr)`
|
|
4343
4739
|
},
|
|
4344
4740
|
children: Array.from({ length: totalCells }).map((_, i) => /* @__PURE__ */ jsx(
|
|
4345
4741
|
"div",
|
|
@@ -4372,10 +4768,17 @@ function ImageGridSplitNodeComponent(props) {
|
|
|
4372
4768
|
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-2", children: [
|
|
4373
4769
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
4374
4770
|
/* @__PURE__ */ jsx("label", { className: "text-xs text-[var(--muted-foreground)]", children: "Format" }),
|
|
4375
|
-
/* @__PURE__ */ jsxs(
|
|
4376
|
-
|
|
4377
|
-
|
|
4378
|
-
|
|
4771
|
+
/* @__PURE__ */ jsxs(
|
|
4772
|
+
Select,
|
|
4773
|
+
{
|
|
4774
|
+
value: nodeData.outputFormat,
|
|
4775
|
+
onValueChange: handleFormatChange,
|
|
4776
|
+
children: [
|
|
4777
|
+
/* @__PURE__ */ jsx(SelectTrigger, { className: "nodrag h-8 w-full", children: /* @__PURE__ */ jsx(SelectValue, {}) }),
|
|
4778
|
+
/* @__PURE__ */ jsx(SelectContent, { children: OUTPUT_FORMATS.map((f) => /* @__PURE__ */ jsx(SelectItem, { value: f.value, children: f.label }, f.value)) })
|
|
4779
|
+
]
|
|
4780
|
+
}
|
|
4781
|
+
)
|
|
4379
4782
|
] }),
|
|
4380
4783
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
4381
4784
|
/* @__PURE__ */ jsxs("label", { className: "text-xs text-[var(--muted-foreground)]", children: [
|
|
@@ -4402,10 +4805,19 @@ function ImageGridSplitNodeComponent(props) {
|
|
|
4402
4805
|
nodeData.outputImages.length,
|
|
4403
4806
|
" images)"
|
|
4404
4807
|
] }),
|
|
4405
|
-
/* @__PURE__ */ jsxs(
|
|
4406
|
-
|
|
4407
|
-
|
|
4408
|
-
|
|
4808
|
+
/* @__PURE__ */ jsxs(
|
|
4809
|
+
Button,
|
|
4810
|
+
{
|
|
4811
|
+
variant: "link",
|
|
4812
|
+
size: "sm",
|
|
4813
|
+
onClick: handleDownloadAll,
|
|
4814
|
+
className: "h-auto p-0",
|
|
4815
|
+
children: [
|
|
4816
|
+
/* @__PURE__ */ jsx(Download, { className: "w-3 h-3" }),
|
|
4817
|
+
"Download All"
|
|
4818
|
+
]
|
|
4819
|
+
}
|
|
4820
|
+
)
|
|
4409
4821
|
] }),
|
|
4410
4822
|
/* @__PURE__ */ jsx(
|
|
4411
4823
|
"div",
|
|
@@ -4420,7 +4832,14 @@ function ImageGridSplitNodeComponent(props) {
|
|
|
4420
4832
|
className: "relative group aspect-square rounded overflow-hidden border border-[var(--border)] cursor-pointer",
|
|
4421
4833
|
onClick: () => setSelectedPreview(selectedPreview === index ? null : index),
|
|
4422
4834
|
children: [
|
|
4423
|
-
/* @__PURE__ */ jsx(
|
|
4835
|
+
/* @__PURE__ */ jsx(
|
|
4836
|
+
"img",
|
|
4837
|
+
{
|
|
4838
|
+
src: img,
|
|
4839
|
+
alt: `Cell ${index + 1}`,
|
|
4840
|
+
className: "w-full h-full object-cover"
|
|
4841
|
+
}
|
|
4842
|
+
),
|
|
4424
4843
|
/* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-black/50 opacity-0 group-hover:opacity-100 transition flex items-center justify-center gap-1", children: /* @__PURE__ */ jsx(
|
|
4425
4844
|
Button,
|
|
4426
4845
|
{
|
|
@@ -4538,7 +4957,8 @@ function OutputGalleryNodeComponent(props) {
|
|
|
4538
4957
|
setLightboxIndex((prev) => {
|
|
4539
4958
|
if (prev === null) return null;
|
|
4540
4959
|
if (direction === "prev" && prev > 0) return prev - 1;
|
|
4541
|
-
if (direction === "next" && prev < imageCountRef.current - 1)
|
|
4960
|
+
if (direction === "next" && prev < imageCountRef.current - 1)
|
|
4961
|
+
return prev + 1;
|
|
4542
4962
|
return prev;
|
|
4543
4963
|
});
|
|
4544
4964
|
}, []);
|
|
@@ -4576,7 +4996,9 @@ function OutputGalleryNodeComponent(props) {
|
|
|
4576
4996
|
img.onerror = () => resolve(blob);
|
|
4577
4997
|
img.src = image;
|
|
4578
4998
|
});
|
|
4579
|
-
await navigator.clipboard.write([
|
|
4999
|
+
await navigator.clipboard.write([
|
|
5000
|
+
new ClipboardItem({ "image/png": pngBlob })
|
|
5001
|
+
]);
|
|
4580
5002
|
} catch {
|
|
4581
5003
|
}
|
|
4582
5004
|
}, [lightboxIndex]);
|
|
@@ -4604,7 +5026,14 @@ function OutputGalleryNodeComponent(props) {
|
|
|
4604
5026
|
{
|
|
4605
5027
|
onClick: () => openLightbox(idx),
|
|
4606
5028
|
className: "aspect-square rounded border border-border hover:border-primary overflow-hidden transition-colors",
|
|
4607
|
-
children: /* @__PURE__ */ jsx(
|
|
5029
|
+
children: /* @__PURE__ */ jsx(
|
|
5030
|
+
"img",
|
|
5031
|
+
{
|
|
5032
|
+
src: img,
|
|
5033
|
+
alt: `Image ${idx + 1}`,
|
|
5034
|
+
className: "w-full h-full object-cover"
|
|
5035
|
+
}
|
|
5036
|
+
)
|
|
4608
5037
|
},
|
|
4609
5038
|
img
|
|
4610
5039
|
)) }) }) }),
|
|
@@ -4615,69 +5044,76 @@ function OutputGalleryNodeComponent(props) {
|
|
|
4615
5044
|
className: "fixed inset-0 bg-black/90 z-[100] flex items-center justify-center p-8",
|
|
4616
5045
|
onClick: closeLightbox,
|
|
4617
5046
|
onContextMenu: (e) => e.stopPropagation(),
|
|
4618
|
-
children: /* @__PURE__ */ jsxs(
|
|
4619
|
-
|
|
4620
|
-
|
|
4621
|
-
|
|
4622
|
-
|
|
4623
|
-
|
|
4624
|
-
|
|
4625
|
-
|
|
4626
|
-
|
|
4627
|
-
|
|
4628
|
-
|
|
4629
|
-
|
|
4630
|
-
|
|
4631
|
-
|
|
4632
|
-
|
|
4633
|
-
|
|
4634
|
-
|
|
4635
|
-
|
|
4636
|
-
|
|
4637
|
-
|
|
4638
|
-
|
|
4639
|
-
|
|
4640
|
-
|
|
4641
|
-
|
|
4642
|
-
|
|
4643
|
-
|
|
4644
|
-
|
|
4645
|
-
|
|
4646
|
-
|
|
4647
|
-
|
|
4648
|
-
|
|
4649
|
-
|
|
4650
|
-
|
|
4651
|
-
|
|
4652
|
-
|
|
4653
|
-
|
|
4654
|
-
|
|
4655
|
-
|
|
4656
|
-
|
|
4657
|
-
|
|
4658
|
-
|
|
4659
|
-
|
|
4660
|
-
|
|
4661
|
-
|
|
4662
|
-
|
|
4663
|
-
|
|
4664
|
-
|
|
4665
|
-
|
|
4666
|
-
|
|
4667
|
-
|
|
4668
|
-
|
|
4669
|
-
|
|
4670
|
-
|
|
4671
|
-
|
|
4672
|
-
|
|
4673
|
-
|
|
4674
|
-
|
|
4675
|
-
|
|
4676
|
-
|
|
4677
|
-
|
|
4678
|
-
|
|
4679
|
-
|
|
4680
|
-
|
|
5047
|
+
children: /* @__PURE__ */ jsxs(
|
|
5048
|
+
"div",
|
|
5049
|
+
{
|
|
5050
|
+
className: "relative max-w-full max-h-full",
|
|
5051
|
+
onClick: (e) => e.stopPropagation(),
|
|
5052
|
+
children: [
|
|
5053
|
+
/* @__PURE__ */ jsx(
|
|
5054
|
+
"img",
|
|
5055
|
+
{
|
|
5056
|
+
src: displayImages[lightboxIndex],
|
|
5057
|
+
alt: `Gallery image ${lightboxIndex + 1}`,
|
|
5058
|
+
className: "max-w-full max-h-[90vh] object-contain rounded"
|
|
5059
|
+
}
|
|
5060
|
+
),
|
|
5061
|
+
/* @__PURE__ */ jsx(
|
|
5062
|
+
"button",
|
|
5063
|
+
{
|
|
5064
|
+
onClick: closeLightbox,
|
|
5065
|
+
className: "absolute top-4 right-4 w-8 h-8 bg-white/10 hover:bg-white/20 rounded text-white text-sm transition-colors flex items-center justify-center",
|
|
5066
|
+
children: /* @__PURE__ */ jsx(X, { className: "w-4 h-4" })
|
|
5067
|
+
}
|
|
5068
|
+
),
|
|
5069
|
+
/* @__PURE__ */ jsxs("div", { className: "absolute top-4 left-4 flex items-center gap-2", children: [
|
|
5070
|
+
/* @__PURE__ */ jsxs(
|
|
5071
|
+
"button",
|
|
5072
|
+
{
|
|
5073
|
+
onClick: downloadImage,
|
|
5074
|
+
className: "px-3 py-1.5 bg-white/10 hover:bg-white/20 rounded text-white text-xs font-medium transition-colors flex items-center gap-1.5",
|
|
5075
|
+
children: [
|
|
5076
|
+
/* @__PURE__ */ jsx(Download, { className: "w-3.5 h-3.5" }),
|
|
5077
|
+
"Download"
|
|
5078
|
+
]
|
|
5079
|
+
}
|
|
5080
|
+
),
|
|
5081
|
+
/* @__PURE__ */ jsxs(
|
|
5082
|
+
"button",
|
|
5083
|
+
{
|
|
5084
|
+
onClick: copyImage,
|
|
5085
|
+
className: "px-3 py-1.5 bg-white/10 hover:bg-white/20 rounded text-white text-xs font-medium transition-colors flex items-center gap-1.5",
|
|
5086
|
+
children: [
|
|
5087
|
+
/* @__PURE__ */ jsx(Copy, { className: "w-3.5 h-3.5" }),
|
|
5088
|
+
"Copy"
|
|
5089
|
+
]
|
|
5090
|
+
}
|
|
5091
|
+
)
|
|
5092
|
+
] }),
|
|
5093
|
+
lightboxIndex > 0 && /* @__PURE__ */ jsx(
|
|
5094
|
+
"button",
|
|
5095
|
+
{
|
|
5096
|
+
onClick: () => navigateLightbox("prev"),
|
|
5097
|
+
className: "absolute left-4 top-1/2 -translate-y-1/2 w-10 h-10 bg-white/10 hover:bg-white/20 rounded-full text-white transition-colors flex items-center justify-center",
|
|
5098
|
+
children: /* @__PURE__ */ jsx(ChevronLeft, { className: "w-5 h-5" })
|
|
5099
|
+
}
|
|
5100
|
+
),
|
|
5101
|
+
lightboxIndex < displayImages.length - 1 && /* @__PURE__ */ jsx(
|
|
5102
|
+
"button",
|
|
5103
|
+
{
|
|
5104
|
+
onClick: () => navigateLightbox("next"),
|
|
5105
|
+
className: "absolute right-4 top-1/2 -translate-y-1/2 w-10 h-10 bg-white/10 hover:bg-white/20 rounded-full text-white transition-colors flex items-center justify-center",
|
|
5106
|
+
children: /* @__PURE__ */ jsx(ChevronRight, { className: "w-5 h-5" })
|
|
5107
|
+
}
|
|
5108
|
+
),
|
|
5109
|
+
/* @__PURE__ */ jsxs("div", { className: "absolute bottom-4 left-1/2 -translate-x-1/2 px-3 py-1.5 bg-black/50 rounded text-white text-xs font-medium", children: [
|
|
5110
|
+
lightboxIndex + 1,
|
|
5111
|
+
" / ",
|
|
5112
|
+
displayImages.length
|
|
5113
|
+
] })
|
|
5114
|
+
]
|
|
5115
|
+
}
|
|
5116
|
+
)
|
|
4681
5117
|
}
|
|
4682
5118
|
),
|
|
4683
5119
|
document.body
|
|
@@ -4686,17 +5122,17 @@ function OutputGalleryNodeComponent(props) {
|
|
|
4686
5122
|
}
|
|
4687
5123
|
var OutputGalleryNode = memo(OutputGalleryNodeComponent);
|
|
4688
5124
|
var MODELS = [
|
|
4689
|
-
{
|
|
4690
|
-
{
|
|
5125
|
+
{ label: "Photon Flash", price: "$0.01", value: "photon-flash-1" },
|
|
5126
|
+
{ label: "Photon", price: "$0.03", value: "photon-1" }
|
|
4691
5127
|
];
|
|
4692
5128
|
var ASPECT_RATIOS2 = [
|
|
4693
|
-
{
|
|
4694
|
-
{
|
|
4695
|
-
{
|
|
4696
|
-
{
|
|
4697
|
-
{
|
|
4698
|
-
{
|
|
4699
|
-
{
|
|
5129
|
+
{ label: "9:16 (TikTok/Reels)", value: "9:16" },
|
|
5130
|
+
{ label: "16:9 (YouTube)", value: "16:9" },
|
|
5131
|
+
{ label: "1:1 (Square)", value: "1:1" },
|
|
5132
|
+
{ label: "4:3", value: "4:3" },
|
|
5133
|
+
{ label: "3:4", value: "3:4" },
|
|
5134
|
+
{ label: "9:21", value: "9:21" },
|
|
5135
|
+
{ label: "21:9 (Ultrawide)", value: "21:9" }
|
|
4700
5136
|
];
|
|
4701
5137
|
function ReframeNodeComponent(props) {
|
|
4702
5138
|
const { id, data } = props;
|
|
@@ -4768,10 +5204,17 @@ function ReframeNodeComponent(props) {
|
|
|
4768
5204
|
] }),
|
|
4769
5205
|
/* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
|
|
4770
5206
|
/* @__PURE__ */ jsx(Label, { className: "text-xs", children: "Target Aspect Ratio" }),
|
|
4771
|
-
/* @__PURE__ */ jsxs(
|
|
4772
|
-
|
|
4773
|
-
|
|
4774
|
-
|
|
5207
|
+
/* @__PURE__ */ jsxs(
|
|
5208
|
+
Select,
|
|
5209
|
+
{
|
|
5210
|
+
value: nodeData.aspectRatio,
|
|
5211
|
+
onValueChange: handleAspectRatioChange,
|
|
5212
|
+
children: [
|
|
5213
|
+
/* @__PURE__ */ jsx(SelectTrigger, { className: "nodrag h-9 w-full", children: /* @__PURE__ */ jsx(SelectValue, {}) }),
|
|
5214
|
+
/* @__PURE__ */ jsx(SelectContent, { children: ASPECT_RATIOS2.map((ratio) => /* @__PURE__ */ jsx(SelectItem, { value: ratio.value, children: ratio.label }, ratio.value)) })
|
|
5215
|
+
]
|
|
5216
|
+
}
|
|
5217
|
+
)
|
|
4775
5218
|
] }),
|
|
4776
5219
|
/* @__PURE__ */ jsx(
|
|
4777
5220
|
GridPositionSelector,
|
|
@@ -4921,10 +5364,17 @@ function ResizeNodeComponent(props) {
|
|
|
4921
5364
|
] }),
|
|
4922
5365
|
/* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
|
|
4923
5366
|
/* @__PURE__ */ jsx(Label, { className: "text-xs", children: "Target Aspect Ratio" }),
|
|
4924
|
-
/* @__PURE__ */ jsxs(
|
|
4925
|
-
|
|
4926
|
-
|
|
4927
|
-
|
|
5367
|
+
/* @__PURE__ */ jsxs(
|
|
5368
|
+
Select,
|
|
5369
|
+
{
|
|
5370
|
+
value: nodeData.targetAspectRatio,
|
|
5371
|
+
onValueChange: handleAspectRatioChange,
|
|
5372
|
+
children: [
|
|
5373
|
+
/* @__PURE__ */ jsx(SelectTrigger, { className: "nodrag h-9 w-full", children: /* @__PURE__ */ jsx(SelectValue, {}) }),
|
|
5374
|
+
/* @__PURE__ */ jsx(SelectContent, { children: LUMA_ASPECT_RATIOS.map((ratio) => /* @__PURE__ */ jsx(SelectItem, { value: ratio, children: ratio }, ratio)) })
|
|
5375
|
+
]
|
|
5376
|
+
}
|
|
5377
|
+
)
|
|
4928
5378
|
] }),
|
|
4929
5379
|
/* @__PURE__ */ jsx(
|
|
4930
5380
|
GridPositionSelector,
|
|
@@ -4995,15 +5445,15 @@ function ResizeNodeComponent(props) {
|
|
|
4995
5445
|
}
|
|
4996
5446
|
var ResizeNode = memo(ResizeNodeComponent);
|
|
4997
5447
|
var STYLE_OPTIONS = [
|
|
4998
|
-
{
|
|
4999
|
-
{
|
|
5000
|
-
{
|
|
5001
|
-
{
|
|
5448
|
+
{ label: "Modern", value: "modern" },
|
|
5449
|
+
{ label: "Default", value: "default" },
|
|
5450
|
+
{ label: "Minimal", value: "minimal" },
|
|
5451
|
+
{ label: "Bold", value: "bold" }
|
|
5002
5452
|
];
|
|
5003
5453
|
var POSITION_OPTIONS = [
|
|
5004
|
-
{
|
|
5005
|
-
{
|
|
5006
|
-
{
|
|
5454
|
+
{ label: "Bottom", value: "bottom" },
|
|
5455
|
+
{ label: "Center", value: "center" },
|
|
5456
|
+
{ label: "Top", value: "top" }
|
|
5007
5457
|
];
|
|
5008
5458
|
function SubtitleNodeComponent(props) {
|
|
5009
5459
|
const { id, data } = props;
|
|
@@ -5018,7 +5468,9 @@ function SubtitleNodeComponent(props) {
|
|
|
5018
5468
|
);
|
|
5019
5469
|
const handlePositionChange = useCallback(
|
|
5020
5470
|
(value) => {
|
|
5021
|
-
updateNodeData(id, {
|
|
5471
|
+
updateNodeData(id, {
|
|
5472
|
+
position: value
|
|
5473
|
+
});
|
|
5022
5474
|
},
|
|
5023
5475
|
[id, updateNodeData]
|
|
5024
5476
|
);
|
|
@@ -5050,10 +5502,17 @@ function SubtitleNodeComponent(props) {
|
|
|
5050
5502
|
] }),
|
|
5051
5503
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
5052
5504
|
/* @__PURE__ */ jsx("label", { className: "text-xs text-[var(--muted-foreground)] block mb-1", children: "Position" }),
|
|
5053
|
-
/* @__PURE__ */ jsxs(
|
|
5054
|
-
|
|
5055
|
-
|
|
5056
|
-
|
|
5505
|
+
/* @__PURE__ */ jsxs(
|
|
5506
|
+
Select,
|
|
5507
|
+
{
|
|
5508
|
+
value: nodeData.position,
|
|
5509
|
+
onValueChange: handlePositionChange,
|
|
5510
|
+
children: [
|
|
5511
|
+
/* @__PURE__ */ jsx(SelectTrigger, { className: "nodrag h-8 w-full", children: /* @__PURE__ */ jsx(SelectValue, {}) }),
|
|
5512
|
+
/* @__PURE__ */ jsx(SelectContent, { children: POSITION_OPTIONS.map((opt) => /* @__PURE__ */ jsx(SelectItem, { value: opt.value, children: opt.label }, opt.value)) })
|
|
5513
|
+
]
|
|
5514
|
+
}
|
|
5515
|
+
)
|
|
5057
5516
|
] }),
|
|
5058
5517
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
5059
5518
|
/* @__PURE__ */ jsxs("label", { className: "text-xs text-[var(--muted-foreground)] block mb-1", children: [
|
|
@@ -5086,7 +5545,14 @@ function SubtitleNodeComponent(props) {
|
|
|
5086
5545
|
/* @__PURE__ */ jsx("span", { className: "text-xs text-[var(--muted-foreground)]", children: nodeData.fontColor })
|
|
5087
5546
|
] }),
|
|
5088
5547
|
nodeData.outputVideo && /* @__PURE__ */ jsxs("div", { className: "relative", children: [
|
|
5089
|
-
/* @__PURE__ */ jsx(
|
|
5548
|
+
/* @__PURE__ */ jsx(
|
|
5549
|
+
"video",
|
|
5550
|
+
{
|
|
5551
|
+
src: nodeData.outputVideo,
|
|
5552
|
+
className: "w-full h-20 object-cover rounded",
|
|
5553
|
+
muted: true
|
|
5554
|
+
}
|
|
5555
|
+
),
|
|
5090
5556
|
/* @__PURE__ */ jsx(
|
|
5091
5557
|
Button,
|
|
5092
5558
|
{
|
|
@@ -5117,31 +5583,31 @@ function SubtitleNodeComponent(props) {
|
|
|
5117
5583
|
}
|
|
5118
5584
|
var SubtitleNode = memo(SubtitleNodeComponent);
|
|
5119
5585
|
var IMAGE_MODELS2 = [
|
|
5120
|
-
{ value: "topaz-standard-v2"
|
|
5121
|
-
{ value: "topaz-low-res-v2"
|
|
5122
|
-
{
|
|
5123
|
-
{ value: "topaz-high-fidelity-v2"
|
|
5124
|
-
{ value: "topaz-text-refine"
|
|
5586
|
+
{ label: "Standard V2", value: "topaz-standard-v2" },
|
|
5587
|
+
{ label: "Low Resolution V2", value: "topaz-low-res-v2" },
|
|
5588
|
+
{ label: "CGI", value: "topaz-cgi" },
|
|
5589
|
+
{ label: "High Fidelity V2", value: "topaz-high-fidelity-v2" },
|
|
5590
|
+
{ label: "Text Refine", value: "topaz-text-refine" }
|
|
5125
5591
|
];
|
|
5126
5592
|
var VIDEO_MODELS2 = [
|
|
5127
|
-
{
|
|
5593
|
+
{ label: "Topaz Video Upscale", value: "topaz-video" }
|
|
5128
5594
|
];
|
|
5129
5595
|
var UPSCALE_FACTORS = [
|
|
5130
|
-
{
|
|
5131
|
-
{
|
|
5132
|
-
{
|
|
5133
|
-
{
|
|
5596
|
+
{ label: "None (enhance only)", value: "None" },
|
|
5597
|
+
{ label: "2x", value: "2x" },
|
|
5598
|
+
{ label: "4x", value: "4x" },
|
|
5599
|
+
{ label: "6x", value: "6x" }
|
|
5134
5600
|
];
|
|
5135
5601
|
var RESOLUTIONS = [
|
|
5136
|
-
{
|
|
5137
|
-
{
|
|
5138
|
-
{
|
|
5602
|
+
{ label: "720p (HD)", value: "720p" },
|
|
5603
|
+
{ label: "1080p (Full HD)", value: "1080p" },
|
|
5604
|
+
{ label: "4K (Ultra HD)", value: "4k" }
|
|
5139
5605
|
];
|
|
5140
5606
|
var FPS_OPTIONS = [
|
|
5141
|
-
{
|
|
5142
|
-
{
|
|
5143
|
-
{
|
|
5144
|
-
{
|
|
5607
|
+
{ label: "15 fps", value: 15 },
|
|
5608
|
+
{ label: "24 fps (Film)", value: 24 },
|
|
5609
|
+
{ label: "30 fps", value: 30 },
|
|
5610
|
+
{ label: "60 fps (Smooth)", value: 60 }
|
|
5145
5611
|
];
|
|
5146
5612
|
function UpscaleNodeComponent(props) {
|
|
5147
5613
|
const { id, data } = props;
|
|
@@ -5162,19 +5628,25 @@ function UpscaleNodeComponent(props) {
|
|
|
5162
5628
|
);
|
|
5163
5629
|
const handleFactorChange = useCallback(
|
|
5164
5630
|
(value) => {
|
|
5165
|
-
updateNodeData(id, {
|
|
5631
|
+
updateNodeData(id, {
|
|
5632
|
+
upscaleFactor: value
|
|
5633
|
+
});
|
|
5166
5634
|
},
|
|
5167
5635
|
[id, updateNodeData]
|
|
5168
5636
|
);
|
|
5169
5637
|
const handleFormatChange = useCallback(
|
|
5170
5638
|
(value) => {
|
|
5171
|
-
updateNodeData(id, {
|
|
5639
|
+
updateNodeData(id, {
|
|
5640
|
+
outputFormat: value
|
|
5641
|
+
});
|
|
5172
5642
|
},
|
|
5173
5643
|
[id, updateNodeData]
|
|
5174
5644
|
);
|
|
5175
5645
|
const handleResolutionChange = useCallback(
|
|
5176
5646
|
(value) => {
|
|
5177
|
-
updateNodeData(id, {
|
|
5647
|
+
updateNodeData(id, {
|
|
5648
|
+
targetResolution: value
|
|
5649
|
+
});
|
|
5178
5650
|
},
|
|
5179
5651
|
[id, updateNodeData]
|
|
5180
5652
|
);
|
|
@@ -5241,6 +5713,10 @@ function UpscaleNodeComponent(props) {
|
|
|
5241
5713
|
}, [isPlaying]);
|
|
5242
5714
|
const getPriceEstimate = useCallback(() => {
|
|
5243
5715
|
const priceMap = {
|
|
5716
|
+
"4k-15": 0.187,
|
|
5717
|
+
"4k-24": 0.299,
|
|
5718
|
+
"4k-30": 0.373,
|
|
5719
|
+
"4k-60": 0.747,
|
|
5244
5720
|
"720p-15": 0.014,
|
|
5245
5721
|
"720p-24": 0.022,
|
|
5246
5722
|
"720p-30": 0.027,
|
|
@@ -5248,11 +5724,7 @@ function UpscaleNodeComponent(props) {
|
|
|
5248
5724
|
"1080p-15": 0.051,
|
|
5249
5725
|
"1080p-24": 0.081,
|
|
5250
5726
|
"1080p-30": 0.101,
|
|
5251
|
-
"1080p-60": 0.203
|
|
5252
|
-
"4k-15": 0.187,
|
|
5253
|
-
"4k-24": 0.299,
|
|
5254
|
-
"4k-30": 0.373,
|
|
5255
|
-
"4k-60": 0.747
|
|
5727
|
+
"1080p-60": 0.203
|
|
5256
5728
|
};
|
|
5257
5729
|
const key = `${nodeData.targetResolution}-${nodeData.targetFps}`;
|
|
5258
5730
|
const perFiveSeconds = priceMap[key] ?? 0.101;
|
|
@@ -5260,7 +5732,16 @@ function UpscaleNodeComponent(props) {
|
|
|
5260
5732
|
}, [nodeData.targetResolution, nodeData.targetFps]);
|
|
5261
5733
|
const models = inputType === "video" ? VIDEO_MODELS2 : IMAGE_MODELS2;
|
|
5262
5734
|
const headerActions = useMemo(
|
|
5263
|
-
() => hasOutput ? /* @__PURE__ */ jsx(
|
|
5735
|
+
() => hasOutput ? /* @__PURE__ */ jsx(
|
|
5736
|
+
Button,
|
|
5737
|
+
{
|
|
5738
|
+
variant: "ghost",
|
|
5739
|
+
size: "icon-sm",
|
|
5740
|
+
onClick: handleExpand,
|
|
5741
|
+
title: "Expand preview",
|
|
5742
|
+
children: /* @__PURE__ */ jsx(Expand, { className: "h-3 w-3" })
|
|
5743
|
+
}
|
|
5744
|
+
) : null,
|
|
5264
5745
|
[hasOutput, handleExpand]
|
|
5265
5746
|
);
|
|
5266
5747
|
return /* @__PURE__ */ jsx(BaseNode, { ...props, headerActions, children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3", children: [
|
|
@@ -5279,20 +5760,34 @@ function UpscaleNodeComponent(props) {
|
|
|
5279
5760
|
inputType === "image" && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
5280
5761
|
/* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
|
|
5281
5762
|
/* @__PURE__ */ jsx(Label, { className: "text-xs", children: "Upscale Factor" }),
|
|
5282
|
-
/* @__PURE__ */ jsxs(
|
|
5283
|
-
|
|
5284
|
-
|
|
5285
|
-
|
|
5763
|
+
/* @__PURE__ */ jsxs(
|
|
5764
|
+
Select,
|
|
5765
|
+
{
|
|
5766
|
+
value: nodeData.upscaleFactor,
|
|
5767
|
+
onValueChange: handleFactorChange,
|
|
5768
|
+
children: [
|
|
5769
|
+
/* @__PURE__ */ jsx(SelectTrigger, { className: "nodrag h-9 w-full", children: /* @__PURE__ */ jsx(SelectValue, {}) }),
|
|
5770
|
+
/* @__PURE__ */ jsx(SelectContent, { children: UPSCALE_FACTORS.map((factor) => /* @__PURE__ */ jsx(SelectItem, { value: factor.value, children: factor.label }, factor.value)) })
|
|
5771
|
+
]
|
|
5772
|
+
}
|
|
5773
|
+
)
|
|
5286
5774
|
] }),
|
|
5287
5775
|
/* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
|
|
5288
5776
|
/* @__PURE__ */ jsx(Label, { className: "text-xs", children: "Output Format" }),
|
|
5289
|
-
/* @__PURE__ */ jsxs(
|
|
5290
|
-
|
|
5291
|
-
|
|
5292
|
-
|
|
5293
|
-
|
|
5294
|
-
|
|
5295
|
-
|
|
5777
|
+
/* @__PURE__ */ jsxs(
|
|
5778
|
+
Select,
|
|
5779
|
+
{
|
|
5780
|
+
value: nodeData.outputFormat,
|
|
5781
|
+
onValueChange: handleFormatChange,
|
|
5782
|
+
children: [
|
|
5783
|
+
/* @__PURE__ */ jsx(SelectTrigger, { className: "nodrag h-9 w-full", children: /* @__PURE__ */ jsx(SelectValue, {}) }),
|
|
5784
|
+
/* @__PURE__ */ jsxs(SelectContent, { children: [
|
|
5785
|
+
/* @__PURE__ */ jsx(SelectItem, { value: "png", children: "PNG" }),
|
|
5786
|
+
/* @__PURE__ */ jsx(SelectItem, { value: "jpg", children: "JPG" })
|
|
5787
|
+
] })
|
|
5788
|
+
]
|
|
5789
|
+
}
|
|
5790
|
+
)
|
|
5296
5791
|
] }),
|
|
5297
5792
|
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
5298
5793
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 nodrag", children: [
|
|
@@ -5304,7 +5799,14 @@ function UpscaleNodeComponent(props) {
|
|
|
5304
5799
|
onCheckedChange: handleFaceEnhancementToggle
|
|
5305
5800
|
}
|
|
5306
5801
|
),
|
|
5307
|
-
/* @__PURE__ */ jsx(
|
|
5802
|
+
/* @__PURE__ */ jsx(
|
|
5803
|
+
Label,
|
|
5804
|
+
{
|
|
5805
|
+
htmlFor: `${id}-face-enhance`,
|
|
5806
|
+
className: "text-xs cursor-pointer",
|
|
5807
|
+
children: "Face Enhancement"
|
|
5808
|
+
}
|
|
5809
|
+
)
|
|
5308
5810
|
] }),
|
|
5309
5811
|
nodeData.faceEnhancement && /* @__PURE__ */ jsxs("div", { className: "space-y-2 pl-1", children: [
|
|
5310
5812
|
/* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
@@ -5351,17 +5853,31 @@ function UpscaleNodeComponent(props) {
|
|
|
5351
5853
|
inputType === "video" && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
5352
5854
|
/* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
|
|
5353
5855
|
/* @__PURE__ */ jsx(Label, { className: "text-xs", children: "Target Resolution" }),
|
|
5354
|
-
/* @__PURE__ */ jsxs(
|
|
5355
|
-
|
|
5356
|
-
|
|
5357
|
-
|
|
5856
|
+
/* @__PURE__ */ jsxs(
|
|
5857
|
+
Select,
|
|
5858
|
+
{
|
|
5859
|
+
value: nodeData.targetResolution,
|
|
5860
|
+
onValueChange: handleResolutionChange,
|
|
5861
|
+
children: [
|
|
5862
|
+
/* @__PURE__ */ jsx(SelectTrigger, { className: "nodrag h-9 w-full", children: /* @__PURE__ */ jsx(SelectValue, {}) }),
|
|
5863
|
+
/* @__PURE__ */ jsx(SelectContent, { children: RESOLUTIONS.map((res) => /* @__PURE__ */ jsx(SelectItem, { value: res.value, children: res.label }, res.value)) })
|
|
5864
|
+
]
|
|
5865
|
+
}
|
|
5866
|
+
)
|
|
5358
5867
|
] }),
|
|
5359
5868
|
/* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
|
|
5360
5869
|
/* @__PURE__ */ jsx(Label, { className: "text-xs", children: "Target Frame Rate" }),
|
|
5361
|
-
/* @__PURE__ */ jsxs(
|
|
5362
|
-
|
|
5363
|
-
|
|
5364
|
-
|
|
5870
|
+
/* @__PURE__ */ jsxs(
|
|
5871
|
+
Select,
|
|
5872
|
+
{
|
|
5873
|
+
value: String(nodeData.targetFps),
|
|
5874
|
+
onValueChange: handleFpsChange,
|
|
5875
|
+
children: [
|
|
5876
|
+
/* @__PURE__ */ jsx(SelectTrigger, { className: "nodrag h-9 w-full", children: /* @__PURE__ */ jsx(SelectValue, {}) }),
|
|
5877
|
+
/* @__PURE__ */ jsx(SelectContent, { children: FPS_OPTIONS.map((fps) => /* @__PURE__ */ jsx(SelectItem, { value: String(fps.value), children: fps.label }, fps.value)) })
|
|
5878
|
+
]
|
|
5879
|
+
}
|
|
5880
|
+
)
|
|
5365
5881
|
] }),
|
|
5366
5882
|
/* @__PURE__ */ jsxs("div", { className: "text-[10px] text-muted-foreground bg-secondary/50 rounded px-2 py-1", children: [
|
|
5367
5883
|
"Estimated cost: ",
|
|
@@ -5487,8 +6003,8 @@ function UpscaleNodeComponent(props) {
|
|
|
5487
6003
|
}
|
|
5488
6004
|
var UpscaleNode = memo(UpscaleNodeComponent);
|
|
5489
6005
|
var SELECTION_MODES = [
|
|
5490
|
-
{
|
|
5491
|
-
{
|
|
6006
|
+
{ label: "Last Frame", value: "last" },
|
|
6007
|
+
{ label: "First Frame", value: "first" }
|
|
5492
6008
|
];
|
|
5493
6009
|
function formatTime(seconds) {
|
|
5494
6010
|
const mins = Math.floor(seconds / 60);
|
|
@@ -5502,7 +6018,9 @@ function VideoFrameExtractNodeComponent(props) {
|
|
|
5502
6018
|
const executeNode = useExecutionStore((state) => state.executeNode);
|
|
5503
6019
|
const handleModeChange = useCallback(
|
|
5504
6020
|
(value) => {
|
|
5505
|
-
updateNodeData(id, {
|
|
6021
|
+
updateNodeData(id, {
|
|
6022
|
+
selectionMode: value
|
|
6023
|
+
});
|
|
5506
6024
|
},
|
|
5507
6025
|
[id, updateNodeData]
|
|
5508
6026
|
);
|
|
@@ -5514,10 +6032,17 @@ function VideoFrameExtractNodeComponent(props) {
|
|
|
5514
6032
|
/* @__PURE__ */ jsx("div", { className: "text-xs text-[var(--muted-foreground)]", children: nodeData.videoDuration ? `Source: ${formatTime(nodeData.videoDuration)}` : "Connect video to extract frame" }),
|
|
5515
6033
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
5516
6034
|
/* @__PURE__ */ jsx("label", { className: "text-xs text-[var(--muted-foreground)] block mb-1", children: "Frame Selection" }),
|
|
5517
|
-
/* @__PURE__ */ jsxs(
|
|
5518
|
-
|
|
5519
|
-
|
|
5520
|
-
|
|
6035
|
+
/* @__PURE__ */ jsxs(
|
|
6036
|
+
Select,
|
|
6037
|
+
{
|
|
6038
|
+
value: nodeData.selectionMode,
|
|
6039
|
+
onValueChange: handleModeChange,
|
|
6040
|
+
children: [
|
|
6041
|
+
/* @__PURE__ */ jsx(SelectTrigger, { className: "nodrag h-8 w-full", children: /* @__PURE__ */ jsx(SelectValue, {}) }),
|
|
6042
|
+
/* @__PURE__ */ jsx(SelectContent, { children: SELECTION_MODES.map((mode) => /* @__PURE__ */ jsx(SelectItem, { value: mode.value, children: mode.label }, mode.value)) })
|
|
6043
|
+
]
|
|
6044
|
+
}
|
|
6045
|
+
)
|
|
5521
6046
|
] }),
|
|
5522
6047
|
nodeData.outputImage && /* @__PURE__ */ jsxs("div", { className: "relative", children: [
|
|
5523
6048
|
/* @__PURE__ */ jsx(
|
|
@@ -5560,14 +6085,14 @@ function VideoFrameExtractNodeComponent(props) {
|
|
|
5560
6085
|
}
|
|
5561
6086
|
var VideoFrameExtractNode = memo(VideoFrameExtractNodeComponent);
|
|
5562
6087
|
var TRANSITIONS = [
|
|
5563
|
-
{
|
|
5564
|
-
{
|
|
5565
|
-
{
|
|
5566
|
-
{
|
|
6088
|
+
{ label: "Cut (No transition)", value: "cut" },
|
|
6089
|
+
{ label: "Crossfade", value: "crossfade" },
|
|
6090
|
+
{ label: "Fade to Black", value: "fade" },
|
|
6091
|
+
{ label: "Wipe", value: "wipe" }
|
|
5567
6092
|
];
|
|
5568
6093
|
var AUDIO_CODECS = [
|
|
5569
|
-
{
|
|
5570
|
-
{
|
|
6094
|
+
{ hint: "Best compatibility", label: "AAC", value: "aac" },
|
|
6095
|
+
{ hint: "Legacy fallback", label: "MP3", value: "mp3" }
|
|
5571
6096
|
];
|
|
5572
6097
|
function VideoStitchNodeComponent(props) {
|
|
5573
6098
|
const { id, data } = props;
|
|
@@ -5640,10 +6165,17 @@ function VideoStitchNodeComponent(props) {
|
|
|
5640
6165
|
] }),
|
|
5641
6166
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
5642
6167
|
/* @__PURE__ */ jsx("label", { className: "text-xs text-[var(--muted-foreground)]", children: "Transition" }),
|
|
5643
|
-
/* @__PURE__ */ jsxs(
|
|
5644
|
-
|
|
5645
|
-
|
|
5646
|
-
|
|
6168
|
+
/* @__PURE__ */ jsxs(
|
|
6169
|
+
Select,
|
|
6170
|
+
{
|
|
6171
|
+
value: nodeData.transitionType,
|
|
6172
|
+
onValueChange: handleTransitionChange,
|
|
6173
|
+
children: [
|
|
6174
|
+
/* @__PURE__ */ jsx(SelectTrigger, { className: "nodrag h-8 w-full", children: /* @__PURE__ */ jsx(SelectValue, {}) }),
|
|
6175
|
+
/* @__PURE__ */ jsx(SelectContent, { children: TRANSITIONS.map((t) => /* @__PURE__ */ jsx(SelectItem, { value: t.value, children: t.label }, t.value)) })
|
|
6176
|
+
]
|
|
6177
|
+
}
|
|
6178
|
+
)
|
|
5647
6179
|
] }),
|
|
5648
6180
|
nodeData.transitionType !== "cut" && /* @__PURE__ */ jsxs("div", { children: [
|
|
5649
6181
|
/* @__PURE__ */ jsxs("label", { className: "text-xs text-[var(--muted-foreground)]", children: [
|
|
@@ -5683,14 +6215,21 @@ function VideoStitchNodeComponent(props) {
|
|
|
5683
6215
|
] }),
|
|
5684
6216
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
5685
6217
|
/* @__PURE__ */ jsx("label", { className: "text-xs text-[var(--muted-foreground)]", children: "Audio Codec" }),
|
|
5686
|
-
/* @__PURE__ */ jsxs(
|
|
5687
|
-
|
|
5688
|
-
|
|
5689
|
-
|
|
5690
|
-
|
|
5691
|
-
|
|
5692
|
-
|
|
5693
|
-
|
|
6218
|
+
/* @__PURE__ */ jsxs(
|
|
6219
|
+
Select,
|
|
6220
|
+
{
|
|
6221
|
+
value: nodeData.audioCodec,
|
|
6222
|
+
onValueChange: handleAudioCodecChange,
|
|
6223
|
+
children: [
|
|
6224
|
+
/* @__PURE__ */ jsx(SelectTrigger, { className: "nodrag h-8 w-full", children: /* @__PURE__ */ jsx(SelectValue, {}) }),
|
|
6225
|
+
/* @__PURE__ */ jsx(SelectContent, { children: AUDIO_CODECS.map((codec) => /* @__PURE__ */ jsxs(SelectItem, { value: codec.value, children: [
|
|
6226
|
+
codec.label,
|
|
6227
|
+
" - ",
|
|
6228
|
+
codec.hint
|
|
6229
|
+
] }, codec.value)) })
|
|
6230
|
+
]
|
|
6231
|
+
}
|
|
6232
|
+
)
|
|
5694
6233
|
] }),
|
|
5695
6234
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 nodrag", children: [
|
|
5696
6235
|
/* @__PURE__ */ jsx(
|
|
@@ -5804,7 +6343,16 @@ function VideoTrimNodeComponent(props) {
|
|
|
5804
6343
|
openNodeDetailModal(id, "preview");
|
|
5805
6344
|
}, [id, openNodeDetailModal]);
|
|
5806
6345
|
const headerActions = useMemo(
|
|
5807
|
-
() => nodeData.outputVideo ? /* @__PURE__ */ jsx(
|
|
6346
|
+
() => nodeData.outputVideo ? /* @__PURE__ */ jsx(
|
|
6347
|
+
Button,
|
|
6348
|
+
{
|
|
6349
|
+
variant: "ghost",
|
|
6350
|
+
size: "icon-sm",
|
|
6351
|
+
onClick: handleExpand,
|
|
6352
|
+
title: "Expand preview",
|
|
6353
|
+
children: /* @__PURE__ */ jsx(Expand, { className: "h-3 w-3" })
|
|
6354
|
+
}
|
|
6355
|
+
) : null,
|
|
5808
6356
|
[nodeData.outputVideo, handleExpand]
|
|
5809
6357
|
);
|
|
5810
6358
|
const trimDuration = nodeData.endTime - nodeData.startTime;
|
|
@@ -5914,36 +6462,36 @@ var VideoTrimNode = memo(VideoTrimNodeComponent);
|
|
|
5914
6462
|
|
|
5915
6463
|
// src/nodes/index.ts
|
|
5916
6464
|
var nodeTypes = {
|
|
6465
|
+
animation: AnimationNode,
|
|
6466
|
+
annotation: AnnotationNode,
|
|
5917
6467
|
// Input nodes
|
|
5918
6468
|
audioInput: AudioInputNode,
|
|
5919
|
-
|
|
5920
|
-
|
|
5921
|
-
|
|
5922
|
-
promptConstructor: PromptConstructorNode,
|
|
6469
|
+
// Output nodes
|
|
6470
|
+
download: DownloadNode,
|
|
6471
|
+
imageCompare: ImageCompareNode,
|
|
5923
6472
|
// AI nodes
|
|
5924
6473
|
imageGen: ImageGenNode,
|
|
5925
|
-
|
|
5926
|
-
|
|
6474
|
+
imageGridSplit: ImageGridSplitNode,
|
|
6475
|
+
imageInput: ImageInputNode,
|
|
5927
6476
|
lipSync: LipSyncNode,
|
|
5928
|
-
|
|
5929
|
-
textToSpeech: TextToSpeechNode,
|
|
5930
|
-
transcribe: TranscribeNode,
|
|
6477
|
+
llm: LLMNode,
|
|
5931
6478
|
motionControl: MotionControlNode,
|
|
6479
|
+
outputGallery: OutputGalleryNode,
|
|
6480
|
+
prompt: PromptNode,
|
|
6481
|
+
promptConstructor: PromptConstructorNode,
|
|
6482
|
+
reframe: ReframeNode,
|
|
5932
6483
|
// Processing nodes
|
|
5933
6484
|
resize: ResizeNode,
|
|
5934
|
-
|
|
5935
|
-
|
|
5936
|
-
|
|
5937
|
-
|
|
5938
|
-
|
|
6485
|
+
subtitle: SubtitleNode,
|
|
6486
|
+
textToSpeech: TextToSpeechNode,
|
|
6487
|
+
transcribe: TranscribeNode,
|
|
6488
|
+
upscale: UpscaleNode,
|
|
6489
|
+
videoFrameExtract: VideoFrameExtractNode,
|
|
6490
|
+
videoGen: VideoGenNode,
|
|
6491
|
+
videoInput: VideoInputNode,
|
|
5939
6492
|
videoStitch: VideoStitchNode,
|
|
5940
6493
|
videoTrim: VideoTrimNode,
|
|
5941
|
-
|
|
5942
|
-
reframe: ReframeNode,
|
|
5943
|
-
upscale: UpscaleNode,
|
|
5944
|
-
subtitle: SubtitleNode,
|
|
5945
|
-
// Output nodes
|
|
5946
|
-
download: DownloadNode,
|
|
6494
|
+
voiceChange: VoiceChangeNode,
|
|
5947
6495
|
// Composition nodes (workflow-as-node)
|
|
5948
6496
|
workflowInput: WorkflowInputNode,
|
|
5949
6497
|
workflowOutput: WorkflowOutputNode,
|