Update persona.js code documentation for exported
| @@ -61,7 +61,11 @@ let savePersonasPage = 0; | ||
| 61 | 61 | const GRID_STORAGE_KEY = 'Personas_GridView'; |
| 62 | 62 | const DEFAULT_DEPTH = 2; |
| 63 | 63 | const DEFAULT_ROLE = 0; |
| 64 | + | |
| 65 | +/** @type {string} The currently selected persona (identified by its avatar) */ | |
| 64 | 66 | export let user_avatar = ''; |
| 67 | + | |
| 68 | +/** @type {FilterHelper} Filter helper for the persona list */ | |
| 65 | 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 | 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 | 460 | export async function convertCharacterToPersona(characterId = null) { |
| 445 | 461 | if (null === characterId) characterId = this_chid; |
| 446 | 462 | |
| 447 | 463 | const avatarUrl = characters[characterId]?.avatar; |
| 448 | 464 | if (!avatarUrl) { |
| 449 | 465 | console.log('No avatar found for this character'); |
| 450 | 466 | return false; |
| 451 | 467 | } |
| 452 | 468 | |
| 453 | 469 | const name = characters[characterId]?.name; |
| @@ -458,7 +474,7 @@ export async function convertCharacterToPersona(characterId = null) { | ||
| 458 | 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 | 475 | if (!confirm) { |
| 460 | 476 | console.log('User cancelled the overwrite of the persona'); |
| 461 | 477 | return false; |
| 462 | 478 | } |
| 463 | 479 | } |
| 464 | 480 | |
| @@ -496,6 +512,7 @@ export async function convertCharacterToPersona(characterId = null) { | ||
| 496 | 512 | await getUserAvatars(true, overwriteName); |
| 497 | 513 | // Reload the persona description |
| 498 | 514 | setPersonaDescription(); |
| 515 | + return true; | |
| 499 | 516 | } |
| 500 | 517 | |
| 501 | 518 | /** |
| @@ -507,6 +524,9 @@ const countPersonaDescriptionTokens = debounce(async () => { | ||
| 507 | 524 | $('#persona_description_token_count').text(String(count)); |
| 508 | 525 | }, debounce_timeout.relaxed); |
| 509 | 526 | |
| 527 | +/** | |
| 528 | + * Updates the UI for the Persona Management page with the current persona values | |
| 529 | + */ | |
| 510 | 530 | export function setPersonaDescription() { |
| 511 | 531 | $('#your_name').text(name1); |
| 512 | 532 | |
| @@ -683,22 +703,33 @@ export async function askForPersonaSelection(title, text, personas, { okButton = | ||
| 683 | 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 | 711 | export function autoSelectPersona(name) { |
| 687 | 712 | for (const [key, value] of Object.entries(power_user.personas)) { |
| 688 | 713 | if (value === name) { |
| 689 | 714 | console.log(`Auto-selecting persona ${key} for name ${name}`); |
| 690 | 715 | setUserAvatar(key); |
| 691 | 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 | 727 | async function renamePersona(avatarId) { |
| 697 | 728 | const currentName = power_user.personas[avatarId]; |
| 698 | 729 | const newName = await Popup.show.input(t`Rename Persona`, t`Enter a new name for this persona:`, currentName); |
| 699 | 730 | if (!newName || newName === currentName) { |
| 700 | 731 | console.debug('User cancelled renaming persona or name is unchanged'); |
| 701 | 732 | return false; |
| 702 | 733 | } |
| 703 | 734 | |
| 704 | 735 | power_user.personas[avatarId] = newName; |
| @@ -712,9 +743,16 @@ async function renamePersona(avatarId) { | ||
| 712 | 743 | await getUserAvatars(true, avatarId); |
| 713 | 744 | updatePersonaUIStates(); |
| 714 | 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 | 756 | const personaName = power_user.personas[user_avatar]; |
| 719 | 757 | if (personaName) { |
| 720 | 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 | 1260 | function updatePersonaUIStates() { |
| 1215 | 1261 | // Update the persona list |
| 1216 | 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 | 1323 | function getPersonaTemporaryLockInfo() { |
| 1272 | 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 | 1348 | async function loadPersonaForCurrentChat({ doRender = false } = {}) { |
| 1290 | 1349 | // Cache persona list to check if they exist |
| 1291 | 1350 | const userAvatars = await getUserAvatars(doRender); |
| @@ -1317,7 +1376,7 @@ async function loadPersonaForCurrentChat({ doRender = false } = {}) { | ||
| 1317 | 1376 | if (chatPersona) { |
| 1318 | 1377 | // If the chat-bound persona is the currently selected one, we can simply exit out |
| 1319 | 1378 | if (chatPersona === user_avatar) { |
| 1320 | 1379 | return false; |
| 1321 | 1380 | } |
| 1322 | 1381 | // Otherwise ask if we want to switch |
| 1323 | 1382 | const autoLock = power_user.persona_auto_lock; |
| @@ -1328,11 +1387,11 @@ async function loadPersonaForCurrentChat({ doRender = false } = {}) { | ||
| 1328 | 1387 | if (autoLock) { |
| 1329 | 1388 | lockPersona('chat'); |
| 1330 | 1389 | } |
| 1331 | 1390 | return false; |
| 1332 | 1391 | } |
| 1333 | 1392 | } else { |
| 1334 | 1393 | // If we don't have a chat-bound persona, we simply return and keep the current one we have |
| 1335 | 1394 | return false; |
| 1336 | 1395 | } |
| 1337 | 1396 | } |
| 1338 | 1397 | |
| @@ -1392,6 +1451,8 @@ async function loadPersonaForCurrentChat({ doRender = false } = {}) { | ||
| 1392 | 1451 | } |
| 1393 | 1452 | |
| 1394 | 1453 | updatePersonaUIStates(); |
| 1454 | + | |
| 1455 | + return !!chatPersona; | |
| 1395 | 1456 | } |
| 1396 | 1457 | |
| 1397 | 1458 | /** |
| @@ -1462,7 +1523,6 @@ export async function showCharConnections() { | ||
| 1462 | 1523 | * |
| 1463 | 1524 | * @returns {PersonaConnection} An object representing the current connection |
| 1464 | 1525 | */ |
| 1465 | - | |
| 1466 | 1526 | export function getCurrentConnectionObj() { |
| 1467 | 1527 | if (selected_group) |
| 1468 | 1528 | return { type: 'group', id: selected_group }; |
| @@ -1579,6 +1639,11 @@ async function syncUserNameToPersona() { | ||
| 1579 | 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 | 1647 | export function retriggerFirstMessageOnEmptyChat() { |
| 1583 | 1648 | if (this_chid >= 0 && !selected_group && chat.length === 1) { |
| 1584 | 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 | 1884 | export async function initPersonas() { |
| 1816 | 1885 | await migrateNonPersonaUser(); |
| 1817 | 1886 | registerPersonaSlashCommands(); |