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 ,
2024-09-01 23:12:33 +02:00
deleteSwipe ,
2023-07-20 19:32:15 +02:00
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 ,
2024-09-10 19:44:27 +02:00
neutralCharacterName ,
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-08-19 20:31:58 +02:00
import { decodeTextTokens , getAvailableTokenizers , getFriendlyTokenizerName , getTextTokens , getTokenCountAsync , selectTokenizer } from './tokenizers.js' ;
2024-10-01 14:02:14 +02:00
import { debounce , delay , equalsIgnoreCaseAndAccents , findChar , getCharIndex , isFalseBoolean , isTrueBoolean , onlyUnique , 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-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-09-30 23:32:24 +02:00
import { slashCommandReturnHelper } from './slash-commands/SlashCommandReturnHelper.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
} ) ) ;
2024-09-29 00:36:13 +02:00
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'char-find' ,
aliases : [ 'findchar' ] ,
callback : ( args , name ) => {
if ( typeof name !== 'string' ) throw new Error ( 'name must be a string' ) ;
if ( args . preferCurrent instanceof SlashCommandClosure || Array . isArray ( args . preferCurrent ) ) throw new Error ( 'preferCurrent cannot be a closure or array' ) ;
2024-09-29 00:55:41 +02:00
if ( args . quiet instanceof SlashCommandClosure || Array . isArray ( args . quiet ) ) throw new Error ( 'quiet cannot be a closure or array' ) ;
2024-09-29 00:36:13 +02:00
2024-09-29 01:17:13 +02:00
const char = findChar ( { name : name , filteredByTags : validateArrayArgString ( args . tag , 'tag' ) , preferCurrentChar : ! isFalseBoolean ( args . preferCurrent ) , quiet : isTrueBoolean ( args . quiet ) } ) ;
2024-09-29 00:36:13 +02:00
return char ? . avatar ? ? '' ;
} ,
returns : 'the avatar key (unique identifier) of the character' ,
namedArgumentList : [
SlashCommandNamedArgument . fromProps ( {
name : 'tag' ,
description : 'Supply one or more tags to filter down to the correct character for the provided name, if multiple characters have the same name.' ,
typeList : [ ARGUMENT _TYPE . STRING ] ,
enumProvider : commonEnumProviders . tags ( 'assigned' ) ,
acceptsMultiple : true ,
} ) ,
SlashCommandNamedArgument . fromProps ( {
name : 'preferCurrent' ,
description : 'Prefer current character or characters in a group, if multiple characters match' ,
typeList : [ ARGUMENT _TYPE . BOOLEAN ] ,
defaultValue : 'true' ,
} ) ,
2024-09-29 00:55:41 +02:00
SlashCommandNamedArgument . fromProps ( {
name : 'quiet' ,
description : 'Do not show warning if multiple charactrers are found' ,
typeList : [ ARGUMENT _TYPE . BOOLEAN ] ,
defaultValue : 'false' ,
enumProvider : commonEnumProviders . boolean ( 'trueFalse' ) ,
} ) ,
2024-09-29 00:36:13 +02:00
] ,
unnamedArgumentList : [
SlashCommandArgument . fromProps ( {
2024-09-29 02:54:12 +02:00
description : 'Character name - or unique character identifier (avatar key)' ,
2024-09-29 00:36:13 +02:00
typeList : [ ARGUMENT _TYPE . STRING ] ,
enumProvider : commonEnumProviders . characters ( 'character' ) ,
forceEnum : false ,
} ) ,
] ,
helpString : `
< div >
Searches for a character and returns its avatar key .
< / d i v >
< div >
This can be used to choose the correct character for something like < code > / s e n d a s < / c o d e > o r o t h e r c o m m a n d s i n n e e d o f a c h a r a c t e r n a m e
if you have multiple characters with the same name .
< / d i v >
< div >
< strong > Example : < / s t r o n g >
< ul >
< li >
< pre > < code > / c h a r - f i n d n a m e = " C h l o e " < / c o d e > < / p r e >
Returns the avatar key for "Chloe" .
< / l i >
< li >
< pre > < code > / s e a r c h n a m e = " C h l o e " t a g = " f r i e n d " < / c o d e > < / p r e >
Returns the avatar key for the character "Chloe" that is tagged with "friend" .
This is useful if you for example have multiple characters named "Chloe" , and the others are "foe" , "goddess" , or anything else ,
so you can actually select the character you are looking for .
< / l i >
< / u l >
< / d i v >
` ,
} ) ) ;
2024-06-29 00:25:10 +02:00
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'sendas' ,
callback : sendMessageAs ,
2024-09-30 23:32:24 +02:00
returns : 'Optionally the text of the sent message, if specified in the "return" argument' ,
2024-06-29 00:25:10 +02:00
namedArgumentList : [
SlashCommandNamedArgument . fromProps ( {
name : 'name' ,
2024-09-29 02:14:06 +02:00
description : 'Character name - or unique character identifier (avatar key)' ,
2024-06-29 00:25:10 +02:00
typeList : [ ARGUMENT _TYPE . STRING ] ,
isRequired : true ,
enumProvider : commonEnumProviders . characters ( 'character' ) ,
forceEnum : false ,
} ) ,
2024-09-15 18:21:31 +02:00
SlashCommandNamedArgument . fromProps ( {
name : 'avatar' ,
2024-09-29 02:14:06 +02:00
description : 'Character avatar override (Can be either avatar key or just the character name to pull the avatar from)' ,
2024-09-15 18:21:31 +02:00
typeList : [ ARGUMENT _TYPE . STRING ] ,
enumProvider : commonEnumProviders . characters ( 'character' ) ,
} ) ,
SlashCommandNamedArgument . fromProps ( {
name : 'compact' ,
description : 'Use compact layout' ,
typeList : [ ARGUMENT _TYPE . BOOLEAN ] ,
defaultValue : 'false' ,
} ) ,
2024-06-29 00:25:10 +02:00
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 } ) ,
} ) ,
2024-09-30 23:32:24 +02:00
SlashCommandNamedArgument . fromProps ( {
name : 'return' ,
description : 'The way how you want the return value to be provided' ,
typeList : [ ARGUMENT _TYPE . STRING ] ,
defaultValue : 'none' ,
enumList : slashCommandReturnHelper . enumList ( { allowObject : true } ) ,
forceEnum : true ,
} ) ,
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 >
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 >
2024-09-29 02:14:06 +02:00
< li >
< pre > < code > / s e n d a s n a m e = " C h l o e " a v a t a r = " B i g B a d B o s s " H e h e h e , I a m t h e b i g b a d e v i l , f e a r m e . < / c o d e > < / p r e >
will send a message as the character "Chloe" , but utilizing the avatar from a character named "BigBadBoss" .
2024-09-29 11:02:54 +02:00
< / l i >
2024-05-12 21:15:05 +02:00
< / 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' ] ,
2024-09-30 23:32:24 +02:00
returns : 'Optionally the text of the sent message, if specified in the "return" argument' ,
2024-06-29 00:25:10 +02:00
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 } ) ,
} ) ,
2024-09-30 23:32:24 +02:00
SlashCommandNamedArgument . fromProps ( {
name : 'return' ,
description : 'The way how you want the return value to be provided' ,
typeList : [ ARGUMENT _TYPE . STRING ] ,
defaultValue : 'none' ,
enumList : slashCommandReturnHelper . enumList ( { allowObject : true } ) ,
forceEnum : true ,
} ) ,
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 >
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 ,
2024-09-30 23:32:24 +02:00
returns : 'Optionally the text of the sent message, if specified in the "return" argument' ,
2024-06-29 00:25:10 +02:00
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 } ) ,
} ) ,
2024-09-30 23:32:24 +02:00
SlashCommandNamedArgument . fromProps ( {
name : 'return' ,
description : 'The way how you want the return value to be provided' ,
typeList : [ ARGUMENT _TYPE . STRING ] ,
defaultValue : 'none' ,
enumList : slashCommandReturnHelper . enumList ( { allowObject : true } ) ,
forceEnum : true ,
} ) ,
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 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 ,
2024-09-29 02:36:33 +02:00
returns : 'The character/group name' ,
2024-06-29 00:25:10 +02:00
unnamedArgumentList : [
SlashCommandArgument . fromProps ( {
2024-09-29 02:36:33 +02:00
description : 'Character name - or unique character identifier (avatar key)' ,
2024-06-29 00:25:10 +02:00
typeList : [ ARGUMENT _TYPE . STRING ] ,
isRequired : true ,
enumProvider : commonEnumProviders . characters ( 'all' ) ,
2024-09-29 02:36:33 +02:00
forceEnum : true ,
2024-06-29 00:25:10 +02:00
} ) ,
] ,
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 ,
2024-09-30 23:37:21 +02:00
returns : 'Optionally the text of the sent message, if specified in the "return" argument' ,
2024-06-29 00:25:10 +02:00
namedArgumentList : [
SlashCommandNamedArgument . fromProps ( {
name : 'name' ,
2024-09-29 02:14:06 +02:00
description : 'Character name - or unique character identifier (avatar key)' ,
2024-06-29 00:25:10 +02:00
typeList : [ ARGUMENT _TYPE . STRING ] ,
isRequired : true ,
enumProvider : commonEnumProviders . characters ( 'character' ) ,
} ) ,
2024-09-30 23:37:21 +02:00
SlashCommandNamedArgument . fromProps ( {
name : 'return' ,
description : 'The way how you want the return value to be provided' ,
typeList : [ ARGUMENT _TYPE . STRING ] ,
defaultValue : 'pipe' ,
enumList : slashCommandReturnHelper . enumList ( { allowObject : true } ) ,
forceEnum : true ,
} ) ,
2024-06-29 00:25:10 +02:00
] ,
unnamedArgumentList : [
new SlashCommandArgument (
2024-09-12 19:13:05 +02:00
'prompt' , [ ARGUMENT _TYPE . STRING ] , false , false ,
2024-06-29 00:25:10 +02:00
) ,
] ,
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 ( {
2024-09-29 02:36:33 +02:00
description : 'Character name - or unique character identifier (avatar key)' ,
2024-06-29 00:25:10 +02:00
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 ,
2024-10-01 00:41:14 +02:00
returns : 'Optionally the text of the sent message, if specified in the "return" argument' ,
2024-06-29 00:25:10 +02:00
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
} ) ,
2024-10-01 00:41:14 +02:00
SlashCommandNamedArgument . fromProps ( {
name : 'return' ,
description : 'The way how you want the return value to be provided' ,
typeList : [ ARGUMENT _TYPE . STRING ] ,
defaultValue : 'none' ,
enumList : slashCommandReturnHelper . enumList ( { allowObject : true } ) ,
forceEnum : true ,
} ) ,
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 ( {
2024-09-29 02:54:12 +02:00
description : 'Character name - or unique character identifier (avatar key)' ,
2024-06-29 00:25:10 +02:00
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-09-06 18:55:50 +02:00
SlashCommandNamedArgument . fromProps ( {
name : 'cssClass' ,
2024-09-06 21:27:53 +02:00
description : 'additional CSS class to add to the toast message (e.g. for custom styling)' ,
2024-09-06 18:55:50 +02:00
typeList : [ ARGUMENT _TYPE . STRING ] ,
} ) ,
SlashCommandNamedArgument . fromProps ( {
name : 'color' ,
description : 'custom CSS color of the toast message. Accepts all valid CSS color values (e.g. \'red\', \'#FF0000\', \'rgb(255, 0, 0)\').<br />>Can be more customizable with the \'cssClass\' argument and custom classes.' ,
2024-09-06 19:03:21 +02:00
} ) ,
SlashCommandNamedArgument . fromProps ( {
name : 'escapeHtml' ,
description : 'whether to escape HTML in the toast message.' ,
typeList : [ ARGUMENT _TYPE . BOOLEAN ] ,
defaultValue : 'true' ,
enumList : commonEnumProviders . boolean ( 'trueFalse' ) ( ) ,
} ) ,
2024-09-06 19:32:35 +02:00
SlashCommandNamedArgument . fromProps ( {
name : 'onClick' ,
description : 'a closure to call when the toast is clicked. This executed closure receives scope as provided in the script. Careful about possible side effects when manipulating variables and more.' ,
typeList : [ ARGUMENT _TYPE . CLOSURE ] ,
} ) ,
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 >
2024-09-06 20:53:39 +02:00
Echoes the provided text to a toast message . Can be used to display informational messages or for pipes debugging .
2024-05-12 21:15:05 +02:00
< / d i v >
< div >
< strong > Example : < / s t r o n g >
< ul >
< li >
2024-09-06 20:53:39 +02:00
< 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 = w a r n i n g T h i s i s a w a r n i n g m e s s a g e < / c o d e > < / p r e >
2024-05-12 21:15:05 +02:00
< / l i >
2024-09-06 20:39:30 +02:00
< li >
2024-09-06 20:53:39 +02:00
< pre > < code > / e c h o c o l o r = p u r p l e T h i s m e s s a g e i s p u r p l e < / c o d e > < / p r e >
2024-09-06 20:39:30 +02:00
< / l i >
< li >
2024-09-06 20:53:39 +02:00
< pre > < code > / e c h o o n C l i c k = { : / e c h o e s c a p e H t m l = f a l s e c o l o r = t r a n s p a r e n t c s s C l a s s = w i d e r _ d i a l o g u e _ p o p u p & l t ; i m g s r c = " / i m g / f i v e . p n g " / & g t ; : } t i m e o u t = 5 0 0 0 C l i c k i n g o n t h i s m e s s a g e w i t h i n 5 s e c o n d s w i l l o p e n t h e i m a g e . < / c o d e > < / p r e >
2024-09-06 20:39:30 +02:00
< / 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 : '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' ,
2024-09-29 02:36:33 +02:00
description : 'in-prompt character name for instruct mode (or unique character identifier (avatar key), which will be used as name)' ,
2024-06-29 00:25:10 +02:00
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 : [
2024-09-06 22:38:02 +02:00
SlashCommandNamedArgument . fromProps ( {
name : 'large' ,
description : 'show large popup' ,
typeList : [ ARGUMENT _TYPE . BOOLEAN ] ,
2024-09-06 23:56:26 +02:00
enumList : commonEnumProviders . boolean ( 'trueFalse' ) ( ) ,
defaultValue : 'false' ,
2024-09-06 22:38:02 +02:00
} ) ,
SlashCommandNamedArgument . fromProps ( {
name : 'wide' ,
description : 'show wide popup' ,
typeList : [ ARGUMENT _TYPE . BOOLEAN ] ,
2024-09-06 23:56:26 +02:00
enumList : commonEnumProviders . boolean ( 'trueFalse' ) ( ) ,
defaultValue : 'false' ,
2024-09-06 22:38:02 +02:00
} ) ,
SlashCommandNamedArgument . fromProps ( {
name : 'wider' ,
description : 'show wider popup' ,
typeList : [ ARGUMENT _TYPE . BOOLEAN ] ,
2024-09-06 23:56:26 +02:00
enumList : commonEnumProviders . boolean ( 'trueFalse' ) ( ) ,
defaultValue : 'false' ,
2024-09-06 22:38:02 +02:00
} ) ,
SlashCommandNamedArgument . fromProps ( {
name : 'transparent' ,
description : 'show transparent popup' ,
typeList : [ ARGUMENT _TYPE . BOOLEAN ] ,
2024-09-06 23:56:26 +02:00
enumList : commonEnumProviders . boolean ( 'trueFalse' ) ( ) ,
defaultValue : 'false' ,
2024-09-06 22:38:02 +02:00
} ) ,
SlashCommandNamedArgument . fromProps ( {
name : 'okButton' ,
description : 'text for the OK button' ,
typeList : [ ARGUMENT _TYPE . STRING ] ,
defaultValue : 'OK' ,
} ) ,
SlashCommandNamedArgument . fromProps ( {
name : 'cancelButton' ,
description : 'text for the Cancel button' ,
typeList : [ ARGUMENT _TYPE . STRING ] ,
} ) ,
2024-09-06 23:29:18 +02:00
SlashCommandNamedArgument . fromProps ( {
name : 'result' ,
2024-09-07 01:21:04 +02:00
description : 'if enabled, returns the popup result (as an integer) instead of the popup text. Resolves to 1 for OK and 0 cancel button, empty string for exiting out.' ,
2024-09-06 23:29:18 +02:00
typeList : [ ARGUMENT _TYPE . BOOLEAN ] ,
2024-09-06 23:56:26 +02:00
enumList : commonEnumProviders . boolean ( 'trueFalse' ) ( ) ,
defaultValue : 'false' ,
2024-09-06 23:29:18 +02:00
} ) ,
2024-06-29 00:25:10 +02:00
] ,
unnamedArgumentList : [
2024-09-06 23:56:26 +02:00
SlashCommandArgument . fromProps ( {
description : 'popup text' ,
typeList : [ ARGUMENT _TYPE . STRING ] ,
isRequired : true ,
} ) ,
2024-06-29 00:25:10 +02:00
] ,
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 >
2024-09-06 22:38:02 +02:00
< 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 = " C o n f i r m " P l e a s e c o n f i r m t h i s a c t i o n . < / c o d e > < / p r e >
2024-05-12 21:15:05 +02:00
< / l i >
2024-09-06 23:56:26 +02:00
< li >
< pre > < code > / p o p u p o k B u t t o n = " L e f t " c a n c e l B u t t o n = " R i g h t " r e s u l t = t r u e D o y o u w a n t t o g o l e f t o r r i g h t ? | / e c h o 0 m e a n s r i g h t , 1 m e a n s l e f t . C h o i c e : { { p i p e } } < / 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 : '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 ,
2024-10-01 00:23:00 +02:00
helpString : 'Lists all script injections for the current chat. Displays injects in a popup by default. Use the <code>return</code> argument to change the return type.' ,
2024-10-01 00:06:18 +02:00
returns : 'Optionalls the JSON object of script injections' ,
2024-09-08 17:24:12 +02:00
namedArgumentList : [
2024-10-01 00:06:18 +02:00
SlashCommandNamedArgument . fromProps ( {
name : 'return' ,
description : 'The way how you want the return value to be provided' ,
typeList : [ ARGUMENT _TYPE . STRING ] ,
2024-10-01 00:23:00 +02:00
defaultValue : 'popup-html' ,
2024-10-01 00:06:18 +02:00
enumList : slashCommandReturnHelper . enumList ( { allowPipe : false , allowObject : true , allowChat : true , allowPopup : true , allowTextVersion : false } ) ,
forceEnum : true ,
} ) ,
// TODO remove some day
2024-09-08 17:24:12 +02:00
SlashCommandNamedArgument . fromProps ( {
name : 'format' ,
2024-10-01 00:37:21 +02:00
description : '!!! DEPRECATED - use "return" instead !!! output format' ,
2024-09-08 17:24:12 +02:00
typeList : [ ARGUMENT _TYPE . STRING ] ,
isRequired : true ,
forceEnum : true ,
enumList : [
new SlashCommandEnumValue ( 'popup' , 'Show injects in a popup.' , enumTypes . enum , enumIcons . default ) ,
new SlashCommandEnumValue ( 'chat' , 'Post a system message to the chat.' , enumTypes . enum , enumIcons . default ) ,
new SlashCommandEnumValue ( 'none' , 'Just return the injects as a JSON object.' , enumTypes . enum , enumIcons . default ) ,
] ,
} ) ,
] ,
2024-06-29 00:25:10 +02:00
} ) ) ;
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' ,
2024-09-06 22:09:06 +02:00
namedArgumentList : [
SlashCommandNamedArgument . fromProps ( {
name : 'quiet' ,
description : 'suppress the toast message on model change' ,
typeList : [ ARGUMENT _TYPE . BOOLEAN ] ,
defaultValue : 'false' ,
enumList : commonEnumProviders . boolean ( 'trueFalse' ) ( ) ,
} ) ,
] ,
2024-06-29 00:25:10 +02:00
unnamedArgumentList : [
SlashCommandArgument . fromProps ( {
description : 'model name' ,
typeList : [ ARGUMENT _TYPE . STRING ] ,
2024-09-07 12:30:26 +02:00
enumProvider : ( ) => getModelOptions ( true ) ? . options ? . map ( option => new SlashCommandEnumValue ( option . value , option . value !== option . text ? option . text : null ) ) ? ? [ ] ,
2024-06-29 00:25:10 +02:00
} ) ,
] ,
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' ) ( ) ,
} ) ,
2024-09-07 12:09:10 +02:00
SlashCommandNamedArgument . fromProps ( {
name : 'quiet' ,
description : 'suppress the toast message on API change' ,
typeList : [ ARGUMENT _TYPE . BOOLEAN ] ,
defaultValue : 'false' ,
enumList : commonEnumProviders . boolean ( 'trueFalse' ) ( ) ,
} ) ,
2024-08-18 11:02:15 +02:00
] ,
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-08-19 20:31:58 +02:00
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'tokenizer' ,
callback : selectTokenizerCallback ,
returns : 'current tokenizer' ,
unnamedArgumentList : [
SlashCommandArgument . fromProps ( {
description : 'tokenizer name' ,
typeList : [ ARGUMENT _TYPE . STRING ] ,
enumList : getAvailableTokenizers ( ) . map ( tokenizer =>
new SlashCommandEnumValue ( tokenizer . tokenizerKey , tokenizer . tokenizerName , enumTypes . enum , enumIcons . default ) ) ,
} ) ,
] ,
helpString : `
< div >
Selects tokenizer by name . Gets the current tokenizer if no name is provided .
< / d i v >
< div >
< strong > Available tokenizers : < / s t r o n g >
< pre > < code > $ { getAvailableTokenizers ( ) . map ( t => t . tokenizerKey ) . join ( ', ' ) } < / c o d e > < / p r e >
< / 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 '' ;
}
2024-09-08 17:24:12 +02:00
async function listInjectsCallback ( args ) {
2024-10-01 00:06:18 +02:00
/** @type {import('./slash-commands/SlashCommandReturnHelper.js').SlashCommandReturnType} */
let returnType = args . return ;
2023-11-28 00:44:13 +01:00
2024-10-01 00:06:18 +02:00
// Old legacy return type handling
if ( args . format ) {
toastr . warning ( ` Legacy argument 'format' with value ' ${ args . format } ' is deprecated. Please use 'return' instead. Routing to the correct return type... ` , 'Deprecation warning' ) ;
const type = String ( args ? . format ) . toLowerCase ( ) . trim ( ) ;
if ( ! chat _metadata . script _injects || ! Object . keys ( chat _metadata . script _injects ) . length ) {
type !== 'none' && toastr . info ( 'No script injections for the current chat' ) ;
}
switch ( type ) {
case 'none' :
returnType = 'none' ;
break ;
case 'chat' :
returnType = 'chat-html' ;
break ;
case 'popup' :
default :
returnType = 'popup-html' ;
break ;
}
2024-09-08 17:24:12 +02:00
}
2024-10-01 00:06:18 +02:00
// Now the actual new return type handling
const buildTextValue = ( injects ) => {
const injectsStr = Object . entries ( injects )
. map ( ( [ id , inject ] ) => {
const position = Object . entries ( extension _prompt _types ) ;
const positionName = position . find ( ( [ _ , value ] ) => value === inject . position ) ? . [ 0 ] ? ? 'unknown' ;
return ` * ** ${ id } **: <code> ${ inject . value } </code> ( ${ positionName } , depth: ${ inject . depth } , scan: ${ inject . scan ? ? false } , role: ${ inject . role ? ? extension _prompt _roles . SYSTEM } ) ` ;
} )
. join ( '\n' ) ;
2024-10-01 01:30:20 +02:00
return ` ### Script injections: \n ${ injectsStr || 'No script injections for the current chat' } ` ;
2024-10-01 00:06:18 +02:00
} ;
2024-09-08 17:24:12 +02:00
2024-10-01 01:30:20 +02:00
return await slashCommandReturnHelper . doReturn ( returnType ? ? 'popup-html' , chat _metadata . script _injects ? ? { } , { objectToStringFunc : buildTextValue } ) ;
2023-11-28 00:44:13 +01:00
}
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 ) {
2024-09-06 23:14:52 +02:00
const safeBody = DOMPurify . sanitize ( value || '' ) ;
const safeHeader = args ? . header && typeof args ? . header === 'string' ? DOMPurify . sanitize ( args . header ) : null ;
2024-09-06 23:29:18 +02:00
const requestedResult = isTrueBoolean ( args ? . result ) ;
2024-09-06 23:14:52 +02:00
2024-09-06 22:22:35 +02:00
/** @type {import('./popup.js').PopupOptions} */
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 ) ,
2024-09-06 22:22:35 +02:00
wider : isTrueBoolean ( args ? . wider ) ,
transparent : isTrueBoolean ( args ? . transparent ) ,
2023-12-07 12:35:48 +01:00
okButton : args ? . okButton !== undefined && typeof args ? . okButton === 'string' ? args . okButton : 'Ok' ,
2024-09-06 22:38:02 +02:00
cancelButton : args ? . cancelButton !== undefined && typeof args ? . cancelButton === 'string' ? args . cancelButton : null ,
2023-12-07 02:26:29 +01:00
} ;
2024-09-06 23:29:18 +02:00
const result = await Popup . show . text ( safeHeader , safeBody , popupOptions ) ;
2024-09-07 01:21:04 +02:00
return String ( requestedResult ? result ? ? '' : 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' ) ;
2024-09-12 22:41:53 +02:00
return '' ;
2023-11-24 18:06:31 +01:00
}
// 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 ;
2024-09-12 22:41:53 +02:00
} catch ( err ) {
2024-09-13 13:12:41 +02:00
console . error ( 'Error on /genraw generation' , err ) ;
2024-09-13 13:22:12 +02:00
toastr . error ( err . message , 'API Error' , { preventDuplicates : true } ) ;
2023-11-24 14:18:49 +01:00
} finally {
if ( lock ) {
activateSendButtons ( ) ;
}
2023-11-24 18:06:31 +01:00
flushEphemeralStoppingStrings ( ) ;
2023-11-24 14:18:49 +01:00
}
2024-09-12 22:41:53 +02:00
return '' ;
2023-11-23 21:50:13 +01:00
}
2024-08-24 14:22:50 +02:00
/ * *
* Callback for the / gen command
* @ param { object } args Named arguments
* @ param { string } value Unnamed argument
* @ returns { Promise < string > } The generated text
* /
2023-12-07 12:35:48 +01:00
async function generateCallback ( args , value ) {
2023-11-21 23:39:17 +01:00
// 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-09-29 02:36:33 +02:00
const char = findChar ( { name : name } ) ;
const result = await generateQuietPrompt ( value , quietToLoud , false , '' , char ? . name ? ? name , length ) ;
2023-11-24 14:18:49 +01:00
return result ;
2024-09-12 22:41:53 +02:00
} catch ( err ) {
2024-09-13 13:12:41 +02:00
console . error ( 'Error on /gen generation' , err ) ;
2024-09-13 13:22:12 +02:00
toastr . error ( err . message , 'API Error' , { preventDuplicates : true } ) ;
2023-11-24 14:18:49 +01:00
} finally {
if ( lock ) {
activateSendButtons ( ) ;
}
2023-11-24 18:06:31 +01:00
flushEphemeralStoppingStrings ( ) ;
2023-11-24 14:18:49 +01:00
}
2024-09-12 22:41:53 +02:00
return '' ;
2023-11-21 23:39:17 +01:00
}
2024-06-29 02:16:36 +02:00
/ * *
*
2024-09-06 19:32:35 +02:00
* @ param { { title ? : string , severity ? : string , timeout ? : string , extendedTimeout ? : string , preventDuplicates ? : string , awaitDismissal ? : string , cssClass ? : string , color ? : string , escapeHtml ? : string , onClick ? : SlashCommandClosure } } 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-09-06 21:18:16 +02:00
let title = args . title ? args . title : undefined ;
2024-06-29 02:16:36 +02:00
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 ;
2024-09-06 19:03:21 +02:00
if ( args . cssClass ) options . toastClass = args . cssClass ;
2024-09-07 13:03:00 +02:00
options . escapeHtml = args . escapeHtml !== undefined ? isTrueBoolean ( args . escapeHtml ) : true ;
2024-06-29 02:16:36 +02:00
// 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-09-06 19:32:35 +02:00
if ( args . onClick ) {
2024-09-06 19:49:32 +02:00
if ( args . onClick instanceof SlashCommandClosure ) {
2024-09-06 20:01:45 +02:00
options . onclick = async ( ) => {
2024-09-06 20:21:32 +02:00
// Execute the slash command directly, with its internal scope and everything. Clear progress handler so it doesn't interfere with command execution progress.
args . onClick . onProgress = null ;
2024-09-06 20:01:45 +02:00
await args . onClick . execute ( ) ;
} ;
2024-09-06 19:49:32 +02:00
} else {
toastr . warning ( 'Invalid onClick provided for /echo command. This is not a closure' ) ;
}
2024-09-06 19:32:35 +02:00
}
2024-06-29 02:16:36 +02:00
2024-09-06 20:31:13 +02:00
// If we allow HTML, we need to sanitize it to prevent security risks
if ( ! options . escapeHtml ) {
2024-09-06 21:18:16 +02:00
if ( title ) title = DOMPurify . sanitize ( title , { FORBID _TAGS : [ 'style' ] } ) ;
2024-09-06 20:31:13 +02:00
value = DOMPurify . sanitize ( value , { FORBID _TAGS : [ 'style' ] } ) ;
}
2024-09-06 18:55:50 +02:00
let toast ;
2023-12-07 02:26:29 +01:00
switch ( severity ) {
case 'error' :
2024-09-06 18:55:50 +02:00
toast = toastr . error ( value , title , options ) ;
2023-12-07 02:26:29 +01:00
break ;
case 'warning' :
2024-09-06 18:55:50 +02:00
toast = toastr . warning ( value , title , options ) ;
2023-12-07 02:26:29 +01:00
break ;
case 'success' :
2024-09-06 18:55:50 +02:00
toast = toastr . success ( value , title , options ) ;
2023-12-07 02:26:29 +01:00
break ;
case 'info' :
default :
2024-09-06 18:55:50 +02:00
toast = toastr . info ( value , title , options ) ;
2023-12-07 02:26:29 +01:00
break ;
}
2024-06-29 02:16:36 +02:00
2024-09-06 18:55:50 +02:00
if ( args . color ) {
toast . css ( 'background-color' , args . color ) ;
}
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 ) {
2024-09-01 23:12:33 +02:00
// Take the provided argument. Null if none provided, which will target the current swipe.
const swipeId = arg && ! isNaN ( Number ( arg ) ) ? ( Number ( arg ) - 1 ) : null ;
2023-11-21 22:28:11 +01:00
2024-09-01 23:12:33 +02:00
const newSwipeId = await deleteSwipe ( swipeId ) ;
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 ) {
2024-09-30 18:38:17 +02:00
toastr . warning ( 'Cannot run /ask command in a group chat!' ) ;
2024-06-14 23:48:41 +02:00
return '' ;
2023-10-16 08:12:12 +02:00
}
2024-09-29 02:14:06 +02:00
if ( ! args . name ) {
toastr . warning ( 'You must specify a name of the character to ask.' ) ;
return '' ;
2023-10-16 08:12:12 +02:00
}
const prevChId = this _chid ;
// Find the character
2024-09-29 02:14:06 +02:00
const character = findChar ( { name : args ? . name } ) ;
if ( ! character ) {
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
}
2024-09-29 02:14:06 +02:00
const chId = getCharIndex ( character ) ;
2024-09-12 19:13:05 +02:00
if ( text ) {
const mesText = getRegexedString ( text . trim ( ) , regex _placement . SLASH _COMMAND ) ;
// Sending a message implicitly saves the chat, so this needs to be done before changing the character
// Otherwise, a corruption will occur
await sendMessageAsUser ( mesText , '' ) ;
}
2024-09-12 11:50:06 +02:00
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
2024-09-29 02:14:06 +02:00
const { name , force _avatar , original _avatar } = getNameAndAvatarForMessage ( character , args ? . name ) ;
2023-10-16 08:12:12 +02:00
2024-09-29 02:14:06 +02:00
setCharacterName ( name ) ;
2023-10-16 08:12:12 +02:00
const restoreCharacter = ( ) => {
2024-09-12 12:02:37 +02:00
if ( String ( this _chid ) !== String ( chId ) ) {
return ;
}
2024-09-29 18:12:26 +02:00
if ( prevChId !== undefined ) {
setCharacterId ( prevChId ) ;
setCharacterName ( characters [ prevChId ] . name ) ;
} else {
setCharacterId ( undefined ) ;
setCharacterName ( neutralCharacterName ) ;
}
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 ] ;
2024-09-29 02:14:06 +02:00
if ( lastMessage && lastMessage ? . name === name ) {
2023-10-16 08:12:12 +02:00
lastMessage . force _avatar = force _avatar ;
lastMessage . original _avatar = original _avatar ;
}
2023-12-02 20:11:06 +01:00
} ;
2023-10-16 08:12:12 +02:00
2024-09-12 09:19:03 +02:00
let askResult = '' ;
2024-09-11 18:34:26 +02:00
// Run generate and restore previous character
2023-10-16 08:12:12 +02:00
try {
2024-09-12 12:02:37 +02:00
eventSource . once ( event _types . MESSAGE _RECEIVED , restoreCharacter ) ;
2024-09-29 02:14:06 +02:00
toastr . info ( ` Asking ${ name } something... ` ) ;
2024-09-12 09:19:03 +02:00
askResult = await Generate ( 'ask_command' ) ;
2024-09-11 18:34:26 +02:00
} catch ( error ) {
2024-09-12 12:02:37 +02:00
restoreCharacter ( ) ;
2024-09-11 18:34:26 +02:00
console . error ( 'Error running /ask command' , error ) ;
} finally {
2024-09-12 12:02:37 +02:00
if ( String ( this _chid ) === String ( prevChId ) ) {
2024-09-12 09:19:03 +02:00
await saveChatConditional ( ) ;
} else {
2024-09-11 18:34:26 +02:00
toastr . error ( 'It is strongly recommended to reload the page.' , 'Something went wrong' ) ;
}
2023-10-16 08:12:12 +02:00
}
2024-09-30 23:37:21 +02:00
const message = askResult ? chat [ chat . length - 1 ] : null ;
return await slashCommandReturnHelper . doReturn ( args . return ? ? 'pipe' , message , { objectToStringFunc : x => x . mes } ) ;
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
}
2024-09-29 02:54:12 +02:00
async function addGroupMemberCallback ( _ , name ) {
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
}
2024-09-29 02:54:12 +02:00
if ( ! name ) {
2023-11-21 21:35:59 +01:00
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
}
2024-09-29 02:54:12 +02:00
const character = findChar ( { name : name , preferCurrentChar : false } ) ;
if ( ! character ) {
console . warn ( ` WARN: No character found for argument ${ name } ` ) ;
2023-11-27 01:18:36 +01:00
return '' ;
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 ) ) {
2024-09-29 02:36:33 +02:00
if ( equalsIgnoreCaseAndAccents ( persona [ 1 ] , name ) ) {
2024-05-06 00:18:59 +02:00
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 ) {
2024-09-30 18:30:09 +02:00
toastr . warning ( 'You must specify text to send' ) ;
2023-07-26 20:00:36 +02:00
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
2024-09-30 18:32:21 +02:00
let message ;
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 ;
2024-09-30 18:32:21 +02:00
message = await sendMessageAsUser ( text , bias , insertAt , compact , name , avatar ) ;
2024-05-06 00:18:59 +02:00
}
else {
2024-09-30 18:32:21 +02:00
message = await sendMessageAsUser ( text , bias , insertAt , compact ) ;
2024-05-06 00:18:59 +02:00
}
2024-10-01 00:41:14 +02:00
return await slashCommandReturnHelper . doReturn ( args . return ? ? 'none' , message , { objectToStringFunc : x => x . mes } ) ;
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 ;
}
2024-09-29 02:36:33 +02:00
// Search for a matching character to get the real name, or take the name provided
const character = findChar ( { name : name } ) ;
name = character ? . name || name ;
2023-07-20 19:32:15 +02:00
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
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 ;
}
2024-09-29 02:36:33 +02:00
const character = findChar ( { name : name } ) ;
if ( character ) {
const chid = getCharIndex ( character ) ;
await openChat ( new String ( chid ) ) ;
setActiveCharacter ( character . avatar ) ;
2024-03-26 17:21:22 +01:00
setActiveGroup ( null ) ;
2024-09-29 02:36:33 +02:00
return character . name ;
2023-07-20 19:32:15 +02:00
}
2024-09-29 02:36:33 +02:00
const group = groups . find ( it => equalsIgnoreCaseAndAccents ( it . name , name ) ) ;
if ( group ) {
await openGroupById ( group . id ) ;
setActiveCharacter ( null ) ;
setActiveGroup ( group . id ) ;
return group . name ;
}
console . warn ( ` No matches found for name " ${ name } " ` ) ;
return '' ;
2023-07-20 19:32:15 +02:00
}
2024-09-29 02:36:33 +02:00
async function openChat ( chid ) {
2023-07-20 19:32:15 +02:00
resetSelectedGroup ( ) ;
2024-09-29 02:36:33 +02:00
setCharacterId ( chid ) ;
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 ) ;
2024-10-01 13:14:19 +02:00
reject ( error ) ;
2024-07-08 19:12:52 +02:00
}
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
}
2024-09-29 00:36:13 +02:00
/ * *
* Checks if an argument is a string array ( or undefined ) , and if not , throws an error
* @ param { string | SlashCommandClosure | ( string | SlashCommandClosure ) [ ] | undefined } arg The named argument to check
* @ param { string } name The name of the argument for the error message
* @ param { object } [ options = { } ] - The optional arguments
* @ param { boolean } [ options . allowUndefined = false ] - Whether the argument can be undefined
* @ throws { Error } If the argument is not an array
* @ returns { string [ ] }
* /
export function validateArrayArgString ( arg , name , { allowUndefined = true } = { } ) {
if ( arg === undefined ) {
if ( allowUndefined ) return undefined ;
throw new Error ( ` Argument " ${ name } " is undefined, but must be a string array ` ) ;
}
if ( ! Array . isArray ( arg ) ) throw new Error ( ` Argument " ${ name } " must be an array ` ) ;
if ( ! arg . every ( x => typeof x === 'string' ) ) throw new Error ( ` Argument " ${ name } " must be an array of strings ` ) ;
return arg ;
}
/ * *
* Checks if an argument is a string or closure array ( or undefined ) , and if not , throws an error
* @ param { string | SlashCommandClosure | ( string | SlashCommandClosure ) [ ] | undefined } arg The named argument to check
* @ param { string } name The name of the argument for the error message
* @ param { object } [ options = { } ] - The optional arguments
* @ param { boolean } [ options . allowUndefined = false ] - Whether the argument can be undefined
* @ throws { Error } If the argument is not an array of strings or closures
* @ returns { ( string | SlashCommandClosure ) [ ] }
* /
export function validateArrayArg ( arg , name , { allowUndefined = true } = { } ) {
if ( arg === undefined ) {
if ( allowUndefined ) return [ ] ;
throw new Error ( ` Argument " ${ name } " is undefined, but must be an array of strings or closures ` ) ;
}
if ( ! Array . isArray ( arg ) ) throw new Error ( ` Argument " ${ name } " must be an array ` ) ;
if ( ! arg . every ( x => typeof x === 'string' || x instanceof SlashCommandClosure ) ) throw new Error ( ` Argument " ${ name } " must be an array of strings or closures ` ) ;
return arg ;
}
2024-09-29 02:14:06 +02:00
/ * *
* Retrieves the name and avatar information for a message
*
* The name of the character will always have precendence over the one given as argument . If you want to specify a different name for the message ,
* explicitly implement this in the code using this .
*
* @ param { object ? } character - The character object to get the avatar data for
* @ param { string ? } name - The name to get the avatar data for
* @ returns { { name : string , force _avatar : string , original _avatar : string } } An object containing the name for the message , forced avatar URL , and original avatar
* /
export function getNameAndAvatarForMessage ( character , name = null ) {
const isNeutralCharacter = ! character && name2 === neutralCharacterName && name === neutralCharacterName ;
const currentChar = characters [ this _chid ] ;
let force _avatar , original _avatar ;
if ( character ? . avatar === currentChar ? . avatar || isNeutralCharacter ) {
// If the targeted character is the currently selected one in a solo chat, we don't need to force any avatars
}
else if ( character && character . avatar !== 'none' ) {
force _avatar = getThumbnailUrl ( 'avatar' , character . avatar ) ;
original _avatar = character . avatar ;
}
else {
force _avatar = default _avatar ;
original _avatar = default _avatar ;
}
return {
name : character ? . name || name ,
force _avatar : force _avatar ,
original _avatar : original _avatar ,
} ;
}
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-09-30 18:13:13 +02:00
toastr . warning ( 'You must specify text to send as' ) ;
2024-06-14 23:48:41 +02:00
return '' ;
2023-07-20 19:32:15 +02:00
}
2024-09-30 18:13:13 +02:00
let name = args . name ? . trim ( ) ;
2023-11-21 01:54:04 +01:00
let mesText ;
2023-07-20 19:32:15 +02:00
2024-09-30 18:13:13 +02:00
if ( ! name ) {
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 ;
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-09-29 00:53:43 +02:00
const character = findChar ( { name : name } ) ;
2024-09-15 17:53:14 +02:00
2024-09-29 00:53:43 +02:00
const avatarCharacter = args . avatar ? findChar ( { name : args . avatar } ) : character ;
2024-09-15 19:20:43 +02:00
if ( args . avatar && ! avatarCharacter ) {
toastr . warning ( ` Character for avatar ${ args . avatar } not found ` ) ;
return '' ;
2024-09-15 18:21:31 +02:00
}
2024-09-29 17:32:18 +02:00
const { name : avatarCharName , force _avatar , original _avatar } = getNameAndAvatarForMessage ( avatarCharacter , name ) ;
2023-07-20 19:32:15 +02:00
const message = {
2024-09-29 17:32:18 +02:00
name : character ? . name || name || avatarCharName ,
2023-07-20 19:32:15 +02:00
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
2024-09-30 23:32:24 +02:00
return await slashCommandReturnHelper . doReturn ( args . return ? ? 'none' , message , { objectToStringFunc : x => x . mes } ) ;
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-09-30 18:30:09 +02:00
toastr . warning ( 'You must specify text to send' ) ;
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
2024-09-30 23:32:24 +02:00
return await slashCommandReturnHelper . doReturn ( args . return ? ? 'none' , message , { objectToStringFunc : x => x . mes } ) ;
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-09-30 18:30:09 +02:00
toastr . warning ( 'You must specify text to send' ) ;
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
2024-09-30 23:32:24 +02:00
return await slashCommandReturnHelper . doReturn ( args . return ? ? 'none' , message , { objectToStringFunc : x => x . mes } ) ;
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
2024-09-07 12:30:26 +02:00
* @ param { boolean } quiet - Whether to suppress toasts
2024-06-21 21:24:37 +02:00
*
2024-10-01 14:02:14 +02:00
* @ returns { { control : HTMLSelectElement | HTMLInputElement , 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-09-07 12:30:26 +02:00
function getModelOptions ( quiet ) {
const nullResult = { control : null , options : null } ;
2024-03-15 15:08:22 +01:00
const modelSelectMap = [
2024-10-01 14:02:14 +02:00
{ id : 'custom_model_textgenerationwebui' , api : 'textgenerationwebui' , type : textgen _types . OOBA } ,
2024-03-15 15:08:22 +01:00
{ 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 } ,
2024-09-08 21:23:25 +02:00
{ id : 'tabby_model' , api : 'textgenerationwebui' , type : textgen _types . TABBY } ,
2024-10-01 22:55:32 +02:00
{ id : 'featherless_model' , api : 'textgenerationwebui' , type : textgen _types . FEATHERLESS } ,
2024-03-15 15:08:22 +01:00
{ 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 } ,
2024-10-01 14:02:14 +02:00
{ id : 'custom_model_id' , 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 :
2024-09-26 22:46:03 +02:00
return null ;
2024-03-15 15:08:22 +01:00
}
}
const apiSubType = getSubType ( ) ;
const modelSelectItem = modelSelectMap . find ( x => x . api == main _api && x . type == apiSubType ) ? . id ;
if ( ! modelSelectItem ) {
2024-09-07 12:30:26 +02:00
! quiet && toastr . info ( 'Setting a model for your API is not supported or not implemented yet.' ) ;
return nullResult ;
2024-03-15 15:08:22 +01:00
}
const modelSelectControl = document . getElementById ( modelSelectItem ) ;
2024-10-01 14:02:14 +02:00
if ( ! ( modelSelectControl instanceof HTMLSelectElement ) && ! ( modelSelectControl instanceof HTMLInputElement ) ) {
2024-09-07 12:30:26 +02:00
! quiet && toastr . error ( ` Model select control not found: ${ main _api } [ ${ apiSubType } ] ` ) ;
return nullResult ;
2024-03-15 15:08:22 +01:00
}
2024-10-01 14:02:14 +02:00
/ * *
* Get options from a HTMLSelectElement or HTMLInputElement with a list .
* @ param { HTMLSelectElement | HTMLInputElement } control Control containing the options
* @ returns { HTMLOptionElement [ ] } Array of options
* /
const getOptions = ( control ) => {
if ( control instanceof HTMLSelectElement ) {
return Array . from ( control . options ) ;
}
const valueOption = new Option ( control . value , control . value ) ;
if ( control instanceof HTMLInputElement && control . list instanceof HTMLDataListElement ) {
return [ valueOption , ... Array . from ( control . list . options ) ] ;
}
return [ valueOption ] ;
} ;
const options = getOptions ( modelSelectControl ) . filter ( x => x . value ) . filter ( onlyUnique ) ;
2024-06-21 21:24:37 +02:00
return { control : modelSelectControl , options } ;
}
/ * *
* Sets a model for the current API .
2024-09-06 22:09:06 +02:00
* @ param { object } args Named arguments
2024-06-21 21:24:37 +02:00
* @ param { string } model New model name
* @ returns { string } New or existing model name
* /
2024-09-06 22:09:06 +02:00
function modelCallback ( args , model ) {
2024-09-07 12:30:26 +02:00
const quiet = isTrueBoolean ( args ? . quiet ) ;
const { control : modelSelectControl , options } = getModelOptions ( quiet ) ;
2024-06-21 21:24:37 +02:00
// 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
2024-04-02 23:52:30 +02:00
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-10-01 14:02:14 +02:00
if ( modelSelectControl instanceof HTMLInputElement ) {
modelSelectControl . value = model ;
$ ( modelSelectControl ) . trigger ( 'input' ) ;
! quiet && toastr . success ( ` Model set to " ${ model } " ` ) ;
return model ;
}
if ( ! options . length ) {
! quiet && toastr . warning ( 'No model options found. Check your API settings.' ) ;
return '' ;
}
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' ) ;
2024-09-06 22:09:06 +02:00
! quiet && 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 {
2024-09-07 12:30:26 +02:00
! quiet && 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 ) {
2024-09-29 11:48:40 +02:00
// Arg is already an array
if ( Array . isArray ( arg ) ) {
return arg ;
}
2024-06-16 12:56:43 +02:00
const list = [ ] ;
try {
2024-09-29 11:48:40 +02:00
// Arg is a JSON-stringified array
2024-06-16 12:56:43 +02:00
const parsedArg = JSON . parse ( arg ) ;
list . push ( ... Array . isArray ( parsedArg ) ? parsedArg : [ arg ] ) ;
} catch {
2024-09-29 11:48:40 +02:00
// Arg is a string
2024-06-16 12:56:43 +02:00
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
2024-09-07 12:09:10 +02:00
* @ param { string ? } [ args . quiet = false ] - whether to suppress toasts
2024-08-18 11:02:15 +02:00
* @ param { string } url - the API URL to set
* @ returns { Promise < string > }
* /
2024-09-07 12:09:10 +02:00
async function setApiUrlCallback ( { api = null , connect = 'true' , quiet = 'false' } , url ) {
const isQuiet = isTrueBoolean ( quiet ) ;
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 ) ) {
2024-09-07 12:09:10 +02:00
! isQuiet && toastr . warning ( ` API ' ${ api } ' is not a valid text_gen API. ` ) ;
2024-08-18 11:02:15 +02:00
return '' ;
}
if ( ! api && ! Object . values ( textgen _types ) . includes ( textgenerationwebui _settings . type ) ) {
2024-09-07 12:09:10 +02:00
! isQuiet && toastr . warning ( ` API ' ${ textgenerationwebui _settings . type } ' is not a valid text_gen API. ` ) ;
return '' ;
}
if ( ! api && main _api !== 'textgenerationwebui' ) {
! isQuiet && toastr . warning ( ` API type ' ${ main _api } ' does not support setting the server URL. ` ) ;
2024-08-18 11:02:15 +02:00
return '' ;
}
2024-08-18 11:18:06 +02:00
if ( api && url && autoConnect && api !== textgenerationwebui _settings . type ) {
2024-09-07 12:09:10 +02:00
! isQuiet && toastr . warning ( ` API ' ${ api } ' is not the currently selected API, so we cannot do an auto-connect. Consider switching to it via /api beforehand. ` ) ;
2024-08-18 11:02:15 +02:00
return '' ;
}
const type = api || textgenerationwebui _settings . type ;
const inputSelector = SERVER _INPUTS [ type ] ;
if ( ! inputSelector ) {
2024-09-07 12:09:10 +02:00
! isQuiet && toastr . warning ( ` API ' ${ type } ' does not have a server url input. ` ) ;
2024-08-18 11:02:15 +02:00
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-08-19 20:31:58 +02:00
async function selectTokenizerCallback ( _ , name ) {
if ( ! name ) {
return getAvailableTokenizers ( ) . find ( tokenizer => tokenizer . tokenizerId === power _user . tokenizer ) ? . tokenizerKey ? ? '' ;
}
const tokenizers = getAvailableTokenizers ( ) ;
const fuse = new Fuse ( tokenizers , { keys : [ 'tokenizerKey' , 'tokenizerName' ] } ) ;
const result = fuse . search ( name ) ;
if ( result . length === 0 ) {
toastr . warning ( ` Tokenizer " ${ name } " not found ` ) ;
return '' ;
}
/** @type {import('./tokenizers.js').Tokenizer} */
const foundTokenizer = result [ 0 ] . item ;
selectTokenizer ( foundTokenizer . tokenizerId ) ;
return foundTokenizer . tokenizerKey ;
}
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
} ) ;