Character settings overrides UI (#4707) * + Implemented support for overrides of system prompt and example messages from chat metadata. This is needed for BYAF import parity. No UI for modifying these yet, but they're easily changeable in the chat files already. These metadata settings are already supported by BYAF import as of the last commit so this just enables them. * + Updated scenario overrides popup to now be a character settings overrides popup. + Updated the template to have a new section for example messages and system prompts + Updated the logic to handle all three overrides + Updated logic to only save when the popup is closed, instead of on every character input. * Preserve import, allow scrolling * Add confirmation on override removal. Always save pending changes. * Update dialog layout, add editor expand buttons * Fix mes examples override in group chats with Join mode * Update group chat button title * Specify that Prefer Char. Prompt is a requirement * Fix group join mode not replacing macros in overrides --------- Co-authored-by: Cohee <18619528+Cohee1207@users.noreply.github.com>

47f744dfdfc075deee7d4c587f7bb6897d9223d4

docfail <4965997+docfail@users.noreply.github.com>

Signed
4 files changed, +108 -42Showing whitespace changes
public/index.html+4 -4
@@ -5698,7 +5698,7 @@
56985698 <div class="chat_lorebook_button menu_button fa-solid fa-passport" title="Chat Lore&#10;Alt+Click to open the lorebook" data-i18n="[title]Chat Lore Alt+Click to open the lorebook"></div>
56995699 <div id="char_connections_button" class="menu_button fa-solid fa-face-smile" title="Connected Personas" data-i18n="[title]Connected Personas"></div>
57005700 <div id="export_button" class="menu_button fa-solid fa-file-export " title="Export and Download" data-i18n="[title]Export and Download"></div>
57015701 <!-- <div id="set_chat_scenarioset_chat_character_settings" class="menu_button fa-solid fa-scroll" title="Set a chat scenario override"></div> -->
57025702 <!-- <div id="set_character_world" class="menu_button fa-solid fa-globe" title="Set a character World Info / Lorebook"></div> -->
57035703 <div id="dupe_button" class="menu_button fa-solid fa-clone " title="Duplicate Character" data-i18n="[title]Duplicate Character"></div>
57045704 <label for="create_button" id="create_button_label" class="menu_button fa-solid fa-user-check" title="Create Character" data-i18n="[title]Create Character">
@@ -5715,8 +5715,8 @@
57155715 <option id="import_character_info" data-i18n="Import Card Lore">
57165716 Import Card Lore
57175717 </option>
57185718 <option id="set_chat_scenarioset_chat_character_settings" data-i18n="ScenarioCharacter OverrideSettings Overrides">
5719- Scenario Override
5719+ Character Settings Overrides
57205720 </option>
57215721 <option id="convert_to_persona" data-i18n="Convert to Persona">
57225722 Convert to Persona
@@ -5907,7 +5907,7 @@
59075907 </div>
59085908 <div id="GroupFavDelOkBack" class="flex-container flexGap5 spaceEvenly flex1">
59095909 <div id="rm_button_back_from_group" class="heightFitContent margin0 menu_button fa-solid fa-fw fa-left-long"></div>
59105910 <div id="rm_group_scenario" class="heightFitContent margin0 menu_button fa-solid fa-fw fa-scroll" title="Set a group chat scenariocharacter settings overrides" data-i18n="[title]Set a group chat scenariocharacter settings overrides"></div>
59115911 <div id="group_favorite_button" class="heightFitContent margin0 menu_button fa-solid fa-fw fa-star" title="Add to Favorites" data-i18n="[title]Add to Favorites"></div>
59125912 <input id="rm_group_fav" type="hidden" />
59135913 <div id="group_open_media_overrides" class="heightFitContent margin0 menu_button menu_button_icon open_media_overrides" title="Click to allow/forbid the use of external media for this group." data-i18n="[title]Click to allow/forbid the use of external media for this group.">
public/script.js+57 -21
@@ -313,6 +313,7 @@ export {
313313 getSystemMessageByType,
314314 event_types,
315315 eventSource,
316+ setCharacterSettingsOverrides as setScenarioOverride,
316317};
317318
318319/**
@@ -2757,11 +2758,14 @@ export function getCharacterCardFields({ chid = null } = {}) {
27572758 }
27582759
27592760 const scenarioText = chat_metadata['scenario'] || character.scenario || '';
2761+ const exampleDialog = chat_metadata['mes_example'] || character.mes_example || '';
2762+ const systemPrompt = chat_metadata['system_prompt'] || character.data?.system_prompt || '';
2763+
27602764 result.description = baseChatReplace(character.description?.trim(), name1, name2);
27612765 result.personality = baseChatReplace(character.personality?.trim(), name1, name2);
27622766 result.scenario = baseChatReplace(scenarioText.trim(), name1, name2);
27632767 result.mesExamples = baseChatReplace(character.mes_example?exampleDialog.trim(), name1, name2);
27642768 result.system = power_user.prefer_character_prompt ? baseChatReplace(character.data?.system_prompt?systemPrompt.trim(), name1, name2) : '';
27652769 result.jailbreak = power_user.prefer_character_jailbreak ? baseChatReplace(character.data?.post_history_instructions?.trim(), name1, name2) : '';
27662770 result.version = character.data?.character_version ?? '';
27672771 result.charDepthPrompt = baseChatReplace(character.data?.extensions?.depth_prompt?.prompt?.trim(), name1, name2);
@@ -7819,7 +7823,7 @@ export function select_selected_character(chid, { switchMenu = true } = {}) {
78197823 $('#char_connections_button').show();
78207824
78217825 // Hide the chat scenario button if we're peeking the group member defs
78227826 $('#set_chat_scenarioset_chat_character_settings').toggle(!selected_group);
78237827
78247828 // Don't update the navbar name if we're peeking the group member defs
78257829 if (!selected_group) {
@@ -7901,7 +7905,7 @@ function select_rm_create({ switchMenu = true } = {}) {
79017905
79027906 switchMenu && selectRightMenuWithAnimation('rm_ch_create_block');
79037907
79047908 $('#set_chat_scenarioset_chat_character_settings').hide();
79057909 $('#delete_button_div').css('display', 'none');
79067910 $('#delete_button').css('display', 'none');
79077911 $('#export_button').css('display', 'none');
@@ -8036,33 +8040,65 @@ function updateFavButtonState(state) {
80368040 $('#favorite_button').toggleClass('fav_off', !state);
80378041}
80388042
80398043export async function setScenarioOverridesetCharacterSettingsOverrides() {
80408044 if (!selected_group && (this_chid === undefined || !characters[this_chid])) {
80418045 console.warn('setScenarioOverridesetCharacterSettingsOverrides() -- no selected group or character');
80428046 return;
80438047 }
80448048
80458049 const metadataValuescenarioOverrideValue = chat_metadata['scenario'] || '';
8050+ const exampleMessagesValue = chat_metadata['mes_example'] || '';
8051+ const systemPromptValue = chat_metadata['system_prompt'] || '';
80468052 const isGroup = !!selected_group;
80478053
80488054 const $template = $(await renderTemplateAsync('scenarioOverride'));
80498055 $template.find('[data-group="true"]').toggle(isGroup);
80508056 $template.find('[data-character="true"]').toggle(!isGroup);
8051- // TODO: Why does this save on every character input? Save on popup close
8057+ const pendingChanges = {
8052- $template.find('.chat_scenario').val(metadataValue).on('input', onScenarioOverrideInput);
8058+ scenario: scenarioOverrideValue,
8053- $template.find('.remove_scenario_override').on('click', onScenarioOverrideRemoveClick);
8059+ examples: exampleMessagesValue,
8060+ system_prompt: systemPromptValue,
8061+ };
80548062
8055- await callGenericPopup($template, POPUP_TYPE.TEXT, '');
8063+ // Keep edits local until the popup is closed/confirmed
8056-}
8064+ const $scenario = $template.find('.chat_scenario');
8065+ $scenario.val(scenarioOverrideValue).on('input', function () {
8066+ pendingChanges.scenario = String($(this).val());
8067+ });
8068+ const $examples = $template.find('.chat_examples');
8069+ $examples.val(exampleMessagesValue).on('input', function () {
8070+ pendingChanges.examples = String($(this).val());
8071+ });
8072+ const $systemPrompt = $template.find('.chat_system_prompt');
8073+ $systemPrompt.val(systemPromptValue).on('input', function () {
8074+ pendingChanges.system_prompt = String($(this).val());
8075+ });
80578076
8058-function onScenarioOverrideInput() {
8077+ $template.find('.remove_scenario_override').on('click', async function () {
8059- const value = String($(this).val());
8078+ const confirm = await Popup.show.confirm(t`Are you sure you want to remove all overrides?`, t`This action cannot be undone.`);
8060- chat_metadata['scenario'] = value;
8079+ if (!confirm) {
8061- saveMetadataDebounced();
8080+ return;
80628081 }
80638082
8064-function onScenarioOverrideRemoveClick() {
8083+ $scenario.val('');
8065- $(this).closest('.scenario_override').find('.chat_scenario').val('').trigger('input');
8084+ pendingChanges.scenario = '';
8085+ $examples.val('');
8086+ pendingChanges.examples = '';
8087+ $systemPrompt.val('');
8088+ pendingChanges.system_prompt = '';
8089+ });
8090+
8091+ // Wait for popup close/confirm.
8092+ await callGenericPopup($template, POPUP_TYPE.TEXT, '', {
8093+ wide: true,
8094+ large: true,
8095+ allowVerticalScrolling: true,
8096+ });
8097+
8098+ chat_metadata['scenario'] = pendingChanges.scenario;
8099+ chat_metadata['mes_example'] = pendingChanges.examples;
8100+ chat_metadata['system_prompt'] = pendingChanges.system_prompt;
8101+ saveMetadataDebounced();
80668102}
80678103
80688104/**
@@ -10304,7 +10340,7 @@ jQuery(async function () {
1030410340 if (!isMouseOverButtonOrMenu()) { hideMenu(); }
1030510341 });
1030610342
1030710343 /* $('#set_chat_scenarioset_chat_character_settings').on('click', setScenarioOverride); */
1030810344
1030910345 ///////////// OPTIMIZED LISTENERS FOR LEFT SIDE OPTIONS POPUP MENU //////////////////////
1031010346 $('#options [id]').on('click', async function (event, customData) {
@@ -11085,8 +11121,8 @@ jQuery(async function () {
1108511121 case 'set_character_world':
1108611122 await openCharacterWorldPopup();
1108711123 break;
1108811124 case 'set_chat_scenarioset_chat_character_settings':
1108911125 await setScenarioOverridesetCharacterSettingsOverrides();
1109011126 break;
1109111127 case 'renameCharButton':
1109211128 await renameCharacter();
public/scripts/group-chats.js+7 -5
@@ -38,6 +38,7 @@ import {
3838 setEditedMessageId,
3939 is_send_press,
4040 name1,
41+ name2,
4142 resetChatState,
4243 setSendButtonState,
4344 getCharacters,
@@ -65,7 +66,7 @@ import {
6566 eventSource,
6667 event_types,
6768 getCurrentChatId,
6869 setScenarioOverridesetCharacterSettingsOverrides,
6970 system_avatar,
7071 isChatSaving,
7172 setExternalAbortController,
@@ -490,7 +491,8 @@ export function getGroupCharacterCards(groupId, characterId) {
490491 return `${prefix}${value}${suffix}`;
491492 }
492493
493494 const scenarioOverride = String(chat_metadata['scenario'] || '');
495+ const mesExamplesOverride = String(chat_metadata['mes_example'] || '');
494496
495497 let descriptions = [];
496498 let personalities = [];
@@ -518,8 +520,8 @@ export function getGroupCharacterCards(groupId, characterId) {
518520
519521 const description = descriptions.filter(x => x.length).join('\n');
520522 const personality = personalities.filter(x => x.length).join('\n');
521523 const scenario = baseChatReplace(scenarioOverride?.trim(), name1, name2) || scenarios.filter(x => x.length).join('\n');
522524 const mesExamples = baseChatReplace(mesExamplesOverride?.trim(), name1, name2) || mesExamplesArray.filter(x => x.length).join('\n');
523525
524526 return { description, personality, scenario, mesExamples };
525527}
@@ -2193,7 +2195,7 @@ jQuery(() => {
21932195 });
21942196 $('#rm_group_filter').on('input', filterGroupMembers);
21952197 $('#rm_group_submit').on('click', createGroup);
21962198 $('#rm_group_scenario').on('click', setScenarioOverridesetCharacterSettingsOverrides);
21972199 $('#rm_group_automode').on('input', function () {
21982200 const value = $(this).prop('checked');
21992201 is_group_automode_enabled = value;
public/scripts/templates/scenarioOverride.html+40 -12
@@ -1,17 +1,45 @@
11<div class="scenario_override range-block flexFlowColumn flex-container justifyLeft">
22 <div class="range-block-title title_restorable alignItemsBaseline">
33 <h3><span data-i18n="Chat ScenarioCharacter Settings Override" class="margin0">Chat ScenarioCharacter Settings Override</span></h3>
4- <div title="Remove" data-i18n="[title]Remove"
4+ <div class="menu_button menu_button_icon remove_scenario_override">
55 <i class="menu_button fa-solid fa-trash-can remove_scenario_override"></divi>
6+ <span data-i18n="Remove">Remove</span>
67 </div>
7- <div class="range-block-counter justifyLeft flex-container flexFlowColumn">
8+ </div>
9+ <div>
810 <strong data-i18n="Unique to this chat.">Unique to this chat.</strong>
911 <span data-group="true" data-i18n="All group members will use the following scenario textvalues instead of what is specified in their character cards.">All group members will use the following scenario textvalues instead of what is specified in their character cards.</span>
1012 <span data-character="true" data-i18n="The following scenario textvalues will be used instead of the value set in the character card.">The following scenario textvalues will be used instead of the value set in the character card.</span>
1113 <span data-i18n="Checkpoints inherit the scenario overrideoverrides from their parent, and can be changed individually after that.">Checkpoints inherit the scenario overrideoverrides from their parent, and can be changed individually after that.</span>
14+ </div>
15+ <hr>
16+ <div class="wide100p flex-container flexFlowColumn">
17+ <h4 class="flex-container alignItemsBaseline">
18+ <span data-i18n="Scenario">Scenario</span>
19+ <i class="editor_maximize fa-solid fa-maximize right_menu_button" data-for="chat_scenario_override" title="Expand the editor" data-i18n="[title]Expand the editor"></i>
20+ </h4>
21+ <div>
22+ <textarea id="chat_scenario_override" class="chat_scenario text_pole" rows="6" data-i18n="[placeholder]Type Scenario here..." placeholder="Type Scenario here..."></textarea>
23+ </div>
24+ <h4 class="flex-container alignItemsBaseline">
25+ <span data-i18n="Examples of dialogue">Examples of dialogue</span>
26+ <i class="editor_maximize fa-solid fa-maximize right_menu_button" data-for="chat_examples_override" title="Expand the editor" data-i18n="[title]Expand the editor"></i>
27+ </h4>
28+ <div>
29+ <textarea id="chat_examples_override" class="chat_examples text_pole" rows="6" data-i18n="[placeholder]Type Example Messages here..." placeholder="Type Example Messages here..."></textarea>
30+ </div>
31+ <h4 class="flex-container alignItemsBaseline">
32+ <span data-i18n="Main Prompt">Main Prompt</span>
33+ <small>
34+ <small>&mdash;</small>
35+ <i class="fa-solid fa-user-cog"></i>
36+ <b data-i18n="Prefer Char. Prompt">Prefer Char. Prompt</b>
37+ <span data-i18n="MUST be enabled!">MUST be enabled!</span>
38+ </small>
39+ <i class="editor_maximize fa-solid fa-maximize right_menu_button" data-for="chat_system_prompt_override" title="Expand the editor" data-i18n="[title]Expand the editor"></i>
40+ </h4>
41+ <div>
42+ <textarea id="chat_system_prompt_override" class="chat_system_prompt text_pole" rows="6" data-i18n="[placeholder]Type System Prompt here..." placeholder="Type System Prompt here..."></textarea>
1243 </div>
13- <div class="range-block-range wide100p">
14- <textarea class="wide100p chat_scenario" class="text_pole" rows="15" data-i18n="[placeholder]Type here..."
15- placeholder="Type here..."></textarea>
1644 </div>
1745</div>