feat: Enhance chapter navigation with improved canonical URL handling and deduplication

This commit is contained in:
JB
2025-10-13 15:04:28 -07:00
parent 1f0cfef310
commit 14cc7103cf
+35 -5
View File
@@ -779,12 +779,26 @@
const fragment = typeof options.fragment === 'string' ? options.fragment : ''; const fragment = typeof options.fragment === 'string' ? options.fragment : '';
const baseHref = target.href.split('#')[0]; const baseHref = target.href.split('#')[0];
const displayHref = fragment ? `${baseHref}#${fragment}` : target.href; const displayHref = fragment ? `${baseHref}#${fragment}` : target.href;
const canonicalCandidate = typeof book.canonical === 'function' ? book.canonical(displayHref) : displayHref;
const isBlobCanonical = typeof canonicalCandidate === 'string' && canonicalCandidate.startsWith('blob:');
const canonicalHref = isBlobCanonical ? null : canonicalCandidate;
const targetHref = canonicalHref || displayHref;
const syncHref = canonicalHref ? canonicalHref.split('#')[0] : baseHref;
console.info('[reader] Displaying chapter', {
requestedIndex: normalizedIndex,
href: target.href,
displayHref,
canonicalHref,
targetHref,
syncHref,
});
suppressRelocate = true; suppressRelocate = true;
await rendition.display(displayHref); await rendition.display(targetHref);
suppressRelocate = false; suppressRelocate = false;
currentChapterIndex = normalizedIndex; currentChapterIndex = normalizedIndex;
updateControls(); updateControls();
await loadChapterSync(target.href); const syncTarget = syncHref || target.normalizedHref || target.href;
await loadChapterSync(syncTarget);
const shouldScroll = options.scroll ?? Boolean(audioEl && !audioEl.paused); const shouldScroll = options.scroll ?? Boolean(audioEl && !audioEl.paused);
if (fragment) { if (fragment) {
highlightChunk(fragment, { scroll: true }); highlightChunk(fragment, { scroll: true });
@@ -991,8 +1005,24 @@
} }
if (!chapters.length && navigation && Array.isArray(navigation.toc)) { if (!chapters.length && navigation && Array.isArray(navigation.toc)) {
chapters = dedupeChapters(normalizeChapterList(flattenToc(navigation.toc))); chapters = normalizeChapterList(flattenToc(navigation.toc));
} else { }
chapters = dedupeChapters(chapters);
if (typeof book.canonical === 'function') {
chapters = chapters.map((chapter) => {
const canonicalCandidate = book.canonical(chapter.href) || chapter.href;
const canonicalNormalized = normalizeHref(canonicalCandidate);
const looksBlob = typeof canonicalCandidate === 'string' && canonicalCandidate.startsWith('blob:');
const effectiveHref = looksBlob ? chapter.href : canonicalCandidate;
const effectiveNormalized = !looksBlob && canonicalNormalized ? canonicalNormalized : (chapter.normalizedHref || normalizeHref(chapter.href));
return {
...chapter,
href: effectiveHref,
normalizedHref: effectiveNormalized,
};
});
chapters = dedupeChapters(chapters); chapters = dedupeChapters(chapters);
} }
@@ -1017,7 +1047,7 @@
if (index >= 0 && index !== currentChapterIndex) { if (index >= 0 && index !== currentChapterIndex) {
currentChapterIndex = index; currentChapterIndex = index;
updateControls(); updateControls();
loadChapterSync(chapters[index].href).then(() => { loadChapterSync(chapters[index].normalizedHref || chapters[index].href).then(() => {
if (audioEl && !audioEl.paused) { if (audioEl && !audioEl.paused) {
syncToTime(audioEl.currentTime || 0, { force: true, scroll: false }); syncToTime(audioEl.currentTime || 0, { force: true, scroll: false });
} }