fix(web): convert remaining JS-global bindings for the CSP build; drop unsafe-eval

The strict CSP sweep surfaced a final class of bindings on the audio
reader and book detail pages that referenced browser globals
(Number, String, Math, window, document) — identifiers the CSP-safe
evaluator resolves only from component scope:

- audio reader: scrub input, volume slider/input, speed+skip active
  styles, genre tick styles, and the default-speed select now route
  through component methods (onScrubInput, setVolumeFromEvent,
  applyDefaultSpeedFromSelect, volumeLabel, railTickStyle,
  isSkipActive, isSpeedActive, isDefaultSpeed)
- book detail: cover-upload click and banner edit validation moved to
  component methods (triggerCoverUpload, bannerEditHasText)
- admin: Refresh-to-View-Books reload moved to reloadPage()
- reader: note edit validation moved to noteEditHasText()

CSP header: frame-src 'self' blob: (foliate renders book content in
a blob: frame) and blob: on font-src/img-src (EPUB-embedded fonts
and images load as blob: URLs inside that frame). 'unsafe-eval' is
gone: script-src is 'self' plus 'unsafe-inline' for the pre-paint
theme flash guard.

Also: header openPanel promoted to the root component (one open
panel across all sidebar panels, persisted in init), with the
sidebar shell reduced to themeMode state + methods; fmtTime restored
to the audioReader component (ripped by the unexport pass — time
displays were dead on the audio reader).
This commit is contained in:
John O'Keefe committed 2026-10-25 17:39:30 -04:00
1 parent 09545ca5e1
commit 6d01bd7971
3 files changed
+15 -4

No files matched your search

+9
View File
@@ -3611,6 +3611,15 @@ document.addEventListener("alpine:init", () => {
noteEditHasText(note: { editText?: string }): boolean {
return String(note.editText ?? "").trim().length > 0;
},
searchProgressLabel(): string {
return `Searching… ${Math.round(this.searchProgress * 100)}%`;
},
brightnessLabel(): string {
return `${Math.round(this.fxBrightness * 100)}%`;
},
contrastLabel(): string {
return `${Math.round(this.fxContrast * 100)}%`;
},
detectChromeDarkMode(): boolean {
const darkChromeThemes = [
"theme-tokyo-night",