diff --git a/public/script.js b/public/script.js index aa6e5f16b..13ece6069 100644 --- a/public/script.js +++ b/public/script.js @@ -101,6 +101,7 @@ import { proxies, loadProxyPresets, selected_proxy, + initOpenai, } from './scripts/openai.js'; import { @@ -915,6 +916,7 @@ async function firstLoadInit() { initKeyboard(); initDynamicStyles(); initTags(); + initOpenai(); await getUserAvatars(true, user_avatar); await getCharacters(); await getBackgrounds(); @@ -9232,12 +9234,15 @@ jQuery(async function () { * @param {HTMLTextAreaElement} e Textarea element to auto-fit */ function autoFitEditTextArea(e) { + const computedStyle = window.getComputedStyle(e); + const maxHeight = parseInt(computedStyle.maxHeight, 10); scroll_holder = chatElement[0].scrollTop; - e.style.height = '0'; - e.style.height = `${e.scrollHeight + 4}px`; + e.style.height = computedStyle.minHeight; + const newHeight = e.scrollHeight + 4; + e.style.height = (newHeight < maxHeight) ? `${newHeight}px` : `${maxHeight}px`; is_use_scroll_holder = true; } - const autoFitEditTextAreaDebounced = debouncedThrottle(autoFitEditTextArea, debounce_timeout.standard); + const autoFitEditTextAreaDebounced = debouncedThrottle(autoFitEditTextArea, debounce_timeout.short); document.addEventListener('input', e => { if (e.target instanceof HTMLTextAreaElement && e.target.classList.contains('edit_textarea')) { const immediately = e.target.scrollHeight > e.target.offsetHeight || e.target.value === ''; diff --git a/public/scripts/RossAscends-mods.js b/public/scripts/RossAscends-mods.js index dceba9063..5c56b3be5 100644 --- a/public/scripts/RossAscends-mods.js +++ b/public/scripts/RossAscends-mods.js @@ -31,7 +31,7 @@ import { SECRET_KEYS, secret_state, } from './secrets.js'; -import { debounce, getStringHash, isValidUrl } from './utils.js'; +import { debounce, debouncedThrottle, getStringHash, isValidUrl } from './utils.js'; import { chat_completion_sources, oai_settings } from './openai.js'; import { getTokenCountAsync } from './tokenizers.js'; import { textgen_types, textgenerationwebui_settings as textgen_settings, getTextGenServer } from './textgen-settings.js'; @@ -694,20 +694,23 @@ const isFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1; * this makes the chat input text area resize vertically to match the text size (limited by CSS at 50% window height) */ function autoFitSendTextArea() { + // Needs to be pulled dynamically because it is affected by font size changes + const computedStyle = window.getComputedStyle(sendTextArea); const originalScrollBottom = chatBlock.scrollHeight - (chatBlock.scrollTop + chatBlock.offsetHeight); if (Math.ceil(sendTextArea.scrollHeight + 3) >= Math.floor(sendTextArea.offsetHeight)) { - // Needs to be pulled dynamically because it is affected by font size changes - const sendTextAreaMinHeight = window.getComputedStyle(sendTextArea).getPropertyValue('min-height'); + const sendTextAreaMinHeight = computedStyle.minHeight; sendTextArea.style.height = sendTextAreaMinHeight; } - sendTextArea.style.height = sendTextArea.scrollHeight + 3 + 'px'; + const maxHeight = parseInt(computedStyle.maxHeight, 10); + const newHeight = sendTextArea.scrollHeight + 3; + sendTextArea.style.height = (newHeight < maxHeight) ? `${newHeight}px` : `${maxHeight}px`; if (!isFirefox) { const newScrollTop = Math.round(chatBlock.scrollHeight - (chatBlock.offsetHeight + originalScrollBottom)); chatBlock.scrollTop = newScrollTop; } } -export const autoFitSendTextAreaDebounced = debounce(autoFitSendTextArea); +export const autoFitSendTextAreaDebounced = debouncedThrottle(autoFitSendTextArea, debounce_timeout.short); // --------------------------------------------------- diff --git a/public/scripts/extensions/caption/index.js b/public/scripts/extensions/caption/index.js index b47f73a2f..dbfbc0d1d 100644 --- a/public/scripts/extensions/caption/index.js +++ b/public/scripts/extensions/caption/index.js @@ -333,8 +333,9 @@ async function getCaptionForFile(file, prompt, quiet) { return caption; } catch (error) { - toastr.error('Failed to caption image.'); - console.log(error); + const errorMessage = error.message || 'Unknown error'; + toastr.error(errorMessage, "Failed to caption image."); + console.error(error); return ''; } finally { diff --git a/public/scripts/extensions/memory/index.js b/public/scripts/extensions/memory/index.js index 2bb5c1269..0bc2ff577 100644 --- a/public/scripts/extensions/memory/index.js +++ b/public/scripts/extensions/memory/index.js @@ -914,7 +914,7 @@ jQuery(async function () { await addExtensionControls(); loadSettings(); - eventSource.on(event_types.MESSAGE_RECEIVED, onChatEvent); + eventSource.makeLast(event_types.CHARACTER_MESSAGE_RENDERED, onChatEvent); eventSource.on(event_types.MESSAGE_DELETED, onChatEvent); eventSource.on(event_types.MESSAGE_EDITED, onChatEvent); eventSource.on(event_types.MESSAGE_SWIPED, onChatEvent); diff --git a/public/scripts/extensions/quick-reply/index.js b/public/scripts/extensions/quick-reply/index.js index 5c5cf7d7e..7bd108243 100644 --- a/public/scripts/extensions/quick-reply/index.js +++ b/public/scripts/extensions/quick-reply/index.js @@ -239,7 +239,7 @@ eventSource.on(event_types.CHAT_CHANGED, (...args)=>executeIfReadyElseQueue(onCh const onUserMessage = async () => { await autoExec.handleUser(); }; -eventSource.on(event_types.USER_MESSAGE_RENDERED, (...args)=>executeIfReadyElseQueue(onUserMessage, args)); +eventSource.makeFirst(event_types.USER_MESSAGE_RENDERED, (...args)=>executeIfReadyElseQueue(onUserMessage, args)); const onAiMessage = async (messageId) => { if (['...'].includes(chat[messageId]?.mes)) { @@ -249,7 +249,7 @@ const onAiMessage = async (messageId) => { await autoExec.handleAi(); }; -eventSource.on(event_types.CHARACTER_MESSAGE_RENDERED, (...args)=>executeIfReadyElseQueue(onAiMessage, args)); +eventSource.makeFirst(event_types.CHARACTER_MESSAGE_RENDERED, (...args)=>executeIfReadyElseQueue(onAiMessage, args)); const onGroupMemberDraft = async () => { await autoExec.handleGroupMemberDraft(); diff --git a/public/scripts/openai.js b/public/scripts/openai.js index 00d319121..b30894f41 100644 --- a/public/scripts/openai.js +++ b/public/scripts/openai.js @@ -4679,22 +4679,23 @@ function runProxyCallback(_, value) { return foundName; } -SlashCommandParser.addCommandObject(SlashCommand.fromProps({ - name: 'proxy', - callback: runProxyCallback, - returns: 'current proxy', - namedArgumentList: [], - unnamedArgumentList: [ - SlashCommandArgument.fromProps({ - description: 'name', - typeList: [ARGUMENT_TYPE.STRING], - isRequired: true, - enumProvider: () => proxies.map(preset => new SlashCommandEnumValue(preset.name, preset.url)), - }), - ], - helpString: 'Sets a proxy preset by name.', -})); - +export function initOpenai() { + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ + name: 'proxy', + callback: runProxyCallback, + returns: 'current proxy', + namedArgumentList: [], + unnamedArgumentList: [ + SlashCommandArgument.fromProps({ + description: 'name', + typeList: [ARGUMENT_TYPE.STRING], + isRequired: true, + enumProvider: () => proxies.map(preset => new SlashCommandEnumValue(preset.name, preset.url)), + }), + ], + helpString: 'Sets a proxy preset by name.', + })); +} $(document).ready(async function () { $('#test_api_button').on('click', testApiConnection); diff --git a/public/scripts/popup.js b/public/scripts/popup.js index 5eba4db4c..ed0a52577 100644 --- a/public/scripts/popup.js +++ b/public/scripts/popup.js @@ -194,7 +194,7 @@ export class Popup { const buttonElement = document.createElement('div'); buttonElement.classList.add('menu_button', 'popup-button-custom', 'result-control'); buttonElement.classList.add(...(button.classes ?? [])); - buttonElement.dataset.result = String(button.result ?? undefined); + buttonElement.dataset.result = String(button.result); // This is expected to also write 'null' or 'staging', to indicate cancel and no action respectively buttonElement.textContent = button.text; buttonElement.dataset.i18n = buttonElement.textContent; buttonElement.tabIndex = 0; @@ -317,9 +317,14 @@ export class Popup { // Bind event listeners for all result controls to their defined event type this.dlg.querySelectorAll('[data-result]').forEach(resultControl => { if (!(resultControl instanceof HTMLElement)) return; - const result = Number(resultControl.dataset.result); - if (String(undefined) === String(resultControl.dataset.result)) return; - if (isNaN(result)) throw new Error('Invalid result control. Result must be a number. ' + resultControl.dataset.result); + // If no value was set, we exit out and don't bind an action + if (String(resultControl.dataset.result) === String(undefined)) return; + + // Make sure that both `POPUP_RESULT` numbers and also `null` as 'cancelled' are supported + const result = String(resultControl.dataset.result) === String(null) ? null + : Number(resultControl.dataset.result); + + if (result !== null && isNaN(result)) throw new Error('Invalid result control. Result must be a number. ' + resultControl.dataset.result); const type = resultControl.dataset.resultEvent || 'click'; resultControl.addEventListener(type, async () => await this.complete(result)); }); diff --git a/public/scripts/power-user.js b/public/scripts/power-user.js index f57de63ff..08fbfb504 100644 --- a/public/scripts/power-user.js +++ b/public/scripts/power-user.js @@ -2734,45 +2734,26 @@ async function doDelMode(_, text) { return ''; } - //first enter delmode - $('#option_delete_mes').trigger('click', { fromSlashCommand: true }); - - //parse valid args - if (text) { - await delay(300); //same as above, need event signal for 'entered del mode' - console.debug('parsing msgs to del'); - let numMesToDel = Number(text); - let lastMesID = Number($('#chat .mes').last().attr('mesid')); - let oldestMesIDToDel = lastMesID - numMesToDel + 1; - - if (oldestMesIDToDel < 0) { - toastr.warning(`Cannot delete more than ${chat.length} messages.`); - return ''; - } - - let oldestMesToDel = $('#chat').find(`.mes[mesid=${oldestMesIDToDel}]`); - - if (!oldestMesIDToDel && lastMesID > 0) { - oldestMesToDel = await loadUntilMesId(oldestMesIDToDel); - - if (!oldestMesToDel || !oldestMesToDel.length) { - return ''; - } - } - - let oldestDelMesCheckbox = $(oldestMesToDel).find('.del_checkbox'); - let newLastMesID = oldestMesIDToDel - 1; - console.debug(`DelMesReport -- numMesToDel: ${numMesToDel}, lastMesID: ${lastMesID}, oldestMesIDToDel:${oldestMesIDToDel}, newLastMesID: ${newLastMesID}`); - oldestDelMesCheckbox.trigger('click'); - let trueNumberOfDeletedMessage = lastMesID - oldestMesIDToDel + 1; - - //await delay(1) - $('#dialogue_del_mes_ok').trigger('click'); - toastr.success(`Deleted ${trueNumberOfDeletedMessage} messages.`); + // Just enter the delete mode. + if (!text) { + $('#option_delete_mes').trigger('click', { fromSlashCommand: true }); return ''; } - return ''; + const count = Number(text); + + // Nothing to delete. + if (count < 1) { + return ''; + } + + if (count > chat.length) { + toastr.warning(`Cannot delete more than ${chat.length} messages.`); + return ''; + } + + const range = `${chat.length - count}-${chat.length - 1}`; + return doMesCut(_, range); } function doResetPanels() { diff --git a/public/scripts/preset-manager.js b/public/scripts/preset-manager.js index 90fe232ba..5d25140dc 100644 --- a/public/scripts/preset-manager.js +++ b/public/scripts/preset-manager.js @@ -1,6 +1,5 @@ import { amount_gen, - callPopup, characters, eventSource, event_types, @@ -19,6 +18,7 @@ import { import { groups, selected_group } from './group-chats.js'; import { instruct_presets } from './instruct-mode.js'; import { kai_settings } from './kai-settings.js'; +import { Popup } from './popup.js'; import { context_presets, getContextSettings, power_user } from './power-user.js'; import { SlashCommand } from './slash-commands/SlashCommand.js'; import { ARGUMENT_TYPE, SlashCommandArgument } from './slash-commands/SlashCommandArgument.js'; @@ -165,11 +165,8 @@ class PresetManager { async savePresetAs() { const inputValue = this.getSelectedPresetName(); - const popupText = ` -
mode
argument allows to control the behavior when multiple items match the text.
+ first
(default) returns the first match below the threshold.best
returns the best match below the threshold.