Merge pull request #3648 from SillyTavern/regex-info-block Add info block for find regex flags
Signed| @@ -16,6 +16,13 @@ | |||
| 16 | </small> | 16 | </small> |
| 17 | <hr /> | 17 | <hr /> |
| 18 | 18 | ||
| 19 | <div id="regex_info_block_wrapper"> | ||
| 20 | <div id="regex_info_block" class="info-block"></div> | ||
| 21 | <a id="regex_info_block_flags_hint" href="http://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_expressions#advanced_searching_with_flags" target="_blank" rel="noopener noreferrer"> | ||
| 22 | <i class="fa-solid fa-circle-info" title="Click here to learn more about regex flags."></i> | ||
| 23 | </a> | ||
| 24 | </div> | ||
| 25 | |||
| 19 | <div id="regex_test_mode" class="flex1 flex-container displayNone"> | 26 | <div id="regex_test_mode" class="flex1 flex-container displayNone"> |
| 20 | <div class="flex1"> | 27 | <div class="flex1"> |
| 21 | <label class="title_restorable" for="regex_test_input"> | 28 | <label class="title_restorable" for="regex_test_input"> |
| @@ -7,7 +7,7 @@ import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from ' | |||
| 7 | import { enumIcons } from '../../slash-commands/SlashCommandCommonEnumsProvider.js'; | 7 | import { enumIcons } from '../../slash-commands/SlashCommandCommonEnumsProvider.js'; |
| 8 | import { SlashCommandEnumValue, enumTypes } from '../../slash-commands/SlashCommandEnumValue.js'; | 8 | import { SlashCommandEnumValue, enumTypes } from '../../slash-commands/SlashCommandEnumValue.js'; |
| 9 | import { SlashCommandParser } from '../../slash-commands/SlashCommandParser.js'; | 9 | import { SlashCommandParser } from '../../slash-commands/SlashCommandParser.js'; |
| 10 | import { download, getFileText, getSortableDelay, uuidv4 } from '../../utils.js'; | 10 | import { download, getFileText, getSortableDelay, regexFromString, setInfoBlock, uuidv4 } from '../../utils.js'; |
| 11 | import { regex_placement, runRegexScript, substitute_find_regex } from './engine.js'; | 11 | import { regex_placement, runRegexScript, substitute_find_regex } from './engine.js'; |
| 12 | import { t } from '../../i18n.js'; | 12 | import { t } from '../../i18n.js'; |
| 13 | import { accountStorage } from '../../util/AccountStorage.js'; | 13 | import { accountStorage } from '../../util/AccountStorage.js'; |
| @@ -258,6 +258,8 @@ async function onRegexEditorOpenClick(existingId, isScoped) { | |||
| 258 | }); | 258 | }); |
| 259 | 259 | ||
| 260 | function updateTestResult() { | 260 | function updateTestResult() { |
| 261 | updateInfoBlock(editorHtml); | ||
| 262 | |||
| 261 | if (!editorHtml.find('#regex_test_mode').is(':visible')) { | 263 | if (!editorHtml.find('#regex_test_mode').is(':visible')) { |
| 262 | return; | 264 | return; |
| 263 | } | 265 | } |
| @@ -276,6 +278,7 @@ async function onRegexEditorOpenClick(existingId, isScoped) { | |||
| 276 | } | 278 | } |
| 277 | 279 | ||
| 278 | editorHtml.find('input, textarea, select').on('input', updateTestResult); | 280 | editorHtml.find('input, textarea, select').on('input', updateTestResult); |
| 281 | updateInfoBlock(editorHtml); | ||
| 279 | 282 | ||
| 280 | const popupResult = await callPopup(editorHtml, 'confirm', undefined, { okButton: t`Save` }); | 283 | const popupResult = await callPopup(editorHtml, 'confirm', undefined, { okButton: t`Save` }); |
| 281 | if (popupResult) { | 284 | if (popupResult) { |
| @@ -305,6 +308,40 @@ async function onRegexEditorOpenClick(existingId, isScoped) { | |||
| 305 | } | 308 | } |
| 306 | } | 309 | } |
| 307 | 310 | ||
| 311 | /** | ||
| 312 | * Updates the info block in the regex editor with hints regarding the find regex. | ||
| 313 | * @param {JQuery<HTMLElement>} editorHtml The editor HTML | ||
| 314 | */ | ||
| 315 | function updateInfoBlock(editorHtml) { | ||
| 316 | const infoBlock = editorHtml.find('.info-block').get(0); | ||
| 317 | const infoBlockFlagsHint = editorHtml.find('#regex_info_block_flags_hint'); | ||
| 318 | const findRegex = String(editorHtml.find('.find_regex').val()); | ||
| 319 | |||
| 320 | infoBlockFlagsHint.hide(); | ||
| 321 | |||
| 322 | // Clear the info block if the find regex is empty | ||
| 323 | if (!findRegex) { | ||
| 324 | setInfoBlock(infoBlock, t`Find Regex is empty`, 'info'); | ||
| 325 | return; | ||
| 326 | } | ||
| 327 | |||
| 328 | try { | ||
| 329 | const regex = regexFromString(findRegex); | ||
| 330 | if (!regex) { | ||
| 331 | throw new Error(t`Invalid Find Regex`); | ||
| 332 | } | ||
| 333 | |||
| 334 | const flagInfo = []; | ||
| 335 | flagInfo.push(regex.flags.includes('g') ? t`Applies to all matches` : t`Applies to the first match`); | ||
| 336 | flagInfo.push(regex.flags.includes('i') ? t`Case insensitive` : t`Case sensitive`); | ||
| 337 | |||
| 338 | setInfoBlock(infoBlock, flagInfo.join('. '), 'hint'); | ||
| 339 | infoBlockFlagsHint.show(); | ||
| 340 | } catch (error) { | ||
| 341 | setInfoBlock(infoBlock, error.message, 'error'); | ||
| 342 | } | ||
| 343 | } | ||
| 344 | |||
| 308 | // Common settings migration function. Some parts will eventually be removed | 345 | // Common settings migration function. Some parts will eventually be removed |
| 309 | // TODO: Maybe migrate placement to strings? | 346 | // TODO: Maybe migrate placement to strings? |
| 310 | function migrateSettings() { | 347 | function migrateSettings() { |
| @@ -418,7 +455,7 @@ async function onRegexImportFileChange(file, isScoped) { | |||
| 418 | } | 455 | } |
| 419 | } | 456 | } |
| 420 | 457 | ||
| 421 | function purgeEmbeddedRegexScripts( { character }){ | 458 | function purgeEmbeddedRegexScripts({ character }) { |
| 422 | const avatar = character?.avatar; | 459 | const avatar = character?.avatar; |
| 423 | 460 | ||
| 424 | if (avatar && extension_settings.character_allowed_regex?.includes(avatar)) { | 461 | if (avatar && extension_settings.character_allowed_regex?.includes(avatar)) { |
| @@ -105,3 +105,24 @@ input.enable_scoped { | |||
| 105 | .disable_regex:not(:checked) ~ .regex-toggle-on { | 105 | .disable_regex:not(:checked) ~ .regex-toggle-on { |
| 106 | display: block; | 106 | display: block; |
| 107 | } | 107 | } |
| 108 | |||
| 109 | #regex_info_block_wrapper { | ||
| 110 | position: relative; | ||
| 111 | } | ||
| 112 | |||
| 113 | #regex_info_block { | ||
| 114 | margin: 10px 0; | ||
| 115 | padding: 5px 20px; | ||
| 116 | font-size: 0.9em; | ||
| 117 | } | ||
| 118 | |||
| 119 | #regex_info_block_wrapper:has(#regex_info_block:empty) { | ||
| 120 | display: none; | ||
| 121 | } | ||
| 122 | |||
| 123 | #regex_info_block_flags_hint { | ||
| 124 | position: absolute; | ||
| 125 | top: 50%; | ||
| 126 | right: 10px; | ||
| 127 | transform: translateY(-50%); | ||
| 128 | } | ||