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, +109 -43Ignore whitespace
public/index.html+4 -4
@@ -5698,7 +5698,7 @@
5698 <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>5698 <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>
5699 <div id="char_connections_button" class="menu_button fa-solid fa-face-smile" title="Connected Personas" data-i18n="[title]Connected Personas"></div>5699 <div id="char_connections_button" class="menu_button fa-solid fa-face-smile" title="Connected Personas" data-i18n="[title]Connected Personas"></div>
5700 <div id="export_button" class="menu_button fa-solid fa-file-export " title="Export and Download" data-i18n="[title]Export and Download"></div>5700 <div id="export_button" class="menu_button fa-solid fa-file-export " title="Export and Download" data-i18n="[title]Export and Download"></div>
5701 <!-- <div id="set_chat_scenario" class="menu_button fa-solid fa-scroll" title="Set a chat scenario override"></div> -->5701 <!-- <div id="set_chat_character_settings" class="menu_button fa-solid fa-scroll" title="Set a chat scenario override"></div> -->
5702 <!-- <div id="set_character_world" class="menu_button fa-solid fa-globe" title="Set a character World Info / Lorebook"></div> -->5702 <!-- <div id="set_character_world" class="menu_button fa-solid fa-globe" title="Set a character World Info / Lorebook"></div> -->
5703 <div id="dupe_button" class="menu_button fa-solid fa-clone " title="Duplicate Character" data-i18n="[title]Duplicate Character"></div>5703 <div id="dupe_button" class="menu_button fa-solid fa-clone " title="Duplicate Character" data-i18n="[title]Duplicate Character"></div>
5704 <label for="create_button" id="create_button_label" class="menu_button fa-solid fa-user-check" title="Create Character" data-i18n="[title]Create Character">5704 <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 @@
5715 <option id="import_character_info" data-i18n="Import Card Lore">5715 <option id="import_character_info" data-i18n="Import Card Lore">
5716 Import Card Lore5716 Import Card Lore
5717 </option>5717 </option>
5718 <option id="set_chat_scenario" data-i18n="Scenario Override">5718 <option id="set_chat_character_settings" data-i18n="Character Settings Overrides">
5719 Scenario Override5719 Character Settings Overrides
5720 </option>5720 </option>
5721 <option id="convert_to_persona" data-i18n="Convert to Persona">5721 <option id="convert_to_persona" data-i18n="Convert to Persona">
5722 Convert to Persona5722 Convert to Persona
@@ -5907,7 +5907,7 @@
5907 </div>5907 </div>
5908 <div id="GroupFavDelOkBack" class="flex-container flexGap5 spaceEvenly flex1">5908 <div id="GroupFavDelOkBack" class="flex-container flexGap5 spaceEvenly flex1">
5909 <div id="rm_button_back_from_group" class="heightFitContent margin0 menu_button fa-solid fa-fw fa-left-long"></div>5909 <div id="rm_button_back_from_group" class="heightFitContent margin0 menu_button fa-solid fa-fw fa-left-long"></div>
5910 <div id="rm_group_scenario" class="heightFitContent margin0 menu_button fa-solid fa-fw fa-scroll" title="Set a group chat scenario" data-i18n="[title]Set a group chat scenario"></div>5910 <div id="rm_group_scenario" class="heightFitContent margin0 menu_button fa-solid fa-fw fa-scroll" title="Set group chat character settings overrides" data-i18n="[title]Set group chat character settings overrides"></div>
5911 <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>5911 <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>
5912 <input id="rm_group_fav" type="hidden" />5912 <input id="rm_group_fav" type="hidden" />
5913 <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.">5913 <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+58 -22
@@ -313,6 +313,7 @@ export {
313 getSystemMessageByType,313 getSystemMessageByType,
314 event_types,314 event_types,
315 eventSource,315 eventSource,
316 setCharacterSettingsOverrides as setScenarioOverride,
316};317};
317318
318/**319/**
@@ -2757,11 +2758,14 @@ export function getCharacterCardFields({ chid = null } = {}) {
2757 }2758 }
27582759
2759 const scenarioText = chat_metadata['scenario'] || character.scenario || '';2760 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
2760 result.description = baseChatReplace(character.description?.trim(), name1, name2);2764 result.description = baseChatReplace(character.description?.trim(), name1, name2);
2761 result.personality = baseChatReplace(character.personality?.trim(), name1, name2);2765 result.personality = baseChatReplace(character.personality?.trim(), name1, name2);
2762 result.scenario = baseChatReplace(scenarioText.trim(), name1, name2);2766 result.scenario = baseChatReplace(scenarioText.trim(), name1, name2);
2763 result.mesExamples = baseChatReplace(character.mes_example?.trim(), name1, name2);2767 result.mesExamples = baseChatReplace(exampleDialog.trim(), name1, name2);
2764 result.system = power_user.prefer_character_prompt ? baseChatReplace(character.data?.system_prompt?.trim(), name1, name2) : '';2768 result.system = power_user.prefer_character_prompt ? baseChatReplace(systemPrompt.trim(), name1, name2) : '';
2765 result.jailbreak = power_user.prefer_character_jailbreak ? baseChatReplace(character.data?.post_history_instructions?.trim(), name1, name2) : '';2769 result.jailbreak = power_user.prefer_character_jailbreak ? baseChatReplace(character.data?.post_history_instructions?.trim(), name1, name2) : '';
2766 result.version = character.data?.character_version ?? '';2770 result.version = character.data?.character_version ?? '';
2767 result.charDepthPrompt = baseChatReplace(character.data?.extensions?.depth_prompt?.prompt?.trim(), name1, name2);2771 result.charDepthPrompt = baseChatReplace(character.data?.extensions?.depth_prompt?.prompt?.trim(), name1, name2);
@@ -7819,7 +7823,7 @@ export function select_selected_character(chid, { switchMenu = true } = {}) {
7819 $('#char_connections_button').show();7823 $('#char_connections_button').show();
78207824
7821 // Hide the chat scenario button if we're peeking the group member defs7825 // Hide the chat scenario button if we're peeking the group member defs
7822 $('#set_chat_scenario').toggle(!selected_group);7826 $('#set_chat_character_settings').toggle(!selected_group);
78237827
7824 // Don't update the navbar name if we're peeking the group member defs7828 // Don't update the navbar name if we're peeking the group member defs
7825 if (!selected_group) {7829 if (!selected_group) {
@@ -7901,7 +7905,7 @@ function select_rm_create({ switchMenu = true } = {}) {
79017905
7902 switchMenu && selectRightMenuWithAnimation('rm_ch_create_block');7906 switchMenu && selectRightMenuWithAnimation('rm_ch_create_block');
79037907
7904 $('#set_chat_scenario').hide();7908 $('#set_chat_character_settings').hide();
7905 $('#delete_button_div').css('display', 'none');7909 $('#delete_button_div').css('display', 'none');
7906 $('#delete_button').css('display', 'none');7910 $('#delete_button').css('display', 'none');
7907 $('#export_button').css('display', 'none');7911 $('#export_button').css('display', 'none');
@@ -8036,33 +8040,65 @@ function updateFavButtonState(state) {
8036 $('#favorite_button').toggleClass('fav_off', !state);8040 $('#favorite_button').toggleClass('fav_off', !state);
8037}8041}
80388042
8039export async function setScenarioOverride() {8043export async function setCharacterSettingsOverrides() {
8040 if (!selected_group && (this_chid === undefined || !characters[this_chid])) {8044 if (!selected_group && (this_chid === undefined || !characters[this_chid])) {
8041 console.warn('setScenarioOverride() -- no selected group or character');8045 console.warn('setCharacterSettingsOverrides() -- no selected group or character');
8042 return;8046 return;
8043 }8047 }
80448048
8045 const metadataValue = chat_metadata['scenario'] || '';8049 const scenarioOverrideValue = chat_metadata['scenario'] || '';
8050 const exampleMessagesValue = chat_metadata['mes_example'] || '';
8051 const systemPromptValue = chat_metadata['system_prompt'] || '';
8046 const isGroup = !!selected_group;8052 const isGroup = !!selected_group;
80478053
8048 const $template = $(await renderTemplateAsync('scenarioOverride'));8054 const $template = $(await renderTemplateAsync('scenarioOverride'));
8049 $template.find('[data-group="true"]').toggle(isGroup);8055 $template.find('[data-group="true"]').toggle(isGroup);
8050 $template.find('[data-character="true"]').toggle(!isGroup);8056 $template.find('[data-character="true"]').toggle(!isGroup);
8051 // TODO: Why does this save on every character input? Save on popup close8057 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
8058function 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;
8062}8081 }
8082
8083 $scenario.val('');
8084 pendingChanges.scenario = '';
8085 $examples.val('');
8086 pendingChanges.examples = '';
8087 $systemPrompt.val('');
8088 pendingChanges.system_prompt = '';
8089 });
80638090
8064function onScenarioOverrideRemoveClick() {8091 // Wait for popup close/confirm.
8065 $(this).closest('.scenario_override').find('.chat_scenario').val('').trigger('input');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();
8066}8102}
80678103
8068/**8104/**
@@ -10304,7 +10340,7 @@ jQuery(async function () {
10304 if (!isMouseOverButtonOrMenu()) { hideMenu(); }10340 if (!isMouseOverButtonOrMenu()) { hideMenu(); }
10305 });10341 });
1030610342
10307 /* $('#set_chat_scenario').on('click', setScenarioOverride); */10343 /* $('#set_chat_character_settings').on('click', setScenarioOverride); */
1030810344
10309 ///////////// OPTIMIZED LISTENERS FOR LEFT SIDE OPTIONS POPUP MENU //////////////////////10345 ///////////// OPTIMIZED LISTENERS FOR LEFT SIDE OPTIONS POPUP MENU //////////////////////
10310 $('#options [id]').on('click', async function (event, customData) {10346 $('#options [id]').on('click', async function (event, customData) {
@@ -11085,8 +11121,8 @@ jQuery(async function () {
11085 case 'set_character_world':11121 case 'set_character_world':
11086 await openCharacterWorldPopup();11122 await openCharacterWorldPopup();
11087 break;11123 break;
11088 case 'set_chat_scenario':11124 case 'set_chat_character_settings':
11089 await setScenarioOverride();11125 await setCharacterSettingsOverrides();
11090 break;11126 break;
11091 case 'renameCharButton':11127 case 'renameCharButton':
11092 await renameCharacter();11128 await renameCharacter();
public/scripts/group-chats.js+7 -5
@@ -38,6 +38,7 @@ import {
38 setEditedMessageId,38 setEditedMessageId,
39 is_send_press,39 is_send_press,
40 name1,40 name1,
41 name2,
41 resetChatState,42 resetChatState,
42 setSendButtonState,43 setSendButtonState,
43 getCharacters,44 getCharacters,
@@ -65,7 +66,7 @@ import {
65 eventSource,66 eventSource,
66 event_types,67 event_types,
67 getCurrentChatId,68 getCurrentChatId,
68 setScenarioOverride,69 setCharacterSettingsOverrides,
69 system_avatar,70 system_avatar,
70 isChatSaving,71 isChatSaving,
71 setExternalAbortController,72 setExternalAbortController,
@@ -490,7 +491,8 @@ export function getGroupCharacterCards(groupId, characterId) {
490 return `${prefix}${value}${suffix}`;491 return `${prefix}${value}${suffix}`;
491 }492 }
492493
493 const scenarioOverride = chat_metadata['scenario'];494 const scenarioOverride = String(chat_metadata['scenario'] || '');
495 const mesExamplesOverride = String(chat_metadata['mes_example'] || '');
494496
495 let descriptions = [];497 let descriptions = [];
496 let personalities = [];498 let personalities = [];
@@ -518,8 +520,8 @@ export function getGroupCharacterCards(groupId, characterId) {
518520
519 const description = descriptions.filter(x => x.length).join('\n');521 const description = descriptions.filter(x => x.length).join('\n');
520 const personality = personalities.filter(x => x.length).join('\n');522 const personality = personalities.filter(x => x.length).join('\n');
521 const scenario = scenarioOverride?.trim() || scenarios.filter(x => x.length).join('\n');523 const scenario = baseChatReplace(scenarioOverride?.trim(), name1, name2) || scenarios.filter(x => x.length).join('\n');
522 const mesExamples = mesExamplesArray.filter(x => x.length).join('\n');524 const mesExamples = baseChatReplace(mesExamplesOverride?.trim(), name1, name2) || mesExamplesArray.filter(x => x.length).join('\n');
523525
524 return { description, personality, scenario, mesExamples };526 return { description, personality, scenario, mesExamples };
525}527}
@@ -2193,7 +2195,7 @@ jQuery(() => {
2193 });2195 });
2194 $('#rm_group_filter').on('input', filterGroupMembers);2196 $('#rm_group_filter').on('input', filterGroupMembers);
2195 $('#rm_group_submit').on('click', createGroup);2197 $('#rm_group_submit').on('click', createGroup);
2196 $('#rm_group_scenario').on('click', setScenarioOverride);2198 $('#rm_group_scenario').on('click', setCharacterSettingsOverrides);
2197 $('#rm_group_automode').on('input', function () {2199 $('#rm_group_automode').on('input', function () {
2198 const value = $(this).prop('checked');2200 const value = $(this).prop('checked');
2199 is_group_automode_enabled = value;2201 is_group_automode_enabled = value;
public/scripts/templates/scenarioOverride.html+40 -12
@@ -1,17 +1,45 @@
1<div class="scenario_override range-block flexFlowColumn flex-container">1<div class="scenario_override flexFlowColumn flex-container justifyLeft">
2 <div class="range-block-title title_restorable">2 <div class="title_restorable alignItemsBaseline">
3 <h3><span data-i18n="Chat Scenario Override" class="margin0">Chat Scenario Override</span></h3>3 <h3><span data-i18n="Chat Character Settings Override" class="margin0">Chat Character Settings Override</span></h3>
4 <div title="Remove" data-i18n="[title]Remove"4 <div class="menu_button menu_button_icon remove_scenario_override">
5 class="menu_button fa-solid fa-trash-can remove_scenario_override"></div>5 <i class="fa-solid fa-trash-can "></i>
6 <span data-i18n="Remove">Remove</span>
7 </div>
6 </div>8 </div>
7 <div class="range-block-counter justifyLeft flex-container flexFlowColumn">9 <div>
8 <strong data-i18n="Unique to this chat.">Unique to this chat.</strong>10 <strong data-i18n="Unique to this chat.">Unique to this chat.</strong>
9 <span data-group="true" data-i18n="All group members will use the following scenario text instead of what is specified in their character cards.">All group members will use the following scenario text instead of what is specified in their character cards.</span>11 <span data-group="true" data-i18n="All group members will use the following values instead of what is specified in their character cards.">All group members will use the following values instead of what is specified in their character cards.</span>
10 <span data-character="true" data-i18n="The following scenario text will be used instead of the value set in the character card.">The following scenario text will be used instead of the value set in the character card.</span>12 <span data-character="true" data-i18n="The following values will be used instead of the value set in the character card.">The following values will be used instead of the value set in the character card.</span>
11 <span data-i18n="Checkpoints inherit the scenario override from their parent, and can be changed individually after that.">Checkpoints inherit the scenario override from their parent, and can be changed individually after that.</span>13 <span data-i18n="Checkpoints inherit the overrides from their parent, and can be changed individually after that.">Checkpoints inherit the overrides from their parent, and can be changed individually after that.</span>
12 </div>14 </div>
13 <div class="range-block-range wide100p">15 <hr>
14 <textarea class="wide100p chat_scenario" class="text_pole" rows="15" data-i18n="[placeholder]Type here..."16 <div class="wide100p flex-container flexFlowColumn">
15 placeholder="Type here..."></textarea>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>
43 </div>
16 </div>44 </div>
17</div>45</div>