fix: implement viewport-based page navigation for EPUBs

- Replace spine-only navigation with scroll-based page navigation
- nextPage: scroll down within chapter, only jump to next spine at chapter end
- previousPage: scroll up within chapter, only jump to previous spine at chapter start
- Use viewportHeight - 120 for accurate page height calculation
- Fallback to spine-only navigation when page calculation not available
This commit is contained in:
2026-04-05 21:23:30 -04:00
parent 7d4c6ac142
commit 35acf87faa
+62 -43
View File
@@ -22,31 +22,43 @@ export function createNavigationAPI() {
readerEvents.emit("beforePageChange", state.currentReader);
if (state.currentReader.type === "ebook") {
if (
state.currentReader.currentSpineIndex <
state.currentReader.cif.spine.length - 1
) {
state.currentReader.currentSpineIndex++;
if (pageCalculationResult) {
const container = document.getElementById("reader-content");
if (container) {
const viewportHeight = window.innerHeight;
const target = getScrollPositionForPage(
pageCalculationResult,
state.currentReader.currentPage ||
state.currentReader.currentSpineIndex + 1,
viewportHeight,
);
if (
target &&
target.spineIndex !== state.currentReader.currentSpineIndex
) {
state.currentReader.currentSpineIndex = target.spineIndex;
container.scrollTop = target.scrollTop;
}
const container = document.getElementById("reader-content");
const viewportHeightAdjusted = window.innerHeight - 120;
if (pageCalculationResult) {
const currentChapter = pageCalculationResult.chapterMap.get(
state.currentReader.currentSpineIndex,
);
if (currentChapter && container) {
const currentPageInChapter = Math.floor(
container.scrollTop / viewportHeightAdjusted,
);
const pagesInCurrentChapter =
currentChapter.endPage - currentChapter.startPage + 1;
if (currentPageInChapter < pagesInCurrentChapter - 1) {
// Still pages left in current chapter - just scroll down
container.scrollTop =
(currentPageInChapter + 1) * viewportHeightAdjusted;
} else if (
state.currentReader.currentSpineIndex <
state.currentReader.cif.spine.length - 1
) {
// At end of chapter, move to next spine
state.currentReader.currentSpineIndex++;
renderSpineItem();
}
}
renderSpineItem();
} else {
// Fallback: just move to next spine
if (
state.currentReader.currentSpineIndex <
state.currentReader.cif.spine.length - 1
) {
state.currentReader.currentSpineIndex++;
renderSpineItem();
}
}
} else if (state.currentReader.type === "pdf") {
const totalPages = state.readerMetadata?.total_pages || 0;
@@ -80,28 +92,35 @@ export function createNavigationAPI() {
readerEvents.emit("beforePageChange", state.currentReader);
if (state.currentReader.type === "ebook") {
if (state.currentReader.currentSpineIndex > 0) {
state.currentReader.currentSpineIndex--;
if (pageCalculationResult) {
const container = document.getElementById("reader-content");
if (container) {
const viewportHeight = window.innerHeight;
const target = getScrollPositionForPage(
pageCalculationResult,
state.currentReader.currentPage ||
state.currentReader.currentSpineIndex + 1,
viewportHeight,
);
if (
target &&
target.spineIndex !== state.currentReader.currentSpineIndex
) {
state.currentReader.currentSpineIndex = target.spineIndex;
container.scrollTop = target.scrollTop;
}
const container = document.getElementById("reader-content");
const viewportHeightAdjusted = window.innerHeight - 120;
if (pageCalculationResult) {
const currentChapter = pageCalculationResult.chapterMap.get(
state.currentReader.currentSpineIndex,
);
if (currentChapter && container) {
const currentPageInChapter = Math.floor(
container.scrollTop / viewportHeightAdjusted,
);
if (currentPageInChapter > 0) {
// Not at start of chapter - just scroll up
container.scrollTop =
(currentPageInChapter - 1) * viewportHeightAdjusted;
} else if (state.currentReader.currentSpineIndex > 0) {
// At start of chapter, move to previous spine
state.currentReader.currentSpineIndex--;
renderSpineItem();
}
}
renderSpineItem();
} else {
// Fallback: just move to previous spine
if (state.currentReader.currentSpineIndex > 0) {
state.currentReader.currentSpineIndex--;
renderSpineItem();
}
}
} else if (state.currentReader.type === "pdf") {
if (state.currentReader.currentPage > 1) {