Extracted `messageEdit` and `messageEditCancel` from `.mes_edit` and `.mes_edit_cancel` (#4633) * 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 * Fixed.. https://github.com/SillyTavern/SillyTavern/pull/4633#issuecomment-3383321321 https://github.com/SillyTavern/SillyTavern/pull/4633#pullrequestreview-3316602505 * Fixed... * Fixed.... https://github.com/SillyTavern/SillyTavern/pull/4633#discussion_r2415124843 https://github.com/SillyTavern/SillyTavern/pull/4633#discussion_r2415124845 * Fix type conversion errors * Fix type error * Fix being unable to close message edit on first message with escape * Fixed. https://github.com/SillyTavern/SillyTavern/pull/4633#issuecomment-3412364573 Another bug found (sorry): Open edit on the last message, move it up, then move it back down. Swipe buttons appear, as far as I understand your intention, they shan't. * showSwipeButtons: return early if message edit is open --------- Co-authored-by: user <user@exmaple.com> Co-authored-by: Cohee <18619528+Cohee1207@users.noreply.github.com>

37271ddad46c187ce1c86329d90829492153f4d8

DeclineThyself <FallenHaze@tutamail.com>

Signed
1 files changed, +127 -91Showing whitespace changes
public/script.js+127 -91
@@ -554,9 +554,10 @@ export let is_send_press = false; //Send generation
554554
555let this_del_mes = -1;555let this_del_mes = -1;
556556
557//message editing557/** @type {string} */
558var this_edit_mes_chname = '';558let this_edit_mes_chname = '';
559var this_edit_mes_id;559/** @type {number|undefined} */
560let this_edit_mes_id = undefined;
560561
561//settings562//settings
562export let settings;563export let settings;
@@ -1494,7 +1495,7 @@ export async function reloadCurrentChat() {
1494export async function sendTextareaMessage() {1495export async function sendTextareaMessage() {
1495 if (is_send_press) return;1496 if (is_send_press) return;
1496 if (isExecutingCommandsFromChatInput) return;1497 if (isExecutingCommandsFromChatInput) return;
1497 if (this_edit_mes_id) return; // don't proceed if editing a message1498 if (this_edit_mes_id >= 0) return; // don't proceed if editing a message
14981499
1499 let generateType;1500 let generateType;
1500 // "Continue on send" is activated when the user hits "send" (or presses enter) on an empty chat box, and the last1501 // "Continue on send" is activated when the user hits "send" (or presses enter) on an empty chat box, and the last
@@ -7210,6 +7211,117 @@ function messageEditAuto(div) {
7210 saveChatDebounced();7211 saveChatDebounced();
7211}7212}
72127213
7214/**
7215 * Create the message edit UI.
7216 * @param {number} edit_mes_id
7217 */
7218async function messageEdit(edit_mes_id) {
7219 hideSwipeButtons();
7220 let chatScrollPosition = chatElement.scrollTop();
7221
7222 this_edit_mes_id = edit_mes_id;
7223
7224 let thisMesDiv = chatElement.children().filter(`.mes[mesid="${edit_mes_id}"]`);
7225
7226 let thisMesBlock = thisMesDiv.find('.mes_block');
7227 let thisMesText = thisMesBlock.find('.mes_text');
7228
7229 thisMesText.empty();
7230 thisMesBlock.find('.mes_buttons').css('display', 'none');
7231 thisMesBlock.find('.mes_edit_buttons').css('display', 'inline-flex');
7232
7233 // Also edit reasoning, if it exists
7234 const reasoningEdit = thisMesBlock.find('.mes_reasoning_edit:visible');
7235 if (reasoningEdit.length > 0) {
7236 reasoningEdit.trigger('click');
7237 }
7238
7239 let text = chat[edit_mes_id]['mes'];
7240 if (chat[edit_mes_id]['is_user']) {
7241 this_edit_mes_chname = name1;
7242 } else if (chat[edit_mes_id]['force_avatar']) {
7243 this_edit_mes_chname = chat[edit_mes_id]['name'];
7244 } else {
7245 this_edit_mes_chname = name2;
7246 }
7247 if (power_user.trim_spaces) {
7248 text = text.trim();
7249 }
7250 thisMesText.append(
7251 '<textarea id=\'curEditTextarea\' class=\'edit_textarea mdHotkeys\'></textarea>',
7252 );
7253
7254 let edit_textarea = thisMesBlock.find('.edit_textarea');
7255 edit_textarea.val(text);
7256
7257 const cssAutofit = CSS.supports('field-sizing', 'content');
7258 if (!cssAutofit) {
7259 edit_textarea.height(0);
7260 edit_textarea.height(edit_textarea[0].scrollHeight);
7261 }
7262 edit_textarea.trigger('focus');
7263 const textAreaElement = /** @type {HTMLTextAreaElement} */ (edit_textarea[0]);
7264 // Sets the cursor at the end of the text
7265 textAreaElement.setSelectionRange(
7266 String(edit_textarea.val()).length,
7267 String(edit_textarea.val()).length,
7268 );
7269 if (Number(this_edit_mes_id) === chat.length - 1) {
7270 chatElement.scrollTop(chatScrollPosition);
7271 }
7272
7273 updateEditArrowClasses();
7274}
7275
7276/**
7277 * Close the open message editor.
7278 * This deletes the user's unsaved changes.
7279 * @param {number} [messageId=this_edit_mes_id]
7280 */
7281async function messageEditCancel(messageId = this_edit_mes_id) {
7282 let text = chat[messageId]['mes'];
7283 let thisMesDiv;
7284 // If this is the button then select it's parent. Otherwise, select by messageId.
7285 if (this?.classList?.contains('mes_edit_cancel')) {
7286 thisMesDiv = $(this).closest('.mes');
7287 } else
7288 {
7289 thisMesDiv = chatElement.children().filter(`[mesid="${messageId}"]`);
7290 }
7291
7292 const thisMesBlock = thisMesDiv.find('.mes_block');
7293 thisMesBlock.find('.mes_text').empty();
7294 thisMesDiv.find('.mes_edit_buttons').css('display', 'none');
7295 thisMesBlock.find('.mes_buttons').css('display', '');
7296 thisMesBlock.find('.mes_text')
7297 .append(messageFormatting(
7298 text,
7299 this_edit_mes_chname,
7300 chat[messageId].is_system,
7301 chat[messageId].is_user,
7302 messageId,
7303 {},
7304 false,
7305 ));
7306 appendMediaToMessage(chat[messageId], thisMesDiv);
7307 addCopyToCodeBlocks(thisMesDiv);
7308
7309 const reasoningEditDone = thisMesBlock.find('.mes_reasoning_edit_cancel:visible');
7310 if (reasoningEditDone.length > 0) {
7311 reasoningEditDone.trigger('click');
7312 }
7313
7314 await eventSource.emit(event_types.MESSAGE_UPDATED, messageId);
7315 if (messageId == this_edit_mes_id) {
7316 this_edit_mes_id = undefined;
7317 }
7318 else {
7319 console.warn(`The message editor was closed on message #${messageId} while #${this_edit_mes_id} is being edited.`);
7320 }
7321
7322 showSwipeButtons();
7323}
7324
7213async function messageEditDone(div) {7325async function messageEditDone(div) {
7214 let { mesBlock, text, mes, bias } = updateMessage(div);7326 let { mesBlock, text, mes, bias } = updateMessage(div);
7215 if (this_edit_mes_id == 0) {7327 if (this_edit_mes_id == 0) {
@@ -7245,6 +7357,7 @@ async function messageEditDone(div) {
7245 await eventSource.emit(event_types.MESSAGE_UPDATED, this_edit_mes_id);7357 await eventSource.emit(event_types.MESSAGE_UPDATED, this_edit_mes_id);
7246 this_edit_mes_id = undefined;7358 this_edit_mes_id = undefined;
7247 await saveChatConditional();7359 await saveChatConditional();
7360 showSwipeButtons();
7248}7361}
72497362
7250/**7363/**
@@ -7901,7 +8014,7 @@ export function callPopup(text, type, inputValue = '', { okButton, rows, wide, w
7901}8014}
79028015
7903export function showSwipeButtons() {8016export function showSwipeButtons() {
7904 if (chat.length === 0) {8017 if (chat.length === 0 || this_edit_mes_id >= 0) {
7905 return;8018 return;
7906 }8019 }
79078020
@@ -8105,7 +8218,7 @@ function updateEditArrowClasses() {
8105 chatElement.find('.mes .mes_edit_up').removeClass('disabled');8218 chatElement.find('.mes .mes_edit_up').removeClass('disabled');
8106 chatElement.find('.mes .mes_edit_down').removeClass('disabled');8219 chatElement.find('.mes .mes_edit_down').removeClass('disabled');
81078220
8108 if (this_edit_mes_id !== undefined) {8221 if (this_edit_mes_id >= 0) {
8109 const down = chatElement.find(`.mes[mesid="${this_edit_mes_id}"] .mes_edit_down`);8222 const down = chatElement.find(`.mes[mesid="${this_edit_mes_id}"] .mes_edit_down`);
8110 const up = chatElement.find(`.mes[mesid="${this_edit_mes_id}"] .mes_edit_up`);8223 const up = chatElement.find(`.mes[mesid="${this_edit_mes_id}"] .mes_edit_up`);
8111 const lastId = Number(chatElement.find('.mes').last().attr('mesid'));8224 const lastId = Number(chatElement.find('.mes').last().attr('mesid'));
@@ -8127,7 +8240,7 @@ function updateEditArrowClasses() {
8127 */8240 */
8128export function closeMessageEditor(what = 'all') {8241export function closeMessageEditor(what = 'all') {
8129 if (what === 'message' || what === 'all') {8242 if (what === 'message' || what === 'all') {
8130 if (this_edit_mes_id) {8243 if (this_edit_mes_id >= 0) {
8131 chatElement.find(`.mes[mesid="${this_edit_mes_id}"] .mes_edit_cancel`).trigger('click');8244 chatElement.find(`.mes[mesid="${this_edit_mes_id}"] .mes_edit_cancel`).trigger('click');
8132 }8245 }
8133 }8246 }
@@ -10046,7 +10159,7 @@ jQuery(async function () {
10046 }10159 }
1004710160
10048 else if (id == 'option_continue') {10161 else if (id == 'option_continue') {
10049 if (this_edit_mes_id) return; // don't proceed if editing a message10162 if (this_edit_mes_id >= 0) return; // don't proceed if editing a message
1005010163
10051 if (is_send_press == false || fromSlashCommand) {10164 if (is_send_press == false || fromSlashCommand) {
10052 is_send_press = true;10165 is_send_press = true;
@@ -10236,8 +10349,7 @@ jQuery(async function () {
10236 return;10349 return;
10237 }*/10350 }*/
1023810351
10239 let chatScrollPosition = chatElement.scrollTop();10352 if (this_edit_mes_id >= 0) {
10240 if (this_edit_mes_id !== undefined) {
10241 let mes_edited = chatElement.find(`[mesid="${this_edit_mes_id}"]`).find('.mes_edit_done');10353 let mes_edited = chatElement.find(`[mesid="${this_edit_mes_id}"]`).find('.mes_edit_done');
10242 if (Number(edit_mes_id) == chat.length - 1) { //if the generating swipe (...)10354 if (Number(edit_mes_id) == chat.length - 1) { //if the generating swipe (...)
10243 let run_edit = true;10355 let run_edit = true;
@@ -10252,55 +10364,9 @@ jQuery(async function () {
10252 }10364 }
10253 await messageEditDone(mes_edited);10365 await messageEditDone(mes_edited);
10254 }10366 }
10255 $(this).closest('.mes_block').find('.mes_text').empty();10367 var edit_mes_id = Number($(this).closest('.mes').attr('mesid'));
10256 $(this).closest('.mes_block').find('.mes_buttons').css('display', 'none');
10257 $(this).closest('.mes_block').find('.mes_edit_buttons').css('display', 'inline-flex');
10258 var edit_mes_id = $(this).closest('.mes').attr('mesid');
10259 this_edit_mes_id = edit_mes_id;
10260
10261 // Also edit reasoning, if it exists
10262 const reasoningEdit = $(this).closest('.mes_block').find('.mes_reasoning_edit:visible');
10263 if (reasoningEdit.length > 0) {
10264 reasoningEdit.trigger('click');
10265 }
1026610368
10267 var text = chat[edit_mes_id]['mes'];10369 await messageEdit(edit_mes_id);
10268 if (chat[edit_mes_id]['is_user']) {
10269 this_edit_mes_chname = name1;
10270 } else if (chat[edit_mes_id]['force_avatar']) {
10271 this_edit_mes_chname = chat[edit_mes_id]['name'];
10272 } else {
10273 this_edit_mes_chname = name2;
10274 }
10275 if (power_user.trim_spaces) {
10276 text = text.trim();
10277 }
10278 $(this)
10279 .closest('.mes_block')
10280 .find('.mes_text')
10281 .append(
10282 '<textarea id=\'curEditTextarea\' class=\'edit_textarea mdHotkeys\'></textarea>',
10283 );
10284 $('#curEditTextarea').val(text);
10285 let edit_textarea = $(this)
10286 .closest('.mes_block')
10287 .find('.edit_textarea');
10288 if (!cssAutofit) {
10289 edit_textarea.height(0);
10290 edit_textarea.height(edit_textarea[0].scrollHeight);
10291 }
10292 edit_textarea.trigger('focus');
10293 const textAreaElement = /** @type {HTMLTextAreaElement} */ (edit_textarea[0]);
10294 // Sets the cursor at the end of the text
10295 textAreaElement.setSelectionRange(
10296 String(edit_textarea.val()).length,
10297 String(edit_textarea.val()).length,
10298 );
10299 if (Number(this_edit_mes_id) === chat.length - 1) {
10300 chatElement.scrollTop(chatScrollPosition);
10301 }
10302
10303 updateEditArrowClasses();
10304 }10370 }
10305 });10371 });
1030610372
@@ -10379,33 +10445,7 @@ jQuery(async function () {
10379 });10445 });
1038010446
10381 $(document).on('click', '.mes_edit_cancel', async function () {10447 $(document).on('click', '.mes_edit_cancel', async function () {
10382 let text = chat[this_edit_mes_id]['mes'];10448 await messageEditCancel.call(this, this_edit_mes_id);
10383
10384 $(this).closest('.mes_block').find('.mes_text').empty();
10385 $(this).closest('.mes_edit_buttons').css('display', 'none');
10386 $(this).closest('.mes_block').find('.mes_buttons').css('display', '');
10387 $(this)
10388 .closest('.mes_block')
10389 .find('.mes_text')
10390 .append(messageFormatting(
10391 text,
10392 this_edit_mes_chname,
10393 chat[this_edit_mes_id].is_system,
10394 chat[this_edit_mes_id].is_user,
10395 this_edit_mes_id,
10396 {},
10397 false,
10398 ));
10399 appendMediaToMessage(chat[this_edit_mes_id], $(this).closest('.mes'));
10400 addCopyToCodeBlocks($(this).closest('.mes'));
10401
10402 const reasoningEditDone = $(this).closest('.mes_block').find('.mes_reasoning_edit_cancel:visible');
10403 if (reasoningEditDone.length > 0) {
10404 reasoningEditDone.trigger('click');
10405 }
10406
10407 await eventSource.emit(event_types.MESSAGE_UPDATED, this_edit_mes_id);
10408 this_edit_mes_id = undefined;
10409 });10449 });
1041010450
10411 $(document).on('click', '.mes_edit_up', async function () {10451 $(document).on('click', '.mes_edit_up', async function () {
@@ -10413,7 +10453,6 @@ jQuery(async function () {
10413 return;10453 return;
10414 }10454 }
1041510455
10416 hideSwipeButtons();
10417 const targetId = Number(this_edit_mes_id) - 1;10456 const targetId = Number(this_edit_mes_id) - 1;
10418 const target = chatElement.find(`.mes[mesid="${targetId}"]`);10457 const target = chatElement.find(`.mes[mesid="${targetId}"]`);
10419 const root = $(this).closest('.mes');10458 const root = $(this).closest('.mes');
@@ -10434,7 +10473,6 @@ jQuery(async function () {
10434 this_edit_mes_id = targetId;10473 this_edit_mes_id = targetId;
10435 updateViewMessageIds();10474 updateViewMessageIds();
10436 await saveChatConditional();10475 await saveChatConditional();
10437 showSwipeButtons();
10438 });10476 });
1043910477
10440 $(document).on('click', '.mes_edit_down', async function () {10478 $(document).on('click', '.mes_edit_down', async function () {
@@ -10442,7 +10480,6 @@ jQuery(async function () {
10442 return;10480 return;
10443 }10481 }
1044410482
10445 hideSwipeButtons();
10446 const targetId = Number(this_edit_mes_id) + 1;10483 const targetId = Number(this_edit_mes_id) + 1;
10447 const target = chatElement.find(`.mes[mesid="${targetId}"]`);10484 const target = chatElement.find(`.mes[mesid="${targetId}"]`);
10448 const root = $(this).closest('.mes');10485 const root = $(this).closest('.mes');
@@ -10463,7 +10500,6 @@ jQuery(async function () {
10463 this_edit_mes_id = targetId;10500 this_edit_mes_id = targetId;
10464 updateViewMessageIds();10501 updateViewMessageIds();
10465 await saveChatConditional();10502 await saveChatConditional();
10466 showSwipeButtons();
10467 });10503 });
1046810504
10469 $(document).on('click', '.mes_edit_copy', async function () {10505 $(document).on('click', '.mes_edit_copy', async function () {
@@ -10493,7 +10529,7 @@ jQuery(async function () {
1049310529
10494 $(document).on('click', '.mes_edit_delete', async function (event, customData) {10530 $(document).on('click', '.mes_edit_delete', async function (event, customData) {
10495 const fromSlashCommand = customData?.fromSlashCommand || false;10531 const fromSlashCommand = customData?.fromSlashCommand || false;
10496 const canDeleteSwipe = (Array.isArray(chat[this_edit_mes_id].swipes) && chat[this_edit_mes_id].swipes.length > 1 && !chat[this_edit_mes_id].is_user && parseInt(this_edit_mes_id) === chat.length - 1);10532 const canDeleteSwipe = (Array.isArray(chat[this_edit_mes_id].swipes) && chat[this_edit_mes_id].swipes.length > 1 && !chat[this_edit_mes_id].is_user && Number(this_edit_mes_id) === chat.length - 1);
1049710533
10498 let deleteOnlySwipe = false;10534 let deleteOnlySwipe = false;
10499 if (power_user.confirm_message_delete && fromSlashCommand !== true) {10535 if (power_user.confirm_message_delete && fromSlashCommand !== true) {
@@ -10886,7 +10922,7 @@ jQuery(async function () {
10886 $('#send_textarea').trigger('focus');10922 $('#send_textarea').trigger('focus');
10887 return;10923 return;
10888 }10924 }
10889 if (!this_edit_mes_id && $('#mes_stop').is(':visible')) {10925 if (this_edit_mes_id === undefined && $('#mes_stop').is(':visible')) {
10890 $('#mes_stop').trigger('click');10926 $('#mes_stop').trigger('click');
10891 if (chat.length && Array.isArray(chat[chat.length - 1].swipes) && chat[chat.length - 1].swipe_id == chat[chat.length - 1].swipes.length) {10927 if (chat.length && Array.isArray(chat[chat.length - 1].swipes) && chat[chat.length - 1].swipe_id == chat[chat.length - 1].swipes.length) {
10892 $('.last_mes .swipe_left').trigger('click');10928 $('.last_mes .swipe_left').trigger('click');