Merge pull request #3290 from SillyTavern/qr-set-rename QR Set buttons for rename and duplicate
Signed| @@ -46,10 +46,12 @@ | |||
| 46 | <div class="qr--title" data-i18n="Edit Quick Replies">Edit Quick Replies</div> | 46 | <div class="qr--title" data-i18n="Edit Quick Replies">Edit Quick Replies</div> |
| 47 | <div class="qr--actions"> | 47 | <div class="qr--actions"> |
| 48 | <select id="qr--set" class="text_pole"></select> | 48 | <select id="qr--set" class="text_pole"></select> |
| 49 | <div class="qr--add menu_button menu_button_icon fa-solid fa-pencil" id="qr--set-rename" title="Rename quick reply set"></div> | ||
| 49 | <div class="qr--add menu_button menu_button_icon fa-solid fa-plus" id="qr--set-new" title="Create new quick reply set"></div> | 50 | <div class="qr--add menu_button menu_button_icon fa-solid fa-plus" id="qr--set-new" title="Create new quick reply set"></div> |
| 50 | <div class="qr--add menu_button menu_button_icon fa-solid fa-file-import" id="qr--set-import" title="Import quick reply set"></div> | 51 | <div class="qr--add menu_button menu_button_icon fa-solid fa-file-import" id="qr--set-import" title="Import quick reply set"></div> |
| 51 | <input type="file" id="qr--set-importFile" accept=".json" hidden> | 52 | <input type="file" id="qr--set-importFile" accept=".json" hidden> |
| 52 | <div class="qr--add menu_button menu_button_icon fa-solid fa-file-export" id="qr--set-export" title="Export quick reply set"></div> | 53 | <div class="qr--add menu_button menu_button_icon fa-solid fa-file-export" id="qr--set-export" title="Export quick reply set"></div> |
| 54 | <div class="qr-add menu_button menu_button_icon fa-solid fa-paste" id="qr--set-duplicate" title="Duplicate quick reply set"></div> | ||
| 53 | <div class="qr--del menu_button menu_button_icon fa-solid fa-trash redWarningBG" id="qr--set-delete" title="Delete quick reply set"></div> | 55 | <div class="qr--del menu_button menu_button_icon fa-solid fa-trash redWarningBG" id="qr--set-delete" title="Delete quick reply set"></div> |
| 54 | </div> | 56 | </div> |
| 55 | </div> | 57 | </div> |
| @@ -1,4 +1,4 @@ | |||
| 1 | import { callPopup } from '../../../../../script.js'; | 1 | import { Popup } from '../../../../popup.js'; |
| 2 | import { getSortableDelay } from '../../../../utils.js'; | 2 | import { getSortableDelay } from '../../../../utils.js'; |
| 3 | import { log, warn } from '../../index.js'; | 3 | import { log, warn } from '../../index.js'; |
| 4 | import { QuickReply } from '../QuickReply.js'; | 4 | import { QuickReply } from '../QuickReply.js'; |
| @@ -111,6 +111,7 @@ export class SettingsUi { | |||
| 111 | 111 | ||
| 112 | prepareQrEditor() { | 112 | prepareQrEditor() { |
| 113 | // qr editor | 113 | // qr editor |
| 114 | this.dom.querySelector('#qr--set-rename').addEventListener('click', async () => this.renameQrSet()); | ||
| 114 | this.dom.querySelector('#qr--set-new').addEventListener('click', async()=>this.addQrSet()); | 115 | this.dom.querySelector('#qr--set-new').addEventListener('click', async()=>this.addQrSet()); |
| 115 | /**@type {HTMLInputElement}*/ | 116 | /**@type {HTMLInputElement}*/ |
| 116 | const importFile = this.dom.querySelector('#qr--set-importFile'); | 117 | const importFile = this.dom.querySelector('#qr--set-importFile'); |
| @@ -119,7 +120,8 @@ export class SettingsUi { | |||
| 119 | importFile.value = null; | 120 | importFile.value = null; |
| 120 | }); | 121 | }); |
| 121 | this.dom.querySelector('#qr--set-import').addEventListener('click', ()=>importFile.click()); | 122 | this.dom.querySelector('#qr--set-import').addEventListener('click', ()=>importFile.click()); |
| 122 | this.dom.querySelector('#qr--set-export').addEventListener('click', async()=>this.exportQrSet()); | 123 | this.dom.querySelector('#qr--set-export').addEventListener('click', async () => this.exportQrSet()); |
| 124 | this.dom.querySelector('#qr--set-duplicate').addEventListener('click', async () => this.duplicateQrSet()); | ||
| 123 | this.dom.querySelector('#qr--set-delete').addEventListener('click', async()=>this.deleteQrSet()); | 125 | this.dom.querySelector('#qr--set-delete').addEventListener('click', async()=>this.deleteQrSet()); |
| 124 | this.dom.querySelector('#qr--set-add').addEventListener('click', async()=>{ | 126 | this.dom.querySelector('#qr--set-add').addEventListener('click', async()=>{ |
| 125 | this.currentQrSet.addQuickReply(); | 127 | this.currentQrSet.addQuickReply(); |
| @@ -279,7 +281,7 @@ export class SettingsUi { | |||
| 279 | } | 281 | } |
| 280 | 282 | ||
| 281 | async deleteQrSet() { | 283 | async deleteQrSet() { |
| 282 | const confirmed = await callPopup(`Are you sure you want to delete the Quick Reply Set "${this.currentQrSet.name}"?<br>This cannot be undone.`, 'confirm'); | 284 | const confirmed = await Popup.show.confirm('Delete Quick Reply Set', `Are you sure you want to delete the Quick Reply Set "${this.currentQrSet.name}"?<br>This cannot be undone.`); |
| 283 | if (confirmed) { | 285 | if (confirmed) { |
| 284 | await this.doDeleteQrSet(this.currentQrSet); | 286 | await this.doDeleteQrSet(this.currentQrSet); |
| 285 | this.rerender(); | 287 | this.rerender(); |
| @@ -303,12 +305,52 @@ export class SettingsUi { | |||
| 303 | this.settings.save(); | 305 | this.settings.save(); |
| 304 | } | 306 | } |
| 305 | 307 | ||
| 308 | async renameQrSet() { | ||
| 309 | const newName = await Popup.show.input('Rename Quick Reply Set', 'Enter a new name:', this.currentQrSet.name); | ||
| 310 | if (newName && newName.length > 0) { | ||
| 311 | const existingSet = QuickReplySet.get(newName); | ||
| 312 | if (existingSet) { | ||
| 313 | toastr.error(`A Quick Reply Set named "${newName}" already exists.`); | ||
| 314 | return; | ||
| 315 | } | ||
| 316 | const oldName = this.currentQrSet.name; | ||
| 317 | this.currentQrSet.name = newName; | ||
| 318 | await this.currentQrSet.save(); | ||
| 319 | |||
| 320 | // Update it in both set lists | ||
| 321 | this.settings.config.setList.forEach(set => { | ||
| 322 | if (set.set.name === oldName) { | ||
| 323 | set.set.name = newName; | ||
| 324 | } | ||
| 325 | }); | ||
| 326 | this.settings.chatConfig?.setList.forEach(set => { | ||
| 327 | if (set.set.name === oldName) { | ||
| 328 | set.set.name = newName; | ||
| 329 | } | ||
| 330 | }); | ||
| 331 | this.settings.save(); | ||
| 332 | |||
| 333 | // Update the option in the current selected QR dropdown. All others will be refreshed via the prepare calls below. | ||
| 334 | /** @type {HTMLOptionElement} */ | ||
| 335 | const option = this.currentSet.querySelector(`#qr--set option[value="${oldName}"]`); | ||
| 336 | option.value = newName; | ||
| 337 | option.textContent = newName; | ||
| 338 | |||
| 339 | this.currentSet.value = newName; | ||
| 340 | this.onQrSetChange(); | ||
| 341 | this.prepareGlobalSetList(); | ||
| 342 | this.prepareChatSetList(); | ||
| 343 | |||
| 344 | console.info(`Quick Reply Set renamed from ""${oldName}" to "${newName}".`); | ||
| 345 | } | ||
| 346 | } | ||
| 347 | |||
| 306 | async addQrSet() { | 348 | async addQrSet() { |
| 307 | const name = await callPopup('Quick Reply Set Name:', 'input'); | 349 | const name = await Popup.show.input('Create a new World Info', 'Enter a name for the new Quick Reply Set:'); |
| 308 | if (name && name.length > 0) { | 350 | if (name && name.length > 0) { |
| 309 | const oldQrs = QuickReplySet.get(name); | 351 | const oldQrs = QuickReplySet.get(name); |
| 310 | if (oldQrs) { | 352 | if (oldQrs) { |
| 311 | const replace = await callPopup(`A Quick Reply Set named "${name}" already exists.<br>Do you want to overwrite the existing Quick Reply Set?<br>The existing set will be deleted. This cannot be undone.`, 'confirm'); | 353 | const replace = Popup.show.confirm('Replace existing World Info', `A Quick Reply Set named "${name}" already exists.<br>Do you want to overwrite the existing Quick Reply Set?<br>The existing set will be deleted. This cannot be undone.`); |
| 312 | if (replace) { | 354 | if (replace) { |
| 313 | const idx = QuickReplySet.list.indexOf(oldQrs); | 355 | const idx = QuickReplySet.list.indexOf(oldQrs); |
| 314 | await this.doDeleteQrSet(oldQrs); | 356 | await this.doDeleteQrSet(oldQrs); |
| @@ -369,7 +411,7 @@ export class SettingsUi { | |||
| 369 | qrs.init(); | 411 | qrs.init(); |
| 370 | const oldQrs = QuickReplySet.get(props.name); | 412 | const oldQrs = QuickReplySet.get(props.name); |
| 371 | if (oldQrs) { | 413 | if (oldQrs) { |
| 372 | const replace = await callPopup(`A Quick Reply Set named "${qrs.name}" already exists.<br>Do you want to overwrite the existing Quick Reply Set?<br>The existing set will be deleted. This cannot be undone.`, 'confirm'); | 414 | const replace = Popup.show.confirm('Replace existing World Info', `A Quick Reply Set named "${name}" already exists.<br>Do you want to overwrite the existing Quick Reply Set?<br>The existing set will be deleted. This cannot be undone.`); |
| 373 | if (replace) { | 415 | if (replace) { |
| 374 | const idx = QuickReplySet.list.indexOf(oldQrs); | 416 | const idx = QuickReplySet.list.indexOf(oldQrs); |
| 375 | await this.doDeleteQrSet(oldQrs); | 417 | await this.doDeleteQrSet(oldQrs); |
| @@ -421,6 +463,40 @@ export class SettingsUi { | |||
| 421 | URL.revokeObjectURL(url); | 463 | URL.revokeObjectURL(url); |
| 422 | } | 464 | } |
| 423 | 465 | ||
| 466 | async duplicateQrSet() { | ||
| 467 | const newName = await Popup.show.input('Duplicate Quick Reply Set', 'Enter a name for the new Quick Reply Set:', `${this.currentQrSet.name} (Copy)`); | ||
| 468 | if (newName && newName.length > 0) { | ||
| 469 | const existingSet = QuickReplySet.get(newName); | ||
| 470 | if (existingSet) { | ||
| 471 | toastr.error(`A Quick Reply Set named "${newName}" already exists.`); | ||
| 472 | return; | ||
| 473 | } | ||
| 474 | const newQrSet = QuickReplySet.from(JSON.parse(JSON.stringify(this.currentQrSet))); | ||
| 475 | newQrSet.name = newName; | ||
| 476 | newQrSet.qrList = this.currentQrSet.qrList.map(qr => QuickReply.from(JSON.parse(JSON.stringify(qr)))); | ||
| 477 | newQrSet.addQuickReply(); | ||
| 478 | const idx = QuickReplySet.list.findIndex(it => it.name.toLowerCase().localeCompare(newName.toLowerCase()) == 1); | ||
| 479 | if (idx > -1) { | ||
| 480 | QuickReplySet.list.splice(idx, 0, newQrSet); | ||
| 481 | } else { | ||
| 482 | QuickReplySet.list.push(newQrSet); | ||
| 483 | } | ||
| 484 | const opt = document.createElement('option'); { | ||
| 485 | opt.value = newQrSet.name; | ||
| 486 | opt.textContent = newQrSet.name; | ||
| 487 | if (idx > -1) { | ||
| 488 | this.currentSet.children[idx].insertAdjacentElement('beforebegin', opt); | ||
| 489 | } else { | ||
| 490 | this.currentSet.append(opt); | ||
| 491 | } | ||
| 492 | } | ||
| 493 | this.currentSet.value = newName; | ||
| 494 | this.onQrSetChange(); | ||
| 495 | this.prepareGlobalSetList(); | ||
| 496 | this.prepareChatSetList(); | ||
| 497 | } | ||
| 498 | } | ||
| 499 | |||
| 424 | selectQrSet(qrs) { | 500 | selectQrSet(qrs) { |
| 425 | this.currentSet.value = qrs.name; | 501 | this.currentSet.value = qrs.name; |
| 426 | this.onQrSetChange(); | 502 | this.onQrSetChange(); |