Fix story page scrolling and ellipsis spacing

This commit is contained in:
2026-05-15 07:35:27 +02:00
parent 74be77b267
commit b8fe8535aa
5 changed files with 60 additions and 21 deletions
+3 -3
View File
@@ -8,7 +8,7 @@ function kap(text, measureText, measure, hyphenation) {
let spaceWidth = measureText('\u00A0');
let nodes = [];
text.split(/([.,:;!?] |\s|\||<.*?>)/u).forEach(function (fragment) {
text.split(/([.,:;!?] |\s|\||<.*?>)/u).forEach(function (fragment) {
let fragmentWidth = measureText(fragment);
if (fragment === ' ') {
@@ -21,8 +21,8 @@ function kap(text, measureText, measure, hyphenation) {
nodes.push(linebreak.penalty(hyphenWidth * 0.25, 100, 1));
} else if (fragment.match(/(<.*?>)/u)) {
nodes.push(linebreak.tag(fragmentWidth, fragment));
} else if (fragment.match(/[.,:;!?] /u)) {
let punctuation = fragment.match(/([.,:;!?])( )/u);
} else if (fragment.match(/[.,:;!?] /u)) {
let punctuation = fragment.match(/([.,:;!?])( )/u);
let punctuationSymbolWidth = measureText(punctuation[1]) * 0.25;
let punctuationWidth = measureText(punctuation[1]) * 0.75 + spaceWidth;
nodes.push(linebreak.box(punctuationSymbolWidth, punctuation[1]));
+1 -1
View File
@@ -150,7 +150,7 @@ class LayoutRendererModule extends BaseModule {
if (node.type === 'box' && node.value !== '' && j < currentBreak.position) {
const followsGlue = j > 0 && nodes[j - 1].type === 'glue';
const isTrailingPunctuation = /^[,.;:!?)]$/.test(node.value) && !followsGlue;
const isTrailingPunctuation = /^[,.;:!?)]$/.test(node.value) && !followsGlue;
// Check if this box follows a penalty (hyphenation point)
if (lastChild && isTrailingPunctuation) {
+19 -5
View File
@@ -34,6 +34,7 @@ class UIDisplayHandlerModule extends BaseModule {
'displayText',
'renderSentence',
'handleDeferredMediaBlock',
'scrollStoryToEnd',
'rerenderStory',
'clear',
'scheduleRerender',
@@ -397,14 +398,11 @@ class UIDisplayHandlerModule extends BaseModule {
}
});
this.scrollStoryToEnd(true);
// Start coordinated playback (animation + TTS), including chapter headings.
await this.playbackCoordinator.play(sentence);
// Scroll to bottom
if (this.pageRight) {
this.pageRight.scrollTop = this.pageRight.scrollHeight;
}
// Call completion callback
if (sentence.onComplete) {
sentence.onComplete();
@@ -464,6 +462,19 @@ class UIDisplayHandlerModule extends BaseModule {
}
}
scrollStoryToEnd(smooth = true) {
if (!this.pageRight) {
return;
}
window.requestAnimationFrame(() => {
this.pageRight.scrollTo({
top: Math.max(0, this.pageRight.scrollHeight - this.pageRight.clientHeight),
behavior: smooth ? 'smooth' : 'auto'
});
});
}
async handleDeferredMediaBlock(sentence) {
document.dispatchEvent(new CustomEvent('story:media-block', {
detail: {
@@ -496,6 +507,9 @@ class UIDisplayHandlerModule extends BaseModule {
this.container.appendChild(this.paragraphContainer);
}
this.renderedItems = [];
if (this.pageRight) {
this.pageRight.scrollTop = 0;
}
}
/**