Skip to content

feat(reader): time left in chapter#138

Merged
bndct-devops merged 6 commits into
mainfrom
feat/chapter-time-left
Jul 19, 2026
Merged

feat(reader): time left in chapter#138
bndct-devops merged 6 commits into
mainfrom
feat/chapter-time-left

Conversation

@bndct-devops

Copy link
Copy Markdown
Owner

Overnight run PR 7/12 — stacked on #137; merge in order.

The KOReader-style "time left in chapter", from two systems Tome already ships: the TOC chapter maps (fraction-of-book boundaries) and the user's true WPM.

  • GET /books/{id}/reader-pacing: chapter boundaries + book word count + personal WPM (exact same rule as the stats tile — finished word-counted books with ≥5 min reconciled read-time; null without history).
  • Reader footer shows "Chapter Name · ~12 min left": words left = word_count × (chapter.end_fraction − current fraction), divided by personal WPM (250 default until history exists — the tooltip says which). No pagination math involved, so it's device/layout-independent.
  • Quietly absent for books without a chapter map or word count (CBZ, unprocessed EPUBs).
  • Race handled: the initial relocate fires before the pacing fetch resolves, so arrival triggers a recompute from the last seen fraction (caught in live verification, fixed, re-verified).

Verification: 2 new backend tests (payload shape, WPM derivation: 60k words / 200min → 300 wpm exactly); live reader on dev shows "Color Illustrations · ~2 min left" against a real 395-wpm personal pace. Build/typecheck clean.

bndct-devops and others added 6 commits July 18, 2026 23:46
- The tooltip resolves the day under the cursor (date + that day's minutes,
  or "no reading" inside a gap) below the book totals — the per-day data was
  only legible as tick opacity before.
- Phones (<480px container): the rail narrows 200->96px so the ribbon keeps
  the viewport; coarse-pointer taps inspect first (tooltip), navigate on the
  second tap on the same bar. Scroll or empty-space tap dismisses.

Book-page activity sparkline (planned as part of this PR) was investigated
and dropped: the per-book stats hero already charts per-day activity for
both live sessions and imported page-stats (sessions are synthesized from
page-stats), so the strip would duplicate an adjacent chart.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
GET /api/meta/whats-new parses CHANGELOG.md for the running version's
section (Unreleased fallback for dev builds); the frontend shows a one-time
dismissible panel when the server version differs from the last one this
browser saw. Fresh browsers baseline silently — first login never opens
with a modal. Panel portals to <body>: the header that mounts it has
backdrop-blur, which would otherwise contain the fixed overlay.

GET /api/meta/update-check (admin-only) compares against the latest GitHub
release, cached in memory 24h (1h after failures), gated by
TOME_UPDATE_CHECK (default true). Settings -> About shows a quiet
"vX.Y.Z available" link only when something is newer.

CHANGELOG.md is now copied into the Docker image for the panel.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Upload dialog: files are sha256-hashed in the browser (sequential, 512MB
cap) and checked via the existing POST /books/check-hashes before upload;
exact duplicates show "already in your library" with a link and are
skipped. The server already deduped silently — this saves the transfer
and makes it visible. Hash-check failures degrade to the old behavior.

Admin -> Covers (GET /admin/covers/audit): flags missing, unreadable, and
low-res covers (PIL header reads only). The floor is 300px — the standard
Google Books cover is 329px and fine in practice; the audit exists for the
98-128px thumbnails. Missing covers offer one-click auto-fix via the
existing cover-candidates + set-cover endpoints (nothing to downgrade);
low-res rows deep-link to the book's cover picker.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Global palette mounted from the shared header: full-text book search via
GET /books?q= (debounced, covers in rows), series/authors ranked from the
facets list client-side (startsWith > includes > shorter), and plain
navigation actions (admin sees Admin). Arrow keys + Enter, Esc closes,
portaled past the header's backdrop-blur containing block. Registered in
the "?" shortcuts help.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
New position_history table (additive, created by the startup create_all
like every other table): upsert_position — the single choke point all
position writers go through — appends an entry whenever the position moves
meaningfully (>=0.2% or locator change; the idle heartbeat can't spam it),
pruned to the newest 40 per user+book.

GET /books/{id}/position-history lists the log + live position;
POST .../{hid}/restore sets the live position back. Restore is an explicit
override of the sticky-completion rule: recovering from a false 100% also
un-finishes the book (status/finished_at), or the position would come back
while the book stayed "read". The restore itself is logged, so it can be
undone the same way. Devices converge on their next pull.

UI: history button on the book page's Reading Stats header opens the log
with per-entry Restore; refreshes stats + status pill after.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
GET /books/{id}/reader-pacing returns the chapter fraction boundaries,
book word count, and the user's true WPM (same rule as the stats tile:
finished word-counted books with >=5min reconciled read-time; null without
history). The reader computes words-left from the current relocate
fraction against the chapter's end_fraction — no pagination involved —
and shows "~N min left" in the footer beside the chapter name (250wpm
default, tooltip says which pace is in use). Recomputes when pacing
arrives, since the initial relocate races the fetch.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@bndct-devops
bndct-devops changed the base branch from feat/position-history to main July 19, 2026 10:10
@bndct-devops
bndct-devops merged commit 5fd383f into main Jul 19, 2026
@bndct-devops
bndct-devops deleted the feat/chapter-time-left branch July 19, 2026 10:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant