Improve performance of printMessages (#4979) * Improve performance of printMessages * Add clarifying comment * Refactor printMessages into redisplayChat. * const https://github.com/SillyTavern/SillyTavern/pull/4981#discussion_r2675632422 * Use `toFixed(3)` instead of 17 decimals. https://github.com/SillyTavern/SillyTavern/pull/4981#discussion_r2675647691 * Removed `.find is faster than .children.` comment. https://github.com/SillyTavern/SillyTavern/pull/4981#discussion_r2675625188 * Simplified add 'last_mes'. https://github.com/SillyTavern/SillyTavern/pull/4981#discussion_r2675616734 --------- Co-authored-by: user <user@exmaple.com>

2d1a96f91d675c618e7fc9832cda3bea5c2ac8d4

Cohee <18619528+Cohee1207@users.noreply.github.com>

Signed
1 files changed, +69 -43Ignore whitespace
public/script.js+69 -43
@@ -8,6 +8,7 @@ import {
8 Popper,8 Popper,
9 initLibraryShims,9 initLibraryShims,
10 default as libs,10 default as libs,
11 lodash,
11} from './lib.js';12} from './lib.js';
1213
13import { humanizedDateTime, favsToHotswap, getMessageTimeStamp, dragElement, isMobile, initRossMods } from './scripts/RossAscends-mods.js';14import { humanizedDateTime, favsToHotswap, getMessageTimeStamp, dragElement, isMobile, initRossMods } from './scripts/RossAscends-mods.js';
@@ -1422,19 +1423,54 @@ export async function printMessages() {
1422 chatElement.append('<div id="show_more_messages">Show more messages</div>');1423 chatElement.append('<div id="show_more_messages">Show more messages</div>');
1423 }1424 }
14241425
1425 for (let i = startIndex; i < chat.length; i++) {1426 await redisplayChat({ startIndex, fade: false });
1426 const item = chat[i];
1427 addOneMessage(item, { scroll: false, forceId: i, showSwipes: false });
1428 }
14291427
1430 chatElement.find('.mes').removeClass('last_mes');
1431 chatElement.find('.mes').last().addClass('last_mes');
1432 refreshSwipeButtons(false, false);
1433 applyStylePins();
1434 scrollChatToBottom({ waitForFrame: true });1428 scrollChatToBottom({ waitForFrame: true });
1435 delay(debounce_timeout.short).then(() => scrollOnMediaLoad());1429 delay(debounce_timeout.short).then(() => scrollOnMediaLoad());
1436}1430}
14371431
1432/**
1433 * Visually updates all chat messages including and after index by removing them, then adding them.
1434 * @param {object} [options] Options
1435 * @param {ChatMessage[]} [options.targetChat=chat] All messages in chat before startIndex will remain unchanged.
1436 * @param {Number} [options.startIndex=0] Everything including and after startIndex will be replaced.
1437 * @param {Boolean} [options.fade=true] When false, the swipe chevrons will not fade in.
1438 */
1439export async function redisplayChat({ targetChat = chat, startIndex = 0, fade = true } = {}) {
1440 const messageElements = chatElement.find('.mes');
1441 messageElements.removeClass('last_mes');
1442
1443 //Remove messages after index.
1444 messageElements.filter(`.mes[mesid="${startIndex}"]`).nextAll('.mes').addBack().remove();
1445
1446 const t1 = performance.now();
1447
1448 const messages = targetChat.slice(startIndex);
1449
1450 if (messages.length > 0) {
1451 const newMessageElements = messages.map( (message, offset) => {
1452 const i = startIndex + offset;
1453 const messageElement = addOneMessage(message, { scroll: false, forceId: i, showSwipes: false, insert: false });
1454
1455 return messageElement[0];
1456 });
1457
1458 //The last_mes has been removed, add it to the new last message.
1459 newMessageElements.at(-1).classList.add('last_mes');
1460
1461 //Append to chat in one DOM update.
1462 chatElement.append(newMessageElements);
1463
1464 applyCharacterTagsToMessageDivs({ mesIds: lodash.range(startIndex, targetChat.length, 1) });
1465 }
1466
1467 refreshSwipeButtons(false, fade);
1468 applyStylePins();
1469 updateEditArrowClasses();
1470
1471 console.info(`Rendered ${targetChat.length - startIndex} messages in ${((performance.now() - t1) / 1000).toFixed(3)} seconds.`);
1472}
1473
1438export function scrollOnMediaLoad() {1474export function scrollOnMediaLoad() {
1439 const started = Date.now();1475 const started = Date.now();
1440 const media = chatElement.find('.mes_block img, .mes_block video, .mes_block audio').toArray();1476 const media = chatElement.find('.mes_block img, .mes_block video, .mes_block audio').toArray();
@@ -2406,9 +2442,10 @@ export function addCopyToCodeBlocks(messageElement) {
2406 * @param {number} [options.insertBefore=null] Message ID to insert the new message before2442 * @param {number} [options.insertBefore=null] Message ID to insert the new message before
2407 * @param {number} [options.forceId=null] Force the message ID2443 * @param {number} [options.forceId=null] Force the message ID
2408 * @param {boolean} [options.showSwipes=true] Whether to refresh the swipe buttons.2444 * @param {boolean} [options.showSwipes=true] Whether to refresh the swipe buttons.
2409 * @returns {void}2445 * @param {boolean} [options.insert=true] Whether to insert the message into the DOM.
2446 * @returns {JQuery<HTMLElement>} The newly added message element
2410 */2447 */
2411export function addOneMessage(mes, { type = 'normal', insertAfter = null, scroll = true, insertBefore = null, forceId = null, showSwipes = true } = {}) {2448export function addOneMessage(mes, { type = 'normal', insertAfter = null, scroll = true, insertBefore = null, forceId = null, showSwipes = true, insert = true } = {}) {
2412 let messageText = mes.mes;2449 let messageText = mes.mes;
2413 const momentDate = timestampToMoment(mes.send_date);2450 const momentDate = timestampToMoment(mes.send_date);
2414 const timestamp = momentDate.isValid() ? momentDate.format('LL LT') : '';2451 const timestamp = momentDate.isValid() ? momentDate.format('LL LT') : '';
@@ -2485,7 +2522,7 @@ export function addOneMessage(mes, { type = 'normal', insertAfter = null, scroll
24852522
2486 const renderedMessage = getMessageFromTemplate(params);2523 const renderedMessage = getMessageFromTemplate(params);
24872524
2488 if (type !== 'swipe') {2525 if (type !== 'swipe' && insert) {
2489 if (!insertAfter && !insertBefore) {2526 if (!insertAfter && !insertBefore) {
2490 chatElement.append(renderedMessage);2527 chatElement.append(renderedMessage);
2491 }2528 }
@@ -2501,7 +2538,7 @@ export function addOneMessage(mes, { type = 'normal', insertAfter = null, scroll
2501 // Callers push the new message to chat before calling addOneMessage2538 // Callers push the new message to chat before calling addOneMessage
2502 const newMessageId = typeof forceId == 'number' ? forceId : chat.length - 1;2539 const newMessageId = typeof forceId == 'number' ? forceId : chat.length - 1;
25032540
2504 const newMessage = chatElement.find(`[mesid="${newMessageId}"]`);2541 const newMessage = insert ? chatElement.find(`[mesid="${newMessageId}"]`) : renderedMessage;
2505 const isSmallSys = mes?.extra?.isSmallSys;2542 const isSmallSys = mes?.extra?.isSmallSys;
25062543
2507 if (isSmallSys === true) {2544 if (isSmallSys === true) {
@@ -2529,25 +2566,24 @@ export function addOneMessage(mes, { type = 'normal', insertAfter = null, scroll
2529 });2566 });
25302567
2531 if (type === 'swipe') {2568 if (type === 'swipe') {
2532 const swipeMessage = chatElement.find(`[mesid="${newMessageId}"]`);2569 newMessage.attr('swipeid', params.swipeId);
2533 swipeMessage.attr('swipeid', params.swipeId);2570 newMessage.find('.mes_text').html(messageText).attr('title', title);
2534 swipeMessage.find('.mes_text').html(messageText).attr('title', title);2571 newMessage.find('.timestamp').text(timestamp).attr('title', `${params.extra.api} - ${params.extra.model}`);
2535 swipeMessage.find('.timestamp').text(timestamp).attr('title', `${params.extra.api} - ${params.extra.model}`);2572 updateReasoningUI(newMessage);
2536 updateReasoningUI(swipeMessage);2573 appendMediaToMessage(mes, newMessage, scroll ? SCROLL_BEHAVIOR.ADJUST : SCROLL_BEHAVIOR.NONE);
2537 appendMediaToMessage(mes, swipeMessage, scroll ? SCROLL_BEHAVIOR.ADJUST : SCROLL_BEHAVIOR.NONE);
2538 if (power_user.timestamp_model_icon && params.extra?.api) {2574 if (power_user.timestamp_model_icon && params.extra?.api) {
2539 insertSVGIcon(swipeMessage, params.extra);2575 insertSVGIcon(newMessage, params.extra);
2540 }2576 }
25412577
2542 if (mes.swipe_id == mes.swipes.length - 1) {2578 if (mes.swipe_id == mes.swipes.length - 1) {
2543 swipeMessage.find('.mes_timer').text(params.timerValue).attr('title', params.timerTitle);2579 newMessage.find('.mes_timer').text(params.timerValue).attr('title', params.timerTitle);
2544 swipeMessage.find('.tokenCounterDisplay').text(`${params.tokenCount}t`);2580 newMessage.find('.tokenCounterDisplay').text(`${params.tokenCount}t`);
2545 } else {2581 } else {
2546 swipeMessage.find('.mes_timer').empty();2582 newMessage.find('.mes_timer').empty();
2547 swipeMessage.find('.tokenCounterDisplay').empty();2583 newMessage.find('.tokenCounterDisplay').empty();
2548 }2584 }
2549 } else {2585 } else {
2550 chatElement.find(`[mesid="${newMessageId}"] .mes_text`).append(messageText);2586 newMessage.find('.mes_text').append(messageText);
2551 appendMediaToMessage(mes, newMessage, scroll ? SCROLL_BEHAVIOR.ADJUST : SCROLL_BEHAVIOR.NONE);2587 appendMediaToMessage(mes, newMessage, scroll ? SCROLL_BEHAVIOR.ADJUST : SCROLL_BEHAVIOR.NONE);
2552 }2588 }
25532589
@@ -2555,7 +2591,12 @@ export function addOneMessage(mes, { type = 'normal', insertAfter = null, scroll
25552591
2556 // Set the swipes counter for all non-user messages.2592 // Set the swipes counter for all non-user messages.
2557 if (!params.isUser) {2593 if (!params.isUser) {
2558 updateSwipeCounter(newMessageId);2594 updateSwipeCounter(newMessageId, { messageElement: newMessage });
2595 }
2596
2597 // The caller should handle the rest after adding a message to DOM.
2598 if (!insert) {
2599 return newMessage;
2559 }2600 }
25602601
2561 //last_mes should always be updated.2602 //last_mes should always be updated.
@@ -2572,6 +2613,8 @@ export function addOneMessage(mes, { type = 'normal', insertAfter = null, scroll
25722613
2573 applyCharacterTagsToMessageDivs({ mesIds: newMessageId });2614 applyCharacterTagsToMessageDivs({ mesIds: newMessageId });
2574 updateEditArrowClasses();2615 updateEditArrowClasses();
2616
2617 return newMessage;
2575}2618}
25762619
2577/**2620/**
@@ -9620,23 +9663,6 @@ export async function createOrEditCharacter(e) {
9620}9663}
96219664
9622/**9665/**
9623 * Visually updates all chat messages including andd after index by removing them, then adding them.
9624 * @param {ChatMessage[]} chat All messages in chat before index will remain unchanged.
9625 * @param {Number} index The last unchanged messageId.
9626 */
9627export async function redisplayChat(chat, index) {
9628 //Remove messages after index.
9629 chatElement.children(`.mes[mesid="${index}"]`).nextAll('.mes').addBack().remove();
9630
9631 //Skip to index, then add extra messages.
9632 for (let i = index; i <= chat.length - 1; i++) {
9633 //addOneMessage will update last_mes.
9634 addOneMessage(chat[i], { scroll: false, showSwipes: false, forceId: i });
9635 }
9636 refreshSwipeButtons();
9637}
9638
9639/**
9640 * Formats a counter for a swipe view.9666 * Formats a counter for a swipe view.
9641 * @param {number} current The current number of items.9667 * @param {number} current The current number of items.
9642 * @param {number} total The total number of items.9668 * @param {number} total The total number of items.
@@ -9790,7 +9816,7 @@ export async function swipe(event, direction, { source, repeated, message = chat
97909816
9791 //Update the chat.9817 //Update the chat.
9792 await loadFromSwipeId(mesId, chat[mesId].swipe_id);9818 await loadFromSwipeId(mesId, chat[mesId].swipe_id);
9793 await redisplayChat(chat, mesId);9819 await redisplayChat({ startIndex: mesId });
9794 }9820 }
9795 else {9821 else {
9796 await Popup.show.confirm(9822 await Popup.show.confirm(