feat(web): sleep timer upgrades — undo toast, chain mode, clock stop, wind-down dim (plan §17)

- Undo: a genuine sleep fire now toasts with a Resume action (toast.ts
  grows an optional action button; acting dismisses).
- Chain: 'End of chapter, then 15 min' — leaving the chapter starts a
  15-min countdown instead of fading (SleepState.followUpMinutes).
- Clock: the popover gains a time input + Arm — armClockTime resolves
  HH:MM to its next occurrence and reuses the minutes machinery.
- Dim: the ticker eases a pointer-events-none overlay toward 35% black
  across a countdown's final 30s; stop/finish resets it.
(Fade-out and countdown labels already existed — §3 built them.)
This commit is contained in:
John O'Keefe committed 2026-10-23 11:06:42 -04:00
1 parent 4769e832c4
commit f8de17723a
6 files changed
+218 -21

No files matched your search

+23
View File
@@ -44,6 +44,12 @@ templ AudioReader(user User, metadata ReaderMetadata) {
"/api/media-items/"+metadata.MediaItemID+"/chapters",
"/api/media-items/"+metadata.MediaItemID+"/progress") }
>
<!-- §17 wind-down dim: the sleep ticker eases this toward 35%
black across a countdown's final 30s; pointer-events-none
keeps the page usable, stop/finish resets it. -->
<div class="fixed inset-0 pointer-events-none z-40"
:style="'opacity:' + sleepDim + '; background: #000; transition: opacity 1s linear;'"
></div>
<div class="h-full flex flex-col items-center justify-center px-4 gap-6 select-none">
<!-- Header: back to the detail page (reader-style exit) and the
chapter launcher. Floating drawer, never in-flow (A2b). -->
@@ -482,6 +488,23 @@ templ AudioReader(user User, metadata ReaderMetadata) {
x-text="opt.label"
></button>
</template>
<!-- §17 clock mode: stop at a wall-clock time (its next
occurrence). The input drives chooseSleepClock(). -->
<div class="flex items-center gap-1.5 px-2 pt-1.5 mt-1 border-t" style="border-color: var(--border);">
<input
type="time"
x-model="sleepClockTime"
class="text-xs px-1.5 py-1 rounded-md flex-1 min-w-0"
style="background-color: var(--surface-hover); color: var(--text-primary); border: 1px solid var(--border);"
aria-label="Stop at time"
/>
<button
type="button"
@click="chooseSleepClock(); sleepOpen = false"
class="text-xs px-2.5 py-1 rounded-md font-semibold"
:style="sleepArmedChoiceKey === 'clock' ? 'background-color: var(--accent-muted); color: var(--text-primary);' : 'background-color: var(--surface-hover); color: var(--text-secondary);'"
>Arm</button>
</div>
</div>
</div>
<div class="relative" @click.outside="volumeOpen = false">