- Add a 6-stage pipeline stepper (Source -> Cast Audiobook -> Cast Characters -> Script Rehearser -> Generate MP3s -> Audiobook) with direct, non-destructive jumps between stages and a prominent guided-tour look - Split PDF import into an explicit "load" then "Extract Text" step, with in-browser OCR (Tesseract.js, vendored) to recover chapter headlines baked into a PDF as images instead of real text - Fix casting feed silently merging pages after leaving/returning: segments now carry their own page number instead of re-guessing it from text - Fix excessive "Unknown" speaker attribution: restore the attribution LLM's output token budget, which had been cut roughly in half and was truncating dialogue-dense passages - Fix Theater Play library cards failing to open (dead pre-migration IndexedDB API calls, missing section navigation) - Fix bulk "Set tag" wiping a voice's existing tags instead of adding to them - Start merging Casting's feed with Script Rehearser's Stage UI: collapsible character sidebar, shared "paper" page styling, inline text editing - Fix a performance regression from that merge (per-row listeners on every redraw) by moving to event delegation - Various layout/clutter fixes: hide reader chrome until a document is loaded, collapse secondary settings by default, fix overlapping toolbar icons, fix duplicate "opening" notifications Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
556 lines
38 KiB
HTML
556 lines
38 KiB
HTML
<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 & 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 Scene description goes here. ALICE Hello, how are you? BOB 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 & 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 :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-cast-row" id="reh-cast-row">
|
||
<button class="reh-cast-toggle" id="reh-cast-toggle" title="Show or hide the character list" aria-expanded="true">
|
||
<span class="mdi mdi-account-group"></span>
|
||
<span id="reh-cast-toggle-label">Characters</span>
|
||
<span class="mdi mdi-chevron-down reh-cast-chev"></span>
|
||
</button>
|
||
<div class="reh-cast-strip" id="reh-cast-strip"></div>
|
||
</div>
|
||
|
||
<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-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>
|
||
<label class="chunk-toggle-label" style="font-size:11px;gap:4px;white-space:nowrap" title="Skip scene headings & 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 & 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 & 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 & 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">
|
||
<!-- 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>
|
||
</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 & 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 -->
|