Feature #187: landing page with animated GIFs #188

Merged
crueber merged 2 commits from feat/issue-187 into main 2026-09-08 03:17:21 +00:00
Owner

Implements Forgejo issue #187 per the plan + R1 (R1 normative on conflict) + review findings.

What changed

Backend (internal/server, doc 06 §§3.1/3.3 + Decisions):

  • Explicit GET /explore (explorePage) next to GET / (spaHome, now shell-only). Chi static-beats-wildcard ordering; without it /explore falls into the /* wildcard's single-segment owner shape.
  • ?format=text (or text/plain Accept w/o text/html) MOVED / → /explore (S1; pre-1.0 no-alias — one canonical home, no silent alias, no 308). / answers the shell unconditionally.
  • explore joins the reserved single-segment UI names (owner literally named explore loses only its /:owner UI page; N2 self-link loop named in the note).
  • uiAsset: concepts/<name>.gif → dist/concepts/, image/gif MIME, no-cache + ETag (never immutable — B1, served prefix pinned /_ui/concepts/).
  • compress bypasses image/* (B2; notes the pre-existing doc/code drift — 12 §3 implied a content-type gate the code never had).

Frontend (doc 12 §§2.3/2.3.0 + Decisions):

  • / → new Landing.jsx (hero in README L3–5 language + 3 CTAs + walgit compat line, 4 alternating concept sections, #quickstart strip with the README push block + auth/SSH one-liners). Zero API calls (no useData/SDK/fetch — pinned by test).
  • /explore → Owners.jsx (logic unchanged — caps/order/stars/activity/overflow; intro slimmed to one-liner + "What is walhub? → /"). Nav owners → / becomes explore → /explore (brand stays /); * fallback renders Landing.
  • New ConceptGif.jsx: still-first (-still.gif renders immediately + <noscript>), animated swap only when prefers-reduced-motion does NOT match (change listener), loading="lazy" + decoding="async" + 640×360, plain img alt only (N1).

Generator (internal/devtools/landinggif/, stdlib only, no new Go modules / no new npm deps):

  • font.go (hand-drawn full-ASCII 5x7, typed shape literals — N5), draw.go (rects/outlines/arrows/2× text/captions), main.go (collab-first scenes, S3), main_test.go (full-ASCII + scene-text coverage — the missing-3 class fails loudly; determinism; timing floors; golden freshness; byte budgets).
  • make landing-gifs is MANUAL (B3 — not a make web prerequisite); 8 checked-in files in web/public/concepts/ (vite copies public/ → dist/ verbatim; zero embed changes).

Frame timings (storyboard acceptance)

push 200 50 50 60 180 180 200 (~8.0s) · bucket 200 60 200 60 50 50 220 (~8.4s) · fetch 200 60 150 50 200 200 (~7.1s) · collab 200 60 150 150 150 200 200 (~9.1s). Key holds ≥ 1.8s, motion steps ≥ 0.5s (floor 40, N4), ≤ 3 moving elements/frame, numbered captions on every frame. Sizes: push 16,773 · bucket 17,793 · fetch 15,874 · collab 22,912 · stills ~2–2.5 KB each · total 82,618 bytes (budgets ≤ 30/30/30/40 KB, total ≤ 150 KB — see EVIDENCE.md E12).

Verification

  • make fmt && make vet clean; go test -race ./internal/server/ ok; make cover 48 packages ≥ 95% (server 95.6%); go test ./internal/devtools/landinggif/ green (incl. golden freshness).
  • node --test web/test/unit/*.test.js: 386 pass (incl. new landing.test.js: routes/nav/CTA+alt pins, /_ui/ prefix, zero-API-call rule, owners move).
  • git diff go.mod go.sum package.json pnpm-lock.yaml empty — no new deps either ecosystem.
  • Live server: /o/r/badsub → 404 plain text (S2 — never reaches the SPA; Solid nested router keeps shell-with-empty-outlet for matched repo paths); /explore?format=text → owner list; /?format=text → HTML shell; GIF serves image/gif 200 uncompressed under Accept-Encoding: gzip while hashed JS still gzips.
  • Real Chromium via :9222 on the canonical host (walgit.localhost:8089): / dark+light (H1 renders, 4 animated GIFs 640 wide), / reduced-motion (4 stills), /explore dark (Owners + slimmed intro) — 4/4 clean, zero console errors. Screenshots: see gifcheck/cdp-*.png (attached from the drive below; CDP script kept outside the repo).
  • Docs in the same change: 06 (§3 tree + gated list + reservation + Decisions), 12 (route table + §2.3.0 pipeline + compression note + Decisions), EVIDENCE.md E12. No DEVIATIONS amendment (no budget change).

Deviations from plan + R1

None material. One nit-level judgment call: bucket.gif ships 7 frames (the "2 sub-frames ×50" read-highlight split from the storyboard) rather than 6 — inside the 6–7 budget row and the 30 KB ceiling.

Implements Forgejo issue #187 per the plan + R1 (R1 normative on conflict) + review findings. ## What changed **Backend (`internal/server`, doc 06 §§3.1/3.3 + Decisions):** - Explicit `GET /explore` (`explorePage`) next to `GET /` (`spaHome`, now shell-only). Chi static-beats-wildcard ordering; without it `/explore` falls into the `/*` wildcard's single-segment owner shape. - `?format=text` (or text/plain Accept w/o text/html) MOVED `/` → `/explore` (S1; pre-1.0 no-alias — one canonical home, no silent alias, no 308). `/` answers the shell unconditionally. - `explore` joins the reserved single-segment UI names (owner literally named `explore` loses only its `/:owner` UI page; N2 self-link loop named in the note). - `uiAsset`: `concepts/<name>.gif` → `dist/concepts/`, `image/gif` MIME, no-cache + ETag (never immutable — B1, served prefix pinned `/_ui/concepts/`). - `compress` bypasses `image/*` (B2; notes the pre-existing doc/code drift — 12 §3 implied a content-type gate the code never had). **Frontend (doc 12 §§2.3/2.3.0 + Decisions):** - `/` → new `Landing.jsx` (hero in README L3–5 language + 3 CTAs + walgit compat line, 4 alternating concept sections, `#quickstart` strip with the README push block + auth/SSH one-liners). Zero API calls (no `useData`/SDK/`fetch` — pinned by test). - `/explore` → `Owners.jsx` (logic unchanged — caps/order/stars/activity/overflow; intro slimmed to one-liner + "What is walhub? → /"). Nav `owners → /` becomes `explore → /explore` (brand stays `/`); `*` fallback renders Landing. - New `ConceptGif.jsx`: still-first (`-still.gif` renders immediately + `<noscript>`), animated swap only when `prefers-reduced-motion` does NOT match (change listener), `loading="lazy"` + `decoding="async"` + 640×360, plain `img alt` only (N1). **Generator (`internal/devtools/landinggif/`, stdlib only, no new Go modules / no new npm deps):** - `font.go` (hand-drawn full-ASCII 5x7, typed shape literals — N5), `draw.go` (rects/outlines/arrows/2× text/captions), `main.go` (collab-first scenes, S3), `main_test.go` (full-ASCII + scene-text coverage — the missing-`3` class fails loudly; determinism; timing floors; golden freshness; byte budgets). - `make landing-gifs` is MANUAL (B3 — not a `make web` prerequisite); 8 checked-in files in `web/public/concepts/` (vite copies `public/` → `dist/` verbatim; zero embed changes). ## Frame timings (storyboard acceptance) push `200 50 50 60 180 180 200` (~8.0s) · bucket `200 60 200 60 50 50 220` (~8.4s) · fetch `200 60 150 50 200 200` (~7.1s) · collab `200 60 150 150 150 200 200` (~9.1s). Key holds ≥ 1.8s, motion steps ≥ 0.5s (floor 40, N4), ≤ 3 moving elements/frame, numbered captions on every frame. Sizes: push 16,773 · bucket 17,793 · fetch 15,874 · collab 22,912 · stills ~2–2.5 KB each · total 82,618 bytes (budgets ≤ 30/30/30/40 KB, total ≤ 150 KB — see EVIDENCE.md E12). ## Verification - `make fmt && make vet` clean; `go test -race ./internal/server/` ok; `make cover` 48 packages ≥ 95% (server 95.6%); `go test ./internal/devtools/landinggif/` green (incl. golden freshness). - `node --test web/test/unit/*.test.js`: 386 pass (incl. new `landing.test.js`: routes/nav/CTA+alt pins, `/_ui/` prefix, zero-API-call rule, owners move). - `git diff go.mod go.sum package.json pnpm-lock.yaml` empty — no new deps either ecosystem. - Live server: `/o/r/badsub` → 404 plain text (S2 — never reaches the SPA; Solid nested router keeps shell-with-empty-outlet for matched repo paths); `/explore?format=text` → owner list; `/?format=text` → HTML shell; GIF serves `image/gif` 200 uncompressed under `Accept-Encoding: gzip` while hashed JS still gzips. - Real Chromium via :9222 on the canonical host (`walgit.localhost:8089`): `/` dark+light (H1 renders, 4 animated GIFs 640 wide), `/` reduced-motion (4 stills), `/explore` dark (Owners + slimmed intro) — 4/4 clean, zero console errors. Screenshots: see `gifcheck/cdp-*.png` (attached from the drive below; CDP script kept outside the repo). - Docs in the same change: 06 (§3 tree + gated list + reservation + Decisions), 12 (route table + §2.3.0 pipeline + compression note + Decisions), EVIDENCE.md E12. No DEVIATIONS amendment (no budget change). ## Deviations from plan + R1 None material. One nit-level judgment call: `bucket.gif` ships 7 frames (the "2 sub-frames ×50" read-highlight split from the storyboard) rather than 6 — inside the 6–7 budget row and the 30 KB ceiling.
Backend (06 section 3.1/3.3): explicit GET /explore (explorePage) next to
GET / (spaHome, now shell-only); ?format=text MOVES from / to /explore
(pre-1.0 no-alias: one canonical home, no 308); explore joins the reserved
single-segment UI names. uiAsset serves concepts/<name>.gif as image/gif
(no-cache + ETag, never immutable); compress bypasses image/* (gzipping
LZW bytes is pure CPU per request; notes the pre-existing doc/code drift).
Frontend (12 section 2.3/2.3.0): / renders Landing.jsx (hero + 4 concept
sections + quickstart, zero API calls); /explore renders Owners.jsx
(logic unchanged, intro slimmed to one-liner + What-is link); nav
owners->explore; * fallback renders Landing. ConceptGif.jsx: still-first,
reduced-motion swap, /_ui/concepts/ prefix, lazy + async + 640x360, img
alt only. Generator internal/devtools/landinggif/ (stdlib image/gif,
hand-drawn full-ASCII 5x7 font + coverage test, deterministic golden,
collab-first scenes per storyboard timings); make landing-gifs is MANUAL
(B3). web/public/concepts/ 8 checked-in files (82,618 bytes total).
Docs: 06 route tree + reservation + Decisions (S1 move, B2 drift, B1
prefix), 12 route table + pipeline + Decisions, EVIDENCE.md E12 entry.
Tests: x_health_test (explore text/shell/503, concepts MIME/cache/304,
compress bypass), landing.test.js (routes/nav/CTA/alt/prefix/zero-call).
push 9.2s / fetch 8.6s / collab 11.1s (bucket 8.4s was already right).
Delays unchanged — acceptance (holds >=1.8s, steps >=0.4s) holds; only
the derived totals in EVIDENCE.md E12 and the scene comments were off.
Sign in to join this conversation.
No description provided.