Interactive graph visualization for MTHDS method pipelines. Renders execution graphs from GraphSpec data — the canonical format produced by Pipelex when tracing pipeline execution.
Core graph logic (builders, layout, analysis) is pure TypeScript with no React dependency. The React layer provides a drop-in GraphViewer component powered by ReactFlow.
npm install @pipelex/mthds-uiReleased versions are listed on the npm page and the GitHub releases page.
| Dependency | Required | Used by |
|---|---|---|
react, react-dom |
no | React layer (graph/react) |
shiki |
no | Syntax highlighting (shiki) |
| Dependency | License | Used by |
|---|---|---|
elkjs |
EPL-2.0 | Graph layout engine |
@xyflow/react |
MIT | Graph rendering (graph/react) |
elkjs (Eclipse Layout Kernel) is licensed under the Eclipse Public License 2.0. See NOTICE for details.
import { GraphViewer } from "@pipelex/mthds-ui/graph/react";
function MethodGraph({ graphspec }) {
return <GraphViewer graphspec={graphspec} />;
}That's it. GraphViewer handles layout, styling, CSS variables, and all ReactFlow internals. All props except graphspec are optional with sensible defaults.
ReactFlow accesses browser globals at module-evaluation time, so it cannot be server-side rendered. Use next/dynamic with ssr: false:
"use client";
import dynamic from "next/dynamic";
import type { GraphViewerProps } from "@pipelex/mthds-ui/graph/react";
import React from "react";
const GraphViewer = dynamic(
() =>
import("@pipelex/mthds-ui/graph/react").then((mod) => ({
default: mod.GraphViewer,
})),
{ ssr: false },
) as React.ComponentType<GraphViewerProps>;
export function MyGraph({ graphspec }) {
return <GraphViewer graphspec={graphspec} />;
}| Prop | Type | Default | Description |
|---|---|---|---|
graphspec |
GraphSpec | null |
— | Graph data (nodes + edges) |
config |
GraphConfig |
DEFAULT_GRAPH_CONFIG |
Layout and visual configuration |
direction |
GraphDirection |
"LR" |
Layout direction: TB, LR, RL, BT |
showControllers |
boolean |
false |
Show controller group outlines |
onNavigateToPipe |
(pipeCode: string, status?: PipeStatus) => void |
— | Callback when a pipe node is clicked |
onReactFlowInit |
(instance: AppRFInstance) => void |
— | Access the underlying ReactFlow instance |
The graph container uses position: absolute; inset: 0 to fill its parent. Make sure the parent element has position: relative and a defined height (e.g. h-full, flex-1, or an explicit height).
GraphSpec is the data format that describes a pipeline execution graph. It's generated by Pipelex when running or validating MTHDS method bundles.
# From a .mthds bundle file
pipelex-agent validate bundle my-method.mthds --view
# The output JSON contains a graphspec fieldOr programmatically via the Pipelex Python SDK:
from pipelex import Pipelex
px = Pipelex()
result = px.validate_bundle("my-method.mthds", view=True)
graphspec = result.graphspec # dict ready for JSON serializationFor authored-structure previews that should not run Pipelex, use the pure TypeScript static builder:
import { buildStaticGraphSpecFromToml } from "@pipelex/mthds-ui/static-graph";
const { spec, diagnostics } = buildStaticGraphSpecFromToml(tomlText);The returned spec has meta: { format: "mthds", mode: "static" } and can be
passed directly to GraphViewer. Static graphs hide runtime status/timing
chrome and are best-effort: diagnostics report incomplete or unresolved authored
content without preventing rendering.
See docs/static-graph.md for the full contract.
interface GraphSpec {
nodes: GraphSpecNode[];
edges: GraphSpecEdge[];
meta: {
format: "mthds";
mode?: "dry" | "live" | "static";
};
}
interface GraphSpecNode {
id: string;
pipe_code?: string; // e.g. "analyze_match"
pipe_type?: string; // e.g. "PipeLLM", "PipeSequence", "PipeExtract"
status?: string; // "succeeded", "failed", "running", "scheduled", "skipped"
io?: {
inputs?: IOItem[];
outputs?: IOItem[];
};
}
interface GraphSpecEdge {
id?: string;
source: string; // Source node ID
target: string; // Target node ID
kind: GraphSpecEdgeKind; // "data", "contains", "batch_item", etc.
label?: string;
}Nodes represent pipes (operations) and stuffs (data artifacts) in the pipeline:
- Pipe nodes — operations like
PipeLLM,PipeSequence,PipeExtract,PipeSearch - Stuff nodes — data flowing between pipes, typed by concepts (e.g.
CandidateProfile,Document)
| Kind | Description |
|---|---|
data |
Data flow — stuff produced by one pipe, consumed by another |
contains |
Containment — a controller pipe wraps child pipes |
batch_item |
Batch processing — items fanned out from a collection |
batch_aggregate |
Batch aggregation — items collected back |
parallel_combine |
Parallel results combined |
{
"nodes": [
{
"id": "extract_text",
"pipe_code": "extract_text",
"pipe_type": "PipeExtract",
"io": {
"inputs": [{ "digest": "input_doc", "name": "document", "concept": "Document" }],
"outputs": [{ "digest": "pages", "name": "pages", "concept": "TextPages" }]
}
},
{
"id": "summarize",
"pipe_code": "summarize",
"pipe_type": "PipeLLM",
"io": {
"inputs": [{ "digest": "pages", "name": "pages", "concept": "TextPages" }],
"outputs": [{ "digest": "summary", "name": "summary", "concept": "Summary" }]
}
}
],
"edges": []
}Full specification: docs.pipelex.com — Execution Graph Tracing
Controls layout and visual behavior. All fields are optional — DEFAULT_GRAPH_CONFIG provides sensible defaults.
import { DEFAULT_GRAPH_CONFIG } from "@pipelex/mthds-ui";
// Override specific settings
const myConfig = {
...DEFAULT_GRAPH_CONFIG,
direction: "LR",
nodesep: 80,
ranksep: 50,
};| Field | Type | Default | Description |
|---|---|---|---|
direction |
GraphDirection |
"LR" |
Layout direction |
showControllers |
boolean |
false |
Show controller group boxes |
nodesep |
number |
50 |
Horizontal spacing between nodes |
ranksep |
number |
100 |
Vertical spacing between ranks |
edgeType |
EdgeType |
"bezier" |
Edge curve style |
initialZoom |
number | null |
null |
Override fit-view zoom (null = auto) |
panToTop |
boolean |
true |
Pan viewport to top after layout |
paletteColors |
Record<string, string> |
(see below) | CSS variable overrides for theming |
GraphViewer applies CSS custom properties from paletteColors on mount. Override colors by passing a custom config:
<GraphViewer
graphspec={graphspec}
config={{
...DEFAULT_GRAPH_CONFIG,
paletteColors: {
...DEFAULT_GRAPH_CONFIG.paletteColors,
"--color-pipe": "#e06c75",
"--color-stuff": "#61afef",
"--color-bg": "#282c34",
},
}}
/>Default palette colors include:
| Variable | Purpose |
|---|---|
--color-pipe |
Pipe node border/accent |
--color-pipe-bg |
Pipe node background |
--color-stuff |
Stuff node border/accent |
--color-stuff-bg |
Stuff node background |
--color-edge |
Edge line color |
--color-batch-item |
Batch item edge color |
--color-batch-aggregate |
Batch aggregate edge color |
--color-bg |
Graph background |
--color-bg-dots |
Background dot pattern |
--font-sans |
Node font family |
--font-mono |
Code/controller font |
See graphConfig.ts for the full default palette.
| Import path | Content |
|---|---|
@pipelex/mthds-ui |
Pure-TS graph logic — types, builders, layout, controllers, config |
@pipelex/mthds-ui/graph/react |
React components — GraphViewer, label helpers, type converters |
@pipelex/mthds-ui/shiki |
MTHDS syntax highlighting with shiki |
Use the graph logic without React — build nodes/edges, run layout, and feed the result to your own renderer:
import {
buildGraph,
getLayoutedElements,
applyControllers,
DEFAULT_GRAPH_CONFIG,
} from "@pipelex/mthds-ui";
// Build graph data from a GraphSpec
const { graphData, analysis } = buildGraph(graphspec, "bezier");
// Apply ELK layout
const { nodes, edges } = getLayoutedElements(graphData.nodes, graphData.edges, "TB");
// Optionally wrap nodes in controller groups
const final = applyControllers(nodes, edges, graphspec, analysis, true);Syntax-highlight MTHDS code with the bundled grammar and themes:
import { highlightMthds, getAvailableThemes } from "@pipelex/mthds-ui/shiki";
const html = await highlightMthds(mthdsSource, "pipelex-dark");npm install
make check # lint + format-check + typecheck
make test # unit tests (vitest)
make build # build to dist/MIT — see LICENSE.
This project depends on elkjs which is licensed under EPL-2.0. See NOTICE for third-party license details.