Component 2 of 3 · the picture

The graph, drawn.

A single-page Svelte app: force-directed graph, scope trees, filters, quality report, commit diff. It holds no analysis logic of its own — it asks the engine six questions over HTTP and draws the answers. Which is what makes it portable: the same build runs in a browser tab, inside the VS Code webview, and on a static host with no engine at all.

The one thing to understand

It needs an API, not a process.

This is the distinction that decides where the visualizer can go. The UI never opens a JSON file and never shells out — it calls the endpoints below. A running binary answers them from a live analysis; a directory of files answers them just as well, because not one of them takes a query parameter.

Live

nao watch . / nao serve

Data
Your source, re-analyzed on save
Can do
Everything: scope changes re-analyze, diff reads git worktrees, the context panel opens files off disk.
Where
Your machine.

Frozen

Any static file host

Data
API responses baked ahead of time
Can do
Read-only: browse, filter, rank, inspect. Anything that would re-run the analyzer answers 403.
Where
This site — /visualizer is exactly this.
the entire read surface
GET /api/repos                    # which repos exist
GET /api/repos/{slug}             # metadata
GET /api/repos/{slug}/graph       # entities + relationships
GET /api/repos/{slug}/index       # names, for search
GET /api/repos/{slug}/details     # metrics, smells
GET /api/repos/{slug}/commits     # history, for the diff picker

Six routes, all keyed by a slug. That finiteness is the whole trick — bake each response once and the app runs with nothing behind it, which is exactly what the page you are on does.

Not a demo build

The actual tool, running here.

Everything on /demo is a small viewer written for this site. This is the opposite: the real UI out of the nao repository, frozen mode, against public repositories it analyzed once. There is no second copy to keep in sync — rebuild nao and this moves with it.

nao UI · … repositories · frozen open full screen ↗

It opens where the real tool opens: on the scope picker. Tick a folder — or take the suggestion it makes — and the graph renders. Scopes past ~2,000 entities are marked and held back rather than dropped on you, which is why the big repositories below want a narrower pick.

What you're driving

Six panels, one graph.

In the editor these sit in the activity bar and follow your cursor. Here they run in a browser tab against a frozen analysis. The code is identical — one Svelte app, three hosts.

PanelDoes
Visual ScopesTick the folders that get drawn.
Analysis ScopeWhat gets analyzed — separate from what gets drawn.
View OptionsEntity / class / module aggregation, depth, density, labels.
FiltersBy language and entity kind, globally or around the selection.
Selection & ContextMetrics for the selected entity and its neighbourhood.
QualityEvery entity ranked, with smell badges, sortable by any metric.

What frozen mode costs

Read-only, on purpose.

  • No repo submissions. nao serve can clone and analyze a GitHub repo on request; that is unbounded work on someone else's machine, so the site's proxy refuses it. The hosted set is fixed.
  • No live reload. The event stream exists to report analysis progress. With submissions off it has nothing to say, so it is switched off too.
  • No diff overlay. Comparing two commits means git worktrees on the server. That one runs locally, in the extension.
  • No source panel. Reading the file behind an entity means reading it off disk. Locally that is your disk; here there is none.

Every one of those is present the moment you run the binary yourself — which is the point of the tool being local. Nothing is a paid tier; it is just the difference between a process and a CDN.