Mobile layout, Status page, backup viewing and targeted restore, typed page mirrors, and dependency updates #41

Merged
jfro merged 12 commits from backups-status-and-mobile into main 2026-10-17 19:58:46 +00:00
Owner

Closes #38, closes #39, closes #40, closes #25. Part of #28 (whole-snapshot restore stays open there).

Features and fixes

  • Mobile layout (#38) ddefe00: on phones the top bar is ☰ + search (+ the library's sync), everything else in a left drawer; the page runs under the status bar with viewport-fit=cover and keeps clear of it with env(safe-area-inset-*) (sticky header, chat panel, search palette, toasts, drawer); browser bars in the page's colors; the viewer's controls drop to their own row.
  • Download a whole backup (#39) b0cef5e: "Download all" streams a snapshot as a zip (zstream, zip64, stored) of .rmdocs in its folder tree; unreadable items are left out and listed in MISSING.txt.
  • Status page and header alert (#40) 3731716: failed syncs, backups, handwriting provider, failed/retrying background jobs (grouped, with the pages they were for), with Retry/Dismiss/Sync now/Back up now. Oban's Pruner now keeps finished jobs a week (it pruned discarded ones after 60 s, so failures vanished).
  • Backup viewer, compare and targeted restore (#28) b3bcd0e: a snapshot's items open in the regular viewer, read only, with a banner (when, and unchanged/changed/trashed/gone now), "Compare with now" side by side without the transcription, and the snapshot's folder path. Restore an item into the app only (a copy in Only here) or as a copy on the tablet (new item, "… (backup Oct 16)", in its folder if still there), or add a backed-up page back to the end of its notebook. Tablet restores are queued and written after the next sync by Backups.RestoreWriter (like the tag writer: snapshot saved, 412 retried). Files of a viewed backup are fetched from the backup into the blob store (hash-checked) and served to that user for 12 h (Backups.Grants). Failed restores show on the Status page.
  • "Imported" is now "Only here" e03c9f4: it holds restores as well as imports; the point is it's not on the tablet. Internal id stays imported.
  • Typed mirrors of pages (#25) 2647a8a: "Mirror as a typed page" adds a typed page with the page's transcription right after it (PageMove :add with :after, an idx between its neighbours as the tablet does), and types new text over it as the transcription changes (:replace). A mirror edited on the tablet is left alone (re-type on request); deleted ones are dropped. Mirrors.Writer after each sync; a transcription change of a mirrored page asks for a sync. See docs/page-mirrors.md.
  • Sync status on every page e8005c7: moved from the library's own header into the shared one via an on_mount hook (RmAnalyzerWeb.SyncStatus) that subscribes once, keeps @sync, ticks the "… ago" and handles the button; it also now appears right after pairing.

Dependencies

  • 851300a security: bandit 1.13.1 (EEF-CVE-2026-105659 HIGH, -105660), decimal 3.2.0 (EEF-CVE-2026-97853). mix hex.audit clean.
  • bf1d669 oban 2.24.2, req 0.7.5, finch 0.24, swoosh 1.29, phoenix_pubsub 2.4.1, mdex 0.14.2, rustler_precompiled 0.10, dns_cluster 0.3.1; new: zstream 0.6.
  • e10a2ab Erlang 29.1.2, Node 24.22.0, Debian trixie-20261005 (mise.toml, Dockerfile, CI image).
  • 889a25e pdfjs-dist 6.4.299; d319f91 daisyUI 5.7.50 (pixel-identical on 17 pages light + 6 dark).

Migrations

backup_restores, page_mirrors.

Testing

760 tests pass; --warnings-as-errors (test and prod), format --check-formatted, deps.unlock --check-unused, hex.audit and npm audit clean; the production Docker image builds (OTP 29.1.2 inside). Everything that writes to the tablet (tablet restores, page restores, mirrors) was tested only against the fake cloud: nothing was written to a real account. Checked in headless Chromium at desktop, 390 px and 360 px widths against a synced dev library.

Closes #38, closes #39, closes #40, closes #25. Part of #28 (whole-snapshot restore stays open there). ## Features and fixes - **Mobile layout (#38)** `ddefe00`: on phones the top bar is ☰ + search (+ the library's sync), everything else in a left drawer; the page runs under the status bar with `viewport-fit=cover` and keeps clear of it with `env(safe-area-inset-*)` (sticky header, chat panel, search palette, toasts, drawer); browser bars in the page's colors; the viewer's controls drop to their own row. - **Download a whole backup (#39)** `b0cef5e`: "Download all" streams a snapshot as a zip (zstream, zip64, stored) of `.rmdoc`s in its folder tree; unreadable items are left out and listed in `MISSING.txt`. - **Status page and header alert (#40)** `3731716`: failed syncs, backups, handwriting provider, failed/retrying background jobs (grouped, with the pages they were for), with Retry/Dismiss/Sync now/Back up now. Oban's Pruner now keeps finished jobs a week (it pruned discarded ones after 60 s, so failures vanished). - **Backup viewer, compare and targeted restore (#28)** `b3bcd0e`: a snapshot's items open in the regular viewer, read only, with a banner (when, and unchanged/changed/trashed/gone now), "Compare with now" side by side without the transcription, and the snapshot's folder path. Restore an item **into the app only** (a copy in Only here) or **as a copy on the tablet** (new item, "… (backup Oct 16)", in its folder if still there), or **add a backed-up page back** to the end of its notebook. Tablet restores are queued and written after the next sync by `Backups.RestoreWriter` (like the tag writer: snapshot saved, 412 retried). Files of a viewed backup are fetched from the backup into the blob store (hash-checked) and served to that user for 12 h (`Backups.Grants`). Failed restores show on the Status page. - **"Imported" is now "Only here"** `e03c9f4`: it holds restores as well as imports; the point is it's not on the tablet. Internal id stays `imported`. - **Typed mirrors of pages (#25)** `2647a8a`: "Mirror as a typed page" adds a typed page with the page's transcription right after it (`PageMove :add` with `:after`, an idx between its neighbours as the tablet does), and types new text over it as the transcription changes (`:replace`). A mirror edited on the tablet is left alone (re-type on request); deleted ones are dropped. `Mirrors.Writer` after each sync; a transcription change of a mirrored page asks for a sync. See `docs/page-mirrors.md`. - **Sync status on every page** `e8005c7`: moved from the library's own header into the shared one via an `on_mount` hook (`RmAnalyzerWeb.SyncStatus`) that subscribes once, keeps `@sync`, ticks the "… ago" and handles the button; it also now appears right after pairing. ## Dependencies - `851300a` **security**: bandit 1.13.1 (EEF-CVE-2026-105659 HIGH, -105660), decimal 3.2.0 (EEF-CVE-2026-97853). `mix hex.audit` clean. - `bf1d669` oban 2.24.2, req 0.7.5, finch 0.24, swoosh 1.29, phoenix_pubsub 2.4.1, mdex 0.14.2, rustler_precompiled 0.10, dns_cluster 0.3.1; new: zstream 0.6. - `e10a2ab` Erlang 29.1.2, Node 24.22.0, Debian trixie-20261005 (mise.toml, Dockerfile, CI image). - `889a25e` pdfjs-dist 6.4.299; `d319f91` daisyUI 5.7.50 (pixel-identical on 17 pages light + 6 dark). ## Migrations `backup_restores`, `page_mirrors`. ## Testing 760 tests pass; `--warnings-as-errors` (test and prod), `format --check-formatted`, `deps.unlock --check-unused`, `hex.audit` and `npm audit` clean; the production Docker image builds (OTP 29.1.2 inside). Everything that writes to the tablet (tablet restores, page restores, mirrors) was tested only against the fake cloud: nothing was written to a real account. Checked in headless Chromium at desktop, 390 px and 360 px widths against a synced dev library.
jfro added 12 commits 2026-10-17 19:49:38 +00:00
bandit < 1.13.0: EEF-CVE-2026-105659 (HIGH, HTTP/2 send deadlines reset
by WINDOW_UPDATE) and EEF-CVE-2026-105660 (HTTP/1 request-target authority
sets conn.host). decimal < 3.1.2: EEF-CVE-2026-97853 (unbounded allocation
in Decimal.round/3).
fix: the sync status is in the header of every page, not just the library's
All checks were successful
CI / test (pull_request) Successful in 1m58s
e8005c795e
jfro merged commit 66cbf75418 into main 2026-10-17 19:58:46 +00:00
jfro deleted branch backups-status-and-mobile 2026-10-17 19:58:47 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
jfro/rm-analyzer!41
No description provided.