import type { ModelGraph } from "../loader/model-graph.js"; import type { Conformance, ConformanceVerdict } from "../query/conformance.js"; /** * Whole-model interactive graph, colored by IMPLEMENTATION CONFORMANCE. Unlike * `flow-mermaid` (one Flow, static), this renders the behavioral backbone — * Features, Flows, Loops, Junctions and the edges between them — and paints * every loop/junction by its `conformance` verdict (met/partial/gap), so the * red nodes ARE the reason the page exists: a modeled obligation the code * can't be shown to satisfy, visible at a glance. * * This is a PROJECTION, not a source of truth: like the mermaid view it is * regenerated into the gitignored `.loopgraph/ws/` side-channel, never * committed (the committed artifact is the YAML model — a descriptive * code-graph commits its graph because the graph IS its product; loopgraph's * product is the model, and this HTML is just a view of it). * * Design red lines held: the output is a SINGLE self-contained HTML string — * inline CSS + inline JS + inline JSON, ZERO external hosts (no CDN, no fonts, * no remote assets) — so it opens over `file://`, survives an Artifact CSP, and * needs no network. Layout is DETERMINISTIC (index-seeded, no RNG): the graph * structure and node positions regenerate identically for the same model, so * `renderGraphHtml` is a pure function of (model, meta). The only per-run * variation in the written file is the staleness timestamp in the header — * exactly as the mermaid `view` output already stamps, and by design (it * records WHEN the projection was generated, not model content). */ /** Visual class of a node: loop/junction verdicts, plus non-graded kinds. */ export type NodeClass = ConformanceVerdict | "dormant" | "structural"; export interface GraphNode { id: string; kind: "feature" | "flow" | "loop" | "junction"; title: string; cls: NodeClass; /** Short gap labels for the tooltip (loops/junctions only). */ gaps: string[]; } export interface GraphEdge { source: string; target: string; /** solid = primary sequence/containment; dashed = guard/cross; dotted = junction span; thin = parent. */ style: "solid" | "dashed" | "dotted" | "thin"; label?: string; } export interface GraphModel { nodes: GraphNode[]; edges: GraphEdge[]; summary: { met: number; partial: number; gap: number; dormant: number; structural: number; }; } /** * Extract the render model from the loaded graph + a conformance result. Nodes * are Features, Flows, Loops (dormant ones included, styled apart) and * Junctions; Scenarios are deliberately NOT nodes — they are leaf evidence * reflected in each loop/junction's verdict color, and rendering one per GWT * would bury the backbone. Edges are emitted only when BOTH endpoints are in * the node set, so a dangling reference (T0's job to flag) never draws a line * to nowhere. */ export declare function computeGraphModel(graph: ModelGraph, conformance: Conformance): GraphModel; export interface GraphHtmlMeta { /** Repo-relative model dir or a display title for the page header. */ title: string; /** Staleness banner text (already formatted by the caller), shown verbatim. */ stalenessBanner: string; /** Whether conformance was resolved against a repo (drives an honesty note). */ repoResolved: boolean; } /** * Render the whole self-contained HTML page. The server side embeds nodes (with * deterministic positions), edges and the summary as JSON; ALL SVG is built by * the inline script from that data — so free-text titles are escaped by the DOM * (createTextNode), never hand-concatenated into markup, and the server does no * SVG string escaping at all. */ export declare function renderGraphHtml(model: GraphModel, meta: GraphHtmlMeta): string; //# sourceMappingURL=graph-html.d.ts.map