footprint-explainable-ui 0.3.1 → 0.4.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/README.md +211 -3
- package/dist/flowchart.cjs +314 -167
- package/dist/flowchart.cjs.map +1 -1
- package/dist/flowchart.d.cts +15 -1
- package/dist/flowchart.d.ts +15 -1
- package/dist/flowchart.js +311 -161
- package/dist/flowchart.js.map +1 -1
- package/dist/index.cjs +64 -11
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -2
- package/dist/index.d.ts +5 -2
- package/dist/index.js +74 -21
- package/dist/index.js.map +1 -1
- package/package.json +5 -2
package/README.md
CHANGED
|
@@ -137,9 +137,34 @@ function PipelineChart({ spec }) {
|
|
|
137
137
|
}
|
|
138
138
|
```
|
|
139
139
|
|
|
140
|
-
###
|
|
140
|
+
### Self-contained traced flowchart (recommended)
|
|
141
141
|
|
|
142
|
-
|
|
142
|
+
`TracedFlowchartView` handles everything — overlay computation, subflow drill-down, breadcrumbs. Just pass `spec` + `snapshots`:
|
|
143
|
+
|
|
144
|
+
```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
|
+
}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Without `snapshots`, it renders a plain static flowchart. With `snapshots`, it shows the execution trace path.
|
|
164
|
+
|
|
165
|
+
### With execution overlay (manual control)
|
|
166
|
+
|
|
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.
|
|
143
168
|
|
|
144
169
|
```tsx
|
|
145
170
|
import { specToReactFlow, type ExecutionOverlay } from "footprint-explainable-ui/flowchart";
|
|
@@ -348,7 +373,8 @@ interface ThemeTokens {
|
|
|
348
373
|
|
|
349
374
|
| Export | Description |
|
|
350
375
|
|---|---|
|
|
351
|
-
| `
|
|
376
|
+
| `TracedFlowchartView` | Self-contained flowchart with trace overlay, subflow drill-down, breadcrumbs |
|
|
377
|
+
| `FlowchartView` | Lower-level ReactFlow wrapper with execution state coloring |
|
|
352
378
|
| `StageNode` | Custom node with state-aware coloring, step badges, pulse rings |
|
|
353
379
|
| `specToReactFlow` | Convert pipeline spec → ReactFlow nodes/edges with path overlay |
|
|
354
380
|
| `TimeTravelDebugger` | Full debugger with flowchart + all panels |
|
|
@@ -373,6 +399,19 @@ All components accept a `size` prop: `"compact"`, `"default"`, or `"detailed"`.
|
|
|
373
399
|
<MemoryInspector snapshots={snapshots} size="detailed" />
|
|
374
400
|
```
|
|
375
401
|
|
|
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
|
+
|
|
376
415
|
## Unstyled Mode
|
|
377
416
|
|
|
378
417
|
Strip all built-in styles for full CSS control. Components render semantic `data-fp` attributes:
|
|
@@ -389,6 +428,175 @@ Strip all built-in styles for full CSS control. Components render semantic `data
|
|
|
389
428
|
|
|
390
429
|
---
|
|
391
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
|
+
```
|
|
588
|
+
|
|
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
|
+
---
|
|
599
|
+
|
|
392
600
|
## License
|
|
393
601
|
|
|
394
602
|
MIT
|