footprint-explainable-ui 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs ADDED
@@ -0,0 +1,1946 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var src_exports = {};
22
+ __export(src_exports, {
23
+ ExplainableShell: () => ExplainableShell,
24
+ FootprintTheme: () => FootprintTheme,
25
+ GanttTimeline: () => GanttTimeline,
26
+ MemoryInspector: () => MemoryInspector,
27
+ NarrativeLog: () => NarrativeLog,
28
+ NarrativeTrace: () => NarrativeTrace,
29
+ ResultPanel: () => ResultPanel,
30
+ ScopeDiff: () => ScopeDiff,
31
+ SnapshotPanel: () => SnapshotPanel,
32
+ TimeTravelControls: () => TimeTravelControls,
33
+ coolDark: () => coolDark,
34
+ createSnapshots: () => createSnapshots,
35
+ defaultTokens: () => defaultTokens,
36
+ themePresets: () => themePresets,
37
+ toVisualizationSnapshots: () => toVisualizationSnapshots,
38
+ tokensToCSSVars: () => tokensToCSSVars,
39
+ useFootprintTheme: () => useFootprintTheme,
40
+ warmDark: () => warmDark,
41
+ warmLight: () => warmLight
42
+ });
43
+ module.exports = __toCommonJS(src_exports);
44
+
45
+ // src/theme/ThemeProvider.tsx
46
+ var import_react = require("react");
47
+
48
+ // src/theme/tokens.ts
49
+ function tokensToCSSVars(tokens) {
50
+ const vars = {};
51
+ if (tokens.colors) {
52
+ const c = tokens.colors;
53
+ if (c.primary) vars["--fp-color-primary"] = c.primary;
54
+ if (c.success) vars["--fp-color-success"] = c.success;
55
+ if (c.error) vars["--fp-color-error"] = c.error;
56
+ if (c.warning) vars["--fp-color-warning"] = c.warning;
57
+ if (c.bgPrimary) vars["--fp-bg-primary"] = c.bgPrimary;
58
+ if (c.bgSecondary) vars["--fp-bg-secondary"] = c.bgSecondary;
59
+ if (c.bgTertiary) vars["--fp-bg-tertiary"] = c.bgTertiary;
60
+ if (c.textPrimary) vars["--fp-text-primary"] = c.textPrimary;
61
+ if (c.textSecondary) vars["--fp-text-secondary"] = c.textSecondary;
62
+ if (c.textMuted) vars["--fp-text-muted"] = c.textMuted;
63
+ if (c.border) vars["--fp-border"] = c.border;
64
+ }
65
+ if (tokens.radius) vars["--fp-radius"] = tokens.radius;
66
+ if (tokens.fontFamily?.sans) vars["--fp-font-sans"] = tokens.fontFamily.sans;
67
+ if (tokens.fontFamily?.mono) vars["--fp-font-mono"] = tokens.fontFamily.mono;
68
+ return vars;
69
+ }
70
+ var defaultTokens = {
71
+ colors: {
72
+ primary: "#6366f1",
73
+ success: "#22c55e",
74
+ error: "#ef4444",
75
+ warning: "#f59e0b",
76
+ bgPrimary: "#0f172a",
77
+ bgSecondary: "#1e293b",
78
+ bgTertiary: "#334155",
79
+ textPrimary: "#f8fafc",
80
+ textSecondary: "#94a3b8",
81
+ textMuted: "#64748b",
82
+ border: "#334155"
83
+ },
84
+ radius: "8px",
85
+ fontFamily: {
86
+ sans: "Inter, system-ui, -apple-system, sans-serif",
87
+ mono: "'JetBrains Mono', 'Fira Code', monospace"
88
+ }
89
+ };
90
+
91
+ // src/theme/ThemeProvider.tsx
92
+ var import_jsx_runtime = require("react/jsx-runtime");
93
+ var ThemeContext = (0, import_react.createContext)({});
94
+ function useFootprintTheme() {
95
+ return (0, import_react.useContext)(ThemeContext);
96
+ }
97
+ function FootprintTheme({ tokens = {}, children }) {
98
+ const cssVars = tokensToCSSVars(tokens);
99
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ThemeContext.Provider, { value: tokens, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: cssVars, className: "fp-theme-root", children }) });
100
+ }
101
+
102
+ // src/theme/styles.ts
103
+ function v(varName, fallback) {
104
+ return `var(${varName}, ${fallback})`;
105
+ }
106
+ var theme = {
107
+ primary: v("--fp-color-primary", "#6366f1"),
108
+ success: v("--fp-color-success", "#22c55e"),
109
+ error: v("--fp-color-error", "#ef4444"),
110
+ warning: v("--fp-color-warning", "#f59e0b"),
111
+ bgPrimary: v("--fp-bg-primary", "#0f172a"),
112
+ bgSecondary: v("--fp-bg-secondary", "#1e293b"),
113
+ bgTertiary: v("--fp-bg-tertiary", "#334155"),
114
+ textPrimary: v("--fp-text-primary", "#f8fafc"),
115
+ textSecondary: v("--fp-text-secondary", "#94a3b8"),
116
+ textMuted: v("--fp-text-muted", "#64748b"),
117
+ border: v("--fp-border", "#334155"),
118
+ radius: v("--fp-radius", "8px"),
119
+ fontSans: v("--fp-font-sans", "Inter, system-ui, -apple-system, sans-serif"),
120
+ fontMono: v("--fp-font-mono", "'JetBrains Mono', 'Fira Code', monospace")
121
+ };
122
+ var fontSize = {
123
+ compact: { label: 10, body: 11, small: 9 },
124
+ default: { label: 11, body: 12, small: 10 },
125
+ detailed: { label: 12, body: 13, small: 11 }
126
+ };
127
+ var padding = {
128
+ compact: 8,
129
+ default: 12,
130
+ detailed: 16
131
+ };
132
+
133
+ // src/theme/presets.ts
134
+ var coolDark = {
135
+ colors: {
136
+ primary: "#6366f1",
137
+ success: "#22c55e",
138
+ error: "#ef4444",
139
+ warning: "#f59e0b",
140
+ bgPrimary: "#0f172a",
141
+ bgSecondary: "#1e293b",
142
+ bgTertiary: "#334155",
143
+ textPrimary: "#f8fafc",
144
+ textSecondary: "#94a3b8",
145
+ textMuted: "#64748b",
146
+ border: "#334155"
147
+ },
148
+ radius: "8px",
149
+ fontFamily: {
150
+ sans: "Inter, system-ui, -apple-system, sans-serif",
151
+ mono: "'JetBrains Mono', 'Fira Code', monospace"
152
+ }
153
+ };
154
+ var warmDark = {
155
+ colors: {
156
+ primary: "#7c6cf0",
157
+ success: "#3dd68c",
158
+ error: "#f06292",
159
+ warning: "#ffb74d",
160
+ bgPrimary: "#1e1a2e",
161
+ bgSecondary: "#2a2540",
162
+ bgTertiary: "#3a3455",
163
+ textPrimary: "#f0e6d6",
164
+ textSecondary: "#a89eb4",
165
+ textMuted: "#6e6480",
166
+ border: "#3a3455"
167
+ },
168
+ radius: "8px",
169
+ fontFamily: {
170
+ sans: "Inter, system-ui, -apple-system, sans-serif",
171
+ mono: "'JetBrains Mono', 'Fira Code', monospace"
172
+ }
173
+ };
174
+ var warmLight = {
175
+ colors: {
176
+ primary: "#7c6cf0",
177
+ success: "#22a860",
178
+ error: "#d94452",
179
+ warning: "#e09030",
180
+ bgPrimary: "#faf5ef",
181
+ bgSecondary: "#f0e6d6",
182
+ bgTertiary: "#e4d5c3",
183
+ textPrimary: "#2e2938",
184
+ textSecondary: "#5c5468",
185
+ textMuted: "#8a7e96",
186
+ border: "#d6c8b4"
187
+ },
188
+ radius: "8px",
189
+ fontFamily: {
190
+ sans: "Inter, system-ui, -apple-system, sans-serif",
191
+ mono: "'JetBrains Mono', 'Fira Code', monospace"
192
+ }
193
+ };
194
+ var themePresets = {
195
+ coolDark,
196
+ warmDark,
197
+ warmLight
198
+ };
199
+
200
+ // src/components/MemoryInspector/MemoryInspector.tsx
201
+ var import_react2 = require("react");
202
+ var import_jsx_runtime2 = require("react/jsx-runtime");
203
+ function MemoryInspector({
204
+ data,
205
+ snapshots,
206
+ selectedIndex = 0,
207
+ showTypes = false,
208
+ highlightNew = true,
209
+ size = "default",
210
+ unstyled = false,
211
+ className,
212
+ style
213
+ }) {
214
+ const { memory, newKeys } = (0, import_react2.useMemo)(() => {
215
+ if (data) {
216
+ return { memory: data, newKeys: /* @__PURE__ */ new Set() };
217
+ }
218
+ if (!snapshots || snapshots.length === 0) {
219
+ return { memory: {}, newKeys: /* @__PURE__ */ new Set() };
220
+ }
221
+ const merged = {};
222
+ for (let i = 0; i <= Math.min(selectedIndex, snapshots.length - 1); i++) {
223
+ Object.assign(merged, snapshots[i]?.memory);
224
+ }
225
+ const nk = /* @__PURE__ */ new Set();
226
+ if (highlightNew && selectedIndex > 0) {
227
+ const prev = {};
228
+ for (let i = 0; i < selectedIndex; i++) {
229
+ Object.assign(prev, snapshots[i]?.memory);
230
+ }
231
+ const current = snapshots[selectedIndex]?.memory ?? {};
232
+ for (const k of Object.keys(current)) {
233
+ if (!(k in prev)) nk.add(k);
234
+ }
235
+ } else if (highlightNew && selectedIndex === 0 && snapshots[0]) {
236
+ for (const k of Object.keys(snapshots[0].memory)) nk.add(k);
237
+ }
238
+ return { memory: merged, newKeys: nk };
239
+ }, [data, snapshots, selectedIndex, highlightNew]);
240
+ const entries = Object.entries(memory);
241
+ const fs = fontSize[size];
242
+ const pad = padding[size];
243
+ if (unstyled) {
244
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className, style, "data-fp": "memory-inspector", children: [
245
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { "data-fp": "memory-label", children: "Memory State" }),
246
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("pre", { "data-fp": "memory-json", children: JSON.stringify(memory, null, 2) })
247
+ ] });
248
+ }
249
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
250
+ "div",
251
+ {
252
+ className,
253
+ style: {
254
+ padding: pad,
255
+ fontFamily: theme.fontSans,
256
+ ...style
257
+ },
258
+ "data-fp": "memory-inspector",
259
+ children: [
260
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
261
+ "span",
262
+ {
263
+ style: {
264
+ fontSize: fs.label,
265
+ fontWeight: 600,
266
+ color: theme.textMuted,
267
+ textTransform: "uppercase",
268
+ letterSpacing: "0.08em"
269
+ },
270
+ children: "Memory State"
271
+ }
272
+ ),
273
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
274
+ "div",
275
+ {
276
+ style: {
277
+ marginTop: 8,
278
+ background: theme.bgSecondary,
279
+ border: `1px solid ${theme.border}`,
280
+ borderRadius: theme.radius,
281
+ padding: `${pad}px ${pad + 4}px`,
282
+ fontFamily: theme.fontMono,
283
+ fontSize: fs.body,
284
+ lineHeight: 1.8
285
+ },
286
+ children: [
287
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { color: theme.textMuted }, children: "{" }),
288
+ entries.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
289
+ "div",
290
+ {
291
+ style: {
292
+ paddingLeft: 16,
293
+ color: theme.textMuted,
294
+ fontStyle: "italic"
295
+ },
296
+ children: "// empty"
297
+ }
298
+ ),
299
+ entries.map(([key, value], i) => {
300
+ const isNew = newKeys.has(key);
301
+ const isLast = i === entries.length - 1;
302
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
303
+ "div",
304
+ {
305
+ style: {
306
+ paddingLeft: 16,
307
+ background: isNew ? `color-mix(in srgb, ${theme.success} 10%, transparent)` : "transparent",
308
+ borderRadius: 4,
309
+ marginLeft: -4,
310
+ marginRight: -4,
311
+ paddingRight: 4
312
+ },
313
+ children: [
314
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { style: { color: theme.primary }, children: [
315
+ '"',
316
+ key,
317
+ '"'
318
+ ] }),
319
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { color: theme.textMuted }, children: ": " }),
320
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { color: theme.success }, children: formatValue(value) }),
321
+ showTypes && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
322
+ "span",
323
+ {
324
+ style: {
325
+ color: theme.textMuted,
326
+ fontSize: fs.small,
327
+ marginLeft: 8,
328
+ opacity: 0.6
329
+ },
330
+ children: [
331
+ "(",
332
+ typeof value,
333
+ ")"
334
+ ]
335
+ }
336
+ ),
337
+ !isLast && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { color: theme.textMuted }, children: "," })
338
+ ]
339
+ },
340
+ key
341
+ );
342
+ }),
343
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { color: theme.textMuted }, children: "}" })
344
+ ]
345
+ }
346
+ )
347
+ ]
348
+ }
349
+ );
350
+ }
351
+ function formatValue(value) {
352
+ if (typeof value === "string") return `"${value}"`;
353
+ if (typeof value === "object" && value !== null) return JSON.stringify(value);
354
+ return String(value);
355
+ }
356
+
357
+ // src/components/NarrativeLog/NarrativeLog.tsx
358
+ var import_react3 = require("react");
359
+ var import_jsx_runtime3 = require("react/jsx-runtime");
360
+ function NarrativeLog({
361
+ snapshots,
362
+ selectedIndex,
363
+ narrative,
364
+ size = "default",
365
+ unstyled = false,
366
+ className,
367
+ style
368
+ }) {
369
+ const entries = (0, import_react3.useMemo)(() => {
370
+ if (narrative) {
371
+ return [{ label: "Output", text: narrative, isCurrent: true }];
372
+ }
373
+ const idx = selectedIndex ?? snapshots.length - 1;
374
+ return snapshots.slice(0, idx + 1).map((s, i) => ({
375
+ label: s.stageLabel,
376
+ text: s.narrative,
377
+ isCurrent: i === idx
378
+ }));
379
+ }, [snapshots, selectedIndex, narrative]);
380
+ const fs = fontSize[size];
381
+ const pad = padding[size];
382
+ if (unstyled) {
383
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className, style, "data-fp": "narrative-log", children: entries.map((entry, i) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { "data-fp": "narrative-entry", "data-current": entry.isCurrent, children: [
384
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("strong", { children: entry.label }),
385
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { children: entry.text })
386
+ ] }, i)) });
387
+ }
388
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
389
+ "div",
390
+ {
391
+ className,
392
+ style: { padding: pad, fontFamily: theme.fontSans, ...style },
393
+ "data-fp": "narrative-log",
394
+ children: [
395
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
396
+ "span",
397
+ {
398
+ style: {
399
+ fontSize: fs.label,
400
+ fontWeight: 600,
401
+ color: theme.textMuted,
402
+ textTransform: "uppercase",
403
+ letterSpacing: "0.08em"
404
+ },
405
+ children: "Execution Log"
406
+ }
407
+ ),
408
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { marginTop: 8, display: "flex", flexDirection: "column" }, children: entries.map((entry, i) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
409
+ "div",
410
+ {
411
+ style: {
412
+ display: "flex",
413
+ gap: 10,
414
+ padding: `${pad}px 0`,
415
+ borderBottom: i < entries.length - 1 ? `1px solid ${theme.border}` : "none"
416
+ },
417
+ children: [
418
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
419
+ "div",
420
+ {
421
+ style: {
422
+ display: "flex",
423
+ flexDirection: "column",
424
+ alignItems: "center",
425
+ width: 12,
426
+ flexShrink: 0,
427
+ paddingTop: 5
428
+ },
429
+ children: [
430
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
431
+ "div",
432
+ {
433
+ style: {
434
+ width: 8,
435
+ height: 8,
436
+ borderRadius: "50%",
437
+ background: entry.isCurrent ? theme.primary : theme.success,
438
+ flexShrink: 0
439
+ }
440
+ }
441
+ ),
442
+ i < entries.length - 1 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
443
+ "div",
444
+ {
445
+ style: {
446
+ width: 1,
447
+ flex: 1,
448
+ background: theme.border,
449
+ marginTop: 4
450
+ }
451
+ }
452
+ )
453
+ ]
454
+ }
455
+ ),
456
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { flex: 1, minWidth: 0 }, children: [
457
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
458
+ "span",
459
+ {
460
+ style: {
461
+ fontSize: fs.label,
462
+ fontWeight: 600,
463
+ color: entry.isCurrent ? theme.primary : theme.textMuted
464
+ },
465
+ children: entry.label
466
+ }
467
+ ),
468
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
469
+ "div",
470
+ {
471
+ style: {
472
+ fontSize: fs.body,
473
+ lineHeight: 1.5,
474
+ color: entry.isCurrent ? theme.textPrimary : theme.textSecondary,
475
+ marginTop: 2
476
+ },
477
+ children: entry.text
478
+ }
479
+ )
480
+ ] })
481
+ ]
482
+ },
483
+ i
484
+ )) })
485
+ ]
486
+ }
487
+ );
488
+ }
489
+
490
+ // src/components/NarrativeTrace/NarrativeTrace.tsx
491
+ var import_react4 = require("react");
492
+ var import_jsx_runtime4 = require("react/jsx-runtime");
493
+ function parseGroups(lines) {
494
+ const groups = [];
495
+ let current = null;
496
+ for (let i = 0; i < lines.length; i++) {
497
+ const line = lines[i];
498
+ const trimmed = line.trimStart();
499
+ const isStep = trimmed.startsWith("Step ") || /^\s/.test(line);
500
+ if (!isStep || !current) {
501
+ current = { header: line, headerIdx: i, steps: [] };
502
+ groups.push(current);
503
+ } else {
504
+ current.steps.push({ text: trimmed, idx: i });
505
+ }
506
+ }
507
+ return groups;
508
+ }
509
+ function NarrativeTrace({
510
+ narrative,
511
+ revealedCount,
512
+ defaultCollapsed = false,
513
+ onStageClick,
514
+ size = "default",
515
+ unstyled = false,
516
+ className,
517
+ style
518
+ }) {
519
+ const revealed = revealedCount != null ? narrative.slice(0, revealedCount) : narrative;
520
+ const future = revealedCount != null ? narrative.slice(revealedCount) : [];
521
+ const revealedGroups = (0, import_react4.useMemo)(() => parseGroups(revealed), [revealed]);
522
+ const futureGroups = (0, import_react4.useMemo)(() => parseGroups(future), [future]);
523
+ const [collapsedSet, setCollapsedSet] = (0, import_react4.useState)(() => {
524
+ if (!defaultCollapsed) return /* @__PURE__ */ new Set();
525
+ return new Set(parseGroups(narrative).map((g) => g.headerIdx));
526
+ });
527
+ const latestRef = (0, import_react4.useRef)(null);
528
+ (0, import_react4.useEffect)(() => {
529
+ latestRef.current?.scrollIntoView({ behavior: "smooth", block: "nearest" });
530
+ }, [revealedGroups.length]);
531
+ const toggle = (0, import_react4.useCallback)((idx) => {
532
+ setCollapsedSet((prev) => {
533
+ const next = new Set(prev);
534
+ if (next.has(idx)) next.delete(idx);
535
+ else next.add(idx);
536
+ return next;
537
+ });
538
+ }, []);
539
+ const lastIdx = revealedGroups.length - 1;
540
+ const fs = fontSize[size];
541
+ const pad = padding[size];
542
+ if (unstyled) {
543
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className, style, "data-fp": "narrative-trace", children: [
544
+ revealedGroups.map((group, gi) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { "data-fp": "narrative-group", "data-latest": gi === lastIdx, children: [
545
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
546
+ "div",
547
+ {
548
+ "data-fp": "narrative-header",
549
+ "data-collapsible": group.steps.length > 0,
550
+ "data-collapsed": collapsedSet.has(group.headerIdx),
551
+ onClick: () => {
552
+ if (group.steps.length > 0) toggle(group.headerIdx);
553
+ onStageClick?.(group.headerIdx);
554
+ },
555
+ children: group.header
556
+ }
557
+ ),
558
+ !collapsedSet.has(group.headerIdx) && group.steps.map((step) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { "data-fp": "narrative-step", children: step.text }, step.idx))
559
+ ] }, group.headerIdx)),
560
+ futureGroups.map((group) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { "data-fp": "narrative-group", "data-future": true, children: [
561
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { "data-fp": "narrative-header", children: group.header }),
562
+ group.steps.map((step) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { "data-fp": "narrative-step", children: step.text }, `f-${step.idx}`))
563
+ ] }, `f-${group.headerIdx}`))
564
+ ] });
565
+ }
566
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
567
+ "div",
568
+ {
569
+ className,
570
+ style: {
571
+ flex: 1,
572
+ overflow: "auto",
573
+ padding: pad,
574
+ fontFamily: theme.fontMono,
575
+ ...style
576
+ },
577
+ "data-fp": "narrative-trace",
578
+ children: [
579
+ revealedGroups.map((group, gi) => {
580
+ const isLatest = gi === lastIdx;
581
+ const isCollapsed = collapsedSet.has(group.headerIdx);
582
+ const hasSteps = group.steps.length > 0;
583
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
584
+ "div",
585
+ {
586
+ ref: isLatest ? latestRef : void 0,
587
+ style: { marginBottom: 2 },
588
+ "data-fp": "narrative-group",
589
+ children: [
590
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
591
+ "div",
592
+ {
593
+ onClick: () => {
594
+ if (hasSteps) toggle(group.headerIdx);
595
+ onStageClick?.(group.headerIdx);
596
+ },
597
+ style: {
598
+ fontSize: fs.body,
599
+ lineHeight: 1.7,
600
+ color: isLatest ? theme.textPrimary : theme.textSecondary,
601
+ padding: `4px ${pad - 4}px`,
602
+ borderRadius: 4,
603
+ background: isLatest ? theme.bgTertiary : "transparent",
604
+ borderLeft: isLatest ? `3px solid ${theme.primary}` : `3px solid ${theme.success}`,
605
+ cursor: hasSteps ? "pointer" : "default",
606
+ fontWeight: 600,
607
+ display: "flex",
608
+ alignItems: "center",
609
+ gap: 6,
610
+ userSelect: "none",
611
+ transition: "all 0.15s ease"
612
+ },
613
+ children: [
614
+ hasSteps && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
615
+ "span",
616
+ {
617
+ style: {
618
+ fontSize: fs.small - 1,
619
+ color: theme.textMuted,
620
+ transition: "transform 0.15s ease",
621
+ transform: isCollapsed ? "rotate(-90deg)" : "rotate(0deg)",
622
+ display: "inline-block",
623
+ width: 10,
624
+ flexShrink: 0
625
+ },
626
+ children: "\u25BC"
627
+ }
628
+ ),
629
+ !hasSteps && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { width: 10, flexShrink: 0 } }),
630
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: group.header })
631
+ ]
632
+ }
633
+ ),
634
+ !isCollapsed && group.steps.map((step) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
635
+ "div",
636
+ {
637
+ style: {
638
+ fontSize: fs.small,
639
+ lineHeight: 1.6,
640
+ color: isLatest ? theme.textSecondary : theme.textMuted,
641
+ padding: `2px ${pad - 4}px 2px ${pad + 20}px`,
642
+ opacity: isLatest ? 0.9 : 0.7,
643
+ transition: "all 0.15s ease"
644
+ },
645
+ "data-fp": "narrative-step",
646
+ children: step.text
647
+ },
648
+ step.idx
649
+ ))
650
+ ]
651
+ },
652
+ group.headerIdx
653
+ );
654
+ }),
655
+ futureGroups.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { opacity: 0.2 }, children: futureGroups.map((group) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { marginBottom: 2 }, children: [
656
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
657
+ "div",
658
+ {
659
+ style: {
660
+ fontSize: fs.body,
661
+ lineHeight: 1.7,
662
+ color: theme.textMuted,
663
+ padding: `4px ${pad - 4}px`,
664
+ borderLeft: `3px solid ${theme.border}`,
665
+ fontWeight: 600,
666
+ paddingLeft: pad + 12
667
+ },
668
+ children: group.header
669
+ }
670
+ ),
671
+ group.steps.map((step) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
672
+ "div",
673
+ {
674
+ style: {
675
+ fontSize: fs.small,
676
+ lineHeight: 1.6,
677
+ color: theme.textMuted,
678
+ padding: `2px ${pad - 4}px 2px ${pad + 20}px`
679
+ },
680
+ children: step.text
681
+ },
682
+ `f-${step.idx}`
683
+ ))
684
+ ] }, `f-${group.headerIdx}`)) })
685
+ ]
686
+ }
687
+ );
688
+ }
689
+
690
+ // src/components/GanttTimeline/GanttTimeline.tsx
691
+ var import_react5 = require("react");
692
+ var import_jsx_runtime5 = require("react/jsx-runtime");
693
+ function GanttTimeline({
694
+ snapshots,
695
+ selectedIndex = 0,
696
+ onSelect,
697
+ size = "default",
698
+ unstyled = false,
699
+ className,
700
+ style
701
+ }) {
702
+ const totalWallTime = (0, import_react5.useMemo)(
703
+ () => Math.max(...snapshots.map((s) => s.startMs + s.durationMs), 1),
704
+ [snapshots]
705
+ );
706
+ const fs = fontSize[size];
707
+ const pad = padding[size];
708
+ const labelWidth = size === "compact" ? 50 : size === "detailed" ? 100 : 80;
709
+ const msWidth = size === "compact" ? 28 : 36;
710
+ if (unstyled) {
711
+ 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)(
712
+ "div",
713
+ {
714
+ "data-fp": "gantt-bar",
715
+ "data-selected": idx === selectedIndex,
716
+ "data-visible": idx <= selectedIndex,
717
+ onClick: () => onSelect?.(idx),
718
+ children: [
719
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { "data-fp": "gantt-label", children: snap.stageLabel }),
720
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { "data-fp": "gantt-duration", children: [
721
+ snap.durationMs,
722
+ "ms"
723
+ ] })
724
+ ]
725
+ },
726
+ snap.stageName
727
+ )) });
728
+ }
729
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
730
+ "div",
731
+ {
732
+ className,
733
+ style: { padding: pad, fontFamily: theme.fontSans, ...style },
734
+ "data-fp": "gantt-timeline",
735
+ children: [
736
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
737
+ "span",
738
+ {
739
+ style: {
740
+ fontSize: fs.label,
741
+ fontWeight: 600,
742
+ color: theme.textMuted,
743
+ textTransform: "uppercase",
744
+ letterSpacing: "0.08em"
745
+ },
746
+ children: size === "compact" ? "Timeline" : "Execution Timeline"
747
+ }
748
+ ),
749
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
750
+ "div",
751
+ {
752
+ style: {
753
+ marginTop: 8,
754
+ display: "flex",
755
+ flexDirection: "column",
756
+ gap: 4
757
+ },
758
+ children: snapshots.map((snap, idx) => {
759
+ const leftPct = snap.startMs / totalWallTime * 100;
760
+ const widthPct = Math.max(snap.durationMs / totalWallTime * 100, 1);
761
+ const isSelected = idx === selectedIndex;
762
+ const isVisible = idx <= selectedIndex;
763
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
764
+ "div",
765
+ {
766
+ onClick: () => onSelect?.(idx),
767
+ style: {
768
+ display: "flex",
769
+ alignItems: "center",
770
+ gap: size === "compact" ? 4 : 8,
771
+ cursor: onSelect ? "pointer" : "default",
772
+ opacity: isVisible ? 1 : 0.3,
773
+ transition: "opacity 0.3s ease"
774
+ },
775
+ children: [
776
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
777
+ "span",
778
+ {
779
+ style: {
780
+ width: labelWidth,
781
+ fontSize: fs.small,
782
+ color: isSelected ? theme.primary : theme.textMuted,
783
+ fontWeight: isSelected ? 600 : 400,
784
+ textAlign: "right",
785
+ flexShrink: 0,
786
+ overflow: "hidden",
787
+ textOverflow: "ellipsis",
788
+ whiteSpace: "nowrap"
789
+ },
790
+ children: snap.stageLabel
791
+ }
792
+ ),
793
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
794
+ "div",
795
+ {
796
+ style: {
797
+ flex: 1,
798
+ height: size === "compact" ? 6 : 8,
799
+ position: "relative",
800
+ background: theme.bgTertiary,
801
+ borderRadius: 3
802
+ },
803
+ children: isVisible && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
804
+ "div",
805
+ {
806
+ style: {
807
+ position: "absolute",
808
+ left: `${leftPct}%`,
809
+ top: 0,
810
+ width: `${widthPct}%`,
811
+ height: "100%",
812
+ borderRadius: 3,
813
+ background: isSelected ? theme.primary : theme.success,
814
+ transition: "width 0.3s ease"
815
+ }
816
+ }
817
+ )
818
+ }
819
+ ),
820
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
821
+ "span",
822
+ {
823
+ style: {
824
+ fontSize: fs.small,
825
+ color: theme.textMuted,
826
+ fontFamily: theme.fontMono,
827
+ width: msWidth,
828
+ flexShrink: 0
829
+ },
830
+ children: [
831
+ snap.durationMs,
832
+ "ms"
833
+ ]
834
+ }
835
+ )
836
+ ]
837
+ },
838
+ snap.stageName
839
+ );
840
+ })
841
+ }
842
+ ),
843
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
844
+ "div",
845
+ {
846
+ style: {
847
+ marginTop: 4,
848
+ marginLeft: labelWidth + (size === "compact" ? 4 : 8),
849
+ marginRight: msWidth + (size === "compact" ? 4 : 8),
850
+ display: "flex",
851
+ justifyContent: "space-between",
852
+ fontSize: fs.small - 1,
853
+ color: theme.textMuted,
854
+ fontFamily: theme.fontMono
855
+ },
856
+ children: [
857
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: "0ms" }),
858
+ size !== "compact" && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { children: [
859
+ (totalWallTime / 2).toFixed(1),
860
+ "ms"
861
+ ] }),
862
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { children: [
863
+ totalWallTime.toFixed(1),
864
+ "ms"
865
+ ] })
866
+ ]
867
+ }
868
+ )
869
+ ]
870
+ }
871
+ );
872
+ }
873
+
874
+ // src/components/SnapshotPanel/SnapshotPanel.tsx
875
+ var import_react6 = require("react");
876
+ var import_jsx_runtime6 = require("react/jsx-runtime");
877
+ function SnapshotPanel({
878
+ snapshots,
879
+ showGantt = true,
880
+ showScrubber = true,
881
+ title = "Pipeline Inspector",
882
+ size = "default",
883
+ unstyled = false,
884
+ className,
885
+ style
886
+ }) {
887
+ const [selectedIndex, setSelectedIndex] = (0, import_react6.useState)(0);
888
+ const fs = fontSize[size];
889
+ const pad = padding[size];
890
+ if (snapshots.length === 0) {
891
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
892
+ "div",
893
+ {
894
+ className,
895
+ style: {
896
+ padding: pad * 2,
897
+ textAlign: "center",
898
+ color: unstyled ? void 0 : theme.textMuted,
899
+ fontSize: fs.body,
900
+ ...style
901
+ },
902
+ "data-fp": "snapshot-panel",
903
+ children: "No snapshots to display"
904
+ }
905
+ );
906
+ }
907
+ if (unstyled) {
908
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className, style, "data-fp": "snapshot-panel", children: [
909
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("h3", { children: title }),
910
+ showScrubber && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
911
+ "input",
912
+ {
913
+ type: "range",
914
+ min: 0,
915
+ max: snapshots.length - 1,
916
+ value: selectedIndex,
917
+ onChange: (e) => setSelectedIndex(parseInt(e.target.value))
918
+ }
919
+ ),
920
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
921
+ MemoryInspector,
922
+ {
923
+ snapshots,
924
+ selectedIndex,
925
+ unstyled: true
926
+ }
927
+ ),
928
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
929
+ NarrativeLog,
930
+ {
931
+ snapshots,
932
+ selectedIndex,
933
+ unstyled: true
934
+ }
935
+ ),
936
+ showGantt && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
937
+ GanttTimeline,
938
+ {
939
+ snapshots,
940
+ selectedIndex,
941
+ onSelect: setSelectedIndex,
942
+ unstyled: true
943
+ }
944
+ )
945
+ ] });
946
+ }
947
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
948
+ "div",
949
+ {
950
+ className,
951
+ style: {
952
+ display: "flex",
953
+ flexDirection: "column",
954
+ height: "100%",
955
+ background: theme.bgPrimary,
956
+ fontFamily: theme.fontSans,
957
+ overflow: "hidden",
958
+ ...style
959
+ },
960
+ "data-fp": "snapshot-panel",
961
+ children: [
962
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
963
+ "div",
964
+ {
965
+ style: {
966
+ padding: `${pad}px ${pad + 4}px`,
967
+ borderBottom: `1px solid ${theme.border}`,
968
+ background: theme.bgSecondary,
969
+ flexShrink: 0
970
+ },
971
+ children: [
972
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
973
+ "div",
974
+ {
975
+ style: {
976
+ display: "flex",
977
+ alignItems: "center",
978
+ gap: 8,
979
+ marginBottom: showScrubber ? 8 : 0
980
+ },
981
+ children: [
982
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
983
+ "span",
984
+ {
985
+ style: {
986
+ fontSize: fs.body + 2,
987
+ fontWeight: 600,
988
+ color: theme.textPrimary
989
+ },
990
+ children: title
991
+ }
992
+ ),
993
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
994
+ "span",
995
+ {
996
+ style: {
997
+ fontSize: fs.small,
998
+ color: theme.textMuted,
999
+ fontFamily: theme.fontMono
1000
+ },
1001
+ children: [
1002
+ selectedIndex + 1,
1003
+ "/",
1004
+ snapshots.length
1005
+ ]
1006
+ }
1007
+ )
1008
+ ]
1009
+ }
1010
+ ),
1011
+ showScrubber && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 8 }, children: [
1012
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1013
+ ScrubButton,
1014
+ {
1015
+ label: "\\u25C0",
1016
+ disabled: selectedIndex === 0,
1017
+ onClick: () => setSelectedIndex((i) => Math.max(0, i - 1))
1018
+ }
1019
+ ),
1020
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1021
+ "input",
1022
+ {
1023
+ type: "range",
1024
+ min: 0,
1025
+ max: snapshots.length - 1,
1026
+ value: selectedIndex,
1027
+ onChange: (e) => setSelectedIndex(parseInt(e.target.value)),
1028
+ style: {
1029
+ flex: 1,
1030
+ height: 4,
1031
+ accentColor: theme.primary,
1032
+ cursor: "pointer"
1033
+ }
1034
+ }
1035
+ ),
1036
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1037
+ ScrubButton,
1038
+ {
1039
+ label: "\\u25B6",
1040
+ disabled: selectedIndex === snapshots.length - 1,
1041
+ onClick: () => setSelectedIndex((i) => Math.min(snapshots.length - 1, i + 1))
1042
+ }
1043
+ )
1044
+ ] })
1045
+ ]
1046
+ }
1047
+ ),
1048
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { flex: 1, overflow: "auto" }, children: [
1049
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1050
+ MemoryInspector,
1051
+ {
1052
+ snapshots,
1053
+ selectedIndex,
1054
+ size
1055
+ }
1056
+ ),
1057
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1058
+ "div",
1059
+ {
1060
+ style: {
1061
+ height: 1,
1062
+ background: theme.border,
1063
+ margin: `0 ${pad}px`
1064
+ }
1065
+ }
1066
+ ),
1067
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1068
+ NarrativeLog,
1069
+ {
1070
+ snapshots,
1071
+ selectedIndex,
1072
+ size
1073
+ }
1074
+ )
1075
+ ] }),
1076
+ showGantt && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1077
+ "div",
1078
+ {
1079
+ style: {
1080
+ borderTop: `1px solid ${theme.border}`,
1081
+ background: theme.bgSecondary,
1082
+ flexShrink: 0
1083
+ },
1084
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1085
+ GanttTimeline,
1086
+ {
1087
+ snapshots,
1088
+ selectedIndex,
1089
+ onSelect: setSelectedIndex,
1090
+ size
1091
+ }
1092
+ )
1093
+ }
1094
+ )
1095
+ ]
1096
+ }
1097
+ );
1098
+ }
1099
+ function ScrubButton({
1100
+ label,
1101
+ disabled,
1102
+ onClick
1103
+ }) {
1104
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1105
+ "button",
1106
+ {
1107
+ onClick,
1108
+ disabled,
1109
+ style: {
1110
+ background: theme.bgTertiary,
1111
+ border: `1px solid ${theme.border}`,
1112
+ color: disabled ? theme.textMuted : theme.textPrimary,
1113
+ borderRadius: 6,
1114
+ width: 28,
1115
+ height: 28,
1116
+ display: "flex",
1117
+ alignItems: "center",
1118
+ justifyContent: "center",
1119
+ cursor: disabled ? "not-allowed" : "pointer",
1120
+ opacity: disabled ? 0.5 : 1,
1121
+ fontSize: 12,
1122
+ flexShrink: 0
1123
+ },
1124
+ children: label
1125
+ }
1126
+ );
1127
+ }
1128
+
1129
+ // src/components/ScopeDiff/ScopeDiff.tsx
1130
+ var import_react7 = require("react");
1131
+ var import_jsx_runtime7 = require("react/jsx-runtime");
1132
+ function computeDiff(prev, curr) {
1133
+ const entries = [];
1134
+ const allKeys = /* @__PURE__ */ new Set([...Object.keys(prev ?? {}), ...Object.keys(curr)]);
1135
+ for (const key of allKeys) {
1136
+ const inPrev = prev != null && key in prev;
1137
+ const inCurr = key in curr;
1138
+ const oldVal = prev?.[key];
1139
+ const newVal = curr[key];
1140
+ if (!inPrev && inCurr) {
1141
+ entries.push({ key, type: "added", newValue: newVal });
1142
+ } else if (inPrev && !inCurr) {
1143
+ entries.push({ key, type: "removed", oldValue: oldVal });
1144
+ } else if (JSON.stringify(oldVal) !== JSON.stringify(newVal)) {
1145
+ entries.push({ key, type: "changed", oldValue: oldVal, newValue: newVal });
1146
+ } else {
1147
+ entries.push({ key, type: "unchanged", newValue: newVal });
1148
+ }
1149
+ }
1150
+ const order = { added: 0, changed: 1, removed: 2, unchanged: 3 };
1151
+ entries.sort((a, b) => order[a.type] - order[b.type]);
1152
+ return entries;
1153
+ }
1154
+ function fmt(v2) {
1155
+ if (typeof v2 === "string") return `"${v2}"`;
1156
+ if (typeof v2 === "object" && v2 !== null) return JSON.stringify(v2, null, 2);
1157
+ return String(v2);
1158
+ }
1159
+ var diffColors = {
1160
+ added: { bg: "rgba(34,197,94,0.10)", fg: "#22c55e", icon: "+" },
1161
+ removed: { bg: "rgba(239,68,68,0.10)", fg: "#ef4444", icon: "-" },
1162
+ changed: { bg: "rgba(245,158,11,0.10)", fg: "#f59e0b", icon: "~" },
1163
+ unchanged: { bg: "transparent", fg: "", icon: " " }
1164
+ };
1165
+ function ScopeDiff({
1166
+ previous,
1167
+ current,
1168
+ hideUnchanged = false,
1169
+ size = "default",
1170
+ unstyled = false,
1171
+ className,
1172
+ style
1173
+ }) {
1174
+ const entries = (0, import_react7.useMemo)(() => computeDiff(previous, current), [previous, current]);
1175
+ const visible = hideUnchanged ? entries.filter((e) => e.type !== "unchanged") : entries;
1176
+ const fs = fontSize[size];
1177
+ const pad = padding[size];
1178
+ if (unstyled) {
1179
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className, style, "data-fp": "scope-diff", children: visible.map((e) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { "data-fp": "diff-entry", "data-type": e.type, children: [
1180
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { "data-fp": "diff-key", children: e.key }),
1181
+ e.type === "changed" && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
1182
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { "data-fp": "diff-old", children: fmt(e.oldValue) }),
1183
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { "data-fp": "diff-new", children: fmt(e.newValue) })
1184
+ ] }),
1185
+ (e.type === "added" || e.type === "unchanged") && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { "data-fp": "diff-value", children: fmt(e.newValue) }),
1186
+ e.type === "removed" && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { "data-fp": "diff-value", children: fmt(e.oldValue) })
1187
+ ] }, e.key)) });
1188
+ }
1189
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
1190
+ "div",
1191
+ {
1192
+ className,
1193
+ style: { padding: pad, fontFamily: theme.fontMono, ...style },
1194
+ "data-fp": "scope-diff",
1195
+ children: [
1196
+ visible.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: { fontSize: fs.body, color: theme.textMuted, fontStyle: "italic" }, children: "No changes" }),
1197
+ visible.map((entry) => {
1198
+ const dc = diffColors[entry.type];
1199
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
1200
+ "div",
1201
+ {
1202
+ style: {
1203
+ display: "flex",
1204
+ alignItems: "flex-start",
1205
+ gap: 8,
1206
+ padding: `4px ${pad - 4}px`,
1207
+ marginBottom: 2,
1208
+ borderRadius: 4,
1209
+ background: dc.bg,
1210
+ fontSize: fs.body,
1211
+ lineHeight: 1.5
1212
+ },
1213
+ "data-fp": "diff-entry",
1214
+ children: [
1215
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1216
+ "span",
1217
+ {
1218
+ style: {
1219
+ width: 16,
1220
+ flexShrink: 0,
1221
+ fontWeight: 700,
1222
+ color: dc.fg || theme.textMuted,
1223
+ textAlign: "center"
1224
+ },
1225
+ children: dc.icon
1226
+ }
1227
+ ),
1228
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: { color: theme.primary, fontWeight: 600, flexShrink: 0 }, children: entry.key }),
1229
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: { color: theme.textMuted }, children: "=" }),
1230
+ entry.type === "changed" ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { children: [
1231
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1232
+ "span",
1233
+ {
1234
+ style: {
1235
+ color: theme.error,
1236
+ textDecoration: "line-through",
1237
+ opacity: 0.7
1238
+ },
1239
+ children: fmt(entry.oldValue)
1240
+ }
1241
+ ),
1242
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: { color: theme.textMuted, margin: "0 4px" }, children: "\u2192" }),
1243
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: { color: theme.success }, children: fmt(entry.newValue) })
1244
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1245
+ "span",
1246
+ {
1247
+ style: {
1248
+ color: entry.type === "added" ? theme.success : entry.type === "removed" ? theme.error : theme.textPrimary
1249
+ },
1250
+ children: fmt(entry.type === "removed" ? entry.oldValue : entry.newValue)
1251
+ }
1252
+ )
1253
+ ]
1254
+ },
1255
+ entry.key
1256
+ );
1257
+ })
1258
+ ]
1259
+ }
1260
+ );
1261
+ }
1262
+
1263
+ // src/components/ResultPanel/ResultPanel.tsx
1264
+ var import_jsx_runtime8 = require("react/jsx-runtime");
1265
+ function ResultPanel({
1266
+ data,
1267
+ logs = [],
1268
+ hideConsole = false,
1269
+ size = "default",
1270
+ unstyled = false,
1271
+ className,
1272
+ style
1273
+ }) {
1274
+ const fs = fontSize[size];
1275
+ const pad = padding[size];
1276
+ if (unstyled) {
1277
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className, style, "data-fp": "result-panel", children: [
1278
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { "data-fp": "result-data", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("pre", { children: data ? JSON.stringify(data, null, 2) : "No data" }) }),
1279
+ !hideConsole && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { "data-fp": "result-console", children: logs.map((line, i) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { "data-fp": "console-line", "data-error": line.startsWith("ERROR"), children: line }, i)) })
1280
+ ] });
1281
+ }
1282
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
1283
+ "div",
1284
+ {
1285
+ className,
1286
+ style: {
1287
+ height: "100%",
1288
+ display: "flex",
1289
+ flexDirection: "column",
1290
+ overflow: "hidden",
1291
+ ...style
1292
+ },
1293
+ "data-fp": "result-panel",
1294
+ children: [
1295
+ data && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { style: { flex: 1, overflow: "auto", padding: pad }, children: [
1296
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1297
+ "div",
1298
+ {
1299
+ style: {
1300
+ fontSize: fs.label,
1301
+ fontWeight: 600,
1302
+ color: theme.textMuted,
1303
+ textTransform: "uppercase",
1304
+ letterSpacing: "0.08em",
1305
+ marginBottom: 8
1306
+ },
1307
+ children: size === "compact" ? "Result" : "Business Result (Scope)"
1308
+ }
1309
+ ),
1310
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1311
+ "pre",
1312
+ {
1313
+ style: {
1314
+ fontSize: fs.body,
1315
+ fontFamily: theme.fontMono,
1316
+ color: theme.textPrimary,
1317
+ background: theme.bgSecondary,
1318
+ padding: pad,
1319
+ borderRadius: theme.radius,
1320
+ overflow: "auto",
1321
+ margin: 0
1322
+ },
1323
+ children: JSON.stringify(data, null, 2)
1324
+ }
1325
+ )
1326
+ ] }),
1327
+ !hideConsole && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
1328
+ "div",
1329
+ {
1330
+ style: {
1331
+ borderTop: `1px solid ${theme.border}`,
1332
+ padding: pad,
1333
+ overflow: "auto",
1334
+ maxHeight: "40%",
1335
+ flexShrink: 0
1336
+ },
1337
+ children: [
1338
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1339
+ "div",
1340
+ {
1341
+ style: {
1342
+ fontSize: fs.label,
1343
+ fontWeight: 600,
1344
+ color: theme.textMuted,
1345
+ textTransform: "uppercase",
1346
+ letterSpacing: "0.08em",
1347
+ marginBottom: 8
1348
+ },
1349
+ children: "Console"
1350
+ }
1351
+ ),
1352
+ logs.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { style: { fontSize: fs.body, color: theme.textMuted, fontStyle: "italic" }, children: "No console output" }),
1353
+ logs.map((line, i) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1354
+ "div",
1355
+ {
1356
+ style: {
1357
+ fontSize: fs.body,
1358
+ fontFamily: theme.fontMono,
1359
+ color: line.startsWith("ERROR") ? theme.error : theme.textPrimary,
1360
+ padding: "2px 0",
1361
+ borderBottom: `1px solid ${theme.bgSecondary}`,
1362
+ whiteSpace: "pre-wrap",
1363
+ wordBreak: "break-word"
1364
+ },
1365
+ children: line
1366
+ },
1367
+ i
1368
+ ))
1369
+ ]
1370
+ }
1371
+ )
1372
+ ]
1373
+ }
1374
+ );
1375
+ }
1376
+
1377
+ // src/components/TimeTravelControls/TimeTravelControls.tsx
1378
+ var import_react8 = require("react");
1379
+ var import_jsx_runtime9 = require("react/jsx-runtime");
1380
+ function TimeTravelControls({
1381
+ snapshots,
1382
+ selectedIndex,
1383
+ onIndexChange,
1384
+ autoPlayable = true,
1385
+ size = "default",
1386
+ unstyled = false,
1387
+ className,
1388
+ style
1389
+ }) {
1390
+ const [playing, setPlaying] = (0, import_react8.useState)(false);
1391
+ const playRef = (0, import_react8.useRef)(null);
1392
+ const total = snapshots.length;
1393
+ const canPrev = selectedIndex > 0;
1394
+ const canNext = selectedIndex < total - 1;
1395
+ (0, import_react8.useEffect)(() => {
1396
+ if (!playing || !autoPlayable) return;
1397
+ if (selectedIndex >= total - 1) {
1398
+ setPlaying(false);
1399
+ return;
1400
+ }
1401
+ const stageDur = snapshots[selectedIndex]?.durationMs ?? 1;
1402
+ const totalDur = snapshots.reduce((s, snap) => s + snap.durationMs, 0) || 1;
1403
+ const fraction = stageDur / totalDur;
1404
+ const baseMs = 3e3;
1405
+ const delay = Math.max(200, Math.min(fraction * baseMs, 2e3));
1406
+ playRef.current = setTimeout(() => {
1407
+ onIndexChange(selectedIndex + 1);
1408
+ }, delay);
1409
+ return () => {
1410
+ if (playRef.current) clearTimeout(playRef.current);
1411
+ };
1412
+ }, [playing, selectedIndex, snapshots, total, onIndexChange, autoPlayable]);
1413
+ const togglePlay = (0, import_react8.useCallback)(() => {
1414
+ if (playing) {
1415
+ setPlaying(false);
1416
+ } else {
1417
+ if (selectedIndex >= total - 1) onIndexChange(0);
1418
+ setPlaying(true);
1419
+ }
1420
+ }, [playing, selectedIndex, total, onIndexChange]);
1421
+ const fs = fontSize[size];
1422
+ if (unstyled) {
1423
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className, style, "data-fp": "time-travel-controls", children: [
1424
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1425
+ "button",
1426
+ {
1427
+ "data-fp": "tt-prev",
1428
+ disabled: !canPrev || playing,
1429
+ onClick: () => {
1430
+ setPlaying(false);
1431
+ onIndexChange(selectedIndex - 1);
1432
+ },
1433
+ children: "Prev"
1434
+ }
1435
+ ),
1436
+ autoPlayable && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { "data-fp": "tt-play", onClick: togglePlay, children: playing ? "Pause" : "Play" }),
1437
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1438
+ "button",
1439
+ {
1440
+ "data-fp": "tt-next",
1441
+ disabled: !canNext || playing,
1442
+ onClick: () => {
1443
+ setPlaying(false);
1444
+ onIndexChange(selectedIndex + 1);
1445
+ },
1446
+ children: "Next"
1447
+ }
1448
+ ),
1449
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { "data-fp": "tt-ticks", children: snapshots.map((snap, i) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1450
+ "button",
1451
+ {
1452
+ "data-fp": "tt-tick",
1453
+ "data-active": i === selectedIndex,
1454
+ "data-done": i < selectedIndex,
1455
+ onClick: () => {
1456
+ setPlaying(false);
1457
+ onIndexChange(i);
1458
+ },
1459
+ title: snap.stageLabel
1460
+ },
1461
+ i
1462
+ )) })
1463
+ ] });
1464
+ }
1465
+ const btnStyle = (disabled) => ({
1466
+ background: theme.bgTertiary,
1467
+ border: `1px solid ${theme.border}`,
1468
+ color: disabled ? theme.textMuted : theme.textPrimary,
1469
+ borderRadius: "6px",
1470
+ padding: "4px 12px",
1471
+ fontSize: fs.body,
1472
+ fontWeight: 600,
1473
+ cursor: disabled ? "not-allowed" : "pointer",
1474
+ opacity: disabled ? 0.5 : 1,
1475
+ flexShrink: 0
1476
+ });
1477
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
1478
+ "div",
1479
+ {
1480
+ className,
1481
+ style: {
1482
+ padding: "6px 12px",
1483
+ background: theme.bgSecondary,
1484
+ borderBottom: `1px solid ${theme.border}`,
1485
+ display: "flex",
1486
+ alignItems: "center",
1487
+ gap: 6,
1488
+ flexShrink: 0,
1489
+ ...style
1490
+ },
1491
+ "data-fp": "time-travel-controls",
1492
+ children: [
1493
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1494
+ "button",
1495
+ {
1496
+ style: btnStyle(!canPrev || playing),
1497
+ disabled: !canPrev || playing,
1498
+ onClick: () => {
1499
+ setPlaying(false);
1500
+ onIndexChange(selectedIndex - 1);
1501
+ },
1502
+ children: "\u25C0"
1503
+ }
1504
+ ),
1505
+ autoPlayable && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1506
+ "button",
1507
+ {
1508
+ onClick: togglePlay,
1509
+ style: {
1510
+ background: playing ? theme.primary : theme.bgTertiary,
1511
+ border: `1px solid ${theme.border}`,
1512
+ color: playing ? "white" : theme.textPrimary,
1513
+ borderRadius: "6px",
1514
+ width: 28,
1515
+ height: 28,
1516
+ display: "flex",
1517
+ alignItems: "center",
1518
+ justifyContent: "center",
1519
+ cursor: "pointer",
1520
+ fontSize: 14,
1521
+ flexShrink: 0
1522
+ },
1523
+ title: playing ? "Pause" : "Play",
1524
+ children: playing ? "\u23F8" : "\u25B6"
1525
+ }
1526
+ ),
1527
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1528
+ "button",
1529
+ {
1530
+ style: btnStyle(!canNext || playing),
1531
+ disabled: !canNext || playing,
1532
+ onClick: () => {
1533
+ setPlaying(false);
1534
+ onIndexChange(selectedIndex + 1);
1535
+ },
1536
+ children: "\u25B6"
1537
+ }
1538
+ ),
1539
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1540
+ "div",
1541
+ {
1542
+ style: {
1543
+ flex: 1,
1544
+ display: "flex",
1545
+ alignItems: "center",
1546
+ gap: 2,
1547
+ padding: "0 4px"
1548
+ },
1549
+ children: snapshots.map((snap, i) => {
1550
+ const isActive = i === selectedIndex;
1551
+ const isDone = i < selectedIndex;
1552
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1553
+ "button",
1554
+ {
1555
+ onClick: () => {
1556
+ setPlaying(false);
1557
+ onIndexChange(i);
1558
+ },
1559
+ title: snap.stageLabel,
1560
+ style: {
1561
+ flex: 1,
1562
+ height: isActive ? 14 : 8,
1563
+ borderRadius: 3,
1564
+ border: "none",
1565
+ cursor: "pointer",
1566
+ background: isActive ? theme.primary : isDone ? theme.success : theme.bgTertiary,
1567
+ opacity: isDone || isActive ? 1 : 0.4,
1568
+ transition: "all 0.15s ease"
1569
+ }
1570
+ },
1571
+ i
1572
+ );
1573
+ })
1574
+ }
1575
+ )
1576
+ ]
1577
+ }
1578
+ );
1579
+ }
1580
+
1581
+ // src/components/ExplainableShell/ExplainableShell.tsx
1582
+ var import_react9 = require("react");
1583
+ var import_jsx_runtime10 = require("react/jsx-runtime");
1584
+ function ExplainableShell({
1585
+ snapshots,
1586
+ resultData,
1587
+ logs = [],
1588
+ narrative = [],
1589
+ tabs = ["result", "explainable", "ai-compatible"],
1590
+ defaultTab,
1591
+ hideConsole = false,
1592
+ renderFlowchart,
1593
+ size = "default",
1594
+ unstyled = false,
1595
+ className,
1596
+ style
1597
+ }) {
1598
+ const [activeTab, setActiveTab] = (0, import_react9.useState)(defaultTab ?? tabs[0]);
1599
+ const [snapshotIdx, setSnapshotIdx] = (0, import_react9.useState)(0);
1600
+ const fs = fontSize[size];
1601
+ const pad = padding[size];
1602
+ const handleSnapshotChange = (0, import_react9.useCallback)((idx) => {
1603
+ setSnapshotIdx(Math.max(0, Math.min(idx, snapshots.length - 1)));
1604
+ }, [snapshots.length]);
1605
+ const revealedCount = (0, import_react9.useMemo)(() => {
1606
+ if (snapshots.length === 0 || narrative.length === 0) return narrative.length;
1607
+ const boundaries = [];
1608
+ for (let i = 0; i < narrative.length; i++) {
1609
+ const trimmed = narrative[i].trimStart();
1610
+ if (trimmed.startsWith("Stage ") && !trimmed.match(/^Stage\s+\d+:\s*Step\s/) || trimmed.startsWith("[")) {
1611
+ boundaries.push(i);
1612
+ }
1613
+ }
1614
+ if (boundaries.length === 0) {
1615
+ const ratio = (snapshotIdx + 1) / snapshots.length;
1616
+ return Math.max(1, Math.ceil(narrative.length * ratio));
1617
+ }
1618
+ const groupsToShow = Math.max(
1619
+ 1,
1620
+ Math.min(
1621
+ Math.floor((snapshotIdx + 1) / snapshots.length * boundaries.length) || 1,
1622
+ boundaries.length
1623
+ )
1624
+ );
1625
+ const endIdx = groupsToShow < boundaries.length ? boundaries[groupsToShow] : narrative.length;
1626
+ return Math.max(1, endIdx);
1627
+ }, [snapshots.length, snapshotIdx, narrative]);
1628
+ const prevMemory = snapshotIdx > 0 ? snapshots[snapshotIdx - 1]?.memory : null;
1629
+ const currMemory = snapshots[snapshotIdx]?.memory ?? {};
1630
+ const tabLabels = {
1631
+ result: "Result",
1632
+ explainable: "Explainable",
1633
+ "ai-compatible": "AI-Compatible"
1634
+ };
1635
+ if (unstyled) {
1636
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className, style, "data-fp": "explainable-shell", children: [
1637
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { "data-fp": "shell-tabs", children: tabs.map((tab) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1638
+ "button",
1639
+ {
1640
+ "data-fp": "shell-tab",
1641
+ "data-active": tab === activeTab,
1642
+ onClick: () => setActiveTab(tab),
1643
+ children: tabLabels[tab]
1644
+ },
1645
+ tab
1646
+ )) }),
1647
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { "data-fp": "shell-content", "data-tab": activeTab, children: [
1648
+ activeTab === "result" && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1649
+ ResultPanel,
1650
+ {
1651
+ data: resultData ?? null,
1652
+ logs,
1653
+ hideConsole,
1654
+ unstyled: true
1655
+ }
1656
+ ),
1657
+ activeTab === "explainable" && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
1658
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1659
+ TimeTravelControls,
1660
+ {
1661
+ snapshots,
1662
+ selectedIndex: snapshotIdx,
1663
+ onIndexChange: handleSnapshotChange,
1664
+ unstyled: true
1665
+ }
1666
+ ),
1667
+ renderFlowchart?.({ snapshots, selectedIndex: snapshotIdx, onNodeClick: handleSnapshotChange }),
1668
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(MemoryInspector, { snapshots, selectedIndex: snapshotIdx, unstyled: true }),
1669
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ScopeDiff, { previous: prevMemory, current: currMemory, unstyled: true }),
1670
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1671
+ GanttTimeline,
1672
+ {
1673
+ snapshots,
1674
+ selectedIndex: snapshotIdx,
1675
+ onSelect: handleSnapshotChange,
1676
+ unstyled: true
1677
+ }
1678
+ )
1679
+ ] }),
1680
+ activeTab === "ai-compatible" && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
1681
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1682
+ TimeTravelControls,
1683
+ {
1684
+ snapshots,
1685
+ selectedIndex: snapshotIdx,
1686
+ onIndexChange: handleSnapshotChange,
1687
+ unstyled: true
1688
+ }
1689
+ ),
1690
+ renderFlowchart?.({ snapshots, selectedIndex: snapshotIdx, onNodeClick: handleSnapshotChange }),
1691
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1692
+ NarrativeTrace,
1693
+ {
1694
+ narrative,
1695
+ revealedCount,
1696
+ unstyled: true
1697
+ }
1698
+ )
1699
+ ] })
1700
+ ] })
1701
+ ] });
1702
+ }
1703
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
1704
+ "div",
1705
+ {
1706
+ className,
1707
+ style: {
1708
+ height: "100%",
1709
+ display: "flex",
1710
+ flexDirection: "column",
1711
+ overflow: "hidden",
1712
+ background: theme.bgPrimary,
1713
+ color: theme.textPrimary,
1714
+ fontFamily: theme.fontSans,
1715
+ ...style
1716
+ },
1717
+ "data-fp": "explainable-shell",
1718
+ children: [
1719
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1720
+ "div",
1721
+ {
1722
+ style: {
1723
+ display: "flex",
1724
+ gap: 0,
1725
+ borderBottom: `1px solid ${theme.border}`,
1726
+ background: theme.bgSecondary,
1727
+ flexShrink: 0
1728
+ },
1729
+ children: tabs.map((tab) => {
1730
+ const active = tab === activeTab;
1731
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1732
+ "button",
1733
+ {
1734
+ onClick: () => setActiveTab(tab),
1735
+ style: {
1736
+ padding: `${pad - 4}px ${pad}px`,
1737
+ fontSize: fs.label,
1738
+ fontWeight: active ? 700 : 500,
1739
+ textTransform: "uppercase",
1740
+ letterSpacing: "0.08em",
1741
+ color: active ? theme.primary : theme.textMuted,
1742
+ background: "transparent",
1743
+ border: "none",
1744
+ borderBottom: active ? `2px solid ${theme.primary}` : "2px solid transparent",
1745
+ cursor: "pointer",
1746
+ transition: "all 0.15s ease"
1747
+ },
1748
+ children: tabLabels[tab]
1749
+ },
1750
+ tab
1751
+ );
1752
+ })
1753
+ }
1754
+ ),
1755
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: { flex: 1, overflow: "hidden", display: "flex", flexDirection: "column" }, children: [
1756
+ activeTab === "result" && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1757
+ ResultPanel,
1758
+ {
1759
+ data: resultData ?? null,
1760
+ logs,
1761
+ hideConsole,
1762
+ size
1763
+ }
1764
+ ),
1765
+ activeTab === "explainable" && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
1766
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1767
+ TimeTravelControls,
1768
+ {
1769
+ snapshots,
1770
+ selectedIndex: snapshotIdx,
1771
+ onIndexChange: handleSnapshotChange,
1772
+ size
1773
+ }
1774
+ ),
1775
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: { flex: 1, display: "flex", overflow: "hidden" }, children: [
1776
+ renderFlowchart && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: { flex: 1, overflow: "hidden", borderRight: `1px solid ${theme.border}` }, children: renderFlowchart({ snapshots, selectedIndex: snapshotIdx, onNodeClick: handleSnapshotChange }) }),
1777
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
1778
+ "div",
1779
+ {
1780
+ style: {
1781
+ width: renderFlowchart ? "40%" : "100%",
1782
+ minWidth: 280,
1783
+ overflow: "auto",
1784
+ display: "flex",
1785
+ flexDirection: "column"
1786
+ },
1787
+ children: [
1788
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1789
+ MemoryInspector,
1790
+ {
1791
+ snapshots,
1792
+ selectedIndex: snapshotIdx,
1793
+ size
1794
+ }
1795
+ ),
1796
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: { borderTop: `1px solid ${theme.border}` }, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1797
+ ScopeDiff,
1798
+ {
1799
+ previous: prevMemory,
1800
+ current: currMemory,
1801
+ hideUnchanged: true,
1802
+ size
1803
+ }
1804
+ ) })
1805
+ ]
1806
+ }
1807
+ )
1808
+ ] }),
1809
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: { borderTop: `1px solid ${theme.border}`, flexShrink: 0 }, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1810
+ GanttTimeline,
1811
+ {
1812
+ snapshots,
1813
+ selectedIndex: snapshotIdx,
1814
+ onSelect: handleSnapshotChange,
1815
+ size
1816
+ }
1817
+ ) })
1818
+ ] }),
1819
+ activeTab === "ai-compatible" && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
1820
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1821
+ TimeTravelControls,
1822
+ {
1823
+ snapshots,
1824
+ selectedIndex: snapshotIdx,
1825
+ onIndexChange: handleSnapshotChange,
1826
+ size
1827
+ }
1828
+ ),
1829
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: { flex: 1, display: "flex", overflow: "hidden" }, children: [
1830
+ renderFlowchart && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { style: { flex: 1, overflow: "hidden", borderRight: `1px solid ${theme.border}` }, children: renderFlowchart({ snapshots, selectedIndex: snapshotIdx, onNodeClick: handleSnapshotChange }) }),
1831
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1832
+ NarrativeTrace,
1833
+ {
1834
+ narrative,
1835
+ revealedCount,
1836
+ size,
1837
+ style: {
1838
+ width: renderFlowchart ? "40%" : "100%",
1839
+ minWidth: 280
1840
+ }
1841
+ }
1842
+ )
1843
+ ] })
1844
+ ] })
1845
+ ] })
1846
+ ]
1847
+ }
1848
+ );
1849
+ }
1850
+
1851
+ // src/adapters/fromRuntimeSnapshot.ts
1852
+ function toVisualizationSnapshots(runtime) {
1853
+ const snapshots = [];
1854
+ flattenTree(runtime.executionTree, snapshots, runtime.sharedState);
1855
+ return snapshots;
1856
+ }
1857
+ function flattenTree(node, out, sharedState, accumulatedMs = 0) {
1858
+ const durationMs = typeof node.metrics?.durationMs === "number" ? node.metrics.durationMs : 1;
1859
+ const startMs = accumulatedMs;
1860
+ const narrative = buildNarrative(node);
1861
+ const memory = {};
1862
+ if (node.logs) {
1863
+ Object.assign(memory, node.logs);
1864
+ }
1865
+ out.push({
1866
+ stageName: node.id,
1867
+ stageLabel: node.name || node.id,
1868
+ memory,
1869
+ narrative,
1870
+ startMs,
1871
+ durationMs,
1872
+ status: "done"
1873
+ });
1874
+ let nextMs = startMs + durationMs;
1875
+ if (node.children && node.children.length > 0) {
1876
+ let maxChildEnd = nextMs;
1877
+ for (const child of node.children) {
1878
+ const childEnd = flattenTree(child, out, sharedState, nextMs);
1879
+ maxChildEnd = Math.max(maxChildEnd, childEnd);
1880
+ }
1881
+ nextMs = maxChildEnd;
1882
+ }
1883
+ if (node.next) {
1884
+ nextMs = flattenTree(node.next, out, sharedState, nextMs);
1885
+ }
1886
+ return nextMs;
1887
+ }
1888
+ function buildNarrative(node) {
1889
+ const parts = [];
1890
+ if (node.name) {
1891
+ parts.push(`Stage "${node.name}" executed.`);
1892
+ }
1893
+ if (node.logs && Object.keys(node.logs).length > 0) {
1894
+ const keys = Object.keys(node.logs);
1895
+ parts.push(`Wrote ${keys.length} key(s): ${keys.join(", ")}.`);
1896
+ }
1897
+ if (node.errors && Object.keys(node.errors).length > 0) {
1898
+ parts.push(`Errors: ${JSON.stringify(node.errors)}`);
1899
+ }
1900
+ if (node.isFork) {
1901
+ parts.push(
1902
+ `Forked into ${node.children?.length ?? 0} parallel branch(es).`
1903
+ );
1904
+ }
1905
+ return parts.join(" ") || `Stage ${node.id} completed.`;
1906
+ }
1907
+ function createSnapshots(stages) {
1908
+ let accMs = 0;
1909
+ return stages.map((s) => {
1910
+ const duration = s.durationMs ?? 1;
1911
+ const snap = {
1912
+ stageName: s.name,
1913
+ stageLabel: s.label ?? s.name,
1914
+ memory: s.memory ?? {},
1915
+ narrative: s.narrative ?? `${s.label ?? s.name} completed.`,
1916
+ startMs: accMs,
1917
+ durationMs: duration,
1918
+ status: "done"
1919
+ };
1920
+ accMs += duration;
1921
+ return snap;
1922
+ });
1923
+ }
1924
+ // Annotate the CommonJS export names for ESM import in node:
1925
+ 0 && (module.exports = {
1926
+ ExplainableShell,
1927
+ FootprintTheme,
1928
+ GanttTimeline,
1929
+ MemoryInspector,
1930
+ NarrativeLog,
1931
+ NarrativeTrace,
1932
+ ResultPanel,
1933
+ ScopeDiff,
1934
+ SnapshotPanel,
1935
+ TimeTravelControls,
1936
+ coolDark,
1937
+ createSnapshots,
1938
+ defaultTokens,
1939
+ themePresets,
1940
+ toVisualizationSnapshots,
1941
+ tokensToCSSVars,
1942
+ useFootprintTheme,
1943
+ warmDark,
1944
+ warmLight
1945
+ });
1946
+ //# sourceMappingURL=index.cjs.map