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>
Signed| @@ -5698,7 +5698,7 @@ | ||
| 5698 | 5698 | <div class="chat_lorebook_button menu_button fa-solid fa-passport" title="Chat Lore Alt+Click to open the lorebook" data-i18n="[title]Chat Lore Alt+Click to open the lorebook"></div> |
| 5699 | 5699 | <div id="char_connections_button" class="menu_button fa-solid fa-face-smile" title="Connected Personas" data-i18n="[title]Connected Personas"></div> |
| 5700 | 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 | 5701 | <!-- <div id="set_chat_scenarioset_chat_character_settings" class="menu_button fa-solid fa-scroll" title="Set a chat scenario override"></div> --> |
| 5702 | 5702 | <!-- <div id="set_character_world" class="menu_button fa-solid fa-globe" title="Set a character World Info / Lorebook"></div> --> |
| 5703 | 5703 | <div id="dupe_button" class="menu_button fa-solid fa-clone " title="Duplicate Character" data-i18n="[title]Duplicate Character"></div> |
| 5704 | 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 | 5715 | <option id="import_character_info" data-i18n="Import Card Lore"> |
| 5716 | 5716 | Import Card Lore |
| 5717 | 5717 | </option> |
| 5718 | 5718 | <option id="set_chat_scenarioset_chat_character_settings" data-i18n="ScenarioCharacter OverrideSettings Overrides"> |
| 5719 | - Scenario Override | |
| 5719 | + Character Settings Overrides | |
| 5720 | 5720 | </option> |
| 5721 | 5721 | <option id="convert_to_persona" data-i18n="Convert to Persona"> |
| 5722 | 5722 | Convert to Persona |
| @@ -5907,7 +5907,7 @@ | ||
| 5907 | 5907 | </div> |
| 5908 | 5908 | <div id="GroupFavDelOkBack" class="flex-container flexGap5 spaceEvenly flex1"> |
| 5909 | 5909 | <div id="rm_button_back_from_group" class="heightFitContent margin0 menu_button fa-solid fa-fw fa-left-long"></div> |
| 5910 | 5910 | <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> |
| 5911 | 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 | 5912 | <input id="rm_group_fav" type="hidden" /> |
| 5913 | 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."> |
| @@ -313,6 +313,7 @@ export { | ||
| 313 | 313 | getSystemMessageByType, |
| 314 | 314 | event_types, |
| 315 | 315 | eventSource, |
| 316 | + setCharacterSettingsOverrides as setScenarioOverride, | |
| 316 | 317 | }; |
| 317 | 318 | |
| 318 | 319 | /** |
| @@ -2757,11 +2758,14 @@ export function getCharacterCardFields({ chid = null } = {}) { | ||
| 2757 | 2758 | } |
| 2758 | 2759 | |
| 2759 | 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 | 2764 | result.description = baseChatReplace(character.description?.trim(), name1, name2); |
| 2761 | 2765 | result.personality = baseChatReplace(character.personality?.trim(), name1, name2); |
| 2762 | 2766 | result.scenario = baseChatReplace(scenarioText.trim(), name1, name2); |
| 2763 | 2767 | result.mesExamples = baseChatReplace(character.mes_example?exampleDialog.trim(), name1, name2); |
| 2764 | 2768 | result.system = power_user.prefer_character_prompt ? baseChatReplace(character.data?.system_prompt?systemPrompt.trim(), name1, name2) : ''; |
| 2765 | 2769 | result.jailbreak = power_user.prefer_character_jailbreak ? baseChatReplace(character.data?.post_history_instructions?.trim(), name1, name2) : ''; |
| 2766 | 2770 | result.version = character.data?.character_version ?? ''; |
| 2767 | 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 | 7823 | $('#char_connections_button').show(); |
| 7820 | 7824 | |
| 7821 | 7825 | // Hide the chat scenario button if we're peeking the group member defs |
| 7822 | 7826 | $('#set_chat_scenarioset_chat_character_settings').toggle(!selected_group); |
| 7823 | 7827 | |
| 7824 | 7828 | // Don't update the navbar name if we're peeking the group member defs |
| 7825 | 7829 | if (!selected_group) { |
| @@ -7901,7 +7905,7 @@ function select_rm_create({ switchMenu = true } = {}) { | ||
| 7901 | 7905 | |
| 7902 | 7906 | switchMenu && selectRightMenuWithAnimation('rm_ch_create_block'); |
| 7903 | 7907 | |
| 7904 | 7908 | $('#set_chat_scenarioset_chat_character_settings').hide(); |
| 7905 | 7909 | $('#delete_button_div').css('display', 'none'); |
| 7906 | 7910 | $('#delete_button').css('display', 'none'); |
| 7907 | 7911 | $('#export_button').css('display', 'none'); |
| @@ -8036,33 +8040,65 @@ function updateFavButtonState(state) { | ||
| 8036 | 8040 | $('#favorite_button').toggleClass('fav_off', !state); |
| 8037 | 8041 | } |
| 8038 | 8042 | |
| 8039 | 8043 | export async function setScenarioOverridesetCharacterSettingsOverrides() { |
| 8040 | 8044 | if (!selected_group && (this_chid === undefined || !characters[this_chid])) { |
| 8041 | 8045 | console.warn('setScenarioOverridesetCharacterSettingsOverrides() -- no selected group or character'); |
| 8042 | 8046 | return; |
| 8043 | 8047 | } |
| 8044 | 8048 | |
| 8045 | 8049 | const metadataValuescenarioOverrideValue = chat_metadata['scenario'] || ''; |
| 8050 | + const exampleMessagesValue = chat_metadata['mes_example'] || ''; | |
| 8051 | + const systemPromptValue = chat_metadata['system_prompt'] || ''; | |
| 8046 | 8052 | const isGroup = !!selected_group; |
| 8047 | 8053 | |
| 8048 | 8054 | const $template = $(await renderTemplateAsync('scenarioOverride')); |
| 8049 | 8055 | $template.find('[data-group="true"]').toggle(isGroup); |
| 8050 | 8056 | $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 | + }; | |
| 8054 | 8062 | |
| 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 | + }); | |
| 8057 | 8076 | |
| 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; | |
| 8062 | 8081 | } |
| 8082 | + | |
| 8083 | + $scenario.val(''); | |
| 8084 | + pendingChanges.scenario = ''; | |
| 8085 | + $examples.val(''); | |
| 8086 | + pendingChanges.examples = ''; | |
| 8087 | + $systemPrompt.val(''); | |
| 8088 | + pendingChanges.system_prompt = ''; | |
| 8089 | + }); | |
| 8063 | 8090 | |
| 8064 | -function 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 | } |
| 8067 | 8103 | |
| 8068 | 8104 | /** |
| @@ -10304,7 +10340,7 @@ jQuery(async function () { | ||
| 10304 | 10340 | if (!isMouseOverButtonOrMenu()) { hideMenu(); } |
| 10305 | 10341 | }); |
| 10306 | 10342 | |
| 10307 | 10343 | /* $('#set_chat_scenarioset_chat_character_settings').on('click', setScenarioOverride); */ |
| 10308 | 10344 | |
| 10309 | 10345 | ///////////// OPTIMIZED LISTENERS FOR LEFT SIDE OPTIONS POPUP MENU ////////////////////// |
| 10310 | 10346 | $('#options [id]').on('click', async function (event, customData) { |
| @@ -11085,8 +11121,8 @@ jQuery(async function () { | ||
| 11085 | 11121 | case 'set_character_world': |
| 11086 | 11122 | await openCharacterWorldPopup(); |
| 11087 | 11123 | break; |
| 11088 | 11124 | case 'set_chat_scenarioset_chat_character_settings': |
| 11089 | 11125 | await setScenarioOverridesetCharacterSettingsOverrides(); |
| 11090 | 11126 | break; |
| 11091 | 11127 | case 'renameCharButton': |
| 11092 | 11128 | await renameCharacter(); |
| @@ -38,6 +38,7 @@ import { | ||
| 38 | 38 | setEditedMessageId, |
| 39 | 39 | is_send_press, |
| 40 | 40 | name1, |
| 41 | + name2, | |
| 41 | 42 | resetChatState, |
| 42 | 43 | setSendButtonState, |
| 43 | 44 | getCharacters, |
| @@ -65,7 +66,7 @@ import { | ||
| 65 | 66 | eventSource, |
| 66 | 67 | event_types, |
| 67 | 68 | getCurrentChatId, |
| 68 | 69 | setScenarioOverridesetCharacterSettingsOverrides, |
| 69 | 70 | system_avatar, |
| 70 | 71 | isChatSaving, |
| 71 | 72 | setExternalAbortController, |
| @@ -490,7 +491,8 @@ export function getGroupCharacterCards(groupId, characterId) { | ||
| 490 | 491 | return `${prefix}${value}${suffix}`; |
| 491 | 492 | } |
| 492 | 493 | |
| 493 | 494 | const scenarioOverride = String(chat_metadata['scenario'] || ''); |
| 495 | + const mesExamplesOverride = String(chat_metadata['mes_example'] || ''); | |
| 494 | 496 | |
| 495 | 497 | let descriptions = []; |
| 496 | 498 | let personalities = []; |
| @@ -518,8 +520,8 @@ export function getGroupCharacterCards(groupId, characterId) { | ||
| 518 | 520 | |
| 519 | 521 | const description = descriptions.filter(x => x.length).join('\n'); |
| 520 | 522 | const personality = personalities.filter(x => x.length).join('\n'); |
| 521 | 523 | const scenario = baseChatReplace(scenarioOverride?.trim(), name1, name2) || scenarios.filter(x => x.length).join('\n'); |
| 522 | 524 | const mesExamples = baseChatReplace(mesExamplesOverride?.trim(), name1, name2) || mesExamplesArray.filter(x => x.length).join('\n'); |
| 523 | 525 | |
| 524 | 526 | return { description, personality, scenario, mesExamples }; |
| 525 | 527 | } |
| @@ -2193,7 +2195,7 @@ jQuery(() => { | ||
| 2193 | 2195 | }); |
| 2194 | 2196 | $('#rm_group_filter').on('input', filterGroupMembers); |
| 2195 | 2197 | $('#rm_group_submit').on('click', createGroup); |
| 2196 | 2198 | $('#rm_group_scenario').on('click', setScenarioOverridesetCharacterSettingsOverrides); |
| 2197 | 2199 | $('#rm_group_automode').on('input', function () { |
| 2198 | 2200 | const value = $(this).prop('checked'); |
| 2199 | 2201 | is_group_automode_enabled = value; |
| @@ -1,17 +1,45 @@ | ||
| 1 | 1 | <div class="scenario_override range-block flexFlowColumn flex-container justifyLeft"> |
| 2 | 2 | <div class="range-block-title title_restorable alignItemsBaseline"> |
| 3 | 3 | <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"> | |
| 5 | 5 | <i class="menu_button fa-solid fa-trash-can remove_scenario_override"></divi> |
| 6 | + <span data-i18n="Remove">Remove</span> | |
| 7 | + </div> | |
| 6 | 8 | </div> |
| 7 | - <div class="range-block-counter justifyLeft flex-container flexFlowColumn"> | |
| 9 | + <div> | |
| 8 | 10 | <strong data-i18n="Unique to this chat.">Unique to this chat.</strong> |
| 9 | 11 | <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> |
| 10 | 12 | <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> |
| 11 | 13 | <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> |
| 12 | 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>—</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 | 44 | </div> |
| 17 | 45 | </div> |