New web/ SPA (React 18 + Vite 5 + TypeScript + React Flow + dagre, all pinned, no CDN). Dark, three-pane layout: summary top bar, filterable task list, center auto-laid pipeline map, and a right drawer showing a selected task's history through each node (timeline with timestamps/duration/cost + Q&A/verdicts/plan). Map nodes color by live state, loop-backs render dashed, clicking a node filters the list, selecting a task highlights its path. Polls /api/state every 4s; topology fetched once. dist/ is gitignored (built on the Mac, rsynced).
55 lines
1.6 KiB
TypeScript
55 lines
1.6 KiB
TypeScript
import { useMemo } from "react";
|
|
import ReactFlow, {
|
|
Background,
|
|
Controls,
|
|
type NodeMouseHandler,
|
|
ReactFlowProvider,
|
|
} from "reactflow";
|
|
import "reactflow/dist/style.css";
|
|
import type { StateResponse, Topology } from "../api";
|
|
import { layout } from "../layout";
|
|
import { MapNode } from "./MapNode";
|
|
|
|
const nodeTypes = { pipeline: MapNode };
|
|
|
|
interface Props {
|
|
topology: Topology;
|
|
state: StateResponse | null;
|
|
pathNodeIds: Set<string> | null;
|
|
onNodeClick: (nodeId: string) => void;
|
|
}
|
|
|
|
// The center pipeline map. Nodes are dagre-laid from the topology; live state
|
|
// colors them; clicking a node filters the task list. Multiple trees (processes
|
|
// off intake) lay out as separate lanes automatically.
|
|
export function PipelineMap({ topology, state, pathNodeIds, onNodeClick }: Props) {
|
|
const { nodes, edges } = useMemo(
|
|
() => layout(topology, state?.nodes ?? {}, pathNodeIds),
|
|
[topology, state, pathNodeIds],
|
|
);
|
|
|
|
const handleNodeClick: NodeMouseHandler = (_evt, node) => onNodeClick(node.id);
|
|
|
|
return (
|
|
<div className="map-pane" aria-label="pipeline map">
|
|
<ReactFlowProvider>
|
|
<ReactFlow
|
|
nodes={nodes}
|
|
edges={edges}
|
|
nodeTypes={nodeTypes}
|
|
fitView
|
|
nodesDraggable={false}
|
|
nodesConnectable={false}
|
|
elementsSelectable
|
|
onNodeClick={handleNodeClick}
|
|
proOptions={{ hideAttribution: true }}
|
|
minZoom={0.3}
|
|
maxZoom={1.6}
|
|
>
|
|
<Background color="#222a36" gap={22} />
|
|
<Controls showInteractive={false} />
|
|
</ReactFlow>
|
|
</ReactFlowProvider>
|
|
</div>
|
|
);
|
|
}
|