footprint-explainable-ui 0.5.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 +764 -531
- 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 +734 -502
- package/dist/flowchart.js.map +1 -1
- package/dist/index.cjs +1248 -485
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +171 -33
- package/dist/index.d.ts +171 -33
- package/dist/index.js +1225 -469
- 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";
|
|
@@ -147,14 +167,57 @@ var warmLight = {
|
|
|
147
167
|
mono: "'JetBrains Mono', 'Fira Code', monospace"
|
|
148
168
|
}
|
|
149
169
|
};
|
|
170
|
+
var coolLight = {
|
|
171
|
+
colors: {
|
|
172
|
+
primary: "#6366f1",
|
|
173
|
+
success: "#22c55e",
|
|
174
|
+
error: "#ef4444",
|
|
175
|
+
warning: "#f59e0b",
|
|
176
|
+
bgPrimary: "#ffffff",
|
|
177
|
+
bgSecondary: "#f9fafb",
|
|
178
|
+
bgTertiary: "#e5e7eb",
|
|
179
|
+
textPrimary: "#18181b",
|
|
180
|
+
textSecondary: "#52525b",
|
|
181
|
+
textMuted: "#a1a1aa",
|
|
182
|
+
border: "#e5e7eb"
|
|
183
|
+
},
|
|
184
|
+
radius: "8px",
|
|
185
|
+
fontFamily: {
|
|
186
|
+
sans: "Inter, system-ui, -apple-system, sans-serif",
|
|
187
|
+
mono: "'JetBrains Mono', 'Fira Code', monospace"
|
|
188
|
+
}
|
|
189
|
+
};
|
|
150
190
|
var themePresets = {
|
|
151
191
|
coolDark,
|
|
192
|
+
coolLight,
|
|
152
193
|
warmDark,
|
|
153
194
|
warmLight
|
|
154
195
|
};
|
|
155
196
|
|
|
197
|
+
// src/theme/useDarkModeTokens.ts
|
|
198
|
+
import { useState, useEffect } from "react";
|
|
199
|
+
function useDarkModeTokens(options) {
|
|
200
|
+
const lightTokens = options?.light ?? coolLight;
|
|
201
|
+
const darkTokens = options?.dark ?? coolDark;
|
|
202
|
+
const [isDark, setIsDark] = useState(
|
|
203
|
+
() => document.documentElement.classList.contains(options?.selector ?? "dark")
|
|
204
|
+
);
|
|
205
|
+
useEffect(() => {
|
|
206
|
+
const cls = options?.selector ?? "dark";
|
|
207
|
+
const obs = new MutationObserver(() => {
|
|
208
|
+
setIsDark(document.documentElement.classList.contains(cls));
|
|
209
|
+
});
|
|
210
|
+
obs.observe(document.documentElement, {
|
|
211
|
+
attributes: true,
|
|
212
|
+
attributeFilter: ["class"]
|
|
213
|
+
});
|
|
214
|
+
return () => obs.disconnect();
|
|
215
|
+
}, [options?.selector]);
|
|
216
|
+
return isDark ? darkTokens : lightTokens;
|
|
217
|
+
}
|
|
218
|
+
|
|
156
219
|
// src/components/MemoryInspector/MemoryInspector.tsx
|
|
157
|
-
import { useMemo } from "react";
|
|
220
|
+
import { useMemo, useRef } from "react";
|
|
158
221
|
import { jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
159
222
|
function MemoryInspector({
|
|
160
223
|
data,
|
|
@@ -167,6 +230,7 @@ function MemoryInspector({
|
|
|
167
230
|
className,
|
|
168
231
|
style
|
|
169
232
|
}) {
|
|
233
|
+
const cacheRef = useRef(null);
|
|
170
234
|
const { memory, newKeys } = useMemo(() => {
|
|
171
235
|
if (data) {
|
|
172
236
|
return { memory: data, newKeys: /* @__PURE__ */ new Set() };
|
|
@@ -174,21 +238,37 @@ function MemoryInspector({
|
|
|
174
238
|
if (!snapshots || snapshots.length === 0) {
|
|
175
239
|
return { memory: {}, newKeys: /* @__PURE__ */ new Set() };
|
|
176
240
|
}
|
|
177
|
-
const
|
|
178
|
-
|
|
179
|
-
|
|
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
|
+
}
|
|
180
254
|
}
|
|
255
|
+
cacheRef.current = { snapshots, index: safeIdx, accumulated: merged };
|
|
181
256
|
const nk = /* @__PURE__ */ new Set();
|
|
182
|
-
if (highlightNew &&
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
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
|
+
}
|
|
186
266
|
}
|
|
187
|
-
const current = snapshots[
|
|
267
|
+
const current = snapshots[safeIdx]?.memory ?? {};
|
|
188
268
|
for (const k of Object.keys(current)) {
|
|
189
269
|
if (!(k in prev)) nk.add(k);
|
|
190
270
|
}
|
|
191
|
-
} else if (highlightNew &&
|
|
271
|
+
} else if (highlightNew && safeIdx === 0 && snapshots[0]) {
|
|
192
272
|
for (const k of Object.keys(snapshots[0].memory)) nk.add(k);
|
|
193
273
|
}
|
|
194
274
|
return { memory: merged, newKeys: nk };
|
|
@@ -197,9 +277,9 @@ function MemoryInspector({
|
|
|
197
277
|
const fs = fontSize[size];
|
|
198
278
|
const pad = padding[size];
|
|
199
279
|
if (unstyled) {
|
|
200
|
-
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: [
|
|
201
281
|
/* @__PURE__ */ jsx2("div", { "data-fp": "memory-label", children: "Memory State" }),
|
|
202
|
-
/* @__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) }) })
|
|
203
283
|
] });
|
|
204
284
|
}
|
|
205
285
|
return /* @__PURE__ */ jsxs(
|
|
@@ -212,6 +292,8 @@ function MemoryInspector({
|
|
|
212
292
|
...style
|
|
213
293
|
},
|
|
214
294
|
"data-fp": "memory-inspector",
|
|
295
|
+
role: "region",
|
|
296
|
+
"aria-label": "Memory state",
|
|
215
297
|
children: [
|
|
216
298
|
/* @__PURE__ */ jsx2(
|
|
217
299
|
"span",
|
|
@@ -444,7 +526,7 @@ function NarrativeLog({
|
|
|
444
526
|
}
|
|
445
527
|
|
|
446
528
|
// src/components/NarrativeTrace/NarrativeTrace.tsx
|
|
447
|
-
import { useState, useCallback, useMemo as useMemo3, useEffect, useRef } from "react";
|
|
529
|
+
import { useState as useState2, useCallback, useMemo as useMemo3, useEffect as useEffect2, useRef as useRef2 } from "react";
|
|
448
530
|
import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
449
531
|
function parseGroups(lines) {
|
|
450
532
|
const groups = [];
|
|
@@ -476,12 +558,12 @@ function NarrativeTrace({
|
|
|
476
558
|
const future = revealedCount != null ? narrative.slice(revealedCount) : [];
|
|
477
559
|
const revealedGroups = useMemo3(() => parseGroups(revealed), [revealed]);
|
|
478
560
|
const futureGroups = useMemo3(() => parseGroups(future), [future]);
|
|
479
|
-
const [collapsedSet, setCollapsedSet] =
|
|
561
|
+
const [collapsedSet, setCollapsedSet] = useState2(() => {
|
|
480
562
|
if (!defaultCollapsed) return /* @__PURE__ */ new Set();
|
|
481
563
|
return new Set(parseGroups(narrative).map((g) => g.headerIdx));
|
|
482
564
|
});
|
|
483
|
-
const latestRef =
|
|
484
|
-
|
|
565
|
+
const latestRef = useRef2(null);
|
|
566
|
+
useEffect2(() => {
|
|
485
567
|
latestRef.current?.scrollIntoView({ behavior: "smooth", block: "nearest" });
|
|
486
568
|
}, [revealedGroups.length]);
|
|
487
569
|
const toggle = useCallback((idx) => {
|
|
@@ -504,19 +586,32 @@ function NarrativeTrace({
|
|
|
504
586
|
"data-fp": "narrative-header",
|
|
505
587
|
"data-collapsible": group.steps.length > 0,
|
|
506
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" : ""}`,
|
|
507
593
|
onClick: () => {
|
|
508
594
|
if (group.steps.length > 0) toggle(group.headerIdx);
|
|
509
595
|
onStageClick?.(group.headerIdx);
|
|
510
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
|
+
},
|
|
511
604
|
children: group.header
|
|
512
605
|
}
|
|
513
606
|
),
|
|
514
607
|
!collapsedSet.has(group.headerIdx) && group.steps.map((step) => /* @__PURE__ */ jsx4("div", { "data-fp": "narrative-step", children: step.text }, step.idx))
|
|
515
608
|
] }, group.headerIdx)),
|
|
516
|
-
futureGroups.
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
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
|
+
] })
|
|
520
615
|
] });
|
|
521
616
|
}
|
|
522
617
|
return /* @__PURE__ */ jsxs3(
|
|
@@ -546,10 +641,21 @@ function NarrativeTrace({
|
|
|
546
641
|
/* @__PURE__ */ jsxs3(
|
|
547
642
|
"div",
|
|
548
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"}`,
|
|
549
648
|
onClick: () => {
|
|
550
649
|
if (hasSteps) toggle(group.headerIdx);
|
|
551
650
|
onStageClick?.(group.headerIdx);
|
|
552
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
|
+
},
|
|
553
659
|
style: {
|
|
554
660
|
fontSize: fs.body,
|
|
555
661
|
lineHeight: 1.7,
|
|
@@ -608,43 +714,25 @@ function NarrativeTrace({
|
|
|
608
714
|
group.headerIdx
|
|
609
715
|
);
|
|
610
716
|
}),
|
|
611
|
-
futureGroups.length > 0 && /* @__PURE__ */
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
},
|
|
624
|
-
children: group.header
|
|
625
|
-
}
|
|
626
|
-
),
|
|
627
|
-
group.steps.map((step) => /* @__PURE__ */ jsx4(
|
|
628
|
-
"div",
|
|
629
|
-
{
|
|
630
|
-
style: {
|
|
631
|
-
fontSize: fs.small,
|
|
632
|
-
lineHeight: 1.6,
|
|
633
|
-
color: theme.textMuted,
|
|
634
|
-
padding: `2px ${pad - 4}px 2px ${pad + 20}px`
|
|
635
|
-
},
|
|
636
|
-
children: step.text
|
|
637
|
-
},
|
|
638
|
-
`f-${step.idx}`
|
|
639
|
-
))
|
|
640
|
-
] }, `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
|
+
] })
|
|
641
729
|
]
|
|
642
730
|
}
|
|
643
731
|
);
|
|
644
732
|
}
|
|
645
733
|
|
|
646
734
|
// src/components/GanttTimeline/GanttTimeline.tsx
|
|
647
|
-
import { useState as
|
|
735
|
+
import { useState as useState3, useMemo as useMemo4, useRef as useRef3, useEffect as useEffect3 } from "react";
|
|
648
736
|
import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
649
737
|
function GanttTimeline({
|
|
650
738
|
snapshots,
|
|
@@ -656,9 +744,9 @@ function GanttTimeline({
|
|
|
656
744
|
style,
|
|
657
745
|
maxVisibleRows = 5
|
|
658
746
|
}) {
|
|
659
|
-
const [expanded, setExpanded] =
|
|
660
|
-
const activeRowRef =
|
|
661
|
-
const scrollContainerRef =
|
|
747
|
+
const [expanded, setExpanded] = useState3(false);
|
|
748
|
+
const activeRowRef = useRef3(null);
|
|
749
|
+
const scrollContainerRef = useRef3(null);
|
|
662
750
|
const totalWallTime = useMemo4(
|
|
663
751
|
() => Math.max(...snapshots.map((s) => s.startMs + s.durationMs), 1),
|
|
664
752
|
[snapshots]
|
|
@@ -670,7 +758,7 @@ function GanttTimeline({
|
|
|
670
758
|
const rowHeight = size === "compact" ? 18 : 22;
|
|
671
759
|
const collapsible = maxVisibleRows > 0 && snapshots.length > maxVisibleRows;
|
|
672
760
|
const showAll = expanded || !collapsible;
|
|
673
|
-
|
|
761
|
+
useEffect3(() => {
|
|
674
762
|
if (!showAll && activeRowRef.current && scrollContainerRef.current) {
|
|
675
763
|
activeRowRef.current.scrollIntoView({
|
|
676
764
|
block: "nearest",
|
|
@@ -679,12 +767,15 @@ function GanttTimeline({
|
|
|
679
767
|
}
|
|
680
768
|
}, [selectedIndex, showAll]);
|
|
681
769
|
if (unstyled) {
|
|
682
|
-
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(
|
|
683
771
|
"div",
|
|
684
772
|
{
|
|
685
773
|
"data-fp": "gantt-bar",
|
|
686
774
|
"data-selected": idx === selectedIndex,
|
|
687
775
|
"data-visible": idx <= selectedIndex,
|
|
776
|
+
role: "option",
|
|
777
|
+
"aria-selected": idx === selectedIndex,
|
|
778
|
+
"aria-label": `${snap.stageLabel}, ${snap.durationMs}ms`,
|
|
688
779
|
onClick: () => onSelect?.(idx),
|
|
689
780
|
children: [
|
|
690
781
|
/* @__PURE__ */ jsx5("span", { "data-fp": "gantt-label", children: snap.stageLabel }),
|
|
@@ -694,7 +785,7 @@ function GanttTimeline({
|
|
|
694
785
|
] })
|
|
695
786
|
]
|
|
696
787
|
},
|
|
697
|
-
snap.stageName
|
|
788
|
+
`${snap.stageName}-${idx}`
|
|
698
789
|
)) });
|
|
699
790
|
}
|
|
700
791
|
return /* @__PURE__ */ jsxs4(
|
|
@@ -750,6 +841,8 @@ function GanttTimeline({
|
|
|
750
841
|
"div",
|
|
751
842
|
{
|
|
752
843
|
ref: scrollContainerRef,
|
|
844
|
+
role: "listbox",
|
|
845
|
+
"aria-label": "Execution timeline",
|
|
753
846
|
style: {
|
|
754
847
|
marginTop: 8,
|
|
755
848
|
display: "flex",
|
|
@@ -770,6 +863,9 @@ function GanttTimeline({
|
|
|
770
863
|
"div",
|
|
771
864
|
{
|
|
772
865
|
ref: isSelected ? activeRowRef : void 0,
|
|
866
|
+
role: "option",
|
|
867
|
+
"aria-selected": isSelected,
|
|
868
|
+
"aria-label": `${snap.stageLabel}, ${snap.durationMs}ms`,
|
|
773
869
|
onClick: () => onSelect?.(idx),
|
|
774
870
|
style: {
|
|
775
871
|
display: "flex",
|
|
@@ -785,6 +881,7 @@ function GanttTimeline({
|
|
|
785
881
|
/* @__PURE__ */ jsx5(
|
|
786
882
|
"span",
|
|
787
883
|
{
|
|
884
|
+
title: snap.stageLabel,
|
|
788
885
|
style: {
|
|
789
886
|
width: labelWidth,
|
|
790
887
|
fontSize: fs.small,
|
|
@@ -844,7 +941,7 @@ function GanttTimeline({
|
|
|
844
941
|
)
|
|
845
942
|
]
|
|
846
943
|
},
|
|
847
|
-
snap.stageName
|
|
944
|
+
`${snap.stageName}-${idx}`
|
|
848
945
|
);
|
|
849
946
|
})
|
|
850
947
|
}
|
|
@@ -881,7 +978,7 @@ function GanttTimeline({
|
|
|
881
978
|
}
|
|
882
979
|
|
|
883
980
|
// src/components/SnapshotPanel/SnapshotPanel.tsx
|
|
884
|
-
import { useState as
|
|
981
|
+
import { useState as useState4 } from "react";
|
|
885
982
|
import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
886
983
|
function SnapshotPanel({
|
|
887
984
|
snapshots,
|
|
@@ -893,7 +990,7 @@ function SnapshotPanel({
|
|
|
893
990
|
className,
|
|
894
991
|
style
|
|
895
992
|
}) {
|
|
896
|
-
const [selectedIndex, setSelectedIndex] =
|
|
993
|
+
const [selectedIndex, setSelectedIndex] = useState4(0);
|
|
897
994
|
const fs = fontSize[size];
|
|
898
995
|
const pad = padding[size];
|
|
899
996
|
if (snapshots.length === 0) {
|
|
@@ -1166,9 +1263,9 @@ function fmt(v2) {
|
|
|
1166
1263
|
return String(v2);
|
|
1167
1264
|
}
|
|
1168
1265
|
var diffColors = {
|
|
1169
|
-
added: { bg:
|
|
1170
|
-
removed: { bg:
|
|
1171
|
-
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: "~" },
|
|
1172
1269
|
unchanged: { bg: "transparent", fg: "", icon: " " }
|
|
1173
1270
|
};
|
|
1174
1271
|
function ScopeDiff({
|
|
@@ -1384,7 +1481,7 @@ function ResultPanel({
|
|
|
1384
1481
|
}
|
|
1385
1482
|
|
|
1386
1483
|
// src/components/StageDetailPanel/StageDetailPanel.tsx
|
|
1387
|
-
import { useState as
|
|
1484
|
+
import { useState as useState5, useMemo as useMemo6, useCallback as useCallback2 } from "react";
|
|
1388
1485
|
import { Fragment as Fragment2, jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1389
1486
|
function computeChanges(prev, curr) {
|
|
1390
1487
|
const changes = [];
|
|
@@ -1816,7 +1913,7 @@ function StageDetailPanel({
|
|
|
1816
1913
|
className,
|
|
1817
1914
|
style
|
|
1818
1915
|
}) {
|
|
1819
|
-
const [internalMode, setInternalMode] =
|
|
1916
|
+
const [internalMode, setInternalMode] = useState5(controlledMode ?? "simple");
|
|
1820
1917
|
const activeMode = controlledMode ?? internalMode;
|
|
1821
1918
|
const handleToggle = useCallback2(() => {
|
|
1822
1919
|
const next = activeMode === "simple" ? "dev" : "simple";
|
|
@@ -1862,7 +1959,7 @@ function StageDetailPanel({
|
|
|
1862
1959
|
}
|
|
1863
1960
|
|
|
1864
1961
|
// src/components/TimeTravelControls/TimeTravelControls.tsx
|
|
1865
|
-
import { useState as
|
|
1962
|
+
import { useState as useState6, useEffect as useEffect4, useRef as useRef4, useCallback as useCallback3 } from "react";
|
|
1866
1963
|
import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1867
1964
|
function TimeTravelControls({
|
|
1868
1965
|
snapshots,
|
|
@@ -1874,12 +1971,12 @@ function TimeTravelControls({
|
|
|
1874
1971
|
className,
|
|
1875
1972
|
style
|
|
1876
1973
|
}) {
|
|
1877
|
-
const [playing, setPlaying] =
|
|
1878
|
-
const playRef =
|
|
1974
|
+
const [playing, setPlaying] = useState6(false);
|
|
1975
|
+
const playRef = useRef4(null);
|
|
1879
1976
|
const total = snapshots.length;
|
|
1880
1977
|
const canPrev = selectedIndex > 0;
|
|
1881
1978
|
const canNext = selectedIndex < total - 1;
|
|
1882
|
-
|
|
1979
|
+
useEffect4(() => {
|
|
1883
1980
|
if (!playing || !autoPlayable) return;
|
|
1884
1981
|
if (selectedIndex >= total - 1) {
|
|
1885
1982
|
setPlaying(false);
|
|
@@ -1905,49 +2002,80 @@ function TimeTravelControls({
|
|
|
1905
2002
|
setPlaying(true);
|
|
1906
2003
|
}
|
|
1907
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
|
+
);
|
|
1908
2022
|
const fs = fontSize[size];
|
|
1909
2023
|
if (unstyled) {
|
|
1910
|
-
return /* @__PURE__ */ jsxs9(
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
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
|
+
);
|
|
1951
2079
|
}
|
|
1952
2080
|
const btnStyle = (disabled) => ({
|
|
1953
2081
|
background: theme.bgTertiary,
|
|
@@ -1976,6 +2104,10 @@ function TimeTravelControls({
|
|
|
1976
2104
|
...style
|
|
1977
2105
|
},
|
|
1978
2106
|
"data-fp": "time-travel-controls",
|
|
2107
|
+
role: "toolbar",
|
|
2108
|
+
"aria-label": "Time travel controls",
|
|
2109
|
+
tabIndex: 0,
|
|
2110
|
+
onKeyDown: handleKeyDown,
|
|
1979
2111
|
children: [
|
|
1980
2112
|
/* @__PURE__ */ jsx10(
|
|
1981
2113
|
"button",
|
|
@@ -1986,6 +2118,7 @@ function TimeTravelControls({
|
|
|
1986
2118
|
setPlaying(false);
|
|
1987
2119
|
onIndexChange(selectedIndex - 1);
|
|
1988
2120
|
},
|
|
2121
|
+
"aria-label": "Previous stage",
|
|
1989
2122
|
children: "\u25C0"
|
|
1990
2123
|
}
|
|
1991
2124
|
),
|
|
@@ -2008,6 +2141,7 @@ function TimeTravelControls({
|
|
|
2008
2141
|
flexShrink: 0
|
|
2009
2142
|
},
|
|
2010
2143
|
title: playing ? "Pause" : "Play",
|
|
2144
|
+
"aria-label": playing ? "Pause" : "Play",
|
|
2011
2145
|
children: playing ? "\u23F8" : "\u25B6"
|
|
2012
2146
|
}
|
|
2013
2147
|
),
|
|
@@ -2020,6 +2154,7 @@ function TimeTravelControls({
|
|
|
2020
2154
|
setPlaying(false);
|
|
2021
2155
|
onIndexChange(selectedIndex + 1);
|
|
2022
2156
|
},
|
|
2157
|
+
"aria-label": "Next stage",
|
|
2023
2158
|
children: "\u25B6"
|
|
2024
2159
|
}
|
|
2025
2160
|
),
|
|
@@ -2066,125 +2201,146 @@ function TimeTravelControls({
|
|
|
2066
2201
|
}
|
|
2067
2202
|
|
|
2068
2203
|
// src/components/ExplainableShell/ExplainableShell.tsx
|
|
2069
|
-
import { useState as
|
|
2070
|
-
|
|
2071
|
-
|
|
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({
|
|
2072
2331
|
snapshots,
|
|
2073
|
-
|
|
2074
|
-
logs = [],
|
|
2075
|
-
narrative = [],
|
|
2076
|
-
tabs = ["result", "explainable", "ai-compatible"],
|
|
2077
|
-
defaultTab,
|
|
2078
|
-
hideConsole = false,
|
|
2079
|
-
renderFlowchart,
|
|
2332
|
+
selectedIndex,
|
|
2080
2333
|
size = "default",
|
|
2081
2334
|
unstyled = false,
|
|
2082
2335
|
className,
|
|
2083
2336
|
style
|
|
2084
2337
|
}) {
|
|
2085
|
-
const [
|
|
2086
|
-
const [
|
|
2087
|
-
const fs = fontSize[size];
|
|
2088
|
-
const pad = padding[size];
|
|
2089
|
-
const handleSnapshotChange = useCallback4((idx) => {
|
|
2090
|
-
setSnapshotIdx(Math.max(0, Math.min(idx, snapshots.length - 1)));
|
|
2091
|
-
}, [snapshots.length]);
|
|
2092
|
-
const revealedCount = useMemo7(() => {
|
|
2093
|
-
if (snapshots.length === 0 || narrative.length === 0) return narrative.length;
|
|
2094
|
-
const boundaries = [];
|
|
2095
|
-
for (let i = 0; i < narrative.length; i++) {
|
|
2096
|
-
const trimmed = narrative[i].trimStart();
|
|
2097
|
-
if (trimmed.startsWith("Stage ") && !trimmed.match(/^Stage\s+\d+:\s*Step\s/) || trimmed.startsWith("[")) {
|
|
2098
|
-
boundaries.push(i);
|
|
2099
|
-
}
|
|
2100
|
-
}
|
|
2101
|
-
if (boundaries.length === 0) {
|
|
2102
|
-
const ratio = (snapshotIdx + 1) / snapshots.length;
|
|
2103
|
-
return Math.max(1, Math.ceil(narrative.length * ratio));
|
|
2104
|
-
}
|
|
2105
|
-
const groupsToShow = Math.max(
|
|
2106
|
-
1,
|
|
2107
|
-
Math.min(
|
|
2108
|
-
Math.floor((snapshotIdx + 1) / snapshots.length * boundaries.length) || 1,
|
|
2109
|
-
boundaries.length
|
|
2110
|
-
)
|
|
2111
|
-
);
|
|
2112
|
-
const endIdx = groupsToShow < boundaries.length ? boundaries[groupsToShow] : narrative.length;
|
|
2113
|
-
return Math.max(1, endIdx);
|
|
2114
|
-
}, [snapshots.length, snapshotIdx, narrative]);
|
|
2115
|
-
const prevMemory = snapshotIdx > 0 ? snapshots[snapshotIdx - 1]?.memory : null;
|
|
2116
|
-
const currMemory = snapshots[snapshotIdx]?.memory ?? {};
|
|
2117
|
-
const tabLabels = {
|
|
2118
|
-
result: "Result",
|
|
2119
|
-
explainable: "Explainable",
|
|
2120
|
-
"ai-compatible": "AI-Compatible"
|
|
2121
|
-
};
|
|
2338
|
+
const prevMemory = selectedIndex > 0 ? snapshots[selectedIndex - 1]?.memory ?? null : null;
|
|
2339
|
+
const currMemory = snapshots[selectedIndex]?.memory ?? {};
|
|
2122
2340
|
if (unstyled) {
|
|
2123
|
-
return /* @__PURE__ */ jsxs10("div", { className, style, "data-fp": "
|
|
2124
|
-
/* @__PURE__ */ jsx11(
|
|
2125
|
-
|
|
2126
|
-
{
|
|
2127
|
-
"data-fp": "shell-tab",
|
|
2128
|
-
"data-active": tab === activeTab,
|
|
2129
|
-
onClick: () => setActiveTab(tab),
|
|
2130
|
-
children: tabLabels[tab]
|
|
2131
|
-
},
|
|
2132
|
-
tab
|
|
2133
|
-
)) }),
|
|
2134
|
-
/* @__PURE__ */ jsxs10("div", { "data-fp": "shell-content", "data-tab": activeTab, children: [
|
|
2135
|
-
activeTab === "result" && /* @__PURE__ */ jsx11(
|
|
2136
|
-
ResultPanel,
|
|
2137
|
-
{
|
|
2138
|
-
data: resultData ?? null,
|
|
2139
|
-
logs,
|
|
2140
|
-
hideConsole,
|
|
2141
|
-
unstyled: true
|
|
2142
|
-
}
|
|
2143
|
-
),
|
|
2144
|
-
activeTab === "explainable" && /* @__PURE__ */ jsxs10(Fragment3, { children: [
|
|
2145
|
-
/* @__PURE__ */ jsx11(
|
|
2146
|
-
TimeTravelControls,
|
|
2147
|
-
{
|
|
2148
|
-
snapshots,
|
|
2149
|
-
selectedIndex: snapshotIdx,
|
|
2150
|
-
onIndexChange: handleSnapshotChange,
|
|
2151
|
-
unstyled: true
|
|
2152
|
-
}
|
|
2153
|
-
),
|
|
2154
|
-
renderFlowchart?.({ snapshots, selectedIndex: snapshotIdx, onNodeClick: handleSnapshotChange }),
|
|
2155
|
-
/* @__PURE__ */ jsx11(MemoryInspector, { snapshots, selectedIndex: snapshotIdx, unstyled: true }),
|
|
2156
|
-
/* @__PURE__ */ jsx11(ScopeDiff, { previous: prevMemory, current: currMemory, unstyled: true }),
|
|
2157
|
-
/* @__PURE__ */ jsx11(
|
|
2158
|
-
GanttTimeline,
|
|
2159
|
-
{
|
|
2160
|
-
snapshots,
|
|
2161
|
-
selectedIndex: snapshotIdx,
|
|
2162
|
-
onSelect: handleSnapshotChange,
|
|
2163
|
-
unstyled: true
|
|
2164
|
-
}
|
|
2165
|
-
)
|
|
2166
|
-
] }),
|
|
2167
|
-
activeTab === "ai-compatible" && /* @__PURE__ */ jsxs10(Fragment3, { children: [
|
|
2168
|
-
/* @__PURE__ */ jsx11(
|
|
2169
|
-
TimeTravelControls,
|
|
2170
|
-
{
|
|
2171
|
-
snapshots,
|
|
2172
|
-
selectedIndex: snapshotIdx,
|
|
2173
|
-
onIndexChange: handleSnapshotChange,
|
|
2174
|
-
unstyled: true
|
|
2175
|
-
}
|
|
2176
|
-
),
|
|
2177
|
-
renderFlowchart?.({ snapshots, selectedIndex: snapshotIdx, onNodeClick: handleSnapshotChange }),
|
|
2178
|
-
/* @__PURE__ */ jsx11(
|
|
2179
|
-
NarrativeTrace,
|
|
2180
|
-
{
|
|
2181
|
-
narrative,
|
|
2182
|
-
revealedCount,
|
|
2183
|
-
unstyled: true
|
|
2184
|
-
}
|
|
2185
|
-
)
|
|
2186
|
-
] })
|
|
2187
|
-
] })
|
|
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 })
|
|
2188
2344
|
] });
|
|
2189
2345
|
}
|
|
2190
2346
|
return /* @__PURE__ */ jsxs10(
|
|
@@ -2192,156 +2348,251 @@ function ExplainableShell({
|
|
|
2192
2348
|
{
|
|
2193
2349
|
className,
|
|
2194
2350
|
style: {
|
|
2195
|
-
|
|
2351
|
+
overflow: "auto",
|
|
2196
2352
|
display: "flex",
|
|
2197
2353
|
flexDirection: "column",
|
|
2198
|
-
overflow: "hidden",
|
|
2199
|
-
background: theme.bgPrimary,
|
|
2200
|
-
color: theme.textPrimary,
|
|
2201
|
-
fontFamily: theme.fontSans,
|
|
2202
2354
|
...style
|
|
2203
2355
|
},
|
|
2204
|
-
"data-fp": "
|
|
2356
|
+
"data-fp": "memory-panel",
|
|
2205
2357
|
children: [
|
|
2206
|
-
/* @__PURE__ */ jsx11(
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
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
|
-
activeTab === "ai-compatible" && /* @__PURE__ */ jsxs10(Fragment3, { children: [
|
|
2307
|
-
/* @__PURE__ */ jsx11(
|
|
2308
|
-
TimeTravelControls,
|
|
2309
|
-
{
|
|
2310
|
-
snapshots,
|
|
2311
|
-
selectedIndex: snapshotIdx,
|
|
2312
|
-
onIndexChange: handleSnapshotChange,
|
|
2313
|
-
size
|
|
2314
|
-
}
|
|
2315
|
-
),
|
|
2316
|
-
/* @__PURE__ */ jsxs10("div", { style: { flex: 1, display: "flex", overflow: "hidden" }, children: [
|
|
2317
|
-
renderFlowchart && /* @__PURE__ */ jsx11("div", { style: { flex: 1, overflow: "hidden", borderRight: `1px solid ${theme.border}` }, children: renderFlowchart({ snapshots, selectedIndex: snapshotIdx, onNodeClick: handleSnapshotChange }) }),
|
|
2318
|
-
/* @__PURE__ */ jsx11(
|
|
2319
|
-
NarrativeTrace,
|
|
2320
|
-
{
|
|
2321
|
-
narrative,
|
|
2322
|
-
revealedCount,
|
|
2323
|
-
size,
|
|
2324
|
-
style: {
|
|
2325
|
-
width: renderFlowchart ? "40%" : "100%",
|
|
2326
|
-
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
|
|
2327
2458
|
}
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
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..."
|
|
2332
2489
|
] })
|
|
2333
2490
|
]
|
|
2334
2491
|
}
|
|
2335
2492
|
);
|
|
2336
2493
|
}
|
|
2337
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
|
+
|
|
2338
2587
|
// src/components/FlowchartView/SubflowTree.tsx
|
|
2339
|
-
import { memo, useState as useState7, useCallback as
|
|
2340
|
-
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";
|
|
2341
2590
|
function specToTree(node) {
|
|
2591
|
+
if (!node) return [];
|
|
2342
2592
|
const entries = [];
|
|
2343
2593
|
const seen = /* @__PURE__ */ new Set();
|
|
2344
2594
|
function walk(n) {
|
|
2595
|
+
if (!n) return;
|
|
2345
2596
|
const id = n.name || n.id || "";
|
|
2346
2597
|
if (seen.has(id)) return;
|
|
2347
2598
|
seen.add(id);
|
|
@@ -2357,7 +2608,7 @@ function specToTree(node) {
|
|
|
2357
2608
|
entries.push(entry);
|
|
2358
2609
|
if (n.children) {
|
|
2359
2610
|
for (const child of n.children) {
|
|
2360
|
-
walk(child);
|
|
2611
|
+
if (child) walk(child);
|
|
2361
2612
|
}
|
|
2362
2613
|
}
|
|
2363
2614
|
if (n.next) {
|
|
@@ -2378,14 +2629,14 @@ var TreeNode = memo(function TreeNode2({
|
|
|
2378
2629
|
const hasChildren = entry.children && entry.children.length > 0;
|
|
2379
2630
|
const isActive = activeStage === entry.name;
|
|
2380
2631
|
const isDone = doneStages?.has(entry.name);
|
|
2381
|
-
const handleClick =
|
|
2632
|
+
const handleClick = useCallback4(() => {
|
|
2382
2633
|
if (hasChildren) {
|
|
2383
2634
|
setExpanded((prev) => !prev);
|
|
2384
2635
|
}
|
|
2385
2636
|
onNodeSelect?.(entry.name, !!entry.isSubflow);
|
|
2386
2637
|
}, [hasChildren, onNodeSelect, entry.name, entry.isSubflow]);
|
|
2387
|
-
return /* @__PURE__ */
|
|
2388
|
-
/* @__PURE__ */
|
|
2638
|
+
return /* @__PURE__ */ jsxs13(Fragment3, { children: [
|
|
2639
|
+
/* @__PURE__ */ jsxs13(
|
|
2389
2640
|
"button",
|
|
2390
2641
|
{
|
|
2391
2642
|
onClick: handleClick,
|
|
@@ -2416,7 +2667,7 @@ var TreeNode = memo(function TreeNode2({
|
|
|
2416
2667
|
}
|
|
2417
2668
|
},
|
|
2418
2669
|
children: [
|
|
2419
|
-
hasChildren ? /* @__PURE__ */
|
|
2670
|
+
hasChildren ? /* @__PURE__ */ jsx14(
|
|
2420
2671
|
"span",
|
|
2421
2672
|
{
|
|
2422
2673
|
style: {
|
|
@@ -2431,8 +2682,8 @@ var TreeNode = memo(function TreeNode2({
|
|
|
2431
2682
|
},
|
|
2432
2683
|
children: "\u25B6"
|
|
2433
2684
|
}
|
|
2434
|
-
) : /* @__PURE__ */
|
|
2435
|
-
/* @__PURE__ */
|
|
2685
|
+
) : /* @__PURE__ */ jsx14("span", { style: { width: 12, flexShrink: 0 } }),
|
|
2686
|
+
/* @__PURE__ */ jsx14(
|
|
2436
2687
|
"span",
|
|
2437
2688
|
{
|
|
2438
2689
|
style: {
|
|
@@ -2444,8 +2695,8 @@ var TreeNode = memo(function TreeNode2({
|
|
|
2444
2695
|
}
|
|
2445
2696
|
}
|
|
2446
2697
|
),
|
|
2447
|
-
/* @__PURE__ */
|
|
2448
|
-
/* @__PURE__ */
|
|
2698
|
+
/* @__PURE__ */ jsxs13("span", { style: { display: "flex", flexDirection: "column", minWidth: 0 }, children: [
|
|
2699
|
+
/* @__PURE__ */ jsxs13(
|
|
2449
2700
|
"span",
|
|
2450
2701
|
{
|
|
2451
2702
|
style: {
|
|
@@ -2457,11 +2708,11 @@ var TreeNode = memo(function TreeNode2({
|
|
|
2457
2708
|
},
|
|
2458
2709
|
children: [
|
|
2459
2710
|
entry.name,
|
|
2460
|
-
entry.isSubflow && /* @__PURE__ */
|
|
2711
|
+
entry.isSubflow && /* @__PURE__ */ jsx14("span", { style: { opacity: 0.5, marginLeft: 4, fontSize: 10 }, children: "\u229E" })
|
|
2461
2712
|
]
|
|
2462
2713
|
}
|
|
2463
2714
|
),
|
|
2464
|
-
entry.description && /* @__PURE__ */
|
|
2715
|
+
entry.description && /* @__PURE__ */ jsx14(
|
|
2465
2716
|
"span",
|
|
2466
2717
|
{
|
|
2467
2718
|
style: {
|
|
@@ -2478,7 +2729,7 @@ var TreeNode = memo(function TreeNode2({
|
|
|
2478
2729
|
]
|
|
2479
2730
|
}
|
|
2480
2731
|
),
|
|
2481
|
-
hasChildren && expanded && /* @__PURE__ */
|
|
2732
|
+
hasChildren && expanded && /* @__PURE__ */ jsx14("div", { children: entry.children.map((child, i) => /* @__PURE__ */ jsx14(
|
|
2482
2733
|
TreeNode2,
|
|
2483
2734
|
{
|
|
2484
2735
|
entry: child,
|
|
@@ -2487,12 +2738,12 @@ var TreeNode = memo(function TreeNode2({
|
|
|
2487
2738
|
doneStages,
|
|
2488
2739
|
onNodeSelect
|
|
2489
2740
|
},
|
|
2490
|
-
`${child.name}-${i}`
|
|
2741
|
+
child.subflowId ?? `${child.name}-${i}`
|
|
2491
2742
|
)) })
|
|
2492
2743
|
] });
|
|
2493
2744
|
});
|
|
2494
2745
|
var SectionLabel = memo(function SectionLabel2({ children }) {
|
|
2495
|
-
return /* @__PURE__ */
|
|
2746
|
+
return /* @__PURE__ */ jsx14(
|
|
2496
2747
|
"div",
|
|
2497
2748
|
{
|
|
2498
2749
|
style: {
|
|
@@ -2516,21 +2767,10 @@ var SubflowTree = memo(function SubflowTree2({
|
|
|
2516
2767
|
className,
|
|
2517
2768
|
style
|
|
2518
2769
|
}) {
|
|
2519
|
-
const tree =
|
|
2520
|
-
const
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
TreeNode,
|
|
2524
|
-
{
|
|
2525
|
-
entry,
|
|
2526
|
-
depth: 0,
|
|
2527
|
-
activeStage,
|
|
2528
|
-
doneStages,
|
|
2529
|
-
onNodeSelect
|
|
2530
|
-
},
|
|
2531
|
-
`${entry.name}-${i}`
|
|
2532
|
-
));
|
|
2533
|
-
return /* @__PURE__ */ jsxs11(
|
|
2770
|
+
const tree = useMemo9(() => specToTree(spec), [spec]);
|
|
2771
|
+
const subflowStages = useMemo9(() => tree.filter((e) => e.isSubflow), [tree]);
|
|
2772
|
+
if (subflowStages.length === 0) return null;
|
|
2773
|
+
return /* @__PURE__ */ jsxs13(
|
|
2534
2774
|
"div",
|
|
2535
2775
|
{
|
|
2536
2776
|
className,
|
|
@@ -2548,123 +2788,639 @@ var SubflowTree = memo(function SubflowTree2({
|
|
|
2548
2788
|
...style
|
|
2549
2789
|
},
|
|
2550
2790
|
children: [
|
|
2551
|
-
!unstyled && /* @__PURE__ */
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2791
|
+
!unstyled && /* @__PURE__ */ jsx14(SectionLabel, { children: "Subflows" }),
|
|
2792
|
+
subflowStages.map((entry, i) => /* @__PURE__ */ jsx14(
|
|
2793
|
+
TreeNode,
|
|
2794
|
+
{
|
|
2795
|
+
entry,
|
|
2796
|
+
depth: 0,
|
|
2797
|
+
activeStage,
|
|
2798
|
+
doneStages,
|
|
2799
|
+
onNodeSelect
|
|
2800
|
+
},
|
|
2801
|
+
entry.subflowId ?? `${entry.name}-${i}`
|
|
2802
|
+
))
|
|
2558
2803
|
]
|
|
2559
2804
|
}
|
|
2560
2805
|
);
|
|
2561
2806
|
});
|
|
2562
2807
|
|
|
2563
|
-
// src/
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
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
|
+
]
|
|
2580
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
|
+
};
|
|
3048
|
+
}
|
|
3049
|
+
function extractSubflowNarrative(entries, subflowName) {
|
|
3050
|
+
const result = [];
|
|
3051
|
+
let inside = false;
|
|
3052
|
+
for (const entry of entries) {
|
|
3053
|
+
if (entry.type === "subflow" && entry.text.includes(subflowName) && entry.text.startsWith("Entering")) {
|
|
3054
|
+
inside = true;
|
|
3055
|
+
continue;
|
|
2581
3056
|
}
|
|
3057
|
+
if (inside && entry.type === "subflow" && entry.text.includes(subflowName) && entry.text.startsWith("Exiting")) break;
|
|
3058
|
+
if (inside) result.push(entry);
|
|
2582
3059
|
}
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
memory,
|
|
2589
|
-
narrative,
|
|
2590
|
-
startMs,
|
|
2591
|
-
durationMs,
|
|
2592
|
-
status: "done",
|
|
2593
|
-
...node.description ? { description: node.description } : void 0,
|
|
2594
|
-
...node.subflowId ? { subflowId: node.subflowId } : void 0,
|
|
2595
|
-
...sfResult ? { subflowResult: sfResult } : void 0
|
|
2596
|
-
});
|
|
2597
|
-
let nextMs = startMs + durationMs;
|
|
2598
|
-
if (node.children && node.children.length > 0) {
|
|
2599
|
-
let maxChildEnd = nextMs;
|
|
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) {
|
|
2600
3065
|
for (const child of node.children) {
|
|
2601
|
-
const
|
|
2602
|
-
|
|
3066
|
+
const f = findSubflowSpecNode(child, name);
|
|
3067
|
+
if (f) return f;
|
|
2603
3068
|
}
|
|
2604
|
-
nextMs = maxChildEnd;
|
|
2605
3069
|
}
|
|
2606
|
-
if (node.next)
|
|
2607
|
-
|
|
2608
|
-
}
|
|
2609
|
-
return nextMs;
|
|
3070
|
+
if (node.next) return findSubflowSpecNode(node.next, name);
|
|
3071
|
+
return null;
|
|
2610
3072
|
}
|
|
2611
|
-
function
|
|
2612
|
-
|
|
2613
|
-
if (node.
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
const keys = Object.keys(node.stageWrites);
|
|
2618
|
-
parts.push(`Wrote ${keys.length} key(s): ${keys.join(", ")}.`);
|
|
2619
|
-
}
|
|
2620
|
-
if (node.errors && Object.keys(node.errors).length > 0) {
|
|
2621
|
-
parts.push(`Errors: ${JSON.stringify(node.errors)}`);
|
|
2622
|
-
}
|
|
2623
|
-
if (node.isFork) {
|
|
2624
|
-
parts.push(
|
|
2625
|
-
`Forked into ${node.children?.length ?? 0} parallel branch(es).`
|
|
2626
|
-
);
|
|
2627
|
-
}
|
|
2628
|
-
return parts.join(" ") || `Stage ${node.id} completed.`;
|
|
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;
|
|
2629
3079
|
}
|
|
2630
|
-
function
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
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
|
+
}
|
|
3226
|
+
} else {
|
|
3227
|
+
setDrillDownStack([]);
|
|
3228
|
+
const idx = snapshots.findIndex((s) => s.stageLabel === name);
|
|
3229
|
+
if (idx >= 0) setSnapshotIdx(idx);
|
|
3230
|
+
}
|
|
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
|
+
] });
|
|
3254
|
+
}
|
|
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
|
+
]
|
|
3395
|
+
}
|
|
3396
|
+
);
|
|
2648
3397
|
}
|
|
2649
3398
|
export {
|
|
2650
3399
|
ExplainableShell,
|
|
2651
3400
|
FootprintTheme,
|
|
2652
3401
|
GanttTimeline,
|
|
2653
3402
|
MemoryInspector,
|
|
3403
|
+
MemoryPanel,
|
|
2654
3404
|
NarrativeLog,
|
|
3405
|
+
NarrativePanel,
|
|
2655
3406
|
NarrativeTrace,
|
|
2656
3407
|
ResultPanel,
|
|
2657
3408
|
ScopeDiff,
|
|
2658
3409
|
SnapshotPanel,
|
|
2659
3410
|
StageDetailPanel,
|
|
3411
|
+
StoryNarrative,
|
|
2660
3412
|
SubflowTree,
|
|
2661
3413
|
TimeTravelControls,
|
|
2662
3414
|
coolDark,
|
|
3415
|
+
coolLight,
|
|
2663
3416
|
createSnapshots,
|
|
2664
3417
|
defaultTokens,
|
|
3418
|
+
rawDefaults,
|
|
3419
|
+
subflowResultToSnapshots,
|
|
2665
3420
|
themePresets,
|
|
2666
3421
|
toVisualizationSnapshots,
|
|
2667
3422
|
tokensToCSSVars,
|
|
3423
|
+
useDarkModeTokens,
|
|
2668
3424
|
useFootprintTheme,
|
|
2669
3425
|
warmDark,
|
|
2670
3426
|
warmLight
|