tts-voice-creator-clone-and.../static/sections/s-rehearser.html
mARTin-B78 ea50267c30 Add unified Studio casting workflow and fix voice/casting pipeline bugs
Introduces the new Studio section (Source -> Characters -> Voices ->
Perform & Export) that reuses the existing Read Aloud/Library/Script
Rehearsal code via DOM reparenting instead of duplicating it, and rolls up
a long tail of bugs found while producing a real audiobook through it:
umlaut-eating name sanitizers, a voice picker that mispositioned itself and
capped results at 60, PDF pagination silently breaking on trimmed \f
markers, a race letting stale audio keep playing after a new line was
clicked, an alias-overlap bug that could silently redirect a voice/image
save onto the wrong character, voice design failing outright during brief
TTS backend restarts instead of retrying, sparse cast entries defaulting to
English/wrong gender, and a reassigned voice never reaching an already-open
Stage session or invalidating its cached audio. Also adds a persistent
per-line audio cache, audiobook export browsing/download, and an inline
voice-design prompt editor. Full details in CHANGELOG.md.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-26 02:03:56 +02:00

570 lines
39 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<div class="section-head">
<span class="section-icon"><span class="mdi mdi-theater"></span></span>
<div class="section-title">
<h2>Script Rehearser</h2>
<p>Upload a script, cast characters to TTS voices or your own mic, then rehearse scene by scene.</p>
</div>
</div>
<div class="wf-stepper" id="wf-stepper-rehearser" hidden></div>
<!-- Phase sub-tabs -->
<div class="reh-subtabs" id="reh-subtabs">
<button class="reh-subtab active" data-phase="1"><span class="mdi mdi-bookshelf"></span> <span>Library</span></button>
<button class="reh-subtab" data-phase="2" disabled><span class="mdi mdi-account-group-outline"></span> <span>Cast</span></button>
<button class="reh-subtab" data-phase="3" disabled><span class="mdi mdi-theater"></span> <span>Stage</span></button>
<button class="reh-subtab" data-phase="4" disabled><span class="mdi mdi-check-circle-outline"></span> <span>Summary</span></button>
</div>
<div class="tab-content" id="tab-rehearser">
<!-- ── Import / Export panel (hidden unless nav item clicked) ── -->
<div class="reh-phase" id="reh-impex-panel" hidden>
<div class="card">
<h2><span class="mdi mdi-swap-vertical"></span> Import / Export</h2>
<p class="card-subtitle">Import a script or saved rehearsal, or export the current session in any format.</p>
<div style="display:flex;align-items:center;gap:10px;margin-bottom:10px">
<h4 class="reh-impex-cat" style="margin:0"><span class="mdi mdi-web"></span> Import from URL</h4>
<button class="btn-secondary btn-sm" id="reh-browse-imsdb-btn-impex" title="Browse 1300+ movie scripts from IMSDb"><span class="mdi mdi-movie-open-outline"></span> Browse scripts</button>
</div>
<div class="reh-impex-url-row">
<span class="mdi mdi-web reh-impex-url-icon"></span>
<input id="reh-impex-url" type="text" class="reh-impex-url-inp"
placeholder="https://www.imsdb.com/scripts/… or https://www.scripts.com/script/the_pianist_72/2"
spellcheck="false" autocomplete="off">
<button class="btn-primary" id="reh-impex-url-btn" type="button">
<span class="mdi mdi-download"></span> Fetch
</button>
</div>
<p class="reh-impex-note" id="reh-impex-url-status"></p>
<h4 class="reh-impex-cat" style="margin-top:14px"><span class="mdi mdi-import"></span> Import from file</h4>
<div class="reh-impex-grid">
<label class="reh-impex-btn">
<span class="mdi mdi-text-box-outline"></span>
<span>.txt / .md</span>
<input type="file" accept=".txt,.md" class="reh-impex-file" data-impex-type="text" style="display:none">
</label>
<label class="reh-impex-btn">
<span class="mdi mdi-file-pdf-box"></span>
<span>.pdf</span>
<input type="file" accept=".pdf" class="reh-impex-file" data-impex-type="pdf" style="display:none">
</label>
<label class="reh-impex-btn">
<span class="mdi mdi-fountain-pen-tip"></span>
<span>.fountain</span>
<input type="file" accept=".fountain" class="reh-impex-file" data-impex-type="fountain" style="display:none">
</label>
<label class="reh-impex-btn">
<span class="mdi mdi-file-code-outline"></span>
<span>.fdx / .osf</span>
<input type="file" accept=".fdx,.osf" class="reh-impex-file" data-impex-type="fdx" style="display:none">
</label>
<label class="reh-impex-btn reh-impex-btn--accent">
<span class="mdi mdi-book-open-variant"></span>
<span>.reh (session)</span>
<input type="file" accept=".reh,.json" class="reh-impex-file" data-impex-type="reh" style="display:none">
</label>
</div>
<h4 class="reh-impex-cat" style="margin-top:20px"><span class="mdi mdi-export"></span> Export</h4>
<p class="reh-impex-note" id="reh-impex-export-note"></p>
<div class="reh-impex-grid">
<button class="reh-impex-btn" id="reh-impex-reh-btn">
<span class="mdi mdi-book-open-variant"></span>
<span>.reh (session)</span>
</button>
<button class="reh-impex-btn" id="reh-impex-fountain-btn">
<span class="mdi mdi-fountain-pen-tip"></span>
<span>.fountain</span>
</button>
<button class="reh-impex-btn" id="reh-impex-fdx-btn">
<span class="mdi mdi-file-code-outline"></span>
<span>.fdx</span>
</button>
<button class="reh-impex-btn" id="reh-impex-txt-btn">
<span class="mdi mdi-text-box-outline"></span>
<span>.txt</span>
</button>
<button class="reh-impex-btn" id="reh-impex-md-btn">
<span class="mdi mdi-language-markdown-outline"></span>
<span>.md</span>
</button>
<button class="reh-impex-btn" id="reh-impex-osf-btn">
<span class="mdi mdi-xml"></span>
<span>.osf</span>
</button>
</div>
<div class="btn-row" style="margin-top:18px">
<button class="btn-secondary" onclick="navRehearserPhase(1)"><span class="mdi mdi-arrow-left"></span> Back to Library</button>
</div>
</div>
</div>
<!-- ── Phase 1: Library + new script ─────────────────────────── -->
<div class="reh-phase" id="reh-phase-1">
<div class="card">
<div class="reh-lib-header">
<h2 style="margin:0"><span class="mdi mdi-bookshelf"></span> Rehearsal library</h2>
<div style="display:flex;gap:8px;align-items:center">
<button class="btn-secondary btn-sm" id="reh-browse-imsdb-btn" title="Browse 1300+ movie scripts from IMSDb"><span class="mdi mdi-movie-open-outline"></span> Browse scripts</button>
<button class="btn-secondary btn-sm" id="reh-lib-view-toggle" title="Toggle shelf / list view"><span class="mdi mdi-view-list"></span></button>
<label class="btn-secondary" style="cursor:pointer;display:inline-flex;align-items:center;gap:6px">
<span class="mdi mdi-import"></span> Import .reh
<input type="file" id="reh-lib-import-file" accept=".reh,.json" style="display:none">
</label>
</div>
</div>
<div id="reh-library-list" class="reh-bookshelf">
<div class="reh-lib-empty"><span class="mdi mdi-book-open-outline"></span><p>No saved rehearsals yet.<br>Parse a script below to start.</p></div>
</div>
</div>
<!-- Drag & drop importer -->
<div class="reh-dropzone" id="reh-dropzone">
<span class="mdi mdi-cloud-upload-outline reh-dropzone-icon"></span>
<strong>Drag &amp; drop a script here</strong>
<span class="note">.txt · .md · .fountain · .pdf · .fdx · .osf · .reh</span>
</div>
<div class="card">
<div class="reh-new-script-head">
<h2 style="margin:0"><span class="mdi mdi-script-text-outline"></span> New script</h2>
<input type="text" id="reh-script-title" class="reh-script-title-inline" placeholder="Script title…">
</div>
<p class="card-subtitle">Paste a script below. Supports standard screenplay format (INT./EXT. headings, CHARACTER name on own line, dialog follows) as well as the simple <code>CHARACTER: text</code> format. Lines starting with <code>#</code> are explicit stage directions.</p>
<div class="reh-input-row">
<textarea id="reh-script-text" placeholder="EXT. LOCATION - DAY&#10;&#10;Scene description goes here.&#10;&#10;ALICE&#10;Hello, how are you?&#10;&#10;BOB&#10;I'm doing great, thank you!" spellcheck="false"></textarea>
<div class="reh-input-side">
<label class="btn-secondary" style="cursor:pointer;display:inline-flex;align-items:center;gap:6px">
<span class="mdi mdi-upload"></span> .txt / .md
<input type="file" id="reh-file-input" accept=".txt,.md,.fountain" style="display:none">
</label>
<label class="btn-secondary" style="cursor:pointer;display:inline-flex;align-items:center;gap:6px">
<span class="mdi mdi-file-pdf-box"></span> .pdf
<input type="file" id="reh-pdf-input" accept=".pdf" style="display:none">
</label>
<label class="btn-secondary" style="cursor:pointer;display:inline-flex;align-items:center;gap:6px">
<span class="mdi mdi-file-code-outline"></span> .fdx / .osf
<input type="file" id="reh-fdx-input" accept=".fdx,.osf,.xml" style="display:none">
</label>
<p class="note" style="font-size:11px;margin-top:8px">Supports:<br>• Screenplay: INT./EXT., CAPS names<br>• Theatre: ACT I / SCENE 1<br><code>CHAR: text</code> · <code># direction</code><br>• Import: .txt .fountain .pdf .fdx .osf</p>
</div>
</div>
<div class="btn-row" style="margin-top:12px">
<button class="btn-primary" id="reh-parse-btn"><span class="mdi mdi-auto-fix"></span> Parse &amp; cast</button>
</div>
</div>
</div>
<!-- ── Phase 2: Cast ──────────────────────────────────────────── -->
<div class="reh-phase" id="reh-phase-2" hidden>
<div class="card">
<h2><span class="mdi mdi-account-group-outline"></span> Cast your characters</h2>
<p class="card-subtitle">Assign a TTS voice or your mic to each character. Add a speaking-style note per character so the voice matches their personality.</p>
<div class="engine-setup-row" style="margin-bottom:12px; padding:10px 12px">
<div class="engine-setup-col no-border" 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="reh-backend-select" aria-label="TTS backend"><option value="">Checking...</option></select>
<button class="btn-secondary" id="reh-fetch-voices-btn" title="Fetch voices"><span class="mdi mdi-refresh"></span></button>
</div>
</div>
</div>
<!-- Narrator is now a regular cast entry — see NARRATOR row in cast list -->
<!-- Auto-cast: match characters to voices (local / online) or design new ones -->
<div class="reh-autodesign" role="group" aria-labelledby="reh-autocast-title">
<div class="reh-autodesign-head">
<span class="mdi mdi-auto-fix" style="color:var(--accent)" aria-hidden="true"></span>
<strong id="reh-autocast-title">Match characters with voices</strong>
<span class="note">— let an LLM cast every character automatically</span>
</div>
<div class="engine-setup-row" style="margin-top:12px; margin-bottom:12px; padding:10px 12px; background:var(--bg); border:1px solid var(--border)">
<div class="engine-setup-col" style="flex:1">
<label class="engine-setup-label"><span class="mdi mdi-brain"></span> Language Model <a href="#" class="engine-setup-link" onclick="navTo('s-llms'); navEnginesCat('llm'); return false;" title="Configure Language Model settings"><span class="mdi mdi-cog-outline"></span></a></label>
<div class="engine-setup-controls">
<select id="reh-llm-url-sel" style="display:none" aria-hidden="true"></select>
<input type="text" id="reh-llm-url" placeholder="http://localhost:11434/v1" list="reh-llm-url-list" aria-label="LLM endpoint URL" title="OpenAI-compatible endpoint (Ollama, LM Studio, vLLM...)">
<datalist id="reh-llm-url-list"></datalist>
<button class="btn-secondary" id="reh-llm-refresh" title="List available models" aria-label="Refresh model list"><span class="mdi mdi-refresh"></span></button>
<select id="reh-llm-model" aria-label="LLM model"><option value="">— default —</option></select>
</div>
</div>
<div class="engine-setup-col no-border" style="flex:0.3">
<label class="engine-setup-label"><span class="mdi mdi-earth"></span> Language</label>
<div class="engine-setup-controls">
<select id="reh-design-lang" aria-label="Voice language">
<option value="English">English</option>
<option value="German">German</option>
<option value="Auto">Auto</option>
<option value="French">French</option>
<option value="Spanish">Spanish</option>
<option value="Italian">Italian</option>
</select>
</div>
</div>
</div>
<!-- Three casting options -->
<div class="reh-cast-options">
<div class="reh-cast-opt">
<div class="reh-co-num">1</div>
<div class="reh-co-body">
<strong><span class="mdi mdi-bookshelf" aria-hidden="true"></span> Match from local library</strong>
<p>The LLM picks the best voice you already have (cloned · designed · imported) for each character. <em>No TTS engine required.</em></p>
<button class="btn-secondary" id="reh-matchlib-btn" title="LLM picks the best existing library voice for each character">
<span class="mdi mdi-account-search-outline" aria-hidden="true"></span> Match local
</button>
</div>
</div>
<div class="reh-cast-opt">
<div class="reh-co-num">2</div>
<div class="reh-co-body">
<strong><span class="mdi mdi-earth" aria-hidden="true"></span> Match from online (fish.audio)</strong>
<p>Search fish.audio's 2M public voices for the best fit per character and import them to your library. <em>Needs internet.</em></p>
<button class="btn-secondary" id="reh-matchonline-btn" title="Search fish.audio for a matching voice per character and import it">
<span class="mdi mdi-cloud-search-outline" aria-hidden="true"></span> Match online
</button>
</div>
</div>
<div class="reh-cast-opt">
<div class="reh-co-num">3</div>
<div class="reh-co-body">
<strong><span class="mdi mdi-magic-staff" aria-hidden="true"></span> Design new voices</strong>
<p>Generate a unique voice per character from the LLM's description. <em>Requires the <b>Voice Design</b> engine (Qwen3-TTS&nbsp;:8021).</em></p>
<button class="btn-primary" id="reh-autodesign-btn" title="Design a brand-new voice for each character via the Voice Design engine">
<span class="mdi mdi-magic-staff" aria-hidden="true"></span> Design all
</button>
</div>
</div>
</div>
<div class="reh-autodesign-progress" id="reh-autodesign-progress" hidden role="progressbar" aria-label="Casting progress">
<div class="reh-synth-track"><div class="reh-synth-fill" id="reh-autodesign-fill"></div></div>
<span id="reh-autodesign-label" class="reh-synth-label">0 / 0</span>
<button class="btn-secondary btn-sm" id="reh-autodesign-cancel" style="margin-left:8px">Cancel</button>
</div>
</div>
<div class="reh-cast-toolbar">
<span class="reh-cast-count" id="reh-cast-count"></span>
<div class="reh-cast-controls">
<input type="search" id="reh-cast-search" class="reh-cast-ctl reh-cast-search" placeholder="Filter name or tag…" autocomplete="off">
<select id="reh-cast-filter-gender" class="reh-cast-ctl" title="Filter by gender">
<option value="">All genders</option>
<option value="F">♀ Female</option>
<option value="M">♂ Male</option>
<option value="N">⚥ Diverse</option>
</select>
<select id="reh-cast-filter-lang" class="reh-cast-ctl" title="Filter by language">
<option value="">All languages</option>
<option>English</option><option>German</option><option>French</option>
<option>Spanish</option><option>Italian</option><option>Portuguese</option>
<option>Dutch</option><option>Polish</option><option>Auto</option>
</select>
<select id="reh-cast-sort" class="reh-cast-ctl" title="Sort characters by">
<option value="name">Sort: Name</option>
<option value="gender">Sort: Gender</option>
<option value="lang">Sort: Language</option>
<option value="lines">Sort: Lines</option>
<option value="tag">Sort: Tag</option>
</select>
<button type="button" id="reh-cast-sort-dir" class="reh-cast-ctl reh-cast-sortdir" title="Toggle ascending / descending" data-dir="asc"><span class="mdi mdi-sort-ascending"></span></button>
</div>
<div class="reh-view-toggle" id="reh-cast-view-toggle" role="tablist" aria-label="Cast view">
<button type="button" class="reh-view-btn active" data-view="card" title="Card view"><span class="mdi mdi-card-account-details-outline"></span> Cards</button>
<button type="button" class="reh-view-btn" data-view="list" title="List view"><span class="mdi mdi-format-list-bulleted"></span> List</button>
</div>
</div>
<div id="reh-cast-list" class="reh-cast-list reh-cast-view-card"></div>
<div class="btn-row" style="margin-top:14px">
<button class="btn-secondary" id="reh-back-1-btn"><span class="mdi mdi-arrow-left"></span> Back</button>
<button class="btn-secondary" id="reh-cast-save-btn"><span class="mdi mdi-content-save-outline"></span> Save to library</button>
<button class="btn-primary" id="reh-start-btn"><span class="mdi mdi-theater"></span> Open rehearsal</button>
</div>
</div>
</div>
<!-- ── Phase 3: Rehearsal ─────────────────────────────────────── -->
<div class="reh-phase reh-stage-wrap" id="reh-phase-3" hidden style="position:relative">
<!-- Director's console -->
<div class="reh-console" id="reh-transport-bar">
<div class="reh-console-transport">
<button class="reh-tb-btn" id="reh-tb-prev" title="Previous line"><span class="mdi mdi-skip-previous"></span></button>
<button class="reh-tb-btn reh-tb-play" id="reh-tb-play" title="Play all"><span class="mdi mdi-play"></span></button>
<button class="reh-tb-btn" id="reh-tb-stop" title="Stop"><span class="mdi mdi-stop"></span></button>
<button class="reh-tb-btn" id="reh-tb-next" title="Next line"><span class="mdi mdi-skip-next"></span></button>
<button class="reh-tb-btn" id="reh-tb-repeat" title="Loop"><span class="mdi mdi-repeat"></span></button>
</div>
<div class="reh-console-progress">
<div class="reh-tb-progress-wrap"><div class="reh-tb-progress" id="reh-tb-progress"></div></div>
<span class="reh-tb-label" id="reh-tb-label">0 / 0</span>
<span id="reh-practice-info" class="reh-practice-info" hidden>
<span style="color:#10b981"></span><span id="reh-practice-from">1</span>
<span style="color:#ef4444;margin-left:3px"></span><span id="reh-practice-to">1</span>
<button class="reh-tb-btn" id="reh-practice-clear" style="width:18px;height:18px;font-size:9px" title="Clear practice range"></button>
</span>
</div>
<div class="reh-console-actions">
<button class="btn-secondary btn-sm" id="reh-tb-synth-all" title="Pre-synthesize all TTS lines for instant playback">
<span class="mdi mdi-lightning-bolt"></span> Synth all
</button>
<button class="btn-secondary btn-sm" id="reh-tb-audiobook" title="Synthesise every line and download the audiobook as MP3 (one file per chapter)">
<span class="mdi mdi-book-music-outline"></span> Audiobook
</button>
<button class="btn-secondary btn-sm" id="reh-tb-browse-exports" title="Browse and re-download audiobook chapters already exported for this book, without re-exporting">
<span class="mdi mdi-folder-music-outline"></span> Exports
</button>
<button class="btn-secondary btn-sm" id="reh-charsheets-btn" title="Extract actor-facing RPG-style character sheets (with sources) for this script">
<span class="mdi mdi-account-details-outline"></span> Character sheets
</button>
<button class="btn-secondary btn-sm" id="reh-tb-resynth-stale" title="Re-synthesize only lines whose tone changed" hidden>
<span class="mdi mdi-refresh" style="color:var(--red)"></span> Re-synth stale
</button>
<button class="btn-secondary btn-sm" id="reh-tb-clean-cache" title="Delete cached audio files on disk for lines that no longer match this script (edited/removed since they were synthesized)">
<span class="mdi mdi-broom"></span> Clean cache
</button>
<label class="chunk-toggle-label" style="font-size:11px;gap:4px;white-space:nowrap" title="Skip scene headings &amp; action descriptions during playback">
<input type="checkbox" id="reh-skip-desc-toggle" checked>
<span>Skip desc.</span>
</label>
<button class="btn-secondary btn-sm" id="reh-bulk-toggle" title="Bulk edit lines — select to ignore, hide or delete"><span class="mdi mdi-checkbox-multiple-marked-outline"></span> Select</button>
<button class="btn-secondary btn-sm" id="reh-edit-script-btn" title="Edit script &amp; title"><span class="mdi mdi-pencil"></span></button>
<button class="btn-secondary btn-sm" id="reh-tb-save" title="Save to library"><span class="mdi mdi-content-save-outline"></span> Save</button>
<button class="btn-secondary btn-sm" id="reh-tb-export" title="Export .reh"><span class="mdi mdi-export"></span></button>
<button class="btn-secondary btn-sm" id="reh-fountain-btn" title="Export as .fountain (plain text)"><span class="mdi mdi-file-document-outline"></span></button>
<span class="reh-fontsize-ctl" title="Adjust the play's text size">
<button class="btn-secondary btn-sm reh-fontsize-btn" id="reh-font-dec" title="Smaller text" aria-label="Decrease font size">A<span class="reh-fontsize-minus"></span></button>
<button class="btn-secondary btn-sm reh-fontsize-btn" id="reh-font-inc" title="Larger text" aria-label="Increase font size">A<span class="reh-fontsize-plus">+</span></button>
</span>
<button class="btn-secondary btn-sm" id="reh-page-mode-btn" title="Switch between A4 pages / scroll / PDF pages"><span class="mdi mdi-file-document-outline"></span> A4 pages</button>
<button class="btn-secondary btn-sm" id="reh-train-open-btn" title="Train mode — practice your lines with cue playback &amp; speech recognition"><span class="mdi mdi-school-outline"></span> Train</button>
<button class="btn-secondary btn-sm" id="reh-exit-btn">Exit</button>
</div>
</div>
<!-- Bulk-edit action bar (shown when "Select" mode is on) -->
<div class="reh-bulk-bar" id="reh-bulk-bar" hidden>
<span class="reh-bulk-count" id="reh-bulk-count">0 selected</span>
<span class="reh-bulk-hint">Click lines to select · Shift-click range</span>
<button class="btn-secondary btn-sm" id="reh-bulk-all" title="Select all visible lines"><span class="mdi mdi-select-all"></span> All</button>
<button class="btn-secondary btn-sm" id="reh-bulk-none" title="Clear selection">Clear</button>
<button class="btn-secondary btn-sm" id="reh-bulk-range" title="Set playback range to the selected lines"><span class="mdi mdi-playlist-play"></span> Set range</button>
<span class="reh-bulk-sep"></span>
<button class="btn-secondary btn-sm" id="reh-bulk-ignore" title="Grey out &amp; skip these lines during playback/synth"><span class="mdi mdi-eye-off-outline"></span> Ignore</button>
<button class="btn-secondary btn-sm" id="reh-bulk-unignore" title="Restore ignored lines"><span class="mdi mdi-eye-outline"></span> Un-ignore</button>
<button class="btn-secondary btn-sm" id="reh-bulk-hide" title="Hide these lines from the script view"><span class="mdi mdi-minus-circle-outline"></span> Hide</button>
<button class="btn-secondary btn-sm" id="reh-bulk-delete" title="Delete these lines permanently"><span class="mdi mdi-trash-can-outline" style="color:var(--red)"></span> Delete</button>
<span class="reh-bulk-sep"></span>
<label class="chunk-toggle-label" style="font-size:11px;gap:4px;white-space:nowrap" title="Reveal hidden lines so you can restore them">
<input type="checkbox" id="reh-bulk-show-hidden">
<span>Show hidden</span>
</label>
<span class="reh-bulk-spacer"></span>
<button class="btn-secondary btn-sm" id="reh-bulk-done" title="Leave bulk-edit mode"><span class="mdi mdi-check"></span> Done</button>
</div>
<!-- Tone / style warning (shown when backend doesn't support style instructions) -->
<div class="reh-tone-warn" id="reh-tone-warn" hidden>
<span class="mdi mdi-alert-outline"></span>
<span id="reh-tone-warn-txt">The current TTS backend has weak style support — tone selections may have little or no effect.</span>
<button class="reh-tone-warn-close" id="reh-tone-warn-close" title="Dismiss"></button>
</div>
<!-- Synthesis progress bar -->
<div class="reh-synth-bar" id="reh-synth-bar" hidden>
<span class="reh-imsdb-spinner" style="flex-shrink:0"></span>
<span class="reh-synth-bar-lbl"><span class="mdi mdi-lightning-bolt"></span> Synthesizing</span>
<div class="reh-synth-track"><div class="reh-synth-fill" id="reh-synth-fill"></div></div>
<span id="reh-synth-label" class="reh-synth-label">0 / 0</span>
<button class="btn-secondary btn-sm" id="reh-synth-cancel">Cancel</button>
</div>
<!-- Stage -->
<div class="reh-stage-area" id="reh-stage-area">
<!-- A4 script page -->
<div class="reh-page-wrap">
<div class="reh-a4-page" id="reh-a4-page">
<div class="reh-page-title" id="reh-page-title"></div>
<div class="reh-script-lines" id="reh-script-lines"></div>
</div>
</div>
<!-- Character sidebar — same look as Read Aloud's Casting sidebar,
plus search/sort and real portraits pulled from the Library. -->
<div class="ab-cv-side" id="reh-cast-row">
<div class="ab-cv-side-head">
<span class="ab-cv-side-title" id="reh-cast-toggle-label">Characters</span>
<button class="ab-cv-side-collapse" id="reh-cast-toggle" type="button" title="Collapse to avatars" aria-expanded="true"><span class="mdi mdi-chevron-left"></span></button>
</div>
<div class="ab-cv-side-tools">
<input type="search" class="ab-cv-side-search" id="reh-cast-side-search" placeholder="Search characters…" autocomplete="off">
<select class="ab-cv-side-sort" id="reh-cast-side-sort" title="Sort characters by">
<option value="lines">Lines</option>
<option value="name">Name</option>
</select>
</div>
<div class="ab-cv-chars" id="reh-cast-strip"></div>
</div>
</div>
<!-- TTS now-playing bar -->
<div class="reh-tts-status-bar" id="reh-tts-status-bar" hidden>
<span class="mdi mdi-volume-high" style="font-size:16px;color:var(--accent)"></span>
<span id="reh-tts-status-txt" style="flex:1;font-size:13px"></span>
<audio id="reh-tts-audio" controls style="max-width:240px;height:28px"></audio>
</div>
<!-- My-turn recording panel -->
<div class="reh-rec-overlay" id="reh-rec-overlay" hidden>
<div class="reh-rec-inner">
<div class="reh-rec-cue" id="reh-rec-cue"></div>
<div class="reh-rec-controls">
<div class="reh-rec-meter-col">
<div class="mic-monitor-head">
<span>Input level</span>
<span class="meter-readout" id="reh-db-readout">-∞ dB</span>
</div>
<div class="mic-meter" id="reh-mic-meter" aria-hidden="true"></div>
<canvas id="reh-live-wave" class="mic-live-wave" width="300" height="40" aria-hidden="true"></canvas>
</div>
<div class="reh-rec-btn-col">
<div class="btn-row" style="gap:8px;flex-wrap:wrap">
<button class="btn-red" id="reh-rec-start"><span class="mdi mdi-record-circle-outline"></span> Record</button>
<button class="btn-secondary" id="reh-rec-stop" disabled><span class="mdi mdi-stop-circle-outline"></span> Stop</button>
<span class="mic-timer" id="reh-rec-time">0:00</span>
<button class="btn-secondary" id="reh-skip-line">Skip</button>
</div>
<audio id="reh-rec-preview" controls style="display:none;width:100%;margin-top:8px"></audio>
<div class="btn-row" style="margin-top:8px" id="reh-rec-confirm-row" hidden>
<button class="btn-green" id="reh-rec-keep"><span class="mdi mdi-check"></span> Keep &amp; continue</button>
<button class="btn-secondary" id="reh-rec-redo"><span class="mdi mdi-refresh"></span> Re-record</button>
</div>
</div>
</div>
</div>
</div>
<!-- ── Train Mode panel ─────────────────────────────────────── -->
<div class="reh-train-panel" id="reh-train-panel" hidden>
<div class="reh-train-header">
<span class="reh-train-badge"><span class="mdi mdi-school-outline"></span> TRAIN</span>
<span id="reh-train-progress" class="reh-train-progress">Turn 1 / 1</span>
<div style="flex:1"></div>
<button class="btn-secondary btn-sm" id="reh-train-exit-btn"><span class="mdi mdi-close"></span> Exit train mode</button>
</div>
<!-- Cue card: what the other character says -->
<div class="reh-train-cue card" id="reh-train-cue">
<div class="reh-train-cue-head">
<span id="reh-train-cue-avatar" class="reh-train-cue-avatar"></span>
<span id="reh-train-cue-speaker" class="reh-train-cue-speaker"></span>
<span class="reh-train-cue-tag">Cue</span>
<span id="reh-train-cue-state" class="reh-train-cue-state"></span>
</div>
<div id="reh-train-cue-text" class="reh-train-cue-text"></div>
</div>
<!-- My line card -->
<div class="reh-train-myline card">
<div class="reh-train-myline-head">
<span class="mdi mdi-microphone" style="color:var(--accent)"></span>
<span id="reh-train-my-speaker" class="reh-train-cue-speaker"></span>
<span class="reh-train-cue-tag reh-train-me-tag">Your line</span>
</div>
<div id="reh-train-my-text" class="reh-train-my-text"></div>
</div>
<!-- Recording area -->
<div class="reh-train-rec card" id="reh-train-rec">
<div class="reh-train-rec-head">
<span id="reh-train-rec-state" class="reh-train-rec-state">Read the lines above · press ▶ to hear the cue, then 🎤 to record</span>
<span class="reh-train-rec-timer" id="reh-train-rec-timer" hidden>0:00</span>
</div>
<div class="reh-train-meters" id="reh-train-meters" hidden>
<div class="mic-monitor-head"><span>Input level</span><span class="meter-readout" id="reh-train-db">-∞ dB</span></div>
<div class="mic-meter" id="reh-train-meter"></div>
<canvas id="reh-train-wave" class="mic-live-wave" width="300" height="36"></canvas>
</div>
<audio id="reh-train-audio-preview" style="display:none;width:100%;margin-top:8px" controls></audio>
</div>
<!-- Comparison result -->
<div class="reh-train-result card" id="reh-train-result" hidden>
<div class="reh-train-result-head">
<span class="mdi mdi-compare-horizontal"></span> Result
<span id="reh-train-score" class="reh-train-score"></span>
</div>
<div id="reh-train-expected" class="reh-train-compare-row">
<span class="reh-train-compare-lbl">Script:</span>
<span id="reh-train-expected-text" class="reh-train-compare-text"></span>
</div>
<div id="reh-train-actual" class="reh-train-compare-row">
<span class="reh-train-compare-lbl">You said:</span>
<span id="reh-train-actual-text" class="reh-train-compare-text"></span>
</div>
</div>
<!-- Controls -->
<div class="reh-train-controls">
<button class="reh-tb-btn" id="reh-train-prev" title="Previous turn"><span class="mdi mdi-skip-previous"></span></button>
<button class="reh-tb-btn" id="reh-train-play" title="Play cue"><span class="mdi mdi-play"></span></button>
<button class="reh-tb-btn reh-train-rec-btn" id="reh-train-record" title="Start recording" disabled><span class="mdi mdi-microphone"></span></button>
<button class="reh-tb-btn" id="reh-train-stop-rec" title="Stop recording" disabled hidden><span class="mdi mdi-stop-circle-outline"></span></button>
<button class="reh-tb-btn" id="reh-train-next" title="Next turn"><span class="mdi mdi-skip-next"></span></button>
<button class="reh-tb-btn" id="reh-train-repeat" title="Repeat cue"><span class="mdi mdi-repeat"></span></button>
</div>
</div>
</div><!-- /phase-3 -->
<!-- ── Phase 4: Summary ───────────────────────────────────────── -->
<div class="reh-phase" id="reh-phase-4" hidden>
<div class="card">
<h2><span class="mdi mdi-check-circle-outline"></span> Session complete</h2>
<p class="card-subtitle">All recorded and synthesized clips from this session.</p>
<div id="reh-summary-list" class="reh-summary-list"></div>
<div class="btn-row" style="margin-top:14px;flex-wrap:wrap">
<button class="btn-primary" id="reh-save-session-btn"><span class="mdi mdi-content-save"></span> Save to library</button>
<button class="btn-secondary" id="reh-export-session-btn" title="Export full session with audio"><span class="mdi mdi-export"></span> .reh</button>
<button class="btn-secondary" id="reh-fountain-export-p4" title="Export script as .fountain text"><span class="mdi mdi-fountain-pen-tip"></span> .fountain</button>
<button class="btn-secondary" id="reh-fdx-export-btn" title="Export as Final Draft XML"><span class="mdi mdi-file-code-outline"></span> .fdx</button>
<button class="btn-secondary" id="reh-osf-export-btn" title="Export as Open Screenplay Format XML"><span class="mdi mdi-xml"></span> .osf</button>
<button class="btn-secondary" id="reh-resume-btn"><span class="mdi mdi-theater"></span> Resume</button>
<button class="btn-secondary" id="reh-new-session-btn"><span class="mdi mdi-refresh"></span> New script</button>
</div>
</div>
</div>
<!-- ── IMSDb browser modal (global, accessible from Library + Import/Export) ── -->
<div class="reh-imsdb-modal" id="reh-imsdb-modal" hidden>
<div class="reh-imsdb-box">
<div class="reh-imsdb-head">
<h3><span class="mdi mdi-movie-open-outline"></span> Script Library</h3>
<input type="text" id="reh-imsdb-search" class="reh-imsdb-search" placeholder="Search IMSDb titles…" spellcheck="false" autocomplete="off">
<span id="reh-imsdb-count" class="reh-imsdb-count"></span>
<a class="btn-secondary btn-sm reh-scripts-link" href="https://www.scripts.com" target="_blank" rel="noopener" title="Open Scripts.com"><span class="mdi mdi-open-in-new"></span> Scripts.com</a>
<div class="reh-imsdb-view-btns">
<button class="reh-imsdb-view-btn active" id="reh-imsdb-cover-btn" title="Cover view"><span class="mdi mdi-view-grid-outline"></span></button>
<button class="reh-imsdb-view-btn" id="reh-imsdb-list-btn" title="List view"><span class="mdi mdi-view-list-outline"></span></button>
</div>
<button class="btn-secondary btn-sm" id="reh-imsdb-close"><span class="mdi mdi-close"></span></button>
</div>
<div class="reh-imsdb-url-row">
<input type="text" id="reh-imsdb-url" class="reh-imsdb-url-input" placeholder="Paste IMSDb or Scripts.com URL…" spellcheck="false" autocomplete="off">
<button type="button" class="btn-primary btn-sm" id="reh-imsdb-url-fetch"><span class="mdi mdi-download"></span> Fetch URL</button>
</div>
<div class="reh-imsdb-grid" id="reh-imsdb-grid">
<div class="reh-imsdb-loading"><span class="reh-imsdb-spinner"></span> Loading catalogue…</div>
</div>
</div>
</div>
</div><!-- /tab-rehearser -->