footprint-explainable-ui
v0.30.0
Published
Themeable React components for visualizing FootPrint pipeline execution
Maintainers
Readme
footprint-explainable-ui
Themeable React components for visualizing footprintjs pipeline execution — time-travel debugging, flowchart overlays, subflow drill-down, and collapsible detail panels.
Part of the footprintjs ecosystem — the self-explaining stack.
Install
npm install footprint-explainable-uiPeer dependencies: react >= 18, react-dom >= 18
For flowchart components, also install:
npm install @xyflow/reactEntry Points
| Import path | What it provides |
|---|---|
| footprint-explainable-ui | Core components, themes, adapters |
| footprint-explainable-ui/flowchart | Flowchart visualization (requires @xyflow/react) |
Quick Start
1. Convert execution data to snapshots
import { FlowChartExecutor } from "footprintjs";
import { toVisualizationSnapshots } from "footprint-explainable-ui";
const executor = new FlowChartExecutor(chart);
await executor.run({ input: data });
const snapshots = toVisualizationSnapshots(
executor.getSnapshot(),
executor.getNarrativeEntries(), // optional — enables rich narrative
);2. Render with the all-in-one shell
import { ExplainableShell } from "footprint-explainable-ui";
function DebugView({ snapshots, narrativeEntries, traceGraph, runtimeOverlay }) {
return (
<ExplainableShell
snapshots={snapshots}
narrativeEntries={narrativeEntries}
traceGraph={traceGraph}
runtimeOverlay={runtimeOverlay}
title="My Pipeline"
panelLabels={{ topology: "What Ran", details: "What Happened", timeline: "How Long" }}
/>
);
}This gives you:
- Flowchart (center) — execution path overlay, click subflow nodes to drill-down
- Topology panel (left) — subflow tree navigator, collapsible via VLinePill handle
- Details panel (right) — Memory state + Narrative tabs, collapsible
- Timeline (bottom) — Gantt-style stage durations, collapsible
- Time-travel slider — scrub through execution steps
- Breadcrumbs — navigate back from subflow drill-down
- Mobile responsive — auto-stacks vertically below 640px
3. Or compose individual components
import {
TimeTravelControls,
MemoryInspector,
ScopeDiff,
GanttTimeline,
NarrativeTrace,
} from "footprint-explainable-ui";
function MyDebugger({ snapshots }) {
const [idx, setIdx] = useState(0);
const current = snapshots[idx];
const previous = idx > 0 ? snapshots[idx - 1] : null;
return (
<>
<TimeTravelControls
snapshots={snapshots}
selectedIndex={idx}
onIndexChange={setIdx}
/>
<MemoryInspector snapshots={snapshots} selectedIndex={idx} />
<ScopeDiff
previous={previous?.memory ?? null}
current={current.memory}
hideUnchanged
/>
<NarrativeTrace narrative={snapshots.map(s => s.narrative)} />
<GanttTimeline snapshots={snapshots} selectedIndex={idx} onSelect={setIdx} />
</>
);
}ExplainableShell
The all-in-one orchestrator. Handles time-travel, subflow drill-down, memory/narrative panels, and responsive layout.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| snapshots | StageSnapshot[] | required | Visualization snapshots |
| traceGraph | TraceGraph \| null | — | Build-time graph — drives the flowchart + subflow drill-down |
| runtimeOverlay | RuntimeOverlay | — | Per-step execution overlay — lights the executed path |
| title | string | "Flowchart" | Breadcrumb root label |
| narrative | string[] | — | Flat narrative lines |
| narrativeEntries | NarrativeEntry[] | — | Structured narrative (rich rendering) |
| panelLabels | PanelLabels | { topology: "Topology", details: "Details", timeline: "Timeline" } | Customize collapsible pill labels |
| defaultExpanded | DefaultExpanded | { details: true } | Which panels start open |
| tabs | ShellTab[] | ["result", "explainable"] | Visible tabs |
| renderFlowchart | (props) => ReactNode | — | Flowchart renderer (pass TracedFlowchartView) |
| resultData | Record<string, unknown> | — | Final output data for Result tab |
| size | "compact" \| "default" \| "detailed" | "default" | Size variant |
| unstyled | boolean | false | Strip styles, render data-fp attributes |
Tracing a value — walk the timeline backward through its causes

Open Inspector → Data Trace and click one of the "Trace a value" chips. The time slider stays the same rail — the stages that made that value light up as stops, everything else fades to unlandable ticks, and the buttons become ◀ earlier cause / toward result ▶. This works because every ingredient of a value was always written earlier in the run than the value it fed, so the dependency chain is a sub-sequence of the timeline you already have. One cursor, no new axis.
- A value made from two ingredients shows both as colored chips —
pressing "earlier cause" visits both (most recent first); nothing is ever
silently skipped. Click a chip to follow just that ingredient (the
breadcrumb shows
key ▸ via ingredient · show all). - Every stop shows the world as it was at that moment — the state panel time-travels with the walk for free.
- Honest absence: a value nobody wrote gets a truthful card ("never written in this run — it arrived with the run's inputs"), and a value not written yet at the cursor's moment says exactly that, naming where its first write happens. Reads-off runs say "unknowable, not absent".
- [Copy story] emits the same text an LLM backtrack tool returns — the human's board and the agent's answer are one artifact.
- Tracing lives on the root rail: drilling into a subflow exits it honestly.
- Trace anything: below the current step's chips, a search box lists every variable the run ever wrote — trace any of them from wherever you stand.
- Forks ask, never assume: at a value made from two or more ingredients the walk-back button becomes ⑂ choose cause… and asks which ingredient to follow (or "visit all, in time order"). Nothing is ever silently picked.

- Unmistakable mode: the whole tracing rail wears its own color
(
--fp-tracing, teal by default — themeable) so tracing can never be confused with normal time-travel.
Panel Labels
Customize the text on collapsible pill buttons. Semantic keys — not tied to position:
<ExplainableShell
panelLabels={{
topology: "What Ran", // left panel (subflow tree)
details: "What Happened", // right panel (memory/narrative)
timeline: "How Long", // bottom panel (Gantt)
}}
/>Default Expanded
Control which panels start open. Desktop default: details panel open (flowchart + memory = the library's unique value). For mobile, pass all false:
// Desktop (default) — memory panel open
<ExplainableShell snapshots={...} traceGraph={...} runtimeOverlay={...} />
// Mobile — all collapsed, flowchart fills screen
<ExplainableShell
snapshots={...}
defaultExpanded={{ details: false }}
/>
// Everything open
<ExplainableShell
snapshots={...}
defaultExpanded={{ topology: true, details: true, timeline: true }}
/>Responsive Layout
The shell auto-detects container width via ResizeObserver:
- Desktop (≥640px): 3-column layout — SubflowTree | Flowchart | Memory/Narrative. Side panels collapse to VLinePill handles.
- Mobile (<640px): Stacked vertical — Flowchart (350px) → collapsible HLinePill sections. All panels auto-collapse on narrow.
Collapsible Panel UX
All panels use the line + pill pattern:
- Collapsed: Thin divider line with a centered pill button (label + arrow)
- Expanded: Full content with a pill handle on the closing edge
- VLinePill (left/right panels): Vertical line with centered vertical pill.
sideprop controls arrow direction. - HLinePill (bottom timeline): Horizontal line with centered pill.
Flowchart Visualization
Import from footprint-explainable-ui/flowchart:
TracedFlowchartView (recommended)
Self-contained flowchart renderer. Handles overlay computation, auto-fitView on resize.
import { TracedFlowchartView } from "footprint-explainable-ui/flowchart";
<div style={{ height: 400 }}>
<TracedFlowchartView
spec={spec}
snapshots={snapshots}
snapshotIndex={idx}
onNodeClick={(nodeId) => handleClick(nodeId)}
/>
</div>Without snapshots, renders a plain static flowchart. With snapshots, shows the execution trace path with Google Maps-style glow.
Auto-fitView: The flowchart automatically calls fitView() when its container resizes (e.g. panel expand/collapse).
Manual control with specToReactFlow
import { specToReactFlow, StageNode, type ExecutionOverlay } from "footprint-explainable-ui/flowchart";
import { ReactFlow } from "@xyflow/react";
const overlay: ExecutionOverlay = {
doneStages: new Set(["LoadOrder", "ProcessPayment"]),
activeStage: "ShipOrder",
executedStages: new Set(["LoadOrder", "ProcessPayment", "ShipOrder"]),
executionOrder: ["LoadOrder", "ProcessPayment", "ShipOrder"],
};
const { nodes, edges } = specToReactFlow(spec, overlay);
<ReactFlow
nodes={nodes}
edges={edges}
nodeTypes={{ stage: StageNode }}
fitView
/>Theming
CSS Variables (recommended)
Consumer controls theme via --fp-* CSS custom properties. Components use var(--fp-*, fallback):
:root {
--fp-color-primary: #7c6cf0;
--fp-bg-primary: #1e1a2e;
--fp-bg-secondary: #2a2540;
--fp-bg-tertiary: #3a3455;
--fp-text-primary: #f0e6d6;
--fp-text-secondary: #b0a898;
--fp-text-muted: #6b6b80;
--fp-border: #3a3455;
--fp-radius: 8px;
--fp-font-sans: 'Inter', system-ui, sans-serif;
--fp-font-mono: 'JetBrains Mono', monospace;
}ThemeProvider
import { FootprintTheme, warmDark } from "footprint-explainable-ui";
<FootprintTheme tokens={warmDark}>
<MyApp />
</FootprintTheme>Built-in Presets
| Preset | Description |
|---|---|
| coolDark | Default — indigo/slate dark theme |
| warmDark | Charcoal-purple with warm text |
| warmLight | Cream/peach light theme |
| coolLight | Light indigo theme |
Components Reference
Core Components
| Component | Description |
|---|---|
| ExplainableShell | All-in-one orchestrator with collapsible panels and responsive layout |
| TimeTravelControls | Play/pause, prev/next, scrubber timeline |
| MemoryPanel | Memory state + scope diff (composite right-panel view) |
| NarrativePanel | Narrative trace with progressive reveal |
| StoryNarrative | Rich rendering of structured NarrativeEntry[] |
| NarrativeTrace | Collapsible stage groups with progressive reveal |
| NarrativeLog | Simple timeline-style execution log |
| ScopeDiff | Side-by-side scope changes (added/changed/removed) |
| ResultPanel | Final pipeline output + console logs |
| MemoryInspector | Accumulated memory state viewer |
| GanttTimeline | Horizontal duration timeline (collapsible) |
| SnapshotPanel | All-in-one inspector (scrubber + memory + narrative + Gantt) |
Flowchart Components (footprint-explainable-ui/flowchart)
| Export | Description |
|---|---|
| TracedFlowchartView | Self-contained flowchart with trace overlay and auto-fitView |
| FlowchartView | Lower-level ReactFlow wrapper |
| StageNode | Custom node with state-aware coloring, step badges, pulse rings |
| specToReactFlow | Convert pipeline spec → ReactFlow nodes/edges with overlay |
| SubflowBreadcrumb | Breadcrumb bar for subflow drill-down |
| SubflowTree | Tree view of all subflows (used in shell's left panel) |
Adapters
| Export | Description |
|---|---|
| toVisualizationSnapshots | Convert FlowChartExecutor.getSnapshot() → StageSnapshot[] |
| subflowResultToSnapshots | Convert subflow result → StageSnapshot[] |
| createSnapshots | Build StageSnapshot[] from simple arrays (testing/custom data) |
Types
| Export | Description |
|---|---|
| PanelLabels | { topology?, details?, timeline? } — pill label customization |
| DefaultExpanded | { topology?, details?, timeline? } — initial panel state |
| StageSnapshot | Core snapshot type for all components |
| NarrativeEntry | Structured narrative entry with type/depth/stageName |
Size Variants
All components accept a size prop: "compact", "default", or "detailed".
<GanttTimeline snapshots={snapshots} size="compact" />
<MemoryInspector snapshots={snapshots} size="detailed" />Unstyled Mode
Strip all built-in styles for full CSS control. Components render semantic data-fp attributes:
<NarrativeTrace narrative={lines} unstyled className="my-narrative" />[data-fp="narrative-header"] { font-weight: bold; }
[data-fp="narrative-step"] { padding-left: 2rem; }Golden-Trace Fixtures (contributors)
The pipeline (structure/runtime translators, dagre layout, snapshot adapter,
narrative sync) is pinned against real footprintjs engine output, not
hand-built mocks. test/fixtures/golden/ holds recorded traces from 4
representative charts (linear+decider, subflow+loop, parallel fork,
pause/resume); test/golden/goldenTraces.test.ts replays them through the full
pipeline and snapshot-asserts the outputs in test/golden/__snapshots__/.
- Engine shape changed (new footprintjs):
npm i -D --save-exact footprintjs@<version> && npm run fixtures:regen. The generator runs every chart twice and fails on any nondeterminism. - Pipeline output changed intentionally (eui edit):
npx vitest run test/golden -u, then review the snapshot diff. test/fixtures/golden/manifest.jsonrecords the footprintjs version the fixtures were recorded with.
footprintjs is a devDependency used ONLY by the generator — the published
library still has zero footprintjs dependency (it consumes plain JSON shapes).
The footprintjs ecosystem
The self-explaining stack — from backend pipelines to AI agents. → overview
| Project | Role | |---|---| | footprintjs | the flowchart pattern (core engine) | | agentfootprint | build self-explaining AI agents | | Explainable UI ← you are here | visualize a footprintjs run | | Lens | debug an agentfootprint run | | Thinking UI | replay an agent run for non-devs |
License
MIT
