Refactored `swipe` and moved `messageEdit` to a separate function. (#4610) * Extracted `messageEdit` and `messageEditCancel` from `.mes_edit` and `.mes_edit_cancel` * Fixed. https://github.com/SillyTavern/SillyTavern/pull/4633#discussion_r241505 https://github.com/SillyTavern/SillyTavern/pull/4633#pullrequestreview-3316588180 * Refactored `swipe` and moved `messageEdit` to a separate function. Also, a few more minor changes. * Fixed bug. https://github.com/SillyTavern/SillyTavern/pull/4610#discussion_r2404789035 https://github.com/SillyTavern/SillyTavern/pull/4610#discussion_r2404789038 * Fixed. https://github.com/SillyTavern/SillyTavern/pull/4610#discussion_r2408682277 https://github.com/SillyTavern/SillyTavern/pull/4610#discussion_r2408689706 https://github.com/SillyTavern/SillyTavern/pull/4610#discussion_r2408690772 https://github.com/SillyTavern/SillyTavern/pull/4610#discussion_r2408697066 https://github.com/SillyTavern/SillyTavern/pull/4610#discussion_r2408705156 https://github.com/SillyTavern/SillyTavern/pull/4610#discussion_r2408708088 https://github.com/SillyTavern/SillyTavern/pull/4610#discussion_r2408725971 https://github.com/SillyTavern/SillyTavern/pull/4610#discussion_r2408726241 https://github.com/SillyTavern/SillyTavern/pull/4610#discussion_r2408740050 https://github.com/SillyTavern/SillyTavern/pull/4610#discussion_r2408745918 https://github.com/SillyTavern/SillyTavern/pull/4610#discussion_r2408753165 https://github.com/SillyTavern/SillyTavern/pull/4610#discussion_r2408761262 https://github.com/SillyTavern/SillyTavern/pull/4610#discussion_r2408764531 https://github.com/SillyTavern/SillyTavern/pull/4610#discussion_r2408781694 https://github.com/SillyTavern/SillyTavern/pull/4610#discussion_r2408784426 https://github.com/SillyTavern/SillyTavern/pull/4610#discussion_r2408794672 https://github.com/SillyTavern/SillyTavern/pull/4610#discussion_r2408802366 https://github.com/SillyTavern/SillyTavern/pull/4610#discussion_r2408803433 * Fixed: https://github.com/SillyTavern/SillyTavern/pull/4610#discussion_r2408702506 https://github.com/SillyTavern/SillyTavern/pull/4610#discussion_r2408805635 https://github.com/SillyTavern/SillyTavern/pull/4610#discussion_r2410368443 https://github.com/SillyTavern/SillyTavern/pull/4610#discussion_r2408791120 * Bugfixes. Incomplete. * Fix formatting * Use scrollTop because scrollIntoView breaks layout on phones. * Only show '?' in `formaSwipeCounter` if something is wrong. https://github.com/SillyTavern/SillyTavern/pull/4610#discussion_r2412169005 * Respect animation_duration if it's >= 0. * Disabled expandNewMessage's animation on Cohee's request. https://github.com/SillyTavern/SillyTavern/pull/4610/files#r2408731744 * Only hide the swipe counter when a generation is ongoing. https://github.com/SillyTavern/SillyTavern/pull/4610#discussion_r2440588779 * refreshSwipeButtons * Adjust swipeDuration constant * feat: add refreshSwipeButtons and isSwipingAllowed to context * Fixed image swipes. https://github.com/SillyTavern/SillyTavern/pull/4610#discussion_r2442489827 https://github.com/SillyTavern/SillyTavern/pull/4610#discussion_r2442490042 * Fixed. https://github.com/SillyTavern/SillyTavern/pull/4610#issuecomment-3418657967 * Fixed: https://github.com/SillyTavern/SillyTavern/pull/4610#discussion_r2442480238 * Fixed. https://github.com/SillyTavern/SillyTavern/pull/4610#discussion_r2442536011 * Minor refactor. * Fixed: https://github.com/SillyTavern/SillyTavern/pull/4610#discussion_r2443359660 https://github.com/SillyTavern/SillyTavern/pull/4610#discussion_r2443359435 * Fixed: https://github.com/SillyTavern/SillyTavern/pull/4610#discussion_r2443357913 * Fix registration of click events * Fix passing data to swipe events --------- Co-authored-by: user <user@exmaple.com> Co-authored-by: Cohee <18619528+Cohee1207@users.noreply.github.com>

20556aa3dde506601ba39fe6bcdf5c8d257855a6

DeclineThyself <FallenHaze@tutamail.com>

Signed
7 files changed, +411 -346Showing whitespace changes
public/script.js+392 -338
@@ -180,8 +180,9 @@ import {
180 shiftUpByOne,180 shiftUpByOne,
181 shiftDownByOne,181 shiftDownByOne,
182 canUseNegativeLookbehind,182 canUseNegativeLookbehind,
183 clamp,
183} from './scripts/utils.js';184} from './scripts/utils.js';
184import { debounce_timeout, GENERATION_TYPE_TRIGGERS, IGNORE_SYMBOL, inject_ids } from './scripts/constants.js';185import { debounce_timeout, GENERATION_TYPE_TRIGGERS, IGNORE_SYMBOL, inject_ids, SWIPE_DIRECTION } from './scripts/constants.js';
185186
186import { cancelDebouncedMetadataSave, doDailyExtensionUpdatesCheck, extension_settings, initExtensions, loadExtensionSettings, runGenerationInterceptors, saveMetadataDebounced } from './scripts/extensions.js';187import { cancelDebouncedMetadataSave, doDailyExtensionUpdatesCheck, extension_settings, initExtensions, loadExtensionSettings, runGenerationInterceptors, saveMetadataDebounced } from './scripts/extensions.js';
187import { COMMENT_NAME_DEFAULT, CONNECT_API_MAP, executeSlashCommandsOnChatInput, initDefaultSlashCommands, isExecutingCommandsFromChatInput, pauseScriptExecution, stopScriptExecution, UNIQUE_APIS } from './scripts/slash-commands.js';188import { COMMENT_NAME_DEFAULT, CONNECT_API_MAP, executeSlashCommandsOnChatInput, initDefaultSlashCommands, isExecutingCommandsFromChatInput, pauseScriptExecution, stopScriptExecution, UNIQUE_APIS } from './scripts/slash-commands.js';
@@ -364,6 +365,7 @@ let default_user_name = 'User';
364export let name1 = default_user_name;365export let name1 = default_user_name;
365export let name2 = systemUserName;366export let name2 = systemUserName;
366export let chat = [];367export let chat = [];
368export let isSwipingAllowed = true; //false when a swipe is in progress, or swiping is blocked.
367let chatSaveTimeout;369let chatSaveTimeout;
368let importFlashTimeout;370let importFlashTimeout;
369export let isChatSaving = false;371export let isChatSaving = false;
@@ -1419,8 +1421,7 @@ export async function printMessages() {
14191421
1420 chatElement.find('.mes').removeClass('last_mes');1422 chatElement.find('.mes').removeClass('last_mes');
1421 chatElement.find('.mes').last().addClass('last_mes');1423 chatElement.find('.mes').last().addClass('last_mes');
1422 hideSwipeButtons();1424 refreshSwipeButtons();
1423 showSwipeButtons();
1424 scrollChatToBottom();1425 scrollChatToBottom();
1425 applyStylePins();1426 applyStylePins();
14261427
@@ -1524,8 +1525,7 @@ export async function deleteMessage(id, swipeDeletionIndex = undefined, askConfi
1524 this_edit_mes_id = undefined;1525 this_edit_mes_id = undefined;
1525 }1526 }
15261527
1527 hideSwipeButtons();1528 refreshSwipeButtons();
1528 showSwipeButtons();
15291529
1530 await eventSource.emit(event_types.MESSAGE_DELETED, chat.length);1530 await eventSource.emit(event_types.MESSAGE_DELETED, chat.length);
1531}1531}
@@ -1549,8 +1549,7 @@ export async function reloadCurrentChat() {
1549 await eventSource.emit(event_types.CHAT_CHANGED, getCurrentChatId());1549 await eventSource.emit(event_types.CHAT_CHANGED, getCurrentChatId());
1550 }1550 }
15511551
1552 hideSwipeButtons();1552 refreshSwipeButtons();
1553 showSwipeButtons();
1554}1553}
15551554
1556/**1555/**
@@ -2143,7 +2142,8 @@ export function addOneMessage(mes, { type = 'normal', insertAfter = null, scroll
2143 });2142 });
21442143
2145 if (type === 'swipe') {2144 if (type === 'swipe') {
2146 const swipeMessage = chatElement.find(`[mesid="${chat.length - 1}"]`);2145 const messageId = forceId ?? chat.length - 1;
2146 const swipeMessage = chatElement.find(`[mesid="${messageId}"]`);
2147 swipeMessage.attr('swipeid', params.swipeId);2147 swipeMessage.attr('swipeid', params.swipeId);
2148 swipeMessage.find('.mes_text').html(messageText).attr('title', title);2148 swipeMessage.find('.mes_text').html(messageText).attr('title', title);
2149 swipeMessage.find('.timestamp').text(timestamp).attr('title', `${params.extra.api} - ${params.extra.model}`);2149 swipeMessage.find('.timestamp').text(timestamp).attr('title', `${params.extra.api} - ${params.extra.model}`);
@@ -2179,8 +2179,7 @@ export function addOneMessage(mes, { type = 'normal', insertAfter = null, scroll
2179 if (showSwipes) {2179 if (showSwipes) {
2180 chatElement.find('.mes').last().addClass('last_mes');2180 chatElement.find('.mes').last().addClass('last_mes');
2181 chatElement.find('.mes').eq(-2).removeClass('last_mes');2181 chatElement.find('.mes').eq(-2).removeClass('last_mes');
2182 hideSwipeButtons();2182 refreshSwipeButtons();
2183 showSwipeButtons();
2184 }2183 }
21852184
2186 // Don't scroll if not inserting last2185 // Don't scroll if not inserting last
@@ -2921,7 +2920,7 @@ class StreamingProcessor {
2921 await this.#checkDomElements(messageId, continueOnReasoning);2920 await this.#checkDomElements(messageId, continueOnReasoning);
2922 this.markUIGenStarted();2921 this.markUIGenStarted();
2923 }2922 }
2924 hideSwipeButtons();2923 hideSwipeButtons({ hideCounters: true });
2925 scrollChatToBottom();2924 scrollChatToBottom();
2926 return messageId;2925 return messageId;
2927 }2926 }
@@ -6059,21 +6058,21 @@ export function syncSwipeToMes(messageId = null, swipeId = null) {
6059 }6058 }
60606059
6061 const targetSwipeId = targetMessage.swipe_id;6060 const targetSwipeId = targetMessage.swipe_id;
6062 if (!targetMessage.swipes[targetSwipeId] || !targetMessage.swipe_info[targetSwipeId]) {6061 if (typeof targetMessage.swipes[targetSwipeId] !== 'string') {
6063 console.warn(`[syncSwipeToMes] Invalid swipe ID: ${targetSwipeId}`);6062 console.warn(`[syncSwipeToMes] Invalid swipe ID: ${targetSwipeId}`);
6064 return false;6063 return false;
6065 }6064 }
60666065
6067 const targetSwipeInfo = targetMessage.swipe_info[targetSwipeId];6066 const targetSwipeInfo = targetMessage?.swipe_info[targetSwipeId];
6068 if (typeof targetSwipeInfo !== 'object') {6067 if (typeof targetSwipeInfo !== 'object') {
6069 return false;6068 console.warn(`[syncSwipeToMes] Invalid swipe info: ${targetSwipeId}`);
6070 }6069 }
60716070
6072 targetMessage.mes = targetMessage.swipes[targetSwipeId];6071 targetMessage.mes = targetMessage.swipes[targetSwipeId];
6073 targetMessage.send_date = targetSwipeInfo.send_date;6072 targetMessage.send_date = targetSwipeInfo?.send_date;
6074 targetMessage.gen_started = targetSwipeInfo.gen_started;6073 targetMessage.gen_started = targetSwipeInfo?.gen_started;
6075 targetMessage.gen_finished = targetSwipeInfo.gen_finished;6074 targetMessage.gen_finished = targetSwipeInfo?.gen_finished;
6076 targetMessage.extra = structuredClone(targetSwipeInfo.extra);6075 targetMessage.extra = structuredClone(targetSwipeInfo?.extra) ?? {};
60776076
6078 return true;6077 return true;
6079}6078}
@@ -6276,11 +6275,11 @@ export async function renameCharacter(name = null, { silent = false, renameChats
6276 const oldName = getCharaFilename(null, { manualAvatarKey: oldAvatar });6275 const oldName = getCharaFilename(null, { manualAvatarKey: oldAvatar });
6277 const newName = getCharaFilename(null, { manualAvatarKey: newAvatar });6276 const newName = getCharaFilename(null, { manualAvatarKey: newAvatar });
62786277
6279 // Replace other auxillery fields where was referenced by avatar key6278 // Replace other auxiliary fields where was referenced by avatar key
6280 // Tag List6279 // Tag List
6281 renameTagKey(oldAvatar, newAvatar);6280 renameTagKey(oldAvatar, newAvatar);
62826281
6283 // Addtional lore books6282 // Additional lore books
6284 const charLore = world_info.charLore?.find(x => x.name == oldName);6283 const charLore = world_info.charLore?.find(x => x.name == oldName);
6285 if (charLore) {6284 if (charLore) {
6286 charLore.name = newName;6285 charLore.name = newName;
@@ -6446,7 +6445,7 @@ export function saveChatDebounced() {
6446 * @param {string} [options.chatName] The name of the chat file to save to6445 * @param {string} [options.chatName] The name of the chat file to save to
6447 * @param {object} [options.withMetadata] Additional metadata to save with the chat6446 * @param {object} [options.withMetadata] Additional metadata to save with the chat
6448 * @param {number} [options.mesId] The message ID to save the chat up to6447 * @param {number} [options.mesId] The message ID to save the chat up to
6449 * @param {boolean} [options.force] Force the saving despire the integrity check result6448 * @param {boolean} [options.force] Force the saving despite the integrity check result
6450 *6449 *
6451 * @returns {Promise<void>}6450 * @returns {Promise<void>}
6452 */6451 */
@@ -7003,8 +7002,7 @@ export async function getSettings() {
70037002
7004 swipes = settings.swipes !== undefined ? !!settings.swipes : true; // enable swipes by default7003 swipes = settings.swipes !== undefined ? !!settings.swipes : true; // enable swipes by default
7005 $('#swipes-checkbox').prop('checked', swipes); /// swipecode7004 $('#swipes-checkbox').prop('checked', swipes); /// swipecode
7006 hideSwipeButtons();7005 refreshSwipeButtons();
7007 showSwipeButtons();
70087006
7009 // Kobold7007 // Kobold
7010 loadKoboldSettings(data, settings.kai_settings ?? settings, settings);7008 loadKoboldSettings(data, settings.kai_settings ?? settings, settings);
@@ -8078,18 +8076,42 @@ export function callPopup(text, type, inputValue = '', { okButton, rows, wide, w
8078 });8076 });
8079}8077}
80808078
8081export function showSwipeButtons() {8079/**
8082 if (chat.length === 0 || this_edit_mes_id >= 0) {8080 * Update the swipe counter for mesId.
8081 * @param {Number} mesId
8082 */
8083export async function updateSwipeCounter(mesId) {
8084 const swipeCounterText = formatSwipeCounter((chat[mesId]?.['swipe_id'] + 1), chat[mesId]?.['swipes']?.length);
8085 const currentMessage = chatElement.children().filter(`[mesid="${mesId}"]`);
8086 const swipeCounter = currentMessage.find('.swipes-counter');
8087 swipeCounter.text(swipeCounterText).show();
8088}
8089
8090/**
8091 * Swipe buttons are often toggled to update their position.
8092 * This should be replaced with a more efficient function.
8093 */
8094export function refreshSwipeButtons() {
8095 hideSwipeButtons();
8096 showSwipeButtons();
8097}
8098
8099export function showSwipeButtons(mesId = chat.length - 1) {
8100 isSwipingAllowed = true;
8101
8102 if (chat.length === 0) {
8083 return;8103 return;
8084 }8104 }
80858105
8086 if (8106 if (
8087 chat[chat.length - 1].is_system ||8107 chat[mesId].is_system ||
8088 !swipes ||8108 !swipes ||
8089 Number($('.mes:last').attr('mesid')) < 0 ||8109 Number($('.mes:last').attr('mesid')) < 0 ||
8090 chat[chat.length - 1].is_user ||8110 chat[mesId].is_user ||
8091 (selected_group && is_group_generating)8111 (selected_group && is_group_generating)
8092 ) { return; }8112 ) {
8113 return;
8114 }
80938115
8094 // swipe_id should be set if alternate greetings are added8116 // swipe_id should be set if alternate greetings are added
8095 if (chat.length == 1 && chat[0].swipe_id === undefined) {8117 if (chat.length == 1 && chat[0].swipe_id === undefined) {
@@ -8099,35 +8121,35 @@ export function showSwipeButtons() {
8099 //had to add this to make the swipe counter work8121 //had to add this to make the swipe counter work
8100 //(copied from the onclick functions for swipe buttons..8122 //(copied from the onclick functions for swipe buttons..
8101 //don't know why the array isn't set for non-swipe messages in Generate or addOneMessage..)8123 //don't know why the array isn't set for non-swipe messages in Generate or addOneMessage..)
8102 if (chat[chat.length - 1]['swipe_id'] === undefined) { // if there is no swipe-message in the last spot of the chat array8124 if (chat[mesId]['swipe_id'] === undefined) { // if there is no swipe-message in the last spot of the chat array
8103 chat[chat.length - 1]['swipe_id'] = 0; // set it to id 08125 chat[mesId]['swipe_id'] = 0; // set it to id 0
8104 chat[chat.length - 1]['swipes'] = []; // empty the array8126 chat[mesId]['swipes'] = []; // empty the array
8105 chat[chat.length - 1]['swipes'][0] = chat[chat.length - 1]['mes']; //assign swipe array with last message from chat8127 chat[mesId]['swipes'][0] = chat[mesId]['mes']; //assign swipe array with last message from chat
8106 chat[chat.length - 1]['swipe_info'] = [];8128 chat[mesId]['swipe_info'] = [];
8107 chat[chat.length - 1]['swipe_info'][0] = {8129 chat[mesId]['swipe_info'][0] = {
8108 'send_date': chat[chat.length - 1]['send_date'],8130 'send_date': chat[mesId]['send_date'],
8109 'gen_started': chat[chat.length - 1]['gen_started'],8131 'gen_started': chat[mesId]['gen_started'],
8110 'gen_finished': chat[chat.length - 1]['gen_finished'],8132 'gen_finished': chat[mesId]['gen_finished'],
8111 'extra': structuredClone(chat[chat.length - 1]['extra']),8133 'extra': structuredClone(chat[mesId]['extra']),
8112 };8134 };
8113 }8135 }
81148136
8115 const currentMessage = chatElement.children().filter(`[mesid="${chat.length - 1}"]`);8137 const currentMessage = chatElement.children().filter(`[mesid="${mesId}"]`);
8116 const swipeId = chat[chat.length - 1].swipe_id;8138 const swipeId = chat[mesId].swipe_id;
8117 const swipeCounterText = formatSwipeCounter((swipeId + 1), chat[chat.length - 1].swipes.length);8139 const swipeCounterText = formatSwipeCounter((swipeId + 1), chat[mesId]['swipes'].length);
8118 const swipeRight = currentMessage.find('.swipe_right');8140 const swipeRight = currentMessage.find('.swipe_right');
8119 const swipeLeft = currentMessage.find('.swipe_left');8141 const swipeLeft = currentMessage.find('.swipe_left');
8120 const swipeCounter = currentMessage.find('.swipes-counter');8142 const swipeCounter = currentMessage.find('.swipes-counter');
81218143
8122 if (swipeId !== undefined && (chat[chat.length - 1].swipes.length > 1 || swipeId > 0)) {8144 if (swipeId !== undefined && (chat[mesId].swipes.length > 1 || swipeId > 0)) {
8123 swipeLeft.css('display', 'flex');8145 swipeLeft.css('display', 'flex');
8124 }8146 }
8125 //only show right when generate is off, or when next right swipe would not make a generate happen8147 //only show right when generate is off, or when next right swipe would not make a generate happen
8126 if (is_send_press === false || chat[chat.length - 1].swipes.length >= swipeId) {8148 if (is_send_press === false || chat[mesId].swipes.length >= swipeId) {
8127 swipeRight.css('display', 'flex').css('opacity', '0.3');8149 swipeRight.css('display', 'flex').css('opacity', '0.3');
8128 swipeCounter.css('opacity', '0.3');8150 swipeCounter.css('opacity', '0.3');
8129 }8151 }
8130 if ((chat[chat.length - 1].swipes.length - swipeId) === 1) {8152 if ((chat[mesId].swipes.length - swipeId) === 1) {
8131 //chevron was moved out of hardcode in HTML to class toggle dependent on last_mes or not8153 //chevron was moved out of hardcode in HTML to class toggle dependent on last_mes or not
8132 //necessary for 'swipe_right' div in past messages to have no chevron if 'show swipes for all messages' is turned on8154 //necessary for 'swipe_right' div in past messages to have no chevron if 'show swipes for all messages' is turned on
8133 swipeRight.css('opacity', '0.7');8155 swipeRight.css('opacity', '0.7');
@@ -8139,10 +8161,17 @@ export function showSwipeButtons() {
8139 lastSwipeCounter.text(swipeCounterText).show();8161 lastSwipeCounter.text(swipeCounterText).show();
8140}8162}
81418163
8142export function hideSwipeButtons() {8164/**
8165 * @param {object} [options] Options
8166 * @param {boolean} [options.hideCounters=false] Also hide the swipes counter.
8167 */
8168export function hideSwipeButtons({ hideCounters = false } = {}) {
8169 isSwipingAllowed = false;
8143 chatElement.find('.swipe_right').hide();8170 chatElement.find('.swipe_right').hide();
8144 chatElement.find('.last_mes .swipes-counter').hide();
8145 chatElement.find('.swipe_left').hide();8171 chatElement.find('.swipe_left').hide();
8172 if (hideCounters === true) {
8173 chatElement.find('.last_mes .swipes-counter').hide();
8174 }
8146}8175}
81478176
8148/**8177/**
@@ -8724,364 +8753,389 @@ export async function createOrEditCharacter(e) {
8724 * @returns {string} The formatted counter.8753 * @returns {string} The formatted counter.
8725 */8754 */
8726function formatSwipeCounter(current, total) {8755function formatSwipeCounter(current, total) {
8727 if (isNaN(current) || isNaN(total)) {8756 if (isNaN(current) && isNaN(total)) {
8728 return '';8757 return '';
8729 }8758 }
87308759 return `${!isNaN(current) ? current : '?'}\u200b/\u200b${!isNaN(total) ? total : '?'}`;
8731 return `${current}\u200b/\u200b${total}`;
8732}8760}
87338761
8734/**8762/**
8735 * Handles the swipe to the left event.8763 * Handles the swipe event.
8736 * @param {JQuery.Event} _event Event.8764 * @param {JQuery.Event} _event Event.
8765 * @param {'left'|'right'} direction The direction to swipe.
8737 * @param {object} params Additional parameters.8766 * @param {object} params Additional parameters.
8738 * @param {string} [params.source] The source of the swipe event.8767 * @param {string} [params.source] The source of the swipe event.
8739 * @param {boolean} [params.repeated] Is the swipe event repeated.8768 * @param {boolean} [params.repeated] Is the swipe event repeated.
8769 * @param {object} [params.message=chat[chat.length - 1]] The chat message to swipe.
8740 */8770 */
8741export function swipe_left(_event, { source, repeated } = {}) {8771export async function swipe(_event, direction, { source, repeated, message = chat[chat.length - 1] } = {}) {
8742 if (chat.length - 1 === Number(this_edit_mes_id)) {8772 if (chat.length === 0) {
8743 closeMessageEditor();8773 console.warn('Swipe was called on an empty chat.');
8774 return;
8744 }8775 }
8745 if (isStreamingEnabled() && streamingProcessor) {8776
8746 streamingProcessor.onStopStreaming();8777 //Only allow one concurrent swipe.
8778 if (!isSwipingAllowed) {
8779 console.info('The swipe has been ignored because another is in progress.');
8780 return;
8747 }8781 }
8782 isSwipingAllowed = false;
87488783
8749 // Make sure ad-hoc changes to extras are saved before swiping away8784 let generation;
8750 syncMesToSwipe();8785 let messageIndex;
87518786
8752 // If the user is holding down the key and we're at the first swipe, don't do anything8787 //Only set messageIndex if message exists because -1 is truthy.
8753 if (source === 'keyboard' && repeated && chat[chat.length - 1].swipe_id === 0) {8788 if (message) {
8789 messageIndex = chat.indexOf(message);
8790 if (messageIndex === -1) {
8791 console.error(`The message must exist in chat. ${message};`);
8754 return;8792 return;
8755 }8793 }
8794 }
87568795
8757 const swipe_duration = 120;8796 const mesId = Number($(this).closest('.mes').attr('mesid') ?? messageIndex ?? chat.length - 1);
8758 const swipe_range = '700px';
8759 chat[chat.length - 1]['swipe_id']--;
87608797
8761 if (chat[chat.length - 1]['swipe_id'] < 0) {8798 const thisMesDiv = chatElement.children().filter(`.mes[mesid="${mesId}"]`);
8762 chat[chat.length - 1]['swipe_id'] = chat[chat.length - 1]['swipes'].length - 1;8799 const thisMesText = thisMesDiv.find('.mes_block .mes_text');
8800 const thisMesDivHeight = thisMesDiv[0]?.scrollHeight;
8801 const thisMesTextHeight = thisMesText[0]?.scrollHeight;
8802 if (![thisMesDiv.length, thisMesText.length].every(num => num > 0 )) {
8803 console.error(`Message #${mesId}'s DOM element is not valid.`);
8804 return;
8763 }8805 }
8806 const originalSwipeId = Number(chat[mesId]?.['swipe_id'] ?? 0);
8807 let newSwipeId = Number(originalSwipeId);
87648808
8765 if (chat[chat.length - 1]['swipe_id'] >= 0) {8809 const isPristine = !chat_metadata?.tainted;
8766 /*$(this).parent().children('swipe_right').css('display', 'flex');8810 const swipeDuration = Math.round(animation_duration * 1.25);
8767 if (chat[chat.length - 1]['swipe_id'] === 0) {8811 const swipeRange = direction === SWIPE_DIRECTION.RIGHT ? -700 : 700;
8768 $(this).css('display', 'none');8812
8769 }*/ // Just in case8813 async function endSwipe() {
8770 if (!Array.isArray(chat[chat.length - 1]['swipe_info'])) {8814 //Wait for the generation to end.
8771 chat[chat.length - 1]['swipe_info'] = [];8815 try {
8772 }8816 await generation;
8773 let this_mes_div = $(this).parent();
8774 let this_mes_block = $(this).parent().children('.mes_block').children('.mes_text');
8775 const this_mes_div_height = this_mes_div[0].scrollHeight;
8776 this_mes_div.css('height', this_mes_div_height);
8777 const this_mes_block_height = this_mes_block[0].scrollHeight;
8778 chat[chat.length - 1]['mes'] = chat[chat.length - 1]['swipes'][chat[chat.length - 1]['swipe_id']];
8779 chat[chat.length - 1]['send_date'] = chat[chat.length - 1].swipe_info[chat[chat.length - 1]['swipe_id']]?.send_date || chat[chat.length - 1].send_date; //load the last mes box with the latest generation
8780 chat[chat.length - 1]['extra'] = structuredClone(chat[chat.length - 1].swipe_info[chat[chat.length - 1]['swipe_id']]?.extra || chat[chat.length - 1].extra);
8781
8782 if (chat[chat.length - 1].extra) {
8783 // if message has memory attached - remove it to allow regen
8784 if (chat[chat.length - 1].extra.memory) {
8785 delete chat[chat.length - 1].extra.memory;
8786 }8817 }
8787 // ditto for display text8818 catch (error) {
8788 if (chat[chat.length - 1].extra.display_text) {8819 console.warn(`Swipe failed, Swiping back. ${error}`);
8789 delete chat[chat.length - 1].extra.display_text;
8790 }8820 }
8821 //Allow for another swipe.
8822 showSwipeButtons();
8823
8824 //Clamp Id between swipes.
8825 let clampedId = clamp(chat[mesId]['swipe_id'], 0, Math.max(0, chat[mesId]['swipes'].length - 1));
8826
8827 //If the id is not within bounds, Swipe back.
8828 if (chat[mesId]['swipe_id'] !== clampedId) {
8829 chat[mesId]['swipe_id'] = clampedId;
8830 syncSwipeToMes(mesId);
8831 addOneMessage(chat[mesId], { type: 'swipe', forceId: mesId, scroll: false });
8791 }8832 }
8792 $(this).parent().children('.mes_block').transition({
8793 x: swipe_range,
8794 duration: animation_duration > 0 ? swipe_duration : 0,
8795 easing: animation_easing,
8796 queue: false,
8797 complete: async function () {
8798 const is_animation_scroll = ($('#chat').scrollTop() >= ($('#chat').prop('scrollHeight') - $('#chat').outerHeight()) - 10);
8799 //console.log('on left swipe click calling addOneMessage');
8800 addOneMessage(chat[chat.length - 1], { type: 'swipe' });
88018833
8802 if (power_user.message_token_count_enabled) {8834 await updateSwipeCounter(mesId);
8803 if (!chat[chat.length - 1].extra) {8835 //Fallback.
8804 chat[chat.length - 1].extra = {};8836 if (mesId != chat.length - 1) {
8837 await updateSwipeCounter(chat.length - 1);
8838 }
8805 }8839 }
88068840
8807 const swipeMessage = $('#chat').find(`[mesid="${chat.length - 1}"]`);8841 async function standardSwipe() {
8808 const tokenCountText = (chat[chat.length - 1]?.extra?.reasoning || '') + chat[chat.length - 1].mes;8842 //If swipe_id has changed, or the source is being deleted.
8809 const tokenCount = await getTokenCountAsync(tokenCountText, 0);8843 if (newSwipeId !== originalSwipeId || source == 'delete')
8810 chat[chat.length - 1]['extra']['token_count'] = tokenCount;8844 {
8811 swipeMessage.find('.tokenCounterDisplay').text(`${tokenCount}t`);8845 //Update the chat.
8846 await loadFromSwipeId(mesId, newSwipeId);
8847 //Transition to the new chat.
8848 await animateSwipe();
8849 }
8850 await endSwipe();
8812 }8851 }
88138852
8814 let new_height = this_mes_div_height - (this_mes_block_height - this_mes_block[0].scrollHeight);8853 /**
8815 if (new_height < 103) new_height = 103;8854 * Sets the message to the newSwipeId and loads it.
8816 this_mes_div.animate({ height: new_height + 'px' }, {8855 * @param {number} mesId
8817 duration: 0, //used to be 1008856 * @param {number} newSwipeId
8818 queue: false,8857 */
8819 progress: function () {8858 async function loadFromSwipeId(mesId, newSwipeId) {
8820 // Scroll the chat down as the message expands8859 //Update the swipe_id.
8860 chat[mesId]['swipe_id'] = newSwipeId;
88218861
8822 if (is_animation_scroll) $('#chat').scrollTop($('#chat')[0].scrollHeight);8862 if (chat[mesId].extra) {
8823 },8863 // if message has memory attached - remove it to allow regen
8824 complete: function () {8864 delete chat[mesId].extra.memory;
8825 this_mes_div.css('height', 'auto');8865
8826 // Scroll the chat down to the bottom once the animation is complete8866 // ditto for display text
8827 if (is_animation_scroll) $('#chat').scrollTop($('#chat')[0].scrollHeight);8867 delete chat[mesId].extra.display_text;
8828 },8868
8829 });8869 delete chat[mesId].extra.image;
8830 $(this).parent().children('.mes_block').transition({8870 delete chat[mesId].extra.image_swipes;
8831 x: '-' + swipe_range,8871 delete chat[mesId].extra.video;
8832 duration: 0,8872 delete chat[mesId].extra.inline_image;
8833 easing: animation_easing,8873 }
8834 queue: false,8874 delete chat[mesId].gen_started;
8835 complete: function () {8875 delete chat[mesId].gen_finished;
8836 $(this).parent().children('.mes_block').transition({8876 //load from swipes.
8837 x: '0px',8877 syncSwipeToMes(mesId, chat[mesId]['swipe_id']);
8838 duration: animation_duration > 0 ? swipe_duration : 0,8878 }
8839 easing: animation_easing,8879
8840 queue: false,8880 //Deepseek-V3.1
8841 complete: async function () {8881 // Helper function to convert transition to promise
8842 appendMediaToMessage(chat[chat.length - 1], $(this).parent().children('.mes_block'));8882 const transitionPromise = (element, properties) => {
8843 await eventSource.emit(event_types.MESSAGE_SWIPED, (chat.length - 1));8883 return new Promise((resolve) => {
8844 saveChatDebounced();8884 element.transition({
8845 },8885 ...properties,
8886 complete: resolve,
8846 });8887 });
8847 },
8848 });8888 });
8849 },8889 };
8890
8891 /**
8892 * Animates a swipe for all messages >= mesId.
8893 * @param {number} mesId
8894 * @param {number} x
8895 * @param {number} duration
8896 */
8897 async function animateSwipeTransition(mesId, x, duration) {
8898 //Selects the swiped message.
8899 const swipedMessagesDiv = chatElement.children().filter((index, div) => {
8900 const $div = $(div);
8901 return mesId === Number($div.attr('mesid'));
8850 });8902 });
8903 const swipedElementsDiv = swipedMessagesDiv.children('.mes_block, .mesAvatarWrapper');
88518904
8852 $(this).parent().children('.avatar').transition({8905 //Swipe.
8853 x: swipe_range,8906 await transitionPromise(swipedElementsDiv, {
8854 duration: animation_duration > 0 ? swipe_duration : 0,8907 x: x,
8855 easing: animation_easing,8908 duration: duration,
8856 queue: false,
8857 complete: function () {
8858 $(this).parent().children('.avatar').transition({
8859 x: '-' + swipe_range,
8860 duration: 0,
8861 easing: animation_easing,8909 easing: animation_easing,
8862 queue: false,8910 queue: false,
8863 complete: function () {8911 });
8864 $(this).parent().children('.avatar').transition({8912 }
8865 x: '0px',8913
8866 duration: animation_duration > 0 ? swipe_duration : 0,8914 function getMessageBottomHeight(thisMesDiv) {
8867 easing: animation_easing,8915 const thisMesRect = thisMesDiv[0].getBoundingClientRect();
8916 //Scroll position + Chat height = Bottom of chat height.
8917 const chatBottom = chatElement.scrollTop() - chatElement.height();
8918 //Message offset from viewport top + height = Bottom of message offset.
8919 const messageBottom = thisMesRect.top + thisMesDiv.height();
8920 // Bottom of chat + Bottom of message offset = target scroll position.
8921 const scrollHeight = (chatBottom + messageBottom);
8922 return scrollHeight;
8923 }
8924
8925 function expandNewMessage(thisMesDiv) {
8926 //Only scroll if the view is not near the bottom.
8927 const is_animation_scroll = (chatElement.scrollTop() >= (chatElement.prop('scrollHeight') - chatElement.outerHeight()) - 10);
8928
8929 let new_height = thisMesDivHeight - (thisMesTextHeight - thisMesText[0].scrollHeight);
8930 if (new_height < 103) new_height = 103;
8931
8932 //Keep the swipe buttons at the same height when scrolling is finished.
8933
8934 //Expand new message.
8935 thisMesDiv.animate({ height: new_height + 'px' }, {
8936 duration: 0, //used to be 100 //Disabled on Cohee's request. https://github.com/SillyTavern/SillyTavern/pull/4610/files#r2408731744
8868 queue: false,8937 queue: false,
8869 complete: function () {8938 progress: function (animation, progress, remainingMs) {
88708939
8940 if (is_animation_scroll) chatElement.scrollTop(getMessageBottomHeight(thisMesDiv));
8871 },8941 },
8872 });8942 complete: function () {
8873 },8943 thisMesDiv.css('height', 'auto');
8874 });8944 //Correct height auto offset.
8945 if (is_animation_scroll) chatElement.scrollTop(getMessageBottomHeight(thisMesDiv));
8875 },8946 },
8876 });8947 });
8877 }8948 }
8878 if (chat[chat.length - 1]['swipe_id'] < 0) {
8879 chat[chat.length - 1]['swipe_id'] = 0;
8880 }
8881}
88828949
8883 /**8950 /**
8884 * Handles the swipe to the right event.8951 * Anime a swipe, optionally running a generation.
8885 * @param {JQuery.Event} [_event] Event.8952 * @param {boolean} run_generate
8886 * @param {object} params Additional parameters.
8887 * @param {string} [params.source] The source of the swipe event.
8888 * @param {boolean} [params.repeated] Is the swipe event repeated.
8889 */8953 */
8890//MARK: swipe_right8954 async function animateSwipe(run_generate = false) {
8891export function swipe_right(_event = null, { source, repeated } = {}) {8955
8892 if (chat.length - 1 === Number(this_edit_mes_id)) {8956 //Swipe out.
8957 await animateSwipeTransition(mesId, swipeRange, swipeDuration);
8958
8959 if (run_generate) {
8960 await updateSwipeCounter(mesId);
8961 //shows "..." while generating
8962 thisMesDiv.find('.mes_text').html('...');
8963 // resets the timer
8964 thisMesDiv.find('.mes_timer').html('');
8965 thisMesDiv.find('.tokenCounterDisplay').text('');
8966 updateReasoningUI(thisMesDiv, { reset: true });
8967 } else {
8968 //console.log('showing previously generated swipe candidate, or "..."');
8969 //console.log('onclick right swipe calling addOneMessage');
8970
8971 //Only scroll when swiping the last message.
8972 const scroll = (mesId == chat.length - 1);
8973 addOneMessage(chat[mesId], { type: 'swipe', forceId: mesId, scroll: scroll });
8974
8975 if (power_user.message_token_count_enabled) {
8976 if (!chat[mesId].extra) {
8977 chat[mesId].extra = {};
8978 }
8979
8980 const tokenCountText = (chat[mesId]?.extra?.reasoning || '') + chat[mesId].mes;
8981 const tokenCount = await getTokenCountAsync(tokenCountText, 0);
8982 chat[mesId]['extra']['token_count'] = tokenCount;
8983 thisMesDiv.find('.tokenCounterDisplay').text(`${tokenCount}t`);
8984 }
8985 }
8986
8987 //Animate expanding to the new message height.
8988 thisMesDiv.css('height', thisMesDivHeight);
8989 expandNewMessage(thisMesDiv);
8990
8991
8992 //Jump to the opposite side.
8993 await animateSwipeTransition(mesId, -swipeRange, 0);
8994
8995 appendMediaToMessage(chat[mesId], thisMesDiv);
8996
8997 await eventSource.emit(event_types.MESSAGE_SWIPED, (mesId));
8998
8999 if (run_generate && !is_send_press) {
9000 is_send_press = true;
9001 generation = Generate('swipe');
9002 } else if (parseInt(chat[mesId]['swipe_id']) !== chat[mesId]['swipes'].length) {
9003 saveChatDebounced();
9004 }
9005
9006 //Swipe in.
9007 await animateSwipeTransition(mesId, 0, swipeDuration);
9008 }
9009
9010 if (mesId === Number(this_edit_mes_id)) {
8893 closeMessageEditor();9011 closeMessageEditor();
8894 }9012 }
9013 if (isStreamingEnabled() && streamingProcessor) {
9014 streamingProcessor.onStopStreaming();
9015 }
88959016
8896 if (isHordeGenerationNotAllowed()) {9017 if (isHordeGenerationNotAllowed()) {
8897 return unblockGeneration();9018 return unblockGeneration();
8898 }9019 }
88999020
8900 // Make sure ad-hoc changes to extras are saved before swiping away9021 //If the swipe is not being deleted.
8901 syncMesToSwipe();9022 if (source != 'delete') {
89029023
8903 const isPristine = !chat_metadata?.tainted;9024 // Make sure ad-hoc changes to extras are saved before swiping away
8904 const swipe_duration = 200;9025 syncMesToSwipe(mesId);
8905 const swipe_range = 700;9026
8906 //console.log(swipe_range);9027 if (chat[mesId]['swipe_id'] === undefined) { // if there is no swipe-message in the last spot of the chat array
8907 let run_generate = false;9028 chat[mesId]['swipe_id'] = 0; // set it to id 0
8908 let run_swipe_right = false;9029 chat[mesId]['swipes'] = []; // empty the array
8909 if (chat[chat.length - 1]['swipe_id'] === undefined) { // if there is no swipe-message in the last spot of the chat array9030 chat[mesId]['swipe_info'] = [];
8910 chat[chat.length - 1]['swipe_id'] = 0; // set it to id 09031 chat[mesId]['swipes'][0] = chat[mesId]['mes']; //assign swipe array with last chat[mesId] from chat
8911 chat[chat.length - 1]['swipes'] = []; // empty the array9032 chat[mesId]['swipe_info'][0] = {
8912 chat[chat.length - 1]['swipe_info'] = [];9033 'send_date': chat[mesId]['send_date'],
8913 chat[chat.length - 1]['swipes'][0] = chat[chat.length - 1]['mes']; //assign swipe array with last message from chat9034 'gen_started': chat[mesId]['gen_started'],
8914 chat[chat.length - 1]['swipe_info'][0] = {9035 'gen_finished': chat[mesId]['gen_finished'],
8915 'send_date': chat[chat.length - 1]['send_date'],9036 'extra': structuredClone(chat[mesId]['extra']),
8916 'gen_started': chat[chat.length - 1]['gen_started'],
8917 'gen_finished': chat[chat.length - 1]['gen_finished'],
8918 'extra': structuredClone(chat[chat.length - 1]['extra']),
8919 };9037 };
8920 //assign swipe info array with last message from chat
8921 }9038 }
8922 // if swipe_right is called on the last alternate greeting in pristine chats, loop back around9039 // If the user is holding down the key and we're at the last or first swipe, don't do anything.
8923 if (chat.length === 1 && chat[0]['swipe_id'] !== undefined && chat[0]['swipe_id'] === chat[0]['swipes'].length - 1 && isPristine) {9040 let isLastSwipe = (direction === SWIPE_DIRECTION.RIGHT) ? (chat[mesId].swipe_id === Math.max(0, chat[mesId]['swipes'].length - 1)) : chat[mesId].swipe_id === 0;
8924 chat[0]['swipe_id'] = 0;9041 if (source === 'keyboard' && repeated && isLastSwipe) {
8925 } else {9042 await endSwipe();
8926 // If the user is holding down the key and we're at the last swipe, don't do anything
8927 if (source === 'keyboard' && repeated && chat[chat.length - 1].swipe_id === chat[chat.length - 1].swipes.length - 1) {
8928 return;9043 return;
8929 }9044 }
8930 // make new slot in array9045 } else if (source == 'delete') {
8931 chat[chat.length - 1]['swipe_id']++;9046 //If the swipe is being deleted.
8932 }9047 await standardSwipe();
8933 if (chat[chat.length - 1].extra) {9048 return;
8934 // if message has memory attached - remove it to allow regen
8935 if (chat[chat.length - 1].extra.memory) {
8936 delete chat[chat.length - 1].extra.memory;
8937 }9049 }
8938 // ditto for display text
8939 if (chat[chat.length - 1].extra.display_text) {
8940 delete chat[chat.length - 1].extra.display_text;
8941 }
8942
8943 delete chat[chat.length - 1].extra.image;
8944 delete chat[chat.length - 1].extra.image_swipes;
8945 delete chat[chat.length - 1].extra.video;
8946 delete chat[chat.length - 1].extra.inline_image;
8947 }
8948 if (!Array.isArray(chat[chat.length - 1]['swipe_info'])) {
8949 chat[chat.length - 1]['swipe_info'] = [];
8950 }
8951 //if swipe id of last message is the same as the length of the 'swipes' array and not the greeting
8952 if (parseInt(chat[chat.length - 1]['swipe_id']) === chat[chat.length - 1]['swipes'].length && (chat.length !== 1 || !isPristine)) {
8953 delete chat[chat.length - 1].gen_started;
8954 delete chat[chat.length - 1].gen_finished;
8955 run_generate = true;
8956 } else if (parseInt(chat[chat.length - 1]['swipe_id']) < chat[chat.length - 1]['swipes'].length) { //otherwise, if the id is less than the number of swipes
8957 chat[chat.length - 1]['mes'] = chat[chat.length - 1]['swipes'][chat[chat.length - 1]['swipe_id']]; //load the last mes box with the latest generation
8958 chat[chat.length - 1]['send_date'] = chat[chat.length - 1]?.swipe_info[chat[chat.length - 1]['swipe_id']]?.send_date || chat[chat.length - 1]['send_date']; //update send date
8959 chat[chat.length - 1]['extra'] = structuredClone(chat[chat.length - 1].swipe_info[chat[chat.length - 1]['swipe_id']]?.extra || chat[chat.length - 1].extra || []);
8960 run_swipe_right = true; //then prepare to do normal right swipe to show next message
8961 }
8962
8963 const currentMessage = $('#chat').children().filter(`[mesid="${chat.length - 1}"]`);
8964 let this_div = currentMessage.find('.swipe_right');
8965 let this_mes_div = this_div.parent().parent();
8966
8967 if (chat[chat.length - 1]['swipe_id'] > chat[chat.length - 1]['swipes'].length) { //if we swipe right while generating (the swipe ID is greater than what we are viewing now)
8968 chat[chat.length - 1]['swipe_id'] = chat[chat.length - 1]['swipes'].length; //show that message slot (will be '...' while generating)
8969 }
8970 if (run_generate) { //hide swipe arrows while generating
8971 this_div.css('display', 'none');
8972 }
8973 // handles animated transitions when swipe right, specifically height transitions between messages
8974 if (run_generate || run_swipe_right) {
8975 let this_mes_block = this_mes_div.find('.mes_block .mes_text');
8976 const this_mes_div_height = this_mes_div[0].scrollHeight;
8977 const this_mes_block_height = this_mes_block[0].scrollHeight;
8978
8979 this_mes_div.children('.swipe_left').css('display', 'flex');
8980 this_mes_div.children('.mes_block').transition({ // this moves the div back and forth
8981 x: '-' + swipe_range,
8982 duration: animation_duration > 0 ? swipe_duration : 0,
8983 easing: animation_easing,
8984 queue: false,
8985 complete: async function () {
8986 const is_animation_scroll = (chatElement.scrollTop() >= (chatElement.prop('scrollHeight') - chatElement.outerHeight()) - 10);
8987 //console.log(parseInt(chat[chat.length-1]['swipe_id']));
8988 //console.log(chat[chat.length-1]['swipes'].length);
8989 const swipeMessage = chatElement.find('[mesid="' + (chat.length - 1) + '"]');
8990 if (run_generate && parseInt(chat[chat.length - 1]['swipe_id']) === chat[chat.length - 1]['swipes'].length) {
8991 //shows "..." while generating
8992 swipeMessage.find('.mes_text').html('...');
8993 // resets the timer
8994 swipeMessage.find('.mes_timer').html('');
8995 swipeMessage.find('.tokenCounterDisplay').text('');
8996 updateReasoningUI(swipeMessage, { reset: true });
8997 } else {
8998 //console.log('showing previously generated swipe candidate, or "..."');
8999 //console.log('onclick right swipe calling addOneMessage');
9000 addOneMessage(chat[chat.length - 1], { type: 'swipe' });
90019050
9002 if (power_user.message_token_count_enabled) {9051 //If swiping left.
9003 if (!chat[chat.length - 1].extra) {9052 if (direction === SWIPE_DIRECTION.LEFT) {
9004 chat[chat.length - 1].extra = {};9053 newSwipeId--;
9054 //Loop to last swipe if negative.
9055 if (newSwipeId < 0) {
9056 newSwipeId = Math.max(0, chat[mesId]['swipes'].length - 1);
9005 }9057 }
90069058 //Limit swipe_id to swipes.
9007 const tokenCountText = (chat[chat.length - 1]?.extra?.reasoning || '') + chat[chat.length - 1].mes;9059 if (newSwipeId > chat[mesId]['swipes'].length - 1) {
9008 const tokenCount = await getTokenCountAsync(tokenCountText, 0);9060 toastr.warning(`The swipe_id for message #${mesId} was ${newSwipeId}. It has been reset to ${chat[mesId]['swipes'].length - 1}.`);
9009 chat[chat.length - 1]['extra']['token_count'] = tokenCount;9061 chat[mesId]['swipe_id'] = chat[mesId]['swipes'].length - 1;
9010 swipeMessage.find('.tokenCounterDisplay').text(`${tokenCount}t`);9062 await endSwipe();
9063 return;
9011 }9064 }
9065 await standardSwipe();
9066 return;
9012 }9067 }
9013 let new_height = this_mes_div_height - (this_mes_block_height - this_mes_block[0].scrollHeight);9068 //If swiping right.
9014 if (new_height < 103) new_height = 103;9069 else if (direction === SWIPE_DIRECTION.RIGHT) {
9070 // make new slot in array
9071 newSwipeId++;
90159072
9073 //Minimum of zero.
9074 if (newSwipeId < 0) {
9075 toastr.warning(`The swipe_id for message #${mesId} was ${newSwipeId}. It has been reset to zero.`);
9076 chat[mesId]['swipe_id'] = 0;
9077 await endSwipe();
9078 return;
9079 }
90169080
9017 this_mes_div.animate({ height: new_height + 'px' }, {9081 //if swipe id of last message is the same as the length of the 'swipes' array and not the greeting.
9018 duration: 0, //used to be 1009082 if (newSwipeId >= chat[mesId]['swipes'].length && ((chat.length !== 1 || !isPristine))) {
9019 queue: false,9083 newSwipeId = chat[mesId]['swipes'].length;
9020 progress: function () {9084
9021 // Scroll the chat down as the message expands9085 //Update the swipe_id.
9022 if (is_animation_scroll) chatElement.scrollTop(chatElement[0].scrollHeight);9086 chat[mesId]['swipe_id'] = newSwipeId;
9023 },9087
9024 complete: function () {9088 //Cancel the generation if it's a user message or the first message in a pristine chat.
9025 this_mes_div.css('height', 'auto');9089 if (chat[mesId].is_user || (mesId === 0 && isPristine)) {
9026 // Scroll the chat down to the bottom once the animation is complete9090 //Cancel swipe.
9027 if (is_animation_scroll) chatElement.scrollTop(chatElement[0].scrollHeight);9091 chat[mesId]['swipe_id'] = originalSwipeId;
9028 },9092 await endSwipe();
9029 });9093 return;
9030 this_mes_div.children('.mes_block').transition({9094 //Generate.
9031 x: swipe_range,
9032 duration: 0,
9033 easing: animation_easing,
9034 queue: false,
9035 complete: function () {
9036 this_mes_div.children('.mes_block').transition({
9037 x: '0px',
9038 duration: animation_duration > 0 ? swipe_duration : 0,
9039 easing: animation_easing,
9040 queue: false,
9041 complete: async function () {
9042 appendMediaToMessage(chat[chat.length - 1], swipeMessage);
9043 await eventSource.emit(event_types.MESSAGE_SWIPED, (chat.length - 1));
9044 if (run_generate && !is_send_press && parseInt(chat[chat.length - 1]['swipe_id']) === chat[chat.length - 1]['swipes'].length) {
9045 console.debug('caught here 2');
9046 is_send_press = true;
9047 await Generate('swipe');
9048 } else {9095 } else {
9049 if (parseInt(chat[chat.length - 1]['swipe_id']) !== chat[chat.length - 1]['swipes'].length) {9096 await loadFromSwipeId(mesId, newSwipeId);
9050 saveChatDebounced();9097 let run_generate = true;
9098 await animateSwipe(run_generate);
9099 await endSwipe();
9100 return;
9101 }
9102 }
9103 else {
9104 // if swipe_right is called on the last alternate greeting in pristine chats, loop back around
9105 if (chat.length === 1 && newSwipeId !== undefined && newSwipeId === chat[0]['swipes'].length && isPristine) {
9106 newSwipeId = 0;
9107 }
9108 }
9109 await standardSwipe();
9110 return;
9051 }9111 }
9052}9112}
9053 },
9054 });
9055 },
9056 });
9057 },
9058 });
9059 this_mes_div.children('.avatar').transition({ // moves avatar along with swipe
9060 x: '-' + swipe_range,
9061 duration: animation_duration > 0 ? swipe_duration : 0,
9062 easing: animation_easing,
9063 queue: false,
9064 complete: function () {
9065 this_mes_div.children('.avatar').transition({
9066 x: swipe_range,
9067 duration: 0,
9068 easing: animation_easing,
9069 queue: false,
9070 complete: function () {
9071 this_mes_div.children('.avatar').transition({
9072 x: '0px',
9073 duration: animation_duration > 0 ? swipe_duration : 0,
9074 easing: animation_easing,
9075 queue: false,
9076 complete: function () {
90779113
9078 },9114/**
9079 });9115 * @deprecated Use `swipe` instead.
9080 },9116 * Handles the swipe to the left event.
9081 });9117 * @param {JQuery.Event} _event Event.
9082 },9118 * @param {object} params Additional parameters.
9083 });9119 * @param {string} [params.source] The source of the swipe event.
9120 * @param {boolean} [params.repeated] Is the swipe event repeated.
9121 * @param {object} [params.message] The chat message to swipe.
9122 */
9123export async function swipe_left(_event, { source, repeated, message } = {}) {
9124 await swipe.call(this, _event, SWIPE_DIRECTION.LEFT, { source: source, repeated: repeated, message: message });
9084}9125}
9126
9127/**
9128 * @deprecated Use `swipe` instead.
9129 * Handles the swipe to the right event.
9130 * @param {JQuery.Event} [_event] Event.
9131 * @param {object} params Additional parameters.
9132 * @param {string} [params.source] The source of the swipe event.
9133 * @param {boolean} [params.repeated] Is the swipe event repeated.
9134 * @param {object} [params.message] The chat message to swipe.
9135 */
9136//MARK: swipe_right
9137export async function swipe_right(_event = null, { source, repeated, message } = {}) {
9138 await swipe.call(this, _event, SWIPE_DIRECTION.RIGHT, { source: source, repeated: repeated, message: message });
9085}9139}
90869140
9087/**9141/**
@@ -9768,8 +9822,8 @@ jQuery(async function () {
9768 ///// SWIPE BUTTON CLICKS ///////9822 ///// SWIPE BUTTON CLICKS ///////
97699823
9770 //limit swiping to only last message clicks9824 //limit swiping to only last message clicks
9771 $(document).on('click', '.last_mes .swipe_right', swipe_right);9825 $(document).on('click', '.last_mes .swipe_right', async (e, data) => await swipe(e, SWIPE_DIRECTION.RIGHT, data));
9772 $(document).on('click', '.last_mes .swipe_left', swipe_left);9826 $(document).on('click', '.last_mes .swipe_left', async (e, data) => await swipe(e, SWIPE_DIRECTION.LEFT, data));
97739827
9774 initCharacterSearch();9828 initCharacterSearch();
97759829
public/scripts/RossAscends-mods.js+3 -2
@@ -19,6 +19,7 @@ import {
19 substituteParams,19 substituteParams,
20 sendTextareaMessage,20 sendTextareaMessage,
21 doNavbarIconClick,21 doNavbarIconClick,
22 isSwipingAllowed,
22} from '../script.js';23} from '../script.js';
2324
24import {25import {
@@ -1109,8 +1110,8 @@ export function initRossMods() {
11091110
1110 if (event.key == 'ArrowLeft') { //swipes left1111 if (event.key == 'ArrowLeft') { //swipes left
1111 if (1112 if (
1113 isSwipingAllowed &&
1112 !isNanogallery2LightboxActive() && // Check if lightbox is NOT active1114 !isNanogallery2LightboxActive() && // Check if lightbox is NOT active
1113 $('.swipe_left:last').css('display') === 'flex' &&
1114 $('#send_textarea').val() === '' &&1115 $('#send_textarea').val() === '' &&
1115 $('#character_popup').css('display') === 'none' &&1116 $('#character_popup').css('display') === 'none' &&
1116 $('#shadow_select_chat_popup').css('display') === 'none' &&1117 $('#shadow_select_chat_popup').css('display') === 'none' &&
@@ -1123,8 +1124,8 @@ export function initRossMods() {
1123 }1124 }
1124 if (event.key == 'ArrowRight') { //swipes right1125 if (event.key == 'ArrowRight') { //swipes right
1125 if (1126 if (
1127 isSwipingAllowed &&
1126 !isNanogallery2LightboxActive() && // Check if lightbox is NOT active1128 !isNanogallery2LightboxActive() && // Check if lightbox is NOT active
1127 $('.swipe_right:last').css('display') === 'flex' &&
1128 $('#send_textarea').val() === '' &&1129 $('#send_textarea').val() === '' &&
1129 $('#character_popup').css('display') === 'none' &&1130 $('#character_popup').css('display') === 'none' &&
1130 $('#shadow_select_chat_popup').css('display') === 'none' &&1131 $('#shadow_select_chat_popup').css('display') === 'none' &&
public/scripts/chats.js+2 -4
@@ -10,12 +10,10 @@ import {
10 event_types,10 event_types,
11 getCurrentChatId,11 getCurrentChatId,
12 getRequestHeaders,12 getRequestHeaders,
13 hideSwipeButtons,
14 name1,13 name1,
15 name2,14 name2,
16 reloadCurrentChat,15 reloadCurrentChat,
17 saveSettingsDebounced,16 saveSettingsDebounced,
18 showSwipeButtons,
19 this_chid,17 this_chid,
20 saveChatConditional,18 saveChatConditional,
21 chat_metadata,19 chat_metadata,
@@ -27,6 +25,7 @@ import {
27 getSystemMessageByType,25 getSystemMessageByType,
28 printMessages,26 printMessages,
29 clearChat,27 clearChat,
28 refreshSwipeButtons,
30} from '../script.js';29} from '../script.js';
31import { selected_group } from './group-chats.js';30import { selected_group } from './group-chats.js';
32import { power_user } from './power-user.js';31import { power_user } from './power-user.js';
@@ -159,8 +158,7 @@ export async function hideChatMessageRange(start, end, unhide, nameFitler = null
159 }158 }
160159
161 // Reload swipes. Useful when a last message is hidden.160 // Reload swipes. Useful when a last message is hidden.
162 hideSwipeButtons();161 refreshSwipeButtons();
163 showSwipeButtons();
164162
165 await saveChatConditional();163 await saveChatConditional();
166}164}
public/scripts/constants.js+8 -0
@@ -66,3 +66,11 @@ export const COMETAPI_IGNORE_PATTERNS = [
66 // Utility models66 // Utility models
67 'embedding', 'search-gpts', 'files_retrieve', 'moderation',67 'embedding', 'search-gpts', 'files_retrieve', 'moderation',
68];68];
69
70/**
71 * @type {{readonly LEFT: 'left', readonly RIGHT: 'right'}}
72 */
73export const SWIPE_DIRECTION = {
74 LEFT: 'left',
75 RIGHT: 'right',
76};
public/scripts/extensions/stable-diffusion/index.js+1 -2
@@ -28,6 +28,7 @@ import {
28} from '../../extensions.js';28} from '../../extensions.js';
29import { selected_group } from '../../group-chats.js';29import { selected_group } from '../../group-chats.js';
30import {30import {
31 clamp,
31 debounce,32 debounce,
32 deepMerge,33 deepMerge,
33 delay,34 delay,
@@ -66,8 +67,6 @@ const MODULE_NAME = 'sd';
66// This is a 1x1 transparent PNG67// This is a 1x1 transparent PNG
67const PNG_PIXEL = 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=';68const PNG_PIXEL = 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=';
68const CUSTOM_STOP_EVENT = 'sd_stop_generation';69const CUSTOM_STOP_EVENT = 'sd_stop_generation';
69const clamp = (value, min, max) => Math.min(Math.max(value, min), max);
70
71const sources = {70const sources = {
72 extras: 'extras',71 extras: 'extras',
73 horde: 'horde',72 horde: 'horde',
public/scripts/st-context.js+4 -0
@@ -56,6 +56,8 @@ import {
56 showSwipeButtons,56 showSwipeButtons,
57 hideSwipeButtons,57 hideSwipeButtons,
58 deleteMessage,58 deleteMessage,
59 refreshSwipeButtons,
60 isSwipingAllowed,
59} from '../script.js';61} from '../script.js';
60import {62import {
61 extension_settings,63 extension_settings,
@@ -212,6 +214,8 @@ export function getContext() {
212 right: swipe_right,214 right: swipe_right,
213 show: showSwipeButtons,215 show: showSwipeButtons,
214 hide: hideSwipeButtons,216 hide: hideSwipeButtons,
217 refresh: refreshSwipeButtons,
218 isAllowed: () => isSwipingAllowed,
215 },219 },
216 variables: {220 variables: {
217 local: {221 local: {
public/scripts/utils.js+1 -0
@@ -2706,3 +2706,4 @@ export async function importFromExternalUrl(url, { preserveFileName = null } = {
2706 break;2706 break;
2707 }2707 }
2708}2708}
2709export const clamp = (value, min, max) => Math.min(Math.max(value, min), max);