fix: player float mode now works; add minimal player style
All checks were successful
Release / Test backend (push) Successful in 52s
Release / Check ui (push) Successful in 1m49s
Release / Docker (push) Successful in 6m29s
Release / Gitea Release (push) Successful in 35s

Float mode was broken because AudioPlayer was unmounted the moment
audioStore.active became true — exactly when the float overlay needs
to render. Fix: keep AudioPlayer mounted in float and minimal modes
regardless of audioStore.active; only standard mode shows the
'Controls below' message.

Adds a third 'minimal' style: a compact single-row bar (skip ±,
play/pause, seek, time) with no voice picker or chapter browser.
Voice picker and chapter button are hidden in the idle pill too.

Settings UI updated to show all three options with a live
description of what each style does.
This commit is contained in:
root
2026-04-11 16:00:46 +05:00
parent b0e23cb50a
commit 1f4d67dc77
2 changed files with 103 additions and 28 deletions

View File

@@ -52,7 +52,7 @@
type LineSpacing = 'compact' | 'normal' | 'relaxed';
type ReadWidth = 'narrow' | 'normal' | 'wide';
type ParaStyle = 'spaced' | 'indented';
type PlayerStyle = 'standard' | 'float';
type PlayerStyle = 'standard' | 'minimal' | 'float';
/** Controls how many lines fit on a page by adjusting the container height offset. */
type PageLines = 'less' | 'normal' | 'more';
@@ -596,27 +596,27 @@
</button>
{#if audioExpanded}
<div class="border border-t-0 border-(--color-border) rounded-b-lg overflow-hidden">
{#if audioStore.slug === data.book.slug && audioStore.chapter === data.chapter.number && audioStore.active}
<!-- Mini-player is already playing this chapter — don't duplicate controls -->
<div class="px-4 py-3 flex items-center gap-2 text-sm text-(--color-muted)">
<svg class="w-4 h-4 text-(--color-brand) shrink-0" fill="currentColor" viewBox="0 0 24 24">
<path d="M12 3v10.55A4 4 0 1014 17V7h4V3h-6z"/>
</svg>
<span>Controls are in the player bar below.</span>
</div>
{:else}
<AudioPlayer
slug={data.book.slug}
chapter={data.chapter.number}
chapterTitle={cleanTitle}
bookTitle={data.book.title}
cover={data.book.cover}
nextChapter={data.next}
chapters={data.chapters}
voices={data.voices}
playerStyle={layout.playerStyle}
wordCount={wordCount}
onProRequired={() => { audioProRequired = true; }}
{#if audioStore.slug === data.book.slug && audioStore.chapter === data.chapter.number && audioStore.active && layout.playerStyle === 'standard'}
<!-- Mini-player is already playing this chapter — don't duplicate controls (standard/minimal mode) -->
<div class="px-4 py-3 flex items-center gap-2 text-sm text-(--color-muted)">
<svg class="w-4 h-4 text-(--color-brand) shrink-0" fill="currentColor" viewBox="0 0 24 24">
<path d="M12 3v10.55A4 4 0 1014 17V7h4V3h-6z"/>
</svg>
<span>Controls are in the player bar below.</span>
</div>
{:else}
<AudioPlayer
slug={data.book.slug}
chapter={data.chapter.number}
chapterTitle={cleanTitle}
bookTitle={data.book.title}
cover={data.book.cover}
nextChapter={data.next}
chapters={data.chapters}
voices={data.voices}
playerStyle={layout.playerStyle}
wordCount={wordCount}
onProRequired={() => { audioProRequired = true; }}
/>
{/if}
</div>
@@ -1169,7 +1169,7 @@
<div class="flex items-center gap-3 px-3 py-2.5">
<span class="text-xs text-(--color-muted) w-14 shrink-0">Style</span>
<div class="flex gap-1.5 flex-1">
{#each ([['standard', 'Standard'], ['float', 'Float']] as const) as [s, lbl]}
{#each ([['standard', 'Standard'], ['minimal', 'Minimal'], ['float', 'Float']] as const) as [s, lbl]}
<button
type="button"
onclick={() => setLayout('playerStyle', s)}
@@ -1182,6 +1182,17 @@
{/each}
</div>
</div>
<div class="px-3 pb-2.5">
<p class="text-[11px] text-(--color-muted)/70 leading-snug">
{#if layout.playerStyle === 'standard'}
Full panel with voice picker and chapter browser.
{:else if layout.playerStyle === 'minimal'}
Compact bar: play/pause, seek, and time only.
{:else}
Draggable overlay — stays visible while you read.
{/if}
</p>
</div>
<!-- Speed -->
<div class="flex items-center gap-3 px-3 py-2.5">