| @@ -276,6 +276,7 @@ import { event_types, eventSource } from './scripts/events.js'; | |||
| 276 | import { initAccessibility } from './scripts/a11y.js'; | 276 | import { initAccessibility } from './scripts/a11y.js'; |
| 277 | import { applyStreamFadeIn } from './scripts/util/stream-fadein.js'; | 277 | import { applyStreamFadeIn } from './scripts/util/stream-fadein.js'; |
| 278 | import { initDomHandlers } from './scripts/dom-handlers.js'; | 278 | import { initDomHandlers } from './scripts/dom-handlers.js'; |
| 279 | import { SimpleMutex } from './scripts/util/SimpleMutex.js'; | ||
| 279 | 280 | ||
| 280 | // API OBJECT FOR EXTERNAL WIRING | 281 | // API OBJECT FOR EXTERNAL WIRING |
| 281 | globalThis.SillyTavern = { | 282 | globalThis.SillyTavern = { |
| @@ -1560,9 +1561,8 @@ export async function reloadCurrentChat() { | |||
| 1560 | export async function sendTextareaMessage() { | 1561 | export 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 | ||
| 1564 | 1564 | ||
| 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 last | 1566 | // "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 | } |
| 1583 | 1583 | ||
| 1584 | Generate(generateType); | 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 | applyCharacterTagsToMessageDivs({ mesIds: newMessageId }); | 2192 | applyCharacterTagsToMessageDivs({ mesIds: newMessageId }); |
| 2193 | updateEditArrowClasses(); | ||
| 2193 | } | 2194 | } |
| 2194 | 2195 | ||
| 2195 | /** | 2196 | /** |
| @@ -9937,8 +9938,9 @@ jQuery(async function () { | |||
| 9937 | $('#option_continue').trigger('click'); | 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 | //menu buttons setup | 9946 | //menu buttons setup |
| @@ -10,10 +10,12 @@ import { isAdmin } from './user.js'; | |||
| 10 | import { addLocaleData, getCurrentLocale, t } from './i18n.js'; | 10 | import { addLocaleData, getCurrentLocale, t } from './i18n.js'; |
| 11 | import { debounce_timeout } from './constants.js'; | 11 | import { debounce_timeout } from './constants.js'; |
| 12 | import { accountStorage } from './util/AccountStorage.js'; | 12 | import { accountStorage } from './util/AccountStorage.js'; |
| 13 | import { SimpleMutex } from './util/SimpleMutex.js'; | ||
| 13 | 14 | ||
| 14 | export { | 15 | export { |
| 15 | getContext, | 16 | getContext, |
| 16 | getApiUrl, | 17 | getApiUrl, |
| 18 | SimpleMutex as ModuleWorkerWrapper, | ||
| 17 | }; | 19 | }; |
| 18 | 20 | ||
| 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 | } |
| 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 | export const extension_settings = { | 129 | export const extension_settings = { |
| 153 | apiUrl: defaultUrl, | 130 | apiUrl: defaultUrl, |
| 154 | apiKey: '', | 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 | } | ||