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-basedchapter.pageIndex into that 1-based slot:
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.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
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:currentPageinitializes to1, the PDF viewer feeds it 1-based pages, andonEpubProgresssetscurrentPage = event.page + 1.epubInitialPagethen derives the 0-based index the reader loads viaepubInitialPage = max(0, currentPage - 1).The EPUB branch of the sidebar chapter list emits the 0-based
chapter.pageIndexinto that 1-based slot:reader-sidebar.component.html(epub branch):(click)="selectPage(chapter.pageIndex)"selectPage→pageSelected.emit(page)→ shellonPageChanged(page)→currentPage.set(pageIndex)epubInitialPage = pageIndex - 1→loadPage(pageIndex - 1)So a chapter at
pageIndex = 5loads page index4.Note this is specifically the epub sidebar → shell path. The epub reader's own navigation (
epub-navigationcomponent →loadPage(pageIndex)) is correct, and the PDF path is correct (1-based pages into a 1-based slot).Fix options
chapter.pageIndex + 1from the epub sidebar branch so it matches the 1-basedonPageChangedconvention, orfeat/epub-search-panel, which deliberately kept off the sharedonPageChangedpath 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.