fix(web): complete the CSP-build migration across remaining surfaces
- audio reader: scrub/volume/speed/genre-tick bindings converted to component methods (onScrubInput, setVolumeFromEvent, applyDefaultSpeedFromSelect, volumeLabel, railTickStyle, isSkipActive/isSpeedActive) — these used Number/Math/String globals that the CSP evaluator cannot resolve - fmtTime restored to the audioReader component: the unexport pass had ripped it from the Alpine.data object (time displays were dead) - book detail: cover-upload click and banner edit validation moved to component methods (triggerCoverUpload, bannerEditHasText) - admin/docs/index components: init() hooks for their x-init chains - CSP header: 'unsafe-eval' dropped — the CSP build needs no eval - e2e spec: replace test.info().annotate (unavailable in this Playwright version) with console logging
This commit is contained in:
1 parent
eb57b7208b
commit
09545ca5e1
16 files changed
+249
-234
No files matched your search
@@ -161,12 +161,12 @@ templ AudioReader(user User, metadata ReaderMetadata) {
|
||||
class="text-xs rounded-md px-1.5 py-1"
|
||||
style="background-color: var(--surface-hover); color: var(--text-primary); border: 1px solid var(--border);"
|
||||
aria-label="Default playback speed"
|
||||
@change="setDefaultSpeed(Number($event.target.value))"
|
||||
@change="applyDefaultSpeedFromSelect($event)"
|
||||
>
|
||||
for _, rate := range SpeedOptions {
|
||||
<option
|
||||
value={ fmt.Sprintf("%g", rate) }
|
||||
:selected={ fmt.Sprintf("Number(defaultSpeed) === %g", rate) }
|
||||
:selected={ fmt.Sprintf("isDefaultSpeed(%g)", rate) }
|
||||
>{ fmt.Sprintf("%g×", rate) }</option>
|
||||
}
|
||||
</select>
|
||||
@@ -245,7 +245,7 @@ templ AudioReader(user User, metadata ReaderMetadata) {
|
||||
:max="chaptersDuration() || 1"
|
||||
:value="scrubbing ? scrubValue : currentTime"
|
||||
class="relative w-full"
|
||||
@input="scrubbing = true; scrubValue = Number($event.target.value)"
|
||||
@input="onScrubInput($event)"
|
||||
@change="commitScrub()"
|
||||
aria-label="Seek position"
|
||||
/>
|
||||
@@ -272,7 +272,7 @@ templ AudioReader(user User, metadata ReaderMetadata) {
|
||||
<div class="relative h-full">
|
||||
<div class="absolute inset-x-0 top-1/2 h-px" style="background-color: var(--border);"></div>
|
||||
<template x-for="tick in railTickList" :key="'rtick' + tick.start">
|
||||
<div class="absolute top-1 bottom-1 w-px" :style="'left:' + ((tick.start - railFrom) / Math.max(1, railTo - railFrom) * 100) + '%;background-color:var(--text-secondary);opacity:0.5;'"></div>
|
||||
<div class="absolute top-1 bottom-1 w-px" :style="railTickStyle(tick)"></div>
|
||||
</template>
|
||||
<div class="absolute top-0 bottom-0 w-0.5 -translate-x-1/2 rounded-full" :style="'left:' + railMarkerPct() + '%;background-color:var(--accent);'"></div>
|
||||
<div class="absolute left-1 top-0.5 text-[10px] tabular-nums whitespace-nowrap" style="color: var(--text-primary);" x-text="fmtTime(scrubValue) + ' — ' + chapterTitleAt(scrubValue)"></div>
|
||||
@@ -397,8 +397,8 @@ templ AudioReader(user User, metadata ReaderMetadata) {
|
||||
type="button"
|
||||
@click="pickSkip(secs)"
|
||||
class="w-full text-left px-3 py-1.5 rounded-md text-sm"
|
||||
:class="Number(skipSeconds) === secs ? 'font-semibold' : ''"
|
||||
:style="Number(skipSeconds) === secs ? 'background-color: var(--accent-muted); color: var(--text-primary);' : 'color: var(--text-secondary);'"
|
||||
:class="isSkipActive(secs) ? 'font-semibold' : ''"
|
||||
:style="isSkipActive(secs) ? 'background-color: var(--accent-muted); color: var(--text-primary);' : 'color: var(--text-secondary);'"
|
||||
x-text="secs + ' seconds'"
|
||||
></button>
|
||||
</template>
|
||||
@@ -427,8 +427,8 @@ templ AudioReader(user User, metadata ReaderMetadata) {
|
||||
type="button"
|
||||
@click="pickSpeed(rate)"
|
||||
class="w-full text-left px-3 py-1.5 rounded-md text-sm"
|
||||
:class="Number(speed) === rate ? 'font-semibold' : ''"
|
||||
:style="Number(speed) === rate ? 'background-color: var(--accent-muted); color: var(--text-primary);' : 'color: var(--text-secondary);'"
|
||||
:class="isSpeedActive(rate) ? 'font-semibold' : ''"
|
||||
:style="isSpeedActive(rate) ? 'background-color: var(--accent-muted); color: var(--text-primary);' : 'color: var(--text-secondary);'"
|
||||
x-text="rate + '×' + (isDefaultSpeed(rate) ? ' · default' : '')"
|
||||
></button>
|
||||
</template>
|
||||
@@ -512,7 +512,7 @@ templ AudioReader(user User, metadata ReaderMetadata) {
|
||||
type="button"
|
||||
@click="volumeOpen = !volumeOpen"
|
||||
class="rounded-lg p-2 hover:bg-surface-hover transition-colors"
|
||||
:aria-label="'Volume ' + Math.round(Number(volume) * 100) + '%'"
|
||||
:aria-label="volumeLabel()"
|
||||
style="color: var(--text-secondary);"
|
||||
>
|
||||
<span x-show="volumeIcon() === 'muted'" x-cloak>
|
||||
@@ -576,7 +576,7 @@ templ AudioReader(user User, metadata ReaderMetadata) {
|
||||
max="1"
|
||||
step="0.05"
|
||||
:value="muted ? 0 : volume"
|
||||
@input="setVolume(Number($event.target.value))"
|
||||
@input="setVolumeFromEvent($event)"
|
||||
class="accent-current"
|
||||
style="width: 7.5rem;"
|
||||
aria-label="Volume"
|
||||
@@ -659,8 +659,8 @@ templ AudioReader(user User, metadata ReaderMetadata) {
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="note-rich text-xs mt-1" x-show="b.notes && editingBookmarkId !== String(b.id)" x-html="bookmarkNoteHtml(b)"></div>
|
||||
<div x-show="editingBookmarkId === String(b.id)" x-cloak class="mt-1">
|
||||
<div class="note-rich text-xs mt-1" x-show="b.notes && !isBookmarkEditing(b)" x-html="bookmarkNoteHtml(b)"></div>
|
||||
<div x-show="isBookmarkEditing(b)" x-cloak class="mt-1">
|
||||
<textarea x-model="editingNote" rows="3" class="input text-sm resize-y" placeholder="Note (markdown)"></textarea>
|
||||
<div class="flex justify-end gap-2 mt-1">
|
||||
<button type="button" @click="editingBookmarkId = ''" class="btn btn-ghost text-xs px-2 py-1">Cancel</button>
|
||||
|
||||
Reference in new issue
Block a user