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.cjs
CHANGED
|
@@ -24,20 +24,27 @@ __export(src_exports, {
|
|
|
24
24
|
FootprintTheme: () => FootprintTheme,
|
|
25
25
|
GanttTimeline: () => GanttTimeline,
|
|
26
26
|
MemoryInspector: () => MemoryInspector,
|
|
27
|
+
MemoryPanel: () => MemoryPanel,
|
|
27
28
|
NarrativeLog: () => NarrativeLog,
|
|
29
|
+
NarrativePanel: () => NarrativePanel,
|
|
28
30
|
NarrativeTrace: () => NarrativeTrace,
|
|
29
31
|
ResultPanel: () => ResultPanel,
|
|
30
32
|
ScopeDiff: () => ScopeDiff,
|
|
31
33
|
SnapshotPanel: () => SnapshotPanel,
|
|
32
34
|
StageDetailPanel: () => StageDetailPanel,
|
|
35
|
+
StoryNarrative: () => StoryNarrative,
|
|
33
36
|
SubflowTree: () => SubflowTree,
|
|
34
37
|
TimeTravelControls: () => TimeTravelControls,
|
|
35
38
|
coolDark: () => coolDark,
|
|
39
|
+
coolLight: () => coolLight,
|
|
36
40
|
createSnapshots: () => createSnapshots,
|
|
37
41
|
defaultTokens: () => defaultTokens,
|
|
42
|
+
rawDefaults: () => rawDefaults,
|
|
43
|
+
subflowResultToSnapshots: () => subflowResultToSnapshots,
|
|
38
44
|
themePresets: () => themePresets,
|
|
39
45
|
toVisualizationSnapshots: () => toVisualizationSnapshots,
|
|
40
46
|
tokensToCSSVars: () => tokensToCSSVars,
|
|
47
|
+
useDarkModeTokens: () => useDarkModeTokens,
|
|
41
48
|
useFootprintTheme: () => useFootprintTheme,
|
|
42
49
|
warmDark: () => warmDark,
|
|
43
50
|
warmLight: () => warmLight
|
|
@@ -69,7 +76,7 @@ function tokensToCSSVars(tokens) {
|
|
|
69
76
|
if (tokens.fontFamily?.mono) vars["--fp-font-mono"] = tokens.fontFamily.mono;
|
|
70
77
|
return vars;
|
|
71
78
|
}
|
|
72
|
-
var
|
|
79
|
+
var rawDefaults = {
|
|
73
80
|
colors: {
|
|
74
81
|
primary: "#6366f1",
|
|
75
82
|
success: "#22c55e",
|
|
@@ -89,6 +96,26 @@ var defaultTokens = {
|
|
|
89
96
|
mono: "'JetBrains Mono', 'Fira Code', monospace"
|
|
90
97
|
}
|
|
91
98
|
};
|
|
99
|
+
var defaultTokens = {
|
|
100
|
+
colors: {
|
|
101
|
+
primary: `var(--fp-color-primary, ${rawDefaults.colors.primary})`,
|
|
102
|
+
success: `var(--fp-color-success, ${rawDefaults.colors.success})`,
|
|
103
|
+
error: `var(--fp-color-error, ${rawDefaults.colors.error})`,
|
|
104
|
+
warning: `var(--fp-color-warning, ${rawDefaults.colors.warning})`,
|
|
105
|
+
bgPrimary: `var(--fp-bg-primary, ${rawDefaults.colors.bgPrimary})`,
|
|
106
|
+
bgSecondary: `var(--fp-bg-secondary, ${rawDefaults.colors.bgSecondary})`,
|
|
107
|
+
bgTertiary: `var(--fp-bg-tertiary, ${rawDefaults.colors.bgTertiary})`,
|
|
108
|
+
textPrimary: `var(--fp-text-primary, ${rawDefaults.colors.textPrimary})`,
|
|
109
|
+
textSecondary: `var(--fp-text-secondary, ${rawDefaults.colors.textSecondary})`,
|
|
110
|
+
textMuted: `var(--fp-text-muted, ${rawDefaults.colors.textMuted})`,
|
|
111
|
+
border: `var(--fp-border, ${rawDefaults.colors.border})`
|
|
112
|
+
},
|
|
113
|
+
radius: `var(--fp-radius, ${rawDefaults.radius})`,
|
|
114
|
+
fontFamily: {
|
|
115
|
+
sans: `var(--fp-font-sans, ${rawDefaults.fontFamily.sans})`,
|
|
116
|
+
mono: `var(--fp-font-mono, ${rawDefaults.fontFamily.mono})`
|
|
117
|
+
}
|
|
118
|
+
};
|
|
92
119
|
|
|
93
120
|
// src/theme/ThemeProvider.tsx
|
|
94
121
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
@@ -193,14 +220,57 @@ var warmLight = {
|
|
|
193
220
|
mono: "'JetBrains Mono', 'Fira Code', monospace"
|
|
194
221
|
}
|
|
195
222
|
};
|
|
223
|
+
var coolLight = {
|
|
224
|
+
colors: {
|
|
225
|
+
primary: "#6366f1",
|
|
226
|
+
success: "#22c55e",
|
|
227
|
+
error: "#ef4444",
|
|
228
|
+
warning: "#f59e0b",
|
|
229
|
+
bgPrimary: "#ffffff",
|
|
230
|
+
bgSecondary: "#f9fafb",
|
|
231
|
+
bgTertiary: "#e5e7eb",
|
|
232
|
+
textPrimary: "#18181b",
|
|
233
|
+
textSecondary: "#52525b",
|
|
234
|
+
textMuted: "#a1a1aa",
|
|
235
|
+
border: "#e5e7eb"
|
|
236
|
+
},
|
|
237
|
+
radius: "8px",
|
|
238
|
+
fontFamily: {
|
|
239
|
+
sans: "Inter, system-ui, -apple-system, sans-serif",
|
|
240
|
+
mono: "'JetBrains Mono', 'Fira Code', monospace"
|
|
241
|
+
}
|
|
242
|
+
};
|
|
196
243
|
var themePresets = {
|
|
197
244
|
coolDark,
|
|
245
|
+
coolLight,
|
|
198
246
|
warmDark,
|
|
199
247
|
warmLight
|
|
200
248
|
};
|
|
201
249
|
|
|
202
|
-
// src/
|
|
250
|
+
// src/theme/useDarkModeTokens.ts
|
|
203
251
|
var import_react2 = require("react");
|
|
252
|
+
function useDarkModeTokens(options) {
|
|
253
|
+
const lightTokens = options?.light ?? coolLight;
|
|
254
|
+
const darkTokens = options?.dark ?? coolDark;
|
|
255
|
+
const [isDark, setIsDark] = (0, import_react2.useState)(
|
|
256
|
+
() => document.documentElement.classList.contains(options?.selector ?? "dark")
|
|
257
|
+
);
|
|
258
|
+
(0, import_react2.useEffect)(() => {
|
|
259
|
+
const cls = options?.selector ?? "dark";
|
|
260
|
+
const obs = new MutationObserver(() => {
|
|
261
|
+
setIsDark(document.documentElement.classList.contains(cls));
|
|
262
|
+
});
|
|
263
|
+
obs.observe(document.documentElement, {
|
|
264
|
+
attributes: true,
|
|
265
|
+
attributeFilter: ["class"]
|
|
266
|
+
});
|
|
267
|
+
return () => obs.disconnect();
|
|
268
|
+
}, [options?.selector]);
|
|
269
|
+
return isDark ? darkTokens : lightTokens;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// src/components/MemoryInspector/MemoryInspector.tsx
|
|
273
|
+
var import_react3 = require("react");
|
|
204
274
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
205
275
|
function MemoryInspector({
|
|
206
276
|
data,
|
|
@@ -213,28 +283,45 @@ function MemoryInspector({
|
|
|
213
283
|
className,
|
|
214
284
|
style
|
|
215
285
|
}) {
|
|
216
|
-
const
|
|
286
|
+
const cacheRef = (0, import_react3.useRef)(null);
|
|
287
|
+
const { memory, newKeys } = (0, import_react3.useMemo)(() => {
|
|
217
288
|
if (data) {
|
|
218
289
|
return { memory: data, newKeys: /* @__PURE__ */ new Set() };
|
|
219
290
|
}
|
|
220
291
|
if (!snapshots || snapshots.length === 0) {
|
|
221
292
|
return { memory: {}, newKeys: /* @__PURE__ */ new Set() };
|
|
222
293
|
}
|
|
223
|
-
const
|
|
224
|
-
|
|
225
|
-
|
|
294
|
+
const safeIdx = Math.min(selectedIndex, snapshots.length - 1);
|
|
295
|
+
let merged;
|
|
296
|
+
const cache = cacheRef.current;
|
|
297
|
+
if (cache && cache.snapshots === snapshots && cache.index <= safeIdx) {
|
|
298
|
+
merged = { ...cache.accumulated };
|
|
299
|
+
for (let i = cache.index + 1; i <= safeIdx; i++) {
|
|
300
|
+
Object.assign(merged, snapshots[i]?.memory);
|
|
301
|
+
}
|
|
302
|
+
} else {
|
|
303
|
+
merged = {};
|
|
304
|
+
for (let i = 0; i <= safeIdx; i++) {
|
|
305
|
+
Object.assign(merged, snapshots[i]?.memory);
|
|
306
|
+
}
|
|
226
307
|
}
|
|
308
|
+
cacheRef.current = { snapshots, index: safeIdx, accumulated: merged };
|
|
227
309
|
const nk = /* @__PURE__ */ new Set();
|
|
228
|
-
if (highlightNew &&
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
310
|
+
if (highlightNew && safeIdx > 0) {
|
|
311
|
+
let prev;
|
|
312
|
+
if (cache && cache.snapshots === snapshots && cache.index === safeIdx - 1) {
|
|
313
|
+
prev = cache.accumulated;
|
|
314
|
+
} else {
|
|
315
|
+
prev = {};
|
|
316
|
+
for (let i = 0; i < safeIdx; i++) {
|
|
317
|
+
Object.assign(prev, snapshots[i]?.memory);
|
|
318
|
+
}
|
|
232
319
|
}
|
|
233
|
-
const current = snapshots[
|
|
320
|
+
const current = snapshots[safeIdx]?.memory ?? {};
|
|
234
321
|
for (const k of Object.keys(current)) {
|
|
235
322
|
if (!(k in prev)) nk.add(k);
|
|
236
323
|
}
|
|
237
|
-
} else if (highlightNew &&
|
|
324
|
+
} else if (highlightNew && safeIdx === 0 && snapshots[0]) {
|
|
238
325
|
for (const k of Object.keys(snapshots[0].memory)) nk.add(k);
|
|
239
326
|
}
|
|
240
327
|
return { memory: merged, newKeys: nk };
|
|
@@ -243,9 +330,9 @@ function MemoryInspector({
|
|
|
243
330
|
const fs = fontSize[size];
|
|
244
331
|
const pad = padding[size];
|
|
245
332
|
if (unstyled) {
|
|
246
|
-
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className, style, "data-fp": "memory-inspector", children: [
|
|
333
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className, style, "data-fp": "memory-inspector", role: "region", "aria-label": "Memory state", children: [
|
|
247
334
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { "data-fp": "memory-label", children: "Memory State" }),
|
|
248
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("pre", { "data-fp": "memory-json", children: JSON.stringify(memory, null, 2) })
|
|
335
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("pre", { "data-fp": "memory-json", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("code", { children: JSON.stringify(memory, null, 2) }) })
|
|
249
336
|
] });
|
|
250
337
|
}
|
|
251
338
|
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
@@ -258,6 +345,8 @@ function MemoryInspector({
|
|
|
258
345
|
...style
|
|
259
346
|
},
|
|
260
347
|
"data-fp": "memory-inspector",
|
|
348
|
+
role: "region",
|
|
349
|
+
"aria-label": "Memory state",
|
|
261
350
|
children: [
|
|
262
351
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
263
352
|
"span",
|
|
@@ -357,7 +446,7 @@ function formatValue(value) {
|
|
|
357
446
|
}
|
|
358
447
|
|
|
359
448
|
// src/components/NarrativeLog/NarrativeLog.tsx
|
|
360
|
-
var
|
|
449
|
+
var import_react4 = require("react");
|
|
361
450
|
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
362
451
|
function NarrativeLog({
|
|
363
452
|
snapshots,
|
|
@@ -368,7 +457,7 @@ function NarrativeLog({
|
|
|
368
457
|
className,
|
|
369
458
|
style
|
|
370
459
|
}) {
|
|
371
|
-
const entries = (0,
|
|
460
|
+
const entries = (0, import_react4.useMemo)(() => {
|
|
372
461
|
if (narrative) {
|
|
373
462
|
return [{ label: "Output", text: narrative, isCurrent: true }];
|
|
374
463
|
}
|
|
@@ -490,7 +579,7 @@ function NarrativeLog({
|
|
|
490
579
|
}
|
|
491
580
|
|
|
492
581
|
// src/components/NarrativeTrace/NarrativeTrace.tsx
|
|
493
|
-
var
|
|
582
|
+
var import_react5 = require("react");
|
|
494
583
|
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
495
584
|
function parseGroups(lines) {
|
|
496
585
|
const groups = [];
|
|
@@ -520,17 +609,17 @@ function NarrativeTrace({
|
|
|
520
609
|
}) {
|
|
521
610
|
const revealed = revealedCount != null ? narrative.slice(0, revealedCount) : narrative;
|
|
522
611
|
const future = revealedCount != null ? narrative.slice(revealedCount) : [];
|
|
523
|
-
const revealedGroups = (0,
|
|
524
|
-
const futureGroups = (0,
|
|
525
|
-
const [collapsedSet, setCollapsedSet] = (0,
|
|
612
|
+
const revealedGroups = (0, import_react5.useMemo)(() => parseGroups(revealed), [revealed]);
|
|
613
|
+
const futureGroups = (0, import_react5.useMemo)(() => parseGroups(future), [future]);
|
|
614
|
+
const [collapsedSet, setCollapsedSet] = (0, import_react5.useState)(() => {
|
|
526
615
|
if (!defaultCollapsed) return /* @__PURE__ */ new Set();
|
|
527
616
|
return new Set(parseGroups(narrative).map((g) => g.headerIdx));
|
|
528
617
|
});
|
|
529
|
-
const latestRef = (0,
|
|
530
|
-
(0,
|
|
618
|
+
const latestRef = (0, import_react5.useRef)(null);
|
|
619
|
+
(0, import_react5.useEffect)(() => {
|
|
531
620
|
latestRef.current?.scrollIntoView({ behavior: "smooth", block: "nearest" });
|
|
532
621
|
}, [revealedGroups.length]);
|
|
533
|
-
const toggle = (0,
|
|
622
|
+
const toggle = (0, import_react5.useCallback)((idx) => {
|
|
534
623
|
setCollapsedSet((prev) => {
|
|
535
624
|
const next = new Set(prev);
|
|
536
625
|
if (next.has(idx)) next.delete(idx);
|
|
@@ -550,19 +639,32 @@ function NarrativeTrace({
|
|
|
550
639
|
"data-fp": "narrative-header",
|
|
551
640
|
"data-collapsible": group.steps.length > 0,
|
|
552
641
|
"data-collapsed": collapsedSet.has(group.headerIdx),
|
|
642
|
+
role: group.steps.length > 0 ? "button" : void 0,
|
|
643
|
+
tabIndex: group.steps.length > 0 ? 0 : void 0,
|
|
644
|
+
"aria-expanded": group.steps.length > 0 ? !collapsedSet.has(group.headerIdx) : void 0,
|
|
645
|
+
"aria-label": `Stage ${gi + 1}, ${group.steps.length} steps${gi === lastIdx ? ", current" : ""}`,
|
|
553
646
|
onClick: () => {
|
|
554
647
|
if (group.steps.length > 0) toggle(group.headerIdx);
|
|
555
648
|
onStageClick?.(group.headerIdx);
|
|
556
649
|
},
|
|
650
|
+
onKeyDown: (e) => {
|
|
651
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
652
|
+
e.preventDefault();
|
|
653
|
+
if (group.steps.length > 0) toggle(group.headerIdx);
|
|
654
|
+
onStageClick?.(group.headerIdx);
|
|
655
|
+
}
|
|
656
|
+
},
|
|
557
657
|
children: group.header
|
|
558
658
|
}
|
|
559
659
|
),
|
|
560
660
|
!collapsedSet.has(group.headerIdx) && group.steps.map((step) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { "data-fp": "narrative-step", children: step.text }, step.idx))
|
|
561
661
|
] }, group.headerIdx)),
|
|
562
|
-
futureGroups.
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
662
|
+
futureGroups.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { "data-fp": "narrative-future-hint", children: [
|
|
663
|
+
futureGroups.length,
|
|
664
|
+
" more ",
|
|
665
|
+
futureGroups.length === 1 ? "stage" : "stages",
|
|
666
|
+
" ahead..."
|
|
667
|
+
] })
|
|
566
668
|
] });
|
|
567
669
|
}
|
|
568
670
|
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
@@ -592,10 +694,21 @@ function NarrativeTrace({
|
|
|
592
694
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
593
695
|
"div",
|
|
594
696
|
{
|
|
697
|
+
role: hasSteps ? "button" : void 0,
|
|
698
|
+
tabIndex: hasSteps ? 0 : void 0,
|
|
699
|
+
"aria-expanded": hasSteps ? !isCollapsed : void 0,
|
|
700
|
+
"aria-label": `Stage ${gi + 1}, ${group.steps.length} steps${isLatest ? ", current" : ", completed"}`,
|
|
595
701
|
onClick: () => {
|
|
596
702
|
if (hasSteps) toggle(group.headerIdx);
|
|
597
703
|
onStageClick?.(group.headerIdx);
|
|
598
704
|
},
|
|
705
|
+
onKeyDown: (e) => {
|
|
706
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
707
|
+
e.preventDefault();
|
|
708
|
+
if (hasSteps) toggle(group.headerIdx);
|
|
709
|
+
onStageClick?.(group.headerIdx);
|
|
710
|
+
}
|
|
711
|
+
},
|
|
599
712
|
style: {
|
|
600
713
|
fontSize: fs.body,
|
|
601
714
|
lineHeight: 1.7,
|
|
@@ -654,43 +767,25 @@ function NarrativeTrace({
|
|
|
654
767
|
group.headerIdx
|
|
655
768
|
);
|
|
656
769
|
}),
|
|
657
|
-
futureGroups.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
},
|
|
670
|
-
children: group.header
|
|
671
|
-
}
|
|
672
|
-
),
|
|
673
|
-
group.steps.map((step) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
674
|
-
"div",
|
|
675
|
-
{
|
|
676
|
-
style: {
|
|
677
|
-
fontSize: fs.small,
|
|
678
|
-
lineHeight: 1.6,
|
|
679
|
-
color: theme.textMuted,
|
|
680
|
-
padding: `2px ${pad - 4}px 2px ${pad + 20}px`
|
|
681
|
-
},
|
|
682
|
-
children: step.text
|
|
683
|
-
},
|
|
684
|
-
`f-${step.idx}`
|
|
685
|
-
))
|
|
686
|
-
] }, `f-${group.headerIdx}`)) })
|
|
770
|
+
futureGroups.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: {
|
|
771
|
+
opacity: 0.3,
|
|
772
|
+
fontSize: fs.small,
|
|
773
|
+
color: theme.textMuted,
|
|
774
|
+
padding: `8px ${pad}px`,
|
|
775
|
+
fontStyle: "italic"
|
|
776
|
+
}, children: [
|
|
777
|
+
futureGroups.length,
|
|
778
|
+
" more ",
|
|
779
|
+
futureGroups.length === 1 ? "stage" : "stages",
|
|
780
|
+
" ahead..."
|
|
781
|
+
] })
|
|
687
782
|
]
|
|
688
783
|
}
|
|
689
784
|
);
|
|
690
785
|
}
|
|
691
786
|
|
|
692
787
|
// src/components/GanttTimeline/GanttTimeline.tsx
|
|
693
|
-
var
|
|
788
|
+
var import_react6 = require("react");
|
|
694
789
|
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
695
790
|
function GanttTimeline({
|
|
696
791
|
snapshots,
|
|
@@ -702,10 +797,10 @@ function GanttTimeline({
|
|
|
702
797
|
style,
|
|
703
798
|
maxVisibleRows = 5
|
|
704
799
|
}) {
|
|
705
|
-
const [expanded, setExpanded] = (0,
|
|
706
|
-
const activeRowRef = (0,
|
|
707
|
-
const scrollContainerRef = (0,
|
|
708
|
-
const totalWallTime = (0,
|
|
800
|
+
const [expanded, setExpanded] = (0, import_react6.useState)(false);
|
|
801
|
+
const activeRowRef = (0, import_react6.useRef)(null);
|
|
802
|
+
const scrollContainerRef = (0, import_react6.useRef)(null);
|
|
803
|
+
const totalWallTime = (0, import_react6.useMemo)(
|
|
709
804
|
() => Math.max(...snapshots.map((s) => s.startMs + s.durationMs), 1),
|
|
710
805
|
[snapshots]
|
|
711
806
|
);
|
|
@@ -716,7 +811,7 @@ function GanttTimeline({
|
|
|
716
811
|
const rowHeight = size === "compact" ? 18 : 22;
|
|
717
812
|
const collapsible = maxVisibleRows > 0 && snapshots.length > maxVisibleRows;
|
|
718
813
|
const showAll = expanded || !collapsible;
|
|
719
|
-
(0,
|
|
814
|
+
(0, import_react6.useEffect)(() => {
|
|
720
815
|
if (!showAll && activeRowRef.current && scrollContainerRef.current) {
|
|
721
816
|
activeRowRef.current.scrollIntoView({
|
|
722
817
|
block: "nearest",
|
|
@@ -725,12 +820,15 @@ function GanttTimeline({
|
|
|
725
820
|
}
|
|
726
821
|
}, [selectedIndex, showAll]);
|
|
727
822
|
if (unstyled) {
|
|
728
|
-
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className, style, "data-fp": "gantt-timeline", children: snapshots.map((snap, idx) => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
823
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className, style, "data-fp": "gantt-timeline", role: "listbox", "aria-label": "Execution timeline", children: snapshots.map((snap, idx) => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
729
824
|
"div",
|
|
730
825
|
{
|
|
731
826
|
"data-fp": "gantt-bar",
|
|
732
827
|
"data-selected": idx === selectedIndex,
|
|
733
828
|
"data-visible": idx <= selectedIndex,
|
|
829
|
+
role: "option",
|
|
830
|
+
"aria-selected": idx === selectedIndex,
|
|
831
|
+
"aria-label": `${snap.stageLabel}, ${snap.durationMs}ms`,
|
|
734
832
|
onClick: () => onSelect?.(idx),
|
|
735
833
|
children: [
|
|
736
834
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { "data-fp": "gantt-label", children: snap.stageLabel }),
|
|
@@ -740,7 +838,7 @@ function GanttTimeline({
|
|
|
740
838
|
] })
|
|
741
839
|
]
|
|
742
840
|
},
|
|
743
|
-
snap.stageName
|
|
841
|
+
`${snap.stageName}-${idx}`
|
|
744
842
|
)) });
|
|
745
843
|
}
|
|
746
844
|
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
@@ -796,6 +894,8 @@ function GanttTimeline({
|
|
|
796
894
|
"div",
|
|
797
895
|
{
|
|
798
896
|
ref: scrollContainerRef,
|
|
897
|
+
role: "listbox",
|
|
898
|
+
"aria-label": "Execution timeline",
|
|
799
899
|
style: {
|
|
800
900
|
marginTop: 8,
|
|
801
901
|
display: "flex",
|
|
@@ -816,6 +916,9 @@ function GanttTimeline({
|
|
|
816
916
|
"div",
|
|
817
917
|
{
|
|
818
918
|
ref: isSelected ? activeRowRef : void 0,
|
|
919
|
+
role: "option",
|
|
920
|
+
"aria-selected": isSelected,
|
|
921
|
+
"aria-label": `${snap.stageLabel}, ${snap.durationMs}ms`,
|
|
819
922
|
onClick: () => onSelect?.(idx),
|
|
820
923
|
style: {
|
|
821
924
|
display: "flex",
|
|
@@ -831,6 +934,7 @@ function GanttTimeline({
|
|
|
831
934
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
832
935
|
"span",
|
|
833
936
|
{
|
|
937
|
+
title: snap.stageLabel,
|
|
834
938
|
style: {
|
|
835
939
|
width: labelWidth,
|
|
836
940
|
fontSize: fs.small,
|
|
@@ -890,7 +994,7 @@ function GanttTimeline({
|
|
|
890
994
|
)
|
|
891
995
|
]
|
|
892
996
|
},
|
|
893
|
-
snap.stageName
|
|
997
|
+
`${snap.stageName}-${idx}`
|
|
894
998
|
);
|
|
895
999
|
})
|
|
896
1000
|
}
|
|
@@ -927,7 +1031,7 @@ function GanttTimeline({
|
|
|
927
1031
|
}
|
|
928
1032
|
|
|
929
1033
|
// src/components/SnapshotPanel/SnapshotPanel.tsx
|
|
930
|
-
var
|
|
1034
|
+
var import_react7 = require("react");
|
|
931
1035
|
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
932
1036
|
function SnapshotPanel({
|
|
933
1037
|
snapshots,
|
|
@@ -939,7 +1043,7 @@ function SnapshotPanel({
|
|
|
939
1043
|
className,
|
|
940
1044
|
style
|
|
941
1045
|
}) {
|
|
942
|
-
const [selectedIndex, setSelectedIndex] = (0,
|
|
1046
|
+
const [selectedIndex, setSelectedIndex] = (0, import_react7.useState)(0);
|
|
943
1047
|
const fs = fontSize[size];
|
|
944
1048
|
const pad = padding[size];
|
|
945
1049
|
if (snapshots.length === 0) {
|
|
@@ -1182,7 +1286,7 @@ function ScrubButton({
|
|
|
1182
1286
|
}
|
|
1183
1287
|
|
|
1184
1288
|
// src/components/ScopeDiff/ScopeDiff.tsx
|
|
1185
|
-
var
|
|
1289
|
+
var import_react8 = require("react");
|
|
1186
1290
|
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
1187
1291
|
function computeDiff(prev, curr) {
|
|
1188
1292
|
const entries = [];
|
|
@@ -1212,9 +1316,9 @@ function fmt(v2) {
|
|
|
1212
1316
|
return String(v2);
|
|
1213
1317
|
}
|
|
1214
1318
|
var diffColors = {
|
|
1215
|
-
added: { bg:
|
|
1216
|
-
removed: { bg:
|
|
1217
|
-
changed: { bg:
|
|
1319
|
+
added: { bg: `color-mix(in srgb, ${theme.success} 10%, transparent)`, fg: theme.success, icon: "+" },
|
|
1320
|
+
removed: { bg: `color-mix(in srgb, ${theme.error} 10%, transparent)`, fg: theme.error, icon: "-" },
|
|
1321
|
+
changed: { bg: `color-mix(in srgb, ${theme.warning} 10%, transparent)`, fg: theme.warning, icon: "~" },
|
|
1218
1322
|
unchanged: { bg: "transparent", fg: "", icon: " " }
|
|
1219
1323
|
};
|
|
1220
1324
|
function ScopeDiff({
|
|
@@ -1226,7 +1330,7 @@ function ScopeDiff({
|
|
|
1226
1330
|
className,
|
|
1227
1331
|
style
|
|
1228
1332
|
}) {
|
|
1229
|
-
const entries = (0,
|
|
1333
|
+
const entries = (0, import_react8.useMemo)(() => computeDiff(previous, current), [previous, current]);
|
|
1230
1334
|
const visible = hideUnchanged ? entries.filter((e) => e.type !== "unchanged") : entries;
|
|
1231
1335
|
const fs = fontSize[size];
|
|
1232
1336
|
const pad = padding[size];
|
|
@@ -1430,7 +1534,7 @@ function ResultPanel({
|
|
|
1430
1534
|
}
|
|
1431
1535
|
|
|
1432
1536
|
// src/components/StageDetailPanel/StageDetailPanel.tsx
|
|
1433
|
-
var
|
|
1537
|
+
var import_react9 = require("react");
|
|
1434
1538
|
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
1435
1539
|
function computeChanges(prev, curr) {
|
|
1436
1540
|
const changes = [];
|
|
@@ -1584,7 +1688,7 @@ function DevView({
|
|
|
1584
1688
|
fs,
|
|
1585
1689
|
pad
|
|
1586
1690
|
}) {
|
|
1587
|
-
const rows = (0,
|
|
1691
|
+
const rows = (0, import_react9.useMemo)(() => buildMemoryRows(currMemory, changes), [currMemory, changes]);
|
|
1588
1692
|
const totalKeys = Object.keys(currMemory).length;
|
|
1589
1693
|
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 12 }, children: [
|
|
1590
1694
|
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 8 }, children: [
|
|
@@ -1786,7 +1890,7 @@ function UnstyledDevView({
|
|
|
1786
1890
|
changes,
|
|
1787
1891
|
currMemory
|
|
1788
1892
|
}) {
|
|
1789
|
-
const rows = (0,
|
|
1893
|
+
const rows = (0, import_react9.useMemo)(() => buildMemoryRows(currMemory, changes), [currMemory, changes]);
|
|
1790
1894
|
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { "data-fp": "stage-detail-dev", children: [
|
|
1791
1895
|
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { "data-fp": "stage-label", children: snapshot.stageLabel }),
|
|
1792
1896
|
rows.map((row) => {
|
|
@@ -1862,9 +1966,9 @@ function StageDetailPanel({
|
|
|
1862
1966
|
className,
|
|
1863
1967
|
style
|
|
1864
1968
|
}) {
|
|
1865
|
-
const [internalMode, setInternalMode] = (0,
|
|
1969
|
+
const [internalMode, setInternalMode] = (0, import_react9.useState)(controlledMode ?? "simple");
|
|
1866
1970
|
const activeMode = controlledMode ?? internalMode;
|
|
1867
|
-
const handleToggle = (0,
|
|
1971
|
+
const handleToggle = (0, import_react9.useCallback)(() => {
|
|
1868
1972
|
const next = activeMode === "simple" ? "dev" : "simple";
|
|
1869
1973
|
setInternalMode(next);
|
|
1870
1974
|
onModeChange?.(next);
|
|
@@ -1872,7 +1976,7 @@ function StageDetailPanel({
|
|
|
1872
1976
|
const snapshot = snapshots[selectedIndex];
|
|
1873
1977
|
const prevMemory = selectedIndex > 0 ? snapshots[selectedIndex - 1]?.memory ?? null : null;
|
|
1874
1978
|
const currMemory = snapshot?.memory ?? {};
|
|
1875
|
-
const changes = (0,
|
|
1979
|
+
const changes = (0, import_react9.useMemo)(
|
|
1876
1980
|
() => computeChanges(prevMemory, currMemory),
|
|
1877
1981
|
[prevMemory, currMemory]
|
|
1878
1982
|
);
|
|
@@ -1908,7 +2012,7 @@ function StageDetailPanel({
|
|
|
1908
2012
|
}
|
|
1909
2013
|
|
|
1910
2014
|
// src/components/TimeTravelControls/TimeTravelControls.tsx
|
|
1911
|
-
var
|
|
2015
|
+
var import_react10 = require("react");
|
|
1912
2016
|
var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
1913
2017
|
function TimeTravelControls({
|
|
1914
2018
|
snapshots,
|
|
@@ -1920,12 +2024,12 @@ function TimeTravelControls({
|
|
|
1920
2024
|
className,
|
|
1921
2025
|
style
|
|
1922
2026
|
}) {
|
|
1923
|
-
const [playing, setPlaying] = (0,
|
|
1924
|
-
const playRef = (0,
|
|
2027
|
+
const [playing, setPlaying] = (0, import_react10.useState)(false);
|
|
2028
|
+
const playRef = (0, import_react10.useRef)(null);
|
|
1925
2029
|
const total = snapshots.length;
|
|
1926
2030
|
const canPrev = selectedIndex > 0;
|
|
1927
2031
|
const canNext = selectedIndex < total - 1;
|
|
1928
|
-
(0,
|
|
2032
|
+
(0, import_react10.useEffect)(() => {
|
|
1929
2033
|
if (!playing || !autoPlayable) return;
|
|
1930
2034
|
if (selectedIndex >= total - 1) {
|
|
1931
2035
|
setPlaying(false);
|
|
@@ -1943,7 +2047,7 @@ function TimeTravelControls({
|
|
|
1943
2047
|
if (playRef.current) clearTimeout(playRef.current);
|
|
1944
2048
|
};
|
|
1945
2049
|
}, [playing, selectedIndex, snapshots, total, onIndexChange, autoPlayable]);
|
|
1946
|
-
const togglePlay = (0,
|
|
2050
|
+
const togglePlay = (0, import_react10.useCallback)(() => {
|
|
1947
2051
|
if (playing) {
|
|
1948
2052
|
setPlaying(false);
|
|
1949
2053
|
} else {
|
|
@@ -1951,49 +2055,80 @@ function TimeTravelControls({
|
|
|
1951
2055
|
setPlaying(true);
|
|
1952
2056
|
}
|
|
1953
2057
|
}, [playing, selectedIndex, total, onIndexChange]);
|
|
2058
|
+
const handleKeyDown = (0, import_react10.useCallback)(
|
|
2059
|
+
(e) => {
|
|
2060
|
+
if (e.key === "ArrowLeft" && canPrev && !playing) {
|
|
2061
|
+
e.preventDefault();
|
|
2062
|
+
setPlaying(false);
|
|
2063
|
+
onIndexChange(selectedIndex - 1);
|
|
2064
|
+
} else if (e.key === "ArrowRight" && canNext && !playing) {
|
|
2065
|
+
e.preventDefault();
|
|
2066
|
+
setPlaying(false);
|
|
2067
|
+
onIndexChange(selectedIndex + 1);
|
|
2068
|
+
} else if (e.key === " " && autoPlayable) {
|
|
2069
|
+
e.preventDefault();
|
|
2070
|
+
togglePlay();
|
|
2071
|
+
}
|
|
2072
|
+
},
|
|
2073
|
+
[canPrev, canNext, playing, selectedIndex, onIndexChange, autoPlayable, togglePlay]
|
|
2074
|
+
);
|
|
1954
2075
|
const fs = fontSize[size];
|
|
1955
2076
|
if (unstyled) {
|
|
1956
|
-
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
|
|
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
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
2077
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
|
|
2078
|
+
"div",
|
|
2079
|
+
{
|
|
2080
|
+
className,
|
|
2081
|
+
style,
|
|
2082
|
+
"data-fp": "time-travel-controls",
|
|
2083
|
+
role: "toolbar",
|
|
2084
|
+
"aria-label": "Time travel controls",
|
|
2085
|
+
tabIndex: 0,
|
|
2086
|
+
onKeyDown: handleKeyDown,
|
|
2087
|
+
children: [
|
|
2088
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
2089
|
+
"button",
|
|
2090
|
+
{
|
|
2091
|
+
"data-fp": "tt-prev",
|
|
2092
|
+
disabled: !canPrev || playing,
|
|
2093
|
+
onClick: () => {
|
|
2094
|
+
setPlaying(false);
|
|
2095
|
+
onIndexChange(selectedIndex - 1);
|
|
2096
|
+
},
|
|
2097
|
+
"aria-label": "Previous stage",
|
|
2098
|
+
children: "Prev"
|
|
2099
|
+
}
|
|
2100
|
+
),
|
|
2101
|
+
autoPlayable && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { "data-fp": "tt-play", onClick: togglePlay, "aria-label": playing ? "Pause" : "Play", children: playing ? "Pause" : "Play" }),
|
|
2102
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
2103
|
+
"button",
|
|
2104
|
+
{
|
|
2105
|
+
"data-fp": "tt-next",
|
|
2106
|
+
disabled: !canNext || playing,
|
|
2107
|
+
onClick: () => {
|
|
2108
|
+
setPlaying(false);
|
|
2109
|
+
onIndexChange(selectedIndex + 1);
|
|
2110
|
+
},
|
|
2111
|
+
"aria-label": "Next stage",
|
|
2112
|
+
children: "Next"
|
|
2113
|
+
}
|
|
2114
|
+
),
|
|
2115
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { "data-fp": "tt-ticks", children: snapshots.map((snap, i) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
2116
|
+
"button",
|
|
2117
|
+
{
|
|
2118
|
+
"data-fp": "tt-tick",
|
|
2119
|
+
"data-active": i === selectedIndex,
|
|
2120
|
+
"data-done": i < selectedIndex,
|
|
2121
|
+
onClick: () => {
|
|
2122
|
+
setPlaying(false);
|
|
2123
|
+
onIndexChange(i);
|
|
2124
|
+
},
|
|
2125
|
+
title: snap.stageLabel
|
|
2126
|
+
},
|
|
2127
|
+
i
|
|
2128
|
+
)) })
|
|
2129
|
+
]
|
|
2130
|
+
}
|
|
2131
|
+
);
|
|
1997
2132
|
}
|
|
1998
2133
|
const btnStyle = (disabled) => ({
|
|
1999
2134
|
background: theme.bgTertiary,
|
|
@@ -2022,6 +2157,10 @@ function TimeTravelControls({
|
|
|
2022
2157
|
...style
|
|
2023
2158
|
},
|
|
2024
2159
|
"data-fp": "time-travel-controls",
|
|
2160
|
+
role: "toolbar",
|
|
2161
|
+
"aria-label": "Time travel controls",
|
|
2162
|
+
tabIndex: 0,
|
|
2163
|
+
onKeyDown: handleKeyDown,
|
|
2025
2164
|
children: [
|
|
2026
2165
|
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
2027
2166
|
"button",
|
|
@@ -2032,6 +2171,7 @@ function TimeTravelControls({
|
|
|
2032
2171
|
setPlaying(false);
|
|
2033
2172
|
onIndexChange(selectedIndex - 1);
|
|
2034
2173
|
},
|
|
2174
|
+
"aria-label": "Previous stage",
|
|
2035
2175
|
children: "\u25C0"
|
|
2036
2176
|
}
|
|
2037
2177
|
),
|
|
@@ -2054,6 +2194,7 @@ function TimeTravelControls({
|
|
|
2054
2194
|
flexShrink: 0
|
|
2055
2195
|
},
|
|
2056
2196
|
title: playing ? "Pause" : "Play",
|
|
2197
|
+
"aria-label": playing ? "Pause" : "Play",
|
|
2057
2198
|
children: playing ? "\u23F8" : "\u25B6"
|
|
2058
2199
|
}
|
|
2059
2200
|
),
|
|
@@ -2066,6 +2207,7 @@ function TimeTravelControls({
|
|
|
2066
2207
|
setPlaying(false);
|
|
2067
2208
|
onIndexChange(selectedIndex + 1);
|
|
2068
2209
|
},
|
|
2210
|
+
"aria-label": "Next stage",
|
|
2069
2211
|
children: "\u25B6"
|
|
2070
2212
|
}
|
|
2071
2213
|
),
|
|
@@ -2112,125 +2254,146 @@ function TimeTravelControls({
|
|
|
2112
2254
|
}
|
|
2113
2255
|
|
|
2114
2256
|
// src/components/ExplainableShell/ExplainableShell.tsx
|
|
2115
|
-
var
|
|
2257
|
+
var import_react15 = require("react");
|
|
2258
|
+
|
|
2259
|
+
// src/adapters/fromRuntimeSnapshot.ts
|
|
2260
|
+
function toVisualizationSnapshots(runtime, narrativeEntries) {
|
|
2261
|
+
const stageNarrativeMap = narrativeEntries?.length ? buildStageNarrativeMap(narrativeEntries) : /* @__PURE__ */ new Map();
|
|
2262
|
+
const snapshots = [];
|
|
2263
|
+
flattenTree(runtime.executionTree, snapshots, runtime.sharedState, 0, runtime.subflowResults, {}, stageNarrativeMap);
|
|
2264
|
+
return snapshots;
|
|
2265
|
+
}
|
|
2266
|
+
function buildStageNarrativeMap(entries) {
|
|
2267
|
+
const map = /* @__PURE__ */ new Map();
|
|
2268
|
+
let currentStageName;
|
|
2269
|
+
for (const entry of entries) {
|
|
2270
|
+
if (entry.stageName) {
|
|
2271
|
+
currentStageName = entry.stageName;
|
|
2272
|
+
}
|
|
2273
|
+
if (currentStageName) {
|
|
2274
|
+
if (!map.has(currentStageName)) {
|
|
2275
|
+
map.set(currentStageName, []);
|
|
2276
|
+
}
|
|
2277
|
+
const indent = " ".repeat(entry.depth);
|
|
2278
|
+
map.get(currentStageName).push(`${indent}${entry.text}`);
|
|
2279
|
+
}
|
|
2280
|
+
}
|
|
2281
|
+
return map;
|
|
2282
|
+
}
|
|
2283
|
+
function flattenTree(node, out, sharedState, accumulatedMs = 0, subflowResults, cumulativeMemory = {}, stageNarrativeMap = /* @__PURE__ */ new Map()) {
|
|
2284
|
+
const durationMs = typeof node.metrics?.durationMs === "number" ? node.metrics.durationMs : 1;
|
|
2285
|
+
const startMs = accumulatedMs;
|
|
2286
|
+
const stageId = node.id || node.name || "unknown";
|
|
2287
|
+
const displayName = node.name || node.id || "unknown";
|
|
2288
|
+
const stageLines = stageNarrativeMap.get(stageId);
|
|
2289
|
+
let narrative;
|
|
2290
|
+
if (stageLines) {
|
|
2291
|
+
narrative = stageLines.join("\n");
|
|
2292
|
+
} else {
|
|
2293
|
+
const parts = [`${displayName} executed.`];
|
|
2294
|
+
if (node.description) parts.push(node.description);
|
|
2295
|
+
if (node.stageWrites) {
|
|
2296
|
+
const keys = Object.keys(node.stageWrites);
|
|
2297
|
+
if (keys.length > 0) parts.push(`Wrote: ${keys.join(", ")}`);
|
|
2298
|
+
}
|
|
2299
|
+
narrative = parts.join("\n");
|
|
2300
|
+
}
|
|
2301
|
+
const memory = { ...cumulativeMemory };
|
|
2302
|
+
if (node.stageWrites) {
|
|
2303
|
+
for (const [key, value] of Object.entries(node.stageWrites)) {
|
|
2304
|
+
if (value === void 0) {
|
|
2305
|
+
delete memory[key];
|
|
2306
|
+
} else {
|
|
2307
|
+
memory[key] = value;
|
|
2308
|
+
}
|
|
2309
|
+
}
|
|
2310
|
+
}
|
|
2311
|
+
const sfResult = subflowResults?.[node.subflowId ?? stageId];
|
|
2312
|
+
out.push({
|
|
2313
|
+
stageName: displayName,
|
|
2314
|
+
stageLabel: stageId,
|
|
2315
|
+
memory,
|
|
2316
|
+
narrative,
|
|
2317
|
+
startMs,
|
|
2318
|
+
durationMs,
|
|
2319
|
+
status: "done",
|
|
2320
|
+
...node.description ? { description: node.description } : void 0,
|
|
2321
|
+
...node.subflowId ? { subflowId: node.subflowId } : void 0,
|
|
2322
|
+
...sfResult ? { subflowResult: sfResult } : void 0
|
|
2323
|
+
});
|
|
2324
|
+
let nextMs = startMs + durationMs;
|
|
2325
|
+
if (node.children && node.children.length > 0) {
|
|
2326
|
+
let maxChildEnd = nextMs;
|
|
2327
|
+
for (const child of node.children) {
|
|
2328
|
+
const childEnd = flattenTree(child, out, sharedState, nextMs, subflowResults, memory, stageNarrativeMap);
|
|
2329
|
+
maxChildEnd = Math.max(maxChildEnd, childEnd);
|
|
2330
|
+
}
|
|
2331
|
+
nextMs = maxChildEnd;
|
|
2332
|
+
}
|
|
2333
|
+
if (node.next) {
|
|
2334
|
+
nextMs = flattenTree(node.next, out, sharedState, nextMs, subflowResults, memory, stageNarrativeMap);
|
|
2335
|
+
}
|
|
2336
|
+
return nextMs;
|
|
2337
|
+
}
|
|
2338
|
+
function subflowResultToSnapshots(subflowResult, narrativeEntries) {
|
|
2339
|
+
if (!subflowResult || typeof subflowResult !== "object") return [];
|
|
2340
|
+
const sf = subflowResult;
|
|
2341
|
+
if (!sf.treeContext?.stageContexts) return [];
|
|
2342
|
+
const runtime = {
|
|
2343
|
+
sharedState: sf.treeContext.globalContext ?? {},
|
|
2344
|
+
executionTree: sf.treeContext.stageContexts,
|
|
2345
|
+
commitLog: sf.treeContext.history ?? []
|
|
2346
|
+
};
|
|
2347
|
+
const snapshots = toVisualizationSnapshots(runtime, narrativeEntries);
|
|
2348
|
+
const prefix = sf.subflowId ? `${sf.subflowId}/` : "";
|
|
2349
|
+
if (prefix) {
|
|
2350
|
+
for (const snap of snapshots) {
|
|
2351
|
+
if (snap.stageName.startsWith(prefix)) {
|
|
2352
|
+
snap.stageName = snap.stageName.slice(prefix.length);
|
|
2353
|
+
}
|
|
2354
|
+
if (snap.stageLabel.startsWith(prefix)) {
|
|
2355
|
+
snap.stageLabel = snap.stageLabel.slice(prefix.length);
|
|
2356
|
+
}
|
|
2357
|
+
}
|
|
2358
|
+
}
|
|
2359
|
+
return snapshots;
|
|
2360
|
+
}
|
|
2361
|
+
function createSnapshots(stages) {
|
|
2362
|
+
let accMs = 0;
|
|
2363
|
+
return stages.map((s) => {
|
|
2364
|
+
const duration = s.durationMs ?? 1;
|
|
2365
|
+
const snap = {
|
|
2366
|
+
stageName: s.name,
|
|
2367
|
+
stageLabel: s.label ?? s.name,
|
|
2368
|
+
memory: s.memory ?? {},
|
|
2369
|
+
narrative: s.narrative ?? `${s.label ?? s.name} completed.`,
|
|
2370
|
+
startMs: accMs,
|
|
2371
|
+
durationMs: duration,
|
|
2372
|
+
status: "done",
|
|
2373
|
+
...s.description ? { description: s.description } : void 0,
|
|
2374
|
+
...s.subflowId ? { subflowId: s.subflowId } : void 0
|
|
2375
|
+
};
|
|
2376
|
+
accMs += duration;
|
|
2377
|
+
return snap;
|
|
2378
|
+
});
|
|
2379
|
+
}
|
|
2380
|
+
|
|
2381
|
+
// src/components/MemoryPanel/MemoryPanel.tsx
|
|
2116
2382
|
var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
2117
|
-
function
|
|
2383
|
+
function MemoryPanel({
|
|
2118
2384
|
snapshots,
|
|
2119
|
-
|
|
2120
|
-
logs = [],
|
|
2121
|
-
narrative = [],
|
|
2122
|
-
tabs = ["result", "explainable", "ai-compatible"],
|
|
2123
|
-
defaultTab,
|
|
2124
|
-
hideConsole = false,
|
|
2125
|
-
renderFlowchart,
|
|
2385
|
+
selectedIndex,
|
|
2126
2386
|
size = "default",
|
|
2127
2387
|
unstyled = false,
|
|
2128
2388
|
className,
|
|
2129
2389
|
style
|
|
2130
2390
|
}) {
|
|
2131
|
-
const
|
|
2132
|
-
const [
|
|
2133
|
-
const fs = fontSize[size];
|
|
2134
|
-
const pad = padding[size];
|
|
2135
|
-
const handleSnapshotChange = (0, import_react10.useCallback)((idx) => {
|
|
2136
|
-
setSnapshotIdx(Math.max(0, Math.min(idx, snapshots.length - 1)));
|
|
2137
|
-
}, [snapshots.length]);
|
|
2138
|
-
const revealedCount = (0, import_react10.useMemo)(() => {
|
|
2139
|
-
if (snapshots.length === 0 || narrative.length === 0) return narrative.length;
|
|
2140
|
-
const boundaries = [];
|
|
2141
|
-
for (let i = 0; i < narrative.length; i++) {
|
|
2142
|
-
const trimmed = narrative[i].trimStart();
|
|
2143
|
-
if (trimmed.startsWith("Stage ") && !trimmed.match(/^Stage\s+\d+:\s*Step\s/) || trimmed.startsWith("[")) {
|
|
2144
|
-
boundaries.push(i);
|
|
2145
|
-
}
|
|
2146
|
-
}
|
|
2147
|
-
if (boundaries.length === 0) {
|
|
2148
|
-
const ratio = (snapshotIdx + 1) / snapshots.length;
|
|
2149
|
-
return Math.max(1, Math.ceil(narrative.length * ratio));
|
|
2150
|
-
}
|
|
2151
|
-
const groupsToShow = Math.max(
|
|
2152
|
-
1,
|
|
2153
|
-
Math.min(
|
|
2154
|
-
Math.floor((snapshotIdx + 1) / snapshots.length * boundaries.length) || 1,
|
|
2155
|
-
boundaries.length
|
|
2156
|
-
)
|
|
2157
|
-
);
|
|
2158
|
-
const endIdx = groupsToShow < boundaries.length ? boundaries[groupsToShow] : narrative.length;
|
|
2159
|
-
return Math.max(1, endIdx);
|
|
2160
|
-
}, [snapshots.length, snapshotIdx, narrative]);
|
|
2161
|
-
const prevMemory = snapshotIdx > 0 ? snapshots[snapshotIdx - 1]?.memory : null;
|
|
2162
|
-
const currMemory = snapshots[snapshotIdx]?.memory ?? {};
|
|
2163
|
-
const tabLabels = {
|
|
2164
|
-
result: "Result",
|
|
2165
|
-
explainable: "Explainable",
|
|
2166
|
-
"ai-compatible": "AI-Compatible"
|
|
2167
|
-
};
|
|
2391
|
+
const prevMemory = selectedIndex > 0 ? snapshots[selectedIndex - 1]?.memory ?? null : null;
|
|
2392
|
+
const currMemory = snapshots[selectedIndex]?.memory ?? {};
|
|
2168
2393
|
if (unstyled) {
|
|
2169
|
-
return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className, style, "data-fp": "
|
|
2170
|
-
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
2171
|
-
|
|
2172
|
-
{
|
|
2173
|
-
"data-fp": "shell-tab",
|
|
2174
|
-
"data-active": tab === activeTab,
|
|
2175
|
-
onClick: () => setActiveTab(tab),
|
|
2176
|
-
children: tabLabels[tab]
|
|
2177
|
-
},
|
|
2178
|
-
tab
|
|
2179
|
-
)) }),
|
|
2180
|
-
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { "data-fp": "shell-content", "data-tab": activeTab, children: [
|
|
2181
|
-
activeTab === "result" && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
2182
|
-
ResultPanel,
|
|
2183
|
-
{
|
|
2184
|
-
data: resultData ?? null,
|
|
2185
|
-
logs,
|
|
2186
|
-
hideConsole,
|
|
2187
|
-
unstyled: true
|
|
2188
|
-
}
|
|
2189
|
-
),
|
|
2190
|
-
activeTab === "explainable" && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
|
|
2191
|
-
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
2192
|
-
TimeTravelControls,
|
|
2193
|
-
{
|
|
2194
|
-
snapshots,
|
|
2195
|
-
selectedIndex: snapshotIdx,
|
|
2196
|
-
onIndexChange: handleSnapshotChange,
|
|
2197
|
-
unstyled: true
|
|
2198
|
-
}
|
|
2199
|
-
),
|
|
2200
|
-
renderFlowchart?.({ snapshots, selectedIndex: snapshotIdx, onNodeClick: handleSnapshotChange }),
|
|
2201
|
-
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(MemoryInspector, { snapshots, selectedIndex: snapshotIdx, unstyled: true }),
|
|
2202
|
-
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ScopeDiff, { previous: prevMemory, current: currMemory, unstyled: true }),
|
|
2203
|
-
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
2204
|
-
GanttTimeline,
|
|
2205
|
-
{
|
|
2206
|
-
snapshots,
|
|
2207
|
-
selectedIndex: snapshotIdx,
|
|
2208
|
-
onSelect: handleSnapshotChange,
|
|
2209
|
-
unstyled: true
|
|
2210
|
-
}
|
|
2211
|
-
)
|
|
2212
|
-
] }),
|
|
2213
|
-
activeTab === "ai-compatible" && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
|
|
2214
|
-
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
2215
|
-
TimeTravelControls,
|
|
2216
|
-
{
|
|
2217
|
-
snapshots,
|
|
2218
|
-
selectedIndex: snapshotIdx,
|
|
2219
|
-
onIndexChange: handleSnapshotChange,
|
|
2220
|
-
unstyled: true
|
|
2221
|
-
}
|
|
2222
|
-
),
|
|
2223
|
-
renderFlowchart?.({ snapshots, selectedIndex: snapshotIdx, onNodeClick: handleSnapshotChange }),
|
|
2224
|
-
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
2225
|
-
NarrativeTrace,
|
|
2226
|
-
{
|
|
2227
|
-
narrative,
|
|
2228
|
-
revealedCount,
|
|
2229
|
-
unstyled: true
|
|
2230
|
-
}
|
|
2231
|
-
)
|
|
2232
|
-
] })
|
|
2233
|
-
] })
|
|
2394
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className, style, "data-fp": "memory-panel", children: [
|
|
2395
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(MemoryInspector, { snapshots, selectedIndex, unstyled: true }),
|
|
2396
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ScopeDiff, { previous: prevMemory, current: currMemory, unstyled: true })
|
|
2234
2397
|
] });
|
|
2235
2398
|
}
|
|
2236
2399
|
return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
|
|
@@ -2238,156 +2401,251 @@ function ExplainableShell({
|
|
|
2238
2401
|
{
|
|
2239
2402
|
className,
|
|
2240
2403
|
style: {
|
|
2241
|
-
|
|
2404
|
+
overflow: "auto",
|
|
2242
2405
|
display: "flex",
|
|
2243
2406
|
flexDirection: "column",
|
|
2244
|
-
overflow: "hidden",
|
|
2245
|
-
background: theme.bgPrimary,
|
|
2246
|
-
color: theme.textPrimary,
|
|
2247
|
-
fontFamily: theme.fontSans,
|
|
2248
2407
|
...style
|
|
2249
2408
|
},
|
|
2250
|
-
"data-fp": "
|
|
2409
|
+
"data-fp": "memory-panel",
|
|
2251
2410
|
children: [
|
|
2252
|
-
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
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
|
-
|
|
2411
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(MemoryInspector, { snapshots, selectedIndex, size }),
|
|
2412
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { style: { borderTop: `1px solid ${theme.border}` }, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ScopeDiff, { previous: prevMemory, current: currMemory, hideUnchanged: true, size }) })
|
|
2413
|
+
]
|
|
2414
|
+
}
|
|
2415
|
+
);
|
|
2416
|
+
}
|
|
2417
|
+
|
|
2418
|
+
// src/components/NarrativePanel/NarrativePanel.tsx
|
|
2419
|
+
var import_react12 = require("react");
|
|
2420
|
+
|
|
2421
|
+
// src/components/StoryNarrative/StoryNarrative.tsx
|
|
2422
|
+
var import_react11 = require("react");
|
|
2423
|
+
var import_jsx_runtime12 = require("react/jsx-runtime");
|
|
2424
|
+
var ENTRY_ICONS = {
|
|
2425
|
+
stage: { icon: "\u25B8", color: theme.primary, label: "Stage" },
|
|
2426
|
+
step: { icon: "\xB7", color: theme.textMuted, label: "Data operation" },
|
|
2427
|
+
condition: { icon: "\u25C7", color: theme.warning, label: "Decision" },
|
|
2428
|
+
fork: { icon: "\u2443", color: theme.primary, label: "Parallel" },
|
|
2429
|
+
subflow: { icon: "\u21B3", color: theme.textSecondary, label: "Subflow" },
|
|
2430
|
+
loop: { icon: "\u21BB", color: theme.warning, label: "Loop" },
|
|
2431
|
+
break: { icon: "\u25A0", color: theme.error, label: "Break" },
|
|
2432
|
+
error: { icon: "\u2717", color: theme.error, label: "Error" }
|
|
2433
|
+
};
|
|
2434
|
+
function StoryNarrative({
|
|
2435
|
+
entries,
|
|
2436
|
+
stageCount,
|
|
2437
|
+
size = "default",
|
|
2438
|
+
unstyled = false,
|
|
2439
|
+
className,
|
|
2440
|
+
style: outerStyle
|
|
2441
|
+
}) {
|
|
2442
|
+
const fs = fontSize[size];
|
|
2443
|
+
const pad = padding[size];
|
|
2444
|
+
const revealedCount = (0, import_react11.useMemo)(() => {
|
|
2445
|
+
let stagesSeen = 0;
|
|
2446
|
+
for (let i = 0; i < entries.length; i++) {
|
|
2447
|
+
const e = entries[i];
|
|
2448
|
+
const isBoundary = e.type === "stage" || e.type === "subflow" && e.text.startsWith("Entering");
|
|
2449
|
+
if (isBoundary) stagesSeen++;
|
|
2450
|
+
if (stagesSeen > stageCount) return i;
|
|
2451
|
+
}
|
|
2452
|
+
return entries.length;
|
|
2453
|
+
}, [entries, stageCount]);
|
|
2454
|
+
const revealed = entries.slice(0, revealedCount);
|
|
2455
|
+
const future = entries.slice(revealedCount);
|
|
2456
|
+
const latestRef = (0, import_react11.useRef)(null);
|
|
2457
|
+
(0, import_react11.useEffect)(() => {
|
|
2458
|
+
latestRef.current?.scrollIntoView({ behavior: "smooth", block: "nearest" });
|
|
2459
|
+
}, [revealed.length]);
|
|
2460
|
+
if (unstyled) {
|
|
2461
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className, style: outerStyle, "data-fp": "story-narrative", role: "log", children: revealed.map((entry, i) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { "data-fp": "narrative-entry", "data-type": entry.type, children: entry.text }, i)) });
|
|
2462
|
+
}
|
|
2463
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
|
|
2464
|
+
"div",
|
|
2465
|
+
{
|
|
2466
|
+
className,
|
|
2467
|
+
style: {
|
|
2468
|
+
flex: 1,
|
|
2469
|
+
overflow: "auto",
|
|
2470
|
+
padding: pad,
|
|
2471
|
+
fontFamily: theme.fontSans,
|
|
2472
|
+
...outerStyle
|
|
2473
|
+
},
|
|
2474
|
+
"data-fp": "story-narrative",
|
|
2475
|
+
role: "log",
|
|
2476
|
+
"aria-label": "Execution narrative",
|
|
2477
|
+
children: [
|
|
2478
|
+
revealed.map((entry, i) => {
|
|
2479
|
+
const meta = ENTRY_ICONS[entry.type] ?? ENTRY_ICONS.step;
|
|
2480
|
+
const isStage = entry.type === "stage";
|
|
2481
|
+
const isDecision = entry.type === "condition";
|
|
2482
|
+
const isError = entry.type === "error";
|
|
2483
|
+
const isLast = i === revealed.length - 1;
|
|
2484
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
|
|
2485
|
+
"div",
|
|
2291
2486
|
{
|
|
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
|
-
overflow: "auto",
|
|
2317
|
-
display: "flex",
|
|
2318
|
-
flexDirection: "column"
|
|
2319
|
-
},
|
|
2320
|
-
children: [
|
|
2321
|
-
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
2322
|
-
MemoryInspector,
|
|
2323
|
-
{
|
|
2324
|
-
snapshots,
|
|
2325
|
-
selectedIndex: snapshotIdx,
|
|
2326
|
-
size
|
|
2327
|
-
}
|
|
2328
|
-
),
|
|
2329
|
-
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { style: { borderTop: `1px solid ${theme.border}` }, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
2330
|
-
ScopeDiff,
|
|
2331
|
-
{
|
|
2332
|
-
previous: prevMemory,
|
|
2333
|
-
current: currMemory,
|
|
2334
|
-
hideUnchanged: true,
|
|
2335
|
-
size
|
|
2336
|
-
}
|
|
2337
|
-
) })
|
|
2338
|
-
]
|
|
2339
|
-
}
|
|
2340
|
-
)
|
|
2341
|
-
] }),
|
|
2342
|
-
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { style: { borderTop: `1px solid ${theme.border}`, flexShrink: 0 }, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
2343
|
-
GanttTimeline,
|
|
2344
|
-
{
|
|
2345
|
-
snapshots,
|
|
2346
|
-
selectedIndex: snapshotIdx,
|
|
2347
|
-
onSelect: handleSnapshotChange,
|
|
2348
|
-
size
|
|
2349
|
-
}
|
|
2350
|
-
) })
|
|
2351
|
-
] }),
|
|
2352
|
-
activeTab === "ai-compatible" && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
|
|
2353
|
-
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
2354
|
-
TimeTravelControls,
|
|
2355
|
-
{
|
|
2356
|
-
snapshots,
|
|
2357
|
-
selectedIndex: snapshotIdx,
|
|
2358
|
-
onIndexChange: handleSnapshotChange,
|
|
2359
|
-
size
|
|
2360
|
-
}
|
|
2361
|
-
),
|
|
2362
|
-
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { style: { flex: 1, display: "flex", overflow: "hidden" }, children: [
|
|
2363
|
-
renderFlowchart && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { style: { flex: 1, overflow: "hidden", borderRight: `1px solid ${theme.border}` }, children: renderFlowchart({ snapshots, selectedIndex: snapshotIdx, onNodeClick: handleSnapshotChange }) }),
|
|
2364
|
-
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
2365
|
-
NarrativeTrace,
|
|
2366
|
-
{
|
|
2367
|
-
narrative,
|
|
2368
|
-
revealedCount,
|
|
2369
|
-
size,
|
|
2370
|
-
style: {
|
|
2371
|
-
width: renderFlowchart ? "40%" : "100%",
|
|
2372
|
-
minWidth: 280
|
|
2487
|
+
ref: isLast ? latestRef : void 0,
|
|
2488
|
+
style: {
|
|
2489
|
+
display: "flex",
|
|
2490
|
+
gap: 8,
|
|
2491
|
+
padding: isStage ? `${pad - 4}px 0` : `2px 0`,
|
|
2492
|
+
marginLeft: entry.depth * 16,
|
|
2493
|
+
borderBottom: isStage ? `1px solid ${theme.border}` : void 0,
|
|
2494
|
+
marginTop: isStage && i > 0 ? 8 : 0
|
|
2495
|
+
},
|
|
2496
|
+
children: [
|
|
2497
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
2498
|
+
"span",
|
|
2499
|
+
{
|
|
2500
|
+
style: {
|
|
2501
|
+
color: meta.color,
|
|
2502
|
+
fontWeight: 700,
|
|
2503
|
+
fontSize: isStage ? fs.body : fs.small,
|
|
2504
|
+
width: 16,
|
|
2505
|
+
textAlign: "center",
|
|
2506
|
+
flexShrink: 0
|
|
2507
|
+
},
|
|
2508
|
+
title: meta.label,
|
|
2509
|
+
"aria-label": meta.label,
|
|
2510
|
+
children: meta.icon
|
|
2373
2511
|
}
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2512
|
+
),
|
|
2513
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
2514
|
+
"span",
|
|
2515
|
+
{
|
|
2516
|
+
style: {
|
|
2517
|
+
fontSize: isStage ? fs.body : fs.small,
|
|
2518
|
+
fontWeight: isStage ? 600 : 400,
|
|
2519
|
+
color: isError ? theme.error : isDecision ? theme.warning : isStage ? theme.textPrimary : theme.textSecondary,
|
|
2520
|
+
lineHeight: 1.6,
|
|
2521
|
+
fontFamily: entry.type === "step" ? theme.fontMono : theme.fontSans
|
|
2522
|
+
},
|
|
2523
|
+
children: entry.text
|
|
2524
|
+
}
|
|
2525
|
+
)
|
|
2526
|
+
]
|
|
2527
|
+
},
|
|
2528
|
+
i
|
|
2529
|
+
);
|
|
2530
|
+
}),
|
|
2531
|
+
future.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { style: {
|
|
2532
|
+
opacity: 0.3,
|
|
2533
|
+
fontSize: fs.small,
|
|
2534
|
+
color: theme.textMuted,
|
|
2535
|
+
padding: `8px 0`,
|
|
2536
|
+
fontStyle: "italic"
|
|
2537
|
+
}, children: [
|
|
2538
|
+
future.length,
|
|
2539
|
+
" more ",
|
|
2540
|
+
future.length === 1 ? "entry" : "entries",
|
|
2541
|
+
" ahead..."
|
|
2378
2542
|
] })
|
|
2379
2543
|
]
|
|
2380
2544
|
}
|
|
2381
2545
|
);
|
|
2382
2546
|
}
|
|
2383
2547
|
|
|
2548
|
+
// src/components/NarrativePanel/NarrativePanel.tsx
|
|
2549
|
+
var import_jsx_runtime13 = require("react/jsx-runtime");
|
|
2550
|
+
function NarrativePanel({
|
|
2551
|
+
snapshots,
|
|
2552
|
+
selectedIndex,
|
|
2553
|
+
narrativeEntries,
|
|
2554
|
+
narrative: narrativeProp,
|
|
2555
|
+
size = "default",
|
|
2556
|
+
unstyled = false,
|
|
2557
|
+
className,
|
|
2558
|
+
style
|
|
2559
|
+
}) {
|
|
2560
|
+
const fs = fontSize[size];
|
|
2561
|
+
const pad = padding[size];
|
|
2562
|
+
const narrative = (0, import_react12.useMemo)(() => {
|
|
2563
|
+
if (narrativeProp && narrativeProp.length > 0) return narrativeProp;
|
|
2564
|
+
const lines = [];
|
|
2565
|
+
for (const snap of snapshots) {
|
|
2566
|
+
const stageLines = (snap.narrative ?? "").split("\n").filter(Boolean);
|
|
2567
|
+
lines.push(...stageLines);
|
|
2568
|
+
}
|
|
2569
|
+
return lines;
|
|
2570
|
+
}, [narrativeProp, snapshots]);
|
|
2571
|
+
const revealedCount = (0, import_react12.useMemo)(() => {
|
|
2572
|
+
if (snapshots.length === 0 || narrative.length === 0) return narrative.length;
|
|
2573
|
+
const stageBoundaries = [];
|
|
2574
|
+
for (let i = 0; i < narrative.length; i++) {
|
|
2575
|
+
const trimmed = narrative[i].trimStart();
|
|
2576
|
+
if (trimmed.startsWith("Stage ") && !trimmed.match(/^Stage\s+\d+:\s*Step\s/)) {
|
|
2577
|
+
stageBoundaries.push(i);
|
|
2578
|
+
}
|
|
2579
|
+
}
|
|
2580
|
+
if (stageBoundaries.length === 0) {
|
|
2581
|
+
const ratio = (selectedIndex + 1) / snapshots.length;
|
|
2582
|
+
return Math.max(1, Math.ceil(narrative.length * ratio));
|
|
2583
|
+
}
|
|
2584
|
+
const groupsToShow = Math.min(selectedIndex + 1, stageBoundaries.length);
|
|
2585
|
+
const endIdx = groupsToShow < stageBoundaries.length ? stageBoundaries[groupsToShow] : narrative.length;
|
|
2586
|
+
return Math.max(1, endIdx);
|
|
2587
|
+
}, [snapshots.length, selectedIndex, narrative]);
|
|
2588
|
+
const hasStructured = narrativeEntries && narrativeEntries.length > 0;
|
|
2589
|
+
if (unstyled) {
|
|
2590
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className, style, "data-fp": "narrative-panel", children: hasStructured ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(StoryNarrative, { entries: narrativeEntries, stageCount: selectedIndex + 1, unstyled: true }) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(NarrativeTrace, { narrative, revealedCount, unstyled: true }) });
|
|
2591
|
+
}
|
|
2592
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
|
|
2593
|
+
"div",
|
|
2594
|
+
{
|
|
2595
|
+
className,
|
|
2596
|
+
style: {
|
|
2597
|
+
overflow: "auto",
|
|
2598
|
+
display: "flex",
|
|
2599
|
+
flexDirection: "column",
|
|
2600
|
+
...style
|
|
2601
|
+
},
|
|
2602
|
+
"data-fp": "narrative-panel",
|
|
2603
|
+
children: [
|
|
2604
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2605
|
+
"div",
|
|
2606
|
+
{
|
|
2607
|
+
style: {
|
|
2608
|
+
padding: `${pad - 4}px ${pad}px`,
|
|
2609
|
+
fontSize: fs.small,
|
|
2610
|
+
color: theme.textMuted,
|
|
2611
|
+
fontStyle: "italic",
|
|
2612
|
+
borderBottom: `1px solid ${theme.border}`,
|
|
2613
|
+
flexShrink: 0
|
|
2614
|
+
},
|
|
2615
|
+
children: "What happened at each stage, what data flowed, what decisions were made, and why."
|
|
2616
|
+
}
|
|
2617
|
+
),
|
|
2618
|
+
hasStructured ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2619
|
+
StoryNarrative,
|
|
2620
|
+
{
|
|
2621
|
+
entries: narrativeEntries,
|
|
2622
|
+
stageCount: selectedIndex + 1,
|
|
2623
|
+
size,
|
|
2624
|
+
style: { flex: 1 }
|
|
2625
|
+
}
|
|
2626
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2627
|
+
NarrativeTrace,
|
|
2628
|
+
{
|
|
2629
|
+
narrative,
|
|
2630
|
+
revealedCount,
|
|
2631
|
+
size,
|
|
2632
|
+
style: { flex: 1 }
|
|
2633
|
+
}
|
|
2634
|
+
)
|
|
2635
|
+
]
|
|
2636
|
+
}
|
|
2637
|
+
);
|
|
2638
|
+
}
|
|
2639
|
+
|
|
2384
2640
|
// src/components/FlowchartView/SubflowTree.tsx
|
|
2385
|
-
var
|
|
2386
|
-
var
|
|
2641
|
+
var import_react13 = require("react");
|
|
2642
|
+
var import_jsx_runtime14 = require("react/jsx-runtime");
|
|
2387
2643
|
function specToTree(node) {
|
|
2644
|
+
if (!node) return [];
|
|
2388
2645
|
const entries = [];
|
|
2389
2646
|
const seen = /* @__PURE__ */ new Set();
|
|
2390
2647
|
function walk(n) {
|
|
2648
|
+
if (!n) return;
|
|
2391
2649
|
const id = n.name || n.id || "";
|
|
2392
2650
|
if (seen.has(id)) return;
|
|
2393
2651
|
seen.add(id);
|
|
@@ -2403,7 +2661,7 @@ function specToTree(node) {
|
|
|
2403
2661
|
entries.push(entry);
|
|
2404
2662
|
if (n.children) {
|
|
2405
2663
|
for (const child of n.children) {
|
|
2406
|
-
walk(child);
|
|
2664
|
+
if (child) walk(child);
|
|
2407
2665
|
}
|
|
2408
2666
|
}
|
|
2409
2667
|
if (n.next) {
|
|
@@ -2413,25 +2671,25 @@ function specToTree(node) {
|
|
|
2413
2671
|
walk(node);
|
|
2414
2672
|
return entries;
|
|
2415
2673
|
}
|
|
2416
|
-
var TreeNode = (0,
|
|
2674
|
+
var TreeNode = (0, import_react13.memo)(function TreeNode2({
|
|
2417
2675
|
entry,
|
|
2418
2676
|
depth,
|
|
2419
2677
|
activeStage,
|
|
2420
2678
|
doneStages,
|
|
2421
2679
|
onNodeSelect
|
|
2422
2680
|
}) {
|
|
2423
|
-
const [expanded, setExpanded] = (0,
|
|
2681
|
+
const [expanded, setExpanded] = (0, import_react13.useState)(true);
|
|
2424
2682
|
const hasChildren = entry.children && entry.children.length > 0;
|
|
2425
2683
|
const isActive = activeStage === entry.name;
|
|
2426
2684
|
const isDone = doneStages?.has(entry.name);
|
|
2427
|
-
const handleClick = (0,
|
|
2685
|
+
const handleClick = (0, import_react13.useCallback)(() => {
|
|
2428
2686
|
if (hasChildren) {
|
|
2429
2687
|
setExpanded((prev) => !prev);
|
|
2430
2688
|
}
|
|
2431
2689
|
onNodeSelect?.(entry.name, !!entry.isSubflow);
|
|
2432
2690
|
}, [hasChildren, onNodeSelect, entry.name, entry.isSubflow]);
|
|
2433
|
-
return /* @__PURE__ */ (0,
|
|
2434
|
-
/* @__PURE__ */ (0,
|
|
2691
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_jsx_runtime14.Fragment, { children: [
|
|
2692
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
|
|
2435
2693
|
"button",
|
|
2436
2694
|
{
|
|
2437
2695
|
onClick: handleClick,
|
|
@@ -2462,7 +2720,7 @@ var TreeNode = (0, import_react11.memo)(function TreeNode2({
|
|
|
2462
2720
|
}
|
|
2463
2721
|
},
|
|
2464
2722
|
children: [
|
|
2465
|
-
hasChildren ? /* @__PURE__ */ (0,
|
|
2723
|
+
hasChildren ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2466
2724
|
"span",
|
|
2467
2725
|
{
|
|
2468
2726
|
style: {
|
|
@@ -2477,8 +2735,8 @@ var TreeNode = (0, import_react11.memo)(function TreeNode2({
|
|
|
2477
2735
|
},
|
|
2478
2736
|
children: "\u25B6"
|
|
2479
2737
|
}
|
|
2480
|
-
) : /* @__PURE__ */ (0,
|
|
2481
|
-
/* @__PURE__ */ (0,
|
|
2738
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { style: { width: 12, flexShrink: 0 } }),
|
|
2739
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2482
2740
|
"span",
|
|
2483
2741
|
{
|
|
2484
2742
|
style: {
|
|
@@ -2490,8 +2748,8 @@ var TreeNode = (0, import_react11.memo)(function TreeNode2({
|
|
|
2490
2748
|
}
|
|
2491
2749
|
}
|
|
2492
2750
|
),
|
|
2493
|
-
/* @__PURE__ */ (0,
|
|
2494
|
-
/* @__PURE__ */ (0,
|
|
2751
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("span", { style: { display: "flex", flexDirection: "column", minWidth: 0 }, children: [
|
|
2752
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
|
|
2495
2753
|
"span",
|
|
2496
2754
|
{
|
|
2497
2755
|
style: {
|
|
@@ -2503,11 +2761,11 @@ var TreeNode = (0, import_react11.memo)(function TreeNode2({
|
|
|
2503
2761
|
},
|
|
2504
2762
|
children: [
|
|
2505
2763
|
entry.name,
|
|
2506
|
-
entry.isSubflow && /* @__PURE__ */ (0,
|
|
2764
|
+
entry.isSubflow && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { style: { opacity: 0.5, marginLeft: 4, fontSize: 10 }, children: "\u229E" })
|
|
2507
2765
|
]
|
|
2508
2766
|
}
|
|
2509
2767
|
),
|
|
2510
|
-
entry.description && /* @__PURE__ */ (0,
|
|
2768
|
+
entry.description && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2511
2769
|
"span",
|
|
2512
2770
|
{
|
|
2513
2771
|
style: {
|
|
@@ -2524,7 +2782,7 @@ var TreeNode = (0, import_react11.memo)(function TreeNode2({
|
|
|
2524
2782
|
]
|
|
2525
2783
|
}
|
|
2526
2784
|
),
|
|
2527
|
-
hasChildren && expanded && /* @__PURE__ */ (0,
|
|
2785
|
+
hasChildren && expanded && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { children: entry.children.map((child, i) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2528
2786
|
TreeNode2,
|
|
2529
2787
|
{
|
|
2530
2788
|
entry: child,
|
|
@@ -2533,12 +2791,12 @@ var TreeNode = (0, import_react11.memo)(function TreeNode2({
|
|
|
2533
2791
|
doneStages,
|
|
2534
2792
|
onNodeSelect
|
|
2535
2793
|
},
|
|
2536
|
-
`${child.name}-${i}`
|
|
2794
|
+
child.subflowId ?? `${child.name}-${i}`
|
|
2537
2795
|
)) })
|
|
2538
2796
|
] });
|
|
2539
2797
|
});
|
|
2540
|
-
var SectionLabel = (0,
|
|
2541
|
-
return /* @__PURE__ */ (0,
|
|
2798
|
+
var SectionLabel = (0, import_react13.memo)(function SectionLabel2({ children }) {
|
|
2799
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2542
2800
|
"div",
|
|
2543
2801
|
{
|
|
2544
2802
|
style: {
|
|
@@ -2553,7 +2811,7 @@ var SectionLabel = (0, import_react11.memo)(function SectionLabel2({ children })
|
|
|
2553
2811
|
}
|
|
2554
2812
|
);
|
|
2555
2813
|
});
|
|
2556
|
-
var SubflowTree = (0,
|
|
2814
|
+
var SubflowTree = (0, import_react13.memo)(function SubflowTree2({
|
|
2557
2815
|
spec,
|
|
2558
2816
|
activeStage,
|
|
2559
2817
|
doneStages,
|
|
@@ -2562,21 +2820,10 @@ var SubflowTree = (0, import_react11.memo)(function SubflowTree2({
|
|
|
2562
2820
|
className,
|
|
2563
2821
|
style
|
|
2564
2822
|
}) {
|
|
2565
|
-
const tree = (0,
|
|
2566
|
-
const
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
TreeNode,
|
|
2570
|
-
{
|
|
2571
|
-
entry,
|
|
2572
|
-
depth: 0,
|
|
2573
|
-
activeStage,
|
|
2574
|
-
doneStages,
|
|
2575
|
-
onNodeSelect
|
|
2576
|
-
},
|
|
2577
|
-
`${entry.name}-${i}`
|
|
2578
|
-
));
|
|
2579
|
-
return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
|
|
2823
|
+
const tree = (0, import_react13.useMemo)(() => specToTree(spec), [spec]);
|
|
2824
|
+
const subflowStages = (0, import_react13.useMemo)(() => tree.filter((e) => e.isSubflow), [tree]);
|
|
2825
|
+
if (subflowStages.length === 0) return null;
|
|
2826
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
|
|
2580
2827
|
"div",
|
|
2581
2828
|
{
|
|
2582
2829
|
className,
|
|
@@ -2594,103 +2841,612 @@ var SubflowTree = (0, import_react11.memo)(function SubflowTree2({
|
|
|
2594
2841
|
...style
|
|
2595
2842
|
},
|
|
2596
2843
|
children: [
|
|
2597
|
-
!unstyled && /* @__PURE__ */ (0,
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2844
|
+
!unstyled && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(SectionLabel, { children: "Subflows" }),
|
|
2845
|
+
subflowStages.map((entry, i) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
2846
|
+
TreeNode,
|
|
2847
|
+
{
|
|
2848
|
+
entry,
|
|
2849
|
+
depth: 0,
|
|
2850
|
+
activeStage,
|
|
2851
|
+
doneStages,
|
|
2852
|
+
onNodeSelect
|
|
2853
|
+
},
|
|
2854
|
+
entry.subflowId ?? `${entry.name}-${i}`
|
|
2855
|
+
))
|
|
2604
2856
|
]
|
|
2605
2857
|
}
|
|
2606
2858
|
);
|
|
2607
2859
|
});
|
|
2608
2860
|
|
|
2609
|
-
// src/
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2861
|
+
// src/components/FlowchartView/SubflowBreadcrumb.tsx
|
|
2862
|
+
var import_react14 = require("react");
|
|
2863
|
+
var import_jsx_runtime15 = require("react/jsx-runtime");
|
|
2864
|
+
var SubflowBreadcrumb = (0, import_react14.memo)(function SubflowBreadcrumb2({
|
|
2865
|
+
breadcrumbs,
|
|
2866
|
+
onNavigate
|
|
2867
|
+
}) {
|
|
2868
|
+
if (breadcrumbs.length <= 1) return null;
|
|
2869
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2870
|
+
"div",
|
|
2871
|
+
{
|
|
2872
|
+
style: {
|
|
2873
|
+
display: "flex",
|
|
2874
|
+
alignItems: "center",
|
|
2875
|
+
gap: 4,
|
|
2876
|
+
padding: "6px 12px",
|
|
2877
|
+
background: theme.bgSecondary,
|
|
2878
|
+
borderBottom: `1px solid ${theme.border}`,
|
|
2879
|
+
fontSize: 12,
|
|
2880
|
+
fontFamily: theme.fontSans,
|
|
2881
|
+
flexShrink: 0,
|
|
2882
|
+
overflowX: "auto"
|
|
2883
|
+
},
|
|
2884
|
+
children: breadcrumbs.map((crumb, i) => {
|
|
2885
|
+
const isLast = i === breadcrumbs.length - 1;
|
|
2886
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("span", { style: { display: "flex", alignItems: "center", gap: 4 }, children: [
|
|
2887
|
+
i > 0 && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { style: { color: theme.textMuted, fontSize: 10 }, children: "\u203A" }),
|
|
2888
|
+
isLast ? /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("span", { style: { display: "flex", alignItems: "center", gap: 6 }, children: [
|
|
2889
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2890
|
+
"span",
|
|
2891
|
+
{
|
|
2892
|
+
style: {
|
|
2893
|
+
color: theme.primary,
|
|
2894
|
+
fontWeight: 600
|
|
2895
|
+
},
|
|
2896
|
+
children: crumb.label
|
|
2897
|
+
}
|
|
2898
|
+
),
|
|
2899
|
+
crumb.description && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
|
|
2900
|
+
"span",
|
|
2901
|
+
{
|
|
2902
|
+
style: {
|
|
2903
|
+
color: theme.textMuted,
|
|
2904
|
+
fontWeight: 400,
|
|
2905
|
+
fontSize: 11
|
|
2906
|
+
},
|
|
2907
|
+
children: [
|
|
2908
|
+
"\u2014 ",
|
|
2909
|
+
crumb.description
|
|
2910
|
+
]
|
|
2911
|
+
}
|
|
2912
|
+
)
|
|
2913
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2914
|
+
"button",
|
|
2915
|
+
{
|
|
2916
|
+
onClick: () => onNavigate(i),
|
|
2917
|
+
style: {
|
|
2918
|
+
background: "none",
|
|
2919
|
+
border: "none",
|
|
2920
|
+
color: theme.textSecondary,
|
|
2921
|
+
cursor: "pointer",
|
|
2922
|
+
padding: "2px 4px",
|
|
2923
|
+
borderRadius: 4,
|
|
2924
|
+
fontSize: 12,
|
|
2925
|
+
fontFamily: "inherit",
|
|
2926
|
+
fontWeight: 500,
|
|
2927
|
+
transition: "color 0.15s"
|
|
2928
|
+
},
|
|
2929
|
+
onMouseEnter: (e) => {
|
|
2930
|
+
e.currentTarget.style.color = `${theme.primary}`;
|
|
2931
|
+
},
|
|
2932
|
+
onMouseLeave: (e) => {
|
|
2933
|
+
e.currentTarget.style.color = `${theme.textSecondary}`;
|
|
2934
|
+
},
|
|
2935
|
+
children: crumb.label
|
|
2936
|
+
}
|
|
2937
|
+
)
|
|
2938
|
+
] }, `${crumb.label}-${i}`);
|
|
2939
|
+
})
|
|
2940
|
+
}
|
|
2941
|
+
);
|
|
2942
|
+
});
|
|
2943
|
+
|
|
2944
|
+
// src/components/ExplainableShell/ExplainableShell.tsx
|
|
2945
|
+
var import_jsx_runtime16 = require("react/jsx-runtime");
|
|
2946
|
+
var HLinePill = (0, import_react15.memo)(function HLinePill2({
|
|
2947
|
+
label,
|
|
2948
|
+
detail,
|
|
2949
|
+
expanded,
|
|
2950
|
+
onClick
|
|
2951
|
+
}) {
|
|
2952
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { style: {
|
|
2953
|
+
display: "flex",
|
|
2954
|
+
alignItems: "center",
|
|
2955
|
+
gap: 0,
|
|
2956
|
+
padding: "0"
|
|
2957
|
+
}, children: [
|
|
2958
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: { flex: 1, height: 1, background: theme.border } }),
|
|
2959
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
|
|
2960
|
+
"button",
|
|
2961
|
+
{
|
|
2962
|
+
onClick,
|
|
2963
|
+
style: {
|
|
2964
|
+
display: "flex",
|
|
2965
|
+
alignItems: "center",
|
|
2966
|
+
gap: 5,
|
|
2967
|
+
padding: "3px 12px",
|
|
2968
|
+
margin: "4px 0",
|
|
2969
|
+
fontSize: 10,
|
|
2970
|
+
fontWeight: 600,
|
|
2971
|
+
fontFamily: "inherit",
|
|
2972
|
+
color: theme.textMuted,
|
|
2973
|
+
background: theme.bgSecondary,
|
|
2974
|
+
border: `1px solid ${theme.border}`,
|
|
2975
|
+
borderRadius: 10,
|
|
2976
|
+
cursor: "pointer",
|
|
2977
|
+
whiteSpace: "nowrap",
|
|
2978
|
+
letterSpacing: "0.04em",
|
|
2979
|
+
textTransform: "uppercase",
|
|
2980
|
+
transition: "color 0.15s ease"
|
|
2981
|
+
},
|
|
2982
|
+
children: [
|
|
2983
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { style: { fontSize: 7 }, children: expanded ? "\u25BC" : "\u25B6" }),
|
|
2984
|
+
label,
|
|
2985
|
+
detail && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { style: { fontWeight: 400, opacity: 0.5, fontSize: 9 }, children: detail })
|
|
2986
|
+
]
|
|
2987
|
+
}
|
|
2988
|
+
),
|
|
2989
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: { flex: 1, height: 1, background: theme.border } })
|
|
2990
|
+
] });
|
|
2991
|
+
});
|
|
2992
|
+
var VLinePill = (0, import_react15.memo)(function VLinePill2({
|
|
2993
|
+
label,
|
|
2994
|
+
expanded,
|
|
2995
|
+
side = "right",
|
|
2996
|
+
onClick
|
|
2997
|
+
}) {
|
|
2998
|
+
const arrow = side === "right" ? expanded ? "\u25B6" : "\u25C0" : expanded ? "\u25C0" : "\u25B6";
|
|
2999
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { style: {
|
|
3000
|
+
display: "flex",
|
|
3001
|
+
flexDirection: "column",
|
|
3002
|
+
alignItems: "center",
|
|
3003
|
+
gap: 0,
|
|
3004
|
+
padding: "0"
|
|
3005
|
+
}, children: [
|
|
3006
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: { flex: 1, width: 1, background: theme.border } }),
|
|
3007
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
|
|
3008
|
+
"button",
|
|
3009
|
+
{
|
|
3010
|
+
onClick,
|
|
3011
|
+
style: {
|
|
3012
|
+
display: "flex",
|
|
3013
|
+
alignItems: "center",
|
|
3014
|
+
gap: 4,
|
|
3015
|
+
padding: "10px 4px",
|
|
3016
|
+
margin: "0 3px",
|
|
3017
|
+
fontSize: 10,
|
|
3018
|
+
fontWeight: 600,
|
|
3019
|
+
fontFamily: "inherit",
|
|
3020
|
+
color: theme.textMuted,
|
|
3021
|
+
background: theme.bgSecondary,
|
|
3022
|
+
border: `1px solid ${theme.border}`,
|
|
3023
|
+
borderRadius: 10,
|
|
3024
|
+
cursor: "pointer",
|
|
3025
|
+
whiteSpace: "nowrap",
|
|
3026
|
+
letterSpacing: "0.04em",
|
|
3027
|
+
textTransform: "uppercase",
|
|
3028
|
+
writingMode: "vertical-lr",
|
|
3029
|
+
transition: "color 0.15s ease"
|
|
3030
|
+
},
|
|
3031
|
+
children: [
|
|
3032
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { style: { fontSize: 7, writingMode: "horizontal-tb" }, children: arrow }),
|
|
3033
|
+
label
|
|
3034
|
+
]
|
|
2626
3035
|
}
|
|
3036
|
+
),
|
|
3037
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: { flex: 1, width: 1, background: theme.border } })
|
|
3038
|
+
] });
|
|
3039
|
+
});
|
|
3040
|
+
var RIGHT_PANEL_LABELS = {
|
|
3041
|
+
memory: "Memory",
|
|
3042
|
+
narrative: "Narrative"
|
|
3043
|
+
};
|
|
3044
|
+
var DetailsContent = (0, import_react15.memo)(function DetailsContent2({
|
|
3045
|
+
snapshots,
|
|
3046
|
+
selectedIndex,
|
|
3047
|
+
narrativeEntries,
|
|
3048
|
+
narrative,
|
|
3049
|
+
size,
|
|
3050
|
+
fillHeight
|
|
3051
|
+
}) {
|
|
3052
|
+
const [rightPanel, setRightPanel] = (0, import_react15.useState)("memory");
|
|
3053
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { style: { flex: 1, display: "flex", flexDirection: "column", overflow: "hidden" }, children: [
|
|
3054
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: { display: "flex", borderBottom: `1px solid ${theme.border}`, flexShrink: 0 }, children: ["memory", "narrative"].map((panel) => {
|
|
3055
|
+
const active = rightPanel === panel;
|
|
3056
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
3057
|
+
"button",
|
|
3058
|
+
{
|
|
3059
|
+
onClick: () => setRightPanel(panel),
|
|
3060
|
+
style: {
|
|
3061
|
+
flex: 1,
|
|
3062
|
+
padding: "6px 8px",
|
|
3063
|
+
fontSize: 11,
|
|
3064
|
+
fontWeight: active ? 600 : 400,
|
|
3065
|
+
color: active ? theme.primary : theme.textMuted,
|
|
3066
|
+
background: active ? `color-mix(in srgb, ${theme.primary} 8%, transparent)` : "transparent",
|
|
3067
|
+
border: "none",
|
|
3068
|
+
borderBottom: active ? `2px solid ${theme.primary}` : "2px solid transparent",
|
|
3069
|
+
cursor: "pointer",
|
|
3070
|
+
textTransform: "uppercase",
|
|
3071
|
+
letterSpacing: "0.06em",
|
|
3072
|
+
fontFamily: "inherit"
|
|
3073
|
+
},
|
|
3074
|
+
children: RIGHT_PANEL_LABELS[panel]
|
|
3075
|
+
},
|
|
3076
|
+
panel
|
|
3077
|
+
);
|
|
3078
|
+
}) }),
|
|
3079
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { style: { flex: 1, overflow: "auto" }, children: [
|
|
3080
|
+
rightPanel === "memory" && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(MemoryPanel, { snapshots, selectedIndex, size, style: fillHeight ? { height: "100%" } : void 0 }),
|
|
3081
|
+
rightPanel === "narrative" && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(NarrativePanel, { snapshots, selectedIndex, narrativeEntries, narrative, size, style: fillHeight ? { height: "100%" } : void 0 })
|
|
3082
|
+
] })
|
|
3083
|
+
] });
|
|
3084
|
+
});
|
|
3085
|
+
function resolveSubflowLevel(parentSpec, parentSnapshots, subflowNodeName, narrativeEntries) {
|
|
3086
|
+
const specNode = findSubflowSpecNode(parentSpec, subflowNodeName);
|
|
3087
|
+
if (!specNode?.subflowStructure) return null;
|
|
3088
|
+
const parentSnap = parentSnapshots.find(
|
|
3089
|
+
(s) => s.stageName === subflowNodeName || s.stageLabel === subflowNodeName
|
|
3090
|
+
);
|
|
3091
|
+
if (!parentSnap?.subflowResult) return null;
|
|
3092
|
+
const sfNarrative = narrativeEntries ? extractSubflowNarrative(narrativeEntries, subflowNodeName) : void 0;
|
|
3093
|
+
const sfSnapshots = subflowResultToSnapshots(parentSnap.subflowResult, sfNarrative);
|
|
3094
|
+
if (sfSnapshots.length === 0) return null;
|
|
3095
|
+
return {
|
|
3096
|
+
subflowId: specNode.subflowId ?? subflowNodeName,
|
|
3097
|
+
label: specNode.subflowName ?? specNode.name,
|
|
3098
|
+
spec: specNode.subflowStructure,
|
|
3099
|
+
snapshots: sfSnapshots
|
|
3100
|
+
};
|
|
3101
|
+
}
|
|
3102
|
+
function extractSubflowNarrative(entries, subflowName) {
|
|
3103
|
+
const result = [];
|
|
3104
|
+
let inside = false;
|
|
3105
|
+
for (const entry of entries) {
|
|
3106
|
+
if (entry.type === "subflow" && entry.text.includes(subflowName) && entry.text.startsWith("Entering")) {
|
|
3107
|
+
inside = true;
|
|
3108
|
+
continue;
|
|
2627
3109
|
}
|
|
3110
|
+
if (inside && entry.type === "subflow" && entry.text.includes(subflowName) && entry.text.startsWith("Exiting")) break;
|
|
3111
|
+
if (inside) result.push(entry);
|
|
2628
3112
|
}
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
memory,
|
|
2635
|
-
narrative,
|
|
2636
|
-
startMs,
|
|
2637
|
-
durationMs,
|
|
2638
|
-
status: "done",
|
|
2639
|
-
...node.description ? { description: node.description } : void 0,
|
|
2640
|
-
...node.subflowId ? { subflowId: node.subflowId } : void 0,
|
|
2641
|
-
...sfResult ? { subflowResult: sfResult } : void 0
|
|
2642
|
-
});
|
|
2643
|
-
let nextMs = startMs + durationMs;
|
|
2644
|
-
if (node.children && node.children.length > 0) {
|
|
2645
|
-
let maxChildEnd = nextMs;
|
|
3113
|
+
return result;
|
|
3114
|
+
}
|
|
3115
|
+
function findSubflowSpecNode(node, name) {
|
|
3116
|
+
if ((node.name === name || node.id === name) && node.isSubflowRoot) return node;
|
|
3117
|
+
if (node.children) {
|
|
2646
3118
|
for (const child of node.children) {
|
|
2647
|
-
const
|
|
2648
|
-
|
|
3119
|
+
const f = findSubflowSpecNode(child, name);
|
|
3120
|
+
if (f) return f;
|
|
2649
3121
|
}
|
|
2650
|
-
nextMs = maxChildEnd;
|
|
2651
|
-
}
|
|
2652
|
-
if (node.next) {
|
|
2653
|
-
nextMs = flattenTree(node.next, out, sharedState, nextMs, subflowResults, memory);
|
|
2654
3122
|
}
|
|
2655
|
-
return
|
|
3123
|
+
if (node.next) return findSubflowSpecNode(node.next, name);
|
|
3124
|
+
return null;
|
|
2656
3125
|
}
|
|
2657
|
-
function
|
|
2658
|
-
|
|
2659
|
-
if (node.
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
const keys = Object.keys(node.stageWrites);
|
|
2664
|
-
parts.push(`Wrote ${keys.length} key(s): ${keys.join(", ")}.`);
|
|
2665
|
-
}
|
|
2666
|
-
if (node.errors && Object.keys(node.errors).length > 0) {
|
|
2667
|
-
parts.push(`Errors: ${JSON.stringify(node.errors)}`);
|
|
2668
|
-
}
|
|
2669
|
-
if (node.isFork) {
|
|
2670
|
-
parts.push(
|
|
2671
|
-
`Forked into ${node.children?.length ?? 0} parallel branch(es).`
|
|
2672
|
-
);
|
|
2673
|
-
}
|
|
2674
|
-
return parts.join(" ") || `Stage ${node.id} completed.`;
|
|
3126
|
+
function hasSubflowNodes(node) {
|
|
3127
|
+
if (!node) return false;
|
|
3128
|
+
if (node.isSubflowRoot) return true;
|
|
3129
|
+
if (node.children?.some((c) => c && hasSubflowNodes(c))) return true;
|
|
3130
|
+
if (node.next && hasSubflowNodes(node.next)) return true;
|
|
3131
|
+
return false;
|
|
2675
3132
|
}
|
|
2676
|
-
function
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
3133
|
+
function ExplainableShell({
|
|
3134
|
+
snapshots,
|
|
3135
|
+
spec,
|
|
3136
|
+
title,
|
|
3137
|
+
resultData,
|
|
3138
|
+
logs = [],
|
|
3139
|
+
narrative,
|
|
3140
|
+
narrativeEntries,
|
|
3141
|
+
tabs = ["result", "explainable"],
|
|
3142
|
+
defaultTab,
|
|
3143
|
+
hideConsole = false,
|
|
3144
|
+
panelLabels,
|
|
3145
|
+
defaultExpanded,
|
|
3146
|
+
renderFlowchart,
|
|
3147
|
+
size = "default",
|
|
3148
|
+
unstyled = false,
|
|
3149
|
+
className,
|
|
3150
|
+
style
|
|
3151
|
+
}) {
|
|
3152
|
+
const leftLabel = panelLabels?.topology ?? "Topology";
|
|
3153
|
+
const rightLabel = panelLabels?.details ?? "Details";
|
|
3154
|
+
const bottomLabel = panelLabels?.timeline ?? "Timeline";
|
|
3155
|
+
const shellRef = (0, import_react15.useRef)(null);
|
|
3156
|
+
const [isNarrow, setIsNarrow] = (0, import_react15.useState)(false);
|
|
3157
|
+
(0, import_react15.useEffect)(() => {
|
|
3158
|
+
const el = shellRef.current;
|
|
3159
|
+
if (!el) return;
|
|
3160
|
+
const ro = new ResizeObserver(([entry]) => {
|
|
3161
|
+
setIsNarrow(entry.contentRect.width < 640);
|
|
3162
|
+
});
|
|
3163
|
+
ro.observe(el);
|
|
3164
|
+
return () => ro.disconnect();
|
|
3165
|
+
}, []);
|
|
3166
|
+
const [activeTab, setActiveTab] = (0, import_react15.useState)(defaultTab ?? tabs[0]);
|
|
3167
|
+
const [snapshotIdx, setSnapshotIdx] = (0, import_react15.useState)(0);
|
|
3168
|
+
const [drillDownStack, setDrillDownStack] = (0, import_react15.useState)([]);
|
|
3169
|
+
const [rightExpanded, setRightExpanded] = (0, import_react15.useState)(defaultExpanded?.details ?? true);
|
|
3170
|
+
const [leftExpanded, setLeftExpanded] = (0, import_react15.useState)(defaultExpanded?.topology ?? false);
|
|
3171
|
+
const [timelineExpanded, setTimelineExpanded] = (0, import_react15.useState)(defaultExpanded?.timeline ?? false);
|
|
3172
|
+
(0, import_react15.useEffect)(() => {
|
|
3173
|
+
if (isNarrow) {
|
|
3174
|
+
setLeftExpanded(false);
|
|
3175
|
+
setRightExpanded(false);
|
|
3176
|
+
setTimelineExpanded(false);
|
|
3177
|
+
}
|
|
3178
|
+
}, [isNarrow]);
|
|
3179
|
+
const triggerReflow = (0, import_react15.useCallback)(() => {
|
|
3180
|
+
requestAnimationFrame(() => window.dispatchEvent(new Event("resize")));
|
|
3181
|
+
}, []);
|
|
3182
|
+
const toggleLeft = (0, import_react15.useCallback)((v2) => {
|
|
3183
|
+
setLeftExpanded(v2);
|
|
3184
|
+
triggerReflow();
|
|
3185
|
+
}, [triggerReflow]);
|
|
3186
|
+
const toggleRight = (0, import_react15.useCallback)((v2) => {
|
|
3187
|
+
setRightExpanded(v2);
|
|
3188
|
+
triggerReflow();
|
|
3189
|
+
}, [triggerReflow]);
|
|
3190
|
+
const toggleTimeline = (0, import_react15.useCallback)(() => {
|
|
3191
|
+
setTimelineExpanded((p) => !p);
|
|
3192
|
+
triggerReflow();
|
|
3193
|
+
}, [triggerReflow]);
|
|
3194
|
+
const isInSubflow = drillDownStack.length > 0;
|
|
3195
|
+
const currentLevel = (0, import_react15.useMemo)(() => {
|
|
3196
|
+
if (drillDownStack.length > 0) {
|
|
3197
|
+
const top = drillDownStack[drillDownStack.length - 1];
|
|
3198
|
+
return { spec: top.spec, snapshots: top.snapshots };
|
|
3199
|
+
}
|
|
3200
|
+
return { spec: spec ?? null, snapshots };
|
|
3201
|
+
}, [drillDownStack, spec, snapshots]);
|
|
3202
|
+
const activeSnapshots = currentLevel.snapshots;
|
|
3203
|
+
const activeSpec = currentLevel.spec;
|
|
3204
|
+
const safeIdx = activeSnapshots.length > 0 ? Math.max(0, Math.min(snapshotIdx, activeSnapshots.length - 1)) : 0;
|
|
3205
|
+
const activeNarrative = (0, import_react15.useMemo)(() => {
|
|
3206
|
+
if (!isInSubflow) return narrative;
|
|
3207
|
+
const lines = [];
|
|
3208
|
+
for (const snap of activeSnapshots) {
|
|
3209
|
+
const stageLines = (snap.narrative ?? "").split("\n").filter(Boolean);
|
|
3210
|
+
lines.push(...stageLines);
|
|
3211
|
+
}
|
|
3212
|
+
return lines.length > 0 ? lines : void 0;
|
|
3213
|
+
}, [isInSubflow, narrative, activeSnapshots]);
|
|
3214
|
+
const activeNarrativeEntries = isInSubflow ? void 0 : narrativeEntries;
|
|
3215
|
+
const breadcrumbs = (0, import_react15.useMemo)(() => {
|
|
3216
|
+
const root = { label: title || "Flowchart", spec, description: spec?.description };
|
|
3217
|
+
return [root, ...drillDownStack.map((e) => ({ label: e.label, spec: e.spec, description: void 0 }))];
|
|
3218
|
+
}, [spec, title, drillDownStack]);
|
|
3219
|
+
const showTreeSidebar = (0, import_react15.useMemo)(() => !!spec && hasSubflowNodes(spec), [spec]);
|
|
3220
|
+
const rootOverlay = (0, import_react15.useMemo)(() => {
|
|
3221
|
+
if (isInSubflow || !snapshots.length) return { activeStage: void 0, doneStages: void 0 };
|
|
3222
|
+
const doneStages = new Set(snapshots.slice(0, safeIdx).map((s) => s.stageLabel));
|
|
3223
|
+
const activeStage = snapshots[safeIdx]?.stageLabel ?? null;
|
|
3224
|
+
return { activeStage, doneStages };
|
|
3225
|
+
}, [isInSubflow, snapshots, safeIdx]);
|
|
3226
|
+
const handleTabChange = (0, import_react15.useCallback)((tab) => {
|
|
3227
|
+
setActiveTab(tab);
|
|
3228
|
+
setDrillDownStack([]);
|
|
3229
|
+
setSnapshotIdx(999);
|
|
3230
|
+
}, []);
|
|
3231
|
+
const handleSnapshotChange = (0, import_react15.useCallback)((idx) => {
|
|
3232
|
+
if (typeof idx === "number") setSnapshotIdx(idx);
|
|
3233
|
+
}, []);
|
|
3234
|
+
const handleDrillDown = (0, import_react15.useCallback)(
|
|
3235
|
+
(nodeName) => {
|
|
3236
|
+
if (!activeSpec) return;
|
|
3237
|
+
const entry = resolveSubflowLevel(activeSpec, activeSnapshots, nodeName, narrativeEntries);
|
|
3238
|
+
if (entry) {
|
|
3239
|
+
setDrillDownStack((prev) => [...prev, { ...entry, parentSnapshotIdx: snapshotIdx }]);
|
|
3240
|
+
setSnapshotIdx(0);
|
|
3241
|
+
}
|
|
3242
|
+
},
|
|
3243
|
+
[activeSpec, activeSnapshots, narrativeEntries, snapshotIdx]
|
|
3244
|
+
);
|
|
3245
|
+
const handleBreadcrumbNavigate = (0, import_react15.useCallback)((level) => {
|
|
3246
|
+
setDrillDownStack((prev) => {
|
|
3247
|
+
const popped = level === 0 ? prev[0] : prev[level];
|
|
3248
|
+
if (popped) setSnapshotIdx(popped.parentSnapshotIdx);
|
|
3249
|
+
return level === 0 ? [] : prev.slice(0, level);
|
|
3250
|
+
});
|
|
3251
|
+
}, []);
|
|
3252
|
+
const handleNodeClick = (0, import_react15.useCallback)(
|
|
3253
|
+
(indexOrId) => {
|
|
3254
|
+
if (typeof indexOrId === "number") {
|
|
3255
|
+
setSnapshotIdx(indexOrId);
|
|
3256
|
+
return;
|
|
3257
|
+
}
|
|
3258
|
+
if (activeSpec) {
|
|
3259
|
+
const sfNode = findSubflowSpecNode(activeSpec, indexOrId);
|
|
3260
|
+
if (sfNode?.subflowStructure) {
|
|
3261
|
+
handleDrillDown(indexOrId);
|
|
3262
|
+
return;
|
|
3263
|
+
}
|
|
3264
|
+
}
|
|
3265
|
+
const idx = activeSnapshots.findIndex((s) => s.stageLabel === indexOrId);
|
|
3266
|
+
if (idx >= 0) setSnapshotIdx(idx);
|
|
3267
|
+
},
|
|
3268
|
+
[activeSpec, activeSnapshots, handleDrillDown]
|
|
3269
|
+
);
|
|
3270
|
+
const handleTreeNodeSelect = (0, import_react15.useCallback)(
|
|
3271
|
+
(name, isSubflow) => {
|
|
3272
|
+
if (isSubflow && spec) {
|
|
3273
|
+
setDrillDownStack([]);
|
|
3274
|
+
const entry = resolveSubflowLevel(spec, snapshots, name, narrativeEntries);
|
|
3275
|
+
if (entry) {
|
|
3276
|
+
setDrillDownStack([{ ...entry, parentSnapshotIdx: snapshotIdx }]);
|
|
3277
|
+
setSnapshotIdx(0);
|
|
3278
|
+
}
|
|
3279
|
+
} else {
|
|
3280
|
+
setDrillDownStack([]);
|
|
3281
|
+
const idx = snapshots.findIndex((s) => s.stageLabel === name);
|
|
3282
|
+
if (idx >= 0) setSnapshotIdx(idx);
|
|
3283
|
+
}
|
|
3284
|
+
},
|
|
3285
|
+
[spec, snapshots, narrativeEntries, snapshotIdx]
|
|
3286
|
+
);
|
|
3287
|
+
const tabLabels = {
|
|
3288
|
+
result: "Result",
|
|
3289
|
+
explainable: "Explainable",
|
|
3290
|
+
"ai-compatible": "AI-Compatible"
|
|
3291
|
+
};
|
|
3292
|
+
if (unstyled) {
|
|
3293
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className, style, "data-fp": "explainable-shell", children: [
|
|
3294
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { "data-fp": "shell-tabs", children: tabs.map((tab) => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("button", { "data-fp": "shell-tab", "data-active": tab === activeTab, onClick: () => handleTabChange(tab), children: tabLabels[tab] }, tab)) }),
|
|
3295
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { "data-fp": "shell-content", "data-tab": activeTab, children: [
|
|
3296
|
+
activeTab === "result" && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(ResultPanel, { data: resultData ?? null, logs, hideConsole, unstyled: true }),
|
|
3297
|
+
(activeTab === "explainable" || activeTab === "ai-compatible") && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
|
|
3298
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(TimeTravelControls, { snapshots: activeSnapshots, selectedIndex: safeIdx, onIndexChange: handleSnapshotChange, unstyled: true }),
|
|
3299
|
+
isInSubflow && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(SubflowBreadcrumb, { breadcrumbs, onNavigate: handleBreadcrumbNavigate }),
|
|
3300
|
+
activeSpec && renderFlowchart?.({ spec: activeSpec, snapshots: activeSnapshots, selectedIndex: safeIdx, onNodeClick: handleNodeClick }),
|
|
3301
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(MemoryPanel, { snapshots: activeSnapshots, selectedIndex: safeIdx, unstyled: true }),
|
|
3302
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(NarrativePanel, { snapshots: activeSnapshots, selectedIndex: safeIdx, narrativeEntries: activeNarrativeEntries, narrative: activeNarrative, unstyled: true }),
|
|
3303
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(GanttTimeline, { snapshots: activeSnapshots, selectedIndex: safeIdx, onSelect: handleSnapshotChange, unstyled: true })
|
|
3304
|
+
] })
|
|
3305
|
+
] })
|
|
3306
|
+
] });
|
|
3307
|
+
}
|
|
3308
|
+
const isVisualizationTab = activeTab === "explainable" || activeTab === "ai-compatible";
|
|
3309
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
|
|
3310
|
+
"div",
|
|
3311
|
+
{
|
|
3312
|
+
ref: shellRef,
|
|
3313
|
+
className,
|
|
3314
|
+
style: {
|
|
3315
|
+
height: "100%",
|
|
3316
|
+
display: "flex",
|
|
3317
|
+
flexDirection: "column",
|
|
3318
|
+
overflow: "hidden",
|
|
3319
|
+
background: theme.bgPrimary,
|
|
3320
|
+
color: theme.textPrimary,
|
|
3321
|
+
fontFamily: theme.fontSans,
|
|
3322
|
+
fontSize: 12,
|
|
3323
|
+
...style
|
|
3324
|
+
},
|
|
3325
|
+
"data-fp": "explainable-shell",
|
|
3326
|
+
children: [
|
|
3327
|
+
tabs.length > 1 && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: {
|
|
3328
|
+
display: "flex",
|
|
3329
|
+
borderBottom: `1px solid ${theme.border}`,
|
|
3330
|
+
background: theme.bgSecondary,
|
|
3331
|
+
flexShrink: 0
|
|
3332
|
+
}, children: tabs.map((tab) => {
|
|
3333
|
+
const active = tab === activeTab;
|
|
3334
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
3335
|
+
"button",
|
|
3336
|
+
{
|
|
3337
|
+
onClick: () => handleTabChange(tab),
|
|
3338
|
+
style: {
|
|
3339
|
+
padding: "6px 14px",
|
|
3340
|
+
fontSize: 11,
|
|
3341
|
+
fontWeight: active ? 700 : 500,
|
|
3342
|
+
textTransform: "uppercase",
|
|
3343
|
+
letterSpacing: "0.08em",
|
|
3344
|
+
color: active ? theme.primary : theme.textMuted,
|
|
3345
|
+
background: "transparent",
|
|
3346
|
+
border: "none",
|
|
3347
|
+
borderBottom: active ? `2px solid ${theme.primary}` : "2px solid transparent",
|
|
3348
|
+
cursor: "pointer",
|
|
3349
|
+
fontFamily: "inherit"
|
|
3350
|
+
},
|
|
3351
|
+
children: tabLabels[tab]
|
|
3352
|
+
},
|
|
3353
|
+
tab
|
|
3354
|
+
);
|
|
3355
|
+
}) }),
|
|
3356
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { style: { flex: 1, overflow: isNarrow ? "auto" : "hidden", display: "flex", flexDirection: "column" }, children: [
|
|
3357
|
+
activeTab === "result" && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(ResultPanel, { data: resultData ?? null, logs, hideConsole, size }),
|
|
3358
|
+
isVisualizationTab && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
|
|
3359
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
3360
|
+
TimeTravelControls,
|
|
3361
|
+
{
|
|
3362
|
+
snapshots: activeSnapshots,
|
|
3363
|
+
selectedIndex: safeIdx,
|
|
3364
|
+
onIndexChange: handleSnapshotChange,
|
|
3365
|
+
size
|
|
3366
|
+
}
|
|
3367
|
+
),
|
|
3368
|
+
isInSubflow && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(SubflowBreadcrumb, { breadcrumbs, onNavigate: handleBreadcrumbNavigate }),
|
|
3369
|
+
isNarrow ? (
|
|
3370
|
+
/* ── Mobile: stacked vertical ── */
|
|
3371
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
|
|
3372
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: { height: 350, flexShrink: 0, overflow: "hidden" }, children: renderFlowchart && activeSpec && renderFlowchart({
|
|
3373
|
+
spec: activeSpec,
|
|
3374
|
+
snapshots: activeSnapshots,
|
|
3375
|
+
selectedIndex: safeIdx,
|
|
3376
|
+
onNodeClick: handleNodeClick
|
|
3377
|
+
}) }),
|
|
3378
|
+
showTreeSidebar && /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
|
|
3379
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(HLinePill, { label: leftLabel, expanded: leftExpanded, onClick: () => toggleLeft(!leftExpanded) }),
|
|
3380
|
+
leftExpanded && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: { maxHeight: 180, overflow: "auto", flexShrink: 0 }, children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
3381
|
+
SubflowTree,
|
|
3382
|
+
{
|
|
3383
|
+
spec,
|
|
3384
|
+
activeStage: rootOverlay.activeStage,
|
|
3385
|
+
doneStages: rootOverlay.doneStages,
|
|
3386
|
+
onNodeSelect: handleTreeNodeSelect
|
|
3387
|
+
}
|
|
3388
|
+
) })
|
|
3389
|
+
] }),
|
|
3390
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(HLinePill, { label: rightLabel, expanded: rightExpanded, onClick: () => toggleRight(!rightExpanded) }),
|
|
3391
|
+
rightExpanded && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: { maxHeight: 250, flexShrink: 0, display: "flex", flexDirection: "column", overflow: "hidden" }, children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
3392
|
+
DetailsContent,
|
|
3393
|
+
{
|
|
3394
|
+
snapshots: activeSnapshots,
|
|
3395
|
+
selectedIndex: safeIdx,
|
|
3396
|
+
narrativeEntries: activeNarrativeEntries,
|
|
3397
|
+
narrative: activeNarrative,
|
|
3398
|
+
size
|
|
3399
|
+
}
|
|
3400
|
+
) }),
|
|
3401
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(HLinePill, { label: bottomLabel, detail: `${activeSnapshots.length} stages`, expanded: timelineExpanded, onClick: toggleTimeline }),
|
|
3402
|
+
timelineExpanded && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: { flexShrink: 0, overflow: "hidden" }, children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(GanttTimeline, { snapshots: activeSnapshots, selectedIndex: safeIdx, onSelect: handleSnapshotChange, size }) })
|
|
3403
|
+
] })
|
|
3404
|
+
) : (
|
|
3405
|
+
/* ── Desktop: side-by-side ── */
|
|
3406
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
|
|
3407
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { style: { flex: 1, display: "flex", overflow: "hidden" }, children: [
|
|
3408
|
+
showTreeSidebar && (leftExpanded ? /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { style: { width: 220, flexShrink: 0, display: "flex", flexDirection: "row", overflow: "hidden" }, children: [
|
|
3409
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: { flex: 1, overflow: "auto" }, children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
3410
|
+
SubflowTree,
|
|
3411
|
+
{
|
|
3412
|
+
spec,
|
|
3413
|
+
activeStage: rootOverlay.activeStage,
|
|
3414
|
+
doneStages: rootOverlay.doneStages,
|
|
3415
|
+
onNodeSelect: handleTreeNodeSelect
|
|
3416
|
+
}
|
|
3417
|
+
) }),
|
|
3418
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(VLinePill, { label: leftLabel, expanded: true, side: "left", onClick: () => toggleLeft(false) })
|
|
3419
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(VLinePill, { label: leftLabel, expanded: false, side: "left", onClick: () => toggleLeft(true) })),
|
|
3420
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: { flex: 1, overflow: "hidden", minWidth: 0 }, children: renderFlowchart && activeSpec && renderFlowchart({
|
|
3421
|
+
spec: activeSpec,
|
|
3422
|
+
snapshots: activeSnapshots,
|
|
3423
|
+
selectedIndex: safeIdx,
|
|
3424
|
+
onNodeClick: handleNodeClick
|
|
3425
|
+
}) }),
|
|
3426
|
+
rightExpanded ? /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { style: { width: "38%", minWidth: 300, maxWidth: 500, display: "flex", flexDirection: "row", overflow: "hidden" }, children: [
|
|
3427
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(VLinePill, { label: rightLabel, expanded: true, onClick: () => toggleRight(false) }),
|
|
3428
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
3429
|
+
DetailsContent,
|
|
3430
|
+
{
|
|
3431
|
+
snapshots: activeSnapshots,
|
|
3432
|
+
selectedIndex: safeIdx,
|
|
3433
|
+
narrativeEntries: activeNarrativeEntries,
|
|
3434
|
+
narrative: activeNarrative,
|
|
3435
|
+
size,
|
|
3436
|
+
fillHeight: true
|
|
3437
|
+
}
|
|
3438
|
+
)
|
|
3439
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(VLinePill, { label: rightLabel, expanded: false, onClick: () => toggleRight(true) })
|
|
3440
|
+
] }),
|
|
3441
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(HLinePill, { label: bottomLabel, detail: `${activeSnapshots.length} stages`, expanded: timelineExpanded, onClick: toggleTimeline }),
|
|
3442
|
+
timelineExpanded && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: { flexShrink: 0, overflow: "hidden" }, children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(GanttTimeline, { snapshots: activeSnapshots, selectedIndex: safeIdx, onSelect: handleSnapshotChange, size }) })
|
|
3443
|
+
] })
|
|
3444
|
+
)
|
|
3445
|
+
] })
|
|
3446
|
+
] })
|
|
3447
|
+
]
|
|
3448
|
+
}
|
|
3449
|
+
);
|
|
2694
3450
|
}
|
|
2695
3451
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2696
3452
|
0 && (module.exports = {
|
|
@@ -2698,20 +3454,27 @@ function createSnapshots(stages) {
|
|
|
2698
3454
|
FootprintTheme,
|
|
2699
3455
|
GanttTimeline,
|
|
2700
3456
|
MemoryInspector,
|
|
3457
|
+
MemoryPanel,
|
|
2701
3458
|
NarrativeLog,
|
|
3459
|
+
NarrativePanel,
|
|
2702
3460
|
NarrativeTrace,
|
|
2703
3461
|
ResultPanel,
|
|
2704
3462
|
ScopeDiff,
|
|
2705
3463
|
SnapshotPanel,
|
|
2706
3464
|
StageDetailPanel,
|
|
3465
|
+
StoryNarrative,
|
|
2707
3466
|
SubflowTree,
|
|
2708
3467
|
TimeTravelControls,
|
|
2709
3468
|
coolDark,
|
|
3469
|
+
coolLight,
|
|
2710
3470
|
createSnapshots,
|
|
2711
3471
|
defaultTokens,
|
|
3472
|
+
rawDefaults,
|
|
3473
|
+
subflowResultToSnapshots,
|
|
2712
3474
|
themePresets,
|
|
2713
3475
|
toVisualizationSnapshots,
|
|
2714
3476
|
tokensToCSSVars,
|
|
3477
|
+
useDarkModeTokens,
|
|
2715
3478
|
useFootprintTheme,
|
|
2716
3479
|
warmDark,
|
|
2717
3480
|
warmLight
|