2023-07-20 19:32:15 +02:00
import {
2023-12-07 02:26:29 +01:00
Generate ,
2024-08-18 11:18:06 +02:00
UNIQUE _APIS ,
2023-12-07 02:26:29 +01:00
activateSendButtons ,
2023-07-20 19:32:15 +02:00
addOneMessage ,
2024-08-18 11:18:06 +02:00
api _server ,
2023-12-07 02:26:29 +01:00
callPopup ,
2023-07-20 19:32:15 +02:00
characters ,
chat ,
chat _metadata ,
2023-12-07 02:26:29 +01:00
comment _avatar ,
deactivateSendButtons ,
2023-07-20 19:32:15 +02:00
default _avatar ,
eventSource ,
event _types ,
2024-03-23 17:44:40 +01:00
extension _prompt _roles ,
2023-12-07 02:26:29 +01:00
extension _prompt _types ,
2023-07-20 19:32:15 +02:00
extractMessageBias ,
2023-12-07 02:26:29 +01:00
generateQuietPrompt ,
generateRaw ,
2023-07-20 19:32:15 +02:00
getThumbnailUrl ,
2023-12-07 02:26:29 +01:00
is _send _press ,
main _api ,
name1 ,
2024-04-28 02:53:17 +02:00
name2 ,
2023-12-07 02:26:29 +01:00
reloadCurrentChat ,
2023-12-21 19:49:03 +01:00
removeMacros ,
2024-06-05 02:19:22 +02:00
renameCharacter ,
2023-07-20 19:32:15 +02:00
saveChatConditional ,
2023-12-07 02:26:29 +01:00
sendMessageAsUser ,
2023-07-20 19:32:15 +02:00
sendSystemMessage ,
2024-03-26 17:21:22 +01:00
setActiveCharacter ,
setActiveGroup ,
2023-12-07 02:26:29 +01:00
setCharacterId ,
setCharacterName ,
setExtensionPrompt ,
2023-07-20 19:32:15 +02:00
setUserName ,
2024-07-03 14:44:17 +02:00
stopGeneration ,
2023-07-20 19:32:15 +02:00
substituteParams ,
system _avatar ,
system _message _types ,
2023-10-16 08:12:12 +02:00
this _chid ,
2023-12-02 19:04:51 +01:00
} from '../script.js' ;
2024-06-29 00:25:10 +02:00
import { SlashCommandParser } from './slash-commands/SlashCommandParser.js' ;
2024-05-12 21:15:05 +02:00
import { SlashCommandParserError } from './slash-commands/SlashCommandParserError.js' ;
2023-12-02 19:04:51 +01:00
import { getMessageTimeStamp } from './RossAscends-mods.js' ;
2024-04-12 09:03:50 +02:00
import { hideChatMessageRange } from './chats.js' ;
2024-06-29 00:25:10 +02:00
import { getContext , saveMetadataDebounced } from './extensions.js' ;
2023-12-02 19:04:51 +01:00
import { getRegexedString , regex _placement } from './extensions/regex/engine.js' ;
2024-06-29 00:25:10 +02:00
import { findGroupMemberId , groups , is _group _generating , openGroupById , resetSelectedGroup , saveGroupChat , selected _group } from './group-chats.js' ;
2024-06-16 04:37:42 +02:00
import { chat _completion _sources , oai _settings , setupChatCompletionPromptManager } from './openai.js' ;
2024-06-26 19:18:05 +02:00
import { autoSelectPersona , retriggerFirstMessageOnEmptyChat , setPersonaLockState , togglePersonaLock , user _avatar } from './personas.js' ;
2023-12-07 02:26:29 +01:00
import { addEphemeralStoppingString , chat _styles , flushEphemeralStoppingStrings , power _user } from './power-user.js' ;
2024-08-18 11:02:15 +02:00
import { SERVER _INPUTS , textgen _types , textgenerationwebui _settings } from './textgen-settings.js' ;
2024-04-13 20:33:19 +02:00
import { decodeTextTokens , getFriendlyTokenizerName , getTextTokens , getTokenCountAsync } from './tokenizers.js' ;
2024-07-16 15:28:33 +02:00
import { debounce , delay , isFalseBoolean , isTrueBoolean , showFontAwesomePicker , stringToRange , trimToEndSentence , trimToStartSentence , waitUntilCondition } from './utils.js' ;
2023-12-02 19:04:51 +01:00
import { registerVariableCommands , resolveVariable } from './variables.js' ;
2024-04-12 01:21:17 +02:00
import { background _settings } from './backgrounds.js' ;
2024-05-12 21:15:05 +02:00
import { SlashCommandClosure } from './slash-commands/SlashCommandClosure.js' ;
import { SlashCommandClosureResult } from './slash-commands/SlashCommandClosureResult.js' ;
import { ARGUMENT _TYPE , SlashCommandArgument , SlashCommandNamedArgument } from './slash-commands/SlashCommandArgument.js' ;
import { AutoComplete } from './autocomplete/AutoComplete.js' ;
import { SlashCommand } from './slash-commands/SlashCommand.js' ;
import { SlashCommandAbortController } from './slash-commands/SlashCommandAbortController.js' ;
import { SlashCommandNamedArgumentAssignment } from './slash-commands/SlashCommandNamedArgumentAssignment.js' ;
2024-06-21 20:04:55 +02:00
import { SlashCommandEnumValue , enumTypes } from './slash-commands/SlashCommandEnumValue.js' ;
2024-06-19 01:40:22 +02:00
import { POPUP _TYPE , Popup , callGenericPopup } from './popup.js' ;
2024-06-21 20:04:55 +02:00
import { commonEnumProviders , enumIcons } from './slash-commands/SlashCommandCommonEnumsProvider.js' ;
2024-06-18 20:29:29 +02:00
import { SlashCommandDebugController } from './slash-commands/SlashCommandDebugController.js' ;
2024-07-04 18:31:27 +02:00
import { SlashCommandBreakController } from './slash-commands/SlashCommandBreakController.js' ;
2024-07-06 00:05:22 +02:00
import { SlashCommandExecutionError } from './slash-commands/SlashCommandExecutionError.js' ;
2024-05-12 21:15:05 +02:00
export {
executeSlashCommands , executeSlashCommandsWithOptions , getSlashCommandsHelp , registerSlashCommand ,
} ;
export const parser = new SlashCommandParser ( ) ;
2024-04-19 13:19:57 +02:00
/ * *
2024-05-12 21:15:05 +02:00
* @ deprecated Use SlashCommandParser . addCommandObject ( ) instead
2024-04-19 13:19:57 +02:00
* /
2024-05-12 21:15:05 +02:00
const registerSlashCommand = SlashCommandParser . addCommand . bind ( SlashCommandParser ) ;
const getSlashCommandsHelp = parser . getHelpString . bind ( parser ) ;
2024-06-29 00:25:10 +02:00
export function initDefaultSlashCommands ( ) {
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : '?' ,
callback : helpCommandCallback ,
aliases : [ 'help' ] ,
unnamedArgumentList : [ SlashCommandArgument . fromProps ( {
description : 'help topic' ,
2024-06-21 20:04:55 +02:00
typeList : [ ARGUMENT _TYPE . STRING ] ,
2024-06-29 00:25:10 +02:00
enumList : [
new SlashCommandEnumValue ( 'slash' , 'slash commands (STscript)' , enumTypes . command , '/' ) ,
new SlashCommandEnumValue ( 'macros' , '{{macros}} (text replacement)' , enumTypes . macro , enumIcons . macro ) ,
new SlashCommandEnumValue ( 'format' , 'chat/text formatting' , enumTypes . name , '★' ) ,
new SlashCommandEnumValue ( 'hotkeys' , 'keyboard shortcuts' , enumTypes . enum , '⏎' ) ,
] ,
} ) ] ,
helpString : 'Get help on macros, chat formatting and commands.' ,
} ) ) ;
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'persona' ,
callback : setNameCallback ,
2024-07-21 23:19:47 +02:00
aliases : [ 'name' ] ,
2024-06-29 00:25:10 +02:00
namedArgumentList : [
new SlashCommandNamedArgument (
'mode' , 'The mode for persona selection. ("lookup" = search for existing persona, "temp" = create a temporary name, set a temporary name, "all" = allow both in the same command)' ,
[ ARGUMENT _TYPE . STRING ] , false , false , 'all' , [ 'lookup' , 'temp' , 'all' ] ,
) ,
] ,
unnamedArgumentList : [
SlashCommandArgument . fromProps ( {
description : 'persona name' ,
typeList : [ ARGUMENT _TYPE . STRING ] ,
isRequired : true ,
enumProvider : commonEnumProviders . personas ,
} ) ,
] ,
helpString : 'Selects the given persona with its name and avatar (by name or avatar url). If no matching persona exists, applies a temporary name.' ,
} ) ) ;
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'sync' ,
callback : syncCallback ,
helpString : 'Syncs the user persona in user-attributed messages in the current chat.' ,
} ) ) ;
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'lock' ,
callback : lockPersonaCallback ,
aliases : [ 'bind' ] ,
helpString : 'Locks/unlocks a persona (name and avatar) to the current chat' ,
unnamedArgumentList : [
SlashCommandArgument . fromProps ( {
description : 'state' ,
typeList : [ ARGUMENT _TYPE . STRING ] ,
isRequired : true ,
defaultValue : 'toggle' ,
enumProvider : commonEnumProviders . boolean ( 'onOffToggle' ) ,
} ) ,
] ,
} ) ) ;
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'bg' ,
callback : setBackgroundCallback ,
aliases : [ 'background' ] ,
returns : 'the current background' ,
unnamedArgumentList : [
SlashCommandArgument . fromProps ( {
2024-07-28 01:17:11 +02:00
description : 'background filename' ,
2024-06-29 00:25:10 +02:00
typeList : [ ARGUMENT _TYPE . STRING ] ,
enumProvider : ( ) => [ ... document . querySelectorAll ( '.bg_example' ) ]
. map ( it => new SlashCommandEnumValue ( it . getAttribute ( 'bgfile' ) ) )
. filter ( it => it . value ? . length ) ,
} ) ,
] ,
helpString : `
2024-05-12 21:15:05 +02:00
< div >
Sets a background according to the provided filename . Partial names allowed .
< / d i v >
2024-07-28 01:17:11 +02:00
< div >
If no background is provided , this will return the currently selected background .
< / d i v >
2024-05-12 21:15:05 +02:00
< div >
< strong > Example : < / s t r o n g >
< ul >
< li >
< pre > < code > / b g b e a c h . j p g < / c o d e > < / p r e >
< / l i >
2024-07-28 01:17:11 +02:00
< li >
< pre > < code > / b g < / c o d e > < / p r e >
< / l i >
2024-05-12 21:15:05 +02:00
< / u l >
< / d i v >
` ,
2024-06-29 00:25:10 +02:00
} ) ) ;
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'sendas' ,
callback : sendMessageAs ,
namedArgumentList : [
SlashCommandNamedArgument . fromProps ( {
name : 'name' ,
description : 'Character name' ,
typeList : [ ARGUMENT _TYPE . STRING ] ,
isRequired : true ,
enumProvider : commonEnumProviders . characters ( 'character' ) ,
forceEnum : false ,
} ) ,
new SlashCommandNamedArgument (
'compact' , 'Use compact layout' , [ ARGUMENT _TYPE . BOOLEAN ] , false , false , 'false' ,
) ,
SlashCommandNamedArgument . fromProps ( {
name : 'at' ,
2024-06-29 02:52:30 +02:00
description : 'position to insert the message (index-based, corresponding to message id). If not set, the message will be inserted at the end of the chat.\nNegative values are accepted and will work similarly to how \'depth\' usually works. For example, -1 will insert the message right before the last message in chat.' ,
2024-06-29 00:25:10 +02:00
typeList : [ ARGUMENT _TYPE . NUMBER ] ,
enumProvider : commonEnumProviders . messages ( { allowIdAfter : true } ) ,
} ) ,
] ,
unnamedArgumentList : [
new SlashCommandArgument (
'text' , [ ARGUMENT _TYPE . STRING ] , true ,
) ,
] ,
helpString : `
2024-05-12 21:15:05 +02:00
< div >
Sends a message as a specific character . Uses the character avatar if it exists in the characters list .
< / d i v >
< div >
< strong > Example : < / s t r o n g >
< ul >
< li >
< pre > < code > / s e n d a s n a m e = " C h l o e " H e l l o , g u y s ! < / c o d e > < / p r e >
will send "Hello, guys!" from "Chloe" .
< / l i >
< / u l >
< / d i v >
< div >
If "compact" is set to true , the message is sent using a compact layout .
< / d i v >
` ,
2024-06-29 00:25:10 +02:00
} ) ) ;
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'sys' ,
callback : sendNarratorMessage ,
aliases : [ 'nar' ] ,
namedArgumentList : [
new SlashCommandNamedArgument (
'compact' ,
'compact layout' ,
[ ARGUMENT _TYPE . BOOLEAN ] ,
false ,
false ,
'false' ,
) ,
SlashCommandNamedArgument . fromProps ( {
name : 'at' ,
2024-06-29 02:52:30 +02:00
description : 'position to insert the message (index-based, corresponding to message id). If not set, the message will be inserted at the end of the chat.\nNegative values are accepted and will work similarly to how \'depth\' usually works. For example, -1 will insert the message right before the last message in chat.' ,
2024-06-29 00:25:10 +02:00
typeList : [ ARGUMENT _TYPE . NUMBER ] ,
enumProvider : commonEnumProviders . messages ( { allowIdAfter : true } ) ,
} ) ,
] ,
unnamedArgumentList : [
new SlashCommandArgument (
'text' , [ ARGUMENT _TYPE . STRING ] , true ,
) ,
] ,
helpString : `
2024-05-12 21:15:05 +02:00
< div >
Sends a message as a system narrator .
< / d i v >
< div >
If < code > compact < / c o d e > i s s e t t o < c o d e > t r u e < / c o d e > , t h e m e s s a g e i s s e n t u s i n g a c o m p a c t l a y o u t .
< / d i v >
< div >
< strong > Example : < / s t r o n g >
< ul >
< li >
< pre > < code > / s y s T h e s u n s e t s i n t h e w e s t . < / c o d e > < / p r e >
< / l i >
< li >
< pre > < code > / s y s c o m p a c t = t r u e A b r i e f n o t e . < / c o d e > < / p r e >
< / l i >
< / u l >
< / d i v >
` ,
2024-06-29 00:25:10 +02:00
} ) ) ;
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'sysname' ,
callback : setNarratorName ,
unnamedArgumentList : [
new SlashCommandArgument (
'name' , [ ARGUMENT _TYPE . STRING ] , false ,
) ,
] ,
helpString : 'Sets a name for future system narrator messages in this chat (display only). Default: System. Leave empty to reset.' ,
} ) ) ;
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'comment' ,
callback : sendCommentMessage ,
namedArgumentList : [
new SlashCommandNamedArgument (
'compact' ,
'Whether to use a compact layout' ,
[ ARGUMENT _TYPE . BOOLEAN ] ,
false ,
false ,
'false' ,
) ,
SlashCommandNamedArgument . fromProps ( {
name : 'at' ,
2024-06-29 02:52:30 +02:00
description : 'position to insert the message (index-based, corresponding to message id). If not set, the message will be inserted at the end of the chat.\nNegative values are accepted and will work similarly to how \'depth\' usually works. For example, -1 will insert the message right before the last message in chat.' ,
2024-06-29 00:25:10 +02:00
typeList : [ ARGUMENT _TYPE . NUMBER ] ,
enumProvider : commonEnumProviders . messages ( { allowIdAfter : true } ) ,
} ) ,
] ,
unnamedArgumentList : [
new SlashCommandArgument (
'text' ,
[ ARGUMENT _TYPE . STRING ] ,
true ,
) ,
] ,
helpString : `
2024-05-12 21:15:05 +02:00
< div >
Adds a note / comment message not part of the chat .
< / d i v >
< div >
If < code > compact < / c o d e > i s s e t t o < c o d e > t r u e < / c o d e > , t h e m e s s a g e i s s e n t u s i n g a c o m p a c t l a y o u t .
< / d i v >
< div >
< strong > Example : < / s t r o n g >
< ul >
< li >
< pre > < code > / c o m m e n t T h i s i s a c o m m e n t < / c o d e > < / p r e >
< / l i >
< li >
< pre > < code > / c o m m e n t c o m p a c t = t r u e T h i s i s a c o m p a c t c o m m e n t < / c o d e > < / p r e >
< / l i >
< / u l >
< / d i v >
` ,
2024-06-29 00:25:10 +02:00
} ) ) ;
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'single' ,
callback : setStoryModeCallback ,
aliases : [ 'story' ] ,
helpString : 'Sets the message style to single document mode without names or avatars visible.' ,
} ) ) ;
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'bubble' ,
callback : setBubbleModeCallback ,
aliases : [ 'bubbles' ] ,
helpString : 'Sets the message style to bubble chat mode.' ,
} ) ) ;
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'flat' ,
callback : setFlatModeCallback ,
aliases : [ 'default' ] ,
helpString : 'Sets the message style to flat chat mode.' ,
} ) ) ;
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'continue' ,
callback : continueChatCallback ,
aliases : [ 'cont' ] ,
2024-07-08 18:43:55 +02:00
namedArgumentList : [
new SlashCommandNamedArgument (
'await' ,
2024-07-08 19:02:21 +02:00
'Whether to await for the continued generation before proceeding' ,
2024-07-08 18:43:55 +02:00
[ ARGUMENT _TYPE . BOOLEAN ] ,
false ,
false ,
'false' ,
) ,
] ,
2024-06-29 00:25:10 +02:00
unnamedArgumentList : [
new SlashCommandArgument (
'prompt' , [ ARGUMENT _TYPE . STRING ] , false ,
) ,
] ,
helpString : `
2024-05-12 21:15:05 +02:00
< div >
Continues the last message in the chat , with an optional additional prompt .
< / d i v >
2024-07-08 18:43:55 +02:00
< div >
2024-07-08 19:02:21 +02:00
If < code > await = true < / c o d e > n a m e d a r g u m e n t i s p a s s e d , t h e c o m m a n d w i l l a w a i t f o r t h e c o n t i n u e d g e n e r a t i o n b e f o r e p r o c e e d i n g .
2024-07-08 18:43:55 +02:00
< / d i v >
2024-05-12 21:15:05 +02:00
< div >
< strong > Example : < / s t r o n g >
< ul >
< li >
< pre > < code > / c o n t i n u e < / c o d e > < / p r e >
2024-07-08 19:02:21 +02:00
Continues the chat with no additional prompt and immediately proceeds to the next command .
2024-05-12 21:15:05 +02:00
< / l i >
< li >
2024-07-08 19:02:21 +02:00
< pre > < code > / c o n t i n u e a w a i t = t r u e L e t ' s e x p l o r e t h i s f u r t h e r . . . < / c o d e > < / p r e >
Continues the chat with the provided prompt and waits for the generation to finish .
2024-05-12 21:15:05 +02:00
< / l i >
< / u l >
< / d i v >
` ,
2024-06-29 00:25:10 +02:00
} ) ) ;
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'go' ,
callback : goToCharacterCallback ,
unnamedArgumentList : [
SlashCommandArgument . fromProps ( {
description : 'name' ,
typeList : [ ARGUMENT _TYPE . STRING ] ,
isRequired : true ,
enumProvider : commonEnumProviders . characters ( 'all' ) ,
} ) ,
] ,
helpString : 'Opens up a chat with the character or group by its name' ,
aliases : [ 'char' ] ,
} ) ) ;
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'rename-char' ,
/** @param {{silent: string, chats: string}} options @param {string} name */
callback : async ( { silent = 'true' , chats = null } , name ) => {
const renamed = await renameCharacter ( name , { silent : isTrueBoolean ( silent ) , renameChats : chats !== null ? isTrueBoolean ( chats ) : null } ) ;
return String ( renamed ) ;
} ,
returns : 'true/false - Whether the rename was successful' ,
namedArgumentList : [
new SlashCommandNamedArgument (
'silent' , 'Hide any blocking popups. (if false, the name is optional. If not supplied, a popup asking for it will appear)' , [ ARGUMENT _TYPE . BOOLEAN ] , false , false , 'true' ,
) ,
new SlashCommandNamedArgument (
'chats' , 'Rename char in all previous chats' , [ ARGUMENT _TYPE . BOOLEAN ] , false , false , '<null>' ,
) ,
] ,
unnamedArgumentList : [
new SlashCommandArgument (
'new char name' , [ ARGUMENT _TYPE . STRING ] , true ,
) ,
] ,
helpString : 'Renames the current character.' ,
} ) ) ;
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'sysgen' ,
callback : generateSystemMessage ,
unnamedArgumentList : [
new SlashCommandArgument (
'prompt' , [ ARGUMENT _TYPE . STRING ] , true ,
) ,
] ,
helpString : 'Generates a system message using a specified prompt.' ,
} ) ) ;
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'ask' ,
callback : askCharacter ,
namedArgumentList : [
SlashCommandNamedArgument . fromProps ( {
name : 'name' ,
description : 'character name' ,
typeList : [ ARGUMENT _TYPE . STRING ] ,
isRequired : true ,
enumProvider : commonEnumProviders . characters ( 'character' ) ,
} ) ,
] ,
unnamedArgumentList : [
new SlashCommandArgument (
'prompt' , [ ARGUMENT _TYPE . STRING ] , true , false ,
) ,
] ,
helpString : 'Asks a specified character card a prompt. Character name must be provided in a named argument.' ,
} ) ) ;
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'delname' ,
callback : deleteMessagesByNameCallback ,
namedArgumentList : [ ] ,
unnamedArgumentList : [
SlashCommandArgument . fromProps ( {
description : 'name' ,
typeList : [ ARGUMENT _TYPE . STRING ] ,
isRequired : true ,
enumProvider : commonEnumProviders . characters ( 'character' ) ,
} ) ,
] ,
aliases : [ 'cancel' ] ,
helpString : `
2024-05-12 21:15:05 +02:00
< div >
Deletes all messages attributed to a specified name .
< / d i v >
< div >
< strong > Example : < / s t r o n g >
< ul >
< li >
< pre > < code > / d e l n a m e J o h n < / c o d e > < / p r e >
< / l i >
< / u l >
< / d i v >
` ,
2024-06-29 00:25:10 +02:00
} ) ) ;
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'send' ,
callback : sendUserMessageCallback ,
namedArgumentList : [
new SlashCommandNamedArgument (
'compact' ,
'whether to use a compact layout' ,
[ ARGUMENT _TYPE . BOOLEAN ] ,
false ,
false ,
'false' ,
) ,
SlashCommandNamedArgument . fromProps ( {
name : 'at' ,
2024-06-29 02:52:30 +02:00
description : 'position to insert the message (index-based, corresponding to message id). If not set, the message will be inserted at the end of the chat.\nNegative values are accepted and will work similarly to how \'depth\' usually works. For example, -1 will insert the message right before the last message in chat.' ,
2024-06-29 00:25:10 +02:00
typeList : [ ARGUMENT _TYPE . NUMBER ] ,
enumProvider : commonEnumProviders . messages ( { allowIdAfter : true } ) ,
} ) ,
SlashCommandNamedArgument . fromProps ( {
name : 'name' ,
description : 'display name' ,
typeList : [ ARGUMENT _TYPE . STRING ] ,
defaultValue : '{{user}}' ,
2024-07-01 15:59:34 +02:00
enumProvider : commonEnumProviders . personas ,
2024-06-29 00:25:10 +02:00
} ) ,
] ,
unnamedArgumentList : [
new SlashCommandArgument (
'text' ,
[ ARGUMENT _TYPE . STRING ] ,
true ,
) ,
] ,
helpString : `
2024-05-12 21:15:05 +02:00
< div >
Adds a user message to the chat log without triggering a generation .
< / d i v >
< div >
If < code > compact < / c o d e > i s s e t t o < c o d e > t r u e < / c o d e > , t h e m e s s a g e i s s e n t u s i n g a c o m p a c t l a y o u t .
< / d i v >
< div >
If < code > name < / c o d e > i s s e t , i t w i l l b e d i s p l a y e d a s t h e m e s s a g e s e n d e r . C a n b e a n e m p t y f o r n o n a m e .
< / d i v >
< div >
< strong > Example : < / s t r o n g >
< ul >
< li >
< pre > < code > / s e n d H e l l o t h e r e ! < / c o d e > < / p r e >
< / l i >
< li >
< pre > < code > / s e n d c o m p a c t = t r u e H i < / c o d e > < / p r e >
< / l i >
< / u l >
< / d i v >
` ,
2024-06-29 00:25:10 +02:00
} ) ) ;
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'trigger' ,
callback : triggerGenerationCallback ,
namedArgumentList : [
new SlashCommandNamedArgument (
'await' ,
'Whether to await for the triggered generation before continuing' ,
[ ARGUMENT _TYPE . BOOLEAN ] ,
false ,
false ,
'false' ,
) ,
] ,
unnamedArgumentList : [
SlashCommandArgument . fromProps ( {
description : 'group member index (starts with 0) or name' ,
typeList : [ ARGUMENT _TYPE . NUMBER , ARGUMENT _TYPE . STRING ] ,
isRequired : false ,
enumProvider : commonEnumProviders . groupMembers ( ) ,
} ) ,
] ,
helpString : `
2024-05-12 21:15:05 +02:00
< div >
Triggers a message generation . If in group , can trigger a message for the specified group member index or name .
< / d i v >
< div >
If < code > await = true < / c o d e > n a m e d a r g u m e n t i s p a s s e d , t h e c o m m a n d w i l l a w a i t f o r t h e t r i g g e r e d g e n e r a t i o n b e f o r e c o n t i n u i n g .
< / d i v >
` ,
2024-06-29 00:25:10 +02:00
} ) ) ;
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'hide' ,
callback : hideMessageCallback ,
unnamedArgumentList : [
SlashCommandArgument . fromProps ( {
description : 'message index (starts with 0) or range' ,
typeList : [ ARGUMENT _TYPE . NUMBER , ARGUMENT _TYPE . RANGE ] ,
isRequired : true ,
enumProvider : commonEnumProviders . messages ( ) ,
} ) ,
] ,
helpString : 'Hides a chat message from the prompt.' ,
} ) ) ;
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'unhide' ,
callback : unhideMessageCallback ,
unnamedArgumentList : [
SlashCommandArgument . fromProps ( {
description : 'message index (starts with 0) or range' ,
typeList : [ ARGUMENT _TYPE . NUMBER , ARGUMENT _TYPE . RANGE ] ,
isRequired : true ,
enumProvider : commonEnumProviders . messages ( ) ,
} ) ,
] ,
helpString : 'Unhides a message from the prompt.' ,
} ) ) ;
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'member-disable' ,
callback : disableGroupMemberCallback ,
aliases : [ 'disable' , 'disablemember' , 'memberdisable' ] ,
unnamedArgumentList : [
SlashCommandArgument . fromProps ( {
description : 'member index (starts with 0) or name' ,
typeList : [ ARGUMENT _TYPE . NUMBER , ARGUMENT _TYPE . STRING ] ,
isRequired : true ,
enumProvider : commonEnumProviders . groupMembers ( ) ,
} ) ,
] ,
helpString : 'Disables a group member from being drafted for replies.' ,
} ) ) ;
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'member-enable' ,
aliases : [ 'enable' , 'enablemember' , 'memberenable' ] ,
callback : enableGroupMemberCallback ,
unnamedArgumentList : [
SlashCommandArgument . fromProps ( {
description : 'member index (starts with 0) or name' ,
typeList : [ ARGUMENT _TYPE . NUMBER , ARGUMENT _TYPE . STRING ] ,
isRequired : true ,
enumProvider : commonEnumProviders . groupMembers ( ) ,
} ) ,
] ,
helpString : 'Enables a group member to be drafted for replies.' ,
} ) ) ;
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'member-add' ,
callback : addGroupMemberCallback ,
aliases : [ 'addmember' , 'memberadd' ] ,
unnamedArgumentList : [
SlashCommandArgument . fromProps ( {
description : 'character name' ,
typeList : [ ARGUMENT _TYPE . STRING ] ,
isRequired : true ,
enumProvider : ( ) => selected _group ? commonEnumProviders . characters ( 'character' ) ( ) : [ ] ,
} ) ,
] ,
helpString : `
2024-05-12 21:15:05 +02:00
< div >
Adds a new group member to the group chat .
< / d i v >
< div >
< strong > Example : < / s t r o n g >
< ul >
< li >
2024-05-17 19:59:00 +02:00
< pre > < code > / m e m b e r - a d d J o h n D o e < / c o d e > < / p r e >
2024-05-12 21:15:05 +02:00
< / l i >
< / u l >
< / d i v >
` ,
2024-06-29 00:25:10 +02:00
} ) ) ;
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'member-remove' ,
callback : removeGroupMemberCallback ,
aliases : [ 'removemember' , 'memberremove' ] ,
unnamedArgumentList : [
SlashCommandArgument . fromProps ( {
description : 'member index (starts with 0) or name' ,
typeList : [ ARGUMENT _TYPE . NUMBER , ARGUMENT _TYPE . STRING ] ,
isRequired : true ,
enumProvider : commonEnumProviders . groupMembers ( ) ,
} ) ,
] ,
helpString : `
2024-05-12 21:15:05 +02:00
< div >
Removes a group member from the group chat .
< / d i v >
< div >
< strong > Example : < / s t r o n g >
< ul >
< li >
2024-05-17 19:59:00 +02:00
< pre > < code > / m e m b e r - r e m o v e 2 < / c o d e > < / p r e >
< pre > < code > / m e m b e r - r e m o v e J o h n D o e < / c o d e > < / p r e >
2024-05-12 21:15:05 +02:00
< / l i >
< / u l >
< / d i v >
` ,
2024-06-29 00:25:10 +02:00
} ) ) ;
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'member-up' ,
callback : moveGroupMemberUpCallback ,
aliases : [ 'upmember' , 'memberup' ] ,
unnamedArgumentList : [
SlashCommandArgument . fromProps ( {
description : 'member index (starts with 0) or name' ,
typeList : [ ARGUMENT _TYPE . NUMBER , ARGUMENT _TYPE . STRING ] ,
isRequired : true ,
enumProvider : commonEnumProviders . groupMembers ( ) ,
} ) ,
] ,
helpString : 'Moves a group member up in the group chat list.' ,
} ) ) ;
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'member-down' ,
callback : moveGroupMemberDownCallback ,
aliases : [ 'downmember' , 'memberdown' ] ,
unnamedArgumentList : [
SlashCommandArgument . fromProps ( {
description : 'member index (starts with 0) or name' ,
typeList : [ ARGUMENT _TYPE . NUMBER , ARGUMENT _TYPE . STRING ] ,
isRequired : true ,
enumProvider : commonEnumProviders . groupMembers ( ) ,
} ) ,
] ,
helpString : 'Moves a group member down in the group chat list.' ,
} ) ) ;
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'peek' ,
callback : peekCallback ,
unnamedArgumentList : [
SlashCommandArgument . fromProps ( {
description : 'member index (starts with 0) or name' ,
typeList : [ ARGUMENT _TYPE . NUMBER , ARGUMENT _TYPE . STRING ] ,
isRequired : true ,
enumProvider : commonEnumProviders . groupMembers ( ) ,
} ) ,
] ,
helpString : `
2024-05-12 21:15:05 +02:00
< div >
Shows a group member character card without switching chats .
< / d i v >
< div >
< strong > Examples : < / s t r o n g >
< ul >
< li >
2024-06-23 17:01:16 +02:00
< pre > < code > / p e e k G l o r i a < / c o d e > < / p r e >
Shows the character card for the character named "Gloria" .
2024-05-12 21:15:05 +02:00
< / l i >
< / u l >
< / d i v >
` ,
2024-06-29 00:25:10 +02:00
} ) ) ;
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'delswipe' ,
callback : deleteSwipeCallback ,
2024-08-09 00:38:51 +02:00
returns : 'the new, currently selected swipe id' ,
2024-06-29 00:25:10 +02:00
aliases : [ 'swipedel' ] ,
unnamedArgumentList : [
SlashCommandArgument . fromProps ( {
description : '1-based swipe id' ,
typeList : [ ARGUMENT _TYPE . NUMBER ] ,
isRequired : true ,
enumProvider : ( ) => Array . isArray ( chat [ chat . length - 1 ] ? . swipes ) ?
chat [ chat . length - 1 ] . swipes . map ( ( /** @type {string} */ swipe , /** @type {number} */ i ) => new SlashCommandEnumValue ( String ( i + 1 ) , swipe , enumTypes . enum , enumIcons . message ) )
: [ ] ,
} ) ,
] ,
helpString : `
2024-05-12 21:15:05 +02:00
< div >
Deletes a swipe from the last chat message . If swipe id is not provided , it deletes the current swipe .
< / d i v >
< div >
< strong > Example : < / s t r o n g >
< ul >
< li >
< pre > < code > / d e l s w i p e < / c o d e > < / p r e >
Deletes the current swipe .
< / l i >
< li >
< pre > < code > / d e l s w i p e 2 < / c o d e > < / p r e >
Deletes the second swipe from the last chat message .
< / l i >
< / u l >
< / d i v >
` ,
2024-06-29 00:25:10 +02:00
} ) ) ;
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'echo' ,
callback : echoCallback ,
returns : 'the text' ,
namedArgumentList : [
new SlashCommandNamedArgument (
'title' , 'title of the toast message' , [ ARGUMENT _TYPE . STRING ] , false ,
) ,
SlashCommandNamedArgument . fromProps ( {
name : 'severity' ,
description : 'severity level of the toast message' ,
typeList : [ ARGUMENT _TYPE . STRING ] ,
defaultValue : 'info' ,
enumProvider : ( ) => [
new SlashCommandEnumValue ( 'info' , 'info' , enumTypes . macro , 'ℹ ️ ' ) ,
new SlashCommandEnumValue ( 'warning' , 'warning' , enumTypes . enum , '⚠️' ) ,
new SlashCommandEnumValue ( 'error' , 'error' , enumTypes . enum , '❗' ) ,
new SlashCommandEnumValue ( 'success' , 'success' , enumTypes . enum , '✅' ) ,
] ,
} ) ,
2024-06-29 02:16:36 +02:00
SlashCommandNamedArgument . fromProps ( {
2024-06-29 02:24:20 +02:00
name : 'timeout' ,
2024-06-29 02:16:36 +02:00
description : 'time in milliseconds to display the toast message. Set this and \'extendedTimeout\' to 0 to show indefinitely until dismissed.' ,
typeList : [ ARGUMENT _TYPE . NUMBER ] ,
defaultValue : ` ${ toastr . options . timeOut } ` ,
} ) ,
SlashCommandNamedArgument . fromProps ( {
name : 'extendedTimeout' ,
2024-06-29 02:24:20 +02:00
description : 'time in milliseconds to display the toast message. Set this and \'timeout\' to 0 to show indefinitely until dismissed.' ,
2024-06-29 02:16:36 +02:00
typeList : [ ARGUMENT _TYPE . NUMBER ] ,
defaultValue : ` ${ toastr . options . extendedTimeOut } ` ,
} ) ,
SlashCommandNamedArgument . fromProps ( {
name : 'preventDuplicates' ,
description : 'prevent duplicate toasts with the same message from being displayed.' ,
typeList : [ ARGUMENT _TYPE . BOOLEAN ] ,
defaultValue : 'false' ,
enumList : commonEnumProviders . boolean ( 'trueFalse' ) ( ) ,
} ) ,
SlashCommandNamedArgument . fromProps ( {
name : 'awaitDismissal' ,
description : 'wait for the toast to be dismissed before continuing.' ,
typeList : [ ARGUMENT _TYPE . BOOLEAN ] ,
defaultValue : 'false' ,
enumList : commonEnumProviders . boolean ( 'trueFalse' ) ( ) ,
} ) ,
2024-06-29 00:25:10 +02:00
] ,
unnamedArgumentList : [
new SlashCommandArgument (
'text' , [ ARGUMENT _TYPE . STRING ] , true ,
) ,
] ,
helpString : `
2024-05-12 21:15:05 +02:00
< div >
Echoes the provided text to a toast message . Useful for pipes debugging .
< / d i v >
< div >
< strong > Example : < / s t r o n g >
< ul >
< li >
< pre > < code > / e c h o t i t l e = " M y M e s s a g e " s e v e r i t y = i n f o T h i s i s a n i n f o m e s s a g e < / c o d e > < / p r e >
< / l i >
< / u l >
< / d i v >
` ,
2024-06-29 00:25:10 +02:00
} ) ) ;
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'gen' ,
callback : generateCallback ,
returns : 'generated text' ,
namedArgumentList : [
new SlashCommandNamedArgument (
'lock' , 'lock user input during generation' , [ ARGUMENT _TYPE . BOOLEAN ] , false , false , null , commonEnumProviders . boolean ( 'onOff' ) ( ) ,
) ,
SlashCommandNamedArgument . fromProps ( {
name : 'name' ,
description : 'in-prompt name for instruct mode' ,
typeList : [ ARGUMENT _TYPE . STRING ] ,
defaultValue : 'System' ,
enumProvider : ( ) => [ ... commonEnumProviders . characters ( 'character' ) ( ) , new SlashCommandEnumValue ( 'System' , null , enumTypes . enum , enumIcons . assistant ) ] ,
forceEnum : false ,
} ) ,
new SlashCommandNamedArgument (
'length' , 'API response length in tokens' , [ ARGUMENT _TYPE . NUMBER ] , false ,
) ,
SlashCommandNamedArgument . fromProps ( {
name : 'as' ,
description : 'role of the output prompt' ,
typeList : [ ARGUMENT _TYPE . STRING ] ,
enumList : [
new SlashCommandEnumValue ( 'system' , null , enumTypes . enum , enumIcons . assistant ) ,
new SlashCommandEnumValue ( 'char' , null , enumTypes . enum , enumIcons . character ) ,
] ,
} ) ,
] ,
unnamedArgumentList : [
new SlashCommandArgument (
'prompt' , [ ARGUMENT _TYPE . STRING ] , true ,
) ,
] ,
helpString : `
2024-05-12 21:15:05 +02:00
< div >
Generates text using the provided prompt and passes it to the next command through the pipe , optionally locking user input while generating and allowing to configure the in - prompt name for instruct mode ( default = "System" ) .
< / d i v >
< div >
"as" argument controls the role of the output prompt : system ( default ) or char . If "length" argument is provided as a number in tokens , allows to temporarily override an API response length .
< / d i v >
` ,
2024-06-29 00:25:10 +02:00
} ) ) ;
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'genraw' ,
callback : generateRawCallback ,
returns : 'generated text' ,
namedArgumentList : [
new SlashCommandNamedArgument (
'lock' , 'lock user input during generation' , [ ARGUMENT _TYPE . BOOLEAN ] , false , false , null , commonEnumProviders . boolean ( 'onOff' ) ( ) ,
) ,
new SlashCommandNamedArgument (
'instruct' , 'use instruct mode' , [ ARGUMENT _TYPE . BOOLEAN ] , false , false , 'on' , commonEnumProviders . boolean ( 'onOff' ) ( ) ,
) ,
new SlashCommandNamedArgument (
'stop' , 'one-time custom stop strings' , [ ARGUMENT _TYPE . LIST ] , false ,
) ,
SlashCommandNamedArgument . fromProps ( {
name : 'as' ,
description : 'role of the output prompt' ,
typeList : [ ARGUMENT _TYPE . STRING ] ,
enumList : [
new SlashCommandEnumValue ( 'system' , null , enumTypes . enum , enumIcons . assistant ) ,
new SlashCommandEnumValue ( 'char' , null , enumTypes . enum , enumIcons . character ) ,
] ,
} ) ,
new SlashCommandNamedArgument (
'system' , 'system prompt at the start' , [ ARGUMENT _TYPE . STRING ] , false ,
) ,
new SlashCommandNamedArgument (
'length' , 'API response length in tokens' , [ ARGUMENT _TYPE . NUMBER ] , false ,
) ,
] ,
unnamedArgumentList : [
new SlashCommandArgument (
'prompt' , [ ARGUMENT _TYPE . STRING ] , true ,
) ,
] ,
helpString : `
2024-05-12 21:15:05 +02:00
< div >
Generates text using the provided prompt and passes it to the next command through the pipe , optionally locking user input while generating . Does not include chat history or character card .
< / d i v >
< div >
Use instruct = off to skip instruct formatting , e . g . < pre > < code > / g e n r a w i n s t r u c t = o f f W h y i s t h e s k y b l u e ? < / c o d e > < / p r e >
< / d i v >
< div >
Use stop = ... with a JSON - serialized array to add one - time custom stop strings , e . g . < pre > < code > / g e n r a w s t o p = [ " \ \ n " ] S a y h i < / c o d e > < / p r e >
< / d i v >
< div >
"as" argument controls the role of the output prompt : system ( default ) or char . "system" argument adds an ( optional ) system prompt at the start .
< / d i v >
< div >
If "length" argument is provided as a number in tokens , allows to temporarily override an API response length .
< / d i v >
` ,
2024-06-29 00:25:10 +02:00
} ) ) ;
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'addswipe' ,
callback : addSwipeCallback ,
2024-08-09 00:37:26 +02:00
returns : 'the new swipe id' ,
2024-06-29 00:25:10 +02:00
aliases : [ 'swipeadd' ] ,
2024-08-09 00:37:26 +02:00
namedArgumentList : [
SlashCommandNamedArgument . fromProps ( {
name : 'switch' ,
description : 'switch to the new swipe' ,
typeList : [ ARGUMENT _TYPE . BOOLEAN ] ,
enumList : commonEnumProviders . boolean ( ) ( ) ,
} ) ,
] ,
2024-06-29 00:25:10 +02:00
unnamedArgumentList : [
new SlashCommandArgument (
'text' , [ ARGUMENT _TYPE . STRING ] , true ,
) ,
] ,
2024-08-09 00:37:26 +02:00
helpString : `
< div >
Adds a swipe to the last chat message .
< / d i v >
< div >
Use switch = true to switch to directly switch to the new swipe .
< / d i v > ` ,
2024-06-29 00:25:10 +02:00
} ) ) ;
2024-07-03 14:44:17 +02:00
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'stop' ,
callback : ( ) => {
const stopped = stopGeneration ( ) ;
return String ( stopped ) ;
} ,
returns : 'true/false, whether the generation was running and got stopped' ,
helpString : `
< div >
Stops the generation and any streaming if it is currently running .
< / d i v >
< div >
Note : This command cannot be executed from the chat input , as sending any message or script from there is blocked during generation .
But it can be executed via automations or QR scripts / buttons .
< / d i v >
` ,
aliases : [ 'generate-stop' ] ,
} ) ) ;
2024-06-29 00:25:10 +02:00
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'abort' ,
callback : abortCallback ,
namedArgumentList : [
SlashCommandNamedArgument . fromProps ( {
name : 'quiet' ,
description : 'Whether to suppress the toast message notifying about the /abort call.' ,
typeList : [ ARGUMENT _TYPE . BOOLEAN ] ,
defaultValue : 'true' ,
} ) ,
] ,
unnamedArgumentList : [
SlashCommandArgument . fromProps ( {
description : 'The reason for aborting command execution. Shown when quiet=false' ,
typeList : [ ARGUMENT _TYPE . STRING ] ,
} ) ,
] ,
helpString : 'Aborts the slash command batch execution.' ,
} ) ) ;
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'fuzzy' ,
callback : fuzzyCallback ,
2024-07-11 20:05:42 +02:00
returns : 'matching item' ,
2024-06-29 00:25:10 +02:00
namedArgumentList : [
2024-07-11 20:05:42 +02:00
SlashCommandNamedArgument . fromProps ( {
name : 'list' ,
description : 'list of items to match against' ,
acceptsMultiple : false ,
isRequired : true ,
typeList : [ ARGUMENT _TYPE . LIST , ARGUMENT _TYPE . VARIABLE _NAME ] ,
enumProvider : commonEnumProviders . variables ( 'all' ) ,
} ) ,
SlashCommandNamedArgument . fromProps ( {
name : 'threshold' ,
description : 'fuzzy match threshold (0.0 to 1.0)' ,
typeList : [ ARGUMENT _TYPE . NUMBER ] ,
isRequired : false ,
defaultValue : '0.4' ,
acceptsMultiple : false ,
} ) ,
SlashCommandNamedArgument . fromProps ( {
name : 'mode' ,
description : 'fuzzy match mode' ,
typeList : [ ARGUMENT _TYPE . STRING ] ,
isRequired : false ,
defaultValue : 'first' ,
acceptsMultiple : false ,
2024-07-11 20:19:09 +02:00
enumList : [
2024-07-11 20:05:42 +02:00
new SlashCommandEnumValue ( 'first' , 'first match below the threshold' , enumTypes . enum , enumIcons . default ) ,
new SlashCommandEnumValue ( 'best' , 'best match below the threshold' , enumTypes . enum , enumIcons . default ) ,
] ,
} ) ,
2024-06-29 00:25:10 +02:00
] ,
unnamedArgumentList : [
new SlashCommandArgument (
'text to search' , [ ARGUMENT _TYPE . STRING ] , true ,
) ,
] ,
helpString : `
2024-05-12 21:15:05 +02:00
< div >
Performs a fuzzy match of each item in the < code > list < / c o d e > a g a i n s t t h e < c o d e > t e x t t o s e a r c h < / c o d e > .
If any item matches , then its name is returned . If no item matches the text , no value is returned .
< / d i v >
< div >
The optional < code > threshold < / c o d e > ( d e f a u l t i s 0 . 4 ) a l l o w s c o n t r o l o v e r t h e m a t c h s t r i c t n e s s .
A low value ( min 0.0 ) means the match is very strict .
At 1.0 ( max ) the match is very loose and will match anything .
< / d i v >
2024-07-11 20:05:42 +02:00
< div >
The optional < code > mode < / c o d e > a r g u m e n t a l l o w s t o c o n t r o l t h e b e h a v i o r w h e n m u l t i p l e i t e m s m a t c h t h e t e x t .
< ul >
< li > < code > first < / c o d e > ( d e f a u l t ) r e t u r n s t h e f i r s t m a t c h b e l o w t h e t h r e s h o l d . < / l i >
< li > < code > best < / c o d e > r e t u r n s t h e b e s t m a t c h b e l o w t h e t h r e s h o l d . < / l i >
< / u l >
< / d i v >
2024-05-12 21:15:05 +02:00
< div >
The returned value passes to the next command through the pipe .
< / d i v >
< div >
< strong > Example : < / s t r o n g >
< ul >
< li >
< pre > < code > / f u z z y l i s t = [ " a " , " b " , " c " ] t h r e s h o l d = 0 . 4 a b c < / c o d e > < / p r e >
< / l i >
< / u l >
< / d i v >
` ,
2024-06-29 00:25:10 +02:00
} ) ) ;
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'pass' ,
callback : ( _ , arg ) => {
// We do not support arrays of closures. Arrays of strings will be send as JSON
if ( Array . isArray ( arg ) && arg . some ( x => x instanceof SlashCommandClosure ) ) throw new Error ( 'Command /pass does not support multiple closures' ) ;
if ( Array . isArray ( arg ) ) return JSON . stringify ( arg ) ;
return arg ;
} ,
returns : 'the provided value' ,
unnamedArgumentList : [
new SlashCommandArgument (
'text' , [ ARGUMENT _TYPE . STRING , ARGUMENT _TYPE . NUMBER , ARGUMENT _TYPE . BOOLEAN , ARGUMENT _TYPE . LIST , ARGUMENT _TYPE . DICTIONARY , ARGUMENT _TYPE . CLOSURE ] , true ,
) ,
] ,
aliases : [ 'return' ] ,
helpString : `
2024-05-12 21:15:05 +02:00
< div >
< pre > < span class = "monospace" > / p a s s ( t e x t ) < / s p a n > – p a s s e s t h e t e x t t o t h e n e x t c o m m a n d t h r o u g h t h e p i p e . < / p r e >
< / d i v >
< div >
< strong > Example : < / s t r o n g >
< ul >
< li > < pre > < code > / p a s s H e l l o w o r l d < / c o d e > < / p r e > < / l i >
< / u l >
< / d i v >
` ,
2024-06-29 00:25:10 +02:00
} ) ) ;
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'delay' ,
callback : delayCallback ,
aliases : [ 'wait' , 'sleep' ] ,
unnamedArgumentList : [
new SlashCommandArgument (
'milliseconds' , [ ARGUMENT _TYPE . NUMBER ] , true ,
) ,
] ,
helpString : `
2024-05-12 21:15:05 +02:00
< div >
Delays the next command in the pipe by the specified number of milliseconds .
< / d i v >
< div >
< strong > Example : < / s t r o n g >
< ul >
< li >
< pre > < code > / d e l a y 1 0 0 0 < / c o d e > < / p r e >
< / l i >
< / u l >
< / d i v >
` ,
2024-06-29 00:25:10 +02:00
} ) ) ;
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'input' ,
aliases : [ 'prompt' ] ,
callback : inputCallback ,
returns : 'user input' ,
namedArgumentList : [
new SlashCommandNamedArgument (
'default' , 'default value of the input field' , [ ARGUMENT _TYPE . STRING ] , false , false , '"string"' ,
) ,
new SlashCommandNamedArgument (
'large' , 'show large input field' , [ ARGUMENT _TYPE . BOOLEAN ] , false , false , 'off' , commonEnumProviders . boolean ( 'onOff' ) ( ) ,
) ,
new SlashCommandNamedArgument (
'wide' , 'show wide input field' , [ ARGUMENT _TYPE . BOOLEAN ] , false , false , 'off' , commonEnumProviders . boolean ( 'onOff' ) ( ) ,
) ,
new SlashCommandNamedArgument (
'okButton' , 'text for the ok button' , [ ARGUMENT _TYPE . STRING ] , false ,
) ,
new SlashCommandNamedArgument (
'rows' , 'number of rows for the input field' , [ ARGUMENT _TYPE . NUMBER ] , false ,
) ,
] ,
unnamedArgumentList : [
new SlashCommandArgument (
'text to display' , [ ARGUMENT _TYPE . STRING ] , false ,
) ,
] ,
helpString : `
2024-05-12 21:15:05 +02:00
< div >
Shows a popup with the provided text and an input field .
The < code > default < / c o d e > a r g u m e n t i s t h e d e f a u l t v a l u e o f t h e i n p u t f i e l d , a n d t h e t e x t a r g u m e n t i s t h e t e x t t o d i s p l a y .
< / d i v >
` ,
2024-06-29 00:25:10 +02:00
} ) ) ;
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'run' ,
aliases : [ 'call' , 'exec' ] ,
callback : runCallback ,
returns : 'result of the executed closure of QR' ,
namedArgumentList : [
new SlashCommandNamedArgument (
'args' , 'named arguments' , [ ARGUMENT _TYPE . STRING , ARGUMENT _TYPE . NUMBER , ARGUMENT _TYPE . BOOLEAN , ARGUMENT _TYPE . LIST , ARGUMENT _TYPE . DICTIONARY ] , false , true ,
) ,
] ,
unnamedArgumentList : [
SlashCommandArgument . fromProps ( {
description : 'scoped variable or qr label' ,
2024-07-06 00:04:33 +02:00
typeList : [ ARGUMENT _TYPE . VARIABLE _NAME , ARGUMENT _TYPE . STRING , ARGUMENT _TYPE . CLOSURE ] ,
2024-06-29 00:25:10 +02:00
isRequired : true ,
2024-07-06 00:09:24 +02:00
enumProvider : ( executor , scope ) => [
... commonEnumProviders . variables ( 'scope' ) ( executor , scope ) ,
2024-06-29 00:25:10 +02:00
... ( typeof window [ 'qrEnumProviderExecutables' ] === 'function' ) ? window [ 'qrEnumProviderExecutables' ] ( ) : [ ] ,
] ,
} ) ,
] ,
helpString : `
2024-05-12 21:15:05 +02:00
< div >
Runs a closure from a scoped variable , or a Quick Reply with the specified name from a currently active preset or from another preset .
Named arguments can be referenced in a QR with < code > { { arg : : key } } < / c o d e > .
< / d i v >
` ,
2024-06-29 00:25:10 +02:00
} ) ) ;
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'messages' ,
callback : getMessagesCallback ,
aliases : [ 'message' ] ,
namedArgumentList : [
new SlashCommandNamedArgument (
'names' , 'show message author names' , [ ARGUMENT _TYPE . BOOLEAN ] , false , false , 'off' , commonEnumProviders . boolean ( 'onOff' ) ( ) ,
) ,
new SlashCommandNamedArgument (
'hidden' , 'include hidden messages' , [ ARGUMENT _TYPE . BOOLEAN ] , false , false , 'on' , commonEnumProviders . boolean ( 'onOff' ) ( ) ,
) ,
SlashCommandNamedArgument . fromProps ( {
name : 'role' ,
description : 'filter messages by role' ,
typeList : [ ARGUMENT _TYPE . STRING ] ,
enumList : [
new SlashCommandEnumValue ( 'system' , null , enumTypes . enum , enumIcons . system ) ,
new SlashCommandEnumValue ( 'assistant' , null , enumTypes . enum , enumIcons . assistant ) ,
new SlashCommandEnumValue ( 'user' , null , enumTypes . enum , enumIcons . user ) ,
] ,
} ) ,
] ,
unnamedArgumentList : [
SlashCommandArgument . fromProps ( {
description : 'message index (starts with 0) or range' ,
typeList : [ ARGUMENT _TYPE . NUMBER , ARGUMENT _TYPE . RANGE ] ,
isRequired : true ,
enumProvider : commonEnumProviders . messages ( ) ,
} ) ,
] ,
returns : 'the specified message or range of messages as a string' ,
helpString : `
2024-05-12 21:15:05 +02:00
< div >
Returns the specified message or range of messages as a string .
< / d i v >
2024-05-13 20:36:55 +02:00
< div >
Use the < code > hidden = off < / c o d e > a r g u m e n t t o e x c l u d e h i d d e n m e s s a g e s .
< / d i v >
< div >
Use the < code > role < / c o d e > a r g u m e n t t o f i l t e r m e s s a g e s b y r o l e . P o s s i b l e v a l u e s a r e : s y s t e m , a s s i s t a n t , u s e r .
< / d i v >
2024-05-12 21:15:05 +02:00
< div >
< strong > Examples : < / s t r o n g >
< ul >
< li >
< pre > < code > / m e s s a g e s 1 0 < / c o d e > < / p r e >
Returns the 10 th message .
< / l i >
< li >
< pre > < code > / m e s s a g e s n a m e s = o n 5 - 1 0 < / c o d e > < / p r e >
Returns messages 5 through 10 with author names .
< / l i >
< / u l >
< / d i v >
` ,
2024-06-29 00:25:10 +02:00
} ) ) ;
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'setinput' ,
callback : setInputCallback ,
unnamedArgumentList : [
new SlashCommandArgument (
'text' , [ ARGUMENT _TYPE . STRING ] , true ,
) ,
] ,
helpString : `
2024-05-12 21:15:05 +02:00
< div >
Sets the user input to the specified text and passes it to the next command through the pipe .
< / d i v >
< div >
< strong > Example : < / s t r o n g >
< ul >
< li >
< pre > < code > / s e t i n p u t H e l l o w o r l d < / c o d e > < / p r e >
< / l i >
< / u l >
< / d i v >
` ,
2024-06-29 00:25:10 +02:00
} ) ) ;
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'popup' ,
callback : popupCallback ,
returns : 'popup text' ,
namedArgumentList : [
new SlashCommandNamedArgument (
'large' , 'show large popup' , [ ARGUMENT _TYPE . BOOLEAN ] , false , false , null , commonEnumProviders . boolean ( 'onOff' ) ( ) ,
) ,
new SlashCommandNamedArgument (
'wide' , 'show wide popup' , [ ARGUMENT _TYPE . BOOLEAN ] , false , false , null , commonEnumProviders . boolean ( 'onOff' ) ( ) ,
) ,
new SlashCommandNamedArgument (
'okButton' , 'text for the OK button' , [ ARGUMENT _TYPE . STRING ] , false ,
) ,
] ,
unnamedArgumentList : [
new SlashCommandArgument (
'text' , [ ARGUMENT _TYPE . STRING ] , true ,
) ,
] ,
helpString : `
2024-05-12 21:15:05 +02:00
< div >
Shows a blocking popup with the specified text and buttons .
Returns the popup text .
< / d i v >
< div >
< strong > Example : < / s t r o n g >
< ul >
< li >
< pre > < code > / p o p u p l a r g e = o n w i d e = o n o k B u t t o n = " S u b m i t " E n t e r s o m e t e x t : < / c o d e > < / p r e >
< / l i >
< / u l >
< / d i v >
` ,
2024-06-29 00:25:10 +02:00
} ) ) ;
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'buttons' ,
callback : buttonsCallback ,
returns : 'clicked button label' ,
namedArgumentList : [
new SlashCommandNamedArgument (
'labels' , 'button labels' , [ ARGUMENT _TYPE . LIST ] , true ,
) ,
] ,
unnamedArgumentList : [
new SlashCommandArgument (
'text' , [ ARGUMENT _TYPE . STRING ] , true ,
) ,
] ,
helpString : `
2024-05-12 21:15:05 +02:00
< div >
Shows a blocking popup with the specified text and buttons .
Returns the clicked button label into the pipe or empty string if canceled .
< / d i v >
< div >
< strong > Example : < / s t r o n g >
< ul >
< li >
< pre > < code > / b u t t o n s l a b e l s = [ " Y e s " , " N o " ] D o y o u w a n t t o c o n t i n u e ? < / c o d e > < / p r e >
< / l i >
< / u l >
< / d i v >
` ,
2024-06-29 00:25:10 +02:00
} ) ) ;
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'trimtokens' ,
callback : trimTokensCallback ,
returns : 'trimmed text' ,
namedArgumentList : [
new SlashCommandNamedArgument (
'limit' , 'number of tokens to keep' , [ ARGUMENT _TYPE . NUMBER ] , true ,
) ,
SlashCommandNamedArgument . fromProps ( {
name : 'direction' ,
description : 'trim direction' ,
typeList : [ ARGUMENT _TYPE . STRING ] ,
isRequired : true ,
enumList : [
new SlashCommandEnumValue ( 'start' , null , enumTypes . enum , '⏪' ) ,
new SlashCommandEnumValue ( 'end' , null , enumTypes . enum , '⏩' ) ,
] ,
} ) ,
] ,
unnamedArgumentList : [
new SlashCommandArgument (
'text' , [ ARGUMENT _TYPE . STRING ] , false ,
) ,
] ,
helpString : `
2024-05-12 21:15:05 +02:00
< div >
Trims the start or end of text to the specified number of tokens .
< / d i v >
< div >
< strong > Example : < / s t r o n g >
< ul >
< li >
< pre > < code > / t r i m t o k e n s l i m i t = 5 d i r e c t i o n = s t a r t T h i s i s a l o n g s e n t e n c e w i t h m a n y w o r d s < / c o d e > < / p r e >
< / l i >
< / u l >
< / d i v >
` ,
2024-06-29 00:25:10 +02:00
} ) ) ;
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'trimstart' ,
callback : trimStartCallback ,
returns : 'trimmed text' ,
unnamedArgumentList : [
new SlashCommandArgument (
'text' , [ ARGUMENT _TYPE . STRING ] , true ,
) ,
] ,
helpString : `
2024-05-12 21:15:05 +02:00
< div >
Trims the text to the start of the first full sentence .
< / d i v >
< div >
< strong > Example : < / s t r o n g >
< ul >
< li >
< pre > < code > / t r i m s t a r t T h i s i s a s e n t e n c e . A n d h e r e i s a n o t h e r s e n t e n c e . < / c o d e > < / p r e >
< / l i >
< / u l >
< / d i v >
` ,
2024-06-29 00:25:10 +02:00
} ) ) ;
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'trimend' ,
callback : trimEndCallback ,
returns : 'trimmed text' ,
unnamedArgumentList : [
new SlashCommandArgument (
'text' , [ ARGUMENT _TYPE . STRING ] , true ,
) ,
] ,
helpString : 'Trims the text to the end of the last full sentence.' ,
} ) ) ;
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'inject' ,
callback : injectCallback ,
namedArgumentList : [
SlashCommandNamedArgument . fromProps ( {
name : 'id' ,
description : 'injection ID or variable name pointing to ID' ,
typeList : [ ARGUMENT _TYPE . STRING ] ,
isRequired : true ,
enumProvider : commonEnumProviders . injects ,
} ) ,
new SlashCommandNamedArgument (
2024-07-27 23:26:44 +02:00
'position' , 'injection position' , [ ARGUMENT _TYPE . STRING ] , false , false , 'after' , [ 'before' , 'after' , 'chat' , 'none' ] ,
2024-06-29 00:25:10 +02:00
) ,
new SlashCommandNamedArgument (
'depth' , 'injection depth' , [ ARGUMENT _TYPE . NUMBER ] , false , false , '4' ,
) ,
new SlashCommandNamedArgument (
'scan' , 'include injection content into World Info scans' , [ ARGUMENT _TYPE . BOOLEAN ] , false , false , 'false' ,
) ,
SlashCommandNamedArgument . fromProps ( {
name : 'role' ,
description : 'role for in-chat injections' ,
typeList : [ ARGUMENT _TYPE . STRING ] ,
isRequired : false ,
enumList : [
new SlashCommandEnumValue ( 'system' , null , enumTypes . enum , enumIcons . system ) ,
new SlashCommandEnumValue ( 'assistant' , null , enumTypes . enum , enumIcons . assistant ) ,
new SlashCommandEnumValue ( 'user' , null , enumTypes . enum , enumIcons . user ) ,
] ,
} ) ,
new SlashCommandNamedArgument (
'ephemeral' , 'remove injection after generation' , [ ARGUMENT _TYPE . BOOLEAN ] , false , false , 'false' ,
) ,
] ,
unnamedArgumentList : [
new SlashCommandArgument (
'text' , [ ARGUMENT _TYPE . STRING ] , false ,
) ,
] ,
2024-07-27 23:26:44 +02:00
helpString : 'Injects a text into the LLM prompt for the current chat. Requires a unique injection ID. Positions: "before" main prompt, "after" main prompt, in-"chat", hidden with "none" (default: after). Depth: injection depth for the prompt (default: 4). Role: role for in-chat injections (default: system). Scan: include injection content into World Info scans (default: false). Hidden injects in "none" position are not inserted into the prompt but can be used for triggering WI entries.' ,
2024-06-29 00:25:10 +02:00
} ) ) ;
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'listinjects' ,
callback : listInjectsCallback ,
helpString : 'Lists all script injections for the current chat.' ,
} ) ) ;
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'flushinject' ,
aliases : [ 'flushinjects' ] ,
unnamedArgumentList : [
SlashCommandArgument . fromProps ( {
description : 'injection ID or a variable name pointing to ID' ,
typeList : [ ARGUMENT _TYPE . STRING ] ,
defaultValue : '' ,
enumProvider : commonEnumProviders . injects ,
} ) ,
] ,
callback : flushInjectsCallback ,
helpString : 'Removes a script injection for the current chat. If no ID is provided, removes all script injections.' ,
} ) ) ;
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'tokens' ,
callback : ( _ , text ) => {
if ( text instanceof SlashCommandClosure || Array . isArray ( text ) ) throw new Error ( 'Unnamed argument cannot be a closure for command /tokens' ) ;
return getTokenCountAsync ( text ) . then ( count => String ( count ) ) ;
} ,
returns : 'number of tokens' ,
unnamedArgumentList : [
new SlashCommandArgument (
'text' , [ ARGUMENT _TYPE . STRING ] , true ,
) ,
] ,
helpString : 'Counts the number of tokens in the provided text.' ,
} ) ) ;
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'model' ,
callback : modelCallback ,
returns : 'current model' ,
unnamedArgumentList : [
SlashCommandArgument . fromProps ( {
description : 'model name' ,
typeList : [ ARGUMENT _TYPE . STRING ] ,
enumProvider : ( ) => getModelOptions ( ) ? . options . map ( option => new SlashCommandEnumValue ( option . value , option . value !== option . text ? option . text : null ) ) ,
} ) ,
] ,
helpString : 'Sets the model for the current API. Gets the current model name if no argument is provided.' ,
} ) ) ;
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'setpromptentry' ,
aliases : [ 'setpromptentries' ] ,
callback : setPromptEntryCallback ,
namedArgumentList : [
SlashCommandNamedArgument . fromProps ( {
name : 'identifier' ,
description : 'Prompt entry identifier(s) to target' ,
typeList : [ ARGUMENT _TYPE . STRING , ARGUMENT _TYPE . LIST ] ,
acceptsMultiple : true ,
enumProvider : ( ) => {
const promptManager = setupChatCompletionPromptManager ( oai _settings ) ;
const prompts = promptManager . serviceSettings . prompts ;
return prompts . map ( prompt => new SlashCommandEnumValue ( prompt . identifier , prompt . name , enumTypes . enum ) ) ;
} ,
} ) ,
SlashCommandNamedArgument . fromProps ( {
name : 'name' ,
description : 'Prompt entry name(s) to target' ,
typeList : [ ARGUMENT _TYPE . STRING , ARGUMENT _TYPE . LIST ] ,
acceptsMultiple : true ,
enumProvider : ( ) => {
const promptManager = setupChatCompletionPromptManager ( oai _settings ) ;
const prompts = promptManager . serviceSettings . prompts ;
return prompts . map ( prompt => new SlashCommandEnumValue ( prompt . name , prompt . identifier , enumTypes . enum ) ) ;
} ,
} ) ,
] ,
unnamedArgumentList : [
SlashCommandArgument . fromProps ( {
description : 'Set entry/entries on or off' ,
typeList : [ ARGUMENT _TYPE . STRING ] ,
isRequired : true ,
acceptsMultiple : false ,
defaultValue : 'toggle' , // unnamed arguments don't support default values yet
enumList : commonEnumProviders . boolean ( 'onOffToggle' ) ( ) ,
} ) ,
] ,
helpString : 'Sets the specified prompt manager entry/entries on or off.' ,
} ) ) ;
2024-08-18 11:18:06 +02:00
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'pick-icon' ,
callback : async ( ) => ( ( await showFontAwesomePicker ( ) ) ? ? false ) . toString ( ) ,
2024-07-16 15:28:33 +02:00
returns : 'The chosen icon name or false if cancelled.' ,
helpString : `
< div > Opens a popup with all the available Font Awesome icons and returns the selected icon ' s name . < / d i v >
< div >
< strong > Example : < / s t r o n g >
< ul >
< li >
< pre > < code > / p i c k - i c o n | \ n / i f l e f t = { { p i p e } } r u l e = e q r i g h t = f a l s e \ n \ t e l s e = { : / e c h o c h o s e n i c o n : " { { p i p e } } " : } \ n \ t { : / e c h o c a n c e l l e d i c o n s e l e c t i o n : } \ n | < / c o d e > < / p r e >
< / l i >
< / u l >
< / d i v >
` ,
} ) ) ;
2024-08-18 11:02:15 +02:00
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'api-url' ,
callback : setApiUrlCallback ,
returns : 'the current API url' ,
aliases : [ 'server' ] ,
namedArgumentList : [
SlashCommandNamedArgument . fromProps ( {
name : 'api' ,
description : 'API to set/get the URL for - if not provided, current API is used' ,
typeList : [ ARGUMENT _TYPE . STRING ] ,
enumList : [
2024-08-18 11:18:06 +02:00
new SlashCommandEnumValue ( 'custom' , 'custom OpenAI-compatible' , enumTypes . getBasedOnIndex ( UNIQUE _APIS . findIndex ( x => x === 'openai' ) ) , 'O' ) ,
new SlashCommandEnumValue ( 'kobold' , 'KoboldAI Classic' , enumTypes . getBasedOnIndex ( UNIQUE _APIS . findIndex ( x => x === 'kobold' ) ) , 'K' ) ,
... Object . values ( textgen _types ) . map ( api => new SlashCommandEnumValue ( api , null , enumTypes . getBasedOnIndex ( UNIQUE _APIS . findIndex ( x => x === 'textgenerationwebui' ) ) , 'T' ) ) ,
2024-08-18 11:02:15 +02:00
] ,
} ) ,
SlashCommandNamedArgument . fromProps ( {
name : 'connect' ,
description : 'Whether to auto-connect to the API after setting the URL' ,
typeList : [ ARGUMENT _TYPE . BOOLEAN ] ,
defaultValue : 'true' ,
enumList : commonEnumProviders . boolean ( 'trueFalse' ) ( ) ,
} ) ,
] ,
unnamedArgumentList : [
SlashCommandArgument . fromProps ( {
description : 'API url to connect to' ,
typeList : [ ARGUMENT _TYPE . STRING ] ,
} ) ,
] ,
helpString : `
< div >
Set the API url / server url for the currently selected API , including the port . If no argument is provided , it will return the current API url .
< / d i v >
< div >
2024-08-18 11:18:06 +02:00
If a manual API is provided to < b > set < / b > t h e U R L , m a k e s u r e t o s e t < c o d e > c o n n e c t = f a l s e < / c o d e > , a s a u t o - c o n n e c t o n l y w o r k s f o r t h e c u r r e n t l y s e l e c t e d A P I ,
2024-08-18 11:02:15 +02:00
or consider switching to it with < code > / a p i < / c o d e > f i r s t .
< / d i v >
< div >
2024-08-18 11:18:06 +02:00
This slash command works for most of the Text Completion sources , KoboldAI Classic , and also Custom OpenAI compatible for the Chat Completion sources . If unsure which APIs are supported ,
2024-08-18 11:02:15 +02:00
check the auto - completion of the optional < code > api < / c o d e > a r g u m e n t o f t h i s c o m m a n d .
< / d i v >
` ,
} ) ) ;
2024-04-19 13:19:57 +02:00
2024-06-29 00:25:10 +02:00
registerVariableCommands ( ) ;
}
2023-07-20 19:32:15 +02:00
const NARRATOR _NAME _KEY = 'narrator_name' ;
const NARRATOR _NAME _DEFAULT = 'System' ;
2023-08-26 19:26:23 +02:00
export const COMMENT _NAME _DEFAULT = 'Note' ;
2023-11-28 00:44:13 +01:00
const SCRIPT _PROMPT _KEY = 'script_inject_' ;
2023-12-07 12:35:48 +01:00
function injectCallback ( args , value ) {
2023-11-28 00:44:13 +01:00
const positions = {
'before' : extension _prompt _types . BEFORE _PROMPT ,
'after' : extension _prompt _types . IN _PROMPT ,
'chat' : extension _prompt _types . IN _CHAT ,
2024-07-27 23:26:44 +02:00
'none' : extension _prompt _types . NONE ,
2023-11-28 00:44:13 +01:00
} ;
2024-03-23 17:44:40 +01:00
const roles = {
'system' : extension _prompt _roles . SYSTEM ,
'user' : extension _prompt _roles . USER ,
'assistant' : extension _prompt _roles . ASSISTANT ,
} ;
2023-11-28 00:44:13 +01:00
2024-06-28 03:53:23 +02:00
const id = args ? . id ;
2024-05-23 01:34:13 +02:00
const ephemeral = isTrueBoolean ( args ? . ephemeral ) ;
2023-11-28 00:44:13 +01:00
if ( ! id ) {
console . warn ( 'WARN: No ID provided for /inject command' ) ;
toastr . warning ( 'No ID provided for /inject command' ) ;
return '' ;
}
const defaultPosition = 'after' ;
const defaultDepth = 4 ;
const positionValue = args ? . position ? ? defaultPosition ;
2023-12-07 12:35:48 +01:00
const position = positions [ positionValue ] ? ? positions [ defaultPosition ] ;
2023-11-28 00:44:13 +01:00
const depthValue = Number ( args ? . depth ) ? ? defaultDepth ;
const depth = isNaN ( depthValue ) ? defaultDepth : depthValue ;
2024-03-23 17:44:40 +01:00
const roleValue = typeof args ? . role === 'string' ? args . role . toLowerCase ( ) . trim ( ) : Number ( args ? . role ? ? extension _prompt _roles . SYSTEM ) ;
const role = roles [ roleValue ] ? ? roles [ extension _prompt _roles . SYSTEM ] ;
const scan = isTrueBoolean ( args ? . scan ) ;
2023-11-28 00:44:13 +01:00
value = value || '' ;
const prefixedId = ` ${ SCRIPT _PROMPT _KEY } ${ id } ` ;
if ( ! chat _metadata . script _injects ) {
chat _metadata . script _injects = { } ;
}
2024-04-27 19:25:55 +02:00
if ( value ) {
const inject = { value , position , depth , scan , role } ;
chat _metadata . script _injects [ id ] = inject ;
} else {
delete chat _metadata . script _injects [ id ] ;
}
2023-11-28 00:44:13 +01:00
2024-03-23 17:44:40 +01:00
setExtensionPrompt ( prefixedId , value , position , depth , scan , role ) ;
2023-11-28 00:44:13 +01:00
saveMetadataDebounced ( ) ;
2024-05-23 01:34:13 +02:00
if ( ephemeral ) {
2024-05-24 20:53:29 +02:00
let deleted = false ;
2024-05-23 01:34:13 +02:00
const unsetInject = ( ) => {
2024-05-24 20:53:29 +02:00
if ( deleted ) {
return ;
}
2024-05-23 01:34:13 +02:00
console . log ( 'Removing ephemeral script injection' , id ) ;
delete chat _metadata . script _injects [ id ] ;
2024-05-24 20:53:29 +02:00
setExtensionPrompt ( prefixedId , '' , position , depth , scan , role ) ;
saveMetadataDebounced ( ) ;
deleted = true ;
2024-05-23 01:34:13 +02:00
} ;
eventSource . once ( event _types . GENERATION _ENDED , unsetInject ) ;
eventSource . once ( event _types . GENERATION _STOPPED , unsetInject ) ;
}
2023-11-28 00:44:13 +01:00
return '' ;
}
2023-12-07 12:35:48 +01:00
function listInjectsCallback ( ) {
2024-06-05 20:20:55 +02:00
if ( ! chat _metadata . script _injects || ! Object . keys ( chat _metadata . script _injects ) . length ) {
2023-11-28 00:44:13 +01:00
toastr . info ( 'No script injections for the current chat' ) ;
return '' ;
}
const injects = Object . entries ( chat _metadata . script _injects )
2023-12-07 12:35:48 +01:00
. map ( ( [ id , inject ] ) => {
2023-11-28 00:44:13 +01:00
const position = Object . entries ( extension _prompt _types ) ;
2023-12-07 12:35:48 +01:00
const positionName = position . find ( ( [ _ , value ] ) => value === inject . position ) ? . [ 0 ] ? ? 'unknown' ;
2024-03-23 17:44:40 +01:00
return ` * ** ${ id } **: <code> ${ inject . value } </code> ( ${ positionName } , depth: ${ inject . depth } , scan: ${ inject . scan ? ? false } , role: ${ inject . role ? ? extension _prompt _roles . SYSTEM } ) ` ;
2023-11-28 00:44:13 +01:00
} )
. join ( '\n' ) ;
const converter = new showdown . Converter ( ) ;
const messageText = ` ### Script injections: \n ${ injects } ` ;
const htmlMessage = DOMPurify . sanitize ( converter . makeHtml ( messageText ) ) ;
sendSystemMessage ( system _message _types . GENERIC , htmlMessage ) ;
}
2024-06-05 20:20:55 +02:00
/ * *
* Flushes script injections for the current chat .
2024-06-28 04:00:32 +02:00
* @ param { import ( './slash-commands/SlashCommand.js' ) . NamedArguments } _ Named arguments
2024-06-05 20:20:55 +02:00
* @ param { string } value Unnamed argument
* @ returns { string } Empty string
* /
2024-06-28 04:00:32 +02:00
function flushInjectsCallback ( _ , value ) {
2023-11-28 00:44:13 +01:00
if ( ! chat _metadata . script _injects ) {
return '' ;
}
2024-06-28 04:00:32 +02:00
const idArgument = value ;
2024-06-05 20:20:55 +02:00
2023-12-07 12:35:48 +01:00
for ( const [ id , inject ] of Object . entries ( chat _metadata . script _injects ) ) {
2024-06-05 20:20:55 +02:00
if ( idArgument && id !== idArgument ) {
continue ;
}
2023-11-28 00:44:13 +01:00
const prefixedId = ` ${ SCRIPT _PROMPT _KEY } ${ id } ` ;
2024-03-23 17:44:40 +01:00
setExtensionPrompt ( prefixedId , '' , inject . position , inject . depth , inject . scan , inject . role ) ;
2024-06-05 20:20:55 +02:00
delete chat _metadata . script _injects [ id ] ;
2023-11-28 00:44:13 +01:00
}
saveMetadataDebounced ( ) ;
return '' ;
}
2023-12-07 12:35:48 +01:00
export function processChatSlashCommands ( ) {
2023-11-28 00:44:13 +01:00
const context = getContext ( ) ;
if ( ! ( context . chatMetadata . script _injects ) ) {
return ;
}
for ( const id of Object . keys ( context . extensionPrompts ) ) {
if ( ! id . startsWith ( SCRIPT _PROMPT _KEY ) ) {
continue ;
}
console . log ( 'Removing script injection' , id ) ;
2023-12-07 12:35:48 +01:00
delete context . extensionPrompts [ id ] ;
2023-11-28 00:44:13 +01:00
}
2023-12-07 12:35:48 +01:00
for ( const [ id , inject ] of Object . entries ( context . chatMetadata . script _injects ) ) {
2023-11-28 00:44:13 +01:00
const prefixedId = ` ${ SCRIPT _PROMPT _KEY } ${ id } ` ;
console . log ( 'Adding script injection' , id ) ;
2024-03-23 17:44:40 +01:00
setExtensionPrompt ( prefixedId , inject . value , inject . position , inject . depth , inject . scan , inject . role ) ;
2023-11-28 00:44:13 +01:00
}
}
2023-07-20 19:32:15 +02:00
2023-12-07 12:35:48 +01:00
function setInputCallback ( _ , value ) {
2024-05-19 21:22:32 +02:00
$ ( '#send_textarea' ) . val ( value || '' ) [ 0 ] . dispatchEvent ( new Event ( 'input' , { bubbles : true } ) ) ;
2023-11-24 16:12:59 +01:00
return value ;
}
2023-12-07 12:35:48 +01:00
function trimStartCallback ( _ , value ) {
2023-11-26 12:55:22 +01:00
if ( ! value ) {
return '' ;
}
return trimToStartSentence ( value ) ;
}
2023-12-07 12:35:48 +01:00
function trimEndCallback ( _ , value ) {
2023-11-26 12:55:22 +01:00
if ( ! value ) {
return '' ;
}
return trimToEndSentence ( value ) ;
}
2024-04-13 20:33:19 +02:00
async function trimTokensCallback ( arg , value ) {
2023-11-26 12:55:22 +01:00
if ( ! value ) {
console . warn ( 'WARN: No argument provided for /trimtokens command' ) ;
return '' ;
}
const limit = Number ( resolveVariable ( arg . limit ) ) ;
if ( isNaN ( limit ) ) {
console . warn ( ` WARN: Invalid limit provided for /trimtokens command: ${ limit } ` ) ;
return value ;
}
if ( limit <= 0 ) {
return '' ;
}
const direction = arg . direction || 'end' ;
2024-04-13 20:33:19 +02:00
const tokenCount = await getTokenCountAsync ( value ) ;
2023-11-26 12:55:22 +01:00
// Token count is less than the limit, do nothing
if ( tokenCount <= limit ) {
return value ;
}
const { tokenizerName , tokenizerId } = getFriendlyTokenizerName ( main _api ) ;
console . debug ( 'Requesting tokenization for /trimtokens command' , tokenizerName ) ;
try {
const textTokens = getTextTokens ( tokenizerId , value ) ;
if ( ! Array . isArray ( textTokens ) || ! textTokens . length ) {
console . warn ( 'WARN: No tokens returned for /trimtokens command, falling back to estimation' ) ;
const percentage = limit / tokenCount ;
const trimIndex = Math . floor ( value . length * percentage ) ;
const trimmedText = direction === 'start' ? value . substring ( trimIndex ) : value . substring ( 0 , value . length - trimIndex ) ;
return trimmedText ;
}
const sliceTokens = direction === 'start' ? textTokens . slice ( 0 , limit ) : textTokens . slice ( - limit ) ;
2024-01-23 06:00:31 +01:00
const { text } = decodeTextTokens ( tokenizerId , sliceTokens ) ;
return text ;
2023-11-26 12:55:22 +01:00
} catch ( error ) {
console . warn ( 'WARN: Tokenization failed for /trimtokens command, returning original' , error ) ;
return value ;
}
}
2023-12-07 12:35:48 +01:00
async function buttonsCallback ( args , text ) {
2023-11-26 16:05:55 +01:00
try {
2024-06-19 01:40:22 +02:00
/** @type {string[]} */
2023-11-26 16:05:55 +01:00
const buttons = JSON . parse ( resolveVariable ( args ? . labels ) ) ;
if ( ! Array . isArray ( buttons ) || ! buttons . length ) {
console . warn ( 'WARN: Invalid labels provided for /buttons command' ) ;
return '' ;
}
2024-06-19 01:40:22 +02:00
// Map custom buttons to results. Start at 2 because 1 and 0 are reserved for ok and cancel
const resultToButtonMap = new Map ( buttons . map ( ( button , index ) => [ index + 2 , button ] ) ) ;
2023-11-26 16:05:55 +01:00
return new Promise ( async ( resolve ) => {
const safeValue = DOMPurify . sanitize ( text || '' ) ;
2024-06-19 01:40:22 +02:00
/** @type {Popup} */
let popup ;
2023-11-26 16:05:55 +01:00
const buttonContainer = document . createElement ( 'div' ) ;
buttonContainer . classList . add ( 'flex-container' , 'flexFlowColumn' , 'wide100p' , 'm-t-1' ) ;
2024-06-19 01:40:22 +02:00
for ( const [ result , button ] of resultToButtonMap ) {
2023-11-26 16:05:55 +01:00
const buttonElement = document . createElement ( 'div' ) ;
2024-06-19 01:40:22 +02:00
buttonElement . classList . add ( 'menu_button' , 'result-control' , 'wide100p' ) ;
buttonElement . dataset . result = String ( result ) ;
2024-06-30 20:44:29 +02:00
buttonElement . addEventListener ( 'click' , async ( ) => {
await popup . complete ( result ) ;
2023-11-26 16:05:55 +01:00
} ) ;
buttonElement . innerText = button ;
buttonContainer . appendChild ( buttonElement ) ;
}
const popupContainer = document . createElement ( 'div' ) ;
popupContainer . innerHTML = safeValue ;
popupContainer . appendChild ( buttonContainer ) ;
2024-06-19 01:40:22 +02:00
popup = new Popup ( popupContainer , POPUP _TYPE . TEXT , '' , { okButton : 'Cancel' } ) ;
popup . show ( )
. then ( ( result => resolve ( typeof result === 'number' ? resultToButtonMap . get ( result ) ? ? '' : '' ) ) )
2023-11-26 16:05:55 +01:00
. catch ( ( ) => resolve ( '' ) ) ;
2023-12-02 20:11:06 +01:00
} ) ;
2023-11-26 16:05:55 +01:00
} catch {
return '' ;
}
}
2023-12-07 12:35:48 +01:00
async function popupCallback ( args , value ) {
2023-11-24 18:50:49 +01:00
const safeValue = DOMPurify . sanitize ( value || '' ) ;
2023-12-07 12:35:48 +01:00
const popupOptions = {
2023-12-07 02:26:29 +01:00
large : isTrueBoolean ( args ? . large ) ,
wide : isTrueBoolean ( args ? . wide ) ,
2023-12-07 12:35:48 +01:00
okButton : args ? . okButton !== undefined && typeof args ? . okButton === 'string' ? args . okButton : 'Ok' ,
2023-12-07 02:26:29 +01:00
} ;
2023-11-24 18:50:49 +01:00
await delay ( 1 ) ;
2024-06-19 01:40:22 +02:00
await callGenericPopup ( safeValue , POPUP _TYPE . TEXT , '' , popupOptions ) ;
2023-11-24 18:50:49 +01:00
await delay ( 1 ) ;
2024-06-21 20:04:55 +02:00
return String ( value ) ;
2023-11-24 18:50:49 +01:00
}
2024-08-18 20:49:23 +02:00
async function getMessagesCallback ( args , value ) {
2023-11-24 16:12:59 +01:00
const includeNames = ! isFalseBoolean ( args ? . names ) ;
2024-05-13 20:36:55 +02:00
const includeHidden = isTrueBoolean ( args ? . hidden ) ;
const role = args ? . role ;
2023-11-24 16:12:59 +01:00
const range = stringToRange ( value , 0 , chat . length - 1 ) ;
if ( ! range ) {
2024-05-13 20:36:55 +02:00
console . warn ( ` WARN: Invalid range provided for /messages command: ${ value } ` ) ;
2023-11-24 16:12:59 +01:00
return '' ;
}
2024-05-13 20:36:55 +02:00
const filterByRole = ( mes ) => {
if ( ! role ) {
return true ;
}
2024-05-13 21:45:06 +02:00
const isNarrator = mes . extra ? . type === system _message _types . NARRATOR ;
2024-05-13 20:36:55 +02:00
if ( role === 'system' ) {
2024-05-13 21:45:06 +02:00
return isNarrator && ! mes . is _user ;
2024-05-13 20:36:55 +02:00
}
if ( role === 'assistant' ) {
2024-05-13 21:45:06 +02:00
return ! isNarrator && ! mes . is _user ;
2024-05-13 20:36:55 +02:00
}
if ( role === 'user' ) {
2024-05-13 21:45:06 +02:00
return ! isNarrator && mes . is _user ;
2024-05-13 20:36:55 +02:00
}
throw new Error ( ` Invalid role provided. Expected one of: system, assistant, user. Got: ${ role } ` ) ;
} ;
2024-08-18 20:49:23 +02:00
const processMessage = async ( mesId ) => {
const msg = chat [ mesId ] ;
if ( ! msg ) {
console . warn ( ` WARN: No message found with ID ${ mesId } ` ) ;
return null ;
2023-11-24 16:12:59 +01:00
}
2024-08-18 20:49:23 +02:00
if ( role && ! filterByRole ( msg ) ) {
console . debug ( ` /messages: Skipping message with ID ${ mesId } due to role filter ` ) ;
return null ;
2024-05-13 20:36:55 +02:00
}
2024-08-18 20:49:23 +02:00
if ( ! includeHidden && msg . is _system ) {
console . debug ( ` /messages: Skipping hidden message with ID ${ mesId } ` ) ;
return null ;
2023-11-26 00:15:19 +01:00
}
2024-08-18 20:49:23 +02:00
return includeNames ? ` ${ msg . name } : ${ msg . mes } ` : msg . mes ;
} ;
2024-08-18 22:37:31 +02:00
const messagePromises = [ ] ;
2024-08-18 20:49:23 +02:00
for ( let rInd = range . start ; rInd <= range . end ; ++ rInd )
2024-08-18 22:37:31 +02:00
messagePromises . push ( processMessage ( rInd ) ) ;
2024-08-18 20:49:23 +02:00
const messages = await Promise . all ( messagePromises ) ;
2024-08-18 21:44:48 +02:00
return messages . filter ( m => m !== null ) . join ( '\n\n' ) ;
2023-11-24 16:12:59 +01:00
}
2023-12-27 13:28:15 +01:00
async function runCallback ( args , name ) {
2023-11-24 14:58:00 +01:00
if ( ! name ) {
2024-05-12 21:15:05 +02:00
throw new Error ( 'No name provided for /run command' ) ;
}
2024-07-06 00:04:33 +02:00
if ( name instanceof SlashCommandClosure ) {
2024-07-23 22:27:27 +02:00
name . breakController = new SlashCommandBreakController ( ) ;
2024-07-12 21:05:39 +02:00
return ( await name . execute ( ) ) ? . pipe ;
2024-07-06 00:04:33 +02:00
}
2024-05-12 21:15:05 +02:00
/**@type {SlashCommandScope} */
const scope = args . _scope ;
if ( scope . existsVariable ( name ) ) {
const closure = scope . getVariable ( name ) ;
if ( ! ( closure instanceof SlashCommandClosure ) ) {
throw new Error ( ` " ${ name } " is not callable. ` ) ;
}
closure . scope . parent = scope ;
2024-07-04 18:31:27 +02:00
closure . breakController = new SlashCommandBreakController ( ) ;
2024-06-27 17:49:12 +02:00
if ( args . _debugController && ! closure . debugController ) {
closure . debugController = args . _debugController ;
}
while ( closure . providedArgumentList . pop ( ) ) ;
2024-05-19 21:22:32 +02:00
closure . argumentList . forEach ( arg => {
2024-05-12 21:15:05 +02:00
if ( Object . keys ( args ) . includes ( arg . name ) ) {
const providedArg = new SlashCommandNamedArgumentAssignment ( ) ;
providedArg . name = arg . name ;
providedArg . value = args [ arg . name ] ;
closure . providedArgumentList . push ( providedArg ) ;
}
} ) ;
const result = await closure . execute ( ) ;
return result . pipe ;
2023-11-24 14:58:00 +01:00
}
2023-12-07 12:35:48 +01:00
if ( typeof window [ 'executeQuickReplyByName' ] !== 'function' ) {
2024-05-12 21:15:05 +02:00
throw new Error ( 'Quick Reply extension is not loaded' ) ;
2023-11-24 14:58:00 +01:00
}
try {
name = name . trim ( ) ;
2024-07-09 00:07:37 +02:00
/**@type {ExecuteSlashCommandsOptions} */
const options = {
abortController : args . _abortController ,
debugController : args . _debugController ,
} ;
return await window [ 'executeQuickReplyByName' ] ( name , args , options ) ;
2023-11-24 14:58:00 +01:00
} catch ( error ) {
2024-07-06 00:04:50 +02:00
throw new Error ( ` Error running Quick Reply " ${ name } ": ${ error . message } ` ) ;
2023-11-24 14:58:00 +01:00
}
}
2024-05-18 20:48:31 +02:00
/ * *
*
2024-06-21 20:04:55 +02:00
* @ param { import ( './slash-commands/SlashCommand.js' ) . NamedArguments } param0
2024-05-18 20:48:31 +02:00
* @ param { string } [ reason ]
* /
function abortCallback ( { _abortController , quiet } , reason ) {
2024-06-21 20:04:55 +02:00
if ( quiet instanceof SlashCommandClosure ) throw new Error ( 'argument \'quiet\' cannot be a closure for command /abort' ) ;
2024-05-18 20:48:31 +02:00
_abortController . abort ( ( reason ? ? '' ) . toString ( ) . length == 0 ? '/abort command executed' : reason , ! isFalseBoolean ( quiet ? ? 'true' ) ) ;
2024-06-20 20:33:45 +02:00
return '' ;
2023-11-23 23:18:07 +01:00
}
2023-12-07 12:35:48 +01:00
async function delayCallback ( _ , amount ) {
2023-11-24 11:49:14 +01:00
if ( ! amount ) {
console . warn ( 'WARN: No amount provided for /delay command' ) ;
2024-06-14 23:48:41 +02:00
return '' ;
2023-11-24 11:49:14 +01:00
}
amount = Number ( amount ) ;
if ( isNaN ( amount ) ) {
amount = 0 ;
}
await delay ( amount ) ;
2024-06-14 23:48:41 +02:00
return '' ;
2023-11-24 11:49:14 +01:00
}
2023-12-07 12:35:48 +01:00
async function inputCallback ( args , prompt ) {
2023-12-07 02:26:29 +01:00
const safeValue = DOMPurify . sanitize ( prompt || '' ) ;
2023-12-07 12:44:49 +01:00
const defaultInput = args ? . default !== undefined && typeof args ? . default === 'string' ? args . default : '' ;
2023-12-07 02:26:29 +01:00
const popupOptions = {
large : isTrueBoolean ( args ? . large ) ,
wide : isTrueBoolean ( args ? . wide ) ,
2023-12-07 12:35:48 +01:00
okButton : args ? . okButton !== undefined && typeof args ? . okButton === 'string' ? args . okButton : 'Ok' ,
2023-12-07 02:26:29 +01:00
rows : args ? . rows !== undefined && typeof args ? . rows === 'string' ? isNaN ( Number ( args . rows ) ) ? 4 : Number ( args . rows ) : 4 ,
} ;
2023-11-24 11:49:14 +01:00
// Do not remove this delay, otherwise the prompt will not show up
await delay ( 1 ) ;
2024-06-21 20:04:55 +02:00
const result = await callGenericPopup ( safeValue , POPUP _TYPE . INPUT , defaultInput , popupOptions ) ;
2023-11-24 11:49:14 +01:00
await delay ( 1 ) ;
2024-06-23 17:16:15 +02:00
return String ( result || '' ) ;
2023-11-24 11:49:14 +01:00
}
2024-03-03 03:43:44 +01:00
/ * *
* Each item in "args.list" is searched within "search_item" using fuzzy search . If any matches it returns the matched "item" .
2024-03-03 15:04:48 +01:00
* @ param { FuzzyCommandArgs } args - arguments containing "list" ( JSON array ) and optionaly "threshold" ( float between 0.0 and 1.0 )
* @ param { string } searchInValue - the string where items of list are searched
* @ returns { string } - the matched item from the list
2024-07-11 20:05:42 +02:00
* @ typedef { { list : string , threshold : string , mode : string } } FuzzyCommandArgs - arguments for / f u z z y c o m m a n d
2024-03-03 15:04:48 +01:00
* @ example / fuzzy list = [ "down" , "left" , "up" , "right" ] "he looks up" | /echo / / should return "up"
* @ link https : //www.fusejs.io/
2024-03-03 03:43:44 +01:00
* /
2024-03-03 15:04:48 +01:00
function fuzzyCallback ( args , searchInValue ) {
if ( ! searchInValue ) {
2023-11-24 01:21:50 +01:00
console . warn ( 'WARN: No argument provided for /fuzzy command' ) ;
return '' ;
}
if ( ! args . list ) {
console . warn ( 'WARN: No list argument provided for /fuzzy command' ) ;
return '' ;
}
try {
2023-11-24 16:41:49 +01:00
const list = JSON . parse ( resolveVariable ( args . list ) ) ;
2023-11-24 01:21:50 +01:00
if ( ! Array . isArray ( list ) ) {
console . warn ( 'WARN: Invalid list argument provided for /fuzzy command' ) ;
return '' ;
}
2024-03-03 03:43:44 +01:00
const params = {
2023-11-24 01:21:50 +01:00
includeScore : true ,
findAllMatches : true ,
ignoreLocation : true ,
2024-03-03 03:43:44 +01:00
threshold : 0.4 ,
} ;
// threshold determines how strict is the match, low threshold value is very strict, at 1 (nearly?) everything matches
2024-03-03 15:04:48 +01:00
if ( 'threshold' in args ) {
2024-07-11 20:05:42 +02:00
params . threshold = parseFloat ( args . threshold ) ;
2024-03-03 15:04:48 +01:00
if ( isNaN ( params . threshold ) ) {
2024-03-03 03:43:44 +01:00
console . warn ( 'WARN: \'threshold\' argument must be a float between 0.0 and 1.0 for /fuzzy command' ) ;
return '' ;
}
2024-03-03 15:04:48 +01:00
if ( params . threshold < 0 ) {
2024-03-03 03:43:44 +01:00
params . threshold = 0 ;
}
2024-03-03 15:04:48 +01:00
if ( params . threshold > 1 ) {
2024-03-03 03:43:44 +01:00
params . threshold = 1 ;
}
}
2024-07-11 20:05:42 +02:00
function getFirstMatch ( ) {
const fuse = new Fuse ( [ searchInValue ] , params ) ;
// each item in the "list" is searched within "search_item", if any matches it returns the matched "item"
for ( const searchItem of list ) {
const result = fuse . search ( searchItem ) ;
console . debug ( '/fuzzy: result' , result ) ;
if ( result . length > 0 ) {
console . info ( '/fuzzy: first matched' , searchItem ) ;
return searchItem ;
}
}
console . info ( '/fuzzy: no match' ) ;
return '' ;
}
function getBestMatch ( ) {
const fuse = new Fuse ( list , params ) ;
const result = fuse . search ( searchInValue ) ;
console . debug ( '/fuzzy: result' , result ) ;
2024-03-03 15:04:48 +01:00
if ( result . length > 0 ) {
2024-07-11 20:05:42 +02:00
console . info ( '/fuzzy: best matched' , result [ 0 ] . item ) ;
return result [ 0 ] . item ;
2024-03-03 03:43:44 +01:00
}
2024-07-11 20:05:42 +02:00
console . info ( '/fuzzy: no match' ) ;
return '' ;
}
switch ( String ( args . mode ) . trim ( ) . toLowerCase ( ) ) {
case 'best' :
return getBestMatch ( ) ;
case 'first' :
default :
return getFirstMatch ( ) ;
2024-03-03 03:43:44 +01:00
}
2023-11-24 01:21:50 +01:00
} catch {
console . warn ( 'WARN: Invalid list argument provided for /fuzzy command' ) ;
return '' ;
}
}
2023-11-24 18:06:31 +01:00
function setEphemeralStopStrings ( value ) {
if ( typeof value === 'string' && value . length ) {
2023-11-23 23:51:27 +01:00
try {
2023-11-24 18:06:31 +01:00
const stopStrings = JSON . parse ( value ) ;
2023-11-23 23:51:27 +01:00
if ( Array . isArray ( stopStrings ) ) {
2023-12-07 02:26:29 +01:00
stopStrings . forEach ( stopString => addEphemeralStoppingString ( stopString ) ) ;
2023-11-23 23:51:27 +01:00
}
} catch {
// Do nothing
}
}
2023-11-24 18:06:31 +01:00
}
2023-12-07 12:35:48 +01:00
async function generateRawCallback ( args , value ) {
2023-11-24 18:06:31 +01:00
if ( ! value ) {
console . warn ( 'WARN: No argument provided for /genraw command' ) ;
return ;
}
// Prevent generate recursion
2024-05-19 21:22:32 +02:00
$ ( '#send_textarea' ) . val ( '' ) [ 0 ] . dispatchEvent ( new Event ( 'input' , { bubbles : true } ) ) ;
2023-11-24 18:06:31 +01:00
const lock = isTrueBoolean ( args ? . lock ) ;
2024-03-28 20:51:02 +01:00
const as = args ? . as || 'system' ;
const quietToLoud = as === 'char' ;
2024-03-31 18:42:12 +02:00
const systemPrompt = resolveVariable ( args ? . system ) || '' ;
2024-03-31 20:02:38 +02:00
const length = Number ( resolveVariable ( args ? . length ) ? ? 0 ) || 0 ;
2023-11-23 23:51:27 +01:00
2023-11-24 14:18:49 +01:00
try {
if ( lock ) {
deactivateSendButtons ( ) ;
}
2023-11-25 23:56:55 +01:00
setEphemeralStopStrings ( resolveVariable ( args ? . stop ) ) ;
2024-03-31 20:02:38 +02:00
const result = await generateRaw ( value , '' , isFalseBoolean ( args ? . instruct ) , quietToLoud , systemPrompt , length ) ;
2023-11-24 14:18:49 +01:00
return result ;
} finally {
if ( lock ) {
activateSendButtons ( ) ;
}
2023-11-24 18:06:31 +01:00
flushEphemeralStoppingStrings ( ) ;
2023-11-24 14:18:49 +01:00
}
2023-11-23 21:50:13 +01:00
}
2023-12-07 12:35:48 +01:00
async function generateCallback ( args , value ) {
2023-11-24 14:18:49 +01:00
if ( ! value ) {
2023-11-21 23:39:17 +01:00
console . warn ( 'WARN: No argument provided for /gen command' ) ;
return ;
}
// Prevent generate recursion
2024-05-19 21:22:32 +02:00
$ ( '#send_textarea' ) . val ( '' ) [ 0 ] . dispatchEvent ( new Event ( 'input' , { bubbles : true } ) ) ;
2023-11-24 14:18:49 +01:00
const lock = isTrueBoolean ( args ? . lock ) ;
2024-03-30 17:13:55 +01:00
const as = args ? . as || 'system' ;
const quietToLoud = as === 'char' ;
2024-03-31 20:02:38 +02:00
const length = Number ( resolveVariable ( args ? . length ) ? ? 0 ) || 0 ;
2023-11-21 23:39:17 +01:00
2023-11-24 14:18:49 +01:00
try {
if ( lock ) {
deactivateSendButtons ( ) ;
}
2023-11-25 23:56:55 +01:00
setEphemeralStopStrings ( resolveVariable ( args ? . stop ) ) ;
2024-01-12 18:16:42 +01:00
const name = args ? . name ;
2024-03-31 20:02:38 +02:00
const result = await generateQuietPrompt ( value , quietToLoud , false , '' , name , length ) ;
2023-11-24 14:18:49 +01:00
return result ;
} finally {
if ( lock ) {
activateSendButtons ( ) ;
}
2023-11-24 18:06:31 +01:00
flushEphemeralStoppingStrings ( ) ;
2023-11-24 14:18:49 +01:00
}
2023-11-21 23:39:17 +01:00
}
2024-06-29 02:16:36 +02:00
/ * *
*
2024-06-29 02:24:20 +02:00
* @ param { { title ? : string , severity ? : string , timeout ? : string , extendedTimeout ? : string , preventDuplicates ? : string , awaitDismissal ? : string } } args - named arguments from the slash command
2024-06-29 02:16:36 +02:00
* @ param { string } value - The string to echo ( unnamed argument from the slash command )
* @ returns { Promise < string > } The text that was echoed
* /
2023-12-07 12:35:48 +01:00
async function echoCallback ( args , value ) {
2024-06-24 03:09:46 +02:00
// Note: We don't need to sanitize input, as toastr is set up by default to escape HTML via toastr options
if ( value === '' ) {
2023-11-21 23:39:17 +01:00
console . warn ( 'WARN: No argument provided for /echo command' ) ;
2024-06-29 02:16:36 +02:00
return '' ;
}
if ( args . severity && ! [ 'error' , 'warning' , 'success' , 'info' ] . includes ( args . severity ) ) {
toastr . warning ( ` Invalid severity provided for /echo command: ${ args . severity } ` ) ;
args . severity = null ;
}
2024-07-09 00:26:15 +02:00
// Make sure that the value is a string
value = String ( value ) ;
2024-06-29 02:16:36 +02:00
const title = args . title ? args . title : undefined ;
const severity = args . severity ? args . severity : 'info' ;
/** @type {ToastrOptions} */
const options = { } ;
2024-06-29 02:24:20 +02:00
if ( args . timeout && ! isNaN ( parseInt ( args . timeout ) ) ) options . timeOut = parseInt ( args . timeout ) ;
if ( args . extendedTimeout && ! isNaN ( parseInt ( args . extendedTimeout ) ) ) options . extendedTimeOut = parseInt ( args . extendedTimeout ) ;
2024-06-29 02:16:36 +02:00
if ( isTrueBoolean ( args . preventDuplicates ) ) options . preventDuplicates = true ;
// Prepare possible await handling
let awaitDismissal = isTrueBoolean ( args . awaitDismissal ) ;
let resolveToastDismissal ;
if ( awaitDismissal ) {
options . onHidden = ( ) => resolveToastDismissal ( value ) ;
2023-11-21 23:39:17 +01:00
}
2024-06-29 02:16:36 +02:00
2023-12-07 02:26:29 +01:00
switch ( severity ) {
case 'error' :
2024-06-29 02:16:36 +02:00
toastr . error ( value , title , options ) ;
2023-12-07 02:26:29 +01:00
break ;
case 'warning' :
2024-06-29 02:16:36 +02:00
toastr . warning ( value , title , options ) ;
2023-12-07 02:26:29 +01:00
break ;
case 'success' :
2024-06-29 02:16:36 +02:00
toastr . success ( value , title , options ) ;
2023-12-07 02:26:29 +01:00
break ;
case 'info' :
default :
2024-06-29 02:16:36 +02:00
toastr . info ( value , title , options ) ;
2023-12-07 02:26:29 +01:00
break ;
}
2024-06-29 02:16:36 +02:00
if ( awaitDismissal ) {
return new Promise ( ( resolve ) => {
resolveToastDismissal = resolve ;
} ) ;
} else {
return value ;
}
2023-11-21 23:39:17 +01:00
}
2024-08-09 00:37:26 +02:00
/ * *
* @ param { { switch ? : string } } args - named arguments
* @ param { string } value - The swipe text to add ( unnamed argument )
* /
async function addSwipeCallback ( args , value ) {
2023-12-07 12:35:48 +01:00
const lastMessage = chat [ chat . length - 1 ] ;
2023-11-22 00:26:17 +01:00
if ( ! lastMessage ) {
2023-12-02 19:04:51 +01:00
toastr . warning ( 'No messages to add swipes to.' ) ;
2024-06-14 23:48:41 +02:00
return '' ;
2023-11-22 00:26:17 +01:00
}
2024-08-09 00:37:26 +02:00
if ( ! value ) {
2023-11-22 00:26:17 +01:00
console . warn ( 'WARN: No argument provided for /addswipe command' ) ;
2024-06-14 23:48:41 +02:00
return '' ;
2023-11-22 00:26:17 +01:00
}
if ( lastMessage . is _user ) {
2023-12-02 19:04:51 +01:00
toastr . warning ( 'Can\'t add swipes to user messages.' ) ;
2024-06-14 23:48:41 +02:00
return '' ;
2023-11-22 00:26:17 +01:00
}
if ( lastMessage . is _system ) {
2023-12-02 19:04:51 +01:00
toastr . warning ( 'Can\'t add swipes to system messages.' ) ;
2024-06-14 23:48:41 +02:00
return '' ;
2023-11-22 00:26:17 +01:00
}
if ( lastMessage . extra ? . image ) {
2023-12-02 19:04:51 +01:00
toastr . warning ( 'Can\'t add swipes to message containing an image.' ) ;
2024-06-14 23:48:41 +02:00
return '' ;
2023-11-22 00:26:17 +01:00
}
if ( ! Array . isArray ( lastMessage . swipes ) ) {
2023-12-07 12:35:48 +01:00
lastMessage . swipes = [ lastMessage . mes ] ;
lastMessage . swipe _info = [ { } ] ;
2023-11-22 00:26:17 +01:00
lastMessage . swipe _id = 0 ;
}
2024-06-26 02:46:53 +02:00
if ( ! Array . isArray ( lastMessage . swipe _info ) ) {
lastMessage . swipe _info = lastMessage . swipes . map ( ( ) => ( { } ) ) ;
}
2023-11-22 00:26:17 +01:00
2024-08-09 00:37:26 +02:00
lastMessage . swipes . push ( value ) ;
2023-11-22 00:26:17 +01:00
lastMessage . swipe _info . push ( {
send _date : getMessageTimeStamp ( ) ,
gen _started : null ,
gen _finished : null ,
extra : {
2024-08-09 00:37:26 +02:00
bias : extractMessageBias ( value ) ,
2023-11-22 00:26:17 +01:00
gen _id : Date . now ( ) ,
api : 'manual' ,
model : 'slash command' ,
2023-12-02 21:06:57 +01:00
} ,
2023-11-23 23:18:07 +01:00
} ) ;
2023-11-22 00:26:17 +01:00
2024-08-09 00:37:26 +02:00
const newSwipeId = lastMessage . swipes . length - 1 ;
if ( isTrueBoolean ( args . switch ) ) {
lastMessage . swipe _id = newSwipeId ;
lastMessage . mes = lastMessage . swipes [ newSwipeId ] ;
}
2023-11-22 00:26:17 +01:00
await saveChatConditional ( ) ;
await reloadCurrentChat ( ) ;
2024-06-14 23:48:41 +02:00
2024-08-09 00:37:26 +02:00
return String ( newSwipeId ) ;
2023-11-22 00:26:17 +01:00
}
2023-12-07 12:35:48 +01:00
async function deleteSwipeCallback ( _ , arg ) {
const lastMessage = chat [ chat . length - 1 ] ;
2023-11-21 22:28:11 +01:00
if ( ! lastMessage || ! Array . isArray ( lastMessage . swipes ) || ! lastMessage . swipes . length ) {
2023-12-02 19:04:51 +01:00
toastr . warning ( 'No messages to delete swipes from.' ) ;
2024-06-14 23:48:41 +02:00
return '' ;
2023-11-21 22:28:11 +01:00
}
if ( lastMessage . swipes . length <= 1 ) {
2023-12-02 19:04:51 +01:00
toastr . warning ( 'Can\'t delete the last swipe.' ) ;
2024-06-14 23:48:41 +02:00
return '' ;
2023-11-21 22:28:11 +01:00
}
const swipeId = arg && ! isNaN ( Number ( arg ) ) ? ( Number ( arg ) - 1 ) : lastMessage . swipe _id ;
2023-11-21 22:33:20 +01:00
if ( swipeId < 0 || swipeId >= lastMessage . swipes . length ) {
toastr . warning ( ` Invalid swipe ID: ${ swipeId + 1 } ` ) ;
2024-06-14 23:48:41 +02:00
return '' ;
2023-11-21 22:33:20 +01:00
}
2023-11-21 22:28:11 +01:00
lastMessage . swipes . splice ( swipeId , 1 ) ;
if ( Array . isArray ( lastMessage . swipe _info ) && lastMessage . swipe _info . length ) {
lastMessage . swipe _info . splice ( swipeId , 1 ) ;
}
const newSwipeId = Math . min ( swipeId , lastMessage . swipes . length - 1 ) ;
lastMessage . swipe _id = newSwipeId ;
2023-12-07 12:35:48 +01:00
lastMessage . mes = lastMessage . swipes [ newSwipeId ] ;
2023-11-21 22:28:11 +01:00
await saveChatConditional ( ) ;
await reloadCurrentChat ( ) ;
2024-06-14 23:48:41 +02:00
2024-08-09 00:38:51 +02:00
return String ( newSwipeId ) ;
2023-11-21 22:28:11 +01:00
}
2024-06-05 20:37:33 +02:00
async function askCharacter ( args , text ) {
2023-10-16 08:12:12 +02:00
// Prevent generate recursion
2024-05-19 21:22:32 +02:00
$ ( '#send_textarea' ) . val ( '' ) [ 0 ] . dispatchEvent ( new Event ( 'input' , { bubbles : true } ) ) ;
2023-10-16 08:12:12 +02:00
// Not supported in group chats
// TODO: Maybe support group chats?
if ( selected _group ) {
2023-12-02 19:04:51 +01:00
toastr . error ( 'Cannot run this command in a group chat!' ) ;
2024-06-14 23:48:41 +02:00
return '' ;
2023-10-16 08:12:12 +02:00
}
if ( ! text ) {
2023-12-02 20:11:06 +01:00
console . warn ( 'WARN: No text provided for /ask command' ) ;
2024-06-05 20:37:33 +02:00
toastr . warning ( 'No text provided for /ask command' ) ;
2024-06-14 23:48:41 +02:00
return '' ;
2023-10-16 08:12:12 +02:00
}
2024-06-05 20:37:33 +02:00
let name = '' ;
let mesText = '' ;
if ( args ? . name ) {
name = args . name . trim ( ) ;
mesText = text . trim ( ) ;
if ( ! name && ! mesText ) {
toastr . warning ( 'You must specify a name and text to ask.' ) ;
2024-06-14 23:48:41 +02:00
return '' ;
2024-06-05 20:37:33 +02:00
}
2023-10-16 08:12:12 +02:00
}
2024-06-05 20:37:33 +02:00
mesText = getRegexedString ( mesText , regex _placement . SLASH _COMMAND ) ;
2023-10-16 08:12:12 +02:00
const prevChId = this _chid ;
// Find the character
const chId = characters . findIndex ( ( e ) => e . name === name ) ;
2023-12-07 12:35:48 +01:00
if ( ! characters [ chId ] || chId === - 1 ) {
2023-12-02 19:04:51 +01:00
toastr . error ( 'Character not found.' ) ;
2024-06-14 23:48:41 +02:00
return '' ;
2023-10-16 08:12:12 +02:00
}
// Override character and send a user message
2024-06-05 20:37:33 +02:00
setCharacterId ( String ( chId ) ) ;
2023-10-16 08:12:12 +02:00
// TODO: Maybe look up by filename instead of name
2023-12-07 12:35:48 +01:00
const character = characters [ chId ] ;
2023-10-16 08:12:12 +02:00
let force _avatar , original _avatar ;
if ( character && character . avatar !== 'none' ) {
force _avatar = getThumbnailUrl ( 'avatar' , character . avatar ) ;
original _avatar = character . avatar ;
}
else {
force _avatar = default _avatar ;
original _avatar = default _avatar ;
}
setCharacterName ( character . name ) ;
2023-11-27 18:48:49 +01:00
await sendMessageAsUser ( mesText , '' ) ;
2023-10-16 08:12:12 +02:00
const restoreCharacter = ( ) => {
setCharacterId ( prevChId ) ;
2023-12-07 12:35:48 +01:00
setCharacterName ( characters [ prevChId ] . name ) ;
2023-10-16 08:12:12 +02:00
// Only force the new avatar if the character name is the same
// This skips if an error was fired
2023-12-07 12:35:48 +01:00
const lastMessage = chat [ chat . length - 1 ] ;
2023-10-16 08:12:12 +02:00
if ( lastMessage && lastMessage ? . name === character . name ) {
lastMessage . force _avatar = force _avatar ;
lastMessage . original _avatar = original _avatar ;
}
// Kill this callback once the event fires
2023-12-02 20:11:06 +01:00
eventSource . removeListener ( event _types . CHARACTER _MESSAGE _RENDERED , restoreCharacter ) ;
} ;
2023-10-16 08:12:12 +02:00
// Run generate and restore previous character on error
try {
toastr . info ( ` Asking ${ character . name } something... ` ) ;
2023-12-02 20:11:06 +01:00
await Generate ( 'ask_command' ) ;
2023-10-16 08:12:12 +02:00
} catch {
2023-12-02 20:11:06 +01:00
restoreCharacter ( ) ;
2023-10-16 08:12:12 +02:00
}
// Restore previous character once message renders
// Hack for generate
eventSource . on ( event _types . CHARACTER _MESSAGE _RENDERED , restoreCharacter ) ;
2024-06-14 23:48:41 +02:00
return '' ;
2023-10-16 08:12:12 +02:00
}
2023-12-07 12:35:48 +01:00
async function hideMessageCallback ( _ , arg ) {
2023-11-03 22:45:56 +01:00
if ( ! arg ) {
console . warn ( 'WARN: No argument provided for /hide command' ) ;
2024-06-14 23:48:41 +02:00
return '' ;
2023-11-03 22:45:56 +01:00
}
2023-11-14 20:37:37 +01:00
const range = stringToRange ( arg , 0 , chat . length - 1 ) ;
2023-11-03 22:45:56 +01:00
2023-11-14 20:37:37 +01:00
if ( ! range ) {
console . warn ( ` WARN: Invalid range provided for /hide command: ${ arg } ` ) ;
2024-06-14 23:48:41 +02:00
return '' ;
2023-11-03 22:45:56 +01:00
}
2024-04-12 09:03:50 +02:00
await hideChatMessageRange ( range . start , range . end , false ) ;
2024-06-14 23:48:41 +02:00
return '' ;
2023-11-03 22:45:56 +01:00
}
2023-12-07 12:35:48 +01:00
async function unhideMessageCallback ( _ , arg ) {
2023-11-03 22:45:56 +01:00
if ( ! arg ) {
console . warn ( 'WARN: No argument provided for /unhide command' ) ;
2023-11-27 01:18:36 +01:00
return '' ;
2023-11-03 22:45:56 +01:00
}
2023-11-14 20:37:37 +01:00
const range = stringToRange ( arg , 0 , chat . length - 1 ) ;
2023-11-03 22:45:56 +01:00
2023-11-14 20:37:37 +01:00
if ( ! range ) {
console . warn ( ` WARN: Invalid range provided for /unhide command: ${ arg } ` ) ;
2023-11-27 01:18:36 +01:00
return '' ;
2023-11-03 22:45:56 +01:00
}
2024-04-12 09:03:50 +02:00
await hideChatMessageRange ( range . start , range . end , true ) ;
2023-11-27 01:18:36 +01:00
return '' ;
2023-11-03 22:45:56 +01:00
}
2023-12-18 00:39:37 +01:00
/ * *
* Copium for running group actions when the member is offscreen .
* @ param { number } chid - character ID
2023-12-19 22:12:14 +01:00
* @ param { string } action - one of 'enable' , 'disable' , 'up' , 'down' , 'view' , 'remove'
2023-12-18 00:39:37 +01:00
* @ returns { void }
* /
function performGroupMemberAction ( chid , action ) {
const memberSelector = ` .group_member[chid=" ${ chid } "] ` ;
// Do not optimize. Paginator gets recreated on every action
const paginationSelector = '#rm_group_members_pagination' ;
const pageSizeSelector = '#rm_group_members_pagination select' ;
let wasOffscreen = false ;
let paginationValue = null ;
let pageValue = null ;
if ( $ ( memberSelector ) . length === 0 ) {
wasOffscreen = true ;
paginationValue = Number ( $ ( pageSizeSelector ) . val ( ) ) ;
pageValue = $ ( paginationSelector ) . pagination ( 'getCurrentPageNum' ) ;
$ ( pageSizeSelector ) . val ( $ ( pageSizeSelector ) . find ( 'option' ) . last ( ) . val ( ) ) . trigger ( 'change' ) ;
}
$ ( memberSelector ) . find ( ` [data-action=" ${ action } "] ` ) . trigger ( 'click' ) ;
if ( wasOffscreen ) {
$ ( pageSizeSelector ) . val ( paginationValue ) . trigger ( 'change' ) ;
2023-12-19 22:12:14 +01:00
if ( $ ( paginationSelector ) . length ) {
$ ( paginationSelector ) . pagination ( 'go' , pageValue ) ;
}
2023-12-18 00:39:37 +01:00
}
}
2023-12-07 12:35:48 +01:00
async function disableGroupMemberCallback ( _ , arg ) {
2023-10-29 18:35:26 +01:00
if ( ! selected _group ) {
2023-12-02 19:04:51 +01:00
toastr . warning ( 'Cannot run /disable command outside of a group chat.' ) ;
2023-11-27 01:18:36 +01:00
return '' ;
2023-11-11 15:12:02 +01:00
}
2023-11-20 22:49:04 +01:00
const chid = findGroupMemberId ( arg ) ;
if ( chid === undefined ) {
console . warn ( ` WARN: No group member found for argument ${ arg } ` ) ;
2023-11-27 01:18:36 +01:00
return '' ;
2023-10-29 18:35:26 +01:00
}
2023-12-18 00:39:37 +01:00
performGroupMemberAction ( chid , 'disable' ) ;
2023-11-27 01:18:36 +01:00
return '' ;
2023-11-20 22:49:04 +01:00
}
2023-10-29 18:35:26 +01:00
2023-12-07 12:35:48 +01:00
async function enableGroupMemberCallback ( _ , arg ) {
2023-11-20 22:49:04 +01:00
if ( ! selected _group ) {
2023-12-02 19:04:51 +01:00
toastr . warning ( 'Cannot run /enable command outside of a group chat.' ) ;
2023-11-27 01:18:36 +01:00
return '' ;
2023-10-29 18:35:26 +01:00
}
2023-11-20 22:49:04 +01:00
const chid = findGroupMemberId ( arg ) ;
2023-10-29 18:35:26 +01:00
2023-11-20 22:49:04 +01:00
if ( chid === undefined ) {
console . warn ( ` WARN: No group member found for argument ${ arg } ` ) ;
2023-11-27 01:18:36 +01:00
return '' ;
2023-10-29 18:35:26 +01:00
}
2023-12-18 00:39:37 +01:00
performGroupMemberAction ( chid , 'enable' ) ;
2023-11-27 01:18:36 +01:00
return '' ;
2023-11-20 22:49:04 +01:00
}
2023-10-29 18:35:26 +01:00
2023-12-07 12:35:48 +01:00
async function moveGroupMemberUpCallback ( _ , arg ) {
2023-11-21 21:35:59 +01:00
if ( ! selected _group ) {
2023-12-02 19:04:51 +01:00
toastr . warning ( 'Cannot run /memberup command outside of a group chat.' ) ;
2023-11-27 01:18:36 +01:00
return '' ;
2023-11-21 21:35:59 +01:00
}
const chid = findGroupMemberId ( arg ) ;
if ( chid === undefined ) {
console . warn ( ` WARN: No group member found for argument ${ arg } ` ) ;
2023-11-27 01:18:36 +01:00
return '' ;
2023-11-21 21:35:59 +01:00
}
2023-12-18 00:39:37 +01:00
performGroupMemberAction ( chid , 'up' ) ;
2023-11-27 01:18:36 +01:00
return '' ;
2023-11-21 21:35:59 +01:00
}
2023-12-07 12:35:48 +01:00
async function moveGroupMemberDownCallback ( _ , arg ) {
2023-11-21 21:35:59 +01:00
if ( ! selected _group ) {
2023-12-02 19:04:51 +01:00
toastr . warning ( 'Cannot run /memberdown command outside of a group chat.' ) ;
2023-11-27 01:18:36 +01:00
return '' ;
2023-11-21 21:35:59 +01:00
}
const chid = findGroupMemberId ( arg ) ;
if ( chid === undefined ) {
console . warn ( ` WARN: No group member found for argument ${ arg } ` ) ;
2023-11-27 01:18:36 +01:00
return '' ;
2023-11-21 21:35:59 +01:00
}
2023-12-18 00:39:37 +01:00
performGroupMemberAction ( chid , 'down' ) ;
2023-11-27 01:18:36 +01:00
return '' ;
2023-11-21 21:35:59 +01:00
}
2023-12-07 12:35:48 +01:00
async function peekCallback ( _ , arg ) {
2023-11-21 21:35:59 +01:00
if ( ! selected _group ) {
2023-12-02 19:04:51 +01:00
toastr . warning ( 'Cannot run /peek command outside of a group chat.' ) ;
2023-11-27 01:18:36 +01:00
return '' ;
2023-11-21 21:35:59 +01:00
}
if ( is _group _generating ) {
2023-12-02 19:04:51 +01:00
toastr . warning ( 'Cannot run /peek command while the group reply is generating.' ) ;
2023-11-27 01:18:36 +01:00
return '' ;
2023-11-21 21:35:59 +01:00
}
const chid = findGroupMemberId ( arg ) ;
if ( chid === undefined ) {
console . warn ( ` WARN: No group member found for argument ${ arg } ` ) ;
2023-11-27 01:18:36 +01:00
return '' ;
2023-11-21 21:35:59 +01:00
}
2023-12-19 22:12:14 +01:00
performGroupMemberAction ( chid , 'view' ) ;
2023-11-27 01:18:36 +01:00
return '' ;
2023-11-21 21:35:59 +01:00
}
2023-12-07 12:35:48 +01:00
async function removeGroupMemberCallback ( _ , arg ) {
2023-11-21 21:35:59 +01:00
if ( ! selected _group ) {
2023-12-02 19:04:51 +01:00
toastr . warning ( 'Cannot run /memberremove command outside of a group chat.' ) ;
2023-11-27 01:18:36 +01:00
return '' ;
2023-11-21 21:35:59 +01:00
}
if ( is _group _generating ) {
2023-12-02 19:04:51 +01:00
toastr . warning ( 'Cannot run /memberremove command while the group reply is generating.' ) ;
2023-11-27 01:18:36 +01:00
return '' ;
2023-11-21 21:35:59 +01:00
}
const chid = findGroupMemberId ( arg ) ;
if ( chid === undefined ) {
console . warn ( ` WARN: No group member found for argument ${ arg } ` ) ;
2023-11-27 01:18:36 +01:00
return '' ;
2023-11-21 21:35:59 +01:00
}
2023-12-18 00:39:37 +01:00
performGroupMemberAction ( chid , 'remove' ) ;
2023-11-27 01:18:36 +01:00
return '' ;
2023-11-21 21:35:59 +01:00
}
2023-12-07 12:35:48 +01:00
async function addGroupMemberCallback ( _ , arg ) {
2023-11-21 21:35:59 +01:00
if ( ! selected _group ) {
2023-12-02 19:04:51 +01:00
toastr . warning ( 'Cannot run /memberadd command outside of a group chat.' ) ;
2023-11-27 01:18:36 +01:00
return '' ;
2023-11-21 21:35:59 +01:00
}
if ( ! arg ) {
console . warn ( 'WARN: No argument provided for /memberadd command' ) ;
2023-11-27 01:18:36 +01:00
return '' ;
2023-11-21 21:35:59 +01:00
}
arg = arg . trim ( ) ;
const chid = findCharacterIndex ( arg ) ;
if ( chid === - 1 ) {
console . warn ( ` WARN: No character found for argument ${ arg } ` ) ;
2023-11-27 01:18:36 +01:00
return '' ;
2023-11-21 21:35:59 +01:00
}
2023-12-07 12:35:48 +01:00
const character = characters [ chid ] ;
2023-11-21 21:35:59 +01:00
const group = groups . find ( x => x . id === selected _group ) ;
if ( ! group || ! Array . isArray ( group . members ) ) {
console . warn ( ` WARN: No group found for ID ${ selected _group } ` ) ;
2023-11-27 01:18:36 +01:00
return '' ;
2023-11-21 21:35:59 +01:00
}
const avatar = character . avatar ;
if ( group . members . includes ( avatar ) ) {
toastr . warning ( ` ${ character . name } is already a member of this group. ` ) ;
2023-11-27 01:18:36 +01:00
return '' ;
2023-11-21 21:35:59 +01:00
}
group . members . push ( avatar ) ;
await saveGroupChat ( selected _group , true ) ;
// Trigger to reload group UI
$ ( '#rm_button_selected_ch' ) . trigger ( 'click' ) ;
2023-11-27 01:18:36 +01:00
return character . name ;
2023-11-21 21:35:59 +01:00
}
2024-02-07 23:20:36 +01:00
async function triggerGenerationCallback ( args , value ) {
const shouldAwait = isTrueBoolean ( args ? . await ) ;
const outerPromise = new Promise ( ( outerResolve ) => setTimeout ( async ( ) => {
2023-12-02 21:34:46 +01:00
try {
await waitUntilCondition ( ( ) => ! is _send _press && ! is _group _generating , 10000 , 100 ) ;
} catch {
console . warn ( 'Timeout waiting for generation unlock' ) ;
toastr . warning ( 'Cannot run /trigger command while the reply is being generated.' ) ;
2024-07-10 23:32:45 +02:00
outerResolve ( Promise . resolve ( '' ) ) ;
2023-12-02 21:34:46 +01:00
return '' ;
}
2023-10-29 18:35:26 +01:00
2023-12-02 21:34:46 +01:00
// Prevent generate recursion
2024-05-19 21:22:32 +02:00
$ ( '#send_textarea' ) . val ( '' ) [ 0 ] . dispatchEvent ( new Event ( 'input' , { bubbles : true } ) ) ;
2023-10-29 18:35:26 +01:00
2023-12-02 21:34:46 +01:00
let chid = undefined ;
2023-10-29 18:35:26 +01:00
2024-02-07 23:20:36 +01:00
if ( selected _group && value ) {
chid = findGroupMemberId ( value ) ;
2023-11-27 10:16:40 +01:00
2023-12-02 21:34:46 +01:00
if ( chid === undefined ) {
2024-02-07 23:20:36 +01:00
console . warn ( ` WARN: No group member found for argument ${ value } ` ) ;
2023-12-02 21:34:46 +01:00
}
2023-11-27 10:16:40 +01:00
}
2023-11-20 22:49:04 +01:00
2024-02-07 23:20:36 +01:00
outerResolve ( new Promise ( innerResolve => setTimeout ( ( ) => innerResolve ( Generate ( 'normal' , { force _chid : chid } ) ) , 100 ) ) ) ;
} , 1 ) ) ;
if ( shouldAwait ) {
const innerPromise = await outerPromise ;
await innerPromise ;
}
2023-12-02 21:34:46 +01:00
2023-11-27 01:18:36 +01:00
return '' ;
2023-10-29 18:35:26 +01:00
}
2024-05-06 00:18:59 +02:00
/ * *
* Find persona by name .
* @ param { string } name Name to search for
* @ returns { string } Persona name
* /
function findPersonaByName ( name ) {
if ( ! name ) {
return null ;
}
for ( const persona of Object . entries ( power _user . personas ) ) {
if ( persona [ 1 ] . toLowerCase ( ) === name . toLowerCase ( ) ) {
return persona [ 0 ] ;
}
}
return null ;
}
2023-10-29 18:35:26 +01:00
2023-12-07 12:35:48 +01:00
async function sendUserMessageCallback ( args , text ) {
2023-07-26 20:00:36 +02:00
if ( ! text ) {
console . warn ( 'WARN: No text provided for /send command' ) ;
return ;
}
text = text . trim ( ) ;
2024-03-27 18:40:34 +01:00
const compact = isTrueBoolean ( args ? . compact ) ;
2023-07-26 20:00:36 +02:00
const bias = extractMessageBias ( text ) ;
2024-06-29 02:52:30 +02:00
let insertAt = Number ( args ? . at ) ;
// Convert possible depth parameter to index
if ( ! isNaN ( insertAt ) && ( insertAt < 0 || insertAt === Number ( - 0 ) ) ) {
// Negative value means going back from current chat length. (E.g.: 8 messages, Depth 1 means insert at index 7)
insertAt = chat . length + insertAt ;
}
2024-05-06 00:18:59 +02:00
if ( 'name' in args ) {
2024-06-28 03:58:04 +02:00
const name = args . name || '' ;
2024-05-06 00:18:59 +02:00
const avatar = findPersonaByName ( name ) || user _avatar ;
await sendMessageAsUser ( text , bias , insertAt , compact , name , avatar ) ;
}
else {
await sendMessageAsUser ( text , bias , insertAt , compact ) ;
}
2023-11-27 01:18:36 +01:00
return '' ;
2023-07-26 20:00:36 +02:00
}
2023-12-07 12:35:48 +01:00
async function deleteMessagesByNameCallback ( _ , name ) {
2023-07-20 19:32:15 +02:00
if ( ! name ) {
console . warn ( 'WARN: No name provided for /delname command' ) ;
return ;
}
name = name . trim ( ) ;
const messagesToDelete = [ ] ;
chat . forEach ( ( value ) => {
if ( value . name === name ) {
messagesToDelete . push ( value ) ;
}
} ) ;
if ( ! messagesToDelete . length ) {
console . debug ( '/delname: Nothing to delete' ) ;
return ;
}
for ( const message of messagesToDelete ) {
const index = chat . indexOf ( message ) ;
if ( index !== - 1 ) {
console . debug ( ` /delname: Deleting message # ${ index } ` , message ) ;
chat . splice ( index , 1 ) ;
}
}
await saveChatConditional ( ) ;
await reloadCurrentChat ( ) ;
toastr . info ( ` Deleted ${ messagesToDelete . length } messages from ${ name } ` ) ;
2023-11-27 01:18:36 +01:00
return '' ;
2023-07-20 19:32:15 +02:00
}
2023-12-07 12:35:48 +01:00
function findCharacterIndex ( name ) {
2023-07-20 19:32:15 +02:00
const matchTypes = [
( a , b ) => a === b ,
( a , b ) => a . startsWith ( b ) ,
( a , b ) => a . includes ( b ) ,
] ;
2024-02-16 19:24:47 +01:00
const exactAvatarMatch = characters . findIndex ( x => x . avatar === name ) ;
if ( exactAvatarMatch !== - 1 ) {
return exactAvatarMatch ;
}
2023-07-20 19:32:15 +02:00
for ( const matchType of matchTypes ) {
const index = characters . findIndex ( x => matchType ( x . name . toLowerCase ( ) , name . toLowerCase ( ) ) ) ;
if ( index !== - 1 ) {
return index ;
}
}
return - 1 ;
}
2023-12-07 12:35:48 +01:00
async function goToCharacterCallback ( _ , name ) {
2023-07-20 19:32:15 +02:00
if ( ! name ) {
console . warn ( 'WARN: No character name provided for /go command' ) ;
return ;
}
name = name . trim ( ) ;
const characterIndex = findCharacterIndex ( name ) ;
if ( characterIndex !== - 1 ) {
2023-11-27 01:18:36 +01:00
await openChat ( new String ( characterIndex ) ) ;
2024-03-26 17:21:22 +01:00
setActiveCharacter ( characters [ characterIndex ] ? . avatar ) ;
setActiveGroup ( null ) ;
2023-12-07 12:35:48 +01:00
return characters [ characterIndex ] ? . name ;
2023-07-20 19:32:15 +02:00
} else {
2024-01-05 19:12:59 +01:00
const group = groups . find ( it => it . name . toLowerCase ( ) == name . toLowerCase ( ) ) ;
2024-01-05 19:02:44 +01:00
if ( group ) {
await openGroupById ( group . id ) ;
2024-03-26 17:21:22 +01:00
setActiveCharacter ( null ) ;
setActiveGroup ( group . id ) ;
2024-01-05 19:02:44 +01:00
return group . name ;
} else {
console . warn ( ` No matches found for name " ${ name } " ` ) ;
return '' ;
}
2023-07-20 19:32:15 +02:00
}
}
2023-12-07 12:35:48 +01:00
async function openChat ( id ) {
2023-07-20 19:32:15 +02:00
resetSelectedGroup ( ) ;
setCharacterId ( id ) ;
2023-11-27 01:18:36 +01:00
await delay ( 1 ) ;
await reloadCurrentChat ( ) ;
2023-07-20 19:32:15 +02:00
}
2024-07-08 18:43:55 +02:00
async function continueChatCallback ( args , prompt ) {
const shouldAwait = isTrueBoolean ( args ? . await ) ;
2023-12-02 21:34:46 +01:00
2024-07-08 19:12:52 +02:00
const outerPromise = new Promise ( async ( resolve , reject ) => {
2023-12-02 21:34:46 +01:00
try {
await waitUntilCondition ( ( ) => ! is _send _press && ! is _group _generating , 10000 , 100 ) ;
} catch {
console . warn ( 'Timeout waiting for generation unlock' ) ;
toastr . warning ( 'Cannot run /continue command while the reply is being generated.' ) ;
2024-07-08 19:12:52 +02:00
return reject ( ) ;
2023-12-02 21:34:46 +01:00
}
2024-07-08 19:12:52 +02:00
try {
2024-07-08 18:43:55 +02:00
// Prevent infinite recursion
$ ( '#send_textarea' ) . val ( '' ) [ 0 ] . dispatchEvent ( new Event ( 'input' , { bubbles : true } ) ) ;
const options = prompt ? . trim ( ) ? { quiet _prompt : prompt . trim ( ) , quietToLoud : true } : { } ;
await Generate ( 'continue' , options ) ;
resolve ( ) ;
2024-07-08 19:12:52 +02:00
} catch ( error ) {
console . error ( 'Error running /continue command:' , error ) ;
reject ( ) ;
}
2024-07-08 18:43:55 +02:00
} ) ;
if ( shouldAwait ) {
await outerPromise ;
}
2023-12-02 19:12:36 +01:00
return '' ;
2023-07-20 19:32:15 +02:00
}
2023-12-07 12:35:48 +01:00
export async function generateSystemMessage ( _ , prompt ) {
2024-05-19 21:22:32 +02:00
$ ( '#send_textarea' ) . val ( '' ) [ 0 ] . dispatchEvent ( new Event ( 'input' , { bubbles : true } ) ) ;
2023-07-20 19:32:15 +02:00
if ( ! prompt ) {
console . warn ( 'WARN: No prompt provided for /sysgen command' ) ;
toastr . warning ( 'You must provide a prompt for the system message' ) ;
2024-06-14 23:48:41 +02:00
return '' ;
2023-07-20 19:32:15 +02:00
}
// Generate and regex the output if applicable
toastr . info ( 'Please wait' , 'Generating...' ) ;
2024-04-19 13:19:57 +02:00
let message = await generateQuietPrompt ( prompt , false , false ) ;
2023-07-20 19:32:15 +02:00
message = getRegexedString ( message , regex _placement . SLASH _COMMAND ) ;
sendNarratorMessage ( _ , message ) ;
2024-06-14 23:48:41 +02:00
return '' ;
2023-07-20 19:32:15 +02:00
}
2023-12-07 12:35:48 +01:00
function syncCallback ( ) {
2023-07-20 19:32:15 +02:00
$ ( '#sync_name_button' ) . trigger ( 'click' ) ;
2024-06-14 23:48:41 +02:00
return '' ;
2023-07-20 19:32:15 +02:00
}
2024-06-26 19:18:05 +02:00
async function lockPersonaCallback ( _args , value ) {
if ( [ 'toggle' , 't' , '' ] . includes ( value . trim ( ) . toLowerCase ( ) ) ) {
await togglePersonaLock ( ) ;
return '' ;
}
if ( isTrueBoolean ( value ) ) {
await setPersonaLockState ( true ) ;
return '' ;
}
if ( isFalseBoolean ( value ) ) {
await setPersonaLockState ( false ) ;
return '' ;
}
2024-06-14 23:48:41 +02:00
return '' ;
2023-07-20 19:32:15 +02:00
}
2023-12-07 12:35:48 +01:00
function setStoryModeCallback ( ) {
2023-07-20 19:32:15 +02:00
$ ( '#chat_display' ) . val ( chat _styles . DOCUMENT ) . trigger ( 'change' ) ;
2024-06-14 23:48:41 +02:00
return '' ;
2023-07-20 19:32:15 +02:00
}
2023-12-07 12:35:48 +01:00
function setBubbleModeCallback ( ) {
2023-07-20 19:32:15 +02:00
$ ( '#chat_display' ) . val ( chat _styles . BUBBLES ) . trigger ( 'change' ) ;
2024-06-14 23:48:41 +02:00
return '' ;
2023-07-20 19:32:15 +02:00
}
2023-12-07 12:35:48 +01:00
function setFlatModeCallback ( ) {
2023-07-20 19:32:15 +02:00
$ ( '#chat_display' ) . val ( chat _styles . DEFAULT ) . trigger ( 'change' ) ;
2024-06-14 23:48:41 +02:00
return '' ;
2023-07-20 19:32:15 +02:00
}
2024-06-11 01:00:13 +02:00
/ * *
2024-06-11 08:55:37 +02:00
* Sets a persona name and optionally an avatar .
* @ param { { mode : 'lookup' | 'temp' | 'all' } } namedArgs Named arguments
* @ param { string } name Name to set
2024-06-14 23:48:41 +02:00
* @ returns { string }
2024-06-11 01:00:13 +02:00
* /
function setNameCallback ( { mode = 'all' } , name ) {
2023-07-20 19:32:15 +02:00
if ( ! name ) {
2024-06-11 08:55:37 +02:00
toastr . warning ( 'You must specify a name to change to' ) ;
2024-06-14 23:48:41 +02:00
return '' ;
2023-07-20 19:32:15 +02:00
}
2024-06-11 01:00:13 +02:00
if ( ! [ 'lookup' , 'temp' , 'all' ] . includes ( mode ) ) {
2024-06-11 08:55:37 +02:00
toastr . warning ( 'Mode must be one of "lookup", "temp" or "all"' ) ;
2024-06-14 23:48:41 +02:00
return '' ;
2024-06-11 01:00:13 +02:00
}
2023-07-20 19:32:15 +02:00
name = name . trim ( ) ;
2024-06-11 01:00:13 +02:00
// If the name matches a persona avatar, or a name, auto-select it
if ( [ 'lookup' , 'all' ] . includes ( mode ) ) {
let persona = Object . entries ( power _user . personas ) . find ( ( [ avatar , _ ] ) => avatar === name ) ? . [ 1 ] ;
if ( ! persona ) persona = Object . entries ( power _user . personas ) . find ( ( [ _ , personaName ] ) => personaName . toLowerCase ( ) === name . toLowerCase ( ) ) ? . [ 1 ] ;
if ( persona ) {
autoSelectPersona ( persona ) ;
2024-03-27 14:16:20 +01:00
retriggerFirstMessageOnEmptyChat ( ) ;
2024-06-14 23:48:41 +02:00
return '' ;
2024-06-11 01:00:13 +02:00
} else if ( mode === 'lookup' ) {
toastr . warning ( ` Persona ${ name } not found ` ) ;
2024-06-14 23:48:41 +02:00
return '' ;
2023-07-20 19:32:15 +02:00
}
}
2024-06-11 01:00:13 +02:00
if ( [ 'temp' , 'all' ] . includes ( mode ) ) {
// Otherwise, set just the name
setUserName ( name ) ; //this prevented quickReply usage
retriggerFirstMessageOnEmptyChat ( ) ;
}
2024-06-14 23:48:41 +02:00
return '' ;
2023-07-20 19:32:15 +02:00
}
2023-12-07 12:35:48 +01:00
async function setNarratorName ( _ , text ) {
2023-07-20 19:32:15 +02:00
const name = text || NARRATOR _NAME _DEFAULT ;
2023-12-07 12:35:48 +01:00
chat _metadata [ NARRATOR _NAME _KEY ] = name ;
2023-07-20 19:32:15 +02:00
toastr . info ( ` System narrator name set to ${ name } ` ) ;
2023-08-23 09:32:48 +02:00
await saveChatConditional ( ) ;
2024-06-14 23:48:41 +02:00
return '' ;
2023-07-20 19:32:15 +02:00
}
2023-12-07 12:35:48 +01:00
export async function sendMessageAs ( args , text ) {
2023-07-20 19:32:15 +02:00
if ( ! text ) {
2024-06-14 23:48:41 +02:00
return '' ;
2023-07-20 19:32:15 +02:00
}
2023-11-21 01:54:04 +01:00
let name ;
let mesText ;
2023-07-20 19:32:15 +02:00
2023-11-27 18:48:49 +01:00
if ( args . name ) {
name = args . name . trim ( ) ;
2023-11-21 01:54:04 +01:00
if ( ! name && ! text ) {
toastr . warning ( 'You must specify a name and text to send as' ) ;
2024-06-14 23:48:41 +02:00
return '' ;
2023-11-21 01:54:04 +01:00
}
} else {
2024-04-28 02:53:17 +02:00
const namelessWarningKey = 'sendAsNamelessWarningShown' ;
if ( localStorage . getItem ( namelessWarningKey ) !== 'true' ) {
toastr . warning ( 'To avoid confusion, please use /sendas name="Character Name"' , 'Name defaulted to {{char}}' , { timeOut : 10000 } ) ;
localStorage . setItem ( namelessWarningKey , 'true' ) ;
2023-11-21 01:54:04 +01:00
}
2024-04-28 02:53:17 +02:00
name = name2 ;
2024-07-28 06:29:37 +02:00
if ( ! text ) {
toastr . warning ( 'You must specify text to send as' ) ;
return '' ;
}
2023-11-21 01:54:04 +01:00
}
2023-07-20 19:32:15 +02:00
2024-07-28 06:29:37 +02:00
mesText = text . trim ( ) ;
2023-07-20 19:32:15 +02:00
// Requires a regex check after the slash command is pushed to output
mesText = getRegexedString ( mesText , regex _placement . SLASH _COMMAND , { characterOverride : name } ) ;
// Messages that do nothing but set bias will be hidden from the context
const bias = extractMessageBias ( mesText ) ;
2023-12-21 19:49:03 +01:00
const isSystem = bias && ! removeMacros ( mesText ) . length ;
2024-03-27 18:40:34 +01:00
const compact = isTrueBoolean ( args ? . compact ) ;
2023-07-20 19:32:15 +02:00
2024-07-01 15:44:01 +02:00
const character = characters . find ( x => x . avatar === name ) ? ? characters . find ( x => x . name === name ) ;
2023-07-20 19:32:15 +02:00
let force _avatar , original _avatar ;
if ( character && character . avatar !== 'none' ) {
force _avatar = getThumbnailUrl ( 'avatar' , character . avatar ) ;
original _avatar = character . avatar ;
}
else {
force _avatar = default _avatar ;
original _avatar = default _avatar ;
}
const message = {
name : name ,
is _user : false ,
is _system : isSystem ,
2023-08-22 17:13:03 +02:00
send _date : getMessageTimeStamp ( ) ,
2023-07-20 19:32:15 +02:00
mes : substituteParams ( mesText ) ,
force _avatar : force _avatar ,
original _avatar : original _avatar ,
extra : {
bias : bias . trim ( ) . length ? bias : null ,
gen _id : Date . now ( ) ,
2024-03-27 18:40:34 +01:00
isSmallSys : compact ,
2024-06-26 18:48:45 +02:00
api : 'manual' ,
model : 'slash command' ,
2023-12-02 21:06:57 +01:00
} ,
2023-07-20 19:32:15 +02:00
} ;
2024-06-26 03:13:05 +02:00
message . swipe _id = 0 ;
message . swipes = [ message . mes ] ;
message . swipes _info = [ {
send _date : message . send _date ,
gen _started : null ,
gen _finished : null ,
extra : {
bias : message . extra . bias ,
gen _id : message . extra . gen _id ,
2024-06-26 18:48:45 +02:00
isSmallSys : compact ,
2024-06-26 03:13:05 +02:00
api : 'manual' ,
model : 'slash command' ,
} ,
} ] ;
2024-06-29 02:52:30 +02:00
let insertAt = Number ( args . at ) ;
// Convert possible depth parameter to index
if ( ! isNaN ( insertAt ) && ( insertAt < 0 || insertAt === Number ( - 0 ) ) ) {
// Negative value means going back from current chat length. (E.g.: 8 messages, Depth 1 means insert at index 7)
insertAt = chat . length + insertAt ;
}
2023-11-27 18:48:49 +01:00
if ( ! isNaN ( insertAt ) && insertAt >= 0 && insertAt <= chat . length ) {
chat . splice ( insertAt , 0 , message ) ;
await saveChatConditional ( ) ;
await eventSource . emit ( event _types . MESSAGE _RECEIVED , insertAt ) ;
await reloadCurrentChat ( ) ;
await eventSource . emit ( event _types . CHARACTER _MESSAGE _RENDERED , insertAt ) ;
} else {
chat . push ( message ) ;
await eventSource . emit ( event _types . MESSAGE _RECEIVED , ( chat . length - 1 ) ) ;
addOneMessage ( message ) ;
await eventSource . emit ( event _types . CHARACTER _MESSAGE _RENDERED , ( chat . length - 1 ) ) ;
await saveChatConditional ( ) ;
}
2024-06-14 23:48:41 +02:00
return '' ;
2023-07-20 19:32:15 +02:00
}
2023-12-07 12:35:48 +01:00
export async function sendNarratorMessage ( args , text ) {
2023-07-20 19:32:15 +02:00
if ( ! text ) {
2024-06-14 23:48:41 +02:00
return '' ;
2023-07-20 19:32:15 +02:00
}
2023-12-07 12:35:48 +01:00
const name = chat _metadata [ NARRATOR _NAME _KEY ] || NARRATOR _NAME _DEFAULT ;
2023-07-20 19:32:15 +02:00
// Messages that do nothing but set bias will be hidden from the context
const bias = extractMessageBias ( text ) ;
2023-12-21 19:49:03 +01:00
const isSystem = bias && ! removeMacros ( text ) . length ;
2024-03-27 18:40:34 +01:00
const compact = isTrueBoolean ( args ? . compact ) ;
2023-07-20 19:32:15 +02:00
const message = {
name : name ,
is _user : false ,
is _system : isSystem ,
2023-08-22 17:13:03 +02:00
send _date : getMessageTimeStamp ( ) ,
2023-07-20 19:32:15 +02:00
mes : substituteParams ( text . trim ( ) ) ,
force _avatar : system _avatar ,
extra : {
type : system _message _types . NARRATOR ,
bias : bias . trim ( ) . length ? bias : null ,
gen _id : Date . now ( ) ,
2024-03-27 18:40:34 +01:00
isSmallSys : compact ,
2024-06-26 18:48:45 +02:00
api : 'manual' ,
model : 'slash command' ,
2023-07-20 19:32:15 +02:00
} ,
} ;
2024-06-29 02:52:30 +02:00
let insertAt = Number ( args . at ) ;
// Convert possible depth parameter to index
if ( ! isNaN ( insertAt ) && ( insertAt < 0 || insertAt === Number ( - 0 ) ) ) {
// Negative value means going back from current chat length. (E.g.: 8 messages, Depth 1 means insert at index 7)
insertAt = chat . length + insertAt ;
}
2023-11-27 18:48:49 +01:00
if ( ! isNaN ( insertAt ) && insertAt >= 0 && insertAt <= chat . length ) {
chat . splice ( insertAt , 0 , message ) ;
await saveChatConditional ( ) ;
await eventSource . emit ( event _types . MESSAGE _SENT , insertAt ) ;
await reloadCurrentChat ( ) ;
await eventSource . emit ( event _types . USER _MESSAGE _RENDERED , insertAt ) ;
} else {
chat . push ( message ) ;
await eventSource . emit ( event _types . MESSAGE _SENT , ( chat . length - 1 ) ) ;
addOneMessage ( message ) ;
await eventSource . emit ( event _types . USER _MESSAGE _RENDERED , ( chat . length - 1 ) ) ;
await saveChatConditional ( ) ;
}
2024-06-14 23:48:41 +02:00
return '' ;
2023-07-20 19:32:15 +02:00
}
2023-12-07 12:35:48 +01:00
export async function promptQuietForLoudResponse ( who , text ) {
2023-09-17 21:00:10 +02:00
let character _id = getContext ( ) . characterId ;
if ( who === 'sys' ) {
2023-12-02 19:04:51 +01:00
text = 'System: ' + text ;
2023-09-17 21:00:10 +02:00
} else if ( who === 'user' ) {
2023-12-02 19:04:51 +01:00
text = name1 + ': ' + text ;
2023-09-17 21:00:10 +02:00
} else if ( who === 'char' ) {
2023-12-07 12:35:48 +01:00
text = characters [ character _id ] . name + ': ' + text ;
2023-09-17 21:00:10 +02:00
} else if ( who === 'raw' ) {
2023-12-02 15:18:49 +01:00
// We don't need to modify the text
2023-09-17 21:00:10 +02:00
}
//text = `${text}${power_user.instruct.enabled ? '' : '\n'}${(power_user.always_force_name2 && who != 'raw') ? characters[character_id].name + ":" : ""}`
2024-04-19 13:19:57 +02:00
let reply = await generateQuietPrompt ( text , true , false ) ;
2023-09-17 21:00:10 +02:00
text = await getRegexedString ( reply , regex _placement . SLASH _COMMAND ) ;
const message = {
2023-12-07 12:35:48 +01:00
name : characters [ character _id ] . name ,
2023-09-17 21:00:10 +02:00
is _user : false ,
is _name : true ,
is _system : false ,
send _date : getMessageTimeStamp ( ) ,
mes : substituteParams ( text . trim ( ) ) ,
extra : {
type : system _message _types . COMMENT ,
gen _id : Date . now ( ) ,
2024-06-26 18:48:45 +02:00
api : 'manual' ,
model : 'slash command' ,
2023-09-17 21:00:10 +02:00
} ,
} ;
chat . push ( message ) ;
await eventSource . emit ( event _types . MESSAGE _SENT , ( chat . length - 1 ) ) ;
addOneMessage ( message ) ;
await eventSource . emit ( event _types . USER _MESSAGE _RENDERED , ( chat . length - 1 ) ) ;
await saveChatConditional ( ) ;
}
2023-12-07 12:35:48 +01:00
async function sendCommentMessage ( args , text ) {
2023-07-20 19:32:15 +02:00
if ( ! text ) {
2024-06-14 23:48:41 +02:00
return '' ;
2023-07-20 19:32:15 +02:00
}
2024-03-27 18:40:34 +01:00
const compact = isTrueBoolean ( args ? . compact ) ;
2023-07-20 19:32:15 +02:00
const message = {
name : COMMENT _NAME _DEFAULT ,
is _user : false ,
2023-08-26 19:26:23 +02:00
is _system : true ,
2023-08-22 17:13:03 +02:00
send _date : getMessageTimeStamp ( ) ,
2023-07-20 19:32:15 +02:00
mes : substituteParams ( text . trim ( ) ) ,
force _avatar : comment _avatar ,
extra : {
type : system _message _types . COMMENT ,
gen _id : Date . now ( ) ,
2024-03-27 18:40:34 +01:00
isSmallSys : compact ,
2024-06-26 18:48:45 +02:00
api : 'manual' ,
model : 'slash command' ,
2023-07-20 19:32:15 +02:00
} ,
} ;
2024-06-29 02:52:30 +02:00
let insertAt = Number ( args . at ) ;
// Convert possible depth parameter to index
if ( ! isNaN ( insertAt ) && ( insertAt < 0 || insertAt === Number ( - 0 ) ) ) {
// Negative value means going back from current chat length. (E.g.: 8 messages, Depth 1 means insert at index 7)
insertAt = chat . length + insertAt ;
}
2023-11-27 18:48:49 +01:00
if ( ! isNaN ( insertAt ) && insertAt >= 0 && insertAt <= chat . length ) {
chat . splice ( insertAt , 0 , message ) ;
await saveChatConditional ( ) ;
await eventSource . emit ( event _types . MESSAGE _SENT , insertAt ) ;
await reloadCurrentChat ( ) ;
await eventSource . emit ( event _types . USER _MESSAGE _RENDERED , insertAt ) ;
} else {
chat . push ( message ) ;
await eventSource . emit ( event _types . MESSAGE _SENT , ( chat . length - 1 ) ) ;
addOneMessage ( message ) ;
await eventSource . emit ( event _types . USER _MESSAGE _RENDERED , ( chat . length - 1 ) ) ;
await saveChatConditional ( ) ;
}
2024-06-14 23:48:41 +02:00
return '' ;
2023-07-20 19:32:15 +02:00
}
2023-11-04 20:35:50 +01:00
/ * *
* Displays a help message from the slash command
* @ param { any } _ Unused
* @ param { string } type Type of help to display
* /
2023-12-07 12:35:48 +01:00
function helpCommandCallback ( _ , type ) {
2023-11-04 20:35:50 +01:00
switch ( type ? . trim ( ) ? . toLowerCase ( ) ) {
2023-07-20 19:32:15 +02:00
case 'slash' :
2023-11-04 20:35:50 +01:00
case 'commands' :
case 'slashes' :
case 'slash commands' :
2023-07-20 19:32:15 +02:00
case '1' :
sendSystemMessage ( system _message _types . SLASH _COMMANDS ) ;
break ;
case 'format' :
2023-11-04 20:35:50 +01:00
case 'formatting' :
case 'formats' :
case 'chat formatting' :
2023-07-20 19:32:15 +02:00
case '2' :
sendSystemMessage ( system _message _types . FORMATTING ) ;
break ;
case 'hotkeys' :
2023-11-04 20:35:50 +01:00
case 'hotkey' :
2023-07-20 19:32:15 +02:00
case '3' :
sendSystemMessage ( system _message _types . HOTKEYS ) ;
break ;
case 'macros' :
2023-11-04 20:35:50 +01:00
case 'macro' :
2023-07-20 19:32:15 +02:00
case '4' :
sendSystemMessage ( system _message _types . MACROS ) ;
break ;
default :
sendSystemMessage ( system _message _types . HELP ) ;
break ;
}
2024-06-14 23:48:41 +02:00
return '' ;
2023-07-20 19:32:15 +02:00
}
2023-08-07 21:21:10 +02:00
$ ( document ) . on ( 'click' , '[data-displayHelp]' , function ( e ) {
e . preventDefault ( ) ;
const page = String ( $ ( this ) . data ( 'displayhelp' ) ) ;
helpCommandCallback ( null , page ) ;
} ) ;
2023-07-20 19:32:15 +02:00
2023-12-07 12:35:48 +01:00
function setBackgroundCallback ( _ , bg ) {
2023-07-20 19:32:15 +02:00
if ( ! bg ) {
2024-04-12 01:21:17 +02:00
// allow reporting of the background name if called without args
// for use in ST Scripts via pipe
return background _settings . name ;
2023-07-20 19:32:15 +02:00
}
2023-11-14 20:43:08 +01:00
2023-07-20 19:32:15 +02:00
console . log ( 'Set background to ' + bg ) ;
2023-12-02 19:04:51 +01:00
const bgElements = Array . from ( document . querySelectorAll ( '.bg_example' ) ) . map ( ( x ) => ( { element : x , bgfile : x . getAttribute ( 'bgfile' ) } ) ) ;
2023-11-14 20:43:08 +01:00
2023-12-07 12:35:48 +01:00
const fuse = new Fuse ( bgElements , { keys : [ 'bgfile' ] } ) ;
2023-11-14 20:43:08 +01:00
const result = fuse . search ( bg ) ;
if ( ! result . length ) {
toastr . error ( ` No background found with name " ${ bg } " ` ) ;
2024-06-14 23:48:41 +02:00
return '' ;
2023-11-14 20:43:08 +01:00
}
2023-12-07 12:35:48 +01:00
const bgElement = result [ 0 ] . item . element ;
2023-11-14 20:43:08 +01:00
if ( bgElement instanceof HTMLElement ) {
bgElement . click ( ) ;
2023-07-20 19:32:15 +02:00
}
2024-06-14 23:48:41 +02:00
return '' ;
2023-07-20 19:32:15 +02:00
}
2024-03-15 15:08:22 +01:00
/ * *
2024-06-21 21:24:37 +02:00
* Retrieves the available model options based on the currently selected main API and its subtype
*
* @ returns { { control : HTMLSelectElement , options : HTMLOptionElement [ ] } ? } An array of objects representing the available model options , or null if not supported
2024-03-15 15:08:22 +01:00
* /
2024-06-21 21:24:37 +02:00
function getModelOptions ( ) {
2024-03-15 15:08:22 +01:00
const modelSelectMap = [
{ id : 'model_togetherai_select' , api : 'textgenerationwebui' , type : textgen _types . TOGETHERAI } ,
{ id : 'openrouter_model' , api : 'textgenerationwebui' , type : textgen _types . OPENROUTER } ,
{ id : 'model_infermaticai_select' , api : 'textgenerationwebui' , type : textgen _types . INFERMATICAI } ,
{ id : 'model_dreamgen_select' , api : 'textgenerationwebui' , type : textgen _types . DREAMGEN } ,
{ id : 'mancer_model' , api : 'textgenerationwebui' , type : textgen _types . MANCER } ,
2024-05-03 00:40:40 +02:00
{ id : 'vllm_model' , api : 'textgenerationwebui' , type : textgen _types . VLLM } ,
2024-03-15 15:08:22 +01:00
{ id : 'aphrodite_model' , api : 'textgenerationwebui' , type : textgen _types . APHRODITE } ,
{ id : 'ollama_model' , api : 'textgenerationwebui' , type : textgen _types . OLLAMA } ,
{ id : 'model_openai_select' , api : 'openai' , type : chat _completion _sources . OPENAI } ,
{ id : 'model_claude_select' , api : 'openai' , type : chat _completion _sources . CLAUDE } ,
{ id : 'model_windowai_select' , api : 'openai' , type : chat _completion _sources . WINDOWAI } ,
{ id : 'model_openrouter_select' , api : 'openai' , type : chat _completion _sources . OPENROUTER } ,
{ id : 'model_ai21_select' , api : 'openai' , type : chat _completion _sources . AI21 } ,
{ id : 'model_google_select' , api : 'openai' , type : chat _completion _sources . MAKERSUITE } ,
{ id : 'model_mistralai_select' , api : 'openai' , type : chat _completion _sources . MISTRALAI } ,
{ id : 'model_custom_select' , api : 'openai' , type : chat _completion _sources . CUSTOM } ,
2024-04-01 23:20:17 +02:00
{ id : 'model_cohere_select' , api : 'openai' , type : chat _completion _sources . COHERE } ,
2024-04-19 23:09:38 +02:00
{ id : 'model_perplexity_select' , api : 'openai' , type : chat _completion _sources . PERPLEXITY } ,
2024-05-05 17:53:12 +02:00
{ id : 'model_groq_select' , api : 'openai' , type : chat _completion _sources . GROQ } ,
2024-06-27 23:51:09 +02:00
{ id : 'model_01ai_select' , api : 'openai' , type : chat _completion _sources . ZEROONEAI } ,
2024-08-08 06:08:03 +02:00
{ id : 'model_blockentropy_select' , api : 'openai' , type : chat _completion _sources . BLOCKENTROPY } ,
2024-03-15 15:08:22 +01:00
{ id : 'model_novel_select' , api : 'novel' , type : null } ,
{ id : 'horde_model' , api : 'koboldhorde' , type : null } ,
] ;
function getSubType ( ) {
switch ( main _api ) {
case 'textgenerationwebui' :
return textgenerationwebui _settings . type ;
case 'openai' :
return oai _settings . chat _completion _source ;
default :
return null ;
}
}
const apiSubType = getSubType ( ) ;
const modelSelectItem = modelSelectMap . find ( x => x . api == main _api && x . type == apiSubType ) ? . id ;
if ( ! modelSelectItem ) {
toastr . info ( 'Setting a model for your API is not supported or not implemented yet.' ) ;
2024-06-21 21:24:37 +02:00
return null ;
2024-03-15 15:08:22 +01:00
}
const modelSelectControl = document . getElementById ( modelSelectItem ) ;
if ( ! ( modelSelectControl instanceof HTMLSelectElement ) ) {
toastr . error ( ` Model select control not found: ${ main _api } [ ${ apiSubType } ] ` ) ;
2024-06-21 21:24:37 +02:00
return null ;
2024-03-15 15:08:22 +01:00
}
const options = Array . from ( modelSelectControl . options ) ;
2024-06-21 21:24:37 +02:00
return { control : modelSelectControl , options } ;
}
/ * *
* Sets a model for the current API .
* @ param { object } _ Unused
* @ param { string } model New model name
* @ returns { string } New or existing model name
* /
function modelCallback ( _ , model ) {
const { control : modelSelectControl , options } = getModelOptions ( ) ;
// If no model was found, the reason was already logged, we just return here
if ( options === null ) {
return '' ;
}
2024-03-15 15:08:22 +01:00
if ( ! options . length ) {
toastr . warning ( 'No model options found. Check your API settings.' ) ;
2024-04-02 23:52:30 +02:00
return '' ;
}
model = String ( model || '' ) . trim ( ) ;
if ( ! model ) {
return modelSelectControl . value ;
2024-03-15 15:08:22 +01:00
}
2024-04-02 23:52:30 +02:00
console . log ( 'Set model to ' + model ) ;
2024-03-15 15:08:22 +01:00
let newSelectedOption = null ;
const fuse = new Fuse ( options , { keys : [ 'text' , 'value' ] } ) ;
const fuzzySearchResult = fuse . search ( model ) ;
const exactValueMatch = options . find ( x => x . value . trim ( ) . toLowerCase ( ) === model . trim ( ) . toLowerCase ( ) ) ;
const exactTextMatch = options . find ( x => x . text . trim ( ) . toLowerCase ( ) === model . trim ( ) . toLowerCase ( ) ) ;
if ( exactValueMatch ) {
newSelectedOption = exactValueMatch ;
} else if ( exactTextMatch ) {
newSelectedOption = exactTextMatch ;
} else if ( fuzzySearchResult . length ) {
newSelectedOption = fuzzySearchResult [ 0 ] . item ;
}
if ( newSelectedOption ) {
modelSelectControl . value = newSelectedOption . value ;
$ ( modelSelectControl ) . trigger ( 'change' ) ;
toastr . success ( ` Model set to " ${ newSelectedOption . text } " ` ) ;
2024-04-02 23:52:30 +02:00
return newSelectedOption . value ;
2024-03-15 15:08:22 +01:00
} else {
toastr . warning ( ` No model found with name " ${ model } " ` ) ;
2024-04-02 23:52:30 +02:00
return '' ;
2024-03-15 15:08:22 +01:00
}
}
2024-06-16 04:37:42 +02:00
/ * *
* Sets state of prompt entries ( toggles ) either via identifier / uuid or name .
* @ param { object } args Object containing arguments
* @ param { string } args . identifier Select prompt entry using an identifier ( uuid )
* @ param { string } args . name Select prompt entry using name
* @ param { string } targetState The targeted state of the entry / entries
* @ returns { String } empty string
* /
function setPromptEntryCallback ( args , targetState ) {
// needs promptManager to manipulate prompt entries
const promptManager = setupChatCompletionPromptManager ( oai _settings ) ;
const prompts = promptManager . serviceSettings . prompts ;
2024-06-16 12:56:43 +02:00
function parseArgs ( arg ) {
const list = [ ] ;
try {
const parsedArg = JSON . parse ( arg ) ;
list . push ( ... Array . isArray ( parsedArg ) ? parsedArg : [ arg ] ) ;
} catch {
list . push ( arg ) ;
}
return list ;
2024-06-16 04:37:42 +02:00
}
2024-06-16 12:16:56 +02:00
2024-06-16 12:56:43 +02:00
let identifiersList = parseArgs ( args . identifier ) ;
let nameList = parseArgs ( args . name ) ;
2024-06-16 04:37:42 +02:00
// Check if identifiers exists in prompt, else remove from list
if ( identifiersList . length !== 0 ) {
2024-06-16 12:56:43 +02:00
identifiersList = identifiersList . filter ( identifier => prompts . some ( prompt => prompt . identifier === identifier ) ) ;
2024-06-16 04:37:42 +02:00
}
if ( nameList . length !== 0 ) {
nameList . forEach ( name => {
// one name could potentially have multiple entries, find all identifiers that match given name
let identifiers = [ ] ;
prompts . forEach ( entry => {
if ( entry . name === name ) {
identifiers . push ( entry . identifier ) ;
}
} ) ;
identifiersList = identifiersList . concat ( identifiers ) ;
} ) ;
}
2024-06-16 12:56:43 +02:00
2024-06-16 04:37:42 +02:00
// Remove duplicates to allow consistent 'toggle'
identifiersList = [ ... new Set ( identifiersList ) ] ;
if ( identifiersList . length === 0 ) return '' ;
// logic adapted from PromptManager.js, handleToggle
2024-06-16 12:56:43 +02:00
const getPromptOrderEntryState = ( promptOrderEntry ) => {
if ( [ 'toggle' , 't' , '' ] . includes ( targetState . trim ( ) . toLowerCase ( ) ) ) {
return ! promptOrderEntry . enabled ;
}
2024-06-16 04:37:42 +02:00
2024-06-16 12:56:43 +02:00
if ( isTrueBoolean ( targetState ) ) {
return true ;
}
2024-06-16 04:37:42 +02:00
2024-06-16 12:56:43 +02:00
if ( isFalseBoolean ( targetState ) ) {
return false ;
}
return promptOrderEntry . enabled ;
} ;
identifiersList . forEach ( promptID => {
const promptOrderEntry = promptManager . getPromptOrderEntry ( promptManager . activeCharacter , promptID ) ;
const counts = promptManager . tokenHandler . getCounts ( ) ;
counts [ promptID ] = null ;
promptOrderEntry . enabled = getPromptOrderEntryState ( promptOrderEntry ) ;
} ) ;
2024-06-16 04:37:42 +02:00
// no need to render for each identifier
promptManager . render ( ) ;
promptManager . saveServiceSettings ( ) ;
return '' ;
}
2024-05-12 21:15:05 +02:00
2024-08-18 11:02:15 +02:00
/ * *
* Sets the API URL and triggers the text generation web UI button click .
*
* @ param { object } args - named args
* @ param { string ? } [ args . api = null ] - the API name to set / get the URL for
* @ param { string ? } [ args . connect = true ] - whether to connect to the API after setting
* @ param { string } url - the API URL to set
* @ returns { Promise < string > }
* /
async function setApiUrlCallback ( { api = null , connect = 'true' } , url ) {
2024-08-18 11:18:06 +02:00
const autoConnect = isTrueBoolean ( connect ) ;
2024-08-18 11:02:15 +02:00
// Special handling for Chat Completion Custom OpenAI compatible, that one can also support API url handling
const isCurrentlyCustomOpenai = main _api === 'openai' && oai _settings . chat _completion _source === chat _completion _sources . CUSTOM ;
if ( api === chat _completion _sources . CUSTOM || ( ! api && isCurrentlyCustomOpenai ) ) {
if ( ! url ) {
return oai _settings . custom _url ? ? '' ;
}
2024-08-18 11:18:06 +02:00
if ( ! isCurrentlyCustomOpenai && autoConnect ) {
toastr . warning ( 'Custom OpenAI API is not the currently selected API, so we cannot do an auto-connect. Consider switching to it via /api beforehand.' ) ;
return '' ;
}
2024-08-18 11:02:15 +02:00
$ ( '#custom_api_url_text' ) . val ( url ) . trigger ( 'input' ) ;
2024-08-18 11:18:06 +02:00
if ( autoConnect ) {
2024-08-18 11:02:15 +02:00
$ ( '#api_button_openai' ) . trigger ( 'click' ) ;
}
return url ;
}
2024-08-18 11:18:06 +02:00
// Special handling for Kobold Classic API
const isCurrentlyKoboldClassic = main _api === 'kobold' ;
if ( api === 'kobold' || ( ! api && isCurrentlyKoboldClassic ) ) {
if ( ! url ) {
return api _server ? ? '' ;
}
if ( ! isCurrentlyKoboldClassic && autoConnect ) {
toastr . warning ( 'Kobold Classic API is not the currently selected API, so we cannot do an auto-connect. Consider switching to it via /api beforehand.' ) ;
return '' ;
}
$ ( '#api_url_text' ) . val ( url ) . trigger ( 'input' ) ;
// trigger blur debounced, so we hide the autocomplete menu
setTimeout ( ( ) => $ ( '#api_url_text' ) . trigger ( 'blur' ) , 1 ) ;
if ( autoConnect ) {
$ ( '#api_button' ) . trigger ( 'click' ) ;
}
return api _server ? ? '' ;
}
2024-08-18 11:02:15 +02:00
// Do some checks and get the api type we are targeting with this command
if ( api && ! Object . values ( textgen _types ) . includes ( api ) ) {
toastr . warning ( ` API ' ${ api } ' is not a valid text_gen API. ` ) ;
return '' ;
}
if ( ! api && ! Object . values ( textgen _types ) . includes ( textgenerationwebui _settings . type ) ) {
toastr . warning ( ` API ' ${ textgenerationwebui _settings . type } ' is not a valid text_gen API. ` ) ;
return '' ;
}
2024-08-18 11:18:06 +02:00
if ( api && url && autoConnect && api !== textgenerationwebui _settings . type ) {
2024-08-18 11:02:15 +02:00
toastr . warning ( ` API ' ${ api } ' is not the currently selected API, so we cannot do an auto-connect. Consider switching to it via /api beforehand. ` ) ;
return '' ;
}
const type = api || textgenerationwebui _settings . type ;
const inputSelector = SERVER _INPUTS [ type ] ;
if ( ! inputSelector ) {
toastr . warning ( ` API ' ${ type } ' does not have a server url input. ` ) ;
return '' ;
}
// If no url was provided, return the current one
if ( ! url ) {
return textgenerationwebui _settings . server _urls [ type ] ? ? '' ;
}
// else, we want to actually set the url
$ ( inputSelector ) . val ( url ) . trigger ( 'input' ) ;
// trigger blur debounced, so we hide the autocomplete menu
setTimeout ( ( ) => $ ( inputSelector ) . trigger ( 'blur' ) , 1 ) ;
// Trigger the auto connect via connect button, if requested
2024-08-18 11:18:06 +02:00
if ( autoConnect ) {
2024-08-18 11:02:15 +02:00
$ ( '#api_button_textgenerationwebui' ) . trigger ( 'click' ) ;
}
// We still re-acquire the value, as it might have been modified by the validation on connect
return textgenerationwebui _settings . server _urls [ type ] ? ? '' ;
}
2024-05-12 21:15:05 +02:00
export let isExecutingCommandsFromChatInput = false ;
export let commandsFromChatInputAbortController ;
2023-11-23 21:36:48 +01:00
/ * *
2024-05-12 21:15:05 +02:00
* Show command execution pause / stop buttons next to chat input .
2023-11-23 21:36:48 +01:00
* /
2024-05-12 21:15:05 +02:00
export function activateScriptButtons ( ) {
document . querySelector ( '#form_sheld' ) . classList . add ( 'isExecutingCommandsFromChatInput' ) ;
}
2023-07-20 19:32:15 +02:00
2024-05-12 21:15:05 +02:00
/ * *
* Hide command execution pause / stop buttons next to chat input .
* /
export function deactivateScriptButtons ( ) {
document . querySelector ( '#form_sheld' ) . classList . remove ( 'isExecutingCommandsFromChatInput' ) ;
}
2023-11-23 23:18:07 +01:00
2024-05-12 21:15:05 +02:00
/ * *
* Toggle pause / continue command execution . Only for commands executed via chat input .
* /
export function pauseScriptExecution ( ) {
if ( commandsFromChatInputAbortController ) {
if ( commandsFromChatInputAbortController . signal . paused ) {
commandsFromChatInputAbortController . continue ( 'Clicked pause button' ) ;
document . querySelector ( '#form_sheld' ) . classList . remove ( 'script_paused' ) ;
} else {
commandsFromChatInputAbortController . pause ( 'Clicked pause button' ) ;
document . querySelector ( '#form_sheld' ) . classList . add ( 'script_paused' ) ;
2023-11-23 23:18:07 +01:00
}
}
2024-05-12 21:15:05 +02:00
}
2023-07-20 19:32:15 +02:00
2024-05-12 21:15:05 +02:00
/ * *
* Stop command execution . Only for commands executed via chat input .
* /
export function stopScriptExecution ( ) {
commandsFromChatInputAbortController ? . abort ( 'Clicked stop button' ) ;
}
2023-07-20 19:32:15 +02:00
2024-05-12 21:15:05 +02:00
/ * *
* Clear up command execution progress bar above chat input .
* @ returns Promise < void >
* /
async function clearCommandProgress ( ) {
if ( isExecutingCommandsFromChatInput ) return ;
document . querySelector ( '#send_textarea' ) . style . setProperty ( '--progDone' , '1' ) ;
await delay ( 250 ) ;
if ( isExecutingCommandsFromChatInput ) return ;
document . querySelector ( '#send_textarea' ) . style . transition = 'none' ;
await delay ( 1 ) ;
document . querySelector ( '#send_textarea' ) . style . setProperty ( '--prog' , '0%' ) ;
document . querySelector ( '#send_textarea' ) . style . setProperty ( '--progDone' , '0' ) ;
document . querySelector ( '#form_sheld' ) . classList . remove ( 'script_success' ) ;
document . querySelector ( '#form_sheld' ) . classList . remove ( 'script_error' ) ;
document . querySelector ( '#form_sheld' ) . classList . remove ( 'script_aborted' ) ;
await delay ( 1 ) ;
document . querySelector ( '#send_textarea' ) . style . transition = null ;
}
/ * *
* Debounced version of clearCommandProgress .
* /
const clearCommandProgressDebounced = debounce ( clearCommandProgress ) ;
2023-07-20 19:32:15 +02:00
2024-05-12 21:15:05 +02:00
/ * *
* @ typedef ExecuteSlashCommandsOptions
* @ prop { boolean } [ handleParserErrors ] ( true ) Whether to handle parser errors ( show toast on error ) or throw .
* @ prop { SlashCommandScope } [ scope ] ( null ) The scope to be used when executing the commands .
* @ prop { boolean } [ handleExecutionErrors ] ( false ) Whether to handle execution errors ( show toast on error ) or throw
2024-05-19 12:23:34 +02:00
* @ prop { { [ id : PARSER _FLAG ] : boolean } } [ parserFlags ] ( null ) Parser flags to apply
2024-05-12 21:15:05 +02:00
* @ prop { SlashCommandAbortController } [ abortController ] ( null ) Controller used to abort or pause command execution
2024-06-18 20:29:29 +02:00
* @ prop { SlashCommandDebugController } [ debugController ] ( null ) Controller used to control debug execution
2024-05-12 21:15:05 +02:00
* @ prop { ( done : number , total : number ) => void } [ onProgress ] ( null ) Callback to handle progress events
2024-07-09 00:07:37 +02:00
* @ prop { string } [ source ] ( null ) String indicating where the code come from ( e . g . , QR name )
2024-05-12 21:15:05 +02:00
* /
2023-07-20 19:32:15 +02:00
2024-05-12 21:15:05 +02:00
/ * *
* @ typedef ExecuteSlashCommandsOnChatInputOptions
* @ prop { SlashCommandScope } [ scope ] ( null ) The scope to be used when executing the commands .
2024-05-19 12:23:34 +02:00
* @ prop { { [ id : PARSER _FLAG ] : boolean } } [ parserFlags ] ( null ) Parser flags to apply
2024-05-12 21:15:05 +02:00
* @ prop { boolean } [ clearChatInput ] ( false ) Whether to clear the chat input textarea
2024-07-09 00:07:37 +02:00
* @ prop { string } [ source ] ( null ) String indicating where the code come from ( e . g . , QR name )
2024-05-12 21:15:05 +02:00
* /
2023-07-20 19:32:15 +02:00
2024-05-12 21:15:05 +02:00
/ * *
* Execute slash commands while showing progress indicator and pause / stop buttons on
* chat input .
* @ param { string } text Slash command text
* @ param { ExecuteSlashCommandsOnChatInputOptions } options
* /
export async function executeSlashCommandsOnChatInput ( text , options = { } ) {
if ( isExecutingCommandsFromChatInput ) return null ;
2023-07-20 19:32:15 +02:00
2024-05-12 21:15:05 +02:00
options = Object . assign ( {
scope : null ,
parserFlags : null ,
clearChatInput : false ,
2024-07-09 00:07:37 +02:00
source : null ,
2024-05-12 21:15:05 +02:00
} , options ) ;
2024-03-25 13:22:39 +01:00
2024-05-12 21:15:05 +02:00
isExecutingCommandsFromChatInput = true ;
commandsFromChatInputAbortController ? . abort ( 'processCommands was called' ) ;
activateScriptButtons ( ) ;
2023-11-04 12:33:09 +01:00
2024-05-12 21:15:05 +02:00
/**@type {HTMLTextAreaElement}*/
const ta = document . querySelector ( '#send_textarea' ) ;
2023-11-23 21:56:52 +01:00
2024-05-12 21:15:05 +02:00
if ( options . clearChatInput ) {
ta . value = '' ;
2024-05-19 21:22:32 +02:00
ta . dispatchEvent ( new Event ( 'input' , { bubbles : true } ) ) ;
2024-05-12 21:15:05 +02:00
}
2023-11-25 17:18:57 +01:00
2024-05-12 21:15:05 +02:00
document . querySelector ( '#send_textarea' ) . style . setProperty ( '--prog' , '0%' ) ;
document . querySelector ( '#send_textarea' ) . style . setProperty ( '--progDone' , '0' ) ;
document . querySelector ( '#form_sheld' ) . classList . remove ( 'script_success' ) ;
document . querySelector ( '#form_sheld' ) . classList . remove ( 'script_error' ) ;
document . querySelector ( '#form_sheld' ) . classList . remove ( 'script_aborted' ) ;
2023-11-24 00:56:43 +01:00
2024-05-12 21:15:05 +02:00
/**@type {SlashCommandClosureResult} */
let result = null ;
2024-07-27 04:20:21 +02:00
let currentProgress = 0 ;
2024-05-12 21:15:05 +02:00
try {
commandsFromChatInputAbortController = new SlashCommandAbortController ( ) ;
result = await executeSlashCommandsWithOptions ( text , {
abortController : commandsFromChatInputAbortController ,
2024-07-23 00:28:30 +02:00
onProgress : ( done , total ) => {
2024-07-27 04:20:21 +02:00
const newProgress = done / total ;
if ( newProgress > currentProgress ) {
2024-07-23 00:28:30 +02:00
currentProgress = newProgress ;
2024-07-27 04:20:21 +02:00
ta . style . setProperty ( '--prog' , ` ${ newProgress * 100 } % ` ) ;
2024-07-23 00:28:30 +02:00
}
} ,
2024-06-14 23:48:41 +02:00
parserFlags : options . parserFlags ,
scope : options . scope ,
2024-07-09 00:07:37 +02:00
source : options . source ,
2024-05-12 21:15:05 +02:00
} ) ;
if ( commandsFromChatInputAbortController . signal . aborted ) {
document . querySelector ( '#form_sheld' ) . classList . add ( 'script_aborted' ) ;
} else {
document . querySelector ( '#form_sheld' ) . classList . add ( 'script_success' ) ;
2023-11-23 23:18:07 +01:00
}
2024-05-12 21:15:05 +02:00
} catch ( e ) {
document . querySelector ( '#form_sheld' ) . classList . add ( 'script_error' ) ;
result = new SlashCommandClosureResult ( ) ;
result . isError = true ;
2024-06-28 12:22:26 +02:00
result . errorMessage = e . message || 'An unknown error occurred' ;
2024-05-18 14:41:02 +02:00
if ( e . cause !== 'abort' ) {
2024-07-06 00:05:22 +02:00
if ( e instanceof SlashCommandExecutionError ) {
/**@type {SlashCommandExecutionError}*/
const ex = e ;
const toast = `
< div > $ { ex . message } < / d i v >
< div > Line : $ { ex . line } Column : $ { ex . column } < / d i v >
< pre style = "text-align:left;" > $ { ex . hint } < / p r e >
` ;
const clickHint = '<p>Click to see details</p>' ;
toastr . error (
` ${ toast } ${ clickHint } ` ,
'SlashCommandExecutionError' ,
{ escapeHtml : false , timeOut : 10000 , onclick : ( ) => callPopup ( toast , 'text' ) } ,
) ;
} else {
toastr . error ( result . errorMessage ) ;
}
2024-05-18 14:41:02 +02:00
}
2024-05-12 21:15:05 +02:00
} finally {
2024-05-19 21:22:32 +02:00
delay ( 1000 ) . then ( ( ) => clearCommandProgressDebounced ( ) ) ;
2023-11-23 23:18:07 +01:00
2024-05-12 21:15:05 +02:00
commandsFromChatInputAbortController = null ;
deactivateScriptButtons ( ) ;
isExecutingCommandsFromChatInput = false ;
}
return result ;
}
2024-02-03 01:06:49 +01:00
2024-05-12 21:15:05 +02:00
/ * *
*
* @ param { string } text Slash command text
* @ param { ExecuteSlashCommandsOptions } [ options ]
* @ returns { Promise < SlashCommandClosureResult > }
* /
async function executeSlashCommandsWithOptions ( text , options = { } ) {
if ( ! text ) {
return null ;
}
options = Object . assign ( {
handleParserErrors : true ,
scope : null ,
handleExecutionErrors : false ,
parserFlags : null ,
abortController : null ,
2024-06-18 20:29:29 +02:00
debugController : null ,
2024-05-12 21:15:05 +02:00
onProgress : null ,
2024-07-09 00:07:37 +02:00
source : null ,
2024-05-12 21:15:05 +02:00
} , options ) ;
2023-11-23 21:56:52 +01:00
2024-05-12 21:15:05 +02:00
let closure ;
try {
2024-05-18 20:48:31 +02:00
closure = parser . parse ( text , true , options . parserFlags , options . abortController ? ? new SlashCommandAbortController ( ) ) ;
2024-05-12 21:15:05 +02:00
closure . scope . parent = options . scope ;
closure . onProgress = options . onProgress ;
2024-06-18 20:29:29 +02:00
closure . debugController = options . debugController ;
2024-07-09 00:07:37 +02:00
closure . source = options . source ;
2024-05-12 21:15:05 +02:00
} catch ( e ) {
if ( options . handleParserErrors && e instanceof SlashCommandParserError ) {
/**@type {SlashCommandParserError}*/
const ex = e ;
const toast = `
< div > $ { ex . message } < / d i v >
< div > Line : $ { ex . line } Column : $ { ex . column } < / d i v >
< pre style = "text-align:left;" > $ { ex . hint } < / p r e >
` ;
const clickHint = '<p>Click to see details</p>' ;
toastr . error (
` ${ toast } ${ clickHint } ` ,
'SlashCommandParserError' ,
2024-05-19 21:22:32 +02:00
{ escapeHtml : false , timeOut : 10000 , onclick : ( ) => callPopup ( toast , 'text' ) } ,
2024-05-12 21:15:05 +02:00
) ;
const result = new SlashCommandClosureResult ( ) ;
return result ;
} else {
throw e ;
2024-01-26 19:58:44 +01:00
}
2024-05-12 21:15:05 +02:00
}
2024-01-26 19:58:44 +01:00
2024-05-12 21:15:05 +02:00
try {
const result = await closure . execute ( ) ;
2024-05-18 20:48:31 +02:00
if ( result . isAborted && ! result . isQuietlyAborted ) {
2024-05-12 21:15:05 +02:00
toastr . warning ( result . abortReason , 'Command execution aborted' ) ;
2024-05-19 13:34:09 +02:00
closure . abortController . signal . isQuiet = true ;
2023-07-20 19:32:15 +02:00
}
2024-05-12 21:15:05 +02:00
return result ;
} catch ( e ) {
if ( options . handleExecutionErrors ) {
2024-07-06 00:05:22 +02:00
if ( e instanceof SlashCommandExecutionError ) {
/**@type {SlashCommandExecutionError}*/
const ex = e ;
const toast = `
< div > $ { ex . message } < / d i v >
< div > Line : $ { ex . line } Column : $ { ex . column } < / d i v >
< pre style = "text-align:left;" > $ { ex . hint } < / p r e >
` ;
const clickHint = '<p>Click to see details</p>' ;
toastr . error (
` ${ toast } ${ clickHint } ` ,
'SlashCommandExecutionError' ,
{ escapeHtml : false , timeOut : 10000 , onclick : ( ) => callPopup ( toast , 'text' ) } ,
) ;
} else {
toastr . error ( e . message ) ;
}
2024-05-12 21:15:05 +02:00
const result = new SlashCommandClosureResult ( ) ;
result . isError = true ;
result . errorMessage = e . message ;
return result ;
} else {
throw e ;
2023-07-20 19:32:15 +02:00
}
}
}
2024-04-19 21:46:49 +02:00
/ * *
2024-05-12 21:15:05 +02:00
* Executes slash commands in the provided text
* @ deprecated Use executeSlashCommandWithOptions instead
* @ param { string } text Slash command text
* @ param { boolean } handleParserErrors Whether to handle parser errors ( show toast on error ) or throw
* @ param { SlashCommandScope } scope The scope to be used when executing the commands .
* @ param { boolean } handleExecutionErrors Whether to handle execution errors ( show toast on error ) or throw
2024-06-14 23:48:41 +02:00
* @ param { { [ id : PARSER _FLAG ] : boolean } } parserFlags Parser flags to apply
2024-05-12 21:15:05 +02:00
* @ param { SlashCommandAbortController } abortController Controller used to abort or pause command execution
* @ param { ( done : number , total : number ) => void } onProgress Callback to handle progress events
* @ returns { Promise < SlashCommandClosureResult > }
2024-04-19 21:46:49 +02:00
* /
2024-05-12 21:15:05 +02:00
async function executeSlashCommands ( text , handleParserErrors = true , scope = null , handleExecutionErrors = false , parserFlags = null , abortController = null , onProgress = null ) {
return executeSlashCommandsWithOptions ( text , {
handleParserErrors ,
scope ,
handleExecutionErrors ,
parserFlags ,
abortController ,
onProgress ,
2023-11-04 00:21:20 +01:00
} ) ;
}
2024-05-12 21:15:05 +02:00
/ * *
*
* @ param { HTMLTextAreaElement } textarea The textarea to receive autocomplete
* @ param { Boolean } isFloating Whether to show the auto complete as a floating window ( e . g . , large QR editor )
2024-07-04 22:53:39 +02:00
* @ returns { Promise < AutoComplete > }
2024-05-12 21:15:05 +02:00
* /
export async function setSlashCommandAutoComplete ( textarea , isFloating = false ) {
2024-05-19 21:22:32 +02:00
function canUseNegativeLookbehind ( ) {
try {
new RegExp ( '(?<!_)' ) ;
return true ;
} catch ( e ) {
return false ;
}
}
if ( ! canUseNegativeLookbehind ( ) ) {
console . warn ( 'Cannot use negative lookbehind in this browser' ) ;
return ;
}
2024-05-12 21:15:05 +02:00
const parser = new SlashCommandParser ( ) ;
const ac = new AutoComplete (
textarea ,
( ) => ac . text [ 0 ] == '/' ,
2024-05-19 21:22:32 +02:00
async ( text , index ) => await parser . getNameAt ( text , index ) ,
2024-05-12 21:15:05 +02:00
isFloating ,
) ;
2024-07-04 22:53:39 +02:00
return ac ;
2024-05-12 21:15:05 +02:00
}
/**@type {HTMLTextAreaElement} */
const sendTextarea = document . querySelector ( '#send_textarea' ) ;
setSlashCommandAutoComplete ( sendTextarea ) ;
sendTextarea . addEventListener ( 'input' , ( ) => {
if ( sendTextarea . value [ 0 ] == '/' ) {
sendTextarea . style . fontFamily = 'monospace' ;
} else {
sendTextarea . style . fontFamily = null ;
}
2023-12-02 20:11:06 +01:00
} ) ;