The Identify/Cast flyouts were hard-anchored to open above their trigger,
which suited the casting panel's bottom footer but clipped them off the top of
the screen in Studio, where the same toolbar sits near the top — the first two
entries were unreachable. They now pick the side with space, prefer opening
downwards, stay clamped inside the viewport and scroll if taller than it.
Also reorders the Identify menu to the sequence the passes should run in, with
the one-click Auto-repair cast first.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Fish-Speech clones from the app's own saved WAV library but was missing from
the set of backends populated with those voices, so the UI probed the Fish
server for a voice-listing endpoint it does not have, showed "Fetched 0
voices" and left the engine unusable despite being healthy. It now reports
the full library (218 voices).
Also adds a Fish-only panel listing all 49 documented emotion tags with
click-to-insert and a multi-emotion example, and corrects the backend's
advertised capabilities: it was flagged style_aware with "emotion markers are
honoured per request", but measurement shows a reference clip's in-context
prosody overwhelms inline tags (10x loudness spread across emotions without a
reference, 1.2x with one). It is now described by what it does well:
deterministic, byte-reproducible cloning.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Fish-Speech emotion tags were silently ignored on non-English books: per-line
emotions are LLM-generated in the book's own language, but Fish-Speech only
recognizes English [tag] markers, and a double-tagging bug was stacking a
broken server-derived tag on top of the client's own. Added a DE->EN
translation table and removed the double-tagging. Also wires the existing
book-profile context and race_species field into character portrait prompts
(previously only used for voice design), adds a recast-until-threshold loop
for casting, and adds backend-aware emotion quick-picks to Read Aloud, Try a
Voice, and Conversation.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
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>
Same pattern as the Casting audiobook Prompt button: a collapsible
textarea with the full extraction prompt (fields + sources/citation
schema), named-preset save/load/delete, and live persistence so edits
apply from the next passage onward. Server-side, _charsheets_prepare()
now accepts a character_sheets_prompt override (shared by the blocking
and streaming endpoints), following the same pattern already used for
the audiobook attribution prompt override.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Was a small 420px dialog with a collapsible one-line JSON preview and a
cramped 2-column chip grid. Now a big panel matching the audiobook
casting live view's shape: passage text (left) and live JSON output
(right) side by side, with a character sidebar on the right edge whose
just-updated entries are highlighted — so the user can watch each sheet
actually being built from its source passage instead of a tiny opaque
progress bar.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The Characters Found sidebar, the "Assign to" popup's character list,
and the "merge with an already-found character" list now show each
character's saved portrait when one exists (same source as the profile
detail panel), falling back to the existing colour-keyed initial-letter
dot otherwise.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Passage/Thinking boxes in the audiobook casting live view can now be
resized vertically. Also fixed the character-list collapse button's
chevron pointing opposite to the direction the panel edge actually moves.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The "LLM Reading…" split view showed the same passage text three times
(reader pane, Passage column, and a full-width block toggled by the
chevron that duplicated the Passage column). Removed the redundant
full-width block, repurposed the chevron to show/hide the two-column
view itself, and swapped the columns to Passage (left) / Thinking (right).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Reasoning-model chain-of-thought is now separated from the spoken/displayed
reply on the server (handles both a dedicated reasoning_content field and
inline <think> blocks, including chat templates that inject the opening tag
as a prompt prefix so it never appears in the stream). The client shows it
in a panel collapsed behind a "Thinking" chevron instead of dumping raw
reasoning text into the chat or speaking it aloud via TTS.
Also moved the noise-gate slider out of its own row and next to the mic
button, grouping it with Auto-stop/Hands-free/Live agent.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- App Routing's output-voice field gets the same searchable
avatar-thumbnail dropdown used elsewhere, as a browse button
alongside the existing free-text input (which must stay editable to
target vd_ Voice Design presets not in the voice library).
- My Voices table: Gender and Rating filter dropdowns existed in the
JS (populateLibraryFilters, libraryFilterMatch) but their <select>
elements had been dropped from the visible layout after an earlier
redesign, replaced with hidden dead placeholders just to keep the
code from erroring - and since populateLibraryFilters() early-returns
if any of the three elements are missing, this silently broke the
already-visible Language/Type dropdowns too. Restored the real
elements and removed the hidden scaffold; added new Tag and Group
dropdowns wired to the same filter state the sidebar chips use.
- Audio effects failing with "pedalboard is not installed" despite
requirements.txt listing it: the package WAS installed, but its
native extension (pedalboard_native) links against libatomic.so.1,
an OS-level shared library missing from the python:3.11-slim-bookworm
base image. Added libatomic1 to the Dockerfile and rebuilt - verified
`import pedalboard` now succeeds in the running container.
- Relabeled "edit ID"/"copy ID" to "rename filename"/"copy filename"
in the voice inspector - the feature already renamed the underlying
.wav/.meta.json/.reference.txt/picture files via the existing
/api/voice/rename endpoint, it just wasn't obvious "ID" meant
"filename."
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Character Sheets generation only ever showed a progress bar - no
visible reading/thinking/filling-out, unlike the casting flow which
already streams the LLM's output live. Refactored
/api/character-sheets into shared _charsheets_prepare/_charsheets_parse
helpers (same split used for attribution) and added
/api/character-sheets/stream, proxying the LLM's SSE stream through
the same shared lock used by the other attribution endpoints.
Client: new csGenerateStream (mirrors audiobookAttributeStream) tries
the streaming endpoint first per passage, updating a new "Live output"
panel in the progress dialog with the raw JSON answer as it's written
- itself the "watch it fill out the sheet" experience, since there's
no separate reasoning channel worth hiding it behind here. Falls back
to the blocking endpoint on any stream failure.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A leftover rule colored the icon accent-blue from when this button
was still .btn-secondary (accent icon on a white background). After
making it the primary blue button in the last release, that same blue
rendered blue-on-blue against the button's own background - invisible.
Confirmed via computed style before/after (blue -> white) rather than
guessing from the screenshot alone.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
csForReaderSelective passed the ENTIRE roster to csGenerate as the
target list, so the "N cast characters queued" progress dialog showed
every character regardless of what was actually checked in the picker
- only the final save step was correctly filtered, making the whole
run look like it ignored the selection. Now only the picked name(s)
go in as the target roster.
Also fixed the "Recast options" dropdown opening off-screen: its
anchor sits in the bottom action bar, so opening downward (the
default) routinely pushed it past the viewport edge. Opens upward
when there isn't enough room below, with more prominent styling.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The backend hint box sat beside the 4 tuning rows via grid-row: 1 /
span 4, forcing it to stretch to their combined height no matter how
short its own text was - usually leaving a large empty gap below a
1-3 line hint. Moved it full-width below the rows instead, sized to
its own content, and dropped the now-redundant two-column grid and
mobile-breakpoint overrides that existed only to manage it.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Merging two characters could block the main thread long enough to
trigger the browser's own "Page Unresponsive" dialog - the merge
itself is a fast array loop, but redrawing the whole feed afterward
(thousands of DOM rows, each running highlightText's regex pass) was
one long synchronous chunk. A plain spinner overlay can't fix that,
since it freezes right along with everything else in the same JS
turn. Split _abMergeCharacters into a fast relabel step plus a new
_abRedrawSegmentsChunked that rebuilds the feed across animation
frames, driving a real progress bar in the busy overlay instead of a
static "please wait".
Also added search + sort (line count / alphabetical) to the Casting
sidebar's character list, matching the Library's character list
controls.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The earlier table-view fix (display:table-row on <tr>) wasn't the
whole story: display:flex directly on a <td> (Stimme, Tags columns)
also broke its table-cell participation in Chromium, rendering that
cell stacked at the PREVIOUS column's x-position regardless of
table-layout mode - confirmed via direct DOM/rect inspection, not
guesswork. Moved flex layout to inner wrapper divs and switched to
table-layout:fixed with an explicit colgroup so column widths are
never re-negotiated by content again.
Also added actual character merging to the "also known as" alias
popup: picking an existing roster entry (e.g. "Schmied" from Darag's
popup, when the LLM split one person into two roster names) reassigns
every one of its segments to the character you opened the popup from,
with undo support - not just a linked library alias that left the
live cast still showing both as separate people.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Table view was rendering as stacked blocks instead of columns: rows
reused .lib-char-card for its event wiring, but that class's
display:flex;flex-direction:column turned every <tr> into a flex
column. Reset to display:table-row and stripped the leaked-in card
chrome.
Split /api/character-generate-prompts into four independent per-field
LLM calls (from two paired calls) and added a `fields` filter, so the
UI can offer one Generate button per prompt box instead of a single
button that always regenerated all four - cheaper, and further
shrinks each response to reduce truncation risk.
Added a Sort dropdown (Role/Alphabet/Lines/Gender/Voice assigned) to
the Characters/Cast list, persisted like the Cards/Table toggle.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
"Cast Characters" always blindly regenerated the whole cast from
scratch, even for a book already fully cast - no way to just look at
what's there or touch up a handful of characters without redoing
everyone. Once clGetAllByTagOrBook finds existing characters for this
book, the button becomes a split control: View Characters (jump to the
Library overview) plus a dropdown for Recast all or Recast selected...
(checkbox picker that re-scans the book but only saves updates for the
characters checked).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Replaces the horizontal avatar strip above the transport bar with a
sidebar next to the script page, reusing Read Aloud's Casting sidebar
classes (.ab-cv-side/.ab-char-item) directly instead of a separate
look. Same collapse-to-avatars control, now shows each character's
line count, and clicking a character scrolls the script to their
first line. Removed the CSS/HTML this replaces.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Table view: one row per character (avatar, name, sex, line count, voice
language, alignment, voice, tags, prompt-availability checks), toggled
next to the card grid and persisted.
Bulk voice auto-assign: checkbox per character + "Auto-assign selected"
per production, sequential so later picks see what earlier ones just
took (avoids duplicate voice assignments).
Character tags (auto-set to the book of origin) are now visible on
cards - the field always existed, cards just never rendered it, so a
character recurring across books had no visible link between records.
Detail fields (Backstory, Motivation, etc.) now show small numbered
links to their exact source citation when the sheet has one, instead
of making the reader search the full "Quellen im Text" list.
Added gender as an actual extracted character-sheet field - the UI
already had a gender icon but the LLM was never asked for the value.
Fixed SillyTavern/Concept Art prompts still coming back empty despite
the earlier token-budget increase: they're the last two fields in one
JSON object, so truncation always cost the same two regardless of the
ceiling. Split into two independent, concurrent LLM calls instead.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The streaming attribution endpoint decoded the LLM's SSE response with
requests' guessed encoding (Latin-1 fallback when no charset is declared),
mangling every German umlaut. Forced UTF-8 explicitly.
The "LLM Thinking" pane duplicated the passage text for models that
ignore the <think> instruction and stream straight into JSON - it now
only shows real reasoning when present, and otherwise labels raw output
honestly instead of passing it off as thinking.
Also fixed three UI bugs found while testing a live multi-hour cast:
- A-/A+ font buttons had no effect (a hardcoded font-size on .ab-cv-row
always overrode the CSS variable they set).
- Typing a name + Enter in the "Assign to" popup (and drag-to-assign,
which reuses it) silently did nothing after the first cast/recast run
in a session - the popup is a page-lifetime singleton but its input
handlers closed over the first run's now-stale assignName/closePopup.
Every popup open now repoints them at the current run.
- "Split text to Unknown Speaker" split at the wrong spot when the
selected phrase repeated earlier in the same paragraph (indexOf found
the first occurrence, not the dragged one). Now uses the exact DOM
range offset instead.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The LLM Reading card splits into thinking-stream (left) and passage
(right), fed by a new SSE endpoint that shares prompt-building and
parsing with the blocking one and falls back to it on any stream
failure (inactivity timeout, not overall). The deterministic resolver
no longer invents speakers from scenery nouns (PLATZ/GESICHTER/
KLEINIGKEIT) — person-noun whitelist plus a clause-subject pattern —
and gains the impersonal post-quote formula and strict two-person
alternation with colon/page/window guards. All reported failure cases
verified against the exact book sentences.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The saved casting prompt was the 2nd-quality verification prompt, so
first-pass attribution ran with the wrong job description and none of
the deduction rules — and the client-side prompt migration had no
anchor to upgrade. The server now appends the rules to any prompt
lacking them, and the saved prompt was reset to the default (backed up
to config/audiobook_prompt.backup.txt). Also: single-pass combined name
regex (a shorter alias could match inside a longer name's data-name
attribute and leak raw style="..." into the feed), stopword filter so a
comma-split alias like "Die, die den Vampir verließ" can't underline
every article, heading-like narration renders bold/centered, and A-/A+
font controls in the casting toolbar sharing the Rehearser Stage scale.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The per-word <span> wrapping behind "click any word to assign" created
~100k DOM nodes at book scale and froze the tab on every feed redraw;
replaced with native caretRangeFromPoint word detection plus a single
reused hover overlay — same UX, zero extra DOM. Export button gained a
2s re-entry guard (queued clicks during a freeze fired as a download
burst) and now delivers one zip: the cast script in Markdown plus a
sheet per character. Character detail view gains a Generation Prompts
section — four fold-out copy boxes (Voice Design, Character Image,
SillyTavern card, Concept Art sheet) filled by one LLM call over the
full profile via the new /api/character-generate-prompts endpoint.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Speed: OCR renders only the heading band (was full page at 2.5x),
Tesseract worker freed after extraction, name-underline index cached
instead of rebuilt per segment, constant regexes hoisted, old drafts
migrate segment page numbers once at load (single-path feed renderer).
Quality: global [hidden]{display:none!important} ends the empty-box bug
class; racy deferred cast-restore + _readerSuppressCastRestore flag
replaced by a synchronous, caller-wins restore; card collapse defaults
move to data-collapse-default markup; duplicated join/colour/alias/LLM-
target helpers now delegate to their canonical implementations; dead
reader state removed; stepper hide-guard fixed for the merged Source key.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- 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>
Voice selection list now shows flag + gender symbol on each row.
Batch results table adds Lang and Gender columns. All columns are
sortable by clicking the header (↑↓ indicator); defaults to Factor
descending. Sort logic handles strings (locale) and numbers uniformly.
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>
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>
- 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>
Click any gap divider to reveal the hidden segments between two Unknown
passages inline — shows speaker + text so context is clear before
making an assignment. Displays line count ("42 lines hidden — click
to expand") so users know what they are opening.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Pulsing blue dot on the Read Aloud nav item while casting is active
- Navigating away and returning restores the cast panel automatically
- Reassigning a segment's speaker decrements the old count so the
Characters Found panel stays in sync; speakers at 0 disappear
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Conversation: adjustable noise gate slider (RMS threshold + 300 ms
minimum burst duration) prevents short noise spikes from triggering
STT; level meter shows gate position as a blue marker
- Conversation stats panel now collapsible (chevron button) to free
chat width; floating expand button restores it; state persists
- Read Aloud: text search input in PDF toolbar (Enter = next hit,
Shift+Enter = previous, Esc = clear)
- Sidebar: tooltip now works for all item types including sub-items
that had no .nav-label span (text extracted by stripping icon/badge)
- Sidebar active section indicator added (.nav-tree-item.active was
previously unstyled — active section now has bg + right accent bar)
- Casting audiobook: prompt instructs LLM to handle ?« / !« endings
and unclosed » at passage end as dialogue; deterministic fallback
also handles unclosed opening quote
Bumps to v1.12.2.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Hovering the collapsed 56 px rail no longer expands the whole sidebar,
which was pushing the main content left and right. Each nav item now
shows a small floating tooltip (JS-positioned fixed div) next to its
icon on hover — layout stays completely stable.
Bumps to v1.12.1.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Sidebar collapses to a 56px icon rail; hovering flies the full menu out as
an overlay (icons + titles/nested items). Language picker moved to Settings.
- Unify settings collapsibles to the app's standard card-collapse style:
Conversation, Read Aloud (drag-&-drop now inside), Try It Out, Casting panel.
- Read Aloud: reordered (settings → toolbar → document → transport/synth) and
the document fits the viewport height so controls below stay visible; remove
the redundant My Books card (lives in Library → Books).
- Conversation: stacked full-width config, fills viewport height; fix
intermittent webm decode in hands-free mode (recorder restarts cleanly,
in-browser WAV encode); barge-in via Live agent.
- Fix casting feed overflow that pushed the sidebar off-screen.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Conversation Playground: STT/LLM/TTS/System prompt now full-width stacked.
- Fix conversation mic upload on ARM64: decode recording in-browser to
16 kHz mono WAV, bypassing server ffmpeg webm/EBML parser (with fallback).
- Document v1.10.0 (unified Library + character tags) and v1.10.1.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Group headers (Voice Actions, Speak, Setup) are now collapsible parents;
Tags nests under Library; Integrations + Settings nest under Setup.
Navigating a section auto-opens its whole ancestor tree chain.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Restructure the sidebar into Voices / Speak / Setup groups and fuse the
Read Aloud and Script Rehearser features into one workspace:
- New combined Library section (Books · Theater Plays · Characters/Cast)
with cross-links (Rehearse a book, Read Aloud a play).
- Character tags like voice tags: one record, many productions, reusable
across books/scripts; seeded with origin book, editable in the editor.
- Shared cast resolution: opening a production fills empty cast slots from
the character roster (matched by book OR tag); voice choices written back
on save. Productions joined by normalized title (prodKey).
- Tags nav group for voices (distinct tags + counts, Cloned/Designed/Fav).
- Fold standalone Characters into Library; Read Aloud now a single entry.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Auto-scroll to bottom is now gated on the user being within 80px of
the bottom. Scrolling up to review or edit a line freezes the feed
in place — new rows are still added, but the viewport doesn't move.
- Row-trimming is likewise suppressed while the user is scrolled up,
so old lines stay visible as long as they're being read/edited.
Trim limit raised from 80 → 600 rows so almost nothing is evicted.
- A floating "↓ Live" pill button appears at the bottom of the feed
whenever the user has scrolled up. Clicking it returns to the live
bottom and re-enables auto-scroll.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Casting view improvements:
- "LLM Reading…" row now shows a preview of the passage being processed.
A chevron button expands it to a full scrollable view of the passage
text (up to 260px), so you can follow what the LLM is reading live.
- Page-break dividers ("Page N") appear in the casting feed whenever the
source PDF page changes, giving a real-time view of page boundaries.
Bug fix:
- Page numbers in the Rehearser script were 0-indexed (PDF-internal) so
the first page break showed "Page 1" for what was PDF page 2, etc.
Now always emits 1-indexed page numbers (\f${page+1}).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Character sheets: stop sending the dead localhost:11434 default; fall back to
the server's configured llm_url so extraction uses the same working LLM.
- Audiobook casting: hoist highlightText to module scope so the "Review & cast"
manual-correction preview renders its segment rows again.
- Stage: unify narrator/dialog font size, add A-/A+ play text-size control
(scales A4 + paginated views), and make the cast chip list collapsible.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add a persistent, book-scoped Character Library (new Characters section,
IndexedDB) that auto-fills from Character-sheet analysis with editable cards.
Enrich extraction with six narrative fields (backstory, relationships,
motivation, fears, mannerisms, voice/speech) plus the greyscale Good↔Evil
alignment bar, arc arrow, and 5-area Deep Analysis. Add ⋯ separators between
non-contiguous passages in Recast unknown, and harden dialogue attribution
against hallucination with same-language emotion tags.
Co-Authored-By: Claude Opus 4.8 <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.
Adds a collapsible "🎲 Seed Finder" panel to the voice inspector pane.
Select a voice, expand the panel, set a seed range (default 1–15), and
click Run to generate audio samples with different seeds.
Each result row has Play and "Use this seed" buttons. "Use this seed"
saves the chosen seed to the faster-qwen3-tts voices.json via the new
/api/tts-voice-seed proxy endpoint (which calls /voice-seed on the TTS
server). The saved row gets a ✓ marker.
Test sentence auto-selects DE/EN mixed or EN-only based on voice name.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Storage: IndexedDB ('reh-library' DB, 'rehearsals' store).
Blobs (recorded mic audio) stored natively; no base64 overhead.
Library panel (Phase 1):
- List of saved rehearsals sorted by last update
- Each row: character-colour avatars, title, progress bar,
line count, recorded-clip count, date
- Active record highlighted in accent colour
- Open / Export .reh / Delete per-row buttons
- Import .reh button at the top
Transport bar (Phase 3):
- Save button (updates existing record if savedId set, else creates new)
- Export button (downloads current state as .reh JSON file)
Phase 4 (session complete):
- Save to library and Export .reh buttons
Data format (.reh file):
- JSON with version=1, title, script text, cast map, backend, lineIndex
- clips serialized with audio as base64 strings (mime + b64 fields)
- clipsFromJson restores Blob objects on import
Behaviour:
- Parsing a brand-new script resets savedId → safe to save as new record
- loadRecord() restores script, cast, lineIndex, clips and jumps to Phase 2
- renderLibraryList() called on init, after save, after delete, after import
Co-Authored-By: Claude Sonnet 4.6 <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>
1. Sample text: expose initCloneSampleText as a window function and call
it from nav.js runSideEffects when the clone section is activated,
ensuring the textarea is always populated even if the IIFE ran before
the element existed.
2. Better sample texts: all 8 languages rewritten to ~38 words / ~15 s,
first-person, phonetically rich, proper Unicode diacritics.
3. Live mic monitor: a level-meter (18-bar) + scrolling oscilloscope
canvas (ring-buffer, 300 px, colour-coded) added to the microphone
card. "Check level" / "Stop monitor" buttons start/stop it
independently; clicking Record starts it automatically.
Uses raw mic constraints (no echo-cancel / AGC) for cleaner voice clone
audio. Mic gain slider and dB readout included.
4. Recording quality: MediaRecorder now requests audioBitsPerSecond:256000
in both voice-clone.js and stt.js.
5. STT engine picker: Recognition engine <select> + Refresh button added
above the Auto-transcribe button in Step 3. refreshSttBackends() now
syncs both stt-tts-stt-backend and clone-stt-backend. The transcribe
call passes the chosen backend to /api/transcribe.
6. File input: explicit extension list added to accept= for OGG/OPUS.
7. CSS: .mic-live-wave style added (dark/light theme variants).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Conversation playground:
- Live speech preview: MediaRecorder sends accumulated audio to
/api/transcribe-bytes every 2.5 s; interim Whisper result shown in
the text input field while recording. Web Speech API tried first as
a faster path when available (HTTPS/localhost).
- VAD auto-stop: AudioContext AnalyserNode measures RMS every frame;
auto-stops after 1.5 s silence with a visible countdown. Auto-stop
toggle to revert to click-to-stop.
- Hands-free mode: mic auto-restarts after the agent finishes speaking
via audio.ended event + generation-counter cancellation. Hands-free
toggle (on by default) to disable.
Navigation:
- Persist active section and sub-page in localStorage; hard-reload
returns to the same page instead of always jumping to My Voices.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Move mic-blocked warning into conv-chat-window (prepend) so it scrolls
with the chat instead of pushing the input bar off-screen
- Remove min-height:400px from conv-chat-window; give conv-chat-panel a
viewport-relative height so the input bar is always visible at the bottom
- loader.js: append ?v=<timestamp> to section fetches to bust browser cache
(was serving stale s-conversation.html after updates)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Frontend:
- Add pill-shaped text input + send button (→) to the left of the mic button
- Enter key or → click sends text directly without recording audio
- Input is disabled while a turn is processing; cleared on submit
- Welcome message updated to mention both input methods
- New CSS: .conv-input-bar, .conv-text-row, .conv-text-inp, .conv-send-btn,
.conv-divider (visual separator between text and mic sections)
Backend:
- /api/conversation/turn: audio is now optional (UploadFile | None)
- New text form field — when provided, STT step is skipped and text is
used as the transcript directly; SSE emits transcript event with stt_ms=null
- Raises 400 if neither audio nor text is supplied
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Engine cards (all TTS + STT, dynamic Docker stack + static):
- Container name field now always visible on every card (was hidden for
absent/not-installed containers)
- Connect button moved out of URL row into a dedicated dc-controls-row;
restyled as solid blue primary CTA
- Connect now toggles to Disconnect (green) when reachable, persists state
- On successful connect, URL is auto-applied to Settings so the backend
appears in TTS/STT dropdowns immediately — no separate "Use as" click needed
- "Use as TTS/STT" button updated: larger, teal "apply" style, tooltip,
gains .active highlight once applied, right-aligned as the trailing action
- Unified control order on every card: [Connect/Disconnect] [Stop|Start|Restart] [Use as →]
- initStaticDockerManagement rebuilt to use the same dc-controls-row layout
as dynamic cards, moving .llm-local-ping from URL row at init time
Conversation section:
- Fix chrome://flags URL being white-on-grey inside the mic-blocked warning
box (global code{background:var(--panel)} overrode the inherited white text)
- Inline-styled the <code> element with a dark translucent background
- Added a Copy button so users can copy the URL without selecting invisible text
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- STT section now has a Quick test panel: select backend, hit mic button,
see transcript. Records via MediaRecorder, posts to /api/transcribe-bytes.
- _transcribe_audio detects the whisperx-gpu 'NoneType/to' error (caused by
pyannote/speaker-diarization-3.1 requiring a HuggingFace token) and
replaces it with an actionable message explaining how to fix it.
- _to_wav_16k added for STT audio conversion (Whisper/wav2vec2 expect 16kHz).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Conversation Playground (new section):
- WhatsApp-style chat UI with user/assistant speech bubbles
- Click-to-record mic button using MediaRecorder API
- STT → LLM streaming → TTS pipeline via SSE (POST /api/conversation/turn)
- LLM tokens stream into assistant bubble in real time
- Audio auto-plays when TTS synthesises the reply
- Right-side stats panel: STT / LLM TTFT / LLM total / TTS / Total with bar chart
- Turn history list with per-turn total time and pass/fail indicator
- Configurable: STT backend, LLM URL + model, TTS backend + voice, system prompt
- Conversation history maintained across turns (last 20 messages sent to LLM)
- GET /api/conversation/llm-models proxies model list from any OpenAI-compatible LLM
XTTS v2 backend:
- Registers xtts as a first-class TTS backend (xtts_url setting, display name,
capabilities, health/voice discovery, OpenAI-compatible generation)
- Added XTTS URL field to Settings → Connections
- Use-as-TTS button now saves to xtts_url (not tts_url)
- Batch benchmark backend select now refreshes alongside perf/preview selectors
VibeVoice fix:
- Added /voices to _TTS_VOICE_ENDPOINTS so VibeVoice voices are discovered
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add URL input row to all docker container cards (defaulting to host.docker.internal:{port}), with Connect button, localStorage persistence, and green card border when reachable
- Map each docker container to its settings key (tts_url, nvidia_tts_url, nvidia_asr_url, tts_stream_url) via DC_USE_MAP; add "Use as TTS/STT" button that applies the URL to settings
- Add "+ Add Custom" button to LLM / STT / TTS sub-page headers, opening a dialog with Name, Role, URL, optional Docker container name, and Description fields
- Custom cards are saved to localStorage, rendered in dc-grid-llm/stt/tts alongside docker cards, have URL input + Use-as + Remove buttons; container name field enables Stop/Start/Restart
- Expose applyAndSaveSettings and probeUrl as window globals so docker card bindings can call them after renderLocalContainers
- Add dialog CSS and .dc-delete-btn red variant
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Rename "LLMs" section to "Engines" with brain icon
- Add three sub-pages (Language Models / Speech to Text / Text to Speech) following the same nav-tree pattern as Settings and My Voices
- Rewrite s-llms.html: three s-engines-page divs, docker container grids (dc-grid-tts, dc-grid-stt), VibeVoice card in TTS section, static cloud API cards per category
- Add navEnginesCat() and applyEnginesPage() to nav.js; engines tree open/close in showSection()
- Remove obsolete initLlmCatTabs IIFE; fix dc-refresh-btn from ID to class-based querySelectorAll
- Make integration cards in Connect Apps collapsible (collapsed by default) with favicon/icon prepended to h3
- Unify all Engines card grids to minmax(380px, 1fr) so local, docker, and cloud cards are the same width
- Add s-engines-page CSS (display:none / is-active:flex)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Batch card pre-populates from active My Voices (checked by default) with
an option to reload from the backend. Select all / deselect all buttons.
Runs each selected voice N times sequentially with a live progress bar
and stop button. Results table updates after every voice and sorts by
avg RTF fastest-first; each row shows a trend badge (faster/slower/stable)
vs the previous session for that voice. All runs are saved to History.
renderPerfHistory hoisted to module level so both single-voice and batch
IIFEs can refresh the History card after saving new entries.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- New s-performance section: dedicated Benchmark nav entry with run form,
per-session results table, RTF trend badge (faster/slower/stable), SVG
sparkline chart, and a History card backed by localStorage (last 50 sessions)
- Performance tab removed from Try It Out; element IDs unchanged so JS works
- renderIntegrationSnippets: adds Python MCP server + Claude Code .mcp.json
config snippets to the Connect Apps page (integration-card-wide styling)
- Save handler: persists all Captures settings fields (stt_language,
stt_preferred_backend, auto_refine, refine_model, refine_* toggles,
captures_default_voice) alongside existing settings
- CSS: integration-card-wide accent border, benchmark history rows, trend
badges, sparkline wrapper, bench-history-toolbar
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Each settings nav item (General, Connections, Playback, Payloads, Storage,
API Keys, Backup, Logs, About) now shows its own isolated card page.
navSettingsCat() toggles .s-settings-page visibility + active state.
Save/Reload use event delegation (.s-save-btn/.s-reload-btn classes).
Default sub-page is Connections on first entry.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
New TTS backend: Kokoro FastAPI (82M) — OpenAI-compatible, 11 built-in
voices, only shows when server is reachable (~300 MB CPU, ~0.1× RTF).
New STT backends in Transcribe dropdown: faster-whisper (CTranslate2 GPU,
~70× RT, 1.5 GB VRAM), whisper.cpp (CPU/CUDA, ~8–15× RT, ~1 GB RAM),
Groq Whisper (fastest cloud, free 2 000 req/day, key shared with Groq LLM).
Backend help panels now show ⚡ speed · ⏰ latency · ⭐ quality · 💾 RAM
metric chips for all TTS and STT backends.
Active Docker Stack cards also get per-container metric chips.
AI Backends section: "Use as STT" / "Use as TTS" one-click buttons on
faster-whisper, whisper.cpp, and Kokoro cards apply URLs to Settings
without leaving the page. Groq Whisper card notes the shared key path.
Settings: Kokoro URL in TTS cluster; faster-whisper URL, whisper.cpp URL,
Groq API key in STT cluster; quick-fill buttons for all local STT engines.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add initLlmsSection() IIFE to app.js: copy buttons, API key persistence with eye toggle
and saved badge, local service URL persistence, Connect/Disconnect toggle with server-side
probe via /api/probe-url (avoids CORS), card turns green on success / red on failure
- Substitute 0.0.0.0 → host.docker.internal before probing (0.0.0.0 not routable from Docker)
- Add /api/local-containers, /api/probe-url, start/stop/restart endpoints to server.py
- Rewrite AI Backends section into Local / Online API categories with Docker stack grid,
local service cards (LLM/STT/TTS) with icons and editable URL inputs, online cloud API cards
- Add bind mounts for static/ and server.py so changes take effect without image rebuild
- Add dc-grid, llm-local-grid CSS with uniform minmax(310px,1fr) card layout
- Fix VOICE_HOST_DIR default via .env so voice folders survive container recreation
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- All .card > h2 cards get a rotating chevron toggle; clicking the h2
collapses/expands all content below it
- Existing <details> elements inside cards are synced to the same
localStorage store so their state also persists across reloads
- Default state: expanded (open); first collapse saves false, reload restores it
- Storage key: 'card-collapse-v1' → per-card slug of heading text
- CSS: chevron box matches opt-group style; <details> summary styled consistently
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- /api/browse-dirs endpoint lists subdirectories at any container path
- '📁 Browse' button next to the path input toggles an inline dir browser
- Breadcrumb navigation lets users click up/down through the filesystem
- 'Use this folder' confirms the selection back into the path input
- Existing 'Set & reload' flow unchanged
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Inspector opt-groups: Save crop, Save text, Save style variation, Save volume
each moved into an .opt-group-footer div (flex, justify-content: flex-end).
Get Voices scraper cards: Import this voice moved into .vs-import-footer
(full-width row, right-aligned) below the pill/link row.
ElevenLabs cards: ↓ Clone moved into .el-vc-footer (full-width, right-aligned)
with accent fill; ▶ Play stays inline with the body.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Backend proxy at /api/elevenlabs/voices forwarding to ElevenLabs shared-voices API
- elevenlabs_api_key added to settings (free key unlocks 12 000+ voices; 3 without key)
- Category pills: All, Featured, Professional, Narration, Conversational, News, Characters, Meditation, Gaming, Training
- Language filter with most common European languages + Arabic/Hindi/ZH/JA/KO
- Gender and age filters, debounced search, pagination (prev/next)
- Voice cards: colored avatar, name, language/gender/age/use-case tags, description, ▶ Play and ↓ Clone buttons
- Play button streams preview MP3 directly; Clone imports the audio into the Clone a Voice flow
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Preview input changed to a resizable textarea (resize: vertical)
- Flag button before textarea opens searchable language picker (23 languages)
- Selecting a language fills textarea with a pre-translated benchmark sentence
- VL_SAMPLE_TEXTS map covers EN/DE/FR/ES/PT/IT/NL/PL/SV/DA/NB/FI/HU/CS/RO/UK/RU/TR/AR/HI/ZH/JA/KO
- setPreviewLang / openPreviewLangPicker / syncPreviewLangBtn wired up globally
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Tags:
- Comma-separated input (tag1, tag2, tag3) with placeholder updated
- Custom autocomplete dropdown on last token, sources from _voices DB + localStorage
- All comma-separated tokens saved individually to localStorage reuse list
Inspector opt-groups:
- Each group wrapped in opt-group-body for clean collapse
- Chevron (▾/▸) in title, click title to toggle open/closed
- Transcript and Loudness open by default; others collapsed
- Loudness title preserves dBFS meta span + chevron
Voice list pane reorder:
- vl-synth-panel moved above vl-filters
- vl-filters now sits directly above voice-list
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Inspector:
- Skinny 2-row header: 72px avatar + name/ID row / subtitle row / note row
- Searchable flag picker (dblclick flag icon) — filtered by voice language, falls back to ALL_FLAGS
- Searchable language picker (dblclick lang code) — shows full language names
- Tag reuse: entered tags persist to localStorage as datalist suggestions
- Compact active toggle (32×18px), slim save button (12px/4px padding)
- Show/hide eye toggle and "✓ Key saved" badge on API key fields
AI Backends (s-llms.html + style.css):
- Full CSS design: pill tabs with active accent, animated section transitions
- Service cards: icon bubbles, tier badges (Free/Demo/Paid), stat chips, endpoint rows, model tags
- Highlighted recommended card with accent border
- Dark code blocks for local service snippets with copy feedback
- Show/hide password toggle and auto-appearing "✓ Key saved" badge per card
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- New light UI: fixed 220px sidebar, single scrolling page, 8 named sections
- Static files split by concern: style.css, app.js, loader.js, nav.js
- Each page section is its own partial in static/sections/s-*.html
- loader.js fetches all section partials in parallel, then loads app.js and nav.js
- All original functionality, element IDs, and API endpoints preserved
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>