{#snippet voiceRow(v: import('$lib/types').Voice)}
selectVoice(v.id)} onkeydown={(e) => e.key === 'Enter' && selectVoice(v.id)} >
{#if audioStore.voice === v.id} {/if}
{voiceLabel(v)} {v.id}
{/snippet} {#if (!audioStore.isCurrentChapter(slug, chapter) && !audioStore.active) || (audioStore.isCurrentChapter(slug, chapter) && (audioStore.status === 'idle' || audioStore.status === 'error')) }
{#if audioStore.isCurrentChapter(slug, chapter) && audioStore.status === 'error'}

{audioStore.errorMsg || 'Failed to load audio.'}

{/if}
{#if playerStyle !== 'minimal'}

{m.reader_play_narration()}

{#if voices.length > 0} {/if} {#if estimatedMinutes > 0} {#if voices.length > 0}·{/if} ~{estimatedMinutes} min {/if} {#if !audioStore.voice.startsWith('cfai:')} · {/if}
{/if} {#if chapters.length > 0 && playerStyle !== 'minimal'} {/if}
{#if showVoicePanel && voices.length > 0}
{m.reader_choose_voice()}
{#if kokoroVoices.length > 0}
Kokoro (GPU)
{#each kokoroVoices as v (v.id)}{@render voiceRow(v)}{/each} {/if} {#if pocketVoices.length > 0}
Pocket TTS (CPU)
{#each pocketVoices as v (v.id)}{@render voiceRow(v)}{/each} {/if} {#if cfaiVoices.length > 0}
Cloudflare AI
{#each cfaiVoices as v (v.id)}{@render voiceRow(v)}{/each} {/if}
{/if}
{:else} {#if !(playerStyle === 'float' && audioStore.isCurrentChapter(slug, chapter) && audioStore.active)} {#if playerStyle === 'minimal' && audioStore.isCurrentChapter(slug, chapter) && audioStore.active}
{ audioStore.seekRequest = parseFloat((e.target as HTMLInputElement).value); }} onchange={(e) => { audioStore.seekRequest = parseFloat((e.target as HTMLInputElement).value); }} class="flex-1 h-1.5 cursor-pointer" style="accent-color: var(--color-brand);" /> {formatTime(audioStore.currentTime)}/{formatDuration(audioStore.duration)}
{:else}
{#if audioStore.chapters.length > 0} {/if} {#if voices.length > 0} {/if}
{#if showVoicePanel && voices.length > 0}
{m.reader_choose_voice()}
{#if kokoroVoices.length > 0}
Kokoro (GPU)
{#each kokoroVoices as v (v.id)} {@render voiceRow(v)} {/each} {/if} {#if pocketVoices.length > 0}
Pocket TTS (CPU)
{#each pocketVoices as v (v.id)} {@render voiceRow(v)} {/each} {/if} {#if cfaiVoices.length > 0}
Cloudflare AI
{#each cfaiVoices as v (v.id)} {@render voiceRow(v)} {/each} {/if}
{/if} {#if audioStore.isCurrentChapter(slug, chapter)} {#if audioStore.status === 'loading'} {:else if audioStore.status === 'generating'}

{m.reader_generating_narration()}

{Math.round(audioStore.progress)}%

{:else if audioStore.status === 'ready'}
{#if audioStore.isPlaying} {m.reader_playing()} {:else} {m.reader_paused()} {/if} {formatTime(audioStore.currentTime)} / {formatDuration(audioStore.duration)}
{#if !audioStore.autoNext && nextChapter != null}
{#if audioStore.nextStatus === 'none'} {:else if audioStore.nextStatus === 'prefetching'} Warming up Ch. {nextChapter}… {:else if audioStore.nextStatus === 'prefetched'} Ch. {nextChapter} ready {:else if audioStore.nextStatus === 'failed'} Warm-up failed {/if}
{/if} {/if} {:else if audioStore.active}

{m.reader_now_playing({ title: audioStore.chapterTitle || `Ch.${audioStore.chapter}` })}

{/if}
{/if} {/if} {/if} {#if showChapterPanel && audioStore.chapters.length > 0} { showChapterPanel = false; }} /> {/if} {#if playerStyle === 'float' && audioStore.isCurrentChapter(slug, chapter) && audioStore.active}
{ floatDragging = false; try { (e.currentTarget as HTMLElement).releasePointerCapture(e.pointerId); } catch { /* ignore */ } }} > {#if audioStore.isPlaying} {/if}
{#if audioStore.status === 'generating' || audioStore.status === 'loading'} {:else if audioStore.isPlaying} {:else} {/if}
{#if audioStore.duration > 0} {@const r = 26} {@const circ = 2 * Math.PI * r} {@const dash = (audioStore.currentTime / audioStore.duration) * circ} {/if}
{/if}