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
Signed| @@ -1,16 +1,13 @@ | |||
| 1 | import { characters, substituteParams, substituteParamsExtended, this_chid } from '../../../script.js'; | 1 | import { characters, substituteParams, substituteParamsExtended, this_chid } from '../../../script.js'; |
| 2 | import { extension_settings } from '../../extensions.js'; | 2 | import { extension_settings } from '../../extensions.js'; |
| 3 | import { regexFromString } from '../../utils.js'; | 3 | import { regexFromString } from '../../utils.js'; |
| 4 | export { | ||
| 5 | regex_placement, | ||
| 6 | getRegexedString, | ||
| 7 | runRegexScript, | ||
| 8 | }; | ||
| 9 | 4 | ||
| 10 | /** | 5 | /** |
| 11 | * @enum {number} Regex scripts types | 6 | * @enum {number} Regex scripts types |
| 7 | * @readonly | ||
| 12 | */ | 8 | */ |
| 13 | export const SCRIPT_TYPES = { | 9 | export 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 = { | |||
| 21 | 18 | ||
| 22 | /** | 19 | /** |
| 23 | * @typedef {object} GetRegexScriptsOptions | 20 | * @typedef {object} GetRegexScriptsOptions |
| 24 | * @property {boolean} allowedOnly only return allowed scripts | 21 | * @property {boolean} allowedOnly Only return allowed scripts |
| 22 | */ | ||
| 23 | |||
| 24 | /** | ||
| 25 | * @type {Readonly<GetRegexScriptsOptions>} | ||
| 25 | */ | 26 | */ |
| 26 | const DEFAULT_GET_REGEX_SCRIPTS_OPTIONS = { allowedOnly: false }; | 27 | const DEFAULT_GET_REGEX_SCRIPTS_OPTIONS = Object.freeze({ allowedOnly: false }); |
| 27 | 28 | ||
| 28 | /** | 29 | /** |
| 29 | * Retrieves the list of regex scripts by combining the scripts from the extension settings and the character data | 30 | * Retrieves the list of regex scripts by combining the scripts from the extension settings and the character data |
| 30 | * | 31 | * |
| 31 | * @param {GetRegexScriptsOptions} option | 32 | * @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 | */ |
| 34 | export function getRegexScripts(option = DEFAULT_GET_REGEX_SCRIPTS_OPTIONS) { | 35 | export 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 | } |
| 37 | 38 | ||
| 38 | /** | 39 | /** |
| 39 | * Retrieves the regex scripts for a specific type. | 40 | * Retrieves the regex scripts for a specific type. |
| 40 | * @param {SCRIPT_TYPES} scriptType | 41 | * @param {SCRIPT_TYPES} scriptType The type of regex scripts to retrieve. |
| 41 | * @param {GetRegexScriptsOptions} option | 42 | * @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 | */ |
| 44 | export function getScriptsByType(scriptType, { allowedOnly } = DEFAULT_GET_REGEX_SCRIPTS_OPTIONS) { | 45 | export 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 | } |
| 59 | 63 | ||
| 60 | /** | 64 | /** |
| 61 | * @enum {number} Where the regex script should be applied | 65 | * @enum {number} Where the regex script should be applied |
| 66 | * @readonly | ||
| 62 | */ | 67 | */ |
| 63 | const regex_placement = { | 68 | export 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 | }; |
| 75 | 80 | ||
| 81 | /** | ||
| 82 | * @enum {number} How to substitute parameters in the find regex | ||
| 83 | * @readonly | ||
| 84 | */ | ||
| 76 | export const substitute_find_regex = { | 85 | export 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 string | 118 | * @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 script | 119 | * @typedef {{characterOverride?: string, isMarkdown?: boolean, isPrompt?: boolean, isEdit?: boolean, depth?: number }} RegexParams The parameters to use for the regex script |
| 111 | */ | 120 | */ |
| 112 | function getRegexedString(rawString, placement, { characterOverride, isMarkdown, isPrompt, isEdit, depth } = {}) { | 121 | export 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 string | 175 | * @returns {string} The new string |
| 167 | * @typedef {{characterOverride?: string}} RegexScriptParams The parameters to use for the regex script | 176 | * @typedef {{characterOverride?: string}} RegexScriptParams The parameters to use for the regex script |
| 168 | */ | 177 | */ |
| 169 | function runRegexScript(regexScript, rawString, { characterOverride } = {}) { | 178 | export 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; |
| @@ -476,10 +476,12 @@ function setToggleAllIcon(allAreChecked) { | |||
| 476 | selectAllIcon.toggleClass('fa-minus', allAreChecked); | 476 | selectAllIcon.toggleClass('fa-minus', allAreChecked); |
| 477 | } | 477 | } |
| 478 | 478 | ||
| 479 | /** | ||
| 480 | * Sets the visibility of the bulk move buttons based on selected scripts. | ||
| 481 | */ | ||
| 479 | function setMoveButtonsVisibility() { | 482 | function 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 | |||
| 538 | 540 | ||
| 539 | // Reload the current chat to undo previous markdown | 541 | // 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 | } |
| 551 | 553 | ||
| 552 | /** | 554 | /** |
| 553 | * Delete a regex script | 555 | * Delete a regex script by ID |
| 554 | * @param {string} id | 556 | * @param {string} id ID of the script to delete |
| 555 | * @param {SCRIPT_TYPES} scriptType | 557 | * @param {SCRIPT_TYPES} scriptType global? scoped? |
| 556 | * @param {boolean} saveSettings | 558 | * @param {boolean} saveSettings Whether to save the settings immediately |
| 557 | * @returns {Promise<void>} | 559 | * @returns {Promise<void>} |
| 558 | */ | 560 | */ |
| 559 | async function deleteRegexScript(id, scriptType, saveSettings = true) { | 561 | async function deleteRegexScript(id, scriptType, saveSettings = true) { |
| @@ -575,17 +577,21 @@ async function deleteRegexScript(id, scriptType, saveSettings = true) { | |||
| 575 | 577 | ||
| 576 | /** | 578 | /** |
| 577 | * Move a regex script from one type to another | 579 | * Move a regex script from one type to another |
| 578 | * @param {import('../../char-data.js').RegexScriptData} script | 580 | * @param {import('../../char-data.js').RegexScriptData} script The script to move |
| 579 | * @param {SCRIPT_TYPES} toType | 581 | * @param {SCRIPT_TYPES} toType Target type |
| 580 | * @param {SCRIPT_TYPES|null} fromType | 582 | * @param {SCRIPT_TYPES|null} fromType Source type, if null it will be determined automatically |
| 581 | * @param {boolean} saveSettings | 583 | * @param {boolean} saveSettings Whether to save the settings immediately |
| 582 | * @returns {Promise<void>} | 584 | * @returns {Promise<void>} |
| 583 | */ | 585 | */ |
| 584 | async function moveRegexScript(script, toType, fromType = null, saveSettings = true) { | 586 | async 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 | }); |
| 1235 | 1241 | ||
| 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 | }); |
| 1239 | 1245 | ||
| 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 | }); |
| 1245 | 1251 | ||
| 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 | } |
| 1467 | 1473 | ||
| 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 | */ | ||
| 1468 | function getScriptType(script) { | 1479 | function getScriptType(script) { |
| 1469 | return getScriptsByType(SCRIPT_TYPES.SCOPED).some(s => s.id === script.id) | 1480 | const scopedScripts = getScriptsByType(SCRIPT_TYPES.SCOPED); |
| 1470 | ? SCRIPT_TYPES.SCOPED | 1481 | 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 | } |
| 1473 | 1492 | ||
| 1474 | function getSelectedScripts() { | 1493 | function getSelectedScripts() { |
| @@ -1591,6 +1610,7 @@ jQuery(async () => { | |||
| 1591 | 1610 | ||
| 1592 | checkboxes.prop('checked', newState); | 1611 | checkboxes.prop('checked', newState); |
| 1593 | setToggleAllIcon(newState); | 1612 | setToggleAllIcon(newState); |
| 1613 | setMoveButtonsVisibility(); | ||
| 1594 | }); | 1614 | }); |
| 1595 | 1615 | ||
| 1596 | $('#bulk_enable_regex').on('click', async function () { | 1616 | $('#bulk_enable_regex').on('click', async function () { |
| @@ -1637,12 +1657,26 @@ jQuery(async () => { | |||
| 1637 | 1657 | ||
| 1638 | // Reload the current chat to undo previous markdown | 1658 | // 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 | }); | ||
| 1646 | 1680 | ||
| 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} color | 1784 | * @property {import('../../slash-commands/SlashCommandEnumValue.js').EnumType} color |
| 1751 | * @property {string} icon | 1785 | * @property {string} icon |
| 1752 | */ | 1786 | */ |
| 1787 | |||
| 1753 | /** | 1788 | /** |
| 1754 | * @param {SCRIPT_TYPES} type | 1789 | * @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 => { |