Graph Visualization

The graph visualization renders dependency graphs as interactive SVG diagrams. It uses the Dagre library for automatic layout of directed acyclic graphs (DAGs), positioning nodes to minimize edge crossings and optimize readability. Nodes display effective states with color-coding (green for healthy, yellow for stale, red for failed), and arrows illustrate state propagation from dependencies to dependents.

This visualization integrates with the core model by incorporating computed effective states from dependencies. It supports full namespace graphs, filtered views, and focused subgraphs (e.g., upstream or downstream from a specific node).

Graph Data Structure

Graph data follows a standardized JSON format, fetched via API endpoints:

interface GraphNode {
  id: string;
  state: string;
  effective_state: string;
  label: string | null;
  reason: string | null;
}

interface GraphEdge {
  from: string;  // Dependent node
  to: string;    // Dependency node
}

interface GraphData {
  namespace: string;
  nodes: GraphNode[];
  edges: GraphEdge[];
}

State colors use fixed palettes:

const STATE_COLORS: Record<
  string,
  { fill: string; stroke: string; text: string }
> = {
  green: { fill: "#64d177", stroke: "#4caf50", text: "#1b5e20" },
  yellow: { fill: "#ff9800", stroke: "#f57c00", text: "#4e2c00" },
  red: { fill: "#f44336", stroke: "#d32f2f", text: "#fff" },
};

Rendering Process

Rendering occurs in src/graph/svg.ts via the renderSvg function, which takes GraphData and outputs a self-contained string:

  1. Node Measurement: Approximates dimensions using monospace-like character widths (CHAR_WIDTH = 7.5 for 13px sans-serif font). Lines include:

Minimum width is 80px; padding is 16x10px; line height is 16px.

function measureNode(node: GraphNode): {
  width: number;
  height: number;
  lines: string[];
} {
  const lines: string[] = [node.id];
  if (node.label) lines.push(node.label);
  if (node.state !== node.effective_state)
    lines.push(`effective: ${node.effective_state}`);
  // Compute max width/height...
}
  1. Dagre Layout:
  1. SVG Generation:
export function renderSvg(graph: GraphData): string {
  // ... Dagre setup and layout ...
  return [
    `<svg xmlns="http://www.w3.org/2000/svg" width="${svgWidth}" height="${svgHeight}" viewBox="0 0 ${svgWidth} ${svgHeight}">`,
    `<style>svg { background: #fff; }</style>`,
    arrowMarker(),
    ...edgeSvgs,
    ...nodeSvgs,
    `</svg>`,
  ].join("\n");
}

The result is a scalable vector graphic suitable for embedding in web pages or exporting.

API Endpoints

Graph data powers the visualization via endpoints in src/routes/graph.ts:

Subgraphs use BFS traversal via getUpstreamNodes/getDownstreamNodes (in src/graph/effective.ts) to include relevant nodes/edges.

Effective states are computed on-the-fly during graph building (via Graph Computation's computeEffectiveState, propagating "worst" state: red > yellow > green).

CLI Integration

The dep graph {namespace} command (in src/cli/commands/graph.ts) fetches GraphData and renders a text-based tree:

This complements SVG for terminal use but is not part of the core visualization.

Design Decisions

This system enables quick visual assessment of dependency health across complex graphs.

Recent changes