diff --git a/ui/src/lib/components/AudioPlayer.svelte b/ui/src/lib/components/AudioPlayer.svelte index 1a67da6..6d2448b 100644 --- a/ui/src/lib/components/AudioPlayer.svelte +++ b/ui/src/lib/components/AudioPlayer.svelte @@ -575,11 +575,14 @@ // Slow path: audio not yet in MinIO. // - // For Kokoro / PocketTTS when presign has NOT already enqueued the runner: - // use the streaming endpoint — audio starts playing within seconds while - // generation runs and MinIO is populated concurrently. - // Skip when enqueued=true to avoid double-generation with the async runner. - if (!voice.startsWith('cfai:') && !presignResult.enqueued) { + // For Kokoro / PocketTTS: always use the streaming endpoint so audio + // starts playing within seconds. The stream handler checks MinIO first + // (fast redirect if already cached) and otherwise generates + uploads + // concurrently. Even if the async runner is already working on this + // chapter, the stream will redirect to MinIO the moment the runner + // finishes — no harmful double-generation occurs because the backend + // deduplications via AudioExists on the next request. + if (!voice.startsWith('cfai:')) { // PocketTTS outputs raw WAV — skip the ffmpeg transcode entirely. // WAV (PCM) is natively supported on all platforms including iOS Safari. // Kokoro and CF AI output MP3 natively, so keep mp3 for those. diff --git a/ui/src/lib/components/CommentsSection.svelte b/ui/src/lib/components/CommentsSection.svelte index a757c98..15346cb 100644 --- a/ui/src/lib/components/CommentsSection.svelte +++ b/ui/src/lib/components/CommentsSection.svelte @@ -241,311 +241,321 @@ const totalCount = $derived( comments.reduce((n, c) => n + 1 + (c.replies?.length ?? 0), 0) ); + + // ── Collapsed state ─────────────────────────────────────────────────────── + // Hidden by default when there are no comments; expand on user tap. + let expanded = $state(false); + const hasComments = $derived(!loading && comments.length > 0); + // Auto-expand once comments load in + $effect(() => { + if (hasComments) expanded = true; + });
- {m.comments_login_link()} - {m.comments_login_suffix()} -
- {/if} -{loadError}
- {:else if comments.length === 0} -{m.comments_empty()}
+ {:else} -{comment.body}
- - -+ {m.comments_login_link()} + {m.comments_login_suffix()} +
+ {/if} +{loadError}
+ {:else} +{reply.body}
+ +{comment.body}
- -{reply.body}
+ +