Regex: Code clean-up (#4628) * Add confirmation for bulk move, add UNKNOWN type, improve JSDoc * Fix toggle all bulk move visibility * Improve function documentation * Refactor currentChatId check and improve JSDoc for getScriptDecorators function * Check against known values in moveRegexScript * Fix console warning spam * Simplify currentChatId check * Prevent moving scripts into the void * Make texts translatable

9dddbbb04a95422e693ab663f0fa6cd9d770743d

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

Signed
2 files changed, +86 -40Showing whitespace changes
public/scripts/extensions/regex/engine.js+24 -15
@@ -1,16 +1,13 @@
1import { characters, substituteParams, substituteParamsExtended, this_chid } from '../../../script.js';1import { characters, substituteParams, substituteParamsExtended, this_chid } from '../../../script.js';
2import { extension_settings } from '../../extensions.js';2import { extension_settings } from '../../extensions.js';
3import { regexFromString } from '../../utils.js';3import { regexFromString } from '../../utils.js';
4export {
5 regex_placement,
6 getRegexedString,
7 runRegexScript,
8};
94
10/**5/**
11 * @enum {number} Regex scripts types6 * @enum {number} Regex scripts types
7 * @readonly
12 */8 */
13export const SCRIPT_TYPES = {9export const SCRIPT_TYPES = {
10 UNKNOWN: -1,
14 GLOBAL: 0,11 GLOBAL: 0,
15 SCOPED: 1,12 SCOPED: 1,
16};13};
@@ -21,28 +18,34 @@ export const SCRIPT_TYPES = {
2118
22/**19/**
23 * @typedef {object} GetRegexScriptsOptions20 * @typedef {object} GetRegexScriptsOptions
24 * @property {boolean} allowedOnly only return allowed scripts21 * @property {boolean} allowedOnly Only return allowed scripts
22 */
23
24/**
25 * @type {Readonly<GetRegexScriptsOptions>}
25 */26 */
26const DEFAULT_GET_REGEX_SCRIPTS_OPTIONS = { allowedOnly: false };27const DEFAULT_GET_REGEX_SCRIPTS_OPTIONS = Object.freeze({ allowedOnly: false });
2728
28/**29/**
29 * Retrieves the list of regex scripts by combining the scripts from the extension settings and the character data30 * Retrieves the list of regex scripts by combining the scripts from the extension settings and the character data
30 *31 *
31 * @param {GetRegexScriptsOptions} option32 * @param {GetRegexScriptsOptions} options Options for retrieving the regex scripts
32 * @returns {RegexScript[]} An array of regex scripts, where each script is an object containing the necessary information.33 * @returns {RegexScript[]} An array of regex scripts, where each script is an object containing the necessary information.
33 */34 */
34export function getRegexScripts(option = DEFAULT_GET_REGEX_SCRIPTS_OPTIONS) {35export function getRegexScripts(options = DEFAULT_GET_REGEX_SCRIPTS_OPTIONS) {
35 return [...Object.values(SCRIPT_TYPES).flatMap(type => getScriptsByType(type, option))];36 return [...Object.values(SCRIPT_TYPES).flatMap(type => getScriptsByType(type, options))];
36}37}
3738
38/**39/**
39 * Retrieves the regex scripts for a specific type.40 * Retrieves the regex scripts for a specific type.
40 * @param {SCRIPT_TYPES} scriptType41 * @param {SCRIPT_TYPES} scriptType The type of regex scripts to retrieve.
41 * @param {GetRegexScriptsOptions} option42 * @param {GetRegexScriptsOptions} options Options for retrieving the regex scripts
42 * @returns {RegexScript[]} An array of regex scripts for the specified type.43 * @returns {RegexScript[]} An array of regex scripts for the specified type.
43 */44 */
44export function getScriptsByType(scriptType, { allowedOnly } = DEFAULT_GET_REGEX_SCRIPTS_OPTIONS) {45export function getScriptsByType(scriptType, { allowedOnly } = DEFAULT_GET_REGEX_SCRIPTS_OPTIONS) {
45 switch (scriptType) {46 switch (scriptType) {
47 case SCRIPT_TYPES.UNKNOWN:
48 return [];
46 case SCRIPT_TYPES.GLOBAL:49 case SCRIPT_TYPES.GLOBAL:
47 return extension_settings.regex ?? [];50 return extension_settings.regex ?? [];
48 case SCRIPT_TYPES.SCOPED: {51 case SCRIPT_TYPES.SCOPED: {
@@ -53,14 +56,16 @@ export function getScriptsByType(scriptType, { allowedOnly } = DEFAULT_GET_REGEX
53 return Array.isArray(scopedScripts) ? scopedScripts : [];56 return Array.isArray(scopedScripts) ? scopedScripts : [];
54 }57 }
55 default:58 default:
59 console.warn(`getScriptsByType: Invalid script type ${scriptType}`);
56 return [];60 return [];
57 }61 }
58}62}
5963
60/**64/**
61 * @enum {number} Where the regex script should be applied65 * @enum {number} Where the regex script should be applied
66 * @readonly
62 */67 */
63const regex_placement = {68export const regex_placement = {
64 /**69 /**
65 * @deprecated MD Display is deprecated. Do not use.70 * @deprecated MD Display is deprecated. Do not use.
66 */71 */
@@ -73,6 +78,10 @@ const regex_placement = {
73 REASONING: 6,78 REASONING: 6,
74};79};
7580
81/**
82 * @enum {number} How to substitute parameters in the find regex
83 * @readonly
84 */
76export const substitute_find_regex = {85export const substitute_find_regex = {
77 NONE: 0,86 NONE: 0,
78 RAW: 1,87 RAW: 1,
@@ -109,7 +118,7 @@ function sanitizeRegexMacro(x) {
109 * @returns {string} The regexed string118 * @returns {string} The regexed string
110 * @typedef {{characterOverride?: string, isMarkdown?: boolean, isPrompt?: boolean, isEdit?: boolean, depth?: number }} RegexParams The parameters to use for the regex script119 * @typedef {{characterOverride?: string, isMarkdown?: boolean, isPrompt?: boolean, isEdit?: boolean, depth?: number }} RegexParams The parameters to use for the regex script
111 */120 */
112function getRegexedString(rawString, placement, { characterOverride, isMarkdown, isPrompt, isEdit, depth } = {}) {121export function getRegexedString(rawString, placement, { characterOverride, isMarkdown, isPrompt, isEdit, depth } = {}) {
113 // WTF have you passed me?122 // WTF have you passed me?
114 if (typeof rawString !== 'string') {123 if (typeof rawString !== 'string') {
115 console.warn('getRegexedString: rawString is not a string. Returning empty string.');124 console.warn('getRegexedString: rawString is not a string. Returning empty string.');
@@ -166,7 +175,7 @@ function getRegexedString(rawString, placement, { characterOverride, isMarkdown,
166 * @returns {string} The new string175 * @returns {string} The new string
167 * @typedef {{characterOverride?: string}} RegexScriptParams The parameters to use for the regex script176 * @typedef {{characterOverride?: string}} RegexScriptParams The parameters to use for the regex script
168 */177 */
169function runRegexScript(regexScript, rawString, { characterOverride } = {}) {178export function runRegexScript(regexScript, rawString, { characterOverride } = {}) {
170 let newString = rawString;179 let newString = rawString;
171 if (!regexScript || !!(regexScript.disabled) || !regexScript?.findRegex || !rawString) {180 if (!regexScript || !!(regexScript.disabled) || !regexScript?.findRegex || !rawString) {
172 return newString;181 return newString;
public/scripts/extensions/regex/index.js+62 -25
@@ -476,10 +476,12 @@ function setToggleAllIcon(allAreChecked) {
476 selectAllIcon.toggleClass('fa-minus', allAreChecked);476 selectAllIcon.toggleClass('fa-minus', allAreChecked);
477}477}
478478
479/**
480 * Sets the visibility of the bulk move buttons based on selected scripts.
481 */
479function setMoveButtonsVisibility() {482function setMoveButtonsVisibility() {
480 const hasGlobalScripts = $('#saved_regex_scripts .regex-script-label:has(.regex_bulk_checkbox:checked)').length > 0;483 const hasGlobalScripts = $('#saved_regex_scripts .regex-script-label:has(.regex_bulk_checkbox:checked)').length > 0;
481 const hasScopedScripts =484 const hasScopedScripts = $('#saved_scoped_scripts .regex-script-label:has(.regex_bulk_checkbox:checked)').length > 0;
482 $('#saved_scoped_scripts .regex-script-label:has(.regex_bulk_checkbox:checked)').length > 0;
483 $('#bulk_regex_move_to_global').toggle(hasScopedScripts);485 $('#bulk_regex_move_to_global').toggle(hasScopedScripts);
484 $('#bulk_regex_move_to_scoped').toggle(hasGlobalScripts);486 $('#bulk_regex_move_to_scoped').toggle(hasGlobalScripts);
485}487}
@@ -538,7 +540,7 @@ async function saveRegexScript(regexScript, existingScriptIndex, scriptType, sav
538540
539 // Reload the current chat to undo previous markdown541 // Reload the current chat to undo previous markdown
540 const currentChatId = getCurrentChatId();542 const currentChatId = getCurrentChatId();
541 if (currentChatId !== undefined && currentChatId !== null) {543 if (currentChatId) {
542 await reloadCurrentChat();544 await reloadCurrentChat();
543 }545 }
544 }546 }
@@ -550,10 +552,10 @@ async function saveRegexScript(regexScript, existingScriptIndex, scriptType, sav
550}552}
551553
552/**554/**
553 * Delete a regex script555 * Delete a regex script by ID
554 * @param {string} id556 * @param {string} id ID of the script to delete
555 * @param {SCRIPT_TYPES} scriptType557 * @param {SCRIPT_TYPES} scriptType global? scoped?
556 * @param {boolean} saveSettings558 * @param {boolean} saveSettings Whether to save the settings immediately
557 * @returns {Promise<void>}559 * @returns {Promise<void>}
558 */560 */
559async function deleteRegexScript(id, scriptType, saveSettings = true) {561async function deleteRegexScript(id, scriptType, saveSettings = true) {
@@ -575,17 +577,21 @@ async function deleteRegexScript(id, scriptType, saveSettings = true) {
575577
576/**578/**
577 * Move a regex script from one type to another579 * Move a regex script from one type to another
578 * @param {import('../../char-data.js').RegexScriptData} script580 * @param {import('../../char-data.js').RegexScriptData} script The script to move
579 * @param {SCRIPT_TYPES} toType581 * @param {SCRIPT_TYPES} toType Target type
580 * @param {SCRIPT_TYPES|null} fromType582 * @param {SCRIPT_TYPES|null} fromType Source type, if null it will be determined automatically
581 * @param {boolean} saveSettings583 * @param {boolean} saveSettings Whether to save the settings immediately
582 * @returns {Promise<void>}584 * @returns {Promise<void>}
583 */585 */
584async function moveRegexScript(script, toType, fromType = null, saveSettings = true) {586async function moveRegexScript(script, toType, fromType = null, saveSettings = true) {
585 if (!fromType) {587 if (!Object.values(SCRIPT_TYPES).includes(toType)) {
588 console.warn(`moveRegexScript: Invalid target script type ${toType}`);
589 return;
590 }
591 if (!Object.values(SCRIPT_TYPES).includes(fromType)) {
586 fromType = getScriptType(script);592 fromType = getScriptType(script);
587 }593 }
588 if (fromType === toType || fromType === -1) {594 if (fromType === toType || fromType === SCRIPT_TYPES.UNKNOWN || toType === SCRIPT_TYPES.UNKNOWN) {
589 return;595 return;
590 }596 }
591 await deleteRegexScript(script.id, fromType, false);597 await deleteRegexScript(script.id, fromType, false);
@@ -1234,13 +1240,13 @@ async function onRegexDebuggerOpenClick() {
1234 });1240 });
12351241
1236 popupContainer.append(navPanel).append(contentPanel);1242 popupContainer.append(navPanel).append(contentPanel);
1237 callGenericPopup(popupContainer, POPUP_TYPE.TEXT, 'Step-by-step Transformation', { wide: true, allowVerticalScrolling: false });1243 callGenericPopup(popupContainer, POPUP_TYPE.TEXT, t`Step-by-step Transformation`, { wide: true, allowVerticalScrolling: false });
1238 });1244 });
12391245
1240 debuggerHtml.find('#regex_debugger_expand_final').on('click', function () {1246 debuggerHtml.find('#regex_debugger_expand_final').on('click', function () {
1241 const content = $('#regex_debugger_final_output').html();1247 const content = $('#regex_debugger_final_output').html();
1242 const popupContent = $('<div class="regex-popup-content"></div>').html(content);1248 const popupContent = $('<div class="regex-popup-content"></div>').html(content);
1243 callGenericPopup(popupContent, POPUP_TYPE.TEXT, 'Final Output', { wide: true, large: true, allowVerticalScrolling: true });1249 callGenericPopup(popupContent, POPUP_TYPE.TEXT, t`Final Output`, { wide: true, large: true, allowVerticalScrolling: true });
1244 });1250 });
12451251
1246 await callGenericPopup(debuggerHtml.children(), POPUP_TYPE.TEXT, '', { wide: true, allowVerticalScrolling: true });1252 await callGenericPopup(debuggerHtml.children(), POPUP_TYPE.TEXT, '', { wide: true, allowVerticalScrolling: true });
@@ -1465,10 +1471,23 @@ async function onRegexImportFileChange(file, scriptType) {
1465 }1471 }
1466}1472}
14671473
1474/**
1475 * Determines the type of a given script.
1476 * @param {RegexScript} script The script to check
1477 * @returns {SCRIPT_TYPES} The script type.
1478 */
1468function getScriptType(script) {1479function getScriptType(script) {
1469 return getScriptsByType(SCRIPT_TYPES.SCOPED).some(s => s.id === script.id)1480 const scopedScripts = getScriptsByType(SCRIPT_TYPES.SCOPED);
1470 ? SCRIPT_TYPES.SCOPED1481 if (scopedScripts.some(s => s.id === script.id)) {
1471 : SCRIPT_TYPES.GLOBAL;1482 return SCRIPT_TYPES.SCOPED;
1483 }
1484
1485 const globalScripts = getScriptsByType(SCRIPT_TYPES.GLOBAL);
1486 if (globalScripts.some(s => s.id === script.id)) {
1487 return SCRIPT_TYPES.GLOBAL;
1488 }
1489
1490 return SCRIPT_TYPES.UNKNOWN;
1472}1491}
14731492
1474function getSelectedScripts() {1493function getSelectedScripts() {
@@ -1591,6 +1610,7 @@ jQuery(async () => {
15911610
1592 checkboxes.prop('checked', newState);1611 checkboxes.prop('checked', newState);
1593 setToggleAllIcon(newState);1612 setToggleAllIcon(newState);
1613 setMoveButtonsVisibility();
1594 });1614 });
15951615
1596 $('#bulk_enable_regex').on('click', async function () {1616 $('#bulk_enable_regex').on('click', async function () {
@@ -1637,12 +1657,26 @@ jQuery(async () => {
16371657
1638 // Reload the current chat to undo previous markdown1658 // Reload the current chat to undo previous markdown
1639 const currentChatId = getCurrentChatId();1659 const currentChatId = getCurrentChatId();
1640 if (currentChatId !== undefined && currentChatId !== null) {1660 if (currentChatId) {
1641 await reloadCurrentChat();1661 await reloadCurrentChat();
1642 }1662 }
1643 }1663 }
1644 $('#bulk_regex_move_to_global').on('click', () => bulkMoveRegexScript(SCRIPT_TYPES.GLOBAL));1664
1645 $('#bulk_regex_move_to_scoped').on('click', () => bulkMoveRegexScript(SCRIPT_TYPES.SCOPED));1665 $('#bulk_regex_move_to_global').on('click', async () => {
1666 const confirm = await callGenericPopup(t`Are you sure you want to move the selected regex scripts to global?`, POPUP_TYPE.CONFIRM);
1667 if (!confirm) {
1668 return;
1669 }
1670 await bulkMoveRegexScript(SCRIPT_TYPES.GLOBAL);
1671 });
1672
1673 $('#bulk_regex_move_to_scoped').on('click', async () => {
1674 const confirm = await callGenericPopup(t`Are you sure you want to move the selected regex scripts to scoped?`, POPUP_TYPE.CONFIRM);
1675 if (!confirm) {
1676 return;
1677 }
1678 await bulkMoveRegexScript(SCRIPT_TYPES.SCOPED);
1679 });
16461680
1647 $('#bulk_delete_regex').on('click', async function () {1681 $('#bulk_delete_regex').on('click', async function () {
1648 const scripts = getSelectedScripts();1682 const scripts = getSelectedScripts();
@@ -1650,7 +1684,7 @@ jQuery(async () => {
1650 toastr.warning(t`No regex scripts selected for deletion.`);1684 toastr.warning(t`No regex scripts selected for deletion.`);
1651 return;1685 return;
1652 }1686 }
1653 const confirm = await callGenericPopup('Are you sure you want to delete the selected regex scripts?', POPUP_TYPE.CONFIRM);1687 const confirm = await callGenericPopup(t`Are you sure you want to delete the selected regex scripts?`, POPUP_TYPE.CONFIRM);
1654 if (!confirm) {1688 if (!confirm) {
1655 return;1689 return;
1656 }1690 }
@@ -1750,9 +1784,10 @@ jQuery(async () => {
1750 * @property {import('../../slash-commands/SlashCommandEnumValue.js').EnumType} color1784 * @property {import('../../slash-commands/SlashCommandEnumValue.js').EnumType} color
1751 * @property {string} icon1785 * @property {string} icon
1752 */1786 */
1787
1753 /**1788 /**
1754 * @param {SCRIPT_TYPES} type1789 * @param {SCRIPT_TYPES} type The script type
1755 * @returns {ScriptDecorators}1790 * @returns {ScriptDecorators} The decorators for the script type
1756 */1791 */
1757 function getScriptDecorators(type) {1792 function getScriptDecorators(type) {
1758 switch (type) {1793 switch (type) {
@@ -1768,13 +1803,15 @@ jQuery(async () => {
1768 color: enumTypes.name,1803 color: enumTypes.name,
1769 icon: 'S',1804 icon: 'S',
1770 };1805 };
1771 }1806 default:
1772 return {1807 return {
1773 typename: 'Unknown',1808 typename: 'Unknown',
1774 color: enumTypes.variable,1809 color: enumTypes.variable,
1775 icon: 'Unknown',1810 icon: 'Unknown',
1776 };1811 };
1777 }1812 }
1813 }
1814
1778 const localEnumProviders = {1815 const localEnumProviders = {
1779 regexScripts: () =>1816 regexScripts: () =>
1780 getRegexScripts().map(script => {1817 getRegexScripts().map(script => {