diff --git a/package-lock.json b/package-lock.json index 2a99a2099..0d599f9ec 100644 --- a/package-lock.json +++ b/package-lock.json @@ -61,6 +61,9 @@ "@types/jquery": "^3.5.29", "eslint": "^8.55.0", "jquery": "^3.6.4" + }, + "engines": { + "node": ">= 18" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -3885,7 +3888,6 @@ }, "node_modules/punycode": { "version": "2.3.1", - "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -4435,8 +4437,15 @@ } }, "node_modules/tr46": { - "version": "0.0.3", - "license": "MIT" + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.0.0.tgz", + "integrity": "sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=18" + } }, "node_modules/truncate-utf8-bytes": { "version": "1.0.2", @@ -4585,19 +4594,27 @@ } }, "node_modules/webidl-conversions": { - "version": "3.0.1", - "license": "BSD-2-Clause" + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "engines": { + "node": ">=12" + } }, "node_modules/whatwg-fetch": { "version": "3.6.18", "license": "MIT" }, "node_modules/whatwg-url": { - "version": "5.0.0", - "license": "MIT", + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.0.0.tgz", + "integrity": "sha512-1lfMEm2IEr7RIV+f4lUNPOqfFL+pO+Xw3fJSqmjX9AbXcXcYOkCe1P6+9VBZB6n94af16NfZf+sSk0JCBZC9aw==", "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" + "tr46": "^5.0.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=18" } }, "node_modules/which": { diff --git a/package.json b/package.json index e8cbda243..03a314006 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,9 @@ "yargs": "^17.7.1", "yauzl": "^2.10.0" }, + "engines": { + "node": ">= 18" + }, "overrides": { "parse-bmfont-xml": { "xml2js": "^0.5.0" @@ -59,6 +62,9 @@ }, "@zeldafan0225/ai_horde": { "esbuild": "npm:dry-uninstall" + }, + "node-fetch": { + "whatwg-url": "^14.0.0" } }, "name": "sillytavern", diff --git a/public/index.html b/public/index.html index 5c2f065d7..63c1d8fa8 100644 --- a/public/index.html +++ b/public/index.html @@ -6279,11 +6279,11 @@
+
+
+
+
-
-
-
-
diff --git a/public/script.js b/public/script.js index 9355f5a35..62bf171df 100644 --- a/public/script.js +++ b/public/script.js @@ -1547,6 +1547,13 @@ function getCharacterSource(chId = this_chid) { return sourceUrl; } + const risuId = characters[chId]?.data?.extensions?.risuai?.source; + + if (Array.isArray(risuId) && risuId.length && typeof risuId[0] === 'string' && risuId[0].startsWith('risurealm:')) { + const realmId = risuId[0].split(':')[1]; + return `https://realm.risuai.net/character/${realmId}`; + } + return ''; } @@ -5938,7 +5945,7 @@ export function changeMainAPI() { getStatusHorde(); getHordeModels(true); } - validateDisabledSamplers() + validateDisabledSamplers(); setupChatCompletionPromptManager(oai_settings); forceCharacterEditorTokenize(); } @@ -6169,7 +6176,7 @@ export async function getSettings() { firstRun = false; } } - await validateDisabledSamplers() + await validateDisabledSamplers(); settingsReady = true; eventSource.emit(event_types.SETTINGS_LOADED); } @@ -10240,19 +10247,8 @@ jQuery(async function () { if (power_user.zoomed_avatar_magnification) { $('.zoomed_avatar_container').izoomify(); - } else { - $(`.zoomed_avatar[forChar="${charname}"] .dragClose`).hide(); } - $('.zoomed_avatar').on('mouseup', (e) => { - if (e.target.closest('.drag-grabber') || e.button !== 0) { - return; - } - $(`.zoomed_avatar[forChar="${charname}"]`).fadeOut(animation_duration, () => { - $(`.zoomed_avatar[forChar="${charname}"]`).remove(); - }); - }); - $('.zoomed_avatar, .zoomed_avatar .dragClose').on('click touchend', (e) => { if (e.target.closest('.dragClose')) { $(`.zoomed_avatar[forChar="${charname}"]`).fadeOut(animation_duration, () => { diff --git a/public/scripts/RossAscends-mods.js b/public/scripts/RossAscends-mods.js index c08378ee9..b0a48d77f 100644 --- a/public/scripts/RossAscends-mods.js +++ b/public/scripts/RossAscends-mods.js @@ -424,7 +424,7 @@ function restoreUserInput() { const userInput = LoadLocal('userInput'); if (userInput) { - $('#send_textarea').val(userInput)[0].dispatchEvent(new Event('input', { bubbles:true })); + $('#send_textarea').val(userInput)[0].dispatchEvent(new Event('input', { bubbles: true })); } } @@ -436,8 +436,6 @@ const saveUserInputDebounced = debounce(saveUserInput); // Make the DIV element draggable: -// THIRD UPDATE, prevent resize window breaks and smartly handle saving - export function dragElement(elmnt) { var hasBeenDraggedByUser = false; var isMouseDown = false; @@ -479,8 +477,8 @@ export function dragElement(elmnt) { } //console.debug(left + width, winWidth, hasBeenDraggedByUser, isMouseDown) const style = getComputedStyle(target); //use computed values because not all CSS are set by default - height = target.offsetHeight; - width = target.offsetWidth; + height = parseInt(style.height) //target.offsetHeight; + width = parseInt(style.width) //target.offsetWidth; top = parseInt(style.top); left = parseInt(style.left); right = parseInt(style.right); @@ -494,19 +492,20 @@ export function dragElement(elmnt) { const topbarstyle = getComputedStyle(topbar); topBarFirstX = parseInt(topbarstyle.marginInline); topBarLastY = parseInt(topbarstyle.height); - - /*console.log(` - winWidth: ${winWidth}, winHeight: ${winHeight} - sheldWidth: ${sheldWidth} - X: ${$(elmnt).css('left')} - Y: ${$(elmnt).css('top')} - MaxX: ${maxX}, MaxY: ${maxY} - height: ${height} - width: ${width} - Topbar 1st X: ${topBarFirstX} - TopBar lastX: ${topBarLastX} - `);*/ - + /* + console.log(` + Observer + winWidth: ${winWidth}, winHeight: ${winHeight} + sheldWidth: sheldWidth + X: ${$(elmnt).css('left')} + Y: ${$(elmnt).css('top')} + MaxX: ${maxX}, MaxY: ${maxY} + height: ${height} + width: ${width} + Topbar 1st X: ${topBarFirstX} + TopBar lastX: topBarLastX + `); + */ //prepare an empty poweruser object for the item being altered if we don't have one already if (!power_user.movingUIState[elmntName]) { @@ -524,24 +523,38 @@ export function dragElement(elmnt) { } //handle resizing - if (!hasBeenDraggedByUser && isMouseDown) { - console.debug('saw resize, NOT header drag'); + if (!hasBeenDraggedByUser && isMouseDown) { //if user is dragging the resize handle (not in header) + //console.debug('saw resize, NOT header drag'); + let imgHeight, imgWidth, imageAspectRatio; + let containerAspectRatio = elmnt.height() / elmnt.width(); + + //force the zoomed avatar container to always be the same aspect ratio as the inner image + if ($(elmnt).attr('id').startsWith('zoomFor_')) { + let zoomedAvatarImage = $(elmnt).find('.zoomed_avatar_img'); + imgHeight = zoomedAvatarImage.height(); + imgWidth = zoomedAvatarImage.width(); + imageAspectRatio = imgHeight / imgWidth; + } + + if (containerAspectRatio !== imageAspectRatio) { + elmnt.css('height', imgHeight); + } //prevent resizing offscreen if (top + elmnt.height() >= winHeight) { - console.debug('resizing height to prevent offscreen'); + //console.debug('resizing height to prevent offscreen'); elmnt.css('height', winHeight - top - 1 + 'px'); } if (left + elmnt.width() >= winWidth) { - console.debug('resizing width to prevent offscreen'); + //console.debug('resizing width to prevent offscreen'); elmnt.css('width', winWidth - left - 1 + 'px'); } //prevent resizing from top left into the top bar if (top < topBarLastY && maxX >= topBarFirstX && left <= topBarFirstX ) { - console.debug('prevent topbar underlap resize'); + //console.debug('prevent topbar underlap resize'); elmnt.css('width', width - 1 + 'px'); } @@ -647,19 +660,20 @@ export function dragElement(elmnt) { // and will defaults to shrink to min value of 100px set in CSS file elmnt.css('height', height); elmnt.css('width', width); - /* - console.log(` - winWidth: ${winWidth}, winHeight: ${winHeight} - sheldWidth: ${sheldWidth} - X: ${$(elmnt).css('left')} - Y: ${$(elmnt).css('top')} - MaxX: ${maxX}, MaxY: ${maxY} - height: ${height} - width: ${width} - Topbar 1st X: ${topBarFirstX} - TopBar lastX: ${topBarLastX} - `); - */ + + /*console.log(` +elementDrag: +winWidth: ${winWidth}, winHeight: ${winHeight} +sheldWidth: sheldWidth +X: ${$(elmnt).css('left')} +Y: ${$(elmnt).css('top')} +MaxX: ${maxX}, MaxY: ${maxY} +height: ${height} +width: ${width} +Topbar 1st X: ${topBarFirstX} +TopBar lastX: topBarLastX +`);*/ + return; } diff --git a/public/scripts/char-data.js b/public/scripts/char-data.js index 20db48c0a..ce5d55446 100644 --- a/public/scripts/char-data.js +++ b/public/scripts/char-data.js @@ -73,6 +73,7 @@ * @property {string} [github_repo] - The gitHub repository associated with the character. * @property {string} [source_url] - The source URL associated with the character. * @property {{full_path: string}} [chub] - The Chub-specific data associated with the character. + * @property {{source: string[]}} [risuai] - The RisuAI-specific data associated with the character. */ /** diff --git a/public/scripts/extensions/tts/gsvi.js b/public/scripts/extensions/tts/gsvi.js new file mode 100644 index 000000000..8aee4ad20 --- /dev/null +++ b/public/scripts/extensions/tts/gsvi.js @@ -0,0 +1,267 @@ + +import { saveTtsProviderSettings } from './index.js'; + +export { GSVITtsProvider }; + +class GSVITtsProvider { + //########// + // Config // + //########// + + settings; + ready = false; + separator = '. '; + + characterList = {}; + voices = []; + /** + * Perform any text processing before passing to TTS engine. + * @param {string} text Input text + * @returns {string} Processed text + */ + processText(text) { + text = text.replace('
', '\n'); // Replace
with newline + return text; + } + + languageLabels = { + 'Multilingual': '多语种混合', + 'Chinese': '中文', + 'English': '英文', + 'Japanese': '日文', + 'Chinese-English': '中英混合', + 'Japanese-English': '日英混合', + }; + defaultSettings = { + provider_endpoint: 'http://127.0.0.1:5000', + + language: '多语种混合', + + cha_name: '', + character_emotion: 'default', + + speed: 1, + + top_k: 6, + top_p: 0.85, + temperature: 0.75, + batch_size: 10, + + stream: false, + stream_chunk_size: 100, + }; + + // Added new methods to obtain characters and emotions + async fetchCharacterList() { + const response = await fetch(this.settings.provider_endpoint + '/character_list'); + if (!response.ok) { + throw new Error(`HTTP ${response.status}: ${await response.text()}`); + } + const characterList = await response.json(); + this.characterList = characterList; + this.voices = Object.keys(characterList); + + } + + + + get settingsHtml() { + let html = ` + + +
+ + + + + + + + + + + + + + + + + + + + + + + +

+ For more information, visit the + GSVI project page. +

+ `; + + return html; + } + + onSettingsChange() { + // Update provider settings based on input fields + this.settings.provider_endpoint = $('#gsvi_tts_endpoint').val(); + this.settings.language = $('#gsvi_api_language').val(); + + + // Update the rest of TTS settings based on input fields + this.settings.speed = parseFloat($('#gsvi_speed').val()); + this.settings.temperature = parseFloat($('#gsvi_temperature').val()); + this.settings.top_k = parseInt($('#gsvi_top_k').val(), 10); + this.settings.top_p = parseFloat($('#gsvi_top_p').val()); + this.settings.batch_size = parseInt($('#gsvi_batch_size').val(), 10); + this.settings.stream = $('#gsvi_tts_streaming').is(':checked'); + this.settings.stream_chunk_size = parseInt($('#gsvi_stream_chunk_size').val(), 10); + + // Update UI to reflect changes + + $('#gsvi_tts_speed_output').text(this.settings.speed); + $('#gsvi_tts_temperature_output').text(this.settings.temperature); + $('#gsvi_top_k_output').text(this.settings.top_k); + $('#gsvi_top_p_output').text(this.settings.top_p); + $('#gsvi_stream_chunk_size_output').text(this.settings.stream_chunk_size); + $('#gsvi_batch_size_output').text(this.settings.batch_size); + + + + + // Persist settings changes + saveTtsProviderSettings(); + + } + + async loadSettings(settings) { + // Populate Provider UI given input settings + if (Object.keys(settings).length === 0) { + console.info('Using default TTS Provider settings'); + } + + // Only accept keys defined in defaultSettings + this.settings = { ...this.defaultSettings, ...settings }; + + // Fetch character and emotion list + // Set initial values from the settings + $('#gsvi_tts_endpoint').val(this.settings.provider_endpoint); + $('#gsvi_api_language').val(this.settings.language); + + $('#gsvi_speed').val(this.settings.speed); + $('#gsvi_temperature').val(this.settings.temperature); + $('#gsvi_top_k').val(this.settings.top_k); + $('#gsvi_top_p').val(this.settings.top_p); + $('#gsvi_batch_size').val(this.settings.batch_size); + $('#gsvi_tts_streaming').prop('checked', this.settings.stream); + $('#gsvi_stream_chunk_size').val(this.settings.stream_chunk_size); + + // Update UI to reflect initial settings + $('#gsvi_tts_speed_output').text(this.settings.speed); + $('#gsvi_tts_temperature_output').text(this.settings.temperature); + $('#gsvi_top_k_output').text(this.settings.top_k); + $('#gsvi_top_p_output').text(this.settings.top_p); + $('#gsvi_stream_chunk_size_output').text(this.settings.stream_chunk_size); + + // Register event listeners to update settings on user interaction + // (Similar to before, ensure event listeners for character and emotion selection are included) + // Register input/change event listeners to update settings on user interaction + $('#gsvi_tts_endpoint').on('input', () => { this.onSettingsChange(); }); + $('#gsvi_api_language').on('change', () => { this.onSettingsChange(); }); + + $('#gsvi_speed').on('input', () => { this.onSettingsChange(); }); + $('#gsvi_temperature').on('input', () => { this.onSettingsChange(); }); + $('#gsvi_top_k').on('input', () => { this.onSettingsChange(); }); + $('#gsvi_top_p').on('input', () => { this.onSettingsChange(); }); + $('#gsvi_batch_size').on('input', () => { this.onSettingsChange(); }); + $('#gsvi_tts_streaming').on('change', () => { this.onSettingsChange(); }); + $('#gsvi_stream_chunk_size').on('input', () => { this.onSettingsChange(); }); + + await this.checkReady(); + console.debug('GSVI: Settings loaded'); + } + + + + // Perform a simple readiness check by trying to fetch voiceIds + async checkReady() { + await Promise.allSettled([this.fetchCharacterList()]); + } + + async onRefreshClick() { + return; + } + + //#################// + // TTS Interfaces // + //#################// + + async getVoice(voiceName) { + if (this.voices.length == 0) { + this.fetchCharacterList(); + } + if (!this.voices.includes(voiceName)) { + throw `TTS Voice name ${voiceName} not found`; + } + return { name: voiceName, voice_id: voiceName, preview_url: false, lang: 'zh-CN' }; + } + + async generateTts(text, voiceId) { + const response = await this.fetchTtsGeneration(text, voiceId); + return response; + } + + //###########// + // API CALLS // + //###########// + async fetchTtsVoiceObjects() { + if (this.voices.length == 0) { + await this.fetchCharacterList(); + } + console.log(this.voices); + const voices = this.voices.map(x => ({ name: x, voice_id: x, preview_url: false, lang: 'zh-CN' })); + return voices; + } + + + async fetchTtsGeneration(inputText, voiceId) { + console.info(`Generating new TTS for voice_id ${voiceId}`); + + + const params = new URLSearchParams(); + params.append('text', inputText); + params.append('cha_name', voiceId); + params.append('text_language', this.settings.language); + params.append('batch_size', this.settings.batch_size.toString()); + params.append('speed', this.settings.speed.toString()); + params.append('top_k', this.settings.top_k.toString()); + params.append('top_p', this.settings.top_p.toString()); + params.append('temperature', this.settings.temperature.toString()); + params.append('stream', this.settings.stream.toString()); + + + return `${this.settings.provider_endpoint}/tts?${params.toString()}`; + + } + + // Interface not used by GSVI TTS + async fetchTtsFromHistory(history_item_id) { + return Promise.resolve(history_item_id); + } + +} diff --git a/public/scripts/extensions/tts/index.js b/public/scripts/extensions/tts/index.js index 2aa0de82f..f73e9d48f 100644 --- a/public/scripts/extensions/tts/index.js +++ b/public/scripts/extensions/tts/index.js @@ -10,6 +10,7 @@ import { NovelTtsProvider } from './novel.js'; import { power_user } from '../../power-user.js'; import { OpenAITtsProvider } from './openai.js'; import { XTTSTtsProvider } from './xtts.js'; +import { GSVITtsProvider } from './gsvi.js'; import { AllTalkTtsProvider } from './alltalk.js'; import { SpeechT5TtsProvider } from './speecht5.js'; import { SlashCommandParser } from '../../slash-commands/SlashCommandParser.js'; @@ -74,6 +75,7 @@ const ttsProviders = { ElevenLabs: ElevenLabsTtsProvider, Silero: SileroTtsProvider, XTTSv2: XTTSTtsProvider, + GSVI: GSVITtsProvider, System: SystemTtsProvider, Coqui: CoquiTtsProvider, Edge: EdgeTtsProvider, diff --git a/public/scripts/power-user.js b/public/scripts/power-user.js index 93186df20..ca72682cd 100644 --- a/public/scripts/power-user.js +++ b/public/scripts/power-user.js @@ -1389,7 +1389,7 @@ async function applyTheme(name) { } async function applyMovingUIPreset(name) { - resetMovablePanels('quiet'); + await resetMovablePanels('quiet'); const movingUIPreset = movingUIPresets.find(x => x.name == name); if (!movingUIPreset) { @@ -1401,6 +1401,7 @@ async function applyMovingUIPreset(name) { console.log('MovingUI Preset applied: ' + name); loadMovingUIState(); + saveSettingsDebounced() } /** @@ -1629,9 +1630,9 @@ function loadPowerUserSettings(settings, data) { $('#stscript_autocomplete_font_scale_counter').val(power_user.stscript.autocomplete.font.scale ?? defaultStscript.autocomplete.font.scale); document.body.style.setProperty('--ac-font-scale', power_user.stscript.autocomplete.font.scale ?? defaultStscript.autocomplete.font.scale.toString()); $('#stscript_autocomplete_width_left').val(power_user.stscript.autocomplete.width.left ?? AUTOCOMPLETE_WIDTH.CHAT); - document.querySelector('#stscript_autocomplete_width_left').dispatchEvent(new Event('input', { bubbles:true })); + document.querySelector('#stscript_autocomplete_width_left').dispatchEvent(new Event('input', { bubbles: true })); $('#stscript_autocomplete_width_right').val(power_user.stscript.autocomplete.width.right ?? AUTOCOMPLETE_WIDTH.CHAT); - document.querySelector('#stscript_autocomplete_width_right').dispatchEvent(new Event('input', { bubbles:true })); + document.querySelector('#stscript_autocomplete_width_right').dispatchEvent(new Event('input', { bubbles: true })); $('#restore_user_input').prop('checked', power_user.restore_user_input); @@ -3671,7 +3672,7 @@ $(document).ready(() => { $('#stscript_autocomplete_font_scale_counter').val(value); power_user.stscript.autocomplete.font.scale = Number(value); document.body.style.setProperty('--ac-font-scale', value.toString()); - window.dispatchEvent(new Event('resize', { bubbles:true })); + window.dispatchEvent(new Event('resize', { bubbles: true })); saveSettingsDebounced(); }); $('#stscript_autocomplete_font_scale_counter').on('input', function () { @@ -3679,7 +3680,7 @@ $(document).ready(() => { $('#stscript_autocomplete_font_scale').val(value); power_user.stscript.autocomplete.font.scale = Number(value); document.body.style.setProperty('--ac-font-scale', value.toString()); - window.dispatchEvent(new Event('resize', { bubbles:true })); + window.dispatchEvent(new Event('resize', { bubbles: true })); saveSettingsDebounced(); }); @@ -3687,7 +3688,7 @@ $(document).ready(() => { const value = $(this).val(); power_user.stscript.autocomplete.width.left = Number(value); /**@type {HTMLElement}*/(this.closest('.doubleRangeInputContainer')).style.setProperty('--value', value.toString()); - window.dispatchEvent(new Event('resize', { bubbles:true })); + window.dispatchEvent(new Event('resize', { bubbles: true })); saveSettingsDebounced(); }); @@ -3695,7 +3696,7 @@ $(document).ready(() => { const value = $(this).val(); power_user.stscript.autocomplete.width.right = Number(value); /**@type {HTMLElement}*/(this.closest('.doubleRangeInputContainer')).style.setProperty('--value', value.toString()); - window.dispatchEvent(new Event('resize', { bubbles:true })); + window.dispatchEvent(new Event('resize', { bubbles: true })); saveSettingsDebounced(); }); @@ -3789,15 +3790,18 @@ $(document).ready(() => { browser_has_focus = false; }); - SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'vn', + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ + name: 'vn', callback: toggleWaifu, helpString: 'Swaps Visual Novel Mode On/Off', })); - SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'newchat', + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ + name: 'newchat', callback: doNewChat, helpString: 'Start a new chat with the current character', })); - SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'random', + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ + name: 'random', callback: doRandomChat, unnamedArgumentList: [ new SlashCommandArgument( @@ -3806,7 +3810,8 @@ $(document).ready(() => { ], helpString: 'Start a new chat with a random character. If an argument is provided, only considers characters that have the specified tag.', })); - SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'delmode', + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ + name: 'delmode', callback: doDelMode, aliases: ['del'], unnamedArgumentList: [ @@ -3816,7 +3821,8 @@ $(document).ready(() => { ], helpString: 'Enter message deletion mode, and auto-deletes last N messages if numeric argument is provided.', })); - SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'cut', + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ + name: 'cut', callback: doMesCut, returns: 'the text of cut messages separated by a newline', unnamedArgumentList: [ @@ -3842,16 +3848,19 @@ $(document).ready(() => { `, aliases: [], })); - SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'resetpanels', + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ + name: 'resetpanels', callback: doResetPanels, helpString: 'resets UI panels to original state', aliases: ['resetui'], })); - SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'bgcol', + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ + name: 'bgcol', callback: setAvgBG, helpString: '– WIP test of auto-bg avg coloring', })); - SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'theme', + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ + name: 'theme', callback: setThemeCallback, unnamedArgumentList: [ new SlashCommandArgument( @@ -3860,7 +3869,8 @@ $(document).ready(() => { ], helpString: 'sets a UI theme by name', })); - SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'movingui', + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ + name: 'movingui', callback: setmovingUIPreset, unnamedArgumentList: [ new SlashCommandArgument( diff --git a/public/scripts/slash-commands.js b/public/scripts/slash-commands.js index be75a9561..d273130fa 100644 --- a/public/scripts/slash-commands.js +++ b/public/scripts/slash-commands.js @@ -73,7 +73,8 @@ export const parser = new SlashCommandParser(); const registerSlashCommand = SlashCommandParser.addCommand.bind(SlashCommandParser); const getSlashCommandsHelp = parser.getHelpString.bind(parser); -SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: '?', +SlashCommandParser.addCommandObject(SlashCommand.fromProps({ + name: '?', callback: helpCommandCallback, aliases: ['help'], unnamedArgumentList: [new SlashCommandArgument( @@ -81,7 +82,8 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: '?', )], helpString: 'Get help on macros, chat formatting and commands.', })); -SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'name', +SlashCommandParser.addCommandObject(SlashCommand.fromProps({ + name: 'name', callback: setNameCallback, unnamedArgumentList: [ new SlashCommandArgument( @@ -91,16 +93,19 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'name', helpString: 'Sets user name and persona avatar (if set).', aliases: ['persona'], })); -SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'sync', +SlashCommandParser.addCommandObject(SlashCommand.fromProps({ + name: 'sync', callback: syncCallback, helpString: 'Syncs the user persona in user-attributed messages in the current chat.', })); -SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'lock', +SlashCommandParser.addCommandObject(SlashCommand.fromProps({ + name: 'lock', callback: bindCallback, aliases: ['bind'], helpString: 'Locks/unlocks a persona (name and avatar) to the current chat', })); -SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'bg', +SlashCommandParser.addCommandObject(SlashCommand.fromProps({ + name: 'bg', callback: setBackgroundCallback, aliases: ['background'], returns: 'the current background', @@ -123,7 +128,8 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'bg',
`, })); -SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'sendas', +SlashCommandParser.addCommandObject(SlashCommand.fromProps({ + name: 'sendas', callback: sendMessageAs, namedArgumentList: [ new SlashCommandNamedArgument( @@ -161,7 +167,8 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'sendas', `, })); -SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'sys', +SlashCommandParser.addCommandObject(SlashCommand.fromProps({ + name: 'sys', callback: sendNarratorMessage, aliases: ['nar'], namedArgumentList: [ @@ -204,7 +211,8 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'sys', `, })); -SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'sysname', +SlashCommandParser.addCommandObject(SlashCommand.fromProps({ + name: 'sysname', callback: setNarratorName, unnamedArgumentList: [ new SlashCommandArgument( @@ -213,7 +221,8 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'sysname', ], helpString: 'Sets a name for future system narrator messages in this chat (display only). Default: System. Leave empty to reset.', })); -SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'comment', +SlashCommandParser.addCommandObject(SlashCommand.fromProps({ + name: 'comment', callback: sendCommentMessage, namedArgumentList: [ new SlashCommandNamedArgument( @@ -257,22 +266,26 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'comment', `, })); -SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'single', +SlashCommandParser.addCommandObject(SlashCommand.fromProps({ + name: 'single', callback: setStoryModeCallback, aliases: ['story'], helpString: 'Sets the message style to single document mode without names or avatars visible.', })); -SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'bubble', +SlashCommandParser.addCommandObject(SlashCommand.fromProps({ + name: 'bubble', callback: setBubbleModeCallback, aliases: ['bubbles'], helpString: 'Sets the message style to bubble chat mode.', })); -SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'flat', +SlashCommandParser.addCommandObject(SlashCommand.fromProps({ + name: 'flat', callback: setFlatModeCallback, aliases: ['default'], helpString: 'Sets the message style to flat chat mode.', })); -SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'continue', +SlashCommandParser.addCommandObject(SlashCommand.fromProps({ + name: 'continue', callback: continueChatCallback, aliases: ['cont'], unnamedArgumentList: [ @@ -299,7 +312,8 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'continue', `, })); -SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'go', +SlashCommandParser.addCommandObject(SlashCommand.fromProps({ + name: 'go', callback: goToCharacterCallback, unnamedArgumentList: [ new SlashCommandArgument( @@ -309,7 +323,8 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'go', helpString: 'Opens up a chat with the character or group by its name', aliases: ['char'], })); -SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'sysgen', +SlashCommandParser.addCommandObject(SlashCommand.fromProps({ + name: 'sysgen', callback: generateSystemMessage, unnamedArgumentList: [ new SlashCommandArgument( @@ -318,7 +333,8 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'sysgen', ], helpString: 'Generates a system message using a specified prompt.', })); -SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'ask', +SlashCommandParser.addCommandObject(SlashCommand.fromProps({ + name: 'ask', callback: askCharacter, unnamedArgumentList: [ new SlashCommandArgument( @@ -330,7 +346,8 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'ask', ], helpString: 'Asks a specified character card a prompt. Character name and prompt have to be separated by a new line.', })); -SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'delname', +SlashCommandParser.addCommandObject(SlashCommand.fromProps({ + name: 'delname', callback: deleteMessagesByNameCallback, namedArgumentList: [], unnamedArgumentList: [ @@ -353,7 +370,8 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'delname', `, })); -SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'send', +SlashCommandParser.addCommandObject(SlashCommand.fromProps({ + name: 'send', callback: sendUserMessageCallback, namedArgumentList: [ new SlashCommandNamedArgument( @@ -408,7 +426,8 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'send', `, })); -SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'trigger', +SlashCommandParser.addCommandObject(SlashCommand.fromProps({ + name: 'trigger', callback: triggerGenerationCallback, namedArgumentList: [ new SlashCommandNamedArgument( @@ -429,7 +448,8 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'trigger', `, })); -SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'hide', +SlashCommandParser.addCommandObject(SlashCommand.fromProps({ + name: 'hide', callback: hideMessageCallback, unnamedArgumentList: [ new SlashCommandArgument( @@ -438,7 +458,8 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'hide', ], helpString: 'Hides a chat message from the prompt.', })); -SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'unhide', +SlashCommandParser.addCommandObject(SlashCommand.fromProps({ + name: 'unhide', callback: unhideMessageCallback, unnamedArgumentList: [ new SlashCommandArgument( @@ -447,7 +468,8 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'unhide', ], helpString: 'Unhides a message from the prompt.', })); -SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'member-disable', +SlashCommandParser.addCommandObject(SlashCommand.fromProps({ + name: 'member-disable', callback: disableGroupMemberCallback, aliases: ['disable', 'disablemember', 'memberdisable'], unnamedArgumentList: [ @@ -457,7 +479,8 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'member-disab ], helpString: 'Disables a group member from being drafted for replies.', })); -SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'member-enable', +SlashCommandParser.addCommandObject(SlashCommand.fromProps({ + name: 'member-enable', aliases: ['enable', 'enablemember', 'memberenable'], callback: enableGroupMemberCallback, unnamedArgumentList: [ @@ -467,7 +490,8 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'member-enabl ], helpString: 'Enables a group member to be drafted for replies.', })); -SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'member-add', +SlashCommandParser.addCommandObject(SlashCommand.fromProps({ + name: 'member-add', callback: addGroupMemberCallback, aliases: ['addmember', 'memberadd'], unnamedArgumentList: [ @@ -489,7 +513,8 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'member-add', `, })); -SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'member-remove', +SlashCommandParser.addCommandObject(SlashCommand.fromProps({ + name: 'member-remove', callback: removeGroupMemberCallback, aliases: ['removemember', 'memberremove'], unnamedArgumentList: [ @@ -512,7 +537,8 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'member-remov `, })); -SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'member-up', +SlashCommandParser.addCommandObject(SlashCommand.fromProps({ + name: 'member-up', callback: moveGroupMemberUpCallback, aliases: ['upmember', 'memberup'], unnamedArgumentList: [ @@ -522,7 +548,8 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'member-up', ], helpString: 'Moves a group member up in the group chat list.', })); -SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'member-down', +SlashCommandParser.addCommandObject(SlashCommand.fromProps({ + name: 'member-down', callback: moveGroupMemberDownCallback, aliases: ['downmember', 'memberdown'], unnamedArgumentList: [ @@ -532,7 +559,8 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'member-down' ], helpString: 'Moves a group member down in the group chat list.', })); -SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'peek', +SlashCommandParser.addCommandObject(SlashCommand.fromProps({ + name: 'peek', callback: peekCallback, unnamedArgumentList: [ new SlashCommandArgument( @@ -558,7 +586,8 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'peek', `, })); -SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'delswipe', +SlashCommandParser.addCommandObject(SlashCommand.fromProps({ + name: 'delswipe', callback: deleteSwipeCallback, aliases: ['swipedel'], unnamedArgumentList: [ @@ -585,7 +614,8 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'delswipe', `, })); -SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'echo', +SlashCommandParser.addCommandObject(SlashCommand.fromProps({ + name: 'echo', callback: echoCallback, returns: 'the text', namedArgumentList: [ @@ -615,7 +645,8 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'echo', `, })); -SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'gen', +SlashCommandParser.addCommandObject(SlashCommand.fromProps({ + name: 'gen', callback: generateCallback, returns: 'generated text', namedArgumentList: [ @@ -646,7 +677,8 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'gen', `, })); -SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'genraw', +SlashCommandParser.addCommandObject(SlashCommand.fromProps({ + name: 'genraw', callback: generateRawCallback, returns: 'generated text', namedArgumentList: [ @@ -692,7 +724,8 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'genraw', `, })); -SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'addswipe', +SlashCommandParser.addCommandObject(SlashCommand.fromProps({ + name: 'addswipe', callback: addSwipeCallback, aliases: ['swipeadd'], unnamedArgumentList: [ @@ -702,10 +735,12 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'addswipe', ], helpString: 'Adds a swipe to the last chat message.', })); -SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'abort', +SlashCommandParser.addCommandObject(SlashCommand.fromProps({ + name: 'abort', callback: abortCallback, namedArgumentList: [ - SlashCommandNamedArgument.fromProps({ name: 'quiet', + SlashCommandNamedArgument.fromProps({ + name: 'quiet', description: 'Whether to suppress the toast message notifying about the /abort call.', typeList: [ARGUMENT_TYPE.BOOLEAN], defaultValue: 'true', @@ -713,13 +748,15 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'abort', }), ], unnamedArgumentList: [ - SlashCommandArgument.fromProps({ description: 'The reason for aborting command execution. Shown when quiet=false', + SlashCommandArgument.fromProps({ + description: 'The reason for aborting command execution. Shown when quiet=false', typeList: [ARGUMENT_TYPE.STRING], }), ], helpString: 'Aborts the slash command batch execution.', })); -SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'fuzzy', +SlashCommandParser.addCommandObject(SlashCommand.fromProps({ + name: 'fuzzy', callback: fuzzyCallback, returns: 'first matching item', namedArgumentList: [ @@ -758,7 +795,8 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'fuzzy', `, })); -SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'pass', +SlashCommandParser.addCommandObject(SlashCommand.fromProps({ + name: 'pass', callback: (_, arg) => arg, returns: 'the provided value', unnamedArgumentList: [ @@ -779,7 +817,8 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'pass', `, })); -SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'delay', +SlashCommandParser.addCommandObject(SlashCommand.fromProps({ + name: 'delay', callback: delayCallback, aliases: ['wait', 'sleep'], unnamedArgumentList: [ @@ -801,7 +840,8 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'delay', `, })); -SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'input', +SlashCommandParser.addCommandObject(SlashCommand.fromProps({ + name: 'input', aliases: ['prompt'], callback: inputCallback, returns: 'user input', @@ -834,7 +874,8 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'input', `, })); -SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'run', +SlashCommandParser.addCommandObject(SlashCommand.fromProps({ + name: 'run', aliases: ['call', 'exec'], callback: runCallback, returns: 'result of the executed closure of QR', @@ -855,7 +896,8 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'run', `, })); -SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'messages', +SlashCommandParser.addCommandObject(SlashCommand.fromProps({ + name: 'messages', callback: getMessagesCallback, aliases: ['message'], namedArgumentList: [ @@ -866,7 +908,7 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'messages', 'hidden', 'include hidden messages', [ARGUMENT_TYPE.BOOLEAN], false, false, 'on', ['off', 'on'], ), new SlashCommandNamedArgument( - 'role', 'filter messages by role' , [ARGUMENT_TYPE.STRING], false, false, null, ['system', 'assistant', 'user'], + 'role', 'filter messages by role', [ARGUMENT_TYPE.STRING], false, false, null, ['system', 'assistant', 'user'], ), ], unnamedArgumentList: [ @@ -900,7 +942,8 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'messages', `, })); -SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'setinput', +SlashCommandParser.addCommandObject(SlashCommand.fromProps({ + name: 'setinput', callback: setInputCallback, unnamedArgumentList: [ new SlashCommandArgument( @@ -921,7 +964,8 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'setinput', `, })); -SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'popup', +SlashCommandParser.addCommandObject(SlashCommand.fromProps({ + name: 'popup', callback: popupCallback, returns: 'popup text', namedArgumentList: [ @@ -955,7 +999,8 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'popup', `, })); -SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'buttons', +SlashCommandParser.addCommandObject(SlashCommand.fromProps({ + name: 'buttons', callback: buttonsCallback, returns: 'clicked button label', namedArgumentList: [ @@ -983,7 +1028,8 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'buttons', `, })); -SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'trimtokens', +SlashCommandParser.addCommandObject(SlashCommand.fromProps({ + name: 'trimtokens', callback: trimTokensCallback, returns: 'trimmed text', namedArgumentList: [ @@ -1013,7 +1059,8 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'trimtokens', `, })); -SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'trimstart', +SlashCommandParser.addCommandObject(SlashCommand.fromProps({ + name: 'trimstart', callback: trimStartCallback, returns: 'trimmed text', unnamedArgumentList: [ @@ -1035,7 +1082,8 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'trimstart', `, })); -SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'trimend', +SlashCommandParser.addCommandObject(SlashCommand.fromProps({ + name: 'trimend', callback: trimEndCallback, returns: 'trimmed text', unnamedArgumentList: [ @@ -1045,7 +1093,8 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'trimend', ], helpString: 'Trims the text to the end of the last full sentence.', })); -SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'inject', +SlashCommandParser.addCommandObject(SlashCommand.fromProps({ + name: 'inject', callback: injectCallback, namedArgumentList: [ new SlashCommandNamedArgument( @@ -1071,15 +1120,18 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'inject', ], helpString: 'Injects a text into the LLM prompt for the current chat. Requires a unique injection ID. Positions: "before" main prompt, "after" main prompt, in-"chat" (default: after). Depth: injection depth for the prompt (default: 4). Role: role for in-chat injections (default: system). Scan: include injection content into World Info scans (default: false).', })); -SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'listinjects', +SlashCommandParser.addCommandObject(SlashCommand.fromProps({ + name: 'listinjects', callback: listInjectsCallback, helpString: 'Lists all script injections for the current chat.', })); -SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'flushinjects', +SlashCommandParser.addCommandObject(SlashCommand.fromProps({ + name: 'flushinjects', callback: flushInjectsCallback, helpString: 'Removes all script injections for the current chat.', })); -SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'tokens', +SlashCommandParser.addCommandObject(SlashCommand.fromProps({ + name: 'tokens', callback: (_, text) => getTokenCountAsync(text), returns: 'number of tokens', unnamedArgumentList: [ @@ -1089,7 +1141,8 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'tokens', ], helpString: 'Counts the number of tokens in the provided text.', })); -SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'model', +SlashCommandParser.addCommandObject(SlashCommand.fromProps({ + name: 'model', callback: modelCallback, returns: 'current model', unnamedArgumentList: [ @@ -1216,7 +1269,7 @@ export function processChatSlashCommands() { } function setInputCallback(_, value) { - $('#send_textarea').val(value || '')[0].dispatchEvent(new Event('input', { bubbles:true })); + $('#send_textarea').val(value || '')[0].dispatchEvent(new Event('input', { bubbles: true })); return value; } @@ -1410,7 +1463,7 @@ async function runCallback(args, name) { throw new Error(`"${name}" is not callable.`); } closure.scope.parent = scope; - closure.argumentList.forEach(arg=>{ + closure.argumentList.forEach(arg => { if (Object.keys(args).includes(arg.name)) { const providedArg = new SlashCommandNamedArgumentAssignment(); providedArg.name = arg.name; @@ -1559,7 +1612,7 @@ async function generateRawCallback(args, value) { } // Prevent generate recursion - $('#send_textarea').val('')[0].dispatchEvent(new Event('input', { bubbles:true })); + $('#send_textarea').val('')[0].dispatchEvent(new Event('input', { bubbles: true })); const lock = isTrueBoolean(args?.lock); const as = args?.as || 'system'; const quietToLoud = as === 'char'; @@ -1589,7 +1642,7 @@ async function generateCallback(args, value) { } // Prevent generate recursion - $('#send_textarea').val('')[0].dispatchEvent(new Event('input', { bubbles:true })); + $('#send_textarea').val('')[0].dispatchEvent(new Event('input', { bubbles: true })); const lock = isTrueBoolean(args?.lock); const as = args?.as || 'system'; const quietToLoud = as === 'char'; @@ -1725,7 +1778,7 @@ async function deleteSwipeCallback(_, arg) { async function askCharacter(_, text) { // Prevent generate recursion - $('#send_textarea').val('')[0].dispatchEvent(new Event('input', { bubbles:true })); + $('#send_textarea').val('')[0].dispatchEvent(new Event('input', { bubbles: true })); // Not supported in group chats // TODO: Maybe support group chats? @@ -2036,7 +2089,7 @@ async function triggerGenerationCallback(args, value) { } // Prevent generate recursion - $('#send_textarea').val('')[0].dispatchEvent(new Event('input', { bubbles:true })); + $('#send_textarea').val('')[0].dispatchEvent(new Event('input', { bubbles: true })); let chid = undefined; @@ -2202,7 +2255,7 @@ function continueChatCallback(_, prompt) { } // Prevent infinite recursion - $('#send_textarea').val('')[0].dispatchEvent(new Event('input', { bubbles:true })); + $('#send_textarea').val('')[0].dispatchEvent(new Event('input', { bubbles: true })); $('#option_continue').trigger('click', { fromSlashCommand: true, additionalPrompt: prompt }); }, 1); @@ -2210,7 +2263,7 @@ function continueChatCallback(_, prompt) { } export async function generateSystemMessage(_, prompt) { - $('#send_textarea').val('')[0].dispatchEvent(new Event('input', { bubbles:true })); + $('#send_textarea').val('')[0].dispatchEvent(new Event('input', { bubbles: true })); if (!prompt) { console.warn('WARN: No prompt provided for /sysgen command'); @@ -2744,7 +2797,7 @@ export async function executeSlashCommandsOnChatInput(text, options = {}) { if (options.clearChatInput) { ta.value = ''; - ta.dispatchEvent(new Event('input', { bubbles:true })); + ta.dispatchEvent(new Event('input', { bubbles: true })); } document.querySelector('#send_textarea').style.setProperty('--prog', '0%'); @@ -2759,7 +2812,7 @@ export async function executeSlashCommandsOnChatInput(text, options = {}) { commandsFromChatInputAbortController = new SlashCommandAbortController(); result = await executeSlashCommandsWithOptions(text, { abortController: commandsFromChatInputAbortController, - onProgress: (done, total)=>ta.style.setProperty('--prog', `${done / total * 100}%`), + onProgress: (done, total) => ta.style.setProperty('--prog', `${done / total * 100}%`), }); if (commandsFromChatInputAbortController.signal.aborted) { document.querySelector('#form_sheld').classList.add('script_aborted'); @@ -2775,7 +2828,7 @@ export async function executeSlashCommandsOnChatInput(text, options = {}) { toastr.error(e.message); } } finally { - delay(1000).then(()=>clearCommandProgressDebounced()); + delay(1000).then(() => clearCommandProgressDebounced()); commandsFromChatInputAbortController = null; deactivateScriptButtons(); @@ -2821,7 +2874,7 @@ async function executeSlashCommandsWithOptions(text, options = {}) { toastr.error( `${toast}${clickHint}`, 'SlashCommandParserError', - { escapeHtml:false, timeOut: 10000, onclick:()=>callPopup(toast, 'text') }, + { escapeHtml: false, timeOut: 10000, onclick: () => callPopup(toast, 'text') }, ); const result = new SlashCommandClosureResult(); return result; @@ -2878,11 +2931,25 @@ async function executeSlashCommands(text, handleParserErrors = true, scope = nul * @param {Boolean} isFloating Whether to show the auto complete as a floating window (e.g., large QR editor) */ export async function setSlashCommandAutoComplete(textarea, isFloating = false) { + function canUseNegativeLookbehind() { + try { + new RegExp('(? ac.text[0] == '/', - async(text, index) => await parser.getNameAt(text, index), + async (text, index) => await parser.getNameAt(text, index), isFloating, ); } diff --git a/public/scripts/slash-commands/SlashCommandParser.js b/public/scripts/slash-commands/SlashCommandParser.js index 171a38491..9b839ca23 100644 --- a/public/scripts/slash-commands/SlashCommandParser.js +++ b/public/scripts/slash-commands/SlashCommandParser.js @@ -109,7 +109,7 @@ export class SlashCommandParser { return this.text[this.index]; } get endOfText() { - return this.index >= this.text.length || /^\s+$/.test(this.ahead); + return this.index >= this.text.length || (/\s/.test(this.char) && /^\s+$/.test(this.ahead)); } diff --git a/public/scripts/utils.js b/public/scripts/utils.js index d62310b3c..63539513a 100644 --- a/public/scripts/utils.js +++ b/public/scripts/utils.js @@ -1341,11 +1341,7 @@ export async function extractTextFromHTML(blob, textSelector = 'body') { */ export async function extractTextFromMarkdown(blob) { const markdown = await blob.text(); - const converter = new showdown.Converter(); - const html = converter.makeHtml(markdown); - const domParser = new DOMParser(); - const document = domParser.parseFromString(DOMPurify.sanitize(html), 'text/html'); - const text = postProcessText(document.body.textContent, false); + const text = postProcessText(markdown, false); return text; } @@ -1629,14 +1625,30 @@ export function highlightRegex(regexStr) { regexStr = escapeHtml(regexStr); // Patterns that we want to highlight only if they are not escaped - const patterns = { - brackets: /(? { diff --git a/public/scripts/world-info.js b/public/scripts/world-info.js index 10a9cc47e..256821b3f 100644 --- a/public/scripts/world-info.js +++ b/public/scripts/world-info.js @@ -186,7 +186,7 @@ class WorldInfoBuffer { result += '\n' + this.#recurseBuffer.join('\n'); } - return this.#transformString(result, entry); + return result; } /** @@ -204,6 +204,7 @@ class WorldInfoBuffer { } // Otherwise we do normal matching of plaintext with the chosen entry settings + haystack = this.#transformString(haystack, entry); const transformedString = this.#transformString(needle, entry); const matchWholeWords = entry.matchWholeWords ?? world_info_match_whole_words; diff --git a/public/style.css b/public/style.css index e7ceb3208..55dbcaeca 100644 --- a/public/style.css +++ b/public/style.css @@ -693,23 +693,35 @@ body .panelControlBar { #send_but { visibility: hidden; } - #rightSendForm > div:not(.mes_send).stscript_btn { - &.stscript_pause, &.stscript_stop { + + #rightSendForm>div:not(.mes_send).stscript_btn { + + &.stscript_pause, + &.stscript_stop { display: flex; } } + &.script_paused { - #rightSendForm > div:not(.mes_send).stscript_btn { + #rightSendForm>div:not(.mes_send).stscript_btn { &.stscript_pause { display: none; } + &.stscript_continue { display: flex; } } } } -#rightSendForm > div:not(.mes_send) { + +@supports not selector(&) { + .stscript_btn { + display: none; + } +} + +#rightSendForm>div:not(.mes_send) { &.stscript_btn { padding-right: 2px; place-self: center; @@ -717,16 +729,20 @@ body .panelControlBar { transition: 0.3s; opacity: 1; display: none; - &.stscript_pause > .fa-solid { + + &.stscript_pause>.fa-solid { background-color: rgb(146, 190, 252); } - &.stscript_continue > .fa-solid { + + &.stscript_continue>.fa-solid { background-color: rgb(146, 190, 252); } - &.stscript_stop > .fa-solid { + + &.stscript_stop>.fa-solid { background-color: rgb(215, 136, 114); } - > .fa-solid { + + >.fa-solid { --toastInfoColor: #2F96B4; --progColor: rgba(0, 128, 0, 0.839); border-radius: 35%; @@ -740,8 +756,7 @@ body .panelControlBar { align-items: center; box-shadow: 0 0 0 var(--progColor), - 0 0 0 var(--progColor) - ; + 0 0 0 var(--progColor); } } } @@ -871,7 +886,7 @@ body .panelControlBar { position: relative; } -#chat .mes.selected{ +#chat .mes.selected { /* background-color: rgb(from var(--SmartThemeQuoteColor) r g b / .5); */ background-color: rgb(102, 0, 0); } @@ -1140,24 +1155,27 @@ select { --prog: 0%; --progDone: 0; border-top: var(--progWidth) solid var(--progColor); - clip-path: polygon( - 0% calc(var(--progDone) * var(--progWidthClip)), - var(--prog) calc(var(--progDone) * var(--progWidthClip)), - var(--prog) var(--progWidthClip), - 100% var(--progWidthClip), - 100% 100%, - 0% 100% - ); + clip-path: polygon(0% calc(var(--progDone) * var(--progWidthClip)), + var(--prog) calc(var(--progDone) * var(--progWidthClip)), + var(--prog) var(--progWidthClip), + 100% var(--progWidthClip), + 100% 100%, + 0% 100%); transition: clip-path 200ms; } + @keyframes script_progress_pulse { - 0%, 100% { + + 0%, + 100% { border-top-color: var(--progColor); } + 50% { border-top-color: var(--progFlashColor); } } + #form_sheld.isExecutingCommandsFromChatInput.script_paused #send_textarea { animation-name: script_progress_pulse; animation-duration: 1500ms; @@ -1169,9 +1187,11 @@ select { #form_sheld.script_success #send_textarea { border-top-color: var(--progSuccessColor); } + #form_sheld.script_error #send_textarea { border-top-color: var(--progErrorColor); } + #form_sheld.script_aborted #send_textarea { border-top-color: var(--progAbortedColor); } @@ -1193,17 +1213,20 @@ select { --direction: row; left: 0; right: 0; + &:before { content: ""; flex: 0 1 calc(var(--targetOffset) * 1px); display: block; pointer-events: none; } + .autoComplete { flex: 0 0 auto; width: 50vw; pointer-events: all; } + &:after { content: ""; flex: 1 1 0; @@ -1233,11 +1256,13 @@ select { flex: 0 1 calc(var(--targetOffset) * 1px - 5vh); display: block; } + .autoComplete-details { flex: 0 0 auto; max-height: 80vh; pointer-events: all; } + &:after { content: ""; flex: 1 1 0; @@ -1248,35 +1273,42 @@ select { flex-direction: row; left: 0; right: 0; + .autoComplete-details { max-height: unset; width: 25vw; } + &.left { &:before { flex: 0 1 calc(var(--targetOffset) * 1px - 25vw); } + &:after { flex: 1 0 auto; max-width: 50vw; } } + &.right { &:before { flex: 0 0 calc(var(--targetOffset) * 1px + 50vw); } } + &.full { &:before { content: ""; flex: 0 1 calc(var(--targetOffset) * 1px); display: block; } + .autoComplete-details { flex: 0 0 auto; max-width: 50vw; width: unset; } + &:after { content: ""; flex: 1 1 0; @@ -1311,6 +1343,7 @@ body[data-stscript-style="dark"] { --ac-style-color-currentParenthesis: rgba(195 118 210 / 1); --ac-style-color-comment: rgba(122 151 90 / 1); } + body[data-stscript-style="light"] { --ac-style-color-border: rgba(200 200 200 / 1); --ac-style-color-background: rgba(248 248 248 / 1); @@ -1331,6 +1364,7 @@ body[data-stscript-style="light"] { --ac-style-color-currentParenthesis: rgba(195 118 210 / 1); --ac-style-color-comment: rgba(70 126 26 / 1); } + body[data-stscript-style="theme"] { --ac-style-color-border: var(--SmartThemeBorderColor); --ac-style-color-background: var(--SmartThemeBlurTintColor); @@ -1350,10 +1384,13 @@ body[data-stscript-style="theme"] { --ac-style-color-currentParenthesis: rgba(195 118 210 / 1); --ac-style-color-comment: rgba(122 151 90 / 1); } + body { --ac-font-scale: 0.8; } -.autoComplete, .autoComplete-details { + +.autoComplete, +.autoComplete-details { --ac-color-border: var(--ac-style-color-border, rgba(69 69 69 / 1)); --ac-color-background: var(--ac-style-color-background, rgba(32 32 32 / 1)); --ac-color-text: var(--ac-style-color-text, rgba(204 204 204 / 1)); @@ -1394,47 +1431,121 @@ body { line-height: 1.2; text-align: left; z-index: 10000; - * { text-shadow: none; } + + * { + text-shadow: none; + } } + body[data-stscript-style] .autoComplete [data-option-type] { - &[data-option-type="enum"] .type { color: var(--ac-color-string); } - &[data-option-type="command"] .type { color: var(--ac-color-cmd); } - &[data-option-type="namedArgument"] .type { color: var(--ac-color-argName); } - &[data-option-type="variable"] .type { color: var(--ac-color-punctuationL1); } - &[data-option-type="qr"] .type { color: var(--ac-color-variable); } - &[data-option-type="macro"] .type { color: var(--ac-color-variableLanguage); } + &[data-option-type="enum"] .type { + color: var(--ac-color-string); + } + + &[data-option-type="command"] .type { + color: var(--ac-color-cmd); + } + + &[data-option-type="namedArgument"] .type { + color: var(--ac-color-argName); + } + + &[data-option-type="variable"] .type { + color: var(--ac-color-punctuationL1); + } + + &[data-option-type="qr"] .type { + color: var(--ac-color-variable); + } + + &[data-option-type="macro"] .type { + color: var(--ac-color-variableLanguage); + } } + body[data-stscript-style] .hljs.language-stscript { - * { text-shadow: none !important; } + * { + text-shadow: none !important; + } + text-shadow: none !important; background-color: var(--ac-style-color-background); color: var(--ac-style-color-text); - .hljs-title.function_ { color: var(--ac-style-color-cmd); } - .hljs-title.function_.invoke__ { color: var(--ac-style-color-cmd); } - .hljs-string { color: var(--ac-style-color-string); } - .hljs-number { color: var(--ac-style-color-number); } - .hljs-variable { color: var(--ac-style-color-variable); } - .hljs-variable.language_ { color: var(--ac-style-color-variableLanguage); } - .hljs-property { color: var(--ac-style-color-argName); } - .hljs-punctuation { color: var(--ac-style-color-punctuation); } - .hljs-keyword { color: var(--ac-style-color-variableLanguage); } - .hljs-comment { color: var(--ac-style-color-comment); } - .hljs-abort { color: var(--ac-style-color-abort, #e38e23); } + .hljs-title.function_ { + color: var(--ac-style-color-cmd); + } + + .hljs-title.function_.invoke__ { + color: var(--ac-style-color-cmd); + } + + .hljs-string { + color: var(--ac-style-color-string); + } + + .hljs-number { + color: var(--ac-style-color-number); + } + + .hljs-variable { + color: var(--ac-style-color-variable); + } + + .hljs-variable.language_ { + color: var(--ac-style-color-variableLanguage); + } + + .hljs-property { + color: var(--ac-style-color-argName); + } + + .hljs-punctuation { + color: var(--ac-style-color-punctuation); + } + + .hljs-keyword { + color: var(--ac-style-color-variableLanguage); + } + + .hljs-comment { + color: var(--ac-style-color-comment); + } + + .hljs-abort { + color: var(--ac-style-color-abort, #e38e23); + } .hljs-closure { - > .hljs-punctuation { color: var(--ac-style-color-punctuation); } + >.hljs-punctuation { + color: var(--ac-style-color-punctuation); + } + .hljs-closure { - > .hljs-punctuation { color: var(--ac-style-color-punctuationL1); } + >.hljs-punctuation { + color: var(--ac-style-color-punctuationL1); + } + .hljs-closure { - > .hljs-punctuation { color: var(--ac-style-color-punctuationL2); } + >.hljs-punctuation { + color: var(--ac-style-color-punctuationL2); + } + .hljs-closure { - > .hljs-punctuation { color: var(--ac-style-color-punctuation); } + >.hljs-punctuation { + color: var(--ac-style-color-punctuation); + } + .hljs-closure { - > .hljs-punctuation { color: var(--ac-style-color-punctuationL1); } + >.hljs-punctuation { + color: var(--ac-style-color-punctuationL1); + } + .hljs-closure { - > .hljs-punctuation { color: var(--ac-style-color-punctuationL2); } + >.hljs-punctuation { + color: var(--ac-style-color-punctuationL2); + } } } } @@ -1450,44 +1561,52 @@ body[data-stscript-style] .hljs.language-stscript { align-items: center; max-height: calc(95vh - var(--bottom)); container-type: inline-size; - > .item { + + >.item { cursor: pointer; padding: 3px; text-shadow: none; display: flex; gap: 0.5em; display: contents; + @container (max-width: 80em) { .specs { grid-column: 2 / 4; } - > .help { + + >.help { grid-column: 2 / 4; padding-left: 1em; opacity: 0.75; height: auto; } } + &.blank { display: block; grid-column: 1 / 4; } - &:hover > * { + + &:hover>* { background-color: var(--ac-color-hoveredBackground); color: var(--ac-color-hoveredText); } - &.selected > * { + + &.selected>* { background-color: var(--ac-color-selectedBackground); color: var(--ac-color-selectedText); } - > * { + + >* { height: 100%; } - > *+* { + + >*+* { padding-left: 0.5em; } - > .type { + >.type { flex: 0 0 auto; display: inline-flex; justify-content: center; @@ -1501,230 +1620,291 @@ body[data-stscript-style] .hljs.language-stscript { /* &:before { content: "["; } &:after { content: "]"; } */ } - > .specs { + + >.specs { align-items: flex-start; - > .name { - > .matched { + + >.name { + >.matched { background-color: var(--ac-color-matchedBackground); color: var(--ac-color-matchedText); font-weight: bold; } } - > .body { + + >.body { flex-wrap: wrap; column-gap: 0.5em; - > .arguments { + + >.arguments { display: contents; height: 100%; } } } - > .help { + + >.help { height: 100%; - > .helpContent { + + >.helpContent { text-overflow: ellipsis; overflow: hidden; - font-size: 0.9em;white-space: nowrap; + font-size: 0.9em; + white-space: nowrap; line-height: 1.2; max-height: 1.2em; display: block; - > * { + + >* { display: contents; } } } } } + .autoComplete-details { display: flex; flex-direction: column; gap: 0.5em; - > .specs { + + >.specs { cursor: default; flex-direction: column; padding: 0.25em 0.25em 0.5em 0.25em; border-bottom: 1px solid var(--ac-color-border); - > .name { + + >.name { font-weight: bold; color: var(--ac-color-text); cursor: help; + &:hover { text-decoration: 1px dotted underline; } } - > .body { + + >.body { flex-direction: column; gap: 0.5em; - > .arguments { + + >.arguments { margin: 0; padding-left: 1.25em; - > .argumentItem::marker { + + >.argumentItem::marker { color: color-mix(in srgb, var(--ac-color-text), var(--ac-style-color-background)); } .argumentSpec { display: flex; gap: 0.5em; + .argument-default { &:before { content: " = "; color: var(--ac-color-text); } + color: var(--ac-color-string); } } .argument { cursor: help; + &:hover:not(:has(.argument-name:hover, .argument-types:hover, .argument-enums:hover)) { text-decoration: 1px dotted underline; } } + .argument-name, .argument-types, .argument-enums, - .argument-default - { + .argument-default { cursor: help; + &:hover { text-decoration: 1px dotted underline; } } - .argument.optional + .argument-description:before, - .argumentSpec:has(.argument.optional) + .argument-description:before - { + .argument.optional+.argument-description:before, + .argumentSpec:has(.argument.optional)+.argument-description:before { content: "(optional) "; color: var(--ac-color-text); opacity: 0.5; } + .argument-description { margin-left: 0.5em; font-family: var(--mainFontFamily); font-size: 0.9em; } } + .returns { cursor: help; + &:hover { text-decoration: 1px dotted underline; } } } } - > .help { + + >.help { padding: 0 0.5em 0.5em 0.5em; + div { margin-block-end: 1em; } + ul { margin: 0; padding-left: 1.5em; } + pre { margin: 0; - > code { + + >code { display: block; padding: 0; } } } - > .aliases { + + >.aliases { padding: 0 0.5em 0.5em 0.5em; - &:before { content: '(alias: '; } - > .alias { - font-family: monospace; - &+.alias:before { content: ', '; } + + &:before { + content: '(alias: '; + } + + >.alias { + font-family: monospace; + + &+.alias:before { + content: ', '; + } + } + + &:after { + content: ')'; } - &:after { content: ')'; } } } -.autoComplete > .item, .autoComplete-details { - > .specs { +.autoComplete>.item, +.autoComplete-details { + >.specs { display: flex; gap: 0.5em; - > .name { + + >.name { font-family: monospace; white-space: nowrap; /* color: var(--ac-color-text); */ } - > .body { + + >.body { display: flex; - > .arguments { + + >.arguments { font-family: monospace; + .argument { white-space: nowrap; + &.namedArgument { &:before { content: "["; color: var(--ac-color-text); } + &:after { content: "]"; color: var(--ac-color-text); } + &.optional:after { content: "]?"; color: var(--ac-color-text); } - > .argument-name { + + >.argument-name { color: var(--ac-color-argName); } } + &.unnamedArgument { &:before { content: "("; color: var(--ac-color-text); } + &.multiple:before { content: "...("; color: var(--ac-color-text); } + &:after { content: ")"; color: var(--ac-color-text); } + &.optional:after { content: ")?"; color: var(--ac-color-text); } } - > .argument-name + .argument-types:before { + + >.argument-name+.argument-types:before { content: "="; color: var(--ac-color-text); } - > .argument-types { + + >.argument-types { color: var(--ac-color-type); word-break: break-all; white-space: break-spaces; - > .argument-type + .argument-type:before { + + >.argument-type+.argument-type:before { content: "|"; color: var(--ac-color-text); - }; + } + + ; } - > .argument-types + .argument-enums, - > .argument-name + .argument-enums - { + + >.argument-types+.argument-enums, + >.argument-name+.argument-enums { &:before { content: "="; color: var(--ac-color-text); } } - > .argument-enums { + + >.argument-enums { color: var(--ac-color-string); word-break: break-all; white-space: break-spaces; - > .argument-enum + .argument-enum:before { + + >.argument-enum+.argument-enum:before { content: "|"; color: var(--ac-color-text); - }; + } + + ; } } } - > .returns { + + >.returns { font-family: monospace; color: var(--ac-color-text); + &:before { content: "=> "; color: var(--ac-color-symbol); @@ -1733,56 +1913,68 @@ body[data-stscript-style] .hljs.language-stscript { } } } + @media screen and (max-width: 1000px) { .autoComplete-wrap { left: 1vw; right: 1vw; } + .autoComplete-detailsWrap:not(.full) { left: 50vw; } } + .slashCommandBrowser { - > .search { + >.search { display: flex; gap: 1em; align-items: baseline; white-space: nowrap; - > .searchLabel { + + >.searchLabel { flex: 1 1 auto; display: flex; gap: 0.5em; align-items: baseline; - > .searchInput { + + >.searchInput { flex: 1 1 auto; } } - > .searchOptions { + + >.searchOptions { display: flex; gap: 1em; align-items: baseline; } } - > .commandContainer { + + >.commandContainer { display: flex; align-items: flex-start; container-type: inline-size; - > .autoComplete { + + >.autoComplete { flex: 1 1 auto; max-height: unset; - > .isFiltered { + + >.isFiltered { display: none; } + .specs { grid-column: 2 / 4; } + .help { grid-column: 2 / 4; padding-left: 1em; opacity: 0.75; } } - > .autoComplete-detailsWrap { + + >.autoComplete-detailsWrap { flex: 0 0 auto; align-self: stretch; width: 30%; @@ -1791,12 +1983,14 @@ body[data-stscript-style] .hljs.language-stscript { &:before { flex: 0 1 calc(var(--targetOffset) * 1px); } - > .autoComplete-details { + + >.autoComplete-details { max-height: 50vh; } } + @container (max-width: 1000px) { - > .autoComplete-detailsWrap { + >.autoComplete-detailsWrap { width: 50%; max-width: unset; position: absolute; @@ -3371,38 +3565,46 @@ input[type="range"]::-webkit-slider-thumb { --markerWidth: 15px; container-type: inline-size; container-name: doubleRangeContainer; - > .doubleRangeInputContainer { + + >.doubleRangeInputContainer { flex: 0 0 50%; overflow: hidden; position: relative; - > datalist { + + >datalist { display: flex; flex-direction: row; justify-content: space-between; font-size: x-small; - > option { + + >option { flex: 0 0 0; width: 0; display: flex; justify-content: center; } } + @container doubleRangeContainer (max-width: 200px) { - > datalist { + >datalist { height: 2.5em; } - &:nth-child(1) > datalist > option { + + &:nth-child(1)>datalist>option { transform: rotate(-45deg); transform-origin: bottom right; } - &:nth-child(2) > datalist > option { + + &:nth-child(2)>datalist>option { transform: rotate(45deg); transform-origin: bottom left; } } - > input::-webkit-slider-thumb { + + >input::-webkit-slider-thumb { z-index: 2; } + &:after { /* shifted to center to hide corners of the inset shadow */ --shift: 2px; @@ -3422,34 +3624,42 @@ input[type="range"]::-webkit-slider-thumb { background-color: var(--SmartThemeQuoteColor); box-shadow: inset 0 0 2px black; } + &:nth-child(1) { --value: 0; padding-left: 1em; - > input { + + >input { direction: rtl; position: relative; padding-right: max(20px, 20%); } - > datalist { + + >datalist { direction: rtl; padding-right: calc(var(--markerWidth)/2 + max(20px, 20%)); padding-left: calc(var(--markerWidth)/2 - 2px); } + &:after { right: -2px; } } + &:nth-child(2) { --value: 0; padding-right: 1em; - > input { + + >input { position: relative; padding-left: max(20px, 20%); } - > datalist { + + >datalist { padding-left: calc(var(--markerWidth)/2 + max(20px, 20%)); padding-right: calc(var(--markerWidth)/2 - 2px); } + &:after { left: -2px; } @@ -4278,6 +4488,7 @@ a { #CustomCSS-textAreaBlock { display: contents; } + #customCSS { flex: 1 1 auto; } @@ -4666,6 +4877,7 @@ body:not(.movingUI) .drawer-content.maximized { backdrop-filter: blur(var(--SmartThemeBlurStrength)); background-color: var(--SmartThemeBlurTintColor); padding: 5px; + border: 1px solid red; } .zoomed_avatar { @@ -4683,12 +4895,13 @@ body:not(.movingUI) .drawer-content.maximized { position: absolute; height: auto; max-height: 90vh !important; - align-items: end; + align-items: start; } -.zoomed_avatar .dragClose { +/*why were we force hiding the close button again..?*/ +/* .zoomed_avatar .dragClose { display: none; -} +} */ .zoomed_avatar_container { width: 100%; @@ -4887,10 +5100,39 @@ body:not(.movingUI) .drawer-content.maximized { } /* CSS styles using a consistent pastel color palette */ -.regex-brackets { color: #FFB347; } /* Pastel Orange */ -.regex-special { color: #B0E0E6; } /* Powder Blue */ -.regex-quantifier { color: #DDA0DD; } /* Plum */ -.regex-operator { color: #FFB6C1; } /* Light Pink */ -.regex-flags { color: #98FB98; } /* Pale Green */ -.regex-delimiter { font-weight: bold; color: #FF6961; } /* Pastel Red */ -.regex-highlight { color: #FAF8F6; } /* Pastel White */ +.regex-brackets { + color: #FFB347; +} + +/* Pastel Orange */ +.regex-special { + color: #B0E0E6; +} + +/* Powder Blue */ +.regex-quantifier { + color: #DDA0DD; +} + +/* Plum */ +.regex-operator { + color: #FFB6C1; +} + +/* Light Pink */ +.regex-flags { + color: #98FB98; +} + +/* Pale Green */ +.regex-delimiter { + font-weight: bold; + color: #FF6961; +} + +/* Pastel Red */ +.regex-highlight { + color: #FAF8F6; +} + +/* Pastel White */ \ No newline at end of file diff --git a/src/endpoints/content-manager.js b/src/endpoints/content-manager.js index c282dba36..f87520189 100644 --- a/src/endpoints/content-manager.js +++ b/src/endpoints/content-manager.js @@ -509,6 +509,40 @@ async function downloadGenericPng(url) { return null; } +/** + * Parse Risu Realm URL to extract the UUID. + * @param {string} url Risu Realm URL + * @returns {string | null} UUID of the character + */ +function parseRisuUrl(url) { + // Example: https://realm.risuai.net/character/7adb0ed8d81855c820b3506980fb40f054ceef010ff0c4bab73730c0ebe92279 + // or https://realm.risuai.net/character/7adb0ed8-d818-55c8-20b3-506980fb40f0 + const pattern = /^https?:\/\/realm\.risuai\.net\/character\/([a-f0-9-]+)\/?$/i; + const match = url.match(pattern); + return match ? match[1] : null; +} + +/** + * Download RisuAI character card + * @param {string} uuid UUID of the character + * @returns {Promise<{buffer: Buffer, fileName: string, fileType: string}>} + */ +async function downloadRisuCharacter(uuid) { + const result = await fetch(`https://realm.risuai.net/api/v1/download/png-v3/${uuid}?non_commercial=true`); + + if (!result.ok) { + const text = await result.text(); + console.log('RisuAI returned error', result.statusText, text); + throw new Error('Failed to download character'); + } + + const buffer = await result.buffer(); + const fileName = `${sanitize(uuid)}.png`; + const fileType = 'image/png'; + + return { buffer, fileName, fileType }; +} + /** * @param {String} url * @returns {String | null } UUID of the character @@ -563,6 +597,7 @@ router.post('/importURL', jsonParser, async (request, response) => { const isJannnyContent = host.includes('janitorai'); const isPygmalionContent = host.includes('pygmalion.chat'); const isAICharacterCardsContent = host.includes('aicharactercards.com'); + const isRisu = host.includes('realm.risuai.net'); const isGeneric = isHostWhitelisted(host); if (isPygmalionContent) { @@ -603,6 +638,14 @@ router.post('/importURL', jsonParser, async (request, response) => { else { return response.sendStatus(404); } + } else if (isRisu) { + const uuid = parseRisuUrl(url); + if (!uuid) { + return response.sendStatus(404); + } + + type = 'character'; + result = await downloadRisuCharacter(uuid); } else if (isGeneric) { console.log('Downloading from generic url.'); type = 'character';