footprint-explainable-ui 0.3.0 → 0.3.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.
Files changed (2) hide show
  1. package/README.md +422 -147
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # footprint-explainable-ui
2
2
 
3
- Themeable React components for visualizing [FootPrint](https://github.com/sanjay1909/footPrint) pipeline execution — time-travel debugging, flowchart overlays, narrative traces, and scope diffs.
3
+ Themeable React components for visualizing [FootPrint](https://github.com/footprintjs/footPrint) pipeline execution — time-travel debugging, flowchart overlays, subflow drill-down, narrative traces, and scope diffs.
4
4
 
5
5
  ## Install
6
6
 
@@ -8,16 +8,44 @@ Themeable React components for visualizing [FootPrint](https://github.com/sanjay
8
8
  npm install footprint-explainable-ui
9
9
  ```
10
10
 
11
- Peer dependencies: `react >= 18`. For flowchart components, also install `@xyflow/react`.
11
+ **Peer dependencies:** `react >= 18`, `react-dom >= 18`
12
+
13
+ For flowchart components, also install:
14
+
15
+ ```bash
16
+ npm install @xyflow/react
17
+ ```
18
+
19
+ ## Entry Points
20
+
21
+ | Import path | What it provides |
22
+ |---|---|
23
+ | `footprint-explainable-ui` | Core components, themes, adapters |
24
+ | `footprint-explainable-ui/flowchart` | Flowchart visualization, subflow navigation (requires `@xyflow/react`) |
12
25
 
13
26
  ## Quick Start
14
27
 
15
- ### All-in-One Shell
28
+ ### 1. Convert FootPrint execution data to snapshots
29
+
30
+ ```typescript
31
+ import { FlowChartExecutor } from "footprint";
32
+ import { toVisualizationSnapshots } from "footprint-explainable-ui";
16
33
 
17
- The `ExplainableShell` gives you a tabbed UI (Result | Explainable | AI-Compatible) with time-travel controls, Gantt timeline, scope diffs, and progressive narrative — out of the box.
34
+ const executor = new FlowChartExecutor(chart);
35
+ await executor.run();
36
+
37
+ // Convert runtime snapshot → visualization snapshots
38
+ const snapshots = toVisualizationSnapshots(executor.getSnapshot());
39
+ ```
40
+
41
+ ### 2. Render with the all-in-one shell
18
42
 
19
43
  ```tsx
20
- import { ExplainableShell, FootprintTheme, warmDark } from "footprint-explainable-ui";
44
+ import {
45
+ ExplainableShell,
46
+ FootprintTheme,
47
+ warmDark,
48
+ } from "footprint-explainable-ui";
21
49
 
22
50
  function App({ snapshots, narrative, result }) {
23
51
  return (
@@ -33,22 +61,21 @@ function App({ snapshots, narrative, result }) {
33
61
  }
34
62
  ```
35
63
 
36
- ### Individual Components
37
-
38
- Every component works standalone. Mix and match:
64
+ ### 3. Or compose individual components
39
65
 
40
66
  ```tsx
41
67
  import {
42
- NarrativeTrace,
68
+ TimeTravelControls,
69
+ MemoryInspector,
43
70
  ScopeDiff,
44
71
  GanttTimeline,
45
- MemoryInspector,
46
- TimeTravelControls,
47
- ResultPanel,
72
+ NarrativeTrace,
48
73
  } from "footprint-explainable-ui";
49
74
 
50
75
  function MyDebugger({ snapshots }) {
51
76
  const [idx, setIdx] = useState(0);
77
+ const current = snapshots[idx];
78
+ const previous = idx > 0 ? snapshots[idx - 1] : null;
52
79
 
53
80
  return (
54
81
  <>
@@ -59,46 +86,200 @@ function MyDebugger({ snapshots }) {
59
86
  />
60
87
  <MemoryInspector snapshots={snapshots} selectedIndex={idx} />
61
88
  <ScopeDiff
62
- previous={idx > 0 ? snapshots[idx - 1].memory : null}
63
- current={snapshots[idx].memory}
89
+ previous={previous?.memory ?? null}
90
+ current={current.memory}
64
91
  hideUnchanged
65
92
  />
93
+ <NarrativeTrace narrative={snapshots.map(s => s.narrative)} />
66
94
  <GanttTimeline snapshots={snapshots} selectedIndex={idx} onSelect={setIdx} />
67
95
  </>
68
96
  );
69
97
  }
70
98
  ```
71
99
 
72
- ## Theming
100
+ ---
101
+
102
+ ## Flowchart Visualization
103
+
104
+ Import from `footprint-explainable-ui/flowchart`:
105
+
106
+ ```tsx
107
+ import {
108
+ StageNode,
109
+ specToReactFlow,
110
+ useSubflowNavigation,
111
+ SubflowBreadcrumb,
112
+ type SpecNode,
113
+ type ExecutionOverlay,
114
+ } from "footprint-explainable-ui/flowchart";
115
+ ```
116
+
117
+ ### Static flowchart from pipeline spec
118
+
119
+ ```tsx
120
+ import { ReactFlow } from "@xyflow/react";
121
+ import "@xyflow/react/dist/style.css";
122
+ import { specToReactFlow, StageNode } from "footprint-explainable-ui/flowchart";
123
+
124
+ const nodeTypes = { stage: StageNode };
125
+
126
+ function PipelineChart({ spec }) {
127
+ const { nodes, edges } = specToReactFlow(spec);
128
+
129
+ return (
130
+ <ReactFlow
131
+ nodes={nodes}
132
+ edges={edges}
133
+ nodeTypes={nodeTypes}
134
+ fitView
135
+ />
136
+ );
137
+ }
138
+ ```
139
+
140
+ ### With execution overlay (time-travel)
141
+
142
+ The overlay highlights which stages have executed, which is active, and the execution path — like a Google Maps route overlay.
143
+
144
+ ```tsx
145
+ import { specToReactFlow, type ExecutionOverlay } from "footprint-explainable-ui/flowchart";
146
+
147
+ // Build overlay from your current time-travel position
148
+ const overlay: ExecutionOverlay = {
149
+ doneStages: new Set(["LoadOrder", "ProcessPayment"]),
150
+ activeStage: "ShipOrder",
151
+ executedStages: new Set(["LoadOrder", "ProcessPayment", "ShipOrder"]),
152
+ executionOrder: ["LoadOrder", "ProcessPayment", "ShipOrder"],
153
+ };
154
+
155
+ const { nodes, edges } = specToReactFlow(spec, overlay);
156
+ ```
157
+
158
+ **Tip:** Compute the overlay from your snapshots array and current index:
159
+
160
+ ```tsx
161
+ function buildOverlay(snapshots, idx): ExecutionOverlay {
162
+ const executionOrder = snapshots.slice(0, idx + 1).map(s => s.stageLabel);
163
+ const doneStages = new Set(snapshots.slice(0, idx).map(s => s.stageLabel));
164
+ const activeStage = snapshots[idx]?.stageLabel ?? null;
165
+ const executedStages = new Set([...doneStages]);
166
+ if (activeStage) executedStages.add(activeStage);
167
+ return { doneStages, activeStage, executedStages, executionOrder };
168
+ }
169
+ ```
170
+
171
+ ### Custom edge colors
172
+
173
+ ```tsx
174
+ const { nodes, edges } = specToReactFlow(spec, overlay, {
175
+ edgeExecuted: "#00ff88", // Completed path
176
+ edgeActive: "#ff6b6b", // Currently executing
177
+ });
178
+ ```
179
+
180
+ ### Subflow drill-down navigation
181
+
182
+ For pipelines with nested subflows, `useSubflowNavigation` manages a breadcrumb stack. Clicking a subflow node drills into its internal flowchart.
183
+
184
+ ```tsx
185
+ import {
186
+ useSubflowNavigation,
187
+ SubflowBreadcrumb,
188
+ specToReactFlow,
189
+ StageNode,
190
+ } from "footprint-explainable-ui/flowchart";
191
+ import { ReactFlow } from "@xyflow/react";
192
+
193
+ const nodeTypes = { stage: StageNode };
194
+
195
+ function DrillDownChart({ spec, overlay }) {
196
+ const subflowNav = useSubflowNavigation(spec);
197
+
198
+ // Get the current level's spec from breadcrumbs
199
+ const currentSpec = subflowNav.breadcrumbs[subflowNav.breadcrumbs.length - 1].spec;
200
+ const { nodes, edges } = specToReactFlow(currentSpec, overlay);
201
+
202
+ return (
203
+ <div>
204
+ {/* Breadcrumb bar: Pipeline > PaymentSubflow */}
205
+ {subflowNav.isInSubflow && (
206
+ <SubflowBreadcrumb
207
+ breadcrumbs={subflowNav.breadcrumbs}
208
+ onNavigate={subflowNav.navigateTo}
209
+ />
210
+ )}
211
+
212
+ <ReactFlow
213
+ nodes={nodes}
214
+ edges={edges}
215
+ nodeTypes={nodeTypes}
216
+ onNodeClick={(_, node) => subflowNav.handleNodeClick(node.id)}
217
+ fitView
218
+ />
219
+ </div>
220
+ );
221
+ }
222
+ ```
223
+
224
+ **`useSubflowNavigation` returns:**
225
+
226
+ | Property | Type | Description |
227
+ |---|---|---|
228
+ | `breadcrumbs` | `BreadcrumbEntry[]` | Stack from root to current level |
229
+ | `nodes` | `Node[]` | ReactFlow nodes for current level |
230
+ | `edges` | `Edge[]` | ReactFlow edges for current level |
231
+ | `handleNodeClick` | `(nodeId) => boolean` | Drills into subflow if applicable |
232
+ | `navigateTo` | `(level) => void` | Jump to breadcrumb level (0 = root) |
233
+ | `isInSubflow` | `boolean` | Whether we're inside a subflow |
234
+ | `currentSubflowNodeName` | `string \| null` | Name of the subflow node drilled into |
235
+
236
+ ### Extracting subflow execution data
237
+
238
+ When drilled into a subflow, extract its execution snapshots from the parent's memory:
239
+
240
+ ```tsx
241
+ import { toVisualizationSnapshots } from "footprint-explainable-ui";
242
+
243
+ // Find the parent stage that contains the subflow result
244
+ const parentSnap = parentSnapshots.find(s => s.stageLabel === subflowNav.currentSubflowNodeName);
245
+ const sfResult = parentSnap?.memory?.subflowResult;
246
+ const tc = sfResult?.treeContext;
247
+
248
+ if (tc?.stageContexts) {
249
+ const subflowSnapshots = toVisualizationSnapshots({
250
+ sharedState: tc.globalContext,
251
+ executionTree: tc.stageContexts,
252
+ commitLog: tc.history ?? [],
253
+ });
254
+
255
+ // Strip builder's "subflowId/" prefix from stage names
256
+ const prefix = sfResult.subflowId ? `${sfResult.subflowId}/` : null;
257
+ if (prefix) {
258
+ for (const snap of subflowSnapshots) {
259
+ if (snap.stageLabel.startsWith(prefix))
260
+ snap.stageLabel = snap.stageLabel.slice(prefix.length);
261
+ if (snap.stageName.startsWith(prefix))
262
+ snap.stageName = snap.stageName.slice(prefix.length);
263
+ }
264
+ }
265
+ }
266
+ ```
267
+
268
+ ---
73
269
 
74
- ### Option 1: ThemeProvider
270
+ ## Theming
75
271
 
76
- Wrap your app with `FootprintTheme` and pass a preset or custom tokens:
272
+ ### ThemeProvider
77
273
 
78
274
  ```tsx
79
275
  import { FootprintTheme, warmDark, warmLight, coolDark } from "footprint-explainable-ui";
80
276
 
81
- // Use a built-in preset
82
277
  <FootprintTheme tokens={warmDark}>
83
278
  <MyApp />
84
279
  </FootprintTheme>
85
-
86
- // Or customize
87
- <FootprintTheme tokens={{
88
- colors: {
89
- primary: "#e91e63",
90
- bgPrimary: "#121212",
91
- textPrimary: "#ffffff",
92
- },
93
- radius: "12px",
94
- }}>
95
- <MyApp />
96
- </FootprintTheme>
97
280
  ```
98
281
 
99
- ### Option 2: CSS Variables
100
-
101
- Set `--fp-*` CSS variables directly — no provider needed:
282
+ ### CSS Variables (no provider needed)
102
283
 
103
284
  ```css
104
285
  :root {
@@ -115,7 +296,7 @@ Set `--fp-*` CSS variables directly — no provider needed:
115
296
  ### Built-in Presets
116
297
 
117
298
  | Preset | Description |
118
- |--------|-------------|
299
+ |---|---|
119
300
  | `coolDark` | Default — indigo/slate dark theme |
120
301
  | `warmDark` | Charcoal-purple with warm text |
121
302
  | `warmLight` | Cream/peach light theme |
@@ -125,55 +306,36 @@ Set `--fp-*` CSS variables directly — no provider needed:
125
306
  ```typescript
126
307
  interface ThemeTokens {
127
308
  colors?: {
128
- primary?: string; // Accent color (buttons, highlights)
129
- success?: string; // Completed stages
130
- error?: string; // Error states
131
- warning?: string; // Warnings
132
- bgPrimary?: string; // Main background
133
- bgSecondary?: string;// Panel/card background
134
- bgTertiary?: string; // Hover/active background
135
- textPrimary?: string;// Main text
136
- textSecondary?: string;// Secondary text
137
- textMuted?: string; // Dimmed text
138
- border?: string; // Borders
309
+ primary?: string; // Accent (buttons, highlights)
310
+ success?: string; // Completed stages
311
+ error?: string; // Error states
312
+ warning?: string; // Warnings
313
+ bgPrimary?: string; // Main background
314
+ bgSecondary?: string; // Panel/card background
315
+ bgTertiary?: string; // Hover/active background
316
+ textPrimary?: string; // Main text
317
+ textSecondary?: string; // Secondary text
318
+ textMuted?: string; // Dimmed text
319
+ border?: string; // Borders
139
320
  };
140
- radius?: string; // Border radius
321
+ radius?: string;
141
322
  fontFamily?: {
142
- sans?: string; // UI text font
143
- mono?: string; // Code/data font
323
+ sans?: string; // UI text font
324
+ mono?: string; // Code/data font
144
325
  };
145
326
  }
146
327
  ```
147
328
 
148
- ## Size Variants
329
+ ---
149
330
 
150
- All components accept a `size` prop: `"compact"`, `"default"`, or `"detailed"`.
331
+ ## Components Reference
151
332
 
152
- ```tsx
153
- <GanttTimeline snapshots={snapshots} size="compact" />
154
- <MemoryInspector snapshots={snapshots} size="detailed" />
155
- ```
156
-
157
- ## Unstyled Mode
158
-
159
- Strip all built-in styles and bring your own. Components render semantic `data-fp` attributes for CSS targeting:
160
-
161
- ```tsx
162
- <NarrativeTrace narrative={lines} unstyled className="my-narrative" />
163
- ```
164
-
165
- ```css
166
- [data-fp="narrative-header"] { font-weight: bold; }
167
- [data-fp="narrative-step"] { padding-left: 2rem; }
168
- [data-fp="narrative-group"][data-latest="true"] { background: highlight; }
169
- ```
170
-
171
- ## Components
333
+ ### Core Components
172
334
 
173
335
  | Component | Description |
174
- |-----------|-------------|
336
+ |---|---|
175
337
  | `ExplainableShell` | Tabbed container: Result / Explainable / AI-Compatible |
176
- | `TimeTravelControls` | Play/pause, prev/next, tick-mark timeline |
338
+ | `TimeTravelControls` | Play/pause, prev/next, scrubber timeline |
177
339
  | `NarrativeTrace` | Collapsible stage groups with progressive reveal |
178
340
  | `NarrativeLog` | Simple timeline-style execution log |
179
341
  | `ScopeDiff` | Side-by-side scope changes (added/changed/removed) |
@@ -182,107 +344,220 @@ Strip all built-in styles and bring your own. Components render semantic `data-f
182
344
  | `GanttTimeline` | Horizontal duration timeline |
183
345
  | `SnapshotPanel` | All-in-one inspector (scrubber + memory + narrative + Gantt) |
184
346
 
185
- ### Flowchart Components (separate entry point)
186
-
187
- Requires `@xyflow/react` as a peer dependency. Import from `footprint-explainable-ui/flowchart`:
188
-
189
- ```bash
190
- npm install @xyflow/react
191
- ```
192
-
193
- ```tsx
194
- import {
195
- FlowchartView,
196
- StageNode,
197
- specToReactFlow,
198
- TimeTravelDebugger,
199
- } from "footprint-explainable-ui/flowchart";
200
- ```
347
+ ### Flowchart Components (`footprint-explainable-ui/flowchart`)
201
348
 
202
349
  | Export | Description |
203
- |--------|-------------|
350
+ |---|---|
204
351
  | `FlowchartView` | ReactFlow pipeline visualization with execution overlay |
205
352
  | `StageNode` | Custom node with state-aware coloring, step badges, pulse rings |
206
353
  | `specToReactFlow` | Convert pipeline spec → ReactFlow nodes/edges with path overlay |
207
354
  | `TimeTravelDebugger` | Full debugger with flowchart + all panels |
355
+ | `SubflowBreadcrumb` | Breadcrumb bar for subflow drill-down |
356
+ | `useSubflowNavigation` | Hook managing subflow drill-down navigation stack |
208
357
 
209
- ### `specToReactFlow` — Pipeline Spec to Flowchart
358
+ ### Adapters
210
359
 
211
- Converts a `builder.toSpec()` structure into ReactFlow nodes and edges. Supports two modes:
360
+ | Export | Description |
361
+ |---|---|
362
+ | `toVisualizationSnapshots` | Convert `FlowChartExecutor.getSnapshot()` → `StageSnapshot[]` |
363
+ | `createSnapshots` | Build `StageSnapshot[]` from simple arrays (testing/custom data) |
364
+
365
+ ---
366
+
367
+ ## Size Variants
368
+
369
+ All components accept a `size` prop: `"compact"`, `"default"`, or `"detailed"`.
212
370
 
213
371
  ```tsx
214
- import { specToReactFlow } from "footprint-explainable-ui/flowchart";
215
- import type { ExecutionOverlay } from "footprint-explainable-ui/flowchart";
372
+ <GanttTimeline snapshots={snapshots} size="compact" />
373
+ <MemoryInspector snapshots={snapshots} size="detailed" />
374
+ ```
216
375
 
217
- // 1. Static flowchart (no execution state)
218
- const { nodes, edges } = specToReactFlow(spec);
376
+ ## Unstyled Mode
219
377
 
220
- // 2. With execution overlay (Google Maps-style path)
221
- const overlay: ExecutionOverlay = {
222
- doneStages: new Set(["ReceiveApp", "PullCredit"]),
223
- activeStage: "CalculateDTI",
224
- executedStages: new Set(["ReceiveApp", "PullCredit", "CalculateDTI"]),
225
- executionOrder: ["ReceiveApp", "PullCredit", "CalculateDTI"],
226
- };
227
- const { nodes, edges } = specToReactFlow(spec, overlay);
378
+ Strip all built-in styles for full CSS control. Components render semantic `data-fp` attributes:
228
379
 
229
- // 3. Custom edge colors (overrides theme defaults)
230
- const { nodes, edges } = specToReactFlow(spec, overlay, {
231
- edgeExecuted: "#00ff88",
232
- edgeActive: "#ff6b6b",
233
- });
380
+ ```tsx
381
+ <NarrativeTrace narrative={lines} unstyled className="my-narrative" />
234
382
  ```
235
383
 
236
- Edge colors default to the library's theme tokens (`success` for executed, `primary` for active). Override per-call via the `colors` parameter.
384
+ ```css
385
+ [data-fp="narrative-header"] { font-weight: bold; }
386
+ [data-fp="narrative-step"] { padding-left: 2rem; }
387
+ [data-fp="narrative-group"][data-latest="true"] { background: highlight; }
388
+ ```
237
389
 
238
- ### `StageNode` — Theme-Aware Node
390
+ ---
239
391
 
240
- `StageNode` reads all colors from `--fp-*` CSS variables:
241
- - **Default state**: uses `--fp-bg-secondary` background, `--fp-text-primary` text
242
- - **Active/done/error**: uses `--fp-color-primary` / `success` / `error` background
243
- - **Step badge**: shows execution order number on executed nodes
244
- - **Font**: uses `--fp-font-sans` for label text
392
+ ## Example: Build a Pipeline Playground
245
393
 
246
- Wrap your flowchart in `FootprintTheme` and the nodes match automatically:
394
+ A complete example combining flowchart, time-travel controls, detail panel, and Gantt timeline — the same pattern used by the [FootPrint Playground](https://footprintjs.github.io/footprint-playground/).
247
395
 
248
396
  ```tsx
249
- <FootprintTheme tokens={warmDark}>
250
- <ReactFlow nodes={nodes} edges={edges} nodeTypes={{ stage: StageNode }} />
251
- </FootprintTheme>
252
- ```
253
-
254
- ## Adapters
397
+ import { useState, useMemo } from "react";
398
+ import { ReactFlow } from "@xyflow/react";
399
+ import "@xyflow/react/dist/style.css";
400
+ import {
401
+ toVisualizationSnapshots,
402
+ GanttTimeline,
403
+ ScopeDiff,
404
+ NarrativeTrace,
405
+ MemoryInspector,
406
+ FootprintTheme,
407
+ warmDark,
408
+ } from "footprint-explainable-ui";
409
+ import {
410
+ StageNode,
411
+ specToReactFlow,
412
+ useSubflowNavigation,
413
+ SubflowBreadcrumb,
414
+ type ExecutionOverlay,
415
+ type SpecNode,
416
+ } from "footprint-explainable-ui/flowchart";
417
+ import { FlowChartExecutor } from "footprint";
418
+
419
+ const nodeTypes = { stage: StageNode };
420
+
421
+ // ─── Hook: time-travel + overlay + subflow drill-down ────────────────
422
+ function useFlowchartData(spec: SpecNode | null, vizSnapshots: any[] | null) {
423
+ const [snapshotIdx, setSnapshotIdx] = useState(0);
424
+ const subflowNav = useSubflowNavigation(spec);
425
+
426
+ const activeSnapshots = vizSnapshots; // extend with subflow logic as needed
427
+
428
+ // Compute execution overlay from current scrubber position
429
+ const overlay = useMemo<ExecutionOverlay | undefined>(() => {
430
+ if (!activeSnapshots) return undefined;
431
+ const executionOrder = activeSnapshots
432
+ .slice(0, snapshotIdx + 1)
433
+ .map((s) => s.stageLabel);
434
+ const doneStages = new Set(
435
+ activeSnapshots.slice(0, snapshotIdx).map((s) => s.stageLabel)
436
+ );
437
+ const activeStage = activeSnapshots[snapshotIdx]?.stageLabel ?? null;
438
+ const executedStages = new Set([...doneStages]);
439
+ if (activeStage) executedStages.add(activeStage);
440
+ return { doneStages, activeStage, executedStages, executionOrder };
441
+ }, [activeSnapshots, snapshotIdx]);
442
+
443
+ // Derive ReactFlow nodes/edges with overlay applied
444
+ const currentSpec =
445
+ subflowNav.breadcrumbs[subflowNav.breadcrumbs.length - 1]?.spec ?? null;
446
+ const flowData = useMemo(() => {
447
+ if (!currentSpec || !activeSnapshots) return null;
448
+ return specToReactFlow(currentSpec, overlay);
449
+ }, [currentSpec, activeSnapshots, overlay]);
450
+
451
+ return {
452
+ subflowNav,
453
+ activeSnapshots,
454
+ snapshotIdx,
455
+ setSnapshotIdx,
456
+ currentSnap: activeSnapshots?.[snapshotIdx] ?? null,
457
+ flowData,
458
+ };
459
+ }
255
460
 
256
- Convert FootPrint runtime snapshots to the `StageSnapshot[]` format:
461
+ // ─── Main component ──────────────────────────────────────────────────
462
+ function PipelinePlayground({ chart, spec }: { chart: any; spec: SpecNode }) {
463
+ const [snapshots, setSnapshots] = useState<any[] | null>(null);
257
464
 
258
- ```typescript
259
- import { toVisualizationSnapshots } from "footprint-explainable-ui";
465
+ async function run() {
466
+ const executor = new FlowChartExecutor(chart);
467
+ await executor.run();
468
+ setSnapshots(toVisualizationSnapshots(executor.getSnapshot()));
469
+ }
260
470
 
261
- const executor = new FlowChartExecutor(pipeline);
262
- await executor.run(scope);
471
+ const { subflowNav, activeSnapshots, snapshotIdx, setSnapshotIdx, currentSnap, flowData } =
472
+ useFlowchartData(spec, snapshots);
263
473
 
264
- const snapshots = toVisualizationSnapshots(executor.getSnapshot());
474
+ return (
475
+ <FootprintTheme tokens={warmDark}>
476
+ <button onClick={run}>Run Pipeline</button>
477
+
478
+ {/* Flowchart with execution overlay */}
479
+ <div style={{ height: 400 }}>
480
+ {subflowNav.isInSubflow && (
481
+ <SubflowBreadcrumb
482
+ breadcrumbs={subflowNav.breadcrumbs}
483
+ onNavigate={subflowNav.navigateTo}
484
+ />
485
+ )}
486
+ {flowData && (
487
+ <ReactFlow
488
+ nodes={flowData.nodes}
489
+ edges={flowData.edges}
490
+ nodeTypes={nodeTypes}
491
+ onNodeClick={(_, node) => subflowNav.handleNodeClick(node.id)}
492
+ fitView
493
+ />
494
+ )}
495
+ </div>
496
+
497
+ {activeSnapshots && (
498
+ <>
499
+ {/* Time-travel scrubber */}
500
+ <div style={{ display: "flex", gap: 8, alignItems: "center" }}>
501
+ <button
502
+ disabled={snapshotIdx <= 0}
503
+ onClick={() => setSnapshotIdx((i) => i - 1)}
504
+ >
505
+ Prev
506
+ </button>
507
+ <input
508
+ type="range"
509
+ min={0}
510
+ max={activeSnapshots.length - 1}
511
+ value={snapshotIdx}
512
+ onChange={(e) => setSnapshotIdx(Number(e.target.value))}
513
+ />
514
+ <button
515
+ disabled={snapshotIdx >= activeSnapshots.length - 1}
516
+ onClick={() => setSnapshotIdx((i) => i + 1)}
517
+ >
518
+ Next
519
+ </button>
520
+ <span>
521
+ {currentSnap?.stageLabel} ({snapshotIdx + 1}/{activeSnapshots.length})
522
+ </span>
523
+ </div>
524
+
525
+ {/* Detail panels */}
526
+ <MemoryInspector
527
+ snapshots={activeSnapshots}
528
+ selectedIndex={snapshotIdx}
529
+ />
530
+ <ScopeDiff
531
+ previous={snapshotIdx > 0 ? activeSnapshots[snapshotIdx - 1].memory : null}
532
+ current={currentSnap?.memory ?? {}}
533
+ hideUnchanged
534
+ />
535
+ <NarrativeTrace
536
+ narrative={activeSnapshots.map((s) => s.narrative)}
537
+ />
538
+ <GanttTimeline
539
+ snapshots={activeSnapshots}
540
+ selectedIndex={snapshotIdx}
541
+ onSelect={setSnapshotIdx}
542
+ />
543
+ </>
544
+ )}
545
+ </FootprintTheme>
546
+ );
547
+ }
265
548
  ```
266
549
 
267
- ## Rendering in Different Contexts
550
+ This gives you:
551
+ - Flowchart with Google Maps-style execution path overlay
552
+ - Click subflow nodes to drill down (breadcrumb navigation back)
553
+ - Prev/Next scrubber synced with flowchart highlighting
554
+ - Memory inspector, scope diffs, narrative trace, and Gantt timeline
555
+ - All themed via `FootprintTheme`
268
556
 
269
- The library is render-target agnostic. Use components in:
557
+ See the full implementation in the [footprint-playground](https://github.com/footprintjs/footprint-playground) repo.
270
558
 
271
- - **Inline content** — drop into any React layout
272
- - **Modal/dialog** — wrap in your modal component
273
- - **Sidebar panel** — use `size="compact"` for narrow panels
274
- - **Full-page dashboard** — use `ExplainableShell` with all tabs
275
- - **Embedded widget** — use `unstyled` mode + custom CSS
559
+ ---
276
560
 
277
- ```tsx
278
- // In a modal
279
- <Dialog>
280
- <ExplainableShell snapshots={snapshots} narrative={narrative} size="compact" />
281
- </Dialog>
282
-
283
- // In a sidebar
284
- <aside style={{ width: 320 }}>
285
- <NarrativeTrace narrative={narrative} size="compact" />
286
- <GanttTimeline snapshots={snapshots} size="compact" />
287
- </aside>
288
- ```
561
+ ## License
562
+
563
+ MIT
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "footprint-explainable-ui",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
4
4
  "description": "Themeable React components for visualizing FootPrint pipeline execution",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",