| @@ -276,6 +276,7 @@ import { event_types, eventSource } from './scripts/events.js'; | ||
| 276 | 276 | import { initAccessibility } from './scripts/a11y.js'; |
| 277 | 277 | import { applyStreamFadeIn } from './scripts/util/stream-fadein.js'; |
| 278 | 278 | import { initDomHandlers } from './scripts/dom-handlers.js'; |
| 279 | +import { SimpleMutex } from './scripts/util/SimpleMutex.js'; | |
| 279 | 280 | |
| 280 | 281 | // API OBJECT FOR EXTERNAL WIRING |
| 281 | 282 | globalThis.SillyTavern = { |
| @@ -1560,9 +1561,8 @@ export async function reloadCurrentChat() { | ||
| 1560 | 1561 | export async function sendTextareaMessage() { |
| 1561 | 1562 | if (is_send_press) return; |
| 1562 | 1563 | if (isExecutingCommandsFromChatInput) return; |
| 1563 | - if (this_edit_mes_id >= 0) return; // don't proceed if editing a message | |
| 1564 | 1564 | |
| 1565 | 1565 | let generateType = 'normal'; |
| 1566 | 1566 | // "Continue on send" is activated when the user hits "send" (or presses enter) on an empty chat box, and the last |
| 1567 | 1567 | // message was sent from a character (not the user or the system). |
| 1568 | 1568 | const textareaText = String($('#send_textarea').val()); |
| @@ -1581,7 +1581,7 @@ export async function sendTextareaMessage() { | ||
| 1581 | 1581 | await newAssistantChat({ temporary: false }); |
| 1582 | 1582 | } |
| 1583 | 1583 | |
| 1584 | 1584 | return await Generate(generateType); |
| 1585 | 1585 | } |
| 1586 | 1586 | |
| 1587 | 1587 | /** |
| @@ -2190,6 +2190,7 @@ export function addOneMessage(mes, { type = 'normal', insertAfter = null, scroll | ||
| 2190 | 2190 | } |
| 2191 | 2191 | |
| 2192 | 2192 | applyCharacterTagsToMessageDivs({ mesIds: newMessageId }); |
| 2193 | + updateEditArrowClasses(); | |
| 2193 | 2194 | } |
| 2194 | 2195 | |
| 2195 | 2196 | /** |
| @@ -9937,8 +9938,9 @@ jQuery(async function () { | ||
| 9937 | 9938 | $('#option_continue').trigger('click'); |
| 9938 | 9939 | }); |
| 9939 | 9940 | |
| 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 | }); |
| 9943 | 9945 | |
| 9944 | 9946 | //menu buttons setup |
| @@ -10,10 +10,12 @@ import { isAdmin } from './user.js'; | ||
| 10 | 10 | import { addLocaleData, getCurrentLocale, t } from './i18n.js'; |
| 11 | 11 | import { debounce_timeout } from './constants.js'; |
| 12 | 12 | import { accountStorage } from './util/AccountStorage.js'; |
| 13 | +import { SimpleMutex } from './util/SimpleMutex.js'; | |
| 13 | 14 | |
| 14 | 15 | export { |
| 15 | 16 | getContext, |
| 16 | 17 | getApiUrl, |
| 18 | + SimpleMutex as ModuleWorkerWrapper, | |
| 17 | 19 | }; |
| 18 | 20 | |
| 19 | 21 | /** @type {string[]} */ |
| @@ -124,31 +126,6 @@ export function renderExtensionTemplateAsync(extensionName, templateId, template | ||
| 124 | 126 | return renderTemplateAsync(`scripts/extensions/${extensionName}/${templateId}.html`, templateData, sanitize, localize, true); |
| 125 | 127 | } |
| 126 | 128 | |
| 127 | -// Disables parallel updates | |
| 128 | -export 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 | - | |
| 152 | 129 | export const extension_settings = { |
| 153 | 130 | apiUrl: defaultUrl, |
| 154 | 131 | apiKey: '', |
| @@ -0,0 +1,44 @@ | ||
| 1 | +/** | |
| 2 | + * A simple mutex class to prevent concurrent updates. | |
| 3 | + */ | |
| 4 | +export 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 | +} | |