Implement WebGL page reserve navigation

This commit is contained in:
2026-06-08 10:25:54 +02:00
parent 3e28d7db23
commit efd1e6cfff
13 changed files with 571 additions and 52 deletions
+30 -1
View File
@@ -18,6 +18,7 @@ class BookPaginationModule extends BaseModule {
this.refreshToken = 0;
this.latestBlockId = 0;
this.latestRenderedBlockId = 0;
this.appliedPageReserveBlocks = new Set();
this.bindMethods([
'initialize',
@@ -26,6 +27,7 @@ class BookPaginationModule extends BaseModule {
'buildSpreads',
'buildPages',
'buildSpreadsFromPages',
'applyPageReserveDirective',
'createBlankPage',
'createTitlePage',
'ensurePage',
@@ -76,7 +78,12 @@ class BookPaginationModule extends BaseModule {
});
this.addEventListener(document, 'webgl-book:page-flip-near-end', (event) => {
const direction = Math.sign(Number(event.detail?.direction || 0));
if (direction !== 0) this.setCurrentSpread(this.currentSpreadIndex + direction);
const targetSpread = Number(event.detail?.targetSpread);
if (Number.isFinite(targetSpread)) {
this.setCurrentSpread(targetSpread);
} else if (direction !== 0) {
this.setCurrentSpread(this.currentSpreadIndex + direction);
}
});
this.reportProgress(100, 'Book pagination ready');
return true;
@@ -102,6 +109,7 @@ class BookPaginationModule extends BaseModule {
this.latestBlockId = 0;
this.latestRenderedBlockId = 0;
this.currentSpreadIndex = 0;
this.appliedPageReserveBlocks.clear();
this.publish();
return;
}
@@ -196,6 +204,7 @@ class BookPaginationModule extends BaseModule {
source.forEach((block) => {
const type = block?.kind || block?.type || 'paragraph';
this.applyPageReserveDirective(block);
if (type === 'image') {
({ pageIndex, pageLine, contentPageNumber } = this.layoutImageBlock(
pages,
@@ -295,6 +304,24 @@ class BookPaginationModule extends BaseModule {
return spreads.filter(Boolean);
}
applyPageReserveDirective(block = {}) {
const directive = block?.metadata?.pageReserve || block?.pageReserve || null;
const blockId = Number(block?.blockId || block?.metadata?.blockId || 0);
const gameId = block?.gameId || block?.metadata?.gameId || this.storyHistory?.currentGameId || 'default';
const key = `${gameId}:${blockId}`;
if (!directive || blockId <= 0 || this.appliedPageReserveBlocks.has(key)) return;
const value = Number(directive.value);
if (!Number.isFinite(value)) return;
this.appliedPageReserveBlocks.add(key);
document.dispatchEvent(new CustomEvent('webgl-book:page-reserve-directive', {
detail: {
blockId,
value,
unit: directive.unit === 'percent' ? 'percent' : 'pages'
}
}));
}
createBlankPage(index = 0, options = {}) {
return {
index,
@@ -841,11 +868,13 @@ class BookPaginationModule extends BaseModule {
}
publish() {
const writtenPageLimit = Math.max(0, (Math.max(0, this.spreads.length - 1) * 2) - 1);
document.dispatchEvent(new CustomEvent('book-pagination:spread-updated', {
detail: {
spread: this.getCurrentSpread(),
spreadIndex: this.currentSpreadIndex,
spreadCount: this.spreads.length,
writtenPageLimit,
latestBlockId: this.latestBlockId,
latestRenderedBlockId: this.latestRenderedBlockId
}