From 7537192c9aff0b5756e697cf2d91b0a428312756 Mon Sep 17 00:00:00 2001 From: Wolfsblvt Date: Thu, 13 Mar 2025 23:55:08 +0100 Subject: [PATCH 1/2] Add /regex-toggle slash command - Add /regex-toggle command, similarly to /extension-toggle - toggles the state of both global and character-bound scripts - Update jsdoc being inconsistent Closes #3613 --- public/scripts/extensions/regex/index.js | 85 +++++++++++++++++++++--- 1 file changed, 76 insertions(+), 9 deletions(-) diff --git a/public/scripts/extensions/regex/index.js b/public/scripts/extensions/regex/index.js index 8576e9680..9e5685521 100644 --- a/public/scripts/extensions/regex/index.js +++ b/public/scripts/extensions/regex/index.js @@ -4,22 +4,16 @@ import { selected_group } from '../../group-chats.js'; import { callGenericPopup, POPUP_TYPE } from '../../popup.js'; import { SlashCommand } from '../../slash-commands/SlashCommand.js'; import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from '../../slash-commands/SlashCommandArgument.js'; -import { enumIcons } from '../../slash-commands/SlashCommandCommonEnumsProvider.js'; +import { commonEnumProviders, enumIcons } from '../../slash-commands/SlashCommandCommonEnumsProvider.js'; import { SlashCommandEnumValue, enumTypes } from '../../slash-commands/SlashCommandEnumValue.js'; import { SlashCommandParser } from '../../slash-commands/SlashCommandParser.js'; -import { download, getFileText, getSortableDelay, regexFromString, setInfoBlock, uuidv4 } from '../../utils.js'; +import { download, equalsIgnoreCaseAndAccents, getFileText, getSortableDelay, isFalseBoolean, isTrueBoolean, regexFromString, setInfoBlock, uuidv4 } from '../../utils.js'; import { regex_placement, runRegexScript, substitute_find_regex } from './engine.js'; import { t } from '../../i18n.js'; import { accountStorage } from '../../util/AccountStorage.js'; /** - * @typedef {object} RegexScript - * @property {string} scriptName - The name of the script - * @property {boolean} disabled - Whether the script is disabled - * @property {string} replaceString - The replace string - * @property {string[]} trimStrings - The trim strings - * @property {string?} findRegex - The find regex - * @property {number?} substituteRegex - The substitute regex + * @typedef {import('../../char-data.js').RegexScriptData} RegexScript */ /** @@ -417,6 +411,42 @@ function runRegexCallback(args, value) { return value; } +/** + * /regex-toggle slash command callback + * @param {{state: string}} args Named arguments + * @param {string} scriptName The name of the script to toggle + * @returns {Promise} The regexed string + */ +async function toggleRegexCallback(args, scriptName) { + if (typeof scriptName !== 'string') throw new Error('Script name must be a string.'); + + const action = isTrueBoolean(args?.state) ? 'enable' : + isFalseBoolean(args?.state) ? 'disable' : + 'toggle'; + + const scripts = getRegexScripts(); + const script = scripts.find(s => equalsIgnoreCaseAndAccents(s.scriptName, scriptName)); + + if (!script) { + toastr.warning(t`Regex script '${scriptName}' not found.`); + return; + } + + script.disabled = action === 'enable' ? false : action === 'disable' ? true : !script.disabled; + + const isScoped = characters[this_chid]?.data?.extensions?.regex_scripts?.some(s => s.id === script.id); + const index = isScoped ? characters[this_chid]?.data?.extensions?.regex_scripts?.indexOf(script) : scripts.indexOf(script); + + await saveRegexScript(script, index, isScoped); + if (script.disabled) { + toastr.success(t`Regex script '${scriptName}' has been disabled.`); + } else { + toastr.success(t`Regex script '${scriptName}' has been enabled.`); + } + + return script.scriptName || ''; +} + /** * Performs the import of the regex file. * @param {File} file Input file @@ -639,6 +669,43 @@ jQuery(async () => { ], helpString: 'Runs a Regex extension script by name on the provided string. The script must be enabled.', })); + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ + name: 'regex-toggle', + callback: toggleRegexCallback, + returns: 'The name of the script that was toggled', + namedArgumentList: [ + SlashCommandNamedArgument.fromProps({ + name: 'state', + description: 'Explicitly set the state of the script (true to enable, false to disable). If not provided, the state will be toggled to the opposite of the current state.', + typeList: [ARGUMENT_TYPE.BOOLEAN], + enumList: commonEnumProviders.boolean('trueFalse')(), + }), + ], + unnamedArgumentList: [ + SlashCommandArgument.fromProps({ + description: 'script name', + typeList: [ARGUMENT_TYPE.STRING], + isRequired: true, + enumProvider: localEnumProviders.regexScripts, + }), + ], + helpString: ` +
+ Toggles the state of a specified regex script. +
+
+ Example: +
    +
  • +
    /regex-toggle MyScript
    +
  • +
  • +
    /regex-toggle state=off Character-specific Script
    +
  • +
+
+ `, + })); eventSource.on(event_types.CHAT_CHANGED, checkEmbeddedRegexScripts); eventSource.on(event_types.CHARACTER_DELETED, purgeEmbeddedRegexScripts); From 18fa33d816a567559bc1844c195f837451ba1c76 Mon Sep 17 00:00:00 2001 From: Wolfsblvt Date: Fri, 14 Mar 2025 01:03:08 +0100 Subject: [PATCH 2/2] On review feedback of /regex-toggle - Add quiet arg to suppress success toast - Fix return values - Switch-case instead of nested ternaries - state uses onOfToggle --- public/scripts/extensions/regex/index.js | 33 +++++++++++++++++++----- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/public/scripts/extensions/regex/index.js b/public/scripts/extensions/regex/index.js index 9e5685521..4805a6ed3 100644 --- a/public/scripts/extensions/regex/index.js +++ b/public/scripts/extensions/regex/index.js @@ -413,13 +413,14 @@ function runRegexCallback(args, value) { /** * /regex-toggle slash command callback - * @param {{state: string}} args Named arguments + * @param {{state: string, quiet: string}} args Named arguments * @param {string} scriptName The name of the script to toggle - * @returns {Promise} The regexed string + * @returns {Promise} The name of the script */ async function toggleRegexCallback(args, scriptName) { if (typeof scriptName !== 'string') throw new Error('Script name must be a string.'); + const quiet = isTrueBoolean(args?.quiet); const action = isTrueBoolean(args?.state) ? 'enable' : isFalseBoolean(args?.state) ? 'disable' : 'toggle'; @@ -429,19 +430,29 @@ async function toggleRegexCallback(args, scriptName) { if (!script) { toastr.warning(t`Regex script '${scriptName}' not found.`); - return; + return ''; } - script.disabled = action === 'enable' ? false : action === 'disable' ? true : !script.disabled; + switch (action) { + case 'enable': + script.disabled = false; + break; + case 'disable': + script.disabled = true; + break; + default: + script.disabled = !script.disabled; + break; + } const isScoped = characters[this_chid]?.data?.extensions?.regex_scripts?.some(s => s.id === script.id); const index = isScoped ? characters[this_chid]?.data?.extensions?.regex_scripts?.indexOf(script) : scripts.indexOf(script); await saveRegexScript(script, index, isScoped); if (script.disabled) { - toastr.success(t`Regex script '${scriptName}' has been disabled.`); + !quiet && toastr.success(t`Regex script '${scriptName}' has been disabled.`); } else { - toastr.success(t`Regex script '${scriptName}' has been enabled.`); + !quiet && toastr.success(t`Regex script '${scriptName}' has been enabled.`); } return script.scriptName || ''; @@ -676,8 +687,16 @@ jQuery(async () => { namedArgumentList: [ SlashCommandNamedArgument.fromProps({ name: 'state', - description: 'Explicitly set the state of the script (true to enable, false to disable). If not provided, the state will be toggled to the opposite of the current state.', + description: 'Explicitly set the state of the script (\'on\' to enable, \'off\' to disable). If not provided, the state will be toggled to the opposite of the current state.', typeList: [ARGUMENT_TYPE.BOOLEAN], + defaultValue: 'toggle', + enumList: commonEnumProviders.boolean('onOffToggle')(), + }), + SlashCommandNamedArgument.fromProps({ + name: 'quiet', + description: 'Suppress the toast message script toggled', + typeList: [ARGUMENT_TYPE.BOOLEAN], + defaultValue: 'false', enumList: commonEnumProviders.boolean('trueFalse')(), }), ],