From 239b764393a2c726dd8cd343fec915c280ee3892 Mon Sep 17 00:00:00 2001 From: Mikkeli Matlock Date: Tue, 15 Sep 2026 17:15:01 +0900 Subject: [PATCH] Document web UI interactions and content-versioned assets Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_015UXA39Sh9coAx4FhRkd8R2 --- CLAUDE.md | 18 ++++++++++++++++-- README.md | 13 ++++++++++--- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index bdfc9d5..c302775 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -115,6 +115,12 @@ A custom mastering toolkit that provides metrics to evaluate audio masterings th - A static site: decoding and every metric run in the visitor's tab; nothing is uploaded or stored. Served with `Content-Security-Policy: connect-src 'none'` (`deploy/nginx.conf`), so the page and its worker cannot make network requests +- The image versions assets by content: everything but `index.html` is copied to + `v//` and the page's references rewritten (Dockerfile `assets` stage); + nginx serves `v/` immutable and the page `no-cache`. Needed because Cloudflare + forces browser caching of .js/.css to 4 h, which paired a fresh index.html with + a stale app.js (`Cannot set properties of null`). Source `web/index.html` keeps + plain relative paths, so serving `web/` directly still works - `decode.js` — `AudioFile.__init__` counterpart. WAV/AIFF PCM & float are parsed directly with libsndfile scaling (bit-identical to `librosa.load`; Chromium's own WAV decoder scales positive int16 by 1/32767). Other formats go through @@ -133,8 +139,16 @@ A custom mastering toolkit that provides metrics to evaluate audio masterings th metric per request, transfers typed arrays back - `app.js` — `MainWindow` + `AnalysisResultsManager` + renderer: track list, overlay set, per-metric result cache, job queue (foreground jobs jump ahead of - background prefetch), uPlot for curves/bands + canvas spectrogram (log axis - drawn from linear rows), draggable reference lines per metric, metric hlines + background prefetch; LUFS + true peak prefetched first for the metadata + panel's loudness summary), uPlot for curves/bands + canvas spectrogram (log + axis drawn from linear rows, cached offscreen image). Guide and reference + lines are painted in a uPlot draw hook, so dragging a line is a cheap redraw + and moves live. One zoom/pan controller serves both renderers: wheel zoom + (Ctrl = x only, Shift = y only, over an axis = that axis), rAF-throttled drag + pan, double-click reset; `state.zoom` survives redraws and resets when units + change. In-plot legend overlay like pyqtgraph's +- Font: CSS `--font` stack prefers M PLUS 1 Code, else the system UI font; + canvas/uPlot text reads the same variable - `vendor/` — uPlot 1.6.32, vendored (the CSP only allows same-origin scripts) - Needs an HTTP server (module scripts and workers don't load from `file://`), but no secure context — plain `http://` on a LAN works diff --git a/README.md b/README.md index 2f9827f..f7ddae5 100644 --- a/README.md +++ b/README.md @@ -71,9 +71,12 @@ not just promised: the page is served with `Content-Security-Policy: connect-src request. Everything the desktop app does works the same way: all seven metrics, the -overlay, lin/log toggles, absolute/relative time and draggable reference lines -(kept per metric), plus the metrics' own reference lines (−14 LUFS, integrated -loudness, 0 dBTP…). Analysis runs at the file's native sample rate. +overlay, lin/log toggles, absolute/relative time, an in-plot legend, wheel zoom +around the cursor (Ctrl: time only, Shift: values only, or scroll over an axis), +live drag-to-pan and double-click to reset, and draggable reference lines (kept +per metric) alongside the metrics' own guides (−14 LUFS, integrated loudness, +0 dBTP…). The metadata panel and file list show integrated loudness, LRA and +max true peak. Analysis runs at the file's native sample rate. Serve `web/` with any static file server, over `http://` or `https://` — no secure context is needed. (ES modules and workers don't load from `file://`, so @@ -122,6 +125,10 @@ float32 paths (true peak, spectrogram dB, RMS) to ~1e-6. The image is nginx (unprivileged, read-only root filesystem) serving `web/` with `deploy/nginx.conf`, which sets the CSP and turns access logging off. There is nothing to persist, upload or log on the server side, so there are no volumes. +Assets are served from a content-hashed `v//` directory (cached +indefinitely) while the page itself is always revalidated, so a deploy can never +leave a browser running old scripts against a new page — even behind a CDN that +overrides cache headers. ### Web architecture