Skip to content

Release v1.8.0 - #252

Merged
Gnathonic merged 88 commits into
mainfrom
develop
Jul 6, 2026
Merged

Release v1.8.0#252
Gnathonic merged 88 commits into
mainfrom
develop

Conversation

@Gnathonic

Copy link
Copy Markdown
Owner

Summary

  • OneDrive and Local Folder cloud sync providers
  • Paged mode page-gap setting + ctrl/cmd+shift+scroll chord ([Feature request] Page gap for Dual Page mode #234)
  • Google Drive reconnect reliability fix (cross-device progress clobber) + UX simplification
  • Pre-release bug fixes found via multi-agent code review (OneDrive upload false-failures/retry, filesystem rename data-loss guards, export sidecar regression, gdrive reconnect double-fire)

See CHANGELOG.md [1.8.0] for the full user-facing list.

Gnathonic and others added 30 commits April 23, 2026 12:24
Add design doc for a File System Access API backed sync provider. Feature-gated
on Chromium browsers; handle persisted in a dedicated IndexedDB database owned
by the provider module.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add filesystemCore stub to cloud-provider-core-registry (never called
  at runtime since FilesystemProvider.supportsWorkerDownload = false)
- Widen VolumeMetadata.cloudProvider from narrow literal union to
  ProviderType, fixing cascading errors in cloud-fields.ts,
  library-placeholders.ts, and download-queue.ts

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
processDownload previously only handled supportsWorkerDownload=true. For the
filesystem provider (which sets that to false), downloads were silently
skipped — the queue item stayed in 'downloading' state forever and blocked
all subsequent items. Add a main-thread branch that downloads via the
provider's downloadFile method, extracts the CBZ with @zip.js/zip.js, and
reuses the existing downloadSidecarEntries + processVolumeData pipeline.
The regex on error.message did not match Chrome's DOMException.message
(which does not contain the string 'NotFoundError'), causing spurious
DELETE_FAILED errors when a series folder was already gone during sync.
…ders

The filesystem provider cannot upload from a worker because a
FileSystemDirectoryHandle is bound to the window that received it via
showDirectoryPicker — structured-cloning it to a worker does not carry the
user-granted permission. Introduce a supportsWorkerUpload flag on SyncProvider
(parallel to the existing supportsWorkerDownload) and, when a provider opts
out, compress + upload the archive and any sidecars on the main thread via
provider.uploadFile. Worker-capable providers (Google Drive, MEGA, WebDAV)
are unaffected.
Previous fix bypassed the pool entirely and ran compression + upload on the
main thread for filesystem. With a parallel queue that meant N volumes could
load, compress, and buffer simultaneously with no shared-memory accounting
and no provider concurrency cap.

Instead, submit the same pool task used for real providers but in the worker's
'null provider' compress-only mode. The worker still does the CPU-heavy
compression (throttled by WorkerPool + sharedMemoryManager); the main-thread
onComplete then performs the single filesystem write via provider.uploadFile
while the task's memory reservation is still held. Sidecars follow the same
path. Worker-upload providers (Drive, MEGA, WebDAV) are unchanged.
Previous fix bypassed the pool and did blob read + decompression + processing
on the main thread for filesystem. Even with the queue being serial that
blocks the UI during decompression and leaves memory untracked by the shared
memory manager.

Instead, use the worker's existing decompress-only mode: prepareData reads
the blob on the main thread (required — FileSystemDirectoryHandle is bound to
the window) and passes it to the worker, which decompresses and returns
entries. Memory reservation, provider concurrency gating, and shared memory
tracking all apply uniformly with the existing worker-download path.
Adds design for a fourth real sync provider using Microsoft Graph API via
MSAL.js. Multi-tenant (common) auth covers personal + work/school accounts.
Worker-capable core follows the Drive/WebDAV pattern so WorkerPool throttling
applies uniformly.
Gnathonic and others added 26 commits July 5, 2026 14:09
The Svelte 5 rewrite of QuickActions (replacing Flowbite's SpeedDial)
dropped the background/shadow on the toggle button, leaving only a
bare plus icon that disappears against light backgrounds.

Fixes #249
feat: OneDrive and Local Folder sync providers + Google Drive re-auth overhaul
Rendered text must never overlap — whatever the quads claim, two visible
lines painting the same pixels is a wrong layout. OPM 28 exposed two ways
sub-threshold overlaps slipped through the re-capture gate (0.7):

- p136: the detector re-captured one caption column as two half-width-
  offset quads with diverged hallucinated texts (bbox overlap 0.41), so
  both rendered stacked on the same ink.
- p176: a wide slanted quad — detector noise, the print is upright — has
  an axis-aligned bbox that swallows the neighboring columns, and the
  merged-column wrap then painted across them.

New final pass in layoutLines resolves every residual rendered-rect
overlap. Clean single-column lines hold their ground; suspect/wrapped/
banded lines are clipped around every clean rect they touch and re-wrap in
the widest surviving gap — which recovers the true column between the
neighbors a slant-inflated quad claimed. Equal-trust collisions first try
nudging apart within their quads' own slack (uniform size preserved; first
round only, since nudging claims new ground), then split the contested
span at its midpoint. Every clip yields a subset of the previous extent,
so resolution never creates a new overlap and converges.

Corpus sweep over 2,193 local .mokuro files (3.17M blocks): the old layout
rendered overlapping text in 106,988 blocks (3.4%); all are resolved with
zero residual overlaps and zero new violations.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
feat(reader): page gap for dual-page spreads + ctrl+shift+scroll (#234)
…n creation

A dropped/timed-out response for the final chunk (or a retry exhausted
against a transient status) was thrown as a hard failure even when Graph
had already finalized the file server-side. Check by path+size before
giving up. Also honor the Retry-After header on 429s instead of a fixed
backoff, and retry transient failures when creating the upload session
(previously only chunk PUTs were retried).
renameFile() overwrote an existing destination with zero conflict check,
unlike WebDAV/OneDrive — now throws unless the occupant matches the
source's size (idempotent-retry convergence, same rule as WebDAV).

renameFolderInner() only renames the syncable files under a folder but
then recursive-deleted the entire old folder, destroying any other file
or subfolder a user had stored there. Now prunes only if verifiably
empty, via the existing removeDirectoryIfEmpty helper.
Merging the export-to-file path with the new main-thread-upload
(filesystem provider) path forced embedMokuroInArchive: false for both,
regressing plain "export to file" — which previously always embedded OCR
data — to the cloud-provider sidecar convention. Only the upload path
should use a sidecar; export-to-file keeps the self-contained archive.
A manual "Reconnect" click is simultaneously the pointerdown that
satisfies an armed gesture-retry AND the button's own click handler, so
both could call requestAccessToken() on the same tick. Wire up the
already-present (but previously unused) isRefreshing flag as a guard.
@vercel

vercel Bot commented Jul 6, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
mokuro-reader Building Building Jul 6, 2026 6:28am
mokuro-reader (develop) Ready Ready Preview, Comment Jul 6, 2026 6:28am

Request Review

@Gnathonic
Gnathonic merged commit 633d3e2 into main Jul 6, 2026
14 checks passed
adrian-tompkins pushed a commit to adrian-tompkins/mokuro-reader that referenced this pull request Aug 2, 2026
…detection

feat: Add import mismatch detection with detailed error modal
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