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
555555let this_del_mes = -1;
556556
557-//message editing
557+/** @type {string} */
558558varlet this_edit_mes_chname = '';
559-var this_edit_mes_id;
559+/** @type {number|undefined} */
560+let this_edit_mes_id = undefined;
560561
561562//settings
562563export let settings;
@@ -1494,7 +1495,7 @@ export async function reloadCurrentChat() {
14941495export async function sendTextareaMessage() {
14951496 if (is_send_press) return;
14961497 if (isExecutingCommandsFromChatInput) return;
14971498 if (this_edit_mes_id >= 0) return; // don't proceed if editing a message
14981499
14991500 let generateType;
15001501 // "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) {
72107211 saveChatDebounced();
72117212}
72127213
7214+/**
7215+ * Create the message edit UI.
7216+ * @param {number} edit_mes_id
7217+ */
7218+async 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+ */
7281+async 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+
72137325async function messageEditDone(div) {
72147326 let { mesBlock, text, mes, bias } = updateMessage(div);
72157327 if (this_edit_mes_id == 0) {
@@ -7245,6 +7357,7 @@ async function messageEditDone(div) {
72457357 await eventSource.emit(event_types.MESSAGE_UPDATED, this_edit_mes_id);
72467358 this_edit_mes_id = undefined;
72477359 await saveChatConditional();
7360+ showSwipeButtons();
72487361}
72497362
72507363/**
@@ -7901,7 +8014,7 @@ export function callPopup(text, type, inputValue = '', { okButton, rows, wide, w
79018014}
79028015
79038016export function showSwipeButtons() {
79048017 if (chat.length === 0 || this_edit_mes_id >= 0) {
79058018 return;
79068019 }
79078020
@@ -8105,7 +8218,7 @@ function updateEditArrowClasses() {
81058218 chatElement.find('.mes .mes_edit_up').removeClass('disabled');
81068219 chatElement.find('.mes .mes_edit_down').removeClass('disabled');
81078220
81088221 if (this_edit_mes_id !=>= undefined0) {
81098222 const down = chatElement.find(`.mes[mesid="${this_edit_mes_id}"] .mes_edit_down`);
81108223 const up = chatElement.find(`.mes[mesid="${this_edit_mes_id}"] .mes_edit_up`);
81118224 const lastId = Number(chatElement.find('.mes').last().attr('mesid'));
@@ -8127,7 +8240,7 @@ function updateEditArrowClasses() {
81278240 */
81288241export function closeMessageEditor(what = 'all') {
81298242 if (what === 'message' || what === 'all') {
81308243 if (this_edit_mes_id >= 0) {
81318244 chatElement.find(`.mes[mesid="${this_edit_mes_id}"] .mes_edit_cancel`).trigger('click');
81328245 }
81338246 }
@@ -10046,7 +10159,7 @@ jQuery(async function () {
1004610159 }
1004710160
1004810161 else if (id == 'option_continue') {
1004910162 if (this_edit_mes_id >= 0) return; // don't proceed if editing a message
1005010163
1005110164 if (is_send_press == false || fromSlashCommand) {
1005210165 is_send_press = true;
@@ -10236,8 +10349,7 @@ jQuery(async function () {
1023610349 return;
1023710350 }*/
1023810351
10239- let chatScrollPosition = chatElement.scrollTop();
10352+ if (this_edit_mes_id >= 0) {
10240- if (this_edit_mes_id !== undefined) {
1024110353 let mes_edited = chatElement.find(`[mesid="${this_edit_mes_id}"]`).find('.mes_edit_done');
1024210354 if (Number(edit_mes_id) == chat.length - 1) { //if the generating swipe (...)
1024310355 let run_edit = true;
@@ -10252,55 +10364,9 @@ jQuery(async function () {
1025210364 }
1025310365 await messageEditDone(mes_edited);
1025410366 }
1025510367 var edit_mes_id = Number($(this).closest('.mes_blockmes').findattr('.mes_textmesid').empty();
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();
1030410370 }
1030510371 });
1030610372
@@ -10379,33 +10445,7 @@ jQuery(async function () {
1037910445 });
1038010446
1038110447 $(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;
1040910449 });
1041010450
1041110451 $(document).on('click', '.mes_edit_up', async function () {
@@ -10413,7 +10453,6 @@ jQuery(async function () {
1041310453 return;
1041410454 }
1041510455
10416- hideSwipeButtons();
1041710456 const targetId = Number(this_edit_mes_id) - 1;
1041810457 const target = chatElement.find(`.mes[mesid="${targetId}"]`);
1041910458 const root = $(this).closest('.mes');
@@ -10434,7 +10473,6 @@ jQuery(async function () {
1043410473 this_edit_mes_id = targetId;
1043510474 updateViewMessageIds();
1043610475 await saveChatConditional();
10437- showSwipeButtons();
1043810476 });
1043910477
1044010478 $(document).on('click', '.mes_edit_down', async function () {
@@ -10442,7 +10480,6 @@ jQuery(async function () {
1044210480 return;
1044310481 }
1044410482
10445- hideSwipeButtons();
1044610483 const targetId = Number(this_edit_mes_id) + 1;
1044710484 const target = chatElement.find(`.mes[mesid="${targetId}"]`);
1044810485 const root = $(this).closest('.mes');
@@ -10463,7 +10500,6 @@ jQuery(async function () {
1046310500 this_edit_mes_id = targetId;
1046410501 updateViewMessageIds();
1046510502 await saveChatConditional();
10466- showSwipeButtons();
1046710503 });
1046810504
1046910505 $(document).on('click', '.mes_edit_copy', async function () {
@@ -10493,7 +10529,7 @@ jQuery(async function () {
1049310529
1049410530 $(document).on('click', '.mes_edit_delete', async function (event, customData) {
1049510531 const fromSlashCommand = customData?.fromSlashCommand || false;
1049610532 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 && parseIntNumber(this_edit_mes_id) === chat.length - 1);
1049710533
1049810534 let deleteOnlySwipe = false;
1049910535 if (power_user.confirm_message_delete && fromSlashCommand !== true) {
@@ -10886,7 +10922,7 @@ jQuery(async function () {
1088610922 $('#send_textarea').trigger('focus');
1088710923 return;
1088810924 }
1088910925 if (!this_edit_mes_id === undefined && $('#mes_stop').is(':visible')) {
1089010926 $('#mes_stop').trigger('click');
1089110927 if (chat.length && Array.isArray(chat[chat.length - 1].swipes) && chat[chat.length - 1].swipe_id == chat[chat.length - 1].swipes.length) {
1089210928 $('.last_mes .swipe_left').trigger('click');