Implement bulk operations for regex
| @@ -21,13 +21,28 @@ | |||
| 21 | <small data-i18n="ext_regex_import_script">Import</small> | 21 | <small data-i18n="ext_regex_import_script">Import</small> |
| 22 | </div> | 22 | </div> |
| 23 | <input type="file" id="import_regex_file" hidden accept="*.json" multiple /> | 23 | <input type="file" id="import_regex_file" hidden accept="*.json" multiple /> |
| 24 | <div id="export_global_regex" class="menu_button menu_button_icon"> | 24 | <label for="regex_bulk_edit" class="menu_button menu_button_icon"> |
| 25 | <i class="fa-solid fa-file-export"></i> | 25 | <input id="regex_bulk_edit" type="checkbox" class="displayNone" /> |
| 26 | <small data-i18n="ext_regex_export_global">Export Global</small> | 26 | <i class="fa-solid fa-edit"></i> |
| 27 | <small data-i18n="ext_regex_bulk_edit">Bulk Edit</small> | ||
| 28 | </label> | ||
| 29 | </div> | ||
| 30 | <div class="regex_bulk_operations flex-container justifyCenter"> | ||
| 31 | <div id="bulk_enable_regex" class="menu_button menu_button_icon"> | ||
| 32 | <i class="fa-solid fa-toggle-on"></i> | ||
| 33 | <small data-i18n="Enable">Enable</small> | ||
| 27 | </div> | 34 | </div> |
| 28 | <div id="export_scoped_regex" class="menu_button menu_button_icon"> | 35 | <div id="bulk_disable_regex" class="menu_button menu_button_icon"> |
| 36 | <i class="fa-solid fa-toggle-off"></i> | ||
| 37 | <small data-i18n="Disable">Disable</small> | ||
| 38 | </div> | ||
| 39 | <div id="bulk_export_regex" class="menu_button menu_button_icon"> | ||
| 29 | <i class="fa-solid fa-file-export"></i> | 40 | <i class="fa-solid fa-file-export"></i> |
| 30 | <small data-i18n="ext_regex_export_scoped">Export Scoped</small> | 41 | <small data-i18n="Export">Export</small> |
| 42 | </div> | ||
| 43 | <div id="bulk_delete_regex" class="menu_button menu_button_icon"> | ||
| 44 | <i class="fa-solid fa-trash"></i> | ||
| 45 | <small data-i18n="Delete">Delete</small> | ||
| 31 | </div> | 46 | </div> |
| 32 | </div> | 47 | </div> |
| 33 | <hr /> | 48 | <hr /> |
| @@ -607,22 +607,67 @@ jQuery(async () => { | |||
| 607 | $('#import_regex_file').trigger('click'); | 607 | $('#import_regex_file').trigger('click'); |
| 608 | }); | 608 | }); |
| 609 | 609 | ||
| 610 | $('#export_global_regex').on('click', async function () { | 610 | function getSelectedScripts() { |
| 611 | const regexScripts = extension_settings?.regex ?? []; | 611 | const scripts = getRegexScripts(); |
| 612 | if (regexScripts.length) { | 612 | const selector = '#regex_container .regex-script-label:has(.regex_bulk_checkbox:checked)'; |
| 613 | const fileName = 'regex-global.json'; | 613 | const selectedIds = Array.from(document.querySelectorAll(selector)).map(e => e.getAttribute('id')).filter(id => id); |
| 614 | const fileData = JSON.stringify(regexScripts, null, 4); | 614 | return scripts.filter(script => selectedIds.includes(script.id)); |
| 615 | download(fileData, fileName, 'application/json'); | 615 | } |
| 616 | |||
| 617 | $('#bulk_enable_regex').on('click', async function () { | ||
| 618 | const scripts = getSelectedScripts().filter(script => script.disabled); | ||
| 619 | if (scripts.length === 0) { | ||
| 620 | toastr.warning(t`No regex scripts selected for enabling.`); | ||
| 621 | return; | ||
| 616 | } | 622 | } |
| 623 | for (const script of scripts) { | ||
| 624 | script.disabled = false; | ||
| 625 | } | ||
| 626 | saveSettingsDebounced(); | ||
| 627 | await loadRegexScripts(); | ||
| 617 | }); | 628 | }); |
| 618 | 629 | ||
| 619 | $('#export_scoped_regex').on('click', async function () { | 630 | $('#bulk_disable_regex').on('click', async function () { |
| 620 | const regexScripts = characters[this_chid]?.data?.extensions?.regex_scripts ?? []; | 631 | const scripts = getSelectedScripts().filter(script => !script.disabled); |
| 621 | if (regexScripts.length) { | 632 | if (scripts.length === 0) { |
| 622 | const fileName = `regex-${sanitizeFileName(characters[this_chid].name)}.json`; | 633 | toastr.warning(t`No regex scripts selected for disabling.`); |
| 623 | const fileData = JSON.stringify(regexScripts, null, 4); | 634 | return; |
| 624 | download(fileData, fileName, 'application/json'); | 635 | } |
| 636 | for (const script of scripts) { | ||
| 637 | script.disabled = true; | ||
| 638 | } | ||
| 639 | saveSettingsDebounced(); | ||
| 640 | await loadRegexScripts(); | ||
| 641 | }); | ||
| 642 | |||
| 643 | $('#bulk_delete_regex').on('click', async function () { | ||
| 644 | const scripts = getSelectedScripts(); | ||
| 645 | if (scripts.length === 0) { | ||
| 646 | toastr.warning(t`No regex scripts selected for deletion.`); | ||
| 647 | return; | ||
| 648 | } | ||
| 649 | const confirm = await callGenericPopup('Are you sure you want to delete the selected regex scripts?', POPUP_TYPE.CONFIRM); | ||
| 650 | if (!confirm) { | ||
| 651 | return; | ||
| 652 | } | ||
| 653 | for (const script of scripts) { | ||
| 654 | const isScoped = characters[this_chid]?.data?.extensions?.regex_scripts?.some(s => s.id === script.id); | ||
| 655 | await deleteRegexScript({ id: script.id, isScoped: isScoped }); | ||
| 625 | } | 656 | } |
| 657 | await reloadCurrentChat(); | ||
| 658 | saveSettingsDebounced(); | ||
| 659 | }); | ||
| 660 | |||
| 661 | $('#bulk_export_regex').on('click', async function () { | ||
| 662 | const scripts = getSelectedScripts(); | ||
| 663 | if (scripts.length === 0) { | ||
| 664 | toastr.warning(t`No regex scripts selected for export.`); | ||
| 665 | return; | ||
| 666 | } | ||
| 667 | const fileName = `regex-${new Date().toISOString()}.json`; | ||
| 668 | const fileData = JSON.stringify(scripts, null, 4); | ||
| 669 | download(fileData, fileName, 'application/json'); | ||
| 670 | await loadRegexScripts(); | ||
| 626 | }); | 671 | }); |
| 627 | 672 | ||
| 628 | let sortableDatas = [ | 673 | let sortableDatas = [ |
| @@ -1,4 +1,5 @@ | |||
| 1 | <div class="regex-script-label flex-container flexnowrap"> | 1 | <div class="regex-script-label flex-container flexnowrap"> |
| 2 | <input type="checkbox" class="regex_bulk_checkbox" /> | ||
| 2 | <span class="drag-handle menu-handle">☰</span> | 3 | <span class="drag-handle menu-handle">☰</span> |
| 3 | <div class="regex_script_name flexGrow overflow-hidden"></div> | 4 | <div class="regex_script_name flexGrow overflow-hidden"></div> |
| 4 | <div class="flex-container flexnowrap"> | 5 | <div class="flex-container flexnowrap"> |
| @@ -56,7 +56,7 @@ | |||
| 56 | } | 56 | } |
| 57 | 57 | ||
| 58 | .regex-script-label { | 58 | .regex-script-label { |
| 59 | align-items: center; | 59 | align-items: baseline; |
| 60 | border: 1px solid var(--SmartThemeBorderColor); | 60 | border: 1px solid var(--SmartThemeBorderColor); |
| 61 | border-radius: 10px; | 61 | border-radius: 10px; |
| 62 | padding: 0 5px; | 62 | padding: 0 5px; |
| @@ -126,3 +126,25 @@ input.enable_scoped { | |||
| 126 | right: 10px; | 126 | right: 10px; |
| 127 | transform: translateY(-50%); | 127 | transform: translateY(-50%); |
| 128 | } | 128 | } |
| 129 | |||
| 130 | .regex_settings label[for="regex_bulk_edit"]:has(#regex_bulk_edit:checked) { | ||
| 131 | color: var(--golden); | ||
| 132 | } | ||
| 133 | |||
| 134 | .regex_settings .regex-script-container .regex-script-label .regex_bulk_checkbox { | ||
| 135 | margin-left: 5px; | ||
| 136 | margin-right: 5px; | ||
| 137 | } | ||
| 138 | |||
| 139 | .regex_settings .regex_bulk_operations, | ||
| 140 | .regex_settings .regex_bulk_checkbox { | ||
| 141 | display: none; | ||
| 142 | } | ||
| 143 | |||
| 144 | .regex_settings:has(#regex_bulk_edit:checked) .regex_bulk_operations { | ||
| 145 | display: flex; | ||
| 146 | } | ||
| 147 | |||
| 148 | .regex_settings:has(#regex_bulk_edit:checked) .regex_bulk_checkbox { | ||
| 149 | display: inline-grid; | ||
| 150 | } | ||