`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>
Signed| @@ -1855,9 +1855,7 @@ export function messageFormatting(mes, ch_name, isSystem, isUser, messageId, san | ||
| 1855 | 1855 | * the value in `extra.api`. |
| 1856 | 1856 | * |
| 1857 | 1857 | * @param {JQuery<HTMLElement>} mes - The message element containing the timestamp where the icon should be inserted or replaced. |
| 1858 | 1858 | * @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". | |
| 1861 | 1859 | */ |
| 1862 | 1860 | function insertSVGIcon(mes, extra) { |
| 1863 | 1861 | // Determine the SVG filename |
| @@ -1905,56 +1903,6 @@ function insertSVGIcon(mes, extra) { | ||
| 1905 | 1903 | createModelImage('thinking-icon', '.mes_reasoning_header_title', true); |
| 1906 | 1904 | } |
| 1907 | 1905 | |
| 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 | - | |
| 1958 | 1906 | /** |
| 1959 | 1907 | * Re-renders a message block with updated content. |
| 1960 | 1908 | * @param {number} messageId Message ID |
| @@ -2172,10 +2120,6 @@ export function appendMediaToMessage(mes, messageElement, scrollBehavior = SCROL | ||
| 2172 | 2120 | chatElement.scrollTop(scrollPosition); |
| 2173 | 2121 | return; |
| 2174 | 2122 | } |
| 2175 | - const newScrollPosition = chatElement.scrollTop(); | |
| 2176 | - if (newScrollPosition > scrollPosition) { | |
| 2177 | - return; | |
| 2178 | - } | |
| 2179 | 2123 | const newChatHeight = chatElement.prop('scrollHeight'); |
| 2180 | 2124 | const diff = newChatHeight - chatHeight; |
| 2181 | 2125 | chatElement.scrollTop(scrollPosition + diff); |
| @@ -2446,6 +2390,9 @@ export function addCopyToCodeBlocks(messageElement) { | ||
| 2446 | 2390 | * @returns {JQuery<HTMLElement>} The newly added message element |
| 2447 | 2391 | */ |
| 2448 | 2392 | export 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 | + | |
| 2449 | 2396 | let messageText = mes.mes; |
| 2450 | 2397 | const momentDate = timestampToMoment(mes.send_date); |
| 2451 | 2398 | const timestamp = momentDate.isValid() ? momentDate.format('LL LT') : ''; |
| @@ -2454,16 +2401,8 @@ export function addOneMessage(mes, { type = 'normal', insertAfter = null, scroll | ||
| 2454 | 2401 | messageText = mes.extra.display_text; |
| 2455 | 2402 | } |
| 2456 | 2403 | |
| 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 | - | |
| 2464 | 2404 | let avatarImg = getThumbnailUrl('persona', user_avatar); |
| 2465 | 2405 | const isSystem = mes.is_system; |
| 2466 | - const title = mes.title; | |
| 2467 | 2406 | |
| 2468 | 2407 | //for non-user messages |
| 2469 | 2408 | if (!mes.is_user) { |
| @@ -2479,7 +2418,7 @@ export function addOneMessage(mes, { type = 'normal', insertAfter = null, scroll | ||
| 2479 | 2418 | } |
| 2480 | 2419 | } |
| 2481 | 2420 | //old processing: |
| 2482 | 2421 | //if messgemessage is from sytemsystem, use the name provided in the message JSONL to proceed, |
| 2483 | 2422 | //if not system message, use name2 (char's name) to proceed |
| 2484 | 2423 | //characterName = mes.is_system || mes.force_avatar ? mes.name : name2; |
| 2485 | 2424 | } else if (mes.is_user && mes.force_avatar) { |
| @@ -2499,46 +2438,70 @@ export function addOneMessage(mes, { type = 'normal', insertAfter = null, scroll | ||
| 2499 | 2438 | sanitizerOverrides, |
| 2500 | 2439 | false, |
| 2501 | 2440 | ); |
| 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 | |
| 2507 | 2446 | 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 | + } | |
| 2522 | 2490 | |
| 2523 | - const renderedMessage = getMessageFromTemplate(params); | |
| 2524 | 2491 | |
| 2525 | 2492 | if (type !== 'swipe' && insert) { |
| 2526 | 2493 | if (!insertAfter && !insertBefore) { |
| 2527 | 2494 | chatElement.append(renderedMessagenewMessage); |
| 2528 | 2495 | } |
| 2529 | 2496 | else if (insertAfter) { |
| 2530 | 2497 | const target = chatElement.find(`.mes[mesid="${insertAfter}"]`); |
| 2531 | 2498 | $(renderedMessagenewMessage).insertAfter(target); |
| 2532 | 2499 | } else { |
| 2533 | 2500 | const target = chatElement.find(`.mes[mesid="${insertBefore}"]`); |
| 2534 | 2501 | $(renderedMessagenewMessage).insertBefore(target); |
| 2535 | 2502 | } |
| 2536 | 2503 | } |
| 2537 | 2504 | |
| 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; | |
| 2542 | 2505 | const isSmallSys = mes?.extra?.isSmallSys; |
| 2543 | 2506 | |
| 2544 | 2507 | if (isSmallSys === true) { |
| @@ -2549,12 +2512,9 @@ export function addOneMessage(mes, { type = 'normal', insertAfter = null, scroll | ||
| 2549 | 2512 | newMessage.addClass('toolCall'); |
| 2550 | 2513 | } |
| 2551 | 2514 | |
| 2552 | - //shows or hides the Prompt display button | |
| 2553 | - let mesIdToFind = type === 'swipe' ? params.mesId - 1 : params.mesId; //Number(newMessage.attr('mesId')); | |
| 2554 | - | |
| 2555 | 2515 | //if we have itemized messages, and the array isn't null.. |
| 2556 | 2516 | if (params!mes.isUser === falseis_user && Array.isArray(itemizedPrompts) && itemizedPrompts.length > 0) { |
| 2557 | 2517 | const itemizedPrompt = itemizedPrompts.find(x => Number(x.mesId) === Number(mesIdToFindnewMessageId)); |
| 2558 | 2518 | if (itemizedPrompt) { |
| 2559 | 2519 | newMessage.find('.mes_prompt').show(); |
| 2560 | 2520 | } |
| @@ -2565,32 +2525,13 @@ export function addOneMessage(mes, { type = 'normal', insertAfter = null, scroll | ||
| 2565 | 2525 | $(this).parent().html('<div class="missing-avatar fa-solid fa-user-slash"></div>'); |
| 2566 | 2526 | }); |
| 2567 | 2527 | |
| 2568 | - if (type === 'swipe') { | |
| 2528 | + appendMediaToMessage(mes, newMessage, scroll ? SCROLL_BEHAVIOR.ADJUST : SCROLL_BEHAVIOR.NONE); | |
| 2569 | 2529 | 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 | - } | |
| 2589 | 2530 | |
| 2590 | 2531 | addCopyToCodeBlocks(newMessage); |
| 2591 | 2532 | |
| 2592 | 2533 | // Set the swipes counter for all non-user messages. |
| 2593 | 2534 | if (!paramsmes.isUseris_user) { |
| 2594 | 2535 | updateSwipeCounter(newMessageId, { messageElement: newMessage }); |
| 2595 | 2536 | } |
| 2596 | 2537 | |