Merge pull request #3290 from SillyTavern/qr-set-rename QR Set buttons for rename and duplicate

f4624364503caeb1a9cd94881c43eb8edf1bddd6

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

Signed
2 files changed, +83 -5Showing whitespace changes
public/scripts/extensions/quick-reply/html/settings.html+2 -0
@@ -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>
public/scripts/extensions/quick-reply/src/ui/SettingsUi.js+81 -5
@@ -1,4 +1,4 @@
1import { callPopup } from '../../../../../script.js';1import { Popup } from '../../../../popup.js';
2import { getSortableDelay } from '../../../../utils.js';2import { getSortableDelay } from '../../../../utils.js';
3import { log, warn } from '../../index.js';3import { log, warn } from '../../index.js';
4import { QuickReply } from '../QuickReply.js';4import { QuickReply } from '../QuickReply.js';
@@ -111,6 +111,7 @@ export class SettingsUi {
111111
112 prepareQrEditor() {112 prepareQrEditor() {
113 // qr editor113 // 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');
@@ -120,6 +121,7 @@ export class SettingsUi {
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 }
280282
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 }
305307
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 }
423465
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();