diff --git a/public/index.html b/public/index.html index a99d8ac6d..c5e5200b9 100644 --- a/public/index.html +++ b/public/index.html @@ -324,7 +324,7 @@
- +
@@ -3122,10 +3122,19 @@
User Avatar
-
+
+ +
+
+ + diff --git a/public/script.js b/public/script.js index c17a78cd6..185784f39 100644 --- a/public/script.js +++ b/public/script.js @@ -3919,6 +3919,7 @@ function appendUserAvatar(name) { template.attr('title', personaName); } template.find('.avatar').attr('imgfile', name); + template.toggleClass('default_persona', name === power_user.default_persona) template.find('img').attr('src', `User Avatars/${name}`); $("#user_avatar_block").append(template); highlightSelectedAvatar(); @@ -3939,9 +3940,9 @@ export function setUserName(value) { name1 = value; if (name1 === undefined || name1 == "") name1 = default_user_name; - console.log(name1); + console.log(`User name changed to ${name1}`); $("#your_name").val(name1); - toastr.success(`Your messages will now be sent as ${name1}`, 'User Name updated'); + toastr.success(`Your messages will now be sent as ${name1}`, 'Current persona updated'); saveSettings("change_name"); } else { toastr.warning('You cannot change your name while sending a message', 'Warning'); @@ -3951,6 +3952,7 @@ export function setUserName(value) { export function autoSelectPersona(name) { for (const [key, value] of Object.entries(power_user.personas)) { if (value === name) { + console.log(`Auto-selecting persona ${key} for name ${name}`); $(`.avatar[imgfile="${key}"]`).trigger('click'); return; } @@ -3967,11 +3969,22 @@ async function bindUserNameToPersona() { const existingPersona = power_user.personas[avatarId]; const personaName = await callPopup('

Enter a name for this persona:

(If empty name is provided, this will unbind the name from this avatar)', 'input', existingPersona || ''); - if (personaName) { + + // If the user clicked cancel, don't do anything + if (personaName === false) { + return; + } + + if (personaName.length > 0) { + // If the user clicked ok and entered a name, bind the name to the persona + console.log(`Binding persona ${avatarId} to name ${personaName}`); power_user.personas[avatarId] = personaName; } else { + // If the user clicked ok, but didn't enter a name, delete the persona + console.log(`Unbinding persona ${avatarId}`); delete power_user.personas[avatarId]; } + saveSettingsDebounced(); await getUserAvatars(); } @@ -3997,6 +4010,57 @@ function setUserAvatar() { } } +async function setUserInfo() { + // TODO Replace with actual implementation + callPopup('This functionality is under development.
Please check back later.', 'text'); +} + +async function setDefaultPersona() { + const avatarId = $(this).closest('.avatar-container').find('.avatar').attr('imgfile'); + + if (!avatarId) { + console.warn('No avatar id found'); + return; + } + + const currentDefault = power_user.default_persona; + + if (power_user.personas[avatarId] === undefined) { + console.warn(`No persona name found for avatar ${avatarId}`); + toastr.warning('You must bind a name to this persona before you can set it as the default.', 'Persona name not set'); + return; + } + + const personaName = power_user.personas[avatarId]; + + if (avatarId === currentDefault) { + const confirm = await callPopup('Are you sure you want to remove the default persona?', 'confirm'); + + if (!confirm) { + console.debug('User cancelled removing default persona'); + return; + } + + console.log(`Removing default persona ${avatarId}`); + toastr.info('This persona will no longer be used by default when you open a new chat.', `Default persona removed`); + delete power_user.default_persona; + } else { + const confirm = await callPopup(`

Are you sure you want to set "${personaName}" as the default persona?

+ This name and avatar will be used for all new chats, as well as existing chats where the user persona is not locked.`, 'confirm'); + + if (!confirm) { + console.debug('User cancelled setting default persona'); + return; + } + + power_user.default_persona = avatarId; + toastr.success('This persona will be used by default when you open a new chat.', `Default persona set to ${personaName}`); + } + + saveSettingsDebounced(); + await getUserAvatars(); +} + async function deleteUserAvatar() { const avatarId = $(this).closest('.avatar-container').find('.avatar').attr('imgfile'); @@ -4006,6 +4070,7 @@ async function deleteUserAvatar() { } if (avatarId == user_avatar) { + console.warn(`User tried to delete their current avatar ${avatarId}`); toastr.warning('You cannot delete the avatar you are currently using', 'Warning'); return; } @@ -4013,6 +4078,7 @@ async function deleteUserAvatar() { const confirm = await callPopup('Are you sure you want to delete this avatar?', 'confirm'); if (!confirm) { + console.debug('User cancelled deleting avatar'); return; } @@ -4025,7 +4091,14 @@ async function deleteUserAvatar() { }); if (request.ok) { + console.log(`Deleted avatar ${avatarId}`); delete power_user.personas[avatarId]; + + if (avatarId === power_user.default_persona) { + toastr.warning('The default persona was deleted. You will need to set a new default persona.', 'Default persona deleted'); + power_user.default_persona = null; + } + saveSettingsDebounced(); await getUserAvatars(); } @@ -4033,6 +4106,7 @@ async function deleteUserAvatar() { function lockUserNameToChat() { if (chat_metadata['persona']) { + console.log(`Unlocking persona for this chat ${chat_metadata['persona']}`); delete chat_metadata['persona']; saveMetadata(); toastr.info('User persona is now unlocked for this chat. Click the "Lock" to bind again.', 'Persona unlocked'); @@ -4040,6 +4114,7 @@ function lockUserNameToChat() { } if (!(user_avatar in power_user.personas)) { + console.log(`Creating a new persona ${user_avatar}`); toastr.info('Creating a new persona for currently selected user name and avatar...', 'Persona not set for this avatar'); power_user.personas[user_avatar] = name1; } @@ -4047,24 +4122,50 @@ function lockUserNameToChat() { chat_metadata['persona'] = user_avatar; saveMetadata(); saveSettingsDebounced(); + console.log(`Locking persona for this chat ${user_avatar}`); toastr.success(`User persona is locked to ${name1} in this chat`); } eventSource.on(event_types.CHAT_CHANGED, () => { - // If persona is locked, select it + // Define a persona for this chat + let chatPersona = ''; + if (chat_metadata['persona']) { - // Find the avatar file - const personaAvatar = $(`.avatar[imgfile="${chat_metadata['persona']}"]`).trigger('click'); - - // Avatar missing (persona deleted) - if (personaAvatar.length == 0) { - console.warn('Persona avatar not found, unlocking persona'); - delete chat_metadata['persona']; - return; - } - - personaAvatar.trigger('click'); + // If persona is locked in chat metadata, select it + console.log(`Using locked persona ${chat_metadata['persona']}`); + chatPersona = chat_metadata['persona']; + } else if (power_user.default_persona) { + // If default persona is set, select it + console.log(`Using default persona ${power_user.default_persona}`); + chatPersona = power_user.default_persona; } + + // No persona set: user current settings + if (!chatPersona) { + console.debug('No default or locked persona set for this chat'); + return; + } + + // Find the avatar file + const personaAvatar = $(`.avatar[imgfile="${chatPersona}"]`).trigger('click'); + + // Avatar missing (persona deleted) + if (chat_metadata['persona'] && personaAvatar.length == 0) { + console.warn('Persona avatar not found, unlocking persona'); + delete chat_metadata['persona']; + return; + } + + // Default persona missing + if (power_user.default_persona && personaAvatar.length == 0) { + console.warn('Default persona avatar not found, clearing default persona'); + power_user.default_persona = null; + saveSettingsDebounced(); + return; + } + + // Persona avatar found, select it + personaAvatar.trigger('click'); }); //***************SETTINGS****************// @@ -6690,7 +6791,7 @@ $(document).ready(function () { if (this_chid !== undefined || selected_group) { // Previously system messages we're allowed to be edited /*const message = $(this).closest(".mes"); - + if (message.data("isSystem")) { return; }*/ @@ -6947,6 +7048,8 @@ $(document).ready(function () { $(document).on('click', '.bind_user_name', bindUserNameToPersona); $(document).on('click', '.delete_avatar', deleteUserAvatar); + $(document).on('click', '.set_default_persona', setDefaultPersona); + $(document).on('click', '.set_user_info', setUserInfo); $('#lock_user_name').on('click', lockUserNameToChat); //**************************CHARACTER IMPORT EXPORT*************************// diff --git a/public/scripts/power-user.js b/public/scripts/power-user.js index 84c496ce8..a330d259b 100644 --- a/public/scripts/power-user.js +++ b/public/scripts/power-user.js @@ -146,6 +146,7 @@ let power_user = { }, personas: {}, + default_persona: null, }; let themes = []; diff --git a/public/scripts/slash-commands.js b/public/scripts/slash-commands.js index 16393e915..88d696548 100644 --- a/public/scripts/slash-commands.js +++ b/public/scripts/slash-commands.js @@ -262,6 +262,7 @@ function executeSlashCommands(text) { continue; } + console.debug('Slash command executing:', result); result.command.callback(result.args, result.value); if (result.command.interruptsGeneration) { diff --git a/public/style.css b/public/style.css index 6c7de3d52..9c3edca97 100644 --- a/public/style.css +++ b/public/style.css @@ -34,6 +34,7 @@ --orangered: rgb(255, 90, 0); --greyCAIbg: rgb(36, 36, 37); --ivory: rgb(220, 220, 210); + --golden: rgba(212, 175, 55, 1); /*Default Theme, will be changed by ToolCool Color Picker*/ @@ -1239,7 +1240,8 @@ select option:not(:checked) { } #form_character_search_form .menu_button, -#GroupFavDelOkBack .menu_button { +#GroupFavDelOkBack .menu_button, +.avatar-container .menu_button { margin: 0; height: fit-content; padding: 5px; @@ -1529,12 +1531,26 @@ input[type=search]:focus::-webkit-search-cancel-button { pointer-events: all; } +.avatar-buttons-bottom { + bottom: 0; + left: 0; +} + +.avatar-buttons-top { + top: 0; + left: 0; +} + +/* Ross should be able to handle this later */ +/*.big-avatars .avatar-buttons{ + justify-content: center; + width: fit-content; +}*/ + .avatar-buttons { pointer-events: none; display: none; position: absolute; - top: 0; - left: 0; width: 100%; justify-content: space-between; } @@ -2077,6 +2093,11 @@ input[type=search]:focus::-webkit-search-cancel-button { margin-right: 12px; } +/* Override toastr default styles */ +body #toast-container>div { + opacity: 0.95; +} + #dialogue_del_mes { display: none; } @@ -2152,6 +2173,15 @@ input[type='checkbox']:not(#nav-toggle):not(#rm_button_panel_pin):not(#lm_button outline: 2px solid transparent; } +#user_avatar_block .default_persona .avatar { + border: 2px solid var(--golden); + box-sizing: content-box; +} + +#user_avatar_block .default_persona .set_default_persona { + color: var(--golden); +} + #user_avatar_block .avatar img { width: 64px; height: 64px; @@ -4621,4 +4651,4 @@ body.waifuMode #avatar_zoom_popup { overflow-y: auto; overflow-x: hidden; } -} \ No newline at end of file +}