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.js
CHANGED
|
@@ -23,7 +23,7 @@ function tokensToCSSVars(tokens) {
|
|
|
23
23
|
if (tokens.fontFamily?.mono) vars["--fp-font-mono"] = tokens.fontFamily.mono;
|
|
24
24
|
return vars;
|
|
25
25
|
}
|
|
26
|
-
var
|
|
26
|
+
var rawDefaults = {
|
|
27
27
|
colors: {
|
|
28
28
|
primary: "#6366f1",
|
|
29
29
|
success: "#22c55e",
|
|
@@ -43,6 +43,26 @@ var defaultTokens = {
|
|
|
43
43
|
mono: "'JetBrains Mono', 'Fira Code', monospace"
|
|
44
44
|
}
|
|
45
45
|
};
|
|
46
|
+
var defaultTokens = {
|
|
47
|
+
colors: {
|
|
48
|
+
primary: `var(--fp-color-primary, ${rawDefaults.colors.primary})`,
|
|
49
|
+
success: `var(--fp-color-success, ${rawDefaults.colors.success})`,
|
|
50
|
+
error: `var(--fp-color-error, ${rawDefaults.colors.error})`,
|
|
51
|
+
warning: `var(--fp-color-warning, ${rawDefaults.colors.warning})`,
|
|
52
|
+
bgPrimary: `var(--fp-bg-primary, ${rawDefaults.colors.bgPrimary})`,
|
|
53
|
+
bgSecondary: `var(--fp-bg-secondary, ${rawDefaults.colors.bgSecondary})`,
|
|
54
|
+
bgTertiary: `var(--fp-bg-tertiary, ${rawDefaults.colors.bgTertiary})`,
|
|
55
|
+
textPrimary: `var(--fp-text-primary, ${rawDefaults.colors.textPrimary})`,
|
|
56
|
+
textSecondary: `var(--fp-text-secondary, ${rawDefaults.colors.textSecondary})`,
|
|
57
|
+
textMuted: `var(--fp-text-muted, ${rawDefaults.colors.textMuted})`,
|
|
58
|
+
border: `var(--fp-border, ${rawDefaults.colors.border})`
|
|
59
|
+
},
|
|
60
|
+
radius: `var(--fp-radius, ${rawDefaults.radius})`,
|
|
61
|
+
fontFamily: {
|
|
62
|
+
sans: `var(--fp-font-sans, ${rawDefaults.fontFamily.sans})`,
|
|
63
|
+
mono: `var(--fp-font-mono, ${rawDefaults.fontFamily.mono})`
|
|
64
|
+
}
|
|
65
|
+
};
|
|
46
66
|
|
|
47
67
|
// src/theme/ThemeProvider.tsx
|
|
48
68
|
import { jsx } from "react/jsx-runtime";
|
|
@@ -197,7 +217,7 @@ function useDarkModeTokens(options) {
|
|
|
197
217
|
}
|
|
198
218
|
|
|
199
219
|
// src/components/MemoryInspector/MemoryInspector.tsx
|
|
200
|
-
import { useMemo } from "react";
|
|
220
|
+
import { useMemo, useRef } from "react";
|
|
201
221
|
import { jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
202
222
|
function MemoryInspector({
|
|
203
223
|
data,
|
|
@@ -210,6 +230,7 @@ function MemoryInspector({
|
|
|
210
230
|
className,
|
|
211
231
|
style
|
|
212
232
|
}) {
|
|
233
|
+
const cacheRef = useRef(null);
|
|
213
234
|
const { memory, newKeys } = useMemo(() => {
|
|
214
235
|
if (data) {
|
|
215
236
|
return { memory: data, newKeys: /* @__PURE__ */ new Set() };
|
|
@@ -217,21 +238,37 @@ function MemoryInspector({
|
|
|
217
238
|
if (!snapshots || snapshots.length === 0) {
|
|
218
239
|
return { memory: {}, newKeys: /* @__PURE__ */ new Set() };
|
|
219
240
|
}
|
|
220
|
-
const
|
|
221
|
-
|
|
222
|
-
|
|
241
|
+
const safeIdx = Math.min(selectedIndex, snapshots.length - 1);
|
|
242
|
+
let merged;
|
|
243
|
+
const cache = cacheRef.current;
|
|
244
|
+
if (cache && cache.snapshots === snapshots && cache.index <= safeIdx) {
|
|
245
|
+
merged = { ...cache.accumulated };
|
|
246
|
+
for (let i = cache.index + 1; i <= safeIdx; i++) {
|
|
247
|
+
Object.assign(merged, snapshots[i]?.memory);
|
|
248
|
+
}
|
|
249
|
+
} else {
|
|
250
|
+
merged = {};
|
|
251
|
+
for (let i = 0; i <= safeIdx; i++) {
|
|
252
|
+
Object.assign(merged, snapshots[i]?.memory);
|
|
253
|
+
}
|
|
223
254
|
}
|
|
255
|
+
cacheRef.current = { snapshots, index: safeIdx, accumulated: merged };
|
|
224
256
|
const nk = /* @__PURE__ */ new Set();
|
|
225
|
-
if (highlightNew &&
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
257
|
+
if (highlightNew && safeIdx > 0) {
|
|
258
|
+
let prev;
|
|
259
|
+
if (cache && cache.snapshots === snapshots && cache.index === safeIdx - 1) {
|
|
260
|
+
prev = cache.accumulated;
|
|
261
|
+
} else {
|
|
262
|
+
prev = {};
|
|
263
|
+
for (let i = 0; i < safeIdx; i++) {
|
|
264
|
+
Object.assign(prev, snapshots[i]?.memory);
|
|
265
|
+
}
|
|
229
266
|
}
|
|
230
|
-
const current = snapshots[
|
|
267
|
+
const current = snapshots[safeIdx]?.memory ?? {};
|
|
231
268
|
for (const k of Object.keys(current)) {
|
|
232
269
|
if (!(k in prev)) nk.add(k);
|
|
233
270
|
}
|
|
234
|
-
} else if (highlightNew &&
|
|
271
|
+
} else if (highlightNew && safeIdx === 0 && snapshots[0]) {
|
|
235
272
|
for (const k of Object.keys(snapshots[0].memory)) nk.add(k);
|
|
236
273
|
}
|
|
237
274
|
return { memory: merged, newKeys: nk };
|
|
@@ -240,9 +277,9 @@ function MemoryInspector({
|
|
|
240
277
|
const fs = fontSize[size];
|
|
241
278
|
const pad = padding[size];
|
|
242
279
|
if (unstyled) {
|
|
243
|
-
return /* @__PURE__ */ jsxs("div", { className, style, "data-fp": "memory-inspector", children: [
|
|
280
|
+
return /* @__PURE__ */ jsxs("div", { className, style, "data-fp": "memory-inspector", role: "region", "aria-label": "Memory state", children: [
|
|
244
281
|
/* @__PURE__ */ jsx2("div", { "data-fp": "memory-label", children: "Memory State" }),
|
|
245
|
-
/* @__PURE__ */ jsx2("pre", { "data-fp": "memory-json", children: JSON.stringify(memory, null, 2) })
|
|
282
|
+
/* @__PURE__ */ jsx2("pre", { "data-fp": "memory-json", children: /* @__PURE__ */ jsx2("code", { children: JSON.stringify(memory, null, 2) }) })
|
|
246
283
|
] });
|
|
247
284
|
}
|
|
248
285
|
return /* @__PURE__ */ jsxs(
|
|
@@ -255,6 +292,8 @@ function MemoryInspector({
|
|
|
255
292
|
...style
|
|
256
293
|
},
|
|
257
294
|
"data-fp": "memory-inspector",
|
|
295
|
+
role: "region",
|
|
296
|
+
"aria-label": "Memory state",
|
|
258
297
|
children: [
|
|
259
298
|
/* @__PURE__ */ jsx2(
|
|
260
299
|
"span",
|
|
@@ -487,7 +526,7 @@ function NarrativeLog({
|
|
|
487
526
|
}
|
|
488
527
|
|
|
489
528
|
// src/components/NarrativeTrace/NarrativeTrace.tsx
|
|
490
|
-
import { useState as useState2, useCallback, useMemo as useMemo3, useEffect as useEffect2, useRef } from "react";
|
|
529
|
+
import { useState as useState2, useCallback, useMemo as useMemo3, useEffect as useEffect2, useRef as useRef2 } from "react";
|
|
491
530
|
import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
492
531
|
function parseGroups(lines) {
|
|
493
532
|
const groups = [];
|
|
@@ -523,7 +562,7 @@ function NarrativeTrace({
|
|
|
523
562
|
if (!defaultCollapsed) return /* @__PURE__ */ new Set();
|
|
524
563
|
return new Set(parseGroups(narrative).map((g) => g.headerIdx));
|
|
525
564
|
});
|
|
526
|
-
const latestRef =
|
|
565
|
+
const latestRef = useRef2(null);
|
|
527
566
|
useEffect2(() => {
|
|
528
567
|
latestRef.current?.scrollIntoView({ behavior: "smooth", block: "nearest" });
|
|
529
568
|
}, [revealedGroups.length]);
|
|
@@ -547,19 +586,32 @@ function NarrativeTrace({
|
|
|
547
586
|
"data-fp": "narrative-header",
|
|
548
587
|
"data-collapsible": group.steps.length > 0,
|
|
549
588
|
"data-collapsed": collapsedSet.has(group.headerIdx),
|
|
589
|
+
role: group.steps.length > 0 ? "button" : void 0,
|
|
590
|
+
tabIndex: group.steps.length > 0 ? 0 : void 0,
|
|
591
|
+
"aria-expanded": group.steps.length > 0 ? !collapsedSet.has(group.headerIdx) : void 0,
|
|
592
|
+
"aria-label": `Stage ${gi + 1}, ${group.steps.length} steps${gi === lastIdx ? ", current" : ""}`,
|
|
550
593
|
onClick: () => {
|
|
551
594
|
if (group.steps.length > 0) toggle(group.headerIdx);
|
|
552
595
|
onStageClick?.(group.headerIdx);
|
|
553
596
|
},
|
|
597
|
+
onKeyDown: (e) => {
|
|
598
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
599
|
+
e.preventDefault();
|
|
600
|
+
if (group.steps.length > 0) toggle(group.headerIdx);
|
|
601
|
+
onStageClick?.(group.headerIdx);
|
|
602
|
+
}
|
|
603
|
+
},
|
|
554
604
|
children: group.header
|
|
555
605
|
}
|
|
556
606
|
),
|
|
557
607
|
!collapsedSet.has(group.headerIdx) && group.steps.map((step) => /* @__PURE__ */ jsx4("div", { "data-fp": "narrative-step", children: step.text }, step.idx))
|
|
558
608
|
] }, group.headerIdx)),
|
|
559
|
-
futureGroups.
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
609
|
+
futureGroups.length > 0 && /* @__PURE__ */ jsxs3("div", { "data-fp": "narrative-future-hint", children: [
|
|
610
|
+
futureGroups.length,
|
|
611
|
+
" more ",
|
|
612
|
+
futureGroups.length === 1 ? "stage" : "stages",
|
|
613
|
+
" ahead..."
|
|
614
|
+
] })
|
|
563
615
|
] });
|
|
564
616
|
}
|
|
565
617
|
return /* @__PURE__ */ jsxs3(
|
|
@@ -589,10 +641,21 @@ function NarrativeTrace({
|
|
|
589
641
|
/* @__PURE__ */ jsxs3(
|
|
590
642
|
"div",
|
|
591
643
|
{
|
|
644
|
+
role: hasSteps ? "button" : void 0,
|
|
645
|
+
tabIndex: hasSteps ? 0 : void 0,
|
|
646
|
+
"aria-expanded": hasSteps ? !isCollapsed : void 0,
|
|
647
|
+
"aria-label": `Stage ${gi + 1}, ${group.steps.length} steps${isLatest ? ", current" : ", completed"}`,
|
|
592
648
|
onClick: () => {
|
|
593
649
|
if (hasSteps) toggle(group.headerIdx);
|
|
594
650
|
onStageClick?.(group.headerIdx);
|
|
595
651
|
},
|
|
652
|
+
onKeyDown: (e) => {
|
|
653
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
654
|
+
e.preventDefault();
|
|
655
|
+
if (hasSteps) toggle(group.headerIdx);
|
|
656
|
+
onStageClick?.(group.headerIdx);
|
|
657
|
+
}
|
|
658
|
+
},
|
|
596
659
|
style: {
|
|
597
660
|
fontSize: fs.body,
|
|
598
661
|
lineHeight: 1.7,
|
|
@@ -651,43 +714,25 @@ function NarrativeTrace({
|
|
|
651
714
|
group.headerIdx
|
|
652
715
|
);
|
|
653
716
|
}),
|
|
654
|
-
futureGroups.length > 0 && /* @__PURE__ */
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
},
|
|
667
|
-
children: group.header
|
|
668
|
-
}
|
|
669
|
-
),
|
|
670
|
-
group.steps.map((step) => /* @__PURE__ */ jsx4(
|
|
671
|
-
"div",
|
|
672
|
-
{
|
|
673
|
-
style: {
|
|
674
|
-
fontSize: fs.small,
|
|
675
|
-
lineHeight: 1.6,
|
|
676
|
-
color: theme.textMuted,
|
|
677
|
-
padding: `2px ${pad - 4}px 2px ${pad + 20}px`
|
|
678
|
-
},
|
|
679
|
-
children: step.text
|
|
680
|
-
},
|
|
681
|
-
`f-${step.idx}`
|
|
682
|
-
))
|
|
683
|
-
] }, `f-${group.headerIdx}`)) })
|
|
717
|
+
futureGroups.length > 0 && /* @__PURE__ */ jsxs3("div", { style: {
|
|
718
|
+
opacity: 0.3,
|
|
719
|
+
fontSize: fs.small,
|
|
720
|
+
color: theme.textMuted,
|
|
721
|
+
padding: `8px ${pad}px`,
|
|
722
|
+
fontStyle: "italic"
|
|
723
|
+
}, children: [
|
|
724
|
+
futureGroups.length,
|
|
725
|
+
" more ",
|
|
726
|
+
futureGroups.length === 1 ? "stage" : "stages",
|
|
727
|
+
" ahead..."
|
|
728
|
+
] })
|
|
684
729
|
]
|
|
685
730
|
}
|
|
686
731
|
);
|
|
687
732
|
}
|
|
688
733
|
|
|
689
734
|
// src/components/GanttTimeline/GanttTimeline.tsx
|
|
690
|
-
import { useState as useState3, useMemo as useMemo4, useRef as
|
|
735
|
+
import { useState as useState3, useMemo as useMemo4, useRef as useRef3, useEffect as useEffect3 } from "react";
|
|
691
736
|
import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
692
737
|
function GanttTimeline({
|
|
693
738
|
snapshots,
|
|
@@ -700,8 +745,8 @@ function GanttTimeline({
|
|
|
700
745
|
maxVisibleRows = 5
|
|
701
746
|
}) {
|
|
702
747
|
const [expanded, setExpanded] = useState3(false);
|
|
703
|
-
const activeRowRef =
|
|
704
|
-
const scrollContainerRef =
|
|
748
|
+
const activeRowRef = useRef3(null);
|
|
749
|
+
const scrollContainerRef = useRef3(null);
|
|
705
750
|
const totalWallTime = useMemo4(
|
|
706
751
|
() => Math.max(...snapshots.map((s) => s.startMs + s.durationMs), 1),
|
|
707
752
|
[snapshots]
|
|
@@ -722,12 +767,15 @@ function GanttTimeline({
|
|
|
722
767
|
}
|
|
723
768
|
}, [selectedIndex, showAll]);
|
|
724
769
|
if (unstyled) {
|
|
725
|
-
return /* @__PURE__ */ jsx5("div", { className, style, "data-fp": "gantt-timeline", children: snapshots.map((snap, idx) => /* @__PURE__ */ jsxs4(
|
|
770
|
+
return /* @__PURE__ */ jsx5("div", { className, style, "data-fp": "gantt-timeline", role: "listbox", "aria-label": "Execution timeline", children: snapshots.map((snap, idx) => /* @__PURE__ */ jsxs4(
|
|
726
771
|
"div",
|
|
727
772
|
{
|
|
728
773
|
"data-fp": "gantt-bar",
|
|
729
774
|
"data-selected": idx === selectedIndex,
|
|
730
775
|
"data-visible": idx <= selectedIndex,
|
|
776
|
+
role: "option",
|
|
777
|
+
"aria-selected": idx === selectedIndex,
|
|
778
|
+
"aria-label": `${snap.stageLabel}, ${snap.durationMs}ms`,
|
|
731
779
|
onClick: () => onSelect?.(idx),
|
|
732
780
|
children: [
|
|
733
781
|
/* @__PURE__ */ jsx5("span", { "data-fp": "gantt-label", children: snap.stageLabel }),
|
|
@@ -737,7 +785,7 @@ function GanttTimeline({
|
|
|
737
785
|
] })
|
|
738
786
|
]
|
|
739
787
|
},
|
|
740
|
-
snap.stageName
|
|
788
|
+
`${snap.stageName}-${idx}`
|
|
741
789
|
)) });
|
|
742
790
|
}
|
|
743
791
|
return /* @__PURE__ */ jsxs4(
|
|
@@ -793,6 +841,8 @@ function GanttTimeline({
|
|
|
793
841
|
"div",
|
|
794
842
|
{
|
|
795
843
|
ref: scrollContainerRef,
|
|
844
|
+
role: "listbox",
|
|
845
|
+
"aria-label": "Execution timeline",
|
|
796
846
|
style: {
|
|
797
847
|
marginTop: 8,
|
|
798
848
|
display: "flex",
|
|
@@ -813,6 +863,9 @@ function GanttTimeline({
|
|
|
813
863
|
"div",
|
|
814
864
|
{
|
|
815
865
|
ref: isSelected ? activeRowRef : void 0,
|
|
866
|
+
role: "option",
|
|
867
|
+
"aria-selected": isSelected,
|
|
868
|
+
"aria-label": `${snap.stageLabel}, ${snap.durationMs}ms`,
|
|
816
869
|
onClick: () => onSelect?.(idx),
|
|
817
870
|
style: {
|
|
818
871
|
display: "flex",
|
|
@@ -828,6 +881,7 @@ function GanttTimeline({
|
|
|
828
881
|
/* @__PURE__ */ jsx5(
|
|
829
882
|
"span",
|
|
830
883
|
{
|
|
884
|
+
title: snap.stageLabel,
|
|
831
885
|
style: {
|
|
832
886
|
width: labelWidth,
|
|
833
887
|
fontSize: fs.small,
|
|
@@ -887,7 +941,7 @@ function GanttTimeline({
|
|
|
887
941
|
)
|
|
888
942
|
]
|
|
889
943
|
},
|
|
890
|
-
snap.stageName
|
|
944
|
+
`${snap.stageName}-${idx}`
|
|
891
945
|
);
|
|
892
946
|
})
|
|
893
947
|
}
|
|
@@ -1209,9 +1263,9 @@ function fmt(v2) {
|
|
|
1209
1263
|
return String(v2);
|
|
1210
1264
|
}
|
|
1211
1265
|
var diffColors = {
|
|
1212
|
-
added: { bg:
|
|
1213
|
-
removed: { bg:
|
|
1214
|
-
changed: { bg:
|
|
1266
|
+
added: { bg: `color-mix(in srgb, ${theme.success} 10%, transparent)`, fg: theme.success, icon: "+" },
|
|
1267
|
+
removed: { bg: `color-mix(in srgb, ${theme.error} 10%, transparent)`, fg: theme.error, icon: "-" },
|
|
1268
|
+
changed: { bg: `color-mix(in srgb, ${theme.warning} 10%, transparent)`, fg: theme.warning, icon: "~" },
|
|
1215
1269
|
unchanged: { bg: "transparent", fg: "", icon: " " }
|
|
1216
1270
|
};
|
|
1217
1271
|
function ScopeDiff({
|
|
@@ -1905,7 +1959,7 @@ function StageDetailPanel({
|
|
|
1905
1959
|
}
|
|
1906
1960
|
|
|
1907
1961
|
// src/components/TimeTravelControls/TimeTravelControls.tsx
|
|
1908
|
-
import { useState as useState6, useEffect as useEffect4, useRef as
|
|
1962
|
+
import { useState as useState6, useEffect as useEffect4, useRef as useRef4, useCallback as useCallback3 } from "react";
|
|
1909
1963
|
import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1910
1964
|
function TimeTravelControls({
|
|
1911
1965
|
snapshots,
|
|
@@ -1918,7 +1972,7 @@ function TimeTravelControls({
|
|
|
1918
1972
|
style
|
|
1919
1973
|
}) {
|
|
1920
1974
|
const [playing, setPlaying] = useState6(false);
|
|
1921
|
-
const playRef =
|
|
1975
|
+
const playRef = useRef4(null);
|
|
1922
1976
|
const total = snapshots.length;
|
|
1923
1977
|
const canPrev = selectedIndex > 0;
|
|
1924
1978
|
const canNext = selectedIndex < total - 1;
|
|
@@ -1948,49 +2002,80 @@ function TimeTravelControls({
|
|
|
1948
2002
|
setPlaying(true);
|
|
1949
2003
|
}
|
|
1950
2004
|
}, [playing, selectedIndex, total, onIndexChange]);
|
|
2005
|
+
const handleKeyDown = useCallback3(
|
|
2006
|
+
(e) => {
|
|
2007
|
+
if (e.key === "ArrowLeft" && canPrev && !playing) {
|
|
2008
|
+
e.preventDefault();
|
|
2009
|
+
setPlaying(false);
|
|
2010
|
+
onIndexChange(selectedIndex - 1);
|
|
2011
|
+
} else if (e.key === "ArrowRight" && canNext && !playing) {
|
|
2012
|
+
e.preventDefault();
|
|
2013
|
+
setPlaying(false);
|
|
2014
|
+
onIndexChange(selectedIndex + 1);
|
|
2015
|
+
} else if (e.key === " " && autoPlayable) {
|
|
2016
|
+
e.preventDefault();
|
|
2017
|
+
togglePlay();
|
|
2018
|
+
}
|
|
2019
|
+
},
|
|
2020
|
+
[canPrev, canNext, playing, selectedIndex, onIndexChange, autoPlayable, togglePlay]
|
|
2021
|
+
);
|
|
1951
2022
|
const fs = fontSize[size];
|
|
1952
2023
|
if (unstyled) {
|
|
1953
|
-
return /* @__PURE__ */ jsxs9(
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
2024
|
+
return /* @__PURE__ */ jsxs9(
|
|
2025
|
+
"div",
|
|
2026
|
+
{
|
|
2027
|
+
className,
|
|
2028
|
+
style,
|
|
2029
|
+
"data-fp": "time-travel-controls",
|
|
2030
|
+
role: "toolbar",
|
|
2031
|
+
"aria-label": "Time travel controls",
|
|
2032
|
+
tabIndex: 0,
|
|
2033
|
+
onKeyDown: handleKeyDown,
|
|
2034
|
+
children: [
|
|
2035
|
+
/* @__PURE__ */ jsx10(
|
|
2036
|
+
"button",
|
|
2037
|
+
{
|
|
2038
|
+
"data-fp": "tt-prev",
|
|
2039
|
+
disabled: !canPrev || playing,
|
|
2040
|
+
onClick: () => {
|
|
2041
|
+
setPlaying(false);
|
|
2042
|
+
onIndexChange(selectedIndex - 1);
|
|
2043
|
+
},
|
|
2044
|
+
"aria-label": "Previous stage",
|
|
2045
|
+
children: "Prev"
|
|
2046
|
+
}
|
|
2047
|
+
),
|
|
2048
|
+
autoPlayable && /* @__PURE__ */ jsx10("button", { "data-fp": "tt-play", onClick: togglePlay, "aria-label": playing ? "Pause" : "Play", children: playing ? "Pause" : "Play" }),
|
|
2049
|
+
/* @__PURE__ */ jsx10(
|
|
2050
|
+
"button",
|
|
2051
|
+
{
|
|
2052
|
+
"data-fp": "tt-next",
|
|
2053
|
+
disabled: !canNext || playing,
|
|
2054
|
+
onClick: () => {
|
|
2055
|
+
setPlaying(false);
|
|
2056
|
+
onIndexChange(selectedIndex + 1);
|
|
2057
|
+
},
|
|
2058
|
+
"aria-label": "Next stage",
|
|
2059
|
+
children: "Next"
|
|
2060
|
+
}
|
|
2061
|
+
),
|
|
2062
|
+
/* @__PURE__ */ jsx10("div", { "data-fp": "tt-ticks", children: snapshots.map((snap, i) => /* @__PURE__ */ jsx10(
|
|
2063
|
+
"button",
|
|
2064
|
+
{
|
|
2065
|
+
"data-fp": "tt-tick",
|
|
2066
|
+
"data-active": i === selectedIndex,
|
|
2067
|
+
"data-done": i < selectedIndex,
|
|
2068
|
+
onClick: () => {
|
|
2069
|
+
setPlaying(false);
|
|
2070
|
+
onIndexChange(i);
|
|
2071
|
+
},
|
|
2072
|
+
title: snap.stageLabel
|
|
2073
|
+
},
|
|
2074
|
+
i
|
|
2075
|
+
)) })
|
|
2076
|
+
]
|
|
2077
|
+
}
|
|
2078
|
+
);
|
|
1994
2079
|
}
|
|
1995
2080
|
const btnStyle = (disabled) => ({
|
|
1996
2081
|
background: theme.bgTertiary,
|
|
@@ -2019,6 +2104,10 @@ function TimeTravelControls({
|
|
|
2019
2104
|
...style
|
|
2020
2105
|
},
|
|
2021
2106
|
"data-fp": "time-travel-controls",
|
|
2107
|
+
role: "toolbar",
|
|
2108
|
+
"aria-label": "Time travel controls",
|
|
2109
|
+
tabIndex: 0,
|
|
2110
|
+
onKeyDown: handleKeyDown,
|
|
2022
2111
|
children: [
|
|
2023
2112
|
/* @__PURE__ */ jsx10(
|
|
2024
2113
|
"button",
|
|
@@ -2029,6 +2118,7 @@ function TimeTravelControls({
|
|
|
2029
2118
|
setPlaying(false);
|
|
2030
2119
|
onIndexChange(selectedIndex - 1);
|
|
2031
2120
|
},
|
|
2121
|
+
"aria-label": "Previous stage",
|
|
2032
2122
|
children: "\u25C0"
|
|
2033
2123
|
}
|
|
2034
2124
|
),
|
|
@@ -2051,6 +2141,7 @@ function TimeTravelControls({
|
|
|
2051
2141
|
flexShrink: 0
|
|
2052
2142
|
},
|
|
2053
2143
|
title: playing ? "Pause" : "Play",
|
|
2144
|
+
"aria-label": playing ? "Pause" : "Play",
|
|
2054
2145
|
children: playing ? "\u23F8" : "\u25B6"
|
|
2055
2146
|
}
|
|
2056
2147
|
),
|
|
@@ -2063,6 +2154,7 @@ function TimeTravelControls({
|
|
|
2063
2154
|
setPlaying(false);
|
|
2064
2155
|
onIndexChange(selectedIndex + 1);
|
|
2065
2156
|
},
|
|
2157
|
+
"aria-label": "Next stage",
|
|
2066
2158
|
children: "\u25B6"
|
|
2067
2159
|
}
|
|
2068
2160
|
),
|
|
@@ -2109,125 +2201,146 @@ function TimeTravelControls({
|
|
|
2109
2201
|
}
|
|
2110
2202
|
|
|
2111
2203
|
// src/components/ExplainableShell/ExplainableShell.tsx
|
|
2112
|
-
import { useState as
|
|
2113
|
-
|
|
2114
|
-
|
|
2204
|
+
import { memo as memo3, useState as useState8, useCallback as useCallback5, useMemo as useMemo10, useRef as useRef6, useEffect as useEffect6 } from "react";
|
|
2205
|
+
|
|
2206
|
+
// src/adapters/fromRuntimeSnapshot.ts
|
|
2207
|
+
function toVisualizationSnapshots(runtime, narrativeEntries) {
|
|
2208
|
+
const stageNarrativeMap = narrativeEntries?.length ? buildStageNarrativeMap(narrativeEntries) : /* @__PURE__ */ new Map();
|
|
2209
|
+
const snapshots = [];
|
|
2210
|
+
flattenTree(runtime.executionTree, snapshots, runtime.sharedState, 0, runtime.subflowResults, {}, stageNarrativeMap);
|
|
2211
|
+
return snapshots;
|
|
2212
|
+
}
|
|
2213
|
+
function buildStageNarrativeMap(entries) {
|
|
2214
|
+
const map = /* @__PURE__ */ new Map();
|
|
2215
|
+
let currentStageName;
|
|
2216
|
+
for (const entry of entries) {
|
|
2217
|
+
if (entry.stageName) {
|
|
2218
|
+
currentStageName = entry.stageName;
|
|
2219
|
+
}
|
|
2220
|
+
if (currentStageName) {
|
|
2221
|
+
if (!map.has(currentStageName)) {
|
|
2222
|
+
map.set(currentStageName, []);
|
|
2223
|
+
}
|
|
2224
|
+
const indent = " ".repeat(entry.depth);
|
|
2225
|
+
map.get(currentStageName).push(`${indent}${entry.text}`);
|
|
2226
|
+
}
|
|
2227
|
+
}
|
|
2228
|
+
return map;
|
|
2229
|
+
}
|
|
2230
|
+
function flattenTree(node, out, sharedState, accumulatedMs = 0, subflowResults, cumulativeMemory = {}, stageNarrativeMap = /* @__PURE__ */ new Map()) {
|
|
2231
|
+
const durationMs = typeof node.metrics?.durationMs === "number" ? node.metrics.durationMs : 1;
|
|
2232
|
+
const startMs = accumulatedMs;
|
|
2233
|
+
const stageId = node.id || node.name || "unknown";
|
|
2234
|
+
const displayName = node.name || node.id || "unknown";
|
|
2235
|
+
const stageLines = stageNarrativeMap.get(stageId);
|
|
2236
|
+
let narrative;
|
|
2237
|
+
if (stageLines) {
|
|
2238
|
+
narrative = stageLines.join("\n");
|
|
2239
|
+
} else {
|
|
2240
|
+
const parts = [`${displayName} executed.`];
|
|
2241
|
+
if (node.description) parts.push(node.description);
|
|
2242
|
+
if (node.stageWrites) {
|
|
2243
|
+
const keys = Object.keys(node.stageWrites);
|
|
2244
|
+
if (keys.length > 0) parts.push(`Wrote: ${keys.join(", ")}`);
|
|
2245
|
+
}
|
|
2246
|
+
narrative = parts.join("\n");
|
|
2247
|
+
}
|
|
2248
|
+
const memory = { ...cumulativeMemory };
|
|
2249
|
+
if (node.stageWrites) {
|
|
2250
|
+
for (const [key, value] of Object.entries(node.stageWrites)) {
|
|
2251
|
+
if (value === void 0) {
|
|
2252
|
+
delete memory[key];
|
|
2253
|
+
} else {
|
|
2254
|
+
memory[key] = value;
|
|
2255
|
+
}
|
|
2256
|
+
}
|
|
2257
|
+
}
|
|
2258
|
+
const sfResult = subflowResults?.[node.subflowId ?? stageId];
|
|
2259
|
+
out.push({
|
|
2260
|
+
stageName: displayName,
|
|
2261
|
+
stageLabel: stageId,
|
|
2262
|
+
memory,
|
|
2263
|
+
narrative,
|
|
2264
|
+
startMs,
|
|
2265
|
+
durationMs,
|
|
2266
|
+
status: "done",
|
|
2267
|
+
...node.description ? { description: node.description } : void 0,
|
|
2268
|
+
...node.subflowId ? { subflowId: node.subflowId } : void 0,
|
|
2269
|
+
...sfResult ? { subflowResult: sfResult } : void 0
|
|
2270
|
+
});
|
|
2271
|
+
let nextMs = startMs + durationMs;
|
|
2272
|
+
if (node.children && node.children.length > 0) {
|
|
2273
|
+
let maxChildEnd = nextMs;
|
|
2274
|
+
for (const child of node.children) {
|
|
2275
|
+
const childEnd = flattenTree(child, out, sharedState, nextMs, subflowResults, memory, stageNarrativeMap);
|
|
2276
|
+
maxChildEnd = Math.max(maxChildEnd, childEnd);
|
|
2277
|
+
}
|
|
2278
|
+
nextMs = maxChildEnd;
|
|
2279
|
+
}
|
|
2280
|
+
if (node.next) {
|
|
2281
|
+
nextMs = flattenTree(node.next, out, sharedState, nextMs, subflowResults, memory, stageNarrativeMap);
|
|
2282
|
+
}
|
|
2283
|
+
return nextMs;
|
|
2284
|
+
}
|
|
2285
|
+
function subflowResultToSnapshots(subflowResult, narrativeEntries) {
|
|
2286
|
+
if (!subflowResult || typeof subflowResult !== "object") return [];
|
|
2287
|
+
const sf = subflowResult;
|
|
2288
|
+
if (!sf.treeContext?.stageContexts) return [];
|
|
2289
|
+
const runtime = {
|
|
2290
|
+
sharedState: sf.treeContext.globalContext ?? {},
|
|
2291
|
+
executionTree: sf.treeContext.stageContexts,
|
|
2292
|
+
commitLog: sf.treeContext.history ?? []
|
|
2293
|
+
};
|
|
2294
|
+
const snapshots = toVisualizationSnapshots(runtime, narrativeEntries);
|
|
2295
|
+
const prefix = sf.subflowId ? `${sf.subflowId}/` : "";
|
|
2296
|
+
if (prefix) {
|
|
2297
|
+
for (const snap of snapshots) {
|
|
2298
|
+
if (snap.stageName.startsWith(prefix)) {
|
|
2299
|
+
snap.stageName = snap.stageName.slice(prefix.length);
|
|
2300
|
+
}
|
|
2301
|
+
if (snap.stageLabel.startsWith(prefix)) {
|
|
2302
|
+
snap.stageLabel = snap.stageLabel.slice(prefix.length);
|
|
2303
|
+
}
|
|
2304
|
+
}
|
|
2305
|
+
}
|
|
2306
|
+
return snapshots;
|
|
2307
|
+
}
|
|
2308
|
+
function createSnapshots(stages) {
|
|
2309
|
+
let accMs = 0;
|
|
2310
|
+
return stages.map((s) => {
|
|
2311
|
+
const duration = s.durationMs ?? 1;
|
|
2312
|
+
const snap = {
|
|
2313
|
+
stageName: s.name,
|
|
2314
|
+
stageLabel: s.label ?? s.name,
|
|
2315
|
+
memory: s.memory ?? {},
|
|
2316
|
+
narrative: s.narrative ?? `${s.label ?? s.name} completed.`,
|
|
2317
|
+
startMs: accMs,
|
|
2318
|
+
durationMs: duration,
|
|
2319
|
+
status: "done",
|
|
2320
|
+
...s.description ? { description: s.description } : void 0,
|
|
2321
|
+
...s.subflowId ? { subflowId: s.subflowId } : void 0
|
|
2322
|
+
};
|
|
2323
|
+
accMs += duration;
|
|
2324
|
+
return snap;
|
|
2325
|
+
});
|
|
2326
|
+
}
|
|
2327
|
+
|
|
2328
|
+
// src/components/MemoryPanel/MemoryPanel.tsx
|
|
2329
|
+
import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
2330
|
+
function MemoryPanel({
|
|
2115
2331
|
snapshots,
|
|
2116
|
-
|
|
2117
|
-
logs = [],
|
|
2118
|
-
narrative = [],
|
|
2119
|
-
tabs = ["result", "explainable", "ai-compatible"],
|
|
2120
|
-
defaultTab,
|
|
2121
|
-
hideConsole = false,
|
|
2122
|
-
renderFlowchart,
|
|
2332
|
+
selectedIndex,
|
|
2123
2333
|
size = "default",
|
|
2124
2334
|
unstyled = false,
|
|
2125
2335
|
className,
|
|
2126
2336
|
style
|
|
2127
2337
|
}) {
|
|
2128
|
-
const [
|
|
2129
|
-
const [
|
|
2130
|
-
const fs = fontSize[size];
|
|
2131
|
-
const pad = padding[size];
|
|
2132
|
-
const handleSnapshotChange = useCallback4((idx) => {
|
|
2133
|
-
setSnapshotIdx(Math.max(0, Math.min(idx, snapshots.length - 1)));
|
|
2134
|
-
}, [snapshots.length]);
|
|
2135
|
-
const revealedCount = useMemo7(() => {
|
|
2136
|
-
if (snapshots.length === 0 || narrative.length === 0) return narrative.length;
|
|
2137
|
-
const boundaries = [];
|
|
2138
|
-
for (let i = 0; i < narrative.length; i++) {
|
|
2139
|
-
const trimmed = narrative[i].trimStart();
|
|
2140
|
-
if (trimmed.startsWith("Stage ") && !trimmed.match(/^Stage\s+\d+:\s*Step\s/) || trimmed.startsWith("[")) {
|
|
2141
|
-
boundaries.push(i);
|
|
2142
|
-
}
|
|
2143
|
-
}
|
|
2144
|
-
if (boundaries.length === 0) {
|
|
2145
|
-
const ratio = (snapshotIdx + 1) / snapshots.length;
|
|
2146
|
-
return Math.max(1, Math.ceil(narrative.length * ratio));
|
|
2147
|
-
}
|
|
2148
|
-
const groupsToShow = Math.max(
|
|
2149
|
-
1,
|
|
2150
|
-
Math.min(
|
|
2151
|
-
Math.floor((snapshotIdx + 1) / snapshots.length * boundaries.length) || 1,
|
|
2152
|
-
boundaries.length
|
|
2153
|
-
)
|
|
2154
|
-
);
|
|
2155
|
-
const endIdx = groupsToShow < boundaries.length ? boundaries[groupsToShow] : narrative.length;
|
|
2156
|
-
return Math.max(1, endIdx);
|
|
2157
|
-
}, [snapshots.length, snapshotIdx, narrative]);
|
|
2158
|
-
const prevMemory = snapshotIdx > 0 ? snapshots[snapshotIdx - 1]?.memory : null;
|
|
2159
|
-
const currMemory = snapshots[snapshotIdx]?.memory ?? {};
|
|
2160
|
-
const tabLabels = {
|
|
2161
|
-
result: "Result",
|
|
2162
|
-
explainable: "Explainable",
|
|
2163
|
-
"ai-compatible": "AI-Compatible"
|
|
2164
|
-
};
|
|
2338
|
+
const prevMemory = selectedIndex > 0 ? snapshots[selectedIndex - 1]?.memory ?? null : null;
|
|
2339
|
+
const currMemory = snapshots[selectedIndex]?.memory ?? {};
|
|
2165
2340
|
if (unstyled) {
|
|
2166
|
-
return /* @__PURE__ */ jsxs10("div", { className, style, "data-fp": "
|
|
2167
|
-
/* @__PURE__ */ jsx11(
|
|
2168
|
-
|
|
2169
|
-
{
|
|
2170
|
-
"data-fp": "shell-tab",
|
|
2171
|
-
"data-active": tab === activeTab,
|
|
2172
|
-
onClick: () => setActiveTab(tab),
|
|
2173
|
-
children: tabLabels[tab]
|
|
2174
|
-
},
|
|
2175
|
-
tab
|
|
2176
|
-
)) }),
|
|
2177
|
-
/* @__PURE__ */ jsxs10("div", { "data-fp": "shell-content", "data-tab": activeTab, children: [
|
|
2178
|
-
activeTab === "result" && /* @__PURE__ */ jsx11(
|
|
2179
|
-
ResultPanel,
|
|
2180
|
-
{
|
|
2181
|
-
data: resultData ?? null,
|
|
2182
|
-
logs,
|
|
2183
|
-
hideConsole,
|
|
2184
|
-
unstyled: true
|
|
2185
|
-
}
|
|
2186
|
-
),
|
|
2187
|
-
activeTab === "explainable" && /* @__PURE__ */ jsxs10(Fragment3, { children: [
|
|
2188
|
-
/* @__PURE__ */ jsx11(
|
|
2189
|
-
TimeTravelControls,
|
|
2190
|
-
{
|
|
2191
|
-
snapshots,
|
|
2192
|
-
selectedIndex: snapshotIdx,
|
|
2193
|
-
onIndexChange: handleSnapshotChange,
|
|
2194
|
-
unstyled: true
|
|
2195
|
-
}
|
|
2196
|
-
),
|
|
2197
|
-
renderFlowchart?.({ snapshots, selectedIndex: snapshotIdx, onNodeClick: handleSnapshotChange }),
|
|
2198
|
-
/* @__PURE__ */ jsx11(MemoryInspector, { snapshots, selectedIndex: snapshotIdx, unstyled: true }),
|
|
2199
|
-
/* @__PURE__ */ jsx11(ScopeDiff, { previous: prevMemory, current: currMemory, unstyled: true }),
|
|
2200
|
-
/* @__PURE__ */ jsx11(
|
|
2201
|
-
GanttTimeline,
|
|
2202
|
-
{
|
|
2203
|
-
snapshots,
|
|
2204
|
-
selectedIndex: snapshotIdx,
|
|
2205
|
-
onSelect: handleSnapshotChange,
|
|
2206
|
-
unstyled: true
|
|
2207
|
-
}
|
|
2208
|
-
)
|
|
2209
|
-
] }),
|
|
2210
|
-
activeTab === "ai-compatible" && /* @__PURE__ */ jsxs10(Fragment3, { children: [
|
|
2211
|
-
/* @__PURE__ */ jsx11(
|
|
2212
|
-
TimeTravelControls,
|
|
2213
|
-
{
|
|
2214
|
-
snapshots,
|
|
2215
|
-
selectedIndex: snapshotIdx,
|
|
2216
|
-
onIndexChange: handleSnapshotChange,
|
|
2217
|
-
unstyled: true
|
|
2218
|
-
}
|
|
2219
|
-
),
|
|
2220
|
-
renderFlowchart?.({ snapshots, selectedIndex: snapshotIdx, onNodeClick: handleSnapshotChange }),
|
|
2221
|
-
/* @__PURE__ */ jsx11(
|
|
2222
|
-
NarrativeTrace,
|
|
2223
|
-
{
|
|
2224
|
-
narrative,
|
|
2225
|
-
revealedCount,
|
|
2226
|
-
unstyled: true
|
|
2227
|
-
}
|
|
2228
|
-
)
|
|
2229
|
-
] })
|
|
2230
|
-
] })
|
|
2341
|
+
return /* @__PURE__ */ jsxs10("div", { className, style, "data-fp": "memory-panel", children: [
|
|
2342
|
+
/* @__PURE__ */ jsx11(MemoryInspector, { snapshots, selectedIndex, unstyled: true }),
|
|
2343
|
+
/* @__PURE__ */ jsx11(ScopeDiff, { previous: prevMemory, current: currMemory, unstyled: true })
|
|
2231
2344
|
] });
|
|
2232
2345
|
}
|
|
2233
2346
|
return /* @__PURE__ */ jsxs10(
|
|
@@ -2235,156 +2348,251 @@ function ExplainableShell({
|
|
|
2235
2348
|
{
|
|
2236
2349
|
className,
|
|
2237
2350
|
style: {
|
|
2238
|
-
|
|
2351
|
+
overflow: "auto",
|
|
2239
2352
|
display: "flex",
|
|
2240
2353
|
flexDirection: "column",
|
|
2241
|
-
overflow: "hidden",
|
|
2242
|
-
background: theme.bgPrimary,
|
|
2243
|
-
color: theme.textPrimary,
|
|
2244
|
-
fontFamily: theme.fontSans,
|
|
2245
2354
|
...style
|
|
2246
2355
|
},
|
|
2247
|
-
"data-fp": "
|
|
2356
|
+
"data-fp": "memory-panel",
|
|
2248
2357
|
children: [
|
|
2249
|
-
/* @__PURE__ */ jsx11(
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
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
|
-
activeTab === "ai-compatible" && /* @__PURE__ */ jsxs10(Fragment3, { children: [
|
|
2350
|
-
/* @__PURE__ */ jsx11(
|
|
2351
|
-
TimeTravelControls,
|
|
2352
|
-
{
|
|
2353
|
-
snapshots,
|
|
2354
|
-
selectedIndex: snapshotIdx,
|
|
2355
|
-
onIndexChange: handleSnapshotChange,
|
|
2356
|
-
size
|
|
2357
|
-
}
|
|
2358
|
-
),
|
|
2359
|
-
/* @__PURE__ */ jsxs10("div", { style: { flex: 1, display: "flex", overflow: "hidden" }, children: [
|
|
2360
|
-
renderFlowchart && /* @__PURE__ */ jsx11("div", { style: { flex: 1, overflow: "hidden", borderRight: `1px solid ${theme.border}` }, children: renderFlowchart({ snapshots, selectedIndex: snapshotIdx, onNodeClick: handleSnapshotChange }) }),
|
|
2361
|
-
/* @__PURE__ */ jsx11(
|
|
2362
|
-
NarrativeTrace,
|
|
2363
|
-
{
|
|
2364
|
-
narrative,
|
|
2365
|
-
revealedCount,
|
|
2366
|
-
size,
|
|
2367
|
-
style: {
|
|
2368
|
-
width: renderFlowchart ? "40%" : "100%",
|
|
2369
|
-
minWidth: 280
|
|
2358
|
+
/* @__PURE__ */ jsx11(MemoryInspector, { snapshots, selectedIndex, size }),
|
|
2359
|
+
/* @__PURE__ */ jsx11("div", { style: { borderTop: `1px solid ${theme.border}` }, children: /* @__PURE__ */ jsx11(ScopeDiff, { previous: prevMemory, current: currMemory, hideUnchanged: true, size }) })
|
|
2360
|
+
]
|
|
2361
|
+
}
|
|
2362
|
+
);
|
|
2363
|
+
}
|
|
2364
|
+
|
|
2365
|
+
// src/components/NarrativePanel/NarrativePanel.tsx
|
|
2366
|
+
import { useMemo as useMemo8 } from "react";
|
|
2367
|
+
|
|
2368
|
+
// src/components/StoryNarrative/StoryNarrative.tsx
|
|
2369
|
+
import { useMemo as useMemo7, useRef as useRef5, useEffect as useEffect5 } from "react";
|
|
2370
|
+
import { jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
2371
|
+
var ENTRY_ICONS = {
|
|
2372
|
+
stage: { icon: "\u25B8", color: theme.primary, label: "Stage" },
|
|
2373
|
+
step: { icon: "\xB7", color: theme.textMuted, label: "Data operation" },
|
|
2374
|
+
condition: { icon: "\u25C7", color: theme.warning, label: "Decision" },
|
|
2375
|
+
fork: { icon: "\u2443", color: theme.primary, label: "Parallel" },
|
|
2376
|
+
subflow: { icon: "\u21B3", color: theme.textSecondary, label: "Subflow" },
|
|
2377
|
+
loop: { icon: "\u21BB", color: theme.warning, label: "Loop" },
|
|
2378
|
+
break: { icon: "\u25A0", color: theme.error, label: "Break" },
|
|
2379
|
+
error: { icon: "\u2717", color: theme.error, label: "Error" }
|
|
2380
|
+
};
|
|
2381
|
+
function StoryNarrative({
|
|
2382
|
+
entries,
|
|
2383
|
+
stageCount,
|
|
2384
|
+
size = "default",
|
|
2385
|
+
unstyled = false,
|
|
2386
|
+
className,
|
|
2387
|
+
style: outerStyle
|
|
2388
|
+
}) {
|
|
2389
|
+
const fs = fontSize[size];
|
|
2390
|
+
const pad = padding[size];
|
|
2391
|
+
const revealedCount = useMemo7(() => {
|
|
2392
|
+
let stagesSeen = 0;
|
|
2393
|
+
for (let i = 0; i < entries.length; i++) {
|
|
2394
|
+
const e = entries[i];
|
|
2395
|
+
const isBoundary = e.type === "stage" || e.type === "subflow" && e.text.startsWith("Entering");
|
|
2396
|
+
if (isBoundary) stagesSeen++;
|
|
2397
|
+
if (stagesSeen > stageCount) return i;
|
|
2398
|
+
}
|
|
2399
|
+
return entries.length;
|
|
2400
|
+
}, [entries, stageCount]);
|
|
2401
|
+
const revealed = entries.slice(0, revealedCount);
|
|
2402
|
+
const future = entries.slice(revealedCount);
|
|
2403
|
+
const latestRef = useRef5(null);
|
|
2404
|
+
useEffect5(() => {
|
|
2405
|
+
latestRef.current?.scrollIntoView({ behavior: "smooth", block: "nearest" });
|
|
2406
|
+
}, [revealed.length]);
|
|
2407
|
+
if (unstyled) {
|
|
2408
|
+
return /* @__PURE__ */ jsx12("div", { className, style: outerStyle, "data-fp": "story-narrative", role: "log", children: revealed.map((entry, i) => /* @__PURE__ */ jsx12("div", { "data-fp": "narrative-entry", "data-type": entry.type, children: entry.text }, i)) });
|
|
2409
|
+
}
|
|
2410
|
+
return /* @__PURE__ */ jsxs11(
|
|
2411
|
+
"div",
|
|
2412
|
+
{
|
|
2413
|
+
className,
|
|
2414
|
+
style: {
|
|
2415
|
+
flex: 1,
|
|
2416
|
+
overflow: "auto",
|
|
2417
|
+
padding: pad,
|
|
2418
|
+
fontFamily: theme.fontSans,
|
|
2419
|
+
...outerStyle
|
|
2420
|
+
},
|
|
2421
|
+
"data-fp": "story-narrative",
|
|
2422
|
+
role: "log",
|
|
2423
|
+
"aria-label": "Execution narrative",
|
|
2424
|
+
children: [
|
|
2425
|
+
revealed.map((entry, i) => {
|
|
2426
|
+
const meta = ENTRY_ICONS[entry.type] ?? ENTRY_ICONS.step;
|
|
2427
|
+
const isStage = entry.type === "stage";
|
|
2428
|
+
const isDecision = entry.type === "condition";
|
|
2429
|
+
const isError = entry.type === "error";
|
|
2430
|
+
const isLast = i === revealed.length - 1;
|
|
2431
|
+
return /* @__PURE__ */ jsxs11(
|
|
2432
|
+
"div",
|
|
2433
|
+
{
|
|
2434
|
+
ref: isLast ? latestRef : void 0,
|
|
2435
|
+
style: {
|
|
2436
|
+
display: "flex",
|
|
2437
|
+
gap: 8,
|
|
2438
|
+
padding: isStage ? `${pad - 4}px 0` : `2px 0`,
|
|
2439
|
+
marginLeft: entry.depth * 16,
|
|
2440
|
+
borderBottom: isStage ? `1px solid ${theme.border}` : void 0,
|
|
2441
|
+
marginTop: isStage && i > 0 ? 8 : 0
|
|
2442
|
+
},
|
|
2443
|
+
children: [
|
|
2444
|
+
/* @__PURE__ */ jsx12(
|
|
2445
|
+
"span",
|
|
2446
|
+
{
|
|
2447
|
+
style: {
|
|
2448
|
+
color: meta.color,
|
|
2449
|
+
fontWeight: 700,
|
|
2450
|
+
fontSize: isStage ? fs.body : fs.small,
|
|
2451
|
+
width: 16,
|
|
2452
|
+
textAlign: "center",
|
|
2453
|
+
flexShrink: 0
|
|
2454
|
+
},
|
|
2455
|
+
title: meta.label,
|
|
2456
|
+
"aria-label": meta.label,
|
|
2457
|
+
children: meta.icon
|
|
2370
2458
|
}
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2459
|
+
),
|
|
2460
|
+
/* @__PURE__ */ jsx12(
|
|
2461
|
+
"span",
|
|
2462
|
+
{
|
|
2463
|
+
style: {
|
|
2464
|
+
fontSize: isStage ? fs.body : fs.small,
|
|
2465
|
+
fontWeight: isStage ? 600 : 400,
|
|
2466
|
+
color: isError ? theme.error : isDecision ? theme.warning : isStage ? theme.textPrimary : theme.textSecondary,
|
|
2467
|
+
lineHeight: 1.6,
|
|
2468
|
+
fontFamily: entry.type === "step" ? theme.fontMono : theme.fontSans
|
|
2469
|
+
},
|
|
2470
|
+
children: entry.text
|
|
2471
|
+
}
|
|
2472
|
+
)
|
|
2473
|
+
]
|
|
2474
|
+
},
|
|
2475
|
+
i
|
|
2476
|
+
);
|
|
2477
|
+
}),
|
|
2478
|
+
future.length > 0 && /* @__PURE__ */ jsxs11("div", { style: {
|
|
2479
|
+
opacity: 0.3,
|
|
2480
|
+
fontSize: fs.small,
|
|
2481
|
+
color: theme.textMuted,
|
|
2482
|
+
padding: `8px 0`,
|
|
2483
|
+
fontStyle: "italic"
|
|
2484
|
+
}, children: [
|
|
2485
|
+
future.length,
|
|
2486
|
+
" more ",
|
|
2487
|
+
future.length === 1 ? "entry" : "entries",
|
|
2488
|
+
" ahead..."
|
|
2375
2489
|
] })
|
|
2376
2490
|
]
|
|
2377
2491
|
}
|
|
2378
2492
|
);
|
|
2379
2493
|
}
|
|
2380
2494
|
|
|
2495
|
+
// src/components/NarrativePanel/NarrativePanel.tsx
|
|
2496
|
+
import { jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
2497
|
+
function NarrativePanel({
|
|
2498
|
+
snapshots,
|
|
2499
|
+
selectedIndex,
|
|
2500
|
+
narrativeEntries,
|
|
2501
|
+
narrative: narrativeProp,
|
|
2502
|
+
size = "default",
|
|
2503
|
+
unstyled = false,
|
|
2504
|
+
className,
|
|
2505
|
+
style
|
|
2506
|
+
}) {
|
|
2507
|
+
const fs = fontSize[size];
|
|
2508
|
+
const pad = padding[size];
|
|
2509
|
+
const narrative = useMemo8(() => {
|
|
2510
|
+
if (narrativeProp && narrativeProp.length > 0) return narrativeProp;
|
|
2511
|
+
const lines = [];
|
|
2512
|
+
for (const snap of snapshots) {
|
|
2513
|
+
const stageLines = (snap.narrative ?? "").split("\n").filter(Boolean);
|
|
2514
|
+
lines.push(...stageLines);
|
|
2515
|
+
}
|
|
2516
|
+
return lines;
|
|
2517
|
+
}, [narrativeProp, snapshots]);
|
|
2518
|
+
const revealedCount = useMemo8(() => {
|
|
2519
|
+
if (snapshots.length === 0 || narrative.length === 0) return narrative.length;
|
|
2520
|
+
const stageBoundaries = [];
|
|
2521
|
+
for (let i = 0; i < narrative.length; i++) {
|
|
2522
|
+
const trimmed = narrative[i].trimStart();
|
|
2523
|
+
if (trimmed.startsWith("Stage ") && !trimmed.match(/^Stage\s+\d+:\s*Step\s/)) {
|
|
2524
|
+
stageBoundaries.push(i);
|
|
2525
|
+
}
|
|
2526
|
+
}
|
|
2527
|
+
if (stageBoundaries.length === 0) {
|
|
2528
|
+
const ratio = (selectedIndex + 1) / snapshots.length;
|
|
2529
|
+
return Math.max(1, Math.ceil(narrative.length * ratio));
|
|
2530
|
+
}
|
|
2531
|
+
const groupsToShow = Math.min(selectedIndex + 1, stageBoundaries.length);
|
|
2532
|
+
const endIdx = groupsToShow < stageBoundaries.length ? stageBoundaries[groupsToShow] : narrative.length;
|
|
2533
|
+
return Math.max(1, endIdx);
|
|
2534
|
+
}, [snapshots.length, selectedIndex, narrative]);
|
|
2535
|
+
const hasStructured = narrativeEntries && narrativeEntries.length > 0;
|
|
2536
|
+
if (unstyled) {
|
|
2537
|
+
return /* @__PURE__ */ jsx13("div", { className, style, "data-fp": "narrative-panel", children: hasStructured ? /* @__PURE__ */ jsx13(StoryNarrative, { entries: narrativeEntries, stageCount: selectedIndex + 1, unstyled: true }) : /* @__PURE__ */ jsx13(NarrativeTrace, { narrative, revealedCount, unstyled: true }) });
|
|
2538
|
+
}
|
|
2539
|
+
return /* @__PURE__ */ jsxs12(
|
|
2540
|
+
"div",
|
|
2541
|
+
{
|
|
2542
|
+
className,
|
|
2543
|
+
style: {
|
|
2544
|
+
overflow: "auto",
|
|
2545
|
+
display: "flex",
|
|
2546
|
+
flexDirection: "column",
|
|
2547
|
+
...style
|
|
2548
|
+
},
|
|
2549
|
+
"data-fp": "narrative-panel",
|
|
2550
|
+
children: [
|
|
2551
|
+
/* @__PURE__ */ jsx13(
|
|
2552
|
+
"div",
|
|
2553
|
+
{
|
|
2554
|
+
style: {
|
|
2555
|
+
padding: `${pad - 4}px ${pad}px`,
|
|
2556
|
+
fontSize: fs.small,
|
|
2557
|
+
color: theme.textMuted,
|
|
2558
|
+
fontStyle: "italic",
|
|
2559
|
+
borderBottom: `1px solid ${theme.border}`,
|
|
2560
|
+
flexShrink: 0
|
|
2561
|
+
},
|
|
2562
|
+
children: "What happened at each stage, what data flowed, what decisions were made, and why."
|
|
2563
|
+
}
|
|
2564
|
+
),
|
|
2565
|
+
hasStructured ? /* @__PURE__ */ jsx13(
|
|
2566
|
+
StoryNarrative,
|
|
2567
|
+
{
|
|
2568
|
+
entries: narrativeEntries,
|
|
2569
|
+
stageCount: selectedIndex + 1,
|
|
2570
|
+
size,
|
|
2571
|
+
style: { flex: 1 }
|
|
2572
|
+
}
|
|
2573
|
+
) : /* @__PURE__ */ jsx13(
|
|
2574
|
+
NarrativeTrace,
|
|
2575
|
+
{
|
|
2576
|
+
narrative,
|
|
2577
|
+
revealedCount,
|
|
2578
|
+
size,
|
|
2579
|
+
style: { flex: 1 }
|
|
2580
|
+
}
|
|
2581
|
+
)
|
|
2582
|
+
]
|
|
2583
|
+
}
|
|
2584
|
+
);
|
|
2585
|
+
}
|
|
2586
|
+
|
|
2381
2587
|
// src/components/FlowchartView/SubflowTree.tsx
|
|
2382
|
-
import { memo, useState as
|
|
2383
|
-
import { Fragment as
|
|
2588
|
+
import { memo, useState as useState7, useCallback as useCallback4, useMemo as useMemo9 } from "react";
|
|
2589
|
+
import { Fragment as Fragment3, jsx as jsx14, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
2384
2590
|
function specToTree(node) {
|
|
2591
|
+
if (!node) return [];
|
|
2385
2592
|
const entries = [];
|
|
2386
2593
|
const seen = /* @__PURE__ */ new Set();
|
|
2387
2594
|
function walk(n) {
|
|
2595
|
+
if (!n) return;
|
|
2388
2596
|
const id = n.name || n.id || "";
|
|
2389
2597
|
if (seen.has(id)) return;
|
|
2390
2598
|
seen.add(id);
|
|
@@ -2400,7 +2608,7 @@ function specToTree(node) {
|
|
|
2400
2608
|
entries.push(entry);
|
|
2401
2609
|
if (n.children) {
|
|
2402
2610
|
for (const child of n.children) {
|
|
2403
|
-
walk(child);
|
|
2611
|
+
if (child) walk(child);
|
|
2404
2612
|
}
|
|
2405
2613
|
}
|
|
2406
2614
|
if (n.next) {
|
|
@@ -2417,18 +2625,18 @@ var TreeNode = memo(function TreeNode2({
|
|
|
2417
2625
|
doneStages,
|
|
2418
2626
|
onNodeSelect
|
|
2419
2627
|
}) {
|
|
2420
|
-
const [expanded, setExpanded] =
|
|
2628
|
+
const [expanded, setExpanded] = useState7(true);
|
|
2421
2629
|
const hasChildren = entry.children && entry.children.length > 0;
|
|
2422
2630
|
const isActive = activeStage === entry.name;
|
|
2423
2631
|
const isDone = doneStages?.has(entry.name);
|
|
2424
|
-
const handleClick =
|
|
2632
|
+
const handleClick = useCallback4(() => {
|
|
2425
2633
|
if (hasChildren) {
|
|
2426
2634
|
setExpanded((prev) => !prev);
|
|
2427
2635
|
}
|
|
2428
2636
|
onNodeSelect?.(entry.name, !!entry.isSubflow);
|
|
2429
2637
|
}, [hasChildren, onNodeSelect, entry.name, entry.isSubflow]);
|
|
2430
|
-
return /* @__PURE__ */
|
|
2431
|
-
/* @__PURE__ */
|
|
2638
|
+
return /* @__PURE__ */ jsxs13(Fragment3, { children: [
|
|
2639
|
+
/* @__PURE__ */ jsxs13(
|
|
2432
2640
|
"button",
|
|
2433
2641
|
{
|
|
2434
2642
|
onClick: handleClick,
|
|
@@ -2459,7 +2667,7 @@ var TreeNode = memo(function TreeNode2({
|
|
|
2459
2667
|
}
|
|
2460
2668
|
},
|
|
2461
2669
|
children: [
|
|
2462
|
-
hasChildren ? /* @__PURE__ */
|
|
2670
|
+
hasChildren ? /* @__PURE__ */ jsx14(
|
|
2463
2671
|
"span",
|
|
2464
2672
|
{
|
|
2465
2673
|
style: {
|
|
@@ -2474,8 +2682,8 @@ var TreeNode = memo(function TreeNode2({
|
|
|
2474
2682
|
},
|
|
2475
2683
|
children: "\u25B6"
|
|
2476
2684
|
}
|
|
2477
|
-
) : /* @__PURE__ */
|
|
2478
|
-
/* @__PURE__ */
|
|
2685
|
+
) : /* @__PURE__ */ jsx14("span", { style: { width: 12, flexShrink: 0 } }),
|
|
2686
|
+
/* @__PURE__ */ jsx14(
|
|
2479
2687
|
"span",
|
|
2480
2688
|
{
|
|
2481
2689
|
style: {
|
|
@@ -2487,8 +2695,8 @@ var TreeNode = memo(function TreeNode2({
|
|
|
2487
2695
|
}
|
|
2488
2696
|
}
|
|
2489
2697
|
),
|
|
2490
|
-
/* @__PURE__ */
|
|
2491
|
-
/* @__PURE__ */
|
|
2698
|
+
/* @__PURE__ */ jsxs13("span", { style: { display: "flex", flexDirection: "column", minWidth: 0 }, children: [
|
|
2699
|
+
/* @__PURE__ */ jsxs13(
|
|
2492
2700
|
"span",
|
|
2493
2701
|
{
|
|
2494
2702
|
style: {
|
|
@@ -2500,11 +2708,11 @@ var TreeNode = memo(function TreeNode2({
|
|
|
2500
2708
|
},
|
|
2501
2709
|
children: [
|
|
2502
2710
|
entry.name,
|
|
2503
|
-
entry.isSubflow && /* @__PURE__ */
|
|
2711
|
+
entry.isSubflow && /* @__PURE__ */ jsx14("span", { style: { opacity: 0.5, marginLeft: 4, fontSize: 10 }, children: "\u229E" })
|
|
2504
2712
|
]
|
|
2505
2713
|
}
|
|
2506
2714
|
),
|
|
2507
|
-
entry.description && /* @__PURE__ */
|
|
2715
|
+
entry.description && /* @__PURE__ */ jsx14(
|
|
2508
2716
|
"span",
|
|
2509
2717
|
{
|
|
2510
2718
|
style: {
|
|
@@ -2521,7 +2729,7 @@ var TreeNode = memo(function TreeNode2({
|
|
|
2521
2729
|
]
|
|
2522
2730
|
}
|
|
2523
2731
|
),
|
|
2524
|
-
hasChildren && expanded && /* @__PURE__ */
|
|
2732
|
+
hasChildren && expanded && /* @__PURE__ */ jsx14("div", { children: entry.children.map((child, i) => /* @__PURE__ */ jsx14(
|
|
2525
2733
|
TreeNode2,
|
|
2526
2734
|
{
|
|
2527
2735
|
entry: child,
|
|
@@ -2530,12 +2738,12 @@ var TreeNode = memo(function TreeNode2({
|
|
|
2530
2738
|
doneStages,
|
|
2531
2739
|
onNodeSelect
|
|
2532
2740
|
},
|
|
2533
|
-
`${child.name}-${i}`
|
|
2741
|
+
child.subflowId ?? `${child.name}-${i}`
|
|
2534
2742
|
)) })
|
|
2535
2743
|
] });
|
|
2536
2744
|
});
|
|
2537
2745
|
var SectionLabel = memo(function SectionLabel2({ children }) {
|
|
2538
|
-
return /* @__PURE__ */
|
|
2746
|
+
return /* @__PURE__ */ jsx14(
|
|
2539
2747
|
"div",
|
|
2540
2748
|
{
|
|
2541
2749
|
style: {
|
|
@@ -2559,10 +2767,10 @@ var SubflowTree = memo(function SubflowTree2({
|
|
|
2559
2767
|
className,
|
|
2560
2768
|
style
|
|
2561
2769
|
}) {
|
|
2562
|
-
const tree =
|
|
2563
|
-
const subflowStages =
|
|
2770
|
+
const tree = useMemo9(() => specToTree(spec), [spec]);
|
|
2771
|
+
const subflowStages = useMemo9(() => tree.filter((e) => e.isSubflow), [tree]);
|
|
2564
2772
|
if (subflowStages.length === 0) return null;
|
|
2565
|
-
return /* @__PURE__ */
|
|
2773
|
+
return /* @__PURE__ */ jsxs13(
|
|
2566
2774
|
"div",
|
|
2567
2775
|
{
|
|
2568
2776
|
className,
|
|
@@ -2580,8 +2788,8 @@ var SubflowTree = memo(function SubflowTree2({
|
|
|
2580
2788
|
...style
|
|
2581
2789
|
},
|
|
2582
2790
|
children: [
|
|
2583
|
-
!unstyled && /* @__PURE__ */
|
|
2584
|
-
subflowStages.map((entry, i) => /* @__PURE__ */
|
|
2791
|
+
!unstyled && /* @__PURE__ */ jsx14(SectionLabel, { children: "Subflows" }),
|
|
2792
|
+
subflowStages.map((entry, i) => /* @__PURE__ */ jsx14(
|
|
2585
2793
|
TreeNode,
|
|
2586
2794
|
{
|
|
2587
2795
|
entry,
|
|
@@ -2590,116 +2798,625 @@ var SubflowTree = memo(function SubflowTree2({
|
|
|
2590
2798
|
doneStages,
|
|
2591
2799
|
onNodeSelect
|
|
2592
2800
|
},
|
|
2593
|
-
`${entry.name}-${i}`
|
|
2801
|
+
entry.subflowId ?? `${entry.name}-${i}`
|
|
2594
2802
|
))
|
|
2595
2803
|
]
|
|
2596
2804
|
}
|
|
2597
2805
|
);
|
|
2598
2806
|
});
|
|
2599
2807
|
|
|
2600
|
-
// src/
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2808
|
+
// src/components/FlowchartView/SubflowBreadcrumb.tsx
|
|
2809
|
+
import { memo as memo2 } from "react";
|
|
2810
|
+
import { jsx as jsx15, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
2811
|
+
var SubflowBreadcrumb = memo2(function SubflowBreadcrumb2({
|
|
2812
|
+
breadcrumbs,
|
|
2813
|
+
onNavigate
|
|
2814
|
+
}) {
|
|
2815
|
+
if (breadcrumbs.length <= 1) return null;
|
|
2816
|
+
return /* @__PURE__ */ jsx15(
|
|
2817
|
+
"div",
|
|
2818
|
+
{
|
|
2819
|
+
style: {
|
|
2820
|
+
display: "flex",
|
|
2821
|
+
alignItems: "center",
|
|
2822
|
+
gap: 4,
|
|
2823
|
+
padding: "6px 12px",
|
|
2824
|
+
background: theme.bgSecondary,
|
|
2825
|
+
borderBottom: `1px solid ${theme.border}`,
|
|
2826
|
+
fontSize: 12,
|
|
2827
|
+
fontFamily: theme.fontSans,
|
|
2828
|
+
flexShrink: 0,
|
|
2829
|
+
overflowX: "auto"
|
|
2830
|
+
},
|
|
2831
|
+
children: breadcrumbs.map((crumb, i) => {
|
|
2832
|
+
const isLast = i === breadcrumbs.length - 1;
|
|
2833
|
+
return /* @__PURE__ */ jsxs14("span", { style: { display: "flex", alignItems: "center", gap: 4 }, children: [
|
|
2834
|
+
i > 0 && /* @__PURE__ */ jsx15("span", { style: { color: theme.textMuted, fontSize: 10 }, children: "\u203A" }),
|
|
2835
|
+
isLast ? /* @__PURE__ */ jsxs14("span", { style: { display: "flex", alignItems: "center", gap: 6 }, children: [
|
|
2836
|
+
/* @__PURE__ */ jsx15(
|
|
2837
|
+
"span",
|
|
2838
|
+
{
|
|
2839
|
+
style: {
|
|
2840
|
+
color: theme.primary,
|
|
2841
|
+
fontWeight: 600
|
|
2842
|
+
},
|
|
2843
|
+
children: crumb.label
|
|
2844
|
+
}
|
|
2845
|
+
),
|
|
2846
|
+
crumb.description && /* @__PURE__ */ jsxs14(
|
|
2847
|
+
"span",
|
|
2848
|
+
{
|
|
2849
|
+
style: {
|
|
2850
|
+
color: theme.textMuted,
|
|
2851
|
+
fontWeight: 400,
|
|
2852
|
+
fontSize: 11
|
|
2853
|
+
},
|
|
2854
|
+
children: [
|
|
2855
|
+
"\u2014 ",
|
|
2856
|
+
crumb.description
|
|
2857
|
+
]
|
|
2858
|
+
}
|
|
2859
|
+
)
|
|
2860
|
+
] }) : /* @__PURE__ */ jsx15(
|
|
2861
|
+
"button",
|
|
2862
|
+
{
|
|
2863
|
+
onClick: () => onNavigate(i),
|
|
2864
|
+
style: {
|
|
2865
|
+
background: "none",
|
|
2866
|
+
border: "none",
|
|
2867
|
+
color: theme.textSecondary,
|
|
2868
|
+
cursor: "pointer",
|
|
2869
|
+
padding: "2px 4px",
|
|
2870
|
+
borderRadius: 4,
|
|
2871
|
+
fontSize: 12,
|
|
2872
|
+
fontFamily: "inherit",
|
|
2873
|
+
fontWeight: 500,
|
|
2874
|
+
transition: "color 0.15s"
|
|
2875
|
+
},
|
|
2876
|
+
onMouseEnter: (e) => {
|
|
2877
|
+
e.currentTarget.style.color = `${theme.primary}`;
|
|
2878
|
+
},
|
|
2879
|
+
onMouseLeave: (e) => {
|
|
2880
|
+
e.currentTarget.style.color = `${theme.textSecondary}`;
|
|
2881
|
+
},
|
|
2882
|
+
children: crumb.label
|
|
2883
|
+
}
|
|
2884
|
+
)
|
|
2885
|
+
] }, `${crumb.label}-${i}`);
|
|
2886
|
+
})
|
|
2887
|
+
}
|
|
2888
|
+
);
|
|
2889
|
+
});
|
|
2890
|
+
|
|
2891
|
+
// src/components/ExplainableShell/ExplainableShell.tsx
|
|
2892
|
+
import { Fragment as Fragment4, jsx as jsx16, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
2893
|
+
var HLinePill = memo3(function HLinePill2({
|
|
2894
|
+
label,
|
|
2895
|
+
detail,
|
|
2896
|
+
expanded,
|
|
2897
|
+
onClick
|
|
2898
|
+
}) {
|
|
2899
|
+
return /* @__PURE__ */ jsxs15("div", { style: {
|
|
2900
|
+
display: "flex",
|
|
2901
|
+
alignItems: "center",
|
|
2902
|
+
gap: 0,
|
|
2903
|
+
padding: "0"
|
|
2904
|
+
}, children: [
|
|
2905
|
+
/* @__PURE__ */ jsx16("div", { style: { flex: 1, height: 1, background: theme.border } }),
|
|
2906
|
+
/* @__PURE__ */ jsxs15(
|
|
2907
|
+
"button",
|
|
2908
|
+
{
|
|
2909
|
+
onClick,
|
|
2910
|
+
style: {
|
|
2911
|
+
display: "flex",
|
|
2912
|
+
alignItems: "center",
|
|
2913
|
+
gap: 5,
|
|
2914
|
+
padding: "3px 12px",
|
|
2915
|
+
margin: "4px 0",
|
|
2916
|
+
fontSize: 10,
|
|
2917
|
+
fontWeight: 600,
|
|
2918
|
+
fontFamily: "inherit",
|
|
2919
|
+
color: theme.textMuted,
|
|
2920
|
+
background: theme.bgSecondary,
|
|
2921
|
+
border: `1px solid ${theme.border}`,
|
|
2922
|
+
borderRadius: 10,
|
|
2923
|
+
cursor: "pointer",
|
|
2924
|
+
whiteSpace: "nowrap",
|
|
2925
|
+
letterSpacing: "0.04em",
|
|
2926
|
+
textTransform: "uppercase",
|
|
2927
|
+
transition: "color 0.15s ease"
|
|
2928
|
+
},
|
|
2929
|
+
children: [
|
|
2930
|
+
/* @__PURE__ */ jsx16("span", { style: { fontSize: 7 }, children: expanded ? "\u25BC" : "\u25B6" }),
|
|
2931
|
+
label,
|
|
2932
|
+
detail && /* @__PURE__ */ jsx16("span", { style: { fontWeight: 400, opacity: 0.5, fontSize: 9 }, children: detail })
|
|
2933
|
+
]
|
|
2934
|
+
}
|
|
2935
|
+
),
|
|
2936
|
+
/* @__PURE__ */ jsx16("div", { style: { flex: 1, height: 1, background: theme.border } })
|
|
2937
|
+
] });
|
|
2938
|
+
});
|
|
2939
|
+
var VLinePill = memo3(function VLinePill2({
|
|
2940
|
+
label,
|
|
2941
|
+
expanded,
|
|
2942
|
+
side = "right",
|
|
2943
|
+
onClick
|
|
2944
|
+
}) {
|
|
2945
|
+
const arrow = side === "right" ? expanded ? "\u25B6" : "\u25C0" : expanded ? "\u25C0" : "\u25B6";
|
|
2946
|
+
return /* @__PURE__ */ jsxs15("div", { style: {
|
|
2947
|
+
display: "flex",
|
|
2948
|
+
flexDirection: "column",
|
|
2949
|
+
alignItems: "center",
|
|
2950
|
+
gap: 0,
|
|
2951
|
+
padding: "0"
|
|
2952
|
+
}, children: [
|
|
2953
|
+
/* @__PURE__ */ jsx16("div", { style: { flex: 1, width: 1, background: theme.border } }),
|
|
2954
|
+
/* @__PURE__ */ jsxs15(
|
|
2955
|
+
"button",
|
|
2956
|
+
{
|
|
2957
|
+
onClick,
|
|
2958
|
+
style: {
|
|
2959
|
+
display: "flex",
|
|
2960
|
+
alignItems: "center",
|
|
2961
|
+
gap: 4,
|
|
2962
|
+
padding: "10px 4px",
|
|
2963
|
+
margin: "0 3px",
|
|
2964
|
+
fontSize: 10,
|
|
2965
|
+
fontWeight: 600,
|
|
2966
|
+
fontFamily: "inherit",
|
|
2967
|
+
color: theme.textMuted,
|
|
2968
|
+
background: theme.bgSecondary,
|
|
2969
|
+
border: `1px solid ${theme.border}`,
|
|
2970
|
+
borderRadius: 10,
|
|
2971
|
+
cursor: "pointer",
|
|
2972
|
+
whiteSpace: "nowrap",
|
|
2973
|
+
letterSpacing: "0.04em",
|
|
2974
|
+
textTransform: "uppercase",
|
|
2975
|
+
writingMode: "vertical-lr",
|
|
2976
|
+
transition: "color 0.15s ease"
|
|
2977
|
+
},
|
|
2978
|
+
children: [
|
|
2979
|
+
/* @__PURE__ */ jsx16("span", { style: { fontSize: 7, writingMode: "horizontal-tb" }, children: arrow }),
|
|
2980
|
+
label
|
|
2981
|
+
]
|
|
2982
|
+
}
|
|
2983
|
+
),
|
|
2984
|
+
/* @__PURE__ */ jsx16("div", { style: { flex: 1, width: 1, background: theme.border } })
|
|
2985
|
+
] });
|
|
2986
|
+
});
|
|
2987
|
+
var RIGHT_PANEL_LABELS = {
|
|
2988
|
+
memory: "Memory",
|
|
2989
|
+
narrative: "Narrative"
|
|
2990
|
+
};
|
|
2991
|
+
var DetailsContent = memo3(function DetailsContent2({
|
|
2992
|
+
snapshots,
|
|
2993
|
+
selectedIndex,
|
|
2994
|
+
narrativeEntries,
|
|
2995
|
+
narrative,
|
|
2996
|
+
size,
|
|
2997
|
+
fillHeight
|
|
2998
|
+
}) {
|
|
2999
|
+
const [rightPanel, setRightPanel] = useState8("memory");
|
|
3000
|
+
return /* @__PURE__ */ jsxs15("div", { style: { flex: 1, display: "flex", flexDirection: "column", overflow: "hidden" }, children: [
|
|
3001
|
+
/* @__PURE__ */ jsx16("div", { style: { display: "flex", borderBottom: `1px solid ${theme.border}`, flexShrink: 0 }, children: ["memory", "narrative"].map((panel) => {
|
|
3002
|
+
const active = rightPanel === panel;
|
|
3003
|
+
return /* @__PURE__ */ jsx16(
|
|
3004
|
+
"button",
|
|
3005
|
+
{
|
|
3006
|
+
onClick: () => setRightPanel(panel),
|
|
3007
|
+
style: {
|
|
3008
|
+
flex: 1,
|
|
3009
|
+
padding: "6px 8px",
|
|
3010
|
+
fontSize: 11,
|
|
3011
|
+
fontWeight: active ? 600 : 400,
|
|
3012
|
+
color: active ? theme.primary : theme.textMuted,
|
|
3013
|
+
background: active ? `color-mix(in srgb, ${theme.primary} 8%, transparent)` : "transparent",
|
|
3014
|
+
border: "none",
|
|
3015
|
+
borderBottom: active ? `2px solid ${theme.primary}` : "2px solid transparent",
|
|
3016
|
+
cursor: "pointer",
|
|
3017
|
+
textTransform: "uppercase",
|
|
3018
|
+
letterSpacing: "0.06em",
|
|
3019
|
+
fontFamily: "inherit"
|
|
3020
|
+
},
|
|
3021
|
+
children: RIGHT_PANEL_LABELS[panel]
|
|
3022
|
+
},
|
|
3023
|
+
panel
|
|
3024
|
+
);
|
|
3025
|
+
}) }),
|
|
3026
|
+
/* @__PURE__ */ jsxs15("div", { style: { flex: 1, overflow: "auto" }, children: [
|
|
3027
|
+
rightPanel === "memory" && /* @__PURE__ */ jsx16(MemoryPanel, { snapshots, selectedIndex, size, style: fillHeight ? { height: "100%" } : void 0 }),
|
|
3028
|
+
rightPanel === "narrative" && /* @__PURE__ */ jsx16(NarrativePanel, { snapshots, selectedIndex, narrativeEntries, narrative, size, style: fillHeight ? { height: "100%" } : void 0 })
|
|
3029
|
+
] })
|
|
3030
|
+
] });
|
|
3031
|
+
});
|
|
3032
|
+
function resolveSubflowLevel(parentSpec, parentSnapshots, subflowNodeName, narrativeEntries) {
|
|
3033
|
+
const specNode = findSubflowSpecNode(parentSpec, subflowNodeName);
|
|
3034
|
+
if (!specNode?.subflowStructure) return null;
|
|
3035
|
+
const parentSnap = parentSnapshots.find(
|
|
3036
|
+
(s) => s.stageName === subflowNodeName || s.stageLabel === subflowNodeName
|
|
3037
|
+
);
|
|
3038
|
+
if (!parentSnap?.subflowResult) return null;
|
|
3039
|
+
const sfNarrative = narrativeEntries ? extractSubflowNarrative(narrativeEntries, subflowNodeName) : void 0;
|
|
3040
|
+
const sfSnapshots = subflowResultToSnapshots(parentSnap.subflowResult, sfNarrative);
|
|
3041
|
+
if (sfSnapshots.length === 0) return null;
|
|
3042
|
+
return {
|
|
3043
|
+
subflowId: specNode.subflowId ?? subflowNodeName,
|
|
3044
|
+
label: specNode.subflowName ?? specNode.name,
|
|
3045
|
+
spec: specNode.subflowStructure,
|
|
3046
|
+
snapshots: sfSnapshots
|
|
3047
|
+
};
|
|
2606
3048
|
}
|
|
2607
|
-
function
|
|
2608
|
-
const
|
|
2609
|
-
let
|
|
3049
|
+
function extractSubflowNarrative(entries, subflowName) {
|
|
3050
|
+
const result = [];
|
|
3051
|
+
let inside = false;
|
|
2610
3052
|
for (const entry of entries) {
|
|
2611
|
-
if (entry.
|
|
2612
|
-
|
|
3053
|
+
if (entry.type === "subflow" && entry.text.includes(subflowName) && entry.text.startsWith("Entering")) {
|
|
3054
|
+
inside = true;
|
|
3055
|
+
continue;
|
|
2613
3056
|
}
|
|
2614
|
-
if (
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
3057
|
+
if (inside && entry.type === "subflow" && entry.text.includes(subflowName) && entry.text.startsWith("Exiting")) break;
|
|
3058
|
+
if (inside) result.push(entry);
|
|
3059
|
+
}
|
|
3060
|
+
return result;
|
|
3061
|
+
}
|
|
3062
|
+
function findSubflowSpecNode(node, name) {
|
|
3063
|
+
if ((node.name === name || node.id === name) && node.isSubflowRoot) return node;
|
|
3064
|
+
if (node.children) {
|
|
3065
|
+
for (const child of node.children) {
|
|
3066
|
+
const f = findSubflowSpecNode(child, name);
|
|
3067
|
+
if (f) return f;
|
|
2620
3068
|
}
|
|
2621
3069
|
}
|
|
2622
|
-
return
|
|
3070
|
+
if (node.next) return findSubflowSpecNode(node.next, name);
|
|
3071
|
+
return null;
|
|
2623
3072
|
}
|
|
2624
|
-
function
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
3073
|
+
function hasSubflowNodes(node) {
|
|
3074
|
+
if (!node) return false;
|
|
3075
|
+
if (node.isSubflowRoot) return true;
|
|
3076
|
+
if (node.children?.some((c) => c && hasSubflowNodes(c))) return true;
|
|
3077
|
+
if (node.next && hasSubflowNodes(node.next)) return true;
|
|
3078
|
+
return false;
|
|
3079
|
+
}
|
|
3080
|
+
function ExplainableShell({
|
|
3081
|
+
snapshots,
|
|
3082
|
+
spec,
|
|
3083
|
+
title,
|
|
3084
|
+
resultData,
|
|
3085
|
+
logs = [],
|
|
3086
|
+
narrative,
|
|
3087
|
+
narrativeEntries,
|
|
3088
|
+
tabs = ["result", "explainable"],
|
|
3089
|
+
defaultTab,
|
|
3090
|
+
hideConsole = false,
|
|
3091
|
+
panelLabels,
|
|
3092
|
+
defaultExpanded,
|
|
3093
|
+
renderFlowchart,
|
|
3094
|
+
size = "default",
|
|
3095
|
+
unstyled = false,
|
|
3096
|
+
className,
|
|
3097
|
+
style
|
|
3098
|
+
}) {
|
|
3099
|
+
const leftLabel = panelLabels?.topology ?? "Topology";
|
|
3100
|
+
const rightLabel = panelLabels?.details ?? "Details";
|
|
3101
|
+
const bottomLabel = panelLabels?.timeline ?? "Timeline";
|
|
3102
|
+
const shellRef = useRef6(null);
|
|
3103
|
+
const [isNarrow, setIsNarrow] = useState8(false);
|
|
3104
|
+
useEffect6(() => {
|
|
3105
|
+
const el = shellRef.current;
|
|
3106
|
+
if (!el) return;
|
|
3107
|
+
const ro = new ResizeObserver(([entry]) => {
|
|
3108
|
+
setIsNarrow(entry.contentRect.width < 640);
|
|
3109
|
+
});
|
|
3110
|
+
ro.observe(el);
|
|
3111
|
+
return () => ro.disconnect();
|
|
3112
|
+
}, []);
|
|
3113
|
+
const [activeTab, setActiveTab] = useState8(defaultTab ?? tabs[0]);
|
|
3114
|
+
const [snapshotIdx, setSnapshotIdx] = useState8(0);
|
|
3115
|
+
const [drillDownStack, setDrillDownStack] = useState8([]);
|
|
3116
|
+
const [rightExpanded, setRightExpanded] = useState8(defaultExpanded?.details ?? true);
|
|
3117
|
+
const [leftExpanded, setLeftExpanded] = useState8(defaultExpanded?.topology ?? false);
|
|
3118
|
+
const [timelineExpanded, setTimelineExpanded] = useState8(defaultExpanded?.timeline ?? false);
|
|
3119
|
+
useEffect6(() => {
|
|
3120
|
+
if (isNarrow) {
|
|
3121
|
+
setLeftExpanded(false);
|
|
3122
|
+
setRightExpanded(false);
|
|
3123
|
+
setTimelineExpanded(false);
|
|
3124
|
+
}
|
|
3125
|
+
}, [isNarrow]);
|
|
3126
|
+
const triggerReflow = useCallback5(() => {
|
|
3127
|
+
requestAnimationFrame(() => window.dispatchEvent(new Event("resize")));
|
|
3128
|
+
}, []);
|
|
3129
|
+
const toggleLeft = useCallback5((v2) => {
|
|
3130
|
+
setLeftExpanded(v2);
|
|
3131
|
+
triggerReflow();
|
|
3132
|
+
}, [triggerReflow]);
|
|
3133
|
+
const toggleRight = useCallback5((v2) => {
|
|
3134
|
+
setRightExpanded(v2);
|
|
3135
|
+
triggerReflow();
|
|
3136
|
+
}, [triggerReflow]);
|
|
3137
|
+
const toggleTimeline = useCallback5(() => {
|
|
3138
|
+
setTimelineExpanded((p) => !p);
|
|
3139
|
+
triggerReflow();
|
|
3140
|
+
}, [triggerReflow]);
|
|
3141
|
+
const isInSubflow = drillDownStack.length > 0;
|
|
3142
|
+
const currentLevel = useMemo10(() => {
|
|
3143
|
+
if (drillDownStack.length > 0) {
|
|
3144
|
+
const top = drillDownStack[drillDownStack.length - 1];
|
|
3145
|
+
return { spec: top.spec, snapshots: top.snapshots };
|
|
3146
|
+
}
|
|
3147
|
+
return { spec: spec ?? null, snapshots };
|
|
3148
|
+
}, [drillDownStack, spec, snapshots]);
|
|
3149
|
+
const activeSnapshots = currentLevel.snapshots;
|
|
3150
|
+
const activeSpec = currentLevel.spec;
|
|
3151
|
+
const safeIdx = activeSnapshots.length > 0 ? Math.max(0, Math.min(snapshotIdx, activeSnapshots.length - 1)) : 0;
|
|
3152
|
+
const activeNarrative = useMemo10(() => {
|
|
3153
|
+
if (!isInSubflow) return narrative;
|
|
3154
|
+
const lines = [];
|
|
3155
|
+
for (const snap of activeSnapshots) {
|
|
3156
|
+
const stageLines = (snap.narrative ?? "").split("\n").filter(Boolean);
|
|
3157
|
+
lines.push(...stageLines);
|
|
3158
|
+
}
|
|
3159
|
+
return lines.length > 0 ? lines : void 0;
|
|
3160
|
+
}, [isInSubflow, narrative, activeSnapshots]);
|
|
3161
|
+
const activeNarrativeEntries = isInSubflow ? void 0 : narrativeEntries;
|
|
3162
|
+
const breadcrumbs = useMemo10(() => {
|
|
3163
|
+
const root = { label: title || "Flowchart", spec, description: spec?.description };
|
|
3164
|
+
return [root, ...drillDownStack.map((e) => ({ label: e.label, spec: e.spec, description: void 0 }))];
|
|
3165
|
+
}, [spec, title, drillDownStack]);
|
|
3166
|
+
const showTreeSidebar = useMemo10(() => !!spec && hasSubflowNodes(spec), [spec]);
|
|
3167
|
+
const rootOverlay = useMemo10(() => {
|
|
3168
|
+
if (isInSubflow || !snapshots.length) return { activeStage: void 0, doneStages: void 0 };
|
|
3169
|
+
const doneStages = new Set(snapshots.slice(0, safeIdx).map((s) => s.stageLabel));
|
|
3170
|
+
const activeStage = snapshots[safeIdx]?.stageLabel ?? null;
|
|
3171
|
+
return { activeStage, doneStages };
|
|
3172
|
+
}, [isInSubflow, snapshots, safeIdx]);
|
|
3173
|
+
const handleTabChange = useCallback5((tab) => {
|
|
3174
|
+
setActiveTab(tab);
|
|
3175
|
+
setDrillDownStack([]);
|
|
3176
|
+
setSnapshotIdx(999);
|
|
3177
|
+
}, []);
|
|
3178
|
+
const handleSnapshotChange = useCallback5((idx) => {
|
|
3179
|
+
if (typeof idx === "number") setSnapshotIdx(idx);
|
|
3180
|
+
}, []);
|
|
3181
|
+
const handleDrillDown = useCallback5(
|
|
3182
|
+
(nodeName) => {
|
|
3183
|
+
if (!activeSpec) return;
|
|
3184
|
+
const entry = resolveSubflowLevel(activeSpec, activeSnapshots, nodeName, narrativeEntries);
|
|
3185
|
+
if (entry) {
|
|
3186
|
+
setDrillDownStack((prev) => [...prev, { ...entry, parentSnapshotIdx: snapshotIdx }]);
|
|
3187
|
+
setSnapshotIdx(0);
|
|
3188
|
+
}
|
|
3189
|
+
},
|
|
3190
|
+
[activeSpec, activeSnapshots, narrativeEntries, snapshotIdx]
|
|
3191
|
+
);
|
|
3192
|
+
const handleBreadcrumbNavigate = useCallback5((level) => {
|
|
3193
|
+
setDrillDownStack((prev) => {
|
|
3194
|
+
const popped = level === 0 ? prev[0] : prev[level];
|
|
3195
|
+
if (popped) setSnapshotIdx(popped.parentSnapshotIdx);
|
|
3196
|
+
return level === 0 ? [] : prev.slice(0, level);
|
|
3197
|
+
});
|
|
3198
|
+
}, []);
|
|
3199
|
+
const handleNodeClick = useCallback5(
|
|
3200
|
+
(indexOrId) => {
|
|
3201
|
+
if (typeof indexOrId === "number") {
|
|
3202
|
+
setSnapshotIdx(indexOrId);
|
|
3203
|
+
return;
|
|
3204
|
+
}
|
|
3205
|
+
if (activeSpec) {
|
|
3206
|
+
const sfNode = findSubflowSpecNode(activeSpec, indexOrId);
|
|
3207
|
+
if (sfNode?.subflowStructure) {
|
|
3208
|
+
handleDrillDown(indexOrId);
|
|
3209
|
+
return;
|
|
3210
|
+
}
|
|
3211
|
+
}
|
|
3212
|
+
const idx = activeSnapshots.findIndex((s) => s.stageLabel === indexOrId);
|
|
3213
|
+
if (idx >= 0) setSnapshotIdx(idx);
|
|
3214
|
+
},
|
|
3215
|
+
[activeSpec, activeSnapshots, handleDrillDown]
|
|
3216
|
+
);
|
|
3217
|
+
const handleTreeNodeSelect = useCallback5(
|
|
3218
|
+
(name, isSubflow) => {
|
|
3219
|
+
if (isSubflow && spec) {
|
|
3220
|
+
setDrillDownStack([]);
|
|
3221
|
+
const entry = resolveSubflowLevel(spec, snapshots, name, narrativeEntries);
|
|
3222
|
+
if (entry) {
|
|
3223
|
+
setDrillDownStack([{ ...entry, parentSnapshotIdx: snapshotIdx }]);
|
|
3224
|
+
setSnapshotIdx(0);
|
|
3225
|
+
}
|
|
2635
3226
|
} else {
|
|
2636
|
-
|
|
3227
|
+
setDrillDownStack([]);
|
|
3228
|
+
const idx = snapshots.findIndex((s) => s.stageLabel === name);
|
|
3229
|
+
if (idx >= 0) setSnapshotIdx(idx);
|
|
2637
3230
|
}
|
|
2638
|
-
}
|
|
3231
|
+
},
|
|
3232
|
+
[spec, snapshots, narrativeEntries, snapshotIdx]
|
|
3233
|
+
);
|
|
3234
|
+
const tabLabels = {
|
|
3235
|
+
result: "Result",
|
|
3236
|
+
explainable: "Explainable",
|
|
3237
|
+
"ai-compatible": "AI-Compatible"
|
|
3238
|
+
};
|
|
3239
|
+
if (unstyled) {
|
|
3240
|
+
return /* @__PURE__ */ jsxs15("div", { className, style, "data-fp": "explainable-shell", children: [
|
|
3241
|
+
/* @__PURE__ */ jsx16("div", { "data-fp": "shell-tabs", children: tabs.map((tab) => /* @__PURE__ */ jsx16("button", { "data-fp": "shell-tab", "data-active": tab === activeTab, onClick: () => handleTabChange(tab), children: tabLabels[tab] }, tab)) }),
|
|
3242
|
+
/* @__PURE__ */ jsxs15("div", { "data-fp": "shell-content", "data-tab": activeTab, children: [
|
|
3243
|
+
activeTab === "result" && /* @__PURE__ */ jsx16(ResultPanel, { data: resultData ?? null, logs, hideConsole, unstyled: true }),
|
|
3244
|
+
(activeTab === "explainable" || activeTab === "ai-compatible") && /* @__PURE__ */ jsxs15(Fragment4, { children: [
|
|
3245
|
+
/* @__PURE__ */ jsx16(TimeTravelControls, { snapshots: activeSnapshots, selectedIndex: safeIdx, onIndexChange: handleSnapshotChange, unstyled: true }),
|
|
3246
|
+
isInSubflow && /* @__PURE__ */ jsx16(SubflowBreadcrumb, { breadcrumbs, onNavigate: handleBreadcrumbNavigate }),
|
|
3247
|
+
activeSpec && renderFlowchart?.({ spec: activeSpec, snapshots: activeSnapshots, selectedIndex: safeIdx, onNodeClick: handleNodeClick }),
|
|
3248
|
+
/* @__PURE__ */ jsx16(MemoryPanel, { snapshots: activeSnapshots, selectedIndex: safeIdx, unstyled: true }),
|
|
3249
|
+
/* @__PURE__ */ jsx16(NarrativePanel, { snapshots: activeSnapshots, selectedIndex: safeIdx, narrativeEntries: activeNarrativeEntries, narrative: activeNarrative, unstyled: true }),
|
|
3250
|
+
/* @__PURE__ */ jsx16(GanttTimeline, { snapshots: activeSnapshots, selectedIndex: safeIdx, onSelect: handleSnapshotChange, unstyled: true })
|
|
3251
|
+
] })
|
|
3252
|
+
] })
|
|
3253
|
+
] });
|
|
2639
3254
|
}
|
|
2640
|
-
const
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
3255
|
+
const isVisualizationTab = activeTab === "explainable" || activeTab === "ai-compatible";
|
|
3256
|
+
return /* @__PURE__ */ jsxs15(
|
|
3257
|
+
"div",
|
|
3258
|
+
{
|
|
3259
|
+
ref: shellRef,
|
|
3260
|
+
className,
|
|
3261
|
+
style: {
|
|
3262
|
+
height: "100%",
|
|
3263
|
+
display: "flex",
|
|
3264
|
+
flexDirection: "column",
|
|
3265
|
+
overflow: "hidden",
|
|
3266
|
+
background: theme.bgPrimary,
|
|
3267
|
+
color: theme.textPrimary,
|
|
3268
|
+
fontFamily: theme.fontSans,
|
|
3269
|
+
fontSize: 12,
|
|
3270
|
+
...style
|
|
3271
|
+
},
|
|
3272
|
+
"data-fp": "explainable-shell",
|
|
3273
|
+
children: [
|
|
3274
|
+
tabs.length > 1 && /* @__PURE__ */ jsx16("div", { style: {
|
|
3275
|
+
display: "flex",
|
|
3276
|
+
borderBottom: `1px solid ${theme.border}`,
|
|
3277
|
+
background: theme.bgSecondary,
|
|
3278
|
+
flexShrink: 0
|
|
3279
|
+
}, children: tabs.map((tab) => {
|
|
3280
|
+
const active = tab === activeTab;
|
|
3281
|
+
return /* @__PURE__ */ jsx16(
|
|
3282
|
+
"button",
|
|
3283
|
+
{
|
|
3284
|
+
onClick: () => handleTabChange(tab),
|
|
3285
|
+
style: {
|
|
3286
|
+
padding: "6px 14px",
|
|
3287
|
+
fontSize: 11,
|
|
3288
|
+
fontWeight: active ? 700 : 500,
|
|
3289
|
+
textTransform: "uppercase",
|
|
3290
|
+
letterSpacing: "0.08em",
|
|
3291
|
+
color: active ? theme.primary : theme.textMuted,
|
|
3292
|
+
background: "transparent",
|
|
3293
|
+
border: "none",
|
|
3294
|
+
borderBottom: active ? `2px solid ${theme.primary}` : "2px solid transparent",
|
|
3295
|
+
cursor: "pointer",
|
|
3296
|
+
fontFamily: "inherit"
|
|
3297
|
+
},
|
|
3298
|
+
children: tabLabels[tab]
|
|
3299
|
+
},
|
|
3300
|
+
tab
|
|
3301
|
+
);
|
|
3302
|
+
}) }),
|
|
3303
|
+
/* @__PURE__ */ jsxs15("div", { style: { flex: 1, overflow: isNarrow ? "auto" : "hidden", display: "flex", flexDirection: "column" }, children: [
|
|
3304
|
+
activeTab === "result" && /* @__PURE__ */ jsx16(ResultPanel, { data: resultData ?? null, logs, hideConsole, size }),
|
|
3305
|
+
isVisualizationTab && /* @__PURE__ */ jsxs15(Fragment4, { children: [
|
|
3306
|
+
/* @__PURE__ */ jsx16(
|
|
3307
|
+
TimeTravelControls,
|
|
3308
|
+
{
|
|
3309
|
+
snapshots: activeSnapshots,
|
|
3310
|
+
selectedIndex: safeIdx,
|
|
3311
|
+
onIndexChange: handleSnapshotChange,
|
|
3312
|
+
size
|
|
3313
|
+
}
|
|
3314
|
+
),
|
|
3315
|
+
isInSubflow && /* @__PURE__ */ jsx16(SubflowBreadcrumb, { breadcrumbs, onNavigate: handleBreadcrumbNavigate }),
|
|
3316
|
+
isNarrow ? (
|
|
3317
|
+
/* ── Mobile: stacked vertical ── */
|
|
3318
|
+
/* @__PURE__ */ jsxs15(Fragment4, { children: [
|
|
3319
|
+
/* @__PURE__ */ jsx16("div", { style: { height: 350, flexShrink: 0, overflow: "hidden" }, children: renderFlowchart && activeSpec && renderFlowchart({
|
|
3320
|
+
spec: activeSpec,
|
|
3321
|
+
snapshots: activeSnapshots,
|
|
3322
|
+
selectedIndex: safeIdx,
|
|
3323
|
+
onNodeClick: handleNodeClick
|
|
3324
|
+
}) }),
|
|
3325
|
+
showTreeSidebar && /* @__PURE__ */ jsxs15(Fragment4, { children: [
|
|
3326
|
+
/* @__PURE__ */ jsx16(HLinePill, { label: leftLabel, expanded: leftExpanded, onClick: () => toggleLeft(!leftExpanded) }),
|
|
3327
|
+
leftExpanded && /* @__PURE__ */ jsx16("div", { style: { maxHeight: 180, overflow: "auto", flexShrink: 0 }, children: /* @__PURE__ */ jsx16(
|
|
3328
|
+
SubflowTree,
|
|
3329
|
+
{
|
|
3330
|
+
spec,
|
|
3331
|
+
activeStage: rootOverlay.activeStage,
|
|
3332
|
+
doneStages: rootOverlay.doneStages,
|
|
3333
|
+
onNodeSelect: handleTreeNodeSelect
|
|
3334
|
+
}
|
|
3335
|
+
) })
|
|
3336
|
+
] }),
|
|
3337
|
+
/* @__PURE__ */ jsx16(HLinePill, { label: rightLabel, expanded: rightExpanded, onClick: () => toggleRight(!rightExpanded) }),
|
|
3338
|
+
rightExpanded && /* @__PURE__ */ jsx16("div", { style: { maxHeight: 250, flexShrink: 0, display: "flex", flexDirection: "column", overflow: "hidden" }, children: /* @__PURE__ */ jsx16(
|
|
3339
|
+
DetailsContent,
|
|
3340
|
+
{
|
|
3341
|
+
snapshots: activeSnapshots,
|
|
3342
|
+
selectedIndex: safeIdx,
|
|
3343
|
+
narrativeEntries: activeNarrativeEntries,
|
|
3344
|
+
narrative: activeNarrative,
|
|
3345
|
+
size
|
|
3346
|
+
}
|
|
3347
|
+
) }),
|
|
3348
|
+
/* @__PURE__ */ jsx16(HLinePill, { label: bottomLabel, detail: `${activeSnapshots.length} stages`, expanded: timelineExpanded, onClick: toggleTimeline }),
|
|
3349
|
+
timelineExpanded && /* @__PURE__ */ jsx16("div", { style: { flexShrink: 0, overflow: "hidden" }, children: /* @__PURE__ */ jsx16(GanttTimeline, { snapshots: activeSnapshots, selectedIndex: safeIdx, onSelect: handleSnapshotChange, size }) })
|
|
3350
|
+
] })
|
|
3351
|
+
) : (
|
|
3352
|
+
/* ── Desktop: side-by-side ── */
|
|
3353
|
+
/* @__PURE__ */ jsxs15(Fragment4, { children: [
|
|
3354
|
+
/* @__PURE__ */ jsxs15("div", { style: { flex: 1, display: "flex", overflow: "hidden" }, children: [
|
|
3355
|
+
showTreeSidebar && (leftExpanded ? /* @__PURE__ */ jsxs15("div", { style: { width: 220, flexShrink: 0, display: "flex", flexDirection: "row", overflow: "hidden" }, children: [
|
|
3356
|
+
/* @__PURE__ */ jsx16("div", { style: { flex: 1, overflow: "auto" }, children: /* @__PURE__ */ jsx16(
|
|
3357
|
+
SubflowTree,
|
|
3358
|
+
{
|
|
3359
|
+
spec,
|
|
3360
|
+
activeStage: rootOverlay.activeStage,
|
|
3361
|
+
doneStages: rootOverlay.doneStages,
|
|
3362
|
+
onNodeSelect: handleTreeNodeSelect
|
|
3363
|
+
}
|
|
3364
|
+
) }),
|
|
3365
|
+
/* @__PURE__ */ jsx16(VLinePill, { label: leftLabel, expanded: true, side: "left", onClick: () => toggleLeft(false) })
|
|
3366
|
+
] }) : /* @__PURE__ */ jsx16(VLinePill, { label: leftLabel, expanded: false, side: "left", onClick: () => toggleLeft(true) })),
|
|
3367
|
+
/* @__PURE__ */ jsx16("div", { style: { flex: 1, overflow: "hidden", minWidth: 0 }, children: renderFlowchart && activeSpec && renderFlowchart({
|
|
3368
|
+
spec: activeSpec,
|
|
3369
|
+
snapshots: activeSnapshots,
|
|
3370
|
+
selectedIndex: safeIdx,
|
|
3371
|
+
onNodeClick: handleNodeClick
|
|
3372
|
+
}) }),
|
|
3373
|
+
rightExpanded ? /* @__PURE__ */ jsxs15("div", { style: { width: "38%", minWidth: 300, maxWidth: 500, display: "flex", flexDirection: "row", overflow: "hidden" }, children: [
|
|
3374
|
+
/* @__PURE__ */ jsx16(VLinePill, { label: rightLabel, expanded: true, onClick: () => toggleRight(false) }),
|
|
3375
|
+
/* @__PURE__ */ jsx16(
|
|
3376
|
+
DetailsContent,
|
|
3377
|
+
{
|
|
3378
|
+
snapshots: activeSnapshots,
|
|
3379
|
+
selectedIndex: safeIdx,
|
|
3380
|
+
narrativeEntries: activeNarrativeEntries,
|
|
3381
|
+
narrative: activeNarrative,
|
|
3382
|
+
size,
|
|
3383
|
+
fillHeight: true
|
|
3384
|
+
}
|
|
3385
|
+
)
|
|
3386
|
+
] }) : /* @__PURE__ */ jsx16(VLinePill, { label: rightLabel, expanded: false, onClick: () => toggleRight(true) })
|
|
3387
|
+
] }),
|
|
3388
|
+
/* @__PURE__ */ jsx16(HLinePill, { label: bottomLabel, detail: `${activeSnapshots.length} stages`, expanded: timelineExpanded, onClick: toggleTimeline }),
|
|
3389
|
+
timelineExpanded && /* @__PURE__ */ jsx16("div", { style: { flexShrink: 0, overflow: "hidden" }, children: /* @__PURE__ */ jsx16(GanttTimeline, { snapshots: activeSnapshots, selectedIndex: safeIdx, onSelect: handleSnapshotChange, size }) })
|
|
3390
|
+
] })
|
|
3391
|
+
)
|
|
3392
|
+
] })
|
|
3393
|
+
] })
|
|
3394
|
+
]
|
|
2659
3395
|
}
|
|
2660
|
-
|
|
2661
|
-
}
|
|
2662
|
-
if (node.next) {
|
|
2663
|
-
nextMs = flattenTree(node.next, out, sharedState, nextMs, subflowResults, memory, stageNarrativeMap);
|
|
2664
|
-
}
|
|
2665
|
-
return nextMs;
|
|
2666
|
-
}
|
|
2667
|
-
function createSnapshots(stages) {
|
|
2668
|
-
let accMs = 0;
|
|
2669
|
-
return stages.map((s) => {
|
|
2670
|
-
const duration = s.durationMs ?? 1;
|
|
2671
|
-
const snap = {
|
|
2672
|
-
stageName: s.name,
|
|
2673
|
-
stageLabel: s.label ?? s.name,
|
|
2674
|
-
memory: s.memory ?? {},
|
|
2675
|
-
narrative: s.narrative ?? `${s.label ?? s.name} completed.`,
|
|
2676
|
-
startMs: accMs,
|
|
2677
|
-
durationMs: duration,
|
|
2678
|
-
status: "done",
|
|
2679
|
-
...s.description ? { description: s.description } : void 0,
|
|
2680
|
-
...s.subflowId ? { subflowId: s.subflowId } : void 0
|
|
2681
|
-
};
|
|
2682
|
-
accMs += duration;
|
|
2683
|
-
return snap;
|
|
2684
|
-
});
|
|
3396
|
+
);
|
|
2685
3397
|
}
|
|
2686
3398
|
export {
|
|
2687
3399
|
ExplainableShell,
|
|
2688
3400
|
FootprintTheme,
|
|
2689
3401
|
GanttTimeline,
|
|
2690
3402
|
MemoryInspector,
|
|
3403
|
+
MemoryPanel,
|
|
2691
3404
|
NarrativeLog,
|
|
3405
|
+
NarrativePanel,
|
|
2692
3406
|
NarrativeTrace,
|
|
2693
3407
|
ResultPanel,
|
|
2694
3408
|
ScopeDiff,
|
|
2695
3409
|
SnapshotPanel,
|
|
2696
3410
|
StageDetailPanel,
|
|
3411
|
+
StoryNarrative,
|
|
2697
3412
|
SubflowTree,
|
|
2698
3413
|
TimeTravelControls,
|
|
2699
3414
|
coolDark,
|
|
2700
3415
|
coolLight,
|
|
2701
3416
|
createSnapshots,
|
|
2702
3417
|
defaultTokens,
|
|
3418
|
+
rawDefaults,
|
|
3419
|
+
subflowResultToSnapshots,
|
|
2703
3420
|
themePresets,
|
|
2704
3421
|
toVisualizationSnapshots,
|
|
2705
3422
|
tokensToCSSVars,
|