Update persona.js code documentation for exported
| @@ -61,7 +61,11 @@ let savePersonasPage = 0; | |||
| 61 | const GRID_STORAGE_KEY = 'Personas_GridView'; | 61 | const GRID_STORAGE_KEY = 'Personas_GridView'; |
| 62 | const DEFAULT_DEPTH = 2; | 62 | const DEFAULT_DEPTH = 2; |
| 63 | const DEFAULT_ROLE = 0; | 63 | const DEFAULT_ROLE = 0; |
| 64 | |||
| 65 | /** @type {string} The currently selected persona (identified by its avatar) */ | ||
| 64 | export let user_avatar = ''; | 66 | export let user_avatar = ''; |
| 67 | |||
| 68 | /** @type {FilterHelper} Filter helper for the persona list */ | ||
| 65 | export const personasFilter = new FilterHelper(debounce(getUserAvatars, debounce_timeout.quick)); | 69 | export const personasFilter = new FilterHelper(debounce(getUserAvatars, debounce_timeout.quick)); |
| 66 | 70 | ||
| 67 | 71 | ||
| @@ -441,13 +445,25 @@ export function initPersona(avatarId, personaName, personaDescription) { | |||
| 441 | saveSettingsDebounced(); | 445 | saveSettingsDebounced(); |
| 442 | } | 446 | } |
| 443 | 447 | ||
| 448 | /** | ||
| 449 | * Converts a character given character (either by character id or the current character) to a persona. | ||
| 450 | * | ||
| 451 | * If a persona with the same name already exists, the user is prompted to confirm whether or not to overwrite it. | ||
| 452 | * If the character description contains {{char}} or {{user}} macros, the user is prompted to confirm whether or not to swap them for persona macros. | ||
| 453 | * | ||
| 454 | * The function creates a new persona with the same name as the character, and sets the persona description to the character description with the macros swapped. | ||
| 455 | * The function also saves the settings and refreshes the persona selector. | ||
| 456 | * | ||
| 457 | * @param {number} [characterId] - The ID of the character to convert to a persona. Defaults to the current character ID. | ||
| 458 | * @returns {Promise<boolean>} A promise that resolves to true if the character was converted, false otherwise. | ||
| 459 | */ | ||
| 444 | export async function convertCharacterToPersona(characterId = null) { | 460 | export async function convertCharacterToPersona(characterId = null) { |
| 445 | if (null === characterId) characterId = this_chid; | 461 | if (null === characterId) characterId = this_chid; |
| 446 | 462 | ||
| 447 | const avatarUrl = characters[characterId]?.avatar; | 463 | const avatarUrl = characters[characterId]?.avatar; |
| 448 | if (!avatarUrl) { | 464 | if (!avatarUrl) { |
| 449 | console.log('No avatar found for this character'); | 465 | console.log('No avatar found for this character'); |
| 450 | return; | 466 | return false; |
| 451 | } | 467 | } |
| 452 | 468 | ||
| 453 | const name = characters[characterId]?.name; | 469 | const name = characters[characterId]?.name; |
| @@ -458,7 +474,7 @@ export async function convertCharacterToPersona(characterId = null) { | |||
| 458 | const confirm = await Popup.show.confirm(t`Overwrite Existing Persona`, t`This character exists as a persona already. Do you want to overwrite it?`); | 474 | const confirm = await Popup.show.confirm(t`Overwrite Existing Persona`, t`This character exists as a persona already. Do you want to overwrite it?`); |
| 459 | if (!confirm) { | 475 | if (!confirm) { |
| 460 | console.log('User cancelled the overwrite of the persona'); | 476 | console.log('User cancelled the overwrite of the persona'); |
| 461 | return; | 477 | return false; |
| 462 | } | 478 | } |
| 463 | } | 479 | } |
| 464 | 480 | ||
| @@ -496,6 +512,7 @@ export async function convertCharacterToPersona(characterId = null) { | |||
| 496 | await getUserAvatars(true, overwriteName); | 512 | await getUserAvatars(true, overwriteName); |
| 497 | // Reload the persona description | 513 | // Reload the persona description |
| 498 | setPersonaDescription(); | 514 | setPersonaDescription(); |
| 515 | return true; | ||
| 499 | } | 516 | } |
| 500 | 517 | ||
| 501 | /** | 518 | /** |
| @@ -507,6 +524,9 @@ const countPersonaDescriptionTokens = debounce(async () => { | |||
| 507 | $('#persona_description_token_count').text(String(count)); | 524 | $('#persona_description_token_count').text(String(count)); |
| 508 | }, debounce_timeout.relaxed); | 525 | }, debounce_timeout.relaxed); |
| 509 | 526 | ||
| 527 | /** | ||
| 528 | * Updates the UI for the Persona Management page with the current persona values | ||
| 529 | */ | ||
| 510 | export function setPersonaDescription() { | 530 | export function setPersonaDescription() { |
| 511 | $('#your_name').text(name1); | 531 | $('#your_name').text(name1); |
| 512 | 532 | ||
| @@ -683,22 +703,33 @@ export async function askForPersonaSelection(title, text, personas, { okButton = | |||
| 683 | return Number(result) >= 100 ? personas[Number(result) - 100] : null; | 703 | return Number(result) >= 100 ? personas[Number(result) - 100] : null; |
| 684 | } | 704 | } |
| 685 | 705 | ||
| 706 | /** | ||
| 707 | * Automatically selects a persona based on the given name if a matching persona exists. | ||
| 708 | * @param {string} name - The name to search for | ||
| 709 | * @returns {boolean} True if a matching persona was found and selected, false otherwise | ||
| 710 | */ | ||
| 686 | export function autoSelectPersona(name) { | 711 | export function autoSelectPersona(name) { |
| 687 | for (const [key, value] of Object.entries(power_user.personas)) { | 712 | for (const [key, value] of Object.entries(power_user.personas)) { |
| 688 | if (value === name) { | 713 | if (value === name) { |
| 689 | console.log(`Auto-selecting persona ${key} for name ${name}`); | 714 | console.log(`Auto-selecting persona ${key} for name ${name}`); |
| 690 | setUserAvatar(key); | 715 | setUserAvatar(key); |
| 691 | return; | 716 | return true; |
| 692 | } | 717 | } |
| 693 | } | 718 | } |
| 719 | return false; | ||
| 694 | } | 720 | } |
| 695 | 721 | ||
| 722 | /** | ||
| 723 | * Renames the persona with the given avatar ID by showing a popup to enter a new name. | ||
| 724 | * @param {string} avatarId - ID of the avatar to rename | ||
| 725 | * @returns {Promise<boolean>} A promise that resolves to true if the persona was renamed, false otherwise | ||
| 726 | */ | ||
| 696 | async function renamePersona(avatarId) { | 727 | async function renamePersona(avatarId) { |
| 697 | const currentName = power_user.personas[avatarId]; | 728 | const currentName = power_user.personas[avatarId]; |
| 698 | const newName = await Popup.show.input(t`Rename Persona`, t`Enter a new name for this persona:`, currentName); | 729 | const newName = await Popup.show.input(t`Rename Persona`, t`Enter a new name for this persona:`, currentName); |
| 699 | if (!newName || newName === currentName) { | 730 | if (!newName || newName === currentName) { |
| 700 | console.debug('User cancelled renaming persona or name is unchanged'); | 731 | console.debug('User cancelled renaming persona or name is unchanged'); |
| 701 | return; | 732 | return false; |
| 702 | } | 733 | } |
| 703 | 734 | ||
| 704 | power_user.personas[avatarId] = newName; | 735 | power_user.personas[avatarId] = newName; |
| @@ -712,9 +743,16 @@ async function renamePersona(avatarId) { | |||
| 712 | await getUserAvatars(true, avatarId); | 743 | await getUserAvatars(true, avatarId); |
| 713 | updatePersonaUIStates(); | 744 | updatePersonaUIStates(); |
| 714 | setPersonaDescription(); | 745 | setPersonaDescription(); |
| 746 | return true; | ||
| 715 | } | 747 | } |
| 716 | 748 | ||
| 717 | function selectCurrentPersona({ toastPersonaNameChange = true } = {}) { | 749 | /** |
| 750 | * Selects the persona with the currently set avatar ID by updating the user name and persona description, and updating the locked persona if the setting is enabled. | ||
| 751 | * @param {object} [options={}] - Optional settings | ||
| 752 | * @param {boolean} [options.toastPersonaNameChange=true] - Whether to show a toast when the persona name is changed | ||
| 753 | * @returns {Promise<void>} | ||
| 754 | */ | ||
| 755 | async function selectCurrentPersona({ toastPersonaNameChange = true } = {}) { | ||
| 718 | const personaName = power_user.personas[user_avatar]; | 756 | const personaName = power_user.personas[user_avatar]; |
| 719 | if (personaName) { | 757 | if (personaName) { |
| 720 | const shouldAutoLock = power_user.persona_auto_lock && user_avatar !== chat_metadata['persona']; | 758 | const shouldAutoLock = power_user.persona_auto_lock && user_avatar !== chat_metadata['persona']; |
| @@ -1211,6 +1249,14 @@ function getPersonaStates(avatarId) { | |||
| 1211 | }; | 1249 | }; |
| 1212 | } | 1250 | } |
| 1213 | 1251 | ||
| 1252 | /** | ||
| 1253 | * Updates the UI to reflect the current states of all personas and the selected user's persona. | ||
| 1254 | * This includes updating class states on avatar containers to indicate default status, chat lock, | ||
| 1255 | * and character lock, as well as updating icons and labels in the persona management panel to reflect | ||
| 1256 | * the current state of the user's persona. | ||
| 1257 | * Additionally, it manages the display of temporary persona lock information. | ||
| 1258 | */ | ||
| 1259 | |||
| 1214 | function updatePersonaUIStates() { | 1260 | function updatePersonaUIStates() { |
| 1215 | // Update the persona list | 1261 | // Update the persona list |
| 1216 | $('#user_avatar_block .avatar-container').each(function () { | 1262 | $('#user_avatar_block .avatar-container').each(function () { |
| @@ -1258,15 +1304,21 @@ function updatePersonaUIStates() { | |||
| 1258 | } | 1304 | } |
| 1259 | 1305 | ||
| 1260 | /** | 1306 | /** |
| 1261 | * Checks if the currently selected persona is temporary due to either a different default persona | 1307 | * @typedef {Object} PersonaLockInfo |
| 1262 | * or a different persona being locked to the current chat. If so, it also returns a string that | 1308 | * @property {boolean} isTemporary - Whether the selected persona is temporary based on current locks. |
| 1263 | * can be used to describe this situation to the user. | 1309 | * @property {boolean} hasDifferentChatLock - True if the chat persona is set and differs from the user avatar. |
| 1310 | * @property {boolean} hasDifferentDefaultLock - True if the default persona is set and differs from the user avatar. | ||
| 1311 | * @property {string} info - Detailed information about the current, chat, and default personas. | ||
| 1312 | */ | ||
| 1313 | |||
| 1314 | /** | ||
| 1315 | * Computes temporary lock information for the current persona. | ||
| 1316 | * | ||
| 1317 | * This function checks whether the currently selected persona is temporary by comparing | ||
| 1318 | * the chat persona and the default persona to the user avatar. If either is different, | ||
| 1319 | * the currently selected persona is considered temporary and a detailed message is generated. | ||
| 1264 | * | 1320 | * |
| 1265 | * @returns {{isTemporary: boolean, hasDifferentChatLock: boolean, hasDifferentDefaultLock: boolean, info: string?}} An object containing 4 properties: | 1321 | * @returns {PersonaLockInfo} An object containing flags and a message describing the persona lock status. |
| 1266 | * - isTemporary: A boolean indicating if the current persona is temporary | ||
| 1267 | * - hasDifferentChatLock: A boolean indicating if the current chat has a different persona locked to it | ||
| 1268 | * - hasDifferentDefaultLock: A boolean indicating if there is a different default persona set | ||
| 1269 | * - info: A string describing the situation, or an empty if not temporary | ||
| 1270 | */ | 1322 | */ |
| 1271 | function getPersonaTemporaryLockInfo() { | 1323 | function getPersonaTemporaryLockInfo() { |
| 1272 | const hasDifferentChatLock = !!chat_metadata['persona'] && chat_metadata['persona'] !== user_avatar; | 1324 | const hasDifferentChatLock = !!chat_metadata['persona'] && chat_metadata['persona'] !== user_avatar; |
| @@ -1286,6 +1338,13 @@ function getPersonaTemporaryLockInfo() { | |||
| 1286 | }; | 1338 | }; |
| 1287 | } | 1339 | } |
| 1288 | 1340 | ||
| 1341 | /** | ||
| 1342 | * Loads the appropriate persona for the current chat session based on locks (chat lock, char lock, default persona) | ||
| 1343 | * | ||
| 1344 | * @param {Object} [options={}] - Optional arguments | ||
| 1345 | * @param {boolean} [options.doRender=false] - Whether to render the persona immediately | ||
| 1346 | * @returns {Promise<boolean>} - A promise that resolves to a boolean indicating whether a persona was selected | ||
| 1347 | */ | ||
| 1289 | async function loadPersonaForCurrentChat({ doRender = false } = {}) { | 1348 | async function loadPersonaForCurrentChat({ doRender = false } = {}) { |
| 1290 | // Cache persona list to check if they exist | 1349 | // Cache persona list to check if they exist |
| 1291 | const userAvatars = await getUserAvatars(doRender); | 1350 | const userAvatars = await getUserAvatars(doRender); |
| @@ -1317,7 +1376,7 @@ async function loadPersonaForCurrentChat({ doRender = false } = {}) { | |||
| 1317 | if (chatPersona) { | 1376 | if (chatPersona) { |
| 1318 | // If the chat-bound persona is the currently selected one, we can simply exit out | 1377 | // If the chat-bound persona is the currently selected one, we can simply exit out |
| 1319 | if (chatPersona === user_avatar) { | 1378 | if (chatPersona === user_avatar) { |
| 1320 | return; | 1379 | return false; |
| 1321 | } | 1380 | } |
| 1322 | // Otherwise ask if we want to switch | 1381 | // Otherwise ask if we want to switch |
| 1323 | const autoLock = power_user.persona_auto_lock; | 1382 | const autoLock = power_user.persona_auto_lock; |
| @@ -1328,11 +1387,11 @@ async function loadPersonaForCurrentChat({ doRender = false } = {}) { | |||
| 1328 | if (autoLock) { | 1387 | if (autoLock) { |
| 1329 | lockPersona('chat'); | 1388 | lockPersona('chat'); |
| 1330 | } | 1389 | } |
| 1331 | return; | 1390 | return false; |
| 1332 | } | 1391 | } |
| 1333 | } else { | 1392 | } else { |
| 1334 | // If we don't have a chat-bound persona, we simply return and keep the current one we have | 1393 | // If we don't have a chat-bound persona, we simply return and keep the current one we have |
| 1335 | return; | 1394 | return false; |
| 1336 | } | 1395 | } |
| 1337 | } | 1396 | } |
| 1338 | 1397 | ||
| @@ -1392,6 +1451,8 @@ async function loadPersonaForCurrentChat({ doRender = false } = {}) { | |||
| 1392 | } | 1451 | } |
| 1393 | 1452 | ||
| 1394 | updatePersonaUIStates(); | 1453 | updatePersonaUIStates(); |
| 1454 | |||
| 1455 | return !!chatPersona; | ||
| 1395 | } | 1456 | } |
| 1396 | 1457 | ||
| 1397 | /** | 1458 | /** |
| @@ -1462,7 +1523,6 @@ export async function showCharConnections() { | |||
| 1462 | * | 1523 | * |
| 1463 | * @returns {PersonaConnection} An object representing the current connection | 1524 | * @returns {PersonaConnection} An object representing the current connection |
| 1464 | */ | 1525 | */ |
| 1465 | |||
| 1466 | export function getCurrentConnectionObj() { | 1526 | export function getCurrentConnectionObj() { |
| 1467 | if (selected_group) | 1527 | if (selected_group) |
| 1468 | return { type: 'group', id: selected_group }; | 1528 | return { type: 'group', id: selected_group }; |
| @@ -1579,6 +1639,11 @@ async function syncUserNameToPersona() { | |||
| 1579 | await reloadCurrentChat(); | 1639 | await reloadCurrentChat(); |
| 1580 | } | 1640 | } |
| 1581 | 1641 | ||
| 1642 | /** | ||
| 1643 | * Retriggers the first message to reload it from the char definition. | ||
| 1644 | * | ||
| 1645 | * Only works if only the first message is present, and not in group mode. | ||
| 1646 | */ | ||
| 1582 | export function retriggerFirstMessageOnEmptyChat() { | 1647 | export function retriggerFirstMessageOnEmptyChat() { |
| 1583 | if (this_chid >= 0 && !selected_group && chat.length === 1) { | 1648 | if (this_chid >= 0 && !selected_group && chat.length === 1) { |
| 1584 | $('#firstmessage_textarea').trigger('input'); | 1649 | $('#firstmessage_textarea').trigger('input'); |
| @@ -1812,6 +1877,10 @@ function registerPersonaSlashCommands() { | |||
| 1812 | })); | 1877 | })); |
| 1813 | } | 1878 | } |
| 1814 | 1879 | ||
| 1880 | /** | ||
| 1881 | * Initializes the persona management and all its functionality. | ||
| 1882 | * This is called during the initialization of the page. | ||
| 1883 | */ | ||
| 1815 | export async function initPersonas() { | 1884 | export async function initPersonas() { |
| 1816 | await migrateNonPersonaUser(); | 1885 | await migrateNonPersonaUser(); |
| 1817 | registerPersonaSlashCommands(); | 1886 | registerPersonaSlashCommands(); |