Unblock textarea send during edit (#4714)

60a8b519512f81f5418de378a09b3ee3b7c65ce1

Cohee <18619528+Cohee1207@users.noreply.github.com>

Signed
3 files changed, +53 -30Showing whitespace changes
public/script.js+7 -5
@@ -276,6 +276,7 @@ import { event_types, eventSource } from './scripts/events.js';
276import { initAccessibility } from './scripts/a11y.js';276import { initAccessibility } from './scripts/a11y.js';
277import { applyStreamFadeIn } from './scripts/util/stream-fadein.js';277import { applyStreamFadeIn } from './scripts/util/stream-fadein.js';
278import { initDomHandlers } from './scripts/dom-handlers.js';278import { initDomHandlers } from './scripts/dom-handlers.js';
279import { SimpleMutex } from './scripts/util/SimpleMutex.js';
279280
280// API OBJECT FOR EXTERNAL WIRING281// API OBJECT FOR EXTERNAL WIRING
281globalThis.SillyTavern = {282globalThis.SillyTavern = {
@@ -1560,9 +1561,8 @@ export async function reloadCurrentChat() {
1560export async function sendTextareaMessage() {1561export async function sendTextareaMessage() {
1561 if (is_send_press) return;1562 if (is_send_press) return;
1562 if (isExecutingCommandsFromChatInput) return;1563 if (isExecutingCommandsFromChatInput) return;
1563 if (this_edit_mes_id >= 0) return; // don't proceed if editing a message
15641564
1565 let generateType;1565 let generateType = 'normal';
1566 // "Continue on send" is activated when the user hits "send" (or presses enter) on an empty chat box, and the last1566 // "Continue on send" is activated when the user hits "send" (or presses enter) on an empty chat box, and the last
1567 // message was sent from a character (not the user or the system).1567 // message was sent from a character (not the user or the system).
1568 const textareaText = String($('#send_textarea').val());1568 const textareaText = String($('#send_textarea').val());
@@ -1581,7 +1581,7 @@ export async function sendTextareaMessage() {
1581 await newAssistantChat({ temporary: false });1581 await newAssistantChat({ temporary: false });
1582 }1582 }
15831583
1584 Generate(generateType);1584 return await Generate(generateType);
1585}1585}
15861586
1587/**1587/**
@@ -2190,6 +2190,7 @@ export function addOneMessage(mes, { type = 'normal', insertAfter = null, scroll
2190 }2190 }
21912191
2192 applyCharacterTagsToMessageDivs({ mesIds: newMessageId });2192 applyCharacterTagsToMessageDivs({ mesIds: newMessageId });
2193 updateEditArrowClasses();
2193}2194}
21942195
2195/**2196/**
@@ -9937,8 +9938,9 @@ jQuery(async function () {
9937 $('#option_continue').trigger('click');9938 $('#option_continue').trigger('click');
9938 });9939 });
99399940
9940 $('#send_but').on('click', function () {9941 const userInputGenerateMutex = new SimpleMutex(sendTextareaMessage);
9941 sendTextareaMessage();9942 $('#send_but').on('click', async function () {
9943 await userInputGenerateMutex.update();
9942 });9944 });
99439945
9944 //menu buttons setup9946 //menu buttons setup
public/scripts/extensions.js+2 -25
@@ -10,10 +10,12 @@ import { isAdmin } from './user.js';
10import { addLocaleData, getCurrentLocale, t } from './i18n.js';10import { addLocaleData, getCurrentLocale, t } from './i18n.js';
11import { debounce_timeout } from './constants.js';11import { debounce_timeout } from './constants.js';
12import { accountStorage } from './util/AccountStorage.js';12import { accountStorage } from './util/AccountStorage.js';
13import { SimpleMutex } from './util/SimpleMutex.js';
1314
14export {15export {
15 getContext,16 getContext,
16 getApiUrl,17 getApiUrl,
18 SimpleMutex as ModuleWorkerWrapper,
17};19};
1820
19/** @type {string[]} */21/** @type {string[]} */
@@ -124,31 +126,6 @@ export function renderExtensionTemplateAsync(extensionName, templateId, template
124 return renderTemplateAsync(`scripts/extensions/${extensionName}/${templateId}.html`, templateData, sanitize, localize, true);126 return renderTemplateAsync(`scripts/extensions/${extensionName}/${templateId}.html`, templateData, sanitize, localize, true);
125}127}
126128
127// Disables parallel updates
128export class ModuleWorkerWrapper {
129 constructor(callback) {
130 this.isBusy = false;
131 this.callback = callback;
132 }
133
134 // Called by the extension
135 async update(...args) {
136 // Don't touch me I'm busy...
137 if (this.isBusy) {
138 return;
139 }
140
141 // I'm free. Let's update!
142 try {
143 this.isBusy = true;
144 await this.callback(...args);
145 }
146 finally {
147 this.isBusy = false;
148 }
149 }
150}
151
152export const extension_settings = {129export const extension_settings = {
153 apiUrl: defaultUrl,130 apiUrl: defaultUrl,
154 apiKey: '',131 apiKey: '',
public/scripts/util/SimpleMutex.js+44 -0
@@ -0,0 +1,44 @@
1/**
2 * A simple mutex class to prevent concurrent updates.
3 */
4export class SimpleMutex {
5 /**
6 * @type {boolean}
7 */
8 isBusy = false;
9
10 /**
11 * @type {Function}
12 */
13 callback = () => {};
14
15 /**
16 * Constructs a SimpleMutex.
17 * @param {Function} callback Callback function.
18 */
19 constructor(callback) {
20 this.isBusy = false;
21 this.callback = callback;
22 }
23
24 /**
25 * Updates the mutex by calling the callback if not busy.
26 * @param {...any} args Callback args
27 * @returns {Promise<void>}
28 */
29 async update(...args) {
30 // Don't touch me I'm busy...
31 if (this.isBusy) {
32 return;
33 }
34
35 // I'm free. Let's update!
36 try {
37 this.isBusy = true;
38 await this.callback(...args);
39 }
40 finally {
41 this.isBusy = false;
42 }
43 }
44}