Commit Graph

269 Commits

Author SHA1 Message Date
b7aade39db Release v1.26.1
Some checks are pending
Test and SonarQube / quality (push) Waiting to run
2026-09-08 23:39:44 +02:00
71a398b82f feat: add magazine reader frontend (P1)
Studio -> Magazines: library, contents tree grouped by section with
confidence/pages/listening-time/audio status, the Stage D review
screen (tick/untick layout blocks, correct title/author, approve or
reject), a reflowed article view with click-to-seek sentence
highlighting, a linked original-page view showing the source bounding
box, and a podcast panel (2-3 host pickers, script generation,
turn-by-turn playback).

Wired into the existing lazy-module system (module-manifest.json,
loader.js, nav.js) rather than a separate app. Reuses /api/tts-preview
for both narration and podcast turns -- no new audio pipeline.

Fixes found building this:
- The pane toggle used role="tab" without the full ARIA tabs pattern
  (aria-selected, roving tabindex, arrow-key nav) -- fails WCAG 2.2 AA
  and hid the buttons from role-based tooling. Replaced with a button
  group + aria-pressed, matching the plain-click interaction it
  actually has.
- Print layout cues ("(continued on page 8)") were being sent to TTS
  verbatim -- narration would have spoken the parenthetical aloud.
  Stripped from the narration copy only; layout.json keeps the
  original for the review screen.
- has_podcast now comes from the articles listing so opening an
  article without a generated script skips a doomed GET instead of a
  routine 404 on every open.

Verified against the real API contract with Playwright (library,
review pre-selection, approval, sentence highlighting + PDF-page
linking, podcast approval gate, zero console errors across the flow)
and manually end-to-end against a real ingested fixture, screenshots
included. Full suite: 94 Python tests, 33/34 desktop Playwright tests
(one unrelated pre-existing flaky DOM-count assertion).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-06 15:00:58 +02:00
6553d5003d fix: preserve custom OCR engine cards 2026-09-05 08:11:57 +02:00
60ef436ce1 feat: add OCR engine profiles 2026-09-04 22:29:11 +02:00
2caa417401 fix: preserve audiobook dialogue boundaries 2026-09-04 15:36:26 +02:00
29f5243f6b test: preserve dialogue-only recast merges 2026-09-04 15:31:45 +02:00
b6f4a9518b test: lock visual identity prompts 2026-09-04 15:30:39 +02:00
8181c491c0 docs: complete frontend namespace migration 2026-09-04 13:43:36 +02:00
18e586d06f refactor: scope conversation state 2026-09-04 13:42:02 +02:00
417d7215d2 refactor: scope i18n state 2026-09-04 13:40:07 +02:00
ee4208ec51 refactor: scope cast compatibility modules 2026-09-04 13:38:40 +02:00
542f44fbb8 refactor: scope seed finder 2026-09-04 13:36:55 +02:00
10f618c179 refactor: scope character columns 2026-09-04 13:35:10 +02:00
d2a8dd43b4 perf: tighten character pagination 2026-09-04 13:31:18 +02:00
7938f9ca74 refactor: scope frontend utilities 2026-09-04 13:27:43 +02:00
d055596504 refactor: scope voice inspector 2026-09-04 13:25:13 +02:00
67ee55d3ac refactor: scope character library 2026-09-04 13:23:54 +02:00
73f8f92826 refactor: scope character sheets 2026-09-04 13:21:31 +02:00
e5bd2a149f refactor: scope voice library 2026-09-04 13:19:52 +02:00
1b7d6b2346 refactor: scope generation module 2026-09-04 13:15:30 +02:00
ababdf6741 refactor: scope stt module 2026-09-04 13:13:10 +02:00
c53c47ce2d refactor: scope ai backends 2026-09-04 13:10:51 +02:00
716e601fac refactor: scope integrations module 2026-09-04 13:06:59 +02:00
3fffae9015 refactor: scope combined library 2026-09-04 13:04:46 +02:00
260c5c22a7 refactor: scope engine support 2026-09-04 13:02:29 +02:00
b747553bf6 refactor: scope routing module 2026-09-04 13:00:10 +02:00
28c72c76dd refactor: scope voice sources 2026-09-04 12:57:53 +02:00
d219aec676 refactor: scope voice clone 2026-09-04 12:55:50 +02:00
998a65f2ba refactor: scope tts preview 2026-09-04 12:53:44 +02:00
656d0473d4 refactor: scope settings module 2026-09-04 12:51:29 +02:00
8002ca1d75 refactor: scope character library 2026-09-04 12:49:22 +02:00
0e615d52de refactor: scope studio module 2026-09-04 12:47:30 +02:00
ac55cd88a3 refactor: scope rehearser cast 2026-09-04 12:44:58 +02:00
df95ada578 refactor: scope rehearser voice design 2026-09-04 12:44:08 +02:00
0f3d410260 refactor: scope rehearser voice picker 2026-09-04 12:43:11 +02:00
dea1de5c57 refactor: scope rehearser stage 2026-09-04 12:42:22 +02:00
da3957347c refactor: scope audiobook network 2026-09-04 12:41:35 +02:00
7ee5f65e00 refactor: scope rehearser library 2026-09-04 12:40:56 +02:00
0ee43421b7 refactor: scope reader module 2026-09-04 12:40:09 +02:00
66c5dd6e0c refactor: scope audiobook core 2026-09-04 12:37:37 +02:00
c6db55e55b refactor: scope rehearser core 2026-09-04 12:33:55 +02:00
6c62a0f75e refactor: scope rehearser recording 2026-09-04 12:31:03 +02:00
3a7f0bf677 refactor: scope rehearser io 2026-09-04 12:30:13 +02:00
a202230654 refactor: scope rehearser playback 2026-09-04 12:29:19 +02:00
cc4fc4ae2e refactor: scope audiobook state modules 2026-09-04 12:26:21 +02:00
302dbe1279 refactor: scope pure frontend leaves 2026-09-04 12:20:33 +02:00
d32985256d docs: close incremental p1 audit 2026-09-04 12:14:47 +02:00
cefd4bd494 refactor: extract rehearser training mode 2026-09-04 12:12:11 +02:00
0c7cc04643 refactor: extract rehearser recording 2026-09-04 12:10:00 +02:00
ba7b3115b9 refactor: extract audiobook preview export 2026-09-04 12:05:50 +02:00
76421dfe88 refactor: extract audiobook casting pipeline 2026-09-04 12:04:05 +02:00
eaf2f29d62 refactor: extract rehearser io 2026-09-04 12:01:38 +02:00
f97fd60e43 refactor: extract rehearser playback 2026-09-04 11:59:09 +02:00
e096b3e529 refactor: wire audiobook namespace APIs 2026-09-04 11:54:56 +02:00
6f2c0b7b3d refactor: extract audiobook cast view 2026-09-04 11:53:04 +02:00
262e37fffd refactor: extract rehearser voice matching 2026-09-04 11:47:51 +02:00
4c8022e8aa refactor: extract audiobook stylesheet 2026-09-04 11:44:55 +02:00
065445a20a refactor: extract rehearser stylesheet 2026-09-04 11:42:43 +02:00
eb008475ed refactor: extract rehearser cast renderer 2026-09-04 11:39:45 +02:00
ae9df4f7f0 feat: distinguish provider configuration and health 2026-09-04 11:35:52 +02:00
7c14f41cb9 refactor: extract audiobook speaker normalization 2026-09-04 11:32:20 +02:00
0d1299b19c refactor: extract audiobook cast cleanup 2026-09-04 11:30:12 +02:00
1951ce0646 refactor: extract audiobook dialogue merging 2026-09-04 11:27:49 +02:00
41349867a1 refactor: extract audiobook cast normalization 2026-09-04 11:25:52 +02:00
0876527554 refactor: extract audiobook quote segmentation 2026-09-04 11:22:15 +02:00
2fde0a176d refactor: extract rehearser voice design 2026-09-04 11:19:01 +02:00
d43959728d refactor: split voice design styles 2026-09-04 11:16:05 +02:00
de099d30c5 refactor: split background job styles 2026-09-04 11:13:36 +02:00
8d9a172993 refactor: split resource diagnostics styles 2026-09-04 11:10:43 +02:00
c0ac5dd775 refactor: extract audiobook cast serialization 2026-09-04 11:08:49 +02:00
d3bc806f33 refactor: extract rehearser training comparison 2026-09-04 11:06:23 +02:00
d613591526 refactor: extract rehearser cast controls 2026-09-04 11:04:13 +02:00
ed3b2d42cd refactor: publish feature APIs through TTVC 2026-09-04 11:00:22 +02:00
e2e20fa89f feat: add request correlation diagnostics 2026-09-04 10:58:46 +02:00
66ebd1a8b8 refactor: extract audiobook parsing primitives 2026-09-04 10:56:31 +02:00
0bfb6d694b refactor: extract rehearser voice picker 2026-09-04 10:54:21 +02:00
e98c5ffe33 refactor: extract rehearser stage controls 2026-09-04 10:52:05 +02:00
b1374c50a0 fix: synchronize frontend release cache version 2026-09-04 10:36:44 +02:00
fe807d1682 Release v1.22.2 2026-09-03 16:12:02 +02:00
2d5ecea53a Release completed voice preview blob URLs 2026-08-19 22:05:31 +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
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
08a6544661 Lazy load character artwork 2026-08-19 17:12:45 +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
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
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
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