Fixed layout thrashing when `Stream Fade-In` and `Auto-scroll Chat` are enabled. (#4791) * Scheduling scrollChatToBottom after the animation frame prevents layout thrashing. https://developer.mozilla.org/en-US/docs/Web/API/Window/requestAnimationFrame#return_value https://gist.github.com/paulirish/5d52fb081b3570c81e3a#file-what-forces-layout-md * Make frame request optional --------- Co-authored-by: user <user@exmaple.com> Co-authored-by: Cohee <18619528+Cohee1207@users.noreply.github.com>

ffe17bb870ee56e6323e852505e76ddce38069ba

DeclineThyself <235079501+DeclineThyself@users.noreply.github.com>

Signed
1 files changed, +32 -5Showing whitespace changes
public/script.js+32 -5
@@ -1451,7 +1451,7 @@ function scrollOnMediaLoad() {
1451 }1451 }
1452 mediaLoaded++;1452 mediaLoaded++;
1453 if (mediaLoaded === media.length) {1453 if (mediaLoaded === media.length) {
1454 scrollChatToBottom();1454 scrollChatToBottom({ waitForFrame: true });
1455 }1455 }
1456 }1456 }
1457}1457}
@@ -2598,8 +2598,19 @@ function formatGenerationTimer(gen_started, gen_finished, tokenCount, reasoningD
2598 return { timerValue, timerTitle };2598 return { timerValue, timerTitle };
2599}2599}
26002600
2601export function scrollChatToBottom() {2601let requestId = null;
2602 if (power_user.auto_scroll_chat_to_bottom) {2602
2603/**
2604 * Scrolls the chat to the bottom if configured to do so.
2605 * @param {object} [options] Options
2606 * @param {boolean} [options.waitForFrame] If true, waits for the animation frame before scrolling
2607 */
2608export function scrollChatToBottom({ waitForFrame } = {}) {
2609 if (!power_user.auto_scroll_chat_to_bottom) {
2610 return;
2611 }
2612
2613 const doScroll = () => {
2603 let position = chatElement[0].scrollHeight;2614 let position = chatElement[0].scrollHeight;
26042615
2605 if (power_user.waifuMode) {2616 if (power_user.waifuMode) {
@@ -2611,7 +2622,23 @@ export function scrollChatToBottom() {
2611 }2622 }
26122623
2613 chatElement.scrollTop(position);2624 chatElement.scrollTop(position);
2625 requestId = null;
2626 };
2627
2628 // Do not check truthiness. requestId can loop to zero.
2629 if (requestId !== null) {
2630 cancelAnimationFrame(requestId);
2631 }
2632
2633 if (!waitForFrame) {
2634 doScroll();
2635 return;
2614 }2636 }
2637
2638 // This prevents layout thrashing.
2639 // https://developer.mozilla.org/en-US/docs/Web/API/Window/requestAnimationFrame#return_value
2640 // https://gist.github.com/paulirish/5d52fb081b3570c81e3a#file-what-forces-layout-md
2641 requestId = requestAnimationFrame(() => doScroll());
2615}2642}
26162643
2617/**2644/**
@@ -3273,7 +3300,7 @@ class StreamingProcessor {
3273 this.markUIGenStarted();3300 this.markUIGenStarted();
3274 }3301 }
3275 hideSwipeButtons({ hideCounters: true });3302 hideSwipeButtons({ hideCounters: true });
3276 scrollChatToBottom();3303 scrollChatToBottom({ waitForFrame: true });
3277 return messageId;3304 return messageId;
3278 }3305 }
32793306
@@ -3376,7 +3403,7 @@ class StreamingProcessor {
3376 }3403 }
33773404
3378 if (!scrollLock) {3405 if (!scrollLock) {
3379 scrollChatToBottom();3406 scrollChatToBottom({ waitForFrame: true });
3380 }3407 }
3381 }3408 }
33823409