footprint-explainable-ui 0.3.2 → 0.5.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 (161) hide show
  1. package/README.md +42 -3
  2. package/dist/adapters/fromRuntimeSnapshot.d.ts +52 -0
  3. package/dist/adapters/fromRuntimeSnapshot.d.ts.map +1 -0
  4. package/dist/adapters/fromRuntimeSnapshot.js +97 -0
  5. package/dist/adapters/fromRuntimeSnapshot.js.map +1 -0
  6. package/dist/components/ExplainableShell/ExplainableShell.d.ts +26 -0
  7. package/dist/components/ExplainableShell/ExplainableShell.d.ts.map +1 -0
  8. package/dist/components/ExplainableShell/ExplainableShell.js +94 -0
  9. package/dist/components/ExplainableShell/ExplainableShell.js.map +1 -0
  10. package/dist/components/ExplainableShell/index.d.ts +3 -0
  11. package/dist/components/ExplainableShell/index.d.ts.map +1 -0
  12. package/dist/components/ExplainableShell/index.js +2 -0
  13. package/dist/components/ExplainableShell/index.js.map +1 -0
  14. package/dist/components/FlowchartView/FlowchartView.d.ts +20 -0
  15. package/dist/components/FlowchartView/FlowchartView.d.ts.map +1 -0
  16. package/dist/components/FlowchartView/FlowchartView.js +80 -0
  17. package/dist/components/FlowchartView/FlowchartView.js.map +1 -0
  18. package/dist/components/FlowchartView/SubflowBreadcrumb.d.ts +11 -0
  19. package/dist/components/FlowchartView/SubflowBreadcrumb.d.ts.map +1 -0
  20. package/dist/components/FlowchartView/SubflowBreadcrumb.js +49 -0
  21. package/dist/components/FlowchartView/SubflowBreadcrumb.js.map +1 -0
  22. package/dist/components/FlowchartView/SubflowTree.d.ts +36 -0
  23. package/dist/components/FlowchartView/SubflowTree.d.ts.map +1 -0
  24. package/dist/components/FlowchartView/SubflowTree.js +143 -0
  25. package/dist/components/FlowchartView/SubflowTree.js.map +1 -0
  26. package/dist/components/FlowchartView/TracedFlowchartView.d.ts +20 -0
  27. package/dist/components/FlowchartView/TracedFlowchartView.d.ts.map +1 -0
  28. package/dist/components/FlowchartView/TracedFlowchartView.js +101 -0
  29. package/dist/components/FlowchartView/TracedFlowchartView.js.map +1 -0
  30. package/dist/components/FlowchartView/index.d.ts +11 -0
  31. package/dist/components/FlowchartView/index.d.ts.map +1 -0
  32. package/dist/components/FlowchartView/index.js +6 -0
  33. package/dist/components/FlowchartView/index.js.map +1 -0
  34. package/dist/components/FlowchartView/specToReactFlow.d.ts +56 -0
  35. package/dist/components/FlowchartView/specToReactFlow.d.ts.map +1 -0
  36. package/dist/components/FlowchartView/specToReactFlow.js +202 -0
  37. package/dist/components/FlowchartView/specToReactFlow.js.map +1 -0
  38. package/dist/components/FlowchartView/useSubflowNavigation.d.ts +35 -0
  39. package/dist/components/FlowchartView/useSubflowNavigation.d.ts.map +1 -0
  40. package/dist/components/FlowchartView/useSubflowNavigation.js +80 -0
  41. package/dist/components/FlowchartView/useSubflowNavigation.js.map +1 -0
  42. package/dist/components/GanttTimeline/GanttTimeline.d.ts +18 -0
  43. package/dist/components/GanttTimeline/GanttTimeline.d.ts.map +1 -0
  44. package/dist/components/GanttTimeline/GanttTimeline.js +123 -0
  45. package/dist/components/GanttTimeline/GanttTimeline.js.map +1 -0
  46. package/dist/components/GanttTimeline/index.d.ts +3 -0
  47. package/dist/components/GanttTimeline/index.d.ts.map +1 -0
  48. package/dist/components/GanttTimeline/index.js +2 -0
  49. package/dist/components/GanttTimeline/index.js.map +1 -0
  50. package/dist/components/MemoryInspector/MemoryInspector.d.ts +19 -0
  51. package/dist/components/MemoryInspector/MemoryInspector.d.ts.map +1 -0
  52. package/dist/components/MemoryInspector/MemoryInspector.js +95 -0
  53. package/dist/components/MemoryInspector/MemoryInspector.js.map +1 -0
  54. package/dist/components/MemoryInspector/index.d.ts +3 -0
  55. package/dist/components/MemoryInspector/index.d.ts.map +1 -0
  56. package/dist/components/MemoryInspector/index.js +2 -0
  57. package/dist/components/MemoryInspector/index.js.map +1 -0
  58. package/dist/components/NarrativeLog/NarrativeLog.d.ts +15 -0
  59. package/dist/components/NarrativeLog/NarrativeLog.d.ts.map +1 -0
  60. package/dist/components/NarrativeLog/NarrativeLog.js +65 -0
  61. package/dist/components/NarrativeLog/NarrativeLog.js.map +1 -0
  62. package/dist/components/NarrativeLog/index.d.ts +3 -0
  63. package/dist/components/NarrativeLog/index.d.ts.map +1 -0
  64. package/dist/components/NarrativeLog/index.js +2 -0
  65. package/dist/components/NarrativeLog/index.js.map +1 -0
  66. package/dist/components/NarrativeTrace/NarrativeTrace.d.ts +13 -0
  67. package/dist/components/NarrativeTrace/NarrativeTrace.d.ts.map +1 -0
  68. package/dist/components/NarrativeTrace/NarrativeTrace.js +127 -0
  69. package/dist/components/NarrativeTrace/NarrativeTrace.js.map +1 -0
  70. package/dist/components/NarrativeTrace/index.d.ts +3 -0
  71. package/dist/components/NarrativeTrace/index.d.ts.map +1 -0
  72. package/dist/components/NarrativeTrace/index.js +2 -0
  73. package/dist/components/NarrativeTrace/index.js.map +1 -0
  74. package/dist/components/ResultPanel/ResultPanel.d.ts +11 -0
  75. package/dist/components/ResultPanel/ResultPanel.d.ts.map +1 -0
  76. package/dist/components/ResultPanel/ResultPanel.js +54 -0
  77. package/dist/components/ResultPanel/ResultPanel.js.map +1 -0
  78. package/dist/components/ResultPanel/index.d.ts +3 -0
  79. package/dist/components/ResultPanel/index.d.ts.map +1 -0
  80. package/dist/components/ResultPanel/index.js +2 -0
  81. package/dist/components/ResultPanel/index.js.map +1 -0
  82. package/dist/components/ScopeDiff/ScopeDiff.d.ts +17 -0
  83. package/dist/components/ScopeDiff/ScopeDiff.d.ts.map +1 -0
  84. package/dist/components/ScopeDiff/ScopeDiff.js +87 -0
  85. package/dist/components/ScopeDiff/ScopeDiff.js.map +1 -0
  86. package/dist/components/ScopeDiff/index.d.ts +3 -0
  87. package/dist/components/ScopeDiff/index.d.ts.map +1 -0
  88. package/dist/components/ScopeDiff/index.js +2 -0
  89. package/dist/components/ScopeDiff/index.js.map +1 -0
  90. package/dist/components/SnapshotPanel/SnapshotPanel.d.ts +17 -0
  91. package/dist/components/SnapshotPanel/SnapshotPanel.d.ts.map +1 -0
  92. package/dist/components/SnapshotPanel/SnapshotPanel.js +85 -0
  93. package/dist/components/SnapshotPanel/SnapshotPanel.js.map +1 -0
  94. package/dist/components/SnapshotPanel/index.d.ts +3 -0
  95. package/dist/components/SnapshotPanel/index.d.ts.map +1 -0
  96. package/dist/components/SnapshotPanel/index.js +2 -0
  97. package/dist/components/SnapshotPanel/index.js.map +1 -0
  98. package/dist/components/StageNode/StageNode.d.ts +31 -0
  99. package/dist/components/StageNode/StageNode.d.ts.map +1 -0
  100. package/dist/components/StageNode/StageNode.js +134 -0
  101. package/dist/components/StageNode/StageNode.js.map +1 -0
  102. package/dist/components/StageNode/index.d.ts +3 -0
  103. package/dist/components/StageNode/index.d.ts.map +1 -0
  104. package/dist/components/StageNode/index.js +2 -0
  105. package/dist/components/StageNode/index.js.map +1 -0
  106. package/dist/components/TimeTravelControls/TimeTravelControls.d.ts +13 -0
  107. package/dist/components/TimeTravelControls/TimeTravelControls.d.ts.map +1 -0
  108. package/dist/components/TimeTravelControls/TimeTravelControls.js +104 -0
  109. package/dist/components/TimeTravelControls/TimeTravelControls.js.map +1 -0
  110. package/dist/components/TimeTravelControls/index.d.ts +3 -0
  111. package/dist/components/TimeTravelControls/index.d.ts.map +1 -0
  112. package/dist/components/TimeTravelControls/index.js +2 -0
  113. package/dist/components/TimeTravelControls/index.js.map +1 -0
  114. package/dist/components/TimeTravelDebugger/TimeTravelDebugger.d.ts +22 -0
  115. package/dist/components/TimeTravelDebugger/TimeTravelDebugger.d.ts.map +1 -0
  116. package/dist/components/TimeTravelDebugger/TimeTravelDebugger.js +104 -0
  117. package/dist/components/TimeTravelDebugger/TimeTravelDebugger.js.map +1 -0
  118. package/dist/components/TimeTravelDebugger/index.d.ts +3 -0
  119. package/dist/components/TimeTravelDebugger/index.d.ts.map +1 -0
  120. package/dist/components/TimeTravelDebugger/index.js +2 -0
  121. package/dist/components/TimeTravelDebugger/index.js.map +1 -0
  122. package/dist/flowchart.cjs +704 -220
  123. package/dist/flowchart.cjs.map +1 -1
  124. package/dist/flowchart.d.cts +55 -1
  125. package/dist/flowchart.d.ts +55 -1
  126. package/dist/flowchart.d.ts.map +1 -0
  127. package/dist/flowchart.js +700 -214
  128. package/dist/flowchart.js.map +1 -1
  129. package/dist/index.cjs +849 -76
  130. package/dist/index.cjs.map +1 -1
  131. package/dist/index.d.cts +95 -3
  132. package/dist/index.d.ts +95 -3
  133. package/dist/index.d.ts.map +1 -0
  134. package/dist/index.js +851 -80
  135. package/dist/index.js.map +1 -1
  136. package/dist/theme/ThemeProvider.d.ts +13 -0
  137. package/dist/theme/ThemeProvider.d.ts.map +1 -0
  138. package/dist/theme/ThemeProvider.js +16 -0
  139. package/dist/theme/ThemeProvider.js.map +1 -0
  140. package/dist/theme/index.d.ts +7 -0
  141. package/dist/theme/index.d.ts.map +1 -0
  142. package/dist/theme/index.js +5 -0
  143. package/dist/theme/index.js.map +1 -0
  144. package/dist/theme/presets.d.ts +15 -0
  145. package/dist/theme/presets.d.ts.map +1 -0
  146. package/dist/theme/presets.js +70 -0
  147. package/dist/theme/presets.js.map +1 -0
  148. package/dist/theme/styles.d.ts +32 -0
  149. package/dist/theme/styles.d.ts.map +1 -0
  150. package/dist/theme/styles.js +37 -0
  151. package/dist/theme/styles.js.map +1 -0
  152. package/dist/theme/tokens.d.ts +28 -0
  153. package/dist/theme/tokens.d.ts.map +1 -0
  154. package/dist/theme/tokens.js +58 -0
  155. package/dist/theme/tokens.js.map +1 -0
  156. package/dist/tsconfig.tsbuildinfo +1 -0
  157. package/dist/types.d.ts +35 -0
  158. package/dist/types.d.ts.map +1 -0
  159. package/dist/types.js +2 -0
  160. package/dist/types.js.map +1 -0
  161. package/package.json +10 -4
package/dist/index.js CHANGED
@@ -644,7 +644,7 @@ function NarrativeTrace({
644
644
  }
645
645
 
646
646
  // src/components/GanttTimeline/GanttTimeline.tsx
647
- import { useMemo as useMemo4 } from "react";
647
+ import { useState as useState2, useMemo as useMemo4, useRef as useRef2, useEffect as useEffect2 } from "react";
648
648
  import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
649
649
  function GanttTimeline({
650
650
  snapshots,
@@ -653,8 +653,12 @@ function GanttTimeline({
653
653
  size = "default",
654
654
  unstyled = false,
655
655
  className,
656
- style
656
+ style,
657
+ maxVisibleRows = 5
657
658
  }) {
659
+ const [expanded, setExpanded] = useState2(false);
660
+ const activeRowRef = useRef2(null);
661
+ const scrollContainerRef = useRef2(null);
658
662
  const totalWallTime = useMemo4(
659
663
  () => Math.max(...snapshots.map((s) => s.startMs + s.durationMs), 1),
660
664
  [snapshots]
@@ -663,6 +667,17 @@ function GanttTimeline({
663
667
  const pad = padding[size];
664
668
  const labelWidth = size === "compact" ? 50 : size === "detailed" ? 100 : 80;
665
669
  const msWidth = size === "compact" ? 28 : 36;
670
+ const rowHeight = size === "compact" ? 18 : 22;
671
+ const collapsible = maxVisibleRows > 0 && snapshots.length > maxVisibleRows;
672
+ const showAll = expanded || !collapsible;
673
+ useEffect2(() => {
674
+ if (!showAll && activeRowRef.current && scrollContainerRef.current) {
675
+ activeRowRef.current.scrollIntoView({
676
+ block: "nearest",
677
+ behavior: "smooth"
678
+ });
679
+ }
680
+ }, [selectedIndex, showAll]);
666
681
  if (unstyled) {
667
682
  return /* @__PURE__ */ jsx5("div", { className, style, "data-fp": "gantt-timeline", children: snapshots.map((snap, idx) => /* @__PURE__ */ jsxs4(
668
683
  "div",
@@ -689,27 +704,62 @@ function GanttTimeline({
689
704
  style: { padding: pad, fontFamily: theme.fontSans, ...style },
690
705
  "data-fp": "gantt-timeline",
691
706
  children: [
692
- /* @__PURE__ */ jsx5(
693
- "span",
707
+ /* @__PURE__ */ jsxs4(
708
+ "div",
694
709
  {
695
710
  style: {
696
- fontSize: fs.label,
697
- fontWeight: 600,
698
- color: theme.textMuted,
699
- textTransform: "uppercase",
700
- letterSpacing: "0.08em"
711
+ display: "flex",
712
+ alignItems: "center",
713
+ justifyContent: "space-between"
701
714
  },
702
- children: size === "compact" ? "Timeline" : "Execution Timeline"
715
+ children: [
716
+ /* @__PURE__ */ jsx5(
717
+ "span",
718
+ {
719
+ style: {
720
+ fontSize: fs.label,
721
+ fontWeight: 600,
722
+ color: theme.textMuted,
723
+ textTransform: "uppercase",
724
+ letterSpacing: "0.08em"
725
+ },
726
+ children: size === "compact" ? "Timeline" : "Execution Timeline"
727
+ }
728
+ ),
729
+ collapsible && /* @__PURE__ */ jsx5(
730
+ "button",
731
+ {
732
+ onClick: () => setExpanded((e) => !e),
733
+ style: {
734
+ background: "none",
735
+ border: `1px solid ${theme.border}`,
736
+ borderRadius: 4,
737
+ color: theme.textSecondary,
738
+ fontSize: fs.small,
739
+ padding: "2px 8px",
740
+ cursor: "pointer",
741
+ fontFamily: theme.fontSans
742
+ },
743
+ children: expanded ? "Collapse" : `${snapshots.length - maxVisibleRows} more...`
744
+ }
745
+ )
746
+ ]
703
747
  }
704
748
  ),
705
749
  /* @__PURE__ */ jsx5(
706
750
  "div",
707
751
  {
752
+ ref: scrollContainerRef,
708
753
  style: {
709
754
  marginTop: 8,
710
755
  display: "flex",
711
756
  flexDirection: "column",
712
- gap: 4
757
+ gap: 4,
758
+ ...showAll ? {} : {
759
+ maxHeight: maxVisibleRows * (rowHeight + 4),
760
+ overflowY: "auto",
761
+ scrollbarWidth: "thin"
762
+ }
713
763
  },
714
764
  children: snapshots.map((snap, idx) => {
715
765
  const leftPct = snap.startMs / totalWallTime * 100;
@@ -719,6 +769,7 @@ function GanttTimeline({
719
769
  return /* @__PURE__ */ jsxs4(
720
770
  "div",
721
771
  {
772
+ ref: isSelected ? activeRowRef : void 0,
722
773
  onClick: () => onSelect?.(idx),
723
774
  style: {
724
775
  display: "flex",
@@ -726,7 +777,9 @@ function GanttTimeline({
726
777
  gap: size === "compact" ? 4 : 8,
727
778
  cursor: onSelect ? "pointer" : "default",
728
779
  opacity: isVisible ? 1 : 0.3,
729
- transition: "opacity 0.3s ease"
780
+ transition: "opacity 0.3s ease",
781
+ height: rowHeight,
782
+ flexShrink: 0
730
783
  },
731
784
  children: [
732
785
  /* @__PURE__ */ jsx5(
@@ -828,7 +881,7 @@ function GanttTimeline({
828
881
  }
829
882
 
830
883
  // src/components/SnapshotPanel/SnapshotPanel.tsx
831
- import { useState as useState2 } from "react";
884
+ import { useState as useState3 } from "react";
832
885
  import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
833
886
  function SnapshotPanel({
834
887
  snapshots,
@@ -840,7 +893,7 @@ function SnapshotPanel({
840
893
  className,
841
894
  style
842
895
  }) {
843
- const [selectedIndex, setSelectedIndex] = useState2(0);
896
+ const [selectedIndex, setSelectedIndex] = useState3(0);
844
897
  const fs = fontSize[size];
845
898
  const pad = padding[size];
846
899
  if (snapshots.length === 0) {
@@ -1330,9 +1383,487 @@ function ResultPanel({
1330
1383
  );
1331
1384
  }
1332
1385
 
1386
+ // src/components/StageDetailPanel/StageDetailPanel.tsx
1387
+ import { useState as useState4, useMemo as useMemo6, useCallback as useCallback2 } from "react";
1388
+ import { Fragment as Fragment2, jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
1389
+ function computeChanges(prev, curr) {
1390
+ const changes = [];
1391
+ const allKeys = /* @__PURE__ */ new Set([...Object.keys(prev ?? {}), ...Object.keys(curr)]);
1392
+ for (const key of allKeys) {
1393
+ const inPrev = prev != null && key in prev;
1394
+ const inCurr = key in curr;
1395
+ const oldVal = prev?.[key];
1396
+ const newVal = curr[key];
1397
+ if (!inPrev && inCurr) {
1398
+ changes.push({ key, type: "added", newValue: newVal });
1399
+ } else if (inPrev && !inCurr) {
1400
+ changes.push({ key, type: "removed", oldValue: oldVal });
1401
+ } else if (JSON.stringify(oldVal) !== JSON.stringify(newVal)) {
1402
+ changes.push({ key, type: "updated", oldValue: oldVal, newValue: newVal });
1403
+ }
1404
+ }
1405
+ const order = { added: 0, updated: 1, removed: 2 };
1406
+ changes.sort((a, b) => order[a.type] - order[b.type]);
1407
+ return changes;
1408
+ }
1409
+ function fmt2(v2) {
1410
+ if (typeof v2 === "string") return `"${v2}"`;
1411
+ if (typeof v2 === "object" && v2 !== null) return JSON.stringify(v2, null, 2);
1412
+ return String(v2);
1413
+ }
1414
+ var changeBadge = {
1415
+ added: { bg: "rgba(34,197,94,0.12)", fg: "#22c55e", label: "ADD" },
1416
+ updated: { bg: "rgba(245,158,11,0.12)", fg: "#f59e0b", label: "UPD" },
1417
+ removed: { bg: "rgba(239,68,68,0.12)", fg: "#ef4444", label: "DEL" }
1418
+ };
1419
+ function SimpleView({
1420
+ snapshot,
1421
+ fs,
1422
+ pad
1423
+ }) {
1424
+ return /* @__PURE__ */ jsxs8("div", { style: { display: "flex", flexDirection: "column", gap: 16 }, children: [
1425
+ /* @__PURE__ */ jsxs8("div", { children: [
1426
+ /* @__PURE__ */ jsx9(
1427
+ "div",
1428
+ {
1429
+ style: {
1430
+ fontSize: fs.label + 2,
1431
+ fontWeight: 700,
1432
+ color: theme.textPrimary
1433
+ },
1434
+ children: snapshot.stageLabel
1435
+ }
1436
+ ),
1437
+ snapshot.description && /* @__PURE__ */ jsx9(
1438
+ "div",
1439
+ {
1440
+ style: {
1441
+ fontSize: fs.body,
1442
+ color: theme.textSecondary,
1443
+ marginTop: 4,
1444
+ lineHeight: 1.5
1445
+ },
1446
+ children: snapshot.description
1447
+ }
1448
+ )
1449
+ ] }),
1450
+ snapshot.status && /* @__PURE__ */ jsxs8("div", { style: { display: "flex", alignItems: "center", gap: 6 }, children: [
1451
+ /* @__PURE__ */ jsx9(
1452
+ "div",
1453
+ {
1454
+ style: {
1455
+ width: 8,
1456
+ height: 8,
1457
+ borderRadius: "50%",
1458
+ background: snapshot.status === "done" ? theme.success : snapshot.status === "active" ? theme.primary : snapshot.status === "error" ? theme.error : theme.textMuted
1459
+ }
1460
+ }
1461
+ ),
1462
+ /* @__PURE__ */ jsx9(
1463
+ "span",
1464
+ {
1465
+ style: {
1466
+ fontSize: fs.small,
1467
+ color: theme.textMuted,
1468
+ textTransform: "uppercase",
1469
+ letterSpacing: "0.05em"
1470
+ },
1471
+ children: snapshot.status
1472
+ }
1473
+ )
1474
+ ] }),
1475
+ snapshot.narrative && /* @__PURE__ */ jsxs8("div", { children: [
1476
+ /* @__PURE__ */ jsx9(
1477
+ "div",
1478
+ {
1479
+ style: {
1480
+ fontSize: fs.label,
1481
+ fontWeight: 600,
1482
+ color: theme.textMuted,
1483
+ textTransform: "uppercase",
1484
+ letterSpacing: "0.08em",
1485
+ marginBottom: 6
1486
+ },
1487
+ children: "What happened"
1488
+ }
1489
+ ),
1490
+ /* @__PURE__ */ jsx9(
1491
+ "div",
1492
+ {
1493
+ style: {
1494
+ fontSize: fs.body,
1495
+ lineHeight: 1.6,
1496
+ color: theme.textPrimary,
1497
+ background: theme.bgSecondary,
1498
+ border: `1px solid ${theme.border}`,
1499
+ borderRadius: theme.radius,
1500
+ padding: pad
1501
+ },
1502
+ children: snapshot.narrative
1503
+ }
1504
+ )
1505
+ ] }),
1506
+ snapshot.durationMs > 0 && /* @__PURE__ */ jsxs8(
1507
+ "div",
1508
+ {
1509
+ style: {
1510
+ fontSize: fs.small,
1511
+ color: theme.textMuted
1512
+ },
1513
+ children: [
1514
+ "Completed in ",
1515
+ snapshot.durationMs < 1 ? "<1" : snapshot.durationMs,
1516
+ "ms"
1517
+ ]
1518
+ }
1519
+ )
1520
+ ] });
1521
+ }
1522
+ function buildMemoryRows(currMemory, changes) {
1523
+ const changeMap = new Map(changes.map((c) => [c.key, c]));
1524
+ const rows = [];
1525
+ for (const change of changes) {
1526
+ rows.push({ kind: "change", change });
1527
+ }
1528
+ const unchangedKeys = Object.keys(currMemory).filter((k) => !changeMap.has(k)).sort();
1529
+ for (const key of unchangedKeys) {
1530
+ rows.push({ kind: "unchanged", key, value: currMemory[key] });
1531
+ }
1532
+ return rows;
1533
+ }
1534
+ function DevView({
1535
+ snapshot,
1536
+ changes,
1537
+ currMemory,
1538
+ fs,
1539
+ pad
1540
+ }) {
1541
+ const rows = useMemo6(() => buildMemoryRows(currMemory, changes), [currMemory, changes]);
1542
+ const totalKeys = Object.keys(currMemory).length;
1543
+ return /* @__PURE__ */ jsxs8("div", { style: { display: "flex", flexDirection: "column", gap: 12 }, children: [
1544
+ /* @__PURE__ */ jsxs8("div", { style: { display: "flex", alignItems: "center", gap: 8 }, children: [
1545
+ /* @__PURE__ */ jsx9(
1546
+ "span",
1547
+ {
1548
+ style: {
1549
+ fontSize: fs.label + 2,
1550
+ fontWeight: 700,
1551
+ color: theme.textPrimary,
1552
+ fontFamily: theme.fontMono
1553
+ },
1554
+ children: snapshot.stageLabel
1555
+ }
1556
+ ),
1557
+ snapshot.durationMs > 0 && /* @__PURE__ */ jsxs8(
1558
+ "span",
1559
+ {
1560
+ style: {
1561
+ fontSize: fs.small,
1562
+ color: theme.textMuted,
1563
+ fontFamily: theme.fontMono
1564
+ },
1565
+ children: [
1566
+ snapshot.durationMs,
1567
+ "ms"
1568
+ ]
1569
+ }
1570
+ )
1571
+ ] }),
1572
+ /* @__PURE__ */ jsxs8(
1573
+ "div",
1574
+ {
1575
+ style: {
1576
+ fontSize: fs.label,
1577
+ fontWeight: 600,
1578
+ color: theme.textMuted,
1579
+ textTransform: "uppercase",
1580
+ letterSpacing: "0.08em"
1581
+ },
1582
+ children: [
1583
+ "Memory",
1584
+ /* @__PURE__ */ jsxs8("span", { style: { fontWeight: 400, marginLeft: 6 }, children: [
1585
+ "(",
1586
+ totalKeys,
1587
+ " key",
1588
+ totalKeys !== 1 ? "s" : "",
1589
+ changes.length > 0 && `, ${changes.length} changed`,
1590
+ ")"
1591
+ ] })
1592
+ ]
1593
+ }
1594
+ ),
1595
+ rows.length === 0 ? /* @__PURE__ */ jsx9(
1596
+ "div",
1597
+ {
1598
+ style: {
1599
+ fontSize: fs.body,
1600
+ color: theme.textMuted,
1601
+ fontStyle: "italic",
1602
+ fontFamily: theme.fontMono,
1603
+ padding: `${pad}px`,
1604
+ background: theme.bgSecondary,
1605
+ borderRadius: theme.radius
1606
+ },
1607
+ children: "Empty memory"
1608
+ }
1609
+ ) : /* @__PURE__ */ jsx9(
1610
+ "div",
1611
+ {
1612
+ style: {
1613
+ fontFamily: theme.fontMono,
1614
+ fontSize: fs.body,
1615
+ background: theme.bgSecondary,
1616
+ border: `1px solid ${theme.border}`,
1617
+ borderRadius: theme.radius,
1618
+ overflow: "hidden"
1619
+ },
1620
+ children: rows.map((row) => {
1621
+ if (row.kind === "change") {
1622
+ const { change } = row;
1623
+ const badge = changeBadge[change.type];
1624
+ return /* @__PURE__ */ jsxs8(
1625
+ "div",
1626
+ {
1627
+ style: {
1628
+ display: "flex",
1629
+ alignItems: "flex-start",
1630
+ gap: 8,
1631
+ padding: `6px ${pad}px`,
1632
+ borderBottom: `1px solid ${theme.border}`,
1633
+ background: badge.bg
1634
+ },
1635
+ "data-fp": "memory-change",
1636
+ "data-type": change.type,
1637
+ children: [
1638
+ /* @__PURE__ */ jsx9(
1639
+ "span",
1640
+ {
1641
+ style: {
1642
+ fontSize: fs.small,
1643
+ fontWeight: 700,
1644
+ color: badge.fg,
1645
+ width: 28,
1646
+ flexShrink: 0,
1647
+ textAlign: "center",
1648
+ lineHeight: 1.8
1649
+ },
1650
+ children: badge.label
1651
+ }
1652
+ ),
1653
+ /* @__PURE__ */ jsx9(
1654
+ "span",
1655
+ {
1656
+ style: {
1657
+ color: theme.primary,
1658
+ fontWeight: 600,
1659
+ flexShrink: 0,
1660
+ lineHeight: 1.8
1661
+ },
1662
+ children: change.key
1663
+ }
1664
+ ),
1665
+ /* @__PURE__ */ jsx9("div", { style: { flex: 1, minWidth: 0, lineHeight: 1.8 }, children: change.type === "updated" ? /* @__PURE__ */ jsxs8(Fragment2, { children: [
1666
+ /* @__PURE__ */ jsx9(
1667
+ "span",
1668
+ {
1669
+ style: {
1670
+ color: theme.error,
1671
+ textDecoration: "line-through",
1672
+ opacity: 0.7
1673
+ },
1674
+ children: fmt2(change.oldValue)
1675
+ }
1676
+ ),
1677
+ /* @__PURE__ */ jsx9("span", { style: { color: theme.textMuted, margin: "0 4px" }, children: "\u2192" }),
1678
+ /* @__PURE__ */ jsx9("span", { style: { color: theme.success }, children: fmt2(change.newValue) })
1679
+ ] }) : change.type === "added" ? /* @__PURE__ */ jsx9("span", { style: { color: theme.success }, children: fmt2(change.newValue) }) : /* @__PURE__ */ jsx9("span", { style: { color: theme.error, textDecoration: "line-through" }, children: fmt2(change.oldValue) }) })
1680
+ ]
1681
+ },
1682
+ change.key
1683
+ );
1684
+ }
1685
+ return /* @__PURE__ */ jsxs8(
1686
+ "div",
1687
+ {
1688
+ style: {
1689
+ display: "flex",
1690
+ alignItems: "flex-start",
1691
+ gap: 8,
1692
+ padding: `6px ${pad}px`,
1693
+ borderBottom: `1px solid ${theme.border}`,
1694
+ opacity: 0.5
1695
+ },
1696
+ "data-fp": "memory-unchanged",
1697
+ children: [
1698
+ /* @__PURE__ */ jsx9(
1699
+ "span",
1700
+ {
1701
+ style: {
1702
+ width: 28,
1703
+ flexShrink: 0,
1704
+ lineHeight: 1.8
1705
+ }
1706
+ }
1707
+ ),
1708
+ /* @__PURE__ */ jsx9(
1709
+ "span",
1710
+ {
1711
+ style: {
1712
+ color: theme.textSecondary,
1713
+ fontWeight: 500,
1714
+ flexShrink: 0,
1715
+ lineHeight: 1.8
1716
+ },
1717
+ children: row.key
1718
+ }
1719
+ ),
1720
+ /* @__PURE__ */ jsx9("div", { style: { flex: 1, minWidth: 0, lineHeight: 1.8, color: theme.textMuted }, children: fmt2(row.value) })
1721
+ ]
1722
+ },
1723
+ row.key
1724
+ );
1725
+ })
1726
+ }
1727
+ )
1728
+ ] });
1729
+ }
1730
+ function UnstyledSimpleView({ snapshot }) {
1731
+ return /* @__PURE__ */ jsxs8("div", { "data-fp": "stage-detail-simple", children: [
1732
+ /* @__PURE__ */ jsx9("div", { "data-fp": "stage-label", children: snapshot.stageLabel }),
1733
+ snapshot.description && /* @__PURE__ */ jsx9("div", { "data-fp": "stage-description", children: snapshot.description }),
1734
+ snapshot.status && /* @__PURE__ */ jsx9("div", { "data-fp": "stage-status", children: snapshot.status }),
1735
+ snapshot.narrative && /* @__PURE__ */ jsx9("div", { "data-fp": "stage-narrative", children: snapshot.narrative })
1736
+ ] });
1737
+ }
1738
+ function UnstyledDevView({
1739
+ snapshot,
1740
+ changes,
1741
+ currMemory
1742
+ }) {
1743
+ const rows = useMemo6(() => buildMemoryRows(currMemory, changes), [currMemory, changes]);
1744
+ return /* @__PURE__ */ jsxs8("div", { "data-fp": "stage-detail-dev", children: [
1745
+ /* @__PURE__ */ jsx9("div", { "data-fp": "stage-label", children: snapshot.stageLabel }),
1746
+ rows.map((row) => {
1747
+ if (row.kind === "change") {
1748
+ const c = row.change;
1749
+ return /* @__PURE__ */ jsxs8("div", { "data-fp": "memory-change", "data-type": c.type, children: [
1750
+ /* @__PURE__ */ jsx9("span", { "data-fp": "change-key", children: c.key }),
1751
+ c.type === "updated" && /* @__PURE__ */ jsxs8(Fragment2, { children: [
1752
+ /* @__PURE__ */ jsx9("span", { "data-fp": "change-old", children: fmt2(c.oldValue) }),
1753
+ /* @__PURE__ */ jsx9("span", { "data-fp": "change-new", children: fmt2(c.newValue) })
1754
+ ] }),
1755
+ c.type === "added" && /* @__PURE__ */ jsx9("span", { "data-fp": "change-value", children: fmt2(c.newValue) }),
1756
+ c.type === "removed" && /* @__PURE__ */ jsx9("span", { "data-fp": "change-value", children: fmt2(c.oldValue) })
1757
+ ] }, c.key);
1758
+ }
1759
+ return /* @__PURE__ */ jsxs8("div", { "data-fp": "memory-unchanged", children: [
1760
+ /* @__PURE__ */ jsx9("span", { "data-fp": "unchanged-key", children: row.key }),
1761
+ /* @__PURE__ */ jsx9("span", { "data-fp": "unchanged-value", children: fmt2(row.value) })
1762
+ ] }, row.key);
1763
+ })
1764
+ ] });
1765
+ }
1766
+ function ModeToggle({
1767
+ activeMode,
1768
+ onToggle,
1769
+ fs,
1770
+ unstyled
1771
+ }) {
1772
+ if (unstyled) {
1773
+ return /* @__PURE__ */ jsx9("button", { "data-fp": "mode-toggle", "data-mode": activeMode, onClick: onToggle, children: activeMode === "simple" ? "Dev" : "Simple" });
1774
+ }
1775
+ return /* @__PURE__ */ jsx9(
1776
+ "div",
1777
+ {
1778
+ style: {
1779
+ display: "inline-flex",
1780
+ borderRadius: 6,
1781
+ border: `1px solid ${theme.border}`,
1782
+ overflow: "hidden",
1783
+ flexShrink: 0
1784
+ },
1785
+ "data-fp": "mode-toggle",
1786
+ children: ["simple", "dev"].map((m) => /* @__PURE__ */ jsx9(
1787
+ "button",
1788
+ {
1789
+ onClick: m !== activeMode ? onToggle : void 0,
1790
+ style: {
1791
+ padding: "4px 10px",
1792
+ fontSize: fs.small,
1793
+ fontWeight: m === activeMode ? 700 : 400,
1794
+ textTransform: "uppercase",
1795
+ letterSpacing: "0.05em",
1796
+ color: m === activeMode ? theme.textPrimary : theme.textMuted,
1797
+ background: m === activeMode ? theme.bgTertiary : "transparent",
1798
+ border: "none",
1799
+ cursor: m === activeMode ? "default" : "pointer"
1800
+ },
1801
+ children: m === "simple" ? "Simple" : "Dev"
1802
+ },
1803
+ m
1804
+ ))
1805
+ }
1806
+ );
1807
+ }
1808
+ function StageDetailPanel({
1809
+ snapshots,
1810
+ selectedIndex,
1811
+ mode: controlledMode,
1812
+ showToggle = false,
1813
+ onModeChange,
1814
+ size = "default",
1815
+ unstyled = false,
1816
+ className,
1817
+ style
1818
+ }) {
1819
+ const [internalMode, setInternalMode] = useState4(controlledMode ?? "simple");
1820
+ const activeMode = controlledMode ?? internalMode;
1821
+ const handleToggle = useCallback2(() => {
1822
+ const next = activeMode === "simple" ? "dev" : "simple";
1823
+ setInternalMode(next);
1824
+ onModeChange?.(next);
1825
+ }, [activeMode, onModeChange]);
1826
+ const snapshot = snapshots[selectedIndex];
1827
+ const prevMemory = selectedIndex > 0 ? snapshots[selectedIndex - 1]?.memory ?? null : null;
1828
+ const currMemory = snapshot?.memory ?? {};
1829
+ const changes = useMemo6(
1830
+ () => computeChanges(prevMemory, currMemory),
1831
+ [prevMemory, currMemory]
1832
+ );
1833
+ const fs = fontSize[size];
1834
+ const pad = padding[size];
1835
+ if (!snapshot) {
1836
+ return /* @__PURE__ */ jsx9("div", { className, style, "data-fp": "stage-detail-panel", children: /* @__PURE__ */ jsx9("div", { style: unstyled ? {} : { color: theme.textMuted, fontSize: fs.body, fontStyle: "italic", padding: pad }, children: "No stage selected" }) });
1837
+ }
1838
+ if (unstyled) {
1839
+ return /* @__PURE__ */ jsxs8("div", { className, style, "data-fp": "stage-detail-panel", "data-mode": activeMode, children: [
1840
+ showToggle && /* @__PURE__ */ jsx9(ModeToggle, { activeMode, onToggle: handleToggle, fs, unstyled: true }),
1841
+ activeMode === "simple" ? /* @__PURE__ */ jsx9(UnstyledSimpleView, { snapshot }) : /* @__PURE__ */ jsx9(UnstyledDevView, { snapshot, changes, currMemory })
1842
+ ] });
1843
+ }
1844
+ return /* @__PURE__ */ jsxs8(
1845
+ "div",
1846
+ {
1847
+ className,
1848
+ style: {
1849
+ padding: pad,
1850
+ fontFamily: theme.fontSans,
1851
+ overflow: "auto",
1852
+ ...style
1853
+ },
1854
+ "data-fp": "stage-detail-panel",
1855
+ "data-mode": activeMode,
1856
+ children: [
1857
+ showToggle && /* @__PURE__ */ jsx9("div", { style: { display: "flex", justifyContent: "flex-end", marginBottom: 12 }, children: /* @__PURE__ */ jsx9(ModeToggle, { activeMode, onToggle: handleToggle, fs, unstyled: false }) }),
1858
+ activeMode === "simple" ? /* @__PURE__ */ jsx9(SimpleView, { snapshot, fs, pad }) : /* @__PURE__ */ jsx9(DevView, { snapshot, changes, currMemory, fs, pad })
1859
+ ]
1860
+ }
1861
+ );
1862
+ }
1863
+
1333
1864
  // src/components/TimeTravelControls/TimeTravelControls.tsx
1334
- import { useState as useState3, useEffect as useEffect2, useRef as useRef2, useCallback as useCallback2 } from "react";
1335
- import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
1865
+ import { useState as useState5, useEffect as useEffect3, useRef as useRef3, useCallback as useCallback3 } from "react";
1866
+ import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
1336
1867
  function TimeTravelControls({
1337
1868
  snapshots,
1338
1869
  selectedIndex,
@@ -1343,12 +1874,12 @@ function TimeTravelControls({
1343
1874
  className,
1344
1875
  style
1345
1876
  }) {
1346
- const [playing, setPlaying] = useState3(false);
1347
- const playRef = useRef2(null);
1877
+ const [playing, setPlaying] = useState5(false);
1878
+ const playRef = useRef3(null);
1348
1879
  const total = snapshots.length;
1349
1880
  const canPrev = selectedIndex > 0;
1350
1881
  const canNext = selectedIndex < total - 1;
1351
- useEffect2(() => {
1882
+ useEffect3(() => {
1352
1883
  if (!playing || !autoPlayable) return;
1353
1884
  if (selectedIndex >= total - 1) {
1354
1885
  setPlaying(false);
@@ -1366,7 +1897,7 @@ function TimeTravelControls({
1366
1897
  if (playRef.current) clearTimeout(playRef.current);
1367
1898
  };
1368
1899
  }, [playing, selectedIndex, snapshots, total, onIndexChange, autoPlayable]);
1369
- const togglePlay = useCallback2(() => {
1900
+ const togglePlay = useCallback3(() => {
1370
1901
  if (playing) {
1371
1902
  setPlaying(false);
1372
1903
  } else {
@@ -1376,8 +1907,8 @@ function TimeTravelControls({
1376
1907
  }, [playing, selectedIndex, total, onIndexChange]);
1377
1908
  const fs = fontSize[size];
1378
1909
  if (unstyled) {
1379
- return /* @__PURE__ */ jsxs8("div", { className, style, "data-fp": "time-travel-controls", children: [
1380
- /* @__PURE__ */ jsx9(
1910
+ return /* @__PURE__ */ jsxs9("div", { className, style, "data-fp": "time-travel-controls", children: [
1911
+ /* @__PURE__ */ jsx10(
1381
1912
  "button",
1382
1913
  {
1383
1914
  "data-fp": "tt-prev",
@@ -1389,8 +1920,8 @@ function TimeTravelControls({
1389
1920
  children: "Prev"
1390
1921
  }
1391
1922
  ),
1392
- autoPlayable && /* @__PURE__ */ jsx9("button", { "data-fp": "tt-play", onClick: togglePlay, children: playing ? "Pause" : "Play" }),
1393
- /* @__PURE__ */ jsx9(
1923
+ autoPlayable && /* @__PURE__ */ jsx10("button", { "data-fp": "tt-play", onClick: togglePlay, children: playing ? "Pause" : "Play" }),
1924
+ /* @__PURE__ */ jsx10(
1394
1925
  "button",
1395
1926
  {
1396
1927
  "data-fp": "tt-next",
@@ -1402,7 +1933,7 @@ function TimeTravelControls({
1402
1933
  children: "Next"
1403
1934
  }
1404
1935
  ),
1405
- /* @__PURE__ */ jsx9("div", { "data-fp": "tt-ticks", children: snapshots.map((snap, i) => /* @__PURE__ */ jsx9(
1936
+ /* @__PURE__ */ jsx10("div", { "data-fp": "tt-ticks", children: snapshots.map((snap, i) => /* @__PURE__ */ jsx10(
1406
1937
  "button",
1407
1938
  {
1408
1939
  "data-fp": "tt-tick",
@@ -1430,7 +1961,7 @@ function TimeTravelControls({
1430
1961
  opacity: disabled ? 0.5 : 1,
1431
1962
  flexShrink: 0
1432
1963
  });
1433
- return /* @__PURE__ */ jsxs8(
1964
+ return /* @__PURE__ */ jsxs9(
1434
1965
  "div",
1435
1966
  {
1436
1967
  className,
@@ -1446,7 +1977,7 @@ function TimeTravelControls({
1446
1977
  },
1447
1978
  "data-fp": "time-travel-controls",
1448
1979
  children: [
1449
- /* @__PURE__ */ jsx9(
1980
+ /* @__PURE__ */ jsx10(
1450
1981
  "button",
1451
1982
  {
1452
1983
  style: btnStyle(!canPrev || playing),
@@ -1458,7 +1989,7 @@ function TimeTravelControls({
1458
1989
  children: "\u25C0"
1459
1990
  }
1460
1991
  ),
1461
- autoPlayable && /* @__PURE__ */ jsx9(
1992
+ autoPlayable && /* @__PURE__ */ jsx10(
1462
1993
  "button",
1463
1994
  {
1464
1995
  onClick: togglePlay,
@@ -1480,7 +2011,7 @@ function TimeTravelControls({
1480
2011
  children: playing ? "\u23F8" : "\u25B6"
1481
2012
  }
1482
2013
  ),
1483
- /* @__PURE__ */ jsx9(
2014
+ /* @__PURE__ */ jsx10(
1484
2015
  "button",
1485
2016
  {
1486
2017
  style: btnStyle(!canNext || playing),
@@ -1492,7 +2023,7 @@ function TimeTravelControls({
1492
2023
  children: "\u25B6"
1493
2024
  }
1494
2025
  ),
1495
- /* @__PURE__ */ jsx9(
2026
+ /* @__PURE__ */ jsx10(
1496
2027
  "div",
1497
2028
  {
1498
2029
  style: {
@@ -1505,7 +2036,7 @@ function TimeTravelControls({
1505
2036
  children: snapshots.map((snap, i) => {
1506
2037
  const isActive = i === selectedIndex;
1507
2038
  const isDone = i < selectedIndex;
1508
- return /* @__PURE__ */ jsx9(
2039
+ return /* @__PURE__ */ jsx10(
1509
2040
  "button",
1510
2041
  {
1511
2042
  onClick: () => {
@@ -1535,8 +2066,8 @@ function TimeTravelControls({
1535
2066
  }
1536
2067
 
1537
2068
  // src/components/ExplainableShell/ExplainableShell.tsx
1538
- import { useState as useState4, useCallback as useCallback3, useMemo as useMemo6 } from "react";
1539
- import { Fragment as Fragment2, jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
2069
+ import { useState as useState6, useCallback as useCallback4, useMemo as useMemo7 } from "react";
2070
+ import { Fragment as Fragment3, jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
1540
2071
  function ExplainableShell({
1541
2072
  snapshots,
1542
2073
  resultData,
@@ -1551,14 +2082,14 @@ function ExplainableShell({
1551
2082
  className,
1552
2083
  style
1553
2084
  }) {
1554
- const [activeTab, setActiveTab] = useState4(defaultTab ?? tabs[0]);
1555
- const [snapshotIdx, setSnapshotIdx] = useState4(0);
2085
+ const [activeTab, setActiveTab] = useState6(defaultTab ?? tabs[0]);
2086
+ const [snapshotIdx, setSnapshotIdx] = useState6(0);
1556
2087
  const fs = fontSize[size];
1557
2088
  const pad = padding[size];
1558
- const handleSnapshotChange = useCallback3((idx) => {
2089
+ const handleSnapshotChange = useCallback4((idx) => {
1559
2090
  setSnapshotIdx(Math.max(0, Math.min(idx, snapshots.length - 1)));
1560
2091
  }, [snapshots.length]);
1561
- const revealedCount = useMemo6(() => {
2092
+ const revealedCount = useMemo7(() => {
1562
2093
  if (snapshots.length === 0 || narrative.length === 0) return narrative.length;
1563
2094
  const boundaries = [];
1564
2095
  for (let i = 0; i < narrative.length; i++) {
@@ -1589,8 +2120,8 @@ function ExplainableShell({
1589
2120
  "ai-compatible": "AI-Compatible"
1590
2121
  };
1591
2122
  if (unstyled) {
1592
- return /* @__PURE__ */ jsxs9("div", { className, style, "data-fp": "explainable-shell", children: [
1593
- /* @__PURE__ */ jsx10("div", { "data-fp": "shell-tabs", children: tabs.map((tab) => /* @__PURE__ */ jsx10(
2123
+ return /* @__PURE__ */ jsxs10("div", { className, style, "data-fp": "explainable-shell", children: [
2124
+ /* @__PURE__ */ jsx11("div", { "data-fp": "shell-tabs", children: tabs.map((tab) => /* @__PURE__ */ jsx11(
1594
2125
  "button",
1595
2126
  {
1596
2127
  "data-fp": "shell-tab",
@@ -1600,8 +2131,8 @@ function ExplainableShell({
1600
2131
  },
1601
2132
  tab
1602
2133
  )) }),
1603
- /* @__PURE__ */ jsxs9("div", { "data-fp": "shell-content", "data-tab": activeTab, children: [
1604
- activeTab === "result" && /* @__PURE__ */ jsx10(
2134
+ /* @__PURE__ */ jsxs10("div", { "data-fp": "shell-content", "data-tab": activeTab, children: [
2135
+ activeTab === "result" && /* @__PURE__ */ jsx11(
1605
2136
  ResultPanel,
1606
2137
  {
1607
2138
  data: resultData ?? null,
@@ -1610,8 +2141,8 @@ function ExplainableShell({
1610
2141
  unstyled: true
1611
2142
  }
1612
2143
  ),
1613
- activeTab === "explainable" && /* @__PURE__ */ jsxs9(Fragment2, { children: [
1614
- /* @__PURE__ */ jsx10(
2144
+ activeTab === "explainable" && /* @__PURE__ */ jsxs10(Fragment3, { children: [
2145
+ /* @__PURE__ */ jsx11(
1615
2146
  TimeTravelControls,
1616
2147
  {
1617
2148
  snapshots,
@@ -1621,9 +2152,9 @@ function ExplainableShell({
1621
2152
  }
1622
2153
  ),
1623
2154
  renderFlowchart?.({ snapshots, selectedIndex: snapshotIdx, onNodeClick: handleSnapshotChange }),
1624
- /* @__PURE__ */ jsx10(MemoryInspector, { snapshots, selectedIndex: snapshotIdx, unstyled: true }),
1625
- /* @__PURE__ */ jsx10(ScopeDiff, { previous: prevMemory, current: currMemory, unstyled: true }),
1626
- /* @__PURE__ */ jsx10(
2155
+ /* @__PURE__ */ jsx11(MemoryInspector, { snapshots, selectedIndex: snapshotIdx, unstyled: true }),
2156
+ /* @__PURE__ */ jsx11(ScopeDiff, { previous: prevMemory, current: currMemory, unstyled: true }),
2157
+ /* @__PURE__ */ jsx11(
1627
2158
  GanttTimeline,
1628
2159
  {
1629
2160
  snapshots,
@@ -1633,8 +2164,8 @@ function ExplainableShell({
1633
2164
  }
1634
2165
  )
1635
2166
  ] }),
1636
- activeTab === "ai-compatible" && /* @__PURE__ */ jsxs9(Fragment2, { children: [
1637
- /* @__PURE__ */ jsx10(
2167
+ activeTab === "ai-compatible" && /* @__PURE__ */ jsxs10(Fragment3, { children: [
2168
+ /* @__PURE__ */ jsx11(
1638
2169
  TimeTravelControls,
1639
2170
  {
1640
2171
  snapshots,
@@ -1644,7 +2175,7 @@ function ExplainableShell({
1644
2175
  }
1645
2176
  ),
1646
2177
  renderFlowchart?.({ snapshots, selectedIndex: snapshotIdx, onNodeClick: handleSnapshotChange }),
1647
- /* @__PURE__ */ jsx10(
2178
+ /* @__PURE__ */ jsx11(
1648
2179
  NarrativeTrace,
1649
2180
  {
1650
2181
  narrative,
@@ -1656,7 +2187,7 @@ function ExplainableShell({
1656
2187
  ] })
1657
2188
  ] });
1658
2189
  }
1659
- return /* @__PURE__ */ jsxs9(
2190
+ return /* @__PURE__ */ jsxs10(
1660
2191
  "div",
1661
2192
  {
1662
2193
  className,
@@ -1672,7 +2203,7 @@ function ExplainableShell({
1672
2203
  },
1673
2204
  "data-fp": "explainable-shell",
1674
2205
  children: [
1675
- /* @__PURE__ */ jsx10(
2206
+ /* @__PURE__ */ jsx11(
1676
2207
  "div",
1677
2208
  {
1678
2209
  style: {
@@ -1684,7 +2215,7 @@ function ExplainableShell({
1684
2215
  },
1685
2216
  children: tabs.map((tab) => {
1686
2217
  const active = tab === activeTab;
1687
- return /* @__PURE__ */ jsx10(
2218
+ return /* @__PURE__ */ jsx11(
1688
2219
  "button",
1689
2220
  {
1690
2221
  onClick: () => setActiveTab(tab),
@@ -1708,8 +2239,8 @@ function ExplainableShell({
1708
2239
  })
1709
2240
  }
1710
2241
  ),
1711
- /* @__PURE__ */ jsxs9("div", { style: { flex: 1, overflow: "hidden", display: "flex", flexDirection: "column" }, children: [
1712
- activeTab === "result" && /* @__PURE__ */ jsx10(
2242
+ /* @__PURE__ */ jsxs10("div", { style: { flex: 1, overflow: "hidden", display: "flex", flexDirection: "column" }, children: [
2243
+ activeTab === "result" && /* @__PURE__ */ jsx11(
1713
2244
  ResultPanel,
1714
2245
  {
1715
2246
  data: resultData ?? null,
@@ -1718,8 +2249,8 @@ function ExplainableShell({
1718
2249
  size
1719
2250
  }
1720
2251
  ),
1721
- activeTab === "explainable" && /* @__PURE__ */ jsxs9(Fragment2, { children: [
1722
- /* @__PURE__ */ jsx10(
2252
+ activeTab === "explainable" && /* @__PURE__ */ jsxs10(Fragment3, { children: [
2253
+ /* @__PURE__ */ jsx11(
1723
2254
  TimeTravelControls,
1724
2255
  {
1725
2256
  snapshots,
@@ -1728,9 +2259,9 @@ function ExplainableShell({
1728
2259
  size
1729
2260
  }
1730
2261
  ),
1731
- /* @__PURE__ */ jsxs9("div", { style: { flex: 1, display: "flex", overflow: "hidden" }, children: [
1732
- renderFlowchart && /* @__PURE__ */ jsx10("div", { style: { flex: 1, overflow: "hidden", borderRight: `1px solid ${theme.border}` }, children: renderFlowchart({ snapshots, selectedIndex: snapshotIdx, onNodeClick: handleSnapshotChange }) }),
1733
- /* @__PURE__ */ jsxs9(
2262
+ /* @__PURE__ */ jsxs10("div", { style: { flex: 1, display: "flex", overflow: "hidden" }, children: [
2263
+ renderFlowchart && /* @__PURE__ */ jsx11("div", { style: { flex: 1, overflow: "hidden", borderRight: `1px solid ${theme.border}` }, children: renderFlowchart({ snapshots, selectedIndex: snapshotIdx, onNodeClick: handleSnapshotChange }) }),
2264
+ /* @__PURE__ */ jsxs10(
1734
2265
  "div",
1735
2266
  {
1736
2267
  style: {
@@ -1741,7 +2272,7 @@ function ExplainableShell({
1741
2272
  flexDirection: "column"
1742
2273
  },
1743
2274
  children: [
1744
- /* @__PURE__ */ jsx10(
2275
+ /* @__PURE__ */ jsx11(
1745
2276
  MemoryInspector,
1746
2277
  {
1747
2278
  snapshots,
@@ -1749,7 +2280,7 @@ function ExplainableShell({
1749
2280
  size
1750
2281
  }
1751
2282
  ),
1752
- /* @__PURE__ */ jsx10("div", { style: { borderTop: `1px solid ${theme.border}` }, children: /* @__PURE__ */ jsx10(
2283
+ /* @__PURE__ */ jsx11("div", { style: { borderTop: `1px solid ${theme.border}` }, children: /* @__PURE__ */ jsx11(
1753
2284
  ScopeDiff,
1754
2285
  {
1755
2286
  previous: prevMemory,
@@ -1762,7 +2293,7 @@ function ExplainableShell({
1762
2293
  }
1763
2294
  )
1764
2295
  ] }),
1765
- /* @__PURE__ */ jsx10("div", { style: { borderTop: `1px solid ${theme.border}`, flexShrink: 0 }, children: /* @__PURE__ */ jsx10(
2296
+ /* @__PURE__ */ jsx11("div", { style: { borderTop: `1px solid ${theme.border}`, flexShrink: 0 }, children: /* @__PURE__ */ jsx11(
1766
2297
  GanttTimeline,
1767
2298
  {
1768
2299
  snapshots,
@@ -1772,8 +2303,8 @@ function ExplainableShell({
1772
2303
  }
1773
2304
  ) })
1774
2305
  ] }),
1775
- activeTab === "ai-compatible" && /* @__PURE__ */ jsxs9(Fragment2, { children: [
1776
- /* @__PURE__ */ jsx10(
2306
+ activeTab === "ai-compatible" && /* @__PURE__ */ jsxs10(Fragment3, { children: [
2307
+ /* @__PURE__ */ jsx11(
1777
2308
  TimeTravelControls,
1778
2309
  {
1779
2310
  snapshots,
@@ -1782,9 +2313,9 @@ function ExplainableShell({
1782
2313
  size
1783
2314
  }
1784
2315
  ),
1785
- /* @__PURE__ */ jsxs9("div", { style: { flex: 1, display: "flex", overflow: "hidden" }, children: [
1786
- renderFlowchart && /* @__PURE__ */ jsx10("div", { style: { flex: 1, overflow: "hidden", borderRight: `1px solid ${theme.border}` }, children: renderFlowchart({ snapshots, selectedIndex: snapshotIdx, onNodeClick: handleSnapshotChange }) }),
1787
- /* @__PURE__ */ jsx10(
2316
+ /* @__PURE__ */ jsxs10("div", { style: { flex: 1, display: "flex", overflow: "hidden" }, children: [
2317
+ renderFlowchart && /* @__PURE__ */ jsx11("div", { style: { flex: 1, overflow: "hidden", borderRight: `1px solid ${theme.border}` }, children: renderFlowchart({ snapshots, selectedIndex: snapshotIdx, onNodeClick: handleSnapshotChange }) }),
2318
+ /* @__PURE__ */ jsx11(
1788
2319
  NarrativeTrace,
1789
2320
  {
1790
2321
  narrative,
@@ -1804,40 +2335,276 @@ function ExplainableShell({
1804
2335
  );
1805
2336
  }
1806
2337
 
2338
+ // src/components/FlowchartView/SubflowTree.tsx
2339
+ import { memo, useState as useState7, useCallback as useCallback5, useMemo as useMemo8 } from "react";
2340
+ import { Fragment as Fragment4, jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
2341
+ function specToTree(node) {
2342
+ const entries = [];
2343
+ const seen = /* @__PURE__ */ new Set();
2344
+ function walk(n) {
2345
+ const id = n.name || n.id || "";
2346
+ if (seen.has(id)) return;
2347
+ seen.add(id);
2348
+ const entry = {
2349
+ name: n.name,
2350
+ description: n.description,
2351
+ subflowId: n.subflowId,
2352
+ isSubflow: !!n.isSubflowRoot
2353
+ };
2354
+ if (n.isSubflowRoot && n.subflowStructure) {
2355
+ entry.children = specToTree(n.subflowStructure);
2356
+ }
2357
+ entries.push(entry);
2358
+ if (n.children) {
2359
+ for (const child of n.children) {
2360
+ walk(child);
2361
+ }
2362
+ }
2363
+ if (n.next) {
2364
+ walk(n.next);
2365
+ }
2366
+ }
2367
+ walk(node);
2368
+ return entries;
2369
+ }
2370
+ var TreeNode = memo(function TreeNode2({
2371
+ entry,
2372
+ depth,
2373
+ activeStage,
2374
+ doneStages,
2375
+ onNodeSelect
2376
+ }) {
2377
+ const [expanded, setExpanded] = useState7(true);
2378
+ const hasChildren = entry.children && entry.children.length > 0;
2379
+ const isActive = activeStage === entry.name;
2380
+ const isDone = doneStages?.has(entry.name);
2381
+ const handleClick = useCallback5(() => {
2382
+ if (hasChildren) {
2383
+ setExpanded((prev) => !prev);
2384
+ }
2385
+ onNodeSelect?.(entry.name, !!entry.isSubflow);
2386
+ }, [hasChildren, onNodeSelect, entry.name, entry.isSubflow]);
2387
+ return /* @__PURE__ */ jsxs11(Fragment4, { children: [
2388
+ /* @__PURE__ */ jsxs11(
2389
+ "button",
2390
+ {
2391
+ onClick: handleClick,
2392
+ "data-fp": "subflow-tree-node",
2393
+ style: {
2394
+ display: "flex",
2395
+ alignItems: "center",
2396
+ gap: 6,
2397
+ width: "100%",
2398
+ border: "none",
2399
+ background: isActive ? `color-mix(in srgb, ${theme.primary} 15%, transparent)` : "transparent",
2400
+ cursor: "pointer",
2401
+ padding: `4px 8px 4px ${8 + depth * 16}px`,
2402
+ fontFamily: theme.fontSans,
2403
+ fontSize: 12,
2404
+ textAlign: "left",
2405
+ borderRadius: 4,
2406
+ transition: "background 0.15s"
2407
+ },
2408
+ onMouseEnter: (e) => {
2409
+ if (!isActive) {
2410
+ e.currentTarget.style.background = `color-mix(in srgb, ${theme.textMuted} 10%, transparent)`;
2411
+ }
2412
+ },
2413
+ onMouseLeave: (e) => {
2414
+ if (!isActive) {
2415
+ e.currentTarget.style.background = "transparent";
2416
+ }
2417
+ },
2418
+ children: [
2419
+ hasChildren ? /* @__PURE__ */ jsx12(
2420
+ "span",
2421
+ {
2422
+ style: {
2423
+ fontSize: 10,
2424
+ color: theme.textMuted,
2425
+ width: 12,
2426
+ textAlign: "center",
2427
+ flexShrink: 0,
2428
+ transition: "transform 0.15s",
2429
+ transform: expanded ? "rotate(90deg)" : "rotate(0deg)",
2430
+ display: "inline-block"
2431
+ },
2432
+ children: "\u25B6"
2433
+ }
2434
+ ) : /* @__PURE__ */ jsx12("span", { style: { width: 12, flexShrink: 0 } }),
2435
+ /* @__PURE__ */ jsx12(
2436
+ "span",
2437
+ {
2438
+ style: {
2439
+ width: 6,
2440
+ height: 6,
2441
+ borderRadius: "50%",
2442
+ flexShrink: 0,
2443
+ background: isActive ? theme.primary : isDone ? theme.success : theme.border
2444
+ }
2445
+ }
2446
+ ),
2447
+ /* @__PURE__ */ jsxs11("span", { style: { display: "flex", flexDirection: "column", minWidth: 0 }, children: [
2448
+ /* @__PURE__ */ jsxs11(
2449
+ "span",
2450
+ {
2451
+ style: {
2452
+ color: isActive ? theme.primary : isDone ? theme.textPrimary : theme.textSecondary,
2453
+ fontWeight: isActive ? 600 : entry.isSubflow ? 500 : 400,
2454
+ whiteSpace: "nowrap",
2455
+ overflow: "hidden",
2456
+ textOverflow: "ellipsis"
2457
+ },
2458
+ children: [
2459
+ entry.name,
2460
+ entry.isSubflow && /* @__PURE__ */ jsx12("span", { style: { opacity: 0.5, marginLeft: 4, fontSize: 10 }, children: "\u229E" })
2461
+ ]
2462
+ }
2463
+ ),
2464
+ entry.description && /* @__PURE__ */ jsx12(
2465
+ "span",
2466
+ {
2467
+ style: {
2468
+ color: theme.textMuted,
2469
+ fontSize: 10,
2470
+ whiteSpace: "nowrap",
2471
+ overflow: "hidden",
2472
+ textOverflow: "ellipsis"
2473
+ },
2474
+ children: entry.description
2475
+ }
2476
+ )
2477
+ ] })
2478
+ ]
2479
+ }
2480
+ ),
2481
+ hasChildren && expanded && /* @__PURE__ */ jsx12("div", { children: entry.children.map((child, i) => /* @__PURE__ */ jsx12(
2482
+ TreeNode2,
2483
+ {
2484
+ entry: child,
2485
+ depth: depth + 1,
2486
+ activeStage,
2487
+ doneStages,
2488
+ onNodeSelect
2489
+ },
2490
+ `${child.name}-${i}`
2491
+ )) })
2492
+ ] });
2493
+ });
2494
+ var SectionLabel = memo(function SectionLabel2({ children }) {
2495
+ return /* @__PURE__ */ jsx12(
2496
+ "div",
2497
+ {
2498
+ style: {
2499
+ padding: "4px 12px 8px",
2500
+ fontSize: 10,
2501
+ fontWeight: 600,
2502
+ textTransform: "uppercase",
2503
+ letterSpacing: "0.05em",
2504
+ color: theme.textMuted
2505
+ },
2506
+ children
2507
+ }
2508
+ );
2509
+ });
2510
+ var SubflowTree = memo(function SubflowTree2({
2511
+ spec,
2512
+ activeStage,
2513
+ doneStages,
2514
+ onNodeSelect,
2515
+ unstyled = false,
2516
+ className,
2517
+ style
2518
+ }) {
2519
+ const tree = useMemo8(() => specToTree(spec), [spec]);
2520
+ const mainStages = useMemo8(() => tree.filter((e) => !e.isSubflow), [tree]);
2521
+ const subflowStages = useMemo8(() => tree.filter((e) => e.isSubflow), [tree]);
2522
+ const renderEntries = (entries) => entries.map((entry, i) => /* @__PURE__ */ jsx12(
2523
+ TreeNode,
2524
+ {
2525
+ entry,
2526
+ depth: 0,
2527
+ activeStage,
2528
+ doneStages,
2529
+ onNodeSelect
2530
+ },
2531
+ `${entry.name}-${i}`
2532
+ ));
2533
+ return /* @__PURE__ */ jsxs11(
2534
+ "div",
2535
+ {
2536
+ className,
2537
+ "data-fp": "subflow-tree",
2538
+ style: {
2539
+ ...unstyled ? {} : {
2540
+ fontFamily: theme.fontSans,
2541
+ fontSize: 12,
2542
+ background: theme.bgPrimary,
2543
+ borderRight: `1px solid ${theme.border}`,
2544
+ overflowY: "auto",
2545
+ overflowX: "hidden",
2546
+ padding: "8px 0"
2547
+ },
2548
+ ...style
2549
+ },
2550
+ children: [
2551
+ !unstyled && /* @__PURE__ */ jsx12(SectionLabel, { children: "Flowchart" }),
2552
+ renderEntries(mainStages),
2553
+ subflowStages.length > 0 && /* @__PURE__ */ jsxs11(Fragment4, { children: [
2554
+ !unstyled && /* @__PURE__ */ jsx12("div", { style: { height: 1, background: theme.border, margin: "8px 12px" } }),
2555
+ !unstyled && /* @__PURE__ */ jsx12(SectionLabel, { children: "Subflows" }),
2556
+ renderEntries(subflowStages)
2557
+ ] })
2558
+ ]
2559
+ }
2560
+ );
2561
+ });
2562
+
1807
2563
  // src/adapters/fromRuntimeSnapshot.ts
1808
2564
  function toVisualizationSnapshots(runtime) {
1809
2565
  const snapshots = [];
1810
- flattenTree(runtime.executionTree, snapshots, runtime.sharedState);
2566
+ flattenTree(runtime.executionTree, snapshots, runtime.sharedState, 0, runtime.subflowResults, {});
1811
2567
  return snapshots;
1812
2568
  }
1813
- function flattenTree(node, out, sharedState, accumulatedMs = 0) {
2569
+ function flattenTree(node, out, sharedState, accumulatedMs = 0, subflowResults, cumulativeMemory = {}) {
1814
2570
  const durationMs = typeof node.metrics?.durationMs === "number" ? node.metrics.durationMs : 1;
1815
2571
  const startMs = accumulatedMs;
1816
2572
  const narrative = buildNarrative(node);
1817
- const memory = {};
1818
- if (node.logs) {
1819
- Object.assign(memory, node.logs);
2573
+ const memory = { ...cumulativeMemory };
2574
+ if (node.stageWrites) {
2575
+ for (const [key, value] of Object.entries(node.stageWrites)) {
2576
+ if (value === void 0) {
2577
+ delete memory[key];
2578
+ } else {
2579
+ memory[key] = value;
2580
+ }
2581
+ }
1820
2582
  }
2583
+ const stageId = node.name || node.id;
2584
+ const sfResult = subflowResults?.[node.subflowId ?? stageId];
1821
2585
  out.push({
1822
- stageName: node.id,
1823
- stageLabel: node.name || node.id,
2586
+ stageName: stageId,
2587
+ stageLabel: stageId,
1824
2588
  memory,
1825
2589
  narrative,
1826
2590
  startMs,
1827
2591
  durationMs,
1828
- status: "done"
2592
+ status: "done",
2593
+ ...node.description ? { description: node.description } : void 0,
2594
+ ...node.subflowId ? { subflowId: node.subflowId } : void 0,
2595
+ ...sfResult ? { subflowResult: sfResult } : void 0
1829
2596
  });
1830
2597
  let nextMs = startMs + durationMs;
1831
2598
  if (node.children && node.children.length > 0) {
1832
2599
  let maxChildEnd = nextMs;
1833
2600
  for (const child of node.children) {
1834
- const childEnd = flattenTree(child, out, sharedState, nextMs);
2601
+ const childEnd = flattenTree(child, out, sharedState, nextMs, subflowResults, memory);
1835
2602
  maxChildEnd = Math.max(maxChildEnd, childEnd);
1836
2603
  }
1837
2604
  nextMs = maxChildEnd;
1838
2605
  }
1839
2606
  if (node.next) {
1840
- nextMs = flattenTree(node.next, out, sharedState, nextMs);
2607
+ nextMs = flattenTree(node.next, out, sharedState, nextMs, subflowResults, memory);
1841
2608
  }
1842
2609
  return nextMs;
1843
2610
  }
@@ -1846,8 +2613,8 @@ function buildNarrative(node) {
1846
2613
  if (node.name) {
1847
2614
  parts.push(`Stage "${node.name}" executed.`);
1848
2615
  }
1849
- if (node.logs && Object.keys(node.logs).length > 0) {
1850
- const keys = Object.keys(node.logs);
2616
+ if (node.stageWrites && Object.keys(node.stageWrites).length > 0) {
2617
+ const keys = Object.keys(node.stageWrites);
1851
2618
  parts.push(`Wrote ${keys.length} key(s): ${keys.join(", ")}.`);
1852
2619
  }
1853
2620
  if (node.errors && Object.keys(node.errors).length > 0) {
@@ -1871,7 +2638,9 @@ function createSnapshots(stages) {
1871
2638
  narrative: s.narrative ?? `${s.label ?? s.name} completed.`,
1872
2639
  startMs: accMs,
1873
2640
  durationMs: duration,
1874
- status: "done"
2641
+ status: "done",
2642
+ ...s.description ? { description: s.description } : void 0,
2643
+ ...s.subflowId ? { subflowId: s.subflowId } : void 0
1875
2644
  };
1876
2645
  accMs += duration;
1877
2646
  return snap;
@@ -1887,6 +2656,8 @@ export {
1887
2656
  ResultPanel,
1888
2657
  ScopeDiff,
1889
2658
  SnapshotPanel,
2659
+ StageDetailPanel,
2660
+ SubflowTree,
1890
2661
  TimeTravelControls,
1891
2662
  coolDark,
1892
2663
  createSnapshots,