Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
5a69481f65
commit
b9e7bcda7f
21
CHANGELOG.md
21
CHANGELOG.md
@ -6,20 +6,18 @@ Follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) · versioned wi
|
||||
---
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
---
|
||||
|
||||
## [1.57.0] — 2026-10-03
|
||||
### Added
|
||||
- **Night theme for driving mode.** A *Colors* group at the end of the display menu: Auto (follows the system, live; default), Light, Dark; remembered in this browser (storage errors are tolerated) and independent of the app's own theme. Dark uses true black, text at about 85% white, a muted blue accent, dimmed pictures (brightness .85), a themed top bar, menu, index with rows, lyric lines and current-line highlight, spoken-word marker, slider, dock buttons (still perfect circles), zoom buttons, *Tap to play*, toasts and share box, and sets the browser theme colour while driving mode is open (restored on exit). All text pairs are at least 4.5:1 and borders 3:1, including *no audio* rows; status icons differ by shape, not just colour. The overlay is painted dark from its first frame. Tested with Playwright (persistence, Auto vs emulated colour scheme, computed-style contrast ratios, first-frame background, theme-color restore).
|
||||
- **Driving mode index shows what the playlist shows.** Each row has a 44 px round play / resume / replay button, the author ("by Name", smaller and muted, ellipsized), and the three-state listening indicator (not played / partly played / played) with accessible text, updated live as articles play and finish. Shares one state helper (`magListenState`) with the playlist.
|
||||
- **Article index in driving mode.** A new *Index* button in the top bar opens a sheet between the top bar and the controls dock that lists every article of the magazine with its title and length (m:ss or h:mm:ss; articles without audio are dimmed, show *no audio* and cannot be chosen). The playing article is highlighted and scrolled into view; tap or press Enter to jump to an article while driving mode keeps running. Escape, the close button or a tap outside closes it and returns focus to the button; Tab stays inside the sheet. Lengths come from the existing articles endpoint (`audio_seconds`), so there is no new backend call; a 113-article list is a plain list rendered at once.
|
||||
- **Share an article or a magazine as a link that opens straight in driving mode.** A *Share* button in the magazine toolbar (whole magazine), one in the playlist header and one in driving mode (current article) build `/#s-magazine?doc=<id>&article=<id>&mode=drive`. They use the browser's share sheet where there is one (it needs https, so on a plain-http LAN address they copy the link instead, with a polite aria-live toast, or show a box with the link if even copying is refused). Opening such a link, also on a first visit and also pasted into an already-open tab, opens the magazine and then driving mode for that article with audio, pictures and text; when the browser blocks autoplay a large *Tap to play* button appears. The link carries ids only (no key or password) and does not change who may reach the app; the ids are validated against a strict pattern and never put into markup, and anything malformed is ignored. Tested with Playwright (desktop deep link, blocked autoplay, hostile parameters, share sheet at 375 px, copy fallback).
|
||||
- **Filter the page grid by status.** Each legend entry is now a toggle button with the number of pages in that status; choose one or several to show only pages that have any of them. It combines with *Show only pages that need me*, the summary says how many pages are shown, *Show all pages* clears everything, a status no page has is greyed out, and the buttons announce their on/off state and work from the keyboard.
|
||||
|
||||
### Added
|
||||
- **Word-level timing with a forced-aligner sidecar.** A new, separate `tts-aligner` container (`aligner/`, torchaudio MMS_FA, CPU by default, optional `--profile gpu`; weights cached in the `aligner-models` volume, no published port, non-root, read-only filesystem, 3 GB limit) answers `POST /align` and `POST /align/batch` with `[{word, start, end}]`. The app (image unchanged, no torch) measures each sentence's words in a background job the first time an article is played (or via `POST .../articles/{id}/align-words`, progress via `GET`), writes `words: [[start_ms, end_ms], ...]` per segment into `audio/<id>.timings.json` (now version 2; readers of version 1 keep working) and `/timings` returns `words` per sentence. The player marks the word from those times in Compact, Lyrics (new per-word marking) and the playlist, and falls back to the previous length-based estimate when no word times exist or the sidecar is down. Measured on one article: after-pause word starts are within 29 ms on average (max 170 ms) of the audio's energy onsets.
|
||||
|
||||
### Fixed
|
||||
- **Driving mode lyrics view no longer leaves the previous line's word highlighted** when the playback moves to the next sentence.
|
||||
- **Sentence timings now also work for interview renders.** Every recorded audio file carries `"interview": true` (set whenever a second voice is configured, even without speaker labels), and the timings endpoint and EPUB export discarded all such records, so the player and the EPUB fell back to estimated sentence positions for every current article. The record is now matched to the text again, for interview renders by the label-stripped length the renderer recorded, so `/timings` returns exact (or anchored) sentence times and speech onsets for these articles too. Records for text edited since the render are still ignored.
|
||||
|
||||
### Changed
|
||||
- **Driving mode index rows are clearly separated.** Each row now has 13 px padding above and below and a thin divider line (the highlighted current row has none), replacing the bare gap, so multi-line titles with the play button, number, duration and status icon read as individual items. Hit areas stay 44 px and the 113-row list still renders at once.
|
||||
- **Driving mode index rows have more breathing room and lighter play buttons.** Rows now have 9 px vertical gap between them, staying tappable and scrollable. Each row's play/resume/replay button is now a light circle with a blue icon (matching the magazine playlist), styled with a surface background, subtle border and the app's primary blue for the icon, instead of a solid blue circle; the visual circle is 34 px with a 44 px accessible touch target maintained via padding.
|
||||
@ -27,10 +25,9 @@ Follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) · versioned wi
|
||||
- **Driving mode has a new layout that always keeps the controls on screen.** The overlay is now one full-height column: a top bar (share, a *Picture / Lyrics / Compact* switch, minimize, close), a content area that shrinks and scrolls inside itself, and a controls dock pinned to the bottom (position slider, time, previous / -15 / play / +15 / next, speed, text, images). On an iPhone SE (375x667) the controls used to fall below the screen, the share button covered the article title and the top of the title was cut off; now the dock is always fully visible from 320x568 up to tablets, in landscape the picture and the three lines sit side by side, and long titles wrap to two lines. *Picture* fills the area (double-tap, pinch, wheel, +/- keys or the zoom buttons to zoom, drag to pan, Escape or the reset button to reset); *Lyrics* shows the whole text, the spoken sentence large and centered, tap a sentence to jump there, and scrolling by hand pauses following until *Back to current*; *Compact* (default) is the picture plus previous / current / next line. The minimize button shrinks everything to just the current line. The choice is remembered. Transport buttons stay perfect circles.
|
||||
- **Driving mode shows three lines of text instead of the whole article.** The previous sentence (small), the current one (large, with the spoken word marked) and the next one (small) sit in a fixed-height box, so the controls below never jump. Lines fade in smoothly (no animation when your device asks for reduced motion), the text is not a live region so a screen reader does not read out every sentence, it is readable at 375 px, and tapping the previous or next line jumps to that sentence. Pictures work as before.
|
||||
|
||||
### Performance
|
||||
- **Big sections render lazily, so the page stays inside its DOM-node budget.** The Magazine section's markup is now fetched and injected the first time you open it (or deep-link into it) instead of at startup; the Performance background-jobs history is fetched and drawn only while Performance is shown (it used to add about 470 nodes at startup); and the language pickers keep a single option in the hidden native `<select>` while the searchable popup renders the full list on demand (setting `select.value` programmatically still works). Together this removes roughly 1,000 DOM nodes from the idle page and fixes the failing `tests/constrained.spec.js` node-budget test (5,862 nodes against a 5,000 limit before, about 4,750 now). Deep links, keyboard and screen-reader behavior are unchanged.
|
||||
|
||||
### Fixed
|
||||
- **Driving mode lyrics view no longer leaves the previous line's word highlighted** when the playback moves to the next sentence.
|
||||
- **Sentence timings now also work for interview renders.** Every recorded audio file carries `"interview": true` (set whenever a second voice is configured, even without speaker labels), and the timings endpoint and EPUB export discarded all such records, so the player and the EPUB fell back to estimated sentence positions for every current article. The record is now matched to the text again, for interview renders by the label-stripped length the renderer recorded, so `/timings` returns exact (or anchored) sentence times and speech onsets for these articles too. Records for text edited since the render are still ignored.
|
||||
- **Character image runs are isolated per character.** The profile + reference-sheet job (`POST /api/character-generate-consistent-images`) could hand back an earlier character's job when the same `Idempotency-Key` was reused for a different character; the key is now bound to a fingerprint of the character's name, book, identity fields, style and prompts, and a mismatch is rejected with 409 instead of returning the other character's images. The fingerprint is not exposed in the status payload. New regression tests (`tests/test_character_image_isolation.py`) generate a dwarf and an elf back to back and concurrently and check that each ComfyUI request carries only its own prompts, props, identity image, seed family and result, and that the request data is not mutated.
|
||||
- **Characters table on phones: the rating stars can be tapped again.** At 320-414 px the pinned checkbox, picture and name columns (328 px) covered the stars of the voice rating; on phones they now scroll with the table, the rating column is wide enough for five 44 px stars, and each star has a *Rate n of 5* label. Browser tests check at 320, 375 and 414 px that no star touches a pinned column, is hit-testable, is at least 44 px and that the page itself does not scroll sideways; the phone case of the profile/rating test is no longer skipped.
|
||||
- **Highlight stays in step through seeking, speed changes and background tabs.** The highlighted sentence is now read from the audio's own position on every animation frame (and immediately on seeking, seeked, rate change, play, pause and when the tab becomes visible) using a binary search over the sentence start times, so there is no cached position to go stale; a jump into a silent gap keeps the sentence just spoken, and the marked word follows the sentence's own start and end, weighted by word length. New browser tests cover seeking back and forward, 1.5x and 2x, speed changes and rapid repeated seeks.
|
||||
@ -46,6 +43,9 @@ Follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) · versioned wi
|
||||
|
||||
---
|
||||
|
||||
### Performance
|
||||
- **Big sections render lazily, so the page stays inside its DOM-node budget.** The Magazine section's markup is now fetched and injected the first time you open it (or deep-link into it) instead of at startup; the Performance background-jobs history is fetched and drawn only while Performance is shown (it used to add about 470 nodes at startup); and the language pickers keep a single option in the hidden native `<select>` while the searchable popup renders the full list on demand (setting `select.value` programmatically still works). Together this removes roughly 1,000 DOM nodes from the idle page and fixes the failing `tests/constrained.spec.js` node-budget test (5,862 nodes against a 5,000 limit before, about 4,750 now). Deep links, keyboard and screen-reader behavior are unchanged.
|
||||
|
||||
## [1.56.3] — 2026-09-30
|
||||
### Fixed
|
||||
- **Deleting a picture left an empty paragraph in its place (seen on c't p. 157 and 159).** A picture region is stored as two records, a visual block and an image entry linked by `source_block`, and the review list shows either one as a row. Delete removed only the row's own record, so the twin stayed at the same spot as an empty, unticked "Paragraph" that could not be told from a real one. Deleting either half now removes both (single delete, bulk delete and the type changes), the articles forget both ids, and Undo brings both back; a hand-drawn picture (no twin) and a text block that was retyped since are untouched. Copies of a magazine that already carry leftovers: *Clean up boxes* re-types an empty picture block that has no picture (not narrated, still listed, reversible; your production issue has 1 such, on page 158). Verified on your real page 159 through the real route on a scratch copy: delete removes both, Undo restores both.
|
||||
@ -4669,7 +4669,8 @@ Initial feature-complete release.
|
||||
|
||||
---
|
||||
|
||||
[Unreleased]: https://github.com/mARTin-B78/tts-voice-creator-clone-and-design-2/compare/v1.56.3...HEAD
|
||||
[Unreleased]: https://github.com/mARTin-B78/tts-voice-creator-clone-and-design-2/compare/v1.57.0...HEAD
|
||||
[1.57.0]: https://github.com/mARTin-B78/tts-voice-creator-clone-and-design-2/compare/v1.56.3...v1.57.0
|
||||
[1.56.3]: https://github.com/mARTin-B78/tts-voice-creator-clone-and-design-2/compare/v1.56.2...v1.56.3
|
||||
[1.56.2]: https://github.com/mARTin-B78/tts-voice-creator-clone-and-design-2/compare/v1.56.1...v1.56.2
|
||||
[1.56.1]: https://github.com/mARTin-B78/tts-voice-creator-clone-and-design-2/compare/v1.56.0...v1.56.1
|
||||
|
||||
@ -6,7 +6,8 @@ Update in the same commit as the change it describes.
|
||||
## Current phase
|
||||
|
||||
Magazine projects, **P0 and P1 complete** (frontend reachable at Studio → Magazines). Audiobook/Rehearser/voice-library
|
||||
features are mature and in daily use.
|
||||
features are mature and in daily use. Latest release v1.57.0: magazine driving mode (views, index, night theme, share
|
||||
deep link) with exact sentence- and word-level text/audio sync via the `tts-aligner` sidecar.
|
||||
|
||||
## Deployment
|
||||
|
||||
@ -27,6 +28,13 @@ Two self-hosted Docker instances, both port 7890:
|
||||
"P2 — deployment" entry for the full setup and the one accepted limitation (voice-change auto-restart of the
|
||||
DGX's TTS containers doesn't reach across hosts — manual restart on the DGX, same as before this existed).
|
||||
|
||||
- **`tts-aligner`** (forced-aligner sidecar, `aligner/`) exists only in the DGX `docker-compose.yml`. The NUC compose
|
||||
file deliberately omits it: with `ALIGNER_URL` unset the app quietly falls back to estimated word timing, so the NUC
|
||||
works unchanged. Known limitation: no measured word times on the NUC.
|
||||
- **Rollback** (either host): check out the previous tag (`git checkout v1.56.3`; on the NUC re-rsync that tree), then
|
||||
`docker compose up -d --force-recreate` (rebuild only if `Dockerfile`/`requirements.txt` changed). Stop the aligner
|
||||
with `docker compose stop tts-aligner`; `audio/<id>.timings.json` version 2 files stay readable by v1 readers.
|
||||
|
||||
## Layout
|
||||
|
||||
| Path | Role |
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
<meta name="format-detection" content="telephone=no">
|
||||
<meta name="color-scheme" content="light dark">
|
||||
<meta name="theme-color" content="#2563EB">
|
||||
<meta name="app-version" content="1.56.3">
|
||||
<meta name="app-version" content="1.57.0">
|
||||
<link rel="manifest" href="/manifest.webmanifest">
|
||||
<link rel="icon" href="/static/icon.svg" type="image/svg+xml">
|
||||
<link rel="apple-touch-icon" href="/static/icon.svg">
|
||||
@ -27,12 +27,12 @@
|
||||
|
||||
<!-- ── Core styles (local — no CDN dependency for first paint) ────────── -->
|
||||
<link rel="stylesheet" href="/static/vendor/mdi/materialdesignicons.min.css">
|
||||
<link rel="stylesheet" href="/static/style.css?v=1.56.3">
|
||||
<link rel="stylesheet" href="/static/css/resource-diagnostics.css?v=1.56.3">
|
||||
<link rel="stylesheet" href="/static/css/background-jobs.css?v=1.56.3">
|
||||
<link rel="stylesheet" href="/static/css/voice-design.css?v=1.56.3">
|
||||
<link rel="stylesheet" href="/static/css/rehearser.css?v=1.56.3">
|
||||
<link rel="stylesheet" href="/static/css/audiobook.css?v=1.56.3">
|
||||
<link rel="stylesheet" href="/static/style.css?v=1.57.0">
|
||||
<link rel="stylesheet" href="/static/css/resource-diagnostics.css?v=1.57.0">
|
||||
<link rel="stylesheet" href="/static/css/background-jobs.css?v=1.57.0">
|
||||
<link rel="stylesheet" href="/static/css/voice-design.css?v=1.57.0">
|
||||
<link rel="stylesheet" href="/static/css/rehearser.css?v=1.57.0">
|
||||
<link rel="stylesheet" href="/static/css/audiobook.css?v=1.57.0">
|
||||
|
||||
|
||||
<!-- ── Flag icons — non-blocking (loaded async, icons appear after JS) ── -->
|
||||
@ -403,7 +403,7 @@ window.toggleNavTree = function(treeId, chevronId) {
|
||||
</script>
|
||||
|
||||
<!-- loader.js: fetches sections → loads JS modules → removes skeleton -->
|
||||
<script src="/static/loader.js?v=1.56.3"></script>
|
||||
<script src="/static/loader.js?v=1.57.0"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user