footprint-explainable-ui 0.6.0 → 0.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/flowchart.js CHANGED
@@ -9,14 +9,14 @@ import {
9
9
  } from "@xyflow/react";
10
10
 
11
11
  // src/components/StageNode/StageNode.tsx
12
- import { memo } from "react";
12
+ import { memo, useEffect as useEffect2, useRef } from "react";
13
13
  import { Handle, Position } from "@xyflow/react";
14
14
 
15
15
  // src/theme/ThemeProvider.tsx
16
16
  import { createContext, useContext } from "react";
17
17
 
18
18
  // src/theme/tokens.ts
19
- var defaultTokens = {
19
+ var rawDefaults = {
20
20
  colors: {
21
21
  primary: "#6366f1",
22
22
  success: "#22c55e",
@@ -36,6 +36,26 @@ var defaultTokens = {
36
36
  mono: "'JetBrains Mono', 'Fira Code', monospace"
37
37
  }
38
38
  };
39
+ var defaultTokens = {
40
+ colors: {
41
+ primary: `var(--fp-color-primary, ${rawDefaults.colors.primary})`,
42
+ success: `var(--fp-color-success, ${rawDefaults.colors.success})`,
43
+ error: `var(--fp-color-error, ${rawDefaults.colors.error})`,
44
+ warning: `var(--fp-color-warning, ${rawDefaults.colors.warning})`,
45
+ bgPrimary: `var(--fp-bg-primary, ${rawDefaults.colors.bgPrimary})`,
46
+ bgSecondary: `var(--fp-bg-secondary, ${rawDefaults.colors.bgSecondary})`,
47
+ bgTertiary: `var(--fp-bg-tertiary, ${rawDefaults.colors.bgTertiary})`,
48
+ textPrimary: `var(--fp-text-primary, ${rawDefaults.colors.textPrimary})`,
49
+ textSecondary: `var(--fp-text-secondary, ${rawDefaults.colors.textSecondary})`,
50
+ textMuted: `var(--fp-text-muted, ${rawDefaults.colors.textMuted})`,
51
+ border: `var(--fp-border, ${rawDefaults.colors.border})`
52
+ },
53
+ radius: `var(--fp-radius, ${rawDefaults.radius})`,
54
+ fontFamily: {
55
+ sans: `var(--fp-font-sans, ${rawDefaults.fontFamily.sans})`,
56
+ mono: `var(--fp-font-mono, ${rawDefaults.fontFamily.mono})`
57
+ }
58
+ };
39
59
 
40
60
  // src/theme/ThemeProvider.tsx
41
61
  import { jsx } from "react/jsx-runtime";
@@ -77,10 +97,169 @@ import { useState, useEffect } from "react";
77
97
 
78
98
  // src/components/StageNode/StageNode.tsx
79
99
  import { Fragment, jsx as jsx2, jsxs } from "react/jsx-runtime";
100
+ var KEYFRAMES_ID = "fp-stage-node-keyframes";
101
+ var KEYFRAMES_CSS = `
102
+ @media (prefers-reduced-motion: no-preference) {
103
+ @keyframes fp-pulse {
104
+ 0%, 100% { opacity: 0.4; transform: scale(1); }
105
+ 50% { opacity: 0.15; transform: scale(1.06); }
106
+ }
107
+ @keyframes fp-blink {
108
+ 0%, 100% { opacity: 1; }
109
+ 50% { opacity: 0.3; }
110
+ }
111
+ }
112
+ @media (prefers-reduced-motion: reduce) {
113
+ @keyframes fp-pulse { 0%, 100% { opacity: 0.3; } }
114
+ @keyframes fp-blink { 0%, 100% { opacity: 1; } }
115
+ }
116
+ `;
117
+ var ICON_SIZE = 16;
118
+ function StageIcon({ type, color }) {
119
+ const s = ICON_SIZE;
120
+ const props = { width: s, height: s, viewBox: `0 0 ${s} ${s}`, fill: "none", style: { flexShrink: 0 } };
121
+ switch (type) {
122
+ // LLM / AI call — brain/sparkle
123
+ case "llm":
124
+ case "ai":
125
+ return /* @__PURE__ */ jsxs("svg", { ...props, children: [
126
+ /* @__PURE__ */ jsx2("circle", { cx: "8", cy: "8", r: "6", stroke: color, strokeWidth: "1.5" }),
127
+ /* @__PURE__ */ jsx2("path", { d: "M5.5 8C5.5 6.5 6.5 5 8 5S10.5 6.5 10.5 8", stroke: color, strokeWidth: "1.2", strokeLinecap: "round" }),
128
+ /* @__PURE__ */ jsx2("circle", { cx: "8", cy: "9.5", r: "1", fill: color }),
129
+ /* @__PURE__ */ jsx2("line", { x1: "8", y1: "2", x2: "8", y2: "3.5", stroke: color, strokeWidth: "1", strokeLinecap: "round" }),
130
+ /* @__PURE__ */ jsx2("line", { x1: "12.5", y1: "4", x2: "11.2", y2: "5", stroke: color, strokeWidth: "1", strokeLinecap: "round" }),
131
+ /* @__PURE__ */ jsx2("line", { x1: "3.5", y1: "4", x2: "4.8", y2: "5", stroke: color, strokeWidth: "1", strokeLinecap: "round" })
132
+ ] });
133
+ // Tool / function call — gear
134
+ case "tool":
135
+ case "function":
136
+ return /* @__PURE__ */ jsxs("svg", { ...props, children: [
137
+ /* @__PURE__ */ jsx2("circle", { cx: "8", cy: "8", r: "3", stroke: color, strokeWidth: "1.5" }),
138
+ [0, 45, 90, 135, 180, 225, 270, 315].map((angle) => {
139
+ const rad = angle * Math.PI / 180;
140
+ const x1 = 8 + Math.cos(rad) * 4.5;
141
+ const y1 = 8 + Math.sin(rad) * 4.5;
142
+ const x2 = 8 + Math.cos(rad) * 6;
143
+ const y2 = 8 + Math.sin(rad) * 6;
144
+ return /* @__PURE__ */ jsx2("line", { x1, y1, x2, y2, stroke: color, strokeWidth: "1.5", strokeLinecap: "round" }, angle);
145
+ })
146
+ ] });
147
+ // RAG / retrieval — magnifying glass + doc
148
+ case "rag":
149
+ case "search":
150
+ case "retrieval":
151
+ return /* @__PURE__ */ jsxs("svg", { ...props, children: [
152
+ /* @__PURE__ */ jsx2("circle", { cx: "7", cy: "7", r: "4", stroke: color, strokeWidth: "1.5" }),
153
+ /* @__PURE__ */ jsx2("line", { x1: "10", y1: "10", x2: "13.5", y2: "13.5", stroke: color, strokeWidth: "1.5", strokeLinecap: "round" }),
154
+ /* @__PURE__ */ jsx2("line", { x1: "5.5", y1: "6", x2: "8.5", y2: "6", stroke: color, strokeWidth: "1", strokeLinecap: "round" }),
155
+ /* @__PURE__ */ jsx2("line", { x1: "5.5", y1: "8", x2: "7.5", y2: "8", stroke: color, strokeWidth: "1", strokeLinecap: "round" })
156
+ ] });
157
+ // Parse / process — diamond with arrows
158
+ case "parse":
159
+ case "process":
160
+ case "transform":
161
+ return /* @__PURE__ */ jsx2("svg", { ...props, children: /* @__PURE__ */ jsx2("rect", { x: "4", y: "4", width: "8", height: "8", rx: "1.5", stroke: color, strokeWidth: "1.5", transform: "rotate(45 8 8)" }) });
162
+ // Start / seed — play triangle
163
+ case "start":
164
+ case "seed":
165
+ case "init":
166
+ return /* @__PURE__ */ jsx2("svg", { ...props, children: /* @__PURE__ */ jsx2("path", { d: "M5 3.5L12.5 8L5 12.5V3.5Z", fill: color, opacity: "0.8" }) });
167
+ // End / finalize — stop square
168
+ case "end":
169
+ case "finalize":
170
+ case "output":
171
+ return /* @__PURE__ */ jsx2("svg", { ...props, children: /* @__PURE__ */ jsx2("rect", { x: "4", y: "4", width: "8", height: "8", rx: "1.5", fill: color, opacity: "0.8" }) });
172
+ // Agent — person silhouette
173
+ case "agent":
174
+ case "orchestrator":
175
+ return /* @__PURE__ */ jsxs("svg", { ...props, children: [
176
+ /* @__PURE__ */ jsx2("circle", { cx: "8", cy: "5", r: "2.5", stroke: color, strokeWidth: "1.5" }),
177
+ /* @__PURE__ */ jsx2("path", { d: "M3.5 14C3.5 11 5.5 9 8 9S12.5 11 12.5 14", stroke: color, strokeWidth: "1.5", strokeLinecap: "round" })
178
+ ] });
179
+ // Swarm — multi-agent
180
+ case "swarm":
181
+ case "multi-agent":
182
+ return /* @__PURE__ */ jsxs("svg", { ...props, children: [
183
+ /* @__PURE__ */ jsx2("circle", { cx: "5", cy: "5", r: "2", stroke: color, strokeWidth: "1.2" }),
184
+ /* @__PURE__ */ jsx2("circle", { cx: "11", cy: "5", r: "2", stroke: color, strokeWidth: "1.2" }),
185
+ /* @__PURE__ */ jsx2("circle", { cx: "8", cy: "11", r: "2", stroke: color, strokeWidth: "1.2" }),
186
+ /* @__PURE__ */ jsx2("line", { x1: "5", y1: "7", x2: "8", y2: "9", stroke: color, strokeWidth: "1", opacity: "0.5" }),
187
+ /* @__PURE__ */ jsx2("line", { x1: "11", y1: "7", x2: "8", y2: "9", stroke: color, strokeWidth: "1", opacity: "0.5" })
188
+ ] });
189
+ // Guard / guardrail — shield
190
+ case "guard":
191
+ case "guardrail":
192
+ case "validate":
193
+ return /* @__PURE__ */ jsxs("svg", { ...props, children: [
194
+ /* @__PURE__ */ jsx2("path", { d: "M8 2L3 5V9C3 11.5 5 13.5 8 14.5C11 13.5 13 11.5 13 9V5L8 2Z", stroke: color, strokeWidth: "1.5", strokeLinejoin: "round" }),
195
+ /* @__PURE__ */ jsx2("path", { d: "M6 8L7.5 9.5L10 6.5", stroke: color, strokeWidth: "1.2", strokeLinecap: "round", strokeLinejoin: "round" })
196
+ ] });
197
+ // Stream — wave
198
+ case "stream":
199
+ case "streaming":
200
+ return /* @__PURE__ */ jsxs("svg", { ...props, children: [
201
+ /* @__PURE__ */ jsx2("path", { d: "M2 8C4 5 6 11 8 8S12 5 14 8", stroke: color, strokeWidth: "1.5", strokeLinecap: "round", fill: "none" }),
202
+ /* @__PURE__ */ jsx2("path", { d: "M2 11C4 8 6 14 8 11S12 8 14 11", stroke: color, strokeWidth: "1", strokeLinecap: "round", fill: "none", opacity: "0.5" })
203
+ ] });
204
+ // Memory / state — database cylinder
205
+ case "memory":
206
+ case "state":
207
+ case "db":
208
+ return /* @__PURE__ */ jsxs("svg", { ...props, children: [
209
+ /* @__PURE__ */ jsx2("ellipse", { cx: "8", cy: "4.5", rx: "5", ry: "2", stroke: color, strokeWidth: "1.3" }),
210
+ /* @__PURE__ */ jsx2("line", { x1: "3", y1: "4.5", x2: "3", y2: "11.5", stroke: color, strokeWidth: "1.3" }),
211
+ /* @__PURE__ */ jsx2("line", { x1: "13", y1: "4.5", x2: "13", y2: "11.5", stroke: color, strokeWidth: "1.3" }),
212
+ /* @__PURE__ */ jsx2("ellipse", { cx: "8", cy: "11.5", rx: "5", ry: "2", stroke: color, strokeWidth: "1.3" })
213
+ ] });
214
+ // Loop — circular arrow
215
+ case "loop":
216
+ case "retry":
217
+ return /* @__PURE__ */ jsxs("svg", { ...props, children: [
218
+ /* @__PURE__ */ jsx2("path", { d: "M12 8A4 4 0 1 1 8 4", stroke: color, strokeWidth: "1.5", strokeLinecap: "round", fill: "none" }),
219
+ /* @__PURE__ */ jsx2("path", { d: "M8 1.5L10.5 4L8 6.5", stroke: color, strokeWidth: "1.3", strokeLinecap: "round", strokeLinejoin: "round", fill: "none" })
220
+ ] });
221
+ // Lazy / service — cloud (deferred resolution, loaded on demand)
222
+ case "lazy":
223
+ case "service":
224
+ case "cloud":
225
+ return /* @__PURE__ */ jsx2("svg", { ...props, children: /* @__PURE__ */ jsx2(
226
+ "path",
227
+ {
228
+ d: "M4.5 12C2.8 12 1.5 10.7 1.5 9C1.5 7.5 2.5 6.3 3.8 6C4 4 5.8 2.5 8 2.5C9.8 2.5 11.3 3.5 11.9 5C13.9 5.2 15.5 6.8 15.5 8.8C15.5 10.8 13.9 12.5 11.8 12.5H4.5",
229
+ stroke: color,
230
+ strokeWidth: "1.3",
231
+ strokeLinecap: "round",
232
+ fill: "none"
233
+ }
234
+ ) });
235
+ // Decision — diamond (already handled by isDecider shape)
236
+ case "decision":
237
+ case "router":
238
+ return /* @__PURE__ */ jsxs("svg", { ...props, children: [
239
+ /* @__PURE__ */ jsx2("path", { d: "M8 2L14 8L8 14L2 8Z", stroke: color, strokeWidth: "1.5", fill: "none" }),
240
+ /* @__PURE__ */ jsx2("circle", { cx: "8", cy: "8", r: "1.5", fill: color })
241
+ ] });
242
+ default:
243
+ return null;
244
+ }
245
+ }
80
246
  var StageNode = memo(function StageNode2({
81
247
  data
82
248
  }) {
83
- const { label, active, done, error, linked, stepNumbers, dimmed, isSubflow, description } = data;
249
+ const { label, active, done, error, linked, icon, stepNumbers, dimmed, isSubflow, isLazy, isDecider, isFork, description } = data;
250
+ const effectiveIcon = icon || (isLazy ? "lazy" : void 0);
251
+ const isLazyUnresolved = isLazy && !done && !active;
252
+ const injectedRef = useRef(false);
253
+ useEffect2(() => {
254
+ if (injectedRef.current) return;
255
+ if (typeof document !== "undefined" && !document.getElementById(KEYFRAMES_ID)) {
256
+ const styleEl = document.createElement("style");
257
+ styleEl.id = KEYFRAMES_ID;
258
+ styleEl.textContent = KEYFRAMES_CSS;
259
+ document.head.appendChild(styleEl);
260
+ }
261
+ injectedRef.current = true;
262
+ }, []);
84
263
  const isOnPath = active || done;
85
264
  const bg = active ? theme.primary : done ? theme.success : error ? theme.error : theme.bgSecondary;
86
265
  const borderColor = active ? theme.primary : done ? theme.success : error ? theme.error : theme.border;
@@ -142,7 +321,8 @@ var StageNode = memo(function StageNode2({
142
321
  style: {
143
322
  position: "absolute",
144
323
  inset: -6,
145
- borderRadius: `calc(${theme.radius} + 4px)`,
324
+ borderRadius: isDecider ? 0 : `calc(${theme.radius} + 4px)`,
325
+ transform: isDecider ? "rotate(45deg)" : void 0,
146
326
  border: `2px solid ${theme.primary}`,
147
327
  opacity: 0.4,
148
328
  animation: "fp-pulse 2s ease-in-out infinite"
@@ -155,107 +335,175 @@ var StageNode = memo(function StageNode2({
155
335
  style: {
156
336
  position: "absolute",
157
337
  inset: -6,
158
- borderRadius: `calc(${theme.radius} + 4px)`,
338
+ borderRadius: isDecider ? 0 : `calc(${theme.radius} + 4px)`,
339
+ transform: isDecider ? "rotate(45deg)" : void 0,
159
340
  border: `2px solid ${theme.primary}`,
160
341
  opacity: 0.3,
161
342
  animation: "fp-pulse 1.5s ease-out infinite"
162
343
  }
163
344
  }
164
345
  ),
165
- /* @__PURE__ */ jsxs(
346
+ isDecider ? /* @__PURE__ */ jsx2(
166
347
  "div",
167
348
  {
168
349
  style: {
169
350
  background: bg,
170
- border: `2px solid ${borderColor}`,
171
- borderRadius: theme.radius,
172
- padding: description ? "8px 16px" : "10px 20px",
173
- display: "flex",
174
- flexDirection: "column",
175
- alignItems: "center",
176
- gap: description ? 2 : 0,
351
+ border: `2px ${isLazyUnresolved ? "dashed" : "solid"} ${borderColor}`,
352
+ borderRadius: 4,
353
+ transform: "rotate(45deg)",
354
+ padding: 20,
177
355
  boxShadow: shadow,
178
356
  transition: "all 0.3s ease",
179
- fontFamily: theme.fontSans,
180
- minWidth: 100,
357
+ display: "flex",
358
+ alignItems: "center",
181
359
  justifyContent: "center"
182
360
  },
183
- children: [
184
- /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: 6 }, children: [
185
- done && /* @__PURE__ */ jsx2("span", { style: { fontSize: 10, color: textColor }, children: "\u2713" }),
186
- active && /* @__PURE__ */ jsx2(
187
- "span",
188
- {
189
- style: {
190
- width: 8,
191
- height: 8,
192
- borderRadius: "50%",
193
- background: "#fff",
194
- animation: "fp-blink 1s ease-in-out infinite",
195
- flexShrink: 0
361
+ children: /* @__PURE__ */ jsxs(
362
+ "div",
363
+ {
364
+ style: {
365
+ transform: "rotate(-45deg)",
366
+ display: "flex",
367
+ flexDirection: "column",
368
+ alignItems: "center",
369
+ gap: 2,
370
+ fontFamily: theme.fontSans
371
+ },
372
+ children: [
373
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: 5 }, children: [
374
+ icon && /* @__PURE__ */ jsx2(StageIcon, { type: icon, color: textColor }),
375
+ !icon && /* @__PURE__ */ jsx2("span", { style: { fontSize: 10, color: textColor }, children: "\u25C7" }),
376
+ /* @__PURE__ */ jsx2(
377
+ "span",
378
+ {
379
+ style: {
380
+ fontSize: 12,
381
+ fontWeight: 600,
382
+ color: textColor,
383
+ whiteSpace: "nowrap"
384
+ },
385
+ children: label
386
+ }
387
+ )
388
+ ] }),
389
+ description && /* @__PURE__ */ jsx2(
390
+ "span",
391
+ {
392
+ style: {
393
+ fontSize: 9,
394
+ fontWeight: 400,
395
+ color: textColor,
396
+ opacity: 0.7,
397
+ whiteSpace: "nowrap",
398
+ overflow: "hidden",
399
+ textOverflow: "ellipsis",
400
+ maxWidth: 130
401
+ },
402
+ children: description
196
403
  }
197
- }
198
- ),
199
- error && /* @__PURE__ */ jsx2("span", { style: { fontSize: 10, color: textColor }, children: "\u2717" }),
200
- /* @__PURE__ */ jsx2(
404
+ )
405
+ ]
406
+ }
407
+ )
408
+ }
409
+ ) : (
410
+ /* Standard rectangular node */
411
+ /* @__PURE__ */ jsxs(
412
+ "div",
413
+ {
414
+ style: {
415
+ background: bg,
416
+ border: `2px ${isLazyUnresolved ? "dashed" : "solid"} ${borderColor}`,
417
+ borderRadius: theme.radius,
418
+ padding: description ? "8px 16px" : "10px 20px",
419
+ display: "flex",
420
+ flexDirection: "column",
421
+ alignItems: "center",
422
+ gap: description ? 2 : 0,
423
+ boxShadow: shadow,
424
+ transition: "all 0.3s ease",
425
+ fontFamily: theme.fontSans,
426
+ minWidth: 100,
427
+ justifyContent: "center"
428
+ },
429
+ children: [
430
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: 6 }, children: [
431
+ effectiveIcon && /* @__PURE__ */ jsx2(StageIcon, { type: effectiveIcon, color: textColor }),
432
+ done && !effectiveIcon && /* @__PURE__ */ jsx2("span", { style: { fontSize: 10, color: textColor }, children: "\u2713" }),
433
+ active && !effectiveIcon && /* @__PURE__ */ jsx2(
434
+ "span",
435
+ {
436
+ style: {
437
+ width: 8,
438
+ height: 8,
439
+ borderRadius: "50%",
440
+ background: "#fff",
441
+ animation: "fp-blink 1s ease-in-out infinite",
442
+ flexShrink: 0
443
+ }
444
+ }
445
+ ),
446
+ error && !effectiveIcon && /* @__PURE__ */ jsx2("span", { style: { fontSize: 10, color: textColor }, children: "\u2717" }),
447
+ /* @__PURE__ */ jsx2(
448
+ "span",
449
+ {
450
+ style: {
451
+ fontSize: 13,
452
+ fontWeight: 500,
453
+ color: textColor,
454
+ whiteSpace: "nowrap"
455
+ },
456
+ children: label
457
+ }
458
+ ),
459
+ isSubflow && /* @__PURE__ */ jsx2(
460
+ "span",
461
+ {
462
+ style: {
463
+ display: "inline-flex",
464
+ alignItems: "center",
465
+ justifyContent: "center",
466
+ width: 16,
467
+ height: 16,
468
+ borderRadius: 3,
469
+ border: `1.5px solid ${textColor}`,
470
+ position: "relative",
471
+ opacity: 0.7,
472
+ flexShrink: 0
473
+ },
474
+ children: /* @__PURE__ */ jsx2(
475
+ "span",
476
+ {
477
+ style: {
478
+ width: 8,
479
+ height: 8,
480
+ borderRadius: 2,
481
+ border: `1px solid ${textColor}`
482
+ }
483
+ }
484
+ )
485
+ }
486
+ )
487
+ ] }),
488
+ description && /* @__PURE__ */ jsx2(
201
489
  "span",
202
490
  {
203
491
  style: {
204
- fontSize: 13,
205
- fontWeight: 500,
492
+ fontSize: 10,
493
+ fontWeight: 400,
206
494
  color: textColor,
207
- whiteSpace: "nowrap"
208
- },
209
- children: label
210
- }
211
- ),
212
- isSubflow && /* @__PURE__ */ jsx2(
213
- "span",
214
- {
215
- style: {
216
- display: "inline-flex",
217
- alignItems: "center",
218
- justifyContent: "center",
219
- width: 16,
220
- height: 16,
221
- borderRadius: 3,
222
- border: `1.5px solid ${textColor}`,
223
- position: "relative",
224
495
  opacity: 0.7,
225
- flexShrink: 0
496
+ whiteSpace: "nowrap",
497
+ overflow: "hidden",
498
+ textOverflow: "ellipsis",
499
+ maxWidth: 160
226
500
  },
227
- children: /* @__PURE__ */ jsx2(
228
- "span",
229
- {
230
- style: {
231
- width: 8,
232
- height: 8,
233
- borderRadius: 2,
234
- border: `1px solid ${textColor}`
235
- }
236
- }
237
- )
501
+ children: description
238
502
  }
239
503
  )
240
- ] }),
241
- description && /* @__PURE__ */ jsx2(
242
- "span",
243
- {
244
- style: {
245
- fontSize: 10,
246
- fontWeight: 400,
247
- color: textColor,
248
- opacity: 0.7,
249
- whiteSpace: "nowrap",
250
- overflow: "hidden",
251
- textOverflow: "ellipsis",
252
- maxWidth: 160
253
- },
254
- children: description
255
- }
256
- )
257
- ]
258
- }
504
+ ]
505
+ }
506
+ )
259
507
  )
260
508
  ]
261
509
  }
@@ -396,23 +644,24 @@ function FlowchartView({
396
644
  }
397
645
 
398
646
  // src/components/FlowchartView/TracedFlowchartView.tsx
399
- import { useState as useState4, useMemo as useMemo4, useCallback as useCallback4 } from "react";
647
+ import { useMemo as useMemo2, useCallback as useCallback2, useEffect as useEffect3 } from "react";
400
648
  import {
401
649
  ReactFlow as ReactFlow2,
402
650
  Background as Background2,
403
- BackgroundVariant as BackgroundVariant2
651
+ BackgroundVariant as BackgroundVariant2,
652
+ useReactFlow
404
653
  } from "@xyflow/react";
405
654
 
406
655
  // src/components/FlowchartView/specToReactFlow.ts
407
656
  var DEFAULT_COLORS = {
408
- edgeDefault: defaultTokens.colors.textMuted,
409
- edgeExecuted: defaultTokens.colors.success,
410
- edgeActive: defaultTokens.colors.primary,
411
- edgeLoop: defaultTokens.colors.warning,
412
- labelDefault: defaultTokens.colors.textSecondary,
413
- labelExecuted: defaultTokens.colors.success,
414
- labelLoop: defaultTokens.colors.warning,
415
- pathGlow: `${defaultTokens.colors.success}4D`
657
+ edgeDefault: rawDefaults.colors.textMuted,
658
+ edgeExecuted: rawDefaults.colors.success,
659
+ edgeActive: rawDefaults.colors.primary,
660
+ edgeLoop: rawDefaults.colors.warning,
661
+ labelDefault: rawDefaults.colors.textSecondary,
662
+ labelExecuted: rawDefaults.colors.success,
663
+ labelLoop: rawDefaults.colors.warning,
664
+ pathGlow: `${rawDefaults.colors.success}4D`
416
665
  // ~30% opacity hex
417
666
  };
418
667
  var Y_STEP = 100;
@@ -420,122 +669,33 @@ var X_SPREAD = 200;
420
669
  function nid(n) {
421
670
  return n.name || n.id || `spec-${Math.random()}`;
422
671
  }
423
- function addEdge(state, source, target, label, isLoop) {
424
- state.edgeCounter++;
425
- const o = state.overlay;
426
- const c = state.colors;
427
- const executed = o && o.executedStages.has(source) && o.executedStages.has(target);
428
- const isLeadingEdge = o && source === o.activeStage && !o.doneStages.has(target);
429
- if (isLoop) {
430
- let loopExecuted = false;
431
- if (o?.executionOrder) {
432
- const lastSourceIdx = o.executionOrder.lastIndexOf(source);
433
- if (lastSourceIdx >= 0) {
434
- loopExecuted = o.executionOrder.slice(lastSourceIdx + 1).includes(target);
435
- }
436
- }
437
- state.edges.push({
438
- id: `se${state.edgeCounter}`,
439
- source,
440
- target,
441
- sourceHandle: "loop-source",
442
- targetHandle: "loop-target",
443
- label: label ?? "loop",
444
- type: "smoothstep",
445
- pathOptions: { offset: 40, borderRadius: 16 },
446
- style: {
447
- stroke: c.edgeLoop,
448
- strokeWidth: loopExecuted ? 3 : 2,
449
- strokeDasharray: "6 3",
450
- opacity: o && !loopExecuted ? 0.35 : 1
451
- },
452
- labelStyle: { fontSize: 10, fontWeight: 700, fill: c.labelLoop },
453
- animated: loopExecuted,
454
- zIndex: 2
455
- });
456
- return;
457
- }
458
- if (executed) {
459
- state.edges.push({
460
- id: `se${state.edgeCounter}-glow`,
461
- source,
462
- target,
463
- style: {
464
- stroke: c.pathGlow,
465
- strokeWidth: 8,
466
- opacity: 0.4
467
- },
468
- zIndex: 0,
469
- selectable: false,
470
- focusable: false
471
- });
472
- state.edges.push({
473
- id: `se${state.edgeCounter}`,
474
- source,
475
- target,
476
- label,
477
- style: {
478
- stroke: isLeadingEdge ? c.edgeActive : c.edgeExecuted,
479
- strokeWidth: 3.5
480
- },
481
- labelStyle: { fontSize: 10, fontWeight: 600, fill: c.labelExecuted },
482
- animated: !!isLeadingEdge,
483
- zIndex: 1
484
- });
485
- } else {
486
- state.edges.push({
487
- id: `se${state.edgeCounter}`,
488
- source,
489
- target,
490
- label,
491
- style: {
492
- stroke: c.edgeDefault,
493
- strokeWidth: 1.5,
494
- opacity: o ? 0.3 : 1
495
- },
496
- labelStyle: { fontSize: 10, fill: c.labelDefault }
497
- });
672
+ function registerNode(state, node) {
673
+ if (node.id && node.name) {
674
+ state.idToName.set(node.id, node.name);
498
675
  }
499
676
  }
500
- function walk(node, state, x, y) {
677
+ function walkLayout(node, state, x, y) {
678
+ if (!node) return { lastIds: [], bottomY: y };
679
+ registerNode(state, node);
501
680
  const id = nid(node);
502
681
  if (state.seen.has(id)) {
503
682
  return { lastIds: [id], bottomY: y };
504
683
  }
505
684
  state.seen.add(id);
506
- const isDecider = node.type === "decider" || node.hasDecider;
685
+ const isDecider = node.type === "decider" || !!node.hasDecider;
507
686
  const isFork = node.type === "fork";
508
- const o = state.overlay;
509
- const isDone = o ? o.doneStages.has(id) : false;
510
- const isActive = o ? o.activeStage === id : false;
511
- const wasExecuted = o ? o.executedStages.has(id) : false;
512
- const dimmed = o && !wasExecuted;
513
- let stepNumbers;
514
- if (o?.executionOrder) {
515
- const nums = [];
516
- for (let i = 0; i < o.executionOrder.length; i++) {
517
- if (o.executionOrder[i] === id) nums.push(i + 1);
518
- }
519
- if (nums.length > 0) stepNumbers = nums;
520
- }
521
687
  state.nodes.push({
522
688
  id,
523
- position: { x, y },
524
- data: {
525
- label: node.name,
526
- active: isActive,
527
- done: isDone,
528
- error: false,
529
- isDecider,
530
- isFork,
531
- description: node.description,
532
- subflowId: node.subflowId,
533
- dimmed,
534
- stepNumbers,
535
- isSubflow: !!node.isSubflowRoot
536
- },
537
- type: "stage",
538
- style: dimmed ? { opacity: 0.35 } : void 0
689
+ x,
690
+ y,
691
+ label: node.name,
692
+ isDecider,
693
+ isFork,
694
+ description: node.description,
695
+ icon: node.icon,
696
+ subflowId: node.subflowId,
697
+ isSubflow: !!node.isSubflowRoot,
698
+ isLazy: node.isLazy
539
699
  });
540
700
  let lastIds = [id];
541
701
  let bottomY = y;
@@ -546,120 +706,257 @@ function walk(node, state, x, y) {
546
706
  const childResults = [];
547
707
  for (let i = 0; i < node.children.length; i++) {
548
708
  const child = node.children[i];
709
+ if (!child) continue;
549
710
  const childX = startX + i * X_SPREAD;
550
711
  const edgeLabel = node.branchIds?.[i];
551
- addEdge(state, id, nid(child), edgeLabel);
552
- const result = walk(child, state, childX, childY);
712
+ state.edgeCounter++;
713
+ state.edges.push({ id: `se${state.edgeCounter}`, source: id, target: nid(child), label: edgeLabel, isLoop: false });
714
+ const result = walkLayout(child, state, childX, childY);
553
715
  childResults.push(result);
554
716
  }
555
717
  lastIds = childResults.flatMap((r) => r.lastIds);
556
718
  bottomY = Math.max(...childResults.map((r) => r.bottomY));
557
719
  }
558
720
  if (node.loopTarget) {
559
- addEdge(state, id, node.loopTarget, "loop", true);
721
+ const resolvedTarget = state.idToName.get(node.loopTarget) ?? node.loopTarget;
722
+ state.edgeCounter++;
723
+ state.edges.push({ id: `se${state.edgeCounter}`, source: id, target: resolvedTarget, label: "loop", isLoop: true });
560
724
  }
561
725
  if (node.next) {
726
+ const rawNextId = nid(node.next);
727
+ const resolvedNextId = state.idToName.get(rawNextId) ?? rawNextId;
728
+ const isLoopRef = node.loopTarget && state.seen.has(resolvedNextId);
729
+ if (isLoopRef) {
730
+ return { lastIds, bottomY };
731
+ }
562
732
  const nextY = bottomY + Y_STEP;
563
- const nextId = nid(node.next);
564
733
  for (const lid of lastIds) {
565
- if (node.loopTarget && lid === id && node.loopTarget === nextId) continue;
566
- addEdge(state, lid, nextId);
734
+ state.edgeCounter++;
735
+ state.edges.push({ id: `se${state.edgeCounter}`, source: lid, target: resolvedNextId, isLoop: false });
567
736
  }
568
- const result = walk(node.next, state, x, nextY);
569
- return result;
737
+ return walkLayout(node.next, state, x, nextY);
570
738
  }
571
739
  return { lastIds, bottomY };
572
740
  }
573
- function specToReactFlow(spec, overlay, colors) {
741
+ function specToLayout(spec) {
574
742
  const state = {
575
743
  nodes: [],
576
744
  edges: [],
577
745
  edgeCounter: 0,
578
746
  seen: /* @__PURE__ */ new Set(),
579
- overlay: overlay ?? null,
580
- colors: { ...DEFAULT_COLORS, ...colors }
747
+ idToName: /* @__PURE__ */ new Map()
581
748
  };
582
- walk(spec, state, 300, 0);
583
- return { nodes: state.nodes, edges: state.edges };
749
+ walkLayout(spec, state, 300, 0);
750
+ return { nodes: state.nodes, edges: state.edges, idToName: state.idToName };
584
751
  }
585
-
586
- // src/components/FlowchartView/useSubflowNavigation.ts
587
- import { useState as useState2, useCallback as useCallback2, useMemo as useMemo2 } from "react";
588
- function useSubflowNavigation(rootSpec, overlay, colors) {
589
- const [stack, setStack] = useState2([]);
590
- const currentSpec = stack.length > 0 ? stack[stack.length - 1].spec : rootSpec;
591
- const { nodes, edges } = useMemo2(() => {
592
- if (!currentSpec) return { nodes: [], edges: [] };
593
- return specToReactFlow(currentSpec, overlay, colors);
594
- }, [currentSpec, overlay, colors]);
595
- const subflowMap = useMemo2(() => {
596
- const map = /* @__PURE__ */ new Map();
597
- if (!currentSpec) return map;
598
- function collectSubflows(node) {
599
- if (node.isSubflowRoot && node.subflowStructure) {
600
- const id = node.name || node.id || "";
601
- map.set(id, node);
752
+ function applyOverlay(layout, overlay, colors) {
753
+ const c = { ...DEFAULT_COLORS, ...colors };
754
+ const o = overlay ?? null;
755
+ const nodes = layout.nodes.map((ln) => {
756
+ const isDone = o ? o.doneStages.has(ln.id) : false;
757
+ const isActive = o ? o.activeStage === ln.id : false;
758
+ const wasExecuted = o ? o.executedStages.has(ln.id) : false;
759
+ const dimmed = o && !wasExecuted;
760
+ let stepNumbers;
761
+ if (o?.executionOrder) {
762
+ const nums = [];
763
+ for (let i = 0; i < o.executionOrder.length; i++) {
764
+ if (o.executionOrder[i] === ln.id) nums.push(i + 1);
602
765
  }
603
- if (node.children) node.children.forEach(collectSubflows);
604
- if (node.next) collectSubflows(node.next);
766
+ if (nums.length > 0) stepNumbers = nums;
605
767
  }
606
- collectSubflows(currentSpec);
607
- return map;
608
- }, [currentSpec]);
609
- const breadcrumbs = useMemo2(() => {
610
- const root = {
611
- label: rootSpec?.name || "Flowchart",
612
- spec: rootSpec,
613
- description: rootSpec?.description
768
+ return {
769
+ id: ln.id,
770
+ position: { x: ln.x, y: ln.y },
771
+ data: {
772
+ label: ln.label,
773
+ active: isActive,
774
+ done: isDone,
775
+ error: false,
776
+ isDecider: ln.isDecider,
777
+ isFork: ln.isFork,
778
+ description: ln.description,
779
+ icon: ln.icon,
780
+ subflowId: ln.subflowId,
781
+ dimmed,
782
+ stepNumbers,
783
+ isSubflow: ln.isSubflow,
784
+ isLazy: ln.isLazy
785
+ },
786
+ type: "stage",
787
+ style: dimmed ? { opacity: 0.35 } : void 0
614
788
  };
615
- return [root, ...stack];
616
- }, [rootSpec, stack]);
617
- const handleNodeClick = useCallback2(
618
- (nodeId) => {
619
- const subflowNode = subflowMap.get(nodeId);
620
- if (!subflowNode?.subflowStructure) return false;
621
- setStack((prev) => [
622
- ...prev,
623
- {
624
- label: subflowNode.subflowName || subflowNode.name,
625
- spec: subflowNode.subflowStructure,
626
- description: subflowNode.description
789
+ });
790
+ const edges = [];
791
+ for (const le of layout.edges) {
792
+ const executed = o && o.executedStages.has(le.source) && o.executedStages.has(le.target);
793
+ const isLeadingEdge = o && le.source === o.activeStage && !o.doneStages.has(le.target);
794
+ if (le.isLoop) {
795
+ let loopExecuted = false;
796
+ if (o?.executionOrder) {
797
+ const lastSourceIdx = o.executionOrder.lastIndexOf(le.source);
798
+ if (lastSourceIdx >= 0) {
799
+ loopExecuted = o.executionOrder.slice(lastSourceIdx + 1).includes(le.target);
627
800
  }
628
- ]);
629
- return true;
630
- },
631
- [subflowMap]
632
- );
633
- const navigateTo = useCallback2(
634
- (level) => {
635
- if (level === 0) {
636
- setStack([]);
637
- } else {
638
- setStack((prev) => prev.slice(0, level));
639
801
  }
802
+ edges.push({
803
+ id: le.id,
804
+ source: le.source,
805
+ target: le.target,
806
+ sourceHandle: "loop-source",
807
+ targetHandle: "loop-target",
808
+ label: le.label ?? "loop",
809
+ type: "smoothstep",
810
+ pathOptions: { offset: 40, borderRadius: 16 },
811
+ style: {
812
+ stroke: c.edgeLoop,
813
+ strokeWidth: loopExecuted ? 3 : 2,
814
+ strokeDasharray: "6 3",
815
+ opacity: o && !loopExecuted ? 0.35 : 1
816
+ },
817
+ labelStyle: { fontSize: 10, fontWeight: 700, fill: c.labelLoop },
818
+ animated: loopExecuted,
819
+ zIndex: 2
820
+ });
821
+ } else if (executed) {
822
+ edges.push({
823
+ id: `${le.id}-glow`,
824
+ source: le.source,
825
+ target: le.target,
826
+ style: { stroke: c.pathGlow, strokeWidth: 8, opacity: 0.4 },
827
+ zIndex: 0,
828
+ selectable: false,
829
+ focusable: false
830
+ });
831
+ edges.push({
832
+ id: le.id,
833
+ source: le.source,
834
+ target: le.target,
835
+ label: le.label,
836
+ style: {
837
+ stroke: isLeadingEdge ? c.edgeActive : c.edgeExecuted,
838
+ strokeWidth: 3.5
839
+ },
840
+ labelStyle: { fontSize: 10, fontWeight: 600, fill: c.labelExecuted },
841
+ animated: !!isLeadingEdge,
842
+ zIndex: 1
843
+ });
844
+ } else {
845
+ edges.push({
846
+ id: le.id,
847
+ source: le.source,
848
+ target: le.target,
849
+ label: le.label,
850
+ style: {
851
+ stroke: c.edgeDefault,
852
+ strokeWidth: 1.5,
853
+ opacity: o ? 0.3 : 1
854
+ },
855
+ labelStyle: { fontSize: 10, fill: c.labelDefault }
856
+ });
857
+ }
858
+ }
859
+ return { nodes, edges };
860
+ }
861
+ function specToReactFlow(spec, overlay, colors) {
862
+ const layout = specToLayout(spec);
863
+ return applyOverlay(layout, overlay, colors);
864
+ }
865
+
866
+ // src/components/FlowchartView/TracedFlowchartView.tsx
867
+ import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
868
+ var defaultNodeTypes = { stage: StageNode };
869
+ function FitViewOnResize() {
870
+ const { fitView } = useReactFlow();
871
+ useEffect3(() => {
872
+ const handler = () => {
873
+ requestAnimationFrame(() => fitView({ padding: 0.3 }));
874
+ };
875
+ window.addEventListener("resize", handler);
876
+ return () => window.removeEventListener("resize", handler);
877
+ }, [fitView]);
878
+ return null;
879
+ }
880
+ function TracedFlowchartView({
881
+ spec,
882
+ snapshots,
883
+ snapshotIndex = 0,
884
+ onNodeClick,
885
+ nodeTypes: customNodeTypes,
886
+ unstyled = false,
887
+ className,
888
+ style
889
+ }) {
890
+ const nodeTypes2 = customNodeTypes ?? defaultNodeTypes;
891
+ const overlay = useMemo2(() => {
892
+ if (!snapshots || snapshots.length === 0) return void 0;
893
+ const executionOrder = snapshots.slice(0, snapshotIndex + 1).map((s) => s.stageLabel);
894
+ const doneStages = new Set(
895
+ snapshots.slice(0, snapshotIndex).map((s) => s.stageLabel)
896
+ );
897
+ const activeStage = snapshots[snapshotIndex]?.stageLabel ?? null;
898
+ const executedStages = /* @__PURE__ */ new Set([...doneStages]);
899
+ if (activeStage) executedStages.add(activeStage);
900
+ return { doneStages, activeStage, executedStages, executionOrder };
901
+ }, [snapshots, snapshotIndex]);
902
+ const layout = useMemo2(() => {
903
+ if (!spec) return null;
904
+ return specToLayout(spec);
905
+ }, [spec]);
906
+ const { nodes, edges } = useMemo2(() => {
907
+ if (!layout) return { nodes: [], edges: [] };
908
+ return applyOverlay(layout, overlay);
909
+ }, [layout, overlay]);
910
+ const handleNodeClick = useCallback2(
911
+ (_, node) => {
912
+ if (!onNodeClick) return;
913
+ onNodeClick(node.id);
640
914
  },
641
- []
915
+ [onNodeClick]
916
+ );
917
+ return /* @__PURE__ */ jsx4(
918
+ "div",
919
+ {
920
+ className,
921
+ style: { width: "100%", height: "100%", ...style },
922
+ "data-fp": "traced-flowchart",
923
+ children: /* @__PURE__ */ jsxs2(
924
+ ReactFlow2,
925
+ {
926
+ nodes,
927
+ edges,
928
+ onNodeClick: handleNodeClick,
929
+ nodeTypes: nodeTypes2,
930
+ fitView: true,
931
+ fitViewOptions: { padding: 0.3 },
932
+ proOptions: { hideAttribution: true },
933
+ panOnDrag: false,
934
+ zoomOnScroll: false,
935
+ zoomOnPinch: false,
936
+ zoomOnDoubleClick: false,
937
+ preventScrolling: false,
938
+ nodesDraggable: false,
939
+ nodesConnectable: false,
940
+ elementsSelectable: !!onNodeClick,
941
+ children: [
942
+ /* @__PURE__ */ jsx4(FitViewOnResize, {}),
943
+ !unstyled && /* @__PURE__ */ jsx4(Background2, { variant: BackgroundVariant2.Dots, gap: 16, size: 1 })
944
+ ]
945
+ }
946
+ )
947
+ }
642
948
  );
643
- return {
644
- breadcrumbs,
645
- nodes,
646
- edges,
647
- handleNodeClick,
648
- navigateTo,
649
- isInSubflow: stack.length > 0,
650
- currentSubflowNodeName: stack.length > 0 ? stack[stack.length - 1].label : null
651
- };
652
949
  }
653
950
 
654
951
  // src/components/FlowchartView/SubflowBreadcrumb.tsx
655
952
  import { memo as memo2 } from "react";
656
- import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
953
+ import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
657
954
  var SubflowBreadcrumb = memo2(function SubflowBreadcrumb2({
658
955
  breadcrumbs,
659
956
  onNavigate
660
957
  }) {
661
958
  if (breadcrumbs.length <= 1) return null;
662
- return /* @__PURE__ */ jsx4(
959
+ return /* @__PURE__ */ jsx5(
663
960
  "div",
664
961
  {
665
962
  style: {
@@ -676,10 +973,10 @@ var SubflowBreadcrumb = memo2(function SubflowBreadcrumb2({
676
973
  },
677
974
  children: breadcrumbs.map((crumb, i) => {
678
975
  const isLast = i === breadcrumbs.length - 1;
679
- return /* @__PURE__ */ jsxs2("span", { style: { display: "flex", alignItems: "center", gap: 4 }, children: [
680
- i > 0 && /* @__PURE__ */ jsx4("span", { style: { color: theme.textMuted, fontSize: 10 }, children: "\u203A" }),
681
- isLast ? /* @__PURE__ */ jsxs2("span", { style: { display: "flex", alignItems: "center", gap: 6 }, children: [
682
- /* @__PURE__ */ jsx4(
976
+ return /* @__PURE__ */ jsxs3("span", { style: { display: "flex", alignItems: "center", gap: 4 }, children: [
977
+ i > 0 && /* @__PURE__ */ jsx5("span", { style: { color: theme.textMuted, fontSize: 10 }, children: "\u203A" }),
978
+ isLast ? /* @__PURE__ */ jsxs3("span", { style: { display: "flex", alignItems: "center", gap: 6 }, children: [
979
+ /* @__PURE__ */ jsx5(
683
980
  "span",
684
981
  {
685
982
  style: {
@@ -689,7 +986,7 @@ var SubflowBreadcrumb = memo2(function SubflowBreadcrumb2({
689
986
  children: crumb.label
690
987
  }
691
988
  ),
692
- crumb.description && /* @__PURE__ */ jsxs2(
989
+ crumb.description && /* @__PURE__ */ jsxs3(
693
990
  "span",
694
991
  {
695
992
  style: {
@@ -703,7 +1000,7 @@ var SubflowBreadcrumb = memo2(function SubflowBreadcrumb2({
703
1000
  ]
704
1001
  }
705
1002
  )
706
- ] }) : /* @__PURE__ */ jsx4(
1003
+ ] }) : /* @__PURE__ */ jsx5(
707
1004
  "button",
708
1005
  {
709
1006
  onClick: () => onNavigate(i),
@@ -734,13 +1031,83 @@ var SubflowBreadcrumb = memo2(function SubflowBreadcrumb2({
734
1031
  );
735
1032
  });
736
1033
 
1034
+ // src/components/FlowchartView/useSubflowNavigation.ts
1035
+ import { useState as useState2, useCallback as useCallback3, useMemo as useMemo3 } from "react";
1036
+ function useSubflowNavigation(rootSpec, overlay, colors) {
1037
+ const [stack, setStack] = useState2([]);
1038
+ const currentSpec = stack.length > 0 ? stack[stack.length - 1].spec : rootSpec;
1039
+ const { nodes, edges } = useMemo3(() => {
1040
+ if (!currentSpec) return { nodes: [], edges: [] };
1041
+ return specToReactFlow(currentSpec, overlay, colors);
1042
+ }, [currentSpec, overlay, colors]);
1043
+ const subflowMap = useMemo3(() => {
1044
+ const map = /* @__PURE__ */ new Map();
1045
+ if (!currentSpec) return map;
1046
+ function collectSubflows(node) {
1047
+ if (node.isSubflowRoot && node.subflowStructure) {
1048
+ const id = node.name || node.id || "";
1049
+ map.set(id, node);
1050
+ }
1051
+ if (node.children) node.children.forEach(collectSubflows);
1052
+ if (node.next) collectSubflows(node.next);
1053
+ }
1054
+ collectSubflows(currentSpec);
1055
+ return map;
1056
+ }, [currentSpec]);
1057
+ const breadcrumbs = useMemo3(() => {
1058
+ const root = {
1059
+ label: rootSpec?.name || "Flowchart",
1060
+ spec: rootSpec,
1061
+ description: rootSpec?.description
1062
+ };
1063
+ return [root, ...stack];
1064
+ }, [rootSpec, stack]);
1065
+ const handleNodeClick = useCallback3(
1066
+ (nodeId) => {
1067
+ const subflowNode = subflowMap.get(nodeId);
1068
+ if (!subflowNode?.subflowStructure) return false;
1069
+ setStack((prev) => [
1070
+ ...prev,
1071
+ {
1072
+ label: subflowNode.subflowName || subflowNode.name,
1073
+ spec: subflowNode.subflowStructure,
1074
+ description: subflowNode.description
1075
+ }
1076
+ ]);
1077
+ return true;
1078
+ },
1079
+ [subflowMap]
1080
+ );
1081
+ const navigateTo = useCallback3(
1082
+ (level) => {
1083
+ if (level === 0) {
1084
+ setStack([]);
1085
+ } else {
1086
+ setStack((prev) => prev.slice(0, level));
1087
+ }
1088
+ },
1089
+ []
1090
+ );
1091
+ return {
1092
+ breadcrumbs,
1093
+ nodes,
1094
+ edges,
1095
+ handleNodeClick,
1096
+ navigateTo,
1097
+ isInSubflow: stack.length > 0,
1098
+ currentSubflowNodeName: stack.length > 0 ? stack[stack.length - 1].label : null
1099
+ };
1100
+ }
1101
+
737
1102
  // src/components/FlowchartView/SubflowTree.tsx
738
- import { memo as memo3, useState as useState3, useCallback as useCallback3, useMemo as useMemo3 } from "react";
739
- import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
1103
+ import { memo as memo3, useState as useState3, useCallback as useCallback4, useMemo as useMemo4 } from "react";
1104
+ import { Fragment as Fragment2, jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
740
1105
  function specToTree(node) {
1106
+ if (!node) return [];
741
1107
  const entries = [];
742
1108
  const seen = /* @__PURE__ */ new Set();
743
- function walk2(n) {
1109
+ function walk(n) {
1110
+ if (!n) return;
744
1111
  const id = n.name || n.id || "";
745
1112
  if (seen.has(id)) return;
746
1113
  seen.add(id);
@@ -756,14 +1123,14 @@ function specToTree(node) {
756
1123
  entries.push(entry);
757
1124
  if (n.children) {
758
1125
  for (const child of n.children) {
759
- walk2(child);
1126
+ if (child) walk(child);
760
1127
  }
761
1128
  }
762
1129
  if (n.next) {
763
- walk2(n.next);
1130
+ walk(n.next);
764
1131
  }
765
1132
  }
766
- walk2(node);
1133
+ walk(node);
767
1134
  return entries;
768
1135
  }
769
1136
  var TreeNode = memo3(function TreeNode2({
@@ -777,14 +1144,14 @@ var TreeNode = memo3(function TreeNode2({
777
1144
  const hasChildren = entry.children && entry.children.length > 0;
778
1145
  const isActive = activeStage === entry.name;
779
1146
  const isDone = doneStages?.has(entry.name);
780
- const handleClick = useCallback3(() => {
1147
+ const handleClick = useCallback4(() => {
781
1148
  if (hasChildren) {
782
1149
  setExpanded((prev) => !prev);
783
1150
  }
784
1151
  onNodeSelect?.(entry.name, !!entry.isSubflow);
785
1152
  }, [hasChildren, onNodeSelect, entry.name, entry.isSubflow]);
786
- return /* @__PURE__ */ jsxs3(Fragment2, { children: [
787
- /* @__PURE__ */ jsxs3(
1153
+ return /* @__PURE__ */ jsxs4(Fragment2, { children: [
1154
+ /* @__PURE__ */ jsxs4(
788
1155
  "button",
789
1156
  {
790
1157
  onClick: handleClick,
@@ -815,7 +1182,7 @@ var TreeNode = memo3(function TreeNode2({
815
1182
  }
816
1183
  },
817
1184
  children: [
818
- hasChildren ? /* @__PURE__ */ jsx5(
1185
+ hasChildren ? /* @__PURE__ */ jsx6(
819
1186
  "span",
820
1187
  {
821
1188
  style: {
@@ -830,8 +1197,8 @@ var TreeNode = memo3(function TreeNode2({
830
1197
  },
831
1198
  children: "\u25B6"
832
1199
  }
833
- ) : /* @__PURE__ */ jsx5("span", { style: { width: 12, flexShrink: 0 } }),
834
- /* @__PURE__ */ jsx5(
1200
+ ) : /* @__PURE__ */ jsx6("span", { style: { width: 12, flexShrink: 0 } }),
1201
+ /* @__PURE__ */ jsx6(
835
1202
  "span",
836
1203
  {
837
1204
  style: {
@@ -843,8 +1210,8 @@ var TreeNode = memo3(function TreeNode2({
843
1210
  }
844
1211
  }
845
1212
  ),
846
- /* @__PURE__ */ jsxs3("span", { style: { display: "flex", flexDirection: "column", minWidth: 0 }, children: [
847
- /* @__PURE__ */ jsxs3(
1213
+ /* @__PURE__ */ jsxs4("span", { style: { display: "flex", flexDirection: "column", minWidth: 0 }, children: [
1214
+ /* @__PURE__ */ jsxs4(
848
1215
  "span",
849
1216
  {
850
1217
  style: {
@@ -856,11 +1223,11 @@ var TreeNode = memo3(function TreeNode2({
856
1223
  },
857
1224
  children: [
858
1225
  entry.name,
859
- entry.isSubflow && /* @__PURE__ */ jsx5("span", { style: { opacity: 0.5, marginLeft: 4, fontSize: 10 }, children: "\u229E" })
1226
+ entry.isSubflow && /* @__PURE__ */ jsx6("span", { style: { opacity: 0.5, marginLeft: 4, fontSize: 10 }, children: "\u229E" })
860
1227
  ]
861
1228
  }
862
1229
  ),
863
- entry.description && /* @__PURE__ */ jsx5(
1230
+ entry.description && /* @__PURE__ */ jsx6(
864
1231
  "span",
865
1232
  {
866
1233
  style: {
@@ -877,7 +1244,7 @@ var TreeNode = memo3(function TreeNode2({
877
1244
  ]
878
1245
  }
879
1246
  ),
880
- hasChildren && expanded && /* @__PURE__ */ jsx5("div", { children: entry.children.map((child, i) => /* @__PURE__ */ jsx5(
1247
+ hasChildren && expanded && /* @__PURE__ */ jsx6("div", { children: entry.children.map((child, i) => /* @__PURE__ */ jsx6(
881
1248
  TreeNode2,
882
1249
  {
883
1250
  entry: child,
@@ -886,12 +1253,12 @@ var TreeNode = memo3(function TreeNode2({
886
1253
  doneStages,
887
1254
  onNodeSelect
888
1255
  },
889
- `${child.name}-${i}`
1256
+ child.subflowId ?? `${child.name}-${i}`
890
1257
  )) })
891
1258
  ] });
892
1259
  });
893
1260
  var SectionLabel = memo3(function SectionLabel2({ children }) {
894
- return /* @__PURE__ */ jsx5(
1261
+ return /* @__PURE__ */ jsx6(
895
1262
  "div",
896
1263
  {
897
1264
  style: {
@@ -915,10 +1282,10 @@ var SubflowTree = memo3(function SubflowTree2({
915
1282
  className,
916
1283
  style
917
1284
  }) {
918
- const tree = useMemo3(() => specToTree(spec), [spec]);
919
- const subflowStages = useMemo3(() => tree.filter((e) => e.isSubflow), [tree]);
1285
+ const tree = useMemo4(() => specToTree(spec), [spec]);
1286
+ const subflowStages = useMemo4(() => tree.filter((e) => e.isSubflow), [tree]);
920
1287
  if (subflowStages.length === 0) return null;
921
- return /* @__PURE__ */ jsxs3(
1288
+ return /* @__PURE__ */ jsxs4(
922
1289
  "div",
923
1290
  {
924
1291
  className,
@@ -936,8 +1303,8 @@ var SubflowTree = memo3(function SubflowTree2({
936
1303
  ...style
937
1304
  },
938
1305
  children: [
939
- !unstyled && /* @__PURE__ */ jsx5(SectionLabel, { children: "Subflows" }),
940
- subflowStages.map((entry, i) => /* @__PURE__ */ jsx5(
1306
+ !unstyled && /* @__PURE__ */ jsx6(SectionLabel, { children: "Subflows" }),
1307
+ subflowStages.map((entry, i) => /* @__PURE__ */ jsx6(
941
1308
  TreeNode,
942
1309
  {
943
1310
  entry,
@@ -946,180 +1313,18 @@ var SubflowTree = memo3(function SubflowTree2({
946
1313
  doneStages,
947
1314
  onNodeSelect
948
1315
  },
949
- `${entry.name}-${i}`
1316
+ entry.subflowId ?? `${entry.name}-${i}`
950
1317
  ))
951
1318
  ]
952
1319
  }
953
1320
  );
954
1321
  });
955
1322
 
956
- // src/components/FlowchartView/TracedFlowchartView.tsx
957
- import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
958
- var nodeTypes2 = { stage: StageNode };
959
- function TracedFlowchartView({
960
- spec,
961
- snapshots,
962
- snapshotIndex = 0,
963
- onNodeClick,
964
- onSubflowChange,
965
- showTree = true,
966
- treeWidth = 200,
967
- unstyled = false,
968
- className,
969
- style
970
- }) {
971
- const [treeVisible, setTreeVisible] = useState4(showTree);
972
- const subflowNav = useSubflowNavigation(spec);
973
- const currentSpec = subflowNav.breadcrumbs.length > 0 ? subflowNav.breadcrumbs[subflowNav.breadcrumbs.length - 1].spec : null;
974
- const overlay = useMemo4(() => {
975
- if (!snapshots || snapshots.length === 0) return void 0;
976
- const executionOrder = snapshots.slice(0, snapshotIndex + 1).map((s) => s.stageLabel);
977
- const doneStages = new Set(
978
- snapshots.slice(0, snapshotIndex).map((s) => s.stageLabel)
979
- );
980
- const activeStage = snapshots[snapshotIndex]?.stageLabel ?? null;
981
- const executedStages = /* @__PURE__ */ new Set([...doneStages]);
982
- if (activeStage) executedStages.add(activeStage);
983
- return { doneStages, activeStage, executedStages, executionOrder };
984
- }, [snapshots, snapshotIndex]);
985
- const { nodes, edges } = useMemo4(() => {
986
- if (!currentSpec) return { nodes: [], edges: [] };
987
- return specToReactFlow(currentSpec, overlay);
988
- }, [currentSpec, overlay]);
989
- const handleNodeClick = useCallback4(
990
- (_, node) => {
991
- if (subflowNav.handleNodeClick(node.id)) {
992
- onSubflowChange?.(true, node.id);
993
- return;
994
- }
995
- if (onNodeClick && snapshots) {
996
- const idx = snapshots.findIndex((s) => s.stageLabel === node.id);
997
- if (idx >= 0) onNodeClick(idx);
998
- } else if (onNodeClick) {
999
- onNodeClick(node.id);
1000
- }
1001
- },
1002
- [subflowNav, onNodeClick, onSubflowChange, snapshots]
1003
- );
1004
- const handleBreadcrumbNavigate = useCallback4(
1005
- (level) => {
1006
- subflowNav.navigateTo(level);
1007
- onSubflowChange?.(level > 0, null);
1008
- },
1009
- [subflowNav, onSubflowChange]
1010
- );
1011
- const handleTreeNodeSelect = useCallback4(
1012
- (name, isSubflow) => {
1013
- if (isSubflow) {
1014
- if (subflowNav.handleNodeClick(name)) {
1015
- onSubflowChange?.(true, name);
1016
- }
1017
- } else if (onNodeClick && snapshots) {
1018
- const idx = snapshots.findIndex((s) => s.stageLabel === name);
1019
- if (idx >= 0) onNodeClick(idx);
1020
- }
1021
- },
1022
- [subflowNav, onNodeClick, onSubflowChange, snapshots]
1023
- );
1024
- return /* @__PURE__ */ jsxs4(
1025
- "div",
1026
- {
1027
- className,
1028
- style: { width: "100%", height: "100%", display: "flex", flexDirection: "row", ...style },
1029
- "data-fp": "traced-flowchart",
1030
- children: [
1031
- showTree && treeVisible && /* @__PURE__ */ jsx6(
1032
- SubflowTree,
1033
- {
1034
- spec,
1035
- activeStage: overlay?.activeStage,
1036
- doneStages: overlay?.doneStages,
1037
- onNodeSelect: handleTreeNodeSelect,
1038
- unstyled,
1039
- style: { width: treeWidth, flexShrink: 0, height: "100%" }
1040
- }
1041
- ),
1042
- /* @__PURE__ */ jsxs4("div", { style: { flex: 1, display: "flex", flexDirection: "column", minWidth: 0, height: "100%" }, children: [
1043
- (subflowNav.isInSubflow || showTree && !treeVisible) && /* @__PURE__ */ jsxs4("div", { style: { display: "flex", alignItems: "stretch", flexShrink: 0 }, children: [
1044
- showTree && !treeVisible && /* @__PURE__ */ jsx6(
1045
- "button",
1046
- {
1047
- onClick: () => setTreeVisible(true),
1048
- "data-fp": "tree-toggle",
1049
- style: unstyled ? {} : {
1050
- background: "transparent",
1051
- border: "none",
1052
- cursor: "pointer",
1053
- padding: "6px 8px",
1054
- fontSize: 10,
1055
- flexShrink: 0
1056
- },
1057
- children: "\u25B6"
1058
- }
1059
- ),
1060
- /* @__PURE__ */ jsx6("div", { style: { flex: 1 }, children: /* @__PURE__ */ jsx6(
1061
- SubflowBreadcrumb,
1062
- {
1063
- breadcrumbs: subflowNav.breadcrumbs,
1064
- onNavigate: handleBreadcrumbNavigate
1065
- }
1066
- ) })
1067
- ] }),
1068
- showTree && treeVisible && /* @__PURE__ */ jsxs4("div", { style: { display: "flex", alignItems: "stretch", flexShrink: 0 }, children: [
1069
- /* @__PURE__ */ jsx6(
1070
- "button",
1071
- {
1072
- onClick: () => setTreeVisible(false),
1073
- "data-fp": "tree-toggle",
1074
- style: unstyled ? {} : {
1075
- background: "transparent",
1076
- border: "none",
1077
- cursor: "pointer",
1078
- padding: "6px 8px",
1079
- fontSize: 10,
1080
- flexShrink: 0
1081
- },
1082
- children: "\u25C0"
1083
- }
1084
- ),
1085
- /* @__PURE__ */ jsx6("div", { style: { flex: 1 }, children: subflowNav.isInSubflow && /* @__PURE__ */ jsx6(
1086
- SubflowBreadcrumb,
1087
- {
1088
- breadcrumbs: subflowNav.breadcrumbs,
1089
- onNavigate: handleBreadcrumbNavigate
1090
- }
1091
- ) })
1092
- ] }),
1093
- /* @__PURE__ */ jsx6("div", { style: { flex: 1, minHeight: 0 }, children: /* @__PURE__ */ jsx6(
1094
- ReactFlow2,
1095
- {
1096
- nodes,
1097
- edges,
1098
- onNodeClick: handleNodeClick,
1099
- nodeTypes: nodeTypes2,
1100
- fitView: true,
1101
- panOnDrag: false,
1102
- zoomOnScroll: false,
1103
- zoomOnPinch: false,
1104
- zoomOnDoubleClick: false,
1105
- preventScrolling: false,
1106
- nodesDraggable: false,
1107
- nodesConnectable: false,
1108
- elementsSelectable: !!onNodeClick,
1109
- children: !unstyled && /* @__PURE__ */ jsx6(Background2, { variant: BackgroundVariant2.Dots, gap: 16, size: 1 })
1110
- }
1111
- ) })
1112
- ] })
1113
- ]
1114
- }
1115
- );
1116
- }
1117
-
1118
1323
  // src/components/TimeTravelDebugger/TimeTravelDebugger.tsx
1119
- import { useState as useState6 } from "react";
1324
+ import { useState as useState5 } from "react";
1120
1325
 
1121
1326
  // src/components/MemoryInspector/MemoryInspector.tsx
1122
- import { useMemo as useMemo5 } from "react";
1327
+ import { useMemo as useMemo5, useRef as useRef2 } from "react";
1123
1328
  import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
1124
1329
  function MemoryInspector({
1125
1330
  data,
@@ -1132,6 +1337,7 @@ function MemoryInspector({
1132
1337
  className,
1133
1338
  style
1134
1339
  }) {
1340
+ const cacheRef = useRef2(null);
1135
1341
  const { memory, newKeys } = useMemo5(() => {
1136
1342
  if (data) {
1137
1343
  return { memory: data, newKeys: /* @__PURE__ */ new Set() };
@@ -1139,21 +1345,37 @@ function MemoryInspector({
1139
1345
  if (!snapshots || snapshots.length === 0) {
1140
1346
  return { memory: {}, newKeys: /* @__PURE__ */ new Set() };
1141
1347
  }
1142
- const merged = {};
1143
- for (let i = 0; i <= Math.min(selectedIndex, snapshots.length - 1); i++) {
1144
- Object.assign(merged, snapshots[i]?.memory);
1348
+ const safeIdx = Math.min(selectedIndex, snapshots.length - 1);
1349
+ let merged;
1350
+ const cache = cacheRef.current;
1351
+ if (cache && cache.snapshots === snapshots && cache.index <= safeIdx) {
1352
+ merged = { ...cache.accumulated };
1353
+ for (let i = cache.index + 1; i <= safeIdx; i++) {
1354
+ Object.assign(merged, snapshots[i]?.memory);
1355
+ }
1356
+ } else {
1357
+ merged = {};
1358
+ for (let i = 0; i <= safeIdx; i++) {
1359
+ Object.assign(merged, snapshots[i]?.memory);
1360
+ }
1145
1361
  }
1362
+ cacheRef.current = { snapshots, index: safeIdx, accumulated: merged };
1146
1363
  const nk = /* @__PURE__ */ new Set();
1147
- if (highlightNew && selectedIndex > 0) {
1148
- const prev = {};
1149
- for (let i = 0; i < selectedIndex; i++) {
1150
- Object.assign(prev, snapshots[i]?.memory);
1364
+ if (highlightNew && safeIdx > 0) {
1365
+ let prev;
1366
+ if (cache && cache.snapshots === snapshots && cache.index === safeIdx - 1) {
1367
+ prev = cache.accumulated;
1368
+ } else {
1369
+ prev = {};
1370
+ for (let i = 0; i < safeIdx; i++) {
1371
+ Object.assign(prev, snapshots[i]?.memory);
1372
+ }
1151
1373
  }
1152
- const current = snapshots[selectedIndex]?.memory ?? {};
1374
+ const current = snapshots[safeIdx]?.memory ?? {};
1153
1375
  for (const k of Object.keys(current)) {
1154
1376
  if (!(k in prev)) nk.add(k);
1155
1377
  }
1156
- } else if (highlightNew && selectedIndex === 0 && snapshots[0]) {
1378
+ } else if (highlightNew && safeIdx === 0 && snapshots[0]) {
1157
1379
  for (const k of Object.keys(snapshots[0].memory)) nk.add(k);
1158
1380
  }
1159
1381
  return { memory: merged, newKeys: nk };
@@ -1162,9 +1384,9 @@ function MemoryInspector({
1162
1384
  const fs = fontSize[size];
1163
1385
  const pad = padding[size];
1164
1386
  if (unstyled) {
1165
- return /* @__PURE__ */ jsxs5("div", { className, style, "data-fp": "memory-inspector", children: [
1387
+ return /* @__PURE__ */ jsxs5("div", { className, style, "data-fp": "memory-inspector", role: "region", "aria-label": "Memory state", children: [
1166
1388
  /* @__PURE__ */ jsx7("div", { "data-fp": "memory-label", children: "Memory State" }),
1167
- /* @__PURE__ */ jsx7("pre", { "data-fp": "memory-json", children: JSON.stringify(memory, null, 2) })
1389
+ /* @__PURE__ */ jsx7("pre", { "data-fp": "memory-json", children: /* @__PURE__ */ jsx7("code", { children: JSON.stringify(memory, null, 2) }) })
1168
1390
  ] });
1169
1391
  }
1170
1392
  return /* @__PURE__ */ jsxs5(
@@ -1177,6 +1399,8 @@ function MemoryInspector({
1177
1399
  ...style
1178
1400
  },
1179
1401
  "data-fp": "memory-inspector",
1402
+ role: "region",
1403
+ "aria-label": "Memory state",
1180
1404
  children: [
1181
1405
  /* @__PURE__ */ jsx7(
1182
1406
  "span",
@@ -1409,7 +1633,7 @@ function NarrativeLog({
1409
1633
  }
1410
1634
 
1411
1635
  // src/components/GanttTimeline/GanttTimeline.tsx
1412
- import { useState as useState5, useMemo as useMemo7, useRef, useEffect as useEffect2 } from "react";
1636
+ import { useState as useState4, useMemo as useMemo7, useRef as useRef3, useEffect as useEffect4 } from "react";
1413
1637
  import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
1414
1638
  function GanttTimeline({
1415
1639
  snapshots,
@@ -1421,9 +1645,9 @@ function GanttTimeline({
1421
1645
  style,
1422
1646
  maxVisibleRows = 5
1423
1647
  }) {
1424
- const [expanded, setExpanded] = useState5(false);
1425
- const activeRowRef = useRef(null);
1426
- const scrollContainerRef = useRef(null);
1648
+ const [expanded, setExpanded] = useState4(false);
1649
+ const activeRowRef = useRef3(null);
1650
+ const scrollContainerRef = useRef3(null);
1427
1651
  const totalWallTime = useMemo7(
1428
1652
  () => Math.max(...snapshots.map((s) => s.startMs + s.durationMs), 1),
1429
1653
  [snapshots]
@@ -1435,7 +1659,7 @@ function GanttTimeline({
1435
1659
  const rowHeight = size === "compact" ? 18 : 22;
1436
1660
  const collapsible = maxVisibleRows > 0 && snapshots.length > maxVisibleRows;
1437
1661
  const showAll = expanded || !collapsible;
1438
- useEffect2(() => {
1662
+ useEffect4(() => {
1439
1663
  if (!showAll && activeRowRef.current && scrollContainerRef.current) {
1440
1664
  activeRowRef.current.scrollIntoView({
1441
1665
  block: "nearest",
@@ -1444,12 +1668,15 @@ function GanttTimeline({
1444
1668
  }
1445
1669
  }, [selectedIndex, showAll]);
1446
1670
  if (unstyled) {
1447
- return /* @__PURE__ */ jsx9("div", { className, style, "data-fp": "gantt-timeline", children: snapshots.map((snap, idx) => /* @__PURE__ */ jsxs7(
1671
+ return /* @__PURE__ */ jsx9("div", { className, style, "data-fp": "gantt-timeline", role: "listbox", "aria-label": "Execution timeline", children: snapshots.map((snap, idx) => /* @__PURE__ */ jsxs7(
1448
1672
  "div",
1449
1673
  {
1450
1674
  "data-fp": "gantt-bar",
1451
1675
  "data-selected": idx === selectedIndex,
1452
1676
  "data-visible": idx <= selectedIndex,
1677
+ role: "option",
1678
+ "aria-selected": idx === selectedIndex,
1679
+ "aria-label": `${snap.stageLabel}, ${snap.durationMs}ms`,
1453
1680
  onClick: () => onSelect?.(idx),
1454
1681
  children: [
1455
1682
  /* @__PURE__ */ jsx9("span", { "data-fp": "gantt-label", children: snap.stageLabel }),
@@ -1459,7 +1686,7 @@ function GanttTimeline({
1459
1686
  ] })
1460
1687
  ]
1461
1688
  },
1462
- snap.stageName
1689
+ `${snap.stageName}-${idx}`
1463
1690
  )) });
1464
1691
  }
1465
1692
  return /* @__PURE__ */ jsxs7(
@@ -1515,6 +1742,8 @@ function GanttTimeline({
1515
1742
  "div",
1516
1743
  {
1517
1744
  ref: scrollContainerRef,
1745
+ role: "listbox",
1746
+ "aria-label": "Execution timeline",
1518
1747
  style: {
1519
1748
  marginTop: 8,
1520
1749
  display: "flex",
@@ -1535,6 +1764,9 @@ function GanttTimeline({
1535
1764
  "div",
1536
1765
  {
1537
1766
  ref: isSelected ? activeRowRef : void 0,
1767
+ role: "option",
1768
+ "aria-selected": isSelected,
1769
+ "aria-label": `${snap.stageLabel}, ${snap.durationMs}ms`,
1538
1770
  onClick: () => onSelect?.(idx),
1539
1771
  style: {
1540
1772
  display: "flex",
@@ -1550,6 +1782,7 @@ function GanttTimeline({
1550
1782
  /* @__PURE__ */ jsx9(
1551
1783
  "span",
1552
1784
  {
1785
+ title: snap.stageLabel,
1553
1786
  style: {
1554
1787
  width: labelWidth,
1555
1788
  fontSize: fs.small,
@@ -1609,7 +1842,7 @@ function GanttTimeline({
1609
1842
  )
1610
1843
  ]
1611
1844
  },
1612
- snap.stageName
1845
+ `${snap.stageName}-${idx}`
1613
1846
  );
1614
1847
  })
1615
1848
  }
@@ -1659,7 +1892,7 @@ function TimeTravelDebugger({
1659
1892
  className,
1660
1893
  style
1661
1894
  }) {
1662
- const [selectedIndex, setSelectedIndex] = useState6(0);
1895
+ const [selectedIndex, setSelectedIndex] = useState5(0);
1663
1896
  const fs = fontSize[size];
1664
1897
  const pad = padding[size];
1665
1898
  if (snapshots.length === 0) {
@@ -1963,6 +2196,8 @@ export {
1963
2196
  SubflowTree,
1964
2197
  TimeTravelDebugger,
1965
2198
  TracedFlowchartView,
2199
+ applyOverlay,
2200
+ specToLayout,
1966
2201
  specToReactFlow,
1967
2202
  useSubflowNavigation
1968
2203
  };