Commit Graph
40 Commits
Author SHA1 Message Date
Andy Dixon eed28b12cd Mitre route corners and run routes into the buildings
Route ribbons were built from independent segments, each extended half
a width past both ends and offset into its lane separately, so every
turn drew overlapping, brighter corners. Each route is now one strip
with mitred vertices shared by neighbouring segments (sharp reversals
fall back to the outgoing normal), and its along-distance is exact.

Routes also used to stop at the street door; both ends now continue
to the nearest point of the building's footprint and just inside, so
the path visibly meets the wall of the file it leaves and reaches.
2026-07-18 15:21:11 +01:00
Andy Dixon c38a65556c Keep poster graphics and text inside the poster
The diagonal stripe graphic started left of the paper and long titles
or unbreakable words (repository and file names) overflowed the sides
and bottom. Posters are now clipped to the paper, and text is fitted:
the largest size at which it wraps within the width, line limit and
remaining height; words too wide even at the smallest size are
shortened individually with an ellipsis so the rest of the text stays.
2026-07-12 15:30:40 +01:00
Andy Dixon 8f3a3337b5 Route dependents to the selected file with pulsing magenta dashes
Files that import the selected file now get street routes too, from
the same single search (paths cost the same both ways), reversed so
they flow toward the selection. They draw as magenta dashes marching
inward with a pulse travelling along them, dashed on the minimap too.
Ribbon geometry moves into city.js and every ribbon keeps to the right
of its direction of travel, so an import and a dependent sharing a
street run in separate lanes instead of overlapping (tested).
2026-07-05 14:33:52 +01:00
Andy Dixon 630636b3af Wall the city in and paste posters on it
A 14 m concrete wall now encloses the city 24 m beyond its outermost
blocks. It is in the collision index (not the pick index), blocked on
the navigation grid so residents and routes stay inside, and walk/fly
positions are clamped within it so nobody leaves by flying over.

Posters are pasted on the city side at random spots, sizes and tilts
(up to 25 degrees), one per ~35 m, overlapping like a real hoarding.
They are painted into one atlas from civic slogans plus facts about the
loaded codebase (welcome, tallest tower, largest district, most
connected file), seeded by the repository name so each city's wall is
stable. The wall shader draws concrete courses and joints with a lit
coping, and the minimap outlines the wall. README covers routes,
residents, the wall and security alerts.
2026-07-05 13:29:21 +01:00
Andy Dixon a3f9ef2d9d Set buildings with security alerts on fire and tape them off
After a GitHub snapshot loads, the viewer's code scanning and Dependabot
alerts are fetched and grouped by file. In the city, affected buildings
smoke, and those with a critical or high alert also burn: GPU particles
whose age derives from time and seed, so emitters upload once. Hazard
tape runs diagonally across every wall, yellow DEPENDABOT ALERT rising
and red CODE SCANNING ALERT falling, crossing as an X when a file has
both; the tape tiles a striped label cut to the wall's diagonal.

The minimap marks alert buildings, the coverage panel shows the alert
count (or why none could be loaded, e.g. signed out or no permission),
and a selected file lists its alerts with severity and GitHub links.
2026-06-27 16:21:48 +01:00
Andy Dixon c605e2b61d Serve GitHub code scanning and Dependabot alerts per snapshot
GET /api/snapshots/{id}/alerts fetches open code scanning alerts
(located at the flagged file and line) and open Dependabot alerts
(located at the vulnerable manifest) for the snapshot's repository,
using the viewer's own GitHub token, in parallel and following Link
pagination only while it stays on the API host. Severities are
normalised to critical/high/medium/low. Each set reports ok, forbidden
(permission not granted or feature disabled) or unavailable; signed-out
viewers and local folders get a reason instead. Snapshots now remember
their GitHub repository URL.
2026-06-27 15:33:55 +01:00
Andy Dixon 9bc5ba483c Add wandering residents to the city
Little glowing green stick figures, in the spirit of Darwinia, stroll
the streets: each walks toward a random reachable point in plain sight
on the navigation grid, pauses, and about a third of the time heads
toward a nearby resident instead, so they gather in small crowds. They
are camera-facing sprites drawn procedurally in the fragment shader
with a walk cycle, flicker and halo, grown with altitude so crowds stay
visible from the helicopter, and fogged like everything else.

About one resident per 2,500 m² of city (40 to 600), spawned only on
the city's own streets. Because they keep the city animating, Settings
has a City residents switch (saved with the other preferences). The
simulation is seedable and tested to keep everyone on walkable ground.
2026-06-20 10:52:58 +01:00
Andy Dixon 33db29defd Draw walkable street routes from a selected file to its imports
City mode builds a 3 m occupancy grid (buildings grown by half a metre
blocked, pavements slightly dearer than roads). Selecting a file runs
one Dijkstra from its building's street doors that stops once every
imported file's doors are settled, then straightens each route to its
necessary turns. Buildings boxed in by neighbours route to the nearest
reachable street. Routes are drawn as glowing ground ribbons with
chevrons flowing toward each dependency (cyan known, amber inferred)
and on the minimap; walking from the helicopter starts at the first
route's door facing along it.

Distances are float64: float32 rounding made equal-cost paths look
like improvements and flooded the queue (a hang on real cities). Tests
cover detours, enclosed targets and a large-city time budget; real
prometheus data routes 24 imports in about 60 ms.
2026-06-14 15:52:41 +01:00
Andy Dixon 1e11e4a650 Show folder paths on blade signs at street corners
Blades named the file they were attached to. They now act as street
signs: for each corner of a folder's block, the building nearest that
corner gets a green blade on each wall facing the corner's two streets,
near the corner end, reading the folder path (shortened from the left
when long; the repository name at the root). Neighbouring blocks each
sign their own corner, so an intersection shows a different folder on
every side. Courtyard walls set back from the street are skipped, and
blades still shorten or disappear when a neighbour is too close. File
names remain on the flat wall and roof signs.
2026-06-13 09:14:13 +01:00
Andy Dixon c31b0b3d39 Add blade signs so names read while walking along walls
Flat wall signs vanish at grazing angles. Walls facing a walker now
also carry blade signs projecting perpendicular to the wall, repeated
along long walls and offset from the flat signs. Each blade is two
back-to-back faces; the sign shader discards whichever face would read
mirrored from the viewer's side. Blades shorten to the free space in
front of the wall and are skipped when a neighbouring building is
closer than 1.2 m of sign.
2026-06-07 11:51:13 +01:00
Andy Dixon 931556317d Document views, city controls and frontend tests 2026-06-06 16:09:19 +01:00
Andy Dixon 4468afec8e Compile the city's shader features only into the city program
Landscape 2D/3D shared one fragment shader with the city's fog, window
and beacon code. Building two variants from the same source with a
CITY define brought 3D frame cost back near its pre-city level in the
profiling setup (native 708 -> ~300 ms/frame under software GL, JS and
canvas 71 -> ~33 ms/frame).
2026-06-06 12:21:04 +01:00
Andy Dixon dd8976b13c Touch controls, guided city tour and shareable places
Touch: in walk and fly the left thumb drives a virtual joystick while
the right thumb looks around; pinch zooms in every view (and climbs
when flying); help text switches for coarse pointers.

Tour (T or the Tour button) flies the helicopter over the six largest
districts with captions of their file and line counts, circling each
before moving on, and stops on any input.

Copy link encodes the GitHub repository, view, city camera and selected
file in the URL fragment. Opening such a link loads the repository
(including through GitHub sign-in for private repos) and restores the
place. Fragments are validated: only github.com repositories and finite
camera values are accepted. Helpers are unit tested.
2026-05-31 21:36:54 +01:00
Andy Dixon f035ecb4a5 Night city: fog, lit windows, import trails and search beacons
City mode now renders as a night scene: exponential fog into a dark sky
tuned per camera, brighter neon edges, and procedural window grids on
every wall whose lit share grows with the file's definition count.

Selecting a file draws animated screen-space ribbons from each importer
to each imported building, arcing higher with distance: cyan and solid
for known links, amber and dashed for inferred ones. Search hits raise
light columns over their buildings so matches are visible across the
city. Both effects are additive and never occlude. District names hide
when the helicopter is close.
2026-05-31 12:10:54 +01:00
Andy Dixon 4f80ca4744 Render city text in WebGL: skyline, roof, wall and street signs
City labels were a 2D overlay that showed through buildings at street
level. labels.js rasterises each label once into a shelf-packed atlas
(partial texSubImage uploads, mipmapped for grazing angles), and a new
instanced quad pass draws them depth-tested but not depth-writing.

District names float above each district's tallest tower facing the
camera, file names sit on roofs near the focus, walls facing a walker
carry name signs repeated every 24 m, and street names are painted on
nearby kerbs. Walking up to the building in the crosshair shows its
source on the facing wall. Signs rebuild only when the focus, heading
or selection changes; an overflowing atlas is cleared and refilled with
the nearest labels first. Placement helpers are unit tested.
2026-05-30 14:56:36 +01:00
Andy Dixon 76c846fdce Walk and fly through the city
Adds walk and fly cameras alongside the helicopter view, switched with
1/2/3 or the city toolbar. Walking uses mouse-look with pointer lock
(or drag), WASD, Shift to run and circle-vs-footprint collision with
sub-stepping so long frames cannot tunnel through buildings; flying
follows the view pitch with Space/C for altitude and clears low roofs.
Double-clicking a street drops you there with an animated descent,
facing the longest open street. A crosshair names the building ahead
(E or click inspects it), the address bar shows the folder you are in,
and a minimap shows your heading and teleports on click.
2026-05-23 14:33:00 +01:00
Andy Dixon 3036763d08 Show blank source lines as blank
highlightSource returns a 'No preview available' placeholder for empty
input, so every empty line in the source viewer showed that text.
2026-05-10 18:11:36 +01:00
Andy Dixon ae8b0a6b80 Add City mode: codebase laid out as streets and buildings
city.js lays the directory tree out at human scale: top-level folders
become districts separated by 18 m avenues, nested folders become
blocks with narrower streets and raised pavements, and files become
buildings whose footprint follows their lines and whose height follows
their complexity. The squarified treemap moves into city.js and is
shared with the landscape.

The renderer gains a second instance layer with per-instance kind,
code-atlas rectangle and selection flag, a helicopter camera in metres,
screen-to-world rays, and geometry lookup per mode. City mode supports
orbit, pan, dolly, ray picking, focusing a building and district names.
Node tests cover layout invariants, heights, ray/box hits and the
spatial index.
2026-05-10 15:50:23 +01:00
Andy Dixon eae93d93dc Render 3D through a real perspective camera
Adds camera.js with perspective, lookAt and matrix helpers. The orbit
view is now a lookAt pose feeding one view-projection matrix shared by
the shaders and project(), replacing the hand-rolled projection with
its clamped depth. A node test checks the new camera reproduces the old
projection to within 0.05 px, so the landscape looks the same; this is
the base the city's walk and fly cameras need (near plane, correct w).
2026-05-03 09:41:31 +01:00
Andy Dixon 94577118a4 Remove the GitHub link from the toolbar 2026-05-02 20:58:54 +01:00
Andy Dixon e7627c8877 Texture 3D roofs on the GPU and draw the grid in WebGL
The overlay used to clip and affine-map a code texture onto every
visible roof and punch every roof out of a 2D ground grid each frame
(about 13,600 saves and 6,800 clips per frame on a 1,676-file repo).

The existing code overview canvas already matches the world layout, so
it is uploaded once as a mipmapped texture and sampled by the roof
shader. The overlay now only draws sharper text for the few roofs close
enough to magnify that texture, and the grid is a WebGL line pass. The
vertex shader emits a real clip-space w so the texture is perspective-
correct; screen positions are unchanged. JS and canvas time per 3D
frame fell from 284 ms to 27 ms in the same profile.
2026-05-02 14:44:09 +01:00
Andy Dixon e39af39eca Cache viewport size and camera trig in 3D projection
project() read canvas.clientWidth/clientHeight on every call, and it
runs for every tile corner each frame. Profiling a 1,676-file repo put
216 ms of each 3D frame in project(); with the size cached per frame
and the trigonometry per camera change it drops to about 7 ms.
2026-04-18 13:02:40 +01:00
Andy Dixon de3dbf8601 Redesign the interface
Deep blue-black theme with a violet-to-cyan accent, frosted floating
panels, self-hosted Inter, a welcome screen with a call to action, a
card-style inspector, accent tabs and toggles, animated progress, and
a restyled open dialog. In 3D the WebGL walls are now lit per side
with a darker foot so buildings read as solids, and the canvas clears
to transparent over the new background. Renamed to CodeNavigator.
2026-04-12 15:04:26 +01:00
Andy Dixon 186f99f436 Start map drags only on the canvas
The viewport's pointerdown handler captured every press inside it,
including presses on floating buttons, so the resulting click could be
retargeted to the viewport and never reach the button.
2026-04-11 13:58:36 +01:00
Andy Dixon 361ecde158 Fix every failed clone being reported as a timeout
The context was cancelled before its error was checked, so any failure
looked like a timeout and private repositories never offered sign-in.
Clone handling moves into cloneRepo, which checks for DeadlineExceeded
specifically, with tests for success, failure with token masking, and
timeout.
2026-04-05 12:44:17 +01:00
Andy Dixon e744ebab1f Time out stalled GitHub clones after ten minutes
Progress streams now stay open until a job ends, so a hung clone would
otherwise hold its job and stream forever.
2026-04-04 11:53:50 +01:00
Andy Dixon 841f27487a Keep progress streams open until the job finishes
The stream used to close after three minutes, which a job queued behind
other users could exceed. It now runs until the job completes or the
client disconnects, sending a keep-alive comment every 15 seconds so
nginx does not time out an idle stream.
2026-03-21 12:35:13 +00:00
Andy Dixon 957c6404c1 Document sessions, limits and GitHub App setup 2026-03-15 14:27:38 +00:00
Andy Dixon 09b57f7afa Offer GitHub sign-in in the frontend
The load dialog shows the GitHub account state with sign-in, sign-out
and repository-selection links. A clone that needs authorisation shows
a Sign in with GitHub or Grant repository access button, and the
repository is resumed automatically on return from GitHub.
2026-03-15 09:52:44 +00:00
Andy Dixon b216b86004 Add GitHub App sign-in for private repositories
/api/github/login redirects to GitHub with a single-use state bound to
the session; the callback exchanges the code for a user token kept in
memory and expired with it. Clones pass the token through git config
in the environment, never the URL or arguments, and ignore host git
config and credential helpers. Failed clones report authRequired
(signin or install) so the frontend can offer the right next step.

Configured with GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET, GITHUB_APP_SLUG.
2026-02-28 12:27:34 +00:00
Andy Dixon 25c8c45de4 Limit concurrent indexing to two runs
Further jobs report a queued phase until a slot frees, so a burst of
users cannot exhaust the container's CPU and memory at once.
2026-02-21 19:55:38 +00:00
Andy Dixon 1c93df022d Expire idle snapshots and abandoned jobs, cap snapshot count
A background sweep every minute drops snapshots unused for an hour,
finished jobs, and uploads never committed, deleting their workspaces.
At most 8 snapshots are held; the least recently used is evicted.
2026-02-21 09:37:43 +00:00
Andy Dixon 25e2d2f390 Keep one snapshot per session instead of one globally
Indexing used to discard every existing snapshot, so concurrent users
wiped out each other's landscapes. Only the same session's previous
snapshot and workspace are replaced now.
2026-02-15 18:15:47 +00:00
Andy Dixon 26800b9b66 Scope jobs and snapshots to a browser session
An HttpOnly, SameSite=Lax session cookie is issued when a job is
created. Job and snapshot routes return 404 to any other session, so
indexed source is only readable by the browser that loaded it.
2026-02-15 10:53:01 +00:00
Andy Dixon d8d4a77ba7 Use random job and snapshot IDs
Timestamp-derived IDs were guessable, and the ID is what grants access
to indexed source. crypto/rand.Text gives 26 base32 characters.
2026-02-07 12:18:26 +00:00
Andy Dixon 981ec88f43 Add Docker build script and README
Multi-stage Dockerfile runs the tests, builds a static binary, and ships
it on Alpine with git for GitHub clones.
2026-02-01 21:48:24 +00:00
Andy Dixon 59d7e99e0b Note invalid UTF-8 replacement difference 2026-02-01 15:33:38 +00:00
Andy Dixon 62c363b031 Prefilter indexer regexps with literal checks
Go's regexp backtracker dominated indexing. Each pattern now runs only
when a cheap necessary-condition check passes, cutting indexing of the
Go standard library source from 4.7s to 1.4s.
Adds BenchmarkIndexDirectory.
2026-01-25 15:57:26 +00:00
Andy Dixon 5816dea6b1 Add Go HTTP/SSE API server with embedded frontend
Implements the API routes (jobs, progress events, snapshots, search,
entities, source delivery with release) to net/http and serves the
static frontend from the same binary on the same origin.
2026-01-25 09:10:28 +00:00
Andy Dixon f040f28325 Add source indexer
Parallel file parsing, import linking, language/layer classification,
and .gitignore/.ignore handling, with tests.
2026-01-24 14:08:43 +00:00