Adds GET .../articles/{id}/timings (exact per-sentence times via plan_sentences); the player uses them, falls back to the length estimate, and gives captions no time slot.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Fixed 1:1 sizes scaled with clamp(), no flex-shrink/stretch; overlay no longer clips when content is taller than the viewport.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Also reuses the existing magFmtClock for the contents list (the duplicate
tripped the frontend collision gate).
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
A fresh import used to stop once laid out, leaving missed text, unverified
OCR and provable misspellings for a separate manual click. _ingest_sync now
runs _quality_run_sync itself, under the same job, so the ingest progress
bar carries through it and the reviewer opens an already-worked magazine.
An Auto-fix failure is reported as an ingest failure without losing the
ingest's own result.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- fuse_paragraph_blocks joins a paragraph split by a column or page break into one
narration block (reader, podcast source and MP3), keeping source regions for highlighting
- language decided once per article; per-article acronym glossary (LLM classifies unknown
acronyms from context), letter-spelled acronym lists, extended DE/EN database
- core/spelling: dictionary findings (compounds, inflections, line-break hyphens, other-language
words), marked in review and listed per page/article; guarded LLM agent fixes; quality run
now includes the spelling pass
- keep suspended compound hyphens (Programmier- und) when merging regions / building layouts
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- The sticky review toolbar wraps to many rows on a phone and covered most of the page image,
so drawing a region or drag-selecting boxes started on a toolbar button; it now scrolls away
below 860px
- Delete-button layout test accepts the phone layout (text on its own line)
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Export the region-type picker handlers and magRunPageQuality to the page (they threw
"not defined", so the picker never opened and Find missed text / Verify OCR did nothing)
- Add a unit test that fails when an inline mag* handler is not exported
- Quality audit: pixel-level clipping check on all four box edges, duplicate-article
detection, detected full-page advertisements skipped and shown green (no manual button)
- Driving mode text is karaoke-style (whole article, current sentence and word highlighted)
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Quality run: per-page missed-text recovery, guarded OCR re-read and a pixel audit
(uncovered ink, overlapping boxes, clipped box edges); report saved as quality_report.json
- Thumbnail grid: quality-stage colours, flag badge, live per-page indicator for every
batch step (OCR, detection, figure fixing, quality run, audio)
- Pages can be approved as advertisements (not narrated, skipped by quality run)
- Driving mode: +/-15 s, seek bar, playback speed, text and image toggles
- Plus the previously uncommitted 1.53.5 magazine/OCR work in the same tree
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Bundles this session's magazine review work: a single "Draw region" tool
replacing six preset-kind buttons; "Table" as a real, non-narrated region
type; uniform auto-OCR for every text-like drawn/re-read region with a
Tesseract fallback when the vision OCR backend is unreachable; a thumbnail
grid view and inline title editing for the article list; batch
approve/reject; standalone MP3 export with embedded title/author/album,
lyrics and cover art (adds mutagen); "Remove all boxes" for starting a
tangled page from scratch; Approve advancing straight to the next article;
a merged, sticky review toolbar; and a fix for stale image previews after
resizing a region's box.
Also fixes several character-portrait rendering biases confirmed live
across this session's DSA audiobook cast (routes/conversation.py): modern
or bald/receding hair regardless of established coverage, armored women
rendering masculinized, duplicated/malformed weapons, a ranged weapon
rendering as a firearm due to a German compound-word regex miss, an
established dwarf rendered with human proportions, and occasional extra
hands or lost non-human species in scene panels.
See CHANGELOG.md [1.43.0] for the full list.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The preview carries each page's regions (percent of the page, type, reading
order) for the reading pass and the labelling pass, drawn over the page image
with a legend. A server restart that killed the import left it "running" for
ever; the status route now reports it as failed. v1.42.0.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
original_text is now a Block field, so paths that rebuild blocks keep it
instead of crashing. Undo snapshots are sealed with a digest of the saved
state and refused once anything else changed; actions without their own undo
point clear the Undo button. v1.41.1.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Portrait backdrop measured 0/4 -> 4/4 plain on the failing character, no
change on the one that already passed. After the portrait passes, the vision
model describes hair, facial hair and eyes, and the turnaround and story
prompts are rebuilt with it -- a character with no hair in his sheet got a
different haircut on the sheet and failed the same-person check 3/3.
Costume cut and colours restored for armed characters. v1.41.0.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Measured with the image check: full-body turnarounds 0/5 -> 10/10 (Karyla),
12/12 on Karyla, Darrag and Eolan after the face fixes. The 4-step model
loses the tail of a ~3000-char prompt. Scars are left out of image prompts:
any scar wording, even "fully healed", rendered as fresh gashes. v1.40.0.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Bulk delete/type for a click- or rectangle-selected set; every destructive
region change returns an undo token (one level, refused once something newer
happened); text inside a region that becomes a picture is folded into it.
TODO: model-based layout detection (PP-DocLayout, LayoutReader) with licence
notes. v1.39.0.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The review selection now follows deletes, merges, splits and text-to-image
changes; approve sends only existing ids and no longer overwrites the image
list with a stale one; deleting a region removes it from articles. v1.38.1.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Split at the cursor and merge-with-next keep article membership right; typed
letters in the editor never fire shortcuts; focus returns to the same row
after a redraw. v1.38.0.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Rules sized on a real 204-page issue before writing them; each re-types
(not read aloud, reversible), none deletes. A run-on author line's sentence
becomes its own paragraph instead of being glued to "the next paragraph",
which was right in only 1 of 5 real cases. Runs on import and re-OCR, and on
demand with a layout backup. v1.37.0.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A hand-ordered page is locked: Fix reading order, type changes and newly drawn
boxes leave it alone until Automatic order. v1.36.0.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Columns are found from where column-width text starts, not from the whitespace
between them (loose OCR boxes shrank one gutter to 4-6 pt and a pull-quote
bridged it, collapsing page 38 into one interleaved column). Headlines get
their own band, pull-quotes are read at the end of the page. Measured over a
204-page issue: 89 sentence continuations kept vs 85.
Each review row gets a type picker (text kinds, page number, source, ad,
image) and its own delete button; text<->image conversions keep the region
in its article. Fix reading order re-reads existing issues without OCR and
leaves hand-ordered pages alone. The shared confirm dialog is now an
alertdialog for screen readers. v1.35.0.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The job checks the portrait crop, then the turnaround, story panel and
same-person against the sheet, re-renders on failure (up to 3 attempts) and
keeps the attempt with the fewest faults. ReActor transfers the profile's face
onto the turnaround and the largest face in the story panel. If the checker is
down, retries stop and the pair is marked unverified. The outcome is saved
with the pair and shown on the character page.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A cold-booting model server would otherwise time out the first passage into tag guessing.
Documents that the custom attribution prompt applies to the classic engine only.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Pooled over three hand-corrected novels (~54k spoken words): agreed 82% right, disputed 48%,
Unknown ~0%, unverified ~76%. The old heuristic bands claimed 16% for lines that were 62% right
and tag/role signals did not discriminate in span casts; classic casts keep the old model.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Verify / recast now run span-natively (no text re-emission): a second differently-windowed
pass marks lines agreed/disputed and records the other answer, without changing any speaker
(~9 min vs ~49 min). Measured on the real pipeline, overruling disputed lines with a reasoning
adjudication made them worse (66.5% -> 61.4%), so that step was removed; agreed lines were 83%
right vs 48.5% for disputed ones, which is the signal used for confidence and review.
Auto-repair no longer runs after a span cast; segments carry a spanId.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- fix: base-appearance matched pair lost its concept image + metadata on reload; visual save
wrote base concept art to the record root; one save path for base and forms now
- fix: extraction no longer lets the LLM author image_prompt (source of avatar/concept drift);
avatar-only generation uses the deterministic prompt and flags an existing pair
- fix: prompt writes and Physical/Clothing edits go to the active form; review endpoint checks
each form's own pair; fingerprint parity with the browser incl. visual_* overrides
- feat: appearance forms panel (add/rename/delete/show, per-form stale flag, generate one/all)
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Split the book into narration/dialogue spans from its own quotation marks and ask
the LLM only who speaks each dialogue span (POST /api/attribute-spans), so text can
no longer be fused, dropped or duplicated and a novel takes ~10 min instead of ~4 h.
Book 1 vs the hand-corrected reference: every-word-right 87.0% -> 92.3%, type
agreement 93.6% -> 97.4%; Books 2/3 (not tuned on) 90.8% / 89.9%.
- static/js/audiobook-spans.js (pure, unit-tested) + audiobook-cast-spans.js
- core/attribution_spans.py, route, tests; scripts/score_attribution.py
- default on; localStorage ab-span-mode=0 restores the classic engine
- architecture/audiobook-attribution.md, Manual, CHANGELOG 1.32.0, TODO
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- fix: Edit boxes and page-nav controls were dead clicks (handlers never exported
to root); resize from any edge/corner instead of only bottom-right
- feat: two-page spread in review (page images above, OCR lists beneath)
- feat: searchable voice picker for narrator and podcast hosts
- feat: Prepare audio synthesises the whole article ahead of playback; playback
also stays three sentences ahead
- fix: sync index.html cache-busting version with VERSION (was stuck at 1.26.12)
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds "Edit boxes" mode to article review: drag an already-detected
region to move it, its corner handle to resize it, or press its x to
delete it -- for correcting a box the layout engine got wrong, not one
it missed entirely ("Draw it" still covers that case). Moving or
resizing opens a confirm panel; "Save" keeps the region's existing
text at its new position, and for text regions "Save & re-OCR" reads
the corrected area again and replaces the text.
New core/magazine.py update_region()/delete_region() and PATCH/DELETE
/api/magazine/{doc}/pages/{page}/regions/{block_id}. An image region
is re-cropped from the page render whenever its bbox changes, since
its saved file is exactly that crop.
Live-verified against the real ingested c't KI Wissen 2026 magazine:
add -> move -> move+re-OCR (correctly kept old text when the corrected
area had nothing readable) -> delete -> confirmed gone, repeat delete
404s.
275 tests passing.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds a "Describe" action per figure in article review: sends the cropped
image to the configured vision engine (with fallback) for a type-aware
one- or two-sentence description -- a chart gets a prompt about what it
measures and its trend, a table about headings and what stands out, etc.
Editable before approval. A new "Read figure descriptions" toggle in the
reading controls speaks these where they occur; purely additive, off by
default, and a figure with no description behaves exactly as before.
New core/magazine.py describe_visual_region()/_chain() reuse the same
reliability guards as page OCR (repetition-loop detection, sentence-
boundary truncation). New routes: POST .../images/{id}/describe
(background job), PATCH .../images/{id} (manual edit), descriptions=1 on
the article-text endpoint.
Live-verified end to end against the ingested c't KI Wissen 2026
magazine. Also found and documented a real, config-dependent limitation:
the currently-configured primary vision engine is transcription-only and
returns empty content for any descriptive prompt (confirmed with three
different prompts) -- not a code defect, but description generation
needs a genuinely caption-capable engine reachable as the fallback.
251 tests passing.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Connect and "Use as LLM" on the LiteLLM Gateway card now resolve that
card's own saved key (engine_api_keys[name]) server-side in
routes/docker.py, ahead of the old global llm_api_key fallback. The
browser can no longer reliably forward a saved key at all -- it's
masked to "__configured__" in every /api/settings response the moment
it's saved -- so relying on a frontend-forwarded header alone left
Connect silently using a stale global key after LiteLLM's own key was
rotated, surfacing a misleading /api/version 404 instead of the real
401.
"Use as LLM" now goes through a dedicated /use-as-llm endpoint that
saves the exact credential Connect just verified (llm_url normalized
to .../v1, llm_api_key copied from the card), rather than a
separately-typed value that could drift again.
Also documents per-user provider-key storage as an explicit item for
the deferred multi-user/identity planning pass.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Two reports from the same mobile session tied together: a voice-library
crash ("fmtDbfs is not defined") and a save button reporting "No audio
ready" despite a file being correctly loaded and trimmed. Root cause
was the same in both -- the module loader fetched each JS file once
with no retry, so a single dropped request on a flaky connection left
that module's functions/variables undefined while every other module
kept executing anyway, producing confusing symptoms nowhere near the
real cause.
loader.js: _load() now retries up to 3 times with backoff before
rejecting. If a module still can't load after that, the app says so
plainly via a toast (or a plain DOM banner if even utils.js failed to
load) instead of silently revealing a partially-broken page. Wired into
both the core-module fallback path and ensureFeatureModules' lazy path.
Loading every section of the app at a 375px viewport and checking for
elements running past the viewport edge (all now 0) found eight real,
independent layout bugs, each with a distinct root cause:
- .section-head's mobile wrap rule targeted .section-header, a class
nothing in the app uses -- dead since it was written. Every section's
icon/title/action row (e.g. "+ New voice") ran off-screen unreachable.
- The synth-mode toggle and Clone-a-Voice source tabs' icon-only mobile
rules matched a <span> wrapper the markup didn't have. Added it, plus
aria-labels so hiding the text doesn't erase the accessible name.
- voice-design.css loads after style.css and re-declares
.design-sample-grid unscoped -- style.css's mobile override always
lost the cascade regardless of viewport. Moved the override to the
file that actually wins.
- Three separate minmax(380-440px,1fr) engine-card grids couldn't fit
one column under ~460px. Collapsed to 1fr with min-width:0 on the
items -- the column fix alone wasn't enough, since a grid item's
content-based min-width is still a floor even inside a 1fr track.
- The routing test row (450px+ minimum) and the conversation input bar
(a single non-wrapping flex row for text field + mic + gate + three
toggles) now wrap under 767px.
- Try It Out's EQ sliders shrank to fit beside their toggle instead of
ever wrapping below it, overflowing their own squeezed container.
Forced onto their own full-width line.
Verified with a Playwright sweep of all 16 sections (0 overflowing
elements, 0 console errors) plus the existing 225-test suite and the
frontend syntax gate.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Resumes an in-progress, uncommitted change from a previous session: the
live "page image + recognised text" preview shown during ingestion was
reworked to render formatted HTML (styled headings, a figure
placeholder) instead of a plain-text dump, but was left with a
duplication bug, dead code, inline styles, and no tests.
- Fixed duplication: a figure block's own transcribed text (usually its
on-screen UI copy) was rendered as an ordinary paragraph in addition
to the new figure placeholder box. Figure/chart/table/code blocks are
now represented only by the placeholder.
- Furniture/sidebar/pullquote/ad text is shown muted instead of being
dropped with no replacement, and is never promoted to a heading
regardless of font size.
- Factored the formatting logic out of the progress closure into a
standalone, unit-tested _render_ingest_preview_html().
- Replaced dead getattr(bbox, "y0", ...) calls (bbox is always a plain
[x, y, w, h] list) with direct indexing, moved statistics/logging
imports to module level, switched manual character replacement to
html.escape(), and bounded the preview by an accumulated line budget
instead of a blind slice that could truncate mid-tag.
- Replaced inline style="..." with CSS classes matching the panel's
existing dark console treatment.
- The frontend now always uses innerHTML (every text node is escaped
server-side), dropping the startsWith('<') content-type sniffing.
- Marked the preview panel aria-hidden -- decorative, and would
otherwise churn noisily for screen reader users on top of the
adjacent progress bar.
Verified against the real c't KI Wissen 2026 layout: a figure's raw
OCR text no longer appears twice, a real headline renders as <h3>, a
footer/pull-quote render muted, and all text is correctly escaped.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
A vision OCR engine grounds a figure far less precisely than it
transcribes one. Over all 138 figure regions of a 204-page issue the
transcribed contents were right while the boxes were routinely offset,
undersized, or sitting on the neighbouring text column -- page 23's card
grid was reported at x 0.24-0.54 where the graphic runs 0.41-0.95.
snap_visual_bboxes() reduces the page render to an ink-coverage grid,
seeds a connected-component search with the engine's box, and re-joins
tiles a gutter apart so a card grid comes back as one figure. Text
blocks are excluded from the mask, which is what stops a figure
swallowing the column beside it. A candidate must be solidly inked or
framed on nearly all of its rows and columns (admitting a white-interior
screenshot), may not shrink below 30% of the seed, and reverts when two
figures land on the same graphic; anything else keeps the box it had.
Runs before the figure crop in both OCR paths, so the images shown
during playback are framed correctly too. Replaces refine_picture_
bounds(), which only recognised coloured panels and had no text fence.
Also adds "Fix figure boundaries" in review and POST
/api/magazine/{id}/figures/refine behind it, so an issue ingested before
this pass can be corrected without re-running OCR. Verified live against
c't KI Wissen 2026: 78 of 138 boxes corrected, the rest declined.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>