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 {
180180 shiftUpByOne,
181181 shiftDownByOne,
182182 canUseNegativeLookbehind,
183+ clamp,
183184} from './scripts/utils.js';
184185import { debounce_timeout, GENERATION_TYPE_TRIGGERS, IGNORE_SYMBOL, inject_ids, SWIPE_DIRECTION } from './scripts/constants.js';
185186
186187import { cancelDebouncedMetadataSave, doDailyExtensionUpdatesCheck, extension_settings, initExtensions, loadExtensionSettings, runGenerationInterceptors, saveMetadataDebounced } from './scripts/extensions.js';
187188import { 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';
364365export let name1 = default_user_name;
365366export let name2 = systemUserName;
366367export let chat = [];
368+export let isSwipingAllowed = true; //false when a swipe is in progress, or swiping is blocked.
367369let chatSaveTimeout;
368370let importFlashTimeout;
369371export let isChatSaving = false;
@@ -1419,8 +1421,7 @@ export async function printMessages() {
14191421
14201422 chatElement.find('.mes').removeClass('last_mes');
14211423 chatElement.find('.mes').last().addClass('last_mes');
14221424 hideSwipeButtonsrefreshSwipeButtons();
1423- showSwipeButtons();
14241425 scrollChatToBottom();
14251426 applyStylePins();
14261427
@@ -1524,8 +1525,7 @@ export async function deleteMessage(id, swipeDeletionIndex = undefined, askConfi
15241525 this_edit_mes_id = undefined;
15251526 }
15261527
15271528 hideSwipeButtonsrefreshSwipeButtons();
1528- showSwipeButtons();
15291529
15301530 await eventSource.emit(event_types.MESSAGE_DELETED, chat.length);
15311531}
@@ -1549,8 +1549,7 @@ export async function reloadCurrentChat() {
15491549 await eventSource.emit(event_types.CHAT_CHANGED, getCurrentChatId());
15501550 }
15511551
15521552 hideSwipeButtonsrefreshSwipeButtons();
1553- showSwipeButtons();
15541553}
15551554
15561555/**
@@ -2143,7 +2142,8 @@ export function addOneMessage(mes, { type = 'normal', insertAfter = null, scroll
21432142 });
21442143
21452144 if (type === 'swipe') {
21462145 const swipeMessagemessageId = chatElement.find(`[mesid="${forceId ?? chat.length - 1}"]`);
2146+ const swipeMessage = chatElement.find(`[mesid="${messageId}"]`);
21472147 swipeMessage.attr('swipeid', params.swipeId);
21482148 swipeMessage.find('.mes_text').html(messageText).attr('title', title);
21492149 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
21792179 if (showSwipes) {
21802180 chatElement.find('.mes').last().addClass('last_mes');
21812181 chatElement.find('.mes').eq(-2).removeClass('last_mes');
21822182 hideSwipeButtonsrefreshSwipeButtons();
2183- showSwipeButtons();
21842183 }
21852184
21862185 // Don't scroll if not inserting last
@@ -2921,7 +2920,7 @@ class StreamingProcessor {
29212920 await this.#checkDomElements(messageId, continueOnReasoning);
29222921 this.markUIGenStarted();
29232922 }
29242923 hideSwipeButtons({ hideCounters: true });
29252924 scrollChatToBottom();
29262925 return messageId;
29272926 }
@@ -6059,21 +6058,21 @@ export function syncSwipeToMes(messageId = null, swipeId = null) {
60596058 }
60606059
60616060 const targetSwipeId = targetMessage.swipe_id;
60626061 if (!typeof targetMessage.swipes[targetSwipeId] || !targetMessage.swipe_info[targetSwipeId]== 'string') {
60636062 console.warn(`[syncSwipeToMes] Invalid swipe ID: ${targetSwipeId}`);
60646063 return false;
60656064 }
60666065
60676066 const targetSwipeInfo = targetMessage?.swipe_info[targetSwipeId];
60686067 if (typeof targetSwipeInfo !== 'object') {
6069- return false;
6068+ console.warn(`[syncSwipeToMes] Invalid swipe info: ${targetSwipeId}`);
60706069 }
60716070
60726071 targetMessage.mes = targetMessage.swipes[targetSwipeId];
60736072 targetMessage.send_date = targetSwipeInfo?.send_date;
60746073 targetMessage.gen_started = targetSwipeInfo?.gen_started;
60756074 targetMessage.gen_finished = targetSwipeInfo?.gen_finished;
60766075 targetMessage.extra = structuredClone(targetSwipeInfo?.extra) ?? {};
60776076
60786077 return true;
60796078}
@@ -6276,11 +6275,11 @@ export async function renameCharacter(name = null, { silent = false, renameChats
62766275 const oldName = getCharaFilename(null, { manualAvatarKey: oldAvatar });
62776276 const newName = getCharaFilename(null, { manualAvatarKey: newAvatar });
62786277
62796278 // Replace other auxilleryauxiliary fields where was referenced by avatar key
62806279 // Tag List
62816280 renameTagKey(oldAvatar, newAvatar);
62826281
62836282 // AddtionalAdditional lore books
62846283 const charLore = world_info.charLore?.find(x => x.name == oldName);
62856284 if (charLore) {
62866285 charLore.name = newName;
@@ -6446,7 +6445,7 @@ export function saveChatDebounced() {
64466445 * @param {string} [options.chatName] The name of the chat file to save to
64476446 * @param {object} [options.withMetadata] Additional metadata to save with the chat
64486447 * @param {number} [options.mesId] The message ID to save the chat up to
64496448 * @param {boolean} [options.force] Force the saving despiredespite the integrity check result
64506449 *
64516450 * @returns {Promise<void>}
64526451 */
@@ -7003,8 +7002,7 @@ export async function getSettings() {
70037002
70047003 swipes = settings.swipes !== undefined ? !!settings.swipes : true; // enable swipes by default
70057004 $('#swipes-checkbox').prop('checked', swipes); /// swipecode
70067005 hideSwipeButtonsrefreshSwipeButtons();
7007- showSwipeButtons();
70087006
70097007 // Kobold
70107008 loadKoboldSettings(data, settings.kai_settings ?? settings, settings);
@@ -8078,18 +8076,42 @@ export function callPopup(text, type, inputValue = '', { okButton, rows, wide, w
80788076 });
80798077}
80808078
8081-export 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+ */
8083+export 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+ */
8094+export function refreshSwipeButtons() {
8095+ hideSwipeButtons();
8096+ showSwipeButtons();
8097+}
8098+
8099+export function showSwipeButtons(mesId = chat.length - 1) {
8100+ isSwipingAllowed = true;
8101+
8102+ if (chat.length === 0) {
80838103 return;
80848104 }
80858105
80868106 if (
80878107 chat[chat.length - 1mesId].is_system ||
80888108 !swipes ||
80898109 Number($('.mes:last').attr('mesid')) < 0 ||
80908110 chat[chat.length - 1mesId].is_user ||
80918111 (selected_group && is_group_generating)
80928112 ) { return; }
8113+ return;
8114+ }
80938115
80948116 // swipe_id should be set if alternate greetings are added
80958117 if (chat.length == 1 && chat[0].swipe_id === undefined) {
@@ -8099,35 +8121,35 @@ export function showSwipeButtons() {
80998121 //had to add this to make the swipe counter work
81008122 //(copied from the onclick functions for swipe buttons..
81018123 //don't know why the array isn't set for non-swipe messages in Generate or addOneMessage..)
81028124 if (chat[chat.length - 1mesId]['swipe_id'] === undefined) { // if there is no swipe-message in the last spot of the chat array
81038125 chat[chat.length - 1mesId]['swipe_id'] = 0; // set it to id 0
81048126 chat[chat.length - 1mesId]['swipes'] = []; // empty the array
81058127 chat[chat.length - 1mesId]['swipes'][0] = chat[chat.length - 1mesId]['mes']; //assign swipe array with last message from chat
81068128 chat[chat.length - 1mesId]['swipe_info'] = [];
81078129 chat[chat.length - 1mesId]['swipe_info'][0] = {
81088130 'send_date': chat[chat.length - 1mesId]['send_date'],
81098131 'gen_started': chat[chat.length - 1mesId]['gen_started'],
81108132 'gen_finished': chat[chat.length - 1mesId]['gen_finished'],
81118133 'extra': structuredClone(chat[chat.length - 1mesId]['extra']),
81128134 };
81138135 }
81148136
81158137 const currentMessage = chatElement.children().filter(`[mesid="${chat.length - 1mesId}"]`);
81168138 const swipeId = chat[chat.length - 1mesId].swipe_id;
81178139 const swipeCounterText = formatSwipeCounter((swipeId + 1), chat[chat.length - 1mesId].['swipes'].length);
81188140 const swipeRight = currentMessage.find('.swipe_right');
81198141 const swipeLeft = currentMessage.find('.swipe_left');
81208142 const swipeCounter = currentMessage.find('.swipes-counter');
81218143
81228144 if (swipeId !== undefined && (chat[chat.length - 1mesId].swipes.length > 1 || swipeId > 0)) {
81238145 swipeLeft.css('display', 'flex');
81248146 }
81258147 //only show right when generate is off, or when next right swipe would not make a generate happen
81268148 if (is_send_press === false || chat[chat.length - 1mesId].swipes.length >= swipeId) {
81278149 swipeRight.css('display', 'flex').css('opacity', '0.3');
81288150 swipeCounter.css('opacity', '0.3');
81298151 }
81308152 if ((chat[chat.length - 1mesId].swipes.length - swipeId) === 1) {
81318153 //chevron was moved out of hardcode in HTML to class toggle dependent on last_mes or not
81328154 //necessary for 'swipe_right' div in past messages to have no chevron if 'show swipes for all messages' is turned on
81338155 swipeRight.css('opacity', '0.7');
@@ -8139,10 +8161,17 @@ export function showSwipeButtons() {
81398161 lastSwipeCounter.text(swipeCounterText).show();
81408162}
81418163
8142-export function hideSwipeButtons() {
8164+/**
8165+ * @param {object} [options] Options
8166+ * @param {boolean} [options.hideCounters=false] Also hide the swipes counter.
8167+ */
8168+export function hideSwipeButtons({ hideCounters = false } = {}) {
8169+ isSwipingAllowed = false;
81438170 chatElement.find('.swipe_right').hide();
8144- chatElement.find('.last_mes .swipes-counter').hide();
81458171 chatElement.find('.swipe_left').hide();
8172+ if (hideCounters === true) {
8173+ chatElement.find('.last_mes .swipes-counter').hide();
8174+ }
81468175}
81478176
81488177/**
@@ -8724,364 +8753,389 @@ export async function createOrEditCharacter(e) {
87248753 * @returns {string} The formatted counter.
87258754 */
87268755function formatSwipeCounter(current, total) {
87278756 if (isNaN(current) ||&& isNaN(total)) {
87288757 return '';
87298758 }
8730-
8759+ return `${!isNaN(current) ? current : '?'}\u200b/\u200b${!isNaN(total) ? total : '?'}`;
8731- return `${current}\u200b/\u200b${total}`;
87328760}
87338761
87348762/**
87358763 * Handles the swipe to the left event.
87368764 * @param {JQuery.Event} _event Event.
8765+ * @param {'left'|'right'} direction The direction to swipe.
87378766 * @param {object} params Additional parameters.
87388767 * @param {string} [params.source] The source of the swipe event.
87398768 * @param {boolean} [params.repeated] Is the swipe event repeated.
8769+ * @param {object} [params.message=chat[chat.length - 1]] The chat message to swipe.
87408770 */
87418771export async function swipe_leftswipe(_event, direction, { source, repeated, message = chat[chat.length - 1] } = {}) {
87428772 if (chat.length - 1 === Number(this_edit_mes_id)0) {
8743- closeMessageEditor();
8773+ console.warn('Swipe was called on an empty chat.');
8774+ return;
87448775 }
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;
87478781 }
8782+ isSwipingAllowed = false;
87488783
8749- // Make sure ad-hoc changes to extras are saved before swiping away
8784+ 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 anything
8787+ //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};`);
87548792 return;
87558793 }
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;
87638805 }
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 case
8813+ 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;
87868817 }
8787- // ditto for display text
8818+ 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;
87908820 }
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 });
87918832 }
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+ }
88058839 }
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();
88128851 }
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 100
8856+ * @param {number} newSwipeId
8818- queue: false,
8857+ */
88198858 progress: async function loadFromSwipeId(mesId, newSwipeId) {
8820- // Scroll the chat down as the message expands
8859+ //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 complete
8866+ // 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,
88468887 });
8847- },
88488888 });
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'));
88508902 });
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,
88558908 easingduration: animation_easingduration,
8856- queue: false,
8857- complete: function () {
8858- $(this).parent().children('.avatar').transition({
8859- x: '-' + swipe_range,
8860- duration: 0,
88618909 easing: animation_easing,
88628910 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
88688937 queue: false,
88698938 complete progress: function (animation, progress, remainingMs) {
88708939
8940+ if (is_animation_scroll) chatElement.scrollTop(getMessageBottomHeight(thisMesDiv));
88718941 },
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));
88758946 },
88768947 });
88778948 }
8878- if (chat[chat.length - 1]['swipe_id'] < 0) {
8879- chat[chat.length - 1]['swipe_id'] = 0;
8880- }
8881-}
88828949
88838950 /**
88848951 * HandlesAnime thea swipe, tooptionally therunning righta eventgeneration.
88858952 * @param {JQuery.Eventboolean} [_event] Event.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.
88898953 */
8890-//MARK: swipe_right
8954+ async function animateSwipe(run_generate = false) {
8891-export 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)) {
88939011 closeMessageEditor();
88949012 }
9013+ if (isStreamingEnabled() && streamingProcessor) {
9014+ streamingProcessor.onStopStreaming();
9015+ }
88959016
88969017 if (isHordeGenerationNotAllowed()) {
88979018 return unblockGeneration();
88989019 }
88999020
8900- // Make sure ad-hoc changes to extras are saved before swiping away
9021+ //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 array
9030+ chat[mesId]['swipe_info'] = [];
8910- chat[chat.length - 1]['swipe_id'] = 0; // set it to id 0
9031+ chat[mesId]['swipes'][0] = chat[mesId]['mes']; //assign swipe array with last chat[mesId] from chat
8911- chat[chat.length - 1]['swipes'] = []; // empty the array
9032+ 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 chat
9034+ 'gen_started': chat[mesId]['gen_started'],
8914- chat[chat.length - 1]['swipe_info'][0] = {
9035+ 'gen_finished': chat[mesId]['gen_finished'],
89159036 'send_dateextra': structuredClone(chat[chat.length - 1mesId]['send_dateextra']),
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']),
89199037 };
8920- //assign swipe info array with last message from chat
89219038 }
8922- // if swipe_right is called on the last alternate greeting in pristine chats, loop back around
9039+ // 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) {
89289043 return;
89299044 }
8930- // make new slot in array
9045+ } 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;
89379049 }
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.
90039052 if (!chat[chat.lengthdirection -=== 1]SWIPE_DIRECTION.extraLEFT) {
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);
90059057 }
9006-
9058+ //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}.`);
90099061 chat[chat.length - 1mesId]['extraswipe_id'] = chat[mesId]['token_countswipes'].length =- tokenCount1;
9010- swipeMessage.find('.tokenCounterDisplay').text(`${tokenCount}t`);
9062+ await endSwipe();
9063+ return;
90119064 }
9065+ await standardSwipe();
9066+ return;
90129067 }
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 100
9082+ 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 expands
9085+ //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 complete
9090+ //Cancel swipe.
9027- if (is_animation_scroll) chatElement.scrollTop(chatElement[0].scrollHeight);
9091+ chat[mesId]['swipe_id'] = originalSwipeId;
9028- },
9092+ await endSwipe();
90299093 })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');
90489095 } 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;
90519111 }
90529112}
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+ */
9123+export async function swipe_left(_event, { source, repeated, message } = {}) {
9124+ await swipe.call(this, _event, SWIPE_DIRECTION.LEFT, { source: source, repeated: repeated, message: message });
90849125}
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
9137+export async function swipe_right(_event = null, { source, repeated, message } = {}) {
9138+ await swipe.call(this, _event, SWIPE_DIRECTION.RIGHT, { source: source, repeated: repeated, message: message });
90859139}
90869140
90879141/**
@@ -9768,8 +9822,8 @@ jQuery(async function () {
97689822 ///// SWIPE BUTTON CLICKS ///////
97699823
97709824 //limit swiping to only last message clicks
97719825 $(document).on('click', '.last_mes .swipe_right', swipe_rightasync (e, data) => await swipe(e, SWIPE_DIRECTION.RIGHT, data));
97729826 $(document).on('click', '.last_mes .swipe_left', swipe_leftasync (e, data) => await swipe(e, SWIPE_DIRECTION.LEFT, data));
97739827
97749828 initCharacterSearch();
97759829
public/scripts/RossAscends-mods.js+3 -2
@@ -19,6 +19,7 @@ import {
1919 substituteParams,
2020 sendTextareaMessage,
2121 doNavbarIconClick,
22+ isSwipingAllowed,
2223} from '../script.js';
2324
2425import {
@@ -1109,8 +1110,8 @@ export function initRossMods() {
11091110
11101111 if (event.key == 'ArrowLeft') { //swipes left
11111112 if (
1113+ isSwipingAllowed &&
11121114 !isNanogallery2LightboxActive() && // Check if lightbox is NOT active
1113- $('.swipe_left:last').css('display') === 'flex' &&
11141115 $('#send_textarea').val() === '' &&
11151116 $('#character_popup').css('display') === 'none' &&
11161117 $('#shadow_select_chat_popup').css('display') === 'none' &&
@@ -1123,8 +1124,8 @@ export function initRossMods() {
11231124 }
11241125 if (event.key == 'ArrowRight') { //swipes right
11251126 if (
1127+ isSwipingAllowed &&
11261128 !isNanogallery2LightboxActive() && // Check if lightbox is NOT active
1127- $('.swipe_right:last').css('display') === 'flex' &&
11281129 $('#send_textarea').val() === '' &&
11291130 $('#character_popup').css('display') === 'none' &&
11301131 $('#shadow_select_chat_popup').css('display') === 'none' &&
public/scripts/chats.js+2 -4
@@ -10,12 +10,10 @@ import {
1010 event_types,
1111 getCurrentChatId,
1212 getRequestHeaders,
13- hideSwipeButtons,
1413 name1,
1514 name2,
1615 reloadCurrentChat,
1716 saveSettingsDebounced,
18- showSwipeButtons,
1917 this_chid,
2018 saveChatConditional,
2119 chat_metadata,
@@ -27,6 +25,7 @@ import {
2725 getSystemMessageByType,
2826 printMessages,
2927 clearChat,
28+ refreshSwipeButtons,
3029} from '../script.js';
3130import { selected_group } from './group-chats.js';
3231import { power_user } from './power-user.js';
@@ -159,8 +158,7 @@ export async function hideChatMessageRange(start, end, unhide, nameFitler = null
159158 }
160159
161160 // Reload swipes. Useful when a last message is hidden.
162161 hideSwipeButtonsrefreshSwipeButtons();
163- showSwipeButtons();
164162
165163 await saveChatConditional();
166164}
public/scripts/constants.js+8 -0
@@ -66,3 +66,11 @@ export const COMETAPI_IGNORE_PATTERNS = [
6666 // Utility models
6767 'embedding', 'search-gpts', 'files_retrieve', 'moderation',
6868];
69+
70+/**
71+ * @type {{readonly LEFT: 'left', readonly RIGHT: 'right'}}
72+ */
73+export const SWIPE_DIRECTION = {
74+ LEFT: 'left',
75+ RIGHT: 'right',
76+};
public/scripts/extensions/stable-diffusion/index.js+1 -2
@@ -28,6 +28,7 @@ import {
2828} from '../../extensions.js';
2929import { selected_group } from '../../group-chats.js';
3030import {
31+ clamp,
3132 debounce,
3233 deepMerge,
3334 delay,
@@ -66,8 +67,6 @@ const MODULE_NAME = 'sd';
6667// This is a 1x1 transparent PNG
6768const PNG_PIXEL = 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=';
6869const CUSTOM_STOP_EVENT = 'sd_stop_generation';
69-const clamp = (value, min, max) => Math.min(Math.max(value, min), max);
70-
7170const sources = {
7271 extras: 'extras',
7372 horde: 'horde',
public/scripts/st-context.js+4 -0
@@ -56,6 +56,8 @@ import {
5656 showSwipeButtons,
5757 hideSwipeButtons,
5858 deleteMessage,
59+ refreshSwipeButtons,
60+ isSwipingAllowed,
5961} from '../script.js';
6062import {
6163 extension_settings,
@@ -212,6 +214,8 @@ export function getContext() {
212214 right: swipe_right,
213215 show: showSwipeButtons,
214216 hide: hideSwipeButtons,
217+ refresh: refreshSwipeButtons,
218+ isAllowed: () => isSwipingAllowed,
215219 },
216220 variables: {
217221 local: {
public/scripts/utils.js+1 -0
@@ -2706,3 +2706,4 @@ export async function importFromExternalUrl(url, { preserveFileName = null } = {
27062706 break;
27072707 }
27082708}
2709+export const clamp = (value, min, max) => Math.min(Math.max(value, min), max);