- 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>
After a successful /api/voices fetch, results are written to sessionStorage
(key ttsvc_vc). On the next page load loadVoiceLibrary() reads the cache and
renders voices immediately before the network request completes, eliminating
the blank-list / skeleton flash entirely.
The background fetch always runs and overwrites the cache with fresh data.
Error handling is graceful: if the network fails but cache was served, the
error is suppressed (stale data stays visible). The Refresh button clears the
cache first to force a full reload cycle.
Silent-refresh logic (from v1.12.19) prevents the second loadVoiceLibrary()
call (engines.js batch E) from blanking already-rendered voices.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Root cause 1 (v1.12.18): `window.loadVoiceLibrary = () => loadVoiceLibrary()`
overwrites the global binding the arrow function references, causing immediate
RangeError: Maximum call stack size exceeded on every call. Changed to direct
assignment `window.loadVoiceLibrary = loadVoiceLibrary`.
Root cause 2 (v1.12.18): `loadSettings()` called `renderSettingsAbout()` which
lives in conversation.js (batch E), loaded after init.js. Guard added with
typeof check; nav.js already calls it safely when the About section opens.
Also (v1.12.18): s-library.html duplicated cl-book-filter / cl-search / cl-grid
from s-characters.html, breaking getElementById. Characters panel in Library
now redirects to s-characters instead of duplicating its DOM nodes.
Also (v1.12.19): engines.js triggers a second loadVoiceLibrary() after nav.js
already rendered voices, blanking the list briefly. Second call now silently
re-fetches without clearing the list when voices are already present.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
After a batch benchmark completes, successful entries are saved to each
voice's meta.json via POST /api/voice/meta. window.loadVoiceLibrary() is
then called so My Voices Factor + WPM columns update instantly without
a manual reload.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Voices table: removed Length, Duration, Time — only Factor and WPM remain
from benchmark data. Setup → Benchmark batch results now shows Duration,
Factor (sorted fastest-first, colour-coded), Time, and WPM. Factor replaces
Avg RTF with the same data flipped to a more intuitive direction (higher=better).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The old combined `21.8s · 1.30x` Speed cell is replaced by two separate
sortable columns:
- Factor (x.xx): audio÷render multiplier, green/amber/red colour-coded
- Time (Xs): total render time
Column order: Length · Duration · Factor · Time · WPM · Seed · dBFS · ...
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Previously the bench/wpm cells only updated after loadVoiceLibrary() at
the end of the full batch. Now each row is patched in-place immediately
after its voice finishes, for both batch and single-voice benchmarks.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
WPM (words per minute) is derived from the benchmark audio duration and
sentence word count, revealing how fast a voice speaks — independent of
GPU speed. 130–180 wpm is comfortable for audiobooks. Column is sortable.
Info (ⓘ) icons on Speed and WPM headers explain both metrics on hover.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Speed column now displays 1.32× instead of 1.3× for better precision.
Sorting by Speed ranks by the RTF multiplier (faster = higher ×) rather
than raw elapsed time, so the sentence length no longer skews the ranking.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Voices cloned from a Script Rehearsal now show the script title in the
SOURCE column without requiring manual entry. `_displaySource()` parses
the note field (`Rehearser · <title> · <character> — …`) and returns the
title segment as the source. Explicit `origin` values still take priority.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add Source input to voice inspector panel (saves to origin in meta.json)
- Auto-detect fish-audio voices via tag — SOURCE column now shows
"fish-audio" for tagged voices even without an explicit origin field
- Bulk "Set source" action in the multi-select toolbar
- Source cell in table reflects the live-displayed value and updates
immediately when changed via the inspector
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- voice-inspector: move `curGender` const before its first use — it was
declared at line 86 but referenced at line 61, causing a ReferenceError
(temporal dead zone) that silently aborted selectVoice() on every click
- Calc dB, Precompute, Batch Seeds now filter to checked voices when a
bulk selection is active, matching Benchmark's existing behaviour
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Added a new 'Table View' button to the library sort bar.
- Implemented a CSS grid layout to display voice properties in a high-density table.
- Added sortable column headers (Name, Lang, Gender, Speed, dBFS, Length, Rating, Source, Seed, Note, Tags, Active).
- Aligned CSS grid to account for the bulk edit checkbox injection.
- Added drag-and-drop profile image support to the Inspector's large avatar.
- Ensured picture updates instantly synchronize across the List, Table, and Inspector views.
Read Aloud (new "Vorlesen" tab):
- PDF (real page render + overlay highlight) / TXT reader with live word
highlighting, voice + speed, per-sentence synthesis-state colours, zoom
(fit-width/height, two-page, ±), resume, and a server-side book library
(syncs across devices; per-unit MP3 audio fetched on demand).
Book -> multi-speaker audiobook:
- "Cast as audiobook" attributes dialogue to characters via the LLM
(guillemet/quote-style aware, turn-taking, recent-context), with a
deterministic speech-tag fallback. Editable preview, non-blocking live
casting panel, then auto-saved as a reopenable Script Rehearser play.
- Audiobook export: synthesise every cast line -> one MP3 per chapter.
Character sheets:
- LLM-extracted, self-filling RPG-style sheets (with page+quote sources)
in both Read Aloud and the Rehearser.
Also: MP3 storage + per-page/sentence export, voice-library "Precompute
embeddings" pre-warm, German "Vorlesen" i18n + flag language toggle,
large-PDF performance (lazy raster, buffer/canvas eviction, yielded parse),
and the Seed Finder changelog entry.
New: routes/reader.py, POST /api/attribute-dialogue, POST /api/character-sheets,
static/js/{reader,audiobook,character-sheets}.js, static/sections/s-reader.html.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- A4 paper page (white, serif font, shadow) renders the full script at once
so you can read ahead while rehearsing
- Each dialog block shows: character avatar (voice library photo or initial
letter), character name in their colour, TTS/Me badge, and dialog text
- Active line gets a blue left-border highlight and auto-scrolls into view
- Transport bar (sticky, above the script):
- Cast strip: all character avatars at a glance
- ⏮ Prev / ▶ Play all / ⏹ Stop / ⏭ Next / 🔁 Repeat
- Progress bar + line counter
- Auto-play: TTS lines synthesize, play, auto-advance; 'me' lines pause
and slide up a sticky recording overlay at the bottom of the page
- Recording overlay shows the line to speak, oscilloscope + meter,
Record / Stop / Keep & continue / Re-record / Skip
- voice-library.js now exports window._voices so the rehearser can resolve
voice IDs to has_picture flags and fetch /api/voice/picture/{id}
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
## Script Rehearser (new feature)
- New section s-rehearser.html + rehearser.js + nav/loader wiring
- Phase 1: paste/upload script (.txt), auto-detect characters from
'CHARACTER: dialog' or ALL-CAPS screenplay format
- Phase 2: assign a TTS voice per character, or mark 'I play this'
- Phase 3: step-through rehearsal — synthesizes other characters via TTS,
shows level-meter + oscilloscope for your own lines, records them from mic
- Phase 4: session summary with per-line audio playback + download
## Connect Apps
- Removed duplicate standalone MCP/speak/hotkey full-width cards
- Kept the integration-grid cards (they use the real server URL from JS)
- Added Global Hotkey Daemon as a proper integration card with snippet-hotkey
populated by integrations.js (uses proxyBase URL dynamically)
## About page
- GET /api/changelog endpoint reads CHANGELOG.md and returns it as text
- Collapsible 'Changelog' <details> card fetches and displays it lazily
## Try It Out
- Reorganised into three cards: Voice & backend / Text to synthesize / Generate
- Backend help panel moved below the voice row (not in the same flex row)
- Style instruction field gains a dynamic badge ('style-aware ✓' / 'weak style')
and a yellow warning when a non-style-aware backend is selected while the
field is filled — wired to both backend-select change and input events
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>