footprint-explainable-ui 0.18.0 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/dist/adapters/fromRuntimeSnapshot.d.ts +33 -4
  2. package/dist/adapters/fromRuntimeSnapshot.d.ts.map +1 -1
  3. package/dist/adapters/fromRuntimeSnapshot.js +162 -33
  4. package/dist/adapters/fromRuntimeSnapshot.js.map +1 -1
  5. package/dist/components/ExplainableShell/ExplainableShell.d.ts +157 -14
  6. package/dist/components/ExplainableShell/ExplainableShell.d.ts.map +1 -1
  7. package/dist/components/ExplainableShell/ExplainableShell.js +676 -68
  8. package/dist/components/ExplainableShell/ExplainableShell.js.map +1 -1
  9. package/dist/components/ExplainableShell/index.d.ts +1 -1
  10. package/dist/components/ExplainableShell/index.d.ts.map +1 -1
  11. package/dist/components/FlowchartView/SubflowTree.d.ts +7 -14
  12. package/dist/components/FlowchartView/SubflowTree.d.ts.map +1 -1
  13. package/dist/components/FlowchartView/SubflowTree.js +56 -46
  14. package/dist/components/FlowchartView/SubflowTree.js.map +1 -1
  15. package/dist/components/FlowchartView/index.d.ts +32 -4
  16. package/dist/components/FlowchartView/index.d.ts.map +1 -1
  17. package/dist/components/FlowchartView/index.js +22 -2
  18. package/dist/components/FlowchartView/index.js.map +1 -1
  19. package/dist/components/FlowchartView/useSubflowNavigation.d.ts +41 -16
  20. package/dist/components/FlowchartView/useSubflowNavigation.d.ts.map +1 -1
  21. package/dist/components/FlowchartView/useSubflowNavigation.js +69 -50
  22. package/dist/components/FlowchartView/useSubflowNavigation.js.map +1 -1
  23. package/dist/components/GanttTimeline/GanttTimeline.d.ts.map +1 -1
  24. package/dist/components/GanttTimeline/GanttTimeline.js +5 -5
  25. package/dist/components/GanttTimeline/GanttTimeline.js.map +1 -1
  26. package/dist/components/MemoryInspector/MemoryInspector.d.ts.map +1 -1
  27. package/dist/components/MemoryInspector/MemoryInspector.js +36 -13
  28. package/dist/components/MemoryInspector/MemoryInspector.js.map +1 -1
  29. package/dist/components/NarrativeTrace/NarrativeTrace.d.ts.map +1 -1
  30. package/dist/components/NarrativeTrace/NarrativeTrace.js +24 -17
  31. package/dist/components/NarrativeTrace/NarrativeTrace.js.map +1 -1
  32. package/dist/components/ScopeDiff/ScopeDiff.js +3 -3
  33. package/dist/components/ScopeDiff/ScopeDiff.js.map +1 -1
  34. package/dist/components/StageNode/StageNode.d.ts +21 -0
  35. package/dist/components/StageNode/StageNode.d.ts.map +1 -1
  36. package/dist/components/StageNode/StageNode.js +189 -9
  37. package/dist/components/StageNode/StageNode.js.map +1 -1
  38. package/dist/components/TimeTravelControls/TimeTravelControls.d.ts.map +1 -1
  39. package/dist/components/TimeTravelControls/TimeTravelControls.js +19 -3
  40. package/dist/components/TimeTravelControls/TimeTravelControls.js.map +1 -1
  41. package/dist/components/TimeTravelDebugger/TimeTravelDebugger.d.ts +19 -8
  42. package/dist/components/TimeTravelDebugger/TimeTravelDebugger.d.ts.map +1 -1
  43. package/dist/components/TimeTravelDebugger/TimeTravelDebugger.js +23 -8
  44. package/dist/components/TimeTravelDebugger/TimeTravelDebugger.js.map +1 -1
  45. package/dist/flowchart.cjs +3485 -1340
  46. package/dist/flowchart.cjs.map +1 -1
  47. package/dist/flowchart.d.cts +1616 -177
  48. package/dist/flowchart.d.ts +1616 -177
  49. package/dist/flowchart.d.ts.map +1 -1
  50. package/dist/flowchart.js +3507 -1384
  51. package/dist/flowchart.js.map +1 -1
  52. package/dist/index.cjs +749 -557
  53. package/dist/index.cjs.map +1 -1
  54. package/dist/index.d.cts +276 -29
  55. package/dist/index.d.ts +276 -29
  56. package/dist/index.d.ts.map +1 -1
  57. package/dist/index.js +742 -547
  58. package/dist/index.js.map +1 -1
  59. package/dist/theme/ThemeProvider.d.ts +14 -0
  60. package/dist/theme/ThemeProvider.d.ts.map +1 -1
  61. package/dist/theme/ThemeProvider.js +15 -1
  62. package/dist/theme/ThemeProvider.js.map +1 -1
  63. package/dist/theme/index.d.ts +4 -2
  64. package/dist/theme/index.d.ts.map +1 -1
  65. package/dist/theme/index.js +3 -2
  66. package/dist/theme/index.js.map +1 -1
  67. package/dist/theme/presets.d.ts +3 -0
  68. package/dist/theme/presets.d.ts.map +1 -1
  69. package/dist/theme/presets.js +22 -0
  70. package/dist/theme/presets.js.map +1 -1
  71. package/dist/theme/tokens.d.ts +22 -1
  72. package/dist/theme/tokens.d.ts.map +1 -1
  73. package/dist/theme/tokens.js +23 -2
  74. package/dist/theme/tokens.js.map +1 -1
  75. package/dist/tsconfig.tsbuildinfo +1 -1
  76. package/dist/types.d.ts +25 -0
  77. package/dist/types.d.ts.map +1 -1
  78. package/package.json +4 -4
  79. package/dist/components/FlowchartView/FlowchartView.d.ts +0 -20
  80. package/dist/components/FlowchartView/FlowchartView.d.ts.map +0 -1
  81. package/dist/components/FlowchartView/FlowchartView.js +0 -80
  82. package/dist/components/FlowchartView/FlowchartView.js.map +0 -1
  83. package/dist/components/FlowchartView/TracedFlowchartView.d.ts +0 -20
  84. package/dist/components/FlowchartView/TracedFlowchartView.d.ts.map +0 -1
  85. package/dist/components/FlowchartView/TracedFlowchartView.js +0 -101
  86. package/dist/components/FlowchartView/TracedFlowchartView.js.map +0 -1
  87. package/dist/components/FlowchartView/specToReactFlow.d.ts +0 -56
  88. package/dist/components/FlowchartView/specToReactFlow.d.ts.map +0 -1
  89. package/dist/components/FlowchartView/specToReactFlow.js +0 -202
  90. package/dist/components/FlowchartView/specToReactFlow.js.map +0 -1
package/dist/index.cjs CHANGED
@@ -143,7 +143,14 @@ function useFootprintTheme() {
143
143
  }
144
144
  function FootprintTheme({ tokens = {}, children }) {
145
145
  const cssVars = tokensToCSSVars(tokens);
146
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ThemeContext.Provider, { value: tokens, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: cssVars, className: "fp-theme-root", children }) });
146
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ThemeContext.Provider, { value: tokens, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
147
+ "div",
148
+ {
149
+ style: { ...cssVars, display: "contents" },
150
+ className: "fp-theme-root",
151
+ children
152
+ }
153
+ ) });
147
154
  }
148
155
 
149
156
  // src/theme/styles.ts
@@ -2272,7 +2279,7 @@ function TimeTravelControls({
2272
2279
  }
2273
2280
 
2274
2281
  // src/components/ExplainableShell/ExplainableShell.tsx
2275
- var import_react24 = require("react");
2282
+ var import_react27 = require("react");
2276
2283
 
2277
2284
  // src/utils/narrativeSync.ts
2278
2285
  function buildEntryRangeIndex(entries) {
@@ -2774,7 +2781,6 @@ function NarrativePanel({
2774
2781
  snapshots,
2775
2782
  selectedIndex,
2776
2783
  narrativeEntries,
2777
- narrative: narrativeProp,
2778
2784
  runtimeSnapshot,
2779
2785
  spec,
2780
2786
  size = "default",
@@ -2785,14 +2791,13 @@ function NarrativePanel({
2785
2791
  const fs = fontSize[size];
2786
2792
  const pad = padding[size];
2787
2793
  const narrative = (0, import_react12.useMemo)(() => {
2788
- if (narrativeProp && narrativeProp.length > 0) return narrativeProp;
2789
2794
  const lines = [];
2790
2795
  for (const snap of snapshots) {
2791
2796
  const stageLines = (snap.narrative ?? "").split("\n").filter(Boolean);
2792
2797
  lines.push(...stageLines);
2793
2798
  }
2794
2799
  return lines;
2795
- }, [narrativeProp, snapshots]);
2800
+ }, [snapshots]);
2796
2801
  const revealedCount = (0, import_react12.useMemo)(() => {
2797
2802
  if (snapshots.length === 0 || narrative.length === 0) return narrative.length;
2798
2803
  const stageBoundaries = [];
@@ -3016,35 +3021,19 @@ function NarrativePanel({
3016
3021
  // src/components/FlowchartView/SubflowTree.tsx
3017
3022
  var import_react13 = require("react");
3018
3023
  var import_jsx_runtime14 = require("react/jsx-runtime");
3019
- function specToTree(node) {
3020
- if (!node) return [];
3024
+ function graphToSubflowEntries(graph) {
3025
+ if (!graph?.nodes?.length) return [];
3021
3026
  const entries = [];
3022
- const seen = /* @__PURE__ */ new Set();
3023
- function walk(n) {
3024
- if (!n) return;
3025
- const id = n.name || n.id || "";
3026
- if (seen.has(id)) return;
3027
- seen.add(id);
3027
+ for (const node of graph.nodes) {
3028
+ if (!node.data?.isSubflow) continue;
3028
3029
  const entry = {
3029
- name: n.name,
3030
- description: n.description,
3031
- subflowId: n.subflowId,
3032
- isSubflow: !!n.isSubflowRoot
3030
+ name: typeof node.data.label === "string" ? node.data.label : node.id,
3031
+ isSubflow: true
3033
3032
  };
3034
- if (n.isSubflowRoot && n.subflowStructure) {
3035
- entry.children = specToTree(n.subflowStructure);
3036
- }
3033
+ if (typeof node.data.description === "string") entry.description = node.data.description;
3034
+ if (typeof node.data.subflowId === "string") entry.subflowId = node.data.subflowId;
3037
3035
  entries.push(entry);
3038
- if (n.children) {
3039
- for (const child of n.children) {
3040
- if (child) walk(child);
3041
- }
3042
- }
3043
- if (n.next) {
3044
- walk(n.next);
3045
- }
3046
3036
  }
3047
- walk(node);
3048
3037
  return entries;
3049
3038
  }
3050
3039
  var TreeNode = (0, import_react13.memo)(function TreeNode2({
@@ -3188,7 +3177,7 @@ var SectionLabel = (0, import_react13.memo)(function SectionLabel2({ children })
3188
3177
  );
3189
3178
  });
3190
3179
  var SubflowTree = (0, import_react13.memo)(function SubflowTree2({
3191
- spec,
3180
+ graph,
3192
3181
  activeStage,
3193
3182
  doneStages,
3194
3183
  onNodeSelect,
@@ -3196,8 +3185,7 @@ var SubflowTree = (0, import_react13.memo)(function SubflowTree2({
3196
3185
  className,
3197
3186
  style
3198
3187
  }) {
3199
- const tree = (0, import_react13.useMemo)(() => specToTree(spec), [spec]);
3200
- const subflowStages = (0, import_react13.useMemo)(() => tree.filter((e) => e.isSubflow), [tree]);
3188
+ const subflowStages = (0, import_react13.useMemo)(() => graphToSubflowEntries(graph), [graph]);
3201
3189
  if (subflowStages.length === 0) return null;
3202
3190
  return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
3203
3191
  "div",
@@ -3317,9 +3305,13 @@ var SubflowBreadcrumb = (0, import_react14.memo)(function SubflowBreadcrumb2({
3317
3305
  );
3318
3306
  });
3319
3307
 
3320
- // src/components/FlowchartView/TracedFlowchartView.tsx
3321
- var import_react18 = require("react");
3322
- var import_react19 = require("@xyflow/react");
3308
+ // src/components/FlowchartView/TracedFlow.tsx
3309
+ var import_react21 = require("react");
3310
+ var import_react22 = require("@xyflow/react");
3311
+
3312
+ // src/components/FlowchartView/TraceFlow.tsx
3313
+ var import_react17 = require("react");
3314
+ var import_react18 = require("@xyflow/react");
3323
3315
 
3324
3316
  // src/components/StageNode/StageNode.tsx
3325
3317
  var import_react15 = require("react");
@@ -3474,7 +3466,7 @@ function StageIcon({ type, color }) {
3474
3466
  var StageNode = (0, import_react15.memo)(function StageNode2({
3475
3467
  data
3476
3468
  }) {
3477
- const { label, active, done, error, linked, icon, stepNumbers, dimmed, isSubflow, isLazy, isDecider, isFork, description } = data;
3469
+ const { label, active, done, error, linked, icon, stepNumbers, dimmed, isSubflow, isLazy, isDecider, isFork, description, stageId, showStageId } = data;
3478
3470
  const effectiveIcon = icon || (isLazy ? "lazy" : void 0);
3479
3471
  const isLazyUnresolved = isLazy && !done && !active;
3480
3472
  const injectedRef = (0, import_react15.useRef)(false);
@@ -3648,6 +3640,23 @@ var StageNode = (0, import_react15.memo)(function StageNode2({
3648
3640
  },
3649
3641
  children: description
3650
3642
  }
3643
+ ),
3644
+ showStageId && stageId && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
3645
+ "span",
3646
+ {
3647
+ style: {
3648
+ fontSize: 8,
3649
+ fontFamily: "ui-monospace, monospace",
3650
+ color: textColor,
3651
+ opacity: 0.55,
3652
+ whiteSpace: "nowrap",
3653
+ overflow: "hidden",
3654
+ textOverflow: "ellipsis",
3655
+ maxWidth: 100
3656
+ },
3657
+ title: `stageId: ${stageId}`,
3658
+ children: stageId
3659
+ }
3651
3660
  )
3652
3661
  ]
3653
3662
  }
@@ -3746,6 +3755,23 @@ var StageNode = (0, import_react15.memo)(function StageNode2({
3746
3755
  },
3747
3756
  children: description
3748
3757
  }
3758
+ ),
3759
+ showStageId && stageId && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
3760
+ "span",
3761
+ {
3762
+ style: {
3763
+ fontSize: 9,
3764
+ fontFamily: "ui-monospace, monospace",
3765
+ color: textColor,
3766
+ opacity: 0.55,
3767
+ whiteSpace: "nowrap",
3768
+ overflow: "hidden",
3769
+ textOverflow: "ellipsis",
3770
+ maxWidth: 160
3771
+ },
3772
+ title: `stageId: ${stageId}`,
3773
+ children: stageId
3774
+ }
3749
3775
  )
3750
3776
  ]
3751
3777
  }
@@ -3776,335 +3802,497 @@ var StageNode = (0, import_react15.memo)(function StageNode2({
3776
3802
  ] });
3777
3803
  });
3778
3804
 
3779
- // src/components/FlowchartView/specToReactFlow.ts
3780
- var import_react17 = require("@xyflow/react");
3781
- var DEFAULT_COLORS = {
3782
- edgeDefault: rawDefaults.colors.textMuted,
3783
- edgeExecuted: rawDefaults.colors.success,
3784
- edgeActive: rawDefaults.colors.primary,
3785
- edgeLoop: rawDefaults.colors.warning,
3786
- labelDefault: rawDefaults.colors.textSecondary,
3787
- labelExecuted: rawDefaults.colors.success,
3788
- labelLoop: rawDefaults.colors.warning,
3789
- pathGlow: `${rawDefaults.colors.success}4D`
3790
- // ~30% opacity hex
3791
- };
3805
+ // src/components/FlowchartView/TraceFlow.tsx
3806
+ var import_jsx_runtime17 = require("react/jsx-runtime");
3792
3807
  var Y_STEP = 100;
3793
3808
  var X_SPREAD = 200;
3794
- function nid(n) {
3795
- return n.id || n.name || `spec-${Math.random()}`;
3796
- }
3797
- function registerNode(state, node) {
3798
- if (node.id && node.name) {
3799
- state.idToName.set(node.id, node.name);
3800
- }
3801
- }
3802
- function walkLayout(node, state, x, y) {
3803
- if (!node) return { lastIds: [], bottomY: y };
3804
- registerNode(state, node);
3805
- const id = nid(node);
3806
- if (state.seen.has(id)) {
3807
- return { lastIds: [id], bottomY: y };
3808
- }
3809
- state.seen.add(id);
3810
- const isDecider = node.type === "decider" || node.type === "selector" || !!node.hasDecider || !!node.hasSelector;
3811
- const isFork = node.type === "fork";
3812
- state.nodes.push({
3813
- id,
3814
- x,
3815
- y,
3816
- label: node.name,
3817
- isDecider,
3818
- isFork,
3819
- description: node.description,
3820
- icon: node.icon,
3821
- subflowId: node.subflowId,
3822
- isSubflow: !!node.isSubflowRoot,
3823
- isLazy: node.isLazy
3824
- });
3825
- let lastIds = [id];
3826
- let bottomY = y;
3827
- if (node.children && node.children.length > 0) {
3828
- const totalWidth = (node.children.length - 1) * X_SPREAD;
3829
- const startX = x - totalWidth / 2;
3830
- const childY = y + Y_STEP;
3831
- const childResults = [];
3832
- for (let i = 0; i < node.children.length; i++) {
3833
- const child = node.children[i];
3834
- if (!child) continue;
3835
- const childX = startX + i * X_SPREAD;
3836
- const edgeLabel = node.branchIds?.[i];
3837
- state.edgeCounter++;
3838
- state.edges.push({ id: `se${state.edgeCounter}`, source: id, target: nid(child), label: edgeLabel, isLoop: false });
3839
- const result = walkLayout(child, state, childX, childY);
3840
- childResults.push(result);
3809
+ var defaultTraceFlowLayout = (graph) => {
3810
+ if (graph.nodes.length === 0) return { nodes: [], edges: graph.edges };
3811
+ const branchChildrenOf = /* @__PURE__ */ new Map();
3812
+ const nextChildrenOf = /* @__PURE__ */ new Map();
3813
+ const hasIncomingForward = /* @__PURE__ */ new Set();
3814
+ for (const e of graph.edges) {
3815
+ const kind = e.data?.kind;
3816
+ if (kind === "loop") continue;
3817
+ hasIncomingForward.add(e.target);
3818
+ if (kind === "fork-branch" || kind === "decision-branch") {
3819
+ const arr = branchChildrenOf.get(e.source) ?? [];
3820
+ arr.push(e.target);
3821
+ branchChildrenOf.set(e.source, arr);
3822
+ } else {
3823
+ const arr = nextChildrenOf.get(e.source) ?? [];
3824
+ arr.push(e.target);
3825
+ nextChildrenOf.set(e.source, arr);
3841
3826
  }
3842
- lastIds = childResults.flatMap((r) => r.lastIds);
3843
- bottomY = Math.max(...childResults.map((r) => r.bottomY));
3844
- }
3845
- if (node.loopTarget) {
3846
- state.edgeCounter++;
3847
- state.edges.push({ id: `se${state.edgeCounter}`, source: id, target: node.loopTarget, label: "loop", isLoop: true });
3848
3827
  }
3849
- if (node.next) {
3850
- const rawNextId = nid(node.next);
3851
- const resolvedNextId = state.idToName.get(rawNextId) ?? rawNextId;
3852
- const isLoopRef = node.loopTarget && state.seen.has(resolvedNextId);
3853
- if (isLoopRef) {
3854
- for (const lid of lastIds) {
3855
- state.edgeCounter++;
3856
- state.edges.push({ id: `se${state.edgeCounter}`, source: lid, target: resolvedNextId, label: "loop", isLoop: true });
3828
+ const seed = graph.nodes.find((n) => !hasIncomingForward.has(n.id)) ?? graph.nodes[0];
3829
+ const positions = /* @__PURE__ */ new Map();
3830
+ function layoutSubtree(nodeId, x, y) {
3831
+ if (positions.has(nodeId)) {
3832
+ return positions.get(nodeId).y;
3833
+ }
3834
+ positions.set(nodeId, { x, y });
3835
+ let deepestBranchY = y;
3836
+ const branches = branchChildrenOf.get(nodeId) ?? [];
3837
+ if (branches.length > 0) {
3838
+ const childY = y + Y_STEP;
3839
+ const totalWidth = (branches.length - 1) * X_SPREAD;
3840
+ const startX = x - totalWidth / 2;
3841
+ for (let i = 0; i < branches.length; i++) {
3842
+ const childId = branches[i];
3843
+ const childX = startX + i * X_SPREAD;
3844
+ const subtreeBottom = layoutSubtree(childId, childX, childY);
3845
+ if (subtreeBottom > deepestBranchY) deepestBranchY = subtreeBottom;
3857
3846
  }
3858
- return { lastIds, bottomY };
3859
3847
  }
3860
- const nextY = bottomY + Y_STEP;
3861
- for (const lid of lastIds) {
3862
- state.edgeCounter++;
3863
- state.edges.push({ id: `se${state.edgeCounter}`, source: lid, target: resolvedNextId, isLoop: false });
3848
+ const nextChildren = nextChildrenOf.get(nodeId) ?? [];
3849
+ if (nextChildren.length > 0) {
3850
+ const nextY = branches.length > 0 ? deepestBranchY + Y_STEP : y + Y_STEP;
3851
+ const totalWidth = (nextChildren.length - 1) * X_SPREAD;
3852
+ const startX = x - totalWidth / 2;
3853
+ let deepestNextY = nextY;
3854
+ for (let i = 0; i < nextChildren.length; i++) {
3855
+ const childId = nextChildren[i];
3856
+ const childX = startX + i * X_SPREAD;
3857
+ const subtreeBottom = layoutSubtree(childId, childX, nextY);
3858
+ if (subtreeBottom > deepestNextY) deepestNextY = subtreeBottom;
3859
+ }
3860
+ return deepestNextY;
3861
+ }
3862
+ return deepestBranchY;
3863
+ }
3864
+ layoutSubtree(seed.id, 0, 0);
3865
+ let maxY = 0;
3866
+ for (const p of positions.values()) {
3867
+ if (p.y > maxY) maxY = p.y;
3868
+ }
3869
+ let orphanY = maxY + Y_STEP;
3870
+ for (const n of graph.nodes) {
3871
+ if (!positions.has(n.id)) {
3872
+ positions.set(n.id, { x: 0, y: orphanY });
3873
+ orphanY += Y_STEP;
3864
3874
  }
3865
- return walkLayout(node.next, state, x, nextY);
3866
3875
  }
3867
- return { lastIds, bottomY };
3876
+ return {
3877
+ nodes: graph.nodes.map((n) => ({
3878
+ ...n,
3879
+ position: positions.get(n.id) ?? n.position
3880
+ })),
3881
+ edges: graph.edges
3882
+ };
3883
+ };
3884
+ var DEFAULT_EDGE_COLORS = {
3885
+ next: rawDefaults.colors.textMuted,
3886
+ forkBranch: rawDefaults.colors.textMuted,
3887
+ decisionBranch: rawDefaults.colors.primary,
3888
+ loop: rawDefaults.colors.warning
3889
+ };
3890
+
3891
+ // src/components/FlowchartView/createTraceRuntimeOverlay.ts
3892
+ function sliceOverlay(overlay, index) {
3893
+ const order = overlay.executionOrder;
3894
+ if (order.length === 0) {
3895
+ return {
3896
+ doneStageIds: /* @__PURE__ */ new Set(),
3897
+ activeStageId: null,
3898
+ executedStageIds: /* @__PURE__ */ new Set(),
3899
+ executedOrderIds: [],
3900
+ errors: overlay.errors
3901
+ };
3902
+ }
3903
+ const clampedIndex = Math.max(0, Math.min(index, order.length - 1));
3904
+ const doneStageIds = /* @__PURE__ */ new Set();
3905
+ for (let i = 0; i < clampedIndex; i++) {
3906
+ doneStageIds.add(order[i].stageId);
3907
+ }
3908
+ const activeStep = order[clampedIndex];
3909
+ const activeStageId = activeStep ? activeStep.stageId : null;
3910
+ const executedStageIds = new Set(doneStageIds);
3911
+ if (activeStageId) executedStageIds.add(activeStageId);
3912
+ const executedOrderIds = order.slice(0, clampedIndex + 1).map((s) => s.stageId);
3913
+ return {
3914
+ doneStageIds,
3915
+ activeStageId,
3916
+ executedStageIds,
3917
+ executedOrderIds,
3918
+ errors: overlay.errors
3919
+ };
3868
3920
  }
3869
- function specToLayout(spec) {
3870
- const state = {
3871
- nodes: [],
3872
- edges: [],
3873
- edgeCounter: 0,
3874
- seen: /* @__PURE__ */ new Set(),
3875
- idToName: /* @__PURE__ */ new Map()
3921
+
3922
+ // src/components/FlowchartView/_internal/subflowDrill.ts
3923
+ function filterGraphForDrill(graph, currentSubflowId) {
3924
+ if (graph.nodes.length === 0) return graph;
3925
+ const matchesScope = (subflowOf) => currentSubflowId === null ? subflowOf === void 0 : subflowOf === currentSubflowId;
3926
+ const visibleIds = /* @__PURE__ */ new Set();
3927
+ for (const n of graph.nodes) {
3928
+ if (matchesScope(n.data?.subflowOf)) visibleIds.add(n.id);
3929
+ }
3930
+ if (visibleIds.size === graph.nodes.length) return graph;
3931
+ return {
3932
+ nodes: graph.nodes.filter((n) => visibleIds.has(n.id)),
3933
+ edges: graph.edges.filter((e) => visibleIds.has(e.source) && visibleIds.has(e.target))
3876
3934
  };
3877
- walkLayout(spec, state, 300, 0);
3878
- return { nodes: state.nodes, edges: state.edges, idToName: state.idToName };
3879
3935
  }
3880
- function applyOverlay(layout, overlay, colors) {
3881
- const c = { ...DEFAULT_COLORS, ...colors };
3882
- const o = overlay ?? null;
3883
- const nodes = layout.nodes.map((ln) => {
3884
- const isDone = o ? o.doneStages.has(ln.id) : false;
3885
- const isActive = o ? o.activeStage === ln.id : false;
3886
- const wasExecuted = o ? o.executedStages.has(ln.id) : false;
3887
- const dimmed = o && !wasExecuted;
3888
- let stepNumbers;
3889
- if (o?.executionOrder) {
3890
- const nums = [];
3891
- for (let i = 0; i < o.executionOrder.length; i++) {
3892
- if (o.executionOrder[i] === ln.id) nums.push(i + 1);
3893
- }
3894
- if (nums.length > 0) stepNumbers = nums;
3936
+ function buildSubflowBreadcrumb(graph, currentSubflowId) {
3937
+ const out = [{ subflowId: null, label: "Chart" }];
3938
+ if (currentSubflowId !== null) {
3939
+ const mount = graph.nodes.find((n) => n.data?.subflowId === currentSubflowId);
3940
+ out.push({
3941
+ subflowId: currentSubflowId,
3942
+ label: mount?.data?.label ?? currentSubflowId
3943
+ });
3944
+ }
3945
+ return out;
3946
+ }
3947
+
3948
+ // src/components/FlowchartView/_internal/overlayProjection.ts
3949
+ function leafId(id) {
3950
+ const i = id.lastIndexOf("/");
3951
+ return i >= 0 ? id.slice(i + 1) : id;
3952
+ }
3953
+ function normalizeSliceLeafIds(slice) {
3954
+ return {
3955
+ doneStageIds: new Set(Array.from(slice.doneStageIds).map(leafId)),
3956
+ activeStageId: slice.activeStageId ? leafId(slice.activeStageId) : null,
3957
+ executedStageIds: new Set(Array.from(slice.executedStageIds).map(leafId)),
3958
+ executedOrderIds: slice.executedOrderIds.map(leafId),
3959
+ errors: new Map(Array.from(slice.errors).map(([k, v2]) => [leafId(k), v2]))
3960
+ };
3961
+ }
3962
+ function aggregateMountStatus(slice, graph, currentSubflowId) {
3963
+ if (graph.nodes.length === 0) return slice;
3964
+ const mounts = graph.nodes.filter((n) => n.data?.isSubflow && n.data?.subflowId);
3965
+ if (mounts.length === 0) return slice;
3966
+ const doneIds = new Set(slice.doneStageIds);
3967
+ let activeId = slice.activeStageId;
3968
+ for (const mount of mounts) {
3969
+ const sfId = mount.data.subflowId;
3970
+ const members = graph.nodes.filter((n) => n.data?.subflowOf === sfId);
3971
+ if (members.length === 0) continue;
3972
+ const anyActive = members.some((m) => m.id === slice.activeStageId);
3973
+ const anyDone = members.some((m) => slice.doneStageIds.has(m.id));
3974
+ const allDone = members.every((m) => slice.doneStageIds.has(m.id));
3975
+ if (allDone) doneIds.add(mount.id);
3976
+ else if ((anyActive || anyDone) && currentSubflowId === null) {
3977
+ activeId = mount.id;
3895
3978
  }
3896
- return {
3897
- id: ln.id,
3898
- position: { x: ln.x, y: ln.y },
3899
- data: {
3900
- label: ln.label,
3901
- active: isActive,
3902
- done: isDone,
3903
- error: false,
3904
- isDecider: ln.isDecider,
3905
- isFork: ln.isFork,
3906
- description: ln.description,
3907
- icon: ln.icon,
3908
- subflowId: ln.subflowId,
3909
- dimmed,
3910
- stepNumbers,
3911
- isSubflow: ln.isSubflow,
3912
- isLazy: ln.isLazy
3913
- },
3914
- type: "stage",
3915
- style: dimmed ? { opacity: 0.35 } : void 0
3916
- };
3917
- });
3918
- const edges = [];
3919
- for (const le of layout.edges) {
3920
- const executed = o && o.executedStages.has(le.source) && o.executedStages.has(le.target);
3921
- const isLeadingEdge = o && le.source === o.activeStage && !o.doneStages.has(le.target);
3922
- if (le.isLoop) {
3923
- let loopExecuted = false;
3924
- if (o?.executionOrder) {
3925
- if (le.source === le.target) {
3926
- const count = o.executionOrder.filter((id) => id === le.source).length;
3927
- loopExecuted = count > 1;
3928
- } else {
3929
- const lastSourceIdx = o.executionOrder.lastIndexOf(le.source);
3930
- if (lastSourceIdx >= 0) {
3931
- loopExecuted = o.executionOrder.slice(lastSourceIdx + 1).includes(le.target);
3932
- }
3933
- }
3934
- }
3935
- edges.push({
3936
- id: le.id,
3937
- source: le.source,
3938
- target: le.target,
3939
- sourceHandle: "loop-source",
3940
- targetHandle: "loop-target",
3941
- label: le.label ?? "loop",
3942
- type: "smoothstep",
3943
- pathOptions: { offset: 60, borderRadius: 20 },
3944
- markerEnd: { type: import_react17.MarkerType.ArrowClosed, color: loopExecuted ? c.edgeActive : c.edgeLoop, width: 16, height: 16 },
3945
- style: {
3946
- stroke: loopExecuted ? c.edgeActive : c.edgeLoop,
3947
- strokeWidth: loopExecuted ? 4 : 2,
3948
- strokeDasharray: loopExecuted ? void 0 : "6 3",
3949
- opacity: o && !loopExecuted ? 0.35 : 1
3950
- },
3951
- labelStyle: { fontSize: 10, fontWeight: 700, fill: loopExecuted ? c.edgeActive : c.labelLoop },
3952
- animated: loopExecuted,
3953
- zIndex: 2
3954
- });
3955
- } else if (executed) {
3956
- edges.push({
3957
- id: `${le.id}-glow`,
3958
- source: le.source,
3959
- target: le.target,
3960
- style: { stroke: c.pathGlow, strokeWidth: 8, opacity: 0.4 },
3961
- zIndex: 0,
3962
- selectable: false,
3963
- focusable: false
3964
- });
3965
- edges.push({
3966
- id: le.id,
3967
- source: le.source,
3968
- target: le.target,
3969
- label: le.label,
3970
- style: {
3971
- stroke: isLeadingEdge ? c.edgeActive : c.edgeExecuted,
3972
- strokeWidth: 3.5
3973
- },
3974
- labelStyle: { fontSize: 10, fontWeight: 600, fill: c.labelExecuted },
3975
- animated: !!isLeadingEdge,
3976
- zIndex: 1
3977
- });
3978
- } else {
3979
- edges.push({
3980
- id: le.id,
3981
- source: le.source,
3982
- target: le.target,
3983
- label: le.label,
3984
- style: {
3985
- stroke: c.edgeDefault,
3986
- strokeWidth: 1.5,
3987
- opacity: o ? 0.3 : 1
3988
- },
3989
- labelStyle: { fontSize: 10, fill: c.labelDefault }
3990
- });
3979
+ }
3980
+ return { ...slice, doneStageIds: doneIds, activeStageId: activeId };
3981
+ }
3982
+
3983
+ // src/components/FlowchartView/_internal/useSubflowDrill.ts
3984
+ var import_react19 = require("react");
3985
+ function useSubflowDrill(graph, onSubflowChange) {
3986
+ const [currentSubflowId, setCurrentSubflowId] = (0, import_react19.useState)(null);
3987
+ const lastGraphRef = (0, import_react19.useRef)(null);
3988
+ if (lastGraphRef.current !== graph) {
3989
+ lastGraphRef.current = graph;
3990
+ if (currentSubflowId !== null && !graph.nodes.some((n) => n.data?.subflowId === currentSubflowId)) {
3991
+ queueMicrotask(() => setCurrentSubflowId(null));
3991
3992
  }
3992
3993
  }
3993
- return { nodes, edges };
3994
+ const lastNotifiedRef = (0, import_react19.useRef)(void 0);
3995
+ (0, import_react19.useEffect)(() => {
3996
+ if (lastNotifiedRef.current === currentSubflowId) return;
3997
+ lastNotifiedRef.current = currentSubflowId;
3998
+ if (currentSubflowId === null) {
3999
+ onSubflowChange?.(null);
4000
+ } else {
4001
+ const mount = graph.nodes.find((n) => n.data?.subflowId === currentSubflowId);
4002
+ if (mount) onSubflowChange?.(mount.id);
4003
+ }
4004
+ }, [currentSubflowId, graph, onSubflowChange]);
4005
+ const drillInto = (0, import_react19.useCallback)((subflowId) => {
4006
+ setCurrentSubflowId(subflowId);
4007
+ }, []);
4008
+ const drillUp = (0, import_react19.useCallback)(() => {
4009
+ setCurrentSubflowId(null);
4010
+ }, []);
4011
+ return { currentSubflowId, drillInto, drillUp, setCurrentSubflowId };
3994
4012
  }
3995
4013
 
3996
- // src/components/FlowchartView/TracedFlowchartView.tsx
3997
- var import_jsx_runtime17 = require("react/jsx-runtime");
3998
- var defaultNodeTypes = { stage: StageNode };
3999
- function FitViewOnResize() {
4000
- const { fitView } = (0, import_react19.useReactFlow)();
4001
- (0, import_react18.useEffect)(() => {
4002
- const handler = () => {
4003
- requestAnimationFrame(() => fitView({ padding: 0.3 }));
4014
+ // src/components/FlowchartView/_internal/useChartAutoRefit.ts
4015
+ var import_react20 = require("react");
4016
+ function useChartAutoRefit(wrapperRef, rfInstance, options = {}) {
4017
+ const duration = options.duration ?? 200;
4018
+ const padding2 = options.padding ?? 0.1;
4019
+ (0, import_react20.useEffect)(() => {
4020
+ const el = wrapperRef.current;
4021
+ if (!el || !rfInstance) return;
4022
+ let raf = 0;
4023
+ const refit = () => {
4024
+ cancelAnimationFrame(raf);
4025
+ raf = requestAnimationFrame(() => {
4026
+ rfInstance.fitView({ duration, padding: padding2 });
4027
+ });
4004
4028
  };
4005
- window.addEventListener("resize", handler);
4006
- const timer = setTimeout(handler, 50);
4029
+ const ro = new ResizeObserver(refit);
4030
+ ro.observe(el);
4031
+ window.addEventListener("resize", refit);
4007
4032
  return () => {
4008
- window.removeEventListener("resize", handler);
4009
- clearTimeout(timer);
4033
+ ro.disconnect();
4034
+ window.removeEventListener("resize", refit);
4035
+ cancelAnimationFrame(raf);
4010
4036
  };
4011
- }, [fitView]);
4012
- return null;
4037
+ }, [rfInstance, wrapperRef, duration, padding2]);
4013
4038
  }
4014
- function TracedFlowchartView({
4015
- spec,
4016
- snapshots,
4017
- snapshotIndex = 0,
4039
+
4040
+ // src/components/FlowchartView/SubflowBreadcrumbBar.tsx
4041
+ var import_jsx_runtime18 = require("react/jsx-runtime");
4042
+ function SubflowBreadcrumbBar({ entries, onNavigate }) {
4043
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
4044
+ "div",
4045
+ {
4046
+ style: {
4047
+ display: "flex",
4048
+ alignItems: "center",
4049
+ gap: 6,
4050
+ padding: "6px 12px",
4051
+ fontSize: 11,
4052
+ background: rawDefaults.colors.bgSecondary,
4053
+ borderBottom: `1px solid ${rawDefaults.colors.border}`,
4054
+ flexShrink: 0
4055
+ },
4056
+ "aria-label": "Subflow breadcrumb",
4057
+ children: entries.map((entry, i) => {
4058
+ const isLast = i === entries.length - 1;
4059
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
4060
+ "span",
4061
+ {
4062
+ style: { display: "inline-flex", alignItems: "center", gap: 6 },
4063
+ children: [
4064
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
4065
+ "button",
4066
+ {
4067
+ type: "button",
4068
+ onClick: () => onNavigate(entry.subflowId),
4069
+ disabled: isLast,
4070
+ style: {
4071
+ background: "transparent",
4072
+ border: "none",
4073
+ padding: 0,
4074
+ fontSize: 11,
4075
+ fontWeight: isLast ? 600 : 500,
4076
+ color: isLast ? rawDefaults.colors.textPrimary : rawDefaults.colors.primary,
4077
+ cursor: isLast ? "default" : "pointer",
4078
+ textDecoration: isLast ? "none" : "underline",
4079
+ fontFamily: "inherit"
4080
+ },
4081
+ children: entry.label
4082
+ }
4083
+ ),
4084
+ !isLast && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { style: { color: rawDefaults.colors.textMuted }, children: "\u203A" })
4085
+ ]
4086
+ },
4087
+ entry.subflowId ?? "__top__"
4088
+ );
4089
+ })
4090
+ }
4091
+ );
4092
+ }
4093
+
4094
+ // src/components/FlowchartView/TracedFlow.tsx
4095
+ var import_jsx_runtime19 = require("react/jsx-runtime");
4096
+ var DEFAULT_COLORS = {
4097
+ default: rawDefaults.colors.textMuted,
4098
+ done: rawDefaults.colors.success,
4099
+ active: rawDefaults.colors.primary,
4100
+ error: rawDefaults.colors.error,
4101
+ loop: rawDefaults.colors.warning
4102
+ };
4103
+ function toStageNodeWithOverlay(node, doneStageIds, activeStageId, errorMessage, executedOrderIds) {
4104
+ const isDone = doneStageIds.has(node.id);
4105
+ const isActive = activeStageId === node.id;
4106
+ const wasExecuted = isDone || isActive;
4107
+ const hasError = !!errorMessage;
4108
+ const dimmed = !wasExecuted && executedOrderIds.length > 0;
4109
+ let stepNumbers;
4110
+ if (executedOrderIds.length > 0) {
4111
+ const nums = [];
4112
+ for (let i = 0; i < executedOrderIds.length; i++) {
4113
+ if (executedOrderIds[i] === node.id) nums.push(i + 1);
4114
+ }
4115
+ if (nums.length > 0) stepNumbers = nums;
4116
+ }
4117
+ const stageData = {
4118
+ label: node.data.label,
4119
+ isDecider: node.data.isDecider,
4120
+ isFork: node.data.isFork,
4121
+ isSubflow: node.data.isSubflow,
4122
+ active: isActive,
4123
+ done: isDone,
4124
+ error: hasError,
4125
+ ...node.data.description !== void 0 && { description: node.data.description },
4126
+ ...node.data.icon !== void 0 && { icon: node.data.icon },
4127
+ ...node.data.subflowId !== void 0 && { subflowId: node.data.subflowId },
4128
+ ...node.data.isLazy === true && { isLazy: true },
4129
+ ...dimmed && { dimmed: true },
4130
+ ...stepNumbers && { stepNumbers },
4131
+ ...errorMessage && { errorMessage }
4132
+ };
4133
+ return {
4134
+ ...node,
4135
+ type: "stageNode",
4136
+ data: stageData,
4137
+ ...dimmed && { style: { opacity: 0.35 } }
4138
+ };
4139
+ }
4140
+ function styleEdgeWithOverlay(edge, doneStageIds, activeStageId, colors) {
4141
+ const kind = edge.data?.kind ?? "next";
4142
+ const sourceExecuted = doneStageIds.has(edge.source) || activeStageId === edge.source;
4143
+ const targetExecuted = doneStageIds.has(edge.target) || activeStageId === edge.target;
4144
+ const traversed = sourceExecuted && targetExecuted;
4145
+ const isLeadingEdge = activeStageId === edge.source && !doneStageIds.has(edge.target);
4146
+ let color = colors.default;
4147
+ if (kind === "loop") color = colors.loop;
4148
+ else if (isLeadingEdge) color = colors.active;
4149
+ else if (traversed) color = colors.done;
4150
+ const styled = {
4151
+ ...edge,
4152
+ type: "smoothstep",
4153
+ animated: isLeadingEdge,
4154
+ style: { stroke: color, strokeWidth: traversed ? 2 : 1.5 },
4155
+ markerEnd: { type: import_react22.MarkerType.ArrowClosed, color, width: 16, height: 16 }
4156
+ };
4157
+ if (kind === "loop") {
4158
+ styled.style = { ...styled.style, strokeDasharray: "4 3" };
4159
+ styled.data = {
4160
+ ...styled.data ?? {},
4161
+ pathOptions: { borderRadius: 14, offset: 36 }
4162
+ };
4163
+ styled.sourceHandle = "loop-source";
4164
+ styled.targetHandle = "loop-target";
4165
+ }
4166
+ return styled;
4167
+ }
4168
+ var nodeTypes = { stageNode: StageNode };
4169
+ function TracedFlow({
4170
+ graph,
4171
+ overlay,
4172
+ scrubIndex,
4173
+ layout: layoutProp,
4174
+ colors: colorOverrides,
4018
4175
  onNodeClick,
4019
- nodeTypes: customNodeTypes,
4020
- unstyled = false,
4176
+ onSubflowChange,
4021
4177
  className,
4022
4178
  style
4023
4179
  }) {
4024
- const nodeTypes = customNodeTypes ?? defaultNodeTypes;
4025
- const overlay = (0, import_react18.useMemo)(() => {
4026
- if (!snapshots || snapshots.length === 0) return void 0;
4027
- const executionOrder = snapshots.slice(0, snapshotIndex + 1).map((s) => s.stageLabel);
4028
- const doneStages = new Set(
4029
- snapshots.slice(0, snapshotIndex).map((s) => s.stageLabel)
4030
- );
4031
- const activeStage = snapshots[snapshotIndex]?.stageLabel ?? null;
4032
- const executedStages = /* @__PURE__ */ new Set([...doneStages]);
4033
- if (activeStage) executedStages.add(activeStage);
4034
- return { doneStages, activeStage, executedStages, executionOrder };
4035
- }, [snapshots, snapshotIndex]);
4036
- const layout = (0, import_react18.useMemo)(() => {
4037
- if (!spec) return null;
4038
- return specToLayout(spec);
4039
- }, [spec]);
4040
- const flowData = (0, import_react18.useMemo)(() => {
4041
- if (!layout) return { nodes: [], edges: [] };
4042
- return applyOverlay(layout, overlay);
4043
- }, [layout, overlay]);
4044
- const [nodes, setNodes, onNodesChange] = (0, import_react19.useNodesState)(flowData.nodes);
4045
- const [edges, setEdges, onEdgesChange] = (0, import_react19.useEdgesState)(flowData.edges);
4046
- (0, import_react18.useEffect)(() => {
4047
- setNodes(flowData.nodes);
4048
- setEdges(flowData.edges);
4049
- }, [flowData, setNodes, setEdges]);
4050
- const handleNodeClick = (0, import_react18.useCallback)(
4180
+ const layout = layoutProp ?? defaultTraceFlowLayout;
4181
+ const colors = (0, import_react21.useMemo)(
4182
+ () => ({ ...DEFAULT_COLORS, ...colorOverrides ?? {} }),
4183
+ [colorOverrides]
4184
+ );
4185
+ const drill = useSubflowDrill(graph, onSubflowChange);
4186
+ const filteredGraph = (0, import_react21.useMemo)(
4187
+ () => filterGraphForDrill(graph, drill.currentSubflowId),
4188
+ [graph, drill.currentSubflowId]
4189
+ );
4190
+ const breadcrumb = (0, import_react21.useMemo)(
4191
+ () => buildSubflowBreadcrumb(graph, drill.currentSubflowId),
4192
+ [graph, drill.currentSubflowId]
4193
+ );
4194
+ const positioned = (0, import_react21.useMemo)(
4195
+ () => layout === "passthrough" ? filteredGraph : layout(filteredGraph),
4196
+ [filteredGraph, layout]
4197
+ );
4198
+ const slice = (0, import_react21.useMemo)(() => {
4199
+ const empty = {
4200
+ doneStageIds: /* @__PURE__ */ new Set(),
4201
+ activeStageId: null,
4202
+ executedStageIds: /* @__PURE__ */ new Set(),
4203
+ executedOrderIds: [],
4204
+ errors: /* @__PURE__ */ new Map()
4205
+ };
4206
+ if (!overlay) return empty;
4207
+ const idx = scrubIndex ?? Math.max(0, overlay.executionOrder.length - 1);
4208
+ const normalized = normalizeSliceLeafIds(sliceOverlay(overlay, idx));
4209
+ return aggregateMountStatus(normalized, graph, drill.currentSubflowId);
4210
+ }, [overlay, scrubIndex, graph, drill.currentSubflowId]);
4211
+ const reactFlowNodes = (0, import_react21.useMemo)(
4212
+ () => positioned.nodes.map(
4213
+ (n) => toStageNodeWithOverlay(
4214
+ n,
4215
+ slice.doneStageIds,
4216
+ slice.activeStageId,
4217
+ slice.errors.get(n.id),
4218
+ slice.executedOrderIds
4219
+ )
4220
+ ),
4221
+ [positioned.nodes, slice]
4222
+ );
4223
+ const reactFlowEdges = (0, import_react21.useMemo)(
4224
+ () => positioned.edges.map(
4225
+ (e) => styleEdgeWithOverlay(e, slice.doneStageIds, slice.activeStageId, colors)
4226
+ ),
4227
+ [positioned.edges, slice, colors]
4228
+ );
4229
+ const handleNodeClick = (0, import_react21.useCallback)(
4051
4230
  (_, node) => {
4052
- if (!onNodeClick) return;
4053
- onNodeClick(node.id);
4231
+ const data = node.data ?? {};
4232
+ if (data.isSubflow && data.subflowId) {
4233
+ drill.drillInto(data.subflowId);
4234
+ }
4235
+ onNodeClick?.(node.id);
4054
4236
  },
4055
- [onNodeClick]
4237
+ [drill, onNodeClick]
4056
4238
  );
4057
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
4239
+ const wrapperRef = (0, import_react21.useRef)(null);
4240
+ const [rfInstance, setRfInstance] = (0, import_react21.useState)(null);
4241
+ useChartAutoRefit(wrapperRef, rfInstance);
4242
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
4058
4243
  "div",
4059
4244
  {
4245
+ ref: wrapperRef,
4060
4246
  className,
4061
- style: { width: "100%", height: "100%", ...style },
4062
- "data-fp": "traced-flowchart",
4063
- children: /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
4064
- import_react19.ReactFlow,
4065
- {
4066
- nodes,
4067
- edges,
4068
- onNodesChange,
4069
- onEdgesChange,
4070
- onNodeClick: handleNodeClick,
4071
- nodeTypes,
4072
- fitView: true,
4073
- fitViewOptions: { padding: 0.3 },
4074
- proOptions: { hideAttribution: true },
4075
- panOnDrag: false,
4076
- zoomOnScroll: false,
4077
- zoomOnPinch: false,
4078
- zoomOnDoubleClick: false,
4079
- preventScrolling: false,
4080
- nodesDraggable: false,
4081
- nodesConnectable: false,
4082
- elementsSelectable: !!onNodeClick,
4083
- children: [
4084
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(FitViewOnResize, {}),
4085
- !unstyled && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_react19.Background, { variant: import_react19.BackgroundVariant.Dots, gap: 16, size: 1 })
4086
- ]
4087
- }
4088
- )
4247
+ style: {
4248
+ width: "100%",
4249
+ height: "100%",
4250
+ minHeight: 300,
4251
+ display: "flex",
4252
+ flexDirection: "column",
4253
+ ...style
4254
+ },
4255
+ children: [
4256
+ breadcrumb.length > 1 && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
4257
+ SubflowBreadcrumbBar,
4258
+ {
4259
+ entries: breadcrumb,
4260
+ onNavigate: drill.setCurrentSubflowId
4261
+ }
4262
+ ),
4263
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { style: { flex: 1, minHeight: 0 }, children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
4264
+ import_react22.ReactFlow,
4265
+ {
4266
+ nodes: reactFlowNodes,
4267
+ edges: reactFlowEdges,
4268
+ nodeTypes,
4269
+ onNodeClick: handleNodeClick,
4270
+ onInit: setRfInstance,
4271
+ fitView: true,
4272
+ proOptions: { hideAttribution: true },
4273
+ children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_react22.Background, { variant: import_react22.BackgroundVariant.Dots, gap: 20, size: 1 })
4274
+ }
4275
+ ) })
4276
+ ]
4089
4277
  }
4090
4278
  );
4091
4279
  }
4092
4280
 
4093
4281
  // src/components/InspectorPanel/InspectorPanel.tsx
4094
- var import_react21 = require("react");
4282
+ var import_react24 = require("react");
4095
4283
 
4096
4284
  // src/components/DataTracePanel/DataTracePanel.tsx
4097
- var import_react20 = require("react");
4098
- var import_jsx_runtime18 = require("react/jsx-runtime");
4099
- var DataTracePanel = (0, import_react20.memo)(function DataTracePanel2({
4285
+ var import_react23 = require("react");
4286
+ var import_jsx_runtime20 = require("react/jsx-runtime");
4287
+ var DataTracePanel = (0, import_react23.memo)(function DataTracePanel2({
4100
4288
  frames,
4101
4289
  selectedStageId,
4102
4290
  onFrameClick,
4103
4291
  fromStageName
4104
4292
  }) {
4105
4293
  if (frames.length === 0) {
4106
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { style: { padding: "14px 14px 12px", fontSize: 13, lineHeight: 1.55 }, children: [
4107
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
4294
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { style: { padding: "14px 14px 12px", fontSize: 13, lineHeight: 1.55 }, children: [
4295
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
4108
4296
  "div",
4109
4297
  {
4110
4298
  style: {
@@ -4118,13 +4306,13 @@ var DataTracePanel = (0, import_react20.memo)(function DataTracePanel2({
4118
4306
  children: "Backward causal chain"
4119
4307
  }
4120
4308
  ),
4121
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { style: { color: theme.textSecondary, marginBottom: 10 }, children: "Trace any value back to the stage that created it \u2014 and everything upstream that influenced it." }),
4122
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { style: { color: theme.textMuted, fontSize: 12 }, children: "Select a stage above to see its dependency chain." })
4309
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { style: { color: theme.textSecondary, marginBottom: 10 }, children: "Trace any value back to the stage that created it \u2014 and everything upstream that influenced it." }),
4310
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { style: { color: theme.textMuted, fontSize: 12 }, children: "Select a stage above to see its dependency chain." })
4123
4311
  ] });
4124
4312
  }
4125
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { style: { padding: "8px 0", fontSize: 13 }, children: [
4126
- fromStageName && /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { style: { padding: "4px 12px 8px" }, children: [
4127
- /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
4313
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { style: { padding: "8px 0", fontSize: 13 }, children: [
4314
+ fromStageName && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { style: { padding: "4px 12px 8px" }, children: [
4315
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
4128
4316
  "div",
4129
4317
  {
4130
4318
  style: {
@@ -4140,7 +4328,7 @@ var DataTracePanel = (0, import_react20.memo)(function DataTracePanel2({
4140
4328
  ]
4141
4329
  }
4142
4330
  ),
4143
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
4331
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
4144
4332
  "div",
4145
4333
  {
4146
4334
  style: {
@@ -4153,7 +4341,7 @@ var DataTracePanel = (0, import_react20.memo)(function DataTracePanel2({
4153
4341
  }
4154
4342
  )
4155
4343
  ] }),
4156
- frames.map((frame, i) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
4344
+ frames.map((frame, i) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
4157
4345
  DataTraceFrame,
4158
4346
  {
4159
4347
  frame,
@@ -4166,14 +4354,14 @@ var DataTracePanel = (0, import_react20.memo)(function DataTracePanel2({
4166
4354
  ))
4167
4355
  ] });
4168
4356
  });
4169
- var DataTraceFrame = (0, import_react20.memo)(function DataTraceFrame2({
4357
+ var DataTraceFrame = (0, import_react23.memo)(function DataTraceFrame2({
4170
4358
  frame,
4171
4359
  isFirst,
4172
4360
  isLast,
4173
4361
  isSelected,
4174
4362
  onClick
4175
4363
  }) {
4176
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
4364
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
4177
4365
  "button",
4178
4366
  {
4179
4367
  onClick: () => onClick?.(frame.runtimeStageId),
@@ -4190,9 +4378,9 @@ var DataTraceFrame = (0, import_react20.memo)(function DataTraceFrame2({
4190
4378
  fontSize: 13
4191
4379
  },
4192
4380
  children: [
4193
- /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 6 }, children: [
4194
- !isFirst && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { style: { color: theme.textMuted, fontSize: 11 }, children: "\u2191" }),
4195
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
4381
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 6 }, children: [
4382
+ !isFirst && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { style: { color: theme.textMuted, fontSize: 11 }, children: "\u2191" }),
4383
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
4196
4384
  "span",
4197
4385
  {
4198
4386
  style: {
@@ -4202,7 +4390,7 @@ var DataTraceFrame = (0, import_react20.memo)(function DataTraceFrame2({
4202
4390
  children: frame.stageName
4203
4391
  }
4204
4392
  ),
4205
- isLast && !isFirst && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
4393
+ isLast && !isFirst && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
4206
4394
  "span",
4207
4395
  {
4208
4396
  style: {
@@ -4214,7 +4402,7 @@ var DataTraceFrame = (0, import_react20.memo)(function DataTraceFrame2({
4214
4402
  }
4215
4403
  )
4216
4404
  ] }),
4217
- frame.keysWritten.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
4405
+ frame.keysWritten.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
4218
4406
  "div",
4219
4407
  {
4220
4408
  style: {
@@ -4226,11 +4414,11 @@ var DataTraceFrame = (0, import_react20.memo)(function DataTraceFrame2({
4226
4414
  children: [
4227
4415
  "wrote:",
4228
4416
  " ",
4229
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { style: { color: theme.textSecondary }, children: frame.keysWritten.join(", ") })
4417
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { style: { color: theme.textSecondary }, children: frame.keysWritten.join(", ") })
4230
4418
  ]
4231
4419
  }
4232
4420
  ),
4233
- frame.linkedBy && /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
4421
+ frame.linkedBy && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
4234
4422
  "div",
4235
4423
  {
4236
4424
  style: {
@@ -4251,17 +4439,17 @@ var DataTraceFrame = (0, import_react20.memo)(function DataTraceFrame2({
4251
4439
  });
4252
4440
 
4253
4441
  // src/components/InspectorPanel/InspectorPanel.tsx
4254
- var import_jsx_runtime19 = require("react/jsx-runtime");
4255
- var InspectorPanel = (0, import_react21.memo)(function InspectorPanel2({
4442
+ var import_jsx_runtime21 = require("react/jsx-runtime");
4443
+ var InspectorPanel = (0, import_react24.memo)(function InspectorPanel2({
4256
4444
  snapshots,
4257
4445
  selectedIndex,
4258
4446
  dataTraceFrames,
4259
4447
  selectedStageId,
4260
4448
  onNavigateToStage
4261
4449
  }) {
4262
- const [tab, setTab] = (0, import_react21.useState)("state");
4450
+ const [tab, setTab] = (0, import_react24.useState)("state");
4263
4451
  const currentSnapshot = snapshots[selectedIndex];
4264
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
4452
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
4265
4453
  "div",
4266
4454
  {
4267
4455
  style: {
@@ -4271,7 +4459,7 @@ var InspectorPanel = (0, import_react21.memo)(function InspectorPanel2({
4271
4459
  overflow: "hidden"
4272
4460
  },
4273
4461
  children: [
4274
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
4462
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
4275
4463
  "div",
4276
4464
  {
4277
4465
  style: {
@@ -4280,7 +4468,7 @@ var InspectorPanel = (0, import_react21.memo)(function InspectorPanel2({
4280
4468
  flexShrink: 0
4281
4469
  },
4282
4470
  children: [
4283
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
4471
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
4284
4472
  TabButton,
4285
4473
  {
4286
4474
  active: tab === "state",
@@ -4288,7 +4476,7 @@ var InspectorPanel = (0, import_react21.memo)(function InspectorPanel2({
4288
4476
  label: "State"
4289
4477
  }
4290
4478
  ),
4291
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
4479
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
4292
4480
  TabButton,
4293
4481
  {
4294
4482
  active: tab === "trace",
@@ -4300,15 +4488,15 @@ var InspectorPanel = (0, import_react21.memo)(function InspectorPanel2({
4300
4488
  ]
4301
4489
  }
4302
4490
  ),
4303
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { style: { flex: 1, overflow: "auto" }, children: [
4304
- tab === "state" && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
4491
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { style: { flex: 1, overflow: "auto" }, children: [
4492
+ tab === "state" && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
4305
4493
  MemoryPanel,
4306
4494
  {
4307
4495
  snapshots,
4308
4496
  selectedIndex
4309
4497
  }
4310
4498
  ),
4311
- tab === "trace" && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
4499
+ tab === "trace" && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
4312
4500
  DataTracePanel,
4313
4501
  {
4314
4502
  frames: dataTraceFrames,
@@ -4328,7 +4516,7 @@ function TabButton({
4328
4516
  label,
4329
4517
  badge
4330
4518
  }) {
4331
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
4519
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
4332
4520
  "button",
4333
4521
  {
4334
4522
  onClick,
@@ -4347,7 +4535,7 @@ function TabButton({
4347
4535
  },
4348
4536
  children: [
4349
4537
  label,
4350
- badge && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
4538
+ badge && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
4351
4539
  "span",
4352
4540
  {
4353
4541
  style: {
@@ -4367,28 +4555,28 @@ function TabButton({
4367
4555
  }
4368
4556
 
4369
4557
  // src/components/InsightPanel/InsightPanel.tsx
4370
- var import_react22 = require("react");
4371
- var import_jsx_runtime20 = require("react/jsx-runtime");
4372
- var InsightPanel = (0, import_react22.memo)(function InsightPanel2({
4558
+ var import_react25 = require("react");
4559
+ var import_jsx_runtime22 = require("react/jsx-runtime");
4560
+ var InsightPanel = (0, import_react25.memo)(function InsightPanel2({
4373
4561
  insights,
4374
4562
  expandedId,
4375
4563
  mode
4376
4564
  }) {
4377
4565
  if (insights.length === 0) {
4378
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { style: { padding: 12, color: theme.textMuted, fontSize: 13 }, children: "No insights available. Attach recorders to see data." });
4566
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { style: { padding: 12, color: theme.textMuted, fontSize: 13 }, children: "No insights available. Attach recorders to see data." });
4379
4567
  }
4380
4568
  if (mode === "grid") {
4381
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(InsightGrid, { insights });
4569
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(InsightGrid, { insights });
4382
4570
  }
4383
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(InsightTabs, { insights, defaultId: expandedId });
4571
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(InsightTabs, { insights, defaultId: expandedId });
4384
4572
  });
4385
- var InsightTabs = (0, import_react22.memo)(function InsightTabs2({
4573
+ var InsightTabs = (0, import_react25.memo)(function InsightTabs2({
4386
4574
  insights,
4387
4575
  defaultId
4388
4576
  }) {
4389
- const [activeId, setActiveId] = (0, import_react22.useState)(defaultId ?? insights[0]?.id);
4577
+ const [activeId, setActiveId] = (0, import_react25.useState)(defaultId ?? insights[0]?.id);
4390
4578
  const active = insights.find((i) => i.id === activeId) ?? insights[0];
4391
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
4579
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
4392
4580
  "div",
4393
4581
  {
4394
4582
  style: {
@@ -4398,7 +4586,7 @@ var InsightTabs = (0, import_react22.memo)(function InsightTabs2({
4398
4586
  overflow: "hidden"
4399
4587
  },
4400
4588
  children: [
4401
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
4589
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
4402
4590
  "div",
4403
4591
  {
4404
4592
  style: {
@@ -4407,7 +4595,7 @@ var InsightTabs = (0, import_react22.memo)(function InsightTabs2({
4407
4595
  flexShrink: 0,
4408
4596
  overflowX: "auto"
4409
4597
  },
4410
- children: insights.map((insight) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
4598
+ children: insights.map((insight) => /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
4411
4599
  "button",
4412
4600
  {
4413
4601
  onClick: () => setActiveId(insight.id),
@@ -4428,16 +4616,16 @@ var InsightTabs = (0, import_react22.memo)(function InsightTabs2({
4428
4616
  ))
4429
4617
  }
4430
4618
  ),
4431
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { style: { flex: 1, overflow: "auto" }, children: active?.render() })
4619
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { style: { flex: 1, overflow: "auto" }, children: active?.render() })
4432
4620
  ]
4433
4621
  }
4434
4622
  );
4435
4623
  });
4436
- var InsightGrid = (0, import_react22.memo)(function InsightGrid2({
4624
+ var InsightGrid = (0, import_react25.memo)(function InsightGrid2({
4437
4625
  insights
4438
4626
  }) {
4439
4627
  const cols = insights.length <= 2 ? 1 : 2;
4440
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
4628
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
4441
4629
  "div",
4442
4630
  {
4443
4631
  style: {
@@ -4448,7 +4636,7 @@ var InsightGrid = (0, import_react22.memo)(function InsightGrid2({
4448
4636
  gap: 1,
4449
4637
  background: theme.border
4450
4638
  },
4451
- children: insights.map((insight) => /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
4639
+ children: insights.map((insight) => /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
4452
4640
  "div",
4453
4641
  {
4454
4642
  style: {
@@ -4458,7 +4646,7 @@ var InsightGrid = (0, import_react22.memo)(function InsightGrid2({
4458
4646
  overflow: "hidden"
4459
4647
  },
4460
4648
  children: [
4461
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
4649
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
4462
4650
  "div",
4463
4651
  {
4464
4652
  style: {
@@ -4473,7 +4661,7 @@ var InsightGrid = (0, import_react22.memo)(function InsightGrid2({
4473
4661
  },
4474
4662
  children: [
4475
4663
  insight.name,
4476
- insight.summary && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
4664
+ insight.summary && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
4477
4665
  "span",
4478
4666
  {
4479
4667
  style: {
@@ -4488,7 +4676,7 @@ var InsightGrid = (0, import_react22.memo)(function InsightGrid2({
4488
4676
  ]
4489
4677
  }
4490
4678
  ),
4491
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { style: { flex: 1, overflow: "auto" }, children: insight.render() })
4679
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { style: { flex: 1, overflow: "auto" }, children: insight.render() })
4492
4680
  ]
4493
4681
  },
4494
4682
  insight.id
@@ -4498,17 +4686,17 @@ var InsightGrid = (0, import_react22.memo)(function InsightGrid2({
4498
4686
  });
4499
4687
 
4500
4688
  // src/components/CompactTimeline/CompactTimeline.tsx
4501
- var import_react23 = require("react");
4502
- var import_jsx_runtime21 = require("react/jsx-runtime");
4503
- var CompactTimeline = (0, import_react23.memo)(function CompactTimeline2({
4689
+ var import_react26 = require("react");
4690
+ var import_jsx_runtime23 = require("react/jsx-runtime");
4691
+ var CompactTimeline = (0, import_react26.memo)(function CompactTimeline2({
4504
4692
  snapshots,
4505
4693
  selectedIndex,
4506
4694
  defaultExpanded = false
4507
4695
  }) {
4508
- const [expanded, setExpanded] = (0, import_react23.useState)(defaultExpanded);
4696
+ const [expanded, setExpanded] = (0, import_react26.useState)(defaultExpanded);
4509
4697
  if (snapshots.length === 0) return null;
4510
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { style: { borderTop: `1px solid ${theme.border}` }, children: [
4511
- /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
4698
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { style: { borderTop: `1px solid ${theme.border}` }, children: [
4699
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
4512
4700
  "button",
4513
4701
  {
4514
4702
  onClick: () => setExpanded((e) => !e),
@@ -4528,13 +4716,13 @@ var CompactTimeline = (0, import_react23.memo)(function CompactTimeline2({
4528
4716
  letterSpacing: "0.5px"
4529
4717
  },
4530
4718
  children: [
4531
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { style: { fontSize: 10 }, children: expanded ? "\u25BC" : "\u25B8" }),
4719
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { style: { fontSize: 10 }, children: expanded ? "\u25BC" : "\u25B8" }),
4532
4720
  "Timeline",
4533
- /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("span", { style: { fontWeight: 400, fontSize: 10 }, children: [
4721
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("span", { style: { fontWeight: 400, fontSize: 10 }, children: [
4534
4722
  snapshots.length,
4535
4723
  " stages"
4536
4724
  ] }),
4537
- !expanded && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
4725
+ !expanded && /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
4538
4726
  "div",
4539
4727
  {
4540
4728
  style: {
@@ -4545,7 +4733,7 @@ var CompactTimeline = (0, import_react23.memo)(function CompactTimeline2({
4545
4733
  marginLeft: 8
4546
4734
  },
4547
4735
  children: [
4548
- snapshots.map((snap, i) => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
4736
+ snapshots.map((snap, i) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
4549
4737
  "div",
4550
4738
  {
4551
4739
  style: {
@@ -4560,7 +4748,7 @@ var CompactTimeline = (0, import_react23.memo)(function CompactTimeline2({
4560
4748
  },
4561
4749
  i
4562
4750
  )),
4563
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
4751
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
4564
4752
  "div",
4565
4753
  {
4566
4754
  style: {
@@ -4578,7 +4766,7 @@ var CompactTimeline = (0, import_react23.memo)(function CompactTimeline2({
4578
4766
  ]
4579
4767
  }
4580
4768
  ),
4581
- expanded && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { style: { padding: "0 12px 8px" }, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
4769
+ expanded && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { style: { padding: "0 12px 8px" }, children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
4582
4770
  GanttTimeline,
4583
4771
  {
4584
4772
  snapshots,
@@ -4589,21 +4777,21 @@ var CompactTimeline = (0, import_react23.memo)(function CompactTimeline2({
4589
4777
  });
4590
4778
 
4591
4779
  // src/components/ExplainableShell/ExplainableShell.tsx
4592
- var import_jsx_runtime22 = require("react/jsx-runtime");
4593
- var HLinePill = (0, import_react24.memo)(function HLinePill2({
4780
+ var import_jsx_runtime24 = require("react/jsx-runtime");
4781
+ var HLinePill = (0, import_react27.memo)(function HLinePill2({
4594
4782
  label,
4595
4783
  detail,
4596
4784
  expanded,
4597
4785
  onClick
4598
4786
  }) {
4599
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { style: {
4787
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { style: {
4600
4788
  display: "flex",
4601
4789
  alignItems: "center",
4602
4790
  gap: 0,
4603
4791
  padding: "0"
4604
4792
  }, children: [
4605
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { style: { flex: 1, height: 1, background: theme.border } }),
4606
- /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
4793
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { style: { flex: 1, height: 1, background: theme.border } }),
4794
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
4607
4795
  "button",
4608
4796
  {
4609
4797
  onClick,
@@ -4627,31 +4815,31 @@ var HLinePill = (0, import_react24.memo)(function HLinePill2({
4627
4815
  transition: "color 0.15s ease"
4628
4816
  },
4629
4817
  children: [
4630
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { style: { fontSize: 7 }, children: expanded ? "\u25BC" : "\u25B6" }),
4818
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { style: { fontSize: 7 }, children: expanded ? "\u25BC" : "\u25B6" }),
4631
4819
  label,
4632
- detail && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { style: { fontWeight: 400, opacity: 0.5, fontSize: 9 }, children: detail })
4820
+ detail && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { style: { fontWeight: 400, opacity: 0.5, fontSize: 9 }, children: detail })
4633
4821
  ]
4634
4822
  }
4635
4823
  ),
4636
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { style: { flex: 1, height: 1, background: theme.border } })
4824
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { style: { flex: 1, height: 1, background: theme.border } })
4637
4825
  ] });
4638
4826
  });
4639
- var VLinePill = (0, import_react24.memo)(function VLinePill2({
4827
+ var VLinePill = (0, import_react27.memo)(function VLinePill2({
4640
4828
  label,
4641
4829
  expanded,
4642
4830
  side = "right",
4643
4831
  onClick
4644
4832
  }) {
4645
4833
  const arrow = side === "right" ? expanded ? "\u25B6" : "\u25C0" : expanded ? "\u25C0" : "\u25B6";
4646
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { style: {
4834
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { style: {
4647
4835
  display: "flex",
4648
4836
  flexDirection: "column",
4649
4837
  alignItems: "center",
4650
4838
  gap: 0,
4651
4839
  padding: "0"
4652
4840
  }, children: [
4653
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { style: { flex: 1, width: 1, background: theme.border } }),
4654
- /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
4841
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { style: { flex: 1, width: 1, background: theme.border } }),
4842
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
4655
4843
  "button",
4656
4844
  {
4657
4845
  onClick,
@@ -4676,12 +4864,12 @@ var VLinePill = (0, import_react24.memo)(function VLinePill2({
4676
4864
  transition: "color 0.15s ease"
4677
4865
  },
4678
4866
  children: [
4679
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { style: { fontSize: 7, writingMode: "horizontal-tb" }, children: arrow }),
4867
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { style: { fontSize: 7, writingMode: "horizontal-tb" }, children: arrow }),
4680
4868
  label
4681
4869
  ]
4682
4870
  }
4683
4871
  ),
4684
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { style: { flex: 1, width: 1, background: theme.border } })
4872
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { style: { flex: 1, width: 1, background: theme.border } })
4685
4873
  ] });
4686
4874
  });
4687
4875
  function detectKeyedSteps(data) {
@@ -4718,9 +4906,9 @@ function KeyedRecorderView({
4718
4906
  snapshots,
4719
4907
  selectedIndex
4720
4908
  }) {
4721
- const [showAggregate, setShowAggregate] = (0, import_react24.useState)(false);
4722
- const detected = (0, import_react24.useMemo)(() => detectKeyedSteps(data), [data]);
4723
- const visibleKeys = (0, import_react24.useMemo)(() => {
4909
+ const [showAggregate, setShowAggregate] = (0, import_react27.useState)(false);
4910
+ const detected = (0, import_react27.useMemo)(() => detectKeyedSteps(data), [data]);
4911
+ const visibleKeys = (0, import_react27.useMemo)(() => {
4724
4912
  const keys = /* @__PURE__ */ new Set();
4725
4913
  for (let i = 0; i <= selectedIndex && i < snapshots.length; i++) {
4726
4914
  const snap = snapshots[i];
@@ -4735,7 +4923,7 @@ function KeyedRecorderView({
4735
4923
  }, [snapshots, selectedIndex, detected?.keyType]);
4736
4924
  const isAtEnd = selectedIndex >= snapshots.length - 1;
4737
4925
  if (!detected) {
4738
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { style: { padding: 12, fontFamily: theme.fontMono, fontSize: 11, whiteSpace: "pre-wrap", overflow: "auto", height: "100%" }, children: typeof data === "string" ? data : JSON.stringify(data, null, 2) });
4926
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { style: { padding: 12, fontFamily: theme.fontMono, fontSize: 11, whiteSpace: "pre-wrap", overflow: "auto", height: "100%" }, children: typeof data === "string" ? data : JSON.stringify(data, null, 2) });
4739
4927
  }
4740
4928
  const steps = detected.steps;
4741
4929
  const hints = extractRenderHints(data);
@@ -4749,13 +4937,13 @@ function KeyedRecorderView({
4749
4937
  }
4750
4938
  }
4751
4939
  const grandTotal = hints?.grandTotal ?? 0;
4752
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { style: { overflow: "auto", height: "100%", display: "flex", flexDirection: "column" }, children: [
4753
- description && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { style: { padding: "6px 12px", fontSize: 11, color: theme.textMuted, fontStyle: "italic", borderBottom: `1px solid ${theme.border}`, flexShrink: 0 }, children: description }),
4754
- /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { style: { padding: 12, flex: 1, overflow: "auto" }, children: [
4940
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { style: { overflow: "auto", height: "100%", display: "flex", flexDirection: "column" }, children: [
4941
+ description && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { style: { padding: "6px 12px", fontSize: 11, color: theme.textMuted, fontStyle: "italic", borderBottom: `1px solid ${theme.border}`, flexShrink: 0 }, children: description }),
4942
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { style: { padding: 12, flex: 1, overflow: "auto" }, children: [
4755
4943
  preferredOperation === "aggregate" ? (
4756
4944
  /* AGGREGATE: collect silently during scrub, button at end to reveal total */
4757
- /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_jsx_runtime22.Fragment, { children: [
4758
- isAtEnd ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { style: { marginBottom: 16 }, children: !showAggregate ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
4945
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(import_jsx_runtime24.Fragment, { children: [
4946
+ isAtEnd ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { style: { marginBottom: 16 }, children: !showAggregate ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
4759
4947
  "button",
4760
4948
  {
4761
4949
  onClick: () => setShowAggregate(true),
@@ -4773,35 +4961,35 @@ function KeyedRecorderView({
4773
4961
  },
4774
4962
  children: "Aggregate \u2014 Show Grand Total"
4775
4963
  }
4776
- ) : /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { style: { padding: "14px 16px", background: `color-mix(in srgb, ${theme.success} 12%, transparent)`, borderRadius: 8, border: `1px solid ${theme.success}44` }, children: [
4777
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { style: { fontSize: 10, color: theme.textMuted, textTransform: "uppercase", letterSpacing: "0.08em", marginBottom: 6, fontWeight: 600 }, children: "Aggregate \u2014 grand total" }),
4778
- numFieldKey && /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { style: { fontSize: 26, fontWeight: 700, color: theme.success }, children: [
4964
+ ) : /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { style: { padding: "14px 16px", background: `color-mix(in srgb, ${theme.success} 12%, transparent)`, borderRadius: 8, border: `1px solid ${theme.success}44` }, children: [
4965
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { style: { fontSize: 10, color: theme.textMuted, textTransform: "uppercase", letterSpacing: "0.08em", marginBottom: 6, fontWeight: 600 }, children: "Aggregate \u2014 grand total" }),
4966
+ numFieldKey && /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { style: { fontSize: 26, fontWeight: 700, color: theme.success }, children: [
4779
4967
  grandTotal < 1 ? grandTotal.toFixed(3) : grandTotal.toFixed(1),
4780
- /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("span", { style: { fontSize: 11, color: theme.textMuted, fontWeight: 400, marginLeft: 8 }, children: [
4968
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("span", { style: { fontSize: 11, color: theme.textMuted, fontWeight: 400, marginLeft: 8 }, children: [
4781
4969
  numFieldKey,
4782
4970
  " \xB7 ",
4783
4971
  allKeys.length,
4784
4972
  " steps"
4785
4973
  ] })
4786
4974
  ] })
4787
- ] }) }) : /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { style: { padding: "10px 14px", background: `color-mix(in srgb, ${theme.textMuted} 6%, transparent)`, borderRadius: 6, marginBottom: 16, border: `1px dashed ${theme.border}` }, children: [
4788
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { style: { fontSize: 10, color: theme.textMuted, textTransform: "uppercase", letterSpacing: "0.08em", fontWeight: 600 }, children: "Collecting data..." }),
4789
- /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { style: { fontSize: 11, color: theme.textMuted, marginTop: 4 }, children: [
4975
+ ] }) }) : /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { style: { padding: "10px 14px", background: `color-mix(in srgb, ${theme.textMuted} 6%, transparent)`, borderRadius: 6, marginBottom: 16, border: `1px dashed ${theme.border}` }, children: [
4976
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { style: { fontSize: 10, color: theme.textMuted, textTransform: "uppercase", letterSpacing: "0.08em", fontWeight: 600 }, children: "Collecting data..." }),
4977
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { style: { fontSize: 11, color: theme.textMuted, marginTop: 4 }, children: [
4790
4978
  visibleEntries.length,
4791
4979
  " of ",
4792
4980
  allKeys.length,
4793
4981
  " steps collected. Scrub to end to aggregate."
4794
4982
  ] })
4795
4983
  ] }),
4796
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { style: { fontSize: 10, color: theme.textMuted, textTransform: "uppercase", letterSpacing: "0.08em", marginBottom: 6, fontWeight: 600 }, children: "Per-step detail" })
4984
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { style: { fontSize: 10, color: theme.textMuted, textTransform: "uppercase", letterSpacing: "0.08em", marginBottom: 6, fontWeight: 600 }, children: "Per-step detail" })
4797
4985
  ] })
4798
4986
  ) : preferredOperation === "accumulate" ? (
4799
4987
  /* ACCUMULATE: running total grows with slider — IS the total at end, no button */
4800
- /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_jsx_runtime22.Fragment, { children: [
4801
- numFieldKey && visibleEntries.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { style: { padding: "10px 14px", background: `color-mix(in srgb, ${theme.primary} 8%, transparent)`, borderRadius: 6, marginBottom: 16 }, children: [
4802
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { style: { fontSize: 10, color: theme.textMuted, textTransform: "uppercase", letterSpacing: "0.08em", marginBottom: 4, fontWeight: 600 }, children: "Accumulate \u2014 running total up to this step" }),
4803
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { style: { fontWeight: 700, fontSize: 18, color: theme.primary }, children: runningTotal < 1 ? runningTotal.toFixed(3) : runningTotal.toFixed(1) }),
4804
- /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("span", { style: { color: theme.textMuted, marginLeft: 8, fontSize: 10 }, children: [
4988
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(import_jsx_runtime24.Fragment, { children: [
4989
+ numFieldKey && visibleEntries.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { style: { padding: "10px 14px", background: `color-mix(in srgb, ${theme.primary} 8%, transparent)`, borderRadius: 6, marginBottom: 16 }, children: [
4990
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { style: { fontSize: 10, color: theme.textMuted, textTransform: "uppercase", letterSpacing: "0.08em", marginBottom: 4, fontWeight: 600 }, children: "Accumulate \u2014 running total up to this step" }),
4991
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { style: { fontWeight: 700, fontSize: 18, color: theme.primary }, children: runningTotal < 1 ? runningTotal.toFixed(3) : runningTotal.toFixed(1) }),
4992
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("span", { style: { color: theme.textMuted, marginLeft: 8, fontSize: 10 }, children: [
4805
4993
  numFieldKey,
4806
4994
  " \xB7 ",
4807
4995
  visibleEntries.length,
@@ -4810,31 +4998,30 @@ function KeyedRecorderView({
4810
4998
  " steps"
4811
4999
  ] })
4812
5000
  ] }),
4813
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { style: { fontSize: 10, color: theme.textMuted, textTransform: "uppercase", letterSpacing: "0.08em", marginBottom: 6, fontWeight: 600 }, children: "Per-step detail" })
5001
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { style: { fontSize: 10, color: theme.textMuted, textTransform: "uppercase", letterSpacing: "0.08em", marginBottom: 6, fontWeight: 600 }, children: "Per-step detail" })
4814
5002
  ] })
4815
5003
  ) : (
4816
5004
  /* TRANSLATE: per-step entries prominent, no totals */
4817
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { style: { fontSize: 10, color: theme.textMuted, textTransform: "uppercase", letterSpacing: "0.08em", marginBottom: 6, fontWeight: 600 }, children: "Translate \u2014 per-step detail" })
5005
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { style: { fontSize: 10, color: theme.textMuted, textTransform: "uppercase", letterSpacing: "0.08em", marginBottom: 6, fontWeight: 600 }, children: "Translate \u2014 per-step detail" })
4818
5006
  ),
4819
5007
  visibleEntries.map((key) => {
4820
5008
  const entry = steps[key];
4821
5009
  const label = entry.stageName ?? key;
4822
5010
  const numVal = numFieldKey ? entry[numFieldKey] : void 0;
4823
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { style: { display: "flex", alignItems: "center", padding: "4px 0", fontSize: 12, fontFamily: theme.fontMono, borderBottom: `1px solid ${theme.border}22` }, children: [
4824
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { style: { color: theme.textMuted, width: 140, flexShrink: 0, fontSize: 10 }, children: key }),
4825
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { style: { fontWeight: 600, flex: 1 }, children: label }),
4826
- numVal !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { style: { color: theme.primary, fontWeight: 700, marginLeft: 8 }, children: numVal < 1 ? numVal.toFixed(3) : numVal.toFixed(1) })
5011
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { style: { display: "flex", alignItems: "center", padding: "4px 0", fontSize: 12, fontFamily: theme.fontMono, borderBottom: `1px solid ${theme.border}22` }, children: [
5012
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { style: { color: theme.textMuted, width: 140, flexShrink: 0, fontSize: 10 }, children: key }),
5013
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { style: { fontWeight: 600, flex: 1 }, children: label }),
5014
+ numVal !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { style: { color: theme.primary, fontWeight: 700, marginLeft: 8 }, children: numVal < 1 ? numVal.toFixed(3) : numVal.toFixed(1) })
4827
5015
  ] }, key);
4828
5016
  }),
4829
- visibleEntries.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { style: { color: theme.textMuted, fontSize: 11, fontStyle: "italic", padding: "8px 0" }, children: "Scrub the slider to reveal entries..." })
5017
+ visibleEntries.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { style: { color: theme.textMuted, fontSize: 11, fontStyle: "italic", padding: "8px 0" }, children: "Scrub the slider to reveal entries..." })
4830
5018
  ] })
4831
5019
  ] });
4832
5020
  }
4833
- var DetailsContent = (0, import_react24.memo)(function DetailsContent2({
5021
+ var DetailsContent = (0, import_react27.memo)(function DetailsContent2({
4834
5022
  snapshots,
4835
5023
  selectedIndex,
4836
5024
  narrativeEntries,
4837
- narrative,
4838
5025
  size,
4839
5026
  fillHeight,
4840
5027
  extraViews
@@ -4843,27 +5030,27 @@ var DetailsContent = (0, import_react24.memo)(function DetailsContent2({
4843
5030
  {
4844
5031
  id: "memory",
4845
5032
  name: "Memory",
4846
- render: ({ snapshots: snaps, selectedIndex: idx }) => /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(MemoryPanel, { snapshots: snaps, selectedIndex: idx, size, style: fillHeight ? { height: "100%" } : void 0 })
5033
+ render: ({ snapshots: snaps, selectedIndex: idx }) => /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(MemoryPanel, { snapshots: snaps, selectedIndex: idx, size, style: fillHeight ? { height: "100%" } : void 0 })
4847
5034
  },
4848
5035
  {
4849
5036
  id: "narrative",
4850
5037
  name: "Narrative",
4851
- render: ({ snapshots: snaps, selectedIndex: idx }) => /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(NarrativePanel, { snapshots: snaps, selectedIndex: idx, narrativeEntries, narrative, size, style: fillHeight ? { height: "100%" } : void 0 })
5038
+ render: ({ snapshots: snaps, selectedIndex: idx }) => /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(NarrativePanel, { snapshots: snaps, selectedIndex: idx, narrativeEntries, size, style: fillHeight ? { height: "100%" } : void 0 })
4852
5039
  }
4853
5040
  ];
4854
5041
  const allViews = [...builtInViews, ...extraViews ?? []];
4855
- const [activeViewId, setActiveViewId] = (0, import_react24.useState)(allViews[0]?.id ?? "memory");
5042
+ const [activeViewId, setActiveViewId] = (0, import_react27.useState)(allViews[0]?.id ?? "memory");
4856
5043
  const viewIds = allViews.map((v2) => v2.id).join(",");
4857
- (0, import_react24.useEffect)(() => {
5044
+ (0, import_react27.useEffect)(() => {
4858
5045
  if (!allViews.find((v2) => v2.id === activeViewId)) {
4859
5046
  setActiveViewId(allViews[0]?.id ?? "memory");
4860
5047
  }
4861
5048
  }, [viewIds]);
4862
5049
  const activeView = allViews.find((v2) => v2.id === activeViewId) ?? allViews[0];
4863
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { style: { flex: 1, display: "flex", flexDirection: "column", overflow: "hidden" }, children: [
4864
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { style: { display: "flex", borderBottom: `1px solid ${theme.border}`, flexShrink: 0, overflowX: "auto" }, children: allViews.map((view) => {
5050
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { style: { flex: 1, display: "flex", flexDirection: "column", overflow: "hidden" }, children: [
5051
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { style: { display: "flex", borderBottom: `1px solid ${theme.border}`, flexShrink: 0, overflowX: "auto" }, children: allViews.map((view) => {
4865
5052
  const active = view.id === activeViewId;
4866
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
5053
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
4867
5054
  "button",
4868
5055
  {
4869
5056
  onClick: () => setActiveViewId(view.id),
@@ -4887,7 +5074,7 @@ var DetailsContent = (0, import_react24.memo)(function DetailsContent2({
4887
5074
  view.id
4888
5075
  );
4889
5076
  }) }),
4890
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { style: { flex: 1, overflow: "auto" }, children: activeView?.render({ snapshots, selectedIndex }) })
5077
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { style: { flex: 1, overflow: "auto" }, children: activeView?.render({ snapshots, selectedIndex }) })
4891
5078
  ] });
4892
5079
  });
4893
5080
  function resolveSubflowLevel(parentSpec, parentSnapshots, subflowNodeName, narrativeEntries) {
@@ -4961,7 +5148,7 @@ function buildDataTrace(commitLog, targetRuntimeStageId, maxDepth = 10) {
4961
5148
  }
4962
5149
  return frames;
4963
5150
  }
4964
- var RightPanel = (0, import_react24.memo)(function RightPanel2({
5151
+ var RightPanel = (0, import_react27.memo)(function RightPanel2({
4965
5152
  mode,
4966
5153
  onModeChange,
4967
5154
  snapshots,
@@ -4971,19 +5158,18 @@ var RightPanel = (0, import_react24.memo)(function RightPanel2({
4971
5158
  activeTab,
4972
5159
  allTabs,
4973
5160
  activeNarrativeEntries,
4974
- activeNarrative,
4975
5161
  recorderViews,
4976
5162
  autoRecorderViews,
4977
5163
  size,
4978
5164
  onNavigateToStage
4979
5165
  }) {
4980
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_jsx_runtime22.Fragment, { children: [
4981
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { style: {
5166
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(import_jsx_runtime24.Fragment, { children: [
5167
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { style: {
4982
5168
  display: "flex",
4983
5169
  borderBottom: `1px solid ${theme.border}`,
4984
5170
  flexShrink: 0,
4985
5171
  background: theme.bgSecondary
4986
- }, children: ["insights", "what"].map((m) => /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
5172
+ }, children: ["insights", "what"].map((m) => /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
4987
5173
  "button",
4988
5174
  {
4989
5175
  onClick: () => onModeChange(m),
@@ -5005,7 +5191,7 @@ var RightPanel = (0, import_react24.memo)(function RightPanel2({
5005
5191
  },
5006
5192
  m
5007
5193
  )) }),
5008
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { style: { flex: 1, overflow: "hidden" }, children: mode === "insights" ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
5194
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { style: { flex: 1, overflow: "hidden" }, children: mode === "insights" ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
5009
5195
  InsightPanel,
5010
5196
  {
5011
5197
  mode: "tabs",
@@ -5014,16 +5200,16 @@ var RightPanel = (0, import_react24.memo)(function RightPanel2({
5014
5200
  id: tab.id,
5015
5201
  name: insightName(tab.name),
5016
5202
  render: () => {
5017
- if (tab.id === "narrative") return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(NarrativePanel, { snapshots, selectedIndex, narrativeEntries: activeNarrativeEntries, narrative: activeNarrative, runtimeSnapshot, spec, size, style: { height: "100%" } });
5203
+ if (tab.id === "narrative") return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(NarrativePanel, { snapshots, selectedIndex, narrativeEntries: activeNarrativeEntries, runtimeSnapshot, spec, size, style: { height: "100%" } });
5018
5204
  const customView = recorderViews?.find((v2) => v2.id === tab.id);
5019
5205
  if (customView?.render) return customView.render({ snapshots, selectedIndex });
5020
5206
  const autoView = autoRecorderViews.find((v2) => v2.id === tab.id);
5021
- if (autoView) return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(KeyedRecorderView, { data: autoView.data, description: autoView.description, preferredOperation: autoView.preferredOperation, snapshots, selectedIndex });
5207
+ if (autoView) return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(KeyedRecorderView, { data: autoView.data, description: autoView.description, preferredOperation: autoView.preferredOperation, snapshots, selectedIndex });
5022
5208
  return null;
5023
5209
  }
5024
5210
  }))
5025
5211
  }
5026
- ) : /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
5212
+ ) : /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
5027
5213
  InspectorPanel,
5028
5214
  {
5029
5215
  snapshots,
@@ -5045,17 +5231,6 @@ function insightName(name) {
5045
5231
  };
5046
5232
  return map[name] ?? name;
5047
5233
  }
5048
- function defaultRenderFlowchart({ spec: s, snapshots: snaps, selectedIndex, onNodeClick }) {
5049
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
5050
- TracedFlowchartView,
5051
- {
5052
- spec: s,
5053
- snapshots: snaps,
5054
- snapshotIndex: selectedIndex,
5055
- onNodeClick
5056
- }
5057
- );
5058
- }
5059
5234
  function ExplainableShell({
5060
5235
  snapshots: snapshotsProp,
5061
5236
  runtimeSnapshot,
@@ -5063,7 +5238,6 @@ function ExplainableShell({
5063
5238
  title,
5064
5239
  resultData: resultDataProp,
5065
5240
  logs = [],
5066
- narrative: narrativeProp,
5067
5241
  narrativeEntries,
5068
5242
  tabs = ["result", "explainable"],
5069
5243
  defaultTab,
@@ -5073,36 +5247,58 @@ function ExplainableShell({
5073
5247
  defaultExpanded,
5074
5248
  recorderViews,
5075
5249
  renderFlowchart,
5250
+ showStageId = false,
5251
+ traceGraph,
5252
+ runtimeOverlay,
5076
5253
  size = "default",
5077
5254
  unstyled = false,
5078
5255
  className,
5079
5256
  style
5080
5257
  }) {
5081
- const derivedFromRuntime = (0, import_react24.useMemo)(() => {
5258
+ const derivedFromRuntime = (0, import_react27.useMemo)(() => {
5082
5259
  if (!runtimeSnapshot) return null;
5083
5260
  try {
5084
5261
  const snaps = toVisualizationSnapshots(runtimeSnapshot, narrativeEntries);
5085
- const narr = [];
5086
- for (const snap of snaps) {
5087
- const lines = (snap.narrative ?? "").split("\n").filter(Boolean);
5088
- narr.push(...lines);
5089
- }
5090
- return { snapshots: snaps, resultData: runtimeSnapshot.sharedState, narrative: narr };
5262
+ return { snapshots: snaps, resultData: runtimeSnapshot.sharedState };
5091
5263
  } catch {
5092
5264
  return null;
5093
5265
  }
5094
5266
  }, [runtimeSnapshot, narrativeEntries]);
5095
5267
  const snapshots = snapshotsProp ?? derivedFromRuntime?.snapshots ?? [];
5096
5268
  const resultData = resultDataProp ?? derivedFromRuntime?.resultData ?? null;
5097
- const narrative = narrativeProp ?? derivedFromRuntime?.narrative;
5098
- const effectiveRenderFlowchart = renderFlowchart ?? (spec ? defaultRenderFlowchart : void 0);
5269
+ const tracedFlowRenderer = (0, import_react27.useMemo)(() => {
5270
+ if (!traceGraph) return void 0;
5271
+ return ({ selectedIndex, snapshots: snapshots2, onNodeClick }) => {
5272
+ const activeRsid = snapshots2[selectedIndex]?.runtimeStageId;
5273
+ let overlayIdx = selectedIndex;
5274
+ if (activeRsid && runtimeOverlay) {
5275
+ const i = runtimeOverlay.executionOrder.findIndex(
5276
+ (s) => s.runtimeStageId === activeRsid
5277
+ );
5278
+ if (i >= 0) overlayIdx = i;
5279
+ }
5280
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
5281
+ TracedFlow,
5282
+ {
5283
+ graph: traceGraph,
5284
+ overlay: runtimeOverlay ?? void 0,
5285
+ scrubIndex: overlayIdx,
5286
+ onNodeClick: (stageId) => onNodeClick?.(stageId),
5287
+ onSubflowChange: (mountId) => {
5288
+ if (mountId !== null) onNodeClick?.(mountId);
5289
+ }
5290
+ }
5291
+ );
5292
+ };
5293
+ }, [traceGraph, runtimeOverlay]);
5294
+ const effectiveRenderFlowchart = renderFlowchart ?? tracedFlowRenderer;
5099
5295
  const leftLabel = panelLabels?.topology ?? "Topology";
5100
5296
  const rightLabel = panelLabels?.details ?? "Details";
5101
5297
  const bottomLabel = panelLabels?.timeline ?? "Timeline";
5102
- const shellRef = (0, import_react24.useRef)(null);
5103
- const [isNarrow, setIsNarrow] = (0, import_react24.useState)(false);
5104
- const [isMedium, setIsMedium] = (0, import_react24.useState)(false);
5105
- (0, import_react24.useEffect)(() => {
5298
+ const shellRef = (0, import_react27.useRef)(null);
5299
+ const [isNarrow, setIsNarrow] = (0, import_react27.useState)(false);
5300
+ const [isMedium, setIsMedium] = (0, import_react27.useState)(false);
5301
+ (0, import_react27.useEffect)(() => {
5106
5302
  const el = shellRef.current;
5107
5303
  if (!el) return;
5108
5304
  const ro = new ResizeObserver(([entry]) => {
@@ -5114,14 +5310,14 @@ function ExplainableShell({
5114
5310
  ro.observe(el);
5115
5311
  return () => ro.disconnect();
5116
5312
  }, []);
5117
- const autoRecorderViews = (0, import_react24.useMemo)(() => {
5313
+ const autoRecorderViews = (0, import_react27.useMemo)(() => {
5118
5314
  const recorders = runtimeSnapshot?.recorders;
5119
5315
  if (!recorders?.length) return [];
5120
5316
  const explicitIds = new Set((recorderViews ?? []).map((v2) => v2.id));
5121
5317
  return recorders.filter((r) => !explicitIds.has(r.id)).map((r) => ({ id: r.id, name: r.name, description: r.description, preferredOperation: r.preferredOperation, data: r.data }));
5122
5318
  }, [runtimeSnapshot, recorderViews]);
5123
- const hasNarrative = !!(narrative?.length || narrativeEntries?.length);
5124
- const allTabs = (0, import_react24.useMemo)(() => {
5319
+ const hasNarrative = !!narrativeEntries?.length;
5320
+ const allTabs = (0, import_react27.useMemo)(() => {
5125
5321
  const tabs2 = [
5126
5322
  { id: "result", name: "Result", description: "Final output and console logs" },
5127
5323
  { id: "memory", name: "Memory", description: "Accumulator \u2014 progressive shared state at each stage" }
@@ -5140,38 +5336,38 @@ function ExplainableShell({
5140
5336
  }, [hasNarrative, recorderViews, autoRecorderViews, hideTabsProp]);
5141
5337
  const validTabIds = new Set(allTabs.map((t) => t.id));
5142
5338
  const resolvedDefault = defaultTab && validTabIds.has(defaultTab) ? defaultTab : allTabs[0]?.id ?? "result";
5143
- const [activeTab, setActiveTab] = (0, import_react24.useState)(resolvedDefault);
5144
- const [snapshotIdx, setSnapshotIdx] = (0, import_react24.useState)(0);
5145
- const [drillDownStack, setDrillDownStack] = (0, import_react24.useState)([]);
5146
- const [rightExpanded, setRightExpanded] = (0, import_react24.useState)(defaultExpanded?.details ?? true);
5147
- const [rightPanelMode, setRightPanelMode] = (0, import_react24.useState)("insights");
5148
- const [leftExpanded, setLeftExpanded] = (0, import_react24.useState)(defaultExpanded?.topology ?? false);
5149
- const [timelineExpanded, setTimelineExpanded] = (0, import_react24.useState)(defaultExpanded?.timeline ?? false);
5150
- (0, import_react24.useEffect)(() => {
5339
+ const [activeTab, setActiveTab] = (0, import_react27.useState)(resolvedDefault);
5340
+ const [snapshotIdx, setSnapshotIdx] = (0, import_react27.useState)(0);
5341
+ const [drillDownStack, setDrillDownStack] = (0, import_react27.useState)([]);
5342
+ const [rightExpanded, setRightExpanded] = (0, import_react27.useState)(defaultExpanded?.details ?? true);
5343
+ const [rightPanelMode, setRightPanelMode] = (0, import_react27.useState)("insights");
5344
+ const [leftExpanded, setLeftExpanded] = (0, import_react27.useState)(defaultExpanded?.topology ?? false);
5345
+ const [timelineExpanded, setTimelineExpanded] = (0, import_react27.useState)(defaultExpanded?.timeline ?? false);
5346
+ (0, import_react27.useEffect)(() => {
5151
5347
  if (isNarrow) {
5152
5348
  setLeftExpanded(false);
5153
5349
  setRightExpanded(false);
5154
5350
  setTimelineExpanded(false);
5155
5351
  }
5156
5352
  }, [isNarrow]);
5157
- const triggerReflow = (0, import_react24.useCallback)(() => {
5353
+ const triggerReflow = (0, import_react27.useCallback)(() => {
5158
5354
  requestAnimationFrame(() => window.dispatchEvent(new Event("resize")));
5159
5355
  setTimeout(() => window.dispatchEvent(new Event("resize")), 320);
5160
5356
  }, []);
5161
- const toggleLeft = (0, import_react24.useCallback)((v2) => {
5357
+ const toggleLeft = (0, import_react27.useCallback)((v2) => {
5162
5358
  setLeftExpanded(v2);
5163
5359
  triggerReflow();
5164
5360
  }, [triggerReflow]);
5165
- const toggleRight = (0, import_react24.useCallback)((v2) => {
5361
+ const toggleRight = (0, import_react27.useCallback)((v2) => {
5166
5362
  setRightExpanded(v2);
5167
5363
  triggerReflow();
5168
5364
  }, [triggerReflow]);
5169
- const toggleTimeline = (0, import_react24.useCallback)(() => {
5365
+ const toggleTimeline = (0, import_react27.useCallback)(() => {
5170
5366
  setTimelineExpanded((p) => !p);
5171
5367
  triggerReflow();
5172
5368
  }, [triggerReflow]);
5173
5369
  const isInSubflow = drillDownStack.length > 0;
5174
- const currentLevel = (0, import_react24.useMemo)(() => {
5370
+ const currentLevel = (0, import_react27.useMemo)(() => {
5175
5371
  if (drillDownStack.length > 0) {
5176
5372
  const top = drillDownStack[drillDownStack.length - 1];
5177
5373
  return { spec: top.spec, snapshots: top.snapshots };
@@ -5181,35 +5377,31 @@ function ExplainableShell({
5181
5377
  const activeSnapshots = currentLevel.snapshots;
5182
5378
  const activeSpec = currentLevel.spec;
5183
5379
  const safeIdx = activeSnapshots.length > 0 ? Math.max(0, Math.min(snapshotIdx, activeSnapshots.length - 1)) : 0;
5184
- const activeNarrative = (0, import_react24.useMemo)(() => {
5185
- if (!isInSubflow) return narrative;
5186
- const lines = [];
5187
- for (const snap of activeSnapshots) {
5188
- const stageLines = (snap.narrative ?? "").split("\n").filter(Boolean);
5189
- lines.push(...stageLines);
5190
- }
5191
- return lines.length > 0 ? lines : void 0;
5192
- }, [isInSubflow, narrative, activeSnapshots]);
5193
5380
  const activeNarrativeEntries = isInSubflow ? void 0 : narrativeEntries;
5194
- const breadcrumbs = (0, import_react24.useMemo)(() => {
5381
+ const breadcrumbs = (0, import_react27.useMemo)(() => {
5195
5382
  const root = { label: title || "Flowchart", spec, description: spec?.description };
5196
5383
  return [root, ...drillDownStack.map((e) => ({ label: e.label, spec: e.spec, description: void 0 }))];
5197
5384
  }, [spec, title, drillDownStack]);
5198
- const showTreeSidebar = (0, import_react24.useMemo)(() => !!spec && hasSubflowNodes(spec), [spec]);
5199
- const rootOverlay = (0, import_react24.useMemo)(() => {
5385
+ const showTreeSidebar = (0, import_react27.useMemo)(() => {
5386
+ if (traceGraph?.nodes?.length) {
5387
+ return traceGraph.nodes.some((n) => n.data?.isSubflow === true);
5388
+ }
5389
+ return !!spec && hasSubflowNodes(spec);
5390
+ }, [traceGraph, spec]);
5391
+ const rootOverlay = (0, import_react27.useMemo)(() => {
5200
5392
  if (isInSubflow || !snapshots.length) return { activeStage: void 0, doneStages: void 0 };
5201
5393
  const doneStages = new Set(snapshots.slice(0, safeIdx).map((s) => s.stageLabel));
5202
5394
  const activeStage = snapshots[safeIdx]?.stageLabel ?? null;
5203
5395
  return { activeStage, doneStages };
5204
5396
  }, [isInSubflow, snapshots, safeIdx]);
5205
- const handleTabChange = (0, import_react24.useCallback)((tab) => {
5397
+ const handleTabChange = (0, import_react27.useCallback)((tab) => {
5206
5398
  setActiveTab(tab);
5207
5399
  setDrillDownStack([]);
5208
5400
  }, []);
5209
- const handleSnapshotChange = (0, import_react24.useCallback)((idx) => {
5401
+ const handleSnapshotChange = (0, import_react27.useCallback)((idx) => {
5210
5402
  if (typeof idx === "number") setSnapshotIdx(idx);
5211
5403
  }, []);
5212
- const handleDrillDown = (0, import_react24.useCallback)(
5404
+ const handleDrillDown = (0, import_react27.useCallback)(
5213
5405
  (nodeName) => {
5214
5406
  if (!activeSpec) return;
5215
5407
  const entry = resolveSubflowLevel(activeSpec, activeSnapshots, nodeName, narrativeEntries);
@@ -5220,14 +5412,14 @@ function ExplainableShell({
5220
5412
  },
5221
5413
  [activeSpec, activeSnapshots, narrativeEntries, snapshotIdx]
5222
5414
  );
5223
- const handleBreadcrumbNavigate = (0, import_react24.useCallback)((level) => {
5415
+ const handleBreadcrumbNavigate = (0, import_react27.useCallback)((level) => {
5224
5416
  setDrillDownStack((prev) => {
5225
5417
  const popped = level === 0 ? prev[0] : prev[level];
5226
5418
  if (popped) setSnapshotIdx(popped.parentSnapshotIdx);
5227
5419
  return level === 0 ? [] : prev.slice(0, level);
5228
5420
  });
5229
5421
  }, []);
5230
- const handleNodeClick = (0, import_react24.useCallback)(
5422
+ const handleNodeClick = (0, import_react27.useCallback)(
5231
5423
  (indexOrId) => {
5232
5424
  if (typeof indexOrId === "number") {
5233
5425
  setSnapshotIdx(indexOrId);
@@ -5245,7 +5437,7 @@ function ExplainableShell({
5245
5437
  },
5246
5438
  [activeSpec, activeSnapshots, handleDrillDown]
5247
5439
  );
5248
- const handleTreeNodeSelect = (0, import_react24.useCallback)(
5440
+ const handleTreeNodeSelect = (0, import_react27.useCallback)(
5249
5441
  (name, isSubflow) => {
5250
5442
  if (isSubflow && spec) {
5251
5443
  setDrillDownStack([]);
@@ -5264,31 +5456,31 @@ function ExplainableShell({
5264
5456
  );
5265
5457
  const tabLabels = new Map(allTabs.map((t) => [t.id, t.name]));
5266
5458
  if (unstyled) {
5267
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className, style, "data-fp": "explainable-shell", children: [
5268
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { "data-fp": "shell-tabs", children: allTabs.map((tab) => /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("button", { "data-fp": "shell-tab", "data-active": tab.id === activeTab, onClick: () => handleTabChange(tab.id), children: tab.name }, tab.id)) }),
5269
- /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { "data-fp": "shell-content", "data-tab": activeTab, children: [
5270
- activeTab === "result" && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ResultPanel, { data: resultData ?? null, logs, hideConsole, unstyled: true }),
5271
- (activeTab === "explainable" || activeTab === "ai-compatible") && /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_jsx_runtime22.Fragment, { children: [
5272
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(TimeTravelControls, { snapshots: activeSnapshots, selectedIndex: safeIdx, onIndexChange: handleSnapshotChange, unstyled: true }),
5273
- isInSubflow && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(SubflowBreadcrumb, { breadcrumbs, onNavigate: handleBreadcrumbNavigate }),
5274
- activeSpec && effectiveRenderFlowchart?.({ spec: activeSpec, snapshots: activeSnapshots, selectedIndex: safeIdx, onNodeClick: handleNodeClick }),
5275
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(MemoryPanel, { snapshots: activeSnapshots, selectedIndex: safeIdx, unstyled: true }),
5276
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(NarrativePanel, { snapshots: activeSnapshots, selectedIndex: safeIdx, narrativeEntries: activeNarrativeEntries, narrative: activeNarrative, unstyled: true }),
5277
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(GanttTimeline, { snapshots: activeSnapshots, selectedIndex: safeIdx, onSelect: handleSnapshotChange, unstyled: true })
5459
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className, style, "data-fp": "explainable-shell", children: [
5460
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { "data-fp": "shell-tabs", children: allTabs.map((tab) => /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("button", { "data-fp": "shell-tab", "data-active": tab.id === activeTab, onClick: () => handleTabChange(tab.id), children: tab.name }, tab.id)) }),
5461
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { "data-fp": "shell-content", "data-tab": activeTab, children: [
5462
+ activeTab === "result" && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(ResultPanel, { data: resultData ?? null, logs, hideConsole, unstyled: true }),
5463
+ (activeTab === "explainable" || activeTab === "ai-compatible") && /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(import_jsx_runtime24.Fragment, { children: [
5464
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(TimeTravelControls, { snapshots: activeSnapshots, selectedIndex: safeIdx, onIndexChange: handleSnapshotChange, unstyled: true }),
5465
+ isInSubflow && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(SubflowBreadcrumb, { breadcrumbs, onNavigate: handleBreadcrumbNavigate }),
5466
+ activeSpec && effectiveRenderFlowchart?.({ spec: activeSpec, snapshots: activeSnapshots, selectedIndex: safeIdx, onNodeClick: handleNodeClick, showStageId }),
5467
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(MemoryPanel, { snapshots: activeSnapshots, selectedIndex: safeIdx, unstyled: true }),
5468
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(NarrativePanel, { snapshots: activeSnapshots, selectedIndex: safeIdx, narrativeEntries: activeNarrativeEntries, unstyled: true }),
5469
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(GanttTimeline, { snapshots: activeSnapshots, selectedIndex: safeIdx, onSelect: handleSnapshotChange, unstyled: true })
5278
5470
  ] })
5279
5471
  ] })
5280
5472
  ] });
5281
5473
  }
5282
5474
  const showTopology = !!effectiveRenderFlowchart && !!activeSpec;
5283
- const detailsContent = (0, import_react24.useMemo)(() => {
5475
+ const detailsContent = (0, import_react27.useMemo)(() => {
5284
5476
  if (activeTab === "result") {
5285
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ResultPanel, { data: resultData ?? null, logs, hideConsole, size });
5477
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(ResultPanel, { data: resultData ?? null, logs, hideConsole, size });
5286
5478
  }
5287
5479
  if (activeTab === "memory") {
5288
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(MemoryPanel, { snapshots: activeSnapshots, selectedIndex: safeIdx, size, style: { height: "100%" } });
5480
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(MemoryPanel, { snapshots: activeSnapshots, selectedIndex: safeIdx, size, style: { height: "100%" } });
5289
5481
  }
5290
5482
  if (activeTab === "narrative") {
5291
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(NarrativePanel, { snapshots: activeSnapshots, selectedIndex: safeIdx, narrativeEntries: activeNarrativeEntries, narrative: activeNarrative, size, style: { height: "100%" } });
5483
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(NarrativePanel, { snapshots: activeSnapshots, selectedIndex: safeIdx, narrativeEntries: activeNarrativeEntries, size, style: { height: "100%" } });
5292
5484
  }
5293
5485
  const customView = recorderViews?.find((v2) => v2.id === activeTab);
5294
5486
  if (customView?.render) {
@@ -5296,7 +5488,7 @@ function ExplainableShell({
5296
5488
  }
5297
5489
  const autoView = autoRecorderViews.find((v2) => v2.id === activeTab);
5298
5490
  if (autoView) {
5299
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
5491
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
5300
5492
  KeyedRecorderView,
5301
5493
  {
5302
5494
  data: autoView.data,
@@ -5308,9 +5500,9 @@ function ExplainableShell({
5308
5500
  );
5309
5501
  }
5310
5502
  return null;
5311
- }, [activeTab, resultData, logs, hideConsole, size, activeSnapshots, safeIdx, activeNarrativeEntries, activeNarrative, recorderViews, autoRecorderViews]);
5312
- const detailsPanel = /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { style: { display: "flex", flexDirection: "column", height: "100%", overflow: "hidden" }, children: [
5313
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { style: {
5503
+ }, [activeTab, resultData, logs, hideConsole, size, activeSnapshots, safeIdx, activeNarrativeEntries, recorderViews, autoRecorderViews]);
5504
+ const detailsPanel = /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { style: { display: "flex", flexDirection: "column", height: "100%", overflow: "hidden" }, children: [
5505
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { style: {
5314
5506
  display: "flex",
5315
5507
  borderBottom: `1px solid ${theme.border}`,
5316
5508
  background: theme.bgSecondary,
@@ -5318,7 +5510,7 @@ function ExplainableShell({
5318
5510
  overflowX: "auto"
5319
5511
  }, children: allTabs.map((tab) => {
5320
5512
  const active = tab.id === activeTab;
5321
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
5513
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
5322
5514
  "button",
5323
5515
  {
5324
5516
  onClick: () => handleTabChange(tab.id),
@@ -5342,9 +5534,9 @@ function ExplainableShell({
5342
5534
  tab.id
5343
5535
  );
5344
5536
  }) }),
5345
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { style: { flex: 1, overflow: "auto" }, children: detailsContent })
5537
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { style: { flex: 1, overflow: "auto" }, children: detailsContent })
5346
5538
  ] });
5347
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
5539
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
5348
5540
  "div",
5349
5541
  {
5350
5542
  ref: shellRef,
@@ -5362,7 +5554,7 @@ function ExplainableShell({
5362
5554
  },
5363
5555
  "data-fp": "explainable-shell",
5364
5556
  children: [
5365
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
5557
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
5366
5558
  TimeTravelControls,
5367
5559
  {
5368
5560
  snapshots: activeSnapshots,
@@ -5371,57 +5563,59 @@ function ExplainableShell({
5371
5563
  size
5372
5564
  }
5373
5565
  ),
5374
- isInSubflow && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(SubflowBreadcrumb, { breadcrumbs, onNavigate: handleBreadcrumbNavigate }),
5375
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { style: { flex: 1, overflow: isNarrow ? "auto" : "hidden", display: "flex", flexDirection: "column" }, children: isNarrow ? (
5566
+ isInSubflow && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(SubflowBreadcrumb, { breadcrumbs, onNavigate: handleBreadcrumbNavigate }),
5567
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { style: { flex: 1, overflow: isNarrow ? "auto" : "hidden", display: "flex", flexDirection: "column" }, children: isNarrow ? (
5376
5568
  /* ── Mobile: stacked vertical ── */
5377
- /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_jsx_runtime22.Fragment, { children: [
5378
- showTopology && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { style: { height: 350, flexShrink: 0, overflow: "hidden" }, children: effectiveRenderFlowchart({
5569
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(import_jsx_runtime24.Fragment, { children: [
5570
+ showTopology && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { style: { height: 350, flexShrink: 0, overflow: "hidden" }, children: effectiveRenderFlowchart({
5379
5571
  spec: activeSpec,
5380
5572
  snapshots: activeSnapshots,
5381
5573
  selectedIndex: safeIdx,
5382
- onNodeClick: handleNodeClick
5574
+ onNodeClick: handleNodeClick,
5575
+ showStageId
5383
5576
  }) }),
5384
- showTreeSidebar && /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_jsx_runtime22.Fragment, { children: [
5385
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(HLinePill, { label: leftLabel, expanded: leftExpanded, onClick: () => toggleLeft(!leftExpanded) }),
5386
- leftExpanded && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { style: { maxHeight: 180, overflow: "auto", flexShrink: 0 }, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
5577
+ showTreeSidebar && /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(import_jsx_runtime24.Fragment, { children: [
5578
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(HLinePill, { label: leftLabel, expanded: leftExpanded, onClick: () => toggleLeft(!leftExpanded) }),
5579
+ leftExpanded && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { style: { maxHeight: 180, overflow: "auto", flexShrink: 0 }, children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
5387
5580
  SubflowTree,
5388
5581
  {
5389
- spec,
5582
+ graph: traceGraph ?? { nodes: [], edges: [] },
5390
5583
  activeStage: rootOverlay.activeStage,
5391
5584
  doneStages: rootOverlay.doneStages,
5392
5585
  onNodeSelect: handleTreeNodeSelect
5393
5586
  }
5394
5587
  ) })
5395
5588
  ] }),
5396
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(HLinePill, { label: rightLabel, expanded: rightExpanded, onClick: () => toggleRight(!rightExpanded) }),
5397
- rightExpanded && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { style: { maxHeight: 350, flexShrink: 0, overflow: "hidden" }, children: detailsPanel }),
5398
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(HLinePill, { label: bottomLabel, detail: `${activeSnapshots.length} stages`, expanded: timelineExpanded, onClick: toggleTimeline }),
5399
- timelineExpanded && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { style: { flexShrink: 0, overflow: "hidden" }, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(GanttTimeline, { snapshots: activeSnapshots, selectedIndex: safeIdx, onSelect: handleSnapshotChange, size }) })
5589
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(HLinePill, { label: rightLabel, expanded: rightExpanded, onClick: () => toggleRight(!rightExpanded) }),
5590
+ rightExpanded && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { style: { maxHeight: 350, flexShrink: 0, overflow: "hidden" }, children: detailsPanel }),
5591
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(HLinePill, { label: bottomLabel, detail: `${activeSnapshots.length} stages`, expanded: timelineExpanded, onClick: toggleTimeline }),
5592
+ timelineExpanded && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { style: { flexShrink: 0, overflow: "hidden" }, children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(GanttTimeline, { snapshots: activeSnapshots, selectedIndex: safeIdx, onSelect: handleSnapshotChange, size }) })
5400
5593
  ] })
5401
5594
  ) : (
5402
5595
  /* ── Desktop: two-column — Flowchart | Right Panel ── */
5403
- /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_jsx_runtime22.Fragment, { children: [
5404
- /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { style: { flex: 1, display: "flex", overflow: "hidden" }, children: [
5405
- showTreeSidebar && (leftExpanded ? /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { style: { width: 180, flexShrink: 0, display: "flex", flexDirection: "row", overflow: "hidden" }, children: [
5406
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { style: { flex: 1, overflow: "auto" }, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
5596
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(import_jsx_runtime24.Fragment, { children: [
5597
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { style: { flex: 1, display: "flex", overflow: "hidden" }, children: [
5598
+ showTreeSidebar && (leftExpanded ? /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { style: { width: 180, flexShrink: 0, display: "flex", flexDirection: "row", overflow: "hidden" }, children: [
5599
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { style: { flex: 1, overflow: "auto" }, children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
5407
5600
  SubflowTree,
5408
5601
  {
5409
- spec,
5602
+ graph: traceGraph ?? { nodes: [], edges: [] },
5410
5603
  activeStage: rootOverlay.activeStage,
5411
5604
  doneStages: rootOverlay.doneStages,
5412
5605
  onNodeSelect: handleTreeNodeSelect
5413
5606
  }
5414
5607
  ) }),
5415
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(VLinePill, { label: "Topology", expanded: true, side: "left", onClick: () => toggleLeft(false) })
5416
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(VLinePill, { label: "Topology", expanded: false, side: "left", onClick: () => toggleLeft(true) })),
5417
- showTopology ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { style: { flex: 1, overflow: "hidden", minWidth: 0 }, children: effectiveRenderFlowchart({
5608
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(VLinePill, { label: "Topology", expanded: true, side: "left", onClick: () => toggleLeft(false) })
5609
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(VLinePill, { label: "Topology", expanded: false, side: "left", onClick: () => toggleLeft(true) })),
5610
+ showTopology ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { style: { flex: 1, overflow: "hidden", minWidth: 0 }, children: effectiveRenderFlowchart({
5418
5611
  spec: activeSpec,
5419
5612
  snapshots: activeSnapshots,
5420
5613
  selectedIndex: safeIdx,
5421
- onNodeClick: handleNodeClick
5422
- }) }) : /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { style: { flex: 1 } }),
5423
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(VLinePill, { label: "Details", expanded: rightExpanded, onClick: () => toggleRight(!rightExpanded) }),
5424
- rightExpanded && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { style: { width: "42%", minWidth: 320, maxWidth: 550, display: "flex", flexDirection: "column", overflow: "hidden" }, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
5614
+ onNodeClick: handleNodeClick,
5615
+ showStageId
5616
+ }) }) : /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { style: { flex: 1 } }),
5617
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(VLinePill, { label: "Details", expanded: rightExpanded, onClick: () => toggleRight(!rightExpanded) }),
5618
+ rightExpanded && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { style: { width: "42%", minWidth: 320, maxWidth: 550, display: "flex", flexDirection: "column", overflow: "hidden" }, children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
5425
5619
  RightPanel,
5426
5620
  {
5427
5621
  mode: rightPanelMode,
@@ -5433,7 +5627,6 @@ function ExplainableShell({
5433
5627
  activeTab,
5434
5628
  allTabs,
5435
5629
  activeNarrativeEntries,
5436
- activeNarrative,
5437
5630
  recorderViews,
5438
5631
  autoRecorderViews,
5439
5632
  size,
@@ -5444,7 +5637,7 @@ function ExplainableShell({
5444
5637
  }
5445
5638
  ) })
5446
5639
  ] }),
5447
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
5640
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
5448
5641
  CompactTimeline,
5449
5642
  {
5450
5643
  snapshots: activeSnapshots,
@@ -5461,8 +5654,8 @@ function ExplainableShell({
5461
5654
 
5462
5655
  // src/components/TraceViewer/TraceViewer.tsx
5463
5656
  var React = __toESM(require("react"), 1);
5464
- var import_react25 = require("react");
5465
- var import_jsx_runtime23 = require("react/jsx-runtime");
5657
+ var import_react28 = require("react");
5658
+ var import_jsx_runtime25 = require("react/jsx-runtime");
5466
5659
  function parseTrace(input) {
5467
5660
  if (input == null) {
5468
5661
  return {
@@ -5525,11 +5718,11 @@ function TraceViewer({
5525
5718
  recorderViews,
5526
5719
  renderFlowchart
5527
5720
  }) {
5528
- const parsed = (0, import_react25.useMemo)(() => parseTrace(trace), [trace]);
5721
+ const parsed = (0, import_react28.useMemo)(() => parseTrace(trace), [trace]);
5529
5722
  React.useEffect(() => {
5530
5723
  if (!parsed.ok && onError) onError(parsed.error);
5531
5724
  }, [parsed, onError]);
5532
- const snapshots = (0, import_react25.useMemo)(() => {
5725
+ const snapshots = (0, import_react28.useMemo)(() => {
5533
5726
  if (!parsed.ok || !parsed.trace.snapshot) return [];
5534
5727
  try {
5535
5728
  return toVisualizationSnapshots(
@@ -5543,12 +5736,11 @@ function TraceViewer({
5543
5736
  if (!parsed.ok || snapshots.length === 0) {
5544
5737
  return fallback ?? null;
5545
5738
  }
5546
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
5739
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
5547
5740
  ExplainableShell,
5548
5741
  {
5549
5742
  snapshots,
5550
5743
  spec: parsed.trace.spec,
5551
- narrative: parsed.trace.narrative,
5552
5744
  narrativeEntries: parsed.trace.narrativeEntries,
5553
5745
  tabs,
5554
5746
  defaultTab,