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