Landing a change request is the payoff for the review loop, and until now the page it landed on looked exactly like any other view.
A successful landing redirects with ?merged=1. static/app.js reads that
parameter once, removes it with replaceState, fetches
static/confetti.browser.js, and fires a single burst. Nothing else loads the
library: a reload, a bookmark, a shared link, or any other page is unchanged and
pays nothing.
Where it departs from the issue
The issue estimated 10.8 KB from the minified file on the jsDelivr CDN. That file
is generated by the CDN, not published upstream. The 1.9.3 npm tarball contains
only dist/confetti.browser.js, so the vendored asset is 24.9 KB raw / 6.9 KB
gzipped, byte-for-byte from the tarball whose sha512 matches the registry’s
dist.integrity (sha512-rFfTURMvmVEX1gyXFgn5QMn81bYk70qa0HLzcIOSVEyl57n6o9ItHeBtUSWdvKAPY0xlvBHno4/v3QPrT83q9g==).
Minifying it ourselves would make the committed bytes depend on a tool that is
not in the repository, so the honest artifact wins.
That size is acceptable only because of the lazy load. Static() serves through
http.FileServerFS with no gzip middleware, so a script in the page shell would
cost every page load 24.9 KB; on the landing redirect it costs one reader,
once.
Changes
static/confetti.browser.jsandstatic/canvas-confetti.LICENSE: vendored like htmx and Lucide, soscript-src 'self'is untouched.useWorkeris left off, so noblob:worker is created anddefault-srcneeds no relaxation.writes.go: the merge redirect carries?merged=1. Refusal paths, including the HTMX fragment reply, are unchanged.static/app.js: the reader of the parameter.disableForReducedMotion: trueandreset()before firing, so a reduced-motion visitor gets the same page with no animation and a back/forward-cache restore cannot stack canvases.templates.goanddocs/ui-plan.md: record why one asset is fetched on demand when nothing else is.- Tests: the merge redirect and the merged page behind it; the asset is served
from the binary,
app.jsnames it once, and no shell page mentions it.
Closes #01M5XZ7T4R88BA9KV1KTSV7ZXP