Commit Graph

199 Commits

Author SHA1 Message Date
4d6eb7bd78 Attribute quotes from a following "X's Stimme" narration (v1.20.25)
"Darrags Stimme klang tonlos." after a quote names who just spoke, but the
voice-announcement rule only looked at the narration before a line, so this
mirror image stayed Unknown. Verified on the book (-> Darrag).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-12 10:43:12 +02:00
d3b162f81f Extend German inquit attribution: missing verbs, subject choice, mistyped inquits (v1.20.24)
Driven by unresolved lines in a real German novel:

- AB_SPEECH_VERBS was missing many common inquit verbs (grunzte, versetzte,
  unterbrach, drängte, herrschte, keuchte, erklärte, widersprach, warf ein …)
  and had no thought verbs at all, so inner monologue ("dachte Lysandra") was
  never attributed.
- A trailing inquit naming two people ("unterbrach die blonde Perdia Lysandra")
  resolved to the wrong one; the grammatical subject speaks, so articles and
  adjectives are skipped and the first name after the verb wins.
- A quote whose own inquit had been mistyped as dialogue could never resolve,
  because the rule required the following segment to be narration. A segment
  opening with a lower-case speech verb is an inquit whatever its type; it is
  now accepted and its type corrected.

Verified on the book: resolves further lines with no LLM call.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-12 10:41:06 +02:00
3434293375 Prefer the newest casting draft instead of always trusting localStorage (v1.20.22)
Opening a book always restored the localStorage draft and then pushed it to the
server as a backfill, so a tab holding an older copy silently overwrote newer
work saved from anywhere else — confirmed live: a cast improved from 79 Unknown
lines to 25 was destroyed by opening the book in a stale tab. Local and server
drafts are now compared by savedAt, the newest wins and is cached locally, and
the server is only backfilled when the local copy is actually newer.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-12 10:29:53 +02:00
56800dab28 Document v1.20.21 fixes in changelog 2026-08-12 10:23:32 +02:00
63ec69ad89 Resolve pronoun-inquit split quotes, use verification prompt for repair pass (v1.20.21)
Two gaps found by validating the repair chain against a real German novel:

Split quotes whose inquit uses a pronoun rather than a name (»Ja, ungewöhnlich«
+ ", antwortete er knapp.") stayed Unknown, because every existing rule
requires a literal name after the speech verb. The antecedent is unambiguous,
so it now resolves the same way the colon rule does — to the last character
named in the narration immediately before the quote. Verified on the book: 2
further lines resolved with no LLM call, including the reported case.

The repairNarration pass also only received the verification prompt when
launched from the menu button; called from auto-repair or programmatically it
fell back to the general casting prompt, which is not written for the "is this
narration actually speech?" question. It now defaults to the verification
prompt in every entry path.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-12 10:23:15 +02:00
30bac15b6e Open toolbar flyout menus downwards when there is room (v1.20.18)
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>
2026-08-12 08:47:17 +02:00
4c686165b3 Add targeted dialogue-repair pass and auto-repair chain, fix rollback on stopped runs (v1.20.16)
Spoken lines that PDF extraction typed as narration were invisible to
"Identify unknown characters" (it only scans type==='dialogue'), so no number
of re-runs could fix them, while the pass that could re-sent all ~1000
narration segments to the LLM. Adds a detector that scores narration for
direct-speech signals and re-checks only those: 36 candidates instead of 1036
on a real novel. Adds "Auto-repair cast" to chain the passes in the right
order automatically after casting.

Also fixes a rollback gap where stopping a run skipped the safety net that
undoes a pass which increased Unknown speakers (confirmed live: 80 -> 159
with no warning), corrects an attribution rule that taught the model the
listener is the speaker, and stops the threshold loop from re-verifying every
narration segment each round.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-12 08:40:38 +02:00
193cbb202d Fix Fish-Speech 0-voices bug, add inline emotion-tag reference to Try It Out (v1.20.8)
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>
2026-08-12 00:08:36 +02:00
003e4f9f46 Fix bundle-breaking TDZ throw, use English emotion instructs, forward Fish gen params (v1.20.6)
The emotion quick-pickers added in 1.20.5 guarded with
`typeof REH_EMOTIONS === 'undefined'`, but REH_EMOTIONS is a const declared
later in the bundle's single shared scope — `typeof` on a const in its
temporal dead zone throws instead of returning "undefined", which aborted
top-level initialization for every module bundled after tts-preview.js.
The pickers now read window.REH_EMOTIONS on a deferred macrotask.

Also: emotion instructions are now always built in English (spoken text and
the native-accent clause stay in the book's language), which controlled A/B
testing showed produces a far cleaner prosodic gradient from Qwen3-TTS; and
Fish-Speech now receives temperature/top_p/repetition_penalty, which it was
the only backend never to have forwarded.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-11 22:00:56 +02:00
5fecbf06d4 Fix Fish-Speech emotion tags, wire book context into portraits, add emotion controls app-wide (v1.20.5)
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>
2026-08-11 18:02:41 +02:00
ac115b25b9 Store the full voice design prompt, not just the clipped summary
For a designed voice the instruct prompt is the voice's identity — the TTS
engine reproduces the voice from that text alone. The only copy saved was the
`note` display summary, clipped to 240 characters, which left 43 of 73 voices
cut off mid-sentence. Save the complete prompt in its own field so the engine
can register a voice from the whole description.

Existing voices keep working from the clipped copy (it still carries gender,
accent and timbre) and pick up the full text when next redesigned.

Pairs with the engine-side fix in tts-dgx-spark-faster-qwen3-tts.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-29 15:57:51 +02:00
a62dd0bac1 Fix voice stability, audio effects, and character/voice pipeline bugs
Voice consistency:
- Read back each voice's pinned seed (Seed Finder / Batch Seeds) on every
  generation. The seed was saved to voice metadata but only ever read by the
  Seed Finder's own benchmark path, so all per-voice seed pinning was inert.
- Stop coercing the "voice_design_playback" stability profile back to
  "voice_clone". The pseudo-backend key isn't a real routing target, so the
  backend-name normalizer silently rewrote it — reintroducing the hardcoded
  seed:0 that profile exists to avoid, overriding every per-voice pin.
- Apply the accent clause on every line, not just at voice-creation time,
  and reorder the instruct so emotion leads and accent trails (Qwen3-TTS
  doesn't reliably follow multiple conflicting instructions).
- Pass an explicit language to Voice Design instead of leaving it on "Auto".

Audio effects:
- Add a limiter after compressor makeup gain. Makeup gain pushed peaks to
  ~1.9, and the final hard clip turned that into broadband distortion that
  swamped the rest of the chain.
- Cascade highpass/lowpass 3 stages each (~18 dB/octave). Single-pole
  filters were too gentle to band-limit speech audibly.
- Add a Bandpass control and wire it into the Telephone/Radio presets —
  compression alone never sounded like a phone; band-limiting is the
  defining trait.

Persona / Try It Out:
- Disable "Apply character persona" with an explanatory tooltip when the
  voice has no persona saved, and error clearly server-side instead of
  silently no-op'ing. Persona is typed manually per voice, never auto-filled.
- Stop dropping applyPersona in the chunked generation path (>200 chars).
- Populate the Voice Design dropdown from the user's own library rather than
  filtering the engine's discovery list, which never contains custom voices.

Navigation and library:
- Use pushState instead of replaceState so browser Back/Forward step through
  in-app navigation instead of leaving the app entirely.
- Show real dialogue line counts in the character sidebar instead of the
  capped reference-quote count (which showed a misleading uniform "12").

Also fixes a crash in /api/transcribe-bytes that referenced an undefined
source_id in its cleanup path.

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

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-26 02:03:56 +02:00
f6e5449eb6 Show source evidence in character sheets 2026-07-09 20:36:17 +02:00
ab58ef2e43 Fix character sheets layout nesting 2026-07-09 20:14:03 +02:00
588ba3fcb8 Refine character sheets and live output UI 2026-07-09 19:47:16 +02:00
8f3fcbae04 Release v1.14.19 2026-07-09 14:11:40 +02:00
17716ac740 Stop retried/recast audiobook casts from creating duplicate scripts (v1.14.18)
A fresh (non-resume) cast unconditionally nulled _audiobook.rehId even
when retrying or recasting a book that already had a linked Rehearsal
Library record from a prior attempt — silently orphaning it and
creating a new one on the next save. Now only nulled for a genuinely
first-ever cast; retries/recasts overwrite the existing record.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 12:23:55 +02:00
606ae125a3 Add editable Prompt panel for Character sheets (v1.14.17)
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>
2026-07-09 12:21:26 +02:00
b5ffa655d8 Redesign Character sheets progress modal into a big split view (v1.14.16)
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>
2026-07-09 11:53:57 +02:00
b96968ac5d Add mid-quote-dash and voice-announcement rules to casting attribution prompt (v1.14.15)
Two gaps observed in real casting output:
1. A mid-quote " - " (em-dash pause) was being read as the end of the
   quotation, handing the rest of the line to a wrong/new speaker.
2. Narration naming whose voice is about to speak (e.g. "Marcians
   Stimme wirkte nicht mehr so fest") wasn't being used to resolve the
   following unattributed line away from 'Unknown'.

Added as rules 11/12 to the client-side AB_DEFAULT_PROMPT (the prompt
actually sent to the LLM) and to normalizeCastingPrompt()'s migration
so already-saved custom prompts pick them up automatically, same
mechanism as the earlier Doppelpunkt-Regel upgrade. Also added
equivalent rules to the server-side fallback prompt in
routes/conversation.py for completeness, though the client-sent prompt
is what's actually used in normal operation.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 02:51:46 +02:00
83808a22e1 Fix assign popup being closed before a name-elsewhere click could fill it (v1.14.14)
A document-level "click outside closes the popup" handler only exempted
clicks on the paragraph's speaker label, not clicks anywhere else in the
passage text. Since mousedown fires before click, this closed the popup
(clearing assignModeRow) before the v1.14.13 name-click logic ever ran,
making "click a name elsewhere to feed the open popup" silently do
nothing. Passage-text clicks/drags no longer auto-close the popup.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 02:44:10 +02:00
cd20df315d Stop click/drag-on-a-name from reassigning the wrong paragraph (v1.14.13)
The intended flow is: click the broken paragraph's speaker label to open
"Assign to", then supply the name (type, click it anywhere in the text,
or drag-select it). But clicking or drag-selecting a name with no popup
open yet used to open/target a popup for whichever paragraph that name
lived in, silently reassigning THAT paragraph instead of the one the
user meant to fix. Both paths now require an already-open popup before
they do anything — a paragraph is only ever selected via its own
speaker label (or the existing double-click fast-assign shortcut).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 02:33:56 +02:00
3db3616830 Show character avatar images in casting pickers (v1.14.12)
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>
2026-07-09 02:09:20 +02:00
2133fe25c7 Stop assign popup from retargeting when reusing a name from another paragraph (v1.14.11)
Clicking a name inside a paragraph's text always retargeted the "Assign
to" popup (and its highlight) to that paragraph, even when the popup was
already open for a different one. That made it look like the wrong
paragraph was being reassigned when the user just wanted to reuse a name
they saw elsewhere. Now, while a popup is open, clicking a name outside
its target paragraph only fills the search box.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 02:02:31 +02:00
f139367f74 Make casting live-view panels resizable, fix collapse chevron direction (v1.14.10)
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>
2026-07-09 01:50:36 +02:00
328e612eb7 Fix empty thinking panels for vLLM builds using a bare "reasoning" key (v1.14.9)
Confirmed by directly probing a streaming response from vllm-0.23.1rc1:
its delta objects carry reasoning text under "reasoning", not the more
common "reasoning_content" key every reasoning-display code path was
checking for. Added a shared _reasoning_text() helper that checks both,
used by the Conversation Playground, audiobook casting's live view, and
character sheets.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 01:42:24 +02:00
8d5050bb80 Raise audiobook casting timeouts to 10min/5min for large models (v1.14.8)
Was 6min/3min. 122B+ models can take longer than that just to produce
a first token; 10min also happens to match the server-side cap
(_request_timeout_seconds), so this is the max without raising that too.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 00:09:04 +02:00
c969150095 Fix triple-duplicated passage in audiobook casting's live LLM view (v1.14.7)
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>
2026-07-08 23:00:16 +02:00
ed76af4976 Add collapsible thinking panel to Conversation Playground, regroup mic controls (v1.14.6)
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>
2026-07-08 22:54:20 +02:00
2a2c34fb56 Fix invisible Add Custom engine dialog and a TDZ error on Engines tab (v1.14.5)
Two unclosed <div class="card"> tags in the TTS settings markup left the
add-engine dialog nested inside the TTS panel's DOM subtree, so it
rendered at zero size whenever another Engines sub-tab (e.g. Speech
Recognition) was active. Also fixed a `let` declared after its first
use, which threw a ReferenceError on every Engines-tab page load.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 21:45:53 +02:00
939d33f7cc Fix Apply effects button staying disabled after streaming preview (v1.14.4)
Streaming playback never produces an in-memory blob, but the enable
logic required one. Now enables unconditionally and lazily
re-synthesizes a buffered blob on demand if effects are applied before
one exists.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 21:26:23 +02:00
bfd1bfb939 Add routing voice-picker, restore voice filters, fix pedalboard (v1.14.3)
- 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>
2026-07-08 21:05:45 +02:00
46ef73bbae feat: enhance TTS language detection with frequency scoring and explicit overrides 2026-07-07 03:59:25 +02:00
ce3ae79132 Fix streams permanently stuck holding the shared LLM lock (v1.14.2)
Confirmed in production: a streaming attribution/character-sheets
request held _attribution_llm_lock for 11+ minutes, well past its
configured timeout, silently rejecting every subsequent passage as
"Attribution engine busy" and falling back to the non-streaming view -
looking exactly like live-thinking had stopped working, when actually
one earlier request never finished.

Root cause: requests' timeout= on a stream=True call only covers the
connect + first byte, not gaps between later body reads. If the LLM
backend goes silent mid-stream (connection left open, no more chunks),
the blocked socket recv() inside iter_lines() can hang indefinitely.
Since that's a native blocking call, not a Python-level yield point,
neither an in-loop wall-clock check nor GeneratorExit from a
disconnected client can interrupt it - both only take effect at the
next bytecode boundary, which never arrives while blocked in the C
extension.

Added _watchdog_close: a daemon thread that force-closes the upstream
connection if the wrapped block hasn't finished within the configured
timeout. Closing the socket from another thread makes the blocked
recv() raise, unblocking the generator so its normal except/finally
cleanup (including releasing the lock) actually runs. Verified against
the live LLM backend post-restart: stream completes normally with
real token-by-token deltas, and the lock is confirmed free immediately
after.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 20:57:49 +02:00
6905219567 Fix PDF-parsing progress pill drifting toward the bottom (v1.14.1)
It used position:sticky with a top:40vh offset - sticky's containing
block grows with the document, so on a long PDF the pill drifted
toward the bottom instead of staying a fixed distance from the
viewport's actual visible top. Switched to position:fixed.

Also investigated a "characters all gone" report: verified directly
in the SQLite database that all 47 records for the book are intact,
and a fresh browser session renders them correctly with no errors -
this was a transient/stale-page display issue, not data loss.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 16:14:54 +02:00
960389ef5b Add live-streaming output to Character Sheets generation (v1.14.0)
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>
2026-07-06 15:28:26 +02:00
f2bdd98a4f Fix invisible icon on the Cast as audiobook button (v1.13.10)
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>
2026-07-06 15:16:25 +02:00
6a9c827ec9 Scope Recast-selected to only the relevant passages (v1.13.9)
Recasting one character out of a 149-passage book was reading the
entire text every time, exactly as flagged: "you only need to read a
couple of paragraphs before and after his name." csForReaderSelective
now matches the picked character's name + known aliases against the
book's chunks, keeps one chunk of context on either side for
pronoun/"he" resolution, and only extracts from those - falling back
to the full book only if nothing matched at all (e.g. a name typo).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 15:10:20 +02:00
1b38fce792 Declutter Read Aloud toolbar: drop redundant Character sheets button (v1.13.8)
Character sheet generation is already available from "Cast Characters"
in the Casting flow, so the standalone button here was a duplicate
entry point. Removed it and made "Cast as audiobook" the primary blue
action, moved to the end of the toolbar as the clear next step in the
pipeline.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 15:01:56 +02:00
07eadeff1e Fix Recast-selected running on the full roster, improve dropdown positioning (v1.13.7)
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>
2026-07-06 14:35:27 +02:00
95a4c95b90 Fix wasted empty space in Read Aloud tuning panel layout (v1.13.6)
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>
2026-07-06 12:16:10 +02:00
ebd18bcc14 Fix merge-freeze with chunked redraw, add sidebar search/sort (v1.13.5)
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>
2026-07-06 12:12:36 +02:00
139cb6b85b Fix table-view column misalignment for real, add character-merge via alias popup (v1.13.4)
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>
2026-07-06 11:58:42 +02:00
cb76a2f237 Fix short quoted-line drag opening Assign popup instead of Split (v1.13.3)
Any selection under 40 chars was treated as "assign this as a
character name," including short quoted lines like "»Henker«." that
the user clearly meant to split into their own Unknown-speaker
segment - guillemets/quotes are never part of a name. Selections
starting with a quote mark now skip the assign-popup hijack and fall
through to the already-visible "Split text to Unknown Speaker" button.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 11:32:06 +02:00
628bd75a82 Fix table-view layout bug, add per-prompt Generate buttons, sort dropdown (v1.13.2)
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>
2026-07-06 11:20:23 +02:00
204bc3a6c6 Add View Characters + recast-all/selected once a book is already cast (v1.13.1)
"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>
2026-07-06 01:03:30 +02:00
04531b25f8 Turn Script Rehearser's Stage character strip into a sidebar (v1.13.0)
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>
2026-07-06 00:58:04 +02:00
8922496ec9 Fix silent Design-all crash in Script Rehearser (v1.12.99)
Server logs showed /api/analyze-characters succeeding on every attempt
but /api/voice-design never once being called - the bulk voice-design
loop accessed rehState.cast[sp].voice unguarded right after that
(multi-second, for a large cast) request resolved. Any cast-entry
change during that window threw an uncaught TypeError, silently
killing the whole run with no toast, no further requests, and the
button stuck disabled. Guarded the access and wrapped the remaining
flow in try/catch/finally so failures are visible and the UI always
resets.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-05 23:15:56 +02:00
5fd1660c09 Add character table view, bulk voice-assign, source links, fix prompt truncation (v1.12.98)
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>
2026-07-05 23:09:59 +02:00