2023-07-20 19:32:15 +02:00
import {
characters ,
saveChat ,
system _messages ,
system _message _types ,
this _chid ,
openCharacterChat ,
chat _metadata ,
getRequestHeaders ,
getThumbnailUrl ,
getCharacters ,
chat ,
saveChatConditional ,
2024-05-24 23:07:36 +02:00
saveItemizedPrompts ,
2023-12-02 19:04:51 +01:00
} from '../script.js' ;
2024-07-24 23:46:19 +02:00
import { humanizedDateTime , getMessageTimeStamp } from './RossAscends-mods.js' ;
2023-07-20 19:32:15 +02:00
import {
getGroupPastChats ,
group _activation _strategy ,
groups ,
2023-08-19 20:44:15 +02:00
openGroupById ,
2023-07-20 19:32:15 +02:00
openGroupChat ,
saveGroupBookmarkChat ,
selected _group ,
2023-12-02 19:04:51 +01:00
} from './group-chats.js' ;
2024-09-08 00:34:03 +02:00
import { hideLoader , showLoader } from './loader.js' ;
2024-09-08 03:28:54 +02:00
import { getLastMessageId } from './macros.js' ;
2024-06-27 01:01:43 +02:00
import { Popup } from './popup.js' ;
2024-09-08 03:28:54 +02:00
import { SlashCommand } from './slash-commands/SlashCommand.js' ;
import { ARGUMENT _TYPE , SlashCommandArgument , SlashCommandNamedArgument } from './slash-commands/SlashCommandArgument.js' ;
import { commonEnumProviders } from './slash-commands/SlashCommandCommonEnumsProvider.js' ;
import { SlashCommandParser } from './slash-commands/SlashCommandParser.js' ;
2023-12-02 19:04:51 +01:00
import { createTagMapFromList } from './tags.js' ;
2024-09-10 21:06:31 +02:00
import { renderTemplateAsync } from './templates.js' ;
2023-07-20 19:32:15 +02:00
import {
getUniqueName ,
2024-09-13 19:44:06 +02:00
isTrueBoolean ,
2023-12-02 19:04:51 +01:00
} from './utils.js' ;
2023-07-20 19:32:15 +02:00
2023-12-03 02:11:14 +01:00
const bookmarkNameToken = 'Checkpoint #' ;
2023-07-20 19:32:15 +02:00
async function getExistingChatNames ( ) {
if ( selected _group ) {
const data = await getGroupPastChats ( selected _group ) ;
return data . map ( x => x . file _name ) ;
} else {
2023-12-04 12:51:45 +01:00
const response = await fetch ( '/api/characters/chats' , {
2023-07-20 19:32:15 +02:00
method : 'POST' ,
headers : getRequestHeaders ( ) ,
2023-12-02 21:06:57 +01:00
body : JSON . stringify ( { avatar _url : characters [ this _chid ] . avatar } ) ,
2023-07-20 19:32:15 +02:00
} ) ;
if ( response . ok ) {
const data = await response . json ( ) ;
return Object . values ( data ) . map ( x => x . file _name . replace ( '.jsonl' , '' ) ) ;
}
}
}
2024-09-10 21:19:35 +02:00
async function getBookmarkName ( { isReplace = false , forceName = null } = { } ) {
2023-07-20 19:32:15 +02:00
const chatNames = await getExistingChatNames ( ) ;
2024-09-10 21:19:35 +02:00
const body = await renderTemplateAsync ( 'createCheckpoint' , { isReplace : isReplace } ) ;
2024-09-12 23:34:37 +02:00
let name = forceName ? ? await Popup . show . input ( 'Create Checkpoint' , body ) ;
// Special handling for confirmed empty input (=> auto-generate name)
if ( name === '' ) {
2023-07-20 19:32:15 +02:00
for ( let i = chatNames . length ; i < 1000 ; i ++ ) {
name = bookmarkNameToken + i ;
if ( ! chatNames . includes ( name ) ) {
break ;
}
}
}
2024-09-12 23:34:37 +02:00
if ( ! name ) {
return null ;
}
2023-07-20 19:32:15 +02:00
return ` ${ name } - ${ humanizedDateTime ( ) } ` ;
}
function getMainChatName ( ) {
if ( chat _metadata ) {
if ( chat _metadata [ 'main_chat' ] ) {
return chat _metadata [ 'main_chat' ] ;
}
// groups didn't support bookmarks before chat metadata was introduced
else if ( selected _group ) {
return null ;
}
else if ( characters [ this _chid ] . chat && characters [ this _chid ] . chat . includes ( bookmarkNameToken ) ) {
const tokenIndex = characters [ this _chid ] . chat . lastIndexOf ( bookmarkNameToken ) ;
chat _metadata [ 'main_chat' ] = characters [ this _chid ] . chat . substring ( 0 , tokenIndex ) . trim ( ) ;
return chat _metadata [ 'main_chat' ] ;
}
}
return null ;
}
2024-09-08 00:41:14 +02:00
export function showBookmarksButtons ( ) {
2023-07-20 19:32:15 +02:00
try {
if ( selected _group ) {
2023-12-02 19:04:51 +01:00
$ ( '#option_convert_to_group' ) . hide ( ) ;
2023-07-20 19:32:15 +02:00
} else {
2023-12-02 19:04:51 +01:00
$ ( '#option_convert_to_group' ) . show ( ) ;
2023-07-20 19:32:15 +02:00
}
if ( chat _metadata [ 'main_chat' ] ) {
// In bookmark chat
2023-12-02 19:04:51 +01:00
$ ( '#option_back_to_main' ) . show ( ) ;
$ ( '#option_new_bookmark' ) . show ( ) ;
2023-07-20 19:32:15 +02:00
} else if ( ! selected _group && ! characters [ this _chid ] . chat ) {
// No chat recorded on character
2023-12-02 19:04:51 +01:00
$ ( '#option_back_to_main' ) . hide ( ) ;
$ ( '#option_new_bookmark' ) . hide ( ) ;
2023-07-20 19:32:15 +02:00
} else {
// In main chat
2023-12-02 19:04:51 +01:00
$ ( '#option_back_to_main' ) . hide ( ) ;
$ ( '#option_new_bookmark' ) . show ( ) ;
2023-07-20 19:32:15 +02:00
}
}
catch {
2023-12-02 19:04:51 +01:00
$ ( '#option_back_to_main' ) . hide ( ) ;
$ ( '#option_new_bookmark' ) . hide ( ) ;
$ ( '#option_convert_to_group' ) . hide ( ) ;
2023-07-20 19:32:15 +02:00
}
}
async function saveBookmarkMenu ( ) {
if ( ! chat . length ) {
2023-12-03 02:11:14 +01:00
toastr . warning ( 'The chat is empty.' , 'Checkpoint creation failed' ) ;
2023-07-20 19:32:15 +02:00
return ;
}
2024-09-08 00:41:14 +02:00
return await createNewBookmark ( chat . length - 1 ) ;
2023-07-20 19:32:15 +02:00
}
2024-09-08 13:33:52 +02:00
// Export is used by Timelines extension. Do not remove.
export async function createBranch ( mesId ) {
2023-09-21 04:48:05 +02:00
if ( ! chat . length ) {
2023-09-21 06:40:38 +02:00
toastr . warning ( 'The chat is empty.' , 'Branch creation failed' ) ;
2023-09-21 04:48:05 +02:00
return ;
}
if ( mesId < 0 || mesId >= chat . length ) {
2023-09-21 06:40:38 +02:00
toastr . warning ( 'Invalid message ID.' , 'Branch creation failed' ) ;
2023-09-21 04:48:05 +02:00
return ;
}
const lastMes = chat [ mesId ] ;
const mainChat = selected _group ? groups ? . find ( x => x . id == selected _group ) ? . chat _id : characters [ this _chid ] . chat ;
const newMetadata = { main _chat : mainChat } ;
2023-12-02 20:11:06 +01:00
let name = ` Branch # ${ mesId } - ${ humanizedDateTime ( ) } ` ;
2023-09-21 04:48:05 +02:00
if ( selected _group ) {
await saveGroupBookmarkChat ( selected _group , name , newMetadata , mesId ) ;
} else {
await saveChat ( name , newMetadata , mesId ) ;
}
// append to branches list if it exists
// otherwise create it
if ( typeof lastMes . extra !== 'object' ) {
lastMes . extra = { } ;
}
if ( typeof lastMes . extra [ 'branches' ] !== 'object' ) {
lastMes . extra [ 'branches' ] = [ ] ;
}
lastMes . extra [ 'branches' ] . push ( name ) ;
return name ;
}
2024-09-08 03:28:54 +02:00
/ * *
* Creates a new bookmark for a message .
*
* @ param { number } mesId - The ID of the message .
* @ param { Object } [ options = { } ] - Optional parameters .
* @ param { string ? } [ options . forceName = null ] - The name to force for the bookmark .
* @ returns { Promise < string ? > } - A promise that resolves to the bookmark name when the bookmark is created .
* /
export async function createNewBookmark ( mesId , { forceName = null } = { } ) {
2024-09-03 19:36:30 +02:00
if ( this _chid === undefined && ! selected _group ) {
2024-09-08 03:28:54 +02:00
toastr . info ( 'No character selected.' , 'Create Checkpoint' ) ;
return null ;
2024-09-03 19:36:30 +02:00
}
2023-07-20 19:32:15 +02:00
if ( ! chat . length ) {
2024-09-08 03:28:54 +02:00
toastr . warning ( 'The chat is empty.' , 'Create Checkpoint' ) ;
return null ;
2023-07-20 19:32:15 +02:00
}
2024-09-08 03:28:54 +02:00
if ( ! chat [ mesId ] ) {
toastr . warning ( 'Invalid message ID.' , 'Create Checkpoint' ) ;
return null ;
2023-07-20 19:32:15 +02:00
}
const lastMes = chat [ mesId ] ;
if ( typeof lastMes . extra !== 'object' ) {
lastMes . extra = { } ;
}
2024-09-10 21:19:35 +02:00
const isReplace = lastMes . extra . bookmark _link ;
2023-07-20 19:32:15 +02:00
2024-09-10 21:19:35 +02:00
let name = await getBookmarkName ( { isReplace : isReplace , forceName : forceName } ) ;
2023-07-20 19:32:15 +02:00
if ( ! name ) {
2024-09-08 03:28:54 +02:00
return null ;
2023-07-20 19:32:15 +02:00
}
const mainChat = selected _group ? groups ? . find ( x => x . id == selected _group ) ? . chat _id : characters [ this _chid ] . chat ;
const newMetadata = { main _chat : mainChat } ;
2024-05-24 23:07:36 +02:00
await saveItemizedPrompts ( name ) ;
2023-07-20 19:32:15 +02:00
if ( selected _group ) {
await saveGroupBookmarkChat ( selected _group , name , newMetadata , mesId ) ;
} else {
await saveChat ( name , newMetadata , mesId ) ;
}
lastMes . extra [ 'bookmark_link' ] = name ;
2024-09-08 00:20:56 +02:00
const mes = $ ( ` .mes[mesid=" ${ mesId } "] ` ) ;
2024-09-08 00:34:03 +02:00
updateBookmarkDisplay ( mes , name ) ;
2023-07-20 19:32:15 +02:00
await saveChatConditional ( ) ;
2024-09-08 03:28:54 +02:00
toastr . success ( 'Click the flag icon next to the message to open the checkpoint chat.' , 'Create Checkpoint' , { timeOut : 10000 } ) ;
return name ;
2023-07-20 19:32:15 +02:00
}
2024-09-08 00:34:03 +02:00
/ * *
* Updates the display of the bookmark on a chat message .
* @ param { JQuery < HTMLElement > } mes - The message element
* @ param { string ? } [ newBookmarkLink = null ] - The new bookmark link ( optional )
* /
export function updateBookmarkDisplay ( mes , newBookmarkLink = null ) {
newBookmarkLink && mes . attr ( 'bookmark_link' , newBookmarkLink ) ;
2024-09-10 21:06:31 +02:00
const bookmarkFlag = mes . find ( '.mes_bookmark' ) ;
bookmarkFlag . attr ( 'title' , ` Checkpoint \n ${ mes . attr ( 'bookmark_link' ) } \n \n ${ bookmarkFlag . data ( 'tooltip' ) } ` ) ;
2024-09-08 00:34:03 +02:00
}
2023-07-20 19:32:15 +02:00
async function backToMainChat ( ) {
const mainChatName = getMainChatName ( ) ;
const allChats = await getExistingChatNames ( ) ;
if ( allChats . includes ( mainChatName ) ) {
if ( selected _group ) {
await openGroupChat ( selected _group , mainChatName ) ;
} else {
await openCharacterChat ( mainChatName ) ;
}
2024-09-08 03:28:54 +02:00
return mainChatName ;
2023-07-20 19:32:15 +02:00
}
2024-09-08 03:28:54 +02:00
return null ;
2023-07-20 19:32:15 +02:00
}
2024-09-08 00:41:14 +02:00
export async function convertSoloToGroupChat ( ) {
2023-07-20 19:32:15 +02:00
if ( selected _group ) {
console . log ( 'Already in group. No need for conversion' ) ;
return ;
}
if ( this _chid === undefined ) {
console . log ( 'Need to have a character selected' ) ;
return ;
}
2024-06-27 01:01:43 +02:00
const confirm = await Popup . show . confirm ( 'Convert to group chat' , 'Are you sure you want to convert this chat to a group chat?<br />This cannot be reverted.' ) ;
2023-12-17 20:14:16 +01:00
if ( ! confirm ) {
return ;
}
2023-07-20 19:32:15 +02:00
const character = characters [ this _chid ] ;
// Populate group required fields
const name = getUniqueName ( ` Group: ${ character . name } ` , y => groups . findIndex ( x => x . name === y ) !== - 1 ) ;
const avatar = getThumbnailUrl ( 'avatar' , character . avatar ) ;
const chatName = humanizedDateTime ( ) ;
const chats = [ chatName ] ;
const members = [ character . avatar ] ;
const activationStrategy = group _activation _strategy . NATURAL ;
const allowSelfResponses = false ;
const favChecked = character . fav || character . fav == 'true' ;
2024-09-08 00:41:14 +02:00
/** @type {any} */
2023-07-20 19:32:15 +02:00
const metadata = Object . assign ( { } , chat _metadata ) ;
delete metadata . main _chat ;
2023-12-06 18:40:58 +01:00
const createGroupResponse = await fetch ( '/api/groups/create' , {
2023-12-02 19:04:51 +01:00
method : 'POST' ,
2023-07-20 19:32:15 +02:00
headers : getRequestHeaders ( ) ,
body : JSON . stringify ( {
name : name ,
members : members ,
avatar _url : avatar ,
allow _self _responses : activationStrategy ,
activation _strategy : allowSelfResponses ,
disabled _members : [ ] ,
chat _metadata : metadata ,
fav : favChecked ,
chat _id : chatName ,
chats : chats ,
} ) ,
} ) ;
if ( ! createGroupResponse . ok ) {
console . error ( 'Group creation unsuccessful' ) ;
return ;
}
const group = await createGroupResponse . json ( ) ;
// Convert tags list and assign to group
2023-12-02 19:04:51 +01:00
createTagMapFromList ( '#tagList' , group . id ) ;
2023-07-20 19:32:15 +02:00
// Update chars list
await getCharacters ( ) ;
// Convert chat to group format
const groupChat = chat . slice ( ) ;
const genIdFirst = Date . now ( ) ;
// Add something if the chat is empty
if ( groupChat . length === 0 ) {
const newMessage = {
... system _messages [ system _message _types . GROUP ] ,
2024-07-24 23:46:19 +02:00
send _date : getMessageTimeStamp ( ) ,
2023-12-02 21:06:57 +01:00
extra : { type : system _message _types . GROUP } ,
2023-07-20 19:32:15 +02:00
} ;
groupChat . push ( newMessage ) ;
}
for ( let index = 0 ; index < groupChat . length ; index ++ ) {
const message = groupChat [ index ] ;
// Save group-chat marker
if ( index == 0 ) {
message . is _group = true ;
}
// Skip messages we don't care about
if ( message . is _user || message . is _system || message . extra ? . type === system _message _types . NARRATOR || message . force _avatar !== undefined ) {
continue ;
}
// Set force fields for solo character
message . name = character . name ;
message . original _avatar = character . avatar ;
message . force _avatar = getThumbnailUrl ( 'avatar' , character . avatar ) ;
// Allow regens of a single message in group
if ( typeof message . extra !== 'object' ) {
message . extra = { gen _id : genIdFirst + index } ;
}
}
// Save group chat
2023-12-06 04:34:52 +01:00
const createChatResponse = await fetch ( '/api/chats/group/save' , {
2023-12-02 19:04:51 +01:00
method : 'POST' ,
2023-07-20 19:32:15 +02:00
headers : getRequestHeaders ( ) ,
body : JSON . stringify ( { id : chatName , chat : groupChat } ) ,
} ) ;
if ( ! createChatResponse . ok ) {
console . error ( 'Group chat creation unsuccessful' ) ;
2024-06-28 03:28:16 +02:00
toastr . error ( 'Group chat creation unsuccessful' ) ;
2023-07-20 19:32:15 +02:00
return ;
}
// Click on the freshly selected group to open it
2023-08-19 20:44:15 +02:00
await openGroupById ( group . id ) ;
2023-07-20 19:32:15 +02:00
toastr . success ( 'The chat has been successfully converted!' ) ;
}
2024-09-08 00:34:03 +02:00
/ * *
* Creates a new branch from the message with the given ID
* @ param { number } mesId Message ID
2024-09-08 03:28:54 +02:00
* @ returns { Promise < string ? > } Branch file name
2024-09-08 00:34:03 +02:00
* /
2024-09-08 00:41:14 +02:00
export async function branchChat ( mesId ) {
2024-09-08 00:34:03 +02:00
if ( this _chid === undefined && ! selected _group ) {
2024-09-08 03:28:54 +02:00
toastr . info ( 'No character selected.' , 'Create Branch' ) ;
return null ;
2024-09-08 00:34:03 +02:00
}
const fileName = await createBranch ( mesId ) ;
await saveItemizedPrompts ( fileName ) ;
if ( selected _group ) {
await openGroupChat ( selected _group , fileName ) ;
} else {
await openCharacterChat ( fileName ) ;
}
return fileName ;
}
2024-09-08 03:28:54 +02:00
function registerBookmarksSlashCommands ( ) {
/ * *
* Validates a message ID . ( Is a number , exists as a message )
*
* @ param { number } mesId - The message ID to validate .
* @ param { string } context - The context of the slash command . Will be used as the title of any toasts .
* @ returns { boolean } - Returns true if the message ID is valid , otherwise false .
* /
function validateMessageId ( mesId , context ) {
if ( isNaN ( mesId ) ) {
toastr . warning ( 'Invalid message ID was provided' , context ) ;
return false ;
}
if ( ! chat [ mesId ] ) {
toastr . warning ( ` Message for id ${ mesId } not found ` , context ) ;
return false ;
}
return true ;
}
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'branch-create' ,
returns : 'Name of the new branch' ,
callback : async ( args , text ) => {
const mesId = Number ( args . mesId ? ? text ? ? getLastMessageId ( ) ) ;
if ( ! validateMessageId ( mesId , 'Create Branch' ) ) return '' ;
const branchName = await branchChat ( mesId ) ;
return branchName ? ? '' ;
} ,
unnamedArgumentList : [
SlashCommandArgument . fromProps ( {
description : 'Message ID' ,
typeList : [ ARGUMENT _TYPE . NUMBER ] ,
enumProvider : commonEnumProviders . messages ( ) ,
} ) ,
] ,
helpString : `
< div >
Create a new branch from the selected message . If no message id is provided , will use the last message .
< / d i v >
< div >
Creating a branch will automatically choose a name for the branch . < br / >
After creating the branch , the branch chat will be automatically opened .
< / d i v >
< div >
Use Checkpoints and < code > / c h e c k p o i n t - c r e a t e < / c o d e > i n s t e a d i f y o u d o n o t w a n t t o j u m p t o t h e n e w c h a t .
< / d i v > ` ,
} ) ) ;
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'checkpoint-create' ,
returns : 'Name of the new checkpoint' ,
callback : async ( args , text ) => {
const mesId = Number ( args . mesId ? ? getLastMessageId ( ) ) ;
if ( ! validateMessageId ( mesId , 'Create Checkpoint' ) ) return '' ;
2024-09-12 23:34:37 +02:00
if ( typeof text !== 'string' ) {
toastr . warning ( 'Checkpoint name must be a string or empty' , 'Create Checkpoint' ) ;
2024-09-08 03:28:54 +02:00
return '' ;
}
const checkPointName = await createNewBookmark ( mesId , { forceName : text } ) ;
return checkPointName ? ? '' ;
} ,
namedArgumentList : [
SlashCommandNamedArgument . fromProps ( {
2024-09-13 19:29:54 +02:00
name : 'mesId' ,
2024-09-08 03:28:54 +02:00
description : 'Message ID' ,
typeList : [ ARGUMENT _TYPE . NUMBER ] ,
enumProvider : commonEnumProviders . messages ( ) ,
} ) ,
] ,
unnamedArgumentList : [
SlashCommandArgument . fromProps ( {
description : 'Checkpoint name' ,
typeList : [ ARGUMENT _TYPE . STRING ] ,
} ) ,
] ,
helpString : `
< div >
2024-09-12 23:34:37 +02:00
Create a new checkpoint for the selected message with the provided name . If no message id is provided , will use the last message . < br / >
Leave the checkpoint name empty to auto - generate one .
2024-09-08 03:28:54 +02:00
< / d i v >
< div >
A created checkpoint will be permanently linked with the message . < br / >
If a checkpoint already exists , the link to it will be overwritten . < br / >
After creating the checkpoint , the checkpoint chat can be opened with the checkpoint flag ,
using the < code > / g o < / c o d e > c o m m a n d w i t h t h e c h e c k p o i n t n a m e o r t h e < c o d e > / c h e c k p o i n t - g o < / c o d e > c o m m a n d o n t h e m e s s a g e .
< / d i v >
< div >
Use Branches and < code > / b r a n c h - c r e a t e < / c o d e > i n s t e a d i f y o u d o w a n t t o j u m p t o t h e n e w c h a t .
< / d i v >
< div >
< strong > Example : < / s t r o n g >
< ul >
< li >
< pre > < code > / c h e c k p o i n t - c r e a t e m e s = { { l a s t C h a r M e s s a g e } } C h e c k p o i n t f o r c h a r r e p l y | / s e t v a r k e y = r e m e m b e r C h e c k p o i n t { { p i p e } } < / c o d e > < / p r e >
Will create a new checkpoint to the latest message of the current character , and save it as a local variable for future use .
< / l i >
< / u l >
< / d i v > ` ,
} ) ) ;
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'checkpoint-go' ,
returns : 'Name of the checkpoint' ,
callback : async ( args , text ) => {
const mesId = Number ( args . mesId ? ? text ? ? getLastMessageId ( ) ) ;
if ( ! validateMessageId ( mesId , 'Open Checkpoint' ) ) return '' ;
const checkPointName = chat [ mesId ] . extra ? . bookmark _link ;
if ( ! checkPointName ) {
toastr . warning ( 'No checkpoint is linked to the selected message' , 'Open Checkpoint' ) ;
return '' ;
}
if ( selected _group ) {
await openGroupChat ( selected _group , checkPointName ) ;
} else {
await openCharacterChat ( checkPointName ) ;
}
return checkPointName ;
} ,
unnamedArgumentList : [
SlashCommandArgument . fromProps ( {
description : 'Message ID' ,
typeList : [ ARGUMENT _TYPE . NUMBER ] ,
enumProvider : commonEnumProviders . messages ( ) ,
} ) ,
] ,
helpString : `
< div >
Open the checkpoint linked to the selected message . If no message id is provided , will use the last message .
< / d i v >
< div >
Use < code > / c h e c k p o i n t - g e t < / c o d e > i f y o u w a n t t o m a k e s u r e t h a t t h e s e l e c t e d m e s s a g e h a s a c h e c k p o i n t .
< / d i v > ` ,
} ) ) ;
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'checkpoint-exit' ,
2024-09-10 21:06:31 +02:00
returns : 'The name of the chat exited to. Returns an empty string if not in a checkpoint chat.' ,
2024-09-08 03:28:54 +02:00
callback : async ( ) => {
const mainChat = await backToMainChat ( ) ;
return mainChat ? ? '' ;
} ,
2024-09-10 21:06:31 +02:00
helpString : 'Exit the checkpoint chat.<br />If not in a checkpoint chat, returns empty string.' ,
2024-09-08 03:28:54 +02:00
} ) ) ;
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'checkpoint-parent' ,
returns : 'Name of the parent chat for this checkpoint' ,
callback : async ( ) => {
const mainChatName = getMainChatName ( ) ;
return mainChatName ? ? '' ;
} ,
2024-09-10 21:06:31 +02:00
helpString : 'Get the name of the parent chat for this checkpoint.<br />If not in a checkpoint chat, returns empty string.' ,
} ) ) ;
2024-09-08 03:28:54 +02:00
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'checkpoint-get' ,
returns : 'Name of the chat' ,
callback : async ( args , text ) => {
const mesId = Number ( args . mesId ? ? text ? ? getLastMessageId ( ) ) ;
if ( ! validateMessageId ( mesId , 'Get Checkpoint' ) ) return '' ;
const checkPointName = chat [ mesId ] . extra ? . bookmark _link ;
return checkPointName ? ? '' ;
} ,
unnamedArgumentList : [
SlashCommandArgument . fromProps ( {
description : 'Message ID' ,
typeList : [ ARGUMENT _TYPE . NUMBER ] ,
enumProvider : commonEnumProviders . messages ( ) ,
} ) ,
] ,
helpString : `
< div >
Get the name of the checkpoint linked to the selected message . If no message id is provided , will use the last message . < br / >
If no checkpoint is linked , the result will be empty .
< / d i v > ` ,
} ) ) ;
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'checkpoint-list' ,
returns : 'JSON array of all existing checkpoints in this chat, as an array' ,
/** @param {{links?: string}} args @returns {Promise<string>} */
callback : async ( args , _ ) => {
2024-09-10 21:06:31 +02:00
const result = Object . entries ( chat )
. filter ( ( [ _ , message ] ) => message . extra ? . bookmark _link )
2024-09-13 19:44:06 +02:00
. map ( ( [ mesId , message ] ) => isTrueBoolean ( args . links ) ? message . extra . bookmark _link : Number ( mesId ) ) ;
2024-09-08 03:28:54 +02:00
return JSON . stringify ( result ) ;
} ,
namedArgumentList : [
SlashCommandNamedArgument . fromProps ( {
name : 'links' ,
description : 'Get a list of all links / chat names of the checkpoints, instead of the message ids' ,
typeList : [ ARGUMENT _TYPE . BOOLEAN ] ,
enumList : commonEnumProviders . boolean ( 'trueFalse' ) ( ) ,
defaultValue : 'false' ,
} ) ,
] ,
helpString : `
< div >
List all existing checkpoints in this chat .
< / d i v >
< div >
Returns a list of all message ids that have a checkpoint , or all checkpoint links if < code > links < / c o d e > i s s e t t o < c o d e > t r u e < / c o d e > . < b r / >
The value will be a JSON array .
< / d i v > ` ,
} ) ) ;
}
export function initBookmarks ( ) {
2023-07-20 19:32:15 +02:00
$ ( '#option_new_bookmark' ) . on ( 'click' , saveBookmarkMenu ) ;
$ ( '#option_back_to_main' ) . on ( 'click' , backToMainChat ) ;
$ ( '#option_convert_to_group' ) . on ( 'click' , convertSoloToGroupChat ) ;
2024-09-08 00:34:03 +02:00
2024-09-10 21:06:31 +02:00
$ ( document ) . on ( 'click' , '.select_chat_block, .mes_bookmark' , async function ( e ) {
2024-09-08 00:34:03 +02:00
// If shift is held down, we are not following the bookmark, but creating a new one
2024-09-13 20:35:42 +02:00
const mes = $ ( this ) . closest ( '.mes' ) ;
if ( e . shiftKey && mes . length ) {
const selectedMesId = mes . attr ( 'mesid' ) ;
2024-09-08 03:28:54 +02:00
await createNewBookmark ( Number ( selectedMesId ) ) ;
2024-09-08 00:34:03 +02:00
return ;
}
2024-09-13 19:31:50 +02:00
const fileName = $ ( this ) . hasClass ( 'mes_bookmark' )
2024-09-08 00:34:03 +02:00
? $ ( this ) . closest ( '.mes' ) . attr ( 'bookmark_link' )
: $ ( this ) . attr ( 'file_name' ) . replace ( '.jsonl' , '' ) ;
2024-09-13 19:31:50 +02:00
if ( ! fileName ) {
2024-09-08 00:34:03 +02:00
return ;
}
try {
showLoader ( ) ;
if ( selected _group ) {
2024-09-13 19:31:50 +02:00
await openGroupChat ( selected _group , fileName ) ;
2024-09-08 00:34:03 +02:00
} else {
2024-09-13 19:31:50 +02:00
await openCharacterChat ( fileName ) ;
2024-09-08 00:34:03 +02:00
}
} finally {
2024-09-08 00:41:14 +02:00
await hideLoader ( ) ;
2024-09-08 00:34:03 +02:00
}
$ ( '#shadow_select_chat_popup' ) . css ( 'display' , 'none' ) ;
$ ( '#load_select_chat_div' ) . css ( 'display' , 'block' ) ;
} ) ;
$ ( document ) . on ( 'click' , '.mes_create_bookmark' , async function ( ) {
2024-09-13 19:31:50 +02:00
const mesId = $ ( this ) . closest ( '.mes' ) . attr ( 'mesid' ) ;
if ( mesId !== undefined ) {
await createNewBookmark ( Number ( mesId ) ) ;
2024-09-08 00:34:03 +02:00
}
} ) ;
$ ( document ) . on ( 'click' , '.mes_create_branch' , async function ( ) {
2024-09-13 19:31:50 +02:00
const mesId = $ ( this ) . closest ( '.mes' ) . attr ( 'mesid' ) ;
if ( mesId !== undefined ) {
await branchChat ( Number ( mesId ) ) ;
2024-09-08 00:34:03 +02:00
}
} ) ;
2024-09-08 03:28:54 +02:00
registerBookmarksSlashCommands ( ) ;
}