From 8660c675b67056c795c19dfb3a0b92a59dde54a9 Mon Sep 17 00:00:00 2001 From: root Date: Sat, 11 Apr 2026 17:20:09 +0500 Subject: [PATCH] fix: suppress mini-bar for float/minimal player styles; persist float position --- ui/src/lib/audio.svelte.ts | 15 +++++++++ ui/src/lib/components/AudioPlayer.svelte | 11 +++---- ui/src/routes/+layout.svelte | 4 +-- .../books/[slug]/chapters/[n]/+page.svelte | 31 +++++++++++++++++-- 4 files changed, 51 insertions(+), 10 deletions(-) diff --git a/ui/src/lib/audio.svelte.ts b/ui/src/lib/audio.svelte.ts index fb77064..7568bd1 100644 --- a/ui/src/lib/audio.svelte.ts +++ b/ui/src/lib/audio.svelte.ts @@ -170,6 +170,21 @@ class AudioStore { return this.status === 'ready' || this.status === 'generating' || this.status === 'loading'; } + /** + * When true the persistent mini-bar in +layout.svelte is hidden. + * Set by the chapter reader page when playerStyle is 'float' or 'minimal' + * so the in-page player is the sole control surface. + * Cleared when leaving the chapter page (page destroy / onDestroy effect). + */ + suppressMiniBar = $state(false); + + /** + * Position of the draggable float overlay (bottom-right anchor offsets). + * Stored here (module singleton) so the position survives chapter navigation. + * x > 0 = moved left; y > 0 = moved up. + */ + floatPos = $state({ x: 0, y: 0 }); + /** True when the currently loaded track matches slug+chapter */ isCurrentChapter(slug: string, chapter: number): boolean { return this.slug === slug && this.chapter === chapter; diff --git a/ui/src/lib/components/AudioPlayer.svelte b/ui/src/lib/components/AudioPlayer.svelte index a4b7f38..6cc1a7a 100644 --- a/ui/src/lib/components/AudioPlayer.svelte +++ b/ui/src/lib/components/AudioPlayer.svelte @@ -945,19 +945,18 @@ } // ── Float player drag state ────────────────────────────────────────────── - /** Position of the floating overlay (bottom-right anchor by default). */ - let floatPos = $state({ x: 0, y: 0 }); + // floatPos lives on audioStore (singleton) so position survives chapter navigation. let floatDragging = $state(false); let floatDragStart = $state({ mx: 0, my: 0, ox: 0, oy: 0 }); function onFloatPointerDown(e: PointerEvent) { floatDragging = true; - floatDragStart = { mx: e.clientX, my: e.clientY, ox: floatPos.x, oy: floatPos.y }; + floatDragStart = { mx: e.clientX, my: e.clientY, ox: audioStore.floatPos.x, oy: audioStore.floatPos.y }; (e.currentTarget as HTMLElement).setPointerCapture(e.pointerId); } function onFloatPointerMove(e: PointerEvent) { if (!floatDragging) return; - floatPos = { + audioStore.floatPos = { x: floatDragStart.ox + (e.clientX - floatDragStart.mx), y: floatDragStart.oy + (e.clientY - floatDragStart.my) }; @@ -1459,8 +1458,8 @@
diff --git a/ui/src/routes/+layout.svelte b/ui/src/routes/+layout.svelte index cbd0136..2d8e656 100644 --- a/ui/src/routes/+layout.svelte +++ b/ui/src/routes/+layout.svelte @@ -512,7 +512,7 @@ style="display:none" > -
+
{#if navigating}
@@ -959,7 +959,7 @@
-{#if audioStore.active} +{#if audioStore.active && !audioStore.suppressMiniBar}
diff --git a/ui/src/routes/books/[slug]/chapters/[n]/+page.svelte b/ui/src/routes/books/[slug]/chapters/[n]/+page.svelte index 6dfa718..4051b61 100644 --- a/ui/src/routes/books/[slug]/chapters/[n]/+page.svelte +++ b/ui/src/routes/books/[slug]/chapters/[n]/+page.svelte @@ -100,6 +100,33 @@ document.documentElement.style.setProperty('--reading-max-width', READ_WIDTHS[layout.readWidth]); }); + // ── Suppress mini-bar for float / minimal player styles ────────────────────── + // The in-page player is the sole control surface for these styles; the layout + // mini-bar would be a duplicate. Clear on page destroy so the mini-bar returns + // on other pages (library, catalogue, etc.) where audio may still be playing. + $effect(() => { + audioStore.suppressMiniBar = layout.playerStyle === 'float' || layout.playerStyle === 'minimal'; + return () => { audioStore.suppressMiniBar = false; }; + }); + + // ── Persist float overlay position across reloads ───────────────────────────── + const FLOAT_POS_KEY = 'reader_float_pos_v1'; + if (browser) { + try { + const saved = localStorage.getItem(FLOAT_POS_KEY); + if (saved) { + const p = JSON.parse(saved) as { x: number; y: number }; + if (typeof p.x === 'number' && typeof p.y === 'number') audioStore.floatPos = p; + } + } catch { /* ignore */ } + } + $effect(() => { + const pos = audioStore.floatPos; + if (browser && (pos.x !== 0 || pos.y !== 0)) { + try { localStorage.setItem(FLOAT_POS_KEY, JSON.stringify(pos)); } catch { /* ignore */ } + } + }); + // ── Scroll progress bar ────────────────────────────────────────────────────── let scrollProgress = $state(0); @@ -797,7 +824,7 @@ {#if layout.readMode === 'scroll' && !layout.focusMode} {@const atTop = scrollProgress <= 0.01} {@const atBottom = scrollProgress >= 0.99} -