Write full documentation with screenshots

README covers what CodeNavigator is, screenshots of each view, quick
start, configuration, keyboard shortcuts and links to docs/: a city
guide (what every visual means, getting around), architecture (indexer,
sessions, GitHub, rendering passes, routing), the HTTP API, GitHub App
setup including the alert permissions, deployment (Docker, systemd,
nginx, limits, security notes) and development (tests, layout,
conventions, design notes).
This commit is contained in:
Andy Dixon
2026-08-09 10:15:59 +01:00
parent 3b4f1505d1
commit d7318acede
13 changed files with 635 additions and 62 deletions
+92
View File
@@ -0,0 +1,92 @@
# Architecture
```
browser ──HTTP/SSE──▶ Go server (main.go, github.go) ──▶ indexer (indexer.go) ──▶ temp workspace
│ │
│ └── GitHub (OAuth token exchange, alerts API, git clone)
└── WebGL2 frontend (web/): app.js · renderer.js · city.js · camera.js · labels.js
```
## Server
One Go binary using only the standard library. The frontend is embedded with `go:embed` and served
from the same origin as the API.
### Indexing (`indexer.go`)
1. **Walk** the checkout in lexical order. Skip `.git`, `node_modules`, `target`, `dist`, `build`
and `.next`, honour `.ignore` files everywhere and `.gitignore` inside git checkouts. Only regular
files count (no symlinks).
2. **Parse** files in parallel (one worker per CPU). Binary files (a NUL byte in the first 4 KB) and
files over 8 MB are skipped. For each file it records lines, bytes, a complexity score (branching
tokens), the first 60 lines as a preview, definitions (class/struct/function patterns) and import
targets (from/import/require/#include/use/using patterns). Each regex has a cheap literal
prefilter, which made indexing about 3× faster than regex alone.
3. **Link** imports to files: relative paths first (`known`), then module-style names resolved by
path suffix or file stem (`inferred`). Ambiguous names are never guessed.
4. Classify each file's **language** (by extension) and **layer** (test, vendor, generated, platform,
library, docs or application) by path.
### Sessions, jobs and snapshots (`main.go`)
- **Sessions:** a random cookie identifies each browser, and jobs, snapshots and GitHub tokens are
owned by it.
- **Jobs:** a job is a GitHub clone or a local upload, reporting progress over SSE.
- **Snapshots:** each session keeps only its latest snapshot. Snapshots idle for an hour are expired,
at most 8 are held (least recently used evicted), and at most 2 indexes run at once.
- **Delivery:** once the scene is sent, previews are released, and each source file is deleted after
every line of it has been fetched.
- **Workspace cleanup:** only paths inside the workspace directory are ever deleted.
### GitHub (`github.go`)
- **Sign-in:** the GitHub App user-authorisation flow. A single-use `state` is bound to the
session, and the resulting user token is held in memory only (8-hour expiry).
- **Clones** pass the token as an HTTP header through `GIT_CONFIG_*` environment variables, never in
arguments or URLs, with host git config and credential helpers disabled.
- **Security alerts** are fetched per request with the viewer's token, following pagination only on
the API host.
## Frontend (`web/`)
Plain ES modules, no framework, no build step.
| Module | Responsibility |
|---|---|
| `app.js` | UI, input, data loading, 2D overlay canvas, city orchestration (signs, routes, residents, tour, links, alerts) |
| `renderer.js` | All WebGL2 drawing: instanced boxes, sign quads, route ribbons, particles, sprites |
| `camera.js` | Matrix maths: perspective, lookAt, inverse, orbit pose |
| `city.js` | Pure city logic, unit tested: layout, collision, navigation grid and routing, sign placement, tape, walls, posters, residents, place links |
| `labels.js` | Text atlas for signs (shelf-packed canvas) and the poster atlas |
| `api.mjs` | `fetch` wrapper and SSE-over-fetch parser |
### Views
- **2D:** squarified treemap in a 1000×680 world, orthographic. Source text is painted into an
overview canvas when zoomed out, and into per-tile textures (LRU, 192 MB budget) when zoomed in.
- **3D:** the same instances extruded, rendered through a perspective orbit camera. Roofs sample the
overview canvas as a GPU texture, and only nearby roofs get sharp per-file textures.
- **City:** `layoutCity` lays the directory tree out in metres. Folders shrink by half a road on
each side (18 m avenues between districts, narrower streets deeper down). Files become buildings,
with footprint ≈ 1.1 m² per line and height 6 m + 10 m × log₂(1 + complexity).
### City rendering passes (in order)
1. **Floor:** ground and folder pavements in nesting order, depth test off, top faces only (so
nested pavements a few centimetres apart can't z-fight).
2. **Buildings and wall:** instanced boxes. The city shader adds procedural windows (lit share from
definition count), neon edges, a concrete wall and exponential fog.
3. **Effects:** additive and never occluding. Search beacons, fire and smoke particles (stateless,
animated in the vertex shader), residents (procedural stick-figure sprites), street routes
(mitred ribbons with chevrons or pulsing dashes, kept in right-hand lanes) and import arcs.
4. **Signs:** textured quads from the label atlas, depth-tested but not depth-writing. A shader test
discards any quad face that would read mirrored, which is how two-faced blade signs work.
### Routing
- **Grid:** a 3 m occupancy grid blocks buildings (grown by 0.5 m) and the city wall, and makes
pavements slightly dearer than roads.
- **Search:** one Dijkstra run from the selected building's doors reaches every import and
dependent (paths cost the same both ways). It uses float64 distances and a settled set.
- **Shaping:** routes are straightened by line-of-sight, extended into both buildings, and boxed-in
targets fall back to the nearest reachable street.