`refactor/perf-printMessages` #2: Removed `getMessageFromTemplate` (#4983) * Reduced redundant swipe code by fully updating the swipe message. * `getMessageFromTemplate` is an inconvenient function that requires more lines than it saves. It cannot be used in any other function, therefore it should not exist. This change also prevents an unnecessary call of `messageTemplate.clone` in addOneMessage when `type = 'swipe'`. ESLint. * Fixed: https://github.com/SillyTavern/SillyTavern/pull/4983#discussion_r2680379455 * Replaced ?? with &&: https://github.com/SillyTavern/SillyTavern/pull/4983#discussion_r2680366010 and removed comments. * Fixed bias check. "0" is falsy. * Remove scroll adjust early bail * Call appendMediaToMessage before updating content Because we need to preserve chatHeight and scrollPosition according to the previous swipe, not the new one --------- Co-authored-by: user <user@exmaple.com> Co-authored-by: Cohee <18619528+Cohee1207@users.noreply.github.com>

a9f2d559f428029964c2dfb7ccc3d429d92f67ea

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

Signed
1 files changed, +62 -121Ignore whitespace
public/script.js+62 -121
@@ -1855,9 +1855,7 @@ export function messageFormatting(mes, ch_name, isSystem, isUser, messageId, san
18551855 * the value in `extra.api`.
18561856 *
18571857 * @param {JQuery<HTMLElement>} mes - The message element containing the timestamp where the icon should be inserted or replaced.
18581858 * @param {ObjectChatMessageExtra} extra - Contains the API and model details.
1859- * @param {string} extra.api - The name of the API, used to determine which SVG to fetch.
1860- * @param {string} extra.model - The model name, used to check for the substring "claude".
18611859 */
18621860function insertSVGIcon(mes, extra) {
18631861 // Determine the SVG filename
@@ -1905,56 +1903,6 @@ function insertSVGIcon(mes, extra) {
19051903 createModelImage('thinking-icon', '.mes_reasoning_header_title', true);
19061904}
19071905
1908-
1909-function getMessageFromTemplate({
1910- mesId,
1911- swipeId,
1912- characterName,
1913- isUser,
1914- avatarImg,
1915- bias,
1916- isSystem,
1917- title,
1918- timerValue,
1919- timerTitle,
1920- bookmarkLink,
1921- forceAvatar,
1922- timestamp,
1923- tokenCount,
1924- extra,
1925- type,
1926-}) {
1927- const mes = messageTemplate.clone();
1928- mes.attr({
1929- 'mesid': mesId,
1930- 'swipeid': swipeId,
1931- 'ch_name': characterName,
1932- 'is_user': isUser,
1933- 'is_system': !!isSystem,
1934- 'bookmark_link': bookmarkLink,
1935- 'force_avatar': !!forceAvatar,
1936- 'timestamp': timestamp,
1937- ...(type ? { type } : {}),
1938- });
1939- mes.find('.avatar img').attr('src', avatarImg);
1940- mes.find('.ch_name .name_text').text(characterName);
1941- mes.find('.mes_bias').html(bias);
1942- mes.find('.timestamp').text(timestamp).attr('title', `${extra?.api ? extra.api + ' - ' : ''}${extra?.model ?? ''}`);
1943- mes.find('.mesIDDisplay').text(`#${mesId}`);
1944- tokenCount && mes.find('.tokenCounterDisplay').text(`${tokenCount}t`);
1945- title && mes.attr('title', title);
1946- timerValue && mes.find('.mes_timer').attr('title', timerTitle).text(timerValue);
1947- bookmarkLink && updateBookmarkDisplay(mes);
1948-
1949- updateReasoningUI(mes);
1950-
1951- if (power_user.timestamp_model_icon && extra?.api) {
1952- insertSVGIcon(mes, extra);
1953- }
1954-
1955- return mes;
1956-}
1957-
19581906/**
19591907 * Re-renders a message block with updated content.
19601908 * @param {number} messageId Message ID
@@ -2172,10 +2120,6 @@ export function appendMediaToMessage(mes, messageElement, scrollBehavior = SCROL
21722120 chatElement.scrollTop(scrollPosition);
21732121 return;
21742122 }
2175- const newScrollPosition = chatElement.scrollTop();
2176- if (newScrollPosition > scrollPosition) {
2177- return;
2178- }
21792123 const newChatHeight = chatElement.prop('scrollHeight');
21802124 const diff = newChatHeight - chatHeight;
21812125 chatElement.scrollTop(scrollPosition + diff);
@@ -2446,6 +2390,9 @@ export function addCopyToCodeBlocks(messageElement) {
24462390 * @returns {JQuery<HTMLElement>} The newly added message element
24472391 */
24482392export function addOneMessage(mes, { type = 'normal', insertAfter = null, scroll = true, insertBefore = null, forceId = null, showSwipes = true, insert = true } = {}) {
2393+ // Callers push the new message to chat before calling addOneMessage
2394+ const newMessageId = typeof forceId == 'number' ? forceId : chat.length - 1;
2395+
24492396 let messageText = mes.mes;
24502397 const momentDate = timestampToMoment(mes.send_date);
24512398 const timestamp = momentDate.isValid() ? momentDate.format('LL LT') : '';
@@ -2454,16 +2401,8 @@ export function addOneMessage(mes, { type = 'normal', insertAfter = null, scroll
24542401 messageText = mes.extra.display_text;
24552402 }
24562403
2457- // Forbidden black magic
2458- // This allows to use "continue" on user messages
2459- if (type === 'swipe' && mes.swipe_id === undefined) {
2460- mes.swipe_id = 0;
2461- mes.swipes = [mes.mes];
2462- }
2463-
24642404 let avatarImg = getThumbnailUrl('persona', user_avatar);
24652405 const isSystem = mes.is_system;
2466- const title = mes.title;
24672406
24682407 //for non-user messages
24692408 if (!mes.is_user) {
@@ -2479,7 +2418,7 @@ export function addOneMessage(mes, { type = 'normal', insertAfter = null, scroll
24792418 }
24802419 }
24812420 //old processing:
24822421 //if messgemessage is from sytemsystem, use the name provided in the message JSONL to proceed,
24832422 //if not system message, use name2 (char's name) to proceed
24842423 //characterName = mes.is_system || mes.force_avatar ? mes.name : name2;
24852424 } else if (mes.is_user && mes.force_avatar) {
@@ -2499,46 +2438,70 @@ export function addOneMessage(mes, { type = 'normal', insertAfter = null, scroll
24992438 sanitizerOverrides,
25002439 false,
25012440 );
2502- const bias = messageFormatting(mes.extra?.bias ?? '', '', false, false, -1, {}, false);
2441+ let newMessage;
2503- let bookmarkLink = mes?.extra?.bookmark_link ?? '';
2442+
2504-
2443+ if (type === 'swipe') {
2505- let params = {
2444+ // Forbidden black magic
2506- mesId: forceId ?? chat.length - 1,
2445+ // This allows to use "continue" on user messages
25072446 swipeId: mes.swipe_id ??= 0,;
2508- characterName: mes.name,
2447+ mes.swipes ??= [mes.mes];
2509- isUser: mes.is_user,
2448+ //This keeps listeners intact.
2510- avatarImg: avatarImg,
2449+ newMessage = chatElement.find(`[mesid="${newMessageId}"]`);
2511- bias: bias,
2450+ } else {
2512- isSystem: isSystem,
2451+ newMessage = messageTemplate.clone();
2513- title: title,
2452+ }
2514- bookmarkLink: bookmarkLink,
2453+
2515- forceAvatar: mes.force_avatar,
2454+ const { timerValue, timerTitle } = formatGenerationTimer(mes.gen_started, mes.gen_finished, mes.extra?.token_count, mes.extra?.reasoning_duration, mes.extra?.time_to_first_token);
2516- timestamp: timestamp,
2455+ const tokenCount = mes.extra?.token_count;
2517- extra: mes.extra,
2456+ const bookmarkLink = mes?.extra?.bookmark_link;
2518- tokenCount: mes.extra?.token_count ?? 0,
2457+
2519- type: mes.extra?.type ?? '',
2458+ newMessage.attr({
2520- ...formatGenerationTimer(mes.gen_started, mes.gen_finished, mes.extra?.token_count, mes.extra?.reasoning_duration, mes.extra?.time_to_first_token),
2459+ 'mesid': newMessageId,
2521- };
2460+ 'swipeid': mes.swipe_id ?? 0,
2461+ 'ch_name': mes.name,
2462+ 'is_user': mes.is_user,
2463+ 'is_system': !!mes.is_system,
2464+ 'bookmark_link': bookmarkLink,
2465+ 'force_avatar': !!mes.force_avatar,
2466+ 'timestamp': timestamp,
2467+ // ...(type ?? { type }),
2468+ 'type': mes.extra?.type ?? '',
2469+ });
2470+
2471+ newMessage.find('.avatar img').attr('src', avatarImg);
2472+ newMessage.find('.ch_name .name_text').text(mes.name);
2473+ newMessage.find('.timestamp').text(timestamp).attr('title', `${mes.extra?.api ? mes.extra.api + ' - ' : ''}${mes.extra?.model ?? ''}`);
2474+ newMessage.find('.mesIDDisplay').text(`#${newMessageId}`);
2475+ tokenCount && newMessage.find('.tokenCounterDisplay').text(`${tokenCount}t`);
2476+ mes.title && newMessage.attr('title', mes.title);
2477+ timerValue && newMessage.find('.mes_timer').attr('title', timerTitle).text(timerValue);
2478+ bookmarkLink && updateBookmarkDisplay(newMessage);
2479+
2480+ if (mes.extra?.bias !== '') {
2481+ const bias = messageFormatting(mes.extra?.bias, '', false, false, -1, {}, false);
2482+ newMessage.find('.mes_bias').html(bias);
2483+ }
2484+
2485+ updateReasoningUI(newMessage);
2486+
2487+ if (power_user.timestamp_model_icon && mes.extra?.api) {
2488+ insertSVGIcon(newMessage, mes.extra);
2489+ }
25222490
2523- const renderedMessage = getMessageFromTemplate(params);
25242491
25252492 if (type !== 'swipe' && insert) {
25262493 if (!insertAfter && !insertBefore) {
25272494 chatElement.append(renderedMessagenewMessage);
25282495 }
25292496 else if (insertAfter) {
25302497 const target = chatElement.find(`.mes[mesid="${insertAfter}"]`);
25312498 $(renderedMessagenewMessage).insertAfter(target);
25322499 } else {
25332500 const target = chatElement.find(`.mes[mesid="${insertBefore}"]`);
25342501 $(renderedMessagenewMessage).insertBefore(target);
25352502 }
25362503 }
25372504
2538- // Callers push the new message to chat before calling addOneMessage
2539- const newMessageId = typeof forceId == 'number' ? forceId : chat.length - 1;
2540-
2541- const newMessage = insert ? chatElement.find(`[mesid="${newMessageId}"]`) : renderedMessage;
25422505 const isSmallSys = mes?.extra?.isSmallSys;
25432506
25442507 if (isSmallSys === true) {
@@ -2549,12 +2512,9 @@ export function addOneMessage(mes, { type = 'normal', insertAfter = null, scroll
25492512 newMessage.addClass('toolCall');
25502513 }
25512514
2552- //shows or hides the Prompt display button
2553- let mesIdToFind = type === 'swipe' ? params.mesId - 1 : params.mesId; //Number(newMessage.attr('mesId'));
2554-
25552515 //if we have itemized messages, and the array isn't null..
25562516 if (params!mes.isUser === falseis_user && Array.isArray(itemizedPrompts) && itemizedPrompts.length > 0) {
25572517 const itemizedPrompt = itemizedPrompts.find(x => Number(x.mesId) === Number(mesIdToFindnewMessageId));
25582518 if (itemizedPrompt) {
25592519 newMessage.find('.mes_prompt').show();
25602520 }
@@ -2565,32 +2525,13 @@ export function addOneMessage(mes, { type = 'normal', insertAfter = null, scroll
25652525 $(this).parent().html('<div class="missing-avatar fa-solid fa-user-slash"></div>');
25662526 });
25672527
2568- if (type === 'swipe') {
2528+ appendMediaToMessage(mes, newMessage, scroll ? SCROLL_BEHAVIOR.ADJUST : SCROLL_BEHAVIOR.NONE);
25692529 newMessage.attrfind('swipeid.mes_text', params).swipeIdhtml(messageText);
2570- newMessage.find('.mes_text').html(messageText).attr('title', title);
2571- newMessage.find('.timestamp').text(timestamp).attr('title', `${params.extra.api} - ${params.extra.model}`);
2572- updateReasoningUI(newMessage);
2573- appendMediaToMessage(mes, newMessage, scroll ? SCROLL_BEHAVIOR.ADJUST : SCROLL_BEHAVIOR.NONE);
2574- if (power_user.timestamp_model_icon && params.extra?.api) {
2575- insertSVGIcon(newMessage, params.extra);
2576- }
2577-
2578- if (mes.swipe_id == mes.swipes.length - 1) {
2579- newMessage.find('.mes_timer').text(params.timerValue).attr('title', params.timerTitle);
2580- newMessage.find('.tokenCounterDisplay').text(`${params.tokenCount}t`);
2581- } else {
2582- newMessage.find('.mes_timer').empty();
2583- newMessage.find('.tokenCounterDisplay').empty();
2584- }
2585- } else {
2586- newMessage.find('.mes_text').append(messageText);
2587- appendMediaToMessage(mes, newMessage, scroll ? SCROLL_BEHAVIOR.ADJUST : SCROLL_BEHAVIOR.NONE);
2588- }
25892530
25902531 addCopyToCodeBlocks(newMessage);
25912532
25922533 // Set the swipes counter for all non-user messages.
25932534 if (!paramsmes.isUseris_user) {
25942535 updateSwipeCounter(newMessageId, { messageElement: newMessage });
25952536 }
25962537