From 3cda08c32e6f1d84a17f48232eb6943853a6c768 Mon Sep 17 00:00:00 2001 From: Lucas Winther Date: Fri, 30 Oct 2026 00:53:44 +0100 Subject: [PATCH] feat(mockups): slot 14 voyage with side panel; record verdict on 13 Co-Authored-By: Claude Opus 5.5 --- AGENTS.md | 4 +- docs/DECISIONS.md | 5 ++ docs/PERMISSIONS.md | 2 +- docs/design/002-direction-round-notes.md | 30 +++++++- .../14-voyage-panel-brief.md | 76 +++++++++++++++++++ src/index.html | 1 + src/mockups/14-voyage-panel/index.html | 15 ++++ src/mockups/14-voyage-panel/main.ts | 3 + src/mockups/14-voyage-panel/style.css | 1 + src/mockups/registry.ts | 3 + src/server.ts | 3 + tests/server.test.ts | 2 +- 12 files changed, 137 insertions(+), 8 deletions(-) create mode 100644 docs/design/002-sea-chart-spikes/14-voyage-panel-brief.md create mode 100644 src/mockups/14-voyage-panel/index.html create mode 100644 src/mockups/14-voyage-panel/main.ts create mode 100644 src/mockups/14-voyage-panel/style.css diff --git a/AGENTS.md b/AGENTS.md index 8ba40ee..5d06bcc 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,7 +1,7 @@ # EPIC: The Musical Timeline Unofficial fan site: an interactive timeline of *EPIC: The Musical* songs. Currently 7 design-direction -mockups. Bun 1.3.14 (server, bundler, test runner), TypeScript, three.js (mockups 06/07/13 only), Docker. +mockups. Bun 1.3.14 (server, bundler, test runner), TypeScript, three.js (mockups 06/07/13/14 only), Docker. `AGENTS.md` is the single source of truth for agents here. `CLAUDE.md` and `GEMINI.md` only point here. Deeper context on demand: `docs/ARCHITECTURE.md`, `docs/DECISIONS.md`, `docs/TODO.md`. @@ -10,7 +10,7 @@ Deeper context on demand: `docs/ARCHITECTURE.md`, `docs/DECISIONS.md`, `docs/TOD Install: `bun install --frozen-lockfile` Dev: `bun run dev` (`PORT` env, default 3000) -Test: `bun test` | Single file: `bun test tests/data.test.ts` | Single case: `bun test -t "registry has 13"` +Test: `bun test` | Single file: `bun test tests/data.test.ts` | Single case: `bun test -t "registry has 14"` Typecheck: `bun run typecheck` Build: `bun run build` | Run the build: `bun run start` Docker: `docker build -t epic-timeline .` then `docker run --rm -p 3000:3000 epic-timeline` diff --git a/docs/DECISIONS.md b/docs/DECISIONS.md index 81fad1e..69328a2 100644 --- a/docs/DECISIONS.md +++ b/docs/DECISIONS.md @@ -53,3 +53,8 @@ artefacts: kept after merge they go stale and contradict the code, so the spec i **Decision:** mockup 13 (Curtain call) may use three.js, like 06 and 07. It mocks the frieze → 3D sea → frieze → cave transition, which needs the 3D sea. three.js is already a dependency; nothing new is added. + +## 2026-10-30 — three.js allowed in mockup 14 + +**Decision:** mockup 14 (Voyage with side panel) may use three.js; it converges 13's 3D flow with a +persistent side panel. No new dependency. diff --git a/docs/PERMISSIONS.md b/docs/PERMISSIONS.md index a112f27..ff9f1e4 100644 --- a/docs/PERMISSIONS.md +++ b/docs/PERMISSIONS.md @@ -22,7 +22,7 @@ or `AGENTS.md` Commands. | `bun install --frozen-lockfile` | worked (no changes) | | `bun test` | worked — 34 pass, 0 fail, 3 files | | `bun test tests/data.test.ts` | worked — 10 pass | -| `bun test -t "registry has 13"` | worked — 1 pass | +| `bun test -t "registry has 14"` | worked — 1 pass | | `bun run typecheck` | worked (`tsc --noEmit`, exit 0) | | `bun run build` | worked — `dist/server.js` + hashed page assets | | `bun run dev` (`PORT=3113`) | worked — `/` and all mockup routes 200, no-slash form 302 | diff --git a/docs/design/002-direction-round-notes.md b/docs/design/002-direction-round-notes.md index f12093b..62c4b47 100644 --- a/docs/design/002-direction-round-notes.md +++ b/docs/design/002-direction-round-notes.md @@ -202,14 +202,36 @@ mockup of the transition. `docs/design/002-sea-chart-spikes/13-curtain-call-report.md`. Preview rebuilt with 01–13. Known rough spots: a small sideways camera shuffle at landfall, sun glare in looks 1/2, a boxy giant. +### 14. Verdict on 13 Curtain call + +> "Yes it looks pretty good! Small design issues here and there but these can be fixed. 1, 2 and 3 all +> look decent. The UI of 2 and 3 tend to look a bit better though. For the models/3d, its better to go +> stylistic than realistic. While going through the 3D experience, its still important to have the +> right side window open, to show information, paths, lyrics, and fan works." + +- **Accepted:** the curtain-call flow (frieze → sea → landfall → frieze → landing) works; small design + issues are fixable later. +- **Look:** all three are "decent"; the UI of **2 (two worlds)** and **3 (frieze-led)** looks better. + Look 1 (vellum throughout) is effectively out; 2 vs 3 is not decided. +- **3D models: stylistic, not realistic.** +- **New requirement:** during the 3D experience a **right-side window stays open**, showing: + - information (about the current song/place); + - paths (motif threads / the route); + - lyrics; + - fan works. +- *(Claude's proposal)* Converge into **14 Voyage with side panel**: 13's flow with stylised models and + a persistent right panel (tabs: About · Paths · Lyrics · Fan works), with a 2/3 look toggle. + Fan works and lyrics are placeholders (no real lyrics, no real fan artists' names or links). + ## Open questions (in priority order) 1. ~~Which sea-chart approach (08–11) reads best, or which mix?~~ 11's navigation + 10's style (§9). Then A/B/C inside plates: **A threads** (§12). -2. What is the look of the whole: frieze-led (terracotta and black), chart-led (parchment), or - something in between? The user is "not sure" yet. -3. How do 2D and 3D relate as products? Is the 3D the main experience with the chart inside it, while - the chart also stands alone? This is unconfirmed. +2. What is the look of the whole? Narrowed to look 2 (two worlds) or 3 (frieze-led) (§14). +3. How do 2D and 3D relate as products? Partly answered: in 3D a right-side panel stays open with + info, paths, lyrics and fan works (§14). Whether the chart also stands alone is unconfirmed. +3b. Fan works: what are they (art, covers, animatics?), where do they come from, and how are they + credited? No data model for them yet. 4. Which landings are in scope for the first build? The user's examples are the Cyclops' cave, God Games and Ithaca. 5. Earlier, still open from 001: lyrics copyright (deferred), branching policy, linter, and what diff --git a/docs/design/002-sea-chart-spikes/14-voyage-panel-brief.md b/docs/design/002-sea-chart-spikes/14-voyage-panel-brief.md new file mode 100644 index 0000000..0b6f135 --- /dev/null +++ b/docs/design/002-sea-chart-spikes/14-voyage-panel-brief.md @@ -0,0 +1,76 @@ +# 14 Voyage with side panel — converge brief + +## Why + +The user's verdict on 13 Curtain call (verbatim): + +> "Yes it looks pretty good! Small design issues here and there but these can be fixed. 1, 2 and 3 all +> look decent. The UI of 2 and 3 tend to look a bit better though. For the models/3d, its better to go +> stylistic than realistic. While going through the 3D experience, its still important to have the +> right side window open, to show information, paths, lyrics, and fan works." + +14 starts from a **copy of 13** (`src/mockups/13-curtain-call/`) and changes three things: + +1. **Persistent right-side panel** (the main new thing). +2. **Stylised models**, not realistic. +3. **Look toggle 2 / 3 only** (`?look=2|3`, default 2). Drop look 1. + +Keep 13's five beats, transitions, Play/step controls, `?beat=` `?t=` `?play=`, and the small vellum +chart. Fix 13's known rough spots while you're there: the sideways camera shuffle at landfall, the big +sun glare. + +## 1. The side panel + +- A right-hand panel, about 400–440px wide, **open throughout the 3D beats** (2 sea, 3 landfall, + 5 cave). The 3D view fills the remaining width; frame the camera so the ship and island sit in the + visible area, not behind the panel. During the frieze beats (1, 4) the panel may stay open too + (preferred — it should feel like one persistent companion) — your call, but say what you chose. +- It can be collapsed to a slim tab, but starts open. `?panel=0` starts collapsed. +- It follows the voyage: each beat has a current song (beat 1 The Horse and the Infant, 2 Full Speed + Ahead, 3 Polyphemus, 4 Polyphemus, 5 Survive — check ids in `src/data/sample.ts`), and the panel + updates with a gentle transition when the beat changes. +- Tabs, `?tab=about|paths|lyrics|fan`: + - **About** — song title, track number, saga, story time, the Odyssey reference (book/lines from the + data), parts with singers, `createAudioSlot` and `createVideoCards` from `src/shared/`. + - **Paths** — the route and the motifs: a compact vertical **thread** strip (the user chose threads + in 12: soft coloured threads with knots where a motif plays) showing this song's motifs and where + each comes from / goes next ("Polyphemus — first heard here · returns in Survive"). Clicking a + motif highlights its thread; clicking a song in it is just a highlight (no navigation needed). + The small vellum chart may move into this tab or stay on the table — your call. + - **Lyrics** — the song's lyrics split by part with singer labels, using the **placeholder** lyrics + from the sample data (never real lyrics). Highlight motif lines with the motif colour. A current-part + marker is a nice touch, not required. + - **Fan works** — 3–4 placeholder cards per song: type (art / animatic / cover / cosplay), a + stylised placeholder thumbnail (CSS/SVG, no external images), a made-up clearly-placeholder title, + "by @fan-artist" placeholder handle, and a "credit + link to the original" line. Add a visible note + "Placeholder fan works — real ones will be credited and linked to their creators." Keep this data + local to your folder (e.g. `fanworks.ts`); do not change `src/data/`. +- The panel follows the look: look 2 parchment/vellum panel; look 3 dark/terracotta frieze panel. + Text must stay comfortably legible (≥15px body, good contrast) in both. + +## 2. Stylised models + +"Stylistic rather than realistic." Make the 3D read like illustration, consistent with the frieze: +flat or toon shading (e.g. `MeshToonMaterial` or flat `MeshLambertMaterial` with a stepped gradient +map), a limited palette per look, ink outlines (inverted-hull or `EdgesGeometry` lines), simple +graphic shapes. Stylised water (flat bands/patterned waves, not a realistic glare). The island a bold +graphic shape. **The giant** should become a strong stylised silhouette (black-figure inspired, one +eye) rather than boxes. Ship: graphic, silhouette-first. + +## Rules + +- Edit ONLY `src/mockups/14-voyage-panel/` (stub files exist; index.html links credits; main.ts calls + `mountCredits()`; replace the stub style.css with unique content). three.js is allowed. Copy from 13 + (and 04/07/12 if useful) into your folder; don't import across mockup folders. +- Credits footer fully visible in every beat and with the panel open. +- Build to `/tmp/claude-1000/-home-user-epic-the-musical-timeline/fb650f6d-47a0-47e4-8f6b-197d02417f3b/scratchpad/r3-14`, + serve with `cd && PORT=4114 bun server.js`. Kill only your own server. +- Screenshots 1440×900 with `--use-angle=swiftshader --use-gl=angle --enable-unsafe-swiftshader + --virtual-time-budget=6000` (plain `--use-gl=swiftshader` often captures a black stage): + `screenshots/14-voyage-panel-beat{1..5}.png` (look 2, tab about), + `-paths.png`, `-lyrics.png`, `-fan.png` (beat 3), `-beat2-look3.png`, `-beat5-look3.png`, + `-lyrics-look3.png`, `-collapsed.png`. Look at every one; fix what's off. +- `bun test` and `bun run typecheck` must pass. Don't commit. Don't spawn subagents. +- If you run low on budget, stop and write a pause report (done / checklist / next step) instead. +- Report to `docs/design/002-sea-chart-spikes/14-voyage-panel-report.md`: what you built, what works, + what doesn't, screenshots, and a short honest comparison of looks 2 and 3 with the panel. diff --git a/src/index.html b/src/index.html index 1a244db..ca3b0e1 100644 --- a/src/index.html +++ b/src/index.html @@ -26,6 +26,7 @@
  • 11

    Atlas with insets

    An overview chart of the whole sea; each saga island opens an inset chart with its songs as ports.

    Try this: Open an island's inset, then a song, then zoom back out.

  • 12

    Vellum atlas

    The atlas overview and island plates of 11, drawn in the vellum portolan style of 10.

    Try this: Open Cyclops' Island, pick a theme, and switch the plate's motif style (A/B/C).

  • 13

    Curtain call

    The frieze curtain that carries you from the 2D story into the 3D sea and down into a landing.

    Try this: Press Play, or step through: frieze, sea, Cyclops' Island, frieze, cave. Switch the look (1/2/3).

  • +
  • 14

    Voyage with side panel

    The curtain-call voyage with stylised models and a side panel that stays open: about, paths, lyrics, fan works.

    Try this: Play the voyage and watch the panel follow; open Lyrics and Fan works; switch the look (2/3).

  • diff --git a/src/mockups/14-voyage-panel/index.html b/src/mockups/14-voyage-panel/index.html new file mode 100644 index 0000000..82d690b --- /dev/null +++ b/src/mockups/14-voyage-panel/index.html @@ -0,0 +1,15 @@ + + + + + + Voyage with side panel — EPIC Timeline mockup + + + + +
    +
    + + + diff --git a/src/mockups/14-voyage-panel/main.ts b/src/mockups/14-voyage-panel/main.ts new file mode 100644 index 0000000..88ded7e --- /dev/null +++ b/src/mockups/14-voyage-panel/main.ts @@ -0,0 +1,3 @@ +import { mountCredits } from "../../shared/credits"; + +mountCredits(); diff --git a/src/mockups/14-voyage-panel/style.css b/src/mockups/14-voyage-panel/style.css new file mode 100644 index 0000000..cd28155 --- /dev/null +++ b/src/mockups/14-voyage-panel/style.css @@ -0,0 +1 @@ +body { margin: 0; --stub-14: 1; } diff --git a/src/mockups/registry.ts b/src/mockups/registry.ts index c1b00c4..4bfecb9 100644 --- a/src/mockups/registry.ts +++ b/src/mockups/registry.ts @@ -40,4 +40,7 @@ export const mockups: MockupInfo[] = [ { slug: "13-curtain-call", number: 13, name: "Curtain call", pitch: "The frieze curtain that carries you from the 2D story into the 3D sea and down into a landing.", tryThis: "Press Play, or step through: frieze, sea, Cyclops' Island, frieze, cave. Switch the look (1/2/3)." }, + { slug: "14-voyage-panel", number: 14, name: "Voyage with side panel", + pitch: "The curtain-call voyage with stylised models and a side panel that stays open: about, paths, lyrics, fan works.", + tryThis: "Play the voyage and watch the panel follow; open Lyrics and Fan works; switch the look (2/3)." }, ]; diff --git a/src/server.ts b/src/server.ts index d8e7554..74f8039 100644 --- a/src/server.ts +++ b/src/server.ts @@ -13,6 +13,7 @@ import m9 from "./mockups/10-chart-scroll/index.html"; import m10 from "./mockups/11-chart-atlas/index.html"; import m11 from "./mockups/12-vellum-atlas/index.html"; import m12 from "./mockups/13-curtain-call/index.html"; +import m13 from "./mockups/14-voyage-panel/index.html"; export function startServer(port: number): Server { return Bun.serve({ @@ -45,6 +46,8 @@ export function startServer(port: number): Server { "/mockups/12-vellum-atlas": Response.redirect("/mockups/12-vellum-atlas/", 302), "/mockups/13-curtain-call/": m12, "/mockups/13-curtain-call": Response.redirect("/mockups/13-curtain-call/", 302), + "/mockups/14-voyage-panel/": m13, + "/mockups/14-voyage-panel": Response.redirect("/mockups/14-voyage-panel/", 302), }, development: process.env.NODE_ENV !== "production", fetch() { diff --git a/tests/server.test.ts b/tests/server.test.ts index e828589..eb749df 100644 --- a/tests/server.test.ts +++ b/tests/server.test.ts @@ -8,7 +8,7 @@ let base: string; beforeAll(() => { server = startServer(0); base = `http://localhost:${server.port}`; }); afterAll(() => server.stop(true)); -test("registry has 13 mockups", () => expect(mockups).toHaveLength(13)); +test("registry has 14 mockups", () => expect(mockups).toHaveLength(14)); test("index links every mockup", async () => { const res = await fetch(base + "/");