v2.0.0 "Omnibus"
An omnibus is a single volume that collects many works. This release is that:
sixteen features in one cut, the largest so far. The jump to 2.0 marks the
first public-facing surface (share links) - everything else stays fully
backward compatible.
Highlights
Your reading life, on one ribbon. The new Timeline tab on Reading Stats
draws every book you've ever read as a bar in time - one lane per series,
volumes as numbered bars, each day's reading shaded inside them. It's lifetime
and fully reconciled (imported KOReader history included), opens at today, and
zooms from years down to weeks. Hover any bar and it answers to the day:
"15 May · 22m".
Sync that has your back. Tome now keeps a short position history per
book, so the classic sync horror story - a device jumping a book to 100% and
propagating it everywhere - is one click to undo, falsely-finished status and
all. And on the device, the new sync on suspend toggles mean your morning
stats are current without ever waking the reader: close the lid, everything
pending catches up (with an aggressive variant that turns WiFi on first for
radio-sleepy devices like PocketBook - thanks @diogocardoso28 for the
request, #128).
The device browser grows shelves. KOReader's TomeSync browser now has four
ways in: series, search, authors - and your shelves from the web app,
resolved live with book counts. Your "German" shelf is one tap away on the
Kindle.
Reading history from your old life. Settings → Import takes a Goodreads
or StoryGraph export and fills the gaps - statuses, ratings, finish dates -
without ever overwriting what Tome already knows. Pre-Tome years finally land
on the timeline.
Share a shelf, a series, or a single book. Each can mint a revocable
public link - a clean read-only page with covers, titles, tags, descriptions,
your ratings and highlights, and your reading itself: time, days, finish
dates, and a day-by-day activity strip. Shared series mirror the real series
page, arcs and arc descriptions included. Links can expire on their own
(1/7/30 days) or live until revoked, and Settings -> Share links lists every
one you've minted. Never files: no downloads, no reading, no route to book
content at all, unguessable and unlisted. Your library stays yours; your
taste gets to travel.
Quality of life, everywhere. Cmd+K command palette. "Time left in chapter"
in the web reader at your measured pace. Edit highlight notes from the web,
synced back to the device. Push notifications to ntfy/Gotify/webhooks. Upload
dedupe before the bytes move. A cover-quality audit. One-click instance backup
with staged, safety-netted restore. A What's-New panel so features stop
shipping invisibly - starting with this release. An audit log that covers
everything again. And the mobile web app finally feels like an app: no more
sideways drag, edge bounce, or double-tap zoom.
KOReader plugin
This release ships plugin build 36 (1.10.0) - covering builds 32→36 for
anyone updating from 1.8.0: position pull strategy, device search + author
browse + status write-back, library sweep, sync on suspend, and shelves.
Upgrade note: the plugin updates gracefully in-app - TomeSync → Settings →
"Check for updates" (or enable auto-check). No breaking changes; existing
installs keep working untouched until you update.
Upgrade
docker pull ghcr.io/bndct-devops/tome:latest && docker compose up -d
No migrations, no config changes required. New optional env vars:
TOME_UPDATE_CHECK and TOME_OUTBOUND_NOTIFY (both default on, both purely
additive).
Full changelog
Added
- Share a shelf, a series, or a single book — metadata only, by design.
Shelves (share icon in the sidebar), series (share icon on the series
page), and individual books (Share button on the book page) can each mint
a revocable public link: a clean read-only page with covers, titles, tags,
descriptions, your ratings and highlights — and your reading of it: time,
reading days, finish dates, and a little day-by-day activity strip. It never exposes files — no
downloads, no reading, no route to book content at all; links are
unguessable, marked noindex, and die the moment you revoke them. - Reading Timeline — your reading life on one ribbon. A new Timeline tab on
Reading Stats draws every book you've ever read as a bar in time: one lane
per series (named in a frozen rail on the left), volumes as numbered bars
spanning first to last active day, and the intensity of each day's reading
shaded inside the bar. Lifetime data, including imported KOReader history,
reconciled the same way as the rest of stats. Zoom from a year-at-a-glance
down to month detail, hover any bar for the cover and totals, click through
to the book. The tab renders full-bleed — edge to edge, viewport-tall — and
the ribbon is also available as a regular tile in the dashboard gallery. - Sync on suspend (KOReader plugin, build 35). Two new opt-in settings
make morning stats current without waking the device (#128). "Sync on
suspend" catches up anything still pending — sessions, ratings, and the
reading-history backfill — when the device goes to sleep, provided WiFi is
already connected. "Aggressive sync" additionally turns WiFi on first for
devices that sleep the radio (e.g. PocketBook), letting the system power it
back down as the device suspends. Everything on this path is
queue-or-resume safe: if the device sleeps before the sync finishes, it
completes on the next connection. Reconnecting WiFi now also catches up the
reading-history backfill (previously launch-only) and flushes pending
sessions even before a book is opened. - Shelves on your device (KOReader plugin, build 36). The TomeSync series
browser gains a Shelves entry: your saved shelves listed with live counts,
each drilling into the same book list as a series — download per book or
all at once, read-status markers included. Shelf filters resolve
server-side (search, series, author, tag, format, language, library,
reading status, rating). - Sync closed books (KOReader plugin, build 34). A new TomeSync menu
action walks the device for books the plugin has never synced — read before
Tome existed, sideloaded, or opened under another launcher — matches them
against your library by content hash, and adopts each one's status, rating
and reading position from its KOReader sidecar. Adoption only fills what
Tome doesn't already have: it can never overwrite live sync state or your
own curation. The sweep is resumable (interrupting it loses nothing) and
cheap to re-run — books are skipped until their sidecar actually changes.
Matched books also become fully synced from then on, positions, highlights
and all. - Search your library from the device (KOReader plugin, build 33). The
series browser gains a "Search library…" entry: submit-based free-text search
over title, author, and series, with your last searches one tap away. Results
open the same drill-down list as a series — tap to download, hold to set read
status. - Browse by author on the device (build 33). A new author axis in the
series browser — the natural way into standalone books, which previously all
hid behind the single "No Series" bucket. Downloads file each book by its own
series/author identity, exactly like the web. - Set read status from the device browser (build 33). Hold any book row in
a series, author, or search list to mark it unread / reading / read on the
server — no need to open the book. The lists also show each book's current
status alongside the existing "on device" marker. - Position pull strategy (KOReader plugin, build 32). What happens on book
open when the server position differs from the device is now configurable,
like stock KOSync: "Server position is ahead" and "Server position is
behind" each offer Ask before jumping / Jump automatically / Do nothing
(TomeSync settings). Defaults keep the historic behavior — forward jumps
happen silently, backward jumps never — and the ask-dialog is deferred a
moment after open so it can never swallow a "on book opening" profile the
way the old layout-reset bug did. - Position history — undo a bad sync. Tome now keeps a short log of every
meaningful reading-position change per book (device, web, manual — the
idle heartbeat doesn't spam it). A history button on the book page's
Reading Stats header lists them, and any entry can be restored as the live
position with one click — including explicitly un-finishing a book that a
device falsely jumped to 100%. Devices pick the restored position up on
their next sync. The classic sync horror story is no longer unrecoverable. - Import your Goodreads or StoryGraph history. Settings → Import reading
history takes either service's CSV export, matches it against your library
(ISBN first, then title/author), and shows a full preview before anything
is written. Importing only fills gaps — statuses, ratings, reviews, and
finish dates you already have are never overwritten — and "to read"
shelves are skipped. Pre-Tome reading finally lands on the lifetime
timeline. - Push notifications to ntfy, Gotify, or any webhook. Settings →
Notifications lets each user add outbound channels; every in-app
notification (wish fulfilled, new volume detected, reading goals) is
pushed the moment it happens instead of waiting for the next visit. Each
channel has a test button, can be paused, and tokens are stored
server-side only.TOME_OUTBOUND_NOTIFY=falseswitches the whole feature
off; nothing is sent unless a user configures a channel. - "Time left in chapter" in the web reader. The reader footer now shows
"~12 min left" beside the chapter name, computed from the book's chapter
map and your own measured reading pace (a sensible default until you have
reading history). Quietly absent for books without a chapter map or word
count. - Edit highlight notes from the web. The Highlights page and the book
page's Highlights & Notes section can now edit (or add) the note on any
highlight — including ones made in KOReader — not just delete them. Edits
win on your devices at their next sync, exactly like an edit made on
another device. - Command palette. Press Cmd+K (Ctrl+K) anywhere to jump straight to a
book, series, author, or page — full-text book search with covers, ranked
series/author matches, and quick navigation, all keyboard-driven. The
header search box hints it, and it's listed under "?" shortcuts help. - Search previews everywhere. On Stats, Highlights, Wishlist, and the
other section pages, typing in the header search now shows a live dropdown
of top matches (Enter still opens the full filtered grid) — previously
those pages gave no feedback until Enter. - Upload knows what you already have. Files added to the upload dialog are
hashed in the browser and checked against the library before anything is
sent — exact duplicates get an "already in your library" note with a link to
the existing book and are skipped, instead of uploading megabytes just for
the server to silently discard them. - Admin → Covers: cover-quality audit. Lists books whose covers are
missing, unreadable, or genuinely low-resolution (real thumbnails, not
standard-source covers), with sizes shown. Books with no cover at all offer
a one-click auto-fix from the cover search (nothing to downgrade); low-res
ones deep-link to the book page to pick a better candidate by eye. - One-click instance backup and staged restore. Admin → Server gains an
Instance backup card: download a consistent snapshot of everything Tome
knows (database + covers + manifest; book files stay on disk), and restore
one by uploading it — the restore is validated, requires typing RESTORE,
and applies at the next server restart so it never happens under a live
database. The previous database is kept alongside as a safety copy. - "What's new" after an upgrade. The first visit after the server moves to
a new release shows a one-time panel with that release's notes, straight
from the changelog — so features stop shipping invisibly. Dismiss it and it
stays gone until the next release; fresh installs never see it. - Update indicator for admins. Settings → About quietly shows "vX.Y.Z
available" (linking to GitHub releases) when a newer release exists, via a
daily-cached server-side check. SetTOME_UPDATE_CHECK=falseto disable
the lookup entirely — nothing else phones home. - Timeline: the tooltip now answers at day level. Hovering a bar resolves
the exact day under the cursor — "15 May · 22m", or "no reading" on a gap
day — alongside the book's totals. The day data was always drawn as tick
intensity; now it's readable. - Timeline on phones. The series rail narrows so the ribbon keeps most of
the screen, and bars are tap-friendly: the first tap shows the details
tooltip, a second tap opens the book (tapping empty space or scrolling
dismisses it). Previously any tap navigated away immediately. - Highlights page polish. The deferred cluster from the original
commonplace-book release: an only-notes filter (show just the highlights
carrying your own notes — composes with search and on-this-day), a real
file export (download all matching highlights as a Markdown file, next to
the existing copy-to-clipboard), keyboard shortcuts (/search,Esc
clear,ccollapse all,nonly-notes,eexport — listed in the?
help), and a shuffle button on the Home tab's highlight spotlight that
re-rolls to a different quote. - Time per chapter. Book pages now show where your reading time went
chapter by chapter: the book's table of contents is extracted at ingest into
device-independent chapter boundaries, and KOReader per-page reading data is
mapped into them — robust to font/margin changes, since every page record is
interpreted against its own pagination. Renders on the book detail page next
to the reading-intensity curve whenever both a chapter map and synced page
data exist. Existing libraries get chapter maps via the Admin → Word Counts
backfill, which now also extracts chapters (and intrinsic page counts, below)
in the same pass. - Intrinsic page counts for fixed-layout books. PDFs and comic archives now
store their real page count at ingest (EPUB deliberately doesn't — reflowable
pagination is not a property of the book). Backfilled by the same admin job.
Fixed
- The audit log covers new features again. A coverage pass wired audit
entries into everything that had shipped without them: instance backup
download and restore staging/cancel/apply, OPDS PIN and KOReader plugin
key lifecycles, book type and series/arc curation, Hardcover account
link/unlink, notification channels (never the token), reading-history
imports, cover changes, bulk downloads, session deletes, highlight
deletes, and position restores. Device sync telemetry and personal
micro-writes stay deliberately unlogged. - Stats on phones: dead gap and misaligned range picker. The Stats page
header paid the notch inset a second time inside the app shell (the top bar
already covers it), opening a large empty band between the top bar and the
page title on the installed app; and when the timeframe picker wrapped below
the title on narrow screens it stayed floated right, reading as misaligned.
The header now sits flush and the picker takes a full, left-aligned row. - The mobile app no longer feels like a draggable website. On phones the
page content could be dragged sideways and rubber-banded (on iOS the Focus
view's scaled cover fan still counted its full unscaled width as scrollable
overflow), the viewport bounced past its edges, double-tap zoomed, and a
long press selected interface text — classic browser-tab behavior. The app
now clamps content to the viewport width, suppresses edge bounce and scroll
chaining, removes double-tap zoom (taps fire immediately), disables text
selection on interface chrome for touch devices (text fields and the reader
keep it), and sizes the shell to the real visible viewport instead of
overhanging behind mobile browser toolbars. - The notifications panel no longer renders behind sticky page content. The
top bar sat at the same layer as page-level sticky toolbars (and below the new
timeline axis), so the open notification dropdown was cut by the Stats
toolbar. The top bar now stacks above content-level sticky elements. - Chapter maps now extract from EPUB2 books. Older EPUBs keep their table
of contents in an NCX file rather than an EPUB3 nav document; chapter
extraction ignored the NCX, so classics and older rips silently produced no
Time-per-chapter data. Extraction now falls back to the NCX whenever the nav
yields nothing — re-run Admin → Word Counts to pick up the affected books. - TOC-less EPUBs no longer re-queue in the Word Counts backfill forever.
Books whose files genuinely have no usable table of contents were re-parsed
on every run because "done" was inferred from having chapter rows; a
dedicated attempt marker now records "tried, nothing there" once. - A fast server clock can no longer silently swallow highlights (build 32).
Highlight edits and deletes made in the web reader are stamped with the
server's clock; on a device whose clock runs behind (UTC container vs local
device time), those stamps landed in the device's future and outranked every
later local change — a re-highlight after a web delete just vanished until
the clocks crossed. The plugin now stamps its wall-clock on every highlight
sync and the server shifts the stamps it minted itself into that device's
clock frame, both when comparing and in what it returns; the plugin
additionally refuses to store any stamp from the future. Older plugins keep
the previous behavior. - The TomeSync plugin no longer bloats KOReader's global settings file
(build 32). Its data tables (book map, pending sessions and ratings, sync
baselines, repair aliases) — which grow with your library and were parsed by
KOReader at every boot — moved into a dedicatedtomesync_state.lua,
migrated automatically and crash-safely on first launch. State for books no
longer on the device is pruned; pending queues are never pruned. - The plugin's series list no longer runs one query per series. Loading the
series browser was an N+1 that scaled with the library; it's now a single
query with the same response. - The Highlights "copy all as Markdown" export never worked. It requested
more highlights than the API's page cap allowed, got a 422, and failed
without any feedback. The cap is raised, the export stays under it, and a
failure now shows an error toast instead of silently doing nothing. - Bindery no longer invents a series from the filename. Prose ebooks named
Title 5.epubused to come out of review with their series set to their own
title (the bare-number manga-chapter heuristic misfiring on novels); the
real series name in(Series Book N)parentheticals — the common
Amazon/Calibre naming — was stripped as noise; and the metadata embedded in
the file itself (calibre:seriesetc.) was extracted but never reached the
review form. All three are fixed: the filename parser now understands
(Series Book N)/(Series #N)markers and theNN. Title - Author (Year)
layout (filling the author field too), bare trailing numbers on EPUB/PDF no
longer fabricate a series, and the review form pre-fills from embedded
metadata with the same "file beats filename" precedence auto-import has
always used.