EPUB sidebar chapter nav is off by one (loads the page before the chapter) #620

Closed
opened 2026-07-16 21:57:55 +00:00 by spikerj · 0 comments
Owner

Summary

Clicking a chapter in the reader sidebar's Chapters tab for an EPUB navigates to the page before the intended chapter — an off-by-one.

Root cause

The reader shell's onPageChanged(page) is 1-based by contract: currentPage initializes to 1, the PDF viewer feeds it 1-based pages, and onEpubProgress sets currentPage = event.page + 1. epubInitialPage then derives the 0-based index the reader loads via epubInitialPage = max(0, currentPage - 1).

The EPUB branch of the sidebar chapter list emits the 0-based chapter.pageIndex into that 1-based slot:

  • reader-sidebar.component.html (epub branch): (click)="selectPage(chapter.pageIndex)"
  • selectPagepageSelected.emit(page) → shell onPageChanged(page)currentPage.set(pageIndex)
  • epubInitialPage = pageIndex - 1loadPage(pageIndex - 1)

So a chapter at pageIndex = 5 loads page index 4.

Note this is specifically the epub sidebar → shell path. The epub reader's own navigation (epub-navigation component → loadPage(pageIndex)) is correct, and the PDF path is correct (1-based pages into a 1-based slot).

Fix options

  • Emit chapter.pageIndex + 1 from the epub sidebar branch so it matches the 1-based onPageChanged convention, or
  • Give epub chapter navigation its own explicit handler that calls into the reader with the 0-based index (mirrors how the new EPUB search panel routes result navigation — see spikersoft-angular feat/epub-search-panel, which deliberately kept off the shared onPageChanged path for exactly this reason).

Either way it deserves its own commit + a reader-sidebar/reader-shell test asserting the resolved page index, so the regression can't recur.

Discovered during

Reader/Kavita-parity epic, PR-5 (EPUB full-text search). Left untouched there to keep the feature PR focused.

## Summary Clicking a chapter in the **reader sidebar's Chapters tab for an EPUB** navigates to the page *before* the intended chapter — an off-by-one. ## Root cause The reader shell's `onPageChanged(page)` is 1-based by contract: `currentPage` initializes to `1`, the PDF viewer feeds it 1-based pages, and `onEpubProgress` sets `currentPage = event.page + 1`. `epubInitialPage` then derives the 0-based index the reader loads via `epubInitialPage = max(0, currentPage - 1)`. The EPUB branch of the sidebar chapter list emits the **0-based** `chapter.pageIndex` into that 1-based slot: - `reader-sidebar.component.html` (epub branch): `(click)="selectPage(chapter.pageIndex)"` - `selectPage` → `pageSelected.emit(page)` → shell `onPageChanged(page)` → `currentPage.set(pageIndex)` - `epubInitialPage = pageIndex - 1` → `loadPage(pageIndex - 1)` So a chapter at `pageIndex = 5` loads page index `4`. Note this is **specifically** the epub sidebar → shell path. The epub reader's own navigation (`epub-navigation` component → `loadPage(pageIndex)`) is correct, and the PDF path is correct (1-based pages into a 1-based slot). ## Fix options - Emit `chapter.pageIndex + 1` from the epub sidebar branch so it matches the 1-based `onPageChanged` convention, **or** - Give epub chapter navigation its own explicit handler that calls into the reader with the 0-based index (mirrors how the new EPUB search panel routes result navigation — see spikersoft-angular `feat/epub-search-panel`, which deliberately kept off the shared `onPageChanged` path for exactly this reason). Either way it deserves its own commit + a reader-sidebar/reader-shell test asserting the resolved page index, so the regression can't recur. ## Discovered during Reader/Kavita-parity epic, PR-5 (EPUB full-text search). Left untouched there to keep the feature PR focused.
Sign in to join this conversation.