mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-06-05 21:59:27 +02:00
Add/expand persona auto selection from char / chat
- Persona selection on chat based on three levels: 1. Chat Locking 2. Char connected personas 3. Default persona - Add popup if multiple personas are connected to char - Add utility function to print persona avatar lists
This commit is contained in:
@ -6383,7 +6383,7 @@ export function buildAvatarList(block, entities, { templateId = 'inline_avatar_t
|
||||
}
|
||||
|
||||
avatarTemplate.attr('data-type', entity.type);
|
||||
avatarTemplate.attr({ 'chid': id, 'id': `CharID${id}` });
|
||||
avatarTemplate.attr('data-chid', id);
|
||||
avatarTemplate.find('img').attr('src', this_avatar).attr('alt', entity.item.name);
|
||||
avatarTemplate.attr('title', `[Character] ${entity.item.name}\nFile: ${entity.item.avatar}`);
|
||||
if (highlightFavs) {
|
||||
@ -6398,8 +6398,14 @@ export function buildAvatarList(block, entities, { templateId = 'inline_avatar_t
|
||||
avatarTemplate.addClass(grpTemplate.attr('class'));
|
||||
avatarTemplate.empty();
|
||||
avatarTemplate.append(grpTemplate.children());
|
||||
avatarTemplate.attr({ 'data-grid': id, 'data-chid': null });
|
||||
avatarTemplate.attr('title', `[Group] ${entity.item.name}`);
|
||||
}
|
||||
else if (entity.type === 'persona') {
|
||||
avatarTemplate.attr({ 'data-pid': id, 'data-chid': null });
|
||||
avatarTemplate.find('img').attr('src', getUserAvatar(entity.item.avatar));
|
||||
avatarTemplate.attr('title', `[Persona] ${entity.item.name}\nFile: ${entity.item.avatar}`);
|
||||
}
|
||||
|
||||
if (interactable) {
|
||||
avatarTemplate.addClass(INTERACTABLE_CONTROL_CLASS);
|
||||
|
@ -485,6 +485,32 @@ export function setPersonaDescription() {
|
||||
updatePersonaConnectionsAvatarList();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Builds a list of persona avatars and populates the given block element with them.
|
||||
*
|
||||
* @param {HTMLElement} block - The HTML element where the avatar list will be rendered
|
||||
* @param {string[]} personas - An array of persona identifiers
|
||||
* @param {Object} [options] - Optional settings for building the avatar list
|
||||
* @param {boolean} [options.empty=true] - Whether to clear the block element before adding avatars
|
||||
* @param {boolean} [options.interactable=false] - Whether the avatars should be interactable
|
||||
* @param {boolean} [options.highlightFavs=true] - Whether to highlight favorite avatars
|
||||
*/
|
||||
export function buildPersonaAvatarList(block, personas, { empty = true, interactable = false, highlightFavs = true } = {}) {
|
||||
const personaEntities = personas.map(avatar => ({
|
||||
type: 'persona',
|
||||
id: avatar,
|
||||
item: {
|
||||
name: power_user.personas[avatar],
|
||||
description: power_user.persona_descriptions[avatar]?.description || '',
|
||||
avatar: avatar,
|
||||
fav: power_user.default_persona === avatar,
|
||||
},
|
||||
}));
|
||||
|
||||
buildAvatarList($(block), personaEntities, { empty: empty, interactable: interactable, highlightFavs: highlightFavs });
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays avatar connections for the current persona.
|
||||
* Converts connections to entities and populates the avatar list. Shows a message if no connections are found.
|
||||
@ -510,6 +536,42 @@ export function updatePersonaConnectionsAvatarList() {
|
||||
$('#persona_connections_list').text('[No connections]');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Displays a popup for persona selection and returns the selected persona.
|
||||
*
|
||||
* @param {string} title - The title to display in the popup
|
||||
* @param {string} text - The text to display in the popup
|
||||
* @param {string[]} personas - An array of persona ids to display for selection
|
||||
* @returns {Promise<string?>} - A promise that resolves to the selected persona id or null if no selection was made
|
||||
*/
|
||||
export async function askForPersonaSelection(title, text, personas) {
|
||||
const content = document.createElement('div');
|
||||
const titleElement = document.createElement('h3');
|
||||
titleElement.textContent = title;
|
||||
content.appendChild(titleElement);
|
||||
|
||||
const textElement = document.createElement('div');
|
||||
textElement.textContent = text;
|
||||
content.appendChild(textElement);
|
||||
|
||||
const personaListBlock = document.createElement('div');
|
||||
personaListBlock.classList.add('persona-list', 'avatars_inline', 'flex-container');
|
||||
content.appendChild(personaListBlock);
|
||||
|
||||
buildPersonaAvatarList(personaListBlock, personas, { interactable: true });
|
||||
|
||||
// Make the persona blocks clickable and close the popup
|
||||
personaListBlock.querySelectorAll('.avatar[data-type="persona"]').forEach(block => {
|
||||
if (!(block instanceof HTMLElement)) return;
|
||||
block.dataset.result = String(100 + personas.indexOf(block.dataset.pid));
|
||||
});
|
||||
|
||||
const popup = new Popup(content, POPUP_TYPE.TEXT, '', {});
|
||||
const result = await popup.show();
|
||||
return Number(result) > 100 ? personas[Number(result) - 100] : null;
|
||||
}
|
||||
|
||||
export function autoSelectPersona(name) {
|
||||
for (const [key, value] of Object.entries(power_user.personas)) {
|
||||
if (value === name) {
|
||||
@ -1068,34 +1130,59 @@ function updatePersonaLockIcons() {
|
||||
}
|
||||
|
||||
async function setChatLockedPersona() {
|
||||
// Cache persona list to check if they exist
|
||||
const userAvatars = await getUserAvatars(false);
|
||||
|
||||
// Define a persona for this chat
|
||||
let chatPersona = '';
|
||||
|
||||
if (chat_metadata['persona']) {
|
||||
// If persona is locked in chat metadata, select it
|
||||
if (chat_metadata['persona']) {
|
||||
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
|
||||
|
||||
// Verify it exists
|
||||
if (!userAvatars.includes(chatPersona)) {
|
||||
console.warn('Chat-locked persona avatar not found, unlocking persona');
|
||||
delete chat_metadata['persona'];
|
||||
updatePersonaLockIcons();
|
||||
chatPersona = '';
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: TEMP
|
||||
power_user.persona_allow_multi_connections = true;
|
||||
|
||||
// Check if we have any persona connected to the current character
|
||||
if (!chatPersona) {
|
||||
const characterKey = menu_type === 'group_edit' ? selected_group : characters[this_chid]?.avatar;
|
||||
const connectedPersonas = Object.entries(power_user.persona_descriptions)
|
||||
.filter(([_, desc]) => desc.connections?.some(conn => conn.type === 'character' && conn.id === characterKey))
|
||||
.map(([key, _]) => key);
|
||||
|
||||
if (connectedPersonas.length > 0) {
|
||||
if (!power_user.persona_allow_multi_connections || connectedPersonas.length === 1) {
|
||||
chatPersona = connectedPersonas[0];
|
||||
toastr.warning(t`More than one persona is connected to this character. Using the first available persona for this chat.`, t`Automatic persona selection`);
|
||||
} else {
|
||||
chatPersona = await askForPersonaSelection(t`Select Persona`,
|
||||
t`Select one of multiple with this character connected persona to use for this chat`,
|
||||
connectedPersonas);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Last check if default persona is set, select it
|
||||
if (!chatPersona && power_user.default_persona) {
|
||||
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 userAvatars = await getUserAvatars(false);
|
||||
|
||||
// Avatar missing (persona deleted)
|
||||
if (chat_metadata['persona'] && !userAvatars.includes(chatPersona)) {
|
||||
// Whatever way we selected a persona, if it doesn't exist, unlock this chat
|
||||
if (chat_metadata['persona'] && !userAvatars.includes(chat_metadata['persona'])) {
|
||||
console.warn('Persona avatar not found, unlocking persona');
|
||||
delete chat_metadata['persona'];
|
||||
updatePersonaLockIcons();
|
||||
return;
|
||||
}
|
||||
|
||||
// Default persona missing
|
||||
@ -1103,11 +1190,13 @@ async function setChatLockedPersona() {
|
||||
console.warn('Default persona avatar not found, clearing default persona');
|
||||
power_user.default_persona = null;
|
||||
saveSettingsDebounced();
|
||||
return;
|
||||
}
|
||||
|
||||
// Persona avatar found, select it
|
||||
if (chatPersona) {
|
||||
setUserAvatar(chatPersona);
|
||||
}
|
||||
|
||||
updatePersonaLockIcons();
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user