2023-11-26 18:56:19 +01:00
import {
characters ,
2024-05-01 13:03:24 +02:00
chat ,
2023-11-26 18:56:19 +01:00
chat _metadata ,
2024-09-16 21:08:40 +02:00
default _user _avatar ,
2023-11-26 18:56:19 +01:00
eventSource ,
event _types ,
getRequestHeaders ,
getThumbnailUrl ,
name1 ,
2024-05-01 13:03:24 +02:00
reloadCurrentChat ,
saveChatConditional ,
2023-11-26 18:56:19 +01:00
saveMetadata ,
saveSettingsDebounced ,
setUserName ,
this _chid ,
2023-12-02 19:04:51 +01:00
} from '../script.js' ;
import { persona _description _positions , power _user } from './power-user.js' ;
2024-04-13 20:33:19 +02:00
import { getTokenCountAsync } from './tokenizers.js' ;
2024-05-01 13:03:24 +02:00
import { PAGINATION _TEMPLATE , debounce , delay , download , ensureImageFormatSupported , flashHighlight , getBase64Async , parseJsonFile } from './utils.js' ;
2024-04-28 18:47:53 +02:00
import { debounce _timeout } from './constants.js' ;
2024-05-01 13:03:24 +02:00
import { FILTER _TYPES , FilterHelper } from './filters.js' ;
import { selected _group } from './group-chats.js' ;
2024-07-25 22:57:00 +02:00
import { POPUP _RESULT , POPUP _TYPE , Popup } from './popup.js' ;
2023-08-29 00:54:11 +02:00
2024-05-01 13:03:24 +02:00
let savePersonasPage = 0 ;
2024-01-31 11:13:01 +01:00
const GRID _STORAGE _KEY = 'Personas_GridView' ;
2024-05-21 00:57:04 +02:00
const DEFAULT _DEPTH = 2 ;
const DEFAULT _ROLE = 0 ;
2024-05-01 13:03:24 +02:00
export let user _avatar = '' ;
export const personasFilter = new FilterHelper ( debounce ( getUserAvatars , debounce _timeout . quick ) ) ;
2024-01-31 11:13:01 +01:00
function switchPersonaGridView ( ) {
const state = localStorage . getItem ( GRID _STORAGE _KEY ) === 'true' ;
$ ( '#user_avatar_block' ) . toggleClass ( 'gridView' , state ) ;
}
2024-05-01 13:03:24 +02:00
/ * *
* Returns the URL of the avatar for the given user avatar Id .
* @ param { string } avatarImg User avatar Id
* @ returns { string } User avatar URL
* /
export function getUserAvatar ( avatarImg ) {
return ` User Avatars/ ${ avatarImg } ` ;
}
export function initUserAvatar ( avatar ) {
user _avatar = avatar ;
reloadUserAvatar ( ) ;
highlightSelectedAvatar ( ) ;
}
/ * *
* Sets a user avatar file
* @ param { string } imgfile Link to an image file
* /
export function setUserAvatar ( imgfile ) {
user _avatar = imgfile && typeof imgfile === 'string' ? imgfile : $ ( this ) . attr ( 'imgfile' ) ;
reloadUserAvatar ( ) ;
highlightSelectedAvatar ( ) ;
selectCurrentPersona ( ) ;
saveSettingsDebounced ( ) ;
$ ( '.zoomed_avatar[forchar]' ) . remove ( ) ;
}
function highlightSelectedAvatar ( ) {
$ ( '#user_avatar_block .avatar-container' ) . removeClass ( 'selected' ) ;
$ ( ` #user_avatar_block .avatar-container[imgfile=" ${ user _avatar } "] ` ) . addClass ( 'selected' ) ;
}
function reloadUserAvatar ( force = false ) {
$ ( '.mes' ) . each ( function ( ) {
const avatarImg = $ ( this ) . find ( '.avatar img' ) ;
if ( force ) {
avatarImg . attr ( 'src' , avatarImg . attr ( 'src' ) ) ;
}
if ( $ ( this ) . attr ( 'is_user' ) == 'true' && $ ( this ) . attr ( 'force_avatar' ) == 'false' ) {
avatarImg . attr ( 'src' , getUserAvatar ( user _avatar ) ) ;
}
} ) ;
}
/ * *
* Sort the given personas
* @ param { string [ ] } personas - The persona names to sort
* @ returns { string [ ] } The sorted persona names arrray , same reference as passed in
* /
function sortPersonas ( personas ) {
const option = $ ( '#persona_sort_order' ) . find ( ':selected' ) ;
if ( option . attr ( 'value' ) === 'search' ) {
personas . sort ( ( a , b ) => {
const aScore = personasFilter . getScore ( FILTER _TYPES . PERSONA _SEARCH , a ) ;
const bScore = personasFilter . getScore ( FILTER _TYPES . PERSONA _SEARCH , b ) ;
return ( aScore - bScore ) ;
} ) ;
} else {
personas . sort ( ( a , b ) => {
const aName = String ( power _user . personas [ a ] || a ) ;
const bName = String ( power _user . personas [ b ] || b ) ;
return power _user . persona _sort _order === 'asc' ? aName . localeCompare ( bName ) : bName . localeCompare ( aName ) ;
} ) ;
}
return personas ;
}
/** Checks the state of the current search, and adds/removes the search sorting option accordingly */
function verifyPersonaSearchSortRule ( ) {
const searchTerm = personasFilter . getFilterData ( FILTER _TYPES . PERSONA _SEARCH ) ;
const searchOption = $ ( '#persona_sort_order option[value="search"]' ) ;
const selector = $ ( '#persona_sort_order' ) ;
const isHidden = searchOption . attr ( 'hidden' ) !== undefined ;
// If we have a search term, we are displaying the sorting option for it
if ( searchTerm && isHidden ) {
searchOption . removeAttr ( 'hidden' ) ;
selector . val ( searchOption . attr ( 'value' ) ) ;
flashHighlight ( selector ) ;
}
// If search got cleared, we make sure to hide the option and go back to the one before
2024-05-20 23:22:56 +02:00
if ( ! searchTerm ) {
2024-05-01 13:03:24 +02:00
searchOption . attr ( 'hidden' , '' ) ;
selector . val ( power _user . persona _sort _order ) ;
}
}
/ * *
* Gets a rendered avatar block .
* @ param { string } name Avatar file name
* @ returns { JQuery < HTMLElement > } Avatar block
* /
function getUserAvatarBlock ( name ) {
const isFirefox = navigator . userAgent . toLowerCase ( ) . indexOf ( 'firefox' ) > - 1 ;
const template = $ ( '#user_avatar_template .avatar-container' ) . clone ( ) ;
const personaName = power _user . personas [ name ] ;
const personaDescription = power _user . persona _descriptions [ name ] ? . description ;
template . find ( '.ch_name' ) . text ( personaName || '[Unnamed Persona]' ) ;
template . find ( '.ch_description' ) . text ( personaDescription || $ ( '#user_avatar_block' ) . attr ( 'no_desc_text' ) ) . toggleClass ( 'text_muted' , ! personaDescription ) ;
template . attr ( 'imgfile' , name ) ;
template . find ( '.avatar' ) . attr ( 'imgfile' , name ) . attr ( 'title' , name ) ;
template . toggleClass ( 'default_persona' , name === power _user . default _persona ) ;
let avatarUrl = getUserAvatar ( name ) ;
if ( isFirefox ) {
avatarUrl += '?t=' + Date . now ( ) ;
}
template . find ( 'img' ) . attr ( 'src' , avatarUrl ) ;
$ ( '#user_avatar_block' ) . append ( template ) ;
return template ;
}
/ * *
* Gets a list of user avatars .
* @ param { boolean } doRender Whether to render the list
* @ param { string } openPageAt Item to be opened at
* @ returns { Promise < string [ ] > } List of avatar file names
* /
export async function getUserAvatars ( doRender = true , openPageAt = '' ) {
const response = await fetch ( '/api/avatars/get' , {
method : 'POST' ,
headers : getRequestHeaders ( ) ,
} ) ;
if ( response . ok ) {
const allEntities = await response . json ( ) ;
if ( ! Array . isArray ( allEntities ) ) {
return [ ] ;
}
if ( ! doRender ) {
return allEntities ;
}
// Before printing the personas, we check if we should enable/disable search sorting
verifyPersonaSearchSortRule ( ) ;
let entities = personasFilter . applyFilters ( allEntities ) ;
entities = sortPersonas ( entities ) ;
const storageKey = 'Personas_PerPage' ;
const listId = '#user_avatar_block' ;
const perPage = Number ( localStorage . getItem ( storageKey ) ) || 5 ;
$ ( '#persona_pagination_container' ) . pagination ( {
dataSource : entities ,
pageSize : perPage ,
sizeChangerOptions : [ 5 , 10 , 25 , 50 , 100 , 250 , 500 , 1000 ] ,
pageRange : 1 ,
pageNumber : savePersonasPage || 1 ,
position : 'top' ,
showPageNumbers : false ,
showSizeChanger : true ,
prevText : '<' ,
nextText : '>' ,
formatNavigator : PAGINATION _TEMPLATE ,
showNavigator : true ,
callback : function ( data ) {
$ ( listId ) . empty ( ) ;
for ( const item of data ) {
$ ( listId ) . append ( getUserAvatarBlock ( item ) ) ;
}
highlightSelectedAvatar ( ) ;
} ,
afterSizeSelectorChange : function ( e ) {
localStorage . setItem ( storageKey , e . target . value ) ;
} ,
afterPaging : function ( e ) {
savePersonasPage = e ;
} ,
afterRender : function ( ) {
$ ( listId ) . scrollTop ( 0 ) ;
} ,
} ) ;
if ( openPageAt ) {
const avatarIndex = entities . indexOf ( openPageAt ) ;
const page = Math . floor ( avatarIndex / perPage ) + 1 ;
if ( avatarIndex !== - 1 ) {
$ ( '#persona_pagination_container' ) . pagination ( 'go' , page ) ;
}
}
return allEntities ;
}
}
2023-08-29 00:54:11 +02:00
/ * *
* Uploads an avatar file to the server
* @ param { string } url URL for the avatar file
* @ param { string } [ name ] Optional name for the avatar file
* @ returns { Promise } Promise object representing the AJAX request
* /
async function uploadUserAvatar ( url , name ) {
const fetchResult = await fetch ( url ) ;
const blob = await fetchResult . blob ( ) ;
2023-12-02 19:04:51 +01:00
const file = new File ( [ blob ] , 'avatar.png' , { type : 'image/png' } ) ;
2023-08-29 00:54:11 +02:00
const formData = new FormData ( ) ;
2023-12-02 19:04:51 +01:00
formData . append ( 'avatar' , file ) ;
2023-08-29 00:54:11 +02:00
if ( name ) {
2023-12-02 19:04:51 +01:00
formData . append ( 'overwrite_name' , name ) ;
2023-08-29 00:54:11 +02:00
}
return jQuery . ajax ( {
2023-12-02 19:04:51 +01:00
type : 'POST' ,
2024-03-19 23:39:48 +01:00
url : '/api/avatars/upload' ,
2023-08-29 00:54:11 +02:00
data : formData ,
beforeSend : ( ) => { } ,
cache : false ,
contentType : false ,
processData : false ,
success : async function ( ) {
2024-01-31 10:01:50 +01:00
await getUserAvatars ( true , name ) ;
2023-08-29 00:54:11 +02:00
} ,
} ) ;
}
2024-05-01 13:03:24 +02:00
async function changeUserAvatar ( e ) {
const form = document . getElementById ( 'form_upload_avatar' ) ;
if ( ! ( form instanceof HTMLFormElement ) ) {
console . error ( 'Form not found' ) ;
return ;
}
const file = e . target . files [ 0 ] ;
if ( ! file ) {
form . reset ( ) ;
return ;
}
const formData = new FormData ( form ) ;
const dataUrl = await getBase64Async ( file ) ;
let url = '/api/avatars/upload' ;
if ( ! power _user . never _resize _avatars ) {
2024-06-25 01:18:10 +02:00
const dlg = new Popup ( 'Set the crop position of the avatar image' , POPUP _TYPE . CROP , '' , { cropImage : dataUrl } ) ;
const result = await dlg . show ( ) ;
if ( ! result ) {
2024-05-01 13:03:24 +02:00
return ;
}
2024-06-25 01:18:10 +02:00
if ( dlg . cropData !== undefined ) {
url += ` ?crop= ${ encodeURIComponent ( JSON . stringify ( dlg . cropData ) ) } ` ;
2024-05-01 13:03:24 +02:00
}
}
const rawFile = formData . get ( 'avatar' ) ;
if ( rawFile instanceof File ) {
const convertedFile = await ensureImageFormatSupported ( rawFile ) ;
formData . set ( 'avatar' , convertedFile ) ;
}
jQuery . ajax ( {
type : 'POST' ,
url : url ,
data : formData ,
beforeSend : ( ) => { } ,
cache : false ,
contentType : false ,
processData : false ,
success : async function ( data ) {
// If the user uploaded a new avatar, we want to make sure it's not cached
const name = formData . get ( 'overwrite_name' ) ;
if ( name ) {
await fetch ( getUserAvatar ( String ( name ) ) , { cache : 'no-cache' } ) ;
reloadUserAvatar ( true ) ;
}
if ( ! name && data . path ) {
await getUserAvatars ( ) ;
await delay ( 500 ) ;
await createPersona ( data . path ) ;
}
await getUserAvatars ( true , name || data . path ) ;
} ,
error : ( jqXHR , exception ) => { } ,
} ) ;
// Will allow to select the same file twice in a row
form . reset ( ) ;
}
2023-11-12 14:47:52 +01:00
/ * *
* Prompts the user to create a persona for the uploaded avatar .
* @ param { string } avatarId User avatar id
* @ returns { Promise } Promise that resolves when the persona is set
* /
export async function createPersona ( avatarId ) {
2024-07-25 22:57:00 +02:00
const personaName = await Popup . show . input ( 'Enter a name for this persona:' , 'Cancel if you\'re just uploading an avatar.' , '' ) ;
2023-11-12 14:47:52 +01:00
if ( ! personaName ) {
console . debug ( 'User cancelled creating a persona' ) ;
return ;
}
2024-07-25 22:57:00 +02:00
const personaDescription = await Popup . show . input ( 'Enter a description for this persona:' , 'You can always add or change it later.' , '' , { rows : 4 } ) ;
2023-11-12 14:47:52 +01:00
initPersona ( avatarId , personaName , personaDescription ) ;
if ( power _user . persona _show _notifications ) {
toastr . success ( ` You can now pick ${ personaName } as a persona in the Persona Management menu. ` , 'Persona Created' ) ;
}
}
2023-08-29 00:54:11 +02:00
async function createDummyPersona ( ) {
2024-07-25 22:57:00 +02:00
const personaName = await Popup . show . input ( 'Enter a name for this persona:' , null ) ;
2023-11-11 15:39:54 +01:00
if ( ! personaName ) {
console . debug ( 'User cancelled creating dummy persona' ) ;
return ;
}
// Date + name (only ASCII) to make it unique
const avatarId = ` ${ Date . now ( ) } - ${ personaName . replace ( /[^a-zA-Z0-9]/g , '' ) } .png ` ;
2023-11-12 14:47:52 +01:00
initPersona ( avatarId , personaName , '' ) ;
2024-09-16 21:08:40 +02:00
await uploadUserAvatar ( default _user _avatar , avatarId ) ;
2023-11-12 14:47:52 +01:00
}
/ * *
* Initializes a persona for the given avatar id .
* @ param { string } avatarId User avatar id
* @ param { string } personaName Name for the persona
* @ param { string } personaDescription Optional description for the persona
* @ returns { void }
* /
export function initPersona ( avatarId , personaName , personaDescription ) {
2023-11-11 15:39:54 +01:00
power _user . personas [ avatarId ] = personaName ;
power _user . persona _descriptions [ avatarId ] = {
2023-11-12 14:47:52 +01:00
description : personaDescription || '' ,
2023-11-11 15:39:54 +01:00
position : persona _description _positions . IN _PROMPT ,
2024-05-21 00:57:04 +02:00
depth : DEFAULT _DEPTH ,
role : DEFAULT _ROLE ,
2023-11-11 15:39:54 +01:00
} ;
saveSettingsDebounced ( ) ;
2023-08-29 00:54:11 +02:00
}
2023-10-21 15:12:09 +02:00
export async function convertCharacterToPersona ( characterId = null ) {
if ( null === characterId ) characterId = this _chid ;
const avatarUrl = characters [ characterId ] ? . avatar ;
2023-08-29 00:54:11 +02:00
if ( ! avatarUrl ) {
2023-12-02 19:04:51 +01:00
console . log ( 'No avatar found for this character' ) ;
2023-08-29 00:54:11 +02:00
return ;
}
2023-10-21 15:12:09 +02:00
const name = characters [ characterId ] ? . name ;
let description = characters [ characterId ] ? . description ;
2023-08-29 00:54:11 +02:00
const overwriteName = ` ${ name } (Persona).png ` ;
if ( overwriteName in power _user . personas ) {
2024-07-14 03:58:27 +02:00
const confirm = await Popup . show . confirm ( 'Overwrite Existing Persona' , 'This character exists as a persona already. Do you want to overwrite it?' ) ;
if ( ! confirm ) {
2023-12-02 19:04:51 +01:00
console . log ( 'User cancelled the overwrite of the persona' ) ;
2023-08-29 00:54:11 +02:00
return ;
}
}
if ( description . includes ( '{{char}}' ) || description . includes ( '{{user}}' ) ) {
2024-07-14 03:58:27 +02:00
const confirm = await Popup . show . confirm ( 'Persona Description Macros' , 'This character has a description that uses <code>{{char}}</code> or <code>{{user}}</code> macros. Do you want to swap them in the persona description?' ) ;
if ( confirm ) {
2023-08-29 00:54:11 +02:00
description = description . replace ( /{{char}}/gi , '{{personaChar}}' ) . replace ( /{{user}}/gi , '{{personaUser}}' ) ;
description = description . replace ( /{{personaUser}}/gi , '{{char}}' ) . replace ( /{{personaChar}}/gi , '{{user}}' ) ;
}
}
const thumbnailAvatar = getThumbnailUrl ( 'avatar' , avatarUrl ) ;
await uploadUserAvatar ( thumbnailAvatar , overwriteName ) ;
power _user . personas [ overwriteName ] = name ;
power _user . persona _descriptions [ overwriteName ] = {
description : description ,
position : persona _description _positions . IN _PROMPT ,
2024-05-21 00:57:04 +02:00
depth : DEFAULT _DEPTH ,
role : DEFAULT _ROLE ,
2023-08-29 00:54:11 +02:00
} ;
// If the user is currently using this persona, update the description
if ( user _avatar === overwriteName ) {
power _user . persona _description = description ;
}
saveSettingsDebounced ( ) ;
2023-12-02 19:04:51 +01:00
console . log ( 'Persona for character created' ) ;
2023-08-29 00:54:11 +02:00
toastr . success ( ` You can now select ${ name } as a persona in the Persona Management menu. ` , 'Persona Created' ) ;
// Refresh the persona selector
2024-01-31 10:01:50 +01:00
await getUserAvatars ( true , overwriteName ) ;
2023-08-29 00:54:11 +02:00
// Reload the persona description
setPersonaDescription ( ) ;
}
/ * *
* Counts the number of tokens in a persona description .
* /
2024-04-13 20:33:19 +02:00
const countPersonaDescriptionTokens = debounce ( async ( ) => {
2023-12-02 19:04:51 +01:00
const description = String ( $ ( '#persona_description' ) . val ( ) ) ;
2024-04-13 20:33:19 +02:00
const count = await getTokenCountAsync ( description ) ;
2023-12-02 19:04:51 +01:00
$ ( '#persona_description_token_count' ) . text ( String ( count ) ) ;
2024-04-28 06:21:47 +02:00
} , debounce _timeout . relaxed ) ;
2023-08-29 00:54:11 +02:00
export function setPersonaDescription ( ) {
if ( power _user . persona _description _position === persona _description _positions . AFTER _CHAR ) {
power _user . persona _description _position = persona _description _positions . IN _PROMPT ;
}
2024-05-21 00:57:04 +02:00
$ ( '#persona_depth_position_settings' ) . toggle ( power _user . persona _description _position === persona _description _positions . AT _DEPTH ) ;
2023-12-02 19:04:51 +01:00
$ ( '#persona_description' ) . val ( power _user . persona _description ) ;
2024-05-21 00:57:04 +02:00
$ ( '#persona_depth_value' ) . val ( power _user . persona _description _depth ? ? DEFAULT _DEPTH ) ;
2023-12-02 19:04:51 +01:00
$ ( '#persona_description_position' )
2023-08-29 00:54:11 +02:00
. val ( power _user . persona _description _position )
2024-05-21 00:57:04 +02:00
. find ( ` option[value=" ${ power _user . persona _description _position } "] ` )
2023-12-02 19:04:51 +01:00
. attr ( 'selected' , String ( true ) ) ;
2024-05-21 00:57:04 +02:00
$ ( '#persona_depth_role' )
. val ( power _user . persona _description _role )
. find ( ` option[value=" ${ power _user . persona _description _role } "] ` )
. prop ( 'selected' , String ( true ) ) ;
2023-08-29 00:54:11 +02:00
countPersonaDescriptionTokens ( ) ;
}
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 } ` ) ;
2024-01-30 18:12:56 +01:00
setUserAvatar ( key ) ;
2023-08-29 00:54:11 +02:00
return ;
}
}
}
2023-12-21 13:56:32 +01:00
/ * *
* Updates the name of a persona if it exists .
* @ param { string } avatarId User avatar id
* @ param { string } newName New name for the persona
* /
2024-05-01 13:03:24 +02:00
async function updatePersonaNameIfExists ( avatarId , newName ) {
2023-12-21 13:56:32 +01:00
if ( avatarId in power _user . personas ) {
power _user . personas [ avatarId ] = newName ;
console . log ( ` Updated persona name for ${ avatarId } to ${ newName } ` ) ;
} else {
2024-05-20 23:40:12 +02:00
power _user . personas [ avatarId ] = newName ;
power _user . persona _descriptions [ avatarId ] = {
description : '' ,
position : persona _description _positions . IN _PROMPT ,
2024-05-21 00:57:04 +02:00
depth : DEFAULT _DEPTH ,
role : DEFAULT _ROLE ,
2024-05-20 23:40:12 +02:00
} ;
console . log ( ` Created persona name for ${ avatarId } as ${ newName } ` ) ;
2023-12-21 13:56:32 +01:00
}
2024-05-20 23:40:12 +02:00
await getUserAvatars ( true , avatarId ) ;
saveSettingsDebounced ( ) ;
2023-12-21 13:56:32 +01:00
}
2024-01-30 19:16:48 +01:00
async function bindUserNameToPersona ( e ) {
e ? . stopPropagation ( ) ;
2023-08-29 00:54:11 +02:00
const avatarId = $ ( this ) . closest ( '.avatar-container' ) . find ( '.avatar' ) . attr ( 'imgfile' ) ;
if ( ! avatarId ) {
console . warn ( 'No avatar id found' ) ;
return ;
}
2024-07-25 22:57:00 +02:00
let personaUnbind = false ;
2023-08-29 00:54:11 +02:00
const existingPersona = power _user . personas [ avatarId ] ;
2024-07-25 22:57:00 +02:00
const personaName = await Popup . show . input (
'Enter a name for this persona:' ,
'(If empty name is provided, this will unbind the name from this avatar)' ,
existingPersona || '' ,
{ onClose : ( p ) => { personaUnbind = p . value === '' && p . result === POPUP _RESULT . AFFIRMATIVE ; } } ) ;
2023-08-29 00:54:11 +02:00
// If the user clicked cancel, don't do anything
2024-07-25 22:57:00 +02:00
if ( personaName === null && ! personaUnbind ) {
2023-08-29 00:54:11 +02:00
return ;
}
2024-07-25 22:57:00 +02:00
if ( personaName && personaName . length > 0 ) {
2023-08-29 00:54:11 +02:00
// 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 ;
const descriptor = power _user . persona _descriptions [ avatarId ] ;
const isCurrentPersona = avatarId === user _avatar ;
// Create a description object if it doesn't exist
if ( ! descriptor ) {
// If the user is currently using this persona, set the description to the current description
power _user . persona _descriptions [ avatarId ] = {
description : isCurrentPersona ? power _user . persona _description : '' ,
position : isCurrentPersona ? power _user . persona _description _position : persona _description _positions . IN _PROMPT ,
2024-05-21 00:57:04 +02:00
depth : isCurrentPersona ? power _user . persona _description _depth : DEFAULT _DEPTH ,
role : isCurrentPersona ? power _user . persona _description _role : DEFAULT _ROLE ,
2023-08-29 00:54:11 +02:00
} ;
}
// If the user is currently using this persona, update the name
if ( isCurrentPersona ) {
console . log ( ` Auto-updating user name to ${ personaName } ` ) ;
setUserName ( 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 ] ;
delete power _user . persona _descriptions [ avatarId ] ;
}
saveSettingsDebounced ( ) ;
2024-01-31 10:01:50 +01:00
await getUserAvatars ( true , avatarId ) ;
2023-08-29 00:54:11 +02:00
setPersonaDescription ( ) ;
}
2024-05-01 13:03:24 +02:00
function selectCurrentPersona ( ) {
2023-08-29 00:54:11 +02:00
const personaName = power _user . personas [ user _avatar ] ;
2023-10-23 22:20:49 +02:00
if ( personaName ) {
2023-08-29 00:54:11 +02:00
const lockedPersona = chat _metadata [ 'persona' ] ;
if ( lockedPersona && lockedPersona !== user _avatar && power _user . persona _show _notifications ) {
toastr . info (
` To permanently set " ${ personaName } " as the selected persona, unlock and relock it using the "Lock" button. Otherwise, the selection resets upon reloading the chat. ` ,
` This chat is locked to a different persona ( ${ power _user . personas [ lockedPersona ] } ). ` ,
2023-12-02 21:06:57 +01:00
{ timeOut : 10000 , extendedTimeOut : 20000 , preventDuplicates : true } ,
2023-08-29 00:54:11 +02:00
) ;
}
2023-10-23 22:20:49 +02:00
if ( personaName !== name1 ) {
console . log ( ` Auto-updating user name to ${ personaName } ` ) ;
setUserName ( personaName ) ;
}
2023-08-29 00:54:11 +02:00
const descriptor = power _user . persona _descriptions [ user _avatar ] ;
if ( descriptor ) {
2024-05-21 00:57:04 +02:00
power _user . persona _description = descriptor . description ? ? '' ;
power _user . persona _description _position = descriptor . position ? ? persona _description _positions . IN _PROMPT ;
power _user . persona _description _depth = descriptor . depth ? ? DEFAULT _DEPTH ;
power _user . persona _description _role = descriptor . role ? ? DEFAULT _ROLE ;
2023-08-29 00:54:11 +02:00
} else {
power _user . persona _description = '' ;
power _user . persona _description _position = persona _description _positions . IN _PROMPT ;
2024-05-21 00:57:04 +02:00
power _user . persona _description _depth = DEFAULT _DEPTH ;
power _user . persona _description _role = DEFAULT _ROLE ;
power _user . persona _descriptions [ user _avatar ] = { description : '' , position : persona _description _positions . IN _PROMPT , depth : DEFAULT _DEPTH , role : DEFAULT _ROLE } ;
2023-08-29 00:54:11 +02:00
}
setPersonaDescription ( ) ;
}
}
2024-06-26 19:18:05 +02:00
/ * *
* Checks if the persona is locked for the current chat .
* @ returns { boolean } Whether the persona is locked
* /
function isPersonaLocked ( ) {
return ! ! chat _metadata [ 'persona' ] ;
}
/ * *
* Locks or unlocks the persona for the current chat .
* @ param { boolean } state Desired lock state
* @ returns { Promise < void > }
* /
export async function setPersonaLockState ( state ) {
return state ? await lockPersona ( ) : await unlockPersona ( ) ;
}
/ * *
* Toggle the persona lock state for the current chat .
* @ returns { Promise < void > }
* /
export async function togglePersonaLock ( ) {
return isPersonaLocked ( )
? await unlockPersona ( )
: await lockPersona ( ) ;
}
/ * *
* Unlock the persona for the current chat .
* /
async function unlockPersona ( ) {
2023-08-29 00:54:11 +02:00
if ( chat _metadata [ 'persona' ] ) {
console . log ( ` Unlocking persona for this chat ${ chat _metadata [ 'persona' ] } ` ) ;
delete chat _metadata [ 'persona' ] ;
await saveMetadata ( ) ;
if ( power _user . persona _show _notifications ) {
toastr . info ( 'User persona is now unlocked for this chat. Click the "Lock" again to revert.' , 'Persona unlocked' ) ;
}
updateUserLockIcon ( ) ;
}
2024-06-26 19:18:05 +02:00
}
2023-08-29 00:54:11 +02:00
2024-06-26 19:18:05 +02:00
/ * *
* Lock the persona for the current chat .
* /
async function lockPersona ( ) {
2023-08-29 00:54:11 +02:00
if ( ! ( user _avatar in power _user . personas ) ) {
console . log ( ` Creating a new persona ${ user _avatar } ` ) ;
if ( power _user . persona _show _notifications ) {
toastr . info (
'Creating a new persona for currently selected user name and avatar...' ,
'Persona not set for this avatar' ,
2023-12-02 21:06:57 +01:00
{ timeOut : 10000 , extendedTimeOut : 20000 } ,
2023-08-29 00:54:11 +02:00
) ;
}
power _user . personas [ user _avatar ] = name1 ;
2024-07-26 18:45:45 +02:00
power _user . persona _descriptions [ user _avatar ] = {
description : '' ,
position : persona _description _positions . IN _PROMPT ,
depth : DEFAULT _DEPTH ,
role : DEFAULT _ROLE ,
} ;
2023-08-29 00:54:11 +02:00
}
chat _metadata [ 'persona' ] = user _avatar ;
await saveMetadata ( ) ;
saveSettingsDebounced ( ) ;
console . log ( ` Locking persona for this chat ${ user _avatar } ` ) ;
if ( power _user . persona _show _notifications ) {
toastr . success ( ` User persona is locked to ${ name1 } in this chat ` ) ;
}
updateUserLockIcon ( ) ;
}
2024-06-26 19:18:05 +02:00
2024-01-30 19:16:48 +01:00
async function deleteUserAvatar ( e ) {
e ? . stopPropagation ( ) ;
2023-08-29 00:54:11 +02:00
const avatarId = $ ( this ) . closest ( '.avatar-container' ) . find ( '.avatar' ) . attr ( 'imgfile' ) ;
if ( ! avatarId ) {
console . warn ( 'No avatar id found' ) ;
return ;
}
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 ;
}
2024-07-25 22:57:00 +02:00
const confirm = await Popup . show . confirm ( 'Are you sure you want to delete this avatar?' , 'All information associated with its linked persona will be lost.' ) ;
2023-08-29 00:54:11 +02:00
if ( ! confirm ) {
console . debug ( 'User cancelled deleting avatar' ) ;
return ;
}
2024-03-19 23:39:48 +01:00
const request = await fetch ( '/api/avatars/delete' , {
2023-12-02 19:04:51 +01:00
method : 'POST' ,
2023-08-29 00:54:11 +02:00
headers : getRequestHeaders ( ) ,
body : JSON . stringify ( {
2023-12-02 19:04:51 +01:00
'avatar' : avatarId ,
2023-08-29 00:54:11 +02:00
} ) ,
} ) ;
if ( request . ok ) {
console . log ( ` Deleted avatar ${ avatarId } ` ) ;
delete power _user . personas [ avatarId ] ;
delete power _user . persona _descriptions [ 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 ;
}
if ( avatarId === chat _metadata [ 'persona' ] ) {
toastr . warning ( 'The locked persona was deleted. You will need to set a new persona for this chat.' , 'Persona deleted' ) ;
delete chat _metadata [ 'persona' ] ;
await saveMetadata ( ) ;
}
saveSettingsDebounced ( ) ;
await getUserAvatars ( ) ;
updateUserLockIcon ( ) ;
}
}
function onPersonaDescriptionInput ( ) {
2023-12-02 19:04:51 +01:00
power _user . persona _description = String ( $ ( '#persona_description' ) . val ( ) ) ;
2023-08-29 00:54:11 +02:00
countPersonaDescriptionTokens ( ) ;
if ( power _user . personas [ user _avatar ] ) {
let object = power _user . persona _descriptions [ user _avatar ] ;
if ( ! object ) {
object = {
description : power _user . persona _description ,
2023-12-02 19:04:51 +01:00
position : Number ( $ ( '#persona_description_position' ) . find ( ':selected' ) . val ( ) ) ,
2024-05-21 00:57:04 +02:00
depth : Number ( $ ( '#persona_depth_value' ) . val ( ) ) ,
role : Number ( $ ( '#persona_depth_role' ) . find ( ':selected' ) . val ( ) ) ,
2023-08-29 00:54:11 +02:00
} ;
power _user . persona _descriptions [ user _avatar ] = object ;
}
object . description = power _user . persona _description ;
}
2024-01-30 18:12:56 +01:00
$ ( ` .avatar-container[imgfile=" ${ user _avatar } "] .ch_description ` )
2024-04-11 09:55:38 +02:00
. text ( power _user . persona _description || $ ( '#user_avatar_block' ) . attr ( 'no_desc_text' ) )
2024-01-30 18:12:56 +01:00
. toggleClass ( 'text_muted' , ! power _user . persona _description ) ;
2023-08-29 00:54:11 +02:00
saveSettingsDebounced ( ) ;
}
2024-05-21 00:57:04 +02:00
function onPersonaDescriptionDepthValueInput ( ) {
power _user . persona _description _depth = Number ( $ ( '#persona_depth_value' ) . val ( ) ) ;
if ( power _user . personas [ user _avatar ] ) {
const object = getOrCreatePersonaDescriptor ( ) ;
object . depth = power _user . persona _description _depth ;
}
saveSettingsDebounced ( ) ;
}
function onPersonaDescriptionDepthRoleInput ( ) {
power _user . persona _description _role = Number ( $ ( '#persona_depth_role' ) . find ( ':selected' ) . val ( ) ) ;
if ( power _user . personas [ user _avatar ] ) {
const object = getOrCreatePersonaDescriptor ( ) ;
object . role = power _user . persona _description _role ;
}
saveSettingsDebounced ( ) ;
}
2023-08-29 00:54:11 +02:00
function onPersonaDescriptionPositionInput ( ) {
power _user . persona _description _position = Number (
2023-12-02 21:06:57 +01:00
$ ( '#persona_description_position' ) . find ( ':selected' ) . val ( ) ,
2023-08-29 00:54:11 +02:00
) ;
if ( power _user . personas [ user _avatar ] ) {
2024-05-21 00:57:04 +02:00
const object = getOrCreatePersonaDescriptor ( ) ;
2023-08-29 00:54:11 +02:00
object . position = power _user . persona _description _position ;
}
saveSettingsDebounced ( ) ;
2024-05-21 00:57:04 +02:00
$ ( '#persona_depth_position_settings' ) . toggle ( power _user . persona _description _position === persona _description _positions . AT _DEPTH ) ;
}
function getOrCreatePersonaDescriptor ( ) {
let object = power _user . persona _descriptions [ user _avatar ] ;
if ( ! object ) {
object = {
description : power _user . persona _description ,
position : power _user . persona _description _position ,
depth : power _user . persona _description _depth ,
role : power _user . persona _description _role ,
} ;
power _user . persona _descriptions [ user _avatar ] = object ;
}
return object ;
2023-08-29 00:54:11 +02:00
}
2024-01-30 19:16:48 +01:00
async function setDefaultPersona ( e ) {
e ? . stopPropagation ( ) ;
2023-08-29 00:54:11 +02:00
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 ) {
2024-07-25 22:57:00 +02:00
const confirm = await Popup . show . confirm ( 'Are you sure you want to remove the default persona?' , personaName ) ;
2023-08-29 00:54:11 +02:00
if ( ! confirm ) {
console . debug ( 'User cancelled removing default persona' ) ;
return ;
}
console . log ( ` Removing default persona ${ avatarId } ` ) ;
if ( power _user . persona _show _notifications ) {
2023-12-02 19:04:51 +01:00
toastr . info ( 'This persona will no longer be used by default when you open a new chat.' , 'Default persona removed' ) ;
2023-08-29 00:54:11 +02:00
}
delete power _user . default _persona ;
} else {
2024-07-25 22:57:00 +02:00
const confirm = await Popup . show . confirm ( ` 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.' ) ;
2023-08-29 00:54:11 +02:00
if ( ! confirm ) {
console . debug ( 'User cancelled setting default persona' ) ;
return ;
}
power _user . default _persona = avatarId ;
if ( power _user . persona _show _notifications ) {
toastr . success ( 'This persona will be used by default when you open a new chat.' , ` Default persona set to ${ personaName } ` ) ;
}
}
saveSettingsDebounced ( ) ;
2024-01-31 10:01:50 +01:00
await getUserAvatars ( true , avatarId ) ;
2023-08-29 00:54:11 +02:00
}
function updateUserLockIcon ( ) {
const hasLock = ! ! chat _metadata [ 'persona' ] ;
$ ( '#lock_user_name' ) . toggleClass ( 'fa-unlock' , ! hasLock ) ;
$ ( '#lock_user_name' ) . toggleClass ( 'fa-lock' , hasLock ) ;
}
2024-01-30 18:12:56 +01:00
async function setChatLockedPersona ( ) {
2023-08-29 00:54:11 +02:00
// Define a persona for this chat
let chatPersona = '' ;
if ( chat _metadata [ 'persona' ] ) {
// 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
2024-01-30 18:12:56 +01:00
const userAvatars = await getUserAvatars ( false ) ;
2023-08-29 00:54:11 +02:00
// Avatar missing (persona deleted)
2024-01-30 18:12:56 +01:00
if ( chat _metadata [ 'persona' ] && ! userAvatars . includes ( chatPersona ) ) {
2023-08-29 00:54:11 +02:00
console . warn ( 'Persona avatar not found, unlocking persona' ) ;
delete chat _metadata [ 'persona' ] ;
updateUserLockIcon ( ) ;
return ;
}
// Default persona missing
2024-01-30 18:12:56 +01:00
if ( power _user . default _persona && ! userAvatars . includes ( power _user . default _persona ) ) {
2023-08-29 00:54:11 +02:00
console . warn ( 'Default persona avatar not found, clearing default persona' ) ;
power _user . default _persona = null ;
saveSettingsDebounced ( ) ;
return ;
}
// Persona avatar found, select it
2024-01-30 18:12:56 +01:00
setUserAvatar ( chatPersona ) ;
2023-08-29 00:54:11 +02:00
updateUserLockIcon ( ) ;
}
2023-11-15 01:09:40 +01:00
function onBackupPersonas ( ) {
const timestamp = new Date ( ) . toISOString ( ) . split ( 'T' ) [ 0 ] . replace ( /-/g , '' ) ;
const filename = ` personas_ ${ timestamp } .json ` ;
const data = JSON . stringify ( {
2023-12-02 19:04:51 +01:00
'personas' : power _user . personas ,
'persona_descriptions' : power _user . persona _descriptions ,
'default_persona' : power _user . default _persona ,
2023-11-15 01:09:40 +01:00
} , null , 2 ) ;
const blob = new Blob ( [ data ] , { type : 'application/json' } ) ;
download ( blob , filename , 'application/json' ) ;
}
async function onPersonasRestoreInput ( e ) {
const file = e . target . files [ 0 ] ;
if ( ! file ) {
console . debug ( 'No file selected' ) ;
return ;
}
const data = await parseJsonFile ( file ) ;
if ( ! data ) {
toastr . warning ( 'Invalid file selected' , 'Persona Management' ) ;
console . debug ( 'Invalid file selected' ) ;
return ;
}
if ( ! data . personas || ! data . persona _descriptions || typeof data . personas !== 'object' || typeof data . persona _descriptions !== 'object' ) {
toastr . warning ( 'Invalid file format' , 'Persona Management' ) ;
console . debug ( 'Invalid file selected' ) ;
return ;
}
2024-01-30 18:12:56 +01:00
const avatarsList = await getUserAvatars ( false ) ;
2023-11-15 01:09:40 +01:00
const warnings = [ ] ;
// Merge personas with existing ones
for ( const [ key , value ] of Object . entries ( data . personas ) ) {
if ( key in power _user . personas ) {
warnings . push ( ` Persona " ${ key } " ( ${ value } ) already exists, skipping ` ) ;
continue ;
}
power _user . personas [ key ] = value ;
// If the avatar is missing, upload it
if ( ! avatarsList . includes ( key ) ) {
warnings . push ( ` Persona image " ${ key } " ( ${ value } ) is missing, uploading default avatar ` ) ;
2024-09-16 21:08:40 +02:00
await uploadUserAvatar ( default _user _avatar , key ) ;
2023-11-15 01:09:40 +01:00
}
}
// Merge persona descriptions with existing ones
for ( const [ key , value ] of Object . entries ( data . persona _descriptions ) ) {
if ( key in power _user . persona _descriptions ) {
warnings . push ( ` Persona description for " ${ key } " ( ${ power _user . personas [ key ] } ) already exists, skipping ` ) ;
continue ;
}
if ( ! power _user . personas [ key ] ) {
warnings . push ( ` Persona for " ${ key } " does not exist, skipping ` ) ;
continue ;
}
power _user . persona _descriptions [ key ] = value ;
}
if ( data . default _persona ) {
if ( data . default _persona in power _user . personas ) {
power _user . default _persona = data . default _persona ;
} else {
warnings . push ( ` Default persona " ${ data . default _persona } " does not exist, skipping ` ) ;
}
}
if ( warnings . length ) {
toastr . success ( 'Personas restored with warnings. Check console for details.' ) ;
console . warn ( ` PERSONA RESTORE REPORT \n ==================== \n ${ warnings . join ( '\n' ) } ` ) ;
} else {
toastr . success ( 'Personas restored successfully.' ) ;
}
await getUserAvatars ( ) ;
setPersonaDescription ( ) ;
saveSettingsDebounced ( ) ;
$ ( '#personas_restore_input' ) . val ( '' ) ;
}
2024-05-01 13:03:24 +02:00
async function syncUserNameToPersona ( ) {
2024-07-25 22:57:00 +02:00
const confirmation = await Popup . show . confirm ( 'Are you sure?' , ` All user-sent messages in this chat will be attributed to ${ name1 } . ` ) ;
2024-05-01 13:03:24 +02:00
if ( ! confirmation ) {
return ;
}
for ( const mes of chat ) {
if ( mes . is _user ) {
mes . name = name1 ;
mes . force _avatar = getUserAvatar ( user _avatar ) ;
}
}
await saveChatConditional ( ) ;
await reloadCurrentChat ( ) ;
}
export function retriggerFirstMessageOnEmptyChat ( ) {
if ( this _chid >= 0 && ! selected _group && chat . length === 1 ) {
$ ( '#firstmessage_textarea' ) . trigger ( 'input' ) ;
}
}
2024-07-25 22:44:34 +02:00
/ * *
* Duplicates a persona .
* @ param { string } avatarId
* @ returns { Promise < void > }
* /
async function duplicatePersona ( avatarId ) {
const personaName = power _user . personas [ avatarId ] ;
if ( ! personaName ) {
2024-07-26 18:45:45 +02:00
toastr . warning ( 'Chosen avatar is not a persona' ) ;
2024-07-25 22:44:34 +02:00
return ;
}
const confirm = await Popup . show . confirm ( 'Are you sure you want to duplicate this persona?' , personaName ) ;
if ( ! confirm ) {
console . debug ( 'User cancelled duplicating persona' ) ;
return ;
}
const newAvatarId = ` ${ Date . now ( ) } - ${ personaName . replace ( /[^a-zA-Z0-9]/g , '' ) } .png ` ;
const descriptor = power _user . persona _descriptions [ avatarId ] ;
power _user . personas [ newAvatarId ] = personaName ;
power _user . persona _descriptions [ newAvatarId ] = {
2024-07-26 18:45:45 +02:00
description : descriptor ? . description ? ? '' ,
position : descriptor ? . position ? ? persona _description _positions . IN _PROMPT ,
depth : descriptor ? . depth ? ? DEFAULT _DEPTH ,
role : descriptor ? . role ? ? DEFAULT _ROLE ,
2024-07-25 22:44:34 +02:00
} ;
await uploadUserAvatar ( getUserAvatar ( avatarId ) , newAvatarId ) ;
await getUserAvatars ( true , newAvatarId ) ;
saveSettingsDebounced ( ) ;
}
2023-08-29 00:54:11 +02:00
export function initPersonas ( ) {
$ ( document ) . on ( 'click' , '.bind_user_name' , bindUserNameToPersona ) ;
$ ( document ) . on ( 'click' , '.set_default_persona' , setDefaultPersona ) ;
$ ( document ) . on ( 'click' , '.delete_avatar' , deleteUserAvatar ) ;
2024-06-26 19:18:05 +02:00
$ ( '#lock_user_name' ) . on ( 'click' , togglePersonaLock ) ;
2023-12-02 19:04:51 +01:00
$ ( '#create_dummy_persona' ) . on ( 'click' , createDummyPersona ) ;
2023-08-29 00:54:11 +02:00
$ ( '#persona_description' ) . on ( 'input' , onPersonaDescriptionInput ) ;
$ ( '#persona_description_position' ) . on ( 'input' , onPersonaDescriptionPositionInput ) ;
2024-05-21 00:57:04 +02:00
$ ( '#persona_depth_value' ) . on ( 'input' , onPersonaDescriptionDepthValueInput ) ;
$ ( '#persona_depth_role' ) . on ( 'input' , onPersonaDescriptionDepthRoleInput ) ;
2023-11-15 01:09:40 +01:00
$ ( '#personas_backup' ) . on ( 'click' , onBackupPersonas ) ;
$ ( '#personas_restore' ) . on ( 'click' , ( ) => $ ( '#personas_restore_input' ) . trigger ( 'click' ) ) ;
$ ( '#personas_restore_input' ) . on ( 'change' , onPersonasRestoreInput ) ;
2024-02-02 03:07:51 +01:00
$ ( '#persona_sort_order' ) . val ( power _user . persona _sort _order ) . on ( 'input' , function ( ) {
2024-04-30 02:27:44 +02:00
const value = String ( $ ( this ) . val ( ) ) ;
// Save sort order, but do not save search sorting, as this is a temporary sorting option
if ( value !== 'search' ) power _user . persona _sort _order = value ;
2024-02-02 03:07:51 +01:00
getUserAvatars ( true , user _avatar ) ;
saveSettingsDebounced ( ) ;
} ) ;
2024-01-31 11:13:01 +01:00
$ ( '#persona_grid_toggle' ) . on ( 'click' , ( ) => {
const state = localStorage . getItem ( GRID _STORAGE _KEY ) === 'true' ;
localStorage . setItem ( GRID _STORAGE _KEY , String ( ! state ) ) ;
switchPersonaGridView ( ) ;
} ) ;
2023-08-29 00:54:11 +02:00
2024-05-01 13:03:24 +02:00
const debouncedPersonaSearch = debounce ( ( searchQuery ) => {
personasFilter . setFilterData ( FILTER _TYPES . PERSONA _SEARCH , searchQuery ) ;
} ) ;
$ ( '#persona_search_bar' ) . on ( 'input' , function ( ) {
const searchQuery = String ( $ ( this ) . val ( ) ) ;
debouncedPersonaSearch ( searchQuery ) ;
} ) ;
$ ( '#sync_name_button' ) . on ( 'click' , syncUserNameToPersona ) ;
$ ( '#avatar_upload_file' ) . on ( 'change' , changeUserAvatar ) ;
$ ( document ) . on ( 'click' , '#user_avatar_block .avatar-container' , function ( ) {
const imgfile = $ ( this ) . attr ( 'imgfile' ) ;
setUserAvatar ( imgfile ) ;
// force firstMes {{user}} update on persona switch
retriggerFirstMessageOnEmptyChat ( ) ;
} ) ;
$ ( '#your_name_button' ) . click ( async function ( ) {
const userName = String ( $ ( '#your_name' ) . val ( ) ) . trim ( ) ;
setUserName ( userName ) ;
await updatePersonaNameIfExists ( user _avatar , userName ) ;
retriggerFirstMessageOnEmptyChat ( ) ;
} ) ;
$ ( document ) . on ( 'click' , '#user_avatar_block .avatar_upload' , function ( ) {
$ ( '#avatar_upload_overwrite' ) . val ( '' ) ;
$ ( '#avatar_upload_file' ) . trigger ( 'click' ) ;
} ) ;
2024-07-25 22:44:34 +02:00
$ ( document ) . on ( 'click' , '#user_avatar_block .duplicate_persona' , function ( e ) {
e . stopPropagation ( ) ;
const avatarId = $ ( this ) . closest ( '.avatar-container' ) . find ( '.avatar' ) . attr ( 'imgfile' ) ;
if ( ! avatarId ) {
console . log ( 'no imgfile' ) ;
return ;
}
duplicatePersona ( avatarId ) ;
} ) ;
2024-05-01 13:03:24 +02:00
$ ( document ) . on ( 'click' , '#user_avatar_block .set_persona_image' , function ( e ) {
e . stopPropagation ( ) ;
const avatarId = $ ( this ) . closest ( '.avatar-container' ) . find ( '.avatar' ) . attr ( 'imgfile' ) ;
if ( ! avatarId ) {
console . log ( 'no imgfile' ) ;
return ;
}
$ ( '#avatar_upload_overwrite' ) . val ( avatarId ) ;
$ ( '#avatar_upload_file' ) . trigger ( 'click' ) ;
} ) ;
2023-12-02 19:04:51 +01:00
eventSource . on ( 'charManagementDropdown' , ( target ) => {
2023-08-29 00:54:11 +02:00
if ( target === 'convert_to_persona' ) {
convertCharacterToPersona ( ) ;
}
} ) ;
eventSource . on ( event _types . CHAT _CHANGED , setChatLockedPersona ) ;
2024-01-31 11:13:01 +01:00
switchPersonaGridView ( ) ;
2023-08-29 00:54:11 +02:00
}