Service map A topology where the edges carry the interesting state. Node health is easy and mostly known; it is the dependency that is timing out which nobody can find in a list. Tokens this needs: --foreground, --card, --muted-foreground, --border, --status-warn, --status-critical The status, chart, scale, state and direction names are an extension, not a rename. shadcn has --destructive and five --chart-* and nothing else in this territory. ──────────────────────────────────────────────────────────────────────── // ServiceMap.tsx ──────────────────────────────────────────────────────────────────────── import type { Status } from "../semantic-status-color/status"; /** * Observed, not drawn. A node is a service that produced traces, an edge is * calls between two of them, and both carry the two dates that give the map * a definition of "new" and of "gone". Positions are stored with the node, * because a map that reshuffles on refresh cannot be learned. */ export type Service = { id: string; x: number; y: number; /** One derived state per node. Five metrics on a node is a hairball. */ status: Status; lastSeen: Date; }; export type Call = { from: string; to: string; /** Per minute, over the window. Width follows it. */ calls: number; errorRate: number; firstSeen: Date; }; const DAY = 86_400_000; /** An edge this young is drawn dashed: the dependency nobody had drawn. */ const NEW_FOR = 7 * DAY; /** A node silent this long is gone, so the map does not accumulate history. */ const GONE_AFTER = 30 * DAY; const UNHEALTHY = 0.05; const NODE_H = 30; const STROKE: Record = { nominal: "stroke-border", warn: "stroke-status-warn", critical: "stroke-status-critical", unknown: "stroke-muted-foreground", }; const TEXT: Record = { nominal: "fill-foreground", warn: "fill-status-warn", critical: "fill-status-critical", unknown: "fill-muted-foreground", }; /** A glyph beside the label, so the state survives greyscale. */ const GLYPH: Record = { nominal: "", warn: "▲ ", critical: "! ", unknown: "? " }; const width = (s: Service) => Math.max(56, s.id.length * 7 + 16); /** Where a line from a to b leaves b's box, so the arrowhead sits on the edge. */ const clip = (a: Service, b: Service) => { const dx = b.x - a.x, dy = b.y - a.y; const t = Math.min((width(b) / 2) / Math.abs(dx || 1e-9), (NODE_H / 2) / Math.abs(dy || 1e-9)); return { x: b.x - dx * t, y: b.y - dy * t }; }; export function ServiceMap({ services, calls, now, onSelect, width: w = 400, height = 250 }: { services: Service[]; calls: Call[]; /** The clock the window is measured from. */ now: Date; onSelect: (id: string) => void; width?: number; height?: number; }) { const live = services.filter((s) => now.getTime() - s.lastSeen.getTime() < GONE_AFTER); const byId = Object.fromEntries(live.map((s) => [s.id, s])); const edges = calls.filter((c) => byId[c.from] && byId[c.to]); const peak = Math.max(...edges.map((c) => c.calls)); return (
{edges.map((c) => { const a = byId[c.from], b = byId[c.to]; const end = clip(a, b); const sick = c.errorRate >= UNHEALTHY; const fresh = now.getTime() - c.firstSeen.getTime() < NEW_FOR; return ( {`${c.from} → ${c.to}: ${c.calls}/min, ${(c.errorRate * 100).toFixed(1)}% errors`} ); })} {live.map((s) => ( onSelect(s.id)} onKeyDown={(e) => e.key === "Enter" && onSelect(s.id)} className="cursor-pointer"> {GLYPH[s.status]}{s.id} ))}
); } ──────────────────────────────────────────────────────────────────────── // demo.tsx ──────────────────────────────────────────────────────────────────────── import { useState } from "react"; import { ServiceMap, type Call, type Service } from "./ServiceMap"; /** * Six services as traces saw them in the last few minutes. checkout to * payments is failing one call in six, so that edge is the sick one while * both nodes' own pages look fine; payments to postgres showed up three days * ago and nobody had drawn it. The clock is fixed so the ages are stable. */ const NOW = new Date("2026-09-15T09:00:00Z"); const ago = (days: number) => new Date(NOW.getTime() - days * 86_400_000); const SERVICES: Service[] = [ { id: "gateway", x: 48, y: 59, status: "nominal", lastSeen: NOW }, { id: "checkout", x: 197, y: 47, status: "nominal", lastSeen: NOW }, { id: "payments", x: 308, y: 47, status: "critical", lastSeen: NOW }, { id: "pricing", x: 190, y: 127, status: "nominal", lastSeen: NOW }, { id: "tax", x: 278, y: 127, status: "nominal", lastSeen: NOW }, { id: "postgres", x: 196, y: 205, status: "nominal", lastSeen: NOW }, // Retired in July. Still in the data, aged off the map. { id: "legacy-cart", x: 80, y: 205, status: "unknown", lastSeen: ago(62) }, ]; const CALLS: Call[] = [ { from: "gateway", to: "checkout", calls: 1180, errorRate: 0.002, firstSeen: ago(400) }, { from: "gateway", to: "pricing", calls: 640, errorRate: 0.001, firstSeen: ago(400) }, { from: "checkout", to: "payments", calls: 310, errorRate: 0.17, firstSeen: ago(400) }, { from: "checkout", to: "tax", calls: 305, errorRate: 0.0, firstSeen: ago(300) }, { from: "pricing", to: "postgres", calls: 900, errorRate: 0.0, firstSeen: ago(400) }, { from: "payments", to: "postgres", calls: 40, errorRate: 0.0, firstSeen: ago(3) }, ]; export default function Demo() { const [picked, setPicked] = useState(null); return (
{picked &&

open traces, logs and dashboards for {picked}

}
); }