The core screen of `codegraph ui`: who calls a symbol on the left, its
verbatim body in the middle with a port on every line that has an outgoing
edge, and what it calls on the right — each callee row placed beside the line
that makes the call, with a hairline connector between them.
The callee rail is the part that is not a list. A row wants to sit at the
centre of its first call-site line and is pushed down only when that would
collide with the row above, so the rail keeps source order; the connector
still runs to the real line, so the displacement is visible rather than
silent. Positions come from measuring the laid-out DOM, so they are
recomputed on resize, on font load and whenever a fold opens.
Honesty is carried in the drawing, not in a footnote: a filled port means the
resolver matched something on that line and a hollow one means it only
guessed; uncertain connectors are dashed and their targets fold away behind
their count; synthesized edges are dashed differently and tagged with the
mechanism that made them; references that leave the index are text with a
soft underline rather than links to nowhere, and they are counted. Long
bodies keep their head plus a window round every call site — windowed on
graph edges only, since a function calling `console.log` two hundred times
would otherwise window round every line and buy nothing. Containers over 80
lines show a members outline with per-member fan-in/fan-out instead of 700
lines of braces.
Two small additions to the read-only API this needed:
* `/api/node` gives every outline member its own fanIn/fanOut (two batched
queries for the whole outline). A class's own fan-out is nearly always
zero because its methods do the calling, so without these the outline
cannot say which member carries weight.
* `/api/stats` gains `blastScale` — the denominator the blast bar is drawn
against, so one symbol's radius reads as wide or narrow *for this repo*.
It is measured across the index's 24 most-depended-on symbols (found with
a new `getTopDependedOn`, distinct dependents rather than edges), memoised
against the index stamp, and reported as sampled; a symbol wider than the
sample becomes the scale instead of overflowing the track.
Verified against a real index in a real browser: parity with the prototype on
`CodeGraph.sync` (259 lines, 27 callee rows, no overlaps), `GraphTraverser`
(20-member outline), a 773-line function (26 windows, 78 connectors), light
and dark, hover linking in both directions, keyboard-only navigation, and
reflow on resize and on fold toggles.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>