Commit Graph

244 Commits

Author SHA1 Message Date
c6ce3e4803 security: keep provider credentials server-side 2026-09-04 07:50:30 +02:00
d19e52fe28 security: harden web container runtime 2026-09-04 07:45:31 +02:00
c78d1683b6 feat: add voice recovery and schema migrations 2026-09-04 07:44:29 +02:00
686ba03ded test: cover P0 security boundaries 2026-09-04 07:34:30 +02:00
440413fcb7 security: harden P0 boundaries and recovery 2026-09-04 07:33:41 +02:00
a8f8779736 Release v1.22.3 2026-09-04 07:28:26 +02:00
fe807d1682 Release v1.22.2 2026-09-03 16:12:02 +02:00
e2f7b90ad9 docs: add agent instructions (CLAUDE.md) 2026-08-26 13:33:39 +02:00
c9d063d0c7 Fix character profile view missing fields (Race, Age, Languages, Nationality, Class) 2026-08-23 12:18:08 +02:00
882e1ede55 Fix audiobook export modal bindings and position for SPA routing 2026-08-23 11:58:36 +02:00
ee17ed97d1 Fix audiobook export modal visibility and eliminate 4-10s TTS delay for narrator blocks 2026-08-23 11:45:20 +02:00
1d8e0ff2a6 feat: Audiobook Export Modal, UI sync fixes, and initialization stability (v1.22.0) 2026-08-23 10:15:46 +02:00
f04c8d5e2b chore: release 1.21.1 - Testing and scratch scripts 2026-08-23 09:27:34 +02:00
fee1b5db74 chore: Ignore entire config directory 2026-08-23 09:13:30 +02:00
f7264a8c07 chore: release 1.21.0 - Consistent ComfyUI Character Renders and Audiobook Export
- Deterministic hash-based character portraits and reference sheets
- Implemented strict early Renaissance aesthetic controls and role-specific headwear rules
- Added audiobook batch export scripts and progress tooling
- Verified book profiles with a new 'confirmed' state tracking mechanism
- Updated Pillow dependency for robust image handling
- Configured .gitignore for audiobook and audio cache exports
2026-08-23 09:12:57 +02:00
fce5a0aa5d Track preview memory cleanup 2026-08-19 22:05:40 +02:00
2bcbbafbcf Refresh production bundle for preview cleanup 2026-08-19 22:05:31 +02:00
2d5ecea53a Release completed voice preview blob URLs 2026-08-19 22:05:31 +02:00
77758acdd6 Track metadata and upload cleanup safeguards 2026-08-19 18:41:41 +02:00
25264517a9 Clean up converted upload sources 2026-08-19 18:41:31 +02:00
e452b0f000 Write voice metadata atomically 2026-08-19 18:39:11 +02:00
11cd5b500c Track completed memory limit safeguard 2026-08-19 18:35:45 +02:00
d3fc2d44ae Add configurable web container memory limits 2026-08-19 18:35:34 +02:00
fd8f545d16 Optimize offscreen character table rendering 2026-08-19 18:35:10 +02:00
6629e8f017 Track completed image lazy loading task 2026-08-19 17:12:53 +02:00
b43034ee5d Refresh production bundle for lazy artwork 2026-08-19 17:12:45 +02:00
08a6544661 Lazy load character artwork 2026-08-19 17:12:45 +02:00
2bb01de94a Refresh production JavaScript bundle 2026-08-19 17:12:07 +02:00
aa15265021 Show generated voice file and preview in character profiles 2026-08-19 17:12:02 +02:00
8cc92ca02b Add low-memory client concurrency guard 2026-08-19 17:01:06 +02:00
496518f190 Stabilize browser smoke tests for current UI 2026-08-19 16:59:39 +02:00
b953ba1a83 Refactor table and audiobook helper modules 2026-08-19 16:39:17 +02:00
388fd601ab Release v1.20.38 2026-08-19 16:36:26 +02:00
189ca51e47 Release v1.20.37 2026-08-19 16:04:30 +02:00
62274a17fc Improve Studio casting and character voice design (v1.20.36) 2026-08-15 13:20:38 +02:00
2013157bda Document the three-book casting validation (v1.20.35)
Re-cast all three volumes end to end and compared each against its existing
cast. Book 03 finished at 9 unknown, below the 25-line cast it replaced, and
its fused multi-turn segments — one voice reading both sides of an exchange —
fell from 60 to 6. Speaker counts stayed level with the hand-corrected books
instead of inflating, confirming the phantom-speaker filter and the label
consolidation both hold.

Agreement with the hand-corrected books is 81-82%. The largest remaining
error is systematic ping-pong inversion, where a whole exchange alternates
onto the wrong speaker, followed by epithets that share no tokens with the
canonical name.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-12 19:44:47 +02:00
35cb21ad70 Reject phantom speakers invented from German noun phrases (v1.20.35)
The caster emits fragments of noun phrases as characters — "in seinem Inneren"
-> Inneren, "die Hand" -> Hand, "mein Geliebter" -> Geliebten, "der Wind" ->
Wind. Each became a cast entry with its own voice while the real speaker's line
was lost to a phantom; measured against a hand-corrected book this was a large
part of why a fresh cast produced 73 speakers where the human had 62.

Such lines are now reset to Unknown — visibly unresolved and still recoverable
by the repair passes, rather than silently wrong. Applied to a real cast:
5 phantoms removed, 73 -> 68 speakers.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-12 16:52:27 +02:00
4d3e4f8e7c Consolidate speaker labels after recast passes too, not only after casting (v1.20.34)
Consolidation ran only at cast completion, but recast/repair passes assign
speakers of their own and can re-introduce the variants just removed —
confirmed live: a freshly cast book gained "Alrik" alongside "Alrik von
Blautann" during auto-repair, splitting one character across two voices again.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-12 14:57:56 +02:00
cd5c325265 Consolidate speaker label variants after casting (v1.20.33)
One character is referred to by several labels across a book — bare vs full
name, a title on its own, a stray fragment — and each variant became its own
cast entry with its own voice, so the character audibly changed voice
mid-scene. Measured against a hand-corrected book this accounted for more of
the disagreements than genuine misattributions.

Variants are folded together only when one label's words are a strict subset
of another's after stripping articles and titles, so "Weber" and "Alter Weber"
stay separate and unrelated names never merge. Applied to a real cast:
Sharraz -> Sharraz Garthai, Baronin -> Baronin Ira von Seewiesen,
Von -> Oberst Alrik von Blautann (40 lines, 58 -> 54 speakers).

Also moves the quotation-mark toggle into the A- / A+ toolbar row.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-12 13:34:40 +02:00
f837ba1a4f Toggle quotation marks with CSS instead of rewriting text (v1.20.31)
CSS cannot target a character inside a text node, so the marks are now wrapped
in their own .ab-q element at render time and hidden with a single class on
body. Toggling is instant regardless of segment count, and the stored text is
never modified.

Also fixes the toggle looking inert on a freshly cast book: it previously only
added missing marks and never hid the ones the book already carried. Button
moved bottom-right as a larger labelled pill, clear of the search controls.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-12 13:14:35 +02:00
da3d442656 Add one-command runner for the book01 manual-vs-automated cast comparison
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-12 11:15:35 +02:00
737687789f Add cast comparison tool for validating automated casting against a manual one
Matches segments by normalised text rather than index (a recast splits and
merges segments, so indices never align) and reports where the automation
disagrees with a hand-corrected cast: lines left Unknown that the human
resolved, lines given a different speaker, and narration/dialogue type
disagreements. Intended for using a manually-optimised book as ground truth to
drive further attribution-rule work.

Known limitation: repeated identical lines match the first occurrence, so
duplicates can mispair.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-12 11:09:38 +02:00
55f634e626 Show quotation marks in the casting script, with a toggle (v1.20.29)
German prose never quotes narration, so displaying the guillemets makes a
mislabeled line obvious at a glance. Extraction strips them from ~80% of
segments, so they are rendered rather than stored: dialogue rows display
wrapped in » «, lines that kept their own marks are left as-is, and the saved
text is untouched so synthesis and exports are unaffected. A toolbar button
toggles visibility and the choice persists.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-12 10:51:46 +02:00
ca654608ac Never merge adjacent Unknown dialogue lines into one segment (v1.20.27)
Adjacent segments merge when they share a speaker, but an unattributed line
carries the speaker "Unknown", so two consecutive Unknown lines compared as
equal and were fused into a single block joined by a blank line. Confirmed
against the source text: Lysandra's line and Perdia's interruption — two
separate paragraphs in the book — became one segment, after which no
attribution pass could separate them and one voice would read both sides of
the exchange.

"Unknown" means the speaker is not known, never that two lines share one.
Narration and lines with a real speaker name merge exactly as before.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-12 10:46:55 +02:00
14ed0c89e7 Do not auto-attribute segments that fuse several speakers' turns (v1.20.26)
A trailing inquit names only the last speaker, so applying it to a block
holding multiple turns silently mis-credited the earlier one — a wrong
attribution is invisible where an Unknown is not. Such blocks are now skipped
by the grammar rules and left for the LLM pass or a manual split.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-12 10:44:34 +02:00
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