@remoraflow/ui 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/LICENSE +674 -0
  2. package/dist/edges/workflow-edge.d.ts +3 -0
  3. package/dist/edges/workflow-edge.d.ts.map +1 -0
  4. package/dist/execution-state.d.ts +34 -0
  5. package/dist/execution-state.d.ts.map +1 -0
  6. package/dist/graph-layout.d.ts +17 -0
  7. package/dist/graph-layout.d.ts.map +1 -0
  8. package/dist/index.d.ts +10 -0
  9. package/dist/index.d.ts.map +1 -0
  10. package/dist/index.js +2708 -0
  11. package/dist/index.js.map +28 -0
  12. package/dist/nodes/agent-loop-node.d.ts +3 -0
  13. package/dist/nodes/agent-loop-node.d.ts.map +1 -0
  14. package/dist/nodes/base-node.d.ts +20 -0
  15. package/dist/nodes/base-node.d.ts.map +1 -0
  16. package/dist/nodes/end-node.d.ts +3 -0
  17. package/dist/nodes/end-node.d.ts.map +1 -0
  18. package/dist/nodes/extract-data-node.d.ts +3 -0
  19. package/dist/nodes/extract-data-node.d.ts.map +1 -0
  20. package/dist/nodes/for-each-node.d.ts +3 -0
  21. package/dist/nodes/for-each-node.d.ts.map +1 -0
  22. package/dist/nodes/group-header-node.d.ts +3 -0
  23. package/dist/nodes/group-header-node.d.ts.map +1 -0
  24. package/dist/nodes/llm-prompt-node.d.ts +3 -0
  25. package/dist/nodes/llm-prompt-node.d.ts.map +1 -0
  26. package/dist/nodes/sleep-node.d.ts +3 -0
  27. package/dist/nodes/sleep-node.d.ts.map +1 -0
  28. package/dist/nodes/start-node.d.ts +3 -0
  29. package/dist/nodes/start-node.d.ts.map +1 -0
  30. package/dist/nodes/start-step-node.d.ts +3 -0
  31. package/dist/nodes/start-step-node.d.ts.map +1 -0
  32. package/dist/nodes/switch-case-node.d.ts +3 -0
  33. package/dist/nodes/switch-case-node.d.ts.map +1 -0
  34. package/dist/nodes/tool-call-node.d.ts +3 -0
  35. package/dist/nodes/tool-call-node.d.ts.map +1 -0
  36. package/dist/nodes/wait-for-condition-node.d.ts +3 -0
  37. package/dist/nodes/wait-for-condition-node.d.ts.map +1 -0
  38. package/dist/panels/step-detail-panel.d.ts +11 -0
  39. package/dist/panels/step-detail-panel.d.ts.map +1 -0
  40. package/dist/theme.d.ts +17 -0
  41. package/dist/theme.d.ts.map +1 -0
  42. package/dist/workflow-viewer.d.ts +41 -0
  43. package/dist/workflow-viewer.d.ts.map +1 -0
  44. package/package.json +48 -0
package/dist/index.js ADDED
@@ -0,0 +1,2708 @@
1
+ // src/execution-state.ts
2
+ var STATUS_PRIORITY = {
3
+ failed: 4,
4
+ running: 3,
5
+ completed: 2,
6
+ skipped: 1,
7
+ pending: 0
8
+ };
9
+ function worstStatus(a, b) {
10
+ return STATUS_PRIORITY[a] >= STATUS_PRIORITY[b] ? a : b;
11
+ }
12
+ function filterToLatestIteration(records) {
13
+ const latestIteration = new Map;
14
+ for (const record of records) {
15
+ for (const seg of record.path) {
16
+ if (seg.type === "for-each") {
17
+ const prev = latestIteration.get(seg.stepId) ?? -1;
18
+ if (seg.iterationIndex > prev) {
19
+ latestIteration.set(seg.stepId, seg.iterationIndex);
20
+ }
21
+ }
22
+ }
23
+ }
24
+ if (latestIteration.size === 0)
25
+ return records;
26
+ return records.filter((record) => {
27
+ for (const seg of record.path) {
28
+ if (seg.type === "for-each") {
29
+ const latest = latestIteration.get(seg.stepId);
30
+ if (latest !== undefined && seg.iterationIndex !== latest) {
31
+ return false;
32
+ }
33
+ }
34
+ }
35
+ return true;
36
+ });
37
+ }
38
+ function deriveStepSummaries(state) {
39
+ const filtered = filterToLatestIteration(state.stepRecords);
40
+ const grouped = new Map;
41
+ for (const record of filtered) {
42
+ const existing = grouped.get(record.stepId);
43
+ if (existing) {
44
+ existing.push(record);
45
+ } else {
46
+ grouped.set(record.stepId, [record]);
47
+ }
48
+ }
49
+ const summaries = new Map;
50
+ for (const [stepId, records] of grouped) {
51
+ let status = "pending";
52
+ let completedCount = 0;
53
+ let failedCount = 0;
54
+ let totalRetries = 0;
55
+ let latestOutput;
56
+ let latestError;
57
+ let latestDurationMs;
58
+ let latestResolvedInputs;
59
+ let latestTrace;
60
+ for (const record of records) {
61
+ status = worstStatus(status, record.status);
62
+ if (record.status === "completed")
63
+ completedCount++;
64
+ if (record.status === "failed")
65
+ failedCount++;
66
+ totalRetries += record.retries.length;
67
+ if (record.output !== undefined)
68
+ latestOutput = record.output;
69
+ if (record.error) {
70
+ latestError = {
71
+ code: record.error.code,
72
+ message: record.error.message
73
+ };
74
+ }
75
+ if (record.durationMs !== undefined) {
76
+ latestDurationMs = record.durationMs;
77
+ }
78
+ if (record.resolvedInputs !== undefined) {
79
+ latestResolvedInputs = record.resolvedInputs;
80
+ }
81
+ if (record.trace !== undefined) {
82
+ latestTrace = record.trace;
83
+ }
84
+ }
85
+ summaries.set(stepId, {
86
+ status,
87
+ executionCount: records.length,
88
+ completedCount,
89
+ failedCount,
90
+ totalRetries,
91
+ latestOutput,
92
+ latestError,
93
+ latestDurationMs,
94
+ latestResolvedInputs,
95
+ latestTrace
96
+ });
97
+ }
98
+ return summaries;
99
+ }
100
+ // src/graph-layout.ts
101
+ import dagre from "@dagrejs/dagre";
102
+ var NODE_WIDTH = 300;
103
+ var NODE_HEIGHT = 180;
104
+ var END_NODE_SIZE = 60;
105
+ var START_NODE_SIZE = 60;
106
+ var GROUP_HEADER_WIDTH = 280;
107
+ var GROUP_HEADER_HEIGHT = 80;
108
+ var GROUP_PADDING = 30;
109
+ var GROUP_HEADER = 40;
110
+ var START_NODE_ID = "__start__";
111
+ function getOrThrow(map, key) {
112
+ const val = map.get(key);
113
+ if (val === undefined)
114
+ throw new Error(`Missing map key: ${String(key)}`);
115
+ return val;
116
+ }
117
+ function stepNodeType(step) {
118
+ switch (step.type) {
119
+ case "tool-call":
120
+ return "toolCall";
121
+ case "llm-prompt":
122
+ return "llmPrompt";
123
+ case "extract-data":
124
+ return "extractData";
125
+ case "switch-case":
126
+ return "switchCase";
127
+ case "for-each":
128
+ return "forEach";
129
+ case "start":
130
+ return "startStep";
131
+ case "end":
132
+ return "end";
133
+ case "sleep":
134
+ return "sleep";
135
+ case "wait-for-condition":
136
+ return "waitForCondition";
137
+ case "agent-loop":
138
+ return "agentLoop";
139
+ }
140
+ }
141
+ function renderExpression(expr) {
142
+ if (expr.type === "literal")
143
+ return JSON.stringify(expr.value);
144
+ if (expr.type === "template")
145
+ return expr.template;
146
+ return expr.expression;
147
+ }
148
+ function nodeSize(step, workflow) {
149
+ if (step.type === "end" && !step.params?.output && !workflow?.outputSchema) {
150
+ return { w: END_NODE_SIZE, h: END_NODE_SIZE };
151
+ }
152
+ return { w: NODE_WIDTH, h: NODE_HEIGHT };
153
+ }
154
+ function collectChildSteps(step, stepMap) {
155
+ const children = new Set;
156
+ const continuation = step.nextStepId;
157
+ const seeds = [];
158
+ if (step.type === "for-each") {
159
+ seeds.push(step.params.loopBodyStepId);
160
+ } else if (step.type === "switch-case") {
161
+ for (const c of step.params.cases) {
162
+ seeds.push(c.branchBodyStepId);
163
+ }
164
+ } else if (step.type === "wait-for-condition") {
165
+ seeds.push(step.params.conditionStepId);
166
+ } else {
167
+ return children;
168
+ }
169
+ const queue = [...seeds];
170
+ while (queue.length > 0) {
171
+ const id = queue.shift();
172
+ if (id === undefined)
173
+ continue;
174
+ if (children.has(id) || id === continuation)
175
+ continue;
176
+ const s = stepMap.get(id);
177
+ if (!s)
178
+ continue;
179
+ children.add(id);
180
+ if (s.nextStepId)
181
+ queue.push(s.nextStepId);
182
+ if (s.type === "switch-case") {
183
+ for (const c of s.params.cases)
184
+ queue.push(c.branchBodyStepId);
185
+ }
186
+ if (s.type === "for-each") {
187
+ queue.push(s.params.loopBodyStepId);
188
+ }
189
+ if (s.type === "wait-for-condition") {
190
+ queue.push(s.params.conditionStepId);
191
+ }
192
+ }
193
+ return children;
194
+ }
195
+ function buildParentMap(workflow, stepMap) {
196
+ const parentMap = new Map;
197
+ const allGroupChildren = new Map;
198
+ for (const step of workflow.steps) {
199
+ if (step.type === "for-each" || step.type === "switch-case" || step.type === "wait-for-condition") {
200
+ allGroupChildren.set(step.id, collectChildSteps(step, stepMap));
201
+ }
202
+ }
203
+ for (const [groupId, children] of allGroupChildren) {
204
+ for (const childId of children) {
205
+ const currentParent = parentMap.get(childId);
206
+ if (!currentParent) {
207
+ parentMap.set(childId, groupId);
208
+ } else {
209
+ const currentParentChildren = allGroupChildren.get(currentParent);
210
+ if (currentParentChildren?.has(groupId)) {
211
+ parentMap.set(childId, groupId);
212
+ }
213
+ }
214
+ }
215
+ }
216
+ return parentMap;
217
+ }
218
+ function getDirectChildren(groupId, parentMap) {
219
+ const direct = new Set;
220
+ for (const [childId, pid] of parentMap) {
221
+ if (pid === groupId)
222
+ direct.add(childId);
223
+ }
224
+ return direct;
225
+ }
226
+ function groupProcessingOrder(groupIds, parentMap) {
227
+ const order = [];
228
+ const visited = new Set;
229
+ function visit(id) {
230
+ if (visited.has(id))
231
+ return;
232
+ visited.add(id);
233
+ for (const [childId, pid] of parentMap) {
234
+ if (pid === id && groupIds.has(childId)) {
235
+ visit(childId);
236
+ }
237
+ }
238
+ order.push(id);
239
+ }
240
+ for (const id of groupIds) {
241
+ visit(id);
242
+ }
243
+ return order;
244
+ }
245
+ function groupHeaderId(groupId) {
246
+ return `__header__${groupId}`;
247
+ }
248
+ function getNodeDimensions(nodeId, groupIds, computedSizes, stepMap, workflow) {
249
+ if (groupIds.has(nodeId)) {
250
+ return getOrThrow(computedSizes, nodeId);
251
+ }
252
+ return nodeSize(getOrThrow(stepMap, nodeId), workflow);
253
+ }
254
+ function buildLayout(workflow, diagnostics = [], executionState) {
255
+ const stepSummaries = executionState ? deriveStepSummaries(executionState) : undefined;
256
+ const diagnosticsByStep = new Map;
257
+ for (const d of diagnostics) {
258
+ if (d.location.stepId) {
259
+ const existing = diagnosticsByStep.get(d.location.stepId) ?? [];
260
+ existing.push(d);
261
+ diagnosticsByStep.set(d.location.stepId, existing);
262
+ }
263
+ }
264
+ const stepMap = new Map;
265
+ for (const step of workflow.steps) {
266
+ stepMap.set(step.id, step);
267
+ }
268
+ const parentMap = buildParentMap(workflow, stepMap);
269
+ const groupIds = new Set;
270
+ for (const step of workflow.steps) {
271
+ if (step.type === "for-each" || step.type === "switch-case" || step.type === "wait-for-condition") {
272
+ const hasChildren = [...parentMap.values()].some((pid) => pid === step.id);
273
+ if (hasChildren)
274
+ groupIds.add(step.id);
275
+ }
276
+ }
277
+ const processOrder = groupProcessingOrder(groupIds, parentMap);
278
+ const computedSizes = new Map;
279
+ const groupChildPositions = new Map;
280
+ for (const groupId of processOrder) {
281
+ const groupStep = getOrThrow(stepMap, groupId);
282
+ const directChildren = getDirectChildren(groupId, parentMap);
283
+ if (directChildren.size === 0) {
284
+ groupIds.delete(groupId);
285
+ continue;
286
+ }
287
+ const subG = new dagre.graphlib.Graph;
288
+ subG.setGraph({ rankdir: "TB", ranksep: 60, nodesep: 40 });
289
+ subG.setDefaultEdgeLabel(() => ({}));
290
+ const headerId = groupHeaderId(groupId);
291
+ subG.setNode(headerId, {
292
+ width: GROUP_HEADER_WIDTH,
293
+ height: GROUP_HEADER_HEIGHT
294
+ });
295
+ for (const childId of directChildren) {
296
+ const { w, h } = getNodeDimensions(childId, groupIds, computedSizes, stepMap, workflow);
297
+ subG.setNode(childId, { width: w, height: h });
298
+ }
299
+ if (groupStep.type === "switch-case") {
300
+ for (const c of groupStep.params.cases) {
301
+ if (directChildren.has(c.branchBodyStepId)) {
302
+ subG.setEdge(headerId, c.branchBodyStepId);
303
+ }
304
+ }
305
+ } else if (groupStep.type === "for-each") {
306
+ if (directChildren.has(groupStep.params.loopBodyStepId)) {
307
+ subG.setEdge(headerId, groupStep.params.loopBodyStepId);
308
+ }
309
+ } else if (groupStep.type === "wait-for-condition") {
310
+ if (directChildren.has(groupStep.params.conditionStepId)) {
311
+ subG.setEdge(headerId, groupStep.params.conditionStepId);
312
+ }
313
+ }
314
+ for (const childId of directChildren) {
315
+ const step = getOrThrow(stepMap, childId);
316
+ if (step.nextStepId && directChildren.has(step.nextStepId)) {
317
+ subG.setEdge(childId, step.nextStepId);
318
+ }
319
+ if (step.type === "switch-case") {
320
+ for (const c of step.params.cases) {
321
+ if (directChildren.has(c.branchBodyStepId)) {
322
+ subG.setEdge(childId, c.branchBodyStepId);
323
+ }
324
+ }
325
+ }
326
+ if (step.type === "for-each") {
327
+ if (directChildren.has(step.params.loopBodyStepId)) {
328
+ subG.setEdge(childId, step.params.loopBodyStepId);
329
+ }
330
+ }
331
+ if (step.type === "wait-for-condition") {
332
+ if (directChildren.has(step.params.conditionStepId)) {
333
+ subG.setEdge(childId, step.params.conditionStepId);
334
+ }
335
+ }
336
+ }
337
+ dagre.layout(subG);
338
+ let minX = Number.POSITIVE_INFINITY;
339
+ let minY = Number.POSITIVE_INFINITY;
340
+ let maxX = Number.NEGATIVE_INFINITY;
341
+ let maxY = Number.NEGATIVE_INFINITY;
342
+ const allSubNodes = new Set(directChildren);
343
+ allSubNodes.add(headerId);
344
+ const rawPositions = new Map;
345
+ for (const nodeId of allSubNodes) {
346
+ const pos = subG.node(nodeId);
347
+ let w;
348
+ let h;
349
+ if (nodeId === headerId) {
350
+ w = GROUP_HEADER_WIDTH;
351
+ h = GROUP_HEADER_HEIGHT;
352
+ } else {
353
+ const dims = getNodeDimensions(nodeId, groupIds, computedSizes, stepMap, workflow);
354
+ w = dims.w;
355
+ h = dims.h;
356
+ }
357
+ const x = pos.x - w / 2;
358
+ const y = pos.y - h / 2;
359
+ rawPositions.set(nodeId, { x, y });
360
+ minX = Math.min(minX, x);
361
+ minY = Math.min(minY, y);
362
+ maxX = Math.max(maxX, x + w);
363
+ maxY = Math.max(maxY, y + h);
364
+ }
365
+ const positions = new Map;
366
+ for (const [nodeId, pos] of rawPositions) {
367
+ positions.set(nodeId, {
368
+ x: pos.x - minX + GROUP_PADDING,
369
+ y: pos.y - minY + GROUP_HEADER
370
+ });
371
+ }
372
+ groupChildPositions.set(groupId, positions);
373
+ const contentWidth = maxX - minX;
374
+ const contentHeight = maxY - minY;
375
+ computedSizes.set(groupId, {
376
+ w: contentWidth + GROUP_PADDING * 2,
377
+ h: contentHeight + GROUP_HEADER + GROUP_PADDING
378
+ });
379
+ }
380
+ const topG = new dagre.graphlib.Graph;
381
+ topG.setGraph({ rankdir: "TB", ranksep: 80, nodesep: 60 });
382
+ topG.setDefaultEdgeLabel(() => ({}));
383
+ const initialStep = stepMap.get(workflow.initialStepId);
384
+ const hasStartStep = initialStep?.type === "start";
385
+ if (!hasStartStep) {
386
+ topG.setNode(START_NODE_ID, {
387
+ width: START_NODE_SIZE,
388
+ height: START_NODE_SIZE
389
+ });
390
+ }
391
+ const topLevelStepIds = [];
392
+ for (const step of workflow.steps) {
393
+ if (!parentMap.has(step.id)) {
394
+ topLevelStepIds.push(step.id);
395
+ const { w, h } = getNodeDimensions(step.id, groupIds, computedSizes, stepMap, workflow);
396
+ topG.setNode(step.id, { width: w, height: h });
397
+ }
398
+ }
399
+ if (!hasStartStep) {
400
+ topG.setEdge(START_NODE_ID, workflow.initialStepId);
401
+ }
402
+ const topLevelSet = new Set(topLevelStepIds);
403
+ for (const stepId of topLevelStepIds) {
404
+ const step = getOrThrow(stepMap, stepId);
405
+ if (step.nextStepId && topLevelSet.has(step.nextStepId)) {
406
+ topG.setEdge(stepId, step.nextStepId);
407
+ }
408
+ if (step.type === "switch-case") {
409
+ for (const c of step.params.cases) {
410
+ if (topLevelSet.has(c.branchBodyStepId)) {
411
+ topG.setEdge(stepId, c.branchBodyStepId);
412
+ }
413
+ }
414
+ }
415
+ if (step.type === "for-each") {
416
+ if (topLevelSet.has(step.params.loopBodyStepId)) {
417
+ topG.setEdge(stepId, step.params.loopBodyStepId);
418
+ }
419
+ }
420
+ if (step.type === "wait-for-condition") {
421
+ if (topLevelSet.has(step.params.conditionStepId)) {
422
+ topG.setEdge(stepId, step.params.conditionStepId);
423
+ }
424
+ }
425
+ }
426
+ dagre.layout(topG);
427
+ const topLevelPositions = new Map;
428
+ if (!hasStartStep) {
429
+ const startPos = topG.node(START_NODE_ID);
430
+ topLevelPositions.set(START_NODE_ID, {
431
+ x: startPos.x - START_NODE_SIZE / 2,
432
+ y: startPos.y - START_NODE_SIZE / 2
433
+ });
434
+ }
435
+ for (const stepId of topLevelStepIds) {
436
+ const pos = topG.node(stepId);
437
+ const { w, h } = getNodeDimensions(stepId, groupIds, computedSizes, stepMap, workflow);
438
+ topLevelPositions.set(stepId, {
439
+ x: pos.x - w / 2,
440
+ y: pos.y - h / 2
441
+ });
442
+ }
443
+ const nodes = [];
444
+ if (!hasStartStep) {
445
+ const startNodePos = getOrThrow(topLevelPositions, START_NODE_ID);
446
+ nodes.push({
447
+ id: START_NODE_ID,
448
+ type: "start",
449
+ position: startNodePos,
450
+ data: {},
451
+ selectable: false
452
+ });
453
+ }
454
+ function addNodesForContext(nodeIds, getPosition, parentId) {
455
+ const groups = [];
456
+ const headers = [];
457
+ const nonGroups = [];
458
+ for (const id of nodeIds) {
459
+ if (groupIds.has(id))
460
+ groups.push(id);
461
+ else if (id.startsWith("__header__"))
462
+ headers.push(id);
463
+ else
464
+ nonGroups.push(id);
465
+ }
466
+ for (const id of groups) {
467
+ const step = getOrThrow(stepMap, id);
468
+ const pos = getPosition(id);
469
+ const size = getOrThrow(computedSizes, id);
470
+ nodes.push({
471
+ id,
472
+ type: stepNodeType(step),
473
+ position: pos,
474
+ data: {
475
+ step,
476
+ diagnostics: diagnosticsByStep.get(id) ?? [],
477
+ isGroup: true,
478
+ groupWidth: size.w,
479
+ groupHeight: size.h,
480
+ hasSourceEdge: !!step.nextStepId,
481
+ executionSummary: stepSummaries?.get(id)
482
+ },
483
+ style: { width: size.w, height: size.h },
484
+ ...parentId ? { parentId, extent: "parent" } : {}
485
+ });
486
+ const childPositions = getOrThrow(groupChildPositions, id);
487
+ addNodesForContext(childPositions.keys(), (childId) => getOrThrow(childPositions, childId), id);
488
+ }
489
+ for (const id of headers) {
490
+ const pos = getPosition(id);
491
+ const gid = id.replace("__header__", "");
492
+ const step = getOrThrow(stepMap, gid);
493
+ const summary = stepSummaries?.get(gid);
494
+ const resolvedInputs = summary?.latestResolvedInputs;
495
+ if (step.type === "switch-case") {
496
+ nodes.push({
497
+ id,
498
+ type: "groupHeader",
499
+ position: pos,
500
+ data: {
501
+ variant: "switch",
502
+ description: step.description,
503
+ expression: renderExpression(step.params.switchOn),
504
+ resolvedExpression: resolvedInputs?.switchOn,
505
+ step,
506
+ diagnostics: diagnosticsByStep.get(gid) ?? []
507
+ },
508
+ ...parentId ? { parentId, extent: "parent" } : {}
509
+ });
510
+ } else if (step.type === "for-each") {
511
+ nodes.push({
512
+ id,
513
+ type: "groupHeader",
514
+ position: pos,
515
+ data: {
516
+ variant: "loop",
517
+ description: step.description,
518
+ target: renderExpression(step.params.target),
519
+ resolvedTarget: resolvedInputs?.target,
520
+ itemName: step.params.itemName,
521
+ step,
522
+ diagnostics: diagnosticsByStep.get(gid) ?? []
523
+ },
524
+ ...parentId ? { parentId, extent: "parent" } : {}
525
+ });
526
+ } else if (step.type === "wait-for-condition") {
527
+ nodes.push({
528
+ id,
529
+ type: "groupHeader",
530
+ position: pos,
531
+ data: {
532
+ variant: "condition",
533
+ description: step.description,
534
+ condition: renderExpression(step.params.condition)
535
+ },
536
+ ...parentId ? { parentId, extent: "parent" } : {}
537
+ });
538
+ }
539
+ }
540
+ for (const id of nonGroups) {
541
+ const step = getOrThrow(stepMap, id);
542
+ const pos = getPosition(id);
543
+ const nodeData = {
544
+ step,
545
+ diagnostics: diagnosticsByStep.get(id) ?? [],
546
+ hasSourceEdge: !!step.nextStepId,
547
+ executionSummary: stepSummaries?.get(id)
548
+ };
549
+ if (step.type === "start" && workflow.inputSchema) {
550
+ nodeData.inputSchema = workflow.inputSchema;
551
+ }
552
+ if (step.type === "end" && workflow.outputSchema) {
553
+ nodeData.outputSchema = workflow.outputSchema;
554
+ }
555
+ nodes.push({
556
+ id,
557
+ type: stepNodeType(step),
558
+ position: pos,
559
+ data: nodeData,
560
+ ...parentId ? { parentId, extent: "parent" } : {}
561
+ });
562
+ }
563
+ }
564
+ addNodesForContext(topLevelStepIds, (id) => getOrThrow(topLevelPositions, id));
565
+ const edges = [];
566
+ const hasExecState = !!stepSummaries;
567
+ function isStepExecuted(stepId) {
568
+ const s = stepSummaries?.get(stepId);
569
+ return s?.status === "completed" || s?.status === "running";
570
+ }
571
+ if (!hasStartStep) {
572
+ edges.push({
573
+ id: `${START_NODE_ID}->${workflow.initialStepId}`,
574
+ source: START_NODE_ID,
575
+ target: workflow.initialStepId,
576
+ type: "workflow",
577
+ data: {
578
+ edgeKind: "sequential",
579
+ executed: hasExecState && isStepExecuted(workflow.initialStepId),
580
+ hasExecutionState: hasExecState
581
+ }
582
+ });
583
+ }
584
+ for (const step of workflow.steps) {
585
+ if (step.type === "switch-case") {
586
+ if (groupIds.has(step.id)) {
587
+ const headerId = groupHeaderId(step.id);
588
+ for (const c of step.params.cases) {
589
+ const label = c.value.type === "default" ? "default" : renderExpression(c.value);
590
+ edges.push({
591
+ id: `${headerId}->${c.branchBodyStepId}`,
592
+ source: headerId,
593
+ target: c.branchBodyStepId,
594
+ label,
595
+ type: "workflow",
596
+ data: {
597
+ edgeKind: "branch",
598
+ executed: hasExecState && isStepExecuted(c.branchBodyStepId),
599
+ hasExecutionState: hasExecState
600
+ }
601
+ });
602
+ }
603
+ }
604
+ if (step.nextStepId) {
605
+ edges.push({
606
+ id: `${step.id}->${step.nextStepId}`,
607
+ source: step.id,
608
+ target: step.nextStepId,
609
+ type: "workflow",
610
+ data: {
611
+ edgeKind: "sequential",
612
+ executed: hasExecState && isStepExecuted(step.nextStepId),
613
+ hasExecutionState: hasExecState
614
+ }
615
+ });
616
+ }
617
+ } else if (step.type === "for-each") {
618
+ if (groupIds.has(step.id)) {
619
+ const headerId = groupHeaderId(step.id);
620
+ edges.push({
621
+ id: `${headerId}->${step.params.loopBodyStepId}`,
622
+ source: headerId,
623
+ target: step.params.loopBodyStepId,
624
+ type: "workflow",
625
+ data: {
626
+ edgeKind: "sequential",
627
+ executed: hasExecState && isStepExecuted(step.params.loopBodyStepId),
628
+ hasExecutionState: hasExecState
629
+ }
630
+ });
631
+ }
632
+ if (step.nextStepId) {
633
+ edges.push({
634
+ id: `${step.id}->${step.nextStepId}`,
635
+ source: step.id,
636
+ target: step.nextStepId,
637
+ type: "workflow",
638
+ data: {
639
+ edgeKind: "sequential",
640
+ executed: hasExecState && isStepExecuted(step.nextStepId),
641
+ hasExecutionState: hasExecState
642
+ }
643
+ });
644
+ }
645
+ } else if (step.type === "wait-for-condition") {
646
+ if (groupIds.has(step.id)) {
647
+ const headerId = groupHeaderId(step.id);
648
+ edges.push({
649
+ id: `${headerId}->${step.params.conditionStepId}`,
650
+ source: headerId,
651
+ target: step.params.conditionStepId,
652
+ type: "workflow",
653
+ data: { edgeKind: "sequential" }
654
+ });
655
+ }
656
+ if (step.nextStepId) {
657
+ edges.push({
658
+ id: `${step.id}->${step.nextStepId}`,
659
+ source: step.id,
660
+ target: step.nextStepId,
661
+ type: "workflow",
662
+ data: { edgeKind: "sequential" }
663
+ });
664
+ }
665
+ } else if (step.nextStepId) {
666
+ edges.push({
667
+ id: `${step.id}->${step.nextStepId}`,
668
+ source: step.id,
669
+ target: step.nextStepId,
670
+ type: "workflow",
671
+ data: {
672
+ edgeKind: "sequential",
673
+ executed: hasExecState && isStepExecuted(step.nextStepId),
674
+ hasExecutionState: hasExecState
675
+ }
676
+ });
677
+ }
678
+ }
679
+ return { nodes, edges };
680
+ }
681
+ // src/panels/step-detail-panel.tsx
682
+ import { jsxDEV } from "react/jsx-dev-runtime";
683
+ function renderExpression2(expr) {
684
+ if (expr.type === "literal")
685
+ return JSON.stringify(expr.value);
686
+ if (expr.type === "template")
687
+ return expr.template;
688
+ return expr.expression;
689
+ }
690
+ function TypeBadge({ type }) {
691
+ const colors = {
692
+ "tool-call": "bg-blue-100 text-blue-700 dark:bg-blue-900/50 dark:text-blue-400",
693
+ "llm-prompt": "bg-violet-100 text-violet-700 dark:bg-violet-900/50 dark:text-violet-400",
694
+ "extract-data": "bg-purple-100 text-purple-700 dark:bg-purple-900/50 dark:text-purple-400",
695
+ "switch-case": "bg-amber-100 text-amber-700 dark:bg-amber-900/50 dark:text-amber-400",
696
+ "for-each": "bg-emerald-100 text-emerald-700 dark:bg-emerald-900/50 dark:text-emerald-400",
697
+ "agent-loop": "bg-teal-100 text-teal-700 dark:bg-teal-900/50 dark:text-teal-400",
698
+ sleep: "bg-yellow-100 text-yellow-700 dark:bg-yellow-900/50 dark:text-yellow-400",
699
+ "wait-for-condition": "bg-orange-100 text-orange-700 dark:bg-orange-900/50 dark:text-orange-400",
700
+ start: "bg-green-100 text-green-700 dark:bg-green-900/50 dark:text-green-400",
701
+ end: "bg-muted text-muted-foreground"
702
+ };
703
+ const fallback = "bg-muted text-muted-foreground";
704
+ return /* @__PURE__ */ jsxDEV("span", {
705
+ className: `text-[11px] font-medium px-2 py-0.5 rounded-full ${colors[type] ?? fallback}`,
706
+ children: type
707
+ }, undefined, false, undefined, this);
708
+ }
709
+ function StatusBadge({ summary }) {
710
+ const colors = {
711
+ pending: "bg-muted text-muted-foreground",
712
+ running: "bg-blue-100 text-blue-700 dark:bg-blue-900/50 dark:text-blue-400",
713
+ completed: "bg-green-100 text-green-700 dark:bg-green-900/50 dark:text-green-400",
714
+ failed: "bg-red-100 text-red-700 dark:bg-red-900/50 dark:text-red-400",
715
+ skipped: "bg-muted text-muted-foreground"
716
+ };
717
+ return /* @__PURE__ */ jsxDEV("span", {
718
+ className: `text-[11px] font-medium px-2 py-0.5 rounded-full ${colors[summary.status]}`,
719
+ children: summary.status
720
+ }, undefined, false, undefined, this);
721
+ }
722
+ function ResolvedCode({
723
+ value,
724
+ expression
725
+ }) {
726
+ const display = typeof value === "string" ? value : JSON.stringify(value, null, 2);
727
+ return /* @__PURE__ */ jsxDEV("pre", {
728
+ className: "text-xs text-emerald-700 bg-emerald-50 dark:text-emerald-400 dark:bg-emerald-950/50 rounded p-2 whitespace-pre-wrap font-mono overflow-auto max-h-[200px] cursor-default",
729
+ title: expression,
730
+ children: display
731
+ }, undefined, false, undefined, this);
732
+ }
733
+ function StepParams({
734
+ step,
735
+ resolvedInputs
736
+ }) {
737
+ const resolved = resolvedInputs;
738
+ switch (step.type) {
739
+ case "tool-call":
740
+ return /* @__PURE__ */ jsxDEV("div", {
741
+ className: "space-y-2",
742
+ children: [
743
+ /* @__PURE__ */ jsxDEV("div", {
744
+ children: [
745
+ /* @__PURE__ */ jsxDEV(Label, {
746
+ children: "Tool"
747
+ }, undefined, false, undefined, this),
748
+ /* @__PURE__ */ jsxDEV(Code, {
749
+ children: step.params.toolName
750
+ }, undefined, false, undefined, this)
751
+ ]
752
+ }, undefined, true, undefined, this),
753
+ Object.keys(step.params.toolInput).length > 0 && /* @__PURE__ */ jsxDEV("div", {
754
+ children: [
755
+ /* @__PURE__ */ jsxDEV(Label, {
756
+ children: "Inputs"
757
+ }, undefined, false, undefined, this),
758
+ /* @__PURE__ */ jsxDEV("div", {
759
+ className: "space-y-1",
760
+ children: Object.entries(step.params.toolInput).map(([key, val]) => {
761
+ const resolvedVal = resolved?.[key];
762
+ const hasResolved = resolvedVal !== undefined;
763
+ return /* @__PURE__ */ jsxDEV("div", {
764
+ className: "flex gap-2 text-xs",
765
+ children: [
766
+ /* @__PURE__ */ jsxDEV("span", {
767
+ className: "font-mono text-muted-foreground",
768
+ children: [
769
+ key,
770
+ ":"
771
+ ]
772
+ }, undefined, true, undefined, this),
773
+ /* @__PURE__ */ jsxDEV("span", {
774
+ className: `font-mono ${hasResolved ? "text-emerald-700 dark:text-emerald-400" : "text-foreground"}`,
775
+ title: hasResolved ? renderExpression2(val) : undefined,
776
+ children: hasResolved ? typeof resolvedVal === "string" ? resolvedVal : JSON.stringify(resolvedVal) : renderExpression2(val)
777
+ }, undefined, false, undefined, this)
778
+ ]
779
+ }, key, true, undefined, this);
780
+ })
781
+ }, undefined, false, undefined, this)
782
+ ]
783
+ }, undefined, true, undefined, this)
784
+ ]
785
+ }, undefined, true, undefined, this);
786
+ case "llm-prompt":
787
+ return /* @__PURE__ */ jsxDEV("div", {
788
+ className: "space-y-2",
789
+ children: [
790
+ /* @__PURE__ */ jsxDEV("div", {
791
+ children: [
792
+ /* @__PURE__ */ jsxDEV(Label, {
793
+ children: "Prompt"
794
+ }, undefined, false, undefined, this),
795
+ resolved?.prompt ? /* @__PURE__ */ jsxDEV(ResolvedCode, {
796
+ value: resolved.prompt,
797
+ expression: step.params.prompt
798
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV("pre", {
799
+ className: "text-xs rounded p-2 whitespace-pre-wrap font-mono text-foreground bg-muted",
800
+ children: step.params.prompt
801
+ }, undefined, false, undefined, this)
802
+ ]
803
+ }, undefined, true, undefined, this),
804
+ /* @__PURE__ */ jsxDEV("div", {
805
+ children: [
806
+ /* @__PURE__ */ jsxDEV(Label, {
807
+ children: "Output Format"
808
+ }, undefined, false, undefined, this),
809
+ /* @__PURE__ */ jsxDEV(Code, {
810
+ children: JSON.stringify(step.params.outputFormat, null, 2)
811
+ }, undefined, false, undefined, this)
812
+ ]
813
+ }, undefined, true, undefined, this)
814
+ ]
815
+ }, undefined, true, undefined, this);
816
+ case "extract-data":
817
+ return /* @__PURE__ */ jsxDEV("div", {
818
+ className: "space-y-2",
819
+ children: [
820
+ /* @__PURE__ */ jsxDEV("div", {
821
+ children: [
822
+ /* @__PURE__ */ jsxDEV(Label, {
823
+ children: "Source"
824
+ }, undefined, false, undefined, this),
825
+ resolved?.sourceData !== undefined ? /* @__PURE__ */ jsxDEV(ResolvedCode, {
826
+ value: resolved.sourceData,
827
+ expression: renderExpression2(step.params.sourceData)
828
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV(Code, {
829
+ children: renderExpression2(step.params.sourceData)
830
+ }, undefined, false, undefined, this)
831
+ ]
832
+ }, undefined, true, undefined, this),
833
+ /* @__PURE__ */ jsxDEV("div", {
834
+ children: [
835
+ /* @__PURE__ */ jsxDEV(Label, {
836
+ children: "Output Format"
837
+ }, undefined, false, undefined, this),
838
+ /* @__PURE__ */ jsxDEV(Code, {
839
+ children: JSON.stringify(step.params.outputFormat, null, 2)
840
+ }, undefined, false, undefined, this)
841
+ ]
842
+ }, undefined, true, undefined, this)
843
+ ]
844
+ }, undefined, true, undefined, this);
845
+ case "switch-case":
846
+ return /* @__PURE__ */ jsxDEV("div", {
847
+ className: "space-y-2",
848
+ children: [
849
+ /* @__PURE__ */ jsxDEV("div", {
850
+ children: [
851
+ /* @__PURE__ */ jsxDEV(Label, {
852
+ children: "Switch On"
853
+ }, undefined, false, undefined, this),
854
+ resolved?.switchOn !== undefined ? /* @__PURE__ */ jsxDEV(ResolvedCode, {
855
+ value: resolved.switchOn,
856
+ expression: renderExpression2(step.params.switchOn)
857
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV(Code, {
858
+ children: renderExpression2(step.params.switchOn)
859
+ }, undefined, false, undefined, this)
860
+ ]
861
+ }, undefined, true, undefined, this),
862
+ /* @__PURE__ */ jsxDEV("div", {
863
+ children: [
864
+ /* @__PURE__ */ jsxDEV(Label, {
865
+ children: "Cases"
866
+ }, undefined, false, undefined, this),
867
+ /* @__PURE__ */ jsxDEV("div", {
868
+ className: "space-y-1",
869
+ children: step.params.cases.map((c) => /* @__PURE__ */ jsxDEV("div", {
870
+ className: "text-xs flex gap-2",
871
+ children: [
872
+ /* @__PURE__ */ jsxDEV("span", {
873
+ className: "font-mono text-muted-foreground",
874
+ children: c.value.type === "default" ? "default" : renderExpression2(c.value)
875
+ }, undefined, false, undefined, this),
876
+ /* @__PURE__ */ jsxDEV("span", {
877
+ className: "text-muted-foreground/50",
878
+ children: "→"
879
+ }, undefined, false, undefined, this),
880
+ /* @__PURE__ */ jsxDEV("span", {
881
+ className: "font-mono text-foreground",
882
+ children: c.branchBodyStepId
883
+ }, undefined, false, undefined, this)
884
+ ]
885
+ }, c.branchBodyStepId, true, undefined, this))
886
+ }, undefined, false, undefined, this)
887
+ ]
888
+ }, undefined, true, undefined, this)
889
+ ]
890
+ }, undefined, true, undefined, this);
891
+ case "for-each":
892
+ return /* @__PURE__ */ jsxDEV("div", {
893
+ className: "space-y-2",
894
+ children: [
895
+ /* @__PURE__ */ jsxDEV("div", {
896
+ children: [
897
+ /* @__PURE__ */ jsxDEV(Label, {
898
+ children: "Target"
899
+ }, undefined, false, undefined, this),
900
+ resolved?.target !== undefined ? /* @__PURE__ */ jsxDEV(ResolvedCode, {
901
+ value: resolved.target,
902
+ expression: renderExpression2(step.params.target)
903
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV(Code, {
904
+ children: renderExpression2(step.params.target)
905
+ }, undefined, false, undefined, this)
906
+ ]
907
+ }, undefined, true, undefined, this),
908
+ /* @__PURE__ */ jsxDEV("div", {
909
+ children: [
910
+ /* @__PURE__ */ jsxDEV(Label, {
911
+ children: "Item Variable"
912
+ }, undefined, false, undefined, this),
913
+ /* @__PURE__ */ jsxDEV(Code, {
914
+ children: step.params.itemName
915
+ }, undefined, false, undefined, this)
916
+ ]
917
+ }, undefined, true, undefined, this),
918
+ /* @__PURE__ */ jsxDEV("div", {
919
+ children: [
920
+ /* @__PURE__ */ jsxDEV(Label, {
921
+ children: "Loop Body"
922
+ }, undefined, false, undefined, this),
923
+ /* @__PURE__ */ jsxDEV(Code, {
924
+ children: step.params.loopBodyStepId
925
+ }, undefined, false, undefined, this)
926
+ ]
927
+ }, undefined, true, undefined, this)
928
+ ]
929
+ }, undefined, true, undefined, this);
930
+ case "sleep":
931
+ return /* @__PURE__ */ jsxDEV("div", {
932
+ className: "space-y-2",
933
+ children: /* @__PURE__ */ jsxDEV("div", {
934
+ children: [
935
+ /* @__PURE__ */ jsxDEV(Label, {
936
+ children: "Duration"
937
+ }, undefined, false, undefined, this),
938
+ resolved?.durationMs !== undefined ? /* @__PURE__ */ jsxDEV(ResolvedCode, {
939
+ value: `${resolved.durationMs}ms`,
940
+ expression: renderExpression2(step.params.durationMs)
941
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV(Code, {
942
+ children: [
943
+ renderExpression2(step.params.durationMs),
944
+ "ms"
945
+ ]
946
+ }, undefined, true, undefined, this)
947
+ ]
948
+ }, undefined, true, undefined, this)
949
+ }, undefined, false, undefined, this);
950
+ case "wait-for-condition":
951
+ return /* @__PURE__ */ jsxDEV("div", {
952
+ className: "space-y-2",
953
+ children: [
954
+ /* @__PURE__ */ jsxDEV("div", {
955
+ children: [
956
+ /* @__PURE__ */ jsxDEV(Label, {
957
+ children: "Condition"
958
+ }, undefined, false, undefined, this),
959
+ resolved?.condition !== undefined ? /* @__PURE__ */ jsxDEV(ResolvedCode, {
960
+ value: resolved.condition,
961
+ expression: renderExpression2(step.params.condition)
962
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV(Code, {
963
+ children: renderExpression2(step.params.condition)
964
+ }, undefined, false, undefined, this)
965
+ ]
966
+ }, undefined, true, undefined, this),
967
+ /* @__PURE__ */ jsxDEV("div", {
968
+ children: [
969
+ /* @__PURE__ */ jsxDEV(Label, {
970
+ children: "Condition Step"
971
+ }, undefined, false, undefined, this),
972
+ /* @__PURE__ */ jsxDEV(Code, {
973
+ children: step.params.conditionStepId
974
+ }, undefined, false, undefined, this)
975
+ ]
976
+ }, undefined, true, undefined, this),
977
+ step.params.maxAttempts && /* @__PURE__ */ jsxDEV("div", {
978
+ children: [
979
+ /* @__PURE__ */ jsxDEV(Label, {
980
+ children: "Max Attempts"
981
+ }, undefined, false, undefined, this),
982
+ /* @__PURE__ */ jsxDEV(Code, {
983
+ children: renderExpression2(step.params.maxAttempts)
984
+ }, undefined, false, undefined, this)
985
+ ]
986
+ }, undefined, true, undefined, this),
987
+ step.params.intervalMs && /* @__PURE__ */ jsxDEV("div", {
988
+ children: [
989
+ /* @__PURE__ */ jsxDEV(Label, {
990
+ children: "Interval"
991
+ }, undefined, false, undefined, this),
992
+ /* @__PURE__ */ jsxDEV(Code, {
993
+ children: [
994
+ renderExpression2(step.params.intervalMs),
995
+ "ms"
996
+ ]
997
+ }, undefined, true, undefined, this)
998
+ ]
999
+ }, undefined, true, undefined, this),
1000
+ step.params.timeoutMs && /* @__PURE__ */ jsxDEV("div", {
1001
+ children: [
1002
+ /* @__PURE__ */ jsxDEV(Label, {
1003
+ children: "Timeout"
1004
+ }, undefined, false, undefined, this),
1005
+ /* @__PURE__ */ jsxDEV(Code, {
1006
+ children: [
1007
+ renderExpression2(step.params.timeoutMs),
1008
+ "ms"
1009
+ ]
1010
+ }, undefined, true, undefined, this)
1011
+ ]
1012
+ }, undefined, true, undefined, this)
1013
+ ]
1014
+ }, undefined, true, undefined, this);
1015
+ case "agent-loop":
1016
+ return /* @__PURE__ */ jsxDEV("div", {
1017
+ className: "space-y-2",
1018
+ children: [
1019
+ /* @__PURE__ */ jsxDEV("div", {
1020
+ children: [
1021
+ /* @__PURE__ */ jsxDEV(Label, {
1022
+ children: "Instructions"
1023
+ }, undefined, false, undefined, this),
1024
+ /* @__PURE__ */ jsxDEV("pre", {
1025
+ className: "text-xs rounded p-2 whitespace-pre-wrap font-mono text-foreground bg-muted",
1026
+ children: step.params.instructions
1027
+ }, undefined, false, undefined, this)
1028
+ ]
1029
+ }, undefined, true, undefined, this),
1030
+ step.params.tools.length > 0 && /* @__PURE__ */ jsxDEV("div", {
1031
+ children: [
1032
+ /* @__PURE__ */ jsxDEV(Label, {
1033
+ children: "Tools"
1034
+ }, undefined, false, undefined, this),
1035
+ /* @__PURE__ */ jsxDEV(Code, {
1036
+ children: step.params.tools.join(", ")
1037
+ }, undefined, false, undefined, this)
1038
+ ]
1039
+ }, undefined, true, undefined, this),
1040
+ /* @__PURE__ */ jsxDEV("div", {
1041
+ children: [
1042
+ /* @__PURE__ */ jsxDEV(Label, {
1043
+ children: "Output Format"
1044
+ }, undefined, false, undefined, this),
1045
+ /* @__PURE__ */ jsxDEV(Code, {
1046
+ children: JSON.stringify(step.params.outputFormat, null, 2)
1047
+ }, undefined, false, undefined, this)
1048
+ ]
1049
+ }, undefined, true, undefined, this),
1050
+ step.params.maxSteps && /* @__PURE__ */ jsxDEV("div", {
1051
+ children: [
1052
+ /* @__PURE__ */ jsxDEV(Label, {
1053
+ children: "Max Steps"
1054
+ }, undefined, false, undefined, this),
1055
+ /* @__PURE__ */ jsxDEV(Code, {
1056
+ children: renderExpression2(step.params.maxSteps)
1057
+ }, undefined, false, undefined, this)
1058
+ ]
1059
+ }, undefined, true, undefined, this)
1060
+ ]
1061
+ }, undefined, true, undefined, this);
1062
+ case "start":
1063
+ return null;
1064
+ case "end":
1065
+ if (step.params?.output) {
1066
+ return /* @__PURE__ */ jsxDEV("div", {
1067
+ className: "space-y-2",
1068
+ children: /* @__PURE__ */ jsxDEV("div", {
1069
+ children: [
1070
+ /* @__PURE__ */ jsxDEV(Label, {
1071
+ children: "Output"
1072
+ }, undefined, false, undefined, this),
1073
+ resolved?.output !== undefined ? /* @__PURE__ */ jsxDEV(ResolvedCode, {
1074
+ value: resolved.output,
1075
+ expression: renderExpression2(step.params.output)
1076
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV(Code, {
1077
+ children: renderExpression2(step.params.output)
1078
+ }, undefined, false, undefined, this)
1079
+ ]
1080
+ }, undefined, true, undefined, this)
1081
+ }, undefined, false, undefined, this);
1082
+ }
1083
+ return null;
1084
+ }
1085
+ }
1086
+ function Label({ children }) {
1087
+ return /* @__PURE__ */ jsxDEV("div", {
1088
+ className: "text-[11px] font-medium uppercase tracking-wide mb-0.5 text-muted-foreground",
1089
+ children
1090
+ }, undefined, false, undefined, this);
1091
+ }
1092
+ function Code({ children }) {
1093
+ return /* @__PURE__ */ jsxDEV("pre", {
1094
+ className: "text-xs rounded p-2 whitespace-pre-wrap font-mono overflow-auto max-h-[200px] text-foreground bg-muted",
1095
+ children
1096
+ }, undefined, false, undefined, this);
1097
+ }
1098
+ function formatPathSegment(seg) {
1099
+ switch (seg.type) {
1100
+ case "for-each":
1101
+ return `Iteration ${seg.iterationIndex}: ${formatValue(seg.itemValue)}`;
1102
+ case "switch-case":
1103
+ return `Case ${seg.matchedCaseIndex}: ${formatValue(seg.matchedValue)}`;
1104
+ case "wait-for-condition":
1105
+ return `Poll attempt ${seg.pollAttempt}`;
1106
+ }
1107
+ }
1108
+ function formatValue(value) {
1109
+ if (typeof value === "string")
1110
+ return value;
1111
+ return JSON.stringify(value);
1112
+ }
1113
+ var recordStatusColors = {
1114
+ pending: "bg-muted text-muted-foreground",
1115
+ running: "bg-blue-100 text-blue-700 dark:bg-blue-900/50 dark:text-blue-400",
1116
+ completed: "bg-green-100 text-green-700 dark:bg-green-900/50 dark:text-green-400",
1117
+ failed: "bg-red-100 text-red-700 dark:bg-red-900/50 dark:text-red-400",
1118
+ skipped: "bg-muted text-muted-foreground"
1119
+ };
1120
+ function ExecutionRecordCard({ record }) {
1121
+ const pathLabel = record.path.length > 0 ? record.path.map(formatPathSegment).join(" > ") : null;
1122
+ return /* @__PURE__ */ jsxDEV("div", {
1123
+ className: "border border-border rounded-md p-2 space-y-1.5",
1124
+ children: [
1125
+ pathLabel && /* @__PURE__ */ jsxDEV("div", {
1126
+ className: "text-[11px] font-medium text-muted-foreground",
1127
+ children: pathLabel
1128
+ }, undefined, false, undefined, this),
1129
+ /* @__PURE__ */ jsxDEV("div", {
1130
+ className: "flex items-center gap-2",
1131
+ children: [
1132
+ /* @__PURE__ */ jsxDEV("span", {
1133
+ className: `text-[11px] font-medium px-2 py-0.5 rounded-full ${recordStatusColors[record.status]}`,
1134
+ children: record.status
1135
+ }, undefined, false, undefined, this),
1136
+ record.durationMs !== undefined && /* @__PURE__ */ jsxDEV("span", {
1137
+ className: "text-[11px] text-muted-foreground",
1138
+ children: [
1139
+ record.durationMs,
1140
+ "ms"
1141
+ ]
1142
+ }, undefined, true, undefined, this),
1143
+ record.retries.length > 0 && /* @__PURE__ */ jsxDEV("span", {
1144
+ className: "text-[11px] text-amber-600 dark:text-amber-400",
1145
+ children: [
1146
+ record.retries.length,
1147
+ " ",
1148
+ record.retries.length === 1 ? "retry" : "retries"
1149
+ ]
1150
+ }, undefined, true, undefined, this)
1151
+ ]
1152
+ }, undefined, true, undefined, this),
1153
+ record.resolvedInputs !== undefined && /* @__PURE__ */ jsxDEV("details", {
1154
+ className: "text-xs",
1155
+ children: [
1156
+ /* @__PURE__ */ jsxDEV("summary", {
1157
+ className: "text-[11px] font-medium text-muted-foreground uppercase tracking-wide cursor-pointer select-none",
1158
+ children: "Resolved Inputs"
1159
+ }, undefined, false, undefined, this),
1160
+ /* @__PURE__ */ jsxDEV(ResolvedCode, {
1161
+ value: record.resolvedInputs
1162
+ }, undefined, false, undefined, this)
1163
+ ]
1164
+ }, undefined, true, undefined, this),
1165
+ record.output !== undefined && /* @__PURE__ */ jsxDEV("details", {
1166
+ className: "text-xs",
1167
+ children: [
1168
+ /* @__PURE__ */ jsxDEV("summary", {
1169
+ className: "text-[11px] font-medium text-muted-foreground uppercase tracking-wide cursor-pointer select-none",
1170
+ children: "Output"
1171
+ }, undefined, false, undefined, this),
1172
+ /* @__PURE__ */ jsxDEV(Code, {
1173
+ children: typeof record.output === "string" ? record.output : JSON.stringify(record.output, null, 2)
1174
+ }, undefined, false, undefined, this)
1175
+ ]
1176
+ }, undefined, true, undefined, this),
1177
+ record.error && /* @__PURE__ */ jsxDEV("div", {
1178
+ className: "text-xs p-2 rounded bg-red-50 text-red-700 border border-red-200 dark:bg-red-900/30 dark:text-red-400 dark:border-red-800",
1179
+ children: [
1180
+ /* @__PURE__ */ jsxDEV("div", {
1181
+ className: "font-medium font-mono",
1182
+ children: record.error.code
1183
+ }, undefined, false, undefined, this),
1184
+ /* @__PURE__ */ jsxDEV("div", {
1185
+ className: "mt-0.5",
1186
+ children: record.error.message
1187
+ }, undefined, false, undefined, this)
1188
+ ]
1189
+ }, undefined, true, undefined, this)
1190
+ ]
1191
+ }, undefined, true, undefined, this);
1192
+ }
1193
+ function StepDetailPanel({
1194
+ step,
1195
+ diagnostics,
1196
+ executionSummary,
1197
+ executionRecords,
1198
+ onClose
1199
+ }) {
1200
+ return /* @__PURE__ */ jsxDEV("div", {
1201
+ className: "w-[340px] border-l h-full min-h-0 overflow-y-auto shadow-lg bg-card border-border",
1202
+ children: [
1203
+ /* @__PURE__ */ jsxDEV("div", {
1204
+ className: "sticky top-0 border-b px-4 py-3 flex items-center justify-between bg-card border-border",
1205
+ children: [
1206
+ /* @__PURE__ */ jsxDEV("div", {
1207
+ className: "flex items-center gap-2",
1208
+ children: [
1209
+ /* @__PURE__ */ jsxDEV(TypeBadge, {
1210
+ type: step.type
1211
+ }, undefined, false, undefined, this),
1212
+ /* @__PURE__ */ jsxDEV("span", {
1213
+ className: "font-medium text-sm truncate text-foreground",
1214
+ children: step.name
1215
+ }, undefined, false, undefined, this)
1216
+ ]
1217
+ }, undefined, true, undefined, this),
1218
+ /* @__PURE__ */ jsxDEV("button", {
1219
+ type: "button",
1220
+ onClick: onClose,
1221
+ className: "text-lg leading-none text-muted-foreground hover:text-foreground",
1222
+ children: "×"
1223
+ }, undefined, false, undefined, this)
1224
+ ]
1225
+ }, undefined, true, undefined, this),
1226
+ /* @__PURE__ */ jsxDEV("div", {
1227
+ className: "px-4 py-3 space-y-4",
1228
+ children: [
1229
+ /* @__PURE__ */ jsxDEV("div", {
1230
+ children: [
1231
+ /* @__PURE__ */ jsxDEV(Label, {
1232
+ children: "Step ID"
1233
+ }, undefined, false, undefined, this),
1234
+ /* @__PURE__ */ jsxDEV("div", {
1235
+ className: "text-xs font-mono text-muted-foreground",
1236
+ children: step.id
1237
+ }, undefined, false, undefined, this)
1238
+ ]
1239
+ }, undefined, true, undefined, this),
1240
+ /* @__PURE__ */ jsxDEV("div", {
1241
+ children: [
1242
+ /* @__PURE__ */ jsxDEV(Label, {
1243
+ children: "Description"
1244
+ }, undefined, false, undefined, this),
1245
+ /* @__PURE__ */ jsxDEV("div", {
1246
+ className: "text-xs text-muted-foreground",
1247
+ children: step.description
1248
+ }, undefined, false, undefined, this)
1249
+ ]
1250
+ }, undefined, true, undefined, this),
1251
+ step.nextStepId && /* @__PURE__ */ jsxDEV("div", {
1252
+ children: [
1253
+ /* @__PURE__ */ jsxDEV(Label, {
1254
+ children: "Next Step"
1255
+ }, undefined, false, undefined, this),
1256
+ /* @__PURE__ */ jsxDEV("div", {
1257
+ className: "text-xs font-mono text-muted-foreground",
1258
+ children: step.nextStepId
1259
+ }, undefined, false, undefined, this)
1260
+ ]
1261
+ }, undefined, true, undefined, this),
1262
+ /* @__PURE__ */ jsxDEV("div", {
1263
+ className: "border-t pt-3 border-border",
1264
+ children: [
1265
+ /* @__PURE__ */ jsxDEV(Label, {
1266
+ children: "Parameters"
1267
+ }, undefined, false, undefined, this),
1268
+ /* @__PURE__ */ jsxDEV("div", {
1269
+ className: "mt-1",
1270
+ children: /* @__PURE__ */ jsxDEV(StepParams, {
1271
+ step,
1272
+ resolvedInputs: executionRecords?.length ? executionRecords[executionRecords.length - 1]?.resolvedInputs : undefined
1273
+ }, undefined, false, undefined, this)
1274
+ }, undefined, false, undefined, this)
1275
+ ]
1276
+ }, undefined, true, undefined, this),
1277
+ executionSummary && /* @__PURE__ */ jsxDEV("div", {
1278
+ className: "border-t border-border pt-3",
1279
+ children: [
1280
+ /* @__PURE__ */ jsxDEV(Label, {
1281
+ children: "Execution"
1282
+ }, undefined, false, undefined, this),
1283
+ /* @__PURE__ */ jsxDEV("div", {
1284
+ className: "mt-1 space-y-2",
1285
+ children: [
1286
+ /* @__PURE__ */ jsxDEV("div", {
1287
+ className: "flex items-center gap-2",
1288
+ children: [
1289
+ /* @__PURE__ */ jsxDEV(StatusBadge, {
1290
+ summary: executionSummary
1291
+ }, undefined, false, undefined, this),
1292
+ executionSummary.latestDurationMs !== undefined && /* @__PURE__ */ jsxDEV("span", {
1293
+ className: "text-[11px] text-muted-foreground",
1294
+ children: [
1295
+ executionSummary.latestDurationMs,
1296
+ "ms"
1297
+ ]
1298
+ }, undefined, true, undefined, this),
1299
+ executionSummary.executionCount > 1 && /* @__PURE__ */ jsxDEV("span", {
1300
+ className: "text-[11px] text-muted-foreground",
1301
+ children: [
1302
+ "(",
1303
+ executionSummary.completedCount,
1304
+ "/",
1305
+ executionSummary.executionCount,
1306
+ " iterations)"
1307
+ ]
1308
+ }, undefined, true, undefined, this)
1309
+ ]
1310
+ }, undefined, true, undefined, this),
1311
+ executionSummary.latestOutput !== undefined && /* @__PURE__ */ jsxDEV("div", {
1312
+ children: [
1313
+ /* @__PURE__ */ jsxDEV(Label, {
1314
+ children: "Output"
1315
+ }, undefined, false, undefined, this),
1316
+ /* @__PURE__ */ jsxDEV(Code, {
1317
+ children: typeof executionSummary.latestOutput === "string" ? executionSummary.latestOutput : JSON.stringify(executionSummary.latestOutput, null, 2)
1318
+ }, undefined, false, undefined, this)
1319
+ ]
1320
+ }, undefined, true, undefined, this),
1321
+ executionSummary.latestError && /* @__PURE__ */ jsxDEV("div", {
1322
+ className: "text-xs p-2 rounded bg-red-50 text-red-700 border border-red-200 dark:bg-red-900/30 dark:text-red-400 dark:border-red-800",
1323
+ children: [
1324
+ /* @__PURE__ */ jsxDEV("div", {
1325
+ className: "font-medium font-mono",
1326
+ children: executionSummary.latestError.code
1327
+ }, undefined, false, undefined, this),
1328
+ /* @__PURE__ */ jsxDEV("div", {
1329
+ className: "mt-0.5",
1330
+ children: executionSummary.latestError.message
1331
+ }, undefined, false, undefined, this)
1332
+ ]
1333
+ }, undefined, true, undefined, this),
1334
+ executionSummary.totalRetries > 0 && /* @__PURE__ */ jsxDEV("div", {
1335
+ className: "text-[11px] text-amber-600 dark:text-amber-400",
1336
+ children: [
1337
+ executionSummary.totalRetries,
1338
+ " ",
1339
+ executionSummary.totalRetries === 1 ? "retry" : "retries",
1340
+ " ",
1341
+ "attempted"
1342
+ ]
1343
+ }, undefined, true, undefined, this)
1344
+ ]
1345
+ }, undefined, true, undefined, this)
1346
+ ]
1347
+ }, undefined, true, undefined, this),
1348
+ executionRecords && executionRecords.length > 0 && /* @__PURE__ */ jsxDEV("div", {
1349
+ className: "border-t border-border pt-3",
1350
+ children: [
1351
+ /* @__PURE__ */ jsxDEV(Label, {
1352
+ children: "Execution History"
1353
+ }, undefined, false, undefined, this),
1354
+ /* @__PURE__ */ jsxDEV("div", {
1355
+ className: "space-y-2 mt-1",
1356
+ children: executionRecords.map((record, i) => /* @__PURE__ */ jsxDEV(ExecutionRecordCard, {
1357
+ record
1358
+ }, `${record.stepId}-${i}`, false, undefined, this))
1359
+ }, undefined, false, undefined, this)
1360
+ ]
1361
+ }, undefined, true, undefined, this),
1362
+ diagnostics.length > 0 && /* @__PURE__ */ jsxDEV("div", {
1363
+ className: "border-t pt-3 border-border",
1364
+ children: [
1365
+ /* @__PURE__ */ jsxDEV(Label, {
1366
+ children: "Diagnostics"
1367
+ }, undefined, false, undefined, this),
1368
+ /* @__PURE__ */ jsxDEV("div", {
1369
+ className: "space-y-2 mt-1",
1370
+ children: diagnostics.map((d) => /* @__PURE__ */ jsxDEV("div", {
1371
+ className: `text-xs p-2 rounded ${d.severity === "error" ? "bg-red-50 text-red-700 border border-red-200 dark:bg-red-900/30 dark:text-red-400 dark:border-red-800" : "bg-amber-50 text-amber-700 border border-amber-200 dark:bg-amber-900/30 dark:text-amber-400 dark:border-amber-800"}`,
1372
+ children: [
1373
+ /* @__PURE__ */ jsxDEV("div", {
1374
+ className: "font-medium font-mono",
1375
+ children: d.code
1376
+ }, undefined, false, undefined, this),
1377
+ /* @__PURE__ */ jsxDEV("div", {
1378
+ className: "mt-0.5",
1379
+ children: d.message
1380
+ }, undefined, false, undefined, this)
1381
+ ]
1382
+ }, `${d.code}-${d.message}`, true, undefined, this))
1383
+ }, undefined, false, undefined, this)
1384
+ ]
1385
+ }, undefined, true, undefined, this)
1386
+ ]
1387
+ }, undefined, true, undefined, this)
1388
+ ]
1389
+ }, undefined, true, undefined, this);
1390
+ }
1391
+ // src/theme.tsx
1392
+ import { useEffect, useMemo, useState } from "react";
1393
+ function useDarkMode() {
1394
+ const [dark, setDark] = useState(() => typeof document !== "undefined" && document.documentElement.classList.contains("dark"));
1395
+ useEffect(() => {
1396
+ const el = document.documentElement;
1397
+ const observer = new MutationObserver(() => {
1398
+ setDark(el.classList.contains("dark"));
1399
+ });
1400
+ observer.observe(el, { attributes: true, attributeFilter: ["class"] });
1401
+ setDark(el.classList.contains("dark"));
1402
+ return () => observer.disconnect();
1403
+ }, []);
1404
+ return dark;
1405
+ }
1406
+ function resolveCssColor(varName, fallback) {
1407
+ if (typeof document === "undefined")
1408
+ return fallback;
1409
+ const raw = getComputedStyle(document.documentElement).getPropertyValue(varName).trim();
1410
+ if (!raw)
1411
+ return fallback;
1412
+ if (/^\d/.test(raw) && !raw.startsWith("0x")) {
1413
+ return `hsl(${raw})`;
1414
+ }
1415
+ return raw;
1416
+ }
1417
+ function useThemeColors() {
1418
+ const dark = useDarkMode();
1419
+ return useMemo(() => {
1420
+ const resolve = (v, fb) => resolveCssColor(v, fb);
1421
+ return {
1422
+ dark,
1423
+ border: resolve("--border", dark ? "#374151" : "#e5e7eb"),
1424
+ mutedForeground: resolve("--muted-foreground", dark ? "#9ca3af" : "#6b7280"),
1425
+ card: resolve("--card", dark ? "#1f2937" : "#ffffff")
1426
+ };
1427
+ }, [dark]);
1428
+ }
1429
+ // src/workflow-viewer.tsx
1430
+ import {
1431
+ Background,
1432
+ Controls,
1433
+ MiniMap,
1434
+ ReactFlow,
1435
+ useEdgesState,
1436
+ useNodesState
1437
+ } from "@xyflow/react";
1438
+ import { useCallback, useEffect as useEffect2, useMemo as useMemo2, useRef, useState as useState2 } from "react";
1439
+
1440
+ // src/edges/workflow-edge.tsx
1441
+ import {
1442
+ BaseEdge,
1443
+ EdgeLabelRenderer,
1444
+ getBezierPath
1445
+ } from "@xyflow/react";
1446
+ import { jsxDEV as jsxDEV2, Fragment } from "react/jsx-dev-runtime";
1447
+ function WorkflowEdge({
1448
+ id,
1449
+ sourceX,
1450
+ sourceY,
1451
+ targetX,
1452
+ targetY,
1453
+ sourcePosition,
1454
+ targetPosition,
1455
+ label,
1456
+ data,
1457
+ markerEnd,
1458
+ style
1459
+ }) {
1460
+ const theme = useThemeColors();
1461
+ const edgeKind = data?.edgeKind ?? "sequential";
1462
+ const isContinuation = edgeKind === "continuation";
1463
+ const isExecuted = data?.executed === true;
1464
+ const hasExecutionState = data?.hasExecutionState === true;
1465
+ const [edgePath, labelX, labelY] = getBezierPath({
1466
+ sourceX,
1467
+ sourceY,
1468
+ sourcePosition,
1469
+ targetX,
1470
+ targetY,
1471
+ targetPosition
1472
+ });
1473
+ let stroke = theme.mutedForeground;
1474
+ let strokeWidth = 1.5;
1475
+ let opacity = isContinuation ? 0.5 : 1;
1476
+ if (hasExecutionState) {
1477
+ if (isExecuted) {
1478
+ stroke = "#22c55e";
1479
+ strokeWidth = 2.5;
1480
+ opacity = 1;
1481
+ } else {
1482
+ opacity = 0.3;
1483
+ }
1484
+ }
1485
+ return /* @__PURE__ */ jsxDEV2(Fragment, {
1486
+ children: [
1487
+ /* @__PURE__ */ jsxDEV2(BaseEdge, {
1488
+ id,
1489
+ path: edgePath,
1490
+ markerEnd,
1491
+ style: {
1492
+ ...style,
1493
+ strokeDasharray: isContinuation ? "6 3" : undefined,
1494
+ stroke,
1495
+ strokeWidth,
1496
+ opacity
1497
+ }
1498
+ }, undefined, false, undefined, this),
1499
+ label && /* @__PURE__ */ jsxDEV2(EdgeLabelRenderer, {
1500
+ children: /* @__PURE__ */ jsxDEV2("div", {
1501
+ style: {
1502
+ position: "absolute",
1503
+ transform: `translate(-50%, -50%) translate(${labelX}px,${labelY}px)`,
1504
+ pointerEvents: "all",
1505
+ zIndex: 10
1506
+ },
1507
+ className: "px-1.5 py-0.5 rounded text-[10px] font-medium border-2 shadow whitespace-nowrap transition-colors duration-150 bg-card text-foreground border-border hover:border-foreground",
1508
+ children: label
1509
+ }, undefined, false, undefined, this)
1510
+ }, undefined, false, undefined, this)
1511
+ ]
1512
+ }, undefined, true, undefined, this);
1513
+ }
1514
+
1515
+ // src/nodes/base-node.tsx
1516
+ import { Handle, Position } from "@xyflow/react";
1517
+ import { jsxDEV as jsxDEV3 } from "react/jsx-dev-runtime";
1518
+ function StatusIcon({ status }) {
1519
+ switch (status) {
1520
+ case "running":
1521
+ return /* @__PURE__ */ jsxDEV3("span", {
1522
+ className: "inline-block w-3.5 h-3.5 rounded-full border-2 border-blue-400 border-t-transparent animate-spin shrink-0"
1523
+ }, undefined, false, undefined, this);
1524
+ case "completed":
1525
+ return /* @__PURE__ */ jsxDEV3("svg", {
1526
+ className: "w-3.5 h-3.5 text-green-500 shrink-0",
1527
+ viewBox: "0 0 16 16",
1528
+ fill: "currentColor",
1529
+ "aria-hidden": "true",
1530
+ role: "img",
1531
+ children: [
1532
+ /* @__PURE__ */ jsxDEV3("title", {
1533
+ children: "Completed"
1534
+ }, undefined, false, undefined, this),
1535
+ /* @__PURE__ */ jsxDEV3("path", {
1536
+ d: "M13.78 4.22a.75.75 0 0 1 0 1.06l-7.25 7.25a.75.75 0 0 1-1.06 0L2.22 9.28a.75.75 0 0 1 1.06-1.06L6 10.94l6.72-6.72a.75.75 0 0 1 1.06 0Z"
1537
+ }, undefined, false, undefined, this)
1538
+ ]
1539
+ }, undefined, true, undefined, this);
1540
+ case "failed":
1541
+ return /* @__PURE__ */ jsxDEV3("svg", {
1542
+ className: "w-3.5 h-3.5 text-red-500 shrink-0",
1543
+ viewBox: "0 0 16 16",
1544
+ fill: "currentColor",
1545
+ "aria-hidden": "true",
1546
+ role: "img",
1547
+ children: [
1548
+ /* @__PURE__ */ jsxDEV3("title", {
1549
+ children: "Failed"
1550
+ }, undefined, false, undefined, this),
1551
+ /* @__PURE__ */ jsxDEV3("path", {
1552
+ d: "M3.72 3.72a.75.75 0 0 1 1.06 0L8 6.94l3.22-3.22a.75.75 0 1 1 1.06 1.06L9.06 8l3.22 3.22a.75.75 0 1 1-1.06 1.06L8 9.06l-3.22 3.22a.75.75 0 0 1-1.06-1.06L6.94 8 3.72 4.78a.75.75 0 0 1 0-1.06Z"
1553
+ }, undefined, false, undefined, this)
1554
+ ]
1555
+ }, undefined, true, undefined, this);
1556
+ default:
1557
+ return null;
1558
+ }
1559
+ }
1560
+ function BaseNode({
1561
+ id,
1562
+ name,
1563
+ typeLabel,
1564
+ typeLabelColor,
1565
+ accent,
1566
+ description,
1567
+ diagnostics,
1568
+ children,
1569
+ selected,
1570
+ hasSourceEdge = true,
1571
+ hasTargetEdge = true,
1572
+ executionSummary
1573
+ }) {
1574
+ const hasErrors = diagnostics.some((d) => d.severity === "error");
1575
+ const hasWarnings = !hasErrors && diagnostics.some((d) => d.severity === "warning");
1576
+ let ringClass = "";
1577
+ let opacityClass = "";
1578
+ if (executionSummary) {
1579
+ switch (executionSummary.status) {
1580
+ case "running":
1581
+ ringClass = "ring-2 ring-blue-400 animate-pulse";
1582
+ break;
1583
+ case "completed":
1584
+ ringClass = "ring-2 ring-green-400";
1585
+ break;
1586
+ case "failed":
1587
+ ringClass = "ring-2 ring-red-500";
1588
+ break;
1589
+ case "skipped":
1590
+ opacityClass = "opacity-50";
1591
+ break;
1592
+ }
1593
+ } else {
1594
+ if (hasErrors)
1595
+ ringClass = "ring-2 ring-red-500";
1596
+ else if (hasWarnings)
1597
+ ringClass = "ring-2 ring-amber-400";
1598
+ else if (selected)
1599
+ ringClass = "ring-2 ring-blue-400";
1600
+ }
1601
+ const hasRing = hasErrors || hasWarnings || selected || !!executionSummary;
1602
+ return /* @__PURE__ */ jsxDEV3("div", {
1603
+ className: `rounded-lg shadow-md border-l-4 w-[300px] transition-shadow duration-150 bg-card ${ringClass} ${opacityClass} ${hasRing ? "" : "hover:ring-2 hover:ring-ring"}`,
1604
+ style: { borderLeftColor: accent },
1605
+ children: [
1606
+ hasTargetEdge && /* @__PURE__ */ jsxDEV3(Handle, {
1607
+ type: "target",
1608
+ position: Position.Top,
1609
+ className: "!w-2 !h-2 !bg-muted-foreground"
1610
+ }, undefined, false, undefined, this),
1611
+ /* @__PURE__ */ jsxDEV3("div", {
1612
+ className: "px-3 py-2.5",
1613
+ children: [
1614
+ /* @__PURE__ */ jsxDEV3("div", {
1615
+ className: "flex items-center justify-between gap-2",
1616
+ children: [
1617
+ /* @__PURE__ */ jsxDEV3("div", {
1618
+ className: "flex items-center gap-2 min-w-0",
1619
+ children: [
1620
+ /* @__PURE__ */ jsxDEV3("span", {
1621
+ className: `text-[10px] font-semibold uppercase tracking-wide shrink-0 ${typeLabelColor}`,
1622
+ children: typeLabel
1623
+ }, undefined, false, undefined, this),
1624
+ /* @__PURE__ */ jsxDEV3("div", {
1625
+ className: "font-medium text-sm truncate text-foreground",
1626
+ children: name
1627
+ }, undefined, false, undefined, this)
1628
+ ]
1629
+ }, undefined, true, undefined, this),
1630
+ /* @__PURE__ */ jsxDEV3("div", {
1631
+ className: "flex items-center gap-1.5 shrink-0",
1632
+ children: [
1633
+ executionSummary && executionSummary.totalRetries > 0 && /* @__PURE__ */ jsxDEV3("span", {
1634
+ className: "text-[10px] px-1 py-0.5 rounded bg-amber-100 text-amber-600 dark:bg-amber-900/50 dark:text-amber-400 font-medium",
1635
+ children: [
1636
+ executionSummary.totalRetries,
1637
+ " ",
1638
+ executionSummary.totalRetries === 1 ? "retry" : "retries"
1639
+ ]
1640
+ }, undefined, true, undefined, this),
1641
+ executionSummary && /* @__PURE__ */ jsxDEV3(StatusIcon, {
1642
+ status: executionSummary.status
1643
+ }, undefined, false, undefined, this),
1644
+ (hasErrors || hasWarnings) && /* @__PURE__ */ jsxDEV3("span", {
1645
+ className: `text-xs px-1.5 py-0.5 rounded-full font-medium ${hasErrors ? "bg-red-100 text-red-700 dark:bg-red-900/50 dark:text-red-400" : "bg-amber-100 text-amber-700 dark:bg-amber-900/50 dark:text-amber-400"}`,
1646
+ children: diagnostics.length
1647
+ }, undefined, false, undefined, this)
1648
+ ]
1649
+ }, undefined, true, undefined, this)
1650
+ ]
1651
+ }, undefined, true, undefined, this),
1652
+ /* @__PURE__ */ jsxDEV3("div", {
1653
+ className: "text-[11px] font-mono text-muted-foreground",
1654
+ children: id
1655
+ }, undefined, false, undefined, this),
1656
+ /* @__PURE__ */ jsxDEV3("div", {
1657
+ className: "text-[11px] mt-1 text-muted-foreground",
1658
+ children: description
1659
+ }, undefined, false, undefined, this),
1660
+ children && /* @__PURE__ */ jsxDEV3("div", {
1661
+ className: "mt-2 border-t pt-2 border-border",
1662
+ children
1663
+ }, undefined, false, undefined, this)
1664
+ ]
1665
+ }, undefined, true, undefined, this),
1666
+ hasSourceEdge && /* @__PURE__ */ jsxDEV3(Handle, {
1667
+ type: "source",
1668
+ position: Position.Bottom,
1669
+ className: "!w-2 !h-2 !bg-muted-foreground"
1670
+ }, undefined, false, undefined, this)
1671
+ ]
1672
+ }, undefined, true, undefined, this);
1673
+ }
1674
+
1675
+ // src/nodes/agent-loop-node.tsx
1676
+ import { jsxDEV as jsxDEV4 } from "react/jsx-dev-runtime";
1677
+ function AgentLoopNode({ data, selected }) {
1678
+ const { step, diagnostics, hasSourceEdge } = data;
1679
+ if (step.type !== "agent-loop")
1680
+ return null;
1681
+ return /* @__PURE__ */ jsxDEV4(BaseNode, {
1682
+ id: step.id,
1683
+ name: step.name,
1684
+ typeLabel: "Agent",
1685
+ typeLabelColor: "text-teal-500",
1686
+ accent: "#14b8a6",
1687
+ description: step.description,
1688
+ diagnostics,
1689
+ selected,
1690
+ hasSourceEdge,
1691
+ children: [
1692
+ /* @__PURE__ */ jsxDEV4("div", {
1693
+ className: "text-[11px] text-muted-foreground italic line-clamp-2 bg-muted rounded p-1.5 font-mono",
1694
+ children: step.params.instructions
1695
+ }, undefined, false, undefined, this),
1696
+ step.params.tools.length > 0 && /* @__PURE__ */ jsxDEV4("div", {
1697
+ className: "mt-1.5 flex gap-1.5 text-[11px]",
1698
+ children: [
1699
+ /* @__PURE__ */ jsxDEV4("span", {
1700
+ className: "text-muted-foreground shrink-0",
1701
+ children: "tools:"
1702
+ }, undefined, false, undefined, this),
1703
+ /* @__PURE__ */ jsxDEV4("span", {
1704
+ className: "font-mono text-muted-foreground truncate",
1705
+ children: step.params.tools.join(", ")
1706
+ }, undefined, false, undefined, this)
1707
+ ]
1708
+ }, undefined, true, undefined, this)
1709
+ ]
1710
+ }, undefined, true, undefined, this);
1711
+ }
1712
+
1713
+ // src/nodes/end-node.tsx
1714
+ import { Handle as Handle2, Position as Position2 } from "@xyflow/react";
1715
+ import { jsxDEV as jsxDEV5 } from "react/jsx-dev-runtime";
1716
+ function renderExpr(expr) {
1717
+ if (expr.type === "literal")
1718
+ return JSON.stringify(expr.value);
1719
+ if (expr.type === "template")
1720
+ return expr.template;
1721
+ return expr.expression;
1722
+ }
1723
+ function EndNode({ data, selected }) {
1724
+ const { step, diagnostics, executionSummary, outputSchema } = data;
1725
+ const schema = outputSchema;
1726
+ const schemaProperties = schema?.properties ? Object.entries(schema.properties) : [];
1727
+ if (step?.type === "end" && (step.params?.output || schemaProperties.length > 0)) {
1728
+ return /* @__PURE__ */ jsxDEV5(BaseNode, {
1729
+ id: step.id,
1730
+ name: step.name,
1731
+ typeLabel: "End",
1732
+ typeLabelColor: "text-muted-foreground",
1733
+ accent: "#6b7280",
1734
+ description: step.description,
1735
+ diagnostics,
1736
+ selected,
1737
+ hasSourceEdge: false,
1738
+ executionSummary,
1739
+ children: [
1740
+ step.params?.output && /* @__PURE__ */ jsxDEV5("div", {
1741
+ className: "flex gap-1.5 text-[11px]",
1742
+ children: [
1743
+ /* @__PURE__ */ jsxDEV5("span", {
1744
+ className: "text-muted-foreground shrink-0",
1745
+ children: "output:"
1746
+ }, undefined, false, undefined, this),
1747
+ /* @__PURE__ */ jsxDEV5("span", {
1748
+ className: "font-mono text-muted-foreground truncate",
1749
+ children: renderExpr(step.params.output)
1750
+ }, undefined, false, undefined, this)
1751
+ ]
1752
+ }, undefined, true, undefined, this),
1753
+ schemaProperties.length > 0 && /* @__PURE__ */ jsxDEV5("div", {
1754
+ className: "mt-1 space-y-0.5",
1755
+ children: [
1756
+ /* @__PURE__ */ jsxDEV5("div", {
1757
+ className: "text-[10px] text-muted-foreground uppercase tracking-wide font-semibold",
1758
+ children: "Output Schema"
1759
+ }, undefined, false, undefined, this),
1760
+ schemaProperties.map(([key, val]) => /* @__PURE__ */ jsxDEV5("div", {
1761
+ className: "flex gap-1.5 text-[11px]",
1762
+ children: [
1763
+ /* @__PURE__ */ jsxDEV5("span", {
1764
+ className: "text-muted-foreground font-medium shrink-0",
1765
+ children: key
1766
+ }, undefined, false, undefined, this),
1767
+ /* @__PURE__ */ jsxDEV5("span", {
1768
+ className: "font-mono text-muted-foreground",
1769
+ children: val?.type
1770
+ }, undefined, false, undefined, this)
1771
+ ]
1772
+ }, key, true, undefined, this))
1773
+ ]
1774
+ }, undefined, true, undefined, this)
1775
+ ]
1776
+ }, undefined, true, undefined, this);
1777
+ }
1778
+ const hasErrors = diagnostics?.some((d) => d.severity === "error");
1779
+ let ringClass = "";
1780
+ if (executionSummary) {
1781
+ switch (executionSummary.status) {
1782
+ case "running":
1783
+ ringClass = "ring-2 ring-blue-400 animate-pulse";
1784
+ break;
1785
+ case "completed":
1786
+ ringClass = "ring-2 ring-green-400";
1787
+ break;
1788
+ case "failed":
1789
+ ringClass = "ring-2 ring-red-500";
1790
+ break;
1791
+ }
1792
+ } else {
1793
+ if (hasErrors)
1794
+ ringClass = "ring-2 ring-red-500";
1795
+ else if (selected)
1796
+ ringClass = "ring-2 ring-blue-400";
1797
+ }
1798
+ const hasRing = hasErrors || selected || !!executionSummary;
1799
+ return /* @__PURE__ */ jsxDEV5("div", {
1800
+ className: `rounded-full w-[60px] h-[60px] flex items-center justify-center shadow-sm border transition-all duration-150 bg-muted border-border hover:bg-accent ${hasRing ? ringClass : "hover:ring-2 hover:ring-ring"}`,
1801
+ children: [
1802
+ /* @__PURE__ */ jsxDEV5(Handle2, {
1803
+ type: "target",
1804
+ position: Position2.Top,
1805
+ className: "!w-2 !h-2 !bg-muted-foreground"
1806
+ }, undefined, false, undefined, this),
1807
+ /* @__PURE__ */ jsxDEV5("span", {
1808
+ className: "text-xs font-medium text-muted-foreground",
1809
+ children: "End"
1810
+ }, undefined, false, undefined, this)
1811
+ ]
1812
+ }, undefined, true, undefined, this);
1813
+ }
1814
+
1815
+ // src/nodes/extract-data-node.tsx
1816
+ import { jsxDEV as jsxDEV6 } from "react/jsx-dev-runtime";
1817
+ function renderExpr2(expr) {
1818
+ if (expr.type === "literal")
1819
+ return JSON.stringify(expr.value);
1820
+ if (expr.type === "template")
1821
+ return expr.template;
1822
+ return expr.expression;
1823
+ }
1824
+ function ExtractDataNode({ data, selected }) {
1825
+ const { step, diagnostics, hasSourceEdge, executionSummary } = data;
1826
+ if (step.type !== "extract-data")
1827
+ return null;
1828
+ const outputFormat = step.params.outputFormat;
1829
+ const outputKeys = outputFormat?.properties ? Object.keys(outputFormat.properties) : [];
1830
+ const resolved = executionSummary?.latestResolvedInputs;
1831
+ const hasSourceResolved = resolved?.sourceData !== undefined;
1832
+ return /* @__PURE__ */ jsxDEV6(BaseNode, {
1833
+ id: step.id,
1834
+ name: step.name,
1835
+ typeLabel: "Extract",
1836
+ typeLabelColor: "text-purple-500",
1837
+ accent: "#a855f7",
1838
+ description: step.description,
1839
+ diagnostics,
1840
+ selected,
1841
+ hasSourceEdge,
1842
+ executionSummary,
1843
+ children: [
1844
+ /* @__PURE__ */ jsxDEV6("div", {
1845
+ className: "flex gap-1.5 text-[11px]",
1846
+ children: [
1847
+ /* @__PURE__ */ jsxDEV6("span", {
1848
+ className: "text-muted-foreground shrink-0",
1849
+ children: "source:"
1850
+ }, undefined, false, undefined, this),
1851
+ /* @__PURE__ */ jsxDEV6("span", {
1852
+ className: `font-mono truncate ${hasSourceResolved ? "text-emerald-700 dark:text-emerald-400" : "text-muted-foreground"}`,
1853
+ title: hasSourceResolved ? renderExpr2(step.params.sourceData) : undefined,
1854
+ children: hasSourceResolved ? typeof resolved.sourceData === "string" ? resolved.sourceData : JSON.stringify(resolved.sourceData) : renderExpr2(step.params.sourceData)
1855
+ }, undefined, false, undefined, this)
1856
+ ]
1857
+ }, undefined, true, undefined, this),
1858
+ outputKeys.length > 0 && /* @__PURE__ */ jsxDEV6("div", {
1859
+ className: "mt-1 flex gap-1.5 text-[11px]",
1860
+ children: [
1861
+ /* @__PURE__ */ jsxDEV6("span", {
1862
+ className: "text-muted-foreground shrink-0",
1863
+ children: "output:"
1864
+ }, undefined, false, undefined, this),
1865
+ /* @__PURE__ */ jsxDEV6("span", {
1866
+ className: "font-mono text-muted-foreground",
1867
+ children: outputKeys.join(", ")
1868
+ }, undefined, false, undefined, this)
1869
+ ]
1870
+ }, undefined, true, undefined, this)
1871
+ ]
1872
+ }, undefined, true, undefined, this);
1873
+ }
1874
+
1875
+ // src/nodes/for-each-node.tsx
1876
+ import { Handle as Handle3, Position as Position3 } from "@xyflow/react";
1877
+ import { jsxDEV as jsxDEV7 } from "react/jsx-dev-runtime";
1878
+ function renderExpr3(expr) {
1879
+ if (expr.type === "literal")
1880
+ return JSON.stringify(expr.value);
1881
+ if (expr.type === "template")
1882
+ return expr.template;
1883
+ return expr.expression;
1884
+ }
1885
+ function ForEachNode({ data, selected }) {
1886
+ const {
1887
+ step,
1888
+ diagnostics,
1889
+ isGroup,
1890
+ groupWidth,
1891
+ groupHeight,
1892
+ hasSourceEdge,
1893
+ executionSummary
1894
+ } = data;
1895
+ if (step.type !== "for-each")
1896
+ return null;
1897
+ if (isGroup) {
1898
+ const hasErrors = diagnostics.some((d) => d.severity === "error");
1899
+ const hasWarnings = !hasErrors && diagnostics.some((d) => d.severity === "warning");
1900
+ let ringClass = "";
1901
+ let borderColor = "border-emerald-300 dark:border-emerald-700";
1902
+ if (executionSummary) {
1903
+ switch (executionSummary.status) {
1904
+ case "running":
1905
+ ringClass = "ring-2 ring-blue-400 animate-pulse";
1906
+ borderColor = "border-blue-300 dark:border-blue-700";
1907
+ break;
1908
+ case "completed":
1909
+ ringClass = "ring-2 ring-green-400";
1910
+ borderColor = "border-green-400 dark:border-green-600";
1911
+ break;
1912
+ case "failed":
1913
+ ringClass = "ring-2 ring-red-500";
1914
+ borderColor = "border-red-300 dark:border-red-700";
1915
+ break;
1916
+ }
1917
+ } else {
1918
+ if (hasErrors)
1919
+ ringClass = "ring-2 ring-red-500";
1920
+ else if (hasWarnings)
1921
+ ringClass = "ring-2 ring-amber-400";
1922
+ else if (selected)
1923
+ ringClass = "ring-2 ring-emerald-400";
1924
+ }
1925
+ return /* @__PURE__ */ jsxDEV7("div", {
1926
+ className: `rounded-xl border-2 border-dashed transition-colors duration-150 ${borderColor} bg-emerald-50/30 hover:bg-emerald-50/60 hover:border-emerald-500 dark:bg-emerald-950/30 dark:hover:bg-emerald-950/50 dark:hover:border-emerald-500 ${ringClass}`,
1927
+ style: { width: groupWidth, height: groupHeight },
1928
+ children: [
1929
+ /* @__PURE__ */ jsxDEV7(Handle3, {
1930
+ type: "target",
1931
+ position: Position3.Top,
1932
+ className: "!bg-emerald-500 !w-2.5 !h-2.5"
1933
+ }, undefined, false, undefined, this),
1934
+ /* @__PURE__ */ jsxDEV7("div", {
1935
+ className: "px-3 py-2 flex items-center gap-2",
1936
+ children: [
1937
+ /* @__PURE__ */ jsxDEV7("span", {
1938
+ className: "text-[10px] font-semibold uppercase tracking-wide text-emerald-500",
1939
+ children: "Loop"
1940
+ }, undefined, false, undefined, this),
1941
+ /* @__PURE__ */ jsxDEV7("span", {
1942
+ className: "text-sm font-medium truncate text-foreground",
1943
+ children: step.name
1944
+ }, undefined, false, undefined, this)
1945
+ ]
1946
+ }, undefined, true, undefined, this),
1947
+ hasSourceEdge && /* @__PURE__ */ jsxDEV7(Handle3, {
1948
+ type: "source",
1949
+ position: Position3.Bottom,
1950
+ className: "!bg-emerald-500 !w-2.5 !h-2.5"
1951
+ }, undefined, false, undefined, this)
1952
+ ]
1953
+ }, undefined, true, undefined, this);
1954
+ }
1955
+ const resolved = executionSummary?.latestResolvedInputs;
1956
+ const hasTargetResolved = resolved?.target !== undefined;
1957
+ return /* @__PURE__ */ jsxDEV7(BaseNode, {
1958
+ id: step.id,
1959
+ name: step.name,
1960
+ typeLabel: "ForEach",
1961
+ typeLabelColor: "text-emerald-500",
1962
+ accent: "#10b981",
1963
+ description: step.description,
1964
+ diagnostics,
1965
+ selected,
1966
+ hasSourceEdge,
1967
+ executionSummary,
1968
+ children: [
1969
+ /* @__PURE__ */ jsxDEV7("div", {
1970
+ className: "flex gap-1.5 text-[11px]",
1971
+ children: [
1972
+ /* @__PURE__ */ jsxDEV7("span", {
1973
+ className: "text-muted-foreground shrink-0",
1974
+ children: "target:"
1975
+ }, undefined, false, undefined, this),
1976
+ /* @__PURE__ */ jsxDEV7("span", {
1977
+ className: `font-mono truncate ${hasTargetResolved ? "text-emerald-700 dark:text-emerald-400" : "text-muted-foreground"}`,
1978
+ title: hasTargetResolved ? renderExpr3(step.params.target) : undefined,
1979
+ children: hasTargetResolved ? `[${Array.isArray(resolved.target) ? resolved.target.length : "?"} items]` : renderExpr3(step.params.target)
1980
+ }, undefined, false, undefined, this)
1981
+ ]
1982
+ }, undefined, true, undefined, this),
1983
+ /* @__PURE__ */ jsxDEV7("div", {
1984
+ className: "mt-0.5 flex gap-1.5 text-[11px]",
1985
+ children: [
1986
+ /* @__PURE__ */ jsxDEV7("span", {
1987
+ className: "text-muted-foreground shrink-0",
1988
+ children: "as:"
1989
+ }, undefined, false, undefined, this),
1990
+ /* @__PURE__ */ jsxDEV7("span", {
1991
+ className: "font-mono text-muted-foreground",
1992
+ children: step.params.itemName
1993
+ }, undefined, false, undefined, this)
1994
+ ]
1995
+ }, undefined, true, undefined, this)
1996
+ ]
1997
+ }, undefined, true, undefined, this);
1998
+ }
1999
+
2000
+ // src/nodes/group-header-node.tsx
2001
+ import { Handle as Handle4, Position as Position4 } from "@xyflow/react";
2002
+ import { jsxDEV as jsxDEV8 } from "react/jsx-dev-runtime";
2003
+ var variantStyles = {
2004
+ switch: {
2005
+ container: "bg-amber-50 dark:bg-amber-950/50 border-amber-300 dark:border-amber-700 hover:border-amber-500",
2006
+ label: "text-amber-600 dark:text-amber-400",
2007
+ mono: "text-amber-800 dark:text-amber-300",
2008
+ resolved: "text-emerald-700 dark:text-emerald-400",
2009
+ desc: "text-amber-700/70 dark:text-amber-400/60",
2010
+ handle: "!bg-amber-500",
2011
+ ring: "ring-amber-400"
2012
+ },
2013
+ loop: {
2014
+ container: "bg-emerald-50 dark:bg-emerald-950/50 border-emerald-300 dark:border-emerald-700 hover:border-emerald-500",
2015
+ label: "text-emerald-600 dark:text-emerald-400",
2016
+ mono: "text-emerald-800 dark:text-emerald-300",
2017
+ resolved: "text-emerald-700 dark:text-emerald-400",
2018
+ desc: "text-emerald-700/70 dark:text-emerald-400/60",
2019
+ handle: "!bg-emerald-500",
2020
+ ring: "ring-emerald-400"
2021
+ },
2022
+ condition: {
2023
+ container: "bg-orange-50 dark:bg-orange-950/50 border-orange-300 dark:border-orange-700 hover:border-orange-500",
2024
+ label: "text-orange-600 dark:text-orange-400",
2025
+ mono: "text-orange-800 dark:text-orange-300",
2026
+ resolved: "text-emerald-700 dark:text-emerald-400",
2027
+ desc: "text-orange-700/70 dark:text-orange-400/60",
2028
+ handle: "!bg-orange-500",
2029
+ ring: "ring-orange-400"
2030
+ }
2031
+ };
2032
+ function formatValue2(value) {
2033
+ if (typeof value === "string")
2034
+ return value;
2035
+ return JSON.stringify(value);
2036
+ }
2037
+ function GroupHeaderNode({ data, selected }) {
2038
+ const {
2039
+ variant,
2040
+ description,
2041
+ expression,
2042
+ resolvedExpression,
2043
+ target,
2044
+ resolvedTarget,
2045
+ itemName,
2046
+ condition
2047
+ } = data;
2048
+ const s = variantStyles[variant];
2049
+ return /* @__PURE__ */ jsxDEV8("div", {
2050
+ className: `border-2 rounded-lg w-[280px] shadow-sm transition-colors duration-150 ${s.container} ${selected ? `ring-2 ${s.ring}` : ""}`,
2051
+ children: [
2052
+ /* @__PURE__ */ jsxDEV8("div", {
2053
+ className: "px-3 py-2",
2054
+ children: [
2055
+ variant === "switch" && /* @__PURE__ */ jsxDEV8("div", {
2056
+ className: "flex items-baseline gap-1.5",
2057
+ children: [
2058
+ /* @__PURE__ */ jsxDEV8("span", {
2059
+ className: `text-[10px] font-bold uppercase tracking-wide ${s.label} shrink-0`,
2060
+ children: "switch"
2061
+ }, undefined, false, undefined, this),
2062
+ /* @__PURE__ */ jsxDEV8("span", {
2063
+ className: `text-[11px] ${s.label}`,
2064
+ children: "on"
2065
+ }, undefined, false, undefined, this),
2066
+ /* @__PURE__ */ jsxDEV8("span", {
2067
+ className: `text-xs font-mono font-medium truncate ${resolvedExpression !== undefined ? s.resolved : s.mono}`,
2068
+ title: resolvedExpression !== undefined ? expression : undefined,
2069
+ children: resolvedExpression !== undefined ? formatValue2(resolvedExpression) : expression
2070
+ }, undefined, false, undefined, this)
2071
+ ]
2072
+ }, undefined, true, undefined, this),
2073
+ variant === "loop" && /* @__PURE__ */ jsxDEV8("div", {
2074
+ className: "flex items-baseline gap-1.5 flex-wrap",
2075
+ children: [
2076
+ /* @__PURE__ */ jsxDEV8("span", {
2077
+ className: `text-[10px] font-bold uppercase tracking-wide ${s.label} shrink-0`,
2078
+ children: "foreach"
2079
+ }, undefined, false, undefined, this),
2080
+ /* @__PURE__ */ jsxDEV8("span", {
2081
+ className: `text-xs font-mono font-medium ${s.mono}`,
2082
+ children: itemName
2083
+ }, undefined, false, undefined, this),
2084
+ /* @__PURE__ */ jsxDEV8("span", {
2085
+ className: `text-[11px] ${s.label}`,
2086
+ children: "in"
2087
+ }, undefined, false, undefined, this),
2088
+ /* @__PURE__ */ jsxDEV8("span", {
2089
+ className: `text-xs font-mono font-medium truncate ${resolvedTarget !== undefined ? s.resolved : s.mono}`,
2090
+ title: resolvedTarget !== undefined ? target : undefined,
2091
+ children: resolvedTarget !== undefined ? `[${Array.isArray(resolvedTarget) ? resolvedTarget.length : "?"} items]` : target
2092
+ }, undefined, false, undefined, this)
2093
+ ]
2094
+ }, undefined, true, undefined, this),
2095
+ variant === "condition" && /* @__PURE__ */ jsxDEV8("div", {
2096
+ className: "flex items-baseline gap-1.5",
2097
+ children: [
2098
+ /* @__PURE__ */ jsxDEV8("span", {
2099
+ className: `text-[10px] font-bold uppercase tracking-wide ${s.label} shrink-0`,
2100
+ children: "wait until"
2101
+ }, undefined, false, undefined, this),
2102
+ /* @__PURE__ */ jsxDEV8("span", {
2103
+ className: `text-xs font-mono font-medium ${s.mono} truncate`,
2104
+ children: condition
2105
+ }, undefined, false, undefined, this)
2106
+ ]
2107
+ }, undefined, true, undefined, this),
2108
+ description && /* @__PURE__ */ jsxDEV8("div", {
2109
+ className: `text-[11px] ${s.desc} mt-0.5 line-clamp-1`,
2110
+ children: description
2111
+ }, undefined, false, undefined, this)
2112
+ ]
2113
+ }, undefined, true, undefined, this),
2114
+ /* @__PURE__ */ jsxDEV8(Handle4, {
2115
+ type: "source",
2116
+ position: Position4.Bottom,
2117
+ className: `${s.handle} !w-2 !h-2`
2118
+ }, undefined, false, undefined, this)
2119
+ ]
2120
+ }, undefined, true, undefined, this);
2121
+ }
2122
+
2123
+ // src/nodes/llm-prompt-node.tsx
2124
+ import { jsxDEV as jsxDEV9 } from "react/jsx-dev-runtime";
2125
+ function LlmPromptNode({ data, selected }) {
2126
+ const { step, diagnostics, hasSourceEdge, executionSummary } = data;
2127
+ if (step.type !== "llm-prompt")
2128
+ return null;
2129
+ const outputFormat = step.params.outputFormat;
2130
+ const outputKeys = outputFormat?.properties ? Object.keys(outputFormat.properties) : [];
2131
+ const resolved = executionSummary?.latestResolvedInputs;
2132
+ const resolvedPrompt = resolved?.prompt;
2133
+ return /* @__PURE__ */ jsxDEV9(BaseNode, {
2134
+ id: step.id,
2135
+ name: step.name,
2136
+ typeLabel: "LLM",
2137
+ typeLabelColor: "text-violet-500",
2138
+ accent: "#8b5cf6",
2139
+ description: step.description,
2140
+ diagnostics,
2141
+ selected,
2142
+ hasSourceEdge,
2143
+ executionSummary,
2144
+ children: [
2145
+ /* @__PURE__ */ jsxDEV9("div", {
2146
+ className: `text-[11px] italic line-clamp-3 rounded p-1.5 font-mono ${resolvedPrompt ? "text-emerald-700 bg-emerald-50 dark:text-emerald-400 dark:bg-emerald-950/50" : "text-muted-foreground bg-muted"}`,
2147
+ title: resolvedPrompt ? step.params.prompt : undefined,
2148
+ children: resolvedPrompt ?? step.params.prompt
2149
+ }, undefined, false, undefined, this),
2150
+ outputKeys.length > 0 && /* @__PURE__ */ jsxDEV9("div", {
2151
+ className: "mt-1.5 text-[11px] text-muted-foreground",
2152
+ children: [
2153
+ /* @__PURE__ */ jsxDEV9("span", {
2154
+ children: "output: "
2155
+ }, undefined, false, undefined, this),
2156
+ /* @__PURE__ */ jsxDEV9("span", {
2157
+ className: "font-mono text-muted-foreground",
2158
+ children: outputKeys.join(", ")
2159
+ }, undefined, false, undefined, this)
2160
+ ]
2161
+ }, undefined, true, undefined, this)
2162
+ ]
2163
+ }, undefined, true, undefined, this);
2164
+ }
2165
+
2166
+ // src/nodes/sleep-node.tsx
2167
+ import { jsxDEV as jsxDEV10 } from "react/jsx-dev-runtime";
2168
+ function renderExpr4(expr) {
2169
+ if (expr.type === "literal")
2170
+ return JSON.stringify(expr.value);
2171
+ if (expr.type === "template")
2172
+ return expr.template;
2173
+ return expr.expression;
2174
+ }
2175
+ function SleepNode({ data, selected }) {
2176
+ const { step, diagnostics, hasSourceEdge } = data;
2177
+ if (step.type !== "sleep")
2178
+ return null;
2179
+ return /* @__PURE__ */ jsxDEV10(BaseNode, {
2180
+ id: step.id,
2181
+ name: step.name,
2182
+ typeLabel: "Sleep",
2183
+ typeLabelColor: "text-amber-500",
2184
+ accent: "#f59e0b",
2185
+ description: step.description,
2186
+ diagnostics,
2187
+ selected,
2188
+ hasSourceEdge,
2189
+ children: /* @__PURE__ */ jsxDEV10("div", {
2190
+ className: "flex gap-1.5 text-[11px]",
2191
+ children: [
2192
+ /* @__PURE__ */ jsxDEV10("span", {
2193
+ className: "text-muted-foreground shrink-0",
2194
+ children: "duration:"
2195
+ }, undefined, false, undefined, this),
2196
+ /* @__PURE__ */ jsxDEV10("span", {
2197
+ className: "font-mono text-muted-foreground truncate",
2198
+ children: [
2199
+ renderExpr4(step.params.durationMs),
2200
+ "ms"
2201
+ ]
2202
+ }, undefined, true, undefined, this)
2203
+ ]
2204
+ }, undefined, true, undefined, this)
2205
+ }, undefined, false, undefined, this);
2206
+ }
2207
+
2208
+ // src/nodes/start-node.tsx
2209
+ import { Handle as Handle5, Position as Position5 } from "@xyflow/react";
2210
+ import { jsxDEV as jsxDEV11 } from "react/jsx-dev-runtime";
2211
+ function StartNode({ selected }) {
2212
+ return /* @__PURE__ */ jsxDEV11("div", {
2213
+ className: `rounded-full w-[60px] h-[60px] flex items-center justify-center shadow-sm border transition-all duration-150 bg-muted border-border hover:bg-accent ${selected ? "ring-2 ring-blue-400" : "hover:ring-2 hover:ring-ring"}`,
2214
+ children: [
2215
+ /* @__PURE__ */ jsxDEV11("span", {
2216
+ className: "text-xs font-medium text-muted-foreground",
2217
+ children: "Start"
2218
+ }, undefined, false, undefined, this),
2219
+ /* @__PURE__ */ jsxDEV11(Handle5, {
2220
+ type: "source",
2221
+ position: Position5.Bottom,
2222
+ className: "!w-2 !h-2 !bg-muted-foreground"
2223
+ }, undefined, false, undefined, this)
2224
+ ]
2225
+ }, undefined, true, undefined, this);
2226
+ }
2227
+
2228
+ // src/nodes/start-step-node.tsx
2229
+ import { jsxDEV as jsxDEV12 } from "react/jsx-dev-runtime";
2230
+ function StartStepNode({ data, selected }) {
2231
+ const { step, diagnostics, hasSourceEdge, executionSummary, inputSchema } = data;
2232
+ if (step.type !== "start")
2233
+ return null;
2234
+ const schema = inputSchema;
2235
+ const properties = schema?.properties ? Object.entries(schema.properties) : [];
2236
+ return /* @__PURE__ */ jsxDEV12(BaseNode, {
2237
+ id: step.id,
2238
+ name: step.name,
2239
+ typeLabel: "Start",
2240
+ typeLabelColor: "text-green-500",
2241
+ accent: "#22c55e",
2242
+ description: step.description,
2243
+ diagnostics,
2244
+ selected,
2245
+ hasSourceEdge,
2246
+ hasTargetEdge: false,
2247
+ executionSummary,
2248
+ children: properties.length > 0 && /* @__PURE__ */ jsxDEV12("div", {
2249
+ className: "space-y-0.5",
2250
+ children: [
2251
+ /* @__PURE__ */ jsxDEV12("div", {
2252
+ className: "text-[10px] text-muted-foreground uppercase tracking-wide font-semibold",
2253
+ children: "Inputs"
2254
+ }, undefined, false, undefined, this),
2255
+ properties.map(([key, val]) => /* @__PURE__ */ jsxDEV12("div", {
2256
+ className: "flex gap-1.5 text-[11px]",
2257
+ children: [
2258
+ /* @__PURE__ */ jsxDEV12("span", {
2259
+ className: "text-muted-foreground font-medium shrink-0",
2260
+ children: key
2261
+ }, undefined, false, undefined, this),
2262
+ /* @__PURE__ */ jsxDEV12("span", {
2263
+ className: "font-mono text-muted-foreground",
2264
+ children: val?.type
2265
+ }, undefined, false, undefined, this)
2266
+ ]
2267
+ }, key, true, undefined, this))
2268
+ ]
2269
+ }, undefined, true, undefined, this)
2270
+ }, undefined, false, undefined, this);
2271
+ }
2272
+
2273
+ // src/nodes/switch-case-node.tsx
2274
+ import { Handle as Handle6, Position as Position6 } from "@xyflow/react";
2275
+ import { jsxDEV as jsxDEV13 } from "react/jsx-dev-runtime";
2276
+ function renderExpr5(expr) {
2277
+ if (expr.type === "literal")
2278
+ return JSON.stringify(expr.value);
2279
+ if (expr.type === "template")
2280
+ return expr.template;
2281
+ return expr.expression;
2282
+ }
2283
+ function SwitchCaseNode({ data, selected }) {
2284
+ const {
2285
+ step,
2286
+ diagnostics,
2287
+ isGroup,
2288
+ groupWidth,
2289
+ groupHeight,
2290
+ hasSourceEdge,
2291
+ executionSummary
2292
+ } = data;
2293
+ if (step.type !== "switch-case")
2294
+ return null;
2295
+ if (isGroup) {
2296
+ const hasErrors = diagnostics.some((d) => d.severity === "error");
2297
+ const hasWarnings = !hasErrors && diagnostics.some((d) => d.severity === "warning");
2298
+ let ringClass = "";
2299
+ let borderColor = "border-amber-300 dark:border-amber-700";
2300
+ if (executionSummary) {
2301
+ switch (executionSummary.status) {
2302
+ case "running":
2303
+ ringClass = "ring-2 ring-blue-400 animate-pulse";
2304
+ borderColor = "border-blue-300 dark:border-blue-700";
2305
+ break;
2306
+ case "completed":
2307
+ ringClass = "ring-2 ring-green-400";
2308
+ borderColor = "border-green-400 dark:border-green-600";
2309
+ break;
2310
+ case "failed":
2311
+ ringClass = "ring-2 ring-red-500";
2312
+ borderColor = "border-red-300 dark:border-red-700";
2313
+ break;
2314
+ }
2315
+ } else {
2316
+ if (hasErrors)
2317
+ ringClass = "ring-2 ring-red-500";
2318
+ else if (hasWarnings)
2319
+ ringClass = "ring-2 ring-amber-400";
2320
+ else if (selected)
2321
+ ringClass = "ring-2 ring-amber-400";
2322
+ }
2323
+ return /* @__PURE__ */ jsxDEV13("div", {
2324
+ className: `rounded-xl border-2 border-dashed transition-colors duration-150 ${borderColor} bg-amber-50/30 hover:bg-amber-50/60 hover:border-amber-500 dark:bg-amber-950/30 dark:hover:bg-amber-950/50 dark:hover:border-amber-500 ${ringClass}`,
2325
+ style: { width: groupWidth, height: groupHeight },
2326
+ children: [
2327
+ /* @__PURE__ */ jsxDEV13(Handle6, {
2328
+ type: "target",
2329
+ position: Position6.Top,
2330
+ className: "!bg-amber-500 !w-2.5 !h-2.5"
2331
+ }, undefined, false, undefined, this),
2332
+ /* @__PURE__ */ jsxDEV13("div", {
2333
+ className: "px-3 py-2 flex items-center gap-2",
2334
+ children: [
2335
+ /* @__PURE__ */ jsxDEV13("span", {
2336
+ className: "text-[10px] font-semibold uppercase tracking-wide text-amber-500",
2337
+ children: "Branch"
2338
+ }, undefined, false, undefined, this),
2339
+ /* @__PURE__ */ jsxDEV13("span", {
2340
+ className: "text-sm font-medium truncate text-foreground",
2341
+ children: step.name
2342
+ }, undefined, false, undefined, this)
2343
+ ]
2344
+ }, undefined, true, undefined, this),
2345
+ hasSourceEdge && /* @__PURE__ */ jsxDEV13(Handle6, {
2346
+ type: "source",
2347
+ position: Position6.Bottom,
2348
+ className: "!bg-amber-500 !w-2.5 !h-2.5"
2349
+ }, undefined, false, undefined, this)
2350
+ ]
2351
+ }, undefined, true, undefined, this);
2352
+ }
2353
+ const resolved = executionSummary?.latestResolvedInputs;
2354
+ const hasSwitchResolved = resolved?.switchOn !== undefined;
2355
+ return /* @__PURE__ */ jsxDEV13(BaseNode, {
2356
+ id: step.id,
2357
+ name: step.name,
2358
+ typeLabel: "Switch",
2359
+ typeLabelColor: "text-amber-500",
2360
+ accent: "#f59e0b",
2361
+ description: step.description,
2362
+ diagnostics,
2363
+ selected,
2364
+ hasSourceEdge,
2365
+ executionSummary,
2366
+ children: [
2367
+ /* @__PURE__ */ jsxDEV13("div", {
2368
+ className: "flex gap-1.5 text-[11px]",
2369
+ children: [
2370
+ /* @__PURE__ */ jsxDEV13("span", {
2371
+ className: "text-muted-foreground shrink-0",
2372
+ children: "on:"
2373
+ }, undefined, false, undefined, this),
2374
+ /* @__PURE__ */ jsxDEV13("span", {
2375
+ className: `font-mono truncate ${hasSwitchResolved ? "text-emerald-700 dark:text-emerald-400" : "text-muted-foreground"}`,
2376
+ title: hasSwitchResolved ? renderExpr5(step.params.switchOn) : undefined,
2377
+ children: hasSwitchResolved ? JSON.stringify(resolved.switchOn) : renderExpr5(step.params.switchOn)
2378
+ }, undefined, false, undefined, this)
2379
+ ]
2380
+ }, undefined, true, undefined, this),
2381
+ /* @__PURE__ */ jsxDEV13("div", {
2382
+ className: "mt-1.5 space-y-0.5",
2383
+ children: step.params.cases.map((c) => /* @__PURE__ */ jsxDEV13("div", {
2384
+ className: "flex items-center gap-1.5 text-[11px]",
2385
+ children: [
2386
+ /* @__PURE__ */ jsxDEV13("span", {
2387
+ className: "font-mono text-muted-foreground",
2388
+ children: c.value.type === "default" ? "default" : renderExpr5(c.value)
2389
+ }, undefined, false, undefined, this),
2390
+ /* @__PURE__ */ jsxDEV13("span", {
2391
+ className: "text-muted-foreground/50",
2392
+ children: "→"
2393
+ }, undefined, false, undefined, this),
2394
+ /* @__PURE__ */ jsxDEV13("span", {
2395
+ className: "font-mono text-muted-foreground",
2396
+ children: c.branchBodyStepId
2397
+ }, undefined, false, undefined, this)
2398
+ ]
2399
+ }, c.branchBodyStepId, true, undefined, this))
2400
+ }, undefined, false, undefined, this)
2401
+ ]
2402
+ }, undefined, true, undefined, this);
2403
+ }
2404
+
2405
+ // src/nodes/tool-call-node.tsx
2406
+ import { jsxDEV as jsxDEV14 } from "react/jsx-dev-runtime";
2407
+ function renderExpr6(expr) {
2408
+ if (expr.type === "literal")
2409
+ return JSON.stringify(expr.value);
2410
+ if (expr.type === "template")
2411
+ return expr.template;
2412
+ return expr.expression;
2413
+ }
2414
+ function formatValue3(value) {
2415
+ if (typeof value === "string")
2416
+ return value;
2417
+ return JSON.stringify(value);
2418
+ }
2419
+ function ToolCallNode({ data, selected }) {
2420
+ const { step, diagnostics, hasSourceEdge, executionSummary } = data;
2421
+ if (step.type !== "tool-call")
2422
+ return null;
2423
+ const entries = Object.entries(step.params.toolInput);
2424
+ const resolved = executionSummary?.latestResolvedInputs;
2425
+ return /* @__PURE__ */ jsxDEV14(BaseNode, {
2426
+ id: step.id,
2427
+ name: step.name,
2428
+ typeLabel: "Tool",
2429
+ typeLabelColor: "text-blue-500",
2430
+ accent: "#3b82f6",
2431
+ description: step.description,
2432
+ diagnostics,
2433
+ selected,
2434
+ hasSourceEdge,
2435
+ executionSummary,
2436
+ children: [
2437
+ /* @__PURE__ */ jsxDEV14("div", {
2438
+ className: "text-xs font-mono font-medium text-foreground",
2439
+ children: step.params.toolName
2440
+ }, undefined, false, undefined, this),
2441
+ entries.length > 0 && /* @__PURE__ */ jsxDEV14("div", {
2442
+ className: "mt-1.5 space-y-0.5",
2443
+ children: entries.map(([key, val]) => {
2444
+ const resolvedVal = resolved?.[key];
2445
+ const hasResolved = resolvedVal !== undefined;
2446
+ return /* @__PURE__ */ jsxDEV14("div", {
2447
+ className: "flex gap-1.5 text-[11px]",
2448
+ children: [
2449
+ /* @__PURE__ */ jsxDEV14("span", {
2450
+ className: "text-muted-foreground shrink-0",
2451
+ children: [
2452
+ key,
2453
+ ":"
2454
+ ]
2455
+ }, undefined, true, undefined, this),
2456
+ /* @__PURE__ */ jsxDEV14("span", {
2457
+ className: `font-mono truncate ${hasResolved ? "text-emerald-700 dark:text-emerald-400" : "text-muted-foreground"}`,
2458
+ title: hasResolved ? renderExpr6(val) : undefined,
2459
+ children: hasResolved ? formatValue3(resolvedVal) : renderExpr6(val)
2460
+ }, undefined, false, undefined, this)
2461
+ ]
2462
+ }, key, true, undefined, this);
2463
+ })
2464
+ }, undefined, false, undefined, this)
2465
+ ]
2466
+ }, undefined, true, undefined, this);
2467
+ }
2468
+
2469
+ // src/nodes/wait-for-condition-node.tsx
2470
+ import { Handle as Handle7, Position as Position7 } from "@xyflow/react";
2471
+ import { jsxDEV as jsxDEV15 } from "react/jsx-dev-runtime";
2472
+ function renderExpr7(expr) {
2473
+ if (expr.type === "literal")
2474
+ return JSON.stringify(expr.value);
2475
+ if (expr.type === "template")
2476
+ return expr.template;
2477
+ return expr.expression;
2478
+ }
2479
+ function WaitForConditionNode({ data, selected }) {
2480
+ const { step, diagnostics, isGroup, groupWidth, groupHeight, hasSourceEdge } = data;
2481
+ if (step.type !== "wait-for-condition")
2482
+ return null;
2483
+ if (isGroup) {
2484
+ const hasErrors = diagnostics.some((d) => d.severity === "error");
2485
+ const hasWarnings = !hasErrors && diagnostics.some((d) => d.severity === "warning");
2486
+ let ringClass = "";
2487
+ if (hasErrors)
2488
+ ringClass = "ring-2 ring-red-500";
2489
+ else if (hasWarnings)
2490
+ ringClass = "ring-2 ring-amber-400";
2491
+ else if (selected)
2492
+ ringClass = "ring-2 ring-orange-400";
2493
+ return /* @__PURE__ */ jsxDEV15("div", {
2494
+ className: `rounded-xl border-2 border-dashed transition-colors duration-150 border-orange-300 bg-orange-50/30 dark:border-orange-700 dark:bg-orange-950/30 ${ringClass}`,
2495
+ style: { width: groupWidth, height: groupHeight },
2496
+ children: [
2497
+ /* @__PURE__ */ jsxDEV15(Handle7, {
2498
+ type: "target",
2499
+ position: Position7.Top,
2500
+ className: "!bg-orange-500 !w-2.5 !h-2.5"
2501
+ }, undefined, false, undefined, this),
2502
+ /* @__PURE__ */ jsxDEV15("div", {
2503
+ className: "px-3 py-2 flex items-center gap-2",
2504
+ children: [
2505
+ /* @__PURE__ */ jsxDEV15("span", {
2506
+ className: "text-[10px] font-semibold uppercase tracking-wide text-orange-500",
2507
+ children: "Wait"
2508
+ }, undefined, false, undefined, this),
2509
+ /* @__PURE__ */ jsxDEV15("span", {
2510
+ className: "text-sm font-medium text-foreground truncate",
2511
+ children: step.name
2512
+ }, undefined, false, undefined, this)
2513
+ ]
2514
+ }, undefined, true, undefined, this),
2515
+ hasSourceEdge && /* @__PURE__ */ jsxDEV15(Handle7, {
2516
+ type: "source",
2517
+ position: Position7.Bottom,
2518
+ className: "!bg-orange-500 !w-2.5 !h-2.5"
2519
+ }, undefined, false, undefined, this)
2520
+ ]
2521
+ }, undefined, true, undefined, this);
2522
+ }
2523
+ return /* @__PURE__ */ jsxDEV15(BaseNode, {
2524
+ id: step.id,
2525
+ name: step.name,
2526
+ typeLabel: "Wait",
2527
+ typeLabelColor: "text-orange-500",
2528
+ accent: "#f97316",
2529
+ description: step.description,
2530
+ diagnostics,
2531
+ selected,
2532
+ hasSourceEdge,
2533
+ children: [
2534
+ /* @__PURE__ */ jsxDEV15("div", {
2535
+ className: "flex gap-1.5 text-[11px]",
2536
+ children: [
2537
+ /* @__PURE__ */ jsxDEV15("span", {
2538
+ className: "text-muted-foreground shrink-0",
2539
+ children: "until:"
2540
+ }, undefined, false, undefined, this),
2541
+ /* @__PURE__ */ jsxDEV15("span", {
2542
+ className: "font-mono text-muted-foreground truncate",
2543
+ children: renderExpr7(step.params.condition)
2544
+ }, undefined, false, undefined, this)
2545
+ ]
2546
+ }, undefined, true, undefined, this),
2547
+ step.params.maxAttempts && /* @__PURE__ */ jsxDEV15("div", {
2548
+ className: "mt-0.5 flex gap-1.5 text-[11px]",
2549
+ children: [
2550
+ /* @__PURE__ */ jsxDEV15("span", {
2551
+ className: "text-muted-foreground shrink-0",
2552
+ children: "max attempts:"
2553
+ }, undefined, false, undefined, this),
2554
+ /* @__PURE__ */ jsxDEV15("span", {
2555
+ className: "font-mono text-muted-foreground",
2556
+ children: renderExpr7(step.params.maxAttempts)
2557
+ }, undefined, false, undefined, this)
2558
+ ]
2559
+ }, undefined, true, undefined, this)
2560
+ ]
2561
+ }, undefined, true, undefined, this);
2562
+ }
2563
+
2564
+ // src/workflow-viewer.tsx
2565
+ import { jsxDEV as jsxDEV16 } from "react/jsx-dev-runtime";
2566
+ var nodeTypes = {
2567
+ toolCall: ToolCallNode,
2568
+ llmPrompt: LlmPromptNode,
2569
+ extractData: ExtractDataNode,
2570
+ switchCase: SwitchCaseNode,
2571
+ groupHeader: GroupHeaderNode,
2572
+ forEach: ForEachNode,
2573
+ end: EndNode,
2574
+ start: StartNode,
2575
+ startStep: StartStepNode,
2576
+ sleep: SleepNode,
2577
+ waitForCondition: WaitForConditionNode,
2578
+ agentLoop: AgentLoopNode
2579
+ };
2580
+ var edgeTypes = {
2581
+ workflow: WorkflowEdge
2582
+ };
2583
+ var EMPTY_DIAGNOSTICS = [];
2584
+ function WorkflowViewer({
2585
+ workflow,
2586
+ diagnostics = EMPTY_DIAGNOSTICS,
2587
+ onStepSelect,
2588
+ executionState,
2589
+ showMinimap = true,
2590
+ minimapWidth = 200,
2591
+ minimapHeight = 150
2592
+ }) {
2593
+ const theme = useThemeColors();
2594
+ const containerRef = useRef(null);
2595
+ const [containerWidth, setContainerWidth] = useState2(0);
2596
+ const layout = useMemo2(() => buildLayout(workflow, diagnostics, executionState), [workflow, diagnostics, executionState]);
2597
+ const [nodes, setNodes, onNodesChange] = useNodesState(layout.nodes);
2598
+ const [edges, setEdges, onEdgesChange] = useEdgesState(layout.edges);
2599
+ const [selectedStep, setSelectedStep] = useState2(null);
2600
+ const [selectedDiagnostics, setSelectedDiagnostics] = useState2(EMPTY_DIAGNOSTICS);
2601
+ const [selectedExecutionSummary, setSelectedExecutionSummary] = useState2();
2602
+ const [selectedExecutionRecords, setSelectedExecutionRecords] = useState2();
2603
+ useEffect2(() => {
2604
+ setNodes(layout.nodes);
2605
+ setEdges(layout.edges);
2606
+ }, [layout, setNodes, setEdges]);
2607
+ useEffect2(() => {
2608
+ const el = containerRef.current;
2609
+ if (!el)
2610
+ return;
2611
+ const observer = new ResizeObserver((entries) => {
2612
+ const entry = entries[0];
2613
+ if (entry)
2614
+ setContainerWidth(entry.contentRect.width);
2615
+ });
2616
+ observer.observe(el);
2617
+ return () => observer.disconnect();
2618
+ }, []);
2619
+ const effectiveMinimapWidth = containerWidth > 0 ? Math.min(minimapWidth, containerWidth * 0.25) : minimapWidth;
2620
+ const effectiveMinimapHeight = minimapHeight * (effectiveMinimapWidth / minimapWidth);
2621
+ const onNodeClick = useCallback((_, node) => {
2622
+ const data = node.data;
2623
+ if (!data.step)
2624
+ return;
2625
+ setSelectedStep(data.step);
2626
+ setSelectedDiagnostics(data.diagnostics);
2627
+ setSelectedExecutionSummary(data.executionSummary);
2628
+ setSelectedExecutionRecords(executionState?.stepRecords.filter((r) => r.stepId === data.step.id));
2629
+ onStepSelect?.(data.step, data.diagnostics);
2630
+ }, [onStepSelect, executionState]);
2631
+ const onPaneClick = useCallback(() => {
2632
+ setSelectedStep(null);
2633
+ setSelectedDiagnostics([]);
2634
+ setSelectedExecutionSummary(undefined);
2635
+ setSelectedExecutionRecords(undefined);
2636
+ onStepSelect?.(null, []);
2637
+ }, [onStepSelect]);
2638
+ return /* @__PURE__ */ jsxDEV16("div", {
2639
+ className: "flex h-full w-full min-h-0",
2640
+ children: [
2641
+ /* @__PURE__ */ jsxDEV16("div", {
2642
+ ref: containerRef,
2643
+ className: "flex-1 relative",
2644
+ children: /* @__PURE__ */ jsxDEV16(ReactFlow, {
2645
+ nodes,
2646
+ edges,
2647
+ onNodesChange,
2648
+ onEdgesChange,
2649
+ onNodeClick,
2650
+ onPaneClick,
2651
+ nodeTypes,
2652
+ edgeTypes,
2653
+ fitView: true,
2654
+ fitViewOptions: { padding: 0.2 },
2655
+ nodesDraggable: false,
2656
+ defaultEdgeOptions: {
2657
+ type: "workflow"
2658
+ },
2659
+ proOptions: { hideAttribution: true },
2660
+ colorMode: theme.dark ? "dark" : "light",
2661
+ children: [
2662
+ /* @__PURE__ */ jsxDEV16(Background, {
2663
+ color: theme.border,
2664
+ gap: 16
2665
+ }, undefined, false, undefined, this),
2666
+ /* @__PURE__ */ jsxDEV16(Controls, {
2667
+ showInteractive: false
2668
+ }, undefined, false, undefined, this),
2669
+ showMinimap && /* @__PURE__ */ jsxDEV16(MiniMap, {
2670
+ nodeStrokeWidth: 2,
2671
+ pannable: true,
2672
+ zoomable: true,
2673
+ style: {
2674
+ width: effectiveMinimapWidth,
2675
+ height: effectiveMinimapHeight,
2676
+ border: `1px solid ${theme.border}`,
2677
+ backgroundColor: theme.card
2678
+ },
2679
+ nodeColor: theme.mutedForeground
2680
+ }, undefined, false, undefined, this)
2681
+ ]
2682
+ }, undefined, true, undefined, this)
2683
+ }, undefined, false, undefined, this),
2684
+ selectedStep && /* @__PURE__ */ jsxDEV16(StepDetailPanel, {
2685
+ step: selectedStep,
2686
+ diagnostics: selectedDiagnostics,
2687
+ executionSummary: selectedExecutionSummary,
2688
+ executionRecords: selectedExecutionRecords,
2689
+ onClose: () => {
2690
+ setSelectedStep(null);
2691
+ setSelectedDiagnostics([]);
2692
+ setSelectedExecutionSummary(undefined);
2693
+ setSelectedExecutionRecords(undefined);
2694
+ onStepSelect?.(null, []);
2695
+ }
2696
+ }, undefined, false, undefined, this)
2697
+ ]
2698
+ }, undefined, true, undefined, this);
2699
+ }
2700
+ export {
2701
+ useDarkMode,
2702
+ deriveStepSummaries,
2703
+ buildLayout,
2704
+ WorkflowViewer,
2705
+ StepDetailPanel
2706
+ };
2707
+
2708
+ //# debugId=67D796A55E18E22964756E2164756E21