Add ability to omit settings from Connection Profiles
| @@ -0,0 +1,16 @@ | ||
| 1 | +<div> | |
| 2 | + <h3>Included settings:</h3> | |
| 3 | + <div class="justifyLeft flex-container flexFlowColumn flexNoGap"> | |
| 4 | + {{#each settings}} | |
| 5 | + <label class="checkbox_label"> | |
| 6 | + {{#if this}} | |
| 7 | + <input type="checkbox" value="{{@key}}" name="exclude" checked> | |
| 8 | + {{else}} | |
| 9 | + <input type="checkbox" value="{{@key}}" name="exclude"> | |
| 10 | + {{/if}} | |
| 11 | + <span>{{@key}}</span> | |
| 12 | + </label> | |
| 13 | + {{/each}} | |
| 14 | + </div> | |
| 15 | + <h3>Profile name:</h3> | |
| 16 | +</div> | |
| @@ -135,6 +135,7 @@ const profilesProvider = () => [ | ||
| 135 | 135 | * @property {string} [context] Context Template |
| 136 | 136 | * @property {string} [instruct-state] Instruct Mode |
| 137 | 137 | * @property {string} [tokenizer] Tokenizer |
| 138 | + * @property {string[]} [exclude] Commands to exclude | |
| 138 | 139 | */ |
| 139 | 140 | |
| 140 | 141 | /** |
| @@ -171,8 +172,13 @@ function findProfileByName(value) { | ||
| 171 | 172 | async function readProfileFromCommands(mode, profile, cleanUp = false) { |
| 172 | 173 | const commands = mode === 'cc' ? CC_COMMANDS : TC_COMMANDS; |
| 173 | 174 | const opposingCommands = mode === 'cc' ? TC_COMMANDS : CC_COMMANDS; |
| 175 | + const excludeList = Array.isArray(profile.exclude) ? profile.exclude : []; | |
| 174 | 176 | for (const command of commands) { |
| 175 | 177 | try { |
| 178 | + if (excludeList.includes(command)) { | |
| 179 | + continue; | |
| 180 | + } | |
| 181 | + | |
| 176 | 182 | const args = getNamedArguments(); |
| 177 | 183 | const result = await SlashCommandParser.commands[command].callback(args, ''); |
| 178 | 184 | if (result) { |
| @@ -208,15 +214,36 @@ async function readProfileFromCommands(mode, profile, cleanUp = false) { | ||
| 208 | 214 | async function createConnectionProfile(forceName = null) { |
| 209 | 215 | const mode = main_api === 'openai' ? 'cc' : 'tc'; |
| 210 | 216 | const id = uuidv4(); |
| 217 | + /** @type {ConnectionProfile} */ | |
| 211 | 218 | const profile = { |
| 212 | 219 | id, |
| 213 | 220 | mode, |
| 221 | + exclude: [], | |
| 214 | 222 | }; |
| 215 | 223 | |
| 216 | 224 | await readProfileFromCommands(mode, profile); |
| 217 | 225 | |
| 218 | 226 | const profileForDisplay = makeFancyProfile(profile); |
| 219 | 227 | const template = $(await renderExtensionTemplateAsync(MODULE_NAME, 'profile', { profile: profileForDisplay })); |
| 228 | + template.find('input[name="exclude"]').on('input', function () { | |
| 229 | + const fancyName = String($(this).val()); | |
| 230 | + const keyName = Object.entries(FANCY_NAMES).find(x => x[1] === fancyName)?.[0]; | |
| 231 | + if (!keyName) { | |
| 232 | + console.warn('Key not found for fancy name:', fancyName); | |
| 233 | + return; | |
| 234 | + } | |
| 235 | + | |
| 236 | + if (!Array.isArray(profile.exclude)) { | |
| 237 | + profile.exclude = []; | |
| 238 | + } | |
| 239 | + | |
| 240 | + const excludeState = !$(this).prop('checked'); | |
| 241 | + if (excludeState) { | |
| 242 | + profile.exclude.push(keyName); | |
| 243 | + } else { | |
| 244 | + profile.exclude.splice(profile.exclude.indexOf(keyName), 1); | |
| 245 | + } | |
| 246 | + }); | |
| 220 | 247 | const isNameTaken = (n) => extension_settings.connectionManager.profiles.some(p => p.name === n); |
| 221 | 248 | const suggestedName = getUniqueName(collapseSpaces(`${profile.api ?? ''} ${profile.model ?? ''} - ${profile.preset ?? ''}`), isNameTaken); |
| 222 | 249 | const name = forceName ?? await callGenericPopup(template, POPUP_TYPE.INPUT, suggestedName, { rows: 2 }); |
| @@ -230,7 +257,13 @@ async function createConnectionProfile(forceName = null) { | ||
| 230 | 257 | return null; |
| 231 | 258 | } |
| 232 | 259 | |
| 233 | - profile.name = name; | |
| 260 | + if (Array.isArray(profile.exclude)) { | |
| 261 | + for (const command of profile.exclude) { | |
| 262 | + delete profile[command]; | |
| 263 | + } | |
| 264 | + } | |
| 265 | + | |
| 266 | + profile.name = String(name); | |
| 234 | 267 | return profile; |
| 235 | 268 | } |
| 236 | 269 | |
| @@ -357,6 +390,9 @@ async function renderDetailsContent(detailsContent) { | ||
| 357 | 390 | const profile = extension_settings.connectionManager.profiles.find(p => p.id === selectedProfile); |
| 358 | 391 | if (profile) { |
| 359 | 392 | const profileForDisplay = makeFancyProfile(profile); |
| 393 | + if (Array.isArray(profile.exclude) && profile.exclude.length > 0) { | |
| 394 | + profileForDisplay['Omitted Settings'] = profile.exclude.map(e => FANCY_NAMES[e]).join(', '); | |
| 395 | + } | |
| 360 | 396 | const template = await renderExtensionTemplateAsync(MODULE_NAME, 'view', { profile: profileForDisplay }); |
| 361 | 397 | detailsContent.innerHTML = template; |
| 362 | 398 | } else { |
| @@ -472,8 +508,8 @@ async function renderDetailsContent(detailsContent) { | ||
| 472 | 508 | await eventSource.emit(event_types.CONNECTION_PROFILE_LOADED, NONE); |
| 473 | 509 | }); |
| 474 | 510 | |
| 475 | 511 | const renameButtoneditButton = document.getElementById('rename_connection_profileedit_connection_profile'); |
| 476 | 512 | renameButtoneditButton.addEventListener('click', async () => { |
| 477 | 513 | const selectedProfile = extension_settings.connectionManager.selectedProfile; |
| 478 | 514 | const profile = extension_settings.connectionManager.profiles.find(p => p.id === selectedProfile); |
| 479 | 515 | if (!profile) { |
| @@ -481,20 +517,48 @@ async function renderDetailsContent(detailsContent) { | ||
| 481 | 517 | return; |
| 482 | 518 | } |
| 483 | 519 | |
| 484 | - const newName = await Popup.show.input('Enter a new name', null, profile.name, { rows: 2 }); | |
| 520 | + if (!Array.isArray(profile.exclude)) { | |
| 521 | + profile.exclude = []; | |
| 522 | + } | |
| 523 | + | |
| 524 | + const commands = profile.mode === 'cc' ? CC_COMMANDS : TC_COMMANDS; | |
| 525 | + const settings = commands.reduce((acc, command) => { | |
| 526 | + const fancyName = FANCY_NAMES[command]; | |
| 527 | + acc[fancyName] = !profile.exclude.includes(command); | |
| 528 | + return acc; | |
| 529 | + }, {}); | |
| 530 | + const template = $(await renderExtensionTemplateAsync(MODULE_NAME, 'edit', { name: profile.name, settings })); | |
| 531 | + const newName = await callGenericPopup(template, POPUP_TYPE.INPUT, profile.name); | |
| 532 | + | |
| 485 | 533 | if (!newName) { |
| 486 | 534 | return; |
| 487 | 535 | } |
| 488 | 536 | |
| 489 | 537 | if (profile.name !== newName && extension_settings.connectionManager.profiles.some(p => p.name === newName)) { |
| 490 | 538 | toastr.error('A profile with the same name already exists.'); |
| 491 | 539 | return; |
| 492 | 540 | } |
| 493 | 541 | |
| 494 | - profile.name = newName; | |
| 542 | + const newExcludeList = template.find('input[name="exclude"]:not(:checked)').map(function () { | |
| 543 | + return Object.entries(FANCY_NAMES).find(x => x[1] === String($(this).val()))?.[0]; | |
| 544 | + }).get(); | |
| 545 | + | |
| 546 | + if (newExcludeList.length !== profile.exclude.length || !newExcludeList.every(e => profile.exclude.includes(e))) { | |
| 547 | + profile.exclude = newExcludeList; | |
| 548 | + for (const command of newExcludeList) { | |
| 549 | + delete profile[command]; | |
| 550 | + } | |
| 551 | + toastr.info('Press "Update" to record them into the profile.', 'Included settings list updated'); | |
| 552 | + } | |
| 553 | + | |
| 554 | + if (profile.name !== newName) { | |
| 555 | + toastr.success('Connection profile renamed.'); | |
| 556 | + profile.name = String(newName); | |
| 557 | + } | |
| 558 | + | |
| 495 | 559 | saveSettingsDebounced(); |
| 496 | 560 | renderConnectionProfiles(profiles); |
| 497 | - toastr.success('Connection profile renamed', '', { timeOut: 1500 }); | |
| 561 | + await renderDetailsContent(detailsContent); | |
| 498 | 562 | }); |
| 499 | 563 | |
| 500 | 564 | /** @type {HTMLElement} */ |
| @@ -2,11 +2,18 @@ | ||
| 2 | 2 | <h2 data-i18n="Creating a Connection Profile"> |
| 3 | 3 | Creating a Connection Profile |
| 4 | 4 | </h2> |
| 5 | 5 | <uldiv class="justifyLeft flex-container flexFlowColumn flexNoGap"> |
| 6 | 6 | {{#each profile}} |
| 7 | - <li><strong data-i18n="{{@key}}">{{@key}}:</strong> {{this}}</li> | |
| 7 | + <label class="checkbox_label"> | |
| 8 | + <input type="checkbox" value="{{@key}}" name="exclude" checked> | |
| 9 | + <span><strong data-i18n="{{@key}}">{{@key}}:</strong> {{this}}</span> | |
| 10 | + </label> | |
| 8 | 11 | {{/each}} |
| 9 | 12 | </uldiv> |
| 13 | + <small> | |
| 14 | + <b>Hint:</b> | |
| 15 | + <i>Click on the setting name to omit it from the profile.</i> | |
| 16 | + </small> | |
| 10 | 17 | <h3 data-i18n="Enter a name:"> |
| 11 | 18 | Enter a name: |
| 12 | 19 | </h3> |
| @@ -13,7 +13,7 @@ | ||
| 13 | 13 | <i id="view_connection_profile" class="menu_button fa-solid fa-info-circle" title="View connection profile details" data-i18n="[title]View connection profile details"></i> |
| 14 | 14 | <i id="create_connection_profile" class="menu_button fa-solid fa-file-circle-plus" title="Create a new connection profile" data-i18n="[title]Create a new connection profile"></i> |
| 15 | 15 | <i id="update_connection_profile" class="menu_button fa-solid fa-save" title="Update a connection profile" data-i18n="[title]Update a connection profile"></i> |
| 16 | 16 | <i id="rename_connection_profileedit_connection_profile" class="menu_button fa-solid fa-pencil" title="RenameEdit a connection profile" data-i18n="[title]RenameEdit a connection profile"></i> |
| 17 | 17 | <i id="reload_connection_profile" class="menu_button fa-solid fa-recycle" title="Reload a connection profile" data-i18n="[title]Reload a connection profile"></i> |
| 18 | 18 | <i id="delete_connection_profile" class="menu_button fa-solid fa-trash-can" title="Delete a connection profile" data-i18n="[title]Delete a connection profile"></i> |
| 19 | 19 | </div> |