From eb57b7208b6d56a89a6b852f387a4ca3bb45c3df Mon Sep 17 00:00:00 2001 From: John O'Keefe Date: Sun, 25 Oct 2026 16:58:28 -0400 Subject: [PATCH] feat(web): switch to the Alpine CSP build; drop 'unsafe-eval' from CSP MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Standard Alpine evaluates every attribute expression through new Function(), which a strict Content-Security-Policy cannot allow. Swap to the official CSP-safe build (@alpinejs/csp), whose parser evaluates expressions against registered component scope without eval. script-src now carries only 'self' and 'unsafe-inline' (the latter still required by the pre-paint theme flash guard). Bindings rewritten out of the unsupported expression shapes: - 21 multi-statement sequences across admin, audio_reader, docs, header, index, and reader became single component methods (init/loadWatchStatus hooks, commitScrub, pickSkip, pickSpeed, pickDefaultSpeed, chooseSleepOption, chooseSleepClockMode, toggleFxInvert, openPageThumbs, submitNote, clearSearch, pickReadingTheme) - the header sidebar's inline x-data (openPanel/themeMode IIFE) became a registered sidebarShell component; openPanel itself moved to the header root so every sidebar panel shares one open state - the appearance-panel moved into the component init() - header init() now also seeds openPanel from localStorage (or the admin section on /admin pages) Alpine magics (, , , ) remain available — the CSP evaluator injects them into scope. The Playwright suite covers the converted surfaces end to end. --- package.json | 1 + templates/admin.templ | 2 +- templates/audio_reader.templ | 12 +++--- templates/bookshelf.templ | 2 +- templates/docs.templ | 2 +- templates/header.templ | 12 +++--- templates/index.templ | 2 +- templates/reader.templ | 10 ++--- web/src/admin.ts | 2 + web/src/alpine.ts | 2 +- web/src/audio.ts | 25 ++++++++++++ web/src/docs.ts | 5 +++ web/src/header.ts | 75 ++++++++++++++++++++++++++++++++++++ web/src/index.ts | 4 ++ web/src/reader/reader.ts | 21 ++++++++++ 15 files changed, 154 insertions(+), 23 deletions(-) diff --git a/package.json b/package.json index efa6a9c3..5a587055 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "turndown-plugin-gfm": "^1.0.2" }, "devDependencies": { + "@alpinejs/csp": "^3.15.8", "@playwright/test": "^1.64.1", "@tailwindcss/forms": "^0.5.11", "@tailwindcss/typography": "^0.5.19", diff --git a/templates/admin.templ b/templates/admin.templ index 48260890..38b8c609 100644 --- a/templates/admin.templ +++ b/templates/admin.templ @@ -10,7 +10,7 @@ templ Admin(user User, stats AdminStats) { - + @Header(user, "/admin")
diff --git a/templates/audio_reader.templ b/templates/audio_reader.templ index 1bbfabcf..043a337c 100644 --- a/templates/audio_reader.templ +++ b/templates/audio_reader.templ @@ -246,7 +246,7 @@ templ AudioReader(user User, metadata ReaderMetadata) { :value="scrubbing ? scrubValue : currentTime" class="relative w-full" @input="scrubbing = true; scrubValue = Number($event.target.value)" - @change="seekTo(scrubValue); scrubbing = false" + @change="commitScrub()" aria-label="Seek position" /> @@ -395,7 +395,7 @@ templ AudioReader(user User, metadata ReaderMetadata) {