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
+57 -38
View File
@@ -22,32 +22,44 @@ export function createNavigationAPI() {
readerEvents.emit("beforePageChange", state.currentReader); readerEvents.emit("beforePageChange", state.currentReader);
if (state.currentReader.type === "ebook") { if (state.currentReader.type === "ebook") {
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();
}
}
} else {
// Fallback: just move to next spine
if ( if (
state.currentReader.currentSpineIndex < state.currentReader.currentSpineIndex <
state.currentReader.cif.spine.length - 1 state.currentReader.cif.spine.length - 1
) { ) {
state.currentReader.currentSpineIndex++; 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;
}
}
}
renderSpineItem(); renderSpineItem();
} }
}
} else if (state.currentReader.type === "pdf") { } else if (state.currentReader.type === "pdf") {
const totalPages = state.readerMetadata?.total_pages || 0; const totalPages = state.readerMetadata?.total_pages || 0;
if (state.currentReader.currentPage < totalPages) { if (state.currentReader.currentPage < totalPages) {
@@ -80,29 +92,36 @@ export function createNavigationAPI() {
readerEvents.emit("beforePageChange", state.currentReader); readerEvents.emit("beforePageChange", state.currentReader);
if (state.currentReader.type === "ebook") { if (state.currentReader.type === "ebook") {
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();
}
}
} else {
// Fallback: just move to previous spine
if (state.currentReader.currentSpineIndex > 0) { if (state.currentReader.currentSpineIndex > 0) {
state.currentReader.currentSpineIndex--; 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;
}
}
}
renderSpineItem(); renderSpineItem();
} }
}
} else if (state.currentReader.type === "pdf") { } else if (state.currentReader.type === "pdf") {
if (state.currentReader.currentPage > 1) { if (state.currentReader.currentPage > 1) {
state.currentReader.currentPage--; state.currentReader.currentPage--;