architecture: the file map (ES modules, no build step) and the harness modes as they are now
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Dhu3iHseRewQ2WpBSVLokG
This commit is contained in:
1 parent
4bbe457421
commit
db33db4673
1 file changed
+37
-8
+37
-8
@@ -1,7 +1,32 @@
|
||||
# How g*ndora fits together
|
||||
|
||||
One page, one script, one worker. This is the map: what each part owns, how a track moves through the system, and
|
||||
the invariants that the playback code relies on. Keep it current when you change any of them.
|
||||
One page, a handful of ES modules, one worker. This is the map: what each part owns, how a track moves through the
|
||||
system, and the invariants that the playback code relies on. Keep it current when you change any of them.
|
||||
|
||||
## File map
|
||||
|
||||
Plain `<script type="module">`, static files served as-is, no build step. `index.html` is markup + CSS + two script
|
||||
tags (`app.js` as a module, `brain.js` as a classic script with `defer`, so it runs after the module).
|
||||
|
||||
| File | Owns |
|
||||
|---|---|
|
||||
| `core.js` | `state` (incl. the one `<audio>`), `hooks`, `$`, `LS`, `toast`, `sleep`, `asDataUrl`, `mmss`, the job counter `ids` |
|
||||
| `covers.js` | pure: `hash32`, `prng`, `keyHue`/`KEYS`, `palette`/`PALETTES`, `drawCover` and the looks, `drawMark`, `drawOnAir` |
|
||||
| `player.js` | the state machine and everything that touches `state.audio`: `setSource`, `toCooking` … `toIdle`, `playJob`, `advance`, `step`, `prime`, the crackle/bed, `SILENCE`, scrubbing, Media Session, the analyser, the transport buttons |
|
||||
| `render.js` | `venice()` behind an injectable fetcher (`setFetcher`), `renderTrack`, `speak` is dj's; `makeNoise`/`noiseSpec`, `makeTone`/`toneSpec`, `wavBlob` |
|
||||
| `dj.js` | `djPrompt`/`askDJ`, `askBreak`, `askMash`, `DJ_SCHEMA`, modes, breaks, voices, personas, `speak` |
|
||||
| `store.js` | `db` (STORAGE.md), `saveStation`, `remember`, `logEvent` (the ledger), tapes incl. the virtual KEPT tape, `tapeSpec` |
|
||||
| `mashup.js` | `mashReference` (OfflineAudioContext) and `mashup` |
|
||||
| `ui/player.js` | cover/title/tags/lyrics, keep/share, the trail, ticks, reactions, emoji tray + picker, sheets scrim |
|
||||
| `ui/library.js` | the stations row, the library view, now-playing bar, tapes |
|
||||
| `ui/sheet.js` | the station sheet |
|
||||
| `ui/taste.js` | the taste view (`reactionWeight`, `drawTaste`, `openTaste`) |
|
||||
| `app.js` | boot: `openStation`, `makeJob`/`cookNext`, links and share, SW + update pill, theme, the key box; sets `window.g` and the globals brain.js uses |
|
||||
| `brain.js` | unchanged: a classic script reading `hooks`, `state`, `db`, `$`, the cover/taste drawers and `seq` as globals |
|
||||
|
||||
`window.g = { state, openStation, cookNext, crackle, hooks, db, wavBlob, $, hash32, render: { setFetcher } }` is for
|
||||
tooling (the harness fragments). The modules import each other in a cycle (`player` ↔ `ui/*` ↔ `app`), which is fine
|
||||
because only function declarations cross it at load time; `core.js` and `covers.js` import nothing.
|
||||
|
||||
## The parts
|
||||
|
||||
@@ -107,17 +132,21 @@ rseed (render seed), cost, renderer, reaction, hits, guess, break/sponsored/spok
|
||||
`tools/harness.sh` loads the live page with the backend stubbed (DJ returns a canned sheet, render returns a generated
|
||||
wav after a configurable delay) and prints one state line per step. Two modes:
|
||||
|
||||
sh tools/harness.sh # tools/harness.html: the station loop, autoplay allowed, from file://
|
||||
sh tools/harness.sh # tools/harness.html: the station loop, autoplay allowed
|
||||
sh tools/harness.sh resume # tools/harness-resume.html: resume a stored station, autoplay blocked
|
||||
sh tools/harness.sh safari # the loop in WebKit (safaridriver), with a real click as the activation
|
||||
|
||||
The loop drives the player through: first play, pre-cook, handoff on end, the crackle hold and its handover,
|
||||
pause/resume, skip. It runs under headless Chrome's virtual time (`--dump-dom --virtual-time-budget`), so it is quick
|
||||
and deterministic.
|
||||
pause/resume, skip. All modes serve the page and the modules from a temp folder on 127.0.0.1 (modules and IndexedDB
|
||||
both need http) and run in real time: Chrome over the DevTools protocol (`tools/harness-cdp.py`, python3 stdlib
|
||||
only), WebKit over safaridriver (`tools/harness-safari.py`). The fragments are inline modules, so they run after
|
||||
`app.js`, reach the page through `window.g` and stub the backend with `render.setFetcher`.
|
||||
|
||||
The resume mode seeds IndexedDB with a station and one rendered track, calls `openStation` with no user gesture and
|
||||
expects the TAP TO PLAY wait (`state.needTap`, `#cover.tap`) with the next track cooked behind it. IndexedDB needs
|
||||
http, so the page is served from a temp folder on 127.0.0.1; and virtual time does not wait for IndexedDB, so this
|
||||
mode is driven in real time over the DevTools protocol by `tools/harness-cdp.py` (python3 stdlib only).
|
||||
expects the TAP TO PLAY wait (`state.needTap`, `#cover.tap`) with the next track cooked behind it.
|
||||
|
||||
`node tools/check-pure.mjs` loads `covers.js` and `render.js`'s generators in node (a canvas stub stands in for the
|
||||
DOM) and draws every look and generates a wav, a noise and a tone once.
|
||||
|
||||
Every line should show the expected `phase`/`cur`/`next`/`paused`/`holding`. Run both before and after any change to
|
||||
playback, the crackle, the hold, the state machine or `playJob`/`advance`. Neither can test real audio output, the
|
||||
|
||||
Reference in new issue
Block a user