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.
@@ -26,6 +26,8 @@ __export(flowchart_exports, {
26
26
  SubflowTree: () => SubflowTree,
27
27
  TimeTravelDebugger: () => TimeTravelDebugger,
28
28
  TracedFlowchartView: () => TracedFlowchartView,
29
+ applyOverlay: () => applyOverlay,
30
+ specToLayout: () => specToLayout,
29
31
  specToReactFlow: () => specToReactFlow,
30
32
  useSubflowNavigation: () => useSubflowNavigation
31
33
  });
@@ -43,7 +45,7 @@ var import_react4 = require("@xyflow/react");
43
45
  var import_react = require("react");
44
46
 
45
47
  // src/theme/tokens.ts
46
- var defaultTokens = {
48
+ var rawDefaults = {
47
49
  colors: {
48
50
  primary: "#6366f1",
49
51
  success: "#22c55e",
@@ -63,6 +65,26 @@ var defaultTokens = {
63
65
  mono: "'JetBrains Mono', 'Fira Code', monospace"
64
66
  }
65
67
  };
68
+ var defaultTokens = {
69
+ colors: {
70
+ primary: `var(--fp-color-primary, ${rawDefaults.colors.primary})`,
71
+ success: `var(--fp-color-success, ${rawDefaults.colors.success})`,
72
+ error: `var(--fp-color-error, ${rawDefaults.colors.error})`,
73
+ warning: `var(--fp-color-warning, ${rawDefaults.colors.warning})`,
74
+ bgPrimary: `var(--fp-bg-primary, ${rawDefaults.colors.bgPrimary})`,
75
+ bgSecondary: `var(--fp-bg-secondary, ${rawDefaults.colors.bgSecondary})`,
76
+ bgTertiary: `var(--fp-bg-tertiary, ${rawDefaults.colors.bgTertiary})`,
77
+ textPrimary: `var(--fp-text-primary, ${rawDefaults.colors.textPrimary})`,
78
+ textSecondary: `var(--fp-text-secondary, ${rawDefaults.colors.textSecondary})`,
79
+ textMuted: `var(--fp-text-muted, ${rawDefaults.colors.textMuted})`,
80
+ border: `var(--fp-border, ${rawDefaults.colors.border})`
81
+ },
82
+ radius: `var(--fp-radius, ${rawDefaults.radius})`,
83
+ fontFamily: {
84
+ sans: `var(--fp-font-sans, ${rawDefaults.fontFamily.sans})`,
85
+ mono: `var(--fp-font-mono, ${rawDefaults.fontFamily.mono})`
86
+ }
87
+ };
66
88
 
67
89
  // src/theme/ThemeProvider.tsx
68
90
  var import_jsx_runtime = require("react/jsx-runtime");
@@ -104,10 +126,169 @@ var import_react2 = require("react");
104
126
 
105
127
  // src/components/StageNode/StageNode.tsx
106
128
  var import_jsx_runtime2 = require("react/jsx-runtime");
129
+ var KEYFRAMES_ID = "fp-stage-node-keyframes";
130
+ var KEYFRAMES_CSS = `
131
+ @media (prefers-reduced-motion: no-preference) {
132
+ @keyframes fp-pulse {
133
+ 0%, 100% { opacity: 0.4; transform: scale(1); }
134
+ 50% { opacity: 0.15; transform: scale(1.06); }
135
+ }
136
+ @keyframes fp-blink {
137
+ 0%, 100% { opacity: 1; }
138
+ 50% { opacity: 0.3; }
139
+ }
140
+ }
141
+ @media (prefers-reduced-motion: reduce) {
142
+ @keyframes fp-pulse { 0%, 100% { opacity: 0.3; } }
143
+ @keyframes fp-blink { 0%, 100% { opacity: 1; } }
144
+ }
145
+ `;
146
+ var ICON_SIZE = 16;
147
+ function StageIcon({ type, color }) {
148
+ const s = ICON_SIZE;
149
+ const props = { width: s, height: s, viewBox: `0 0 ${s} ${s}`, fill: "none", style: { flexShrink: 0 } };
150
+ switch (type) {
151
+ // LLM / AI call — brain/sparkle
152
+ case "llm":
153
+ case "ai":
154
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { ...props, children: [
155
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "8", cy: "8", r: "6", stroke: color, strokeWidth: "1.5" }),
156
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("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" }),
157
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "8", cy: "9.5", r: "1", fill: color }),
158
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "8", y1: "2", x2: "8", y2: "3.5", stroke: color, strokeWidth: "1", strokeLinecap: "round" }),
159
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "12.5", y1: "4", x2: "11.2", y2: "5", stroke: color, strokeWidth: "1", strokeLinecap: "round" }),
160
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "3.5", y1: "4", x2: "4.8", y2: "5", stroke: color, strokeWidth: "1", strokeLinecap: "round" })
161
+ ] });
162
+ // Tool / function call — gear
163
+ case "tool":
164
+ case "function":
165
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { ...props, children: [
166
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "8", cy: "8", r: "3", stroke: color, strokeWidth: "1.5" }),
167
+ [0, 45, 90, 135, 180, 225, 270, 315].map((angle) => {
168
+ const rad = angle * Math.PI / 180;
169
+ const x1 = 8 + Math.cos(rad) * 4.5;
170
+ const y1 = 8 + Math.sin(rad) * 4.5;
171
+ const x2 = 8 + Math.cos(rad) * 6;
172
+ const y2 = 8 + Math.sin(rad) * 6;
173
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1, y1, x2, y2, stroke: color, strokeWidth: "1.5", strokeLinecap: "round" }, angle);
174
+ })
175
+ ] });
176
+ // RAG / retrieval — magnifying glass + doc
177
+ case "rag":
178
+ case "search":
179
+ case "retrieval":
180
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { ...props, children: [
181
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "7", cy: "7", r: "4", stroke: color, strokeWidth: "1.5" }),
182
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "10", y1: "10", x2: "13.5", y2: "13.5", stroke: color, strokeWidth: "1.5", strokeLinecap: "round" }),
183
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "5.5", y1: "6", x2: "8.5", y2: "6", stroke: color, strokeWidth: "1", strokeLinecap: "round" }),
184
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "5.5", y1: "8", x2: "7.5", y2: "8", stroke: color, strokeWidth: "1", strokeLinecap: "round" })
185
+ ] });
186
+ // Parse / process — diamond with arrows
187
+ case "parse":
188
+ case "process":
189
+ case "transform":
190
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { ...props, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { x: "4", y: "4", width: "8", height: "8", rx: "1.5", stroke: color, strokeWidth: "1.5", transform: "rotate(45 8 8)" }) });
191
+ // Start / seed — play triangle
192
+ case "start":
193
+ case "seed":
194
+ case "init":
195
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { ...props, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M5 3.5L12.5 8L5 12.5V3.5Z", fill: color, opacity: "0.8" }) });
196
+ // End / finalize — stop square
197
+ case "end":
198
+ case "finalize":
199
+ case "output":
200
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { ...props, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { x: "4", y: "4", width: "8", height: "8", rx: "1.5", fill: color, opacity: "0.8" }) });
201
+ // Agent — person silhouette
202
+ case "agent":
203
+ case "orchestrator":
204
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { ...props, children: [
205
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "8", cy: "5", r: "2.5", stroke: color, strokeWidth: "1.5" }),
206
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M3.5 14C3.5 11 5.5 9 8 9S12.5 11 12.5 14", stroke: color, strokeWidth: "1.5", strokeLinecap: "round" })
207
+ ] });
208
+ // Swarm — multi-agent
209
+ case "swarm":
210
+ case "multi-agent":
211
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { ...props, children: [
212
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "5", cy: "5", r: "2", stroke: color, strokeWidth: "1.2" }),
213
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "11", cy: "5", r: "2", stroke: color, strokeWidth: "1.2" }),
214
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "8", cy: "11", r: "2", stroke: color, strokeWidth: "1.2" }),
215
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "5", y1: "7", x2: "8", y2: "9", stroke: color, strokeWidth: "1", opacity: "0.5" }),
216
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "11", y1: "7", x2: "8", y2: "9", stroke: color, strokeWidth: "1", opacity: "0.5" })
217
+ ] });
218
+ // Guard / guardrail — shield
219
+ case "guard":
220
+ case "guardrail":
221
+ case "validate":
222
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { ...props, children: [
223
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("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" }),
224
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M6 8L7.5 9.5L10 6.5", stroke: color, strokeWidth: "1.2", strokeLinecap: "round", strokeLinejoin: "round" })
225
+ ] });
226
+ // Stream — wave
227
+ case "stream":
228
+ case "streaming":
229
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { ...props, children: [
230
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M2 8C4 5 6 11 8 8S12 5 14 8", stroke: color, strokeWidth: "1.5", strokeLinecap: "round", fill: "none" }),
231
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M2 11C4 8 6 14 8 11S12 8 14 11", stroke: color, strokeWidth: "1", strokeLinecap: "round", fill: "none", opacity: "0.5" })
232
+ ] });
233
+ // Memory / state — database cylinder
234
+ case "memory":
235
+ case "state":
236
+ case "db":
237
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { ...props, children: [
238
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("ellipse", { cx: "8", cy: "4.5", rx: "5", ry: "2", stroke: color, strokeWidth: "1.3" }),
239
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "3", y1: "4.5", x2: "3", y2: "11.5", stroke: color, strokeWidth: "1.3" }),
240
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: "13", y1: "4.5", x2: "13", y2: "11.5", stroke: color, strokeWidth: "1.3" }),
241
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("ellipse", { cx: "8", cy: "11.5", rx: "5", ry: "2", stroke: color, strokeWidth: "1.3" })
242
+ ] });
243
+ // Loop — circular arrow
244
+ case "loop":
245
+ case "retry":
246
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { ...props, children: [
247
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M12 8A4 4 0 1 1 8 4", stroke: color, strokeWidth: "1.5", strokeLinecap: "round", fill: "none" }),
248
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M8 1.5L10.5 4L8 6.5", stroke: color, strokeWidth: "1.3", strokeLinecap: "round", strokeLinejoin: "round", fill: "none" })
249
+ ] });
250
+ // Lazy / service — cloud (deferred resolution, loaded on demand)
251
+ case "lazy":
252
+ case "service":
253
+ case "cloud":
254
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { ...props, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
255
+ "path",
256
+ {
257
+ 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",
258
+ stroke: color,
259
+ strokeWidth: "1.3",
260
+ strokeLinecap: "round",
261
+ fill: "none"
262
+ }
263
+ ) });
264
+ // Decision — diamond (already handled by isDecider shape)
265
+ case "decision":
266
+ case "router":
267
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { ...props, children: [
268
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M8 2L14 8L8 14L2 8Z", stroke: color, strokeWidth: "1.5", fill: "none" }),
269
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "8", cy: "8", r: "1.5", fill: color })
270
+ ] });
271
+ default:
272
+ return null;
273
+ }
274
+ }
107
275
  var StageNode = (0, import_react3.memo)(function StageNode2({
108
276
  data
109
277
  }) {
110
- const { label, active, done, error, linked, stepNumbers, dimmed, isSubflow, description } = data;
278
+ const { label, active, done, error, linked, icon, stepNumbers, dimmed, isSubflow, isLazy, isDecider, isFork, description } = data;
279
+ const effectiveIcon = icon || (isLazy ? "lazy" : void 0);
280
+ const isLazyUnresolved = isLazy && !done && !active;
281
+ const injectedRef = (0, import_react3.useRef)(false);
282
+ (0, import_react3.useEffect)(() => {
283
+ if (injectedRef.current) return;
284
+ if (typeof document !== "undefined" && !document.getElementById(KEYFRAMES_ID)) {
285
+ const styleEl = document.createElement("style");
286
+ styleEl.id = KEYFRAMES_ID;
287
+ styleEl.textContent = KEYFRAMES_CSS;
288
+ document.head.appendChild(styleEl);
289
+ }
290
+ injectedRef.current = true;
291
+ }, []);
111
292
  const isOnPath = active || done;
112
293
  const bg = active ? theme.primary : done ? theme.success : error ? theme.error : theme.bgSecondary;
113
294
  const borderColor = active ? theme.primary : done ? theme.success : error ? theme.error : theme.border;
@@ -169,7 +350,8 @@ var StageNode = (0, import_react3.memo)(function StageNode2({
169
350
  style: {
170
351
  position: "absolute",
171
352
  inset: -6,
172
- borderRadius: `calc(${theme.radius} + 4px)`,
353
+ borderRadius: isDecider ? 0 : `calc(${theme.radius} + 4px)`,
354
+ transform: isDecider ? "rotate(45deg)" : void 0,
173
355
  border: `2px solid ${theme.primary}`,
174
356
  opacity: 0.4,
175
357
  animation: "fp-pulse 2s ease-in-out infinite"
@@ -182,107 +364,175 @@ var StageNode = (0, import_react3.memo)(function StageNode2({
182
364
  style: {
183
365
  position: "absolute",
184
366
  inset: -6,
185
- borderRadius: `calc(${theme.radius} + 4px)`,
367
+ borderRadius: isDecider ? 0 : `calc(${theme.radius} + 4px)`,
368
+ transform: isDecider ? "rotate(45deg)" : void 0,
186
369
  border: `2px solid ${theme.primary}`,
187
370
  opacity: 0.3,
188
371
  animation: "fp-pulse 1.5s ease-out infinite"
189
372
  }
190
373
  }
191
374
  ),
192
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
375
+ isDecider ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
193
376
  "div",
194
377
  {
195
378
  style: {
196
379
  background: bg,
197
- border: `2px solid ${borderColor}`,
198
- borderRadius: theme.radius,
199
- padding: description ? "8px 16px" : "10px 20px",
200
- display: "flex",
201
- flexDirection: "column",
202
- alignItems: "center",
203
- gap: description ? 2 : 0,
380
+ border: `2px ${isLazyUnresolved ? "dashed" : "solid"} ${borderColor}`,
381
+ borderRadius: 4,
382
+ transform: "rotate(45deg)",
383
+ padding: 20,
204
384
  boxShadow: shadow,
205
385
  transition: "all 0.3s ease",
206
- fontFamily: theme.fontSans,
207
- minWidth: 100,
386
+ display: "flex",
387
+ alignItems: "center",
208
388
  justifyContent: "center"
209
389
  },
210
- children: [
211
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 6 }, children: [
212
- done && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { fontSize: 10, color: textColor }, children: "\u2713" }),
213
- active && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
214
- "span",
215
- {
216
- style: {
217
- width: 8,
218
- height: 8,
219
- borderRadius: "50%",
220
- background: "#fff",
221
- animation: "fp-blink 1s ease-in-out infinite",
222
- flexShrink: 0
390
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
391
+ "div",
392
+ {
393
+ style: {
394
+ transform: "rotate(-45deg)",
395
+ display: "flex",
396
+ flexDirection: "column",
397
+ alignItems: "center",
398
+ gap: 2,
399
+ fontFamily: theme.fontSans
400
+ },
401
+ children: [
402
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 5 }, children: [
403
+ icon && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(StageIcon, { type: icon, color: textColor }),
404
+ !icon && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { fontSize: 10, color: textColor }, children: "\u25C7" }),
405
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
406
+ "span",
407
+ {
408
+ style: {
409
+ fontSize: 12,
410
+ fontWeight: 600,
411
+ color: textColor,
412
+ whiteSpace: "nowrap"
413
+ },
414
+ children: label
415
+ }
416
+ )
417
+ ] }),
418
+ description && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
419
+ "span",
420
+ {
421
+ style: {
422
+ fontSize: 9,
423
+ fontWeight: 400,
424
+ color: textColor,
425
+ opacity: 0.7,
426
+ whiteSpace: "nowrap",
427
+ overflow: "hidden",
428
+ textOverflow: "ellipsis",
429
+ maxWidth: 130
430
+ },
431
+ children: description
223
432
  }
224
- }
225
- ),
226
- error && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { fontSize: 10, color: textColor }, children: "\u2717" }),
227
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
433
+ )
434
+ ]
435
+ }
436
+ )
437
+ }
438
+ ) : (
439
+ /* Standard rectangular node */
440
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
441
+ "div",
442
+ {
443
+ style: {
444
+ background: bg,
445
+ border: `2px ${isLazyUnresolved ? "dashed" : "solid"} ${borderColor}`,
446
+ borderRadius: theme.radius,
447
+ padding: description ? "8px 16px" : "10px 20px",
448
+ display: "flex",
449
+ flexDirection: "column",
450
+ alignItems: "center",
451
+ gap: description ? 2 : 0,
452
+ boxShadow: shadow,
453
+ transition: "all 0.3s ease",
454
+ fontFamily: theme.fontSans,
455
+ minWidth: 100,
456
+ justifyContent: "center"
457
+ },
458
+ children: [
459
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 6 }, children: [
460
+ effectiveIcon && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(StageIcon, { type: effectiveIcon, color: textColor }),
461
+ done && !effectiveIcon && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { fontSize: 10, color: textColor }, children: "\u2713" }),
462
+ active && !effectiveIcon && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
463
+ "span",
464
+ {
465
+ style: {
466
+ width: 8,
467
+ height: 8,
468
+ borderRadius: "50%",
469
+ background: "#fff",
470
+ animation: "fp-blink 1s ease-in-out infinite",
471
+ flexShrink: 0
472
+ }
473
+ }
474
+ ),
475
+ error && !effectiveIcon && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { fontSize: 10, color: textColor }, children: "\u2717" }),
476
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
477
+ "span",
478
+ {
479
+ style: {
480
+ fontSize: 13,
481
+ fontWeight: 500,
482
+ color: textColor,
483
+ whiteSpace: "nowrap"
484
+ },
485
+ children: label
486
+ }
487
+ ),
488
+ isSubflow && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
489
+ "span",
490
+ {
491
+ style: {
492
+ display: "inline-flex",
493
+ alignItems: "center",
494
+ justifyContent: "center",
495
+ width: 16,
496
+ height: 16,
497
+ borderRadius: 3,
498
+ border: `1.5px solid ${textColor}`,
499
+ position: "relative",
500
+ opacity: 0.7,
501
+ flexShrink: 0
502
+ },
503
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
504
+ "span",
505
+ {
506
+ style: {
507
+ width: 8,
508
+ height: 8,
509
+ borderRadius: 2,
510
+ border: `1px solid ${textColor}`
511
+ }
512
+ }
513
+ )
514
+ }
515
+ )
516
+ ] }),
517
+ description && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
228
518
  "span",
229
519
  {
230
520
  style: {
231
- fontSize: 13,
232
- fontWeight: 500,
521
+ fontSize: 10,
522
+ fontWeight: 400,
233
523
  color: textColor,
234
- whiteSpace: "nowrap"
235
- },
236
- children: label
237
- }
238
- ),
239
- isSubflow && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
240
- "span",
241
- {
242
- style: {
243
- display: "inline-flex",
244
- alignItems: "center",
245
- justifyContent: "center",
246
- width: 16,
247
- height: 16,
248
- borderRadius: 3,
249
- border: `1.5px solid ${textColor}`,
250
- position: "relative",
251
524
  opacity: 0.7,
252
- flexShrink: 0
525
+ whiteSpace: "nowrap",
526
+ overflow: "hidden",
527
+ textOverflow: "ellipsis",
528
+ maxWidth: 160
253
529
  },
254
- children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
255
- "span",
256
- {
257
- style: {
258
- width: 8,
259
- height: 8,
260
- borderRadius: 2,
261
- border: `1px solid ${textColor}`
262
- }
263
- }
264
- )
530
+ children: description
265
531
  }
266
532
  )
267
- ] }),
268
- description && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
269
- "span",
270
- {
271
- style: {
272
- fontSize: 10,
273
- fontWeight: 400,
274
- color: textColor,
275
- opacity: 0.7,
276
- whiteSpace: "nowrap",
277
- overflow: "hidden",
278
- textOverflow: "ellipsis",
279
- maxWidth: 160
280
- },
281
- children: description
282
- }
283
- )
284
- ]
285
- }
533
+ ]
534
+ }
535
+ )
286
536
  )
287
537
  ]
288
538
  }
@@ -423,19 +673,19 @@ function FlowchartView({
423
673
  }
424
674
 
425
675
  // src/components/FlowchartView/TracedFlowchartView.tsx
426
- var import_react10 = require("react");
427
- var import_react11 = require("@xyflow/react");
676
+ var import_react7 = require("react");
677
+ var import_react8 = require("@xyflow/react");
428
678
 
429
679
  // src/components/FlowchartView/specToReactFlow.ts
430
680
  var DEFAULT_COLORS = {
431
- edgeDefault: defaultTokens.colors.textMuted,
432
- edgeExecuted: defaultTokens.colors.success,
433
- edgeActive: defaultTokens.colors.primary,
434
- edgeLoop: defaultTokens.colors.warning,
435
- labelDefault: defaultTokens.colors.textSecondary,
436
- labelExecuted: defaultTokens.colors.success,
437
- labelLoop: defaultTokens.colors.warning,
438
- pathGlow: `${defaultTokens.colors.success}4D`
681
+ edgeDefault: rawDefaults.colors.textMuted,
682
+ edgeExecuted: rawDefaults.colors.success,
683
+ edgeActive: rawDefaults.colors.primary,
684
+ edgeLoop: rawDefaults.colors.warning,
685
+ labelDefault: rawDefaults.colors.textSecondary,
686
+ labelExecuted: rawDefaults.colors.success,
687
+ labelLoop: rawDefaults.colors.warning,
688
+ pathGlow: `${rawDefaults.colors.success}4D`
439
689
  // ~30% opacity hex
440
690
  };
441
691
  var Y_STEP = 100;
@@ -443,122 +693,33 @@ var X_SPREAD = 200;
443
693
  function nid(n) {
444
694
  return n.name || n.id || `spec-${Math.random()}`;
445
695
  }
446
- function addEdge(state, source, target, label, isLoop) {
447
- state.edgeCounter++;
448
- const o = state.overlay;
449
- const c = state.colors;
450
- const executed = o && o.executedStages.has(source) && o.executedStages.has(target);
451
- const isLeadingEdge = o && source === o.activeStage && !o.doneStages.has(target);
452
- if (isLoop) {
453
- let loopExecuted = false;
454
- if (o?.executionOrder) {
455
- const lastSourceIdx = o.executionOrder.lastIndexOf(source);
456
- if (lastSourceIdx >= 0) {
457
- loopExecuted = o.executionOrder.slice(lastSourceIdx + 1).includes(target);
458
- }
459
- }
460
- state.edges.push({
461
- id: `se${state.edgeCounter}`,
462
- source,
463
- target,
464
- sourceHandle: "loop-source",
465
- targetHandle: "loop-target",
466
- label: label ?? "loop",
467
- type: "smoothstep",
468
- pathOptions: { offset: 40, borderRadius: 16 },
469
- style: {
470
- stroke: c.edgeLoop,
471
- strokeWidth: loopExecuted ? 3 : 2,
472
- strokeDasharray: "6 3",
473
- opacity: o && !loopExecuted ? 0.35 : 1
474
- },
475
- labelStyle: { fontSize: 10, fontWeight: 700, fill: c.labelLoop },
476
- animated: loopExecuted,
477
- zIndex: 2
478
- });
479
- return;
480
- }
481
- if (executed) {
482
- state.edges.push({
483
- id: `se${state.edgeCounter}-glow`,
484
- source,
485
- target,
486
- style: {
487
- stroke: c.pathGlow,
488
- strokeWidth: 8,
489
- opacity: 0.4
490
- },
491
- zIndex: 0,
492
- selectable: false,
493
- focusable: false
494
- });
495
- state.edges.push({
496
- id: `se${state.edgeCounter}`,
497
- source,
498
- target,
499
- label,
500
- style: {
501
- stroke: isLeadingEdge ? c.edgeActive : c.edgeExecuted,
502
- strokeWidth: 3.5
503
- },
504
- labelStyle: { fontSize: 10, fontWeight: 600, fill: c.labelExecuted },
505
- animated: !!isLeadingEdge,
506
- zIndex: 1
507
- });
508
- } else {
509
- state.edges.push({
510
- id: `se${state.edgeCounter}`,
511
- source,
512
- target,
513
- label,
514
- style: {
515
- stroke: c.edgeDefault,
516
- strokeWidth: 1.5,
517
- opacity: o ? 0.3 : 1
518
- },
519
- labelStyle: { fontSize: 10, fill: c.labelDefault }
520
- });
696
+ function registerNode(state, node) {
697
+ if (node.id && node.name) {
698
+ state.idToName.set(node.id, node.name);
521
699
  }
522
700
  }
523
- function walk(node, state, x, y) {
701
+ function walkLayout(node, state, x, y) {
702
+ if (!node) return { lastIds: [], bottomY: y };
703
+ registerNode(state, node);
524
704
  const id = nid(node);
525
705
  if (state.seen.has(id)) {
526
706
  return { lastIds: [id], bottomY: y };
527
707
  }
528
708
  state.seen.add(id);
529
- const isDecider = node.type === "decider" || node.hasDecider;
709
+ const isDecider = node.type === "decider" || !!node.hasDecider;
530
710
  const isFork = node.type === "fork";
531
- const o = state.overlay;
532
- const isDone = o ? o.doneStages.has(id) : false;
533
- const isActive = o ? o.activeStage === id : false;
534
- const wasExecuted = o ? o.executedStages.has(id) : false;
535
- const dimmed = o && !wasExecuted;
536
- let stepNumbers;
537
- if (o?.executionOrder) {
538
- const nums = [];
539
- for (let i = 0; i < o.executionOrder.length; i++) {
540
- if (o.executionOrder[i] === id) nums.push(i + 1);
541
- }
542
- if (nums.length > 0) stepNumbers = nums;
543
- }
544
711
  state.nodes.push({
545
712
  id,
546
- position: { x, y },
547
- data: {
548
- label: node.name,
549
- active: isActive,
550
- done: isDone,
551
- error: false,
552
- isDecider,
553
- isFork,
554
- description: node.description,
555
- subflowId: node.subflowId,
556
- dimmed,
557
- stepNumbers,
558
- isSubflow: !!node.isSubflowRoot
559
- },
560
- type: "stage",
561
- style: dimmed ? { opacity: 0.35 } : void 0
713
+ x,
714
+ y,
715
+ label: node.name,
716
+ isDecider,
717
+ isFork,
718
+ description: node.description,
719
+ icon: node.icon,
720
+ subflowId: node.subflowId,
721
+ isSubflow: !!node.isSubflowRoot,
722
+ isLazy: node.isLazy
562
723
  });
563
724
  let lastIds = [id];
564
725
  let bottomY = y;
@@ -569,120 +730,257 @@ function walk(node, state, x, y) {
569
730
  const childResults = [];
570
731
  for (let i = 0; i < node.children.length; i++) {
571
732
  const child = node.children[i];
733
+ if (!child) continue;
572
734
  const childX = startX + i * X_SPREAD;
573
735
  const edgeLabel = node.branchIds?.[i];
574
- addEdge(state, id, nid(child), edgeLabel);
575
- const result = walk(child, state, childX, childY);
736
+ state.edgeCounter++;
737
+ state.edges.push({ id: `se${state.edgeCounter}`, source: id, target: nid(child), label: edgeLabel, isLoop: false });
738
+ const result = walkLayout(child, state, childX, childY);
576
739
  childResults.push(result);
577
740
  }
578
741
  lastIds = childResults.flatMap((r) => r.lastIds);
579
742
  bottomY = Math.max(...childResults.map((r) => r.bottomY));
580
743
  }
581
744
  if (node.loopTarget) {
582
- addEdge(state, id, node.loopTarget, "loop", true);
745
+ const resolvedTarget = state.idToName.get(node.loopTarget) ?? node.loopTarget;
746
+ state.edgeCounter++;
747
+ state.edges.push({ id: `se${state.edgeCounter}`, source: id, target: resolvedTarget, label: "loop", isLoop: true });
583
748
  }
584
749
  if (node.next) {
750
+ const rawNextId = nid(node.next);
751
+ const resolvedNextId = state.idToName.get(rawNextId) ?? rawNextId;
752
+ const isLoopRef = node.loopTarget && state.seen.has(resolvedNextId);
753
+ if (isLoopRef) {
754
+ return { lastIds, bottomY };
755
+ }
585
756
  const nextY = bottomY + Y_STEP;
586
- const nextId = nid(node.next);
587
757
  for (const lid of lastIds) {
588
- if (node.loopTarget && lid === id && node.loopTarget === nextId) continue;
589
- addEdge(state, lid, nextId);
758
+ state.edgeCounter++;
759
+ state.edges.push({ id: `se${state.edgeCounter}`, source: lid, target: resolvedNextId, isLoop: false });
590
760
  }
591
- const result = walk(node.next, state, x, nextY);
592
- return result;
761
+ return walkLayout(node.next, state, x, nextY);
593
762
  }
594
763
  return { lastIds, bottomY };
595
764
  }
596
- function specToReactFlow(spec, overlay, colors) {
765
+ function specToLayout(spec) {
597
766
  const state = {
598
767
  nodes: [],
599
768
  edges: [],
600
769
  edgeCounter: 0,
601
770
  seen: /* @__PURE__ */ new Set(),
602
- overlay: overlay ?? null,
603
- colors: { ...DEFAULT_COLORS, ...colors }
771
+ idToName: /* @__PURE__ */ new Map()
604
772
  };
605
- walk(spec, state, 300, 0);
606
- return { nodes: state.nodes, edges: state.edges };
773
+ walkLayout(spec, state, 300, 0);
774
+ return { nodes: state.nodes, edges: state.edges, idToName: state.idToName };
607
775
  }
608
-
609
- // src/components/FlowchartView/useSubflowNavigation.ts
610
- var import_react7 = require("react");
611
- function useSubflowNavigation(rootSpec, overlay, colors) {
612
- const [stack, setStack] = (0, import_react7.useState)([]);
613
- const currentSpec = stack.length > 0 ? stack[stack.length - 1].spec : rootSpec;
614
- const { nodes, edges } = (0, import_react7.useMemo)(() => {
615
- if (!currentSpec) return { nodes: [], edges: [] };
616
- return specToReactFlow(currentSpec, overlay, colors);
617
- }, [currentSpec, overlay, colors]);
618
- const subflowMap = (0, import_react7.useMemo)(() => {
619
- const map = /* @__PURE__ */ new Map();
620
- if (!currentSpec) return map;
621
- function collectSubflows(node) {
622
- if (node.isSubflowRoot && node.subflowStructure) {
623
- const id = node.name || node.id || "";
624
- map.set(id, node);
776
+ function applyOverlay(layout, overlay, colors) {
777
+ const c = { ...DEFAULT_COLORS, ...colors };
778
+ const o = overlay ?? null;
779
+ const nodes = layout.nodes.map((ln) => {
780
+ const isDone = o ? o.doneStages.has(ln.id) : false;
781
+ const isActive = o ? o.activeStage === ln.id : false;
782
+ const wasExecuted = o ? o.executedStages.has(ln.id) : false;
783
+ const dimmed = o && !wasExecuted;
784
+ let stepNumbers;
785
+ if (o?.executionOrder) {
786
+ const nums = [];
787
+ for (let i = 0; i < o.executionOrder.length; i++) {
788
+ if (o.executionOrder[i] === ln.id) nums.push(i + 1);
625
789
  }
626
- if (node.children) node.children.forEach(collectSubflows);
627
- if (node.next) collectSubflows(node.next);
790
+ if (nums.length > 0) stepNumbers = nums;
628
791
  }
629
- collectSubflows(currentSpec);
630
- return map;
631
- }, [currentSpec]);
632
- const breadcrumbs = (0, import_react7.useMemo)(() => {
633
- const root = {
634
- label: rootSpec?.name || "Flowchart",
635
- spec: rootSpec,
636
- description: rootSpec?.description
792
+ return {
793
+ id: ln.id,
794
+ position: { x: ln.x, y: ln.y },
795
+ data: {
796
+ label: ln.label,
797
+ active: isActive,
798
+ done: isDone,
799
+ error: false,
800
+ isDecider: ln.isDecider,
801
+ isFork: ln.isFork,
802
+ description: ln.description,
803
+ icon: ln.icon,
804
+ subflowId: ln.subflowId,
805
+ dimmed,
806
+ stepNumbers,
807
+ isSubflow: ln.isSubflow,
808
+ isLazy: ln.isLazy
809
+ },
810
+ type: "stage",
811
+ style: dimmed ? { opacity: 0.35 } : void 0
637
812
  };
638
- return [root, ...stack];
639
- }, [rootSpec, stack]);
640
- const handleNodeClick = (0, import_react7.useCallback)(
641
- (nodeId) => {
642
- const subflowNode = subflowMap.get(nodeId);
643
- if (!subflowNode?.subflowStructure) return false;
644
- setStack((prev) => [
645
- ...prev,
646
- {
647
- label: subflowNode.subflowName || subflowNode.name,
648
- spec: subflowNode.subflowStructure,
649
- description: subflowNode.description
813
+ });
814
+ const edges = [];
815
+ for (const le of layout.edges) {
816
+ const executed = o && o.executedStages.has(le.source) && o.executedStages.has(le.target);
817
+ const isLeadingEdge = o && le.source === o.activeStage && !o.doneStages.has(le.target);
818
+ if (le.isLoop) {
819
+ let loopExecuted = false;
820
+ if (o?.executionOrder) {
821
+ const lastSourceIdx = o.executionOrder.lastIndexOf(le.source);
822
+ if (lastSourceIdx >= 0) {
823
+ loopExecuted = o.executionOrder.slice(lastSourceIdx + 1).includes(le.target);
650
824
  }
651
- ]);
652
- return true;
653
- },
654
- [subflowMap]
655
- );
656
- const navigateTo = (0, import_react7.useCallback)(
657
- (level) => {
658
- if (level === 0) {
659
- setStack([]);
660
- } else {
661
- setStack((prev) => prev.slice(0, level));
662
825
  }
826
+ edges.push({
827
+ id: le.id,
828
+ source: le.source,
829
+ target: le.target,
830
+ sourceHandle: "loop-source",
831
+ targetHandle: "loop-target",
832
+ label: le.label ?? "loop",
833
+ type: "smoothstep",
834
+ pathOptions: { offset: 40, borderRadius: 16 },
835
+ style: {
836
+ stroke: c.edgeLoop,
837
+ strokeWidth: loopExecuted ? 3 : 2,
838
+ strokeDasharray: "6 3",
839
+ opacity: o && !loopExecuted ? 0.35 : 1
840
+ },
841
+ labelStyle: { fontSize: 10, fontWeight: 700, fill: c.labelLoop },
842
+ animated: loopExecuted,
843
+ zIndex: 2
844
+ });
845
+ } else if (executed) {
846
+ edges.push({
847
+ id: `${le.id}-glow`,
848
+ source: le.source,
849
+ target: le.target,
850
+ style: { stroke: c.pathGlow, strokeWidth: 8, opacity: 0.4 },
851
+ zIndex: 0,
852
+ selectable: false,
853
+ focusable: false
854
+ });
855
+ edges.push({
856
+ id: le.id,
857
+ source: le.source,
858
+ target: le.target,
859
+ label: le.label,
860
+ style: {
861
+ stroke: isLeadingEdge ? c.edgeActive : c.edgeExecuted,
862
+ strokeWidth: 3.5
863
+ },
864
+ labelStyle: { fontSize: 10, fontWeight: 600, fill: c.labelExecuted },
865
+ animated: !!isLeadingEdge,
866
+ zIndex: 1
867
+ });
868
+ } else {
869
+ edges.push({
870
+ id: le.id,
871
+ source: le.source,
872
+ target: le.target,
873
+ label: le.label,
874
+ style: {
875
+ stroke: c.edgeDefault,
876
+ strokeWidth: 1.5,
877
+ opacity: o ? 0.3 : 1
878
+ },
879
+ labelStyle: { fontSize: 10, fill: c.labelDefault }
880
+ });
881
+ }
882
+ }
883
+ return { nodes, edges };
884
+ }
885
+ function specToReactFlow(spec, overlay, colors) {
886
+ const layout = specToLayout(spec);
887
+ return applyOverlay(layout, overlay, colors);
888
+ }
889
+
890
+ // src/components/FlowchartView/TracedFlowchartView.tsx
891
+ var import_jsx_runtime4 = require("react/jsx-runtime");
892
+ var defaultNodeTypes = { stage: StageNode };
893
+ function FitViewOnResize() {
894
+ const { fitView } = (0, import_react8.useReactFlow)();
895
+ (0, import_react7.useEffect)(() => {
896
+ const handler = () => {
897
+ requestAnimationFrame(() => fitView({ padding: 0.3 }));
898
+ };
899
+ window.addEventListener("resize", handler);
900
+ return () => window.removeEventListener("resize", handler);
901
+ }, [fitView]);
902
+ return null;
903
+ }
904
+ function TracedFlowchartView({
905
+ spec,
906
+ snapshots,
907
+ snapshotIndex = 0,
908
+ onNodeClick,
909
+ nodeTypes: customNodeTypes,
910
+ unstyled = false,
911
+ className,
912
+ style
913
+ }) {
914
+ const nodeTypes2 = customNodeTypes ?? defaultNodeTypes;
915
+ const overlay = (0, import_react7.useMemo)(() => {
916
+ if (!snapshots || snapshots.length === 0) return void 0;
917
+ const executionOrder = snapshots.slice(0, snapshotIndex + 1).map((s) => s.stageLabel);
918
+ const doneStages = new Set(
919
+ snapshots.slice(0, snapshotIndex).map((s) => s.stageLabel)
920
+ );
921
+ const activeStage = snapshots[snapshotIndex]?.stageLabel ?? null;
922
+ const executedStages = /* @__PURE__ */ new Set([...doneStages]);
923
+ if (activeStage) executedStages.add(activeStage);
924
+ return { doneStages, activeStage, executedStages, executionOrder };
925
+ }, [snapshots, snapshotIndex]);
926
+ const layout = (0, import_react7.useMemo)(() => {
927
+ if (!spec) return null;
928
+ return specToLayout(spec);
929
+ }, [spec]);
930
+ const { nodes, edges } = (0, import_react7.useMemo)(() => {
931
+ if (!layout) return { nodes: [], edges: [] };
932
+ return applyOverlay(layout, overlay);
933
+ }, [layout, overlay]);
934
+ const handleNodeClick = (0, import_react7.useCallback)(
935
+ (_, node) => {
936
+ if (!onNodeClick) return;
937
+ onNodeClick(node.id);
663
938
  },
664
- []
939
+ [onNodeClick]
940
+ );
941
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
942
+ "div",
943
+ {
944
+ className,
945
+ style: { width: "100%", height: "100%", ...style },
946
+ "data-fp": "traced-flowchart",
947
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
948
+ import_react8.ReactFlow,
949
+ {
950
+ nodes,
951
+ edges,
952
+ onNodeClick: handleNodeClick,
953
+ nodeTypes: nodeTypes2,
954
+ fitView: true,
955
+ fitViewOptions: { padding: 0.3 },
956
+ proOptions: { hideAttribution: true },
957
+ panOnDrag: false,
958
+ zoomOnScroll: false,
959
+ zoomOnPinch: false,
960
+ zoomOnDoubleClick: false,
961
+ preventScrolling: false,
962
+ nodesDraggable: false,
963
+ nodesConnectable: false,
964
+ elementsSelectable: !!onNodeClick,
965
+ children: [
966
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(FitViewOnResize, {}),
967
+ !unstyled && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react8.Background, { variant: import_react8.BackgroundVariant.Dots, gap: 16, size: 1 })
968
+ ]
969
+ }
970
+ )
971
+ }
665
972
  );
666
- return {
667
- breadcrumbs,
668
- nodes,
669
- edges,
670
- handleNodeClick,
671
- navigateTo,
672
- isInSubflow: stack.length > 0,
673
- currentSubflowNodeName: stack.length > 0 ? stack[stack.length - 1].label : null
674
- };
675
973
  }
676
974
 
677
975
  // src/components/FlowchartView/SubflowBreadcrumb.tsx
678
- var import_react8 = require("react");
679
- var import_jsx_runtime4 = require("react/jsx-runtime");
680
- var SubflowBreadcrumb = (0, import_react8.memo)(function SubflowBreadcrumb2({
976
+ var import_react9 = require("react");
977
+ var import_jsx_runtime5 = require("react/jsx-runtime");
978
+ var SubflowBreadcrumb = (0, import_react9.memo)(function SubflowBreadcrumb2({
681
979
  breadcrumbs,
682
980
  onNavigate
683
981
  }) {
684
982
  if (breadcrumbs.length <= 1) return null;
685
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
983
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
686
984
  "div",
687
985
  {
688
986
  style: {
@@ -699,10 +997,10 @@ var SubflowBreadcrumb = (0, import_react8.memo)(function SubflowBreadcrumb2({
699
997
  },
700
998
  children: breadcrumbs.map((crumb, i) => {
701
999
  const isLast = i === breadcrumbs.length - 1;
702
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { style: { display: "flex", alignItems: "center", gap: 4 }, children: [
703
- i > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { color: theme.textMuted, fontSize: 10 }, children: "\u203A" }),
704
- isLast ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { style: { display: "flex", alignItems: "center", gap: 6 }, children: [
705
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1000
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { style: { display: "flex", alignItems: "center", gap: 4 }, children: [
1001
+ i > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { style: { color: theme.textMuted, fontSize: 10 }, children: "\u203A" }),
1002
+ isLast ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { style: { display: "flex", alignItems: "center", gap: 6 }, children: [
1003
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
706
1004
  "span",
707
1005
  {
708
1006
  style: {
@@ -712,7 +1010,7 @@ var SubflowBreadcrumb = (0, import_react8.memo)(function SubflowBreadcrumb2({
712
1010
  children: crumb.label
713
1011
  }
714
1012
  ),
715
- crumb.description && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1013
+ crumb.description && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
716
1014
  "span",
717
1015
  {
718
1016
  style: {
@@ -726,7 +1024,7 @@ var SubflowBreadcrumb = (0, import_react8.memo)(function SubflowBreadcrumb2({
726
1024
  ]
727
1025
  }
728
1026
  )
729
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1027
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
730
1028
  "button",
731
1029
  {
732
1030
  onClick: () => onNavigate(i),
@@ -757,13 +1055,83 @@ var SubflowBreadcrumb = (0, import_react8.memo)(function SubflowBreadcrumb2({
757
1055
  );
758
1056
  });
759
1057
 
1058
+ // src/components/FlowchartView/useSubflowNavigation.ts
1059
+ var import_react10 = require("react");
1060
+ function useSubflowNavigation(rootSpec, overlay, colors) {
1061
+ const [stack, setStack] = (0, import_react10.useState)([]);
1062
+ const currentSpec = stack.length > 0 ? stack[stack.length - 1].spec : rootSpec;
1063
+ const { nodes, edges } = (0, import_react10.useMemo)(() => {
1064
+ if (!currentSpec) return { nodes: [], edges: [] };
1065
+ return specToReactFlow(currentSpec, overlay, colors);
1066
+ }, [currentSpec, overlay, colors]);
1067
+ const subflowMap = (0, import_react10.useMemo)(() => {
1068
+ const map = /* @__PURE__ */ new Map();
1069
+ if (!currentSpec) return map;
1070
+ function collectSubflows(node) {
1071
+ if (node.isSubflowRoot && node.subflowStructure) {
1072
+ const id = node.name || node.id || "";
1073
+ map.set(id, node);
1074
+ }
1075
+ if (node.children) node.children.forEach(collectSubflows);
1076
+ if (node.next) collectSubflows(node.next);
1077
+ }
1078
+ collectSubflows(currentSpec);
1079
+ return map;
1080
+ }, [currentSpec]);
1081
+ const breadcrumbs = (0, import_react10.useMemo)(() => {
1082
+ const root = {
1083
+ label: rootSpec?.name || "Flowchart",
1084
+ spec: rootSpec,
1085
+ description: rootSpec?.description
1086
+ };
1087
+ return [root, ...stack];
1088
+ }, [rootSpec, stack]);
1089
+ const handleNodeClick = (0, import_react10.useCallback)(
1090
+ (nodeId) => {
1091
+ const subflowNode = subflowMap.get(nodeId);
1092
+ if (!subflowNode?.subflowStructure) return false;
1093
+ setStack((prev) => [
1094
+ ...prev,
1095
+ {
1096
+ label: subflowNode.subflowName || subflowNode.name,
1097
+ spec: subflowNode.subflowStructure,
1098
+ description: subflowNode.description
1099
+ }
1100
+ ]);
1101
+ return true;
1102
+ },
1103
+ [subflowMap]
1104
+ );
1105
+ const navigateTo = (0, import_react10.useCallback)(
1106
+ (level) => {
1107
+ if (level === 0) {
1108
+ setStack([]);
1109
+ } else {
1110
+ setStack((prev) => prev.slice(0, level));
1111
+ }
1112
+ },
1113
+ []
1114
+ );
1115
+ return {
1116
+ breadcrumbs,
1117
+ nodes,
1118
+ edges,
1119
+ handleNodeClick,
1120
+ navigateTo,
1121
+ isInSubflow: stack.length > 0,
1122
+ currentSubflowNodeName: stack.length > 0 ? stack[stack.length - 1].label : null
1123
+ };
1124
+ }
1125
+
760
1126
  // src/components/FlowchartView/SubflowTree.tsx
761
- var import_react9 = require("react");
762
- var import_jsx_runtime5 = require("react/jsx-runtime");
1127
+ var import_react11 = require("react");
1128
+ var import_jsx_runtime6 = require("react/jsx-runtime");
763
1129
  function specToTree(node) {
1130
+ if (!node) return [];
764
1131
  const entries = [];
765
1132
  const seen = /* @__PURE__ */ new Set();
766
- function walk2(n) {
1133
+ function walk(n) {
1134
+ if (!n) return;
767
1135
  const id = n.name || n.id || "";
768
1136
  if (seen.has(id)) return;
769
1137
  seen.add(id);
@@ -779,35 +1147,35 @@ function specToTree(node) {
779
1147
  entries.push(entry);
780
1148
  if (n.children) {
781
1149
  for (const child of n.children) {
782
- walk2(child);
1150
+ if (child) walk(child);
783
1151
  }
784
1152
  }
785
1153
  if (n.next) {
786
- walk2(n.next);
1154
+ walk(n.next);
787
1155
  }
788
1156
  }
789
- walk2(node);
1157
+ walk(node);
790
1158
  return entries;
791
1159
  }
792
- var TreeNode = (0, import_react9.memo)(function TreeNode2({
1160
+ var TreeNode = (0, import_react11.memo)(function TreeNode2({
793
1161
  entry,
794
1162
  depth,
795
1163
  activeStage,
796
1164
  doneStages,
797
1165
  onNodeSelect
798
1166
  }) {
799
- const [expanded, setExpanded] = (0, import_react9.useState)(true);
1167
+ const [expanded, setExpanded] = (0, import_react11.useState)(true);
800
1168
  const hasChildren = entry.children && entry.children.length > 0;
801
1169
  const isActive = activeStage === entry.name;
802
1170
  const isDone = doneStages?.has(entry.name);
803
- const handleClick = (0, import_react9.useCallback)(() => {
1171
+ const handleClick = (0, import_react11.useCallback)(() => {
804
1172
  if (hasChildren) {
805
1173
  setExpanded((prev) => !prev);
806
1174
  }
807
1175
  onNodeSelect?.(entry.name, !!entry.isSubflow);
808
1176
  }, [hasChildren, onNodeSelect, entry.name, entry.isSubflow]);
809
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
810
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1177
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
1178
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
811
1179
  "button",
812
1180
  {
813
1181
  onClick: handleClick,
@@ -838,7 +1206,7 @@ var TreeNode = (0, import_react9.memo)(function TreeNode2({
838
1206
  }
839
1207
  },
840
1208
  children: [
841
- hasChildren ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1209
+ hasChildren ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
842
1210
  "span",
843
1211
  {
844
1212
  style: {
@@ -853,8 +1221,8 @@ var TreeNode = (0, import_react9.memo)(function TreeNode2({
853
1221
  },
854
1222
  children: "\u25B6"
855
1223
  }
856
- ) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { style: { width: 12, flexShrink: 0 } }),
857
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1224
+ ) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { style: { width: 12, flexShrink: 0 } }),
1225
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
858
1226
  "span",
859
1227
  {
860
1228
  style: {
@@ -866,8 +1234,8 @@ var TreeNode = (0, import_react9.memo)(function TreeNode2({
866
1234
  }
867
1235
  }
868
1236
  ),
869
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { style: { display: "flex", flexDirection: "column", minWidth: 0 }, children: [
870
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1237
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { style: { display: "flex", flexDirection: "column", minWidth: 0 }, children: [
1238
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
871
1239
  "span",
872
1240
  {
873
1241
  style: {
@@ -879,11 +1247,11 @@ var TreeNode = (0, import_react9.memo)(function TreeNode2({
879
1247
  },
880
1248
  children: [
881
1249
  entry.name,
882
- entry.isSubflow && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { style: { opacity: 0.5, marginLeft: 4, fontSize: 10 }, children: "\u229E" })
1250
+ entry.isSubflow && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { style: { opacity: 0.5, marginLeft: 4, fontSize: 10 }, children: "\u229E" })
883
1251
  ]
884
1252
  }
885
1253
  ),
886
- entry.description && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1254
+ entry.description && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
887
1255
  "span",
888
1256
  {
889
1257
  style: {
@@ -900,7 +1268,7 @@ var TreeNode = (0, import_react9.memo)(function TreeNode2({
900
1268
  ]
901
1269
  }
902
1270
  ),
903
- hasChildren && expanded && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { children: entry.children.map((child, i) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1271
+ hasChildren && expanded && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { children: entry.children.map((child, i) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
904
1272
  TreeNode2,
905
1273
  {
906
1274
  entry: child,
@@ -909,12 +1277,12 @@ var TreeNode = (0, import_react9.memo)(function TreeNode2({
909
1277
  doneStages,
910
1278
  onNodeSelect
911
1279
  },
912
- `${child.name}-${i}`
1280
+ child.subflowId ?? `${child.name}-${i}`
913
1281
  )) })
914
1282
  ] });
915
1283
  });
916
- var SectionLabel = (0, import_react9.memo)(function SectionLabel2({ children }) {
917
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1284
+ var SectionLabel = (0, import_react11.memo)(function SectionLabel2({ children }) {
1285
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
918
1286
  "div",
919
1287
  {
920
1288
  style: {
@@ -929,7 +1297,7 @@ var SectionLabel = (0, import_react9.memo)(function SectionLabel2({ children })
929
1297
  }
930
1298
  );
931
1299
  });
932
- var SubflowTree = (0, import_react9.memo)(function SubflowTree2({
1300
+ var SubflowTree = (0, import_react11.memo)(function SubflowTree2({
933
1301
  spec,
934
1302
  activeStage,
935
1303
  doneStages,
@@ -938,10 +1306,10 @@ var SubflowTree = (0, import_react9.memo)(function SubflowTree2({
938
1306
  className,
939
1307
  style
940
1308
  }) {
941
- const tree = (0, import_react9.useMemo)(() => specToTree(spec), [spec]);
942
- const subflowStages = (0, import_react9.useMemo)(() => tree.filter((e) => e.isSubflow), [tree]);
1309
+ const tree = (0, import_react11.useMemo)(() => specToTree(spec), [spec]);
1310
+ const subflowStages = (0, import_react11.useMemo)(() => tree.filter((e) => e.isSubflow), [tree]);
943
1311
  if (subflowStages.length === 0) return null;
944
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1312
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
945
1313
  "div",
946
1314
  {
947
1315
  className,
@@ -959,8 +1327,8 @@ var SubflowTree = (0, import_react9.memo)(function SubflowTree2({
959
1327
  ...style
960
1328
  },
961
1329
  children: [
962
- !unstyled && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(SectionLabel, { children: "Subflows" }),
963
- subflowStages.map((entry, i) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1330
+ !unstyled && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(SectionLabel, { children: "Subflows" }),
1331
+ subflowStages.map((entry, i) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
964
1332
  TreeNode,
965
1333
  {
966
1334
  entry,
@@ -969,175 +1337,13 @@ var SubflowTree = (0, import_react9.memo)(function SubflowTree2({
969
1337
  doneStages,
970
1338
  onNodeSelect
971
1339
  },
972
- `${entry.name}-${i}`
1340
+ entry.subflowId ?? `${entry.name}-${i}`
973
1341
  ))
974
1342
  ]
975
1343
  }
976
1344
  );
977
1345
  });
978
1346
 
979
- // src/components/FlowchartView/TracedFlowchartView.tsx
980
- var import_jsx_runtime6 = require("react/jsx-runtime");
981
- var nodeTypes2 = { stage: StageNode };
982
- function TracedFlowchartView({
983
- spec,
984
- snapshots,
985
- snapshotIndex = 0,
986
- onNodeClick,
987
- onSubflowChange,
988
- showTree = true,
989
- treeWidth = 200,
990
- unstyled = false,
991
- className,
992
- style
993
- }) {
994
- const [treeVisible, setTreeVisible] = (0, import_react10.useState)(showTree);
995
- const subflowNav = useSubflowNavigation(spec);
996
- const currentSpec = subflowNav.breadcrumbs.length > 0 ? subflowNav.breadcrumbs[subflowNav.breadcrumbs.length - 1].spec : null;
997
- const overlay = (0, import_react10.useMemo)(() => {
998
- if (!snapshots || snapshots.length === 0) return void 0;
999
- const executionOrder = snapshots.slice(0, snapshotIndex + 1).map((s) => s.stageLabel);
1000
- const doneStages = new Set(
1001
- snapshots.slice(0, snapshotIndex).map((s) => s.stageLabel)
1002
- );
1003
- const activeStage = snapshots[snapshotIndex]?.stageLabel ?? null;
1004
- const executedStages = /* @__PURE__ */ new Set([...doneStages]);
1005
- if (activeStage) executedStages.add(activeStage);
1006
- return { doneStages, activeStage, executedStages, executionOrder };
1007
- }, [snapshots, snapshotIndex]);
1008
- const { nodes, edges } = (0, import_react10.useMemo)(() => {
1009
- if (!currentSpec) return { nodes: [], edges: [] };
1010
- return specToReactFlow(currentSpec, overlay);
1011
- }, [currentSpec, overlay]);
1012
- const handleNodeClick = (0, import_react10.useCallback)(
1013
- (_, node) => {
1014
- if (subflowNav.handleNodeClick(node.id)) {
1015
- onSubflowChange?.(true, node.id);
1016
- return;
1017
- }
1018
- if (onNodeClick && snapshots) {
1019
- const idx = snapshots.findIndex((s) => s.stageLabel === node.id);
1020
- if (idx >= 0) onNodeClick(idx);
1021
- } else if (onNodeClick) {
1022
- onNodeClick(node.id);
1023
- }
1024
- },
1025
- [subflowNav, onNodeClick, onSubflowChange, snapshots]
1026
- );
1027
- const handleBreadcrumbNavigate = (0, import_react10.useCallback)(
1028
- (level) => {
1029
- subflowNav.navigateTo(level);
1030
- onSubflowChange?.(level > 0, null);
1031
- },
1032
- [subflowNav, onSubflowChange]
1033
- );
1034
- const handleTreeNodeSelect = (0, import_react10.useCallback)(
1035
- (name, isSubflow) => {
1036
- if (isSubflow) {
1037
- if (subflowNav.handleNodeClick(name)) {
1038
- onSubflowChange?.(true, name);
1039
- }
1040
- } else if (onNodeClick && snapshots) {
1041
- const idx = snapshots.findIndex((s) => s.stageLabel === name);
1042
- if (idx >= 0) onNodeClick(idx);
1043
- }
1044
- },
1045
- [subflowNav, onNodeClick, onSubflowChange, snapshots]
1046
- );
1047
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1048
- "div",
1049
- {
1050
- className,
1051
- style: { width: "100%", height: "100%", display: "flex", flexDirection: "row", ...style },
1052
- "data-fp": "traced-flowchart",
1053
- children: [
1054
- showTree && treeVisible && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1055
- SubflowTree,
1056
- {
1057
- spec,
1058
- activeStage: overlay?.activeStage,
1059
- doneStages: overlay?.doneStages,
1060
- onNodeSelect: handleTreeNodeSelect,
1061
- unstyled,
1062
- style: { width: treeWidth, flexShrink: 0, height: "100%" }
1063
- }
1064
- ),
1065
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { flex: 1, display: "flex", flexDirection: "column", minWidth: 0, height: "100%" }, children: [
1066
- (subflowNav.isInSubflow || showTree && !treeVisible) && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { display: "flex", alignItems: "stretch", flexShrink: 0 }, children: [
1067
- showTree && !treeVisible && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1068
- "button",
1069
- {
1070
- onClick: () => setTreeVisible(true),
1071
- "data-fp": "tree-toggle",
1072
- style: unstyled ? {} : {
1073
- background: "transparent",
1074
- border: "none",
1075
- cursor: "pointer",
1076
- padding: "6px 8px",
1077
- fontSize: 10,
1078
- flexShrink: 0
1079
- },
1080
- children: "\u25B6"
1081
- }
1082
- ),
1083
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { flex: 1 }, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1084
- SubflowBreadcrumb,
1085
- {
1086
- breadcrumbs: subflowNav.breadcrumbs,
1087
- onNavigate: handleBreadcrumbNavigate
1088
- }
1089
- ) })
1090
- ] }),
1091
- showTree && treeVisible && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { display: "flex", alignItems: "stretch", flexShrink: 0 }, children: [
1092
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1093
- "button",
1094
- {
1095
- onClick: () => setTreeVisible(false),
1096
- "data-fp": "tree-toggle",
1097
- style: unstyled ? {} : {
1098
- background: "transparent",
1099
- border: "none",
1100
- cursor: "pointer",
1101
- padding: "6px 8px",
1102
- fontSize: 10,
1103
- flexShrink: 0
1104
- },
1105
- children: "\u25C0"
1106
- }
1107
- ),
1108
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { flex: 1 }, children: subflowNav.isInSubflow && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1109
- SubflowBreadcrumb,
1110
- {
1111
- breadcrumbs: subflowNav.breadcrumbs,
1112
- onNavigate: handleBreadcrumbNavigate
1113
- }
1114
- ) })
1115
- ] }),
1116
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { flex: 1, minHeight: 0 }, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1117
- import_react11.ReactFlow,
1118
- {
1119
- nodes,
1120
- edges,
1121
- onNodeClick: handleNodeClick,
1122
- nodeTypes: nodeTypes2,
1123
- fitView: true,
1124
- panOnDrag: false,
1125
- zoomOnScroll: false,
1126
- zoomOnPinch: false,
1127
- zoomOnDoubleClick: false,
1128
- preventScrolling: false,
1129
- nodesDraggable: false,
1130
- nodesConnectable: false,
1131
- elementsSelectable: !!onNodeClick,
1132
- children: !unstyled && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_react11.Background, { variant: import_react11.BackgroundVariant.Dots, gap: 16, size: 1 })
1133
- }
1134
- ) })
1135
- ] })
1136
- ]
1137
- }
1138
- );
1139
- }
1140
-
1141
1347
  // src/components/TimeTravelDebugger/TimeTravelDebugger.tsx
1142
1348
  var import_react15 = require("react");
1143
1349
 
@@ -1155,6 +1361,7 @@ function MemoryInspector({
1155
1361
  className,
1156
1362
  style
1157
1363
  }) {
1364
+ const cacheRef = (0, import_react12.useRef)(null);
1158
1365
  const { memory, newKeys } = (0, import_react12.useMemo)(() => {
1159
1366
  if (data) {
1160
1367
  return { memory: data, newKeys: /* @__PURE__ */ new Set() };
@@ -1162,21 +1369,37 @@ function MemoryInspector({
1162
1369
  if (!snapshots || snapshots.length === 0) {
1163
1370
  return { memory: {}, newKeys: /* @__PURE__ */ new Set() };
1164
1371
  }
1165
- const merged = {};
1166
- for (let i = 0; i <= Math.min(selectedIndex, snapshots.length - 1); i++) {
1167
- Object.assign(merged, snapshots[i]?.memory);
1372
+ const safeIdx = Math.min(selectedIndex, snapshots.length - 1);
1373
+ let merged;
1374
+ const cache = cacheRef.current;
1375
+ if (cache && cache.snapshots === snapshots && cache.index <= safeIdx) {
1376
+ merged = { ...cache.accumulated };
1377
+ for (let i = cache.index + 1; i <= safeIdx; i++) {
1378
+ Object.assign(merged, snapshots[i]?.memory);
1379
+ }
1380
+ } else {
1381
+ merged = {};
1382
+ for (let i = 0; i <= safeIdx; i++) {
1383
+ Object.assign(merged, snapshots[i]?.memory);
1384
+ }
1168
1385
  }
1386
+ cacheRef.current = { snapshots, index: safeIdx, accumulated: merged };
1169
1387
  const nk = /* @__PURE__ */ new Set();
1170
- if (highlightNew && selectedIndex > 0) {
1171
- const prev = {};
1172
- for (let i = 0; i < selectedIndex; i++) {
1173
- Object.assign(prev, snapshots[i]?.memory);
1388
+ if (highlightNew && safeIdx > 0) {
1389
+ let prev;
1390
+ if (cache && cache.snapshots === snapshots && cache.index === safeIdx - 1) {
1391
+ prev = cache.accumulated;
1392
+ } else {
1393
+ prev = {};
1394
+ for (let i = 0; i < safeIdx; i++) {
1395
+ Object.assign(prev, snapshots[i]?.memory);
1396
+ }
1174
1397
  }
1175
- const current = snapshots[selectedIndex]?.memory ?? {};
1398
+ const current = snapshots[safeIdx]?.memory ?? {};
1176
1399
  for (const k of Object.keys(current)) {
1177
1400
  if (!(k in prev)) nk.add(k);
1178
1401
  }
1179
- } else if (highlightNew && selectedIndex === 0 && snapshots[0]) {
1402
+ } else if (highlightNew && safeIdx === 0 && snapshots[0]) {
1180
1403
  for (const k of Object.keys(snapshots[0].memory)) nk.add(k);
1181
1404
  }
1182
1405
  return { memory: merged, newKeys: nk };
@@ -1185,9 +1408,9 @@ function MemoryInspector({
1185
1408
  const fs = fontSize[size];
1186
1409
  const pad = padding[size];
1187
1410
  if (unstyled) {
1188
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className, style, "data-fp": "memory-inspector", children: [
1411
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className, style, "data-fp": "memory-inspector", role: "region", "aria-label": "Memory state", children: [
1189
1412
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { "data-fp": "memory-label", children: "Memory State" }),
1190
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("pre", { "data-fp": "memory-json", children: JSON.stringify(memory, null, 2) })
1413
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("pre", { "data-fp": "memory-json", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("code", { children: JSON.stringify(memory, null, 2) }) })
1191
1414
  ] });
1192
1415
  }
1193
1416
  return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
@@ -1200,6 +1423,8 @@ function MemoryInspector({
1200
1423
  ...style
1201
1424
  },
1202
1425
  "data-fp": "memory-inspector",
1426
+ role: "region",
1427
+ "aria-label": "Memory state",
1203
1428
  children: [
1204
1429
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1205
1430
  "span",
@@ -1467,12 +1692,15 @@ function GanttTimeline({
1467
1692
  }
1468
1693
  }, [selectedIndex, showAll]);
1469
1694
  if (unstyled) {
1470
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className, style, "data-fp": "gantt-timeline", children: snapshots.map((snap, idx) => /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
1695
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className, style, "data-fp": "gantt-timeline", role: "listbox", "aria-label": "Execution timeline", children: snapshots.map((snap, idx) => /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
1471
1696
  "div",
1472
1697
  {
1473
1698
  "data-fp": "gantt-bar",
1474
1699
  "data-selected": idx === selectedIndex,
1475
1700
  "data-visible": idx <= selectedIndex,
1701
+ role: "option",
1702
+ "aria-selected": idx === selectedIndex,
1703
+ "aria-label": `${snap.stageLabel}, ${snap.durationMs}ms`,
1476
1704
  onClick: () => onSelect?.(idx),
1477
1705
  children: [
1478
1706
  /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { "data-fp": "gantt-label", children: snap.stageLabel }),
@@ -1482,7 +1710,7 @@ function GanttTimeline({
1482
1710
  ] })
1483
1711
  ]
1484
1712
  },
1485
- snap.stageName
1713
+ `${snap.stageName}-${idx}`
1486
1714
  )) });
1487
1715
  }
1488
1716
  return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
@@ -1538,6 +1766,8 @@ function GanttTimeline({
1538
1766
  "div",
1539
1767
  {
1540
1768
  ref: scrollContainerRef,
1769
+ role: "listbox",
1770
+ "aria-label": "Execution timeline",
1541
1771
  style: {
1542
1772
  marginTop: 8,
1543
1773
  display: "flex",
@@ -1558,6 +1788,9 @@ function GanttTimeline({
1558
1788
  "div",
1559
1789
  {
1560
1790
  ref: isSelected ? activeRowRef : void 0,
1791
+ role: "option",
1792
+ "aria-selected": isSelected,
1793
+ "aria-label": `${snap.stageLabel}, ${snap.durationMs}ms`,
1561
1794
  onClick: () => onSelect?.(idx),
1562
1795
  style: {
1563
1796
  display: "flex",
@@ -1573,6 +1806,7 @@ function GanttTimeline({
1573
1806
  /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1574
1807
  "span",
1575
1808
  {
1809
+ title: snap.stageLabel,
1576
1810
  style: {
1577
1811
  width: labelWidth,
1578
1812
  fontSize: fs.small,
@@ -1632,7 +1866,7 @@ function GanttTimeline({
1632
1866
  )
1633
1867
  ]
1634
1868
  },
1635
- snap.stageName
1869
+ `${snap.stageName}-${idx}`
1636
1870
  );
1637
1871
  })
1638
1872
  }
@@ -1987,6 +2221,8 @@ function ScrubButton({
1987
2221
  SubflowTree,
1988
2222
  TimeTravelDebugger,
1989
2223
  TracedFlowchartView,
2224
+ applyOverlay,
2225
+ specToLayout,
1990
2226
  specToReactFlow,
1991
2227
  useSubflowNavigation
1992
2228
  });