diff --git a/ui/src/lib/components/ListeningMode.svelte b/ui/src/lib/components/ListeningMode.svelte index da30799..e9cb86c 100644 --- a/ui/src/lib/components/ListeningMode.svelte +++ b/ui/src/lib/components/ListeningMode.svelte @@ -7,9 +7,11 @@ interface Props { /** Called when the user closes the overlay. */ onclose: () => void; + /** When true, open the chapter picker immediately on mount. */ + openChapters?: boolean; } - let { onclose }: Props = $props(); + let { onclose, openChapters = false }: Props = $props(); // Voices come from the store (populated by AudioPlayer on mount/play) const voices = $derived(audioStore.voices); @@ -18,7 +20,8 @@ const cfaiVoices = $derived(voices.filter((v) => v.engine === 'cfai')); let showVoiceModal = $state(false); - let showChapterModal = $state(false); + // svelte-ignore state_referenced_locally + let showChapterModal = $state(openChapters && audioStore.chapters.length > 0); let voiceSearch = $state(''); let samplePlayingVoice = $state(null); let sampleAudio: HTMLAudioElement | null = null; @@ -419,19 +422,19 @@ {/if} - -
+ +
-
+
{#if audioStore.cover} {:else} -
+
@@ -444,165 +447,170 @@

{audioStore.bookTitle}

- -
- -
- {formatTime(audioStore.currentTime)} - {formatTime(audioStore.duration)} + +
+ + +
+ +
+ {formatTime(audioStore.currentTime)} + {formatTime(audioStore.duration)} +
-
- -
- - {#if audioStore.chapter > 1 && audioStore.slug} - - - - - - {:else} -
- {/if} - - - - - - - - - - - {#if audioStore.nextChapter !== null && audioStore.slug} - + + + + + + + + + + {#if audioStore.nextChapter !== null && audioStore.slug} + + + + + + {:else} +
+ {/if} +
+ + +
+ +
+ {#each SPEED_OPTIONS as s} + + {/each} +
+ + +
+ Auto + {#if audioStore.autoNext && audioStore.nextStatus === 'prefetching'} + + {:else if audioStore.autoNext && audioStore.nextStatus === 'prefetched'} + + {/if} + - -
- -
- {#each SPEED_OPTIONS as s} - - {/each} + +
- - - - -
diff --git a/ui/src/routes/+layout.svelte b/ui/src/routes/+layout.svelte index ee78740..304c00c 100644 --- a/ui/src/routes/+layout.svelte +++ b/ui/src/routes/+layout.svelte @@ -33,28 +33,13 @@ ]; // Chapter list drawer state for the mini-player - let chapterDrawerOpen = $state(false); - let activeChapterEl = $state(null); let listeningModeOpen = $state(false); + let listeningModeChapters = $state(false); // Build time formatted in the user's local timezone (populated on mount so // SSR and CSR don't produce a mismatch — SSR renders nothing, hydration fills it in). let buildTimeLocal = $state(''); - function setIfActive(node: HTMLElement, isActive: boolean) { - if (isActive) activeChapterEl = node; - return { - update(nowActive: boolean) { if (nowActive) activeChapterEl = node; }, - destroy() { if (activeChapterEl === node) activeChapterEl = null; } - }; - } - - $effect(() => { - if (chapterDrawerOpen && activeChapterEl) { - activeChapterEl.scrollIntoView({ block: 'center' }); - } - }); - $effect(() => { if (env.PUBLIC_BUILD_TIME && env.PUBLIC_BUILD_TIME !== 'unknown') { buildTimeLocal = new Date(env.PUBLIC_BUILD_TIME).toLocaleString(); @@ -792,52 +777,6 @@ {#if audioStore.active}
- - {#if chapterDrawerOpen && audioStore.chapters.length > 0} -
-
- -
- {m.player_chapters()} - -
- - -
-
- {/if} - {#if audioStore.status === 'generating' || audioStore.status === 'loading'}
@@ -864,10 +803,10 @@
- +
- - - {#if listeningModeOpen} - (listeningModeOpen = false)} /> - {/if} +{/if} + + +{#if listeningModeOpen} + { listeningModeOpen = false; listeningModeChapters = false; }} + openChapters={listeningModeChapters} + /> {/if}