Files
codegraph/ui
Colby McHenryandClaude Opus 5 87afc50e76 feat(ui): the search palette, entry points and a trail that survives the URL (CG-45)
Search: `/` or ⌘K focuses the box; results arrive grouped by kind with their
glyph, signature and file:line, ↑/↓/Enter walk them, Esc dismisses. A group
appears where its best result did, so flattening the groups reproduces the
ranking the keyboard walks — the panel's flat item list IS that concatenation.
A flow question ("how does X reach Y", "X -> Y") is recognised and searches
both endpoints with a note, rather than offering a row that would land on the
phase-2 Flow view.

Entry points answer "where do I start" on the empty screen and in the resting
palette, all derived from the graph: routes, files that run something at module
level (the engine records a top-level statement as an edge out of the file node,
which is what makes src/bin/codegraph.ts the root of the CLI flow — ranked by
calls x the files they reach, so a registration table calling into itself does
not outrank the CLI), and the most depended-on symbols. Tests are excluded from
both derived lists.

Trail: hops record the direction they were walked (→ into a call, ← up to a
caller), clicking one truncates back to it, Clear keeps the place instead of
throwing it away, and the whole walk travels in the URL. A shared or reloaded
trail arrives as ids, so hops learn their names back through a new batch
endpoint and a session name cache — without it, walking back across a
truncation redrew earlier hops as raw hashes. "Read as flow" stays hidden until
there is a Flow view to send it to.

New endpoints: /api/entrypoints and /api/nodes. New engine reads:
getTopCallingFiles, getFileDependentCounts.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-27 00:54:18 -05:00
..

ui/ — the codegraph ui viewer

The browser reader for an indexed project: Svelte 5 + Vite, built as static files and served by the CLI over loopback. An npm workspace of the engine, so npm ci at the repo root installs its toolchain; nothing here is a runtime dependency of the engine and nothing here is published to npm on its own.

Design spec (every token, size and measurement): ../docs/design/codegraph-ui-design-spec.md.

Build

npm run build          # from the repo root: tsc -> copy-assets -> this app
npm run build:ui       # just this app, plus the dist assertion
npm run dev -w ui      # Vite dev server on 127.0.0.1:5174
npm run check -w ui    # svelte-check

npm run build emits dist/viewer/ (index.html + hashed assets). scripts/check-ui-build.mjs then asserts the tree is complete, so a broken UI build fails the release instead of shipping a CLI that serves a 404. The same check runs again in scripts/build-bundle.sh (after the bundle stage copies dist) and in scripts/pack-npm.sh (after each archive is unpacked).

Why dist/viewer and not dist/ui

src/ui/ is the engine's terminal UI (shimmer progress and its worker) and tsc compiles it to dist/ui/. Pointing Vite there deletes those modules — the CLI then dies at startup with Cannot find module '../ui/shimmer-progress' — and would also leave the static server handing out compiled engine internals. check-ui-build.mjs re-asserts the compiled engine is intact after every UI build so that mistake cannot land twice.

Layout

src/
  main.ts                 fonts + tokens, mounts App into index.html's #app
  app.css                 design tokens (light/dark), reset, shell grid
  App.svelte              top bar / trail bar / main, global keys
  lib/router.svelte.ts    hash router: #/s/<id>, #/file/<path>, #/map, #/flow
  lib/trail.svelte.ts     the walked path; mirrored into the `t` query param
  lib/kinds.ts            kind glyph letters
  components/             TopBar, TrailBar, KindGlyph
  views/                  one component per route

Fonts (Archivo Variable, IBM Plex Mono) are vendored through @fontsource* and emitted into dist/viewer/assets: a local reader must work offline and must not announce the project to a font CDN.

Routes

hash view
#/ nothing selected
#/s/<id>?hl=<line>&t=<trail> symbol view
#/file/<path>?hl=<line> file view
#/map module map — reserved, phase 2
#/flow[/<key>] flow strip — reserved, phase 2

Node ids and file paths are encoded per slash-separated segment, so #/file/src/mcp/tools.ts stays readable and still round-trips a segment containing a reserved character. Build hashes with symbolHref() / fileHref() rather than by hand.