Merge branch 'staging' into swipe-nums-for-all-mes
| @@ -2,17 +2,26 @@ | ||
| 2 | 2 | pushd %~dp0 |
| 3 | 3 | git --version > nul 2>&1 |
| 4 | 4 | if %errorlevel% neq 0 ( |
| 5 | 5 | echo Git[91mGit is not installed on this system. Skipping update.[0m |
| 6 | - echo If you installed with a zip file, you will need to download the new zip and install it manually. | |
| 6 | + echo Install it from https://git-scm.com/downloads | |
| 7 | + goto end | |
| 7 | 8 | ) else ( |
| 9 | + if not exist .git ( | |
| 10 | + echo [91mNot running from a Git repository. Reinstall using an officially supported method to get updates.[0m | |
| 11 | + echo See: https://docs.sillytavern.app/installation/windows/ | |
| 12 | + goto end | |
| 13 | + ) | |
| 8 | 14 | call git pull --rebase --autostash |
| 9 | 15 | if %errorlevel% neq 0 ( |
| 10 | 16 | REM incase there is still something wrong |
| 11 | 17 | echo There[91mThere were errors while updating. Please download the latest version manually.[0m |
| 18 | + echo See the update FAQ at https://docs.sillytavern.app/usage/update/#common-update-problems | |
| 19 | + goto end | |
| 12 | 20 | ) |
| 13 | 21 | ) |
| 14 | 22 | set NODE_ENV=production |
| 15 | 23 | call npm install --no-audit --no-fund --loglevel=error --no-progress --omit=dev |
| 16 | 24 | node server.js %* |
| 25 | +:end | |
| 17 | 26 | pause |
| 18 | 27 | popd |
| @@ -5,8 +5,14 @@ pushd %~dp0 | ||
| 5 | 5 | echo Checking Git installation |
| 6 | 6 | git --version > nul 2>&1 |
| 7 | 7 | if %errorlevel% neq 0 ( |
| 8 | 8 | echo Git[91mGit is not installed on this system. Skipping update.[0m |
| 9 | - echo If you installed with a zip file, you will need to download the new zip and install it manually. | |
| 9 | + echo Install it from https://git-scm.com/downloads | |
| 10 | + goto end | |
| 11 | +) | |
| 12 | + | |
| 13 | +if not exist .git ( | |
| 14 | + echo [91mNot running from a Git repository. Reinstall using an officially supported method to get updates.[0m | |
| 15 | + echo See: https://docs.sillytavern.app/installation/windows/ | |
| 10 | 16 | goto end |
| 11 | 17 | ) |
| 12 | 18 | |
| @@ -89,7 +95,8 @@ git pull --rebase --autostash origin %TARGET_BRANCH% | ||
| 89 | 95 | |
| 90 | 96 | :install |
| 91 | 97 | if %errorlevel% neq 0 ( |
| 92 | 98 | echo There[91mThere were errors while updating. Please check manually.[0m |
| 99 | + echo See the update FAQ at https://docs.sillytavern.app/usage/update/#common-update-problems | |
| 93 | 100 | goto end |
| 94 | 101 | ) |
| 95 | 102 | |
| @@ -118,7 +118,6 @@ extras: | ||
| 118 | 118 | classificationModel: Cohee/distilbert-base-uncased-go-emotions-onnx |
| 119 | 119 | captioningModel: Xenova/vit-gpt2-image-captioning |
| 120 | 120 | embeddingModel: Cohee/jina-embeddings-v2-base-en |
| 121 | - promptExpansionModel: Cohee/fooocus_expansion-onnx | |
| 122 | 121 | speechToTextModel: Xenova/whisper-small |
| 123 | 122 | textToSpeechModel: Xenova/speecht5_tts |
| 124 | 123 | # -- OPENAI CONFIGURATION -- |
| @@ -4797,7 +4797,7 @@ export function removeMacros(str) { | ||
| 4797 | 4797 | * @param {boolean} [compact] Send as a compact display message. |
| 4798 | 4798 | * @param {string} [name] Name of the user sending the message. Defaults to name1. |
| 4799 | 4799 | * @param {string} [avatar] Avatar of the user sending the message. Defaults to user_avatar. |
| 4800 | 4800 | * @returns {Promise<voidany>} A promise that resolves whento the message when it is inserted. |
| 4801 | 4801 | */ |
| 4802 | 4802 | export async function sendMessageAsUser(messageText, messageBias, insertAt = null, compact = false, name = name1, avatar = user_avatar) { |
| 4803 | 4803 | messageText = getRegexedString(messageText, regex_placement.USER_INPUT); |
| @@ -4844,6 +4844,8 @@ export async function sendMessageAsUser(messageText, messageBias, insertAt = nul | ||
| 4844 | 4844 | await eventSource.emit(event_types.USER_MESSAGE_RENDERED, chat_id); |
| 4845 | 4845 | await saveChatConditional(); |
| 4846 | 4846 | } |
| 4847 | + | |
| 4848 | + return message; | |
| 4847 | 4849 | } |
| 4848 | 4850 | |
| 4849 | 4851 | /** |
| @@ -12,6 +12,8 @@ import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from ' | ||
| 12 | 12 | import { isFunctionCallingSupported } from '../../openai.js'; |
| 13 | 13 | import { SlashCommandEnumValue, enumTypes } from '../../slash-commands/SlashCommandEnumValue.js'; |
| 14 | 14 | import { commonEnumProviders } from '../../slash-commands/SlashCommandCommonEnumsProvider.js'; |
| 15 | +import { slashCommandReturnHelper } from '../../slash-commands/SlashCommandReturnHelper.js'; | |
| 16 | +import { SlashCommandClosure } from '../../slash-commands/SlashCommandClosure.js'; | |
| 15 | 17 | export { MODULE_NAME }; |
| 16 | 18 | |
| 17 | 19 | const MODULE_NAME = 'expressions'; |
| @@ -2134,18 +2136,42 @@ function migrateSettings() { | ||
| 2134 | 2136 | name: 'classify-expressions', |
| 2135 | 2137 | aliases: ['expressions'], |
| 2136 | 2138 | callback: async (args) => { |
| 2137 | - const list = await getExpressionsList(); | |
| 2139 | + /** @type {import('../../slash-commands/SlashCommandReturnHelper.js').SlashCommandReturnType} */ | |
| 2138 | - switch (String(args.format).toLowerCase()) { | |
| 2140 | + // @ts-ignore | |
| 2139 | - case 'json': | |
| 2141 | + let returnType = args.return; | |
| 2140 | - return JSON.stringify(list); | |
| 2142 | + | |
| 2141 | - default: | |
| 2143 | + // Old legacy return type handling | |
| 2142 | - return list.join(', '); | |
| 2144 | + if (args.format) { | |
| 2145 | + toastr.warning(`Legacy argument 'format' with value '${args.format}' is deprecated. Please use 'return' instead. Routing to the correct return type...`, 'Deprecation warning'); | |
| 2146 | + const type = String(args?.format).toLowerCase().trim(); | |
| 2147 | + switch (type) { | |
| 2148 | + case 'json': | |
| 2149 | + returnType = 'object'; | |
| 2150 | + break; | |
| 2151 | + default: | |
| 2152 | + returnType = 'pipe'; | |
| 2153 | + break; | |
| 2154 | + } | |
| 2143 | 2155 | } |
| 2156 | + | |
| 2157 | + // Now the actual new return type handling | |
| 2158 | + const list = await getExpressionsList(); | |
| 2159 | + | |
| 2160 | + return await slashCommandReturnHelper.doReturn(returnType ?? 'pipe', list, { objectToStringFunc: list => list.join(', ') }); | |
| 2144 | 2161 | }, |
| 2145 | 2162 | namedArgumentList: [ |
| 2146 | 2163 | SlashCommandNamedArgument.fromProps({ |
| 2164 | + name: 'return', | |
| 2165 | + description: 'The way how you want the return value to be provided', | |
| 2166 | + typeList: [ARGUMENT_TYPE.STRING], | |
| 2167 | + defaultValue: 'pipe', | |
| 2168 | + enumList: slashCommandReturnHelper.enumList({ allowObject: true }), | |
| 2169 | + forceEnum: true, | |
| 2170 | + }), | |
| 2171 | + // TODO remove some day | |
| 2172 | + SlashCommandNamedArgument.fromProps({ | |
| 2147 | 2173 | name: 'format', |
| 2148 | 2174 | description: '!!! DEPRECATED - use "return" instead !!! The format to return the list in: comma-separated plain text or JSON array. Default is plain text.', |
| 2149 | 2175 | typeList: [ARGUMENT_TYPE.STRING], |
| 2150 | 2176 | enumList: [ |
| 2151 | 2177 | new SlashCommandEnumValue('plain', null, enumTypes.enum, ', '), |
| @@ -20,7 +20,7 @@ import { | ||
| 20 | 20 | } from '../../../script.js'; |
| 21 | 21 | import { getApiUrl, getContext, extension_settings, doExtrasFetch, modules, renderExtensionTemplateAsync, writeExtensionField } from '../../extensions.js'; |
| 22 | 22 | import { selected_group } from '../../group-chats.js'; |
| 23 | 23 | import { stringFormat, initScrollHeight, resetScrollHeight, getCharaFilename, saveBase64AsFile, getBase64Async, delay, isTrueBoolean, debounce, isFalseBoolean } from '../../utils.js'; |
| 24 | 24 | import { getMessageTimeStamp, humanizedDateTime } from '../../RossAscends-mods.js'; |
| 25 | 25 | import { SECRET_KEYS, secret_state, writeSecret } from '../../secrets.js'; |
| 26 | 26 | import { getNovelUnlimitedImageGeneration, getNovelAnlas, loadNovelSubscriptionData } from '../../nai-settings.js'; |
| @@ -31,6 +31,7 @@ import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from ' | ||
| 31 | 31 | import { debounce_timeout } from '../../constants.js'; |
| 32 | 32 | import { SlashCommandEnumValue } from '../../slash-commands/SlashCommandEnumValue.js'; |
| 33 | 33 | import { POPUP_RESULT, POPUP_TYPE, Popup, callGenericPopup } from '../../popup.js'; |
| 34 | +import { commonEnumProviders } from '../../slash-commands/SlashCommandCommonEnumsProvider.js'; | |
| 34 | 35 | export { MODULE_NAME }; |
| 35 | 36 | |
| 36 | 37 | const MODULE_NAME = 'sd'; |
| @@ -221,7 +222,6 @@ const defaultSettings = { | ||
| 221 | 222 | |
| 222 | 223 | // Refine mode |
| 223 | 224 | refine_mode: false, |
| 224 | - expand: false, | |
| 225 | 225 | interactive_mode: false, |
| 226 | 226 | multimodal_captioning: false, |
| 227 | 227 | snap: false, |
| @@ -240,7 +240,7 @@ const defaultSettings = { | ||
| 240 | 240 | drawthings_auth: '', |
| 241 | 241 | |
| 242 | 242 | hr_upscaler: 'Latent', |
| 243 | 243 | hr_scale: 21.0, |
| 244 | 244 | hr_scale_min: 1.0, |
| 245 | 245 | hr_scale_max: 4.0, |
| 246 | 246 | hr_scale_step: 0.1, |
| @@ -260,10 +260,6 @@ const defaultSettings = { | ||
| 260 | 260 | clip_skip: 1, |
| 261 | 261 | |
| 262 | 262 | // NovelAI settings |
| 263 | - novel_upscale_ratio_min: 1.0, | |
| 264 | - novel_upscale_ratio_max: 4.0, | |
| 265 | - novel_upscale_ratio_step: 0.1, | |
| 266 | - novel_upscale_ratio: 1.0, | |
| 267 | 263 | novel_anlas_guard: false, |
| 268 | 264 | novel_sm: false, |
| 269 | 265 | novel_sm_dyn: false, |
| @@ -416,7 +412,6 @@ async function loadSettings() { | ||
| 416 | 412 | $('#sd_hr_scale').val(extension_settings.sd.hr_scale).trigger('input'); |
| 417 | 413 | $('#sd_denoising_strength').val(extension_settings.sd.denoising_strength).trigger('input'); |
| 418 | 414 | $('#sd_hr_second_pass_steps').val(extension_settings.sd.hr_second_pass_steps).trigger('input'); |
| 419 | - $('#sd_novel_upscale_ratio').val(extension_settings.sd.novel_upscale_ratio).trigger('input'); | |
| 420 | 415 | $('#sd_novel_anlas_guard').prop('checked', extension_settings.sd.novel_anlas_guard); |
| 421 | 416 | $('#sd_novel_sm').prop('checked', extension_settings.sd.novel_sm); |
| 422 | 417 | $('#sd_novel_sm_dyn').prop('checked', extension_settings.sd.novel_sm_dyn); |
| @@ -430,7 +425,6 @@ async function loadSettings() { | ||
| 430 | 425 | $('#sd_restore_faces').prop('checked', extension_settings.sd.restore_faces); |
| 431 | 426 | $('#sd_enable_hr').prop('checked', extension_settings.sd.enable_hr); |
| 432 | 427 | $('#sd_refine_mode').prop('checked', extension_settings.sd.refine_mode); |
| 433 | - $('#sd_expand').prop('checked', extension_settings.sd.expand); | |
| 434 | 428 | $('#sd_multimodal_captioning').prop('checked', extension_settings.sd.multimodal_captioning); |
| 435 | 429 | $('#sd_auto_url').val(extension_settings.sd.auto_url); |
| 436 | 430 | $('#sd_auto_auth').val(extension_settings.sd.auto_auth); |
| @@ -644,37 +638,13 @@ async function onSaveStyleClick() { | ||
| 644 | 638 | saveSettingsDebounced(); |
| 645 | 639 | } |
| 646 | 640 | |
| 647 | -async function expandPrompt(prompt) { | |
| 648 | - try { | |
| 649 | - const response = await fetch('/api/sd/expand', { | |
| 650 | - method: 'POST', | |
| 651 | - headers: getRequestHeaders(), | |
| 652 | - body: JSON.stringify({ prompt: prompt }), | |
| 653 | - }); | |
| 654 | - | |
| 655 | - if (!response.ok) { | |
| 656 | - throw new Error('API returned an error.'); | |
| 657 | - } | |
| 658 | - | |
| 659 | - const data = await response.json(); | |
| 660 | - return data.prompt; | |
| 661 | - } catch { | |
| 662 | - return prompt; | |
| 663 | - } | |
| 664 | -} | |
| 665 | - | |
| 666 | 641 | /** |
| 667 | 642 | * Modifies prompt based on auto-expansion and user inputs. |
| 668 | 643 | * @param {string} prompt Prompt to refine |
| 669 | - * @param {boolean} allowExpand Whether to allow auto-expansion | |
| 670 | 644 | * @param {boolean} isNegative Whether the prompt is a negative one |
| 671 | 645 | * @returns {Promise<string>} Refined prompt |
| 672 | 646 | */ |
| 673 | 647 | async function refinePrompt(prompt, allowExpand, isNegative = false) { |
| 674 | - if (allowExpand && extension_settings.sd.expand) { | |
| 675 | - prompt = await expandPrompt(prompt); | |
| 676 | - } | |
| 677 | - | |
| 678 | 648 | if (extension_settings.sd.refine_mode) { |
| 679 | 649 | const text = isNegative ? '<h3>Review and edit the <i>negative</i> prompt:</h3>' : '<h3>Review and edit the prompt:</h3>'; |
| 680 | 650 | const refinedPrompt = await callGenericPopup(text + 'Press "Cancel" to abort the image generation.', POPUP_TYPE.INPUT, prompt.trim(), { rows: 5, okButton: 'Continue' }); |
| @@ -800,11 +770,6 @@ function combinePrefixes(str1, str2, macro = '') { | ||
| 800 | 770 | return process(result); |
| 801 | 771 | } |
| 802 | 772 | |
| 803 | -function onExpandInput() { | |
| 804 | - extension_settings.sd.expand = !!$(this).prop('checked'); | |
| 805 | - saveSettingsDebounced(); | |
| 806 | -} | |
| 807 | - | |
| 808 | 773 | function onRefineModeInput() { |
| 809 | 774 | extension_settings.sd.refine_mode = !!$('#sd_refine_mode').prop('checked'); |
| 810 | 775 | saveSettingsDebounced(); |
| @@ -969,12 +934,6 @@ async function onViewAnlasClick() { | ||
| 969 | 934 | toastr.info(`Free image generation: ${unlimitedGeneration ? 'Yes' : 'No'}`, `Anlas: ${anlas}`); |
| 970 | 935 | } |
| 971 | 936 | |
| 972 | -function onNovelUpscaleRatioInput() { | |
| 973 | - extension_settings.sd.novel_upscale_ratio = Number($('#sd_novel_upscale_ratio').val()); | |
| 974 | - $('#sd_novel_upscale_ratio_value').val(extension_settings.sd.novel_upscale_ratio.toFixed(1)); | |
| 975 | - saveSettingsDebounced(); | |
| 976 | -} | |
| 977 | - | |
| 978 | 937 | function onNovelAnlasGuardInput() { |
| 979 | 938 | extension_settings.sd.novel_anlas_guard = !!$('#sd_novel_anlas_guard').prop('checked'); |
| 980 | 939 | saveSettingsDebounced(); |
| @@ -2273,6 +2232,25 @@ function getRawLastMessage() { | ||
| 2273 | 2232 | } |
| 2274 | 2233 | |
| 2275 | 2234 | /** |
| 2235 | + * Ensure that the selected option exists in the dropdown. | |
| 2236 | + * @param {string} setting Setting key | |
| 2237 | + * @param {string} selector Dropdown selector | |
| 2238 | + * @returns {void} | |
| 2239 | + */ | |
| 2240 | +function ensureSelectionExists(setting, selector) { | |
| 2241 | + /** @type {HTMLSelectElement} */ | |
| 2242 | + const selectElement = document.querySelector(selector); | |
| 2243 | + if (!selectElement) { | |
| 2244 | + return; | |
| 2245 | + } | |
| 2246 | + const options = Array.from(selectElement.options); | |
| 2247 | + const value = extension_settings.sd[setting]; | |
| 2248 | + if (selectElement.selectedOptions.length && !options.some(option => option.value === value)) { | |
| 2249 | + extension_settings.sd[setting] = selectElement.selectedOptions[0].value; | |
| 2250 | + } | |
| 2251 | +} | |
| 2252 | + | |
| 2253 | +/** | |
| 2276 | 2254 | * Generates an image based on the given trigger word. |
| 2277 | 2255 | * @param {string} initiator The initiator of the image generation |
| 2278 | 2256 | * @param {Record<string, object>} args Command arguments |
| @@ -2292,8 +2270,8 @@ async function generatePicture(initiator, args, trigger, message, callback) { | ||
| 2292 | 2270 | return; |
| 2293 | 2271 | } |
| 2294 | 2272 | |
| 2295 | - extension_settings.sd.sampler = $('#sd_sampler').find(':selected').val(); | |
| 2273 | + ensureSelectionExists('sampler', '#sd_sampler'); | |
| 2296 | - extension_settings.sd.model = $('#sd_model').find(':selected').val(); | |
| 2274 | + ensureSelectionExists('model', '#sd_model'); | |
| 2297 | 2275 | |
| 2298 | 2276 | trigger = trigger.trim(); |
| 2299 | 2277 | const generationType = getGenerationType(trigger); |
| @@ -2441,7 +2419,7 @@ async function getPrompt(generationType, message, trigger, quietPrompt, combineN | ||
| 2441 | 2419 | } |
| 2442 | 2420 | |
| 2443 | 2421 | if (generationType !== generationMode.FREE) { |
| 2444 | 2422 | prompt = await refinePrompt(prompt, truefalse); |
| 2445 | 2423 | } |
| 2446 | 2424 | |
| 2447 | 2425 | return prompt; |
| @@ -2469,7 +2447,7 @@ function generateFreeModePrompt(trigger, combineNegatives) { | ||
| 2469 | 2447 | return message.original_avatar.replace(/\.[^/.]+$/, ''); |
| 2470 | 2448 | } |
| 2471 | 2449 | } |
| 2472 | - throw new Error('No usable messages found.'); | |
| 2450 | + return ''; | |
| 2473 | 2451 | }; |
| 2474 | 2452 | |
| 2475 | 2453 | const key = getLastCharacterKey(); |
| @@ -3031,7 +3009,7 @@ async function generateNovelImage(prompt, negativePrompt, signal) { | ||
| 3031 | 3009 | width: width, |
| 3032 | 3010 | height: height, |
| 3033 | 3011 | negative_prompt: negativePrompt, |
| 3034 | 3012 | upscale_ratio: extension_settings.sd.novel_upscale_ratiohr_scale, |
| 3035 | 3013 | decrisper: extension_settings.sd.novel_decrisper, |
| 3036 | 3014 | sm: sm, |
| 3037 | 3015 | sm_dyn: sm_dyn, |
| @@ -3613,8 +3591,8 @@ async function sdMessageButton(e) { | ||
| 3613 | 3591 | try { |
| 3614 | 3592 | setBusyIcon(true); |
| 3615 | 3593 | if (hasSavedImage) { |
| 3616 | 3594 | const prompt = await refinePrompt(message.extra.title, false, false); |
| 3617 | 3595 | const negative = hasSavedNegative ? await refinePrompt(message.extra.negative, false, true) : ''; |
| 3618 | 3596 | message.extra.title = prompt; |
| 3619 | 3597 | |
| 3620 | 3598 | const generationType = message?.extra?.generationType ?? generationMode.FREE; |
| @@ -3756,8 +3734,8 @@ async function onImageSwiped({ message, element, direction }) { | ||
| 3756 | 3734 | eventSource.once(CUSTOM_STOP_EVENT, stopListener); |
| 3757 | 3735 | const callback = () => { }; |
| 3758 | 3736 | const hasNegative = message.extra.negative; |
| 3759 | 3737 | const prompt = await refinePrompt(message.extra.title, false, false); |
| 3760 | 3738 | const negativePromptPrefix = hasNegative ? await refinePrompt(message.extra.negative, false, true) : ''; |
| 3761 | 3739 | const characterName = context.groupId |
| 3762 | 3740 | ? context.groups[Object.keys(context.groups).filter(x => context.groups[x].id === context.groupId)[0]]?.id?.toString() |
| 3763 | 3741 | : context.characters[context.characterId]?.name; |
| @@ -3788,12 +3766,85 @@ async function onImageSwiped({ message, element, direction }) { | ||
| 3788 | 3766 | await context.saveChat(); |
| 3789 | 3767 | } |
| 3790 | 3768 | |
| 3769 | +/** | |
| 3770 | + * Applies the command arguments to the extension settings. | |
| 3771 | + * @typedef {import('../../slash-commands/SlashCommand.js').NamedArguments} NamedArguments | |
| 3772 | + * @typedef {import('../../slash-commands/SlashCommand.js').NamedArgumentsCapture} NamedArgumentsCapture | |
| 3773 | + * @param {NamedArguments | NamedArgumentsCapture} args - Command arguments | |
| 3774 | + * @returns {Record<string, any>} - Current settings before applying the command arguments | |
| 3775 | + */ | |
| 3776 | +function applyCommandArguments(args) { | |
| 3777 | + const overrideSettings = {}; | |
| 3778 | + const currentSettings = {}; | |
| 3779 | + const settingMap = { | |
| 3780 | + 'edit': 'refine_mode', | |
| 3781 | + 'extend': 'free_extend', | |
| 3782 | + 'multimodal': 'multimodal_captioning', | |
| 3783 | + 'seed': 'seed', | |
| 3784 | + 'width': 'width', | |
| 3785 | + 'height': 'height', | |
| 3786 | + 'steps': 'steps', | |
| 3787 | + 'cfg': 'scale', | |
| 3788 | + 'skip': 'clip_skip', | |
| 3789 | + 'model': 'model', | |
| 3790 | + 'sampler': 'sampler', | |
| 3791 | + 'scheduler': 'scheduler', | |
| 3792 | + 'vae': 'vae', | |
| 3793 | + 'upscaler': 'hr_upscaler', | |
| 3794 | + 'scale': 'hr_scale', | |
| 3795 | + 'hires': 'enable_hr', | |
| 3796 | + 'denoise': 'denoising_strength', | |
| 3797 | + '2ndpass': 'hr_second_pass_steps', | |
| 3798 | + 'faces': 'restore_faces', | |
| 3799 | + }; | |
| 3800 | + | |
| 3801 | + for (const [param, setting] of Object.entries(settingMap)) { | |
| 3802 | + if (args[param] === undefined || defaultSettings[setting] === undefined) { | |
| 3803 | + continue; | |
| 3804 | + } | |
| 3805 | + currentSettings[setting] = extension_settings.sd[setting]; | |
| 3806 | + const value = String(args[param]); | |
| 3807 | + const type = typeof defaultSettings[setting]; | |
| 3808 | + switch (type) { | |
| 3809 | + case 'boolean': | |
| 3810 | + overrideSettings[setting] = isTrueBoolean(value) || !isFalseBoolean(value); | |
| 3811 | + break; | |
| 3812 | + case 'number': | |
| 3813 | + overrideSettings[setting] = Number(value); | |
| 3814 | + break; | |
| 3815 | + default: | |
| 3816 | + overrideSettings[setting] = value; | |
| 3817 | + break; | |
| 3818 | + } | |
| 3819 | + } | |
| 3820 | + | |
| 3821 | + Object.assign(extension_settings.sd, overrideSettings); | |
| 3822 | + return currentSettings; | |
| 3823 | +} | |
| 3824 | + | |
| 3791 | 3825 | jQuery(async () => { |
| 3792 | 3826 | await addSDGenButtons(); |
| 3793 | 3827 | |
| 3828 | + const getSelectEnumProvider = (id, text) => () => Array.from(document.querySelectorAll(`#${id} > [value]`)).map(x => new SlashCommandEnumValue(x.getAttribute('value'), text ? x.textContent : null)); | |
| 3829 | + | |
| 3794 | 3830 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| 3795 | 3831 | name: 'imagine', |
| 3796 | - callback: (args, trigger) => generatePicture(initiators.command, args, String(trigger)), | |
| 3832 | + returns: 'URL of the generated image, or an empty string if the generation failed', | |
| 3833 | + callback: async (args, trigger) => { | |
| 3834 | + const currentSettings = applyCommandArguments(args); | |
| 3835 | + | |
| 3836 | + try { | |
| 3837 | + return await generatePicture(initiators.command, args, String(trigger)); | |
| 3838 | + } catch (error) { | |
| 3839 | + console.error('Failed to generate image:', error); | |
| 3840 | + return ''; | |
| 3841 | + } finally { | |
| 3842 | + if (Object.keys(currentSettings).length) { | |
| 3843 | + Object.assign(extension_settings.sd, currentSettings); | |
| 3844 | + saveSettingsDebounced(); | |
| 3845 | + } | |
| 3846 | + } | |
| 3847 | + }, | |
| 3797 | 3848 | aliases: ['sd', 'img', 'image'], |
| 3798 | 3849 | namedArgumentList: [ |
| 3799 | 3850 | new SlashCommandNamedArgument( |
| @@ -3803,6 +3854,164 @@ jQuery(async () => { | ||
| 3803 | 3854 | name: 'negative', |
| 3804 | 3855 | description: 'negative prompt prefix', |
| 3805 | 3856 | typeList: [ARGUMENT_TYPE.STRING], |
| 3857 | + isRequired: false, | |
| 3858 | + acceptsMultiple: false, | |
| 3859 | + }), | |
| 3860 | + SlashCommandNamedArgument.fromProps({ | |
| 3861 | + name: 'extend', | |
| 3862 | + description: 'auto-extend free mode prompts with the LLM', | |
| 3863 | + typeList: [ARGUMENT_TYPE.BOOLEAN], | |
| 3864 | + enumProvider: commonEnumProviders.boolean('trueFalse'), | |
| 3865 | + isRequired: false, | |
| 3866 | + acceptsMultiple: false, | |
| 3867 | + }), | |
| 3868 | + SlashCommandNamedArgument.fromProps({ | |
| 3869 | + name: 'edit', | |
| 3870 | + description: 'edit the prompt before generation', | |
| 3871 | + typeList: [ARGUMENT_TYPE.BOOLEAN], | |
| 3872 | + enumProvider: commonEnumProviders.boolean('trueFalse'), | |
| 3873 | + isRequired: false, | |
| 3874 | + acceptsMultiple: false, | |
| 3875 | + }), | |
| 3876 | + SlashCommandNamedArgument.fromProps({ | |
| 3877 | + name: 'multimodal', | |
| 3878 | + description: 'use multimodal captioning (for portraits only)', | |
| 3879 | + typeList: [ARGUMENT_TYPE.BOOLEAN], | |
| 3880 | + enumProvider: commonEnumProviders.boolean('trueFalse'), | |
| 3881 | + isRequired: false, | |
| 3882 | + acceptsMultiple: false, | |
| 3883 | + }), | |
| 3884 | + SlashCommandNamedArgument.fromProps({ | |
| 3885 | + name: 'snap', | |
| 3886 | + description: 'snap auto-adjusted dimensions to the nearest known resolution (portraits and backgrounds only)', | |
| 3887 | + typeList: [ARGUMENT_TYPE.BOOLEAN], | |
| 3888 | + enumProvider: commonEnumProviders.boolean('trueFalse'), | |
| 3889 | + isRequired: false, | |
| 3890 | + acceptsMultiple: false, | |
| 3891 | + }), | |
| 3892 | + SlashCommandNamedArgument.fromProps({ | |
| 3893 | + name: 'seed', | |
| 3894 | + description: 'random seed', | |
| 3895 | + isRequired: false, | |
| 3896 | + typeList: [ARGUMENT_TYPE.NUMBER], | |
| 3897 | + acceptsMultiple: false, | |
| 3898 | + }), | |
| 3899 | + SlashCommandNamedArgument.fromProps({ | |
| 3900 | + name: 'width', | |
| 3901 | + description: 'image width', | |
| 3902 | + isRequired: false, | |
| 3903 | + typeList: [ARGUMENT_TYPE.NUMBER], | |
| 3904 | + acceptsMultiple: false, | |
| 3905 | + }), | |
| 3906 | + SlashCommandNamedArgument.fromProps({ | |
| 3907 | + name: 'height', | |
| 3908 | + description: 'image height', | |
| 3909 | + isRequired: false, | |
| 3910 | + typeList: [ARGUMENT_TYPE.NUMBER], | |
| 3911 | + acceptsMultiple: false, | |
| 3912 | + }), | |
| 3913 | + SlashCommandNamedArgument.fromProps({ | |
| 3914 | + name: 'steps', | |
| 3915 | + description: 'number of steps', | |
| 3916 | + isRequired: false, | |
| 3917 | + typeList: [ARGUMENT_TYPE.NUMBER], | |
| 3918 | + acceptsMultiple: false, | |
| 3919 | + }), | |
| 3920 | + SlashCommandNamedArgument.fromProps({ | |
| 3921 | + name: 'cfg', | |
| 3922 | + description: 'CFG scale', | |
| 3923 | + isRequired: false, | |
| 3924 | + typeList: [ARGUMENT_TYPE.NUMBER], | |
| 3925 | + acceptsMultiple: false, | |
| 3926 | + }), | |
| 3927 | + SlashCommandNamedArgument.fromProps({ | |
| 3928 | + name: 'skip', | |
| 3929 | + description: 'CLIP skip layers', | |
| 3930 | + isRequired: false, | |
| 3931 | + typeList: [ARGUMENT_TYPE.NUMBER], | |
| 3932 | + acceptsMultiple: false, | |
| 3933 | + }), | |
| 3934 | + SlashCommandNamedArgument.fromProps({ | |
| 3935 | + name: 'model', | |
| 3936 | + description: 'model override', | |
| 3937 | + isRequired: false, | |
| 3938 | + typeList: [ARGUMENT_TYPE.STRING], | |
| 3939 | + acceptsMultiple: false, | |
| 3940 | + forceEnum: true, | |
| 3941 | + enumProvider: getSelectEnumProvider('sd_model', true), | |
| 3942 | + }), | |
| 3943 | + SlashCommandNamedArgument.fromProps({ | |
| 3944 | + name: 'sampler', | |
| 3945 | + description: 'sampler override', | |
| 3946 | + isRequired: false, | |
| 3947 | + typeList: [ARGUMENT_TYPE.STRING], | |
| 3948 | + acceptsMultiple: false, | |
| 3949 | + forceEnum: true, | |
| 3950 | + enumProvider: getSelectEnumProvider('sd_sampler', false), | |
| 3951 | + }), | |
| 3952 | + SlashCommandNamedArgument.fromProps({ | |
| 3953 | + name: 'scheduler', | |
| 3954 | + description: 'scheduler override', | |
| 3955 | + isRequired: false, | |
| 3956 | + typeList: [ARGUMENT_TYPE.STRING], | |
| 3957 | + acceptsMultiple: false, | |
| 3958 | + forceEnum: true, | |
| 3959 | + enumProvider: getSelectEnumProvider('sd_scheduler', false), | |
| 3960 | + }), | |
| 3961 | + SlashCommandNamedArgument.fromProps({ | |
| 3962 | + name: 'vae', | |
| 3963 | + description: 'VAE name override', | |
| 3964 | + isRequired: false, | |
| 3965 | + typeList: [ARGUMENT_TYPE.STRING], | |
| 3966 | + acceptsMultiple: false, | |
| 3967 | + forceEnum: true, | |
| 3968 | + enumProvider: getSelectEnumProvider('sd_vae', false), | |
| 3969 | + }), | |
| 3970 | + SlashCommandNamedArgument.fromProps({ | |
| 3971 | + name: 'upscaler', | |
| 3972 | + description: 'upscaler override', | |
| 3973 | + isRequired: false, | |
| 3974 | + typeList: [ARGUMENT_TYPE.STRING], | |
| 3975 | + acceptsMultiple: false, | |
| 3976 | + forceEnum: true, | |
| 3977 | + enumProvider: getSelectEnumProvider('sd_hr_upscaler', false), | |
| 3978 | + }), | |
| 3979 | + SlashCommandNamedArgument.fromProps({ | |
| 3980 | + name: 'hires', | |
| 3981 | + description: 'enable high-res fix', | |
| 3982 | + isRequired: false, | |
| 3983 | + typeList: [ARGUMENT_TYPE.BOOLEAN], | |
| 3984 | + acceptsMultiple: false, | |
| 3985 | + enumProvider: commonEnumProviders.boolean('trueFalse'), | |
| 3986 | + }), | |
| 3987 | + SlashCommandNamedArgument.fromProps({ | |
| 3988 | + name: 'scale', | |
| 3989 | + description: 'upscale amount', | |
| 3990 | + isRequired: false, | |
| 3991 | + typeList: [ARGUMENT_TYPE.NUMBER], | |
| 3992 | + acceptsMultiple: false, | |
| 3993 | + }), | |
| 3994 | + SlashCommandNamedArgument.fromProps({ | |
| 3995 | + name: 'denoise', | |
| 3996 | + description: 'denoising strength', | |
| 3997 | + isRequired: false, | |
| 3998 | + typeList: [ARGUMENT_TYPE.NUMBER], | |
| 3999 | + acceptsMultiple: false, | |
| 4000 | + }), | |
| 4001 | + SlashCommandNamedArgument.fromProps({ | |
| 4002 | + name: '2ndpass', | |
| 4003 | + description: 'second pass steps', | |
| 4004 | + isRequired: false, | |
| 4005 | + typeList: [ARGUMENT_TYPE.NUMBER], | |
| 4006 | + acceptsMultiple: false, | |
| 4007 | + }), | |
| 4008 | + SlashCommandNamedArgument.fromProps({ | |
| 4009 | + name: 'faces', | |
| 4010 | + description: 'restore faces', | |
| 4011 | + isRequired: false, | |
| 4012 | + typeList: [ARGUMENT_TYPE.BOOLEAN], | |
| 4013 | + acceptsMultiple: false, | |
| 4014 | + enumProvider: commonEnumProviders.boolean('trueFalse'), | |
| 3806 | 4015 | }), |
| 3807 | 4016 | ], |
| 3808 | 4017 | unnamedArgumentList: [ |
| @@ -3824,6 +4033,66 @@ jQuery(async () => { | ||
| 3824 | 4033 | })); |
| 3825 | 4034 | |
| 3826 | 4035 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| 4036 | + name: 'imagine-source', | |
| 4037 | + aliases: ['sd-source', 'img-source'], | |
| 4038 | + returns: 'a name of the current generation source', | |
| 4039 | + unnamedArgumentList: [ | |
| 4040 | + SlashCommandArgument.fromProps({ | |
| 4041 | + description: 'source name', | |
| 4042 | + typeList: [ARGUMENT_TYPE.STRING], | |
| 4043 | + isRequired: false, | |
| 4044 | + forceEnum: true, | |
| 4045 | + enumProvider: getSelectEnumProvider('sd_source', true), | |
| 4046 | + }), | |
| 4047 | + ], | |
| 4048 | + helpString: 'If an argument is provided, change the source of the image generation, e.g. <code>/imagine-source comfy</code>. Returns the current source.', | |
| 4049 | + callback: async (_args, name) => { | |
| 4050 | + if (!name) { | |
| 4051 | + return extension_settings.sd.source; | |
| 4052 | + } | |
| 4053 | + const isKnownSource = Object.keys(sources).includes(String(name)); | |
| 4054 | + if (!isKnownSource) { | |
| 4055 | + throw new Error('The value provided is not a valid image generation source.'); | |
| 4056 | + } | |
| 4057 | + const option = document.querySelector(`#sd_source [value="${name}"]`); | |
| 4058 | + if (!(option instanceof HTMLOptionElement)) { | |
| 4059 | + throw new Error('Could not find the source option in the dropdown.'); | |
| 4060 | + } | |
| 4061 | + option.selected = true; | |
| 4062 | + await onSourceChange(); | |
| 4063 | + return extension_settings.sd.source; | |
| 4064 | + }, | |
| 4065 | + })); | |
| 4066 | + | |
| 4067 | + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | |
| 4068 | + name: 'imagine-style', | |
| 4069 | + aliases: ['sd-style', 'img-style'], | |
| 4070 | + returns: 'a name of the current style', | |
| 4071 | + unnamedArgumentList: [ | |
| 4072 | + SlashCommandArgument.fromProps({ | |
| 4073 | + description: 'style name', | |
| 4074 | + typeList: [ARGUMENT_TYPE.STRING], | |
| 4075 | + isRequired: false, | |
| 4076 | + forceEnum: true, | |
| 4077 | + enumProvider: getSelectEnumProvider('sd_style', false), | |
| 4078 | + }), | |
| 4079 | + ], | |
| 4080 | + helpString: 'If an argument is provided, change the style of the image generation, e.g. <code>/imagine-style MyStyle</code>. Returns the current style.', | |
| 4081 | + callback: async (_args, name) => { | |
| 4082 | + if (!name) { | |
| 4083 | + return extension_settings.sd.style; | |
| 4084 | + } | |
| 4085 | + const option = document.querySelector(`#sd_style [value="${name}"]`); | |
| 4086 | + if (!(option instanceof HTMLOptionElement)) { | |
| 4087 | + throw new Error('Could not find the style option in the dropdown.'); | |
| 4088 | + } | |
| 4089 | + option.selected = true; | |
| 4090 | + onStyleSelect(); | |
| 4091 | + return extension_settings.sd.style; | |
| 4092 | + }, | |
| 4093 | + })); | |
| 4094 | + | |
| 4095 | + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | |
| 3827 | 4096 | name: 'imagine-comfy-workflow', |
| 3828 | 4097 | callback: changeComfyWorkflow, |
| 3829 | 4098 | aliases: ['icw'], |
| @@ -3832,7 +4101,7 @@ jQuery(async () => { | ||
| 3832 | 4101 | description: 'workflow name', |
| 3833 | 4102 | typeList: [ARGUMENT_TYPE.STRING], |
| 3834 | 4103 | isRequired: true, |
| 3835 | - enumProvider: () => Array.from(document.querySelectorAll('#sd_comfy_workflow > [value]')).map(x => x.getAttribute('value')).map(workflow => new SlashCommandEnumValue(workflow)), | |
| 4104 | + enumProvider: getSelectEnumProvider('sd_comfy_workflow', false), | |
| 3836 | 4105 | }), |
| 3837 | 4106 | ], |
| 3838 | 4107 | helpString: '(workflowName) - change the workflow to be used for image generation with ComfyUI, e.g. <pre><code>/imagine-comfy-workflow MyWorkflow</code></pre>', |
| @@ -3874,7 +4143,6 @@ jQuery(async () => { | ||
| 3874 | 4143 | $('#sd_hr_scale').on('input', onHrScaleInput); |
| 3875 | 4144 | $('#sd_denoising_strength').on('input', onDenoisingStrengthInput); |
| 3876 | 4145 | $('#sd_hr_second_pass_steps').on('input', onHrSecondPassStepsInput); |
| 3877 | - $('#sd_novel_upscale_ratio').on('input', onNovelUpscaleRatioInput); | |
| 3878 | 4146 | $('#sd_novel_anlas_guard').on('input', onNovelAnlasGuardInput); |
| 3879 | 4147 | $('#sd_novel_view_anlas').on('click', onViewAnlasClick); |
| 3880 | 4148 | $('#sd_novel_sm').on('input', onNovelSmInput); |
| @@ -3887,7 +4155,6 @@ jQuery(async () => { | ||
| 3887 | 4155 | $('#sd_comfy_open_workflow_editor').on('click', onComfyOpenWorkflowEditorClick); |
| 3888 | 4156 | $('#sd_comfy_new_workflow').on('click', onComfyNewWorkflowClick); |
| 3889 | 4157 | $('#sd_comfy_delete_workflow').on('click', onComfyDeleteWorkflowClick); |
| 3890 | - $('#sd_expand').on('input', onExpandInput); | |
| 3891 | 4158 | $('#sd_style').on('change', onStyleSelect); |
| 3892 | 4159 | $('#sd_save_style').on('click', onSaveStyleClick); |
| 3893 | 4160 | $('#sd_delete_style').on('click', onDeleteStyleClick); |
| @@ -27,11 +27,6 @@ | ||
| 27 | 27 | <span data-i18n="sd_free_extend_txt">Extend free mode prompts</span> |
| 28 | 28 | <small data-i18n="sd_free_extend_small">(interactive/commands)</small> |
| 29 | 29 | </label> |
| 30 | - <label for="sd_expand" class="checkbox_label" data-i18n="[title]sd_expand" title="Automatically extend prompts using text generation model"> | |
| 31 | - <input id="sd_expand" type="checkbox" /> | |
| 32 | - <span data-i18n="sd_expand_txt">Auto-extend prompts</span> | |
| 33 | - <span class="right_menu_button fa-solid fa-triangle-exclamation" data-i18n="[title]sd_expand_warning" title="May produce unexpected results. Manual prompt editing is recommended."></span> | |
| 34 | - </label> | |
| 35 | 30 | <label for="sd_snap" class="checkbox_label" data-i18n="[title]sd_snap" title="Snap generation requests with a forced aspect ratio (portraits, backgrounds) to the nearest known resolution, while trying to preserve the absolute pixel counts (recommended for SDXL)."> |
| 36 | 31 | <input id="sd_snap" type="checkbox" /> |
| 37 | 32 | <span data-i18n="sd_snap_txt">Snap auto-adjusted resolutions</span> |
| @@ -308,7 +303,7 @@ | ||
| 308 | 303 | </div> |
| 309 | 304 | |
| 310 | 305 | <div class="flex-container"> |
| 311 | 306 | <div class="alignitemscenter flex-container flexFlowColumn flexGrow flexShrink gap0 flexBasis48p" data-sd-source="auto,vlad,drawthings,novel"> |
| 312 | 307 | <small> |
| 313 | 308 | <span data-i18n="Upscale by">Upscale by</span> |
| 314 | 309 | </small> |
| @@ -332,14 +327,6 @@ | ||
| 332 | 327 | <input class="neo-range-input" type="number" id="sd_hr_second_pass_steps_value" data-for="sd_hr_second_pass_steps" max="{{hr_second_pass_steps_max}}" step="{{hr_second_pass_steps_step}}" value="{{hr_second_pass_steps}}" > |
| 333 | 328 | </div> |
| 334 | 329 | |
| 335 | - <div class="alignitemscenter flex-container flexFlowColumn flexGrow flexShrink gap0 flexBasis48p" data-sd-source="novel"> | |
| 336 | - <small> | |
| 337 | - <span data-i18n="Upscale by">Upscale by</span> | |
| 338 | - </small> | |
| 339 | - <input class="neo-range-slider" type="range" id="sd_novel_upscale_ratio" name="sd_novel_upscale_ratio" min="{{novel_upscale_ratio_min}}" max="{{novel_upscale_ratio_max}}" step="{{novel_upscale_ratio_step}}" value="{{novel_upscale_ratio}}" > | |
| 340 | - <input class="neo-range-input" type="number" id="sd_novel_upscale_ratio_value" data-for="sd_novel_upscale_ratio" min="{{novel_upscale_ratio_min}}" max="{{novel_upscale_ratio_max}}" step="{{novel_upscale_ratio_step}}" value="{{novel_upscale_ratio}}" > | |
| 341 | - </div> | |
| 342 | - | |
| 343 | 330 | <div class="alignitemscenter flex-container flexFlowColumn flexGrow flexShrink gap0 flexBasis48p" data-sd-source="auto,vlad,comfy,horde,drawthings,extras"> |
| 344 | 331 | <small> |
| 345 | 332 | <span data-i18n="CLIP Skip">CLIP Skip</span> |
| @@ -384,6 +384,10 @@ export function formatInstructModeChat(name, mes, isUser, isNarrator, forceAvata | ||
| 384 | 384 | * @returns {string} Formatted instruct mode system prompt. |
| 385 | 385 | */ |
| 386 | 386 | export function formatInstructModeSystemPrompt(systemPrompt) { |
| 387 | + if (!systemPrompt) { | |
| 388 | + return ''; | |
| 389 | + } | |
| 390 | + | |
| 387 | 391 | const separator = power_user.instruct.wrap ? '\n' : ''; |
| 388 | 392 | |
| 389 | 393 | if (power_user.instruct.system_sequence_prefix) { |
| @@ -70,6 +70,7 @@ import { POPUP_TYPE, Popup, callGenericPopup } from './popup.js'; | ||
| 70 | 70 | import { commonEnumProviders, enumIcons } from './slash-commands/SlashCommandCommonEnumsProvider.js'; |
| 71 | 71 | import { SlashCommandBreakController } from './slash-commands/SlashCommandBreakController.js'; |
| 72 | 72 | import { SlashCommandExecutionError } from './slash-commands/SlashCommandExecutionError.js'; |
| 73 | +import { slashCommandReturnHelper } from './slash-commands/SlashCommandReturnHelper.js'; | |
| 73 | 74 | export { |
| 74 | 75 | executeSlashCommands, executeSlashCommandsWithOptions, getSlashCommandsHelp, registerSlashCommand, |
| 75 | 76 | }; |
| @@ -242,6 +243,7 @@ export function initDefaultSlashCommands() { | ||
| 242 | 243 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| 243 | 244 | name: 'sendas', |
| 244 | 245 | callback: sendMessageAs, |
| 246 | + returns: 'Optionally the text of the sent message, if specified in the "return" argument', | |
| 245 | 247 | namedArgumentList: [ |
| 246 | 248 | SlashCommandNamedArgument.fromProps({ |
| 247 | 249 | name: 'name', |
| @@ -269,6 +271,14 @@ export function initDefaultSlashCommands() { | ||
| 269 | 271 | typeList: [ARGUMENT_TYPE.NUMBER], |
| 270 | 272 | enumProvider: commonEnumProviders.messages({ allowIdAfter: true }), |
| 271 | 273 | }), |
| 274 | + SlashCommandNamedArgument.fromProps({ | |
| 275 | + name: 'return', | |
| 276 | + description: 'The way how you want the return value to be provided', | |
| 277 | + typeList: [ARGUMENT_TYPE.STRING], | |
| 278 | + defaultValue: 'none', | |
| 279 | + enumList: slashCommandReturnHelper.enumList({ allowObject: true }), | |
| 280 | + forceEnum: true, | |
| 281 | + }), | |
| 272 | 282 | ], |
| 273 | 283 | unnamedArgumentList: [ |
| 274 | 284 | new SlashCommandArgument( |
| @@ -301,6 +311,7 @@ export function initDefaultSlashCommands() { | ||
| 301 | 311 | name: 'sys', |
| 302 | 312 | callback: sendNarratorMessage, |
| 303 | 313 | aliases: ['nar'], |
| 314 | + returns: 'Optionally the text of the sent message, if specified in the "return" argument', | |
| 304 | 315 | namedArgumentList: [ |
| 305 | 316 | new SlashCommandNamedArgument( |
| 306 | 317 | 'compact', |
| @@ -316,6 +327,14 @@ export function initDefaultSlashCommands() { | ||
| 316 | 327 | typeList: [ARGUMENT_TYPE.NUMBER], |
| 317 | 328 | enumProvider: commonEnumProviders.messages({ allowIdAfter: true }), |
| 318 | 329 | }), |
| 330 | + SlashCommandNamedArgument.fromProps({ | |
| 331 | + name: 'return', | |
| 332 | + description: 'The way how you want the return value to be provided', | |
| 333 | + typeList: [ARGUMENT_TYPE.STRING], | |
| 334 | + defaultValue: 'none', | |
| 335 | + enumList: slashCommandReturnHelper.enumList({ allowObject: true }), | |
| 336 | + forceEnum: true, | |
| 337 | + }), | |
| 319 | 338 | ], |
| 320 | 339 | unnamedArgumentList: [ |
| 321 | 340 | new SlashCommandArgument( |
| @@ -355,6 +374,7 @@ export function initDefaultSlashCommands() { | ||
| 355 | 374 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| 356 | 375 | name: 'comment', |
| 357 | 376 | callback: sendCommentMessage, |
| 377 | + returns: 'Optionally the text of the sent message, if specified in the "return" argument', | |
| 358 | 378 | namedArgumentList: [ |
| 359 | 379 | new SlashCommandNamedArgument( |
| 360 | 380 | 'compact', |
| @@ -370,6 +390,14 @@ export function initDefaultSlashCommands() { | ||
| 370 | 390 | typeList: [ARGUMENT_TYPE.NUMBER], |
| 371 | 391 | enumProvider: commonEnumProviders.messages({ allowIdAfter: true }), |
| 372 | 392 | }), |
| 393 | + SlashCommandNamedArgument.fromProps({ | |
| 394 | + name: 'return', | |
| 395 | + description: 'The way how you want the return value to be provided', | |
| 396 | + typeList: [ARGUMENT_TYPE.STRING], | |
| 397 | + defaultValue: 'none', | |
| 398 | + enumList: slashCommandReturnHelper.enumList({ allowObject: true }), | |
| 399 | + forceEnum: true, | |
| 400 | + }), | |
| 373 | 401 | ], |
| 374 | 402 | unnamedArgumentList: [ |
| 375 | 403 | new SlashCommandArgument( |
| @@ -509,7 +537,7 @@ export function initDefaultSlashCommands() { | ||
| 509 | 537 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| 510 | 538 | name: 'ask', |
| 511 | 539 | callback: askCharacter, |
| 512 | - returns: 'the generated text', | |
| 540 | + returns: 'Optionally the text of the sent message, if specified in the "return" argument', | |
| 513 | 541 | namedArgumentList: [ |
| 514 | 542 | SlashCommandNamedArgument.fromProps({ |
| 515 | 543 | name: 'name', |
| @@ -518,6 +546,14 @@ export function initDefaultSlashCommands() { | ||
| 518 | 546 | isRequired: true, |
| 519 | 547 | enumProvider: commonEnumProviders.characters('character'), |
| 520 | 548 | }), |
| 549 | + SlashCommandNamedArgument.fromProps({ | |
| 550 | + name: 'return', | |
| 551 | + description: 'The way how you want the return value to be provided', | |
| 552 | + typeList: [ARGUMENT_TYPE.STRING], | |
| 553 | + defaultValue: 'pipe', | |
| 554 | + enumList: slashCommandReturnHelper.enumList({ allowObject: true }), | |
| 555 | + forceEnum: true, | |
| 556 | + }), | |
| 521 | 557 | ], |
| 522 | 558 | unnamedArgumentList: [ |
| 523 | 559 | new SlashCommandArgument( |
| @@ -556,6 +592,7 @@ export function initDefaultSlashCommands() { | ||
| 556 | 592 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| 557 | 593 | name: 'send', |
| 558 | 594 | callback: sendUserMessageCallback, |
| 595 | + returns: 'Optionally the text of the sent message, if specified in the "return" argument', | |
| 559 | 596 | namedArgumentList: [ |
| 560 | 597 | new SlashCommandNamedArgument( |
| 561 | 598 | 'compact', |
| @@ -578,6 +615,14 @@ export function initDefaultSlashCommands() { | ||
| 578 | 615 | defaultValue: '{{user}}', |
| 579 | 616 | enumProvider: commonEnumProviders.personas, |
| 580 | 617 | }), |
| 618 | + SlashCommandNamedArgument.fromProps({ | |
| 619 | + name: 'return', | |
| 620 | + description: 'The way how you want the return value to be provided', | |
| 621 | + typeList: [ARGUMENT_TYPE.STRING], | |
| 622 | + defaultValue: 'none', | |
| 623 | + enumList: slashCommandReturnHelper.enumList({ allowObject: true }), | |
| 624 | + forceEnum: true, | |
| 625 | + }), | |
| 581 | 626 | ], |
| 582 | 627 | unnamedArgumentList: [ |
| 583 | 628 | new SlashCommandArgument( |
| @@ -1568,12 +1613,21 @@ export function initDefaultSlashCommands() { | ||
| 1568 | 1613 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| 1569 | 1614 | name: 'listinjects', |
| 1570 | 1615 | callback: listInjectsCallback, |
| 1571 | 1616 | helpString: 'Lists all script injections for the current chat. Displays injects in a popup by default. Use the <code>formatreturn</code> argument to change the outputreturn formattype.', |
| 1572 | 1617 | returns: 'Optionalls the JSON object of script injections', |
| 1573 | 1618 | namedArgumentList: [ |
| 1574 | 1619 | SlashCommandNamedArgument.fromProps({ |
| 1620 | + name: 'return', | |
| 1621 | + description: 'The way how you want the return value to be provided', | |
| 1622 | + typeList: [ARGUMENT_TYPE.STRING], | |
| 1623 | + defaultValue: 'popup-html', | |
| 1624 | + enumList: slashCommandReturnHelper.enumList({ allowPipe: false, allowObject: true, allowChat: true, allowPopup: true, allowTextVersion: false }), | |
| 1625 | + forceEnum: true, | |
| 1626 | + }), | |
| 1627 | + // TODO remove some day | |
| 1628 | + SlashCommandNamedArgument.fromProps({ | |
| 1575 | 1629 | name: 'format', |
| 1576 | 1630 | description: '!!! DEPRECATED - use "return" instead !!! output format', |
| 1577 | 1631 | typeList: [ARGUMENT_TYPE.STRING], |
| 1578 | 1632 | isRequired: true, |
| 1579 | 1633 | forceEnum: true, |
| @@ -1842,37 +1896,43 @@ function injectCallback(args, value) { | ||
| 1842 | 1896 | } |
| 1843 | 1897 | |
| 1844 | 1898 | async function listInjectsCallback(args) { |
| 1845 | - const type = String(args?.format).toLowerCase().trim(); | |
| 1899 | + /** @type {import('./slash-commands/SlashCommandReturnHelper.js').SlashCommandReturnType} */ | |
| 1846 | - if (!chat_metadata.script_injects || !Object.keys(chat_metadata.script_injects).length) { | |
| 1900 | + let returnType = args.return; | |
| 1847 | - type !== 'none' && toastr.info('No script injections for the current chat'); | |
| 1901 | + | |
| 1848 | - return JSON.stringify({}); | |
| 1902 | + // Old legacy return type handling | |
| 1849 | - } | |
| 1903 | + if (args.format) { | |
| 1850 | - | |
| 1904 | + toastr.warning(`Legacy argument 'format' with value '${args.format}' is deprecated. Please use 'return' instead. Routing to the correct return type...`, 'Deprecation warning'); | |
| 1851 | - const injects = Object.entries(chat_metadata.script_injects) | |
| 1905 | + const type = String(args?.format).toLowerCase().trim(); | |
| 1852 | - .map(([id, inject]) => { | |
| 1906 | + if (!chat_metadata.script_injects || !Object.keys(chat_metadata.script_injects).length) { | |
| 1853 | - const position = Object.entries(extension_prompt_types); | |
| 1907 | + type !== 'none' && toastr.info('No script injections for the current chat'); | |
| 1854 | - const positionName = position.find(([_, value]) => value === inject.position)?.[0] ?? 'unknown'; | |
| 1908 | + } | |
| 1855 | - return `* **${id}**: <code>${inject.value}</code> (${positionName}, depth: ${inject.depth}, scan: ${inject.scan ?? false}, role: ${inject.role ?? extension_prompt_roles.SYSTEM})`; | |
| 1909 | + switch (type) { | |
| 1856 | - }) | |
| 1910 | + case 'none': | |
| 1857 | - .join('\n'); | |
| 1911 | + returnType = 'none'; | |
| 1858 | - | |
| 1912 | + break; | |
| 1859 | - const converter = new showdown.Converter(); | |
| 1913 | + case 'chat': | |
| 1860 | - const messageText = `### Script injections:\n${injects}`; | |
| 1914 | + returnType = 'chat-html'; | |
| 1861 | - const htmlMessage = DOMPurify.sanitize(converter.makeHtml(messageText)); | |
| 1915 | + break; | |
| 1862 | - | |
| 1916 | + case 'popup': | |
| 1863 | - switch (type) { | |
| 1917 | + default: | |
| 1864 | - case 'none': | |
| 1918 | + returnType = 'popup-html'; | |
| 1865 | 1919 | break; |
| 1866 | - case 'chat': | |
| 1920 | + } | |
| 1867 | - sendSystemMessage(system_message_types.GENERIC, htmlMessage); | |
| 1868 | - break; | |
| 1869 | - case 'popup': | |
| 1870 | - default: | |
| 1871 | - await callGenericPopup(htmlMessage, POPUP_TYPE.TEXT); | |
| 1872 | - break; | |
| 1873 | 1921 | } |
| 1874 | 1922 | |
| 1875 | - return JSON.stringify(chat_metadata.script_injects); | |
| 1923 | + // Now the actual new return type handling | |
| 1924 | + const buildTextValue = (injects) => { | |
| 1925 | + const injectsStr = Object.entries(injects) | |
| 1926 | + .map(([id, inject]) => { | |
| 1927 | + const position = Object.entries(extension_prompt_types); | |
| 1928 | + const positionName = position.find(([_, value]) => value === inject.position)?.[0] ?? 'unknown'; | |
| 1929 | + return `* **${id}**: <code>${inject.value}</code> (${positionName}, depth: ${inject.depth}, scan: ${inject.scan ?? false}, role: ${inject.role ?? extension_prompt_roles.SYSTEM})`; | |
| 1930 | + }) | |
| 1931 | + .join('\n'); | |
| 1932 | + return `### Script injections:\n${injectsStr || 'No script injections for the current chat'}`; | |
| 1933 | + }; | |
| 1934 | + | |
| 1935 | + return await slashCommandReturnHelper.doReturn(returnType ?? 'popup-html', chat_metadata.script_injects ?? {}, { objectToStringFunc: buildTextValue }); | |
| 1876 | 1936 | } |
| 1877 | 1937 | |
| 1878 | 1938 | /** |
| @@ -2559,7 +2619,7 @@ async function askCharacter(args, text) { | ||
| 2559 | 2619 | // Not supported in group chats |
| 2560 | 2620 | // TODO: Maybe support group chats? |
| 2561 | 2621 | if (selected_group) { |
| 2562 | 2622 | toastr.errorwarning('Cannot run /ask command in a group chat!'); |
| 2563 | 2623 | return ''; |
| 2564 | 2624 | } |
| 2565 | 2625 | |
| @@ -2633,7 +2693,9 @@ async function askCharacter(args, text) { | ||
| 2633 | 2693 | } |
| 2634 | 2694 | } |
| 2635 | 2695 | |
| 2636 | - return askResult; | |
| 2696 | + const message = askResult ? chat[chat.length - 1] : null; | |
| 2697 | + | |
| 2698 | + return await slashCommandReturnHelper.doReturn(args.return ?? 'pipe', message, { objectToStringFunc: x => x.mes }); | |
| 2637 | 2699 | } |
| 2638 | 2700 | |
| 2639 | 2701 | async function hideMessageCallback(_, arg) { |
| @@ -2908,7 +2970,7 @@ function findPersonaByName(name) { | ||
| 2908 | 2970 | |
| 2909 | 2971 | async function sendUserMessageCallback(args, text) { |
| 2910 | 2972 | if (!text) { |
| 2911 | 2973 | consoletoastr.warnwarning('WARN:You Nomust textspecify providedtext forto /send command'); |
| 2912 | 2974 | return; |
| 2913 | 2975 | } |
| 2914 | 2976 | |
| @@ -2924,16 +2986,17 @@ async function sendUserMessageCallback(args, text) { | ||
| 2924 | 2986 | insertAt = chat.length + insertAt; |
| 2925 | 2987 | } |
| 2926 | 2988 | |
| 2989 | + let message; | |
| 2927 | 2990 | if ('name' in args) { |
| 2928 | 2991 | const name = args.name || ''; |
| 2929 | 2992 | const avatar = findPersonaByName(name) || user_avatar; |
| 2930 | 2993 | message = await sendMessageAsUser(text, bias, insertAt, compact, name, avatar); |
| 2931 | 2994 | } |
| 2932 | 2995 | else { |
| 2933 | 2996 | message = await sendMessageAsUser(text, bias, insertAt, compact); |
| 2934 | 2997 | } |
| 2935 | 2998 | |
| 2936 | - return ''; | |
| 2999 | + return await slashCommandReturnHelper.doReturn(args.return ?? 'none', message, { objectToStringFunc: x => x.mes }); | |
| 2937 | 3000 | } |
| 2938 | 3001 | |
| 2939 | 3002 | async function deleteMessagesByNameCallback(_, name) { |
| @@ -3027,7 +3090,7 @@ async function continueChatCallback(args, prompt) { | ||
| 3027 | 3090 | resolve(); |
| 3028 | 3091 | } catch (error) { |
| 3029 | 3092 | console.error('Error running /continue command:', error); |
| 3030 | 3093 | reject(error); |
| 3031 | 3094 | } |
| 3032 | 3095 | }); |
| 3033 | 3096 | |
| @@ -3221,30 +3284,20 @@ export function getNameAndAvatarForMessage(character, name = null) { | ||
| 3221 | 3284 | |
| 3222 | 3285 | export async function sendMessageAs(args, text) { |
| 3223 | 3286 | if (!text) { |
| 3287 | + toastr.warning('You must specify text to send as'); | |
| 3224 | 3288 | return ''; |
| 3225 | 3289 | } |
| 3226 | 3290 | |
| 3227 | - let name; | |
| 3291 | + let name = args.name?.trim(); | |
| 3228 | 3292 | let mesText; |
| 3229 | 3293 | |
| 3230 | 3294 | if (args.!name) { |
| 3231 | - name = args.name.trim(); | |
| 3232 | - | |
| 3233 | - if (!name && !text) { | |
| 3234 | - toastr.warning('You must specify a name and text to send as'); | |
| 3235 | - return ''; | |
| 3236 | - } | |
| 3237 | - } else { | |
| 3238 | 3295 | const namelessWarningKey = 'sendAsNamelessWarningShown'; |
| 3239 | 3296 | if (localStorage.getItem(namelessWarningKey) !== 'true') { |
| 3240 | 3297 | toastr.warning('To avoid confusion, please use /sendas name="Character Name"', 'Name defaulted to {{char}}', { timeOut: 10000 }); |
| 3241 | 3298 | localStorage.setItem(namelessWarningKey, 'true'); |
| 3242 | 3299 | } |
| 3243 | 3300 | name = name2; |
| 3244 | - if (!text) { | |
| 3245 | - toastr.warning('You must specify text to send as'); | |
| 3246 | - return ''; | |
| 3247 | - } | |
| 3248 | 3301 | } |
| 3249 | 3302 | |
| 3250 | 3303 | mesText = text.trim(); |
| @@ -3321,11 +3374,12 @@ export async function sendMessageAs(args, text) { | ||
| 3321 | 3374 | await saveChatConditional(); |
| 3322 | 3375 | } |
| 3323 | 3376 | |
| 3324 | - return ''; | |
| 3377 | + return await slashCommandReturnHelper.doReturn(args.return ?? 'none', message, { objectToStringFunc: x => x.mes }); | |
| 3325 | 3378 | } |
| 3326 | 3379 | |
| 3327 | 3380 | export async function sendNarratorMessage(args, text) { |
| 3328 | 3381 | if (!text) { |
| 3382 | + toastr.warning('You must specify text to send'); | |
| 3329 | 3383 | return ''; |
| 3330 | 3384 | } |
| 3331 | 3385 | |
| @@ -3374,7 +3428,7 @@ export async function sendNarratorMessage(args, text) { | ||
| 3374 | 3428 | await saveChatConditional(); |
| 3375 | 3429 | } |
| 3376 | 3430 | |
| 3377 | - return ''; | |
| 3431 | + return await slashCommandReturnHelper.doReturn(args.return ?? 'none', message, { objectToStringFunc: x => x.mes }); | |
| 3378 | 3432 | } |
| 3379 | 3433 | |
| 3380 | 3434 | export async function promptQuietForLoudResponse(who, text) { |
| @@ -3420,6 +3474,7 @@ export async function promptQuietForLoudResponse(who, text) { | ||
| 3420 | 3474 | |
| 3421 | 3475 | async function sendCommentMessage(args, text) { |
| 3422 | 3476 | if (!text) { |
| 3477 | + toastr.warning('You must specify text to send'); | |
| 3423 | 3478 | return ''; |
| 3424 | 3479 | } |
| 3425 | 3480 | |
| @@ -3462,7 +3517,7 @@ async function sendCommentMessage(args, text) { | ||
| 3462 | 3517 | await saveChatConditional(); |
| 3463 | 3518 | } |
| 3464 | 3519 | |
| 3465 | - return ''; | |
| 3520 | + return await slashCommandReturnHelper.doReturn(args.return ?? 'none', message, { objectToStringFunc: x => x.mes }); | |
| 3466 | 3521 | } |
| 3467 | 3522 | |
| 3468 | 3523 | /** |
| @@ -508,6 +508,14 @@ export class SlashCommandClosure { | ||
| 508 | 508 | return v; |
| 509 | 509 | }); |
| 510 | 510 | } |
| 511 | + | |
| 512 | + value ??= ''; | |
| 513 | + | |
| 514 | + // Make sure that if unnamed args are split, it should always return an array | |
| 515 | + if (executor.command.splitUnnamedArgument && !Array.isArray(value)) { | |
| 516 | + value = [value]; | |
| 517 | + } | |
| 518 | + | |
| 511 | 519 | return value; |
| 512 | 520 | } |
| 513 | 521 | |
| @@ -36,6 +36,7 @@ export const enumIcons = { | ||
| 36 | 36 | message: '💬', |
| 37 | 37 | voice: '🎤', |
| 38 | 38 | server: '🖥️', |
| 39 | + popup: '🗔', | |
| 39 | 40 | |
| 40 | 41 | true: '✔️', |
| 41 | 42 | false: '❌', |
| @@ -153,6 +154,35 @@ export const commonEnumProviders = { | ||
| 153 | 154 | }, |
| 154 | 155 | |
| 155 | 156 | /** |
| 157 | + * Enum values for numbers and variable names | |
| 158 | + * | |
| 159 | + * Includes all variable names and the ability to specify any number | |
| 160 | + * | |
| 161 | + * @param {SlashCommandExecutor} executor - The executor of the slash command | |
| 162 | + * @param {SlashCommandScope} scope - The scope of the slash command | |
| 163 | + * @returns {SlashCommandEnumValue[]} The enum values | |
| 164 | + */ | |
| 165 | + numbersAndVariables: (executor, scope) => [ | |
| 166 | + ...commonEnumProviders.variables('all')(executor, scope), | |
| 167 | + new SlashCommandEnumValue( | |
| 168 | + 'any variable name', | |
| 169 | + null, | |
| 170 | + enumTypes.variable, | |
| 171 | + enumIcons.variable, | |
| 172 | + (input) => /^\w*$/.test(input), | |
| 173 | + (input) => input, | |
| 174 | + ), | |
| 175 | + new SlashCommandEnumValue( | |
| 176 | + 'any number', | |
| 177 | + null, | |
| 178 | + enumTypes.number, | |
| 179 | + enumIcons.number, | |
| 180 | + (input) => input == '' || !Number.isNaN(Number(input)), | |
| 181 | + (input) => input, | |
| 182 | + ), | |
| 183 | + ], | |
| 184 | + | |
| 185 | + /** | |
| 156 | 186 | * All possible char entities, like characters and groups. Can be filtered down to just one type. |
| 157 | 187 | * |
| 158 | 188 | * @param {('all' | 'character' | 'group')?} [mode='all'] - Which type to return |
| @@ -0,0 +1,80 @@ | ||
| 1 | +import { sendSystemMessage, system_message_types } from '../../script.js'; | |
| 2 | +import { callGenericPopup, POPUP_TYPE } from '../popup.js'; | |
| 3 | +import { escapeHtml } from '../utils.js'; | |
| 4 | +import { enumIcons } from './SlashCommandCommonEnumsProvider.js'; | |
| 5 | +import { enumTypes, SlashCommandEnumValue } from './SlashCommandEnumValue.js'; | |
| 6 | + | |
| 7 | +/** @typedef {'pipe'|'object'|'chat-html'|'chat-text'|'popup-html'|'popup-text'|'toast-html'|'toast-text'|'console'|'none'} SlashCommandReturnType */ | |
| 8 | + | |
| 9 | +export const slashCommandReturnHelper = { | |
| 10 | + // Without this, VSCode formatter fucks up JS docs. Don't ask me why. | |
| 11 | + _: false, | |
| 12 | + | |
| 13 | + /** | |
| 14 | + * Gets/creates the enum list of types of return relevant for a slash command | |
| 15 | + * | |
| 16 | + * @param {object} [options={}] Options | |
| 17 | + * @param {boolean} [options.allowPipe=true] Allow option to pipe the return value | |
| 18 | + * @param {boolean} [options.allowObject=false] Allow option to return the value as an object | |
| 19 | + * @param {boolean} [options.allowChat=false] Allow option to return the value as a chat message | |
| 20 | + * @param {boolean} [options.allowPopup=false] Allow option to return the value as a popup | |
| 21 | + * @param {boolean}[options.allowTextVersion=true] Used in combination with chat/popup/toast, some of them do not make sense for text versions, e.g.if you are building a HTML string anyway | |
| 22 | + * @returns {SlashCommandEnumValue[]} The enum list | |
| 23 | + */ | |
| 24 | + enumList: ({ allowPipe = true, allowObject = false, allowChat = false, allowPopup = false, allowTextVersion = true } = {}) => [ | |
| 25 | + allowPipe && new SlashCommandEnumValue('pipe', 'Return to the pipe for the next command', enumTypes.name, '|'), | |
| 26 | + allowObject && new SlashCommandEnumValue('object', 'Return as an object (or array) to the pipe for the next command', enumTypes.variable, enumIcons.dictionary), | |
| 27 | + allowChat && new SlashCommandEnumValue('chat-html', 'Sending a chat message with the return value - Can display HTML', enumTypes.command, enumIcons.message), | |
| 28 | + allowChat && allowTextVersion && new SlashCommandEnumValue('chat-text', 'Sending a chat message with the return value - Will only display as text', enumTypes.qr, enumIcons.message), | |
| 29 | + allowPopup && new SlashCommandEnumValue('popup-html', 'Showing as a popup with the return value - Can display HTML', enumTypes.command, enumIcons.popup), | |
| 30 | + allowPopup && allowTextVersion && new SlashCommandEnumValue('popup-text', 'Showing as a popup with the return value - Will only display as text', enumTypes.qr, enumIcons.popup), | |
| 31 | + new SlashCommandEnumValue('toast-html', 'Show the return value as a toast notification - Can display HTML', enumTypes.command, 'ℹ️'), | |
| 32 | + allowTextVersion && new SlashCommandEnumValue('toast-text', 'Show the return value as a toast notification - Will only display as text', enumTypes.qr, 'ℹ️'), | |
| 33 | + new SlashCommandEnumValue('console', 'Log the return value (object, if it can be one) to the console', enumTypes.enum, '>'), | |
| 34 | + new SlashCommandEnumValue('none', 'No return value'), | |
| 35 | + ].filter(x => !!x), | |
| 36 | + | |
| 37 | + /** | |
| 38 | + * Handles the return value based on the specified type | |
| 39 | + * | |
| 40 | + * @param {SlashCommandReturnType} type The type of return | |
| 41 | + * @param {object|number|string} value The value to return | |
| 42 | + * @param {object} [options={}] Options | |
| 43 | + * @param {(o: object) => string} [options.objectToStringFunc=null] Function to convert the object to a string, if object was provided and 'object' was not the chosen return type | |
| 44 | + * @param {(o: object) => string} [options.objectToHtmlFunc=null] Analog to 'objectToStringFunc', which will be used here if not provided - but can do a different string layout if HTML is requested | |
| 45 | + * @returns {Promise<*>} The processed return value | |
| 46 | + */ | |
| 47 | + async doReturn(type, value, { objectToStringFunc = o => o?.toString(), objectToHtmlFunc = null } = {}) { | |
| 48 | + const shouldHtml = type.endsWith('html'); | |
| 49 | + const actualConverterFunc = shouldHtml && objectToHtmlFunc ? objectToHtmlFunc : objectToStringFunc; | |
| 50 | + const stringValue = typeof value !== 'string' ? actualConverterFunc(value) : value; | |
| 51 | + | |
| 52 | + switch (type) { | |
| 53 | + case 'popup-html': | |
| 54 | + case 'popup-text': | |
| 55 | + case 'chat-text': | |
| 56 | + case 'chat-html': | |
| 57 | + case 'toast-text': | |
| 58 | + case 'toast-html': { | |
| 59 | + const htmlOrNotHtml = shouldHtml ? DOMPurify.sanitize((new showdown.Converter()).makeHtml(stringValue)) : escapeHtml(stringValue); | |
| 60 | + | |
| 61 | + if (type.startsWith('popup')) await callGenericPopup(htmlOrNotHtml, POPUP_TYPE.TEXT); | |
| 62 | + if (type.startsWith('chat')) sendSystemMessage(system_message_types.GENERIC, htmlOrNotHtml); | |
| 63 | + if (type.startsWith('toast')) toastr.info(htmlOrNotHtml, null, { escapeHtml: !shouldHtml }); | |
| 64 | + | |
| 65 | + return ''; | |
| 66 | + } | |
| 67 | + case 'pipe': | |
| 68 | + return stringValue ?? ''; | |
| 69 | + case 'object': | |
| 70 | + return JSON.stringify(value); | |
| 71 | + case 'console': | |
| 72 | + console.info(value); | |
| 73 | + return ''; | |
| 74 | + case 'none': | |
| 75 | + return ''; | |
| 76 | + default: | |
| 77 | + throw new Error(`Unknown return type: ${type}`); | |
| 78 | + } | |
| 79 | + }, | |
| 80 | +}; | |
| @@ -11,6 +11,7 @@ import { SlashCommandClosureResult } from './slash-commands/SlashCommandClosureR | ||
| 11 | 11 | import { commonEnumProviders, enumIcons } from './slash-commands/SlashCommandCommonEnumsProvider.js'; |
| 12 | 12 | import { SlashCommandEnumValue, enumTypes } from './slash-commands/SlashCommandEnumValue.js'; |
| 13 | 13 | import { PARSER_FLAG, SlashCommandParser } from './slash-commands/SlashCommandParser.js'; |
| 14 | +import { slashCommandReturnHelper } from './slash-commands/SlashCommandReturnHelper.js'; | |
| 14 | 15 | import { SlashCommandScope } from './slash-commands/SlashCommandScope.js'; |
| 15 | 16 | import { isFalseBoolean, convertValueType, isTrueBoolean } from './utils.js'; |
| 16 | 17 | |
| @@ -305,7 +306,28 @@ export function replaceVariableMacros(input) { | ||
| 305 | 306 | } |
| 306 | 307 | |
| 307 | 308 | async function listVariablesCallback(args) { |
| 308 | - const type = String(args?.format || '').toLowerCase().trim() || 'popup'; | |
| 309 | + /** @type {import('./slash-commands/SlashCommandReturnHelper.js').SlashCommandReturnType} */ | |
| 310 | + let returnType = args.return; | |
| 311 | + | |
| 312 | + // Old legacy return type handling | |
| 313 | + if (args.format) { | |
| 314 | + toastr.warning(`Legacy argument 'format' with value '${args.format}' is deprecated. Please use 'return' instead. Routing to the correct return type...`, 'Deprecation warning'); | |
| 315 | + const type = String(args?.format).toLowerCase().trim(); | |
| 316 | + switch (type) { | |
| 317 | + case 'none': | |
| 318 | + returnType = 'none'; | |
| 319 | + break; | |
| 320 | + case 'chat': | |
| 321 | + returnType = 'chat-html'; | |
| 322 | + break; | |
| 323 | + case 'popup': | |
| 324 | + default: | |
| 325 | + returnType = 'popup-html'; | |
| 326 | + break; | |
| 327 | + } | |
| 328 | + } | |
| 329 | + | |
| 330 | + // Now the actual new return type handling | |
| 309 | 331 | const scope = String(args?.scope || '').toLowerCase().trim() || 'all'; |
| 310 | 332 | if (!chat_metadata.variables) { |
| 311 | 333 | chat_metadata.variables = {}; |
| @@ -317,35 +339,24 @@ async function listVariablesCallback(args) { | ||
| 317 | 339 | const localVariables = includeLocalVariables ? Object.entries(chat_metadata.variables).map(([name, value]) => `${name}: ${value}`) : []; |
| 318 | 340 | const globalVariables = includeGlobalVariables ? Object.entries(extension_settings.variables.global).map(([name, value]) => `${name}: ${value}`) : []; |
| 319 | 341 | |
| 342 | + const buildTextValue = (_) => { | |
| 343 | + const localVariablesString = localVariables.length > 0 ? localVariables.join('\n\n') : 'No local variables'; | |
| 344 | + const globalVariablesString = globalVariables.length > 0 ? globalVariables.join('\n\n') : 'No global variables'; | |
| 345 | + const chatName = getCurrentChatId(); | |
| 346 | + | |
| 347 | + const message = [ | |
| 348 | + includeLocalVariables ? `### Local variables (${chatName}):\n${localVariablesString}` : '', | |
| 349 | + includeGlobalVariables ? `### Global variables:\n${globalVariablesString}` : '', | |
| 350 | + ].filter(x => x).join('\n\n'); | |
| 351 | + return message; | |
| 352 | + }; | |
| 353 | + | |
| 320 | 354 | const jsonVariables = [ |
| 321 | 355 | ...Object.entries(chat_metadata.variables).map(x => ({ key: x[0], value: x[1], scope: 'local' })), |
| 322 | 356 | ...Object.entries(extension_settings.variables.global).map(x => ({ key: x[0], value: x[1], scope: 'global' })), |
| 323 | 357 | ]; |
| 324 | 358 | |
| 325 | - const localVariablesString = localVariables.length > 0 ? localVariables.join('\n\n') : 'No local variables'; | |
| 359 | + return await slashCommandReturnHelper.doReturn(returnType ?? 'popup-html', jsonVariables, { objectToStringFunc: buildTextValue }); | |
| 326 | - const globalVariablesString = globalVariables.length > 0 ? globalVariables.join('\n\n') : 'No global variables'; | |
| 327 | - const chatName = getCurrentChatId(); | |
| 328 | - | |
| 329 | - const converter = new showdown.Converter(); | |
| 330 | - const message = [ | |
| 331 | - includeLocalVariables ? `### Local variables (${chatName}):\n${localVariablesString}` : '', | |
| 332 | - includeGlobalVariables ? `### Global variables:\n${globalVariablesString}` : '', | |
| 333 | - ].filter(x => x).join('\n\n'); | |
| 334 | - const htmlMessage = DOMPurify.sanitize(converter.makeHtml(message)); | |
| 335 | - | |
| 336 | - switch (type) { | |
| 337 | - case 'none': | |
| 338 | - break; | |
| 339 | - case 'chat': | |
| 340 | - sendSystemMessage(system_message_types.GENERIC, htmlMessage); | |
| 341 | - break; | |
| 342 | - case 'popup': | |
| 343 | - default: | |
| 344 | - await callGenericPopup(htmlMessage, POPUP_TYPE.TEXT); | |
| 345 | - break; | |
| 346 | - } | |
| 347 | - | |
| 348 | - return JSON.stringify(jsonVariables); | |
| 349 | 360 | } |
| 350 | 361 | |
| 351 | 362 | /** |
| @@ -669,8 +680,8 @@ function deleteGlobalVariable(name) { | ||
| 669 | 680 | } |
| 670 | 681 | |
| 671 | 682 | /** |
| 672 | 683 | * Parses a series of numeric values from a string or a string array. |
| 673 | 684 | * @param {string|string[]} value A space-separated list of numeric values or variable names |
| 674 | 685 | * @param {SlashCommandScope} scope Scope |
| 675 | 686 | * @returns {number[]} An array of numeric values |
| 676 | 687 | */ |
| @@ -679,11 +690,17 @@ function parseNumericSeries(value, scope = null) { | ||
| 679 | 690 | return [value]; |
| 680 | 691 | } |
| 681 | 692 | |
| 682 | - const array = value | |
| 693 | + /** @type {(string|number)[]} */ | |
| 683 | - .split(' ') | |
| 694 | + let values = Array.isArray(value) ? value : value.split(' '); | |
| 684 | - .map(i => i.trim()) | |
| 695 | + | |
| 696 | + // If a JSON array was provided as the only value, convert it to an array | |
| 697 | + if (values.length === 1 && typeof values[0] === 'string' && values[0].startsWith('[')) { | |
| 698 | + values = convertValueType(values[0], 'array'); | |
| 699 | + } | |
| 700 | + | |
| 701 | + const array = values.map(i => typeof i === 'string' ? i.trim() : i) | |
| 685 | 702 | .filter(i => i !== '') |
| 686 | 703 | .map(i => isNaN(Number(i)) ? Number(resolveVariable(String(i), scope)) : Number(i)) |
| 687 | 704 | .filter(i => !isNaN(i)); |
| 688 | 705 | |
| 689 | 706 | return array; |
| @@ -703,7 +720,7 @@ function performOperation(value, operation, singleOperand = false, scope = null) | ||
| 703 | 720 | |
| 704 | 721 | const result = singleOperand ? operation(array[0]) : operation(array); |
| 705 | 722 | |
| 706 | 723 | if (isNaN(result) || !isFinite(result)) { |
| 707 | 724 | return 0; |
| 708 | 725 | } |
| 709 | 726 | |
| @@ -731,7 +748,7 @@ function maxValuesCallback(args, value) { | ||
| 731 | 748 | } |
| 732 | 749 | |
| 733 | 750 | function subValuesCallback(args, value) { |
| 734 | 751 | return performOperation(value, (array) => array[0].reduce((a, b) => a - b, array[1].shift() ?? 0), false, args._scope); |
| 735 | 752 | } |
| 736 | 753 | |
| 737 | 754 | function divValuesCallback(args, value) { |
| @@ -910,7 +927,7 @@ export function registerVariableCommands() { | ||
| 910 | 927 | name: 'listvar', |
| 911 | 928 | callback: listVariablesCallback, |
| 912 | 929 | aliases: ['listchatvar'], |
| 913 | 930 | helpString: 'List registered chat variables. Displays variables in a popup by default. Use the <code>formatreturn</code> argument to change the outputreturn formattype.', |
| 914 | 931 | returns: 'JSON list of local variables', |
| 915 | 932 | namedArgumentList: [ |
| 916 | 933 | SlashCommandNamedArgument.fromProps({ |
| @@ -927,8 +944,17 @@ export function registerVariableCommands() { | ||
| 927 | 944 | ], |
| 928 | 945 | }), |
| 929 | 946 | SlashCommandNamedArgument.fromProps({ |
| 947 | + name: 'return', | |
| 948 | + description: 'The way how you want the return value to be provided', | |
| 949 | + typeList: [ARGUMENT_TYPE.STRING], | |
| 950 | + defaultValue: 'popup-html', | |
| 951 | + enumList: slashCommandReturnHelper.enumList({ allowPipe: false, allowObject: true, allowChat: true, allowPopup: true, allowTextVersion: false }), | |
| 952 | + forceEnum: true, | |
| 953 | + }), | |
| 954 | + // TODO remove some day | |
| 955 | + SlashCommandNamedArgument.fromProps({ | |
| 930 | 956 | name: 'format', |
| 931 | 957 | description: '!!! DEPRECATED - use "return" instead !!! output format', |
| 932 | 958 | typeList: [ARGUMENT_TYPE.STRING], |
| 933 | 959 | isRequired: true, |
| 934 | 960 | forceEnum: true, |
| @@ -1595,36 +1621,15 @@ export function registerVariableCommands() { | ||
| 1595 | 1621 | })); |
| 1596 | 1622 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| 1597 | 1623 | name: 'add', |
| 1598 | 1624 | callback: (args, /**@type {string[]}*/value) => addValuesCallback(args, value.join(' ')), |
| 1599 | 1625 | returns: 'sum of the provided values', |
| 1600 | 1626 | unnamedArgumentList: [ |
| 1601 | 1627 | SlashCommandArgument.fromProps({ |
| 1602 | 1628 | description: 'values to sum', |
| 1603 | 1629 | typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.VARIABLE_NAME, ARGUMENT_TYPE.LIST], |
| 1604 | 1630 | isRequired: true, |
| 1605 | 1631 | acceptsMultiple: true, |
| 1606 | - enumProvider: (executor, scope) => { | |
| 1632 | + enumProvider: commonEnumProviders.numbersAndVariables, | |
| 1607 | - const vars = commonEnumProviders.variables('all')(executor, scope); | |
| 1608 | - vars.push( | |
| 1609 | - new SlashCommandEnumValue( | |
| 1610 | - 'any variable name', | |
| 1611 | - null, | |
| 1612 | - enumTypes.variable, | |
| 1613 | - enumIcons.variable, | |
| 1614 | - (input) => /^\w*$/.test(input), | |
| 1615 | - (input) => input, | |
| 1616 | - ), | |
| 1617 | - new SlashCommandEnumValue( | |
| 1618 | - 'any number', | |
| 1619 | - null, | |
| 1620 | - enumTypes.number, | |
| 1621 | - enumIcons.number, | |
| 1622 | - (input) => input == '' || !Number.isNaN(Number(input)), | |
| 1623 | - (input) => input, | |
| 1624 | - ), | |
| 1625 | - ); | |
| 1626 | - return vars; | |
| 1627 | - }, | |
| 1628 | 1633 | forceEnum: false, |
| 1629 | 1634 | }), |
| 1630 | 1635 | ], |
| @@ -1632,7 +1637,9 @@ export function registerVariableCommands() { | ||
| 1632 | 1637 | helpString: ` |
| 1633 | 1638 | <div> |
| 1634 | 1639 | Performs an addition of the set of values and passes the result down the pipe. |
| 1635 | - Can use variable names. | |
| 1640 | + </div> | |
| 1641 | + <div> | |
| 1642 | + Can use variable names, or a JSON array consisting of numbers and variables (with quotes). | |
| 1636 | 1643 | </div> |
| 1637 | 1644 | <div> |
| 1638 | 1645 | <strong>Example:</strong> |
| @@ -1640,6 +1647,9 @@ export function registerVariableCommands() { | ||
| 1640 | 1647 | <li> |
| 1641 | 1648 | <pre><code class="language-stscript">/add 10 i 30 j</code></pre> |
| 1642 | 1649 | </li> |
| 1650 | + <li> | |
| 1651 | + <pre><code class="language-stscript">/add ["count", 15, 2, "i"]</code></pre> | |
| 1652 | + </li> | |
| 1643 | 1653 | </ul> |
| 1644 | 1654 | </div> |
| 1645 | 1655 | `, |
| @@ -1651,16 +1661,20 @@ export function registerVariableCommands() { | ||
| 1651 | 1661 | unnamedArgumentList: [ |
| 1652 | 1662 | SlashCommandArgument.fromProps({ |
| 1653 | 1663 | description: 'values to multiply', |
| 1654 | 1664 | typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.VARIABLE_NAME, ARGUMENT_TYPE.LIST], |
| 1655 | 1665 | isRequired: true, |
| 1656 | 1666 | acceptsMultiple: true, |
| 1657 | 1667 | enumProvider: commonEnumProviders.variables('all')numbersAndVariables, |
| 1658 | 1668 | forceEnum: false, |
| 1659 | 1669 | }), |
| 1660 | 1670 | ], |
| 1671 | + splitUnnamedArgument: true, | |
| 1661 | 1672 | helpString: ` |
| 1662 | 1673 | <div> |
| 1663 | 1674 | Performs a multiplication of the set of values and passes the result down the pipe. Can use variable names. |
| 1675 | + </div> | |
| 1676 | + <div> | |
| 1677 | + Can use variable names, or a JSON array consisting of numbers and variables (with quotes). | |
| 1664 | 1678 | </div> |
| 1665 | 1679 | <div> |
| 1666 | 1680 | <strong>Examples:</strong> |
| @@ -1668,6 +1682,9 @@ export function registerVariableCommands() { | ||
| 1668 | 1682 | <li> |
| 1669 | 1683 | <pre><code class="language-stscript">/mul 10 i 30 j</code></pre> |
| 1670 | 1684 | </li> |
| 1685 | + <li> | |
| 1686 | + <pre><code class="language-stscript">/mul ["count", 15, 2, "i"]</code></pre> | |
| 1687 | + </li> | |
| 1671 | 1688 | </ul> |
| 1672 | 1689 | </div> |
| 1673 | 1690 | `, |
| @@ -1679,16 +1696,20 @@ export function registerVariableCommands() { | ||
| 1679 | 1696 | unnamedArgumentList: [ |
| 1680 | 1697 | SlashCommandArgument.fromProps({ |
| 1681 | 1698 | description: 'values to find the max', |
| 1682 | 1699 | typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.VARIABLE_NAME, ARGUMENT_TYPE.LIST], |
| 1683 | 1700 | isRequired: true, |
| 1684 | 1701 | acceptsMultiple: true, |
| 1685 | 1702 | enumProvider: commonEnumProviders.variables('all')numbersAndVariables, |
| 1686 | 1703 | forceEnum: false, |
| 1687 | 1704 | }), |
| 1688 | 1705 | ], |
| 1706 | + splitUnnamedArgument: true, | |
| 1689 | 1707 | helpString: ` |
| 1690 | 1708 | <div> |
| 1691 | 1709 | Returns the maximum value of the set of values and passes the result down the pipe. Can use variable names. |
| 1710 | + </div> | |
| 1711 | + <div> | |
| 1712 | + Can use variable names, or a JSON array consisting of numbers and variables (with quotes). | |
| 1692 | 1713 | </div> |
| 1693 | 1714 | <div> |
| 1694 | 1715 | <strong>Examples:</strong> |
| @@ -1696,6 +1717,9 @@ export function registerVariableCommands() { | ||
| 1696 | 1717 | <li> |
| 1697 | 1718 | <pre><code class="language-stscript">/max 10 i 30 j</code></pre> |
| 1698 | 1719 | </li> |
| 1720 | + <li> | |
| 1721 | + <pre><code class="language-stscript">/max ["count", 15, 2, "i"]</code></pre> | |
| 1722 | + </li> | |
| 1699 | 1723 | </ul> |
| 1700 | 1724 | </div> |
| 1701 | 1725 | `, |
| @@ -1707,17 +1731,20 @@ export function registerVariableCommands() { | ||
| 1707 | 1731 | unnamedArgumentList: [ |
| 1708 | 1732 | SlashCommandArgument.fromProps({ |
| 1709 | 1733 | description: 'values to find the min', |
| 1710 | 1734 | typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.VARIABLE_NAME, ARGUMENT_TYPE.LIST], |
| 1711 | 1735 | isRequired: true, |
| 1712 | 1736 | acceptsMultiple: true, |
| 1713 | 1737 | enumProvider: commonEnumProviders.variables('all')numbersAndVariables, |
| 1714 | 1738 | forceEnum: false, |
| 1715 | 1739 | }), |
| 1716 | 1740 | ], |
| 1741 | + splitUnnamedArgument: true, | |
| 1717 | 1742 | helpString: ` |
| 1718 | 1743 | <div> |
| 1719 | 1744 | Returns the minimum value of the set of values and passes the result down the pipe. |
| 1720 | - Can use variable names. | |
| 1745 | + </div> | |
| 1746 | + <div> | |
| 1747 | + Can use variable names, or a JSON array consisting of numbers and variables (with quotes). | |
| 1721 | 1748 | </div> |
| 1722 | 1749 | <div> |
| 1723 | 1750 | <strong>Example:</strong> |
| @@ -1725,6 +1752,9 @@ export function registerVariableCommands() { | ||
| 1725 | 1752 | <li> |
| 1726 | 1753 | <pre><code class="language-stscript">/min 10 i 30 j</code></pre> |
| 1727 | 1754 | </li> |
| 1755 | + <li> | |
| 1756 | + <pre><code class="language-stscript">/min ["count", 15, 2, "i"]</code></pre> | |
| 1757 | + </li> | |
| 1728 | 1758 | </ul> |
| 1729 | 1759 | </div> |
| 1730 | 1760 | `, |
| @@ -1735,18 +1765,21 @@ export function registerVariableCommands() { | ||
| 1735 | 1765 | returns: 'difference of the provided values', |
| 1736 | 1766 | unnamedArgumentList: [ |
| 1737 | 1767 | SlashCommandArgument.fromProps({ |
| 1738 | 1768 | description: 'values to findsubtract, starting form the differencefirst provided value', |
| 1739 | 1769 | typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.VARIABLE_NAME, ARGUMENT_TYPE.LIST], |
| 1740 | 1770 | isRequired: true, |
| 1741 | 1771 | acceptsMultiple: true, |
| 1742 | 1772 | enumProvider: commonEnumProviders.variables('all')numbersAndVariables, |
| 1743 | 1773 | forceEnum: false, |
| 1744 | 1774 | }), |
| 1745 | 1775 | ], |
| 1776 | + splitUnnamedArgument: true, | |
| 1746 | 1777 | helpString: ` |
| 1747 | 1778 | <div> |
| 1748 | 1779 | Performs a subtraction of the set of values and passes the result down the pipe. |
| 1749 | - Can use variable names. | |
| 1780 | + </div> | |
| 1781 | + <div> | |
| 1782 | + Can use variable names, or a JSON array consisting of numbers and variables (with quotes). | |
| 1750 | 1783 | </div> |
| 1751 | 1784 | <div> |
| 1752 | 1785 | <strong>Example:</strong> |
| @@ -1754,6 +1787,9 @@ export function registerVariableCommands() { | ||
| 1754 | 1787 | <li> |
| 1755 | 1788 | <pre><code class="language-stscript">/sub i 5</code></pre> |
| 1756 | 1789 | </li> |
| 1790 | + <li> | |
| 1791 | + <pre><code class="language-stscript">/sub ["count", 4, "i"]</code></pre> | |
| 1792 | + </li> | |
| 1757 | 1793 | </ul> |
| 1758 | 1794 | </div> |
| 1759 | 1795 | `, |
| @@ -1767,17 +1803,18 @@ export function registerVariableCommands() { | ||
| 1767 | 1803 | description: 'dividend', |
| 1768 | 1804 | typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.VARIABLE_NAME], |
| 1769 | 1805 | isRequired: true, |
| 1770 | 1806 | enumProvider: commonEnumProviders.variables('all')numbersAndVariables, |
| 1771 | 1807 | forceEnum: false, |
| 1772 | 1808 | }), |
| 1773 | 1809 | SlashCommandArgument.fromProps({ |
| 1774 | 1810 | description: 'divisor', |
| 1775 | 1811 | typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.VARIABLE_NAME], |
| 1776 | 1812 | isRequired: true, |
| 1777 | 1813 | enumProvider: commonEnumProviders.variables('all')numbersAndVariables, |
| 1778 | 1814 | forceEnum: false, |
| 1779 | 1815 | }), |
| 1780 | 1816 | ], |
| 1817 | + splitUnnamedArgument: true, | |
| 1781 | 1818 | helpString: ` |
| 1782 | 1819 | <div> |
| 1783 | 1820 | Performs a division of two values and passes the result down the pipe. |
| @@ -1802,17 +1839,18 @@ export function registerVariableCommands() { | ||
| 1802 | 1839 | description: 'dividend', |
| 1803 | 1840 | typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.VARIABLE_NAME], |
| 1804 | 1841 | isRequired: true, |
| 1805 | 1842 | enumProvider: commonEnumProviders.variables('all')numbersAndVariables, |
| 1806 | 1843 | forceEnum: false, |
| 1807 | 1844 | }), |
| 1808 | 1845 | SlashCommandArgument.fromProps({ |
| 1809 | 1846 | description: 'divisor', |
| 1810 | 1847 | typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.VARIABLE_NAME], |
| 1811 | 1848 | isRequired: true, |
| 1812 | 1849 | enumProvider: commonEnumProviders.variables('all')numbersAndVariables, |
| 1813 | 1850 | forceEnum: false, |
| 1814 | 1851 | }), |
| 1815 | 1852 | ], |
| 1853 | + splitUnnamedArgument: true, | |
| 1816 | 1854 | helpString: ` |
| 1817 | 1855 | <div> |
| 1818 | 1856 | Performs a modulo operation of two values and passes the result down the pipe. |
| @@ -1837,17 +1875,18 @@ export function registerVariableCommands() { | ||
| 1837 | 1875 | description: 'base', |
| 1838 | 1876 | typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.VARIABLE_NAME], |
| 1839 | 1877 | isRequired: true, |
| 1840 | 1878 | enumProvider: commonEnumProviders.variables('all')numbersAndVariables, |
| 1841 | 1879 | forceEnum: false, |
| 1842 | 1880 | }), |
| 1843 | 1881 | SlashCommandArgument.fromProps({ |
| 1844 | 1882 | description: 'exponent', |
| 1845 | 1883 | typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.VARIABLE_NAME], |
| 1846 | 1884 | isRequired: true, |
| 1847 | 1885 | enumProvider: commonEnumProviders.variables('all')numbersAndVariables, |
| 1848 | 1886 | forceEnum: false, |
| 1849 | 1887 | }), |
| 1850 | 1888 | ], |
| 1889 | + splitUnnamedArgument: true, | |
| 1851 | 1890 | helpString: ` |
| 1852 | 1891 | <div> |
| 1853 | 1892 | Performs a power operation of two values and passes the result down the pipe. |
| @@ -1872,7 +1911,7 @@ export function registerVariableCommands() { | ||
| 1872 | 1911 | description: 'value', |
| 1873 | 1912 | typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.VARIABLE_NAME], |
| 1874 | 1913 | isRequired: true, |
| 1875 | 1914 | enumProvider: commonEnumProviders.variables('all')numbersAndVariables, |
| 1876 | 1915 | forceEnum: false, |
| 1877 | 1916 | }), |
| 1878 | 1917 | ], |
| @@ -1900,7 +1939,7 @@ export function registerVariableCommands() { | ||
| 1900 | 1939 | description: 'value', |
| 1901 | 1940 | typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.VARIABLE_NAME], |
| 1902 | 1941 | isRequired: true, |
| 1903 | 1942 | enumProvider: commonEnumProviders.variables('all')numbersAndVariables, |
| 1904 | 1943 | forceEnum: false, |
| 1905 | 1944 | }), |
| 1906 | 1945 | ], |
| @@ -1929,7 +1968,7 @@ export function registerVariableCommands() { | ||
| 1929 | 1968 | description: 'value', |
| 1930 | 1969 | typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.VARIABLE_NAME], |
| 1931 | 1970 | isRequired: true, |
| 1932 | 1971 | enumProvider: commonEnumProviders.variables('all')numbersAndVariables, |
| 1933 | 1972 | forceEnum: false, |
| 1934 | 1973 | }), |
| 1935 | 1974 | ], |
| @@ -1957,7 +1996,7 @@ export function registerVariableCommands() { | ||
| 1957 | 1996 | description: 'value', |
| 1958 | 1997 | typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.VARIABLE_NAME], |
| 1959 | 1998 | isRequired: true, |
| 1960 | 1999 | enumProvider: commonEnumProviders.variables('all')numbersAndVariables, |
| 1961 | 2000 | forceEnum: false, |
| 1962 | 2001 | }), |
| 1963 | 2002 | ], |
| @@ -1985,7 +2024,7 @@ export function registerVariableCommands() { | ||
| 1985 | 2024 | description: 'value', |
| 1986 | 2025 | typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.VARIABLE_NAME], |
| 1987 | 2026 | isRequired: true, |
| 1988 | 2027 | enumProvider: commonEnumProviders.variables('all')numbersAndVariables, |
| 1989 | 2028 | forceEnum: false, |
| 1990 | 2029 | }), |
| 1991 | 2030 | ], |
| @@ -2013,7 +2052,7 @@ export function registerVariableCommands() { | ||
| 2013 | 2052 | description: 'value', |
| 2014 | 2053 | typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.VARIABLE_NAME], |
| 2015 | 2054 | isRequired: true, |
| 2016 | 2055 | enumProvider: commonEnumProviders.variables('all')numbersAndVariables, |
| 2017 | 2056 | forceEnum: false, |
| 2018 | 2057 | }), |
| 2019 | 2058 | ], |
| @@ -323,7 +323,7 @@ async function sendMakerSuiteRequest(request, response) { | ||
| 323 | 323 | ? (stream ? 'streamGenerateContent' : 'generateContent') |
| 324 | 324 | : (isText ? 'generateText' : 'generateMessage'); |
| 325 | 325 | |
| 326 | 326 | const generateResponse = await fetch(`${apiUrl.origin}/${apiVersion}/models/${model}:${responseType}?key=${apiKey}${stream ? '&alt=sse' : ''}`, { |
| 327 | 327 | body: JSON.stringify(body), |
| 328 | 328 | method: 'POST', |
| 329 | 329 | headers: { |
| @@ -10,41 +10,6 @@ const { readSecret, SECRET_KEYS } = require('./secrets.js'); | ||
| 10 | 10 | const FormData = require('form-data'); |
| 11 | 11 | |
| 12 | 12 | /** |
| 13 | - * Sanitizes a string. | |
| 14 | - * @param {string} x String to sanitize | |
| 15 | - * @returns {string} Sanitized string | |
| 16 | - */ | |
| 17 | -function safeStr(x) { | |
| 18 | - x = String(x); | |
| 19 | - x = x.replace(/ +/g, ' '); | |
| 20 | - x = x.trim(); | |
| 21 | - x = x.replace(/^[\s,.]+|[\s,.]+$/g, ''); | |
| 22 | - return x; | |
| 23 | -} | |
| 24 | - | |
| 25 | -const splitStrings = [ | |
| 26 | - ', extremely', | |
| 27 | - ', intricate,', | |
| 28 | -]; | |
| 29 | - | |
| 30 | -const dangerousPatterns = '[]【】()()|::'; | |
| 31 | - | |
| 32 | -/** | |
| 33 | - * Removes patterns from a string. | |
| 34 | - * @param {string} x String to sanitize | |
| 35 | - * @param {string} pattern Pattern to remove | |
| 36 | - * @returns {string} Sanitized string | |
| 37 | - */ | |
| 38 | -function removePattern(x, pattern) { | |
| 39 | - for (let i = 0; i < pattern.length; i++) { | |
| 40 | - let p = pattern[i]; | |
| 41 | - let regex = new RegExp('\\' + p, 'g'); | |
| 42 | - x = x.replace(regex, ''); | |
| 43 | - } | |
| 44 | - return x; | |
| 45 | -} | |
| 46 | - | |
| 47 | -/** | |
| 48 | 13 | * Gets the comfy workflows. |
| 49 | 14 | * @param {import('../users.js').UserDirectoryList} directories |
| 50 | 15 | * @returns {string[]} List of comfy workflows |
| @@ -391,40 +356,6 @@ router.post('/sd-next/upscalers', jsonParser, async (request, response) => { | ||
| 391 | 356 | } |
| 392 | 357 | }); |
| 393 | 358 | |
| 394 | -/** | |
| 395 | - * SD prompt expansion using GPT-2 text generation model. | |
| 396 | - * Adapted from: https://github.com/lllyasviel/Fooocus/blob/main/modules/expansion.py | |
| 397 | - */ | |
| 398 | -router.post('/expand', jsonParser, async (request, response) => { | |
| 399 | - const originalPrompt = request.body.prompt; | |
| 400 | - | |
| 401 | - if (!originalPrompt) { | |
| 402 | - console.warn('No prompt provided for SD expansion.'); | |
| 403 | - return response.send({ prompt: '' }); | |
| 404 | - } | |
| 405 | - | |
| 406 | - console.log('Refine prompt input:', originalPrompt); | |
| 407 | - const splitString = splitStrings[Math.floor(Math.random() * splitStrings.length)]; | |
| 408 | - let prompt = safeStr(originalPrompt) + splitString; | |
| 409 | - | |
| 410 | - try { | |
| 411 | - const task = 'text-generation'; | |
| 412 | - const module = await import('../transformers.mjs'); | |
| 413 | - const pipe = await module.default.getPipeline(task); | |
| 414 | - | |
| 415 | - const result = await pipe(prompt, { num_beams: 1, max_new_tokens: 256, do_sample: true }); | |
| 416 | - | |
| 417 | - const newText = result[0].generated_text; | |
| 418 | - const newPrompt = safeStr(removePattern(newText, dangerousPatterns)); | |
| 419 | - console.log('Refine prompt output:', newPrompt); | |
| 420 | - | |
| 421 | - return response.send({ prompt: newPrompt }); | |
| 422 | - } catch { | |
| 423 | - console.warn('Failed to load transformers.js pipeline.'); | |
| 424 | - return response.send({ prompt: originalPrompt }); | |
| 425 | - } | |
| 426 | -}); | |
| 427 | - | |
| 428 | 359 | const comfy = express.Router(); |
| 429 | 360 | |
| 430 | 361 | comfy.post('/ping', jsonParser, async (request, response) => { |
| @@ -31,12 +31,6 @@ const tasks = { | ||
| 31 | 31 | configField: 'extras.embeddingModel', |
| 32 | 32 | quantized: true, |
| 33 | 33 | }, |
| 34 | - 'text-generation': { | |
| 35 | - defaultModel: 'Cohee/fooocus_expansion-onnx', | |
| 36 | - pipeline: null, | |
| 37 | - configField: 'extras.promptExpansionModel', | |
| 38 | - quantized: false, | |
| 39 | - }, | |
| 40 | 34 | 'automatic-speech-recognition': { |
| 41 | 35 | defaultModel: 'Xenova/whisper-small', |
| 42 | 36 | pipeline: null, |