@runtypelabs/react-flow 0.1.5 → 0.1.7
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/.turbo/turbo-build.log +7 -7
- package/CHANGELOG.md +14 -0
- package/README.md +21 -21
- package/dist/index.js +152 -52
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +152 -52
- package/dist/index.mjs.map +1 -1
- package/example/CHANGELOG.md +16 -0
- package/example/node_modules/.bin/vite +2 -2
- package/example/package.json +1 -1
- package/example/src/App.tsx +269 -166
- package/example/src/main.tsx +1 -2
- package/example/tsconfig.json +0 -1
- package/example/vite.config.ts +0 -1
- package/package.json +2 -2
- package/src/components/RuntypeFlowEditor.tsx +57 -21
- package/src/components/nodes/BaseNode.tsx +7 -6
- package/src/components/nodes/CodeNode.tsx +9 -3
- package/src/components/nodes/ConditionalNode.tsx +15 -12
- package/src/components/nodes/FetchUrlNode.tsx +10 -8
- package/src/components/nodes/PromptNode.tsx +10 -6
- package/src/components/nodes/SendEmailNode.tsx +2 -5
- package/src/hooks/useFlowValidation.ts +1 -5
- package/src/hooks/useRuntypeFlow.ts +3 -12
- package/src/index.ts +0 -1
- package/src/types/index.ts +1 -2
- package/src/utils/adapter.ts +85 -75
- package/src/utils/layout.ts +21 -25
- package/tsconfig.json +0 -1
- package/tsup.config.ts +0 -1
package/src/utils/adapter.ts
CHANGED
|
@@ -6,9 +6,9 @@ import type { FlowStepType } from '../flow-step-types'
|
|
|
6
6
|
// ============================================================================
|
|
7
7
|
|
|
8
8
|
const NODE_WIDTH = 280
|
|
9
|
-
const NODE_HEIGHT = 200
|
|
10
|
-
const NODE_SPACING_X = 350
|
|
11
|
-
const NODE_SPACING_Y = 80
|
|
9
|
+
const NODE_HEIGHT = 200 // Increased for better card visibility
|
|
10
|
+
const NODE_SPACING_X = 350 // Horizontal space between main steps
|
|
11
|
+
const NODE_SPACING_Y = 80 // Vertical space between branch steps
|
|
12
12
|
const BRANCH_OFFSET_X = 350 // Horizontal offset for branches to the right of conditional
|
|
13
13
|
const BRANCH_OFFSET_Y = -80 // Vertical offset for true branch (above center)
|
|
14
14
|
const FALSE_BRANCH_GAP = 100 // Gap between true and false branches
|
|
@@ -35,9 +35,9 @@ export function flowStepsToNodes(
|
|
|
35
35
|
steps: FlowStep[],
|
|
36
36
|
options?: FlowStepsToNodesOptions
|
|
37
37
|
): RuntypeNode[] {
|
|
38
|
-
const {
|
|
39
|
-
onChange,
|
|
40
|
-
onDelete,
|
|
38
|
+
const {
|
|
39
|
+
onChange,
|
|
40
|
+
onDelete,
|
|
41
41
|
startPosition = { x: 50, y: 200 },
|
|
42
42
|
idPrefix = '',
|
|
43
43
|
parentId,
|
|
@@ -68,77 +68,80 @@ export function flowStepsToNodes(
|
|
|
68
68
|
draggable: true,
|
|
69
69
|
selectable: true,
|
|
70
70
|
}
|
|
71
|
-
|
|
71
|
+
|
|
72
72
|
if (parentId) {
|
|
73
73
|
node.parentId = parentId
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
nodes.push(node)
|
|
77
|
-
|
|
77
|
+
|
|
78
78
|
// DEBUG: Log node positions
|
|
79
79
|
console.log(`[flowStepsToNodes] Placed "${step.name}" (${step.type}) at x=${currentX}`)
|
|
80
|
-
|
|
80
|
+
|
|
81
81
|
// Handle conditional branches - position to the right with true above, false below
|
|
82
82
|
if (step.type === 'conditional' && step.config) {
|
|
83
83
|
const config = step.config as { trueSteps?: FlowStep[]; falseSteps?: FlowStep[] }
|
|
84
84
|
const branchX = currentX + BRANCH_OFFSET_X
|
|
85
|
-
|
|
85
|
+
|
|
86
86
|
// Calculate branch lengths (horizontal extent)
|
|
87
87
|
const trueBranchLength = config.trueSteps?.length || 0
|
|
88
88
|
const falseBranchLength = config.falseSteps?.length || 0
|
|
89
89
|
const maxBranchLength = Math.max(trueBranchLength, falseBranchLength)
|
|
90
|
-
|
|
90
|
+
|
|
91
91
|
// Calculate true branch height (vertical extent for stacking)
|
|
92
92
|
const trueBranchHeight = trueBranchLength * (NODE_HEIGHT + NODE_SPACING_Y)
|
|
93
|
-
|
|
93
|
+
|
|
94
94
|
// True branch nodes (positioned to the right, above center)
|
|
95
95
|
if (config.trueSteps && config.trueSteps.length > 0) {
|
|
96
96
|
const trueBranchY = startPosition.y + BRANCH_OFFSET_Y
|
|
97
97
|
const trueBranchNodes = flowStepsToNodes(config.trueSteps, {
|
|
98
98
|
onChange,
|
|
99
99
|
onDelete,
|
|
100
|
-
startPosition: {
|
|
101
|
-
x: branchX,
|
|
102
|
-
y: trueBranchY
|
|
100
|
+
startPosition: {
|
|
101
|
+
x: branchX,
|
|
102
|
+
y: trueBranchY,
|
|
103
103
|
},
|
|
104
104
|
idPrefix: `${nodeId}-true-`,
|
|
105
105
|
parentId: nodeId,
|
|
106
106
|
})
|
|
107
|
-
|
|
107
|
+
|
|
108
108
|
nodes.push(...trueBranchNodes)
|
|
109
109
|
maxBranchY = Math.max(maxBranchY, trueBranchY + trueBranchHeight)
|
|
110
110
|
}
|
|
111
|
-
|
|
111
|
+
|
|
112
112
|
// False branch nodes (positioned to the right, below center)
|
|
113
113
|
if (config.falseSteps && config.falseSteps.length > 0) {
|
|
114
114
|
// Position false branch below true branch (or at center + gap if no true branch)
|
|
115
|
-
const falseBranchY =
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
115
|
+
const falseBranchY =
|
|
116
|
+
trueBranchLength > 0
|
|
117
|
+
? startPosition.y + BRANCH_OFFSET_Y + trueBranchHeight + FALSE_BRANCH_GAP
|
|
118
|
+
: startPosition.y + NODE_HEIGHT + NODE_SPACING_Y
|
|
119
|
+
|
|
119
120
|
const falseBranchNodes = flowStepsToNodes(config.falseSteps, {
|
|
120
121
|
onChange,
|
|
121
122
|
onDelete,
|
|
122
|
-
startPosition: {
|
|
123
|
-
x: branchX,
|
|
124
|
-
y: falseBranchY
|
|
123
|
+
startPosition: {
|
|
124
|
+
x: branchX,
|
|
125
|
+
y: falseBranchY,
|
|
125
126
|
},
|
|
126
127
|
idPrefix: `${nodeId}-false-`,
|
|
127
128
|
parentId: nodeId,
|
|
128
129
|
})
|
|
129
|
-
|
|
130
|
+
|
|
130
131
|
nodes.push(...falseBranchNodes)
|
|
131
132
|
const falseBranchHeight = config.falseSteps.length * (NODE_HEIGHT + NODE_SPACING_Y)
|
|
132
133
|
maxBranchY = Math.max(maxBranchY, falseBranchY + falseBranchHeight)
|
|
133
134
|
}
|
|
134
|
-
|
|
135
|
+
|
|
135
136
|
// Skip past the conditional AND all its branch steps
|
|
136
137
|
// Branches start at branchX, so we need to account for:
|
|
137
138
|
// - The offset from conditional to first branch step (BRANCH_OFFSET_X)
|
|
138
139
|
// - All the branch steps (maxBranchLength * (NODE_WIDTH + NODE_SPACING_X))
|
|
139
140
|
if (maxBranchLength > 0) {
|
|
140
|
-
const advance = BRANCH_OFFSET_X +
|
|
141
|
-
console.log(
|
|
141
|
+
const advance = BRANCH_OFFSET_X + maxBranchLength * (NODE_WIDTH + NODE_SPACING_X)
|
|
142
|
+
console.log(
|
|
143
|
+
`[flowStepsToNodes] Conditional "${step.name}" has ${maxBranchLength} branch steps, advancing currentX by ${advance}`
|
|
144
|
+
)
|
|
142
145
|
currentX += advance
|
|
143
146
|
console.log(`[flowStepsToNodes] After conditional, currentX = ${currentX}`)
|
|
144
147
|
} else {
|
|
@@ -162,19 +165,21 @@ export function flowStepsToNodes(
|
|
|
162
165
|
*/
|
|
163
166
|
export function nodesToFlowSteps(nodes: RuntypeNode[]): FlowStep[] {
|
|
164
167
|
// Filter out branch nodes (handled within conditional steps)
|
|
165
|
-
const topLevelNodes = nodes.filter(
|
|
166
|
-
|
|
168
|
+
const topLevelNodes = nodes.filter(
|
|
169
|
+
(n) => !n.parentId && !n.id.includes('-true-') && !n.id.includes('-false-')
|
|
170
|
+
)
|
|
171
|
+
|
|
167
172
|
// Sort by X position to determine order (horizontal layout)
|
|
168
173
|
const sortedNodes = [...topLevelNodes].sort((a, b) => a.position.x - b.position.x)
|
|
169
|
-
|
|
174
|
+
|
|
170
175
|
return sortedNodes.map((node, index) => {
|
|
171
176
|
const step = node.data.step
|
|
172
|
-
|
|
177
|
+
|
|
173
178
|
// Handle conditional steps - extract nested steps
|
|
174
179
|
if (step.type === 'conditional') {
|
|
175
180
|
const trueSteps = extractBranchSteps(nodes, node.id, 'true')
|
|
176
181
|
const falseSteps = extractBranchSteps(nodes, node.id, 'false')
|
|
177
|
-
|
|
182
|
+
|
|
178
183
|
return {
|
|
179
184
|
...step,
|
|
180
185
|
order: index,
|
|
@@ -185,7 +190,7 @@ export function nodesToFlowSteps(nodes: RuntypeNode[]): FlowStep[] {
|
|
|
185
190
|
},
|
|
186
191
|
}
|
|
187
192
|
}
|
|
188
|
-
|
|
193
|
+
|
|
189
194
|
return {
|
|
190
195
|
...step,
|
|
191
196
|
order: index,
|
|
@@ -196,14 +201,18 @@ export function nodesToFlowSteps(nodes: RuntypeNode[]): FlowStep[] {
|
|
|
196
201
|
/**
|
|
197
202
|
* Extract branch steps from conditional node
|
|
198
203
|
*/
|
|
199
|
-
function extractBranchSteps(
|
|
204
|
+
function extractBranchSteps(
|
|
205
|
+
nodes: RuntypeNode[],
|
|
206
|
+
parentId: string,
|
|
207
|
+
branch: 'true' | 'false'
|
|
208
|
+
): FlowStep[] {
|
|
200
209
|
// Match nodes with the branch prefix pattern
|
|
201
210
|
const branchPrefix = `${parentId}-${branch}-`
|
|
202
|
-
const branchNodes = nodes.filter(n => n.id.startsWith(branchPrefix))
|
|
203
|
-
|
|
211
|
+
const branchNodes = nodes.filter((n) => n.id.startsWith(branchPrefix))
|
|
212
|
+
|
|
204
213
|
// Sort by X position (horizontal layout within branches)
|
|
205
214
|
const sortedBranchNodes = [...branchNodes].sort((a, b) => a.position.x - b.position.x)
|
|
206
|
-
|
|
215
|
+
|
|
207
216
|
return sortedBranchNodes.map((node, index) => ({
|
|
208
217
|
...node.data.step,
|
|
209
218
|
order: index,
|
|
@@ -221,24 +230,25 @@ export function createEdgesFromNodes(nodes: RuntypeNode[]): RuntypeEdge[] {
|
|
|
221
230
|
const edges: RuntypeEdge[] = []
|
|
222
231
|
|
|
223
232
|
// Helper to check if a node is a branch node
|
|
224
|
-
const isBranchNode = (n: RuntypeNode) =>
|
|
225
|
-
|
|
233
|
+
const isBranchNode = (n: RuntypeNode) =>
|
|
234
|
+
n.parentId || n.id.includes('-true-') || n.id.includes('-false-')
|
|
235
|
+
|
|
226
236
|
// Get top-level nodes sorted by X position (horizontal layout)
|
|
227
237
|
const topLevelNodes = nodes
|
|
228
|
-
.filter(n => !isBranchNode(n))
|
|
238
|
+
.filter((n) => !isBranchNode(n))
|
|
229
239
|
.sort((a, b) => a.position.x - b.position.x)
|
|
230
|
-
|
|
240
|
+
|
|
231
241
|
// Create sequential edges for top-level nodes
|
|
232
242
|
// Skip conditionals - their branches connect to the next step instead
|
|
233
243
|
for (let i = 0; i < topLevelNodes.length - 1; i++) {
|
|
234
244
|
const sourceNode = topLevelNodes[i]
|
|
235
245
|
const targetNode = topLevelNodes[i + 1]
|
|
236
|
-
|
|
246
|
+
|
|
237
247
|
// Skip edge from conditional - branches will connect to next step
|
|
238
248
|
if (sourceNode.data.step.type === 'conditional') {
|
|
239
249
|
continue
|
|
240
250
|
}
|
|
241
|
-
|
|
251
|
+
|
|
242
252
|
edges.push({
|
|
243
253
|
id: `edge-${sourceNode.id}-${targetNode.id}`,
|
|
244
254
|
source: sourceNode.id,
|
|
@@ -249,29 +259,30 @@ export function createEdgesFromNodes(nodes: RuntypeNode[]): RuntypeEdge[] {
|
|
|
249
259
|
data: { stepOrder: i },
|
|
250
260
|
})
|
|
251
261
|
}
|
|
252
|
-
|
|
262
|
+
|
|
253
263
|
// Create edges for conditional branches
|
|
254
|
-
const conditionalNodes = nodes.filter(
|
|
255
|
-
|
|
264
|
+
const conditionalNodes = nodes.filter(
|
|
265
|
+
(n) => n.data.step.type === 'conditional' && !isBranchNode(n)
|
|
266
|
+
)
|
|
267
|
+
|
|
256
268
|
for (const conditionalNode of conditionalNodes) {
|
|
257
269
|
const conditionalId = conditionalNode.id
|
|
258
|
-
const conditionalIndex = topLevelNodes.findIndex(n => n.id === conditionalId)
|
|
259
|
-
const nextMainStep =
|
|
260
|
-
? topLevelNodes[conditionalIndex + 1]
|
|
261
|
-
|
|
262
|
-
|
|
270
|
+
const conditionalIndex = topLevelNodes.findIndex((n) => n.id === conditionalId)
|
|
271
|
+
const nextMainStep =
|
|
272
|
+
conditionalIndex < topLevelNodes.length - 1 ? topLevelNodes[conditionalIndex + 1] : null
|
|
273
|
+
|
|
263
274
|
// Find true branch nodes (contain -true- after the conditional ID)
|
|
264
275
|
// Sort by X position since branches flow horizontally
|
|
265
276
|
const trueBranchNodes = nodes
|
|
266
|
-
.filter(n => n.id.startsWith(`${conditionalId}-true-`))
|
|
277
|
+
.filter((n) => n.id.startsWith(`${conditionalId}-true-`))
|
|
267
278
|
.sort((a, b) => a.position.x - b.position.x)
|
|
268
|
-
|
|
279
|
+
|
|
269
280
|
// Find false branch nodes (contain -false- after the conditional ID)
|
|
270
281
|
// Sort by X position since branches flow horizontally
|
|
271
282
|
const falseBranchNodes = nodes
|
|
272
|
-
.filter(n => n.id.startsWith(`${conditionalId}-false-`))
|
|
283
|
+
.filter((n) => n.id.startsWith(`${conditionalId}-false-`))
|
|
273
284
|
.sort((a, b) => a.position.x - b.position.x)
|
|
274
|
-
|
|
285
|
+
|
|
275
286
|
// Connect conditional to first true branch node (branch goes right)
|
|
276
287
|
if (trueBranchNodes.length > 0) {
|
|
277
288
|
edges.push({
|
|
@@ -288,7 +299,7 @@ export function createEdgesFromNodes(nodes: RuntypeNode[]): RuntypeEdge[] {
|
|
|
288
299
|
labelBgBorderRadius: 4,
|
|
289
300
|
style: { stroke: '#22c55e', strokeWidth: 2 },
|
|
290
301
|
})
|
|
291
|
-
|
|
302
|
+
|
|
292
303
|
// Connect true branch nodes sequentially (horizontal connections)
|
|
293
304
|
for (let i = 0; i < trueBranchNodes.length - 1; i++) {
|
|
294
305
|
edges.push({
|
|
@@ -301,7 +312,7 @@ export function createEdgesFromNodes(nodes: RuntypeNode[]): RuntypeEdge[] {
|
|
|
301
312
|
style: { stroke: '#22c55e', strokeWidth: 1.5 },
|
|
302
313
|
})
|
|
303
314
|
}
|
|
304
|
-
|
|
315
|
+
|
|
305
316
|
// Connect last true branch node to next main step (convergence)
|
|
306
317
|
if (nextMainStep) {
|
|
307
318
|
const lastTrueNode = trueBranchNodes[trueBranchNodes.length - 1]
|
|
@@ -332,7 +343,7 @@ export function createEdgesFromNodes(nodes: RuntypeNode[]): RuntypeEdge[] {
|
|
|
332
343
|
style: { stroke: '#22c55e', strokeWidth: 2 },
|
|
333
344
|
})
|
|
334
345
|
}
|
|
335
|
-
|
|
346
|
+
|
|
336
347
|
// Connect conditional to first false branch node (branch goes right, below true)
|
|
337
348
|
if (falseBranchNodes.length > 0) {
|
|
338
349
|
edges.push({
|
|
@@ -349,7 +360,7 @@ export function createEdgesFromNodes(nodes: RuntypeNode[]): RuntypeEdge[] {
|
|
|
349
360
|
labelBgBorderRadius: 4,
|
|
350
361
|
style: { stroke: '#ef4444', strokeWidth: 2 },
|
|
351
362
|
})
|
|
352
|
-
|
|
363
|
+
|
|
353
364
|
// Connect false branch nodes sequentially (horizontal connections)
|
|
354
365
|
for (let i = 0; i < falseBranchNodes.length - 1; i++) {
|
|
355
366
|
edges.push({
|
|
@@ -362,7 +373,7 @@ export function createEdgesFromNodes(nodes: RuntypeNode[]): RuntypeEdge[] {
|
|
|
362
373
|
style: { stroke: '#ef4444', strokeWidth: 1.5 },
|
|
363
374
|
})
|
|
364
375
|
}
|
|
365
|
-
|
|
376
|
+
|
|
366
377
|
// Connect last false branch node to next main step (convergence)
|
|
367
378
|
if (nextMainStep) {
|
|
368
379
|
const lastFalseNode = falseBranchNodes[falseBranchNodes.length - 1]
|
|
@@ -393,7 +404,7 @@ export function createEdgesFromNodes(nodes: RuntypeNode[]): RuntypeEdge[] {
|
|
|
393
404
|
style: { stroke: '#ef4444', strokeWidth: 2 },
|
|
394
405
|
})
|
|
395
406
|
}
|
|
396
|
-
|
|
407
|
+
|
|
397
408
|
// Handle case where conditional has no branches - connect directly to next step
|
|
398
409
|
if (trueBranchNodes.length === 0 && falseBranchNodes.length === 0 && nextMainStep) {
|
|
399
410
|
edges.push({
|
|
@@ -406,7 +417,7 @@ export function createEdgesFromNodes(nodes: RuntypeNode[]): RuntypeEdge[] {
|
|
|
406
417
|
})
|
|
407
418
|
}
|
|
408
419
|
}
|
|
409
|
-
|
|
420
|
+
|
|
410
421
|
return edges
|
|
411
422
|
}
|
|
412
423
|
|
|
@@ -419,13 +430,13 @@ export function createEdgesFromNodes(nodes: RuntypeNode[]): RuntypeEdge[] {
|
|
|
419
430
|
*/
|
|
420
431
|
export function getDefaultStepName(type: FlowStepType): string {
|
|
421
432
|
const names: Record<FlowStepType, string> = {
|
|
422
|
-
|
|
433
|
+
prompt: 'AI Prompt',
|
|
423
434
|
'fetch-url': 'Fetch URL',
|
|
424
435
|
'retrieve-record': 'Retrieve Record',
|
|
425
436
|
'fetch-github': 'Fetch GitHub',
|
|
426
437
|
'api-call': 'API Call',
|
|
427
438
|
'transform-data': 'Transform Data',
|
|
428
|
-
|
|
439
|
+
conditional: 'Conditional',
|
|
429
440
|
'set-variable': 'Set Variable',
|
|
430
441
|
'upsert-record': 'Upsert Record',
|
|
431
442
|
'send-email': 'Send Email',
|
|
@@ -433,13 +444,13 @@ export function getDefaultStepName(type: FlowStepType): string {
|
|
|
433
444
|
'send-event': 'Send Event',
|
|
434
445
|
'send-stream': 'Send Stream',
|
|
435
446
|
'update-record': 'Update Record',
|
|
436
|
-
|
|
447
|
+
search: 'Search',
|
|
437
448
|
'generate-embedding': 'Generate Embedding',
|
|
438
449
|
'vector-search': 'Vector Search',
|
|
439
450
|
'tool-call': 'Tool Call',
|
|
440
451
|
'wait-until': 'Wait Until',
|
|
441
452
|
}
|
|
442
|
-
|
|
453
|
+
|
|
443
454
|
return names[type] || type
|
|
444
455
|
}
|
|
445
456
|
|
|
@@ -448,7 +459,7 @@ export function getDefaultStepName(type: FlowStepType): string {
|
|
|
448
459
|
*/
|
|
449
460
|
export function createDefaultStep(type: FlowStepType, order: number = 0): FlowStep {
|
|
450
461
|
const id = `step-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`
|
|
451
|
-
|
|
462
|
+
|
|
452
463
|
const baseStep = {
|
|
453
464
|
id,
|
|
454
465
|
type,
|
|
@@ -456,7 +467,7 @@ export function createDefaultStep(type: FlowStepType, order: number = 0): FlowSt
|
|
|
456
467
|
order,
|
|
457
468
|
enabled: true,
|
|
458
469
|
}
|
|
459
|
-
|
|
470
|
+
|
|
460
471
|
switch (type) {
|
|
461
472
|
case 'prompt':
|
|
462
473
|
return {
|
|
@@ -469,7 +480,7 @@ export function createDefaultStep(type: FlowStepType, order: number = 0): FlowSt
|
|
|
469
480
|
outputVariable: `${type}_result`,
|
|
470
481
|
},
|
|
471
482
|
}
|
|
472
|
-
|
|
483
|
+
|
|
473
484
|
case 'fetch-url':
|
|
474
485
|
return {
|
|
475
486
|
...baseStep,
|
|
@@ -482,7 +493,7 @@ export function createDefaultStep(type: FlowStepType, order: number = 0): FlowSt
|
|
|
482
493
|
outputVariable: 'fetch_result',
|
|
483
494
|
},
|
|
484
495
|
}
|
|
485
|
-
|
|
496
|
+
|
|
486
497
|
case 'transform-data':
|
|
487
498
|
return {
|
|
488
499
|
...baseStep,
|
|
@@ -491,7 +502,7 @@ export function createDefaultStep(type: FlowStepType, order: number = 0): FlowSt
|
|
|
491
502
|
outputVariable: 'transform_result',
|
|
492
503
|
},
|
|
493
504
|
}
|
|
494
|
-
|
|
505
|
+
|
|
495
506
|
case 'conditional':
|
|
496
507
|
return {
|
|
497
508
|
...baseStep,
|
|
@@ -501,7 +512,7 @@ export function createDefaultStep(type: FlowStepType, order: number = 0): FlowSt
|
|
|
501
512
|
falseSteps: [],
|
|
502
513
|
},
|
|
503
514
|
}
|
|
504
|
-
|
|
515
|
+
|
|
505
516
|
case 'send-email':
|
|
506
517
|
return {
|
|
507
518
|
...baseStep,
|
|
@@ -513,7 +524,7 @@ export function createDefaultStep(type: FlowStepType, order: number = 0): FlowSt
|
|
|
513
524
|
outputVariable: 'email_result',
|
|
514
525
|
},
|
|
515
526
|
}
|
|
516
|
-
|
|
527
|
+
|
|
517
528
|
default:
|
|
518
529
|
return {
|
|
519
530
|
...baseStep,
|
|
@@ -541,4 +552,3 @@ export function cloneStep(step: FlowStep): FlowStep {
|
|
|
541
552
|
config: JSON.parse(JSON.stringify(step.config)),
|
|
542
553
|
}
|
|
543
554
|
}
|
|
544
|
-
|
package/src/utils/layout.ts
CHANGED
|
@@ -50,17 +50,17 @@ export function autoLayout(
|
|
|
50
50
|
// Build adjacency map from edges
|
|
51
51
|
const adjacencyMap = new Map<string, string[]>()
|
|
52
52
|
const incomingMap = new Map<string, string[]>()
|
|
53
|
-
|
|
53
|
+
|
|
54
54
|
for (const edge of edges) {
|
|
55
55
|
const existing = adjacencyMap.get(edge.source) || []
|
|
56
56
|
adjacencyMap.set(edge.source, [...existing, edge.target])
|
|
57
|
-
|
|
57
|
+
|
|
58
58
|
const incoming = incomingMap.get(edge.target) || []
|
|
59
59
|
incomingMap.set(edge.target, [...incoming, edge.source])
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
// Find root nodes (nodes with no incoming edges)
|
|
63
|
-
const rootNodes = nodes.filter(node => {
|
|
63
|
+
const rootNodes = nodes.filter((node) => {
|
|
64
64
|
const incoming = incomingMap.get(node.id)
|
|
65
65
|
return !incoming || incoming.length === 0
|
|
66
66
|
})
|
|
@@ -76,7 +76,7 @@ export function autoLayout(
|
|
|
76
76
|
|
|
77
77
|
// Position nodes using BFS
|
|
78
78
|
const queue: Array<{ nodeId: string; x: number; y: number; depth: number }> = []
|
|
79
|
-
|
|
79
|
+
|
|
80
80
|
// Start with root nodes
|
|
81
81
|
let startX = startPosition.x
|
|
82
82
|
for (const rootNode of rootNodes) {
|
|
@@ -86,25 +86,25 @@ export function autoLayout(
|
|
|
86
86
|
|
|
87
87
|
while (queue.length > 0) {
|
|
88
88
|
const { nodeId, x, y, depth } = queue.shift()!
|
|
89
|
-
|
|
89
|
+
|
|
90
90
|
if (visited.has(nodeId)) continue
|
|
91
91
|
visited.add(nodeId)
|
|
92
|
-
|
|
92
|
+
|
|
93
93
|
positionedNodes.set(nodeId, { x, y })
|
|
94
|
-
|
|
94
|
+
|
|
95
95
|
// Get children
|
|
96
96
|
const children = adjacencyMap.get(nodeId) || []
|
|
97
|
-
const node = nodes.find(n => n.id === nodeId)
|
|
98
|
-
|
|
97
|
+
const node = nodes.find((n) => n.id === nodeId)
|
|
98
|
+
|
|
99
99
|
// Check if this is a conditional node
|
|
100
100
|
const isConditional = node?.data.step.type === 'conditional'
|
|
101
|
-
|
|
101
|
+
|
|
102
102
|
if (isConditional && children.length > 0) {
|
|
103
103
|
// Position conditional branches side by side
|
|
104
|
-
const trueBranch = children.filter(c => c.includes('-true-'))
|
|
105
|
-
const falseBranch = children.filter(c => c.includes('-false-'))
|
|
106
|
-
const normalChildren = children.filter(c => !c.includes('-true-') && !c.includes('-false-'))
|
|
107
|
-
|
|
104
|
+
const trueBranch = children.filter((c) => c.includes('-true-'))
|
|
105
|
+
const falseBranch = children.filter((c) => c.includes('-false-'))
|
|
106
|
+
const normalChildren = children.filter((c) => !c.includes('-true-') && !c.includes('-false-'))
|
|
107
|
+
|
|
108
108
|
// Position true branch to the right
|
|
109
109
|
let trueY = y + nodeHeight + verticalSpacing
|
|
110
110
|
for (const childId of trueBranch) {
|
|
@@ -118,7 +118,7 @@ export function autoLayout(
|
|
|
118
118
|
trueY += nodeHeight + verticalSpacing
|
|
119
119
|
}
|
|
120
120
|
}
|
|
121
|
-
|
|
121
|
+
|
|
122
122
|
// Position false branch to the left
|
|
123
123
|
let falseY = y + nodeHeight + verticalSpacing
|
|
124
124
|
for (const childId of falseBranch) {
|
|
@@ -132,7 +132,7 @@ export function autoLayout(
|
|
|
132
132
|
falseY += nodeHeight + verticalSpacing
|
|
133
133
|
}
|
|
134
134
|
}
|
|
135
|
-
|
|
135
|
+
|
|
136
136
|
// Position normal children below
|
|
137
137
|
const maxBranchY = Math.max(trueY, falseY)
|
|
138
138
|
let childY = maxBranchY
|
|
@@ -151,7 +151,7 @@ export function autoLayout(
|
|
|
151
151
|
// Position children in sequence
|
|
152
152
|
let childY = y + nodeHeight + verticalSpacing
|
|
153
153
|
let childX = x
|
|
154
|
-
|
|
154
|
+
|
|
155
155
|
for (let i = 0; i < children.length; i++) {
|
|
156
156
|
const childId = children[i]
|
|
157
157
|
if (!visited.has(childId)) {
|
|
@@ -178,7 +178,7 @@ export function autoLayout(
|
|
|
178
178
|
}
|
|
179
179
|
|
|
180
180
|
// Apply positions to nodes
|
|
181
|
-
return nodes.map(node => {
|
|
181
|
+
return nodes.map((node) => {
|
|
182
182
|
const position = positionedNodes.get(node.id)
|
|
183
183
|
if (position) {
|
|
184
184
|
return {
|
|
@@ -220,7 +220,7 @@ export function centerNodes(
|
|
|
220
220
|
const offsetY = (viewportHeight - contentHeight) / 2 - minY
|
|
221
221
|
|
|
222
222
|
// Apply offset
|
|
223
|
-
return nodes.map(node => ({
|
|
223
|
+
return nodes.map((node) => ({
|
|
224
224
|
...node,
|
|
225
225
|
position: {
|
|
226
226
|
x: node.position.x + offsetX,
|
|
@@ -232,11 +232,8 @@ export function centerNodes(
|
|
|
232
232
|
/**
|
|
233
233
|
* Align nodes to a grid
|
|
234
234
|
*/
|
|
235
|
-
export function snapToGrid(
|
|
236
|
-
nodes
|
|
237
|
-
gridSize: number = 20
|
|
238
|
-
): RuntypeNode[] {
|
|
239
|
-
return nodes.map(node => ({
|
|
235
|
+
export function snapToGrid(nodes: RuntypeNode[], gridSize: number = 20): RuntypeNode[] {
|
|
236
|
+
return nodes.map((node) => ({
|
|
240
237
|
...node,
|
|
241
238
|
position: {
|
|
242
239
|
x: Math.round(node.position.x / gridSize) * gridSize,
|
|
@@ -281,4 +278,3 @@ export function getNodesBoundingBox(nodes: RuntypeNode[]): {
|
|
|
281
278
|
height: maxY - minY,
|
|
282
279
|
}
|
|
283
280
|
}
|
|
284
|
-
|
package/tsconfig.json
CHANGED
package/tsup.config.ts
CHANGED