footprint-explainable-ui 0.6.0 → 0.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +158 -401
- package/dist/flowchart.cjs +721 -485
- package/dist/flowchart.cjs.map +1 -1
- package/dist/flowchart.d.cts +68 -15
- package/dist/flowchart.d.ts +68 -15
- package/dist/flowchart.js +723 -488
- package/dist/flowchart.js.map +1 -1
- package/dist/index.cjs +1158 -436
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +115 -31
- package/dist/index.d.ts +115 -31
- package/dist/index.js +1159 -442
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -24,18 +24,23 @@ __export(src_exports, {
|
|
|
24
24
|
FootprintTheme: () => FootprintTheme,
|
|
25
25
|
GanttTimeline: () => GanttTimeline,
|
|
26
26
|
MemoryInspector: () => MemoryInspector,
|
|
27
|
+
MemoryPanel: () => MemoryPanel,
|
|
27
28
|
NarrativeLog: () => NarrativeLog,
|
|
29
|
+
NarrativePanel: () => NarrativePanel,
|
|
28
30
|
NarrativeTrace: () => NarrativeTrace,
|
|
29
31
|
ResultPanel: () => ResultPanel,
|
|
30
32
|
ScopeDiff: () => ScopeDiff,
|
|
31
33
|
SnapshotPanel: () => SnapshotPanel,
|
|
32
34
|
StageDetailPanel: () => StageDetailPanel,
|
|
35
|
+
StoryNarrative: () => StoryNarrative,
|
|
33
36
|
SubflowTree: () => SubflowTree,
|
|
34
37
|
TimeTravelControls: () => TimeTravelControls,
|
|
35
38
|
coolDark: () => coolDark,
|
|
36
39
|
coolLight: () => coolLight,
|
|
37
40
|
createSnapshots: () => createSnapshots,
|
|
38
41
|
defaultTokens: () => defaultTokens,
|
|
42
|
+
rawDefaults: () => rawDefaults,
|
|
43
|
+
subflowResultToSnapshots: () => subflowResultToSnapshots,
|
|
39
44
|
themePresets: () => themePresets,
|
|
40
45
|
toVisualizationSnapshots: () => toVisualizationSnapshots,
|
|
41
46
|
tokensToCSSVars: () => tokensToCSSVars,
|
|
@@ -71,7 +76,7 @@ function tokensToCSSVars(tokens) {
|
|
|
71
76
|
if (tokens.fontFamily?.mono) vars["--fp-font-mono"] = tokens.fontFamily.mono;
|
|
72
77
|
return vars;
|
|
73
78
|
}
|
|
74
|
-
var
|
|
79
|
+
var rawDefaults = {
|
|
75
80
|
colors: {
|
|
76
81
|
primary: "#6366f1",
|
|
77
82
|
success: "#22c55e",
|
|
@@ -91,6 +96,26 @@ var defaultTokens = {
|
|
|
91
96
|
mono: "'JetBrains Mono', 'Fira Code', monospace"
|
|
92
97
|
}
|
|
93
98
|
};
|
|
99
|
+
var defaultTokens = {
|
|
100
|
+
colors: {
|
|
101
|
+
primary: `var(--fp-color-primary, ${rawDefaults.colors.primary})`,
|
|
102
|
+
success: `var(--fp-color-success, ${rawDefaults.colors.success})`,
|
|
103
|
+
error: `var(--fp-color-error, ${rawDefaults.colors.error})`,
|
|
104
|
+
warning: `var(--fp-color-warning, ${rawDefaults.colors.warning})`,
|
|
105
|
+
bgPrimary: `var(--fp-bg-primary, ${rawDefaults.colors.bgPrimary})`,
|
|
106
|
+
bgSecondary: `var(--fp-bg-secondary, ${rawDefaults.colors.bgSecondary})`,
|
|
107
|
+
bgTertiary: `var(--fp-bg-tertiary, ${rawDefaults.colors.bgTertiary})`,
|
|
108
|
+
textPrimary: `var(--fp-text-primary, ${rawDefaults.colors.textPrimary})`,
|
|
109
|
+
textSecondary: `var(--fp-text-secondary, ${rawDefaults.colors.textSecondary})`,
|
|
110
|
+
textMuted: `var(--fp-text-muted, ${rawDefaults.colors.textMuted})`,
|
|
111
|
+
border: `var(--fp-border, ${rawDefaults.colors.border})`
|
|
112
|
+
},
|
|
113
|
+
radius: `var(--fp-radius, ${rawDefaults.radius})`,
|
|
114
|
+
fontFamily: {
|
|
115
|
+
sans: `var(--fp-font-sans, ${rawDefaults.fontFamily.sans})`,
|
|
116
|
+
mono: `var(--fp-font-mono, ${rawDefaults.fontFamily.mono})`
|
|
117
|
+
}
|
|
118
|
+
};
|
|
94
119
|
|
|
95
120
|
// src/theme/ThemeProvider.tsx
|
|
96
121
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
@@ -258,6 +283,7 @@ function MemoryInspector({
|
|
|
258
283
|
className,
|
|
259
284
|
style
|
|
260
285
|
}) {
|
|
286
|
+
const cacheRef = (0, import_react3.useRef)(null);
|
|
261
287
|
const { memory, newKeys } = (0, import_react3.useMemo)(() => {
|
|
262
288
|
if (data) {
|
|
263
289
|
return { memory: data, newKeys: /* @__PURE__ */ new Set() };
|
|
@@ -265,21 +291,37 @@ function MemoryInspector({
|
|
|
265
291
|
if (!snapshots || snapshots.length === 0) {
|
|
266
292
|
return { memory: {}, newKeys: /* @__PURE__ */ new Set() };
|
|
267
293
|
}
|
|
268
|
-
const
|
|
269
|
-
|
|
270
|
-
|
|
294
|
+
const safeIdx = Math.min(selectedIndex, snapshots.length - 1);
|
|
295
|
+
let merged;
|
|
296
|
+
const cache = cacheRef.current;
|
|
297
|
+
if (cache && cache.snapshots === snapshots && cache.index <= safeIdx) {
|
|
298
|
+
merged = { ...cache.accumulated };
|
|
299
|
+
for (let i = cache.index + 1; i <= safeIdx; i++) {
|
|
300
|
+
Object.assign(merged, snapshots[i]?.memory);
|
|
301
|
+
}
|
|
302
|
+
} else {
|
|
303
|
+
merged = {};
|
|
304
|
+
for (let i = 0; i <= safeIdx; i++) {
|
|
305
|
+
Object.assign(merged, snapshots[i]?.memory);
|
|
306
|
+
}
|
|
271
307
|
}
|
|
308
|
+
cacheRef.current = { snapshots, index: safeIdx, accumulated: merged };
|
|
272
309
|
const nk = /* @__PURE__ */ new Set();
|
|
273
|
-
if (highlightNew &&
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
310
|
+
if (highlightNew && safeIdx > 0) {
|
|
311
|
+
let prev;
|
|
312
|
+
if (cache && cache.snapshots === snapshots && cache.index === safeIdx - 1) {
|
|
313
|
+
prev = cache.accumulated;
|
|
314
|
+
} else {
|
|
315
|
+
prev = {};
|
|
316
|
+
for (let i = 0; i < safeIdx; i++) {
|
|
317
|
+
Object.assign(prev, snapshots[i]?.memory);
|
|
318
|
+
}
|
|
277
319
|
}
|
|
278
|
-
const current = snapshots[
|
|
320
|
+
const current = snapshots[safeIdx]?.memory ?? {};
|
|
279
321
|
for (const k of Object.keys(current)) {
|
|
280
322
|
if (!(k in prev)) nk.add(k);
|
|
281
323
|
}
|
|
282
|
-
} else if (highlightNew &&
|
|
324
|
+
} else if (highlightNew && safeIdx === 0 && snapshots[0]) {
|
|
283
325
|
for (const k of Object.keys(snapshots[0].memory)) nk.add(k);
|
|
284
326
|
}
|
|
285
327
|
return { memory: merged, newKeys: nk };
|
|
@@ -288,9 +330,9 @@ function MemoryInspector({
|
|
|
288
330
|
const fs = fontSize[size];
|
|
289
331
|
const pad = padding[size];
|
|
290
332
|
if (unstyled) {
|
|
291
|
-
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className, style, "data-fp": "memory-inspector", children: [
|
|
333
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className, style, "data-fp": "memory-inspector", role: "region", "aria-label": "Memory state", children: [
|
|
292
334
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { "data-fp": "memory-label", children: "Memory State" }),
|
|
293
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("pre", { "data-fp": "memory-json", children: JSON.stringify(memory, null, 2) })
|
|
335
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("pre", { "data-fp": "memory-json", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("code", { children: JSON.stringify(memory, null, 2) }) })
|
|
294
336
|
] });
|
|
295
337
|
}
|
|
296
338
|
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
@@ -303,6 +345,8 @@ function MemoryInspector({
|
|
|
303
345
|
...style
|
|
304
346
|
},
|
|
305
347
|
"data-fp": "memory-inspector",
|
|
348
|
+
role: "region",
|
|
349
|
+
"aria-label": "Memory state",
|
|
306
350
|
children: [
|
|
307
351
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
308
352
|
"span",
|
|
@@ -595,19 +639,32 @@ function NarrativeTrace({
|
|
|
595
639
|
"data-fp": "narrative-header",
|
|
596
640
|
"data-collapsible": group.steps.length > 0,
|
|
597
641
|
"data-collapsed": collapsedSet.has(group.headerIdx),
|
|
642
|
+
role: group.steps.length > 0 ? "button" : void 0,
|
|
643
|
+
tabIndex: group.steps.length > 0 ? 0 : void 0,
|
|
644
|
+
"aria-expanded": group.steps.length > 0 ? !collapsedSet.has(group.headerIdx) : void 0,
|
|
645
|
+
"aria-label": `Stage ${gi + 1}, ${group.steps.length} steps${gi === lastIdx ? ", current" : ""}`,
|
|
598
646
|
onClick: () => {
|
|
599
647
|
if (group.steps.length > 0) toggle(group.headerIdx);
|
|
600
648
|
onStageClick?.(group.headerIdx);
|
|
601
649
|
},
|
|
650
|
+
onKeyDown: (e) => {
|
|
651
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
652
|
+
e.preventDefault();
|
|
653
|
+
if (group.steps.length > 0) toggle(group.headerIdx);
|
|
654
|
+
onStageClick?.(group.headerIdx);
|
|
655
|
+
}
|
|
656
|
+
},
|
|
602
657
|
children: group.header
|
|
603
658
|
}
|
|
604
659
|
),
|
|
605
660
|
!collapsedSet.has(group.headerIdx) && group.steps.map((step) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { "data-fp": "narrative-step", children: step.text }, step.idx))
|
|
606
661
|
] }, group.headerIdx)),
|
|
607
|
-
futureGroups.
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
662
|
+
futureGroups.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { "data-fp": "narrative-future-hint", children: [
|
|
663
|
+
futureGroups.length,
|
|
664
|
+
" more ",
|
|
665
|
+
futureGroups.length === 1 ? "stage" : "stages",
|
|
666
|
+
" ahead..."
|
|
667
|
+
] })
|
|
611
668
|
] });
|
|
612
669
|
}
|
|
613
670
|
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
@@ -637,10 +694,21 @@ function NarrativeTrace({
|
|
|
637
694
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
638
695
|
"div",
|
|
639
696
|
{
|
|
697
|
+
role: hasSteps ? "button" : void 0,
|
|
698
|
+
tabIndex: hasSteps ? 0 : void 0,
|
|
699
|
+
"aria-expanded": hasSteps ? !isCollapsed : void 0,
|
|
700
|
+
"aria-label": `Stage ${gi + 1}, ${group.steps.length} steps${isLatest ? ", current" : ", completed"}`,
|
|
640
701
|
onClick: () => {
|
|
641
702
|
if (hasSteps) toggle(group.headerIdx);
|
|
642
703
|
onStageClick?.(group.headerIdx);
|
|
643
704
|
},
|
|
705
|
+
onKeyDown: (e) => {
|
|
706
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
707
|
+
e.preventDefault();
|
|
708
|
+
if (hasSteps) toggle(group.headerIdx);
|
|
709
|
+
onStageClick?.(group.headerIdx);
|
|
710
|
+
}
|
|
711
|
+
},
|
|
644
712
|
style: {
|
|
645
713
|
fontSize: fs.body,
|
|
646
714
|
lineHeight: 1.7,
|
|
@@ -699,36 +767,18 @@ function NarrativeTrace({
|
|
|
699
767
|
group.headerIdx
|
|
700
768
|
);
|
|
701
769
|
}),
|
|
702
|
-
futureGroups.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
},
|
|
715
|
-
children: group.header
|
|
716
|
-
}
|
|
717
|
-
),
|
|
718
|
-
group.steps.map((step) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
719
|
-
"div",
|
|
720
|
-
{
|
|
721
|
-
style: {
|
|
722
|
-
fontSize: fs.small,
|
|
723
|
-
lineHeight: 1.6,
|
|
724
|
-
color: theme.textMuted,
|
|
725
|
-
padding: `2px ${pad - 4}px 2px ${pad + 20}px`
|
|
726
|
-
},
|
|
727
|
-
children: step.text
|
|
728
|
-
},
|
|
729
|
-
`f-${step.idx}`
|
|
730
|
-
))
|
|
731
|
-
] }, `f-${group.headerIdx}`)) })
|
|
770
|
+
futureGroups.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: {
|
|
771
|
+
opacity: 0.3,
|
|
772
|
+
fontSize: fs.small,
|
|
773
|
+
color: theme.textMuted,
|
|
774
|
+
padding: `8px ${pad}px`,
|
|
775
|
+
fontStyle: "italic"
|
|
776
|
+
}, children: [
|
|
777
|
+
futureGroups.length,
|
|
778
|
+
" more ",
|
|
779
|
+
futureGroups.length === 1 ? "stage" : "stages",
|
|
780
|
+
" ahead..."
|
|
781
|
+
] })
|
|
732
782
|
]
|
|
733
783
|
}
|
|
734
784
|
);
|
|
@@ -770,12 +820,15 @@ function GanttTimeline({
|
|
|
770
820
|
}
|
|
771
821
|
}, [selectedIndex, showAll]);
|
|
772
822
|
if (unstyled) {
|
|
773
|
-
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className, style, "data-fp": "gantt-timeline", children: snapshots.map((snap, idx) => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
823
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className, style, "data-fp": "gantt-timeline", role: "listbox", "aria-label": "Execution timeline", children: snapshots.map((snap, idx) => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
774
824
|
"div",
|
|
775
825
|
{
|
|
776
826
|
"data-fp": "gantt-bar",
|
|
777
827
|
"data-selected": idx === selectedIndex,
|
|
778
828
|
"data-visible": idx <= selectedIndex,
|
|
829
|
+
role: "option",
|
|
830
|
+
"aria-selected": idx === selectedIndex,
|
|
831
|
+
"aria-label": `${snap.stageLabel}, ${snap.durationMs}ms`,
|
|
779
832
|
onClick: () => onSelect?.(idx),
|
|
780
833
|
children: [
|
|
781
834
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { "data-fp": "gantt-label", children: snap.stageLabel }),
|
|
@@ -785,7 +838,7 @@ function GanttTimeline({
|
|
|
785
838
|
] })
|
|
786
839
|
]
|
|
787
840
|
},
|
|
788
|
-
snap.stageName
|
|
841
|
+
`${snap.stageName}-${idx}`
|
|
789
842
|
)) });
|
|
790
843
|
}
|
|
791
844
|
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
@@ -841,6 +894,8 @@ function GanttTimeline({
|
|
|
841
894
|
"div",
|
|
842
895
|
{
|
|
843
896
|
ref: scrollContainerRef,
|
|
897
|
+
role: "listbox",
|
|
898
|
+
"aria-label": "Execution timeline",
|
|
844
899
|
style: {
|
|
845
900
|
marginTop: 8,
|
|
846
901
|
display: "flex",
|
|
@@ -861,6 +916,9 @@ function GanttTimeline({
|
|
|
861
916
|
"div",
|
|
862
917
|
{
|
|
863
918
|
ref: isSelected ? activeRowRef : void 0,
|
|
919
|
+
role: "option",
|
|
920
|
+
"aria-selected": isSelected,
|
|
921
|
+
"aria-label": `${snap.stageLabel}, ${snap.durationMs}ms`,
|
|
864
922
|
onClick: () => onSelect?.(idx),
|
|
865
923
|
style: {
|
|
866
924
|
display: "flex",
|
|
@@ -876,6 +934,7 @@ function GanttTimeline({
|
|
|
876
934
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
877
935
|
"span",
|
|
878
936
|
{
|
|
937
|
+
title: snap.stageLabel,
|
|
879
938
|
style: {
|
|
880
939
|
width: labelWidth,
|
|
881
940
|
fontSize: fs.small,
|
|
@@ -935,7 +994,7 @@ function GanttTimeline({
|
|
|
935
994
|
)
|
|
936
995
|
]
|
|
937
996
|
},
|
|
938
|
-
snap.stageName
|
|
997
|
+
`${snap.stageName}-${idx}`
|
|
939
998
|
);
|
|
940
999
|
})
|
|
941
1000
|
}
|
|
@@ -1257,9 +1316,9 @@ function fmt(v2) {
|
|
|
1257
1316
|
return String(v2);
|
|
1258
1317
|
}
|
|
1259
1318
|
var diffColors = {
|
|
1260
|
-
added: { bg:
|
|
1261
|
-
removed: { bg:
|
|
1262
|
-
changed: { bg:
|
|
1319
|
+
added: { bg: `color-mix(in srgb, ${theme.success} 10%, transparent)`, fg: theme.success, icon: "+" },
|
|
1320
|
+
removed: { bg: `color-mix(in srgb, ${theme.error} 10%, transparent)`, fg: theme.error, icon: "-" },
|
|
1321
|
+
changed: { bg: `color-mix(in srgb, ${theme.warning} 10%, transparent)`, fg: theme.warning, icon: "~" },
|
|
1263
1322
|
unchanged: { bg: "transparent", fg: "", icon: " " }
|
|
1264
1323
|
};
|
|
1265
1324
|
function ScopeDiff({
|
|
@@ -1996,49 +2055,80 @@ function TimeTravelControls({
|
|
|
1996
2055
|
setPlaying(true);
|
|
1997
2056
|
}
|
|
1998
2057
|
}, [playing, selectedIndex, total, onIndexChange]);
|
|
2058
|
+
const handleKeyDown = (0, import_react10.useCallback)(
|
|
2059
|
+
(e) => {
|
|
2060
|
+
if (e.key === "ArrowLeft" && canPrev && !playing) {
|
|
2061
|
+
e.preventDefault();
|
|
2062
|
+
setPlaying(false);
|
|
2063
|
+
onIndexChange(selectedIndex - 1);
|
|
2064
|
+
} else if (e.key === "ArrowRight" && canNext && !playing) {
|
|
2065
|
+
e.preventDefault();
|
|
2066
|
+
setPlaying(false);
|
|
2067
|
+
onIndexChange(selectedIndex + 1);
|
|
2068
|
+
} else if (e.key === " " && autoPlayable) {
|
|
2069
|
+
e.preventDefault();
|
|
2070
|
+
togglePlay();
|
|
2071
|
+
}
|
|
2072
|
+
},
|
|
2073
|
+
[canPrev, canNext, playing, selectedIndex, onIndexChange, autoPlayable, togglePlay]
|
|
2074
|
+
);
|
|
1999
2075
|
const fs = fontSize[size];
|
|
2000
2076
|
if (unstyled) {
|
|
2001
|
-
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2077
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
|
|
2078
|
+
"div",
|
|
2079
|
+
{
|
|
2080
|
+
className,
|
|
2081
|
+
style,
|
|
2082
|
+
"data-fp": "time-travel-controls",
|
|
2083
|
+
role: "toolbar",
|
|
2084
|
+
"aria-label": "Time travel controls",
|
|
2085
|
+
tabIndex: 0,
|
|
2086
|
+
onKeyDown: handleKeyDown,
|
|
2087
|
+
children: [
|
|
2088
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
2089
|
+
"button",
|
|
2090
|
+
{
|
|
2091
|
+
"data-fp": "tt-prev",
|
|
2092
|
+
disabled: !canPrev || playing,
|
|
2093
|
+
onClick: () => {
|
|
2094
|
+
setPlaying(false);
|
|
2095
|
+
onIndexChange(selectedIndex - 1);
|
|
2096
|
+
},
|
|
2097
|
+
"aria-label": "Previous stage",
|
|
2098
|
+
children: "Prev"
|
|
2099
|
+
}
|
|
2100
|
+
),
|
|
2101
|
+
autoPlayable && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { "data-fp": "tt-play", onClick: togglePlay, "aria-label": playing ? "Pause" : "Play", children: playing ? "Pause" : "Play" }),
|
|
2102
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
2103
|
+
"button",
|
|
2104
|
+
{
|
|
2105
|
+
"data-fp": "tt-next",
|
|
2106
|
+
disabled: !canNext || playing,
|
|
2107
|
+
onClick: () => {
|
|
2108
|
+
setPlaying(false);
|
|
2109
|
+
onIndexChange(selectedIndex + 1);
|
|
2110
|
+
},
|
|
2111
|
+
"aria-label": "Next stage",
|
|
2112
|
+
children: "Next"
|
|
2113
|
+
}
|
|
2114
|
+
),
|
|
2115
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { "data-fp": "tt-ticks", children: snapshots.map((snap, i) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
2116
|
+
"button",
|
|
2117
|
+
{
|
|
2118
|
+
"data-fp": "tt-tick",
|
|
2119
|
+
"data-active": i === selectedIndex,
|
|
2120
|
+
"data-done": i < selectedIndex,
|
|
2121
|
+
onClick: () => {
|
|
2122
|
+
setPlaying(false);
|
|
2123
|
+
onIndexChange(i);
|
|
2124
|
+
},
|
|
2125
|
+
title: snap.stageLabel
|
|
2126
|
+
},
|
|
2127
|
+
i
|
|
2128
|
+
)) })
|
|
2129
|
+
]
|
|
2130
|
+
}
|
|
2131
|
+
);
|
|
2042
2132
|
}
|
|
2043
2133
|
const btnStyle = (disabled) => ({
|
|
2044
2134
|
background: theme.bgTertiary,
|
|
@@ -2067,6 +2157,10 @@ function TimeTravelControls({
|
|
|
2067
2157
|
...style
|
|
2068
2158
|
},
|
|
2069
2159
|
"data-fp": "time-travel-controls",
|
|
2160
|
+
role: "toolbar",
|
|
2161
|
+
"aria-label": "Time travel controls",
|
|
2162
|
+
tabIndex: 0,
|
|
2163
|
+
onKeyDown: handleKeyDown,
|
|
2070
2164
|
children: [
|
|
2071
2165
|
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
2072
2166
|
"button",
|
|
@@ -2077,6 +2171,7 @@ function TimeTravelControls({
|
|
|
2077
2171
|
setPlaying(false);
|
|
2078
2172
|
onIndexChange(selectedIndex - 1);
|
|
2079
2173
|
},
|
|
2174
|
+
"aria-label": "Previous stage",
|
|
2080
2175
|
children: "\u25C0"
|
|
2081
2176
|
}
|
|
2082
2177
|
),
|
|
@@ -2099,6 +2194,7 @@ function TimeTravelControls({
|
|
|
2099
2194
|
flexShrink: 0
|
|
2100
2195
|
},
|
|
2101
2196
|
title: playing ? "Pause" : "Play",
|
|
2197
|
+
"aria-label": playing ? "Pause" : "Play",
|
|
2102
2198
|
children: playing ? "\u23F8" : "\u25B6"
|
|
2103
2199
|
}
|
|
2104
2200
|
),
|
|
@@ -2111,6 +2207,7 @@ function TimeTravelControls({
|
|
|
2111
2207
|
setPlaying(false);
|
|
2112
2208
|
onIndexChange(selectedIndex + 1);
|
|
2113
2209
|
},
|
|
2210
|
+
"aria-label": "Next stage",
|
|
2114
2211
|
children: "\u25B6"
|
|
2115
2212
|
}
|
|
2116
2213
|
),
|
|
@@ -2157,125 +2254,146 @@ function TimeTravelControls({
|
|
|
2157
2254
|
}
|
|
2158
2255
|
|
|
2159
2256
|
// src/components/ExplainableShell/ExplainableShell.tsx
|
|
2160
|
-
var
|
|
2257
|
+
var import_react15 = require("react");
|
|
2258
|
+
|
|
2259
|
+
// src/adapters/fromRuntimeSnapshot.ts
|
|
2260
|
+
function toVisualizationSnapshots(runtime, narrativeEntries) {
|
|
2261
|
+
const stageNarrativeMap = narrativeEntries?.length ? buildStageNarrativeMap(narrativeEntries) : /* @__PURE__ */ new Map();
|
|
2262
|
+
const snapshots = [];
|
|
2263
|
+
flattenTree(runtime.executionTree, snapshots, runtime.sharedState, 0, runtime.subflowResults, {}, stageNarrativeMap);
|
|
2264
|
+
return snapshots;
|
|
2265
|
+
}
|
|
2266
|
+
function buildStageNarrativeMap(entries) {
|
|
2267
|
+
const map = /* @__PURE__ */ new Map();
|
|
2268
|
+
let currentStageName;
|
|
2269
|
+
for (const entry of entries) {
|
|
2270
|
+
if (entry.stageName) {
|
|
2271
|
+
currentStageName = entry.stageName;
|
|
2272
|
+
}
|
|
2273
|
+
if (currentStageName) {
|
|
2274
|
+
if (!map.has(currentStageName)) {
|
|
2275
|
+
map.set(currentStageName, []);
|
|
2276
|
+
}
|
|
2277
|
+
const indent = " ".repeat(entry.depth);
|
|
2278
|
+
map.get(currentStageName).push(`${indent}${entry.text}`);
|
|
2279
|
+
}
|
|
2280
|
+
}
|
|
2281
|
+
return map;
|
|
2282
|
+
}
|
|
2283
|
+
function flattenTree(node, out, sharedState, accumulatedMs = 0, subflowResults, cumulativeMemory = {}, stageNarrativeMap = /* @__PURE__ */ new Map()) {
|
|
2284
|
+
const durationMs = typeof node.metrics?.durationMs === "number" ? node.metrics.durationMs : 1;
|
|
2285
|
+
const startMs = accumulatedMs;
|
|
2286
|
+
const stageId = node.id || node.name || "unknown";
|
|
2287
|
+
const displayName = node.name || node.id || "unknown";
|
|
2288
|
+
const stageLines = stageNarrativeMap.get(stageId);
|
|
2289
|
+
let narrative;
|
|
2290
|
+
if (stageLines) {
|
|
2291
|
+
narrative = stageLines.join("\n");
|
|
2292
|
+
} else {
|
|
2293
|
+
const parts = [`${displayName} executed.`];
|
|
2294
|
+
if (node.description) parts.push(node.description);
|
|
2295
|
+
if (node.stageWrites) {
|
|
2296
|
+
const keys = Object.keys(node.stageWrites);
|
|
2297
|
+
if (keys.length > 0) parts.push(`Wrote: ${keys.join(", ")}`);
|
|
2298
|
+
}
|
|
2299
|
+
narrative = parts.join("\n");
|
|
2300
|
+
}
|
|
2301
|
+
const memory = { ...cumulativeMemory };
|
|
2302
|
+
if (node.stageWrites) {
|
|
2303
|
+
for (const [key, value] of Object.entries(node.stageWrites)) {
|
|
2304
|
+
if (value === void 0) {
|
|
2305
|
+
delete memory[key];
|
|
2306
|
+
} else {
|
|
2307
|
+
memory[key] = value;
|
|
2308
|
+
}
|
|
2309
|
+
}
|
|
2310
|
+
}
|
|
2311
|
+
const sfResult = subflowResults?.[node.subflowId ?? stageId];
|
|
2312
|
+
out.push({
|
|
2313
|
+
stageName: displayName,
|
|
2314
|
+
stageLabel: stageId,
|
|
2315
|
+
memory,
|
|
2316
|
+
narrative,
|
|
2317
|
+
startMs,
|
|
2318
|
+
durationMs,
|
|
2319
|
+
status: "done",
|
|
2320
|
+
...node.description ? { description: node.description } : void 0,
|
|
2321
|
+
...node.subflowId ? { subflowId: node.subflowId } : void 0,
|
|
2322
|
+
...sfResult ? { subflowResult: sfResult } : void 0
|
|
2323
|
+
});
|
|
2324
|
+
let nextMs = startMs + durationMs;
|
|
2325
|
+
if (node.children && node.children.length > 0) {
|
|
2326
|
+
let maxChildEnd = nextMs;
|
|
2327
|
+
for (const child of node.children) {
|
|
2328
|
+
const childEnd = flattenTree(child, out, sharedState, nextMs, subflowResults, memory, stageNarrativeMap);
|
|
2329
|
+
maxChildEnd = Math.max(maxChildEnd, childEnd);
|
|
2330
|
+
}
|
|
2331
|
+
nextMs = maxChildEnd;
|
|
2332
|
+
}
|
|
2333
|
+
if (node.next) {
|
|
2334
|
+
nextMs = flattenTree(node.next, out, sharedState, nextMs, subflowResults, memory, stageNarrativeMap);
|
|
2335
|
+
}
|
|
2336
|
+
return nextMs;
|
|
2337
|
+
}
|
|
2338
|
+
function subflowResultToSnapshots(subflowResult, narrativeEntries) {
|
|
2339
|
+
if (!subflowResult || typeof subflowResult !== "object") return [];
|
|
2340
|
+
const sf = subflowResult;
|
|
2341
|
+
if (!sf.treeContext?.stageContexts) return [];
|
|
2342
|
+
const runtime = {
|
|
2343
|
+
sharedState: sf.treeContext.globalContext ?? {},
|
|
2344
|
+
executionTree: sf.treeContext.stageContexts,
|
|
2345
|
+
commitLog: sf.treeContext.history ?? []
|
|
2346
|
+
};
|
|
2347
|
+
const snapshots = toVisualizationSnapshots(runtime, narrativeEntries);
|
|
2348
|
+
const prefix = sf.subflowId ? `${sf.subflowId}/` : "";
|
|
2349
|
+
if (prefix) {
|
|
2350
|
+
for (const snap of snapshots) {
|
|
2351
|
+
if (snap.stageName.startsWith(prefix)) {
|
|
2352
|
+
snap.stageName = snap.stageName.slice(prefix.length);
|
|
2353
|
+
}
|
|
2354
|
+
if (snap.stageLabel.startsWith(prefix)) {
|
|
2355
|
+
snap.stageLabel = snap.stageLabel.slice(prefix.length);
|
|
2356
|
+
}
|
|
2357
|
+
}
|
|
2358
|
+
}
|
|
2359
|
+
return snapshots;
|
|
2360
|
+
}
|
|
2361
|
+
function createSnapshots(stages) {
|
|
2362
|
+
let accMs = 0;
|
|
2363
|
+
return stages.map((s) => {
|
|
2364
|
+
const duration = s.durationMs ?? 1;
|
|
2365
|
+
const snap = {
|
|
2366
|
+
stageName: s.name,
|
|
2367
|
+
stageLabel: s.label ?? s.name,
|
|
2368
|
+
memory: s.memory ?? {},
|
|
2369
|
+
narrative: s.narrative ?? `${s.label ?? s.name} completed.`,
|
|
2370
|
+
startMs: accMs,
|
|
2371
|
+
durationMs: duration,
|
|
2372
|
+
status: "done",
|
|
2373
|
+
...s.description ? { description: s.description } : void 0,
|
|
2374
|
+
...s.subflowId ? { subflowId: s.subflowId } : void 0
|
|
2375
|
+
};
|
|
2376
|
+
accMs += duration;
|
|
2377
|
+
return snap;
|
|
2378
|
+
});
|
|
2379
|
+
}
|
|
2380
|
+
|
|
2381
|
+
// src/components/MemoryPanel/MemoryPanel.tsx
|
|
2161
2382
|
var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
2162
|
-
function
|
|
2383
|
+
function MemoryPanel({
|
|
2163
2384
|
snapshots,
|
|
2164
|
-
|
|
2165
|
-
logs = [],
|
|
2166
|
-
narrative = [],
|
|
2167
|
-
tabs = ["result", "explainable", "ai-compatible"],
|
|
2168
|
-
defaultTab,
|
|
2169
|
-
hideConsole = false,
|
|
2170
|
-
renderFlowchart,
|
|
2385
|
+
selectedIndex,
|
|
2171
2386
|
size = "default",
|
|
2172
2387
|
unstyled = false,
|
|
2173
2388
|
className,
|
|
2174
2389
|
style
|
|
2175
2390
|
}) {
|
|
2176
|
-
const
|
|
2177
|
-
const [
|
|
2178
|
-
const fs = fontSize[size];
|
|
2179
|
-
const pad = padding[size];
|
|
2180
|
-
const handleSnapshotChange = (0, import_react11.useCallback)((idx) => {
|
|
2181
|
-
setSnapshotIdx(Math.max(0, Math.min(idx, snapshots.length - 1)));
|
|
2182
|
-
}, [snapshots.length]);
|
|
2183
|
-
const revealedCount = (0, import_react11.useMemo)(() => {
|
|
2184
|
-
if (snapshots.length === 0 || narrative.length === 0) return narrative.length;
|
|
2185
|
-
const boundaries = [];
|
|
2186
|
-
for (let i = 0; i < narrative.length; i++) {
|
|
2187
|
-
const trimmed = narrative[i].trimStart();
|
|
2188
|
-
if (trimmed.startsWith("Stage ") && !trimmed.match(/^Stage\s+\d+:\s*Step\s/) || trimmed.startsWith("[")) {
|
|
2189
|
-
boundaries.push(i);
|
|
2190
|
-
}
|
|
2191
|
-
}
|
|
2192
|
-
if (boundaries.length === 0) {
|
|
2193
|
-
const ratio = (snapshotIdx + 1) / snapshots.length;
|
|
2194
|
-
return Math.max(1, Math.ceil(narrative.length * ratio));
|
|
2195
|
-
}
|
|
2196
|
-
const groupsToShow = Math.max(
|
|
2197
|
-
1,
|
|
2198
|
-
Math.min(
|
|
2199
|
-
Math.floor((snapshotIdx + 1) / snapshots.length * boundaries.length) || 1,
|
|
2200
|
-
boundaries.length
|
|
2201
|
-
)
|
|
2202
|
-
);
|
|
2203
|
-
const endIdx = groupsToShow < boundaries.length ? boundaries[groupsToShow] : narrative.length;
|
|
2204
|
-
return Math.max(1, endIdx);
|
|
2205
|
-
}, [snapshots.length, snapshotIdx, narrative]);
|
|
2206
|
-
const prevMemory = snapshotIdx > 0 ? snapshots[snapshotIdx - 1]?.memory : null;
|
|
2207
|
-
const currMemory = snapshots[snapshotIdx]?.memory ?? {};
|
|
2208
|
-
const tabLabels = {
|
|
2209
|
-
result: "Result",
|
|
2210
|
-
explainable: "Explainable",
|
|
2211
|
-
"ai-compatible": "AI-Compatible"
|
|
2212
|
-
};
|
|
2391
|
+
const prevMemory = selectedIndex > 0 ? snapshots[selectedIndex - 1]?.memory ?? null : null;
|
|
2392
|
+
const currMemory = snapshots[selectedIndex]?.memory ?? {};
|
|
2213
2393
|
if (unstyled) {
|
|
2214
|
-
return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className, style, "data-fp": "
|
|
2215
|
-
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
2216
|
-
|
|
2217
|
-
{
|
|
2218
|
-
"data-fp": "shell-tab",
|
|
2219
|
-
"data-active": tab === activeTab,
|
|
2220
|
-
onClick: () => setActiveTab(tab),
|
|
2221
|
-
children: tabLabels[tab]
|
|
2222
|
-
},
|
|
2223
|
-
tab
|
|
2224
|
-
)) }),
|
|
2225
|
-
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { "data-fp": "shell-content", "data-tab": activeTab, children: [
|
|
2226
|
-
activeTab === "result" && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
2227
|
-
ResultPanel,
|
|
2228
|
-
{
|
|
2229
|
-
data: resultData ?? null,
|
|
2230
|
-
logs,
|
|
2231
|
-
hideConsole,
|
|
2232
|
-
unstyled: true
|
|
2233
|
-
}
|
|
2234
|
-
),
|
|
2235
|
-
activeTab === "explainable" && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
|
|
2236
|
-
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
2237
|
-
TimeTravelControls,
|
|
2238
|
-
{
|
|
2239
|
-
snapshots,
|
|
2240
|
-
selectedIndex: snapshotIdx,
|
|
2241
|
-
onIndexChange: handleSnapshotChange,
|
|
2242
|
-
unstyled: true
|
|
2243
|
-
}
|
|
2244
|
-
),
|
|
2245
|
-
renderFlowchart?.({ snapshots, selectedIndex: snapshotIdx, onNodeClick: handleSnapshotChange }),
|
|
2246
|
-
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(MemoryInspector, { snapshots, selectedIndex: snapshotIdx, unstyled: true }),
|
|
2247
|
-
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ScopeDiff, { previous: prevMemory, current: currMemory, unstyled: true }),
|
|
2248
|
-
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
2249
|
-
GanttTimeline,
|
|
2250
|
-
{
|
|
2251
|
-
snapshots,
|
|
2252
|
-
selectedIndex: snapshotIdx,
|
|
2253
|
-
onSelect: handleSnapshotChange,
|
|
2254
|
-
unstyled: true
|
|
2255
|
-
}
|
|
2256
|
-
)
|
|
2257
|
-
] }),
|
|
2258
|
-
activeTab === "ai-compatible" && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
|
|
2259
|
-
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
2260
|
-
TimeTravelControls,
|
|
2261
|
-
{
|
|
2262
|
-
snapshots,
|
|
2263
|
-
selectedIndex: snapshotIdx,
|
|
2264
|
-
onIndexChange: handleSnapshotChange,
|
|
2265
|
-
unstyled: true
|
|
2266
|
-
}
|
|
2267
|
-
),
|
|
2268
|
-
renderFlowchart?.({ snapshots, selectedIndex: snapshotIdx, onNodeClick: handleSnapshotChange }),
|
|
2269
|
-
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
2270
|
-
NarrativeTrace,
|
|
2271
|
-
{
|
|
2272
|
-
narrative,
|
|
2273
|
-
revealedCount,
|
|
2274
|
-
unstyled: true
|
|
2275
|
-
}
|
|
2276
|
-
)
|
|
2277
|
-
] })
|
|
2278
|
-
] })
|
|
2394
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className, style, "data-fp": "memory-panel", children: [
|
|
2395
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(MemoryInspector, { snapshots, selectedIndex, unstyled: true }),
|
|
2396
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ScopeDiff, { previous: prevMemory, current: currMemory, unstyled: true })
|
|
2279
2397
|
] });
|
|
2280
2398
|
}
|
|
2281
2399
|
return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
|
|
@@ -2283,156 +2401,251 @@ function ExplainableShell({
|
|
|
2283
2401
|
{
|
|
2284
2402
|
className,
|
|
2285
2403
|
style: {
|
|
2286
|
-
|
|
2404
|
+
overflow: "auto",
|
|
2287
2405
|
display: "flex",
|
|
2288
2406
|
flexDirection: "column",
|
|
2289
|
-
overflow: "hidden",
|
|
2290
|
-
background: theme.bgPrimary,
|
|
2291
|
-
color: theme.textPrimary,
|
|
2292
|
-
fontFamily: theme.fontSans,
|
|
2293
2407
|
...style
|
|
2294
2408
|
},
|
|
2295
|
-
"data-fp": "
|
|
2409
|
+
"data-fp": "memory-panel",
|
|
2296
2410
|
children: [
|
|
2297
|
-
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
activeTab === "ai-compatible" && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
|
|
2398
|
-
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
2399
|
-
TimeTravelControls,
|
|
2400
|
-
{
|
|
2401
|
-
snapshots,
|
|
2402
|
-
selectedIndex: snapshotIdx,
|
|
2403
|
-
onIndexChange: handleSnapshotChange,
|
|
2404
|
-
size
|
|
2405
|
-
}
|
|
2406
|
-
),
|
|
2407
|
-
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { style: { flex: 1, display: "flex", overflow: "hidden" }, children: [
|
|
2408
|
-
renderFlowchart && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { style: { flex: 1, overflow: "hidden", borderRight: `1px solid ${theme.border}` }, children: renderFlowchart({ snapshots, selectedIndex: snapshotIdx, onNodeClick: handleSnapshotChange }) }),
|
|
2409
|
-
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
2410
|
-
NarrativeTrace,
|
|
2411
|
-
{
|
|
2412
|
-
narrative,
|
|
2413
|
-
revealedCount,
|
|
2414
|
-
size,
|
|
2415
|
-
style: {
|
|
2416
|
-
width: renderFlowchart ? "40%" : "100%",
|
|
2417
|
-
minWidth: 280
|
|
2411
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(MemoryInspector, { snapshots, selectedIndex, size }),
|
|
2412
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { style: { borderTop: `1px solid ${theme.border}` }, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ScopeDiff, { previous: prevMemory, current: currMemory, hideUnchanged: true, size }) })
|
|
2413
|
+
]
|
|
2414
|
+
}
|
|
2415
|
+
);
|
|
2416
|
+
}
|
|
2417
|
+
|
|
2418
|
+
// src/components/NarrativePanel/NarrativePanel.tsx
|
|
2419
|
+
var import_react12 = require("react");
|
|
2420
|
+
|
|
2421
|
+
// src/components/StoryNarrative/StoryNarrative.tsx
|
|
2422
|
+
var import_react11 = require("react");
|
|
2423
|
+
var import_jsx_runtime12 = require("react/jsx-runtime");
|
|
2424
|
+
var ENTRY_ICONS = {
|
|
2425
|
+
stage: { icon: "\u25B8", color: theme.primary, label: "Stage" },
|
|
2426
|
+
step: { icon: "\xB7", color: theme.textMuted, label: "Data operation" },
|
|
2427
|
+
condition: { icon: "\u25C7", color: theme.warning, label: "Decision" },
|
|
2428
|
+
fork: { icon: "\u2443", color: theme.primary, label: "Parallel" },
|
|
2429
|
+
subflow: { icon: "\u21B3", color: theme.textSecondary, label: "Subflow" },
|
|
2430
|
+
loop: { icon: "\u21BB", color: theme.warning, label: "Loop" },
|
|
2431
|
+
break: { icon: "\u25A0", color: theme.error, label: "Break" },
|
|
2432
|
+
error: { icon: "\u2717", color: theme.error, label: "Error" }
|
|
2433
|
+
};
|
|
2434
|
+
function StoryNarrative({
|
|
2435
|
+
entries,
|
|
2436
|
+
stageCount,
|
|
2437
|
+
size = "default",
|
|
2438
|
+
unstyled = false,
|
|
2439
|
+
className,
|
|
2440
|
+
style: outerStyle
|
|
2441
|
+
}) {
|
|
2442
|
+
const fs = fontSize[size];
|
|
2443
|
+
const pad = padding[size];
|
|
2444
|
+
const revealedCount = (0, import_react11.useMemo)(() => {
|
|
2445
|
+
let stagesSeen = 0;
|
|
2446
|
+
for (let i = 0; i < entries.length; i++) {
|
|
2447
|
+
const e = entries[i];
|
|
2448
|
+
const isBoundary = e.type === "stage" || e.type === "subflow" && e.text.startsWith("Entering");
|
|
2449
|
+
if (isBoundary) stagesSeen++;
|
|
2450
|
+
if (stagesSeen > stageCount) return i;
|
|
2451
|
+
}
|
|
2452
|
+
return entries.length;
|
|
2453
|
+
}, [entries, stageCount]);
|
|
2454
|
+
const revealed = entries.slice(0, revealedCount);
|
|
2455
|
+
const future = entries.slice(revealedCount);
|
|
2456
|
+
const latestRef = (0, import_react11.useRef)(null);
|
|
2457
|
+
(0, import_react11.useEffect)(() => {
|
|
2458
|
+
latestRef.current?.scrollIntoView({ behavior: "smooth", block: "nearest" });
|
|
2459
|
+
}, [revealed.length]);
|
|
2460
|
+
if (unstyled) {
|
|
2461
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className, style: outerStyle, "data-fp": "story-narrative", role: "log", children: revealed.map((entry, i) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { "data-fp": "narrative-entry", "data-type": entry.type, children: entry.text }, i)) });
|
|
2462
|
+
}
|
|
2463
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
|
|
2464
|
+
"div",
|
|
2465
|
+
{
|
|
2466
|
+
className,
|
|
2467
|
+
style: {
|
|
2468
|
+
flex: 1,
|
|
2469
|
+
overflow: "auto",
|
|
2470
|
+
padding: pad,
|
|
2471
|
+
fontFamily: theme.fontSans,
|
|
2472
|
+
...outerStyle
|
|
2473
|
+
},
|
|
2474
|
+
"data-fp": "story-narrative",
|
|
2475
|
+
role: "log",
|
|
2476
|
+
"aria-label": "Execution narrative",
|
|
2477
|
+
children: [
|
|
2478
|
+
revealed.map((entry, i) => {
|
|
2479
|
+
const meta = ENTRY_ICONS[entry.type] ?? ENTRY_ICONS.step;
|
|
2480
|
+
const isStage = entry.type === "stage";
|
|
2481
|
+
const isDecision = entry.type === "condition";
|
|
2482
|
+
const isError = entry.type === "error";
|
|
2483
|
+
const isLast = i === revealed.length - 1;
|
|
2484
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
|
|
2485
|
+
"div",
|
|
2486
|
+
{
|
|
2487
|
+
ref: isLast ? latestRef : void 0,
|
|
2488
|
+
style: {
|
|
2489
|
+
display: "flex",
|
|
2490
|
+
gap: 8,
|
|
2491
|
+
padding: isStage ? `${pad - 4}px 0` : `2px 0`,
|
|
2492
|
+
marginLeft: entry.depth * 16,
|
|
2493
|
+
borderBottom: isStage ? `1px solid ${theme.border}` : void 0,
|
|
2494
|
+
marginTop: isStage && i > 0 ? 8 : 0
|
|
2495
|
+
},
|
|
2496
|
+
children: [
|
|
2497
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
2498
|
+
"span",
|
|
2499
|
+
{
|
|
2500
|
+
style: {
|
|
2501
|
+
color: meta.color,
|
|
2502
|
+
fontWeight: 700,
|
|
2503
|
+
fontSize: isStage ? fs.body : fs.small,
|
|
2504
|
+
width: 16,
|
|
2505
|
+
textAlign: "center",
|
|
2506
|
+
flexShrink: 0
|
|
2507
|
+
},
|
|
2508
|
+
title: meta.label,
|
|
2509
|
+
"aria-label": meta.label,
|
|
2510
|
+
children: meta.icon
|
|
2418
2511
|
}
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2512
|
+
),
|
|
2513
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
2514
|
+
"span",
|
|
2515
|
+
{
|
|
2516
|
+
style: {
|
|
2517
|
+
fontSize: isStage ? fs.body : fs.small,
|
|
2518
|
+
fontWeight: isStage ? 600 : 400,
|
|
2519
|
+
color: isError ? theme.error : isDecision ? theme.warning : isStage ? theme.textPrimary : theme.textSecondary,
|
|
2520
|
+
lineHeight: 1.6,
|
|
2521
|
+
fontFamily: entry.type === "step" ? theme.fontMono : theme.fontSans
|
|
2522
|
+
},
|
|
2523
|
+
children: entry.text
|
|
2524
|
+
}
|
|
2525
|
+
)
|
|
2526
|
+
]
|
|
2527
|
+
},
|
|
2528
|
+
i
|
|
2529
|
+
);
|
|
2530
|
+
}),
|
|
2531
|
+
future.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { style: {
|
|
2532
|
+
opacity: 0.3,
|
|
2533
|
+
fontSize: fs.small,
|
|
2534
|
+
color: theme.textMuted,
|
|
2535
|
+
padding: `8px 0`,
|
|
2536
|
+
fontStyle: "italic"
|
|
2537
|
+
}, children: [
|
|
2538
|
+
future.length,
|
|
2539
|
+
" more ",
|
|
2540
|
+
future.length === 1 ? "entry" : "entries",
|
|
2541
|
+
" ahead..."
|
|
2423
2542
|
] })
|
|
2424
2543
|
]
|
|
2425
2544
|
}
|
|
2426
2545
|
);
|
|
2427
2546
|
}
|
|
2428
2547
|
|
|
2548
|
+
// src/components/NarrativePanel/NarrativePanel.tsx
|
|
2549
|
+
var import_jsx_runtime13 = require("react/jsx-runtime");
|
|
2550
|
+
function NarrativePanel({
|
|
2551
|
+
snapshots,
|
|
2552
|
+
selectedIndex,
|
|
2553
|
+
narrativeEntries,
|
|
2554
|
+
narrative: narrativeProp,
|
|
2555
|
+
size = "default",
|
|
2556
|
+
unstyled = false,
|
|
2557
|
+
className,
|
|
2558
|
+
style
|
|
2559
|
+
}) {
|
|
2560
|
+
const fs = fontSize[size];
|
|
2561
|
+
const pad = padding[size];
|
|
2562
|
+
const narrative = (0, import_react12.useMemo)(() => {
|
|
2563
|
+
if (narrativeProp && narrativeProp.length > 0) return narrativeProp;
|
|
2564
|
+
const lines = [];
|
|
2565
|
+
for (const snap of snapshots) {
|
|
2566
|
+
const stageLines = (snap.narrative ?? "").split("\n").filter(Boolean);
|
|
2567
|
+
lines.push(...stageLines);
|
|
2568
|
+
}
|
|
2569
|
+
return lines;
|
|
2570
|
+
}, [narrativeProp, snapshots]);
|
|
2571
|
+
const revealedCount = (0, import_react12.useMemo)(() => {
|
|
2572
|
+
if (snapshots.length === 0 || narrative.length === 0) return narrative.length;
|
|
2573
|
+
const stageBoundaries = [];
|
|
2574
|
+
for (let i = 0; i < narrative.length; i++) {
|
|
2575
|
+
const trimmed = narrative[i].trimStart();
|
|
2576
|
+
if (trimmed.startsWith("Stage ") && !trimmed.match(/^Stage\s+\d+:\s*Step\s/)) {
|
|
2577
|
+
stageBoundaries.push(i);
|
|
2578
|
+
}
|
|
2579
|
+
}
|
|
2580
|
+
if (stageBoundaries.length === 0) {
|
|
2581
|
+
const ratio = (selectedIndex + 1) / snapshots.length;
|
|
2582
|
+
return Math.max(1, Math.ceil(narrative.length * ratio));
|
|
2583
|
+
}
|
|
2584
|
+
const groupsToShow = Math.min(selectedIndex + 1, stageBoundaries.length);
|
|
2585
|
+
const endIdx = groupsToShow < stageBoundaries.length ? stageBoundaries[groupsToShow] : narrative.length;
|
|
2586
|
+
return Math.max(1, endIdx);
|
|
2587
|
+
}, [snapshots.length, selectedIndex, narrative]);
|
|
2588
|
+
const hasStructured = narrativeEntries && narrativeEntries.length > 0;
|
|
2589
|
+
if (unstyled) {
|
|
2590
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className, style, "data-fp": "narrative-panel", children: hasStructured ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(StoryNarrative, { entries: narrativeEntries, stageCount: selectedIndex + 1, unstyled: true }) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(NarrativeTrace, { narrative, revealedCount, unstyled: true }) });
|
|
2591
|
+
}
|
|
2592
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
|
|
2593
|
+
"div",
|
|
2594
|
+
{
|
|
2595
|
+
className,
|
|
2596
|
+
style: {
|
|
2597
|
+
overflow: "auto",
|
|
2598
|
+
display: "flex",
|
|
2599
|
+
flexDirection: "column",
|
|
2600
|
+
...style
|
|
2601
|
+
},
|
|
2602
|
+
"data-fp": "narrative-panel",
|
|
2603
|
+
children: [
|
|
2604
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2605
|
+
"div",
|
|
2606
|
+
{
|
|
2607
|
+
style: {
|
|
2608
|
+
padding: `${pad - 4}px ${pad}px`,
|
|
2609
|
+
fontSize: fs.small,
|
|
2610
|
+
color: theme.textMuted,
|
|
2611
|
+
fontStyle: "italic",
|
|
2612
|
+
borderBottom: `1px solid ${theme.border}`,
|
|
2613
|
+
flexShrink: 0
|
|
2614
|
+
},
|
|
2615
|
+
children: "What happened at each stage, what data flowed, what decisions were made, and why."
|
|
2616
|
+
}
|
|
2617
|
+
),
|
|
2618
|
+
hasStructured ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2619
|
+
StoryNarrative,
|
|
2620
|
+
{
|
|
2621
|
+
entries: narrativeEntries,
|
|
2622
|
+
stageCount: selectedIndex + 1,
|
|
2623
|
+
size,
|
|
2624
|
+
style: { flex: 1 }
|
|
2625
|
+
}
|
|
2626
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2627
|
+
NarrativeTrace,
|
|
2628
|
+
{
|
|
2629
|
+
narrative,
|
|
2630
|
+
revealedCount,
|
|
2631
|
+
size,
|
|
2632
|
+
style: { flex: 1 }
|
|
2633
|
+
}
|
|
2634
|
+
)
|
|
2635
|
+
]
|
|
2636
|
+
}
|
|
2637
|
+
);
|
|
2638
|
+
}
|
|
2639
|
+
|
|
2429
2640
|
// src/components/FlowchartView/SubflowTree.tsx
|
|
2430
|
-
var
|
|
2431
|
-
var
|
|
2641
|
+
var import_react13 = require("react");
|
|
2642
|
+
var import_jsx_runtime14 = require("react/jsx-runtime");
|
|
2432
2643
|
function specToTree(node) {
|
|
2644
|
+
if (!node) return [];
|
|
2433
2645
|
const entries = [];
|
|
2434
2646
|
const seen = /* @__PURE__ */ new Set();
|
|
2435
2647
|
function walk(n) {
|
|
2648
|
+
if (!n) return;
|
|
2436
2649
|
const id = n.name || n.id || "";
|
|
2437
2650
|
if (seen.has(id)) return;
|
|
2438
2651
|
seen.add(id);
|
|
@@ -2448,7 +2661,7 @@ function specToTree(node) {
|
|
|
2448
2661
|
entries.push(entry);
|
|
2449
2662
|
if (n.children) {
|
|
2450
2663
|
for (const child of n.children) {
|
|
2451
|
-
walk(child);
|
|
2664
|
+
if (child) walk(child);
|
|
2452
2665
|
}
|
|
2453
2666
|
}
|
|
2454
2667
|
if (n.next) {
|
|
@@ -2458,25 +2671,25 @@ function specToTree(node) {
|
|
|
2458
2671
|
walk(node);
|
|
2459
2672
|
return entries;
|
|
2460
2673
|
}
|
|
2461
|
-
var TreeNode = (0,
|
|
2674
|
+
var TreeNode = (0, import_react13.memo)(function TreeNode2({
|
|
2462
2675
|
entry,
|
|
2463
2676
|
depth,
|
|
2464
2677
|
activeStage,
|
|
2465
2678
|
doneStages,
|
|
2466
2679
|
onNodeSelect
|
|
2467
2680
|
}) {
|
|
2468
|
-
const [expanded, setExpanded] = (0,
|
|
2681
|
+
const [expanded, setExpanded] = (0, import_react13.useState)(true);
|
|
2469
2682
|
const hasChildren = entry.children && entry.children.length > 0;
|
|
2470
2683
|
const isActive = activeStage === entry.name;
|
|
2471
2684
|
const isDone = doneStages?.has(entry.name);
|
|
2472
|
-
const handleClick = (0,
|
|
2685
|
+
const handleClick = (0, import_react13.useCallback)(() => {
|
|
2473
2686
|
if (hasChildren) {
|
|
2474
2687
|
setExpanded((prev) => !prev);
|
|
2475
2688
|
}
|
|
2476
2689
|
onNodeSelect?.(entry.name, !!entry.isSubflow);
|
|
2477
2690
|
}, [hasChildren, onNodeSelect, entry.name, entry.isSubflow]);
|
|
2478
|
-
return /* @__PURE__ */ (0,
|
|
2479
|
-
/* @__PURE__ */ (0,
|
|
2691
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_jsx_runtime14.Fragment, { children: [
|
|
2692
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
|
|
2480
2693
|
"button",
|
|
2481
2694
|
{
|
|
2482
2695
|
onClick: handleClick,
|
|
@@ -2507,7 +2720,7 @@ var TreeNode = (0, import_react12.memo)(function TreeNode2({
|
|
|
2507
2720
|
}
|
|
2508
2721
|
},
|
|
2509
2722
|
children: [
|
|
2510
|
-
hasChildren ? /* @__PURE__ */ (0,
|
|
2723
|
+
hasChildren ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2511
2724
|
"span",
|
|
2512
2725
|
{
|
|
2513
2726
|
style: {
|
|
@@ -2522,8 +2735,8 @@ var TreeNode = (0, import_react12.memo)(function TreeNode2({
|
|
|
2522
2735
|
},
|
|
2523
2736
|
children: "\u25B6"
|
|
2524
2737
|
}
|
|
2525
|
-
) : /* @__PURE__ */ (0,
|
|
2526
|
-
/* @__PURE__ */ (0,
|
|
2738
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { style: { width: 12, flexShrink: 0 } }),
|
|
2739
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2527
2740
|
"span",
|
|
2528
2741
|
{
|
|
2529
2742
|
style: {
|
|
@@ -2535,8 +2748,8 @@ var TreeNode = (0, import_react12.memo)(function TreeNode2({
|
|
|
2535
2748
|
}
|
|
2536
2749
|
}
|
|
2537
2750
|
),
|
|
2538
|
-
/* @__PURE__ */ (0,
|
|
2539
|
-
/* @__PURE__ */ (0,
|
|
2751
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("span", { style: { display: "flex", flexDirection: "column", minWidth: 0 }, children: [
|
|
2752
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
|
|
2540
2753
|
"span",
|
|
2541
2754
|
{
|
|
2542
2755
|
style: {
|
|
@@ -2548,11 +2761,11 @@ var TreeNode = (0, import_react12.memo)(function TreeNode2({
|
|
|
2548
2761
|
},
|
|
2549
2762
|
children: [
|
|
2550
2763
|
entry.name,
|
|
2551
|
-
entry.isSubflow && /* @__PURE__ */ (0,
|
|
2764
|
+
entry.isSubflow && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { style: { opacity: 0.5, marginLeft: 4, fontSize: 10 }, children: "\u229E" })
|
|
2552
2765
|
]
|
|
2553
2766
|
}
|
|
2554
2767
|
),
|
|
2555
|
-
entry.description && /* @__PURE__ */ (0,
|
|
2768
|
+
entry.description && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2556
2769
|
"span",
|
|
2557
2770
|
{
|
|
2558
2771
|
style: {
|
|
@@ -2569,7 +2782,7 @@ var TreeNode = (0, import_react12.memo)(function TreeNode2({
|
|
|
2569
2782
|
]
|
|
2570
2783
|
}
|
|
2571
2784
|
),
|
|
2572
|
-
hasChildren && expanded && /* @__PURE__ */ (0,
|
|
2785
|
+
hasChildren && expanded && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { children: entry.children.map((child, i) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2573
2786
|
TreeNode2,
|
|
2574
2787
|
{
|
|
2575
2788
|
entry: child,
|
|
@@ -2578,12 +2791,12 @@ var TreeNode = (0, import_react12.memo)(function TreeNode2({
|
|
|
2578
2791
|
doneStages,
|
|
2579
2792
|
onNodeSelect
|
|
2580
2793
|
},
|
|
2581
|
-
`${child.name}-${i}`
|
|
2794
|
+
child.subflowId ?? `${child.name}-${i}`
|
|
2582
2795
|
)) })
|
|
2583
2796
|
] });
|
|
2584
2797
|
});
|
|
2585
|
-
var SectionLabel = (0,
|
|
2586
|
-
return /* @__PURE__ */ (0,
|
|
2798
|
+
var SectionLabel = (0, import_react13.memo)(function SectionLabel2({ children }) {
|
|
2799
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2587
2800
|
"div",
|
|
2588
2801
|
{
|
|
2589
2802
|
style: {
|
|
@@ -2598,7 +2811,7 @@ var SectionLabel = (0, import_react12.memo)(function SectionLabel2({ children })
|
|
|
2598
2811
|
}
|
|
2599
2812
|
);
|
|
2600
2813
|
});
|
|
2601
|
-
var SubflowTree = (0,
|
|
2814
|
+
var SubflowTree = (0, import_react13.memo)(function SubflowTree2({
|
|
2602
2815
|
spec,
|
|
2603
2816
|
activeStage,
|
|
2604
2817
|
doneStages,
|
|
@@ -2607,10 +2820,10 @@ var SubflowTree = (0, import_react12.memo)(function SubflowTree2({
|
|
|
2607
2820
|
className,
|
|
2608
2821
|
style
|
|
2609
2822
|
}) {
|
|
2610
|
-
const tree = (0,
|
|
2611
|
-
const subflowStages = (0,
|
|
2823
|
+
const tree = (0, import_react13.useMemo)(() => specToTree(spec), [spec]);
|
|
2824
|
+
const subflowStages = (0, import_react13.useMemo)(() => tree.filter((e) => e.isSubflow), [tree]);
|
|
2612
2825
|
if (subflowStages.length === 0) return null;
|
|
2613
|
-
return /* @__PURE__ */ (0,
|
|
2826
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
|
|
2614
2827
|
"div",
|
|
2615
2828
|
{
|
|
2616
2829
|
className,
|
|
@@ -2628,8 +2841,8 @@ var SubflowTree = (0, import_react12.memo)(function SubflowTree2({
|
|
|
2628
2841
|
...style
|
|
2629
2842
|
},
|
|
2630
2843
|
children: [
|
|
2631
|
-
!unstyled && /* @__PURE__ */ (0,
|
|
2632
|
-
subflowStages.map((entry, i) => /* @__PURE__ */ (0,
|
|
2844
|
+
!unstyled && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(SectionLabel, { children: "Subflows" }),
|
|
2845
|
+
subflowStages.map((entry, i) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2633
2846
|
TreeNode,
|
|
2634
2847
|
{
|
|
2635
2848
|
entry,
|
|
@@ -2638,98 +2851,602 @@ var SubflowTree = (0, import_react12.memo)(function SubflowTree2({
|
|
|
2638
2851
|
doneStages,
|
|
2639
2852
|
onNodeSelect
|
|
2640
2853
|
},
|
|
2641
|
-
`${entry.name}-${i}`
|
|
2854
|
+
entry.subflowId ?? `${entry.name}-${i}`
|
|
2642
2855
|
))
|
|
2643
2856
|
]
|
|
2644
2857
|
}
|
|
2645
2858
|
);
|
|
2646
2859
|
});
|
|
2647
2860
|
|
|
2648
|
-
// src/
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2861
|
+
// src/components/FlowchartView/SubflowBreadcrumb.tsx
|
|
2862
|
+
var import_react14 = require("react");
|
|
2863
|
+
var import_jsx_runtime15 = require("react/jsx-runtime");
|
|
2864
|
+
var SubflowBreadcrumb = (0, import_react14.memo)(function SubflowBreadcrumb2({
|
|
2865
|
+
breadcrumbs,
|
|
2866
|
+
onNavigate
|
|
2867
|
+
}) {
|
|
2868
|
+
if (breadcrumbs.length <= 1) return null;
|
|
2869
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2870
|
+
"div",
|
|
2871
|
+
{
|
|
2872
|
+
style: {
|
|
2873
|
+
display: "flex",
|
|
2874
|
+
alignItems: "center",
|
|
2875
|
+
gap: 4,
|
|
2876
|
+
padding: "6px 12px",
|
|
2877
|
+
background: theme.bgSecondary,
|
|
2878
|
+
borderBottom: `1px solid ${theme.border}`,
|
|
2879
|
+
fontSize: 12,
|
|
2880
|
+
fontFamily: theme.fontSans,
|
|
2881
|
+
flexShrink: 0,
|
|
2882
|
+
overflowX: "auto"
|
|
2883
|
+
},
|
|
2884
|
+
children: breadcrumbs.map((crumb, i) => {
|
|
2885
|
+
const isLast = i === breadcrumbs.length - 1;
|
|
2886
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("span", { style: { display: "flex", alignItems: "center", gap: 4 }, children: [
|
|
2887
|
+
i > 0 && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { style: { color: theme.textMuted, fontSize: 10 }, children: "\u203A" }),
|
|
2888
|
+
isLast ? /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("span", { style: { display: "flex", alignItems: "center", gap: 6 }, children: [
|
|
2889
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2890
|
+
"span",
|
|
2891
|
+
{
|
|
2892
|
+
style: {
|
|
2893
|
+
color: theme.primary,
|
|
2894
|
+
fontWeight: 600
|
|
2895
|
+
},
|
|
2896
|
+
children: crumb.label
|
|
2897
|
+
}
|
|
2898
|
+
),
|
|
2899
|
+
crumb.description && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
|
|
2900
|
+
"span",
|
|
2901
|
+
{
|
|
2902
|
+
style: {
|
|
2903
|
+
color: theme.textMuted,
|
|
2904
|
+
fontWeight: 400,
|
|
2905
|
+
fontSize: 11
|
|
2906
|
+
},
|
|
2907
|
+
children: [
|
|
2908
|
+
"\u2014 ",
|
|
2909
|
+
crumb.description
|
|
2910
|
+
]
|
|
2911
|
+
}
|
|
2912
|
+
)
|
|
2913
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2914
|
+
"button",
|
|
2915
|
+
{
|
|
2916
|
+
onClick: () => onNavigate(i),
|
|
2917
|
+
style: {
|
|
2918
|
+
background: "none",
|
|
2919
|
+
border: "none",
|
|
2920
|
+
color: theme.textSecondary,
|
|
2921
|
+
cursor: "pointer",
|
|
2922
|
+
padding: "2px 4px",
|
|
2923
|
+
borderRadius: 4,
|
|
2924
|
+
fontSize: 12,
|
|
2925
|
+
fontFamily: "inherit",
|
|
2926
|
+
fontWeight: 500,
|
|
2927
|
+
transition: "color 0.15s"
|
|
2928
|
+
},
|
|
2929
|
+
onMouseEnter: (e) => {
|
|
2930
|
+
e.currentTarget.style.color = `${theme.primary}`;
|
|
2931
|
+
},
|
|
2932
|
+
onMouseLeave: (e) => {
|
|
2933
|
+
e.currentTarget.style.color = `${theme.textSecondary}`;
|
|
2934
|
+
},
|
|
2935
|
+
children: crumb.label
|
|
2936
|
+
}
|
|
2937
|
+
)
|
|
2938
|
+
] }, `${crumb.label}-${i}`);
|
|
2939
|
+
})
|
|
2940
|
+
}
|
|
2941
|
+
);
|
|
2942
|
+
});
|
|
2943
|
+
|
|
2944
|
+
// src/components/ExplainableShell/ExplainableShell.tsx
|
|
2945
|
+
var import_jsx_runtime16 = require("react/jsx-runtime");
|
|
2946
|
+
var HLinePill = (0, import_react15.memo)(function HLinePill2({
|
|
2947
|
+
label,
|
|
2948
|
+
detail,
|
|
2949
|
+
expanded,
|
|
2950
|
+
onClick
|
|
2951
|
+
}) {
|
|
2952
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { style: {
|
|
2953
|
+
display: "flex",
|
|
2954
|
+
alignItems: "center",
|
|
2955
|
+
gap: 0,
|
|
2956
|
+
padding: "0"
|
|
2957
|
+
}, children: [
|
|
2958
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: { flex: 1, height: 1, background: theme.border } }),
|
|
2959
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
|
|
2960
|
+
"button",
|
|
2961
|
+
{
|
|
2962
|
+
onClick,
|
|
2963
|
+
style: {
|
|
2964
|
+
display: "flex",
|
|
2965
|
+
alignItems: "center",
|
|
2966
|
+
gap: 5,
|
|
2967
|
+
padding: "3px 12px",
|
|
2968
|
+
margin: "4px 0",
|
|
2969
|
+
fontSize: 10,
|
|
2970
|
+
fontWeight: 600,
|
|
2971
|
+
fontFamily: "inherit",
|
|
2972
|
+
color: theme.textMuted,
|
|
2973
|
+
background: theme.bgSecondary,
|
|
2974
|
+
border: `1px solid ${theme.border}`,
|
|
2975
|
+
borderRadius: 10,
|
|
2976
|
+
cursor: "pointer",
|
|
2977
|
+
whiteSpace: "nowrap",
|
|
2978
|
+
letterSpacing: "0.04em",
|
|
2979
|
+
textTransform: "uppercase",
|
|
2980
|
+
transition: "color 0.15s ease"
|
|
2981
|
+
},
|
|
2982
|
+
children: [
|
|
2983
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { style: { fontSize: 7 }, children: expanded ? "\u25BC" : "\u25B6" }),
|
|
2984
|
+
label,
|
|
2985
|
+
detail && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { style: { fontWeight: 400, opacity: 0.5, fontSize: 9 }, children: detail })
|
|
2986
|
+
]
|
|
2987
|
+
}
|
|
2988
|
+
),
|
|
2989
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: { flex: 1, height: 1, background: theme.border } })
|
|
2990
|
+
] });
|
|
2991
|
+
});
|
|
2992
|
+
var VLinePill = (0, import_react15.memo)(function VLinePill2({
|
|
2993
|
+
label,
|
|
2994
|
+
expanded,
|
|
2995
|
+
side = "right",
|
|
2996
|
+
onClick
|
|
2997
|
+
}) {
|
|
2998
|
+
const arrow = side === "right" ? expanded ? "\u25B6" : "\u25C0" : expanded ? "\u25C0" : "\u25B6";
|
|
2999
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { style: {
|
|
3000
|
+
display: "flex",
|
|
3001
|
+
flexDirection: "column",
|
|
3002
|
+
alignItems: "center",
|
|
3003
|
+
gap: 0,
|
|
3004
|
+
padding: "0"
|
|
3005
|
+
}, children: [
|
|
3006
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: { flex: 1, width: 1, background: theme.border } }),
|
|
3007
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
|
|
3008
|
+
"button",
|
|
3009
|
+
{
|
|
3010
|
+
onClick,
|
|
3011
|
+
style: {
|
|
3012
|
+
display: "flex",
|
|
3013
|
+
alignItems: "center",
|
|
3014
|
+
gap: 4,
|
|
3015
|
+
padding: "10px 4px",
|
|
3016
|
+
margin: "0 3px",
|
|
3017
|
+
fontSize: 10,
|
|
3018
|
+
fontWeight: 600,
|
|
3019
|
+
fontFamily: "inherit",
|
|
3020
|
+
color: theme.textMuted,
|
|
3021
|
+
background: theme.bgSecondary,
|
|
3022
|
+
border: `1px solid ${theme.border}`,
|
|
3023
|
+
borderRadius: 10,
|
|
3024
|
+
cursor: "pointer",
|
|
3025
|
+
whiteSpace: "nowrap",
|
|
3026
|
+
letterSpacing: "0.04em",
|
|
3027
|
+
textTransform: "uppercase",
|
|
3028
|
+
writingMode: "vertical-lr",
|
|
3029
|
+
transition: "color 0.15s ease"
|
|
3030
|
+
},
|
|
3031
|
+
children: [
|
|
3032
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { style: { fontSize: 7, writingMode: "horizontal-tb" }, children: arrow }),
|
|
3033
|
+
label
|
|
3034
|
+
]
|
|
3035
|
+
}
|
|
3036
|
+
),
|
|
3037
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: { flex: 1, width: 1, background: theme.border } })
|
|
3038
|
+
] });
|
|
3039
|
+
});
|
|
3040
|
+
var RIGHT_PANEL_LABELS = {
|
|
3041
|
+
memory: "Memory",
|
|
3042
|
+
narrative: "Narrative"
|
|
3043
|
+
};
|
|
3044
|
+
var DetailsContent = (0, import_react15.memo)(function DetailsContent2({
|
|
3045
|
+
snapshots,
|
|
3046
|
+
selectedIndex,
|
|
3047
|
+
narrativeEntries,
|
|
3048
|
+
narrative,
|
|
3049
|
+
size,
|
|
3050
|
+
fillHeight
|
|
3051
|
+
}) {
|
|
3052
|
+
const [rightPanel, setRightPanel] = (0, import_react15.useState)("memory");
|
|
3053
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { style: { flex: 1, display: "flex", flexDirection: "column", overflow: "hidden" }, children: [
|
|
3054
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: { display: "flex", borderBottom: `1px solid ${theme.border}`, flexShrink: 0 }, children: ["memory", "narrative"].map((panel) => {
|
|
3055
|
+
const active = rightPanel === panel;
|
|
3056
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
3057
|
+
"button",
|
|
3058
|
+
{
|
|
3059
|
+
onClick: () => setRightPanel(panel),
|
|
3060
|
+
style: {
|
|
3061
|
+
flex: 1,
|
|
3062
|
+
padding: "6px 8px",
|
|
3063
|
+
fontSize: 11,
|
|
3064
|
+
fontWeight: active ? 600 : 400,
|
|
3065
|
+
color: active ? theme.primary : theme.textMuted,
|
|
3066
|
+
background: active ? `color-mix(in srgb, ${theme.primary} 8%, transparent)` : "transparent",
|
|
3067
|
+
border: "none",
|
|
3068
|
+
borderBottom: active ? `2px solid ${theme.primary}` : "2px solid transparent",
|
|
3069
|
+
cursor: "pointer",
|
|
3070
|
+
textTransform: "uppercase",
|
|
3071
|
+
letterSpacing: "0.06em",
|
|
3072
|
+
fontFamily: "inherit"
|
|
3073
|
+
},
|
|
3074
|
+
children: RIGHT_PANEL_LABELS[panel]
|
|
3075
|
+
},
|
|
3076
|
+
panel
|
|
3077
|
+
);
|
|
3078
|
+
}) }),
|
|
3079
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { style: { flex: 1, overflow: "auto" }, children: [
|
|
3080
|
+
rightPanel === "memory" && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(MemoryPanel, { snapshots, selectedIndex, size, style: fillHeight ? { height: "100%" } : void 0 }),
|
|
3081
|
+
rightPanel === "narrative" && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(NarrativePanel, { snapshots, selectedIndex, narrativeEntries, narrative, size, style: fillHeight ? { height: "100%" } : void 0 })
|
|
3082
|
+
] })
|
|
3083
|
+
] });
|
|
3084
|
+
});
|
|
3085
|
+
function resolveSubflowLevel(parentSpec, parentSnapshots, subflowNodeName, narrativeEntries) {
|
|
3086
|
+
const specNode = findSubflowSpecNode(parentSpec, subflowNodeName);
|
|
3087
|
+
if (!specNode?.subflowStructure) return null;
|
|
3088
|
+
const parentSnap = parentSnapshots.find(
|
|
3089
|
+
(s) => s.stageName === subflowNodeName || s.stageLabel === subflowNodeName
|
|
3090
|
+
);
|
|
3091
|
+
if (!parentSnap?.subflowResult) return null;
|
|
3092
|
+
const sfNarrative = narrativeEntries ? extractSubflowNarrative(narrativeEntries, subflowNodeName) : void 0;
|
|
3093
|
+
const sfSnapshots = subflowResultToSnapshots(parentSnap.subflowResult, sfNarrative);
|
|
3094
|
+
if (sfSnapshots.length === 0) return null;
|
|
3095
|
+
return {
|
|
3096
|
+
subflowId: specNode.subflowId ?? subflowNodeName,
|
|
3097
|
+
label: specNode.subflowName ?? specNode.name,
|
|
3098
|
+
spec: specNode.subflowStructure,
|
|
3099
|
+
snapshots: sfSnapshots
|
|
3100
|
+
};
|
|
2654
3101
|
}
|
|
2655
|
-
function
|
|
2656
|
-
const
|
|
2657
|
-
let
|
|
3102
|
+
function extractSubflowNarrative(entries, subflowName) {
|
|
3103
|
+
const result = [];
|
|
3104
|
+
let inside = false;
|
|
2658
3105
|
for (const entry of entries) {
|
|
2659
|
-
if (entry.
|
|
2660
|
-
|
|
3106
|
+
if (entry.type === "subflow" && entry.text.includes(subflowName) && entry.text.startsWith("Entering")) {
|
|
3107
|
+
inside = true;
|
|
3108
|
+
continue;
|
|
2661
3109
|
}
|
|
2662
|
-
if (
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
3110
|
+
if (inside && entry.type === "subflow" && entry.text.includes(subflowName) && entry.text.startsWith("Exiting")) break;
|
|
3111
|
+
if (inside) result.push(entry);
|
|
3112
|
+
}
|
|
3113
|
+
return result;
|
|
3114
|
+
}
|
|
3115
|
+
function findSubflowSpecNode(node, name) {
|
|
3116
|
+
if ((node.name === name || node.id === name) && node.isSubflowRoot) return node;
|
|
3117
|
+
if (node.children) {
|
|
3118
|
+
for (const child of node.children) {
|
|
3119
|
+
const f = findSubflowSpecNode(child, name);
|
|
3120
|
+
if (f) return f;
|
|
2668
3121
|
}
|
|
2669
3122
|
}
|
|
2670
|
-
return
|
|
3123
|
+
if (node.next) return findSubflowSpecNode(node.next, name);
|
|
3124
|
+
return null;
|
|
2671
3125
|
}
|
|
2672
|
-
function
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
3126
|
+
function hasSubflowNodes(node) {
|
|
3127
|
+
if (!node) return false;
|
|
3128
|
+
if (node.isSubflowRoot) return true;
|
|
3129
|
+
if (node.children?.some((c) => c && hasSubflowNodes(c))) return true;
|
|
3130
|
+
if (node.next && hasSubflowNodes(node.next)) return true;
|
|
3131
|
+
return false;
|
|
3132
|
+
}
|
|
3133
|
+
function ExplainableShell({
|
|
3134
|
+
snapshots,
|
|
3135
|
+
spec,
|
|
3136
|
+
title,
|
|
3137
|
+
resultData,
|
|
3138
|
+
logs = [],
|
|
3139
|
+
narrative,
|
|
3140
|
+
narrativeEntries,
|
|
3141
|
+
tabs = ["result", "explainable"],
|
|
3142
|
+
defaultTab,
|
|
3143
|
+
hideConsole = false,
|
|
3144
|
+
panelLabels,
|
|
3145
|
+
defaultExpanded,
|
|
3146
|
+
renderFlowchart,
|
|
3147
|
+
size = "default",
|
|
3148
|
+
unstyled = false,
|
|
3149
|
+
className,
|
|
3150
|
+
style
|
|
3151
|
+
}) {
|
|
3152
|
+
const leftLabel = panelLabels?.topology ?? "Topology";
|
|
3153
|
+
const rightLabel = panelLabels?.details ?? "Details";
|
|
3154
|
+
const bottomLabel = panelLabels?.timeline ?? "Timeline";
|
|
3155
|
+
const shellRef = (0, import_react15.useRef)(null);
|
|
3156
|
+
const [isNarrow, setIsNarrow] = (0, import_react15.useState)(false);
|
|
3157
|
+
(0, import_react15.useEffect)(() => {
|
|
3158
|
+
const el = shellRef.current;
|
|
3159
|
+
if (!el) return;
|
|
3160
|
+
const ro = new ResizeObserver(([entry]) => {
|
|
3161
|
+
setIsNarrow(entry.contentRect.width < 640);
|
|
3162
|
+
});
|
|
3163
|
+
ro.observe(el);
|
|
3164
|
+
return () => ro.disconnect();
|
|
3165
|
+
}, []);
|
|
3166
|
+
const [activeTab, setActiveTab] = (0, import_react15.useState)(defaultTab ?? tabs[0]);
|
|
3167
|
+
const [snapshotIdx, setSnapshotIdx] = (0, import_react15.useState)(0);
|
|
3168
|
+
const [drillDownStack, setDrillDownStack] = (0, import_react15.useState)([]);
|
|
3169
|
+
const [rightExpanded, setRightExpanded] = (0, import_react15.useState)(defaultExpanded?.details ?? true);
|
|
3170
|
+
const [leftExpanded, setLeftExpanded] = (0, import_react15.useState)(defaultExpanded?.topology ?? false);
|
|
3171
|
+
const [timelineExpanded, setTimelineExpanded] = (0, import_react15.useState)(defaultExpanded?.timeline ?? false);
|
|
3172
|
+
(0, import_react15.useEffect)(() => {
|
|
3173
|
+
if (isNarrow) {
|
|
3174
|
+
setLeftExpanded(false);
|
|
3175
|
+
setRightExpanded(false);
|
|
3176
|
+
setTimelineExpanded(false);
|
|
3177
|
+
}
|
|
3178
|
+
}, [isNarrow]);
|
|
3179
|
+
const triggerReflow = (0, import_react15.useCallback)(() => {
|
|
3180
|
+
requestAnimationFrame(() => window.dispatchEvent(new Event("resize")));
|
|
3181
|
+
}, []);
|
|
3182
|
+
const toggleLeft = (0, import_react15.useCallback)((v2) => {
|
|
3183
|
+
setLeftExpanded(v2);
|
|
3184
|
+
triggerReflow();
|
|
3185
|
+
}, [triggerReflow]);
|
|
3186
|
+
const toggleRight = (0, import_react15.useCallback)((v2) => {
|
|
3187
|
+
setRightExpanded(v2);
|
|
3188
|
+
triggerReflow();
|
|
3189
|
+
}, [triggerReflow]);
|
|
3190
|
+
const toggleTimeline = (0, import_react15.useCallback)(() => {
|
|
3191
|
+
setTimelineExpanded((p) => !p);
|
|
3192
|
+
triggerReflow();
|
|
3193
|
+
}, [triggerReflow]);
|
|
3194
|
+
const isInSubflow = drillDownStack.length > 0;
|
|
3195
|
+
const currentLevel = (0, import_react15.useMemo)(() => {
|
|
3196
|
+
if (drillDownStack.length > 0) {
|
|
3197
|
+
const top = drillDownStack[drillDownStack.length - 1];
|
|
3198
|
+
return { spec: top.spec, snapshots: top.snapshots };
|
|
3199
|
+
}
|
|
3200
|
+
return { spec: spec ?? null, snapshots };
|
|
3201
|
+
}, [drillDownStack, spec, snapshots]);
|
|
3202
|
+
const activeSnapshots = currentLevel.snapshots;
|
|
3203
|
+
const activeSpec = currentLevel.spec;
|
|
3204
|
+
const safeIdx = activeSnapshots.length > 0 ? Math.max(0, Math.min(snapshotIdx, activeSnapshots.length - 1)) : 0;
|
|
3205
|
+
const activeNarrative = (0, import_react15.useMemo)(() => {
|
|
3206
|
+
if (!isInSubflow) return narrative;
|
|
3207
|
+
const lines = [];
|
|
3208
|
+
for (const snap of activeSnapshots) {
|
|
3209
|
+
const stageLines = (snap.narrative ?? "").split("\n").filter(Boolean);
|
|
3210
|
+
lines.push(...stageLines);
|
|
3211
|
+
}
|
|
3212
|
+
return lines.length > 0 ? lines : void 0;
|
|
3213
|
+
}, [isInSubflow, narrative, activeSnapshots]);
|
|
3214
|
+
const activeNarrativeEntries = isInSubflow ? void 0 : narrativeEntries;
|
|
3215
|
+
const breadcrumbs = (0, import_react15.useMemo)(() => {
|
|
3216
|
+
const root = { label: title || "Flowchart", spec, description: spec?.description };
|
|
3217
|
+
return [root, ...drillDownStack.map((e) => ({ label: e.label, spec: e.spec, description: void 0 }))];
|
|
3218
|
+
}, [spec, title, drillDownStack]);
|
|
3219
|
+
const showTreeSidebar = (0, import_react15.useMemo)(() => !!spec && hasSubflowNodes(spec), [spec]);
|
|
3220
|
+
const rootOverlay = (0, import_react15.useMemo)(() => {
|
|
3221
|
+
if (isInSubflow || !snapshots.length) return { activeStage: void 0, doneStages: void 0 };
|
|
3222
|
+
const doneStages = new Set(snapshots.slice(0, safeIdx).map((s) => s.stageLabel));
|
|
3223
|
+
const activeStage = snapshots[safeIdx]?.stageLabel ?? null;
|
|
3224
|
+
return { activeStage, doneStages };
|
|
3225
|
+
}, [isInSubflow, snapshots, safeIdx]);
|
|
3226
|
+
const handleTabChange = (0, import_react15.useCallback)((tab) => {
|
|
3227
|
+
setActiveTab(tab);
|
|
3228
|
+
setDrillDownStack([]);
|
|
3229
|
+
setSnapshotIdx(999);
|
|
3230
|
+
}, []);
|
|
3231
|
+
const handleSnapshotChange = (0, import_react15.useCallback)((idx) => {
|
|
3232
|
+
if (typeof idx === "number") setSnapshotIdx(idx);
|
|
3233
|
+
}, []);
|
|
3234
|
+
const handleDrillDown = (0, import_react15.useCallback)(
|
|
3235
|
+
(nodeName) => {
|
|
3236
|
+
if (!activeSpec) return;
|
|
3237
|
+
const entry = resolveSubflowLevel(activeSpec, activeSnapshots, nodeName, narrativeEntries);
|
|
3238
|
+
if (entry) {
|
|
3239
|
+
setDrillDownStack((prev) => [...prev, { ...entry, parentSnapshotIdx: snapshotIdx }]);
|
|
3240
|
+
setSnapshotIdx(0);
|
|
3241
|
+
}
|
|
3242
|
+
},
|
|
3243
|
+
[activeSpec, activeSnapshots, narrativeEntries, snapshotIdx]
|
|
3244
|
+
);
|
|
3245
|
+
const handleBreadcrumbNavigate = (0, import_react15.useCallback)((level) => {
|
|
3246
|
+
setDrillDownStack((prev) => {
|
|
3247
|
+
const popped = level === 0 ? prev[0] : prev[level];
|
|
3248
|
+
if (popped) setSnapshotIdx(popped.parentSnapshotIdx);
|
|
3249
|
+
return level === 0 ? [] : prev.slice(0, level);
|
|
3250
|
+
});
|
|
3251
|
+
}, []);
|
|
3252
|
+
const handleNodeClick = (0, import_react15.useCallback)(
|
|
3253
|
+
(indexOrId) => {
|
|
3254
|
+
if (typeof indexOrId === "number") {
|
|
3255
|
+
setSnapshotIdx(indexOrId);
|
|
3256
|
+
return;
|
|
3257
|
+
}
|
|
3258
|
+
if (activeSpec) {
|
|
3259
|
+
const sfNode = findSubflowSpecNode(activeSpec, indexOrId);
|
|
3260
|
+
if (sfNode?.subflowStructure) {
|
|
3261
|
+
handleDrillDown(indexOrId);
|
|
3262
|
+
return;
|
|
3263
|
+
}
|
|
3264
|
+
}
|
|
3265
|
+
const idx = activeSnapshots.findIndex((s) => s.stageLabel === indexOrId);
|
|
3266
|
+
if (idx >= 0) setSnapshotIdx(idx);
|
|
3267
|
+
},
|
|
3268
|
+
[activeSpec, activeSnapshots, handleDrillDown]
|
|
3269
|
+
);
|
|
3270
|
+
const handleTreeNodeSelect = (0, import_react15.useCallback)(
|
|
3271
|
+
(name, isSubflow) => {
|
|
3272
|
+
if (isSubflow && spec) {
|
|
3273
|
+
setDrillDownStack([]);
|
|
3274
|
+
const entry = resolveSubflowLevel(spec, snapshots, name, narrativeEntries);
|
|
3275
|
+
if (entry) {
|
|
3276
|
+
setDrillDownStack([{ ...entry, parentSnapshotIdx: snapshotIdx }]);
|
|
3277
|
+
setSnapshotIdx(0);
|
|
3278
|
+
}
|
|
2683
3279
|
} else {
|
|
2684
|
-
|
|
3280
|
+
setDrillDownStack([]);
|
|
3281
|
+
const idx = snapshots.findIndex((s) => s.stageLabel === name);
|
|
3282
|
+
if (idx >= 0) setSnapshotIdx(idx);
|
|
2685
3283
|
}
|
|
2686
|
-
}
|
|
3284
|
+
},
|
|
3285
|
+
[spec, snapshots, narrativeEntries, snapshotIdx]
|
|
3286
|
+
);
|
|
3287
|
+
const tabLabels = {
|
|
3288
|
+
result: "Result",
|
|
3289
|
+
explainable: "Explainable",
|
|
3290
|
+
"ai-compatible": "AI-Compatible"
|
|
3291
|
+
};
|
|
3292
|
+
if (unstyled) {
|
|
3293
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className, style, "data-fp": "explainable-shell", children: [
|
|
3294
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { "data-fp": "shell-tabs", children: tabs.map((tab) => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("button", { "data-fp": "shell-tab", "data-active": tab === activeTab, onClick: () => handleTabChange(tab), children: tabLabels[tab] }, tab)) }),
|
|
3295
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { "data-fp": "shell-content", "data-tab": activeTab, children: [
|
|
3296
|
+
activeTab === "result" && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(ResultPanel, { data: resultData ?? null, logs, hideConsole, unstyled: true }),
|
|
3297
|
+
(activeTab === "explainable" || activeTab === "ai-compatible") && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
|
|
3298
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(TimeTravelControls, { snapshots: activeSnapshots, selectedIndex: safeIdx, onIndexChange: handleSnapshotChange, unstyled: true }),
|
|
3299
|
+
isInSubflow && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(SubflowBreadcrumb, { breadcrumbs, onNavigate: handleBreadcrumbNavigate }),
|
|
3300
|
+
activeSpec && renderFlowchart?.({ spec: activeSpec, snapshots: activeSnapshots, selectedIndex: safeIdx, onNodeClick: handleNodeClick }),
|
|
3301
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(MemoryPanel, { snapshots: activeSnapshots, selectedIndex: safeIdx, unstyled: true }),
|
|
3302
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(NarrativePanel, { snapshots: activeSnapshots, selectedIndex: safeIdx, narrativeEntries: activeNarrativeEntries, narrative: activeNarrative, unstyled: true }),
|
|
3303
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(GanttTimeline, { snapshots: activeSnapshots, selectedIndex: safeIdx, onSelect: handleSnapshotChange, unstyled: true })
|
|
3304
|
+
] })
|
|
3305
|
+
] })
|
|
3306
|
+
] });
|
|
2687
3307
|
}
|
|
2688
|
-
const
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
3308
|
+
const isVisualizationTab = activeTab === "explainable" || activeTab === "ai-compatible";
|
|
3309
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
|
|
3310
|
+
"div",
|
|
3311
|
+
{
|
|
3312
|
+
ref: shellRef,
|
|
3313
|
+
className,
|
|
3314
|
+
style: {
|
|
3315
|
+
height: "100%",
|
|
3316
|
+
display: "flex",
|
|
3317
|
+
flexDirection: "column",
|
|
3318
|
+
overflow: "hidden",
|
|
3319
|
+
background: theme.bgPrimary,
|
|
3320
|
+
color: theme.textPrimary,
|
|
3321
|
+
fontFamily: theme.fontSans,
|
|
3322
|
+
fontSize: 12,
|
|
3323
|
+
...style
|
|
3324
|
+
},
|
|
3325
|
+
"data-fp": "explainable-shell",
|
|
3326
|
+
children: [
|
|
3327
|
+
tabs.length > 1 && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: {
|
|
3328
|
+
display: "flex",
|
|
3329
|
+
borderBottom: `1px solid ${theme.border}`,
|
|
3330
|
+
background: theme.bgSecondary,
|
|
3331
|
+
flexShrink: 0
|
|
3332
|
+
}, children: tabs.map((tab) => {
|
|
3333
|
+
const active = tab === activeTab;
|
|
3334
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
3335
|
+
"button",
|
|
3336
|
+
{
|
|
3337
|
+
onClick: () => handleTabChange(tab),
|
|
3338
|
+
style: {
|
|
3339
|
+
padding: "6px 14px",
|
|
3340
|
+
fontSize: 11,
|
|
3341
|
+
fontWeight: active ? 700 : 500,
|
|
3342
|
+
textTransform: "uppercase",
|
|
3343
|
+
letterSpacing: "0.08em",
|
|
3344
|
+
color: active ? theme.primary : theme.textMuted,
|
|
3345
|
+
background: "transparent",
|
|
3346
|
+
border: "none",
|
|
3347
|
+
borderBottom: active ? `2px solid ${theme.primary}` : "2px solid transparent",
|
|
3348
|
+
cursor: "pointer",
|
|
3349
|
+
fontFamily: "inherit"
|
|
3350
|
+
},
|
|
3351
|
+
children: tabLabels[tab]
|
|
3352
|
+
},
|
|
3353
|
+
tab
|
|
3354
|
+
);
|
|
3355
|
+
}) }),
|
|
3356
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { style: { flex: 1, overflow: isNarrow ? "auto" : "hidden", display: "flex", flexDirection: "column" }, children: [
|
|
3357
|
+
activeTab === "result" && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(ResultPanel, { data: resultData ?? null, logs, hideConsole, size }),
|
|
3358
|
+
isVisualizationTab && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
|
|
3359
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
3360
|
+
TimeTravelControls,
|
|
3361
|
+
{
|
|
3362
|
+
snapshots: activeSnapshots,
|
|
3363
|
+
selectedIndex: safeIdx,
|
|
3364
|
+
onIndexChange: handleSnapshotChange,
|
|
3365
|
+
size
|
|
3366
|
+
}
|
|
3367
|
+
),
|
|
3368
|
+
isInSubflow && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(SubflowBreadcrumb, { breadcrumbs, onNavigate: handleBreadcrumbNavigate }),
|
|
3369
|
+
isNarrow ? (
|
|
3370
|
+
/* ── Mobile: stacked vertical ── */
|
|
3371
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
|
|
3372
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: { height: 350, flexShrink: 0, overflow: "hidden" }, children: renderFlowchart && activeSpec && renderFlowchart({
|
|
3373
|
+
spec: activeSpec,
|
|
3374
|
+
snapshots: activeSnapshots,
|
|
3375
|
+
selectedIndex: safeIdx,
|
|
3376
|
+
onNodeClick: handleNodeClick
|
|
3377
|
+
}) }),
|
|
3378
|
+
showTreeSidebar && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
|
|
3379
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(HLinePill, { label: leftLabel, expanded: leftExpanded, onClick: () => toggleLeft(!leftExpanded) }),
|
|
3380
|
+
leftExpanded && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: { maxHeight: 180, overflow: "auto", flexShrink: 0 }, children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
3381
|
+
SubflowTree,
|
|
3382
|
+
{
|
|
3383
|
+
spec,
|
|
3384
|
+
activeStage: rootOverlay.activeStage,
|
|
3385
|
+
doneStages: rootOverlay.doneStages,
|
|
3386
|
+
onNodeSelect: handleTreeNodeSelect
|
|
3387
|
+
}
|
|
3388
|
+
) })
|
|
3389
|
+
] }),
|
|
3390
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(HLinePill, { label: rightLabel, expanded: rightExpanded, onClick: () => toggleRight(!rightExpanded) }),
|
|
3391
|
+
rightExpanded && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: { maxHeight: 250, flexShrink: 0, display: "flex", flexDirection: "column", overflow: "hidden" }, children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
3392
|
+
DetailsContent,
|
|
3393
|
+
{
|
|
3394
|
+
snapshots: activeSnapshots,
|
|
3395
|
+
selectedIndex: safeIdx,
|
|
3396
|
+
narrativeEntries: activeNarrativeEntries,
|
|
3397
|
+
narrative: activeNarrative,
|
|
3398
|
+
size
|
|
3399
|
+
}
|
|
3400
|
+
) }),
|
|
3401
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(HLinePill, { label: bottomLabel, detail: `${activeSnapshots.length} stages`, expanded: timelineExpanded, onClick: toggleTimeline }),
|
|
3402
|
+
timelineExpanded && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: { flexShrink: 0, overflow: "hidden" }, children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(GanttTimeline, { snapshots: activeSnapshots, selectedIndex: safeIdx, onSelect: handleSnapshotChange, size }) })
|
|
3403
|
+
] })
|
|
3404
|
+
) : (
|
|
3405
|
+
/* ── Desktop: side-by-side ── */
|
|
3406
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
|
|
3407
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { style: { flex: 1, display: "flex", overflow: "hidden" }, children: [
|
|
3408
|
+
showTreeSidebar && (leftExpanded ? /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { style: { width: 220, flexShrink: 0, display: "flex", flexDirection: "row", overflow: "hidden" }, children: [
|
|
3409
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: { flex: 1, overflow: "auto" }, children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
3410
|
+
SubflowTree,
|
|
3411
|
+
{
|
|
3412
|
+
spec,
|
|
3413
|
+
activeStage: rootOverlay.activeStage,
|
|
3414
|
+
doneStages: rootOverlay.doneStages,
|
|
3415
|
+
onNodeSelect: handleTreeNodeSelect
|
|
3416
|
+
}
|
|
3417
|
+
) }),
|
|
3418
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(VLinePill, { label: leftLabel, expanded: true, side: "left", onClick: () => toggleLeft(false) })
|
|
3419
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(VLinePill, { label: leftLabel, expanded: false, side: "left", onClick: () => toggleLeft(true) })),
|
|
3420
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: { flex: 1, overflow: "hidden", minWidth: 0 }, children: renderFlowchart && activeSpec && renderFlowchart({
|
|
3421
|
+
spec: activeSpec,
|
|
3422
|
+
snapshots: activeSnapshots,
|
|
3423
|
+
selectedIndex: safeIdx,
|
|
3424
|
+
onNodeClick: handleNodeClick
|
|
3425
|
+
}) }),
|
|
3426
|
+
rightExpanded ? /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { style: { width: "38%", minWidth: 300, maxWidth: 500, display: "flex", flexDirection: "row", overflow: "hidden" }, children: [
|
|
3427
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(VLinePill, { label: rightLabel, expanded: true, onClick: () => toggleRight(false) }),
|
|
3428
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
3429
|
+
DetailsContent,
|
|
3430
|
+
{
|
|
3431
|
+
snapshots: activeSnapshots,
|
|
3432
|
+
selectedIndex: safeIdx,
|
|
3433
|
+
narrativeEntries: activeNarrativeEntries,
|
|
3434
|
+
narrative: activeNarrative,
|
|
3435
|
+
size,
|
|
3436
|
+
fillHeight: true
|
|
3437
|
+
}
|
|
3438
|
+
)
|
|
3439
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(VLinePill, { label: rightLabel, expanded: false, onClick: () => toggleRight(true) })
|
|
3440
|
+
] }),
|
|
3441
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(HLinePill, { label: bottomLabel, detail: `${activeSnapshots.length} stages`, expanded: timelineExpanded, onClick: toggleTimeline }),
|
|
3442
|
+
timelineExpanded && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: { flexShrink: 0, overflow: "hidden" }, children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(GanttTimeline, { snapshots: activeSnapshots, selectedIndex: safeIdx, onSelect: handleSnapshotChange, size }) })
|
|
3443
|
+
] })
|
|
3444
|
+
)
|
|
3445
|
+
] })
|
|
3446
|
+
] })
|
|
3447
|
+
]
|
|
2707
3448
|
}
|
|
2708
|
-
|
|
2709
|
-
}
|
|
2710
|
-
if (node.next) {
|
|
2711
|
-
nextMs = flattenTree(node.next, out, sharedState, nextMs, subflowResults, memory, stageNarrativeMap);
|
|
2712
|
-
}
|
|
2713
|
-
return nextMs;
|
|
2714
|
-
}
|
|
2715
|
-
function createSnapshots(stages) {
|
|
2716
|
-
let accMs = 0;
|
|
2717
|
-
return stages.map((s) => {
|
|
2718
|
-
const duration = s.durationMs ?? 1;
|
|
2719
|
-
const snap = {
|
|
2720
|
-
stageName: s.name,
|
|
2721
|
-
stageLabel: s.label ?? s.name,
|
|
2722
|
-
memory: s.memory ?? {},
|
|
2723
|
-
narrative: s.narrative ?? `${s.label ?? s.name} completed.`,
|
|
2724
|
-
startMs: accMs,
|
|
2725
|
-
durationMs: duration,
|
|
2726
|
-
status: "done",
|
|
2727
|
-
...s.description ? { description: s.description } : void 0,
|
|
2728
|
-
...s.subflowId ? { subflowId: s.subflowId } : void 0
|
|
2729
|
-
};
|
|
2730
|
-
accMs += duration;
|
|
2731
|
-
return snap;
|
|
2732
|
-
});
|
|
3449
|
+
);
|
|
2733
3450
|
}
|
|
2734
3451
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2735
3452
|
0 && (module.exports = {
|
|
@@ -2737,18 +3454,23 @@ function createSnapshots(stages) {
|
|
|
2737
3454
|
FootprintTheme,
|
|
2738
3455
|
GanttTimeline,
|
|
2739
3456
|
MemoryInspector,
|
|
3457
|
+
MemoryPanel,
|
|
2740
3458
|
NarrativeLog,
|
|
3459
|
+
NarrativePanel,
|
|
2741
3460
|
NarrativeTrace,
|
|
2742
3461
|
ResultPanel,
|
|
2743
3462
|
ScopeDiff,
|
|
2744
3463
|
SnapshotPanel,
|
|
2745
3464
|
StageDetailPanel,
|
|
3465
|
+
StoryNarrative,
|
|
2746
3466
|
SubflowTree,
|
|
2747
3467
|
TimeTravelControls,
|
|
2748
3468
|
coolDark,
|
|
2749
3469
|
coolLight,
|
|
2750
3470
|
createSnapshots,
|
|
2751
3471
|
defaultTokens,
|
|
3472
|
+
rawDefaults,
|
|
3473
|
+
subflowResultToSnapshots,
|
|
2752
3474
|
themePresets,
|
|
2753
3475
|
toVisualizationSnapshots,
|
|
2754
3476
|
tokensToCSSVars,
|