You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat: /map page — force-directed spec map clustered by tag similarity (#5647)
## Summary
- Backend: new `GET /api/specs/map` endpoint — one row per spec with the
highest-rated implementation's preview URLs, quality score, and full tag
bag (spec + impl).
- Frontend: new `/map` page powered by `react-force-graph-2d` —
image-thumbnail nodes positioned by client-side weighted-Jaccard
similarity over tags (IDF-weighted, sparse KNN edges).
- Wired into `NavBar` as a top-level link and registered as a
lazy-loaded route.
Closes#5646.
## Approach in one paragraph
Tag similarity, IDF weighting, and KNN edge construction all happen
client-side as pure helpers in `app/src/pages/MapPage.helpers.ts` (19
unit tests). 327 specs × pairwise weighted-Jaccard runs in ~30 ms in the
browser, so there's no need to precompute positions on the server. The
wrapper's built-in d3-force physics handles the layout from a sparse
link list (K=5, min-sim 0.05 → ~1.6k edges). Best-impl thumbnails are
eager-preloaded and attached to nodes as they resolve, so the canvas
re-paints organically without restarting physics. Hover highlights
neighbors, click navigates to the spec page, and a visually-hidden
anchor list mirrors the canvas for screen readers + keyboard users.
## Test plan
- [x] `uv run ruff check api/ tests/` — clean
- [x] `uv run ruff format --check api/ tests/` — clean
- [x] `uv run pytest tests/unit/api/ tests/unit/core/` — 739 passed (5
new SpecsRouter tests for the endpoint)
- [x] `cd app && yarn lint` — no new errors in MapPage files
(pre-existing baseline drift in unrelated files unchanged)
- [x] `cd app && yarn tsc --noEmit` — clean
- [x] `cd app && yarn test` — 421 passed (19 new helper tests + 3 new
page smoke tests)
- [x] `cd app && yarn build` — succeeds; `/map` chunk is 193 KB raw / 63
KB gz, lazy-loaded only when visiting `/map`
- [ ] Manual visual QA in dev server: nav link appears, ~327 thumbnails
render, scatter/bar/line clusters form distinct neighborhoods, hover
highlights neighbors + dims the rest, click → spec page, theme toggle
switches link colors and thumbnails, mobile breakpoint usable
## Notes / known follow-ups
1. **Optional edge tag labels** (the user's "optional" feature) —
explicitly out of scope here, deferred to a follow-up issue. Idea: on
hover, render shared-tag chips at link midpoints as DOM overlays.
2. **Layout opt-out via negative margins** in `MapPage.tsx` cancels
`RootLayout`'s container padding so the canvas goes full-bleed. TODO
comment left in place; cleaner long-term answer is a router-level layout
switch analog to the existing `mastheadSticks` flag.
3. **Thumbnail size**: full-size GCS PNGs at 327 × ~40 KB ≈ 13 MB.
Acceptable on warm CDN, slightly heavy on cold. If perf review pushes
back, the follow-up is to add a smaller variant in the responsive-image
pipeline.
4. **K=5, min-sim=0.05** are gut-feel values; helpers are pure so tuning
is a one-line PR after live observation.
5. **Pre-existing eslint baseline drift**: 35
`react-hooks/set-state-in-effect` and `no-undef` errors in unrelated
files were already present before this branch. None added; one
previously-introduced one in `MapPage.tsx` was refactored away during
review.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
0 commit comments