From 92ec45af4b72bbb5f1fbe8890dfb708d0005214a Mon Sep 17 00:00:00 2001 From: LenAnderson Date: Mon, 25 Mar 2024 18:16:18 -0400 Subject: [PATCH 01/27] call processHotkeys debounced --- public/scripts/RossAscends-mods.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/public/scripts/RossAscends-mods.js b/public/scripts/RossAscends-mods.js index da93d1ed7..fcbaf35b6 100644 --- a/public/scripts/RossAscends-mods.js +++ b/public/scripts/RossAscends-mods.js @@ -884,8 +884,9 @@ export function initRossMods() { } $(document).on('keydown', function (event) { - processHotkeys(event.originalEvent); + processHotkeysDebounced(event.originalEvent); }); + const processHotkeysDebounced = debounce(processHotkeys); //Additional hotkeys CTRL+ENTER and CTRL+UPARROW /** From e567aa2c314e81aba0e6df0f85df90f7f95bc8e2 Mon Sep 17 00:00:00 2001 From: LenAnderson Date: Tue, 26 Mar 2024 12:09:26 -0400 Subject: [PATCH 02/27] replace debounce with other performance improvements - remove debounce from processHotkey - replace dom-queries in conditions with vars - replace some jQuery in conditions with vanilla JS --- public/scripts/RossAscends-mods.js | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/public/scripts/RossAscends-mods.js b/public/scripts/RossAscends-mods.js index fcbaf35b6..ee6402ae4 100644 --- a/public/scripts/RossAscends-mods.js +++ b/public/scripts/RossAscends-mods.js @@ -884,9 +884,13 @@ export function initRossMods() { } $(document).on('keydown', function (event) { - processHotkeysDebounced(event.originalEvent); + processHotkeys(event.originalEvent); }); - const processHotkeysDebounced = debounce(processHotkeys); + + const hotkeyTargets = { + 'send_textarea': sendTextArea, + 'dialogue_popup_input': document.querySelector('#dialogue_popup_input'), + }; //Additional hotkeys CTRL+ENTER and CTRL+UPARROW /** @@ -894,17 +898,19 @@ export function initRossMods() { */ function processHotkeys(event) { //Enter to send when send_textarea in focus - if ($(':focus').attr('id') === 'send_textarea') { + if (document.activeElement == hotkeyTargets['send_textarea']) { const sendOnEnter = shouldSendOnEnter(); if (!event.shiftKey && !event.ctrlKey && !event.altKey && event.key == 'Enter' && sendOnEnter) { event.preventDefault(); sendTextareaMessage(); + return; } } - if ($(':focus').attr('id') === 'dialogue_popup_input' && !isMobile()) { + if (document.activeElement == hotkeyTargets['dialogue_popup_input'] && !isMobile()) { if (!event.shiftKey && !event.ctrlKey && event.key == 'Enter') { event.preventDefault(); $('#dialogue_popup_ok').trigger('click'); + return; } } //ctrl+shift+up to scroll to context line @@ -916,6 +922,7 @@ export function initRossMods() { scrollTop: contextLine.offset().top - $('#chat').offset().top + $('#chat').scrollTop(), }, 300); } else { toastr.warning('Context line not found, send a message first!'); } + return; } //ctrl+shift+down to scroll to bottom of chat if (event.shiftKey && event.ctrlKey && event.key == 'ArrowDown') { @@ -923,6 +930,7 @@ export function initRossMods() { $('#chat').animate({ scrollTop: $('#chat').prop('scrollHeight'), }, 300); + return; } // Alt+Enter or AltGr+Enter to Continue @@ -930,6 +938,7 @@ export function initRossMods() { if (is_send_press == false) { console.debug('Continuing with Alt+Enter'); $('#option_continue').trigger('click'); + return; } } @@ -939,6 +948,7 @@ export function initRossMods() { if (editMesDone.length > 0) { console.debug('Accepting edits with Ctrl+Enter'); editMesDone.trigger('click'); + return; } else if (is_send_press == false) { const skipConfirmKey = 'RegenerateWithCtrlEnter'; const skipConfirm = LoadLocalBool(skipConfirmKey); @@ -965,6 +975,7 @@ export function initRossMods() { doRegenerate(); }); } + return; } else { console.debug('Ctrl+Enter ignored'); } @@ -973,7 +984,7 @@ export function initRossMods() { // Helper function to check if nanogallery2's lightbox is active function isNanogallery2LightboxActive() { // Check if the body has the 'nGY2On' class, adjust this based on actual behavior - return $('body').hasClass('nGY2_body_scrollbar'); + return document.body.classList.contains('nGY2_body_scrollbar'); } if (event.key == 'ArrowLeft') { //swipes left @@ -986,6 +997,7 @@ export function initRossMods() { !isInputElementInFocus() ) { $('.swipe_left:last').click(); + return; } } if (event.key == 'ArrowRight') { //swipes right @@ -998,13 +1010,14 @@ export function initRossMods() { !isInputElementInFocus() ) { $('.swipe_right:last').click(); + return; } } if (event.ctrlKey && event.key == 'ArrowUp') { //edits last USER message if chatbar is empty and focused if ( - $('#send_textarea').val() === '' && + hotkeyTargets['send_textarea'].value === '' && chatbarInFocus === true && ($('.swipe_right:last').css('display') === 'flex' || $('.last_mes').attr('is_system') === 'true') && $('#character_popup').css('display') === 'none' && @@ -1015,6 +1028,7 @@ export function initRossMods() { const editMes = lastIsUserMes.querySelector('.mes_block .mes_edit'); if (editMes !== null) { $(editMes).trigger('click'); + return; } } } @@ -1022,7 +1036,7 @@ export function initRossMods() { if (event.key == 'ArrowUp') { //edits last message if chatbar is empty and focused console.log('got uparrow input'); if ( - $('#send_textarea').val() === '' && + hotkeyTargets['send_textarea'].value === '' && chatbarInFocus === true && //$('.swipe_right:last').css('display') === 'flex' && $('.last_mes .mes_buttons').is(':visible') && @@ -1033,6 +1047,7 @@ export function initRossMods() { const editMes = lastMes.querySelector('.mes_block .mes_edit'); if (editMes !== null) { $(editMes).click(); + return; } } } From 69d195ef31563965f0357838cab4a8c7279562e4 Mon Sep 17 00:00:00 2001 From: LenAnderson Date: Tue, 26 Mar 2024 12:11:00 -0400 Subject: [PATCH 03/27] improve send_textarea autofit performance - only expand immediately - shrink debounced --- public/scripts/RossAscends-mods.js | 38 +++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/public/scripts/RossAscends-mods.js b/public/scripts/RossAscends-mods.js index ee6402ae4..f7d2e5139 100644 --- a/public/scripts/RossAscends-mods.js +++ b/public/scripts/RossAscends-mods.js @@ -658,6 +658,28 @@ export async function initMovingUI() { } } +/**@type {HTMLTextAreaElement} */ +const sendTextArea = document.querySelector('#send_textarea'); +/** + * this makes the chat input text area resize vertically to match the text size (limited by CSS at 50% window height) + */ +function autoFitSendTextArea() { + const isFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1; + /**@type {HTMLDivElement} */ + const chatBlock = document.querySelector('#chat'); + const originalScrollBottom = chatBlock.scrollHeight - (chatBlock.scrollTop + chatBlock.offsetHeight); + if (sendTextArea.scrollHeight == sendTextArea.offsetHeight) { + sendTextArea.style.height = window.getComputedStyle(sendTextArea).getPropertyValue('min-height'); + } + sendTextArea.style.height = sendTextArea.scrollHeight + 0.3 + 'px'; + + if (!isFirefox) { + const newScrollTop = Math.round(chatBlock.scrollHeight - (chatBlock.offsetHeight + originalScrollBottom)); + chatBlock.scrollTop = newScrollTop; + } +} +export const autoFitSendTextAreaDebounced = debounce(autoFitSendTextArea); + // --------------------------------------------------- export function initRossMods() { @@ -820,17 +842,11 @@ export function initRossMods() { saveSettingsDebounced(); }); - //this makes the chat input text area resize vertically to match the text size (limited by CSS at 50% window height) - $('#send_textarea').on('input', function () { - const isFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1; - const chatBlock = $('#chat'); - const originalScrollBottom = chatBlock[0].scrollHeight - (chatBlock.scrollTop() + chatBlock.outerHeight()); - this.style.height = window.getComputedStyle(this).getPropertyValue('min-height'); - this.style.height = this.scrollHeight + 0.3 + 'px'; - - if (!isFirefox) { - const newScrollTop = Math.round(chatBlock[0].scrollHeight - (chatBlock.outerHeight() + originalScrollBottom)); - chatBlock.scrollTop(newScrollTop); + sendTextArea.addEventListener('input', ()=>{ + if (sendTextArea.scrollHeight > sendTextArea.offsetHeight) { + autoFitSendTextArea(); + } else { + autoFitSendTextAreaDebounced(); } saveUserInput(); }); From d9022db7d9b0ab76d11e4bd07d14621f3e12f835 Mon Sep 17 00:00:00 2001 From: LenAnderson Date: Tue, 26 Mar 2024 12:11:15 -0400 Subject: [PATCH 04/27] debounce saving of user input in send_textarea --- public/scripts/RossAscends-mods.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/public/scripts/RossAscends-mods.js b/public/scripts/RossAscends-mods.js index f7d2e5139..9f73494c9 100644 --- a/public/scripts/RossAscends-mods.js +++ b/public/scripts/RossAscends-mods.js @@ -399,6 +399,7 @@ function saveUserInput() { const userInput = String($('#send_textarea').val()); SaveLocal('userInput', userInput); } +const saveUserInputDebounced = debounce(saveUserInput); // Make the DIV element draggable: @@ -848,7 +849,7 @@ export function initRossMods() { } else { autoFitSendTextAreaDebounced(); } - saveUserInput(); + saveUserInputDebounced(); }); restoreUserInput(); From 3debc063727429d5fef3518d0cf99230c1341347 Mon Sep 17 00:00:00 2001 From: LenAnderson Date: Tue, 26 Mar 2024 12:32:23 -0400 Subject: [PATCH 05/27] fix for jQuery input event not triggering real input event --- public/scripts/RossAscends-mods.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/scripts/RossAscends-mods.js b/public/scripts/RossAscends-mods.js index 9f73494c9..aaf9d5233 100644 --- a/public/scripts/RossAscends-mods.js +++ b/public/scripts/RossAscends-mods.js @@ -843,7 +843,7 @@ export function initRossMods() { saveSettingsDebounced(); }); - sendTextArea.addEventListener('input', ()=>{ + $(sendTextArea).on('input', ()=>{ if (sendTextArea.scrollHeight > sendTextArea.offsetHeight) { autoFitSendTextArea(); } else { From 6a51855f193955925e295cf05574d46532f364b5 Mon Sep 17 00:00:00 2001 From: based Date: Wed, 27 Mar 2024 13:52:51 +1000 Subject: [PATCH 06/27] Update Makersuite models --- public/index.html | 19 ++++++++++++++----- public/scripts/openai.js | 25 +++++++++++++++---------- src/prompt-converters.js | 8 +++++++- 3 files changed, 36 insertions(+), 16 deletions(-) diff --git a/public/index.html b/public/index.html index 751cce6b3..baf5c2d3d 100644 --- a/public/index.html +++ b/public/index.html @@ -2604,11 +2604,20 @@

Google Model

diff --git a/public/scripts/openai.js b/public/scripts/openai.js index 5acecfe7d..3a722dc97 100644 --- a/public/scripts/openai.js +++ b/public/scripts/openai.js @@ -3507,11 +3507,11 @@ async function onModelChange() { if (oai_settings.chat_completion_source == chat_completion_sources.MAKERSUITE) { if (oai_settings.max_context_unlocked) { $('#openai_max_context').attr('max', unlocked_max); - } else if (value === 'gemini-1.5-pro') { + } else if (value === 'gemini-1.5-pro-latest') { $('#openai_max_context').attr('max', max_1mil); - } else if (value === 'gemini-pro') { + } else if (value === 'gemini-ultra' || value === 'gemini-1.0-pro-latest' || value === 'gemini-pro' || value === 'gemini-1.0-ultra-latest') { $('#openai_max_context').attr('max', max_32k); - } else if (value === 'gemini-pro-vision') { + } else if (value === 'gemini-1.0-pro-vision-latest' || value === 'gemini-pro-vision') { $('#openai_max_context').attr('max', max_16k); } else { $('#openai_max_context').attr('max', max_8k); @@ -3939,21 +3939,26 @@ export function isImageInliningSupported() { return false; } - const gpt4v = 'gpt-4-vision'; - const geminiProV = 'gemini-pro-vision'; - const claude = 'claude-3'; - if (!oai_settings.image_inlining) { return false; } + // gultra just isn't being offered as multimodal, thanks google. + const visionSupportedModels = [ + 'gpt-4-vision', + 'gemini-1.0-pro-vision-latest', + 'gemini-1.5-pro-latest', + 'gemini-pro-vision', + 'claude-3' + ]; + switch (oai_settings.chat_completion_source) { case chat_completion_sources.OPENAI: - return oai_settings.openai_model.includes(gpt4v); + return visionSupportedModels.some(model => oai_settings.openai_model.includes(model)); case chat_completion_sources.MAKERSUITE: - return oai_settings.google_model.includes(geminiProV); + return visionSupportedModels.some(model => oai_settings.google_model.includes(model)); case chat_completion_sources.CLAUDE: - return oai_settings.claude_model.includes(claude); + return visionSupportedModels.some(model => oai_settings.claude_model.includes(model)); case chat_completion_sources.OPENROUTER: return !oai_settings.openrouter_force_instruct; case chat_completion_sources.CUSTOM: diff --git a/src/prompt-converters.js b/src/prompt-converters.js index 52161b661..3648fa1f8 100644 --- a/src/prompt-converters.js +++ b/src/prompt-converters.js @@ -196,7 +196,13 @@ function convertGooglePrompt(messages, model) { let lastRole = ''; let currentText = ''; - const isMultimodal = model === 'gemini-pro-vision'; + const visionSupportedModels = [ + 'gemini-1.0-pro-vision-latest', + 'gemini-1.5-pro-latest', + 'gemini-pro-vision', + ]; + + const isMultimodal = visionSupportedModels.includes(model); if (isMultimodal) { const combinedText = messages.map((message) => { From 9bd3a526aacf894e113584b27109b7d8e58077a3 Mon Sep 17 00:00:00 2001 From: Alexander Abushady <44341163+AAbushady@users.noreply.github.com> Date: Tue, 26 Mar 2024 23:57:24 -0400 Subject: [PATCH 07/27] Fix for unique swipes Fix for unique swipes in Aphrodite --- public/scripts/textgen-settings.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/scripts/textgen-settings.js b/public/scripts/textgen-settings.js index b16b67173..515355e59 100644 --- a/public/scripts/textgen-settings.js +++ b/public/scripts/textgen-settings.js @@ -972,7 +972,6 @@ export function getTextGenGenerationData(finalPrompt, maxTokens, isImpersonate, 'typical_p': settings.typical_p, 'typical': settings.typical_p, 'sampler_seed': settings.seed, - 'seed': settings.seed, 'min_p': settings.min_p, 'repetition_penalty': settings.rep_pen, 'frequency_penalty': settings.freq_pen, @@ -1024,6 +1023,7 @@ export function getTextGenGenerationData(finalPrompt, maxTokens, isImpersonate, 'penalty_alpha': settings.type === OOBA ? settings.penalty_alpha : undefined, 'temperature_last': (settings.type === OOBA || settings.type === APHRODITE || settings.type == TABBY) ? settings.temperature_last : undefined, 'do_sample': settings.type === OOBA ? settings.do_sample : undefined, + 'seed': settings.seed, 'guidance_scale': cfgValues?.guidanceScale?.value ?? settings.guidance_scale ?? 1, 'negative_prompt': cfgValues?.negativePrompt ?? substituteParams(settings.negative_prompt) ?? '', 'grammar_string': settings.grammar_string, From f3b9920f227780f091d2cd57526e78c4544b885d Mon Sep 17 00:00:00 2001 From: based Date: Wed, 27 Mar 2024 15:48:26 +1000 Subject: [PATCH 08/27] actually convert the prompts properly --- src/prompt-converters.js | 94 +++++++++++++++++++++++----------------- 1 file changed, 54 insertions(+), 40 deletions(-) diff --git a/src/prompt-converters.js b/src/prompt-converters.js index 3648fa1f8..0b5af8f80 100644 --- a/src/prompt-converters.js +++ b/src/prompt-converters.js @@ -192,9 +192,6 @@ function convertClaudeMessages(messages, prefillString, useSysPrompt, humanMsgFi function convertGooglePrompt(messages, model) { // This is a 1x1 transparent PNG const PNG_PIXEL = 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII='; - const contents = []; - let lastRole = ''; - let currentText = ''; const visionSupportedModels = [ 'gemini-1.0-pro-vision-latest', @@ -203,48 +200,65 @@ function convertGooglePrompt(messages, model) { ]; const isMultimodal = visionSupportedModels.includes(model); + let hasImage = false; - if (isMultimodal) { - const combinedText = messages.map((message) => { - const role = message.role === 'assistant' ? 'MODEL: ' : 'USER: '; - return role + message.content; - }).join('\n\n').trim(); + const contents = []; + messages.forEach((message, index) => { + // fix the roles + if (message.role === 'system') { + message.role = 'user'; + } else if (message.role === 'assistant') { + message.role = 'model'; + } - const imageEntry = messages.find((message) => message.content?.[1]?.image_url); - const imageData = imageEntry?.content?.[1]?.image_url?.data ?? PNG_PIXEL; - contents.push({ - parts: [ - { text: combinedText }, - { - inlineData: { - mimeType: 'image/png', - data: imageData, - }, - }, - ], - role: 'user', - }); - } else { - messages.forEach((message, index) => { - const role = message.role === 'assistant' ? 'model' : 'user'; - if (lastRole === role) { - currentText += '\n\n' + message.content; + // similar story as claude + if (message.name) { + if (Array.isArray(message.content)) { + message.content[0].text = `${message.name}: ${message.content[0].text}`; } else { - if (currentText !== '') { - contents.push({ - parts: [{ text: currentText.trim() }], - role: lastRole, + message.content = `${message.name}: ${message.content}`; + } + delete message.name; + } + + //create the prompt parts + const parts = []; + if (typeof message.content === 'string') { + parts.push({ text: message.content }); + } else if (Array.isArray(message.content)) { + message.content.forEach((part) => { + if (part.type === 'text') { + parts.push({ text: part.text }); + } else if (part.type === 'image_url' && isMultimodal) { + parts.push({ + inlineData: { + mimeType: 'image/png', + data: part.image_url.url, + }, }); + hasImage = true; } - currentText = message.content; - lastRole = role; - } - if (index === messages.length - 1) { - contents.push({ - parts: [{ text: currentText.trim() }], - role: lastRole, - }); - } + }); + } + + // merge consecutive messages with the same role + if (index > 0 && message.role === contents[contents.length - 1].role) { + contents[contents.length - 1].parts[0].text += '\n\n' + parts[0].text; + } else { + contents.push({ + role: message.role, + parts: parts, + }); + } + }); + + // pro 1.5 doesn't require a dummy image to be attached, other vision models do + if (isMultimodal && model !== 'gemini-1.5-pro-latest' && !hasImage) { + contents[0].parts.push({ + inlineData: { + mimeType: 'image/png', + data: PNG_PIXEL, + }, }); } From 53848e1b0d97c60df7a38749d9590d6223300de6 Mon Sep 17 00:00:00 2001 From: Wolfsblvt Date: Wed, 27 Mar 2024 09:49:59 +0100 Subject: [PATCH 09/27] Persona retrigger first message consistency fix --- public/script.js | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/public/script.js b/public/script.js index 38ed270cd..61a139231 100644 --- a/public/script.js +++ b/public/script.js @@ -5720,6 +5720,9 @@ export function setUserName(value) { toastr.success(`Your messages will now be sent as ${name1}`, 'Current persona updated'); } saveSettingsDebounced(); + + // force firstMes {{user}} update on persona switch + retriggerFirstMessageOnEmptychat(); } /** @@ -5733,6 +5736,15 @@ export function setUserAvatar(imgfile) { selectCurrentPersona(); saveSettingsDebounced(); $('.zoomed_avatar[forchar]').remove(); + + // force firstMes {{user}} update on persona switch + retriggerFirstMessageOnEmptychat(); +} + +function retriggerFirstMessageOnEmptychat() { + if (this_chid >= 0 && !selected_group && chat.length === 1) { + $('#firstmessage_textarea').trigger('input'); + } } async function uploadUserAvatar(e) { @@ -8467,11 +8479,6 @@ jQuery(async function () { $(document).on('click', '#user_avatar_block .avatar-container', function () { const imgfile = $(this).attr('imgfile'); setUserAvatar(imgfile); - - // force firstMes {{user}} update on persona switch - if (this_chid >= 0 && !selected_group && chat.length === 1) { - $('#firstmessage_textarea').trigger('input'); - } }); $(document).on('click', '#user_avatar_block .avatar_upload', function () { $('#avatar_upload_overwrite').val(''); From 8c83095979a0e85250d8043d09f770303fd6d65c Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Wed, 27 Mar 2024 14:20:35 +0200 Subject: [PATCH 10/27] Fix npm audit --- package-lock.json | 58 ++++++++--------------------------------------- package.json | 2 +- 2 files changed, 10 insertions(+), 50 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0298605fe..8be17c4f4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,7 +21,7 @@ "cookie-parser": "^1.4.6", "cors": "^2.8.5", "csrf-csrf": "^2.2.3", - "express": "^4.18.2", + "express": "^4.19.2", "form-data": "^4.0.0", "google-translate-api-browser": "^3.0.1", "gpt3-tokenizer": "^1.1.5", @@ -1752,15 +1752,16 @@ "version": "0.1.12" }, "node_modules/express": { - "version": "4.18.2", - "license": "MIT", + "version": "4.19.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", + "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.20.1", + "body-parser": "1.20.2", "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.5.0", + "cookie": "0.6.0", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", @@ -1791,55 +1792,14 @@ "node": ">= 0.10.0" } }, - "node_modules/express/node_modules/body-parser": { - "version": "1.20.1", - "license": "MIT", - "dependencies": { - "bytes": "3.1.2", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.11.0", - "raw-body": "2.5.1", - "type-is": "~1.6.18", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/express/node_modules/bytes": { - "version": "3.1.2", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, "node_modules/express/node_modules/cookie": { - "version": "0.5.0", - "license": "MIT", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", "engines": { "node": ">= 0.6" } }, - "node_modules/express/node_modules/raw-body": { - "version": "2.5.1", - "license": "MIT", - "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/express/node_modules/safe-buffer": { "version": "5.2.1", "funding": [ diff --git a/package.json b/package.json index e8085c240..970b8efbc 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "cookie-parser": "^1.4.6", "cors": "^2.8.5", "csrf-csrf": "^2.2.3", - "express": "^4.18.2", + "express": "^4.19.2", "form-data": "^4.0.0", "google-translate-api-browser": "^3.0.1", "gpt3-tokenizer": "^1.1.5", From 71168f161c30f7aa668f9a1b31cd68b3dd18d398 Mon Sep 17 00:00:00 2001 From: deffcolony <61471128+deffcolony@users.noreply.github.com> Date: Wed, 27 Mar 2024 13:46:23 +0100 Subject: [PATCH 11/27] @echo off Its cleaner like UpdateAndStart.bat already has --- Start.bat | 1 + 1 file changed, 1 insertion(+) diff --git a/Start.bat b/Start.bat index 8bd3a7e07..8d1bfcdd7 100644 --- a/Start.bat +++ b/Start.bat @@ -1,3 +1,4 @@ +@echo off pushd %~dp0 set NODE_ENV=production call npm install --no-audit --no-fund --quiet --omit=dev From 6d02223bd49977216b87b94660360d8c1ded8f97 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Wed, 27 Mar 2024 15:16:20 +0200 Subject: [PATCH 12/27] Fix cases with persona autoswitch --- public/script.js | 12 +++++------- public/scripts/slash-commands.js | 3 +++ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/public/script.js b/public/script.js index 61a139231..e56f4ec33 100644 --- a/public/script.js +++ b/public/script.js @@ -5720,9 +5720,6 @@ export function setUserName(value) { toastr.success(`Your messages will now be sent as ${name1}`, 'Current persona updated'); } saveSettingsDebounced(); - - // force firstMes {{user}} update on persona switch - retriggerFirstMessageOnEmptychat(); } /** @@ -5736,12 +5733,9 @@ export function setUserAvatar(imgfile) { selectCurrentPersona(); saveSettingsDebounced(); $('.zoomed_avatar[forchar]').remove(); - - // force firstMes {{user}} update on persona switch - retriggerFirstMessageOnEmptychat(); } -function retriggerFirstMessageOnEmptychat() { +export function retriggerFirstMessageOnEmptyChat() { if (this_chid >= 0 && !selected_group && chat.length === 1) { $('#firstmessage_textarea').trigger('input'); } @@ -8479,6 +8473,9 @@ jQuery(async function () { $(document).on('click', '#user_avatar_block .avatar-container', function () { const imgfile = $(this).attr('imgfile'); setUserAvatar(imgfile); + + // force firstMes {{user}} update on persona switch + retriggerFirstMessageOnEmptyChat(); }); $(document).on('click', '#user_avatar_block .avatar_upload', function () { $('#avatar_upload_overwrite').val(''); @@ -9582,6 +9579,7 @@ jQuery(async function () { const userName = String($('#your_name').val()).trim(); setUserName(userName); await updatePersonaNameIfExists(user_avatar, userName); + retriggerFirstMessageOnEmptyChat(); }); $('#sync_name_button').on('click', async function () { diff --git a/public/scripts/slash-commands.js b/public/scripts/slash-commands.js index 76d4b11db..a6e0020ef 100644 --- a/public/scripts/slash-commands.js +++ b/public/scripts/slash-commands.js @@ -22,6 +22,7 @@ import { name1, reloadCurrentChat, removeMacros, + retriggerFirstMessageOnEmptyChat, saveChatConditional, sendMessageAsUser, sendSystemMessage, @@ -1340,12 +1341,14 @@ function setNameCallback(_, name) { for (let persona of Object.values(power_user.personas)) { if (persona.toLowerCase() === name.toLowerCase()) { autoSelectPersona(name); + retriggerFirstMessageOnEmptyChat(); return; } } // Otherwise, set just the name setUserName(name); //this prevented quickReply usage + retriggerFirstMessageOnEmptyChat(); } async function setNarratorName(_, text) { From 0bf6835de21a815b4d51c181d8272b437b10e9ff Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Wed, 27 Mar 2024 19:40:34 +0200 Subject: [PATCH 13/27] Add "compact" argument to message sending commands. --- public/script.js | 7 +++++-- public/scripts/slash-commands.js | 17 ++++++++++++----- public/style.css | 5 +++++ 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/public/script.js b/public/script.js index e56f4ec33..d223ef889 100644 --- a/public/script.js +++ b/public/script.js @@ -4178,9 +4178,10 @@ export function removeMacros(str) { * @param {string} messageText Message text. * @param {string} messageBias Message bias. * @param {number} [insertAt] Optional index to insert the message at. + * @params {boolean} [compact] Send as a compact display message. * @returns {Promise} A promise that resolves when the message is inserted. */ -export async function sendMessageAsUser(messageText, messageBias, insertAt = null) { +export async function sendMessageAsUser(messageText, messageBias, insertAt = null, compact = false) { messageText = getRegexedString(messageText, regex_placement.USER_INPUT); const message = { @@ -4189,7 +4190,9 @@ export async function sendMessageAsUser(messageText, messageBias, insertAt = nul is_system: false, send_date: getMessageTimeStamp(), mes: substituteParams(messageText), - extra: {}, + extra: { + isSmallSys: compact, + }, }; if (power_user.message_token_count_enabled) { diff --git a/public/scripts/slash-commands.js b/public/scripts/slash-commands.js index a6e0020ef..20f3e8666 100644 --- a/public/scripts/slash-commands.js +++ b/public/scripts/slash-commands.js @@ -204,10 +204,10 @@ parser.addCommand('name', setNameCallback, ['persona'], '(filename) – sets a background according to filename, partial names allowed', false, true); -parser.addCommand('sendas', sendMessageAs, [], ' – sends message as a specific character. Uses character avatar if it exists in the characters list. Example that will send "Hello, guys!" from "Chloe": /sendas name="Chloe" Hello, guys!', true, true); -parser.addCommand('sys', sendNarratorMessage, ['nar'], '(text) – sends message as a system narrator', false, true); +parser.addCommand('sendas', sendMessageAs, [], '[name=CharName compact=true/false (text)] – sends message as a specific character. Uses character avatar if it exists in the characters list. Example that will send "Hello, guys!" from "Chloe": /sendas name="Chloe" Hello, guys!. If "compact" is set to true, the message is sent using a compact layout.', true, true); +parser.addCommand('sys', sendNarratorMessage, ['nar'], '[compact=true/false (text)] – sends message as a system narrator. If "compact" is set to true, the message is sent using a compact layout.', false, true); parser.addCommand('sysname', setNarratorName, [], '(name) – sets a name for future system narrator messages in this chat (display only). Default: System. Leave empty to reset.', true, true); -parser.addCommand('comment', sendCommentMessage, [], '(text) – adds a note/comment message not part of the chat', false, true); +parser.addCommand('comment', sendCommentMessage, [], '[compact=true/false (text)] – adds a note/comment message not part of the chat. If "compact" is set to true, the message is sent using a compact layout.', false, true); parser.addCommand('single', setStoryModeCallback, ['story'], ' – sets the message style to single document mode without names or avatars visible', true, true); parser.addCommand('bubble', setBubbleModeCallback, ['bubbles'], ' – sets the message style to bubble chat mode', true, true); parser.addCommand('flat', setFlatModeCallback, ['default'], ' – sets the message style to flat chat mode', true, true); @@ -216,7 +216,7 @@ parser.addCommand('go', goToCharacterCallback, ['char'], '(prompt) – generates a system message using a specified prompt', true, true); parser.addCommand('ask', askCharacter, [], '(prompt) – asks a specified character card a prompt', true, true); parser.addCommand('delname', deleteMessagesByNameCallback, ['cancel'], '(name) – deletes all messages attributed to a specified name', true, true); -parser.addCommand('send', sendUserMessageCallback, [], '(text) – adds a user message to the chat log without triggering a generation', true, true); +parser.addCommand('send', sendUserMessageCallback, [], '[compact=true/false (text)] – adds a user message to the chat log without triggering a generation. If "compact" is set to true, the message is sent using a compact layout.', true, true); parser.addCommand('trigger', triggerGenerationCallback, [], ' await=true/false – triggers a message generation. If in group, can trigger a message for the specified group member index or name. If await=true named argument passed, the command will await for the triggered generation before continuing.', true, true); parser.addCommand('hide', hideMessageCallback, [], '(message index or range) – hides a chat message from the prompt', true, true); parser.addCommand('unhide', unhideMessageCallback, [], '(message index or range) – unhides a message from the prompt', true, true); @@ -1176,9 +1176,10 @@ async function sendUserMessageCallback(args, text) { } text = text.trim(); + const compact = isTrueBoolean(args?.compact); const bias = extractMessageBias(text); const insertAt = Number(resolveVariable(args?.at)); - await sendMessageAsUser(text, bias, insertAt); + await sendMessageAsUser(text, bias, insertAt, compact); return ''; } @@ -1391,6 +1392,7 @@ export async function sendMessageAs(args, text) { // Messages that do nothing but set bias will be hidden from the context const bias = extractMessageBias(mesText); const isSystem = bias && !removeMacros(mesText).length; + const compact = isTrueBoolean(args?.compact); const character = characters.find(x => x.name === name); let force_avatar, original_avatar; @@ -1415,6 +1417,7 @@ export async function sendMessageAs(args, text) { extra: { bias: bias.trim().length ? bias : null, gen_id: Date.now(), + isSmallSys: compact, }, }; @@ -1444,6 +1447,7 @@ export async function sendNarratorMessage(args, text) { // Messages that do nothing but set bias will be hidden from the context const bias = extractMessageBias(text); const isSystem = bias && !removeMacros(text).length; + const compact = isTrueBoolean(args?.compact); const message = { name: name, @@ -1456,6 +1460,7 @@ export async function sendNarratorMessage(args, text) { type: system_message_types.NARRATOR, bias: bias.trim().length ? bias : null, gen_id: Date.now(), + isSmallSys: compact, }, }; @@ -1520,6 +1525,7 @@ async function sendCommentMessage(args, text) { return; } + const compact = isTrueBoolean(args?.compact); const message = { name: COMMENT_NAME_DEFAULT, is_user: false, @@ -1530,6 +1536,7 @@ async function sendCommentMessage(args, text) { extra: { type: system_message_types.COMMENT, gen_id: Date.now(), + isSmallSys: compact, }, }; diff --git a/public/style.css b/public/style.css index 91932cb47..cacdc2679 100644 --- a/public/style.css +++ b/public/style.css @@ -382,6 +382,11 @@ small { text-align: center; } +.mes.smallSysMes .swipe_right, +.mes.smallSysMes .swipe_left { + display: none !important; +} + .mes.smallSysMes .mes_text { padding: 0 !important; text-align: center; From 45bd8c18ed9386091d832a15ca5d06b9d4796354 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Wed, 27 Mar 2024 19:49:14 +0200 Subject: [PATCH 14/27] Remove last paragraph margin for compact layout --- public/style.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/public/style.css b/public/style.css index cacdc2679..3f54d5cec 100644 --- a/public/style.css +++ b/public/style.css @@ -382,6 +382,10 @@ small { text-align: center; } +.mes.smallSysMes .mes_text p:last-child { + margin: 0; +} + .mes.smallSysMes .swipe_right, .mes.smallSysMes .swipe_left { display: none !important; From 9bd1d79f086d70127e657f684fd93d35849a872e Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Wed, 27 Mar 2024 20:43:40 +0200 Subject: [PATCH 15/27] Save a backup before deleting from new chat menu --- public/script.js | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/public/script.js b/public/script.js index d223ef889..80d19eeb4 100644 --- a/public/script.js +++ b/public/script.js @@ -8603,8 +8603,13 @@ jQuery(async function () { await clearChat(); chat.length = 0; - chat_file_for_del = getCurrentChatDetails().sessionName; - const isDelChatCheckbox = document.getElementById('del_chat_checkbox').checked; + chat_file_for_del = getCurrentChatDetails()?.sessionName; + const isDelChatCheckbox = document.getElementById('del_chat_checkbox')?.checked; + + // Make it easier to find in backups + if (isDelChatCheckbox) { + await saveChatConditional(); + } if (selected_group) { //Fix it; When you're creating a new group chat (but not when initially converting from the existing regular chat), the first greeting message doesn't automatically get translated. @@ -8670,14 +8675,13 @@ jQuery(async function () { $('#form_create').submit(createOrEditCharacter); $('#delete_button').on('click', function () { - popup_type = 'del_ch'; callPopup(`

Delete the character?

THIS IS PERMANENT!


`, + Also delete the chat files +
`, 'del_ch', '', ); }); @@ -8985,7 +8989,7 @@ jQuery(async function () {
`, 'new_chat', ''); } From 5264e2b19492f8cebfcf1070705b264f35f9d519 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Wed, 27 Mar 2024 20:44:04 +0200 Subject: [PATCH 16/27] Emit events when a new group chat is created --- public/script.js | 1 - public/scripts/group-chats.js | 4 ++++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/public/script.js b/public/script.js index 80d19eeb4..ba787581b 100644 --- a/public/script.js +++ b/public/script.js @@ -8612,7 +8612,6 @@ jQuery(async function () { } if (selected_group) { - //Fix it; When you're creating a new group chat (but not when initially converting from the existing regular chat), the first greeting message doesn't automatically get translated. await createNewGroupChat(selected_group); if (isDelChatCheckbox) await deleteGroupChat(selected_group, chat_file_for_del); } diff --git a/public/scripts/group-chats.js b/public/scripts/group-chats.js index 57da890ee..27708edc5 100644 --- a/public/scripts/group-chats.js +++ b/public/scripts/group-chats.js @@ -189,6 +189,8 @@ export async function getGroupChat(groupId) { await printMessages(); } else { sendSystemMessage(system_message_types.GROUP, '', { isSmallSys: true }); + await eventSource.emit(event_types.MESSAGE_RECEIVED, (chat.length - 1)); + await eventSource.emit(event_types.CHARACTER_MESSAGE_RENDERED, (chat.length - 1)); if (group && Array.isArray(group.members)) { for (let member of group.members) { const character = characters.find(x => x.avatar === member || x.name === member); @@ -199,7 +201,9 @@ export async function getGroupChat(groupId) { const mes = await getFirstCharacterMessage(character); chat.push(mes); + await eventSource.emit(event_types.MESSAGE_RECEIVED, (chat.length - 1)); addOneMessage(mes); + await eventSource.emit(event_types.CHARACTER_MESSAGE_RENDERED, (chat.length - 1)); } } await saveGroupChat(groupId, false); From 55d855b65584e7dd4053dfe94ee82f518904bd6a Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Wed, 27 Mar 2024 22:18:20 +0200 Subject: [PATCH 17/27] Autofit immediately if input value is empty --- public/scripts/RossAscends-mods.js | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/public/scripts/RossAscends-mods.js b/public/scripts/RossAscends-mods.js index aaf9d5233..bae2ff6ea 100644 --- a/public/scripts/RossAscends-mods.js +++ b/public/scripts/RossAscends-mods.js @@ -661,16 +661,18 @@ export async function initMovingUI() { /**@type {HTMLTextAreaElement} */ const sendTextArea = document.querySelector('#send_textarea'); +const chatBlock = document.getElementById('chat'); +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() { - const isFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1; - /**@type {HTMLDivElement} */ - const chatBlock = document.querySelector('#chat'); const originalScrollBottom = chatBlock.scrollHeight - (chatBlock.scrollTop + chatBlock.offsetHeight); if (sendTextArea.scrollHeight == sendTextArea.offsetHeight) { - sendTextArea.style.height = window.getComputedStyle(sendTextArea).getPropertyValue('min-height'); + // Needs to be pulled dynamically because it is affected by font size changes + const sendTextAreaMinHeight = window.getComputedStyle(sendTextArea).getPropertyValue('min-height'); + sendTextArea.style.height = sendTextAreaMinHeight; } sendTextArea.style.height = sendTextArea.scrollHeight + 0.3 + 'px'; @@ -843,8 +845,8 @@ export function initRossMods() { saveSettingsDebounced(); }); - $(sendTextArea).on('input', ()=>{ - if (sendTextArea.scrollHeight > sendTextArea.offsetHeight) { + $(sendTextArea).on('input', () => { + if (sendTextArea.scrollHeight > sendTextArea.offsetHeight || sendTextArea.value === '') { autoFitSendTextArea(); } else { autoFitSendTextAreaDebounced(); From 11b3162aa422b07b4870824699228f6e37888d7a Mon Sep 17 00:00:00 2001 From: Wolfsblvt Date: Wed, 27 Mar 2024 22:11:19 +0100 Subject: [PATCH 18/27] Include swipeid to all messages in chat --- public/script.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/public/script.js b/public/script.js index 38ed270cd..77b5728f6 100644 --- a/public/script.js +++ b/public/script.js @@ -1855,6 +1855,7 @@ function insertSVGIcon(mes, extra) { function getMessageFromTemplate({ mesId, + swipeId, characterName, isUser, avatarImg, @@ -1872,6 +1873,7 @@ function getMessageFromTemplate({ const mes = messageTemplate.clone(); mes.attr({ 'mesid': mesId, + 'swipeid': swipeId, 'ch_name': characterName, 'is_user': isUser, 'is_system': !!isSystem, @@ -2018,6 +2020,7 @@ function addOneMessage(mes, { type = 'normal', insertAfter = null, scroll = true let params = { mesId: forceId ?? chat.length - 1, + swipeId: mes.swipes?.indexOf(mes.mes) ?? mes.swipe_id ?? 0, characterName: mes.name, isUser: mes.is_user, avatarImg: avatarImg, From b8c6e6c85c92b6a0487c8b88f7dfe2b180d877a6 Mon Sep 17 00:00:00 2001 From: Wolfsblvt Date: Wed, 27 Mar 2024 23:12:40 +0100 Subject: [PATCH 19/27] Remove unnecessary swipe id check --- public/script.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/script.js b/public/script.js index 77b5728f6..c86106b8f 100644 --- a/public/script.js +++ b/public/script.js @@ -2020,7 +2020,7 @@ function addOneMessage(mes, { type = 'normal', insertAfter = null, scroll = true let params = { mesId: forceId ?? chat.length - 1, - swipeId: mes.swipes?.indexOf(mes.mes) ?? mes.swipe_id ?? 0, + swipeId: mes.swipe_id ?? 0, characterName: mes.name, isUser: mes.is_user, avatarImg: avatarImg, From 6ed604593c084a5b4e642cf3a27f9dc2486324a9 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Thu, 28 Mar 2024 01:13:54 +0200 Subject: [PATCH 20/27] Copy tags on duplicating --- public/script.js | 5 +++++ public/scripts/BulkEditOverlay.js | 14 +++++++++++--- public/scripts/tags.js | 22 +++++++++++++++++----- src/endpoints/characters.js | 2 +- 4 files changed, 34 insertions(+), 9 deletions(-) diff --git a/public/script.js b/public/script.js index 59e7995f1..d904d1ab7 100644 --- a/public/script.js +++ b/public/script.js @@ -172,6 +172,7 @@ import { importTags, tag_filter_types, compareTagsForSort, + initTags, } from './scripts/tags.js'; import { SECRET_KEYS, @@ -412,6 +413,7 @@ export const event_types = { CHARACTER_FIRST_MESSAGE_SELECTED: 'character_first_message_selected', // TODO: Naming convention is inconsistent with other events CHARACTER_DELETED: 'characterDeleted', + CHARACTER_DUPLICATED: 'character_duplicated', }; export const eventSource = new EventEmitter(); @@ -864,6 +866,7 @@ async function firstLoadInit() { getSystemMessages(); sendSystemMessage(system_message_types.WELCOME); initLocales(); + initTags(); await getUserAvatars(true, user_avatar); await getCharacters(); await getBackgrounds(); @@ -4323,6 +4326,8 @@ async function DupeChar() { }); if (response.ok) { toastr.success('Character Duplicated'); + const data = await response.json(); + await eventSource.emit(event_types.CHARACTER_DUPLICATED, { oldAvatar: body.avatar_url, newAvatar: data.path }); getCharacters(); } } diff --git a/public/scripts/BulkEditOverlay.js b/public/scripts/BulkEditOverlay.js index eb69f279b..d8f2f17cb 100644 --- a/public/scripts/BulkEditOverlay.js +++ b/public/scripts/BulkEditOverlay.js @@ -48,16 +48,24 @@ class CharacterContextMenu { * Duplicate one or more characters * * @param characterId - * @returns {Promise} + * @returns {Promise} */ static duplicate = async (characterId) => { const character = CharacterContextMenu.#getCharacter(characterId); + const body = { avatar_url: character.avatar }; - return fetch('/api/characters/duplicate', { + const result = await fetch('/api/characters/duplicate', { method: 'POST', headers: getRequestHeaders(), - body: JSON.stringify({ avatar_url: character.avatar }), + body: JSON.stringify(), }); + + if (!result.ok) { + throw new Error('Character not duplicated'); + } + + const data = await result.json(); + await eventSource.emit(event_types.CHARACTER_DUPLICATED, { oldAvatar: body.avatar_url, newAvatar: data.path }); }; /** diff --git a/public/scripts/tags.js b/public/scripts/tags.js index 2615d87b3..a995a52f2 100644 --- a/public/scripts/tags.js +++ b/public/scripts/tags.js @@ -8,6 +8,8 @@ import { entitiesFilter, printCharacters, buildAvatarList, + eventSource, + event_types, } from '../script.js'; // eslint-disable-next-line no-unused-vars import { FILTER_TYPES, FILTER_STATES, isFilterState, FilterHelper } from './filters.js'; @@ -132,7 +134,7 @@ function filterByTagState(entities, { globalDisplayFilters = false, subForEntity } if (subForEntity !== undefined && subForEntity.type === 'tag') { - entities = filterTagSubEntities(subForEntity.item, entities, { filterHidden : filterHidden }); + entities = filterTagSubEntities(subForEntity.item, entities, { filterHidden: filterHidden }); } return entities; @@ -1140,7 +1142,7 @@ function onClearAllFiltersClick() { // We have to manually go through the elements and unfilter by clicking... // Thankfully nearly all filter controls are three-state-toggles const filterTags = $('.rm_tag_controls .rm_tag_filter').find('.tag'); - for(const tag of filterTags) { + for (const tag of filterTags) { const toggleState = $(tag).attr('data-toggle-state'); if (toggleState !== undefined && !isFilterState(toggleState ?? FILTER_STATES.UNDEFINED, FILTER_STATES.UNDEFINED)) { toggleTagThreeState($(tag), { stateOverride: FILTER_STATES.UNDEFINED, simulateClick: true }); @@ -1151,7 +1153,17 @@ function onClearAllFiltersClick() { $('#character_search_bar').val('').trigger('input'); } -jQuery(() => { +/** + * Copy tags from one character to another. + * @param {{oldAvatar: string, newAvatar: string}} data Event data + */ +function copyTags(data) { + const prevTagMap = tag_map[data.oldAvatar] || []; + const newTagMap = tag_map[data.newAvatar] || []; + tag_map[data.newAvatar] = Array.from(new Set([...prevTagMap, ...newTagMap])); +} + +export function initTags() { createTagInput('#tagInput', '#tagList'); createTagInput('#groupTagInput', '#groupTagList'); @@ -1168,5 +1180,5 @@ jQuery(() => { $(document).on('click', '.tag_view_create', onTagCreateClick); $(document).on('click', '.tag_view_backup', onTagsBackupClick); $(document).on('click', '.tag_view_restore', onBackupRestoreClick); -}); - + eventSource.on(event_types.CHARACTER_DUPLICATED, copyTags); +} diff --git a/src/endpoints/characters.js b/src/endpoints/characters.js index e2063def3..613f88c79 100644 --- a/src/endpoints/characters.js +++ b/src/endpoints/characters.js @@ -1008,7 +1008,7 @@ router.post('/duplicate', jsonParser, async function (request, response) { fs.copyFileSync(filename, newFilename); console.log(`${filename} was copied to ${newFilename}`); - response.sendStatus(200); + response.send({ path: path.parse(newFilename).base }); } catch (error) { console.error(error); From 6ca2111ef61e2458cf9d5dfed9d1ebcfd864b3c5 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Thu, 28 Mar 2024 01:15:14 +0200 Subject: [PATCH 21/27] Honey I broke mass duplicator --- public/scripts/BulkEditOverlay.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/scripts/BulkEditOverlay.js b/public/scripts/BulkEditOverlay.js index d8f2f17cb..50a2ba478 100644 --- a/public/scripts/BulkEditOverlay.js +++ b/public/scripts/BulkEditOverlay.js @@ -57,7 +57,7 @@ class CharacterContextMenu { const result = await fetch('/api/characters/duplicate', { method: 'POST', headers: getRequestHeaders(), - body: JSON.stringify(), + body: JSON.stringify(body), }); if (!result.ok) { From 03d255442e921109a196933eca063a779fe8b2c4 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Thu, 28 Mar 2024 01:17:43 +0200 Subject: [PATCH 22/27] Return display avatar name on hover --- public/script.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/script.js b/public/script.js index d904d1ab7..5025242eb 100644 --- a/public/script.js +++ b/public/script.js @@ -1240,7 +1240,7 @@ function getCharacterBlock(item, id) { const template = $('#character_template .character_select').clone(); template.attr({ 'chid': id, 'id': `CharID${id}` }); template.find('img').attr('src', this_avatar).attr('alt', item.name); - template.find('.avatar').attr('title', `[Character] ${item.name}`); + template.find('.avatar').attr('title', `[Character] ${item.name}\n${item.avatar}`); template.find('.ch_name').text(item.name).attr('title', `[Character] ${item.name}`); if (power_user.show_card_avatar_urls) { template.find('.ch_avatar_url').text(item.avatar); From 945e3e3b0ea98f0f21a20c23e275ebea4359f655 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Thu, 28 Mar 2024 01:27:28 +0200 Subject: [PATCH 23/27] Add prefix to avatar tooltip --- public/script.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/script.js b/public/script.js index 5025242eb..36539cc9a 100644 --- a/public/script.js +++ b/public/script.js @@ -1240,7 +1240,7 @@ function getCharacterBlock(item, id) { const template = $('#character_template .character_select').clone(); template.attr({ 'chid': id, 'id': `CharID${id}` }); template.find('img').attr('src', this_avatar).attr('alt', item.name); - template.find('.avatar').attr('title', `[Character] ${item.name}\n${item.avatar}`); + template.find('.avatar').attr('title', `[Character] ${item.name}\nFile: ${item.avatar}`); template.find('.ch_name').text(item.name).attr('title', `[Character] ${item.name}`); if (power_user.show_card_avatar_urls) { template.find('.ch_avatar_url').text(item.avatar); From 4a71bda1dcbf92679b1dcfbb4cb0ebdf9ca7547d Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Thu, 28 Mar 2024 01:28:41 +0200 Subject: [PATCH 24/27] Bit tighter character panel layout --- public/index.html | 8 ++++---- public/style.css | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/public/index.html b/public/index.html index baf5c2d3d..03fd07a4c 100644 --- a/public/index.html +++ b/public/index.html @@ -4110,7 +4110,7 @@
- +
@@ -4130,7 +4130,7 @@

-
-
+
First message @@ -4193,7 +4193,7 @@
- +
diff --git a/public/style.css b/public/style.css index 3f54d5cec..6e000651d 100644 --- a/public/style.css +++ b/public/style.css @@ -1416,7 +1416,7 @@ input[type="file"] { } .extension_token_counter { - font-size: calc(var(--mainFontSize) * 0.9); + font-size: calc(var(--mainFontSize) * 0.875); width: 100%; text-align: right; margin-bottom: 5px; From 6e411b06b9b823af79a1874439a4221836846359 Mon Sep 17 00:00:00 2001 From: P3il4 <42489293+P3il4@users.noreply.github.com> Date: Thu, 28 Mar 2024 03:32:57 +0300 Subject: [PATCH 25/27] Fix prompt cache after API error --- public/script.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/public/script.js b/public/script.js index 36539cc9a..97e30e42f 100644 --- a/public/script.js +++ b/public/script.js @@ -3973,6 +3973,8 @@ async function Generate(type, { automatic_trigger, force_name2, quiet_prompt, qu toastr.error(exception.error.message, 'Error', { timeOut: 10000, extendedTimeOut: 20000 }); } + generatedPromptCache = ''; + unblockGeneration(); console.log(exception); streamingProcessor = null; From 3e49c9d02c6e9a0537a941c6f6c21ef554e10b89 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Thu, 28 Mar 2024 14:47:24 +0200 Subject: [PATCH 26/27] &rcub => } --- public/scripts/templates/macros.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/scripts/templates/macros.html b/public/scripts/templates/macros.html index 34b768ac1..a2e1ff009 100644 --- a/public/scripts/templates/macros.html +++ b/public/scripts/templates/macros.html @@ -33,7 +33,7 @@
  • {{time_UTC±#}} – the current time in the specified UTC time zone offset, e.g. UTC-4 or UTC+2
  • {{idle_duration}} – the time since the last user message was sent
  • {{bias "text here"}} – sets a behavioral bias for the AI until the next user input. Quotes around the text are important.
  • -
  • {{roll:(formula)}} – rolls a dice. (ex: >{{roll:1d6}&rcub will roll a 6-sided dice and return a number between 1 and 6)
  • +
  • {{roll:(formula)}} – rolls a dice. (ex: >{{roll:1d6}} will roll a 6-sided dice and return a number between 1 and 6)
  • {{random:(args)}} – returns a random item from the list. (ex: {{random:1,2,3,4}} will return 1 of the 4 numbers at random. Works with text lists too.
  • {{random::(arg1)::(arg2)}} – alternative syntax for random that allows to use commas in the list items.
  • {{banned "text here"}} – dynamically add text in the quotes to banned words sequences, if Text Generation WebUI backend used. Do nothing for others backends. Can be used anywhere (Character description, WI, AN, etc.) Quotes around the text are important.
  • From 7c99d87238b2c40d7b2e30d8e9756aa4622c1bbb Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Thu, 28 Mar 2024 20:22:14 +0200 Subject: [PATCH 27/27] Also clear prompt cache on streaming stop --- public/script.js | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/public/script.js b/public/script.js index 97e30e42f..f999f9a82 100644 --- a/public/script.js +++ b/public/script.js @@ -2733,9 +2733,7 @@ class StreamingProcessor { const continueMsg = this.type === 'continue' ? this.messageAlreadyGenerated : undefined; saveLogprobsForActiveMessage(this.messageLogprobs.filter(Boolean), continueMsg); await saveChatConditional(); - activateSendButtons(); - showSwipeButtons(); - setGenerationProgress(0); + unblockGeneration(); generatedPromptCache = ''; //console.log("Generated text size:", text.length, text) @@ -2778,11 +2776,8 @@ class StreamingProcessor { this.isStopped = true; this.hideMessageButtons(this.messageId); - $('#send_textarea').removeAttr('disabled'); - is_send_press = false; - activateSendButtons(); - setGenerationProgress(0); - showSwipeButtons(); + generatedPromptCache = ''; + unblockGeneration(); } setFirstSwipe(messageId) {