Skip to content

feat(sync): three way (kobo, koreader and bookorbit) progress and annotation sync#338

Merged
neonsolstice merged 41 commits into
mainfrom
BO-337-three-way-sync
Jun 26, 2026
Merged

feat(sync): three way (kobo, koreader and bookorbit) progress and annotation sync#338
neonsolstice merged 41 commits into
mainfrom
BO-337-three-way-sync

Conversation

@neonsolstice

Copy link
Copy Markdown
Collaborator

What does this PR do?

Introduces a complete three-way synchronization system for reading progress, annotations, and reading sessions across KOReader, Kobo, and BookOrbit.

  • Implements KOReader sync APIs for reading progress, annotations, and reading stats.
  • Implements Kobo sync APIs for reading state, progress, and annotations.
  • Re-adds robust reading session tracking and syncing within BookOrbit across these clients.
  • Bridges the position formats between the different ecosystems using the position-converter module.

Closes #337

How did you test this?

  • Synced progress and annotations from KOReader to BookOrbit, then validated them on a connected Kobo device.
  • Synced highlights from Kobo to BookOrbit, then fetched them successfully in KOReader.
  • Verified that reading sessions (start time, duration, progress) correctly populate in BookOrbit statistics after syncing from both clients.

Screenshots

Anything non-obvious in the diff?

There is significant logic in bridging the position formats (e.g., KOReader progress percentages vs. Kobo chapter spans) through the position-converter module to ensure accuracy across the ecosystems.

Checklist

  • I've read through my own diff
  • This PR was discussed in an issue and has maintainer approval (for new features)
  • Lint and tests pass locally
  • No unintended files included (build artifacts, .env, personal configs)

@coderabbitai

coderabbitai Bot commented Jun 14, 2026

Copy link
Copy Markdown

Important

Review skipped

Too many files!

This PR contains 291 files, which is 141 over the limit of 150.

To get a review, narrow the scope:
• coderabbit review --type committed # exclude uncommitted changes
• coderabbit review --dir # limit to a subdirectory
• coderabbit review --base # compare against a closer base

Upgrade to a paid plan to raise the limit.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 3f8b2278-514d-418f-b555-d1054169819e

📥 Commits

Reviewing files that changed from the base of the PR and between ab37107 and 4313938.

📒 Files selected for processing (291)
  • Dockerfile
  • client/src/assets/theme/tokens.css
  • client/src/components/AppHeader.vue
  • client/src/features/annotations/components/AnnotationBookCombobox.vue
  • client/src/features/annotations/components/AnnotationBookGroup.vue
  • client/src/features/annotations/components/AnnotationBookThumb.vue
  • client/src/features/annotations/components/AnnotationBulkBar.vue
  • client/src/features/annotations/components/AnnotationCard.vue
  • client/src/features/annotations/components/AnnotationFilterChips.vue
  • client/src/features/annotations/components/AnnotationFiltersPanel.vue
  • client/src/features/annotations/components/AnnotationListItem.vue
  • client/src/features/annotations/components/AnnotationPagination.vue
  • client/src/features/annotations/components/AnnotationSummaryBar.vue
  • client/src/features/annotations/components/AnnotationSyncDetailPanel.vue
  • client/src/features/annotations/components/AnnotationToolbar.vue
  • client/src/features/annotations/components/__tests__/AnnotationBookCombobox.spec.ts
  • client/src/features/annotations/components/__tests__/AnnotationBulkBar.spec.ts
  • client/src/features/annotations/components/__tests__/AnnotationFiltersPanel.spec.ts
  • client/src/features/annotations/components/__tests__/AnnotationListItem.spec.ts
  • client/src/features/annotations/components/__tests__/AnnotationPagination.spec.ts
  • client/src/features/annotations/components/__tests__/AnnotationSummaryBar.spec.ts
  • client/src/features/annotations/components/__tests__/AnnotationToolbar.spec.ts
  • client/src/features/annotations/composables/__tests__/useAnnotationMutations.spec.ts
  • client/src/features/annotations/composables/__tests__/useAnnotationSelection.spec.ts
  • client/src/features/annotations/composables/__tests__/useAnnotationsHub.spec.ts
  • client/src/features/annotations/composables/__tests__/useAnnotationsUrlSync.spec.ts
  • client/src/features/annotations/composables/__tests__/useAnnotationsUrlSync.wiring.spec.ts
  • client/src/features/annotations/composables/__tests__/useDensity.spec.ts
  • client/src/features/annotations/composables/useAnnotationMutations.ts
  • client/src/features/annotations/composables/useAnnotationSelection.ts
  • client/src/features/annotations/composables/useAnnotationSyncDetail.ts
  • client/src/features/annotations/composables/useAnnotationsHub.ts
  • client/src/features/annotations/composables/useAnnotationsUrlSync.ts
  • client/src/features/annotations/composables/useDensity.ts
  • client/src/features/annotations/lib/__tests__/filter-chips.spec.ts
  • client/src/features/annotations/lib/filter-chips.ts
  • client/src/features/annotations/lib/filter-options.ts
  • client/src/features/annotations/lib/pill-styles.ts
  • client/src/features/annotations/views/AnnotationsHubView.vue
  • client/src/features/book/components/detail/tabs/AddSessionDialog.vue
  • client/src/features/book/components/detail/tabs/HighlightCard.vue
  • client/src/features/book/components/detail/tabs/HighlightChapterGroup.vue
  • client/src/features/book/components/detail/tabs/HighlightNoteEditor.vue
  • client/src/features/book/components/detail/tabs/HighlightsExportMenu.vue
  • client/src/features/book/components/detail/tabs/HighlightsFilterBar.vue
  • client/src/features/book/components/detail/tabs/HighlightsTab.vue
  • client/src/features/book/components/detail/tabs/ReadingLogExportMenu.vue
  • client/src/features/book/components/detail/tabs/ReadingLogHeatmap.vue
  • client/src/features/book/components/detail/tabs/ReadingLogHero.vue
  • client/src/features/book/components/detail/tabs/ReadingLogJourneyChart.vue
  • client/src/features/book/components/detail/tabs/ReadingLogSourceSplit.vue
  • client/src/features/book/components/detail/tabs/ReadingLogSparkline.vue
  • client/src/features/book/components/detail/tabs/ReadingLogStatsStrip.vue
  • client/src/features/book/components/detail/tabs/ReadingLogTab.vue
  • client/src/features/book/components/detail/tabs/ReadingLogTable.vue
  • client/src/features/book/components/detail/tabs/__tests__/HighlightsExportMenu.spec.ts
  • client/src/features/book/components/detail/tabs/__tests__/HighlightsFilterBar.spec.ts
  • client/src/features/book/components/detail/tabs/__tests__/ReadingLogHeatmap.spec.ts
  • client/src/features/book/components/detail/tabs/__tests__/ReadingLogHero.spec.ts
  • client/src/features/book/components/detail/tabs/__tests__/ReadingLogSourceSplit.spec.ts
  • client/src/features/book/components/detail/tabs/__tests__/ReadingLogStatsStrip.spec.ts
  • client/src/features/book/components/detail/tabs/__tests__/ReadingLogTab.spec.ts
  • client/src/features/book/components/detail/tabs/__tests__/ReadingLogTable.spec.ts
  • client/src/features/book/composables/__tests__/useBookReadingLog.spec.ts
  • client/src/features/book/composables/useBookHighlights.test.ts
  • client/src/features/book/composables/useBookHighlights.ts
  • client/src/features/book/composables/useBookReadingLog.ts
  • client/src/features/dashboard/components/widgets/ReadingRhythmWidget.vue
  • client/src/features/kobo/composables/useKoboSettings.ts
  • client/src/features/koreader/composables/__tests__/useKoreaderSync.spec.ts
  • client/src/features/koreader/composables/useKoreaderSync.ts
  • client/src/features/reader/ReaderView.vue
  • client/src/features/reader/epub/components/ReaderSidebar.vue
  • client/src/features/reader/epub/components/SelectionPopup.vue
  • client/src/features/reader/epub/components/__tests__/ReaderSidebar.spec.ts
  • client/src/features/reader/epub/components/__tests__/SelectionPopup.spec.ts
  • client/src/features/reader/epub/composables/__tests__/useAnnotations.spec.ts
  • client/src/features/reader/epub/composables/useAnnotations.ts
  • client/src/features/reader/epub/composables/useFoliateAnnotations.ts
  • client/src/features/reader/pdf-v4/PdfV4ReaderView.vue
  • client/src/features/settings/IntegrationsAllSettings.vue
  • client/src/features/settings/KoboSettings.vue
  • client/src/features/settings/KoreaderSettings.vue
  • client/src/features/settings/SettingsHeader.vue
  • client/src/features/settings/__tests__/IntegrationsAllSettings.spec.ts
  • client/src/features/settings/__tests__/KoreaderSettings.spec.ts
  • client/src/features/settings/__tests__/SettingsHeader.spec.ts
  • client/src/features/settings/__tests__/integrations-tabs.spec.ts
  • client/src/features/settings/lib/integrations-tabs.ts
  • client/src/features/statistics/api/statistics.api.test.ts
  • client/src/features/statistics/api/statistics.api.ts
  • client/src/features/statistics/components/BreakdownSelect.vue
  • client/src/features/statistics/components/StatisticsGrid.vue
  • client/src/features/statistics/components/library/LibraryMetadataCompletenessHeatmapChart.vue
  • client/src/features/statistics/components/user/FavoriteReadingDaysChart.vue
  • client/src/features/statistics/components/user/GenreReadingTimeTreemapChart.vue
  • client/src/features/statistics/components/user/PeakReadingHoursChart.vue
  • client/src/features/statistics/components/user/ReadingClockChart.vue
  • client/src/features/statistics/components/user/ReadingHeatmapChart.vue
  • client/src/features/statistics/components/user/ReadingPaceScatterChart.vue
  • client/src/features/statistics/components/user/ReadingSessionTimelineChart.vue
  • client/src/features/statistics/components/user/SourceDistributionChart.vue
  • client/src/features/statistics/components/user/__tests__/SourceDistributionChart.spec.ts
  • client/src/features/statistics/composables/useStatisticsQuery.ts
  • client/src/features/statistics/composables/useUserGoalTrajectory.ts
  • client/src/features/statistics/composables/useUserReadingSourceDistribution.ts
  • client/src/features/statistics/lib/breakdown.test.ts
  • client/src/features/statistics/lib/breakdown.ts
  • client/src/features/statistics/lib/source-bucket-colors.test.ts
  • client/src/features/statistics/lib/source-bucket-colors.ts
  • client/src/features/statistics/statistics-chart-meta.ts
  • client/src/lib/echarts.ts
  • client/src/lib/heatmap-palette.ts
  • client/src/router/index.ts
  • client/src/views/BookDetailView.vue
  • koreader-plugin/README.md
  • koreader-plugin/bookorbit.koplugin/_meta.lua
  • koreader-plugin/bookorbit.koplugin/bookorbit_annotations.lua
  • koreader-plugin/bookorbit.koplugin/bookorbit_api.lua
  • koreader-plugin/bookorbit.koplugin/bookorbit_book_sync.lua
  • koreader-plugin/bookorbit.koplugin/bookorbit_catalog.lua
  • koreader-plugin/bookorbit.koplugin/bookorbit_catalog_download.lua
  • koreader-plugin/bookorbit.koplugin/bookorbit_catalog_util.lua
  • koreader-plugin/bookorbit.koplugin/bookorbit_catalog_widgets.lua
  • koreader-plugin/bookorbit.koplugin/bookorbit_menu_pin.lua
  • koreader-plugin/bookorbit.koplugin/bookorbit_sidecar.lua
  • koreader-plugin/bookorbit.koplugin/bookorbit_state.lua
  • koreader-plugin/bookorbit.koplugin/bookorbit_stats_reader.lua
  • koreader-plugin/bookorbit.koplugin/bookorbit_sweep.lua
  • koreader-plugin/bookorbit.koplugin/bookorbit_updater.lua
  • koreader-plugin/bookorbit.koplugin/main.lua
  • packages/types/src/__tests__/reading-session-source-bucket.spec.ts
  • packages/types/src/annotation.ts
  • packages/types/src/index.ts
  • packages/types/src/kobo.ts
  • packages/types/src/koreader.ts
  • packages/types/src/reading-session-source-bucket.ts
  • packages/types/src/reading-session.ts
  • packages/types/src/statistics.ts
  • packages/types/src/user-statistics.ts
  • scripts/e2e/suite-registry.mjs
  • server/.env.example
  • server/src/common/utils/reading-daily-stats.utils.test.ts
  • server/src/common/utils/reading-daily-stats.utils.ts
  • server/src/common/utils/semver.utils.test.ts
  • server/src/common/utils/semver.utils.ts
  • server/src/config/config.ts
  • server/src/config/env.validation.ts
  • server/src/db/migrations/0027_consolidated-koreader-annotations.sql
  • server/src/db/migrations/0028_kobo-annotation-spoke.sql
  • server/src/db/migrations/0029_reading-session-source-book-id.sql
  • server/src/db/migrations/0030_backfill-reading-session-source-book-id.sql
  • server/src/db/migrations/0031_reading-session-book-id-not-null.sql
  • server/src/db/migrations/meta/0027_snapshot.json
  • server/src/db/migrations/meta/0028_snapshot.json
  • server/src/db/migrations/meta/0029_snapshot.json
  • server/src/db/migrations/meta/0030_snapshot.json
  • server/src/db/migrations/meta/0031_snapshot.json
  • server/src/db/migrations/meta/_journal.json
  • server/src/db/schema/kobo.ts
  • server/src/db/schema/koreader.ts
  • server/src/db/schema/reader.ts
  • server/src/main.ts
  • server/src/modules/achievement/achievement.repository.ts
  • server/src/modules/annotation/annotation-conversion.service.ts
  • server/src/modules/annotation/annotation-export.service.test.ts
  • server/src/modules/annotation/annotation-export.service.ts
  • server/src/modules/annotation/annotation-hub.controller.ts
  • server/src/modules/annotation/annotation-hub.service.test.ts
  • server/src/modules/annotation/annotation-hub.service.ts
  • server/src/modules/annotation/annotation-position.repository.ts
  • server/src/modules/annotation/annotation-style-map.test.ts
  • server/src/modules/annotation/annotation-style-map.ts
  • server/src/modules/annotation/annotation-sync.repository.ts
  • server/src/modules/annotation/annotation-sync.service.test.ts
  • server/src/modules/annotation/annotation-sync.service.ts
  • server/src/modules/annotation/annotation.constants.ts
  • server/src/modules/annotation/annotation.module.test.ts
  • server/src/modules/annotation/annotation.module.ts
  • server/src/modules/annotation/annotation.repository.test.ts
  • server/src/modules/annotation/annotation.repository.ts
  • server/src/modules/annotation/annotation.service.test.ts
  • server/src/modules/annotation/annotation.service.ts
  • server/src/modules/annotation/dto/annotation-dto-validation.test.ts
  • server/src/modules/annotation/dto/annotation-hub.dto.ts
  • server/src/modules/annotation/dto/annotation-response.dto.ts
  • server/src/modules/annotation/dto/create-annotation.dto.ts
  • server/src/modules/architecture/architecture-boundaries.test.ts
  • server/src/modules/book/book-read.service.ts
  • server/src/modules/book/book.repository.test.ts
  • server/src/modules/book/book.repository.ts
  • server/src/modules/book/book.service.ts
  • server/src/modules/dashboard/dashboard-widget.repository.ts
  • server/src/modules/kobo/dto/update-settings.dto.ts
  • server/src/modules/kobo/guards/kobo-token.guard.ts
  • server/src/modules/kobo/kobo-reading-services.controller.ts
  • server/src/modules/kobo/kobo-sync.controller.ts
  • server/src/modules/kobo/kobo.module.test.ts
  • server/src/modules/kobo/kobo.module.ts
  • server/src/modules/kobo/services/kepubify-binary.service.ts
  • server/src/modules/kobo/services/kobo-analytics.service.test.ts
  • server/src/modules/kobo/services/kobo-annotation-exchange.service.ts
  • server/src/modules/kobo/services/kobo-annotation-materializer.service.ts
  • server/src/modules/kobo/services/kobo-annotation-payload.ts
  • server/src/modules/kobo/services/kobo-kepub-context.service.ts
  • server/src/modules/kobo/services/kobo-progress-bridge.service.ts
  • server/src/modules/kobo/services/kobo-reading-state.service.test.ts
  • server/src/modules/kobo/services/kobo-reading-state.service.ts
  • server/src/modules/kobo/services/kobo-settings.service.ts
  • server/src/modules/koreader/dto/index.ts
  • server/src/modules/koreader/dto/koreader-catalog-query.dto.ts
  • server/src/modules/koreader/dto/koreader-exchange.dto.test.ts
  • server/src/modules/koreader/dto/koreader-exchange.dto.ts
  • server/src/modules/koreader/dto/koreader-package.dto.ts
  • server/src/modules/koreader/dto/koreader-plugin.dto.ts
  • server/src/modules/koreader/dto/koreader-progress.dto.test.ts
  • server/src/modules/koreader/dto/koreader-progress.dto.ts
  • server/src/modules/koreader/koreader-annotation-exchange.service.test.ts
  • server/src/modules/koreader/koreader-annotation-exchange.service.ts
  • server/src/modules/koreader/koreader-catalog.controller.test.ts
  • server/src/modules/koreader/koreader-catalog.controller.ts
  • server/src/modules/koreader/koreader-catalog.service.test.ts
  • server/src/modules/koreader/koreader-catalog.service.ts
  • server/src/modules/koreader/koreader-package.service.test.ts
  • server/src/modules/koreader/koreader-package.service.ts
  • server/src/modules/koreader/koreader-plugin-annotation.service.test.ts
  • server/src/modules/koreader/koreader-plugin-annotation.service.ts
  • server/src/modules/koreader/koreader-plugin-source.test.ts
  • server/src/modules/koreader/koreader-plugin.controller.test.ts
  • server/src/modules/koreader/koreader-plugin.controller.ts
  • server/src/modules/koreader/koreader-plugin.repository.ts
  • server/src/modules/koreader/koreader-plugin.service.test.ts
  • server/src/modules/koreader/koreader-plugin.service.ts
  • server/src/modules/koreader/koreader-stats.service.test.ts
  • server/src/modules/koreader/koreader-stats.service.ts
  • server/src/modules/koreader/koreader-stats.util.test.ts
  • server/src/modules/koreader/koreader-stats.util.ts
  • server/src/modules/koreader/koreader.controller.ts
  • server/src/modules/koreader/koreader.module.ts
  • server/src/modules/koreader/koreader.repository.ts
  • server/src/modules/koreader/koreader.service.test.ts
  • server/src/modules/koreader/koreader.service.ts
  • server/src/modules/metadata/lib/opf-parser.test.ts
  • server/src/modules/metadata/lib/opf-parser.ts
  • server/src/modules/migration/executor/migration-import.repository.test.ts
  • server/src/modules/migration/executor/migration-import.repository.ts
  • server/src/modules/opds/__tests__/opds-book.service.test.ts
  • server/src/modules/opds/opds-book.service.ts
  • server/src/modules/opds/opds.module.ts
  • server/src/modules/position-converter/cfi.utils.ts
  • server/src/modules/position-converter/chapter-text-index.ts
  • server/src/modules/position-converter/epub-dom.service.test.ts
  • server/src/modules/position-converter/epub-dom.service.ts
  • server/src/modules/position-converter/kepub-dom.service.test.ts
  • server/src/modules/position-converter/kepub-dom.service.ts
  • server/src/modules/position-converter/kobo-span-converter.service.test.ts
  • server/src/modules/position-converter/kobo-span-converter.service.ts
  • server/src/modules/position-converter/kobo-span.core.test.ts
  • server/src/modules/position-converter/kobo-span.core.ts
  • server/src/modules/position-converter/position-converter.core.test.ts
  • server/src/modules/position-converter/position-converter.core.ts
  • server/src/modules/position-converter/position-converter.module.ts
  • server/src/modules/position-converter/position-converter.service.ts
  • server/src/modules/position-converter/xpointer.utils.ts
  • server/src/modules/reader/epub/epub.module.test.ts
  • server/src/modules/reader/epub/epub.module.ts
  • server/src/modules/reader/epub/epub.service.test.ts
  • server/src/modules/reader/epub/epub.service.ts
  • server/src/modules/reading-session/book-reading-session.controller.test.ts
  • server/src/modules/reading-session/book-reading-session.controller.ts
  • server/src/modules/reading-session/dto/create-manual-reading-session.dto.test.ts
  • server/src/modules/reading-session/dto/create-manual-reading-session.dto.ts
  • server/src/modules/reading-session/reading-session.repository.test.ts
  • server/src/modules/reading-session/reading-session.repository.ts
  • server/src/modules/reading-session/reading-session.service.test.ts
  • server/src/modules/reading-session/reading-session.service.ts
  • server/src/modules/user-book-status/user-book-status.repository.test.ts
  • server/src/modules/user-book-status/user-book-status.repository.ts
  • server/src/modules/user-book-status/user-book-status.service.test.ts
  • server/src/modules/user-book-status/user-book-status.service.ts
  • server/src/modules/user-statistics/user-statistics.controller.test.ts
  • server/src/modules/user-statistics/user-statistics.controller.ts
  • server/src/modules/user-statistics/user-statistics.repository.test.ts
  • server/src/modules/user-statistics/user-statistics.repository.ts
  • server/src/modules/user-statistics/user-statistics.service.test.ts
  • server/src/modules/user-statistics/user-statistics.service.ts
  • server/src/scripts/annotation-conversion-smoke.ts
  • server/src/scripts/backfill-reading-session-events.ts
  • server/test/annotations-hub.e2e-spec.ts
  • server/test/kobo-annotation-sync.e2e-spec.ts
  • server/test/koreader-annotation-exchange.e2e-spec.ts

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added ci Changes to CI/CD workflows, automation, or developer tooling. server Changes affecting server-side code, APIs, or backend behavior. client Changes affecting the client application or frontend behavior. packages labels Jun 14, 2026
@neonsolstice
neonsolstice force-pushed the BO-337-three-way-sync branch from f5a4fbe to 4d36998 Compare June 14, 2026 17:58
@neonsolstice neonsolstice changed the title BO-337-three-way-annotation-progress-sessions-sync feat (sync): three way (kobo, koreader and bookorbit) progress and annotation sync Jun 14, 2026
@neonsolstice neonsolstice reopened this Jun 14, 2026
@neonsolstice neonsolstice changed the title feat (sync): three way (kobo, koreader and bookorbit) progress and annotation sync feat(sync): three way (kobo, koreader and bookorbit) progress and annotation sync Jun 14, 2026
@neonsolstice neonsolstice reopened this Jun 14, 2026
@neonsolstice
neonsolstice force-pushed the BO-337-three-way-sync branch 2 times, most recently from bb08621 to 6f64644 Compare June 14, 2026 18:55
@neonsolstice
neonsolstice force-pushed the BO-337-three-way-sync branch 4 times, most recently from 69b91aa to fe6b2c4 Compare June 21, 2026 06:48
@neonsolstice
neonsolstice force-pushed the BO-337-three-way-sync branch 5 times, most recently from c608ef4 to 0fb5ca0 Compare June 25, 2026 04:14
Introduces a custom KOReader plugin to synchronize reading progress, stats, and annotations. Adds backend controllers, services, and database schema updates to ingest plugin data. Updates the frontend to display KOReader annotations alongside built-in reader highlights.
Zips the KOReader plugin alongside a generated provision.lua file that contains the user's sync credentials and server URL. This allows users to download a ready-to-use plugin zip directly from their settings page, bypassing the need for manual configuration on the e-reader.
Introduces bidirectional annotation synchronization between the KOReader plugin and the web reader.

- Implements a hub-and-spoke sync engine for annotations across devices
- Adds a position converter to seamlessly translate KOReader XPointer coordinates to EPUB CFI paths
- Includes migrations to update annotation schemas for sync tracking
- Adds an Annotations Hub UI on the client to manage cross-device notes and highlights
- Updates the KOReader Lua plugin to push and pull annotation changes
…ploads

KOReader datetimes carry no timezone. Minting them from UTC server time
often lands in the device's future, freezing the device-side upload
watermark. This adds deviceTime to the API to compute a clock offset and
bring minted datetimes into the device's frame.

Also configures the Lua JSON encoder to emit empty arrays instead of
objects for empty lists, fixing server-side validation failures, and
scrubs JSON nulls from responses to avoid truthiness bugs.
Consolidate KOReader annotations sync and remove legacy frontend components and database migrations.
- Move jumpFileId and pageno to base AnnotationItem
- Add originBreakdown to AnnotationStats
- Update backend services and repositories to provide these fields
- Update frontend components to utilize the new fields
Add backend queries and frontend charts to break down reading time and sessions by source (web, koreader, kobo, manual).

Includes a new SourceDistributionChart for user statistics and a ReadingLogSourceSplit for book-level stats.
Add an interactive progress toast to the KOReader plugin during sync sweeps to keep the user informed of the current phase without flooding the e-ink screen. Update the plugin's sync status label to display matched and unmatched book counts, and refine strategy labels. Additionally, widen the Kobo and KOReader settings containers in the web client to max-w-4xl for better readability.
Scale the book facet list by fetching only the top books by recent activity and adding a typeahead search combobox to filter by title or author. Pinned book selections are always preserved in the result set to prevent the selected filter from disappearing.
…ion management

Extracted complex state logic from useAnnotationsHub and useBookHighlights into smaller, focused composables (useAnnotationMutations, useAnnotationSelection). Created distinct components for toolbar, bulk operations, pagination, and filter panels to improve maintainability. Improved URL sync logic for annotation filters and fixed a test mock in statistics.
Main added 0019_add_lubimyczytac_provider_id while this branch
independently used 0019-0023 for the koreader/kobo/reading-session
migrations. Keep main's migration at 0019 and renumber this branch's to
0020-0024, rebuild the journal, repoint the snapshot prevId chain, and
fold the lubimyczytac_id column into the affected snapshots.

Verified: drizzle-kit generate reports no schema changes against the
merged schema, and all 25 migrations apply cleanly in order on a fresh
database.
Main's 0020 (achievements devices + reading_sessions.source) and the
branch's migrations both claimed slot 0020 when diverging from the same
base. Renumber the branch migrations 0020-0024 to 0021-0025 so main's
0020 lands first, and rebuild the journal plus the snapshot prevId
chain to match.

Main's 0020 already adds reading_sessions.source as NOT NULL DEFAULT
'web', so rewrite 0023 to drop the default and NOT NULL and widen the
source check to the union ('web','koreader','manual','kobo') instead of
re-adding the column. Adjust the 0024 backfill to re-attribute kor:%
sessions to 'koreader', since main's default already set every existing
row to 'web' and the previous IS NULL guard would no-op.

Drop a duplicate source assertion in the repository spec left by the
merge.
Wrap the ECharts containers as relative and position the charts absolute
inset-0 so their rendered size cannot feed back into the grid row height,
preventing the autoresize growth loop in the reading log.
Rebased BO-337 onto main, which added 0021_add_book_series_memberships
and 0022_add_aladin_id. Renumbered the feature migrations 0021-0025 to
0023-0027, restored main's 0021/0022 snapshots, rebuilt the journal, and
re-pointed the first feature snapshot's prevId to main's aladin snapshot.
Regenerated the final 0027 snapshot to capture the merged schema.

Validated: drizzle-kit generate reports no schema changes, and all 28
migrations apply cleanly in order on a fresh database.
Keep the rebased client code on the installed icon package and update server utility imports for the dependency versions on main.
Expose JSON catalog endpoints for KOReader credentials and wire the
plugin to browse, search, download, and link files to sync.

Ref #337
Merge separate BookOrbit sync and browse menu items into a single unified
menu. Improve catalog navigation by adding first/last page support and
properly cleaning up thumbnail jobs on menu return or close.

Ref #337
Extend the OPDS catalog endpoints to support filtering by read status,
file format, and explicit book IDs. Add new sort orders for ascending
recent/updated dates and recently read status.
Move the search option from a dedicated row in the root catalog view to
the top-left title bar button for better accessibility across all non-book
contexts. Reorder the root sections to prioritize 'Continue reading' and
'Recently added' at the top of the list.

Ref #337
Adds support for browsing distinct authors and series in the native
KOReader catalog, complete with pagination and progress tracking.

This updates the backend OPDS/KOReader services to return
paginated lists of authors and series, and introduces new
Lua widgets in the plugin to render them.
Normalize provider identifier prefixes during OPF parsing so malformed scheme values do not exceed database column limits. Add accessible labels to reader color buttons.
Pace vs Goal chart hardcoded goalBooks=12 regardless of the user's saved
reading goal. The API call now reads from dashboardConfig.readingGoal and
re-fetches reactively via a new extraWatchSources option on useStatisticsQuery.

Search inside a Smart Scope returned unfiltered results because getBooksPage
early-returned from getBooksBySmartScope before the q clause was applied. The
q filter is now forwarded and AND-ed with the scope's where clause.

The reading log journey chart grew infinitely in multi-column layout. The
h-full + flex-1 + echarts autoresize combination created a grid row-height
feedback loop. Adding lg:items-start to the grid container prevents row
stretching and breaks the loop.

KOReader plugin: the old bookorbit-koplugin stored settings under the same
bookorbit key with a different schema. The new plugin read those stale values
without resetting behavior keys (sync_forward, sync_backward, catalog settings),
causing the sync prompt to hang and crash on second tap. Adds settings_version
detection and migrates old settings to defaults while preserving credentials
and provision_fingerprint.

Ref #337
Calling downloadmgr:chooseDir while a ButtonDialog was still open caused
a crash on Android when the user tapped a folder in the chooser. The fix
closes the download dialog first, then reopens it via nextTick once the
new download directory has been saved.

Any filename customisation made before picking a folder is reset on reopen,
which is an acceptable tradeoff against the crash.

Ref #337
Daily reading statistics were previously computed using UTC
for date boundaries, which led to incorrect daily values for
users in other timezones.

This updates the aggregation logic, the reading session daily
stats service, and the backfill script to respect the user's
configured timezone when bucketting sessions into days.

Ref #337
…lank

Date Started and Date Finished remained null after KOReader sync or
progress-based auto-update, silently breaking achievements and dashboard
widgets that filter by these dates.

Fall back to MIN(startedAt) and MAX(endedAt) from reading sessions when
the status fields would otherwise be null. Explicit clears, unread/want_to_read
statuses, and finishedAt values that would precede startedAt are all guarded.
Adds a version check and one-tap update flow to the KOReader plugin.

Server: two new KOReader-auth endpoints - GET /koreader/plugin/version returns
the latest plugin and server versions; GET /koreader/plugin/package serves a
code-only zip (no provision file, so credentials are never overwritten).

Plugin: new 'Plugin: vX.Y.Z' label and 'Check for update' button in the menu.
Update uses an atomic backup/stage/rename swap instead of in-place extraction,
with POSIX single-quote shell escaping and unknown-version error detection.
Preserve main's Hardcover sync migration at 0023 and shift the annotation sync migration series to 0024 through 0028 after rebasing onto main.
Prompt users to update their KOReader plugin when a new version is released to ensure they have the latest sync fixes and features.

Ref #337
@neonsolstice
neonsolstice force-pushed the BO-337-three-way-sync branch from a880296 to 4313938 Compare June 26, 2026 02:00
@neonsolstice
neonsolstice merged commit 6da3c91 into main Jun 26, 2026
31 checks passed
@neonsolstice
neonsolstice deleted the BO-337-three-way-sync branch June 26, 2026 02:13
@github-actions

Copy link
Copy Markdown

🎉 This PR is included in version 2.0.0 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

@github-actions github-actions Bot added the released Issue or PR is included in a released version. label Jun 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci Changes to CI/CD workflows, automation, or developer tooling. client Changes affecting the client application or frontend behavior. packages released Issue or PR is included in a released version. server Changes affecting server-side code, APIs, or backend behavior.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature] 3-Way Reading Progress, Annotation, and Session Sync Across KOReader, Kobo, and BookOrbit

1 participant