Highlight personas from current chat in popup
| @@ -9994,6 +9994,7 @@ jQuery(async function () { | ||
| 9994 | 9994 | const message = t`The following personas are connected to the current character.\n\nClick on a persona to select it for the current character.\nShift + Click to unlink the persona from the character.`; |
| 9995 | 9995 | const selectedPersona = await askForPersonaSelection(t`Persona Connections`, message, connections, { |
| 9996 | 9996 | okButton: t`Ok`, |
| 9997 | + highlightPersonas: true, | |
| 9997 | 9998 | shiftClickHandler: (element, ev) => { |
| 9998 | 9999 | |
| 9999 | 10000 | const personaId = $(element).attr('data-pid'); |
| @@ -22,7 +22,7 @@ import { | ||
| 22 | 22 | } from '../script.js'; |
| 23 | 23 | import { persona_description_positions, power_user } from './power-user.js'; |
| 24 | 24 | import { getTokenCountAsync } from './tokenizers.js'; |
| 25 | 25 | import { PAGINATION_TEMPLATE, debounce, delay, download, ensureImageFormatSupported, flashHighlight, getBase64Async, getCharIndex, onlyUnique, parseJsonFile } from './utils.js'; |
| 26 | 26 | import { debounce_timeout } from './constants.js'; |
| 27 | 27 | import { FILTER_TYPES, FilterHelper } from './filters.js'; |
| 28 | 28 | import { groups, selected_group } from './group-chats.js'; |
| @@ -40,6 +40,8 @@ import { saveMetadataDebounced } from './extensions.js'; | ||
| 40 | 40 | |
| 41 | 41 | /** @typedef {'chat' | 'character' | 'default'} PersonaLockType Type of the persona lock */ |
| 42 | 42 | |
| 43 | +const USER_AVATAR_PATH = 'User Avatars/'; | |
| 44 | + | |
| 43 | 45 | let savePersonasPage = 0; |
| 44 | 46 | const GRID_STORAGE_KEY = 'Personas_GridView'; |
| 45 | 47 | const DEFAULT_DEPTH = 2; |
| @@ -58,7 +60,7 @@ function switchPersonaGridView() { | ||
| 58 | 60 | * @returns {string} User avatar URL |
| 59 | 61 | */ |
| 60 | 62 | export function getUserAvatar(avatarImg) { |
| 61 | 63 | return `User Avatars/${USER_AVATAR_PATH}${avatarImg}`; |
| 62 | 64 | } |
| 63 | 65 | |
| 64 | 66 | export function initUserAvatar(avatar) { |
| @@ -486,6 +488,17 @@ export function setPersonaDescription() { | ||
| 486 | 488 | updatePersonaConnectionsAvatarList(); |
| 487 | 489 | } |
| 488 | 490 | |
| 491 | +/** | |
| 492 | + * Gets a list of all personas in the current chat. | |
| 493 | + * | |
| 494 | + * @returns {string[]} An array of persona identifiers | |
| 495 | + */ | |
| 496 | +function getPersonasOfCurrentChat() { | |
| 497 | + const personas = chat.filter(message => String(message.force_avatar).startsWith(USER_AVATAR_PATH)) | |
| 498 | + .map(message => message.force_avatar.replace(USER_AVATAR_PATH, '')) | |
| 499 | + .filter(onlyUnique); | |
| 500 | + return personas; | |
| 501 | +} | |
| 489 | 502 | |
| 490 | 503 | /** |
| 491 | 504 | * Builds a list of persona avatars and populates the given block element with them. |
| @@ -547,9 +560,10 @@ export function updatePersonaConnectionsAvatarList() { | ||
| 547 | 560 | * @param {Object} [options] - Optional settings for the popup |
| 548 | 561 | * @param {string} [options.okButton='None'] - The label for the OK button |
| 549 | 562 | * @param {(element: HTMLElement, ev: MouseEvent) => any} [options.shiftClickHandler] - A function to handle shift-click |
| 563 | + * @param {boolean|string[]} [options.highlightPersonas=false] - Whether to highlight personas - either by providing a list of persona keys, or true to highlight all present in current chat | |
| 550 | 564 | * @returns {Promise<string?>} - A promise that resolves to the selected persona id or null if no selection was made |
| 551 | 565 | */ |
| 552 | 566 | export async function askForPersonaSelection(title, text, personas, { okButton = 'None', shiftClickHandler = undefined, highlightPersonas = false } = {}) { |
| 553 | 567 | const content = document.createElement('div'); |
| 554 | 568 | const titleElement = document.createElement('h3'); |
| 555 | 569 | titleElement.textContent = title; |
| @@ -569,6 +583,8 @@ export async function askForPersonaSelection(title, text, personas, { okButton = | ||
| 569 | 583 | else |
| 570 | 584 | personaListBlock.textContent = '[No personas]'; |
| 571 | 585 | |
| 586 | + const personasToHighlight = highlightPersonas instanceof Array ? highlightPersonas : (highlightPersonas ? getPersonasOfCurrentChat() : []); | |
| 587 | + | |
| 572 | 588 | // Make the persona blocks clickable and close the popup |
| 573 | 589 | personaListBlock.querySelectorAll('.avatar[data-type="persona"]').forEach(block => { |
| 574 | 590 | if (!(block instanceof HTMLElement)) return; |
| @@ -581,6 +597,12 @@ export async function askForPersonaSelection(title, text, personas, { okButton = | ||
| 581 | 597 | } |
| 582 | 598 | }); |
| 583 | 599 | } |
| 600 | + | |
| 601 | + if (personasToHighlight && personasToHighlight.includes(block.dataset.pid)) { | |
| 602 | + block.classList.add('is_active'); | |
| 603 | + block.title = block.title + '\n\n' + t`Was used in current chat.`; | |
| 604 | + if (block.classList.contains('is_fav')) block.title = block.title + '\n' + t`Is your default persona.`; | |
| 605 | + } | |
| 584 | 606 | }); |
| 585 | 607 | |
| 586 | 608 | const popup = new Popup(content, POPUP_TYPE.TEXT, '', { okButton: okButton }); |
| @@ -1225,7 +1247,7 @@ async function loadPersonaForCurrentChat() { | ||
| 1225 | 1247 | } else { |
| 1226 | 1248 | chatPersona = await askForPersonaSelection(t`Select Persona`, |
| 1227 | 1249 | t`Multiple personas are connected to this character.\nSelect a persona to use for this chat.`, |
| 1228 | - connectedPersonas); | |
| 1250 | + connectedPersonas, { highlightPersonas: true }); | |
| 1229 | 1251 | } |
| 1230 | 1252 | } |
| 1231 | 1253 | |
| @@ -3151,6 +3151,10 @@ input[type=search]:focus::-webkit-search-cancel-button { | ||
| 3151 | 3151 | color: var(--golden); |
| 3152 | 3152 | } |
| 3153 | 3153 | |
| 3154 | +.avatar.is_active { | |
| 3155 | + outline: 2px solid var(--active); | |
| 3156 | +} | |
| 3157 | + | |
| 3154 | 3158 | #fav_chara_wrap { |
| 3155 | 3159 | display: flex; |
| 3156 | 3160 | margin: 5px 0px; |