pickDefaultRoot ignored files loose in the repository root, so git's
top-level .c files made builtin/ the program's root. And a source folder
whose files all sit in one folder (Express lib/, R's R/, Erlang src/)
drew one box at any depth; pickDefaultView opens the whole repository
then, when it draws more.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
A Maven / Gradle project keeps every line of Java under
src/main/java/org/<company>/<app>/, and those folders hold nothing but the
next one. The Map cut one folder per level, so petclinic drew the whole
program as one `src/main/java/org` box, and the deepest grouping option
(4) stopped at `src/main/java/org/springframework` — no setting reached the
packages seven levels down.
A folder with exactly one subfolder and no file of its own (read from
every indexed file, tests included, so ids don't move when tests are
toggled) now joins the level below it. moduleIdFor takes the set as an
optional fourth argument — without it nothing changes — and
pickDefaultDepth counts the same levels. Collapsing never splits a group
at the same depth (a pass-through folder has one child), so repos without
such chains keep their grouping; a lone chain like koel's
`app/Console` → `app/Console/Commands` is renamed to the folder that holds
the files.
The box label (the wire `label`, which nothing read) now elides a chain of
three or more folders — `src/main/java/…/petclinic/owner` — and ModuleNode
and the width calculation use it; the tooltip and side panel keep the full
path. Everywhere else the label equals the id, so other maps look the same.
petclinic's default map: 8 boxes (owner, vet, model, system, the package's
root files, resources) instead of one; realworld opens on io/spring's api,
application, core, graphql, infrastructure.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Adds a new grouping system for the Map with a new grouping depth control, exposes per-module dependents (files and modules) to drive a weight bar, and renders it on each module. Introduces a MapKey to explain visuals, collapses lone root-file buckets for clearer labeling, and supports a nullable depth value to let the provider pick grouping. The Symbol tab now has its own address (#/s) when nothing is selected, and routing/top-bar logic is updated accordingly. Also updates export SVG rendering to include weight-based bars, and extends tests and docs to cover the new visuals and behavior.
`GET /api/map` rolls the whole edge table up to module granularity in one
`GROUP BY`, and the Map tab draws it: one box per directory, dependencies
pointing down, nothing placed by hand.
Two decisions carry the screen.
The vertical order rests on each link's `declared` weight — the edges resolved
through an import, a qualified name, an inheritance clause or a typed receiver —
not on its raw count. Bare name matching resolves `run`, `push` and `finish`
across unrelated directories, and layering on raw counts put `src/db` directly
under `src/bin` on this repository's own index. On declared edges the same data
reproduces the pipeline CLAUDE.md describes, with a third of the mutual pairs.
When too few links carry a declared edge to describe a project, the layout falls
back to raw counts and the side panel says so.
And the aggregation is a single scan. Grouping by the symbol names as well as
the modules costs nothing extra — the join is what is expensive — so one query
yields both the link weights and the tooltip's symbol pairs. Measured against
this index inflated to 800k edges: 1.28s for one scan against 1.89s for two,
which is the difference between meeting and missing the cold budget on a
ten-thousand-file repository. Cached answers come back in ~3ms.
Nothing is dropped silently: thin links are hidden until a module they touch is
selected and counted in the panel, uncertain references are excluded from every
number on screen and the total is printed, and mutual dependencies, module loops
and file-level circular imports are listed rather than straightened away. An
edge that still points up after layering is drawn dashed on selection instead of
being reversed or removed.
The layout — cycle-breaking, longest-path layering, barycenter ordering, ports —
is a pure function of the payload in `ui/src/lib/map-model.ts`, so the tests
toggle and the selection cost no round-trip and the same project always draws
the same picture. Svelte Flow supplies pan, zoom and fit; never a layout.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>