diff --git a/README.md b/README.md index acc92a0..30d46e7 100644 --- a/README.md +++ b/README.md @@ -8,10 +8,11 @@ firmware contains Qt 6.10.3, the `pocketbook2` platform plugin and Other devices, including Verse Pro, need separate validation. - **Hello** is a small Qt Quick example using PocketBook controls. -- **[Readest Sync](apps/readest-sync/README.md)** downloads EPUBs and synchronizes - native-reader progress with Readest Cloud. Its Qt6 UI has been launched on InkPad; - full device acceptance of the latest build is pending. The previous InkView UI's login, - networking, downloads, progress sync and cover browsing were user-confirmed. +- **[Readest Sync](apps/readest-sync/README.md)** browses PocketBook and Readest + books, downloads/uploads EPUBs, and synchronizes native-reader progress, + highlights and attached notes. The current Qt6 app is device-validated on the + target above. See the [illustrated user guide](apps/readest-sync/docs/USER-GUIDE.md) + for screenshots captured with the simulator's mock cloud. ## Build diff --git a/apps/readest-sync/README.md b/apps/readest-sync/README.md index d86c992..c600eb1 100644 --- a/apps/readest-sync/README.md +++ b/apps/readest-sync/README.md @@ -1,24 +1,29 @@ # Readest Sync -Native PocketBook app for downloading DRM-free EPUBs from Readest Cloud and -synchronizing reading progress with Readest on iPhone, browser and KOReader. - -**Status: Qt6 UI starts on InkPad; full acceptance of the latest build is pending.** -The previous InkView implementation's login, Wi-Fi recovery, downloads, -bidirectional resume and cover browsing were user-confirmed. Qt Quick now owns -screens, input, timers and cloud cover decoding, using firmware PocketBook -controls. InkView remains only in the device adapter for startup, Wi-Fi, native -reader handoff and local EPUB cover extraction. Existing data formats and native -progress safeguards are retained. The supported native handoff target remains -InkPad 4 (PB743G), firmware `U743g.6.11.1683`; Verse Pro is deferred. -Host tests cover authentication, HTTPS, integrity, download recovery, native -position safeguards, reconciliation, and Qt UI behavior. The device acceptance -checklist is at the end of this README. Diagnostic fixture generation and the -backup-and-verify installer are described below. +Native PocketBook app for browsing local and Readest libraries, transferring +DRM-free EPUBs, and synchronizing reading progress, highlights and attached notes. + +**Device-validated on InkPad 4 (PB743G), firmware `U743g.6.11.1683`.** +The Qt Quick interface uses PocketBook controls. Library browsing, downloads, +reader handoff, progress synchronization, bidirectional annotation changes, and +startup/Home behavior have been checked on this device. Other models and firmware, +including Verse Pro, require separate validation. Automatic orientation changes +remain an [open device limitation](https://github.com/42lizard/pocketbook/issues/25); +the simulator can exercise both layouts. + +- [Illustrated user guide](docs/USER-GUIDE.md): library, menus, downloads, reading, + position conflicts, highlights and notes. +- [Installation instructions](release/README.md). +- [Mock simulator scenarios](../../tools/pocketbook-simulator/apps/readest-sync/README.md). +- [Annotation protocol and native storage](docs/research/readest-annotation-protocol.md). + +The user guide contains screenshots from the **Mock cloud** simulator, with +synthetic books only. Simulator rendering approximates the firmware controls; +e-ink refresh, the native keyboard and native reader remain device features. ## Application structure -QML screens use named commands and explicit controller properties. Library tiles +QML screens use named commands and explicit controller properties. Library rows come from `LibraryModel`, whose roles include account/hash identity, availability, cover path and both reading percentages. Search, availability filtering and paging operate on this in-memory model; the view supplies its page capacity. @@ -42,7 +47,9 @@ partial outcome and does not open the reader; the next Sync reconciles fresh sta An uncertain native commit is also reported explicitly, without an automatic retry. `OperationRunner` runs one operation at a time, joins it before publishing its -result, and owns cancellation, network timeout and keepalive timing. Device +result, and owns cancellation, network timeout and keepalive timing. Online and +offline operations prevent standby through result delivery. A one-second firmware +grace period allows queued UI updates before normal power saving resumes. Device connection callbacks retain their own lifetime token: a late callback cannot access a destroyed runner. A foreground operation can take over the pending connection of a cancelled cover request, retaining its original timeout instead @@ -77,7 +84,7 @@ work and completions to exercise cancellation, retries and account/page changes. commands, independent controller instances, stable book identities, filtering, worker shutdown and late callbacks; simulator scenarios cover native sync and reader handoff. This refactor does not change persisted formats or native-position -validation rules. Hardware acceptance of the refactored build is still required. +validation rules. Device acceptance complements these automated checks; repeat it for new firmware or behavior changes. ## Build and package @@ -191,7 +198,8 @@ make them confidential through Unix file permissions. Sign-out removes the saved session without revoking sessions on the user's other devices. `operations.log` records refresh phases, process IDs, timestamps and peak memory -in KiB to help diagnose device exits. It contains no credentials or book data and +in KiB to help diagnose device exits. It includes monotonic milliseconds for +elapsed-time comparisons; the wall clock alone can change or span suspend. It contains no credentials or book data and is truncated at 64 KiB. It is a best-effort phase log, not a crash backtrace. New EPUB downloads use `Title - Author.epub` (or `Title.epub` when no author is @@ -200,15 +208,18 @@ are replaced with spaces and long names are shortened. Separate managed directories prevent books with the same name from overwriting each other. Existing downloads keep their filenames and reading positions. -Existing EPUBs on internal storage and SD card are matched to the Readest library -by content hash during refresh and before a download. Startup displays the cached -library without a device-wide scan. Matching books -are registered at their original paths; they are not copied, renamed, or modified. -Use **Scan device** to find newly copied books without Wi-Fi. Scans run in the -background and can be cancelled; large collections take longer to inspect. -System/hidden folders, symlinks, and the app's managed download directory are -excluded (managed downloads use their account-specific recovery metadata). -Only matching Readest library entries are linked; unrelated books are left alone. +Startup loads cached cloud metadata and scans the **PocketBook library index** +without requesting Wi-Fi. It does not recursively walk every storage directory. +Indexed EPUBs appear even when signed out. **Menu → Scan device** updates this +inventory; **Refresh library** also refreshes cloud metadata and availability. +Unindexed files must first be discovered by the PocketBook library. + +Matching uses EPUB content fingerprints rather than titles. Existing files stay +at their original paths; they are not copied or renamed. File stamps retain +fingerprints and metadata for unchanged EPUBs. The first inventory inserts its +rows; later scans insert new rows, update changed rows and remove missing rows. +Unchanged rows are not rewritten. Removing an inventory row does not delete an +EPUB or remove a book from Readest. Each managed download has a metadata file recording its account, Readest book hash, original-byte SHA-256, size and filename. Recovery validates completed @@ -217,12 +228,12 @@ invalid metadata or duplicate local copies are reported. ## Large libraries -Discovery persists candidate fingerprints keyed by path, size, device/inode and -modification/change timestamps. Unchanged candidates need no EPUB content reads. -New candidates use Readest's bounded partial fingerprint (at most twelve 1 KiB -samples). Only matching candidates undergo full SHA-256 and archive validation -before registration. Opening and syncing still verify the original EPUB bytes. -Matches and candidate-index updates are each committed as a batch. +The indexed library scan caches fingerprints and metadata by path, size, +device/inode and modification/change timestamps. Unchanged files need no EPUB +content reads. Changed files use Readest's bounded partial fingerprint (at most +twelve 1 KiB samples) and reload their metadata. Opening, syncing and transferring +still validate the original EPUB bytes. A first launch after USB or a changed +file stamp can take longer than a subsequent launch. The library snapshot loads saved sync metadata in one query and native percentages in one grouped query. Cover checks read headers rather than whole image files. @@ -317,10 +328,11 @@ a default route. The app waits up to 60 seconds and refreshes the Wi-Fi power-off timer every 30 seconds while an online action is active. Keepalive calls run off the UI thread, with at most one in flight, so a stalled firmware network manager cannot block Cancel or the connection timeout. -Online actions also prevent CPU standby while connecting and transferring, so -background work and the connection deadline can continue without screen taps. -Completion, failure, cancellation, and exit release that protection and stop -the keepalive. Normal OS power saving resumes afterward. Startup, sign-out, and +All service operations, including offline startup and scans, prevent CPU standby +through the UI completion callback. A one-second firmware grace period then +allows queued rendering to complete. Failure, cancellation and exit also release +the protection; the app does not permanently disable power saving. Network +keepalive remains limited to online operations. Startup, sign-out, and **Read offline** do not request Wi-Fi. Temporary diagnostics remain enabled in `system/readest-sync/network.log`. @@ -363,14 +375,14 @@ Successful sign-in atomically replaces the session file. Download records and reading state remain intact. Explicit conflict choices can restore a missing position from the valid selected side; selecting an empty position is rejected. -### Tiled library +### Library list and book details Library refresh uses limited requests and retains the API’s trailing timestamp ties. Repeated fractional-millisecond boundaries use bounded lookahead, up to a requested limit of 1,000 rows. Responses remain capped at 4 MiB; an unresolved or oversized boundary reports an error without advancing the cursor. -Each tile shows separate **PocketBook** and **Readest** reading percentages. +Book details show separate **PocketBook** and **Readest** reading percentages. PocketBook uses saved native page counts, read directly from the firmware database in a short read-only transaction at startup, after operations, and on return from the reader. Readest uses the @@ -379,11 +391,16 @@ library values. `—` means no usable saved percentage is available. The readers paginate differently, so their percentages can differ at the same location. These labels are display-only and never determine a sync position. -The library shows six book tiles in portrait and four in landscape, with title, -author and file availability. Tap a tile for its actions. Use **Previous/Next** -or the hardware page buttons to change pages; search still filters title/author. -Choose **All books**, **Available to download**, **On device**, or **Progress only** -above the tiles. Availability filters combine with search and reset pagination. +The library uses full-width rows with a small cover, title, author and status. +Tap a row for book details. The footer arrows and hardware page buttons change +pages. **Menu** opens search, filters, refresh, device scan and account actions. +The hardware menu key uses the same menu and is disabled during blocking actions. +Long-pressing a row offers its available contextual actions. + +Search matches title or author. Filters include **All books**, **Available to +download**, **On device**, **Progress only**, and **PocketBook only**. Search and +filters combine and reset pagination. Page capacity depends on the layout and +screen size. - **On device**: a downloaded or matched existing EPUB is present locally. - **Available to download**: no local copy is linked and Readest storage lists one supported EPUB. @@ -395,7 +412,7 @@ above the tiles. Availability filters combine with search and reset pagination. Choose **Refresh library** once after installing this update. It checks the account's paginated storage listing and saves availability for offline browsing. -Cloud covers are downloaded for visible tiles in the background after refresh, +Cloud covers are downloaded for visible rows in the background after refresh, not for the entire library before it becomes usable. Book actions take priority over this background work. When the storage listing has no cover entry, the app also asks Readest for its canonical cover key, as @@ -443,9 +460,10 @@ docker compose run --rm qt6 python3 tools/check_qt6_firmware.py \ build/readest-sync/readest-sync.app build/readest-sync/firmware-6.11.1683 ``` -Before accepting the migration on InkPad, test launch, native keyboard sign-in, -refresh, all library pages (especially 8/9), Wi-Fi recovery, an EPUB download, -Read offline, native-reader return, and both progress conflict choices. +For future device acceptance, test launch and Home exit, native keyboard sign-in, +refresh and paging, Wi-Fi recovery, an EPUB download, Read offline, native-reader +return, both progress conflict choices, and highlight/note creation, editing and +deletion in both directions. Repeat Sync to check that it creates no duplicates. ## PocketBook library and uploads @@ -479,4 +497,34 @@ Interrupted uploads retain their completed stages across restarts and require check the remote state and use the latest local position. Pending work belongs to the account that started it. Removing a local file changes its device availability, but does not delete its Readest entry. There are no automatic or -bulk uploads and no synchronized deletion. +bulk EPUB uploads and no synchronized deletion of EPUB files. Annotation +deletions are synchronized separately, as described below. + +## Highlights and attached notes + +For a linked, verified EPUB, **Sync now** and online **Open** reconcile highlights +and their attached text notes in both directions. Create, edit or delete them in +the native PocketBook reader or Readest, close the native reader, then sync the +book. **Refresh library** does not synchronize every book's annotations, and +**Read offline** makes no cloud request. The app has no annotation editor or list; +inspect the annotations in the readers. + +Annotation writes are enabled only for the validated PB743G firmware above. +Passage ranges must resolve against the same EPUB text; unsupported ranges, +styles or colors are skipped with a warning. Conflicting edits are retained and +reported rather than silently choosing one side. A warning can leave annotation +sync incomplete even when opening the book succeeds. Do not interpret the +reading-position conflict buttons as annotation conflict resolution controls. + +An app-owned journal records native/cloud identities, baselines and pending +uploads. Retries reuse their identities to avoid duplicates. Native reads use a +short read-only SQLite transaction; native writes use a guarded transaction with +an unchanged-state check. Neither operation copies the complete firmware DB. +Cloud acknowledgements and a fresh preflight read are checked, but Readest's API +has no conditional-write operation, so a concurrent cloud edit still has a race +window. Avoid editing the same note on two devices while a sync is in progress. + +Device validation covered a Readest edit arriving on PocketBook, a PocketBook +replacement arriving in the sync journal with matching cloud state, deletions, +and repeated synchronization. Automated checks additionally cover interrupted +uploads, retries, stale responses, unsupported replacements and conflicts. diff --git a/apps/readest-sync/docs/USER-GUIDE.md b/apps/readest-sync/docs/USER-GUIDE.md new file mode 100644 index 0000000..2396d39 --- /dev/null +++ b/apps/readest-sync/docs/USER-GUIDE.md @@ -0,0 +1,138 @@ +# Readest Sync user guide + +Readest Sync brings DRM-free EPUBs, reading positions, highlights and attached +notes between your PocketBook and Readest. Books open in the PocketBook native +reader; Readest Sync manages transfers and synchronization. + +The screenshots below show the current app in **Mock cloud** simulator mode with +synthetic books. They are not device screenshots. The app has been validated on +InkPad 4 (PB743G), firmware `U743g.6.11.1683`; other devices need separate testing. + +## Start with your library + +Follow the [installation instructions](../release/README.md), then launch +**Readest Sync** from Applications. Sign in with your Readest email and password +and choose **Menu → Refresh library**. + +The library combines Readest entries with EPUBs known to the PocketBook library. +Startup loads cached cloud information and scans the native library index without +requesting Wi-Fi. You can browse local books while signed out. + +Mock library showing covers, titles, authors, availability and page navigation + +Tap a row to open its book details. Use the footer arrows or hardware page keys +to browse pages. Covers load as you browse; books without a cover use a placeholder. + +## Search, filter and refresh + +Open **Menu** using the header button or a short press of the hardware Menu key, +where available. The same availability rules apply to both controls; the menu +stays closed while an operation or a conflict choice requires your attention. + +Library menu with Search, Filter, Refresh library, Scan device, Sign out and Close + +| Action | What it does | +| --- | --- | +| Search | Matches titles and authors. | +| Filter | Shows all books, books available to download, books on device, progress-only entries or PocketBook-only books. | +| Refresh library | Fetches current Readest metadata and availability and refreshes the local inventory. | +| Scan device | Refreshes EPUBs from the PocketBook library index. It does not recursively scan every folder. | +| Sign out | Removes the saved session and retains local EPUBs. | + +Search and filters work together. If a newly copied EPUB is missing, let the +PocketBook library discover it, then choose **Scan device**. + +## Download, upload and read + +For a cloud book, choose **Download EPUB** in its details. Readest must hold the +original EPUB; an entry with reading progress alone cannot be downloaded. The app +validates the EPUB before installing it under `Books/Readest`. + +Downloaded mock book showing PocketBook and Readest progress, Sync now, Read offline and Open + +Available actions depend on the book's state: + +| Action | What it does | +| --- | --- | +| Open | Synchronizes the linked book and opens the native reader. A pending incoming position changes the label to **Open at Readest position**. | +| Sync now | Synchronizes reading progress and supported annotations for this book. An incoming reading position is staged until you choose Open. | +| Read offline | Opens the local EPUB using its existing native position, without contacting Readest. | +| Upload to Readest / Upload EPUB to Readest | Uploads a local EPUB when the corresponding action is offered. | +| Re-upload to Readest | Explicitly restores a book removed from Readest. | + +Local books remain at their original paths. Refresh, sign-out and cloud book +removal do not delete local EPUBs. A removed cloud book with a local copy remains +available offline. + +After reading, return to Readest Sync and choose **Sync now** to send your latest +position and annotations. If a new book has no native reading settings, open and +close it once before syncing. The percentages in details can differ because the +two readers paginate differently; the app synchronizes the passage location. + +## Choose a reading position + +If both reading positions changed, the app asks which one to use. Check the +PocketBook and Readest values in the book details before choosing. + +Reading-position conflict offering Use PocketBook position, Use Readest position and Cancel + +- **Use PocketBook position** chooses the local reading position for synchronization. +- **Use Readest position** stages the cloud position; choose **Open at Readest position** to apply it in the native reader. +- **Cancel** leaves the choice unresolved. + +Open can also offer **Open at PocketBook position** and **Open at Readest +position**, including when you explicitly want to move backward. Opening at the +PocketBook position in that choice opens locally without updating Readest. The +app rechecks positions before applying your choice and rejects stale choices. + +## Synchronize highlights and notes + +Create highlights and edit their attached notes in the PocketBook native reader +or in Readest. For a linked book with the matching original EPUB: + +1. Finish the edit and close the reader so it saves the change. +2. Select the book in Readest Sync and choose **Sync now**, or use online **Open**. +3. Open the book in the other reader and check the highlighted passage and note. + +Synchronization supports creates, edits and deletions in both directions. +Readest Sync has no annotation editor or annotation list of its own. **Refresh +library** does not synchronize annotations for every book; **Read offline** does +not contact the cloud. + +Unsupported passages or annotation styles are skipped with a warning rather than +placed at an estimated location. If both readers changed the same annotation, +both versions are retained and the conflict is reported. The reading-position +choice shown above does not resolve annotation conflicts. Pending annotation +changes survive interrupted operations so a later sync can reconcile them. + +## Navigation and device behavior + +Back returns from book details to the library; Back from the library exits the +app. The hardware Menu key uses a short press. Key availability depends on the +device; a long press can invoke a firmware action instead. + +Automatic G-sensor rotation outside the native reader remains an +[open device limitation](https://github.com/42lizard/pocketbook/issues/25). +The simulator supports both portrait and landscape layouts. Its rendering does +not reproduce e-ink refresh timing or prove behavior on other firmware. + +## Try it without an account + +Start the [PocketBook simulator](../../../tools/pocketbook-simulator/README.md) +and choose **Mock cloud**. Its synthetic library includes downloadable books, +progress-only entries and transfer faults. The mock reader lets you exercise +handoff and reading-position changes: + +Simulator-only mock reader with chapter controls and Back to Readest Sync + +This panel is a simulator aid, not the PocketBook reader. It does not render EPUBs +or provide an annotation editor. Follow the +[mock scenarios and screenshot instructions](../../../tools/pocketbook-simulator/apps/readest-sync/README.md) +to reproduce the workflows. + +These five unmodified screenshots were captured on 2026-09-26 from the app at +`e0fd528`, using the simulator integration test with `READEST_DOC_SCREENSHOTS` and +an isolated temporary mock profile. No real account or library data is included. + +For storage, diagnostics, architecture and test commands, see the +[technical README](../README.md). diff --git a/apps/readest-sync/docs/images/book-details.png b/apps/readest-sync/docs/images/book-details.png new file mode 100644 index 0000000..798d673 Binary files /dev/null and b/apps/readest-sync/docs/images/book-details.png differ diff --git a/apps/readest-sync/docs/images/library-menu.png b/apps/readest-sync/docs/images/library-menu.png new file mode 100644 index 0000000..bbc4e4c Binary files /dev/null and b/apps/readest-sync/docs/images/library-menu.png differ diff --git a/apps/readest-sync/docs/images/library.png b/apps/readest-sync/docs/images/library.png new file mode 100644 index 0000000..f2cdff4 Binary files /dev/null and b/apps/readest-sync/docs/images/library.png differ diff --git a/apps/readest-sync/docs/images/mock-reader.png b/apps/readest-sync/docs/images/mock-reader.png new file mode 100644 index 0000000..65db5bf Binary files /dev/null and b/apps/readest-sync/docs/images/mock-reader.png differ diff --git a/apps/readest-sync/docs/images/position-conflict.png b/apps/readest-sync/docs/images/position-conflict.png new file mode 100644 index 0000000..d0e68b5 Binary files /dev/null and b/apps/readest-sync/docs/images/position-conflict.png differ diff --git a/apps/readest-sync/release/README.md b/apps/readest-sync/release/README.md index b08c5ee..5b68fa6 100644 --- a/apps/readest-sync/release/README.md +++ b/apps/readest-sync/release/README.md @@ -24,6 +24,28 @@ computer. On Linux, if necessary, mark the copied `.app` executable with `chmod To undo an update, close the app and restore the two backed-up files by USB. +## Use + +Sign in, choose **Menu → Refresh library**, and select a book. Download its EPUB +if needed. **Open** synchronizes before opening the native reader; **Read offline** +uses the current PocketBook position without contacting Readest. **Sync now** +reconciles reading progress, highlights and attached notes for that book. + +Highlights and attached notes are created/edited in PocketBook or Readest, not +inside this app. Their edits and deletions synchronize in both directions on the +validated firmware. Unsupported annotations or conflicting edits produce a +warning; reading-position choices do not resolve annotation conflicts. + +Existing PocketBook books appear in the library without signing in. Use **Scan +device** to refresh the app's inventory from the native library index and **Upload to Readest** to transfer +a device-only EPUB. The original file stays in place. + +The [online user guide](https://github.com/42lizard/pocketbook/blob/main/apps/readest-sync/docs/USER-GUIDE.md) +contains mock-simulator screenshots and explains the actions and limitations. + +Restoring an older executable does not undo reading positions or annotations +already synchronized to PocketBook or Readest. + ## Checksums and build identity The accompanying `.zip.sha256` file verifies the ZIP download. For example, @@ -34,7 +56,7 @@ identifies the app version and exact source commit. CI checks compilation, the ARM ABI, and automated tests. A draft release still needs device validation before publication, including startup, login, refresh, -download, and progress sync. No live account credentials are used in CI. +download, progress sync, and annotation edits/deletions in both directions. No live account credentials are used in CI. ## Licenses diff --git a/tools/pocketbook-simulator/README.md b/tools/pocketbook-simulator/README.md index a03b3c5..9758958 100644 --- a/tools/pocketbook-simulator/README.md +++ b/tools/pocketbook-simulator/README.md @@ -52,7 +52,8 @@ pixels in the remote display, not browser HTML inputs. Open **Simulator → Cloud service** and choose: - **Mock cloud**: local demo account, 51 generated books, configurable HTTP and - download failures, and simulated reading-position conflicts. Requests never + download failures, and simulated reading-position conflicts. Annotation storage and the notes + endpoint are also modeled for automated sync tests. Requests never fall back to the network. Use **Sign in to demo account**, then **Refresh library**. - **Real Readest**: the app's existing HTTPS client connects to Readest. Hide the simulator panel, enter your real credentials in the app, and refresh your @@ -71,6 +72,8 @@ that EPUB or manufacture native reading positions. Login, library, covers and downloads can be tested against the live account. Actual reading and passage interoperability still need a device or an eventual desktop reader integration. +For feature screenshots, see the [illustrated user guide](../../apps/readest-sync/docs/USER-GUIDE.md). + See [Readest scenarios](apps/readest-sync/README.md) for mock sync and recovery tests. ## Storage and screenshots diff --git a/tools/pocketbook-simulator/apps/readest-sync/README.md b/tools/pocketbook-simulator/apps/readest-sync/README.md index cc04c25..bc2abff 100644 --- a/tools/pocketbook-simulator/apps/readest-sync/README.md +++ b/tools/pocketbook-simulator/apps/readest-sync/README.md @@ -5,8 +5,8 @@ and QML shims. This directory contains only Readest-specific mock cloud fixtures reader/progress simulation, HTTPS routing and integration tests. In **Mock cloud**, sign in to the demo account and refresh the library. It has -51 distinct generated EPUB identities: nine portrait pages or thirteen landscape -pages. Some entries have position data only, multiple EPUBs, or no cover. Covers +51 distinct generated EPUB identities: seven portrait pages or nine landscape +pages at the default sizes. Some entries have position data only, multiple EPUBs, or no cover. Covers include PNG and JPEG stored as `cover.png`. Book 01 always has one EPUB. ## Reading and conflict scenario @@ -16,7 +16,7 @@ include PNG and JPEG stored as `cover.png`. Book 01 always has one EPUB. 2. Choose **Sync now** to establish the initial chapter-1 baseline. 3. In Simulator, change Readest to **Chapter 2**. Hide the panel, choose **Sync now**, then **Open at Readest position**. The app applies the position through its - normal database backup and conditional update path. + normal guarded SQLite transaction and conditional update path. 4. Turn the simulated reader to chapter 3 and return. Change Readest to chapter 1. **Sync now** now offers the app's conflict choices. 5. Choose **Use Readest position**, then **Open at Readest position** to test an @@ -74,3 +74,32 @@ docker compose run --rm simulator sh -ec ' ctest --test-dir build/readest-sync/simulator --output-on-failure ' ``` + +## Annotation coverage + +Mock mode supplies a native annotation database and the notes sync endpoint. +The automated annotation suite covers highlights and attached notes in both +directions, including edits, deletions, retries and conflicts. The simulator +reader panel is a position/handoff model, not an EPUB renderer or annotation +editor; use the native reader and Readest for visual annotation acceptance. + +## Documentation screenshots + +The [user guide](../../../../apps/readest-sync/docs/USER-GUIDE.md) uses the real +app QML with mock fixtures. Recreate its screenshots without accessing a real +account or changing the interactive simulator's storage: + +```sh +docker compose run --rm -e READEST_DOC_SCREENSHOTS=/workspace/build/readest-sync/doc-screenshots simulator sh -ec ' + cmake --build build/readest-sync/simulator --target simulator-test -j2 + ctest --test-dir build/readest-sync/simulator -R "^simulator$" --output-on-failure +' +``` + +Build/configure the simulator first using the command above if the build directory +does not yet exist. This test uses a temporary mock profile. It captures the library, +library menu, downloaded book details, mock reader and reading-position conflict. +Review the resulting PNGs, then copy them into +`apps/readest-sync/docs/images/`. The images retain the **Simulator · Mock** label; +they are not device screenshots. `READEST_UI_PREVIEW` remains available for the +broader test preview set, including orientation layouts. diff --git a/tools/pocketbook-simulator/apps/readest-sync/test.cpp b/tools/pocketbook-simulator/apps/readest-sync/test.cpp index de346ee..de5d504 100644 --- a/tools/pocketbook-simulator/apps/readest-sync/test.cpp +++ b/tools/pocketbook-simulator/apps/readest-sync/test.cpp @@ -73,6 +73,18 @@ int main(int argc,char** argv) { engine.load(QUrl("qrc:/Main.qml")); assert(!engine.rootObjects().isEmpty()); auto* window=qobject_cast(engine.rootObjects().first()); assert(window); sim.attach(engine,control); + const auto documentation=qEnvironmentVariable("READEST_DOC_SCREENSHOTS"); + auto capture=[&](const char* name) { + if(documentation.isEmpty()) return; + assert(!sim.realCloud()); + auto* overlay=window->findChild("simulatorOverlay"); assert(overlay); + const bool wasOpen=overlay->property("panelOpen").toBool(); + overlay->setProperty("panelOpen",false); + settle(250); + assert(QDir().mkpath(documentation)); + assert(window->grabWindow().save(documentation+"/"+name+".png")); + overlay->setProperty("panelOpen",wasOpen); + }; if(sim.realCloud()) { // A mock session at the old location must not sign in the real profile. assert(QDir().mkpath(scratch.path()+"/system/readest-sync")); @@ -124,6 +136,12 @@ int main(int argc,char** argv) { assert(control.library()->entries().size()==51 && control.library()->pages()==7); settle(1500); // Only the visible covers load after the metadata refresh completes. assert(!control.library()->at(0).cover.empty()); + capture("library"); + if(!documentation.isEmpty()) { + sim.button(Qt::Key_Menu); settle(); + capture("library-menu"); + sim.button(Qt::Key_Menu); settle(); + } sim.network(3); control.refreshLibrary(); assert(control.busy()); finish(control); assert(control.status().toStdString().find("timed out")!=std::string::npos); control.refreshLibrary(); assert(control.status().toStdString().find("still finishing")!=std::string::npos); @@ -147,14 +165,17 @@ int main(int argc,char** argv) { } sim.transfer(0); action(control,"Download EPUB"); assert(!current().book.path.empty()); + capture("book-details"); const auto path=current().book.path; action(control,"Read offline"); assert(!sim.readerPath().isEmpty() && sim.chapter()==1); + capture("mock-reader"); sim.closeReader(); finish(control); action(control,"Sync now"); assert(control.status()=="Reading positions are synchronized."); sim.remoteChapter(2); action(control,"Sync now"); assert(control.status().startsWith("Readest position downloaded")); action(control,"Open at Readest position"); assert(sim.chapter()==2 && !sim.readerPath().isEmpty()); sim.turnReader(1); sim.closeReader(); finish(control); sim.remoteChapter(1); action(control,"Sync now"); assert(control.status().startsWith("Both positions differ")); + capture("position-conflict"); auto* conflictDetails=activeItem(window,"nativeBookDetailsPage"); assert(conflictDetails); sim.button(Qt::Key_Menu); settle(); assert(!conflictDetails->property("menuOpen").toBool()); action(control,"Use Readest position");