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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # footprint-explainable-ui
2
2
 
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.
3
+ Themeable React components for visualizing [footprintjs](https://github.com/footprintjs/footPrint) pipeline execution — time-travel debugging, flowchart overlays, subflow drill-down, and collapsible detail panels.
4
4
 
5
5
  ## Install
6
6
 
@@ -21,46 +21,62 @@ npm install @xyflow/react
21
21
  | Import path | What it provides |
22
22
  |---|---|
23
23
  | `footprint-explainable-ui` | Core components, themes, adapters |
24
- | `footprint-explainable-ui/flowchart` | Flowchart visualization, subflow navigation (requires `@xyflow/react`) |
24
+ | `footprint-explainable-ui/flowchart` | Flowchart visualization (requires `@xyflow/react`) |
25
25
 
26
26
  ## Quick Start
27
27
 
28
- ### 1. Convert FootPrint execution data to snapshots
28
+ ### 1. Convert execution data to snapshots
29
29
 
30
30
  ```typescript
31
- import { FlowChartExecutor } from "footprint";
31
+ import { FlowChartExecutor } from "footprintjs";
32
32
  import { toVisualizationSnapshots } from "footprint-explainable-ui";
33
33
 
34
34
  const executor = new FlowChartExecutor(chart);
35
- await executor.run();
35
+ await executor.run({ input: data });
36
36
 
37
- // Convert runtime snapshot → visualization snapshots
38
- const snapshots = toVisualizationSnapshots(executor.getSnapshot());
37
+ const snapshots = toVisualizationSnapshots(
38
+ executor.getSnapshot(),
39
+ executor.getNarrativeEntries(), // optional — enables rich narrative
40
+ );
39
41
  ```
40
42
 
41
43
  ### 2. Render with the all-in-one shell
42
44
 
43
45
  ```tsx
44
- import {
45
- ExplainableShell,
46
- FootprintTheme,
47
- warmDark,
48
- } from "footprint-explainable-ui";
46
+ import { ExplainableShell } from "footprint-explainable-ui";
47
+ import { TracedFlowchartView } from "footprint-explainable-ui/flowchart";
49
48
 
50
- function App({ snapshots, narrative, result }) {
49
+ function DebugView({ snapshots, spec, narrative, narrativeEntries }) {
51
50
  return (
52
- <FootprintTheme tokens={warmDark}>
53
- <ExplainableShell
54
- snapshots={snapshots}
55
- narrative={narrative}
56
- resultData={result}
57
- logs={consoleLogs}
58
- />
59
- </FootprintTheme>
51
+ <ExplainableShell
52
+ snapshots={snapshots}
53
+ spec={spec}
54
+ narrative={narrative}
55
+ narrativeEntries={narrativeEntries}
56
+ title="My Pipeline"
57
+ panelLabels={{ topology: "What Ran", details: "What Happened", timeline: "How Long" }}
58
+ renderFlowchart={({ spec, snapshots, selectedIndex, onNodeClick }) => (
59
+ <TracedFlowchartView
60
+ spec={spec}
61
+ snapshots={snapshots}
62
+ snapshotIndex={selectedIndex}
63
+ onNodeClick={onNodeClick}
64
+ />
65
+ )}
66
+ />
60
67
  );
61
68
  }
62
69
  ```
63
70
 
71
+ This gives you:
72
+ - **Flowchart** (center) — execution path overlay, click subflow nodes to drill-down
73
+ - **Topology panel** (left) — subflow tree navigator, collapsible via VLinePill handle
74
+ - **Details panel** (right) — Memory state + Narrative tabs, collapsible
75
+ - **Timeline** (bottom) — Gantt-style stage durations, collapsible
76
+ - **Time-travel slider** — scrub through execution steps
77
+ - **Breadcrumbs** — navigate back from subflow drill-down
78
+ - **Mobile responsive** — auto-stacks vertically below 640px
79
+
64
80
  ### 3. Or compose individual components
65
81
 
66
82
  ```tsx
@@ -99,225 +115,161 @@ function MyDebugger({ snapshots }) {
99
115
 
100
116
  ---
101
117
 
102
- ## Flowchart Visualization
118
+ ## ExplainableShell
103
119
 
104
- Import from `footprint-explainable-ui/flowchart`:
120
+ The all-in-one orchestrator. Handles time-travel, subflow drill-down, memory/narrative panels, and responsive layout.
105
121
 
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
122
+ ### Props
118
123
 
119
- ```tsx
120
- import { ReactFlow } from "@xyflow/react";
121
- import "@xyflow/react/dist/style.css";
122
- import { specToReactFlow, StageNode } from "footprint-explainable-ui/flowchart";
124
+ | Prop | Type | Default | Description |
125
+ |---|---|---|---|
126
+ | `snapshots` | `StageSnapshot[]` | required | Visualization snapshots |
127
+ | `spec` | `SpecNode \| null` | — | Pipeline spec (enables flowchart + subflow tree) |
128
+ | `title` | `string` | `"Flowchart"` | Breadcrumb root label |
129
+ | `narrative` | `string[]` | — | Flat narrative lines |
130
+ | `narrativeEntries` | `NarrativeEntry[]` | — | Structured narrative (rich rendering) |
131
+ | `panelLabels` | `PanelLabels` | `{ topology: "Topology", details: "Details", timeline: "Timeline" }` | Customize collapsible pill labels |
132
+ | `defaultExpanded` | `DefaultExpanded` | `{ details: true }` | Which panels start open |
133
+ | `tabs` | `ShellTab[]` | `["result", "explainable"]` | Visible tabs |
134
+ | `renderFlowchart` | `(props) => ReactNode` | — | Flowchart renderer (pass `TracedFlowchartView`) |
135
+ | `resultData` | `Record<string, unknown>` | — | Final output data for Result tab |
136
+ | `size` | `"compact" \| "default" \| "detailed"` | `"default"` | Size variant |
137
+ | `unstyled` | `boolean` | `false` | Strip styles, render `data-fp` attributes |
123
138
 
124
- const nodeTypes = { stage: StageNode };
139
+ ### Panel Labels
125
140
 
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
- ### Self-contained traced flowchart (recommended)
141
-
142
- `TracedFlowchartView` handles everything — overlay computation, subflow drill-down, breadcrumbs. Just pass `spec` + `snapshots`:
141
+ Customize the text on collapsible pill buttons. Semantic keys — not tied to position:
143
142
 
144
143
  ```tsx
145
- import { TracedFlowchartView } from "footprint-explainable-ui/flowchart";
146
-
147
- function MyDebugger({ spec, snapshots }) {
148
- const [idx, setIdx] = useState(0);
149
-
150
- return (
151
- <div style={{ height: 400 }}>
152
- <TracedFlowchartView
153
- spec={spec}
154
- snapshots={snapshots}
155
- snapshotIndex={idx}
156
- onNodeClick={(i) => setIdx(i as number)}
157
- />
158
- </div>
159
- );
160
- }
144
+ <ExplainableShell
145
+ panelLabels={{
146
+ topology: "What Ran", // left panel (subflow tree)
147
+ details: "What Happened", // right panel (memory/narrative)
148
+ timeline: "How Long", // bottom panel (Gantt)
149
+ }}
150
+ />
161
151
  ```
162
152
 
163
- Without `snapshots`, it renders a plain static flowchart. With `snapshots`, it shows the execution trace path.
164
-
165
- ### With execution overlay (manual control)
153
+ ### Default Expanded
166
154
 
167
- For full control, use `specToReactFlow` directly. The overlay highlights which stages have executed, which is active, and the execution path like a Google Maps route overlay.
155
+ Control which panels start open. Desktop default: details panel open (flowchart + memory = the library's unique value). For mobile, pass all `false`:
168
156
 
169
157
  ```tsx
170
- import { specToReactFlow, type ExecutionOverlay } from "footprint-explainable-ui/flowchart";
158
+ // Desktop (default) memory panel open
159
+ <ExplainableShell snapshots={...} spec={...} />
171
160
 
172
- // Build overlay from your current time-travel position
173
- const overlay: ExecutionOverlay = {
174
- doneStages: new Set(["LoadOrder", "ProcessPayment"]),
175
- activeStage: "ShipOrder",
176
- executedStages: new Set(["LoadOrder", "ProcessPayment", "ShipOrder"]),
177
- executionOrder: ["LoadOrder", "ProcessPayment", "ShipOrder"],
178
- };
161
+ // Mobile all collapsed, flowchart fills screen
162
+ <ExplainableShell
163
+ snapshots={...}
164
+ defaultExpanded={{ details: false }}
165
+ />
179
166
 
180
- const { nodes, edges } = specToReactFlow(spec, overlay);
167
+ // Everything open
168
+ <ExplainableShell
169
+ snapshots={...}
170
+ defaultExpanded={{ topology: true, details: true, timeline: true }}
171
+ />
181
172
  ```
182
173
 
183
- **Tip:** Compute the overlay from your snapshots array and current index:
174
+ ### Responsive Layout
184
175
 
185
- ```tsx
186
- function buildOverlay(snapshots, idx): ExecutionOverlay {
187
- const executionOrder = snapshots.slice(0, idx + 1).map(s => s.stageLabel);
188
- const doneStages = new Set(snapshots.slice(0, idx).map(s => s.stageLabel));
189
- const activeStage = snapshots[idx]?.stageLabel ?? null;
190
- const executedStages = new Set([...doneStages]);
191
- if (activeStage) executedStages.add(activeStage);
192
- return { doneStages, activeStage, executedStages, executionOrder };
193
- }
194
- ```
176
+ The shell auto-detects container width via `ResizeObserver`:
195
177
 
196
- ### Custom edge colors
178
+ - **Desktop (≥640px):** 3-column layout — SubflowTree | Flowchart | Memory/Narrative. Side panels collapse to VLinePill handles.
179
+ - **Mobile (<640px):** Stacked vertical — Flowchart (350px) → collapsible HLinePill sections. All panels auto-collapse on narrow.
197
180
 
198
- ```tsx
199
- const { nodes, edges } = specToReactFlow(spec, overlay, {
200
- edgeExecuted: "#00ff88", // Completed path
201
- edgeActive: "#ff6b6b", // Currently executing
202
- });
203
- ```
181
+ ### Collapsible Panel UX
204
182
 
205
- ### Subflow drill-down navigation
183
+ All panels use the **line + pill** pattern:
184
+ - **Collapsed:** Thin divider line with a centered pill button (label + arrow)
185
+ - **Expanded:** Full content with a pill handle on the closing edge
186
+ - **VLinePill** (left/right panels): Vertical line with centered vertical pill. `side` prop controls arrow direction.
187
+ - **HLinePill** (bottom timeline): Horizontal line with centered pill.
206
188
 
207
- For pipelines with nested subflows, `useSubflowNavigation` manages a breadcrumb stack. Clicking a subflow node drills into its internal flowchart.
189
+ ---
208
190
 
209
- ```tsx
210
- import {
211
- useSubflowNavigation,
212
- SubflowBreadcrumb,
213
- specToReactFlow,
214
- StageNode,
215
- } from "footprint-explainable-ui/flowchart";
216
- import { ReactFlow } from "@xyflow/react";
191
+ ## Flowchart Visualization
217
192
 
218
- const nodeTypes = { stage: StageNode };
193
+ Import from `footprint-explainable-ui/flowchart`:
219
194
 
220
- function DrillDownChart({ spec, overlay }) {
221
- const subflowNav = useSubflowNavigation(spec);
195
+ ### TracedFlowchartView (recommended)
222
196
 
223
- // Get the current level's spec from breadcrumbs
224
- const currentSpec = subflowNav.breadcrumbs[subflowNav.breadcrumbs.length - 1].spec;
225
- const { nodes, edges } = specToReactFlow(currentSpec, overlay);
197
+ Self-contained flowchart renderer. Handles overlay computation, auto-fitView on resize.
226
198
 
227
- return (
228
- <div>
229
- {/* Breadcrumb bar: Pipeline > PaymentSubflow */}
230
- {subflowNav.isInSubflow && (
231
- <SubflowBreadcrumb
232
- breadcrumbs={subflowNav.breadcrumbs}
233
- onNavigate={subflowNav.navigateTo}
234
- />
235
- )}
199
+ ```tsx
200
+ import { TracedFlowchartView } from "footprint-explainable-ui/flowchart";
236
201
 
237
- <ReactFlow
238
- nodes={nodes}
239
- edges={edges}
240
- nodeTypes={nodeTypes}
241
- onNodeClick={(_, node) => subflowNav.handleNodeClick(node.id)}
242
- fitView
243
- />
244
- </div>
245
- );
246
- }
202
+ <div style={{ height: 400 }}>
203
+ <TracedFlowchartView
204
+ spec={spec}
205
+ snapshots={snapshots}
206
+ snapshotIndex={idx}
207
+ onNodeClick={(nodeId) => handleClick(nodeId)}
208
+ />
209
+ </div>
247
210
  ```
248
211
 
249
- **`useSubflowNavigation` returns:**
212
+ Without `snapshots`, renders a plain static flowchart. With `snapshots`, shows the execution trace path with Google Maps-style glow.
250
213
 
251
- | Property | Type | Description |
252
- |---|---|---|
253
- | `breadcrumbs` | `BreadcrumbEntry[]` | Stack from root to current level |
254
- | `nodes` | `Node[]` | ReactFlow nodes for current level |
255
- | `edges` | `Edge[]` | ReactFlow edges for current level |
256
- | `handleNodeClick` | `(nodeId) => boolean` | Drills into subflow if applicable |
257
- | `navigateTo` | `(level) => void` | Jump to breadcrumb level (0 = root) |
258
- | `isInSubflow` | `boolean` | Whether we're inside a subflow |
259
- | `currentSubflowNodeName` | `string \| null` | Name of the subflow node drilled into |
214
+ **Auto-fitView:** The flowchart automatically calls `fitView()` when its container resizes (e.g. panel expand/collapse).
260
215
 
261
- ### Extracting subflow execution data
262
-
263
- When drilled into a subflow, extract its execution snapshots from the parent's memory:
216
+ ### Manual control with specToReactFlow
264
217
 
265
218
  ```tsx
266
- import { toVisualizationSnapshots } from "footprint-explainable-ui";
219
+ import { specToReactFlow, StageNode, type ExecutionOverlay } from "footprint-explainable-ui/flowchart";
220
+ import { ReactFlow } from "@xyflow/react";
267
221
 
268
- // Find the parent stage that contains the subflow result
269
- const parentSnap = parentSnapshots.find(s => s.stageLabel === subflowNav.currentSubflowNodeName);
270
- const sfResult = parentSnap?.memory?.subflowResult;
271
- const tc = sfResult?.treeContext;
272
-
273
- if (tc?.stageContexts) {
274
- const subflowSnapshots = toVisualizationSnapshots({
275
- sharedState: tc.globalContext,
276
- executionTree: tc.stageContexts,
277
- commitLog: tc.history ?? [],
278
- });
279
-
280
- // Strip builder's "subflowId/" prefix from stage names
281
- const prefix = sfResult.subflowId ? `${sfResult.subflowId}/` : null;
282
- if (prefix) {
283
- for (const snap of subflowSnapshots) {
284
- if (snap.stageLabel.startsWith(prefix))
285
- snap.stageLabel = snap.stageLabel.slice(prefix.length);
286
- if (snap.stageName.startsWith(prefix))
287
- snap.stageName = snap.stageName.slice(prefix.length);
288
- }
289
- }
290
- }
222
+ const overlay: ExecutionOverlay = {
223
+ doneStages: new Set(["LoadOrder", "ProcessPayment"]),
224
+ activeStage: "ShipOrder",
225
+ executedStages: new Set(["LoadOrder", "ProcessPayment", "ShipOrder"]),
226
+ executionOrder: ["LoadOrder", "ProcessPayment", "ShipOrder"],
227
+ };
228
+
229
+ const { nodes, edges } = specToReactFlow(spec, overlay);
230
+
231
+ <ReactFlow
232
+ nodes={nodes}
233
+ edges={edges}
234
+ nodeTypes={{ stage: StageNode }}
235
+ fitView
236
+ />
291
237
  ```
292
238
 
293
239
  ---
294
240
 
295
241
  ## Theming
296
242
 
297
- ### ThemeProvider
298
-
299
- ```tsx
300
- import { FootprintTheme, warmDark, warmLight, coolDark } from "footprint-explainable-ui";
301
-
302
- <FootprintTheme tokens={warmDark}>
303
- <MyApp />
304
- </FootprintTheme>
305
- ```
243
+ ### CSS Variables (recommended)
306
244
 
307
- ### CSS Variables (no provider needed)
245
+ Consumer controls theme via `--fp-*` CSS custom properties. Components use `var(--fp-*, fallback)`:
308
246
 
309
247
  ```css
310
248
  :root {
311
249
  --fp-color-primary: #7c6cf0;
312
250
  --fp-bg-primary: #1e1a2e;
313
251
  --fp-bg-secondary: #2a2540;
252
+ --fp-bg-tertiary: #3a3455;
314
253
  --fp-text-primary: #f0e6d6;
254
+ --fp-text-secondary: #b0a898;
255
+ --fp-text-muted: #6b6b80;
315
256
  --fp-border: #3a3455;
316
257
  --fp-radius: 8px;
258
+ --fp-font-sans: 'Inter', system-ui, sans-serif;
317
259
  --fp-font-mono: 'JetBrains Mono', monospace;
318
260
  }
319
261
  ```
320
262
 
263
+ ### ThemeProvider
264
+
265
+ ```tsx
266
+ import { FootprintTheme, warmDark } from "footprint-explainable-ui";
267
+
268
+ <FootprintTheme tokens={warmDark}>
269
+ <MyApp />
270
+ </FootprintTheme>
271
+ ```
272
+
321
273
  ### Built-in Presets
322
274
 
323
275
  | Preset | Description |
@@ -325,31 +277,7 @@ import { FootprintTheme, warmDark, warmLight, coolDark } from "footprint-explain
325
277
  | `coolDark` | Default — indigo/slate dark theme |
326
278
  | `warmDark` | Charcoal-purple with warm text |
327
279
  | `warmLight` | Cream/peach light theme |
328
-
329
- ### Token Reference
330
-
331
- ```typescript
332
- interface ThemeTokens {
333
- colors?: {
334
- primary?: string; // Accent (buttons, highlights)
335
- success?: string; // Completed stages
336
- error?: string; // Error states
337
- warning?: string; // Warnings
338
- bgPrimary?: string; // Main background
339
- bgSecondary?: string; // Panel/card background
340
- bgTertiary?: string; // Hover/active background
341
- textPrimary?: string; // Main text
342
- textSecondary?: string; // Secondary text
343
- textMuted?: string; // Dimmed text
344
- border?: string; // Borders
345
- };
346
- radius?: string;
347
- fontFamily?: {
348
- sans?: string; // UI text font
349
- mono?: string; // Code/data font
350
- };
351
- }
352
- ```
280
+ | `coolLight` | Light indigo theme |
353
281
 
354
282
  ---
355
283
 
@@ -359,35 +287,47 @@ interface ThemeTokens {
359
287
 
360
288
  | Component | Description |
361
289
  |---|---|
362
- | `ExplainableShell` | Tabbed container: Result / Explainable / AI-Compatible |
290
+ | `ExplainableShell` | All-in-one orchestrator with collapsible panels and responsive layout |
363
291
  | `TimeTravelControls` | Play/pause, prev/next, scrubber timeline |
292
+ | `MemoryPanel` | Memory state + scope diff (composite right-panel view) |
293
+ | `NarrativePanel` | Narrative trace with progressive reveal |
294
+ | `StoryNarrative` | Rich rendering of structured `NarrativeEntry[]` |
364
295
  | `NarrativeTrace` | Collapsible stage groups with progressive reveal |
365
296
  | `NarrativeLog` | Simple timeline-style execution log |
366
297
  | `ScopeDiff` | Side-by-side scope changes (added/changed/removed) |
367
298
  | `ResultPanel` | Final pipeline output + console logs |
368
299
  | `MemoryInspector` | Accumulated memory state viewer |
369
- | `GanttTimeline` | Horizontal duration timeline |
300
+ | `GanttTimeline` | Horizontal duration timeline (collapsible) |
370
301
  | `SnapshotPanel` | All-in-one inspector (scrubber + memory + narrative + Gantt) |
371
302
 
372
303
  ### Flowchart Components (`footprint-explainable-ui/flowchart`)
373
304
 
374
305
  | Export | Description |
375
306
  |---|---|
376
- | `TracedFlowchartView` | Self-contained flowchart with trace overlay, subflow drill-down, breadcrumbs |
377
- | `FlowchartView` | Lower-level ReactFlow wrapper with execution state coloring |
307
+ | `TracedFlowchartView` | Self-contained flowchart with trace overlay and auto-fitView |
308
+ | `FlowchartView` | Lower-level ReactFlow wrapper |
378
309
  | `StageNode` | Custom node with state-aware coloring, step badges, pulse rings |
379
- | `specToReactFlow` | Convert pipeline spec → ReactFlow nodes/edges with path overlay |
380
- | `TimeTravelDebugger` | Full debugger with flowchart + all panels |
310
+ | `specToReactFlow` | Convert pipeline spec → ReactFlow nodes/edges with overlay |
381
311
  | `SubflowBreadcrumb` | Breadcrumb bar for subflow drill-down |
382
- | `useSubflowNavigation` | Hook managing subflow drill-down navigation stack |
312
+ | `SubflowTree` | Tree view of all subflows (used in shell's left panel) |
383
313
 
384
314
  ### Adapters
385
315
 
386
316
  | Export | Description |
387
317
  |---|---|
388
318
  | `toVisualizationSnapshots` | Convert `FlowChartExecutor.getSnapshot()` → `StageSnapshot[]` |
319
+ | `subflowResultToSnapshots` | Convert subflow result → `StageSnapshot[]` |
389
320
  | `createSnapshots` | Build `StageSnapshot[]` from simple arrays (testing/custom data) |
390
321
 
322
+ ### Types
323
+
324
+ | Export | Description |
325
+ |---|---|
326
+ | `PanelLabels` | `{ topology?, details?, timeline? }` — pill label customization |
327
+ | `DefaultExpanded` | `{ topology?, details?, timeline? }` — initial panel state |
328
+ | `StageSnapshot` | Core snapshot type for all components |
329
+ | `NarrativeEntry` | Structured narrative entry with type/depth/stageName |
330
+
391
331
  ---
392
332
 
393
333
  ## Size Variants
@@ -399,19 +339,6 @@ All components accept a `size` prop: `"compact"`, `"default"`, or `"detailed"`.
399
339
  <MemoryInspector snapshots={snapshots} size="detailed" />
400
340
  ```
401
341
 
402
- ### Collapsible GanttTimeline
403
-
404
- By default, the Gantt timeline collapses to 5 rows with an expand toggle. Auto-scrolls to keep the active stage visible:
405
-
406
- ```tsx
407
- <GanttTimeline
408
- snapshots={snapshots}
409
- selectedIndex={idx}
410
- onSelect={setIdx}
411
- maxVisibleRows={5} // default — set 0 to disable collapse
412
- />
413
- ```
414
-
415
342
  ## Unstyled Mode
416
343
 
417
344
  Strip all built-in styles for full CSS control. Components render semantic `data-fp` attributes:
@@ -423,178 +350,8 @@ Strip all built-in styles for full CSS control. Components render semantic `data
423
350
  ```css
424
351
  [data-fp="narrative-header"] { font-weight: bold; }
425
352
  [data-fp="narrative-step"] { padding-left: 2rem; }
426
- [data-fp="narrative-group"][data-latest="true"] { background: highlight; }
427
- ```
428
-
429
- ---
430
-
431
- ## Example: Build a Pipeline Playground
432
-
433
- 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/).
434
-
435
- ```tsx
436
- import { useState, useMemo } from "react";
437
- import { ReactFlow } from "@xyflow/react";
438
- import "@xyflow/react/dist/style.css";
439
- import {
440
- toVisualizationSnapshots,
441
- GanttTimeline,
442
- ScopeDiff,
443
- NarrativeTrace,
444
- MemoryInspector,
445
- FootprintTheme,
446
- warmDark,
447
- } from "footprint-explainable-ui";
448
- import {
449
- StageNode,
450
- specToReactFlow,
451
- useSubflowNavigation,
452
- SubflowBreadcrumb,
453
- type ExecutionOverlay,
454
- type SpecNode,
455
- } from "footprint-explainable-ui/flowchart";
456
- import { FlowChartExecutor } from "footprint";
457
-
458
- const nodeTypes = { stage: StageNode };
459
-
460
- // ─── Hook: time-travel + overlay + subflow drill-down ────────────────
461
- function useFlowchartData(spec: SpecNode | null, vizSnapshots: any[] | null) {
462
- const [snapshotIdx, setSnapshotIdx] = useState(0);
463
- const subflowNav = useSubflowNavigation(spec);
464
-
465
- const activeSnapshots = vizSnapshots; // extend with subflow logic as needed
466
-
467
- // Compute execution overlay from current scrubber position
468
- const overlay = useMemo<ExecutionOverlay | undefined>(() => {
469
- if (!activeSnapshots) return undefined;
470
- const executionOrder = activeSnapshots
471
- .slice(0, snapshotIdx + 1)
472
- .map((s) => s.stageLabel);
473
- const doneStages = new Set(
474
- activeSnapshots.slice(0, snapshotIdx).map((s) => s.stageLabel)
475
- );
476
- const activeStage = activeSnapshots[snapshotIdx]?.stageLabel ?? null;
477
- const executedStages = new Set([...doneStages]);
478
- if (activeStage) executedStages.add(activeStage);
479
- return { doneStages, activeStage, executedStages, executionOrder };
480
- }, [activeSnapshots, snapshotIdx]);
481
-
482
- // Derive ReactFlow nodes/edges with overlay applied
483
- const currentSpec =
484
- subflowNav.breadcrumbs[subflowNav.breadcrumbs.length - 1]?.spec ?? null;
485
- const flowData = useMemo(() => {
486
- if (!currentSpec || !activeSnapshots) return null;
487
- return specToReactFlow(currentSpec, overlay);
488
- }, [currentSpec, activeSnapshots, overlay]);
489
-
490
- return {
491
- subflowNav,
492
- activeSnapshots,
493
- snapshotIdx,
494
- setSnapshotIdx,
495
- currentSnap: activeSnapshots?.[snapshotIdx] ?? null,
496
- flowData,
497
- };
498
- }
499
-
500
- // ─── Main component ──────────────────────────────────────────────────
501
- function PipelinePlayground({ chart, spec }: { chart: any; spec: SpecNode }) {
502
- const [snapshots, setSnapshots] = useState<any[] | null>(null);
503
-
504
- async function run() {
505
- const executor = new FlowChartExecutor(chart);
506
- await executor.run();
507
- setSnapshots(toVisualizationSnapshots(executor.getSnapshot()));
508
- }
509
-
510
- const { subflowNav, activeSnapshots, snapshotIdx, setSnapshotIdx, currentSnap, flowData } =
511
- useFlowchartData(spec, snapshots);
512
-
513
- return (
514
- <FootprintTheme tokens={warmDark}>
515
- <button onClick={run}>Run Pipeline</button>
516
-
517
- {/* Flowchart with execution overlay */}
518
- <div style={{ height: 400 }}>
519
- {subflowNav.isInSubflow && (
520
- <SubflowBreadcrumb
521
- breadcrumbs={subflowNav.breadcrumbs}
522
- onNavigate={subflowNav.navigateTo}
523
- />
524
- )}
525
- {flowData && (
526
- <ReactFlow
527
- nodes={flowData.nodes}
528
- edges={flowData.edges}
529
- nodeTypes={nodeTypes}
530
- onNodeClick={(_, node) => subflowNav.handleNodeClick(node.id)}
531
- fitView
532
- />
533
- )}
534
- </div>
535
-
536
- {activeSnapshots && (
537
- <>
538
- {/* Time-travel scrubber */}
539
- <div style={{ display: "flex", gap: 8, alignItems: "center" }}>
540
- <button
541
- disabled={snapshotIdx <= 0}
542
- onClick={() => setSnapshotIdx((i) => i - 1)}
543
- >
544
- Prev
545
- </button>
546
- <input
547
- type="range"
548
- min={0}
549
- max={activeSnapshots.length - 1}
550
- value={snapshotIdx}
551
- onChange={(e) => setSnapshotIdx(Number(e.target.value))}
552
- />
553
- <button
554
- disabled={snapshotIdx >= activeSnapshots.length - 1}
555
- onClick={() => setSnapshotIdx((i) => i + 1)}
556
- >
557
- Next
558
- </button>
559
- <span>
560
- {currentSnap?.stageLabel} ({snapshotIdx + 1}/{activeSnapshots.length})
561
- </span>
562
- </div>
563
-
564
- {/* Detail panels */}
565
- <MemoryInspector
566
- snapshots={activeSnapshots}
567
- selectedIndex={snapshotIdx}
568
- />
569
- <ScopeDiff
570
- previous={snapshotIdx > 0 ? activeSnapshots[snapshotIdx - 1].memory : null}
571
- current={currentSnap?.memory ?? {}}
572
- hideUnchanged
573
- />
574
- <NarrativeTrace
575
- narrative={activeSnapshots.map((s) => s.narrative)}
576
- />
577
- <GanttTimeline
578
- snapshots={activeSnapshots}
579
- selectedIndex={snapshotIdx}
580
- onSelect={setSnapshotIdx}
581
- />
582
- </>
583
- )}
584
- </FootprintTheme>
585
- );
586
- }
587
353
  ```
588
354
 
589
- This gives you:
590
- - Flowchart with Google Maps-style execution path overlay
591
- - Click subflow nodes to drill down (breadcrumb navigation back)
592
- - Prev/Next scrubber synced with flowchart highlighting
593
- - Memory inspector, scope diffs, narrative trace, and Gantt timeline
594
- - All themed via `FootprintTheme`
595
-
596
- See the full implementation in the [footprint-playground](https://github.com/footprintjs/footprint-playground) repo.
597
-
598
355
  ---
599
356
 
600
357
  ## License