Merge pull request #3682 from SillyTavern/feat-add-regex-toggle-command Add `/regex-toggle` slash command

c9e716d42f8c8ad80226746c94eb2d645865ebbc

Cohee <18619528+Cohee1207@users.noreply.github.com>

Signed
1 files changed, +95 -9Showing whitespace changes
public/scripts/extensions/regex/index.js+95 -9
@@ -4,22 +4,16 @@ import { selected_group } from '../../group-chats.js';
44import { callGenericPopup, POPUP_TYPE } from '../../popup.js';
55import { SlashCommand } from '../../slash-commands/SlashCommand.js';
66import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from '../../slash-commands/SlashCommandArgument.js';
77import { commonEnumProviders, enumIcons } from '../../slash-commands/SlashCommandCommonEnumsProvider.js';
88import { SlashCommandEnumValue, enumTypes } from '../../slash-commands/SlashCommandEnumValue.js';
99import { SlashCommandParser } from '../../slash-commands/SlashCommandParser.js';
1010import { download, equalsIgnoreCaseAndAccents, getFileText, getSortableDelay, isFalseBoolean, isTrueBoolean, regexFromString, setInfoBlock, uuidv4 } from '../../utils.js';
1111import { regex_placement, runRegexScript, substitute_find_regex } from './engine.js';
1212import { t } from '../../i18n.js';
1313import { accountStorage } from '../../util/AccountStorage.js';
1414
1515/**
1616 * @typedef {objectimport('../../char-data.js').RegexScriptData} RegexScript
17- * @property {string} scriptName - The name of the script
18- * @property {boolean} disabled - Whether the script is disabled
19- * @property {string} replaceString - The replace string
20- * @property {string[]} trimStrings - The trim strings
21- * @property {string?} findRegex - The find regex
22- * @property {number?} substituteRegex - The substitute regex
2317 */
2418
2519/**
@@ -418,6 +412,53 @@ function runRegexCallback(args, value) {
418412}
419413
420414/**
415+ * /regex-toggle slash command callback
416+ * @param {{state: string, quiet: string}} args Named arguments
417+ * @param {string} scriptName The name of the script to toggle
418+ * @returns {Promise<string>} The name of the script
419+ */
420+async function toggleRegexCallback(args, scriptName) {
421+ if (typeof scriptName !== 'string') throw new Error('Script name must be a string.');
422+
423+ const quiet = isTrueBoolean(args?.quiet);
424+ const action = isTrueBoolean(args?.state) ? 'enable' :
425+ isFalseBoolean(args?.state) ? 'disable' :
426+ 'toggle';
427+
428+ const scripts = getRegexScripts();
429+ const script = scripts.find(s => equalsIgnoreCaseAndAccents(s.scriptName, scriptName));
430+
431+ if (!script) {
432+ toastr.warning(t`Regex script '${scriptName}' not found.`);
433+ return '';
434+ }
435+
436+ switch (action) {
437+ case 'enable':
438+ script.disabled = false;
439+ break;
440+ case 'disable':
441+ script.disabled = true;
442+ break;
443+ default:
444+ script.disabled = !script.disabled;
445+ break;
446+ }
447+
448+ const isScoped = characters[this_chid]?.data?.extensions?.regex_scripts?.some(s => s.id === script.id);
449+ const index = isScoped ? characters[this_chid]?.data?.extensions?.regex_scripts?.indexOf(script) : scripts.indexOf(script);
450+
451+ await saveRegexScript(script, index, isScoped);
452+ if (script.disabled) {
453+ !quiet && toastr.success(t`Regex script '${scriptName}' has been disabled.`);
454+ } else {
455+ !quiet && toastr.success(t`Regex script '${scriptName}' has been enabled.`);
456+ }
457+
458+ return script.scriptName || '';
459+}
460+
461+/**
421462 * Performs the import of the regex file.
422463 * @param {File} file Input file
423464 * @param {boolean} isScoped Is the script scoped to a character?
@@ -639,6 +680,51 @@ jQuery(async () => {
639680 ],
640681 helpString: 'Runs a Regex extension script by name on the provided string. The script must be enabled.',
641682 }));
683+ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
684+ name: 'regex-toggle',
685+ callback: toggleRegexCallback,
686+ returns: 'The name of the script that was toggled',
687+ namedArgumentList: [
688+ SlashCommandNamedArgument.fromProps({
689+ name: 'state',
690+ 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.',
691+ typeList: [ARGUMENT_TYPE.BOOLEAN],
692+ defaultValue: 'toggle',
693+ enumList: commonEnumProviders.boolean('onOffToggle')(),
694+ }),
695+ SlashCommandNamedArgument.fromProps({
696+ name: 'quiet',
697+ description: 'Suppress the toast message script toggled',
698+ typeList: [ARGUMENT_TYPE.BOOLEAN],
699+ defaultValue: 'false',
700+ enumList: commonEnumProviders.boolean('trueFalse')(),
701+ }),
702+ ],
703+ unnamedArgumentList: [
704+ SlashCommandArgument.fromProps({
705+ description: 'script name',
706+ typeList: [ARGUMENT_TYPE.STRING],
707+ isRequired: true,
708+ enumProvider: localEnumProviders.regexScripts,
709+ }),
710+ ],
711+ helpString: `
712+ <div>
713+ Toggles the state of a specified regex script.
714+ </div>
715+ <div>
716+ <strong>Example:</strong>
717+ <ul>
718+ <li>
719+ <pre><code class="language-stscript">/regex-toggle MyScript</code></pre>
720+ </li>
721+ <li>
722+ <pre><code class="language-stscript">/regex-toggle state=off Character-specific Script</code></pre>
723+ </li>
724+ </ul>
725+ </div>
726+ `,
727+ }));
642728
643729 eventSource.on(event_types.CHAT_CHANGED, checkEmbeddedRegexScripts);
644730 eventSource.on(event_types.CHARACTER_DELETED, purgeEmbeddedRegexScripts);