Character sheet generation is already available from "Cast Characters" in the Casting flow, so the standalone button here was a duplicate entry point. Removed it and made "Cast as audiobook" the primary blue action, moved to the end of the toolbar as the clear next step in the pipeline. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
197 lines
14 KiB
HTML
197 lines
14 KiB
HTML
<div class="section-head">
|
||
<span class="section-icon"><span class="mdi mdi-book-open-page-variant-outline"></span></span>
|
||
<div class="section-title">
|
||
<h2>Read Aloud</h2>
|
||
<p>Import a PDF or text document, pick a voice and speed, then have it read to you while the word being spoken is highlighted.</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="wf-stepper" id="wf-stepper-reader" hidden></div>
|
||
|
||
<div class="tab-content" id="tab-reader">
|
||
|
||
<div id="reader-main-view">
|
||
<!-- ① Import + controls (collapsible) ───────────────────────────── -->
|
||
<div class="card reader-config-card" id="reader-config-card" data-collapse-default="closed" style="padding:14px">
|
||
<h2><span class="mdi mdi-tune-variant"></span> Voice & synthesis settings</h2>
|
||
<div class="engine-setup-row" style="margin-bottom:8px">
|
||
<div class="engine-setup-col" style="flex:1">
|
||
<label class="engine-setup-label"><span class="mdi mdi-account-voice"></span> Text to Speech <a href="#" class="engine-setup-link" onclick="navTo('s-llms'); navEnginesCat('tts'); return false;" title="Configure Text to Speech settings"><span class="mdi mdi-cog-outline"></span></a></label>
|
||
<div class="engine-setup-controls">
|
||
<select id="reader-backend-select" aria-label="TTS backend"><option value="">Checking...</option></select>
|
||
<button class="btn-secondary" id="reader-fetch-voices-btn" title="Fetch available voices"><span class="mdi mdi-refresh"></span></button>
|
||
<select id="reader-voice-select" aria-label="Voice"><option value="">— fetch voices —</option></select>
|
||
</div>
|
||
</div>
|
||
<div class="engine-setup-col no-border" style="flex:1">
|
||
<label class="engine-setup-label"><span class="mdi mdi-tune"></span> Synthesis Options</label>
|
||
<div class="engine-setup-controls" style="flex-wrap:nowrap">
|
||
<input type="text" id="reader-instruct" placeholder="Optional style / tone" autocomplete="off" style="width:140px">
|
||
<select id="reader-chunk-mode" title="Voice consistency" style="width:120px">
|
||
<option value="sentence">Per sentence</option>
|
||
<option value="paragraph">Per paragraph</option>
|
||
<option value="page">Per page</option>
|
||
</select>
|
||
<label class="reader-ctl reader-norm-toggle" title="Even out loudness" style="margin:0; font-size:11px">
|
||
<input type="checkbox" id="reader-normalize" checked> Norm
|
||
</label>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="reader-tuning-panel" aria-label="Read Aloud tuning">
|
||
<div class="reader-tuning-row">
|
||
<label for="reader-speed">Speed <span id="reader-speed-label" class="reader-speed-label">1.0×</span></label>
|
||
<input type="range" id="reader-speed" min="0.5" max="2" step="0.05" value="1" aria-label="Reading speed">
|
||
<span class="reader-tuning-help">Playback speed while reading.</span>
|
||
</div>
|
||
<div class="reader-tuning-row">
|
||
<label for="reader-seed">Seed</label>
|
||
<input type="number" id="reader-seed" placeholder="auto" autocomplete="off">
|
||
<span class="reader-tuning-help">Blank uses auto; a fixed value can make repeated synthesis more consistent.</span>
|
||
</div>
|
||
<div class="reader-tuning-row">
|
||
<label for="reader-temp">Temperature</label>
|
||
<input type="number" id="reader-temp" min="0" max="1.5" step="0.05" placeholder="default" autocomplete="off">
|
||
<span class="reader-tuning-help">Lower values, for example 0.3, keep cloned voices steadier.</span>
|
||
</div>
|
||
<div class="reader-tuning-row">
|
||
<label for="reader-tts-speed">Native speed</label>
|
||
<input type="number" id="reader-tts-speed" min="0.5" max="2" step="0.05" value="1" placeholder="1.0" autocomplete="off">
|
||
<span class="reader-tuning-help">Backend voice speed before Read Aloud playback control.</span>
|
||
</div>
|
||
<div class="reader-backend-hint" id="reader-backend-hint"></div>
|
||
</div>
|
||
|
||
<!-- ② Text paste + drag & drop / empty state ────────────────────── -->
|
||
<div class="reader-source-panel" id="reader-import-area">
|
||
<div class="reader-source-head">
|
||
<div>
|
||
<strong><span class="mdi mdi-file-document-plus-outline"></span> Source</strong>
|
||
<span>Paste text, drop a document, or import a file.</span>
|
||
</div>
|
||
</div>
|
||
<div class="reader-source-grid">
|
||
<div class="reader-paste-panel">
|
||
<div class="reader-source-method">
|
||
<span class="mdi mdi-text-box-outline"></span>
|
||
<div><strong>Paste text</strong><span>Plain text, Markdown, chapters, notes, or draft prose.</span></div>
|
||
</div>
|
||
<textarea id="reader-paste-text" class="reader-paste-text" rows="5" placeholder="Paste text here..." aria-label="Paste text to read aloud"></textarea>
|
||
<div class="reader-paste-actions">
|
||
<button class="btn-primary btn-sm" id="reader-paste-load" type="button"><span class="mdi mdi-text-box-check-outline"></span> Use pasted text</button>
|
||
</div>
|
||
</div>
|
||
<div class="reader-dropzone" id="reader-dropzone">
|
||
<span class="mdi mdi-cloud-upload-outline reader-dropzone-icon"></span>
|
||
<strong>Drop a document</strong>
|
||
<span class="note">PDF, TXT, or Markdown</span>
|
||
<label class="btn-secondary btn-sm reader-file-pick" title="Import a PDF or text file">
|
||
<span class="mdi mdi-file-upload-outline"></span> Import file
|
||
<input type="file" id="reader-file-input" accept=".pdf,.txt,.md" style="display:none">
|
||
</label>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ②b Extract Text — shown after a PDF's pages are loaded/visible but before
|
||
getTextContent()+OCR has run; this is where extraction options belong ── -->
|
||
<div class="reader-extract-banner" id="reader-extract-banner" hidden>
|
||
<span class="mdi mdi-text-recognition reader-extract-icon"></span>
|
||
<div class="reader-extract-info">
|
||
<strong>PDF loaded · <span id="reader-extract-pagecount">0</span> pages</strong>
|
||
<p>Extract the text to enable reading, search, and casting.</p>
|
||
</div>
|
||
<label class="reader-ctl" title="Recover chapter headings that are embedded as images in the PDF (rather than real text) using in-browser OCR. Slightly slows extraction." style="margin:0; font-size:11px">
|
||
<input type="checkbox" id="reader-ocr-headings" checked> OCR headings
|
||
</label>
|
||
<button class="btn-primary btn-sm" id="reader-extract-btn" type="button"><span class="mdi mdi-text-recognition"></span> Extract Text</button>
|
||
</div>
|
||
|
||
<!-- ③ Zoom toolbar + search + status legend (PDF) — above the document ───── -->
|
||
<div class="reader-toolbar" id="reader-toolbar" hidden>
|
||
<div class="reader-zoom" id="reader-zoom">
|
||
<button class="btn-secondary btn-sm" id="reader-zoom-fitw" title="Fit width"><span class="mdi mdi-arrow-expand-horizontal"></span> Fit width</button>
|
||
<button class="btn-secondary btn-sm" id="reader-zoom-fith" title="Fit height"><span class="mdi mdi-arrow-expand-vertical"></span> Fit height</button>
|
||
<button class="btn-secondary btn-sm" id="reader-zoom-two" title="Two pages side by side"><span class="mdi mdi-book-open-outline"></span> Two pages</button>
|
||
<span class="reader-zoom-sep"></span>
|
||
<button class="btn-secondary btn-sm" id="reader-zoom-out" title="Zoom out"><span class="mdi mdi-minus"></span></button>
|
||
<span class="reader-zoom-pct" id="reader-zoom-pct">100%</span>
|
||
<button class="btn-secondary btn-sm" id="reader-zoom-in" title="Zoom in"><span class="mdi mdi-plus"></span></button>
|
||
</div>
|
||
<div class="reader-search-wrap">
|
||
<span class="mdi mdi-magnify reader-search-icon"></span>
|
||
<input type="search" id="reader-search" class="reader-search-inp" placeholder="Search…" autocomplete="off" aria-label="Search document text">
|
||
<span class="reader-search-status" id="reader-search-status"></span>
|
||
<button class="reader-search-btn" id="reader-search-prev" type="button" title="Previous match" aria-label="Previous search match"><span class="mdi mdi-chevron-up"></span></button>
|
||
<button class="reader-search-btn" id="reader-search-next" type="button" title="Next match" aria-label="Next search match"><span class="mdi mdi-chevron-down"></span></button>
|
||
<button class="reader-search-btn" id="reader-search-clear" type="button" title="Clear search" aria-label="Clear search"><span class="mdi mdi-close"></span></button>
|
||
</div>
|
||
<div class="reader-legend">
|
||
<span class="reader-leg reader-leg-pending">Not synthesised</span>
|
||
<span class="reader-leg reader-leg-synth">Synthesising</span>
|
||
<span class="reader-leg reader-leg-ready">Ready</span>
|
||
<span class="reader-leg reader-leg-reading">Reading</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ④ Document viewport — central main content ───────────────────── -->
|
||
<div class="reader-doc" id="reader-doc"></div>
|
||
|
||
<!-- ⑤ Transport — below the document ─────────────────────────────── -->
|
||
<div class="reader-transport" id="reader-transport" hidden>
|
||
<div class="reader-tb-group">
|
||
<button class="reh-tb-btn" id="reader-prev" title="Previous sentence"><span class="mdi mdi-skip-previous"></span></button>
|
||
<button class="reh-tb-btn reh-tb-play" id="reader-play" title="Play / pause"><span class="mdi mdi-play"></span></button>
|
||
<button class="reh-tb-btn" id="reader-stop" title="Stop"><span class="mdi mdi-stop"></span></button>
|
||
<button class="reh-tb-btn" id="reader-next" title="Next sentence"><span class="mdi mdi-skip-next"></span></button>
|
||
</div>
|
||
<div class="reader-progress-wrap">
|
||
<div class="reader-progress-track"><div class="reader-progress-fill" id="reader-progress-fill"></div></div>
|
||
<span class="reader-progress-label" id="reader-progress-label">0 / 0</span>
|
||
</div>
|
||
<span class="reader-doc-title" id="reader-doc-title"></span>
|
||
</div>
|
||
|
||
<!-- ⑥ Synthesise-ahead bar — below the document ──────────────────── -->
|
||
<div class="reader-synthbar" id="reader-synthbar" hidden>
|
||
<button class="btn-primary btn-sm" id="reader-synth-all" title="Pre-synthesise the chosen range for gap-free reading">
|
||
<span class="mdi mdi-lightning-bolt"></span> Synthesise <span id="reader-synth-scope">all</span>
|
||
</button>
|
||
<span class="reader-page-range" id="reader-page-range" hidden>
|
||
<span class="note">pages</span>
|
||
<input type="number" id="reader-page-from" min="1" value="1" class="reader-page-inp" aria-label="From page">
|
||
<span class="note">–</span>
|
||
<input type="number" id="reader-page-to" min="1" value="1" class="reader-page-inp" aria-label="To page">
|
||
</span>
|
||
<button class="btn-secondary btn-sm" id="reader-select-toggle" title="Click sentences to mark a start and an end"><span class="mdi mdi-cursor-default-click-outline" id="reader-select-icon"></span> <span id="reader-select-label">Select range</span></button>
|
||
<span class="reader-sel-hint" id="reader-sel-hint" hidden></span>
|
||
<span class="reader-sel-info" id="reader-sel-info" hidden>
|
||
<span id="reader-sel-text">selection</span>
|
||
<button class="reader-sel-clear" id="reader-sel-clear" title="Clear selection">✕</button>
|
||
</span>
|
||
<button class="btn-secondary btn-sm" id="reader-save-lib" title="Save this document + its synthesised audio to your library"><span class="mdi mdi-content-save-outline"></span> Save to library</button>
|
||
<span class="reader-export">
|
||
<select id="reader-export-mode" class="reader-page-inp" style="width:auto" aria-label="Export granularity" title="Choose how to group the exported MP3 files">
|
||
<option value="page">1 MP3 / page</option>
|
||
<option value="sentence">1 MP3 / sentence</option>
|
||
</select>
|
||
<button class="btn-secondary btn-sm" id="reader-export-btn" title="Download the synthesised audio as MP3 (synthesises any missing sentences first)"><span class="mdi mdi-download"></span> Export MP3</button>
|
||
</span>
|
||
<button class="btn-primary btn-sm" id="reader-audiobook-btn" title="Let an LLM detect characters & dialogue in this scope, then open it in the Script Rehearser to cast a voice per character"><span class="mdi mdi-drama-masks"></span> Cast as audiobook</button>
|
||
<div class="reader-synth-prog" id="reader-synth-prog" hidden>
|
||
<div class="reader-synth-track"><div class="reader-synth-fill" id="reader-synth-fill"></div></div>
|
||
<span class="reader-synth-label" id="reader-synth-label">0 / 0</span>
|
||
<button class="btn-secondary btn-sm" id="reader-synth-cancel">Cancel</button>
|
||
</div>
|
||
</div>
|
||
</div><!-- /reader-main-view -->
|
||
|
||
<!-- ⑦ Audiobook Casting Panel ──────────────────────────────────── -->
|
||
<div id="reader-audiobook-panel" hidden></div>
|
||
|
||
<!-- Saved books now live in the combined Library → Books section. -->
|
||
|
||
</div><!-- /tab-reader -->
|