@runtypelabs/react-flow 1.0.114 → 1.0.120

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/index.mjs ADDED
@@ -0,0 +1,3408 @@
1
+ import { memo, useCallback, useEffect, useMemo, useRef, useState } from "react";
2
+ import { Background, BackgroundVariant, ConnectionLineType, Controls, Handle, MiniMap, Panel, Position, ReactFlow, addEdge, useEdgesState, useNodesState } from "@xyflow/react";
3
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
4
+ //#region src/types/index.ts
5
+ /**
6
+ * Supported node types that have custom components
7
+ */
8
+ const SUPPORTED_NODE_TYPES = [
9
+ "prompt",
10
+ "fetch-url",
11
+ "transform-data",
12
+ "conditional",
13
+ "send-email"
14
+ ];
15
+ /**
16
+ * Check if a step type has a custom node component
17
+ */
18
+ function isSupportedNodeType(type) {
19
+ return SUPPORTED_NODE_TYPES.includes(type);
20
+ }
21
+ //#endregion
22
+ //#region src/utils/adapter.ts
23
+ const NODE_HEIGHT = 200;
24
+ const NODE_SPACING_Y = 80;
25
+ const BRANCH_OFFSET_X = 350;
26
+ const BRANCH_OFFSET_Y = -80;
27
+ const FALSE_BRANCH_GAP = 100;
28
+ /**
29
+ * Convert Runtype FlowStep array to React Flow Node array
30
+ * Handles nested conditional branches with vertical stacking below conditional
31
+ */
32
+ function flowStepsToNodes(steps, options) {
33
+ const { onChange, onDelete, startPosition = {
34
+ x: 50,
35
+ y: 200
36
+ }, idPrefix = "", parentId } = options ?? {};
37
+ const sortedSteps = [...steps].sort((a, b) => a.order - b.order);
38
+ const nodes = [];
39
+ let currentX = startPosition.x;
40
+ let maxBranchY = startPosition.y;
41
+ for (const step of sortedSteps) {
42
+ const nodeId = idPrefix ? `${idPrefix}${step.id}` : step.id;
43
+ const nodeData = {
44
+ step,
45
+ label: step.name || getDefaultStepName(step.type),
46
+ onChange,
47
+ onDelete
48
+ };
49
+ const node = {
50
+ id: nodeId,
51
+ type: step.type,
52
+ position: {
53
+ x: currentX,
54
+ y: startPosition.y
55
+ },
56
+ data: nodeData,
57
+ draggable: true,
58
+ selectable: true
59
+ };
60
+ if (parentId) node.parentId = parentId;
61
+ nodes.push(node);
62
+ console.log(`[flowStepsToNodes] Placed "${step.name}" (${step.type}) at x=${currentX}`);
63
+ if (step.type === "conditional" && step.config) {
64
+ const config = step.config;
65
+ const branchX = currentX + BRANCH_OFFSET_X;
66
+ const trueBranchLength = config.trueSteps?.length || 0;
67
+ const falseBranchLength = config.falseSteps?.length || 0;
68
+ const maxBranchLength = Math.max(trueBranchLength, falseBranchLength);
69
+ const trueBranchHeight = trueBranchLength * 280;
70
+ if (config.trueSteps && config.trueSteps.length > 0) {
71
+ const trueBranchY = startPosition.y + BRANCH_OFFSET_Y;
72
+ const trueBranchNodes = flowStepsToNodes(config.trueSteps, {
73
+ onChange,
74
+ onDelete,
75
+ startPosition: {
76
+ x: branchX,
77
+ y: trueBranchY
78
+ },
79
+ idPrefix: `${nodeId}-true-`,
80
+ parentId: nodeId
81
+ });
82
+ nodes.push(...trueBranchNodes);
83
+ maxBranchY = Math.max(maxBranchY, trueBranchY + trueBranchHeight);
84
+ }
85
+ if (config.falseSteps && config.falseSteps.length > 0) {
86
+ const falseBranchY = trueBranchLength > 0 ? startPosition.y + BRANCH_OFFSET_Y + trueBranchHeight + FALSE_BRANCH_GAP : startPosition.y + NODE_HEIGHT + NODE_SPACING_Y;
87
+ const falseBranchNodes = flowStepsToNodes(config.falseSteps, {
88
+ onChange,
89
+ onDelete,
90
+ startPosition: {
91
+ x: branchX,
92
+ y: falseBranchY
93
+ },
94
+ idPrefix: `${nodeId}-false-`,
95
+ parentId: nodeId
96
+ });
97
+ nodes.push(...falseBranchNodes);
98
+ const falseBranchHeight = config.falseSteps.length * 280;
99
+ maxBranchY = Math.max(maxBranchY, falseBranchY + falseBranchHeight);
100
+ }
101
+ if (maxBranchLength > 0) {
102
+ const advance = BRANCH_OFFSET_X + maxBranchLength * 630;
103
+ console.log(`[flowStepsToNodes] Conditional "${step.name}" has ${maxBranchLength} branch steps, advancing currentX by ${advance}`);
104
+ currentX += advance;
105
+ console.log(`[flowStepsToNodes] After conditional, currentX = ${currentX}`);
106
+ } else currentX += 630;
107
+ } else currentX += 630;
108
+ }
109
+ return nodes;
110
+ }
111
+ /**
112
+ * Convert React Flow Node array back to Runtype FlowStep array
113
+ */
114
+ function nodesToFlowSteps(nodes) {
115
+ return [...nodes.filter((n) => !n.parentId && !n.id.includes("-true-") && !n.id.includes("-false-"))].sort((a, b) => a.position.x - b.position.x).map((node, index) => {
116
+ const step = node.data.step;
117
+ if (step.type === "conditional") {
118
+ const trueSteps = extractBranchSteps(nodes, node.id, "true");
119
+ const falseSteps = extractBranchSteps(nodes, node.id, "false");
120
+ return {
121
+ ...step,
122
+ order: index,
123
+ config: {
124
+ ...step.config,
125
+ trueSteps,
126
+ falseSteps
127
+ }
128
+ };
129
+ }
130
+ return {
131
+ ...step,
132
+ order: index
133
+ };
134
+ });
135
+ }
136
+ /**
137
+ * Extract branch steps from conditional node
138
+ */
139
+ function extractBranchSteps(nodes, parentId, branch) {
140
+ const branchPrefix = `${parentId}-${branch}-`;
141
+ return [...nodes.filter((n) => n.id.startsWith(branchPrefix))].sort((a, b) => a.position.x - b.position.x).map((node, index) => ({
142
+ ...node.data.step,
143
+ order: index
144
+ }));
145
+ }
146
+ /**
147
+ * Create edges connecting nodes in sequence
148
+ */
149
+ function createEdgesFromNodes(nodes) {
150
+ const edges = [];
151
+ const isBranchNode = (n) => n.parentId || n.id.includes("-true-") || n.id.includes("-false-");
152
+ const topLevelNodes = nodes.filter((n) => !isBranchNode(n)).sort((a, b) => a.position.x - b.position.x);
153
+ for (let i = 0; i < topLevelNodes.length - 1; i++) {
154
+ const sourceNode = topLevelNodes[i];
155
+ const targetNode = topLevelNodes[i + 1];
156
+ if (sourceNode.data.step.type === "conditional") continue;
157
+ edges.push({
158
+ id: `edge-${sourceNode.id}-${targetNode.id}`,
159
+ source: sourceNode.id,
160
+ target: targetNode.id,
161
+ sourceHandle: "output",
162
+ type: "smoothstep",
163
+ animated: false,
164
+ data: { stepOrder: i }
165
+ });
166
+ }
167
+ const conditionalNodes = nodes.filter((n) => n.data.step.type === "conditional" && !isBranchNode(n));
168
+ for (const conditionalNode of conditionalNodes) {
169
+ const conditionalId = conditionalNode.id;
170
+ const conditionalIndex = topLevelNodes.findIndex((n) => n.id === conditionalId);
171
+ const nextMainStep = conditionalIndex < topLevelNodes.length - 1 ? topLevelNodes[conditionalIndex + 1] : null;
172
+ const trueBranchNodes = nodes.filter((n) => n.id.startsWith(`${conditionalId}-true-`)).sort((a, b) => a.position.x - b.position.x);
173
+ const falseBranchNodes = nodes.filter((n) => n.id.startsWith(`${conditionalId}-false-`)).sort((a, b) => a.position.x - b.position.x);
174
+ if (trueBranchNodes.length > 0) {
175
+ edges.push({
176
+ id: `edge-${conditionalId}-to-true-branch`,
177
+ source: conditionalId,
178
+ target: trueBranchNodes[0].id,
179
+ sourceHandle: "true",
180
+ type: "smoothstep",
181
+ animated: false,
182
+ label: "True",
183
+ labelStyle: {
184
+ fill: "#22c55e",
185
+ fontWeight: 600,
186
+ fontSize: 11
187
+ },
188
+ labelBgStyle: {
189
+ fill: "#f0fdf4",
190
+ fillOpacity: .9
191
+ },
192
+ labelBgPadding: [4, 6],
193
+ labelBgBorderRadius: 4,
194
+ style: {
195
+ stroke: "#22c55e",
196
+ strokeWidth: 2
197
+ }
198
+ });
199
+ for (let i = 0; i < trueBranchNodes.length - 1; i++) edges.push({
200
+ id: `edge-true-${trueBranchNodes[i].id}-${trueBranchNodes[i + 1].id}`,
201
+ source: trueBranchNodes[i].id,
202
+ target: trueBranchNodes[i + 1].id,
203
+ sourceHandle: "output",
204
+ type: "smoothstep",
205
+ animated: false,
206
+ style: {
207
+ stroke: "#22c55e",
208
+ strokeWidth: 1.5
209
+ }
210
+ });
211
+ if (nextMainStep) {
212
+ const lastTrueNode = trueBranchNodes[trueBranchNodes.length - 1];
213
+ edges.push({
214
+ id: `edge-true-${lastTrueNode.id}-to-${nextMainStep.id}`,
215
+ source: lastTrueNode.id,
216
+ target: nextMainStep.id,
217
+ sourceHandle: "output",
218
+ type: "smoothstep",
219
+ animated: false,
220
+ style: {
221
+ stroke: "#22c55e",
222
+ strokeWidth: 1.5
223
+ }
224
+ });
225
+ }
226
+ } else if (nextMainStep) edges.push({
227
+ id: `edge-${conditionalId}-true-to-${nextMainStep.id}`,
228
+ source: conditionalId,
229
+ target: nextMainStep.id,
230
+ sourceHandle: "true",
231
+ type: "smoothstep",
232
+ animated: false,
233
+ label: "True",
234
+ labelStyle: {
235
+ fill: "#22c55e",
236
+ fontWeight: 600,
237
+ fontSize: 11
238
+ },
239
+ labelBgStyle: {
240
+ fill: "#f0fdf4",
241
+ fillOpacity: .9
242
+ },
243
+ labelBgPadding: [4, 6],
244
+ labelBgBorderRadius: 4,
245
+ style: {
246
+ stroke: "#22c55e",
247
+ strokeWidth: 2
248
+ }
249
+ });
250
+ if (falseBranchNodes.length > 0) {
251
+ edges.push({
252
+ id: `edge-${conditionalId}-to-false-branch`,
253
+ source: conditionalId,
254
+ target: falseBranchNodes[0].id,
255
+ sourceHandle: "false",
256
+ type: "smoothstep",
257
+ animated: false,
258
+ label: "False",
259
+ labelStyle: {
260
+ fill: "#ef4444",
261
+ fontWeight: 600,
262
+ fontSize: 11
263
+ },
264
+ labelBgStyle: {
265
+ fill: "#fef2f2",
266
+ fillOpacity: .9
267
+ },
268
+ labelBgPadding: [4, 6],
269
+ labelBgBorderRadius: 4,
270
+ style: {
271
+ stroke: "#ef4444",
272
+ strokeWidth: 2
273
+ }
274
+ });
275
+ for (let i = 0; i < falseBranchNodes.length - 1; i++) edges.push({
276
+ id: `edge-false-${falseBranchNodes[i].id}-${falseBranchNodes[i + 1].id}`,
277
+ source: falseBranchNodes[i].id,
278
+ target: falseBranchNodes[i + 1].id,
279
+ sourceHandle: "output",
280
+ type: "smoothstep",
281
+ animated: false,
282
+ style: {
283
+ stroke: "#ef4444",
284
+ strokeWidth: 1.5
285
+ }
286
+ });
287
+ if (nextMainStep) {
288
+ const lastFalseNode = falseBranchNodes[falseBranchNodes.length - 1];
289
+ edges.push({
290
+ id: `edge-false-${lastFalseNode.id}-to-${nextMainStep.id}`,
291
+ source: lastFalseNode.id,
292
+ target: nextMainStep.id,
293
+ sourceHandle: "output",
294
+ type: "smoothstep",
295
+ animated: false,
296
+ style: {
297
+ stroke: "#ef4444",
298
+ strokeWidth: 1.5
299
+ }
300
+ });
301
+ }
302
+ } else if (nextMainStep) edges.push({
303
+ id: `edge-${conditionalId}-false-to-${nextMainStep.id}`,
304
+ source: conditionalId,
305
+ target: nextMainStep.id,
306
+ sourceHandle: "false",
307
+ type: "smoothstep",
308
+ animated: false,
309
+ label: "False",
310
+ labelStyle: {
311
+ fill: "#ef4444",
312
+ fontWeight: 600,
313
+ fontSize: 11
314
+ },
315
+ labelBgStyle: {
316
+ fill: "#fef2f2",
317
+ fillOpacity: .9
318
+ },
319
+ labelBgPadding: [4, 6],
320
+ labelBgBorderRadius: 4,
321
+ style: {
322
+ stroke: "#ef4444",
323
+ strokeWidth: 2
324
+ }
325
+ });
326
+ if (trueBranchNodes.length === 0 && falseBranchNodes.length === 0 && nextMainStep) edges.push({
327
+ id: `edge-${conditionalId}-to-${nextMainStep.id}`,
328
+ source: conditionalId,
329
+ target: nextMainStep.id,
330
+ sourceHandle: "output",
331
+ type: "smoothstep",
332
+ animated: false
333
+ });
334
+ }
335
+ return edges;
336
+ }
337
+ /**
338
+ * Get default name for a step type
339
+ */
340
+ function getDefaultStepName(type) {
341
+ return {
342
+ prompt: "AI Prompt",
343
+ crawl: "Crawl Website",
344
+ "fetch-url": "Fetch URL",
345
+ "retrieve-record": "Retrieve Record",
346
+ "get-record": "Get Record",
347
+ "list-records": "List Records",
348
+ "api-call": "API Call",
349
+ "transform-data": "Transform Data",
350
+ conditional: "Conditional",
351
+ "set-variable": "Set Variable",
352
+ "upsert-record": "Upsert Record",
353
+ "send-email": "Send Email",
354
+ "send-event": "Send Event",
355
+ "send-stream": "Send Stream",
356
+ "update-record": "Update Record",
357
+ search: "Search",
358
+ "generate-embedding": "Generate Embedding",
359
+ "vector-search": "Vector Search",
360
+ "tool-call": "Tool Call",
361
+ "wait-until": "Wait Until",
362
+ "paginate-api": "Paginate API",
363
+ "store-vector": "Store Vector"
364
+ }[type] || type;
365
+ }
366
+ /**
367
+ * Create a new flow step with default configuration
368
+ */
369
+ function createDefaultStep(type, order = 0) {
370
+ const baseStep = {
371
+ id: `step-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
372
+ type,
373
+ name: getDefaultStepName(type),
374
+ order,
375
+ enabled: true
376
+ };
377
+ switch (type) {
378
+ case "prompt": return {
379
+ ...baseStep,
380
+ config: {
381
+ mode: "instruction",
382
+ model: "",
383
+ userPrompt: "",
384
+ responseFormat: "text",
385
+ outputVariable: `${type}_result`
386
+ }
387
+ };
388
+ case "crawl": return {
389
+ ...baseStep,
390
+ config: {
391
+ url: "",
392
+ formats: ["markdown"],
393
+ render: false,
394
+ outputVariable: "crawl_result"
395
+ }
396
+ };
397
+ case "fetch-url": return {
398
+ ...baseStep,
399
+ config: {
400
+ http: {
401
+ url: "",
402
+ method: "GET"
403
+ },
404
+ responseType: "json",
405
+ outputVariable: "fetch_result"
406
+ }
407
+ };
408
+ case "transform-data": return {
409
+ ...baseStep,
410
+ config: {
411
+ script: "// Transform your data here\nreturn { result: input }",
412
+ outputVariable: "transform_result"
413
+ }
414
+ };
415
+ case "conditional": return {
416
+ ...baseStep,
417
+ config: {
418
+ condition: "true",
419
+ trueSteps: [],
420
+ falseSteps: []
421
+ }
422
+ };
423
+ case "send-email": return {
424
+ ...baseStep,
425
+ config: {
426
+ from: "{{_flow.id}}@runtype.email",
427
+ to: "",
428
+ subject: "",
429
+ html: "",
430
+ outputVariable: "email_result"
431
+ }
432
+ };
433
+ default: return {
434
+ ...baseStep,
435
+ config: { outputVariable: `${type.replace(/-/g, "_")}_result` }
436
+ };
437
+ }
438
+ }
439
+ /**
440
+ * Generate a unique step ID
441
+ */
442
+ function generateStepId(prefix = "step") {
443
+ return `${prefix}-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
444
+ }
445
+ /**
446
+ * Clone a step with a new ID
447
+ */
448
+ function cloneStep(step) {
449
+ return {
450
+ ...step,
451
+ id: generateStepId(step.type),
452
+ config: JSON.parse(JSON.stringify(step.config))
453
+ };
454
+ }
455
+ //#endregion
456
+ //#region src/utils/layout.ts
457
+ const DEFAULT_NODE_WIDTH = 280;
458
+ const DEFAULT_NODE_HEIGHT = 150;
459
+ const HORIZONTAL_SPACING = 100;
460
+ const VERTICAL_SPACING = 80;
461
+ const BRANCH_OFFSET = 350;
462
+ /**
463
+ * Automatically layout nodes in a flow-like arrangement
464
+ */
465
+ function autoLayout(nodes, edges, options = {}) {
466
+ const { direction = "vertical", startPosition = {
467
+ x: 400,
468
+ y: 50
469
+ }, nodeWidth = DEFAULT_NODE_WIDTH, nodeHeight = DEFAULT_NODE_HEIGHT, horizontalSpacing = HORIZONTAL_SPACING, verticalSpacing = VERTICAL_SPACING, branchOffset = BRANCH_OFFSET } = options;
470
+ const adjacencyMap = /* @__PURE__ */ new Map();
471
+ const incomingMap = /* @__PURE__ */ new Map();
472
+ for (const edge of edges) {
473
+ const existing = adjacencyMap.get(edge.source) || [];
474
+ adjacencyMap.set(edge.source, [...existing, edge.target]);
475
+ const incoming = incomingMap.get(edge.target) || [];
476
+ incomingMap.set(edge.target, [...incoming, edge.source]);
477
+ }
478
+ const rootNodes = nodes.filter((node) => {
479
+ const incoming = incomingMap.get(node.id);
480
+ return !incoming || incoming.length === 0;
481
+ });
482
+ if (rootNodes.length === 0 && nodes.length > 0) rootNodes.push(nodes[0]);
483
+ const positionedNodes = /* @__PURE__ */ new Map();
484
+ const visited = /* @__PURE__ */ new Set();
485
+ const queue = [];
486
+ let startX = startPosition.x;
487
+ for (const rootNode of rootNodes) {
488
+ queue.push({
489
+ nodeId: rootNode.id,
490
+ x: startX,
491
+ y: startPosition.y,
492
+ depth: 0
493
+ });
494
+ startX += nodeWidth + horizontalSpacing;
495
+ }
496
+ while (queue.length > 0) {
497
+ const { nodeId, x, y, depth } = queue.shift();
498
+ if (visited.has(nodeId)) continue;
499
+ visited.add(nodeId);
500
+ positionedNodes.set(nodeId, {
501
+ x,
502
+ y
503
+ });
504
+ const children = adjacencyMap.get(nodeId) || [];
505
+ if (nodes.find((n) => n.id === nodeId)?.data.step.type === "conditional" && children.length > 0) {
506
+ const trueBranch = children.filter((c) => c.includes("-true-"));
507
+ const falseBranch = children.filter((c) => c.includes("-false-"));
508
+ const normalChildren = children.filter((c) => !c.includes("-true-") && !c.includes("-false-"));
509
+ let trueY = y + nodeHeight + verticalSpacing;
510
+ for (const childId of trueBranch) if (!visited.has(childId)) {
511
+ queue.push({
512
+ nodeId: childId,
513
+ x: x + branchOffset,
514
+ y: trueY,
515
+ depth: depth + 1
516
+ });
517
+ trueY += nodeHeight + verticalSpacing;
518
+ }
519
+ let falseY = y + nodeHeight + verticalSpacing;
520
+ for (const childId of falseBranch) if (!visited.has(childId)) {
521
+ queue.push({
522
+ nodeId: childId,
523
+ x: x - branchOffset,
524
+ y: falseY,
525
+ depth: depth + 1
526
+ });
527
+ falseY += nodeHeight + verticalSpacing;
528
+ }
529
+ let childY = Math.max(trueY, falseY);
530
+ for (const childId of normalChildren) if (!visited.has(childId)) {
531
+ queue.push({
532
+ nodeId: childId,
533
+ x,
534
+ y: childY,
535
+ depth: depth + 1
536
+ });
537
+ childY += nodeHeight + verticalSpacing;
538
+ }
539
+ } else {
540
+ let childY = y + nodeHeight + verticalSpacing;
541
+ let childX = x;
542
+ for (let i = 0; i < children.length; i++) {
543
+ const childId = children[i];
544
+ if (!visited.has(childId)) {
545
+ if (direction === "horizontal") {
546
+ queue.push({
547
+ nodeId: childId,
548
+ x: childX + nodeWidth + horizontalSpacing,
549
+ y,
550
+ depth: depth + 1
551
+ });
552
+ childX += nodeWidth + horizontalSpacing;
553
+ } else {
554
+ queue.push({
555
+ nodeId: childId,
556
+ x,
557
+ y: childY,
558
+ depth: depth + 1
559
+ });
560
+ childY += nodeHeight + verticalSpacing;
561
+ }
562
+ }
563
+ }
564
+ }
565
+ }
566
+ return nodes.map((node) => {
567
+ const position = positionedNodes.get(node.id);
568
+ if (position) return {
569
+ ...node,
570
+ position
571
+ };
572
+ return node;
573
+ });
574
+ }
575
+ /**
576
+ * Center nodes within a viewport
577
+ */
578
+ function centerNodes(nodes, viewportWidth, viewportHeight) {
579
+ if (nodes.length === 0) return nodes;
580
+ let minX = Infinity;
581
+ let maxX = -Infinity;
582
+ let minY = Infinity;
583
+ let maxY = -Infinity;
584
+ for (const node of nodes) {
585
+ minX = Math.min(minX, node.position.x);
586
+ maxX = Math.max(maxX, node.position.x + DEFAULT_NODE_WIDTH);
587
+ minY = Math.min(minY, node.position.y);
588
+ maxY = Math.max(maxY, node.position.y + DEFAULT_NODE_HEIGHT);
589
+ }
590
+ const contentWidth = maxX - minX;
591
+ const contentHeight = maxY - minY;
592
+ const offsetX = (viewportWidth - contentWidth) / 2 - minX;
593
+ const offsetY = (viewportHeight - contentHeight) / 2 - minY;
594
+ return nodes.map((node) => ({
595
+ ...node,
596
+ position: {
597
+ x: node.position.x + offsetX,
598
+ y: node.position.y + offsetY
599
+ }
600
+ }));
601
+ }
602
+ /**
603
+ * Align nodes to a grid
604
+ */
605
+ function snapToGrid(nodes, gridSize = 20) {
606
+ return nodes.map((node) => ({
607
+ ...node,
608
+ position: {
609
+ x: Math.round(node.position.x / gridSize) * gridSize,
610
+ y: Math.round(node.position.y / gridSize) * gridSize
611
+ }
612
+ }));
613
+ }
614
+ /**
615
+ * Get the bounding box of all nodes
616
+ */
617
+ function getNodesBoundingBox(nodes) {
618
+ if (nodes.length === 0) return {
619
+ minX: 0,
620
+ maxX: 0,
621
+ minY: 0,
622
+ maxY: 0,
623
+ width: 0,
624
+ height: 0
625
+ };
626
+ let minX = Infinity;
627
+ let maxX = -Infinity;
628
+ let minY = Infinity;
629
+ let maxY = -Infinity;
630
+ for (const node of nodes) {
631
+ minX = Math.min(minX, node.position.x);
632
+ maxX = Math.max(maxX, node.position.x + DEFAULT_NODE_WIDTH);
633
+ minY = Math.min(minY, node.position.y);
634
+ maxY = Math.max(maxY, node.position.y + DEFAULT_NODE_HEIGHT);
635
+ }
636
+ return {
637
+ minX,
638
+ maxX,
639
+ minY,
640
+ maxY,
641
+ width: maxX - minX,
642
+ height: maxY - minY
643
+ };
644
+ }
645
+ //#endregion
646
+ //#region src/hooks/useRuntypeFlow.ts
647
+ function useRuntypeFlow(options) {
648
+ const { client, flowId: initialFlowId, initialName = "", initialDescription = "", initialSteps = [], onChange, autoLayoutOnLoad = true } = options;
649
+ const [flowId, setFlowId] = useState(initialFlowId || null);
650
+ const [flowName, setFlowName] = useState(initialName);
651
+ const [flowDescription, setFlowDescription] = useState(initialDescription);
652
+ const [isLoading, setIsLoading] = useState(false);
653
+ const [isSaving, setIsSaving] = useState(false);
654
+ const [error, setError] = useState(null);
655
+ const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false);
656
+ const [nodes, setNodes, onNodesChange] = useNodesState([]);
657
+ const [edges, setEdges, onEdgesChange] = useEdgesState([]);
658
+ const lastSavedState = useRef("");
659
+ const handleStepChange = useCallback((stepId, updates) => {
660
+ setNodes((nds) => nds.map((node) => {
661
+ if (node.id === stepId) return {
662
+ ...node,
663
+ data: {
664
+ ...node.data,
665
+ step: {
666
+ ...node.data.step,
667
+ ...updates,
668
+ config: updates.config ? {
669
+ ...node.data.step.config,
670
+ ...updates.config
671
+ } : node.data.step.config
672
+ }
673
+ }
674
+ };
675
+ return node;
676
+ }));
677
+ setHasUnsavedChanges(true);
678
+ }, [setNodes]);
679
+ const handleStepDelete = useCallback((stepId) => {
680
+ setNodes((nds) => nds.filter((node) => node.id !== stepId));
681
+ setEdges((eds) => eds.filter((edge) => edge.source !== stepId && edge.target !== stepId));
682
+ setHasUnsavedChanges(true);
683
+ }, [setNodes, setEdges]);
684
+ const handleConnect = useCallback((connection) => {
685
+ setEdges((eds) => addEdge({
686
+ ...connection,
687
+ type: "smoothstep"
688
+ }, eds));
689
+ setHasUnsavedChanges(true);
690
+ }, [setEdges]);
691
+ /**
692
+ * Load a flow by ID
693
+ */
694
+ const loadFlow = useCallback(async (id) => {
695
+ setIsLoading(true);
696
+ setError(null);
697
+ try {
698
+ const flow = await client.flows.get(id);
699
+ let newNodes = flowStepsToNodes(Array.isArray(flow.flowSteps) ? flow.flowSteps.map((s) => ({
700
+ id: s.id,
701
+ type: s.type,
702
+ name: s.name ?? "",
703
+ order: s.order ?? 0,
704
+ enabled: s.enabled ?? true,
705
+ config: s.config ?? {}
706
+ })) : [], {
707
+ onChange: handleStepChange,
708
+ onDelete: handleStepDelete
709
+ });
710
+ if (autoLayoutOnLoad) {
711
+ const newEdges = createEdgesFromNodes(newNodes);
712
+ newNodes = autoLayout(newNodes, newEdges);
713
+ }
714
+ const newEdges = createEdgesFromNodes(newNodes);
715
+ setFlowId(id);
716
+ setFlowName(flow.name || "");
717
+ setFlowDescription("");
718
+ setNodes(newNodes);
719
+ setEdges(newEdges);
720
+ setHasUnsavedChanges(false);
721
+ lastSavedState.current = JSON.stringify(nodesToFlowSteps(newNodes));
722
+ } catch (err) {
723
+ setError(err instanceof Error ? err : /* @__PURE__ */ new Error("Failed to load flow"));
724
+ throw err;
725
+ } finally {
726
+ setIsLoading(false);
727
+ }
728
+ }, [
729
+ client,
730
+ handleStepChange,
731
+ handleStepDelete,
732
+ autoLayoutOnLoad,
733
+ setNodes,
734
+ setEdges
735
+ ]);
736
+ /**
737
+ * Save the current flow
738
+ */
739
+ const saveFlow = useCallback(async () => {
740
+ if (!flowId) throw new Error("No flow ID. Use createFlow() to create a new flow first.");
741
+ setIsSaving(true);
742
+ setError(null);
743
+ try {
744
+ const steps = nodesToFlowSteps(nodes);
745
+ await client.flows.update(flowId, {
746
+ name: flowName,
747
+ flowSteps: steps.map((step) => ({
748
+ type: step.type,
749
+ name: step.name,
750
+ order: step.order,
751
+ enabled: step.enabled,
752
+ config: step.config
753
+ }))
754
+ });
755
+ setHasUnsavedChanges(false);
756
+ lastSavedState.current = JSON.stringify(steps);
757
+ return {
758
+ id: flowId,
759
+ name: flowName,
760
+ description: flowDescription,
761
+ steps
762
+ };
763
+ } catch (err) {
764
+ setError(err instanceof Error ? err : /* @__PURE__ */ new Error("Failed to save flow"));
765
+ throw err;
766
+ } finally {
767
+ setIsSaving(false);
768
+ }
769
+ }, [
770
+ flowId,
771
+ flowName,
772
+ flowDescription,
773
+ nodes,
774
+ client
775
+ ]);
776
+ /**
777
+ * Create a new flow
778
+ */
779
+ const createFlow = useCallback(async (name, description) => {
780
+ setIsSaving(true);
781
+ setError(null);
782
+ try {
783
+ const flow = await client.flows.create({
784
+ name,
785
+ description
786
+ });
787
+ setFlowId(flow.id);
788
+ setFlowName(name);
789
+ setFlowDescription(description || "");
790
+ setNodes([]);
791
+ setEdges([]);
792
+ setHasUnsavedChanges(false);
793
+ lastSavedState.current = "[]";
794
+ return {
795
+ id: flow.id,
796
+ name,
797
+ description,
798
+ steps: []
799
+ };
800
+ } catch (err) {
801
+ setError(err instanceof Error ? err : /* @__PURE__ */ new Error("Failed to create flow"));
802
+ throw err;
803
+ } finally {
804
+ setIsSaving(false);
805
+ }
806
+ }, [
807
+ client,
808
+ setNodes,
809
+ setEdges
810
+ ]);
811
+ /**
812
+ * Delete a step
813
+ */
814
+ const deleteStep = useCallback((stepId) => {
815
+ handleStepDelete(stepId);
816
+ }, [handleStepDelete]);
817
+ /**
818
+ * Update a step's configuration
819
+ */
820
+ const updateStep = useCallback((stepId, updates) => {
821
+ handleStepChange(stepId, updates);
822
+ }, [handleStepChange]);
823
+ /**
824
+ * Add a new step
825
+ */
826
+ const addStep = useCallback((type, position) => {
827
+ const newStep = createDefaultStep(type, nodes.length);
828
+ const newNode = {
829
+ id: newStep.id,
830
+ type: newStep.type,
831
+ position: position || {
832
+ x: 400,
833
+ y: nodes.length * 230 + 50
834
+ },
835
+ data: {
836
+ step: newStep,
837
+ label: newStep.name,
838
+ onChange: handleStepChange,
839
+ onDelete: handleStepDelete
840
+ },
841
+ draggable: true,
842
+ selectable: true
843
+ };
844
+ setNodes((nds) => [...nds, newNode]);
845
+ if (nodes.length > 0) {
846
+ const lastNode = nodes[nodes.length - 1];
847
+ setEdges((eds) => [...eds, {
848
+ id: `edge-${lastNode.id}-${newNode.id}`,
849
+ source: lastNode.id,
850
+ target: newNode.id,
851
+ type: "smoothstep"
852
+ }]);
853
+ }
854
+ setHasUnsavedChanges(true);
855
+ }, [
856
+ nodes,
857
+ setNodes,
858
+ setEdges,
859
+ handleStepChange,
860
+ handleStepDelete
861
+ ]);
862
+ useEffect(() => {
863
+ if (initialFlowId) loadFlow(initialFlowId);
864
+ else if (initialSteps.length > 0) {
865
+ let newNodes = flowStepsToNodes(initialSteps, {
866
+ onChange: handleStepChange,
867
+ onDelete: handleStepDelete
868
+ });
869
+ if (autoLayoutOnLoad) {
870
+ const newEdges = createEdgesFromNodes(newNodes);
871
+ newNodes = autoLayout(newNodes, newEdges);
872
+ }
873
+ const newEdges = createEdgesFromNodes(newNodes);
874
+ setNodes(newNodes);
875
+ setEdges(newEdges);
876
+ }
877
+ }, []);
878
+ useEffect(() => {
879
+ onChange?.(nodes, edges);
880
+ }, [
881
+ nodes,
882
+ edges,
883
+ onChange
884
+ ]);
885
+ useEffect(() => {
886
+ const currentState = JSON.stringify(nodesToFlowSteps(nodes));
887
+ if (lastSavedState.current && currentState !== lastSavedState.current) setHasUnsavedChanges(true);
888
+ }, [nodes]);
889
+ return {
890
+ nodes,
891
+ edges,
892
+ onNodesChange,
893
+ onEdgesChange,
894
+ onConnect: handleConnect,
895
+ flowName,
896
+ flowDescription,
897
+ flowId,
898
+ setFlowName,
899
+ setFlowDescription,
900
+ loadFlow,
901
+ saveFlow,
902
+ createFlow,
903
+ deleteStep,
904
+ updateStep,
905
+ addStep,
906
+ isLoading,
907
+ isSaving,
908
+ error,
909
+ hasUnsavedChanges
910
+ };
911
+ }
912
+ //#endregion
913
+ //#region src/hooks/useFlowValidation.ts
914
+ const promptValidation = { validate: (step) => {
915
+ const errors = [];
916
+ const config = step.config;
917
+ if (!config.model) errors.push({
918
+ stepId: step.id,
919
+ field: "model",
920
+ message: "Model is required"
921
+ });
922
+ if (!config.userPrompt?.trim()) errors.push({
923
+ stepId: step.id,
924
+ field: "userPrompt",
925
+ message: "User prompt is required"
926
+ });
927
+ if (!config.outputVariable?.trim()) errors.push({
928
+ stepId: step.id,
929
+ field: "outputVariable",
930
+ message: "Output variable is required"
931
+ });
932
+ return errors;
933
+ } };
934
+ const fetchUrlValidation = { validate: (step) => {
935
+ const errors = [];
936
+ const config = step.config;
937
+ if (!config.http?.url?.trim()) errors.push({
938
+ stepId: step.id,
939
+ field: "http.url",
940
+ message: "URL is required"
941
+ });
942
+ else {
943
+ const url = config.http.url;
944
+ if (!url.startsWith("http://") && !url.startsWith("https://") && !url.includes("{{")) errors.push({
945
+ stepId: step.id,
946
+ field: "http.url",
947
+ message: "URL must start with http:// or https://"
948
+ });
949
+ }
950
+ if (!config.outputVariable?.trim()) errors.push({
951
+ stepId: step.id,
952
+ field: "outputVariable",
953
+ message: "Output variable is required"
954
+ });
955
+ return errors;
956
+ } };
957
+ const transformDataValidation = { validate: (step) => {
958
+ const errors = [];
959
+ const config = step.config;
960
+ if (!config.script?.trim()) errors.push({
961
+ stepId: step.id,
962
+ field: "script",
963
+ message: "Script is required"
964
+ });
965
+ if (!config.outputVariable?.trim()) errors.push({
966
+ stepId: step.id,
967
+ field: "outputVariable",
968
+ message: "Output variable is required"
969
+ });
970
+ return errors;
971
+ } };
972
+ const conditionalValidation = { validate: (step, allSteps) => {
973
+ const errors = [];
974
+ const config = step.config;
975
+ if (!config.condition?.trim()) errors.push({
976
+ stepId: step.id,
977
+ field: "condition",
978
+ message: "Condition is required"
979
+ });
980
+ if (config.trueSteps && config.trueSteps.length > 0) for (const nestedStep of config.trueSteps) {
981
+ const nestedErrors = validateStep(nestedStep, allSteps);
982
+ errors.push(...nestedErrors.map((e) => ({
983
+ ...e,
984
+ message: `True branch: ${e.message}`
985
+ })));
986
+ }
987
+ if (config.falseSteps && config.falseSteps.length > 0) for (const nestedStep of config.falseSteps) {
988
+ const nestedErrors = validateStep(nestedStep, allSteps);
989
+ errors.push(...nestedErrors.map((e) => ({
990
+ ...e,
991
+ message: `False branch: ${e.message}`
992
+ })));
993
+ }
994
+ return errors;
995
+ } };
996
+ const sendEmailValidation = { validate: (step) => {
997
+ const errors = [];
998
+ const config = step.config;
999
+ if (!config.to?.trim()) errors.push({
1000
+ stepId: step.id,
1001
+ field: "to",
1002
+ message: "Recipient (To) is required"
1003
+ });
1004
+ else {
1005
+ const to = config.to;
1006
+ if (!to.includes("@") && !to.includes("{{")) errors.push({
1007
+ stepId: step.id,
1008
+ field: "to",
1009
+ message: "Invalid email address"
1010
+ });
1011
+ }
1012
+ if (!config.subject?.trim()) errors.push({
1013
+ stepId: step.id,
1014
+ field: "subject",
1015
+ message: "Subject is required"
1016
+ });
1017
+ if (!config.html?.trim() && !config.text?.trim()) errors.push({
1018
+ stepId: step.id,
1019
+ field: "html",
1020
+ message: "Email content (HTML or text) is required"
1021
+ });
1022
+ if (!config.outputVariable?.trim()) errors.push({
1023
+ stepId: step.id,
1024
+ field: "outputVariable",
1025
+ message: "Output variable is required"
1026
+ });
1027
+ return errors;
1028
+ } };
1029
+ const outputVariableWarning = { check: (step, allSteps) => {
1030
+ const warnings = [];
1031
+ const config = step.config;
1032
+ if (config.outputVariable) {
1033
+ if (allSteps.filter((s) => s.id !== step.id && s.config.outputVariable === config.outputVariable).length > 0) warnings.push({
1034
+ stepId: step.id,
1035
+ field: "outputVariable",
1036
+ message: `Output variable "${config.outputVariable}" is also used by another step`
1037
+ });
1038
+ }
1039
+ return warnings;
1040
+ } };
1041
+ const emptyBranchWarning = { check: (step) => {
1042
+ const warnings = [];
1043
+ if (step.type === "conditional") {
1044
+ const config = step.config;
1045
+ if (!config.trueSteps || config.trueSteps.length === 0) warnings.push({
1046
+ stepId: step.id,
1047
+ field: "trueSteps",
1048
+ message: "True branch has no steps"
1049
+ });
1050
+ if (!config.falseSteps || config.falseSteps.length === 0) warnings.push({
1051
+ stepId: step.id,
1052
+ field: "falseSteps",
1053
+ message: "False branch has no steps"
1054
+ });
1055
+ }
1056
+ return warnings;
1057
+ } };
1058
+ const validationRules = {
1059
+ prompt: promptValidation,
1060
+ "fetch-url": fetchUrlValidation,
1061
+ "transform-data": transformDataValidation,
1062
+ conditional: conditionalValidation,
1063
+ "send-email": sendEmailValidation
1064
+ };
1065
+ const warningRules = [outputVariableWarning, emptyBranchWarning];
1066
+ function validateStep(step, allSteps) {
1067
+ const errors = [];
1068
+ if (!step.name?.trim()) errors.push({
1069
+ stepId: step.id,
1070
+ field: "name",
1071
+ message: "Step name is required"
1072
+ });
1073
+ const rule = validationRules[step.type];
1074
+ if (rule) errors.push(...rule.validate(step, allSteps));
1075
+ return errors;
1076
+ }
1077
+ function checkWarnings(step, allSteps) {
1078
+ const warnings = [];
1079
+ for (const rule of warningRules) warnings.push(...rule.check(step, allSteps));
1080
+ return warnings;
1081
+ }
1082
+ function useFlowValidation(options) {
1083
+ const { steps: providedSteps, nodes } = options;
1084
+ const steps = useMemo(() => {
1085
+ if (providedSteps) return providedSteps;
1086
+ if (nodes) return nodes.map((n) => n.data.step);
1087
+ return [];
1088
+ }, [providedSteps, nodes]);
1089
+ const result = useMemo(() => {
1090
+ const errors = [];
1091
+ const warnings = [];
1092
+ for (const step of steps) {
1093
+ errors.push(...validateStep(step, steps));
1094
+ warnings.push(...checkWarnings(step, steps));
1095
+ }
1096
+ return {
1097
+ isValid: errors.length === 0,
1098
+ errors,
1099
+ warnings
1100
+ };
1101
+ }, [steps]);
1102
+ return {
1103
+ result,
1104
+ validateStep: useCallback((step) => validateStep(step, steps), [steps]),
1105
+ isStepValid: useCallback((stepId) => {
1106
+ return !result.errors.some((e) => e.stepId === stepId);
1107
+ }, [result.errors]),
1108
+ getStepErrors: useCallback((stepId) => {
1109
+ return result.errors.filter((e) => e.stepId === stepId);
1110
+ }, [result.errors]),
1111
+ getStepWarnings: useCallback((stepId) => {
1112
+ return result.warnings.filter((w) => w.stepId === stepId);
1113
+ }, [result.warnings])
1114
+ };
1115
+ }
1116
+ //#endregion
1117
+ //#region src/components/nodes/BaseNode.tsx
1118
+ const styles$6 = {
1119
+ container: (selected) => ({
1120
+ minWidth: "280px",
1121
+ maxWidth: "320px",
1122
+ backgroundColor: "#ffffff",
1123
+ borderRadius: "12px",
1124
+ border: selected ? "2px solid #6366f1" : "1px solid #e5e7eb",
1125
+ boxShadow: selected ? "0 0 0 2px rgba(99, 102, 241, 0.2), 0 4px 6px -1px rgba(0, 0, 0, 0.1)" : "0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06)",
1126
+ overflow: "hidden",
1127
+ transition: "border-color 0.15s ease, box-shadow 0.15s ease"
1128
+ }),
1129
+ header: (color) => ({
1130
+ display: "flex",
1131
+ alignItems: "center",
1132
+ gap: "8px",
1133
+ padding: "10px 12px",
1134
+ backgroundColor: color,
1135
+ borderBottom: "1px solid rgba(0, 0, 0, 0.05)"
1136
+ }),
1137
+ iconWrapper: {
1138
+ display: "flex",
1139
+ alignItems: "center",
1140
+ justifyContent: "center",
1141
+ width: "28px",
1142
+ height: "28px",
1143
+ borderRadius: "6px",
1144
+ backgroundColor: "rgba(255, 255, 255, 0.9)",
1145
+ color: "#374151"
1146
+ },
1147
+ headerContent: {
1148
+ flex: 1,
1149
+ minWidth: 0
1150
+ },
1151
+ typeLabel: {
1152
+ fontSize: "10px",
1153
+ fontWeight: 600,
1154
+ textTransform: "uppercase",
1155
+ letterSpacing: "0.05em",
1156
+ color: "rgba(0, 0, 0, 0.5)",
1157
+ marginBottom: "2px"
1158
+ },
1159
+ stepName: {
1160
+ fontSize: "13px",
1161
+ fontWeight: 600,
1162
+ color: "#1f2937",
1163
+ overflow: "hidden",
1164
+ textOverflow: "ellipsis",
1165
+ whiteSpace: "nowrap"
1166
+ },
1167
+ body: { padding: "12px" },
1168
+ handle: {
1169
+ width: "12px",
1170
+ height: "12px",
1171
+ borderRadius: "50%",
1172
+ border: "2px solid #ffffff"
1173
+ },
1174
+ targetHandle: { backgroundColor: "#6366f1" },
1175
+ sourceHandle: { backgroundColor: "#10b981" },
1176
+ deleteButton: {
1177
+ padding: "4px",
1178
+ borderRadius: "4px",
1179
+ backgroundColor: "transparent",
1180
+ border: "none",
1181
+ cursor: "pointer",
1182
+ color: "#9ca3af",
1183
+ display: "flex",
1184
+ alignItems: "center",
1185
+ justifyContent: "center",
1186
+ transition: "color 0.15s ease, background-color 0.15s ease"
1187
+ },
1188
+ enabledBadge: (enabled) => ({
1189
+ display: "inline-flex",
1190
+ alignItems: "center",
1191
+ padding: "2px 6px",
1192
+ borderRadius: "4px",
1193
+ fontSize: "10px",
1194
+ fontWeight: 500,
1195
+ backgroundColor: enabled ? "rgba(16, 185, 129, 0.1)" : "rgba(239, 68, 68, 0.1)",
1196
+ color: enabled ? "#059669" : "#dc2626"
1197
+ })
1198
+ };
1199
+ const NODE_HEADER_COLORS = {
1200
+ prompt: "#f3e8ff",
1201
+ "fetch-url": "#dbeafe",
1202
+ "transform-data": "#fef3c7",
1203
+ conditional: "#fce7f3",
1204
+ "send-email": "#d1fae5",
1205
+ default: "#f3f4f6"
1206
+ };
1207
+ const BaseNode = memo(function BaseNode({ data, selected = false, id, typeLabel, icon, headerColor, showSourceHandle = true, showTargetHandle = true, additionalSourceHandles, children }) {
1208
+ const { step, onDelete } = data;
1209
+ const color = headerColor || NODE_HEADER_COLORS[step.type] || NODE_HEADER_COLORS.default;
1210
+ const handleDelete = (e) => {
1211
+ e.stopPropagation();
1212
+ onDelete?.(id);
1213
+ };
1214
+ return /* @__PURE__ */ jsxs("div", {
1215
+ style: styles$6.container(selected),
1216
+ children: [
1217
+ showTargetHandle && /* @__PURE__ */ jsx(Handle, {
1218
+ type: "target",
1219
+ position: Position.Left,
1220
+ style: {
1221
+ ...styles$6.handle,
1222
+ ...styles$6.targetHandle
1223
+ }
1224
+ }),
1225
+ /* @__PURE__ */ jsxs("div", {
1226
+ style: styles$6.header(color),
1227
+ children: [
1228
+ /* @__PURE__ */ jsx("div", {
1229
+ style: styles$6.iconWrapper,
1230
+ children: icon
1231
+ }),
1232
+ /* @__PURE__ */ jsxs("div", {
1233
+ style: styles$6.headerContent,
1234
+ children: [/* @__PURE__ */ jsx("div", {
1235
+ style: styles$6.typeLabel,
1236
+ children: typeLabel
1237
+ }), /* @__PURE__ */ jsx("div", {
1238
+ style: styles$6.stepName,
1239
+ title: step.name,
1240
+ children: step.name || "Untitled Step"
1241
+ })]
1242
+ }),
1243
+ /* @__PURE__ */ jsxs("div", {
1244
+ style: {
1245
+ display: "flex",
1246
+ alignItems: "center",
1247
+ gap: "4px"
1248
+ },
1249
+ children: [/* @__PURE__ */ jsx("span", {
1250
+ style: styles$6.enabledBadge(step.enabled),
1251
+ children: step.enabled ? "Active" : "Disabled"
1252
+ }), onDelete && /* @__PURE__ */ jsx("button", {
1253
+ style: styles$6.deleteButton,
1254
+ onClick: handleDelete,
1255
+ title: "Delete step",
1256
+ onMouseEnter: (e) => {
1257
+ e.currentTarget.style.backgroundColor = "rgba(239, 68, 68, 0.1)";
1258
+ e.currentTarget.style.color = "#dc2626";
1259
+ },
1260
+ onMouseLeave: (e) => {
1261
+ e.currentTarget.style.backgroundColor = "transparent";
1262
+ e.currentTarget.style.color = "#9ca3af";
1263
+ },
1264
+ children: /* @__PURE__ */ jsx(DeleteIcon, {})
1265
+ })]
1266
+ })
1267
+ ]
1268
+ }),
1269
+ /* @__PURE__ */ jsx("div", {
1270
+ style: styles$6.body,
1271
+ className: "nodrag",
1272
+ children
1273
+ }),
1274
+ showSourceHandle && /* @__PURE__ */ jsx(Handle, {
1275
+ type: "source",
1276
+ position: Position.Right,
1277
+ id: "output",
1278
+ style: {
1279
+ ...styles$6.handle,
1280
+ ...styles$6.sourceHandle
1281
+ }
1282
+ }),
1283
+ additionalSourceHandles?.map((handle) => /* @__PURE__ */ jsx(Handle, {
1284
+ type: "source",
1285
+ position: handle.position,
1286
+ id: handle.id,
1287
+ style: {
1288
+ ...styles$6.handle,
1289
+ backgroundColor: handle.color || "#10b981",
1290
+ ...handle.style
1291
+ }
1292
+ }, handle.id))
1293
+ ]
1294
+ });
1295
+ });
1296
+ const DeleteIcon = () => /* @__PURE__ */ jsxs("svg", {
1297
+ width: "14",
1298
+ height: "14",
1299
+ viewBox: "0 0 24 24",
1300
+ fill: "none",
1301
+ stroke: "currentColor",
1302
+ strokeWidth: "2",
1303
+ strokeLinecap: "round",
1304
+ strokeLinejoin: "round",
1305
+ children: [
1306
+ /* @__PURE__ */ jsx("path", { d: "M3 6h18" }),
1307
+ /* @__PURE__ */ jsx("path", { d: "M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6" }),
1308
+ /* @__PURE__ */ jsx("path", { d: "M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2" })
1309
+ ]
1310
+ });
1311
+ const BrainIcon = () => /* @__PURE__ */ jsxs("svg", {
1312
+ width: "16",
1313
+ height: "16",
1314
+ viewBox: "0 0 24 24",
1315
+ fill: "none",
1316
+ stroke: "currentColor",
1317
+ strokeWidth: "2",
1318
+ strokeLinecap: "round",
1319
+ strokeLinejoin: "round",
1320
+ children: [
1321
+ /* @__PURE__ */ jsx("path", { d: "M12 5a3 3 0 1 0-5.997.125 4 4 0 0 0-2.526 5.77 4 4 0 0 0 .556 6.588A4 4 0 1 0 12 18Z" }),
1322
+ /* @__PURE__ */ jsx("path", { d: "M12 5a3 3 0 1 1 5.997.125 4 4 0 0 1 2.526 5.77 4 4 0 0 1-.556 6.588A4 4 0 1 1 12 18Z" }),
1323
+ /* @__PURE__ */ jsx("path", { d: "M15 13a4.5 4.5 0 0 1-3-4 4.5 4.5 0 0 1-3 4" }),
1324
+ /* @__PURE__ */ jsx("path", { d: "M17.599 6.5a3 3 0 0 0 .399-1.375" }),
1325
+ /* @__PURE__ */ jsx("path", { d: "M6.003 5.125A3 3 0 0 0 6.401 6.5" }),
1326
+ /* @__PURE__ */ jsx("path", { d: "M3.477 10.896a4 4 0 0 1 .585-.396" }),
1327
+ /* @__PURE__ */ jsx("path", { d: "M19.938 10.5a4 4 0 0 1 .585.396" }),
1328
+ /* @__PURE__ */ jsx("path", { d: "M6 18a4 4 0 0 1-1.967-.516" }),
1329
+ /* @__PURE__ */ jsx("path", { d: "M19.967 17.484A4 4 0 0 1 18 18" })
1330
+ ]
1331
+ });
1332
+ const GlobeIcon$1 = () => /* @__PURE__ */ jsxs("svg", {
1333
+ width: "16",
1334
+ height: "16",
1335
+ viewBox: "0 0 24 24",
1336
+ fill: "none",
1337
+ stroke: "currentColor",
1338
+ strokeWidth: "2",
1339
+ strokeLinecap: "round",
1340
+ strokeLinejoin: "round",
1341
+ children: [
1342
+ /* @__PURE__ */ jsx("circle", {
1343
+ cx: "12",
1344
+ cy: "12",
1345
+ r: "10"
1346
+ }),
1347
+ /* @__PURE__ */ jsx("path", { d: "M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20" }),
1348
+ /* @__PURE__ */ jsx("path", { d: "M2 12h20" })
1349
+ ]
1350
+ });
1351
+ const CodeIcon$1 = () => /* @__PURE__ */ jsxs("svg", {
1352
+ width: "16",
1353
+ height: "16",
1354
+ viewBox: "0 0 24 24",
1355
+ fill: "none",
1356
+ stroke: "currentColor",
1357
+ strokeWidth: "2",
1358
+ strokeLinecap: "round",
1359
+ strokeLinejoin: "round",
1360
+ children: [/* @__PURE__ */ jsx("polyline", { points: "16 18 22 12 16 6" }), /* @__PURE__ */ jsx("polyline", { points: "8 6 2 12 8 18" })]
1361
+ });
1362
+ const GitBranchIcon = () => /* @__PURE__ */ jsxs("svg", {
1363
+ width: "16",
1364
+ height: "16",
1365
+ viewBox: "0 0 24 24",
1366
+ fill: "none",
1367
+ stroke: "currentColor",
1368
+ strokeWidth: "2",
1369
+ strokeLinecap: "round",
1370
+ strokeLinejoin: "round",
1371
+ children: [
1372
+ /* @__PURE__ */ jsx("line", {
1373
+ x1: "6",
1374
+ y1: "3",
1375
+ x2: "6",
1376
+ y2: "15"
1377
+ }),
1378
+ /* @__PURE__ */ jsx("circle", {
1379
+ cx: "18",
1380
+ cy: "6",
1381
+ r: "3"
1382
+ }),
1383
+ /* @__PURE__ */ jsx("circle", {
1384
+ cx: "6",
1385
+ cy: "18",
1386
+ r: "3"
1387
+ }),
1388
+ /* @__PURE__ */ jsx("path", { d: "M18 9a9 9 0 0 1-9 9" })
1389
+ ]
1390
+ });
1391
+ const MailIcon$1 = () => /* @__PURE__ */ jsxs("svg", {
1392
+ width: "16",
1393
+ height: "16",
1394
+ viewBox: "0 0 24 24",
1395
+ fill: "none",
1396
+ stroke: "currentColor",
1397
+ strokeWidth: "2",
1398
+ strokeLinecap: "round",
1399
+ strokeLinejoin: "round",
1400
+ children: [/* @__PURE__ */ jsx("rect", {
1401
+ width: "20",
1402
+ height: "16",
1403
+ x: "2",
1404
+ y: "4",
1405
+ rx: "2"
1406
+ }), /* @__PURE__ */ jsx("path", { d: "m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7" })]
1407
+ });
1408
+ //#endregion
1409
+ //#region src/components/nodes/PromptNode.tsx
1410
+ const styles$5 = {
1411
+ field: { marginBottom: "12px" },
1412
+ label: {
1413
+ display: "block",
1414
+ fontSize: "11px",
1415
+ fontWeight: 600,
1416
+ color: "#6b7280",
1417
+ marginBottom: "4px",
1418
+ textTransform: "uppercase",
1419
+ letterSpacing: "0.03em"
1420
+ },
1421
+ input: {
1422
+ width: "100%",
1423
+ padding: "8px 10px",
1424
+ fontSize: "12px",
1425
+ border: "1px solid #e5e7eb",
1426
+ borderRadius: "6px",
1427
+ backgroundColor: "#f9fafb",
1428
+ color: "#1f2937",
1429
+ outline: "none",
1430
+ transition: "border-color 0.15s ease, box-shadow 0.15s ease"
1431
+ },
1432
+ textarea: {
1433
+ width: "100%",
1434
+ padding: "8px 10px",
1435
+ fontSize: "12px",
1436
+ border: "1px solid #e5e7eb",
1437
+ borderRadius: "6px",
1438
+ backgroundColor: "#f9fafb",
1439
+ color: "#1f2937",
1440
+ outline: "none",
1441
+ resize: "vertical",
1442
+ minHeight: "60px",
1443
+ fontFamily: "inherit",
1444
+ transition: "border-color 0.15s ease, box-shadow 0.15s ease"
1445
+ },
1446
+ select: {
1447
+ width: "100%",
1448
+ padding: "8px 10px",
1449
+ fontSize: "12px",
1450
+ border: "1px solid #e5e7eb",
1451
+ borderRadius: "6px",
1452
+ backgroundColor: "#f9fafb",
1453
+ color: "#1f2937",
1454
+ outline: "none",
1455
+ cursor: "pointer"
1456
+ },
1457
+ row: {
1458
+ display: "flex",
1459
+ gap: "8px"
1460
+ },
1461
+ modeBadge: (isAgent) => ({
1462
+ display: "inline-flex",
1463
+ alignItems: "center",
1464
+ padding: "2px 8px",
1465
+ borderRadius: "12px",
1466
+ fontSize: "10px",
1467
+ fontWeight: 600,
1468
+ backgroundColor: isAgent ? "#dbeafe" : "#f3e8ff",
1469
+ color: isAgent ? "#1d4ed8" : "#7c3aed",
1470
+ textTransform: "uppercase",
1471
+ letterSpacing: "0.05em"
1472
+ }),
1473
+ preview: {
1474
+ fontSize: "11px",
1475
+ color: "#6b7280",
1476
+ backgroundColor: "#f3f4f6",
1477
+ padding: "8px",
1478
+ borderRadius: "6px",
1479
+ fontFamily: "monospace",
1480
+ whiteSpace: "pre-wrap",
1481
+ wordBreak: "break-word",
1482
+ maxHeight: "80px",
1483
+ overflow: "auto"
1484
+ }
1485
+ };
1486
+ const PromptNode = memo(function PromptNode(props) {
1487
+ const { data, selected, id } = props;
1488
+ const { step, onChange } = data;
1489
+ const config = step.config;
1490
+ const [isExpanded, setIsExpanded] = useState(false);
1491
+ const handleChange = useCallback((field, value) => {
1492
+ onChange?.(id, { config: {
1493
+ ...config,
1494
+ [field]: value
1495
+ } });
1496
+ }, [
1497
+ id,
1498
+ config,
1499
+ onChange
1500
+ ]);
1501
+ const handleNameChange = useCallback((e) => {
1502
+ onChange?.(id, { name: e.target.value });
1503
+ }, [id, onChange]);
1504
+ const isAgentMode = config.mode === "agent";
1505
+ return /* @__PURE__ */ jsxs(BaseNode, {
1506
+ data,
1507
+ selected,
1508
+ id,
1509
+ typeLabel: "AI Prompt",
1510
+ icon: /* @__PURE__ */ jsx(BrainIcon, {}),
1511
+ headerColor: NODE_HEADER_COLORS.prompt,
1512
+ children: [
1513
+ /* @__PURE__ */ jsxs("div", {
1514
+ style: styles$5.field,
1515
+ children: [/* @__PURE__ */ jsx("label", {
1516
+ style: styles$5.label,
1517
+ children: "Step Name"
1518
+ }), /* @__PURE__ */ jsx("input", {
1519
+ style: styles$5.input,
1520
+ value: step.name,
1521
+ onChange: handleNameChange,
1522
+ placeholder: "Enter step name",
1523
+ onKeyDown: (e) => e.stopPropagation(),
1524
+ onFocus: (e) => {
1525
+ e.target.style.borderColor = "#6366f1";
1526
+ e.target.style.boxShadow = "0 0 0 2px rgba(99, 102, 241, 0.1)";
1527
+ },
1528
+ onBlur: (e) => {
1529
+ e.target.style.borderColor = "#e5e7eb";
1530
+ e.target.style.boxShadow = "none";
1531
+ }
1532
+ })]
1533
+ }),
1534
+ /* @__PURE__ */ jsxs("div", {
1535
+ style: styles$5.field,
1536
+ children: [/* @__PURE__ */ jsxs("div", {
1537
+ style: {
1538
+ display: "flex",
1539
+ justifyContent: "space-between",
1540
+ alignItems: "center",
1541
+ marginBottom: "4px"
1542
+ },
1543
+ children: [/* @__PURE__ */ jsx("label", {
1544
+ style: styles$5.label,
1545
+ children: "Mode"
1546
+ }), /* @__PURE__ */ jsx("span", {
1547
+ style: styles$5.modeBadge(isAgentMode),
1548
+ children: isAgentMode ? "Agent" : "Instruction"
1549
+ })]
1550
+ }), /* @__PURE__ */ jsxs("select", {
1551
+ style: styles$5.select,
1552
+ value: config.mode || "instruction",
1553
+ onChange: (e) => handleChange("mode", e.target.value),
1554
+ onKeyDown: (e) => e.stopPropagation(),
1555
+ children: [/* @__PURE__ */ jsx("option", {
1556
+ value: "instruction",
1557
+ children: "Instruction Mode"
1558
+ }), /* @__PURE__ */ jsx("option", {
1559
+ value: "agent",
1560
+ children: "Agent Mode"
1561
+ })]
1562
+ })]
1563
+ }),
1564
+ /* @__PURE__ */ jsxs("div", {
1565
+ style: styles$5.field,
1566
+ children: [/* @__PURE__ */ jsx("label", {
1567
+ style: styles$5.label,
1568
+ children: "Model"
1569
+ }), /* @__PURE__ */ jsx("input", {
1570
+ style: styles$5.input,
1571
+ value: config.model || "",
1572
+ onChange: (e) => handleChange("model", e.target.value),
1573
+ placeholder: "e.g., gpt-4, claude-3-sonnet",
1574
+ onKeyDown: (e) => e.stopPropagation()
1575
+ })]
1576
+ }),
1577
+ /* @__PURE__ */ jsxs("div", {
1578
+ style: styles$5.field,
1579
+ children: [/* @__PURE__ */ jsx("label", {
1580
+ style: styles$5.label,
1581
+ children: "User Prompt"
1582
+ }), /* @__PURE__ */ jsx("textarea", {
1583
+ style: styles$5.textarea,
1584
+ value: config.userPrompt || "",
1585
+ onChange: (e) => handleChange("userPrompt", e.target.value),
1586
+ placeholder: "Enter your prompt...",
1587
+ rows: 3,
1588
+ onKeyDown: (e) => e.stopPropagation()
1589
+ })]
1590
+ }),
1591
+ isExpanded && /* @__PURE__ */ jsxs(Fragment, { children: [
1592
+ config.systemPrompt !== void 0 && /* @__PURE__ */ jsxs("div", {
1593
+ style: styles$5.field,
1594
+ children: [/* @__PURE__ */ jsx("label", {
1595
+ style: styles$5.label,
1596
+ children: "System Prompt"
1597
+ }), /* @__PURE__ */ jsx("textarea", {
1598
+ style: styles$5.textarea,
1599
+ value: config.systemPrompt || "",
1600
+ onChange: (e) => handleChange("systemPrompt", e.target.value),
1601
+ placeholder: "System instructions...",
1602
+ rows: 2,
1603
+ onKeyDown: (e) => e.stopPropagation()
1604
+ })]
1605
+ }),
1606
+ /* @__PURE__ */ jsxs("div", {
1607
+ style: styles$5.row,
1608
+ children: [/* @__PURE__ */ jsxs("div", {
1609
+ style: {
1610
+ ...styles$5.field,
1611
+ flex: 1
1612
+ },
1613
+ children: [/* @__PURE__ */ jsx("label", {
1614
+ style: styles$5.label,
1615
+ children: "Response Format"
1616
+ }), /* @__PURE__ */ jsxs("select", {
1617
+ style: styles$5.select,
1618
+ value: config.responseFormat || "text",
1619
+ onChange: (e) => handleChange("responseFormat", e.target.value),
1620
+ onKeyDown: (e) => e.stopPropagation(),
1621
+ children: [
1622
+ /* @__PURE__ */ jsx("option", {
1623
+ value: "text",
1624
+ children: "Text"
1625
+ }),
1626
+ /* @__PURE__ */ jsx("option", {
1627
+ value: "json",
1628
+ children: "JSON"
1629
+ }),
1630
+ /* @__PURE__ */ jsx("option", {
1631
+ value: "markdown",
1632
+ children: "Markdown"
1633
+ }),
1634
+ /* @__PURE__ */ jsx("option", {
1635
+ value: "html",
1636
+ children: "HTML"
1637
+ })
1638
+ ]
1639
+ })]
1640
+ }), /* @__PURE__ */ jsxs("div", {
1641
+ style: {
1642
+ ...styles$5.field,
1643
+ flex: 1
1644
+ },
1645
+ children: [/* @__PURE__ */ jsx("label", {
1646
+ style: styles$5.label,
1647
+ children: "Temperature"
1648
+ }), /* @__PURE__ */ jsx("input", {
1649
+ style: styles$5.input,
1650
+ type: "number",
1651
+ min: "0",
1652
+ max: "2",
1653
+ step: "0.1",
1654
+ value: config.temperature ?? .7,
1655
+ onChange: (e) => handleChange("temperature", parseFloat(e.target.value)),
1656
+ onKeyDown: (e) => e.stopPropagation()
1657
+ })]
1658
+ })]
1659
+ }),
1660
+ /* @__PURE__ */ jsxs("div", {
1661
+ style: styles$5.field,
1662
+ children: [/* @__PURE__ */ jsx("label", {
1663
+ style: styles$5.label,
1664
+ children: "Output Variable"
1665
+ }), /* @__PURE__ */ jsx("input", {
1666
+ style: styles$5.input,
1667
+ value: config.outputVariable || "",
1668
+ onChange: (e) => handleChange("outputVariable", e.target.value),
1669
+ placeholder: "result",
1670
+ onKeyDown: (e) => e.stopPropagation()
1671
+ })]
1672
+ })
1673
+ ] }),
1674
+ /* @__PURE__ */ jsx("button", {
1675
+ onClick: () => setIsExpanded(!isExpanded),
1676
+ style: {
1677
+ width: "100%",
1678
+ padding: "6px",
1679
+ fontSize: "11px",
1680
+ color: "#6366f1",
1681
+ backgroundColor: "transparent",
1682
+ border: "1px dashed #e5e7eb",
1683
+ borderRadius: "6px",
1684
+ cursor: "pointer",
1685
+ marginTop: "4px"
1686
+ },
1687
+ children: isExpanded ? "Show Less" : "Show More Options"
1688
+ })
1689
+ ]
1690
+ });
1691
+ });
1692
+ //#endregion
1693
+ //#region src/components/nodes/FetchUrlNode.tsx
1694
+ const styles$4 = {
1695
+ field: { marginBottom: "12px" },
1696
+ label: {
1697
+ display: "block",
1698
+ fontSize: "11px",
1699
+ fontWeight: 600,
1700
+ color: "#6b7280",
1701
+ marginBottom: "4px",
1702
+ textTransform: "uppercase",
1703
+ letterSpacing: "0.03em"
1704
+ },
1705
+ input: {
1706
+ width: "100%",
1707
+ padding: "8px 10px",
1708
+ fontSize: "12px",
1709
+ border: "1px solid #e5e7eb",
1710
+ borderRadius: "6px",
1711
+ backgroundColor: "#f9fafb",
1712
+ color: "#1f2937",
1713
+ outline: "none",
1714
+ transition: "border-color 0.15s ease, box-shadow 0.15s ease"
1715
+ },
1716
+ textarea: {
1717
+ width: "100%",
1718
+ padding: "8px 10px",
1719
+ fontSize: "12px",
1720
+ border: "1px solid #e5e7eb",
1721
+ borderRadius: "6px",
1722
+ backgroundColor: "#f9fafb",
1723
+ color: "#1f2937",
1724
+ outline: "none",
1725
+ resize: "vertical",
1726
+ minHeight: "60px",
1727
+ fontFamily: "monospace",
1728
+ transition: "border-color 0.15s ease, box-shadow 0.15s ease"
1729
+ },
1730
+ select: {
1731
+ width: "100%",
1732
+ padding: "8px 10px",
1733
+ fontSize: "12px",
1734
+ border: "1px solid #e5e7eb",
1735
+ borderRadius: "6px",
1736
+ backgroundColor: "#f9fafb",
1737
+ color: "#1f2937",
1738
+ outline: "none",
1739
+ cursor: "pointer"
1740
+ },
1741
+ row: {
1742
+ display: "flex",
1743
+ gap: "8px"
1744
+ },
1745
+ methodBadge: (method) => {
1746
+ const colors = {
1747
+ GET: {
1748
+ bg: "#d1fae5",
1749
+ text: "#059669"
1750
+ },
1751
+ POST: {
1752
+ bg: "#dbeafe",
1753
+ text: "#1d4ed8"
1754
+ },
1755
+ PUT: {
1756
+ bg: "#fef3c7",
1757
+ text: "#d97706"
1758
+ },
1759
+ DELETE: {
1760
+ bg: "#fee2e2",
1761
+ text: "#dc2626"
1762
+ },
1763
+ PATCH: {
1764
+ bg: "#e0e7ff",
1765
+ text: "#4f46e5"
1766
+ }
1767
+ };
1768
+ const color = colors[method] || colors.GET;
1769
+ return {
1770
+ display: "inline-flex",
1771
+ alignItems: "center",
1772
+ padding: "2px 8px",
1773
+ borderRadius: "4px",
1774
+ fontSize: "10px",
1775
+ fontWeight: 700,
1776
+ backgroundColor: color.bg,
1777
+ color: color.text,
1778
+ fontFamily: "monospace"
1779
+ };
1780
+ },
1781
+ urlPreview: {
1782
+ fontSize: "11px",
1783
+ color: "#6b7280",
1784
+ backgroundColor: "#f3f4f6",
1785
+ padding: "6px 8px",
1786
+ borderRadius: "4px",
1787
+ fontFamily: "monospace",
1788
+ wordBreak: "break-all",
1789
+ marginTop: "4px"
1790
+ }
1791
+ };
1792
+ const FetchUrlNode = memo(function FetchUrlNode(props) {
1793
+ const { data, selected, id } = props;
1794
+ const { step, onChange } = data;
1795
+ const config = step.config;
1796
+ const [isExpanded, setIsExpanded] = useState(false);
1797
+ const handleChange = useCallback((field, value) => {
1798
+ if (field.startsWith("http.")) {
1799
+ const httpField = field.replace("http.", "");
1800
+ onChange?.(id, { config: {
1801
+ ...config,
1802
+ http: {
1803
+ ...config.http,
1804
+ [httpField]: value
1805
+ }
1806
+ } });
1807
+ } else onChange?.(id, { config: {
1808
+ ...config,
1809
+ [field]: value
1810
+ } });
1811
+ }, [
1812
+ id,
1813
+ config,
1814
+ onChange
1815
+ ]);
1816
+ const handleNameChange = useCallback((e) => {
1817
+ onChange?.(id, { name: e.target.value });
1818
+ }, [id, onChange]);
1819
+ const method = config.http?.method || "GET";
1820
+ return /* @__PURE__ */ jsxs(BaseNode, {
1821
+ data,
1822
+ selected,
1823
+ id,
1824
+ typeLabel: "Fetch URL",
1825
+ icon: /* @__PURE__ */ jsx(GlobeIcon$1, {}),
1826
+ headerColor: NODE_HEADER_COLORS["fetch-url"],
1827
+ children: [
1828
+ /* @__PURE__ */ jsxs("div", {
1829
+ style: styles$4.field,
1830
+ children: [/* @__PURE__ */ jsx("label", {
1831
+ style: styles$4.label,
1832
+ children: "Step Name"
1833
+ }), /* @__PURE__ */ jsx("input", {
1834
+ style: styles$4.input,
1835
+ value: step.name,
1836
+ onChange: handleNameChange,
1837
+ placeholder: "Enter step name",
1838
+ onKeyDown: (e) => {
1839
+ e.stopPropagation();
1840
+ }
1841
+ })]
1842
+ }),
1843
+ /* @__PURE__ */ jsxs("div", {
1844
+ style: styles$4.field,
1845
+ children: [/* @__PURE__ */ jsxs("div", {
1846
+ style: {
1847
+ display: "flex",
1848
+ justifyContent: "space-between",
1849
+ alignItems: "center",
1850
+ marginBottom: "4px"
1851
+ },
1852
+ children: [/* @__PURE__ */ jsx("label", {
1853
+ style: styles$4.label,
1854
+ children: "Method"
1855
+ }), /* @__PURE__ */ jsx("span", {
1856
+ style: styles$4.methodBadge(method),
1857
+ children: method
1858
+ })]
1859
+ }), /* @__PURE__ */ jsxs("select", {
1860
+ style: styles$4.select,
1861
+ value: method,
1862
+ onChange: (e) => handleChange("http.method", e.target.value),
1863
+ onKeyDown: (e) => e.stopPropagation(),
1864
+ children: [
1865
+ /* @__PURE__ */ jsx("option", {
1866
+ value: "GET",
1867
+ children: "GET"
1868
+ }),
1869
+ /* @__PURE__ */ jsx("option", {
1870
+ value: "POST",
1871
+ children: "POST"
1872
+ }),
1873
+ /* @__PURE__ */ jsx("option", {
1874
+ value: "PUT",
1875
+ children: "PUT"
1876
+ }),
1877
+ /* @__PURE__ */ jsx("option", {
1878
+ value: "DELETE",
1879
+ children: "DELETE"
1880
+ }),
1881
+ /* @__PURE__ */ jsx("option", {
1882
+ value: "PATCH",
1883
+ children: "PATCH"
1884
+ })
1885
+ ]
1886
+ })]
1887
+ }),
1888
+ /* @__PURE__ */ jsxs("div", {
1889
+ style: styles$4.field,
1890
+ children: [
1891
+ /* @__PURE__ */ jsx("label", {
1892
+ style: styles$4.label,
1893
+ children: "URL"
1894
+ }),
1895
+ /* @__PURE__ */ jsx("input", {
1896
+ style: styles$4.input,
1897
+ value: config.http?.url || "",
1898
+ onChange: (e) => handleChange("http.url", e.target.value),
1899
+ placeholder: "https://api.example.com/endpoint",
1900
+ onKeyDown: (e) => e.stopPropagation()
1901
+ }),
1902
+ config.http?.url && /* @__PURE__ */ jsx("div", {
1903
+ style: styles$4.urlPreview,
1904
+ children: config.http.url
1905
+ })
1906
+ ]
1907
+ }),
1908
+ isExpanded && /* @__PURE__ */ jsxs(Fragment, { children: [
1909
+ (method === "POST" || method === "PUT" || method === "PATCH") && /* @__PURE__ */ jsxs("div", {
1910
+ style: styles$4.field,
1911
+ children: [/* @__PURE__ */ jsx("label", {
1912
+ style: styles$4.label,
1913
+ children: "Request Body"
1914
+ }), /* @__PURE__ */ jsx("textarea", {
1915
+ style: styles$4.textarea,
1916
+ value: config.http?.body || "",
1917
+ onChange: (e) => handleChange("http.body", e.target.value),
1918
+ placeholder: "{\"key\": \"value\"}",
1919
+ rows: 3,
1920
+ onKeyDown: (e) => e.stopPropagation()
1921
+ })]
1922
+ }),
1923
+ /* @__PURE__ */ jsxs("div", {
1924
+ style: styles$4.field,
1925
+ children: [/* @__PURE__ */ jsx("label", {
1926
+ style: styles$4.label,
1927
+ children: "Headers (JSON)"
1928
+ }), /* @__PURE__ */ jsx("textarea", {
1929
+ style: styles$4.textarea,
1930
+ value: config.http?.headers ? JSON.stringify(config.http.headers, null, 2) : "",
1931
+ onChange: (e) => {
1932
+ try {
1933
+ const headers = JSON.parse(e.target.value);
1934
+ handleChange("http.headers", headers);
1935
+ } catch {}
1936
+ },
1937
+ placeholder: "{\"Content-Type\": \"application/json\"}",
1938
+ rows: 2,
1939
+ onKeyDown: (e) => e.stopPropagation()
1940
+ })]
1941
+ }),
1942
+ /* @__PURE__ */ jsxs("div", {
1943
+ style: styles$4.row,
1944
+ children: [/* @__PURE__ */ jsxs("div", {
1945
+ style: {
1946
+ ...styles$4.field,
1947
+ flex: 1
1948
+ },
1949
+ children: [/* @__PURE__ */ jsx("label", {
1950
+ style: styles$4.label,
1951
+ children: "Response Type"
1952
+ }), /* @__PURE__ */ jsxs("select", {
1953
+ style: styles$4.select,
1954
+ value: config.responseType || "json",
1955
+ onChange: (e) => handleChange("responseType", e.target.value),
1956
+ onKeyDown: (e) => e.stopPropagation(),
1957
+ children: [
1958
+ /* @__PURE__ */ jsx("option", {
1959
+ value: "json",
1960
+ children: "JSON"
1961
+ }),
1962
+ /* @__PURE__ */ jsx("option", {
1963
+ value: "text",
1964
+ children: "Text"
1965
+ }),
1966
+ /* @__PURE__ */ jsx("option", {
1967
+ value: "xml",
1968
+ children: "XML"
1969
+ })
1970
+ ]
1971
+ })]
1972
+ }), /* @__PURE__ */ jsxs("div", {
1973
+ style: {
1974
+ ...styles$4.field,
1975
+ flex: 1
1976
+ },
1977
+ children: [/* @__PURE__ */ jsx("label", {
1978
+ style: styles$4.label,
1979
+ children: "On Error"
1980
+ }), /* @__PURE__ */ jsxs("select", {
1981
+ style: styles$4.select,
1982
+ value: config.errorHandling || "fail",
1983
+ onChange: (e) => handleChange("errorHandling", e.target.value),
1984
+ onKeyDown: (e) => e.stopPropagation(),
1985
+ children: [
1986
+ /* @__PURE__ */ jsx("option", {
1987
+ value: "fail",
1988
+ children: "Fail"
1989
+ }),
1990
+ /* @__PURE__ */ jsx("option", {
1991
+ value: "continue",
1992
+ children: "Continue"
1993
+ }),
1994
+ /* @__PURE__ */ jsx("option", {
1995
+ value: "default",
1996
+ children: "Use Default"
1997
+ })
1998
+ ]
1999
+ })]
2000
+ })]
2001
+ }),
2002
+ /* @__PURE__ */ jsxs("div", {
2003
+ style: styles$4.field,
2004
+ children: [/* @__PURE__ */ jsx("label", {
2005
+ style: styles$4.label,
2006
+ children: "Output Variable"
2007
+ }), /* @__PURE__ */ jsx("input", {
2008
+ style: styles$4.input,
2009
+ value: config.outputVariable || "",
2010
+ onChange: (e) => handleChange("outputVariable", e.target.value),
2011
+ placeholder: "api_response",
2012
+ onKeyDown: (e) => e.stopPropagation()
2013
+ })]
2014
+ })
2015
+ ] }),
2016
+ /* @__PURE__ */ jsx("button", {
2017
+ onClick: () => setIsExpanded(!isExpanded),
2018
+ style: {
2019
+ width: "100%",
2020
+ padding: "6px",
2021
+ fontSize: "11px",
2022
+ color: "#6366f1",
2023
+ backgroundColor: "transparent",
2024
+ border: "1px dashed #e5e7eb",
2025
+ borderRadius: "6px",
2026
+ cursor: "pointer",
2027
+ marginTop: "4px"
2028
+ },
2029
+ children: isExpanded ? "Show Less" : "Show More Options"
2030
+ })
2031
+ ]
2032
+ });
2033
+ });
2034
+ //#endregion
2035
+ //#region src/components/nodes/CodeNode.tsx
2036
+ const styles$3 = {
2037
+ field: { marginBottom: "12px" },
2038
+ label: {
2039
+ display: "block",
2040
+ fontSize: "11px",
2041
+ fontWeight: 600,
2042
+ color: "#6b7280",
2043
+ marginBottom: "4px",
2044
+ textTransform: "uppercase",
2045
+ letterSpacing: "0.03em"
2046
+ },
2047
+ input: {
2048
+ width: "100%",
2049
+ padding: "8px 10px",
2050
+ fontSize: "12px",
2051
+ border: "1px solid #e5e7eb",
2052
+ borderRadius: "6px",
2053
+ backgroundColor: "#f9fafb",
2054
+ color: "#1f2937",
2055
+ outline: "none",
2056
+ transition: "border-color 0.15s ease, box-shadow 0.15s ease"
2057
+ },
2058
+ codeArea: {
2059
+ width: "100%",
2060
+ padding: "10px",
2061
+ fontSize: "11px",
2062
+ border: "1px solid #e5e7eb",
2063
+ borderRadius: "6px",
2064
+ backgroundColor: "#1e1e1e",
2065
+ color: "#d4d4d4",
2066
+ outline: "none",
2067
+ resize: "vertical",
2068
+ minHeight: "100px",
2069
+ fontFamily: "\"Fira Code\", \"Monaco\", \"Consolas\", monospace",
2070
+ lineHeight: 1.5,
2071
+ tabSize: 2
2072
+ },
2073
+ select: {
2074
+ width: "100%",
2075
+ padding: "8px 10px",
2076
+ fontSize: "12px",
2077
+ border: "1px solid #e5e7eb",
2078
+ borderRadius: "6px",
2079
+ backgroundColor: "#f9fafb",
2080
+ color: "#1f2937",
2081
+ outline: "none",
2082
+ cursor: "pointer"
2083
+ },
2084
+ row: {
2085
+ display: "flex",
2086
+ gap: "8px"
2087
+ },
2088
+ languageBadge: (language) => {
2089
+ const colors = {
2090
+ javascript: {
2091
+ bg: "#fef3c7",
2092
+ text: "#d97706"
2093
+ },
2094
+ typescript: {
2095
+ bg: "#dbeafe",
2096
+ text: "#1d4ed8"
2097
+ },
2098
+ python: {
2099
+ bg: "#d1fae5",
2100
+ text: "#059669"
2101
+ }
2102
+ };
2103
+ const color = colors[language] || colors.javascript;
2104
+ return {
2105
+ display: "inline-flex",
2106
+ alignItems: "center",
2107
+ padding: "2px 8px",
2108
+ borderRadius: "4px",
2109
+ fontSize: "10px",
2110
+ fontWeight: 600,
2111
+ backgroundColor: color.bg,
2112
+ color: color.text,
2113
+ textTransform: "capitalize"
2114
+ };
2115
+ },
2116
+ lineNumbers: {
2117
+ display: "flex",
2118
+ flexDirection: "column",
2119
+ alignItems: "flex-end",
2120
+ paddingRight: "8px",
2121
+ marginRight: "8px",
2122
+ borderRight: "1px solid #3f3f46",
2123
+ color: "#6b7280",
2124
+ fontSize: "11px",
2125
+ fontFamily: "\"Fira Code\", \"Monaco\", \"Consolas\", monospace",
2126
+ lineHeight: 1.5,
2127
+ userSelect: "none"
2128
+ },
2129
+ codePreview: {
2130
+ fontSize: "10px",
2131
+ color: "#9ca3af",
2132
+ marginTop: "4px"
2133
+ }
2134
+ };
2135
+ const CodeNode = memo(function CodeNode(props) {
2136
+ const { data, selected, id } = props;
2137
+ const { step, onChange } = data;
2138
+ const config = step.config;
2139
+ const [isExpanded, setIsExpanded] = useState(false);
2140
+ const handleChange = useCallback((field, value) => {
2141
+ onChange?.(id, { config: {
2142
+ ...config,
2143
+ [field]: value
2144
+ } });
2145
+ }, [
2146
+ id,
2147
+ config,
2148
+ onChange
2149
+ ]);
2150
+ const handleNameChange = useCallback((e) => {
2151
+ onChange?.(id, { name: e.target.value });
2152
+ }, [id, onChange]);
2153
+ const language = config.language || "javascript";
2154
+ const lineCount = (config.script || "").split("\n").length;
2155
+ return /* @__PURE__ */ jsxs(BaseNode, {
2156
+ data,
2157
+ selected,
2158
+ id,
2159
+ typeLabel: "Run Code",
2160
+ icon: /* @__PURE__ */ jsx(CodeIcon$1, {}),
2161
+ headerColor: NODE_HEADER_COLORS["transform-data"],
2162
+ children: [
2163
+ /* @__PURE__ */ jsxs("div", {
2164
+ style: styles$3.field,
2165
+ children: [/* @__PURE__ */ jsx("label", {
2166
+ style: styles$3.label,
2167
+ children: "Step Name"
2168
+ }), /* @__PURE__ */ jsx("input", {
2169
+ style: styles$3.input,
2170
+ value: step.name,
2171
+ onChange: handleNameChange,
2172
+ placeholder: "Enter step name",
2173
+ onKeyDown: (e) => e.stopPropagation()
2174
+ })]
2175
+ }),
2176
+ /* @__PURE__ */ jsxs("div", {
2177
+ style: styles$3.field,
2178
+ children: [
2179
+ /* @__PURE__ */ jsxs("div", {
2180
+ style: {
2181
+ display: "flex",
2182
+ justifyContent: "space-between",
2183
+ alignItems: "center",
2184
+ marginBottom: "4px"
2185
+ },
2186
+ children: [/* @__PURE__ */ jsx("label", {
2187
+ style: styles$3.label,
2188
+ children: "Code"
2189
+ }), /* @__PURE__ */ jsx("span", {
2190
+ style: styles$3.languageBadge(language),
2191
+ children: language
2192
+ })]
2193
+ }),
2194
+ /* @__PURE__ */ jsx("textarea", {
2195
+ style: styles$3.codeArea,
2196
+ value: config.script || "",
2197
+ onChange: (e) => handleChange("script", e.target.value),
2198
+ placeholder: `// Write your ${language} code here\nreturn { result: input }`,
2199
+ spellCheck: false,
2200
+ onKeyDown: (e) => e.stopPropagation()
2201
+ }),
2202
+ /* @__PURE__ */ jsxs("div", {
2203
+ style: styles$3.codePreview,
2204
+ children: [
2205
+ lineCount,
2206
+ " line",
2207
+ lineCount !== 1 ? "s" : ""
2208
+ ]
2209
+ })
2210
+ ]
2211
+ }),
2212
+ isExpanded && /* @__PURE__ */ jsxs(Fragment, { children: [
2213
+ /* @__PURE__ */ jsxs("div", {
2214
+ style: styles$3.row,
2215
+ children: [/* @__PURE__ */ jsxs("div", {
2216
+ style: {
2217
+ ...styles$3.field,
2218
+ flex: 1
2219
+ },
2220
+ children: [/* @__PURE__ */ jsx("label", {
2221
+ style: styles$3.label,
2222
+ children: "Language"
2223
+ }), /* @__PURE__ */ jsxs("select", {
2224
+ style: styles$3.select,
2225
+ value: language,
2226
+ onChange: (e) => handleChange("language", e.target.value),
2227
+ onKeyDown: (e) => e.stopPropagation(),
2228
+ children: [
2229
+ /* @__PURE__ */ jsx("option", {
2230
+ value: "javascript",
2231
+ children: "JavaScript"
2232
+ }),
2233
+ /* @__PURE__ */ jsx("option", {
2234
+ value: "typescript",
2235
+ children: "TypeScript"
2236
+ }),
2237
+ /* @__PURE__ */ jsx("option", {
2238
+ value: "python",
2239
+ children: "Python"
2240
+ })
2241
+ ]
2242
+ })]
2243
+ }), /* @__PURE__ */ jsxs("div", {
2244
+ style: {
2245
+ ...styles$3.field,
2246
+ flex: 1
2247
+ },
2248
+ children: [/* @__PURE__ */ jsx("label", {
2249
+ style: styles$3.label,
2250
+ children: "Sandbox"
2251
+ }), /* @__PURE__ */ jsxs("select", {
2252
+ style: styles$3.select,
2253
+ value: config.sandboxProvider || "quickjs",
2254
+ onChange: (e) => handleChange("sandboxProvider", e.target.value),
2255
+ onKeyDown: (e) => e.stopPropagation(),
2256
+ children: [/* @__PURE__ */ jsx("option", {
2257
+ value: "quickjs",
2258
+ children: "QuickJS (Fast)"
2259
+ }), /* @__PURE__ */ jsx("option", {
2260
+ value: "daytona",
2261
+ children: "Daytona (Full)"
2262
+ })]
2263
+ })]
2264
+ })]
2265
+ }),
2266
+ /* @__PURE__ */ jsxs("div", {
2267
+ style: styles$3.field,
2268
+ children: [/* @__PURE__ */ jsx("label", {
2269
+ style: styles$3.label,
2270
+ children: "On Error"
2271
+ }), /* @__PURE__ */ jsxs("select", {
2272
+ style: styles$3.select,
2273
+ value: config.errorHandling || "fail",
2274
+ onChange: (e) => handleChange("errorHandling", e.target.value),
2275
+ onKeyDown: (e) => e.stopPropagation(),
2276
+ children: [
2277
+ /* @__PURE__ */ jsx("option", {
2278
+ value: "fail",
2279
+ children: "Fail"
2280
+ }),
2281
+ /* @__PURE__ */ jsx("option", {
2282
+ value: "continue",
2283
+ children: "Continue"
2284
+ }),
2285
+ /* @__PURE__ */ jsx("option", {
2286
+ value: "default",
2287
+ children: "Use Default"
2288
+ })
2289
+ ]
2290
+ })]
2291
+ }),
2292
+ /* @__PURE__ */ jsxs("div", {
2293
+ style: styles$3.field,
2294
+ children: [/* @__PURE__ */ jsx("label", {
2295
+ style: styles$3.label,
2296
+ children: "Output Variable"
2297
+ }), /* @__PURE__ */ jsx("input", {
2298
+ style: styles$3.input,
2299
+ value: config.outputVariable || "",
2300
+ onChange: (e) => handleChange("outputVariable", e.target.value),
2301
+ placeholder: "transform_result",
2302
+ onKeyDown: (e) => e.stopPropagation()
2303
+ })]
2304
+ })
2305
+ ] }),
2306
+ /* @__PURE__ */ jsx("button", {
2307
+ onClick: () => setIsExpanded(!isExpanded),
2308
+ style: {
2309
+ width: "100%",
2310
+ padding: "6px",
2311
+ fontSize: "11px",
2312
+ color: "#6366f1",
2313
+ backgroundColor: "transparent",
2314
+ border: "1px dashed #e5e7eb",
2315
+ borderRadius: "6px",
2316
+ cursor: "pointer",
2317
+ marginTop: "4px"
2318
+ },
2319
+ children: isExpanded ? "Show Less" : "Show More Options"
2320
+ })
2321
+ ]
2322
+ });
2323
+ });
2324
+ //#endregion
2325
+ //#region src/components/nodes/ConditionalNode.tsx
2326
+ const styles$2 = {
2327
+ field: { marginBottom: "12px" },
2328
+ label: {
2329
+ display: "block",
2330
+ fontSize: "11px",
2331
+ fontWeight: 600,
2332
+ color: "#6b7280",
2333
+ marginBottom: "4px",
2334
+ textTransform: "uppercase",
2335
+ letterSpacing: "0.03em"
2336
+ },
2337
+ input: {
2338
+ width: "100%",
2339
+ padding: "8px 10px",
2340
+ fontSize: "12px",
2341
+ border: "1px solid #e5e7eb",
2342
+ borderRadius: "6px",
2343
+ backgroundColor: "#f9fafb",
2344
+ color: "#1f2937",
2345
+ outline: "none",
2346
+ transition: "border-color 0.15s ease, box-shadow 0.15s ease"
2347
+ },
2348
+ conditionArea: {
2349
+ width: "100%",
2350
+ padding: "10px",
2351
+ fontSize: "12px",
2352
+ border: "1px solid #e5e7eb",
2353
+ borderRadius: "6px",
2354
+ backgroundColor: "#fef7ee",
2355
+ color: "#92400e",
2356
+ outline: "none",
2357
+ resize: "vertical",
2358
+ minHeight: "60px",
2359
+ fontFamily: "\"Fira Code\", \"Monaco\", \"Consolas\", monospace",
2360
+ lineHeight: 1.5
2361
+ },
2362
+ branchInfo: {
2363
+ display: "flex",
2364
+ gap: "12px",
2365
+ marginTop: "8px"
2366
+ },
2367
+ branchCard: (type) => ({
2368
+ flex: 1,
2369
+ padding: "10px",
2370
+ borderRadius: "8px",
2371
+ backgroundColor: type === "true" ? "#d1fae5" : "#fee2e2",
2372
+ border: `1px solid ${type === "true" ? "#a7f3d0" : "#fecaca"}`
2373
+ }),
2374
+ branchLabel: (type) => ({
2375
+ fontSize: "10px",
2376
+ fontWeight: 700,
2377
+ textTransform: "uppercase",
2378
+ letterSpacing: "0.05em",
2379
+ color: type === "true" ? "#059669" : "#dc2626",
2380
+ marginBottom: "4px"
2381
+ }),
2382
+ branchCount: {
2383
+ fontSize: "18px",
2384
+ fontWeight: 700,
2385
+ color: "#1f2937"
2386
+ },
2387
+ branchCountLabel: {
2388
+ fontSize: "10px",
2389
+ color: "#6b7280"
2390
+ },
2391
+ helpText: {
2392
+ fontSize: "10px",
2393
+ color: "#9ca3af",
2394
+ marginTop: "4px",
2395
+ fontStyle: "italic"
2396
+ }
2397
+ };
2398
+ const ConditionalNode = memo(function ConditionalNode(props) {
2399
+ const { data, selected, id } = props;
2400
+ const { step, onChange } = data;
2401
+ const config = step.config;
2402
+ const [isExpanded, setIsExpanded] = useState(false);
2403
+ const handleChange = useCallback((field, value) => {
2404
+ onChange?.(id, { config: {
2405
+ ...config,
2406
+ [field]: value
2407
+ } });
2408
+ }, [
2409
+ id,
2410
+ config,
2411
+ onChange
2412
+ ]);
2413
+ const handleNameChange = useCallback((e) => {
2414
+ onChange?.(id, { name: e.target.value });
2415
+ }, [id, onChange]);
2416
+ const trueStepsCount = config.trueSteps?.length || 0;
2417
+ const falseStepsCount = config.falseSteps?.length || 0;
2418
+ return /* @__PURE__ */ jsxs(BaseNode, {
2419
+ data,
2420
+ selected,
2421
+ id,
2422
+ typeLabel: "Conditional",
2423
+ icon: /* @__PURE__ */ jsx(GitBranchIcon, {}),
2424
+ headerColor: NODE_HEADER_COLORS.conditional,
2425
+ showSourceHandle: false,
2426
+ additionalSourceHandles: [{
2427
+ id: "true",
2428
+ position: Position.Right,
2429
+ label: "True",
2430
+ color: "#22c55e",
2431
+ style: { top: "40%" }
2432
+ }, {
2433
+ id: "false",
2434
+ position: Position.Right,
2435
+ label: "False",
2436
+ color: "#ef4444",
2437
+ style: { top: "60%" }
2438
+ }],
2439
+ children: [
2440
+ /* @__PURE__ */ jsxs("div", {
2441
+ style: styles$2.field,
2442
+ children: [/* @__PURE__ */ jsx("label", {
2443
+ style: styles$2.label,
2444
+ children: "Step Name"
2445
+ }), /* @__PURE__ */ jsx("input", {
2446
+ style: styles$2.input,
2447
+ value: step.name,
2448
+ onChange: handleNameChange,
2449
+ placeholder: "Enter step name",
2450
+ onKeyDown: (e) => e.stopPropagation()
2451
+ })]
2452
+ }),
2453
+ /* @__PURE__ */ jsxs("div", {
2454
+ style: styles$2.field,
2455
+ children: [
2456
+ /* @__PURE__ */ jsx("label", {
2457
+ style: styles$2.label,
2458
+ children: "Condition (JavaScript)"
2459
+ }),
2460
+ /* @__PURE__ */ jsx("textarea", {
2461
+ style: styles$2.conditionArea,
2462
+ value: config.condition || "",
2463
+ onChange: (e) => handleChange("condition", e.target.value),
2464
+ placeholder: "user_type === 'premium'",
2465
+ spellCheck: false,
2466
+ onKeyDown: (e) => e.stopPropagation()
2467
+ }),
2468
+ /* @__PURE__ */ jsxs("div", {
2469
+ style: styles$2.helpText,
2470
+ children: [
2471
+ "Access variables directly: ",
2472
+ /* @__PURE__ */ jsx("code", { children: "variable_name" }),
2473
+ ", ",
2474
+ /* @__PURE__ */ jsx("code", { children: "_record.metadata.field" })
2475
+ ]
2476
+ })
2477
+ ]
2478
+ }),
2479
+ /* @__PURE__ */ jsxs("div", {
2480
+ style: styles$2.branchInfo,
2481
+ children: [/* @__PURE__ */ jsxs("div", {
2482
+ style: styles$2.branchCard("true"),
2483
+ children: [
2484
+ /* @__PURE__ */ jsxs("div", {
2485
+ style: styles$2.branchLabel("true"),
2486
+ children: [/* @__PURE__ */ jsx(TrueIcon, {}), " True Branch"]
2487
+ }),
2488
+ /* @__PURE__ */ jsx("div", {
2489
+ style: styles$2.branchCount,
2490
+ children: trueStepsCount
2491
+ }),
2492
+ /* @__PURE__ */ jsxs("div", {
2493
+ style: styles$2.branchCountLabel,
2494
+ children: ["step", trueStepsCount !== 1 ? "s" : ""]
2495
+ })
2496
+ ]
2497
+ }), /* @__PURE__ */ jsxs("div", {
2498
+ style: styles$2.branchCard("false"),
2499
+ children: [
2500
+ /* @__PURE__ */ jsxs("div", {
2501
+ style: styles$2.branchLabel("false"),
2502
+ children: [/* @__PURE__ */ jsx(FalseIcon, {}), " False Branch"]
2503
+ }),
2504
+ /* @__PURE__ */ jsx("div", {
2505
+ style: styles$2.branchCount,
2506
+ children: falseStepsCount
2507
+ }),
2508
+ /* @__PURE__ */ jsxs("div", {
2509
+ style: styles$2.branchCountLabel,
2510
+ children: ["step", falseStepsCount !== 1 ? "s" : ""]
2511
+ })
2512
+ ]
2513
+ })]
2514
+ }),
2515
+ isExpanded && /* @__PURE__ */ jsx("div", {
2516
+ style: { marginTop: "12px" },
2517
+ children: /* @__PURE__ */ jsxs("div", {
2518
+ style: styles$2.field,
2519
+ children: [/* @__PURE__ */ jsx("label", {
2520
+ style: styles$2.label,
2521
+ children: "Condition Examples"
2522
+ }), /* @__PURE__ */ jsxs("div", {
2523
+ style: {
2524
+ fontSize: "11px",
2525
+ color: "#6b7280",
2526
+ lineHeight: 1.6
2527
+ },
2528
+ children: [
2529
+ /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("code", { children: "status === 'active'" }), " - Check equality"] }),
2530
+ /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsxs("code", { children: [
2531
+ "count ",
2532
+ ">",
2533
+ " 10"
2534
+ ] }), " - Numeric comparison"] }),
2535
+ /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsxs("code", { children: [
2536
+ "data && data.length ",
2537
+ ">",
2538
+ " 0"
2539
+ ] }), " - Check array"] }),
2540
+ /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("code", { children: "_record.metadata.type === 'premium'" }), " - API input"] })
2541
+ ]
2542
+ })]
2543
+ })
2544
+ }),
2545
+ /* @__PURE__ */ jsx("button", {
2546
+ onClick: () => setIsExpanded(!isExpanded),
2547
+ style: {
2548
+ width: "100%",
2549
+ padding: "6px",
2550
+ fontSize: "11px",
2551
+ color: "#6366f1",
2552
+ backgroundColor: "transparent",
2553
+ border: "1px dashed #e5e7eb",
2554
+ borderRadius: "6px",
2555
+ cursor: "pointer",
2556
+ marginTop: "12px"
2557
+ },
2558
+ children: isExpanded ? "Hide Examples" : "Show Examples"
2559
+ })
2560
+ ]
2561
+ });
2562
+ });
2563
+ const TrueIcon = () => /* @__PURE__ */ jsx("svg", {
2564
+ width: "12",
2565
+ height: "12",
2566
+ viewBox: "0 0 24 24",
2567
+ fill: "none",
2568
+ stroke: "currentColor",
2569
+ strokeWidth: "2",
2570
+ strokeLinecap: "round",
2571
+ strokeLinejoin: "round",
2572
+ style: {
2573
+ display: "inline",
2574
+ marginRight: "4px",
2575
+ verticalAlign: "middle"
2576
+ },
2577
+ children: /* @__PURE__ */ jsx("polyline", { points: "20 6 9 17 4 12" })
2578
+ });
2579
+ const FalseIcon = () => /* @__PURE__ */ jsxs("svg", {
2580
+ width: "12",
2581
+ height: "12",
2582
+ viewBox: "0 0 24 24",
2583
+ fill: "none",
2584
+ stroke: "currentColor",
2585
+ strokeWidth: "2",
2586
+ strokeLinecap: "round",
2587
+ strokeLinejoin: "round",
2588
+ style: {
2589
+ display: "inline",
2590
+ marginRight: "4px",
2591
+ verticalAlign: "middle"
2592
+ },
2593
+ children: [/* @__PURE__ */ jsx("line", {
2594
+ x1: "18",
2595
+ y1: "6",
2596
+ x2: "6",
2597
+ y2: "18"
2598
+ }), /* @__PURE__ */ jsx("line", {
2599
+ x1: "6",
2600
+ y1: "6",
2601
+ x2: "18",
2602
+ y2: "18"
2603
+ })]
2604
+ });
2605
+ //#endregion
2606
+ //#region src/components/nodes/SendEmailNode.tsx
2607
+ const styles$1 = {
2608
+ field: { marginBottom: "12px" },
2609
+ label: {
2610
+ display: "block",
2611
+ fontSize: "11px",
2612
+ fontWeight: 600,
2613
+ color: "#6b7280",
2614
+ marginBottom: "4px",
2615
+ textTransform: "uppercase",
2616
+ letterSpacing: "0.03em"
2617
+ },
2618
+ input: {
2619
+ width: "100%",
2620
+ padding: "8px 10px",
2621
+ fontSize: "12px",
2622
+ border: "1px solid #e5e7eb",
2623
+ borderRadius: "6px",
2624
+ backgroundColor: "#f9fafb",
2625
+ color: "#1f2937",
2626
+ outline: "none",
2627
+ transition: "border-color 0.15s ease, box-shadow 0.15s ease"
2628
+ },
2629
+ textarea: {
2630
+ width: "100%",
2631
+ padding: "8px 10px",
2632
+ fontSize: "12px",
2633
+ border: "1px solid #e5e7eb",
2634
+ borderRadius: "6px",
2635
+ backgroundColor: "#f9fafb",
2636
+ color: "#1f2937",
2637
+ outline: "none",
2638
+ resize: "vertical",
2639
+ minHeight: "80px",
2640
+ fontFamily: "inherit",
2641
+ transition: "border-color 0.15s ease, box-shadow 0.15s ease"
2642
+ },
2643
+ select: {
2644
+ width: "100%",
2645
+ padding: "8px 10px",
2646
+ fontSize: "12px",
2647
+ border: "1px solid #e5e7eb",
2648
+ borderRadius: "6px",
2649
+ backgroundColor: "#f9fafb",
2650
+ color: "#1f2937",
2651
+ outline: "none",
2652
+ cursor: "pointer"
2653
+ },
2654
+ row: {
2655
+ display: "flex",
2656
+ gap: "8px"
2657
+ },
2658
+ emailPreview: {
2659
+ backgroundColor: "#f8fafc",
2660
+ border: "1px solid #e2e8f0",
2661
+ borderRadius: "8px",
2662
+ padding: "10px",
2663
+ marginTop: "8px"
2664
+ },
2665
+ previewHeader: {
2666
+ fontSize: "10px",
2667
+ color: "#64748b",
2668
+ marginBottom: "4px"
2669
+ },
2670
+ previewValue: {
2671
+ fontSize: "12px",
2672
+ color: "#1e293b",
2673
+ fontWeight: 500,
2674
+ marginBottom: "8px",
2675
+ wordBreak: "break-all"
2676
+ },
2677
+ variableHint: {
2678
+ display: "inline-flex",
2679
+ alignItems: "center",
2680
+ padding: "2px 6px",
2681
+ borderRadius: "4px",
2682
+ fontSize: "10px",
2683
+ backgroundColor: "#f0f9ff",
2684
+ color: "#0369a1",
2685
+ fontFamily: "monospace",
2686
+ marginRight: "4px",
2687
+ marginTop: "4px"
2688
+ }
2689
+ };
2690
+ const SendEmailNode = memo(function SendEmailNode(props) {
2691
+ const { data, selected, id } = props;
2692
+ const { step, onChange } = data;
2693
+ const config = step.config;
2694
+ const [isExpanded, setIsExpanded] = useState(false);
2695
+ const handleChange = useCallback((field, value) => {
2696
+ onChange?.(id, { config: {
2697
+ ...config,
2698
+ [field]: value
2699
+ } });
2700
+ }, [
2701
+ id,
2702
+ config,
2703
+ onChange
2704
+ ]);
2705
+ const handleNameChange = useCallback((e) => {
2706
+ onChange?.(id, { name: e.target.value });
2707
+ }, [id, onChange]);
2708
+ const hasVariables = (value) => /\{\{[\w._]+\}\}/.test(value);
2709
+ return /* @__PURE__ */ jsxs(BaseNode, {
2710
+ data,
2711
+ selected,
2712
+ id,
2713
+ typeLabel: "Send Email",
2714
+ icon: /* @__PURE__ */ jsx(MailIcon$1, {}),
2715
+ headerColor: NODE_HEADER_COLORS["send-email"],
2716
+ children: [
2717
+ /* @__PURE__ */ jsxs("div", {
2718
+ style: styles$1.field,
2719
+ children: [/* @__PURE__ */ jsx("label", {
2720
+ style: styles$1.label,
2721
+ children: "Step Name"
2722
+ }), /* @__PURE__ */ jsx("input", {
2723
+ style: styles$1.input,
2724
+ value: step.name,
2725
+ onChange: handleNameChange,
2726
+ placeholder: "Enter step name",
2727
+ onKeyDown: (e) => e.stopPropagation()
2728
+ })]
2729
+ }),
2730
+ /* @__PURE__ */ jsxs("div", {
2731
+ style: styles$1.field,
2732
+ children: [
2733
+ /* @__PURE__ */ jsx("label", {
2734
+ style: styles$1.label,
2735
+ children: "To"
2736
+ }),
2737
+ /* @__PURE__ */ jsx("input", {
2738
+ style: styles$1.input,
2739
+ value: config.to || "",
2740
+ onChange: (e) => handleChange("to", e.target.value),
2741
+ placeholder: "recipient@example.com or {{_record.metadata.email}}",
2742
+ onKeyDown: (e) => e.stopPropagation()
2743
+ }),
2744
+ hasVariables(config.to || "") && /* @__PURE__ */ jsx("span", {
2745
+ style: styles$1.variableHint,
2746
+ children: "Uses variable"
2747
+ })
2748
+ ]
2749
+ }),
2750
+ /* @__PURE__ */ jsxs("div", {
2751
+ style: styles$1.field,
2752
+ children: [/* @__PURE__ */ jsx("label", {
2753
+ style: styles$1.label,
2754
+ children: "Subject"
2755
+ }), /* @__PURE__ */ jsx("input", {
2756
+ style: styles$1.input,
2757
+ value: config.subject || "",
2758
+ onChange: (e) => handleChange("subject", e.target.value),
2759
+ placeholder: "Your subject line",
2760
+ onKeyDown: (e) => e.stopPropagation()
2761
+ })]
2762
+ }),
2763
+ /* @__PURE__ */ jsxs("div", {
2764
+ style: styles$1.field,
2765
+ children: [/* @__PURE__ */ jsx("label", {
2766
+ style: styles$1.label,
2767
+ children: "HTML Content"
2768
+ }), /* @__PURE__ */ jsx("textarea", {
2769
+ style: styles$1.textarea,
2770
+ value: config.html || "",
2771
+ onChange: (e) => handleChange("html", e.target.value),
2772
+ placeholder: "<p>Your email content...</p>\n\nUse {{variable}} for dynamic content",
2773
+ rows: 4,
2774
+ onKeyDown: (e) => e.stopPropagation()
2775
+ })]
2776
+ }),
2777
+ isExpanded && /* @__PURE__ */ jsxs(Fragment, { children: [
2778
+ /* @__PURE__ */ jsxs("div", {
2779
+ style: styles$1.field,
2780
+ children: [/* @__PURE__ */ jsx("label", {
2781
+ style: styles$1.label,
2782
+ children: "From"
2783
+ }), /* @__PURE__ */ jsx("input", {
2784
+ style: styles$1.input,
2785
+ value: config.from || "",
2786
+ onChange: (e) => handleChange("from", e.target.value),
2787
+ placeholder: "{{_flow.id}}@runtype.email",
2788
+ onKeyDown: (e) => e.stopPropagation()
2789
+ })]
2790
+ }),
2791
+ /* @__PURE__ */ jsxs("div", {
2792
+ style: styles$1.row,
2793
+ children: [/* @__PURE__ */ jsxs("div", {
2794
+ style: {
2795
+ ...styles$1.field,
2796
+ flex: 1
2797
+ },
2798
+ children: [/* @__PURE__ */ jsx("label", {
2799
+ style: styles$1.label,
2800
+ children: "CC"
2801
+ }), /* @__PURE__ */ jsx("input", {
2802
+ style: styles$1.input,
2803
+ value: config.cc || "",
2804
+ onChange: (e) => handleChange("cc", e.target.value),
2805
+ placeholder: "cc@example.com",
2806
+ onKeyDown: (e) => e.stopPropagation()
2807
+ })]
2808
+ }), /* @__PURE__ */ jsxs("div", {
2809
+ style: {
2810
+ ...styles$1.field,
2811
+ flex: 1
2812
+ },
2813
+ children: [/* @__PURE__ */ jsx("label", {
2814
+ style: styles$1.label,
2815
+ children: "BCC"
2816
+ }), /* @__PURE__ */ jsx("input", {
2817
+ style: styles$1.input,
2818
+ value: config.bcc || "",
2819
+ onChange: (e) => handleChange("bcc", e.target.value),
2820
+ placeholder: "bcc@example.com",
2821
+ onKeyDown: (e) => e.stopPropagation()
2822
+ })]
2823
+ })]
2824
+ }),
2825
+ /* @__PURE__ */ jsxs("div", {
2826
+ style: styles$1.field,
2827
+ children: [/* @__PURE__ */ jsx("label", {
2828
+ style: styles$1.label,
2829
+ children: "Reply To"
2830
+ }), /* @__PURE__ */ jsx("input", {
2831
+ style: styles$1.input,
2832
+ value: config.replyTo || "",
2833
+ onChange: (e) => handleChange("replyTo", e.target.value),
2834
+ placeholder: "reply@example.com",
2835
+ onKeyDown: (e) => e.stopPropagation()
2836
+ })]
2837
+ }),
2838
+ /* @__PURE__ */ jsxs("div", {
2839
+ style: styles$1.field,
2840
+ children: [/* @__PURE__ */ jsx("label", {
2841
+ style: styles$1.label,
2842
+ children: "Plain Text (Fallback)"
2843
+ }), /* @__PURE__ */ jsx("textarea", {
2844
+ style: styles$1.textarea,
2845
+ value: config.text || "",
2846
+ onChange: (e) => handleChange("text", e.target.value),
2847
+ placeholder: "Plain text version of your email...",
2848
+ rows: 2,
2849
+ onKeyDown: (e) => e.stopPropagation()
2850
+ })]
2851
+ }),
2852
+ /* @__PURE__ */ jsxs("div", {
2853
+ style: styles$1.row,
2854
+ children: [/* @__PURE__ */ jsxs("div", {
2855
+ style: {
2856
+ ...styles$1.field,
2857
+ flex: 1
2858
+ },
2859
+ children: [/* @__PURE__ */ jsx("label", {
2860
+ style: styles$1.label,
2861
+ children: "On Error"
2862
+ }), /* @__PURE__ */ jsxs("select", {
2863
+ style: styles$1.select,
2864
+ value: config.errorHandling || "fail",
2865
+ onChange: (e) => handleChange("errorHandling", e.target.value),
2866
+ onKeyDown: (e) => e.stopPropagation(),
2867
+ children: [
2868
+ /* @__PURE__ */ jsx("option", {
2869
+ value: "fail",
2870
+ children: "Fail"
2871
+ }),
2872
+ /* @__PURE__ */ jsx("option", {
2873
+ value: "continue",
2874
+ children: "Continue"
2875
+ }),
2876
+ /* @__PURE__ */ jsx("option", {
2877
+ value: "default",
2878
+ children: "Use Default"
2879
+ })
2880
+ ]
2881
+ })]
2882
+ }), /* @__PURE__ */ jsxs("div", {
2883
+ style: {
2884
+ ...styles$1.field,
2885
+ flex: 1
2886
+ },
2887
+ children: [/* @__PURE__ */ jsx("label", {
2888
+ style: styles$1.label,
2889
+ children: "Output Variable"
2890
+ }), /* @__PURE__ */ jsx("input", {
2891
+ style: styles$1.input,
2892
+ value: config.outputVariable || "",
2893
+ onChange: (e) => handleChange("outputVariable", e.target.value),
2894
+ placeholder: "email_result",
2895
+ onKeyDown: (e) => e.stopPropagation()
2896
+ })]
2897
+ })]
2898
+ })
2899
+ ] }),
2900
+ (config.to || config.subject) && /* @__PURE__ */ jsxs("div", {
2901
+ style: styles$1.emailPreview,
2902
+ children: [
2903
+ /* @__PURE__ */ jsx("div", {
2904
+ style: styles$1.previewHeader,
2905
+ children: "Preview"
2906
+ }),
2907
+ config.to && /* @__PURE__ */ jsxs("div", {
2908
+ style: styles$1.previewValue,
2909
+ children: [
2910
+ /* @__PURE__ */ jsx("strong", { children: "To:" }),
2911
+ " ",
2912
+ config.to
2913
+ ]
2914
+ }),
2915
+ config.subject && /* @__PURE__ */ jsxs("div", {
2916
+ style: styles$1.previewValue,
2917
+ children: [
2918
+ /* @__PURE__ */ jsx("strong", { children: "Subject:" }),
2919
+ " ",
2920
+ config.subject
2921
+ ]
2922
+ })
2923
+ ]
2924
+ }),
2925
+ /* @__PURE__ */ jsx("button", {
2926
+ onClick: () => setIsExpanded(!isExpanded),
2927
+ style: {
2928
+ width: "100%",
2929
+ padding: "6px",
2930
+ fontSize: "11px",
2931
+ color: "#6366f1",
2932
+ backgroundColor: "transparent",
2933
+ border: "1px dashed #e5e7eb",
2934
+ borderRadius: "6px",
2935
+ cursor: "pointer",
2936
+ marginTop: "8px"
2937
+ },
2938
+ children: isExpanded ? "Show Less" : "Show More Options"
2939
+ })
2940
+ ]
2941
+ });
2942
+ });
2943
+ //#endregion
2944
+ //#region src/components/RuntypeFlowEditor.tsx
2945
+ const styles = {
2946
+ container: {
2947
+ width: "100%",
2948
+ height: "100%",
2949
+ position: "relative"
2950
+ },
2951
+ toolbar: {
2952
+ display: "flex",
2953
+ alignItems: "center",
2954
+ gap: "8px",
2955
+ padding: "8px 12px",
2956
+ backgroundColor: "#ffffff",
2957
+ borderRadius: "8px",
2958
+ boxShadow: "0 2px 8px rgba(0, 0, 0, 0.1)",
2959
+ border: "1px solid #e5e7eb"
2960
+ },
2961
+ toolbarButton: {
2962
+ display: "flex",
2963
+ alignItems: "center",
2964
+ gap: "6px",
2965
+ padding: "8px 12px",
2966
+ fontSize: "12px",
2967
+ fontWeight: 500,
2968
+ backgroundColor: "#f9fafb",
2969
+ border: "1px solid #e5e7eb",
2970
+ borderRadius: "6px",
2971
+ cursor: "pointer",
2972
+ color: "#374151",
2973
+ transition: "all 0.15s ease"
2974
+ },
2975
+ toolbarButtonPrimary: {
2976
+ backgroundColor: "#6366f1",
2977
+ borderColor: "#6366f1",
2978
+ color: "#ffffff"
2979
+ },
2980
+ toolbarDivider: {
2981
+ width: "1px",
2982
+ height: "24px",
2983
+ backgroundColor: "#e5e7eb",
2984
+ margin: "0 4px"
2985
+ },
2986
+ statusPanel: {
2987
+ display: "flex",
2988
+ alignItems: "center",
2989
+ gap: "8px",
2990
+ padding: "8px 12px",
2991
+ backgroundColor: "#ffffff",
2992
+ borderRadius: "8px",
2993
+ boxShadow: "0 2px 8px rgba(0, 0, 0, 0.1)",
2994
+ border: "1px solid #e5e7eb",
2995
+ fontSize: "12px",
2996
+ color: "#6b7280"
2997
+ },
2998
+ statusBadge: (isValid) => ({
2999
+ display: "inline-flex",
3000
+ alignItems: "center",
3001
+ padding: "2px 8px",
3002
+ borderRadius: "12px",
3003
+ fontSize: "10px",
3004
+ fontWeight: 600,
3005
+ backgroundColor: isValid ? "#d1fae5" : "#fee2e2",
3006
+ color: isValid ? "#059669" : "#dc2626"
3007
+ }),
3008
+ addStepMenu: {
3009
+ position: "absolute",
3010
+ top: "100%",
3011
+ left: 0,
3012
+ marginTop: "4px",
3013
+ backgroundColor: "#ffffff",
3014
+ borderRadius: "8px",
3015
+ boxShadow: "0 4px 12px rgba(0, 0, 0, 0.15)",
3016
+ border: "1px solid #e5e7eb",
3017
+ padding: "4px",
3018
+ zIndex: 1e3,
3019
+ minWidth: "200px"
3020
+ },
3021
+ addStepMenuItem: {
3022
+ display: "flex",
3023
+ alignItems: "center",
3024
+ gap: "8px",
3025
+ padding: "8px 12px",
3026
+ fontSize: "12px",
3027
+ color: "#374151",
3028
+ backgroundColor: "transparent",
3029
+ border: "none",
3030
+ borderRadius: "4px",
3031
+ cursor: "pointer",
3032
+ width: "100%",
3033
+ textAlign: "left",
3034
+ transition: "background-color 0.15s ease"
3035
+ },
3036
+ flowNameInput: {
3037
+ padding: "6px 10px",
3038
+ fontSize: "14px",
3039
+ fontWeight: 600,
3040
+ border: "1px solid transparent",
3041
+ borderRadius: "4px",
3042
+ backgroundColor: "transparent",
3043
+ color: "#1f2937",
3044
+ outline: "none",
3045
+ transition: "border-color 0.15s ease, background-color 0.15s ease",
3046
+ minWidth: "200px"
3047
+ },
3048
+ unsavedBadge: {
3049
+ display: "inline-flex",
3050
+ alignItems: "center",
3051
+ padding: "2px 6px",
3052
+ borderRadius: "4px",
3053
+ fontSize: "10px",
3054
+ fontWeight: 500,
3055
+ backgroundColor: "#fef3c7",
3056
+ color: "#d97706"
3057
+ }
3058
+ };
3059
+ const nodeTypes = {
3060
+ prompt: PromptNode,
3061
+ "fetch-url": FetchUrlNode,
3062
+ "transform-data": CodeNode,
3063
+ conditional: ConditionalNode,
3064
+ "send-email": SendEmailNode,
3065
+ default: DefaultNode
3066
+ };
3067
+ function DefaultNode(props) {
3068
+ const { data, selected, id } = props;
3069
+ return /* @__PURE__ */ jsx(BaseNode, {
3070
+ data,
3071
+ selected,
3072
+ id,
3073
+ typeLabel: data.step.type,
3074
+ icon: /* @__PURE__ */ jsx(BrainIcon, {}),
3075
+ headerColor: NODE_HEADER_COLORS.default,
3076
+ children: /* @__PURE__ */ jsx("div", {
3077
+ style: {
3078
+ fontSize: "12px",
3079
+ color: "#6b7280"
3080
+ },
3081
+ children: "This step type is not yet fully supported in the visual editor."
3082
+ })
3083
+ });
3084
+ }
3085
+ const STEP_TYPE_OPTIONS = [
3086
+ {
3087
+ type: "prompt",
3088
+ label: "AI Prompt",
3089
+ icon: /* @__PURE__ */ jsx(PromptIcon, {}),
3090
+ color: NODE_HEADER_COLORS.prompt
3091
+ },
3092
+ {
3093
+ type: "fetch-url",
3094
+ label: "Fetch URL",
3095
+ icon: /* @__PURE__ */ jsx(GlobeIcon, {}),
3096
+ color: NODE_HEADER_COLORS["fetch-url"]
3097
+ },
3098
+ {
3099
+ type: "transform-data",
3100
+ label: "Run Code",
3101
+ icon: /* @__PURE__ */ jsx(CodeIcon, {}),
3102
+ color: NODE_HEADER_COLORS["transform-data"]
3103
+ },
3104
+ {
3105
+ type: "conditional",
3106
+ label: "Conditional",
3107
+ icon: /* @__PURE__ */ jsx(BranchIcon, {}),
3108
+ color: NODE_HEADER_COLORS.conditional
3109
+ },
3110
+ {
3111
+ type: "send-email",
3112
+ label: "Send Email",
3113
+ icon: /* @__PURE__ */ jsx(MailIcon, {}),
3114
+ color: NODE_HEADER_COLORS["send-email"]
3115
+ }
3116
+ ];
3117
+ function RuntypeFlowEditor({ client, flowId, initialName = "Untitled Flow", initialDescription = "", initialSteps = [], onSave, onChange, onStepSelect, showToolbar = true, readOnly = false, className }) {
3118
+ const [showAddMenu, setShowAddMenu] = useState(false);
3119
+ const [, setSelectedNode] = useState(null);
3120
+ const { nodes, edges, onNodesChange, onEdgesChange, onConnect, flowName, setFlowName, saveFlow, addStep, isLoading, isSaving, error, hasUnsavedChanges } = useRuntypeFlow({
3121
+ client,
3122
+ flowId,
3123
+ initialName,
3124
+ initialDescription,
3125
+ initialSteps,
3126
+ onChange,
3127
+ autoLayoutOnLoad: true
3128
+ });
3129
+ const { result: validationResult } = useFlowValidation({ nodes });
3130
+ const handleSave = useCallback(async () => {
3131
+ try {
3132
+ const savedFlow = await saveFlow();
3133
+ onSave?.(savedFlow);
3134
+ } catch (err) {
3135
+ console.error("Failed to save flow:", err);
3136
+ }
3137
+ }, [saveFlow, onSave]);
3138
+ const handleAddStep = useCallback((type) => {
3139
+ addStep(type);
3140
+ setShowAddMenu(false);
3141
+ }, [addStep]);
3142
+ const handleNodeClick = useCallback((_, node) => {
3143
+ setSelectedNode(node);
3144
+ onStepSelect?.(node.data.step);
3145
+ }, [onStepSelect]);
3146
+ const handlePaneClick = useCallback(() => {
3147
+ setSelectedNode(null);
3148
+ onStepSelect?.(null);
3149
+ }, [onStepSelect]);
3150
+ const minimapNodeColor = useCallback((node) => {
3151
+ return NODE_HEADER_COLORS[node.data.step.type] || NODE_HEADER_COLORS.default;
3152
+ }, []);
3153
+ return /* @__PURE__ */ jsxs("div", {
3154
+ style: styles.container,
3155
+ className,
3156
+ children: [/* @__PURE__ */ jsxs(ReactFlow, {
3157
+ nodes,
3158
+ edges,
3159
+ onNodesChange: readOnly ? void 0 : onNodesChange,
3160
+ onEdgesChange: readOnly ? void 0 : onEdgesChange,
3161
+ onConnect: readOnly ? void 0 : onConnect,
3162
+ onNodeClick: handleNodeClick,
3163
+ onPaneClick: handlePaneClick,
3164
+ nodeTypes,
3165
+ fitView: true,
3166
+ fitViewOptions: { padding: .2 },
3167
+ snapToGrid: true,
3168
+ snapGrid: [20, 20],
3169
+ connectionLineType: ConnectionLineType.SmoothStep,
3170
+ defaultEdgeOptions: {
3171
+ type: "smoothstep",
3172
+ animated: false
3173
+ },
3174
+ proOptions: { hideAttribution: true },
3175
+ children: [
3176
+ /* @__PURE__ */ jsx(Background, {
3177
+ variant: BackgroundVariant.Dots,
3178
+ gap: 20,
3179
+ size: 1,
3180
+ color: "#d1d5db"
3181
+ }),
3182
+ /* @__PURE__ */ jsx(Controls, { showInteractive: !readOnly }),
3183
+ /* @__PURE__ */ jsx(MiniMap, {
3184
+ nodeColor: minimapNodeColor,
3185
+ nodeStrokeWidth: 3,
3186
+ zoomable: true,
3187
+ pannable: true,
3188
+ style: {
3189
+ backgroundColor: "#f9fafb",
3190
+ border: "1px solid #e5e7eb",
3191
+ borderRadius: "8px"
3192
+ }
3193
+ }),
3194
+ showToolbar && /* @__PURE__ */ jsx(Panel, {
3195
+ position: "top-left",
3196
+ children: /* @__PURE__ */ jsxs("div", {
3197
+ style: styles.toolbar,
3198
+ children: [
3199
+ /* @__PURE__ */ jsx("input", {
3200
+ style: styles.flowNameInput,
3201
+ value: flowName,
3202
+ onChange: (e) => setFlowName(e.target.value),
3203
+ placeholder: "Flow name...",
3204
+ disabled: readOnly,
3205
+ onFocus: (e) => {
3206
+ e.target.style.borderColor = "#6366f1";
3207
+ e.target.style.backgroundColor = "#ffffff";
3208
+ },
3209
+ onBlur: (e) => {
3210
+ e.target.style.borderColor = "transparent";
3211
+ e.target.style.backgroundColor = "transparent";
3212
+ }
3213
+ }),
3214
+ hasUnsavedChanges && /* @__PURE__ */ jsx("span", {
3215
+ style: styles.unsavedBadge,
3216
+ children: "Unsaved"
3217
+ }),
3218
+ /* @__PURE__ */ jsx("div", { style: styles.toolbarDivider }),
3219
+ !readOnly && /* @__PURE__ */ jsxs("div", {
3220
+ style: { position: "relative" },
3221
+ children: [/* @__PURE__ */ jsxs("button", {
3222
+ style: styles.toolbarButton,
3223
+ onClick: () => setShowAddMenu(!showAddMenu),
3224
+ onMouseEnter: (e) => {
3225
+ e.currentTarget.style.backgroundColor = "#f3f4f6";
3226
+ e.currentTarget.style.borderColor = "#d1d5db";
3227
+ },
3228
+ onMouseLeave: (e) => {
3229
+ e.currentTarget.style.backgroundColor = "#f9fafb";
3230
+ e.currentTarget.style.borderColor = "#e5e7eb";
3231
+ },
3232
+ children: [/* @__PURE__ */ jsx(PlusIcon, {}), " Add Step"]
3233
+ }), showAddMenu && /* @__PURE__ */ jsx("div", {
3234
+ style: styles.addStepMenu,
3235
+ children: STEP_TYPE_OPTIONS.map((option) => /* @__PURE__ */ jsxs("button", {
3236
+ style: styles.addStepMenuItem,
3237
+ onClick: () => handleAddStep(option.type),
3238
+ onMouseEnter: (e) => {
3239
+ e.currentTarget.style.backgroundColor = option.color;
3240
+ },
3241
+ onMouseLeave: (e) => {
3242
+ e.currentTarget.style.backgroundColor = "transparent";
3243
+ },
3244
+ children: [option.icon, option.label]
3245
+ }, option.type))
3246
+ })]
3247
+ }),
3248
+ !readOnly && flowId && /* @__PURE__ */ jsx("button", {
3249
+ style: {
3250
+ ...styles.toolbarButton,
3251
+ ...styles.toolbarButtonPrimary
3252
+ },
3253
+ onClick: handleSave,
3254
+ disabled: isSaving || !hasUnsavedChanges,
3255
+ onMouseEnter: (e) => {
3256
+ if (!isSaving && hasUnsavedChanges) e.currentTarget.style.backgroundColor = "#4f46e5";
3257
+ },
3258
+ onMouseLeave: (e) => {
3259
+ e.currentTarget.style.backgroundColor = "#6366f1";
3260
+ },
3261
+ children: isSaving ? "Saving..." : "Save"
3262
+ })
3263
+ ]
3264
+ })
3265
+ }),
3266
+ /* @__PURE__ */ jsx(Panel, {
3267
+ position: "top-right",
3268
+ children: /* @__PURE__ */ jsxs("div", {
3269
+ style: styles.statusPanel,
3270
+ children: [
3271
+ /* @__PURE__ */ jsx("span", {
3272
+ style: styles.statusBadge(validationResult.isValid),
3273
+ children: validationResult.isValid ? "Valid" : `${validationResult.errors.length} Error(s)`
3274
+ }),
3275
+ /* @__PURE__ */ jsxs("span", { children: [nodes.length, " steps"] }),
3276
+ isLoading && /* @__PURE__ */ jsx("span", { children: "Loading..." }),
3277
+ error && /* @__PURE__ */ jsxs("span", {
3278
+ style: { color: "#dc2626" },
3279
+ children: ["Error: ", error.message]
3280
+ })
3281
+ ]
3282
+ })
3283
+ })
3284
+ ]
3285
+ }), showAddMenu && /* @__PURE__ */ jsx("div", {
3286
+ style: {
3287
+ position: "fixed",
3288
+ top: 0,
3289
+ left: 0,
3290
+ right: 0,
3291
+ bottom: 0,
3292
+ zIndex: 999
3293
+ },
3294
+ onClick: () => setShowAddMenu(false)
3295
+ })]
3296
+ });
3297
+ }
3298
+ function PlusIcon() {
3299
+ return /* @__PURE__ */ jsxs("svg", {
3300
+ width: "14",
3301
+ height: "14",
3302
+ viewBox: "0 0 24 24",
3303
+ fill: "none",
3304
+ stroke: "currentColor",
3305
+ strokeWidth: "2",
3306
+ children: [/* @__PURE__ */ jsx("line", {
3307
+ x1: "12",
3308
+ y1: "5",
3309
+ x2: "12",
3310
+ y2: "19"
3311
+ }), /* @__PURE__ */ jsx("line", {
3312
+ x1: "5",
3313
+ y1: "12",
3314
+ x2: "19",
3315
+ y2: "12"
3316
+ })]
3317
+ });
3318
+ }
3319
+ function PromptIcon() {
3320
+ return /* @__PURE__ */ jsxs("svg", {
3321
+ width: "14",
3322
+ height: "14",
3323
+ viewBox: "0 0 24 24",
3324
+ fill: "none",
3325
+ stroke: "currentColor",
3326
+ strokeWidth: "2",
3327
+ children: [/* @__PURE__ */ jsx("path", { d: "M12 5a3 3 0 1 0-5.997.125 4 4 0 0 0-2.526 5.77 4 4 0 0 0 .556 6.588A4 4 0 1 0 12 18Z" }), /* @__PURE__ */ jsx("path", { d: "M12 5a3 3 0 1 1 5.997.125 4 4 0 0 1 2.526 5.77 4 4 0 0 1-.556 6.588A4 4 0 1 1 12 18Z" })]
3328
+ });
3329
+ }
3330
+ function GlobeIcon() {
3331
+ return /* @__PURE__ */ jsxs("svg", {
3332
+ width: "14",
3333
+ height: "14",
3334
+ viewBox: "0 0 24 24",
3335
+ fill: "none",
3336
+ stroke: "currentColor",
3337
+ strokeWidth: "2",
3338
+ children: [
3339
+ /* @__PURE__ */ jsx("circle", {
3340
+ cx: "12",
3341
+ cy: "12",
3342
+ r: "10"
3343
+ }),
3344
+ /* @__PURE__ */ jsx("path", { d: "M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20" }),
3345
+ /* @__PURE__ */ jsx("path", { d: "M2 12h20" })
3346
+ ]
3347
+ });
3348
+ }
3349
+ function CodeIcon() {
3350
+ return /* @__PURE__ */ jsxs("svg", {
3351
+ width: "14",
3352
+ height: "14",
3353
+ viewBox: "0 0 24 24",
3354
+ fill: "none",
3355
+ stroke: "currentColor",
3356
+ strokeWidth: "2",
3357
+ children: [/* @__PURE__ */ jsx("polyline", { points: "16 18 22 12 16 6" }), /* @__PURE__ */ jsx("polyline", { points: "8 6 2 12 8 18" })]
3358
+ });
3359
+ }
3360
+ function BranchIcon() {
3361
+ return /* @__PURE__ */ jsxs("svg", {
3362
+ width: "14",
3363
+ height: "14",
3364
+ viewBox: "0 0 24 24",
3365
+ fill: "none",
3366
+ stroke: "currentColor",
3367
+ strokeWidth: "2",
3368
+ children: [
3369
+ /* @__PURE__ */ jsx("line", {
3370
+ x1: "6",
3371
+ y1: "3",
3372
+ x2: "6",
3373
+ y2: "15"
3374
+ }),
3375
+ /* @__PURE__ */ jsx("circle", {
3376
+ cx: "18",
3377
+ cy: "6",
3378
+ r: "3"
3379
+ }),
3380
+ /* @__PURE__ */ jsx("circle", {
3381
+ cx: "6",
3382
+ cy: "18",
3383
+ r: "3"
3384
+ }),
3385
+ /* @__PURE__ */ jsx("path", { d: "M18 9a9 9 0 0 1-9 9" })
3386
+ ]
3387
+ });
3388
+ }
3389
+ function MailIcon() {
3390
+ return /* @__PURE__ */ jsxs("svg", {
3391
+ width: "14",
3392
+ height: "14",
3393
+ viewBox: "0 0 24 24",
3394
+ fill: "none",
3395
+ stroke: "currentColor",
3396
+ strokeWidth: "2",
3397
+ children: [/* @__PURE__ */ jsx("rect", {
3398
+ width: "20",
3399
+ height: "16",
3400
+ x: "2",
3401
+ y: "4",
3402
+ rx: "2"
3403
+ }), /* @__PURE__ */ jsx("path", { d: "m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7" })]
3404
+ });
3405
+ }
3406
+ //#endregion
3407
+ export { BaseNode, CodeNode, ConditionalNode, FetchUrlNode, PromptNode, RuntypeFlowEditor, SUPPORTED_NODE_TYPES, SendEmailNode, autoLayout, centerNodes, cloneStep, createDefaultStep, createEdgesFromNodes, flowStepsToNodes, generateStepId, getDefaultStepName, getNodesBoundingBox, isSupportedNodeType, nodesToFlowSteps, snapToGrid, useFlowValidation, useRuntypeFlow };
3408
+