2024-10-16 23:11:13 +03:00
import { Fuse , DOMPurify } from '../lib.js' ;
2024-10-16 22:15:38 +03:00
2023-07-20 20:32:15 +03:00
import {
2023-12-06 19:26:29 -06:00
Generate ,
2024-08-18 12:18:06 +03:00
UNIQUE _APIS ,
2023-12-06 19:26:29 -06:00
activateSendButtons ,
2023-07-20 20:32:15 +03:00
addOneMessage ,
2024-08-18 12:18:06 +03:00
api _server ,
2023-12-06 19:26:29 -06:00
callPopup ,
2023-07-20 20:32:15 +03:00
characters ,
chat ,
chat _metadata ,
2023-12-06 19:26:29 -06:00
comment _avatar ,
deactivateSendButtons ,
2023-07-20 20:32:15 +03:00
default _avatar ,
2024-09-01 23:12:33 +02:00
deleteSwipe ,
2023-07-20 20:32:15 +03:00
eventSource ,
event _types ,
2024-03-23 18:44:40 +02:00
extension _prompt _roles ,
2023-12-06 19:26:29 -06:00
extension _prompt _types ,
2023-07-20 20:32:15 +03:00
extractMessageBias ,
2023-12-06 19:26:29 -06:00
generateQuietPrompt ,
generateRaw ,
2023-07-20 20:32:15 +03:00
getThumbnailUrl ,
2023-12-06 19:26:29 -06:00
is _send _press ,
main _api ,
name1 ,
2024-04-28 03:53:17 +03:00
name2 ,
2024-09-10 19:44:27 +02:00
neutralCharacterName ,
2023-12-06 19:26:29 -06:00
reloadCurrentChat ,
2023-12-21 20:49:03 +02:00
removeMacros ,
2024-06-05 02:19:22 +02:00
renameCharacter ,
2023-07-20 20:32:15 +03:00
saveChatConditional ,
2023-12-06 19:26:29 -06:00
sendMessageAsUser ,
2023-07-20 20:32:15 +03:00
sendSystemMessage ,
2024-03-26 12:21:22 -04:00
setActiveCharacter ,
setActiveGroup ,
2023-12-06 19:26:29 -06:00
setCharacterId ,
setCharacterName ,
setExtensionPrompt ,
2023-07-20 20:32:15 +03:00
setUserName ,
2024-07-03 14:44:17 +02:00
stopGeneration ,
2023-07-20 20:32:15 +03:00
substituteParams ,
system _avatar ,
system _message _types ,
2023-10-16 02:12:12 -04:00
this _chid ,
2023-12-02 13:04:51 -05:00
} from '../script.js' ;
2024-06-29 00:25:10 +02:00
import { SlashCommandParser } from './slash-commands/SlashCommandParser.js' ;
2024-05-12 15:15:05 -04:00
import { SlashCommandParserError } from './slash-commands/SlashCommandParserError.js' ;
2024-12-21 22:25:14 +02:00
import { getMessageTimeStamp , isMobile } from './RossAscends-mods.js' ;
2024-04-12 16:03:50 +09:00
import { hideChatMessageRange } from './chats.js' ;
2024-06-29 00:25:10 +02:00
import { getContext , saveMetadataDebounced } from './extensions.js' ;
2023-12-02 13:04:51 -05: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-12-05 22:06:16 +02:00
import { chat _completion _sources , oai _settings , promptManager } from './openai.js' ;
2024-06-26 20:18:05 +03:00
import { autoSelectPersona , retriggerFirstMessageOnEmptyChat , setPersonaLockState , togglePersonaLock , user _avatar } from './personas.js' ;
2023-12-06 19:26:29 -06:00
import { addEphemeralStoppingString , chat _styles , flushEphemeralStoppingStrings , power _user } from './power-user.js' ;
2024-08-18 12:02:15 +03:00
import { SERVER _INPUTS , textgen _types , textgenerationwebui _settings } from './textgen-settings.js' ;
2024-08-19 21:31:58 +03:00
import { decodeTextTokens , getAvailableTokenizers , getFriendlyTokenizerName , getTextTokens , getTokenCountAsync , selectTokenizer } from './tokenizers.js' ;
2024-10-01 15:02:14 +03:00
import { debounce , delay , equalsIgnoreCaseAndAccents , findChar , getCharIndex , isFalseBoolean , isTrueBoolean , onlyUnique , showFontAwesomePicker , stringToRange , trimToEndSentence , trimToStartSentence , waitUntilCondition } from './utils.js' ;
2023-12-02 13:04:51 -05:00
import { registerVariableCommands , resolveVariable } from './variables.js' ;
2024-04-12 08:21:17 +09:00
import { background _settings } from './backgrounds.js' ;
2024-05-12 15:15:05 -04: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 12:31:27 -04:00
import { SlashCommandBreakController } from './slash-commands/SlashCommandBreakController.js' ;
2024-07-05 18:05:22 -04: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 15:15:05 -04:00
export {
executeSlashCommands , executeSlashCommandsWithOptions , getSlashCommandsHelp , registerSlashCommand ,
} ;
export const parser = new SlashCommandParser ( ) ;
2024-04-19 14:19:57 +03:00
/ * *
2024-05-12 15:15:05 -04:00
* @ deprecated Use SlashCommandParser . addCommandObject ( ) instead
2024-04-19 14:19:57 +03:00
* /
2024-05-12 15:15:05 -04:00
const registerSlashCommand = SlashCommandParser . addCommand . bind ( SlashCommandParser ) ;
const getSlashCommandsHelp = parser . getHelpString . bind ( parser ) ;
2024-12-13 00:02:24 +02:00
/ * *
* Converts a SlashCommandClosure to a filter function that returns a boolean .
* @ param { SlashCommandClosure } closure
* @ returns { ( ) => Promise < boolean > }
* /
function closureToFilter ( closure ) {
return async ( ) => {
2024-12-13 01:23:13 +02:00
try {
const localClosure = closure . getCopy ( ) ;
localClosure . onProgress = ( ) => { } ;
const result = await localClosure . execute ( ) ;
return isTrueBoolean ( result . pipe ) ;
} catch ( e ) {
console . error ( 'Error executing filter closure' , e ) ;
return false ;
}
2024-12-13 00:02:24 +02:00
} ;
}
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 16:19:47 -05: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 15:15:05 -04: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 15:15:05 -04: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 15:15:05 -04: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-12-04 12:57:02 +00: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 (including -0) 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 15:15:05 -04: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 15:15:05 -04: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-12-04 12:57:02 +00: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 (including -0) 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 15:15:05 -04: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-12-04 12:57:02 +00: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 (including -0) 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 15:15:05 -04: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 20:02:21 +03: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 15:15:05 -04: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 20:02:21 +03: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 15:15:05 -04: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 20:02:21 +03:00
Continues the chat with no additional prompt and immediately proceeds to the next command .
2024-05-12 15:15:05 -04:00
< / l i >
< li >
2024-07-08 20:02:21 +03: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 15:15:05 -04: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 20:13:05 +03: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 15:15:05 -04: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-12-04 12:57:02 +00: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 (including -0) 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 13:59:34 +00: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 15:15:05 -04: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 15:15:05 -04: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 15:15:05 -04: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 20:59:00 +03: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 15:15:05 -04: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 15:15:05 -04: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 20:59:00 +03: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 15:15:05 -04: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 15:15:05 -04: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 18:01:16 +03: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 15:15:05 -04: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 15:15:05 -04: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 22:27:53 +03: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 15:15:05 -04: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 15:15:05 -04: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 15:15:05 -04:00
< / l i >
2024-09-06 22:39:30 +04: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 22:39:30 +04: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 22:39:30 +04:00
< / l i >
2024-05-12 15:15:05 -04: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 15:15:05 -04: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 15:15:05 -04: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 21:05:42 +03:00
returns : 'matching item' ,
2024-06-29 00:25:10 +02:00
namedArgumentList : [
2024-07-11 21:05:42 +03: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 21:19:09 +03:00
enumList : [
2024-07-11 21:05:42 +03: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 15:15:05 -04: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 21:05:42 +03: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 15:15:05 -04: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 15:15:05 -04: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 15:15:05 -04: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 15:15:05 -04: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-05 18:04:33 -04:00
typeList : [ ARGUMENT _TYPE . VARIABLE _NAME , ARGUMENT _TYPE . STRING , ARGUMENT _TYPE . CLOSURE ] ,
2024-06-29 00:25:10 +02:00
isRequired : true ,
2024-07-05 18:09:24 -04: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 15:15:05 -04: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 15:15:05 -04:00
< div >
Returns the specified message or range of messages as a string .
< / d i v >
2024-05-13 21:36:55 +03: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 15:15:05 -04: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 15:15:05 -04: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-10-23 23:03:07 +03:00
SlashCommandNamedArgument . fromProps ( {
name : 'scroll' ,
description : 'allows vertical scrolling of the content' ,
typeList : [ ARGUMENT _TYPE . BOOLEAN ] ,
enumList : commonEnumProviders . boolean ( 'trueFalse' ) ( ) ,
defaultValue : 'true' ,
} ) ,
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 15:15:05 -04: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 15:15:05 -04: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 15:15:05 -04: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 15:15:05 -04: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 15:15:05 -04: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 15:15:05 -04: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-28 00:26:44 +03: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' ,
) ,
2024-12-13 00:02:24 +02:00
SlashCommandNamedArgument . fromProps ( {
name : 'filter' ,
description : 'if a filter is defined, an injection will only be performed if the closure returns true' ,
typeList : [ ARGUMENT _TYPE . CLOSURE ] ,
isRequired : false ,
acceptsMultiple : false ,
} ) ,
2024-06-29 00:25:10 +02:00
] ,
unnamedArgumentList : [
new SlashCommandArgument (
'text' , [ ARGUMENT _TYPE . STRING ] , false ,
) ,
] ,
2024-07-28 00:26:44 +03: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 18:24:12 +03: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 18:24:12 +03:00
SlashCommandNamedArgument . fromProps ( {
name : 'format' ,
2024-10-01 00:37:21 +02:00
description : '!!! DEPRECATED - use "return" instead !!! output format' ,
2024-09-08 18:24:12 +03: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 23:09:06 +03: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 13:30:26 +03: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.' ,
} ) ) ;
2024-12-05 12:38:21 -06:00
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'getpromptentry' ,
aliases : [ 'getpromptentries' ] ,
callback : getPromptEntryCallback ,
returns : 'true/false state of prompt(s)' ,
namedArgumentList : [
SlashCommandNamedArgument . fromProps ( {
name : 'identifier' ,
description : 'Prompt entry identifier(s) to retrieve' ,
typeList : [ ARGUMENT _TYPE . STRING , ARGUMENT _TYPE . LIST ] ,
acceptsMultiple : true ,
enumProvider : ( ) =>
promptManager . serviceSettings . prompts
. map ( prompt => prompt . identifier )
2024-12-05 22:06:16 +02:00
. map ( identifier => new SlashCommandEnumValue ( identifier ) ) ,
2024-12-05 12:38:21 -06:00
} ) ,
SlashCommandNamedArgument . fromProps ( {
name : 'name' ,
description : 'Prompt entry name(s) to retrieve' ,
typeList : [ ARGUMENT _TYPE . STRING , ARGUMENT _TYPE . LIST ] ,
acceptsMultiple : true ,
enumProvider : ( ) =>
promptManager . serviceSettings . prompts
. map ( prompt => prompt . name )
2024-12-05 22:06:16 +02:00
. map ( name => new SlashCommandEnumValue ( name ) ) ,
2024-12-05 12:38:21 -06:00
} ) ,
SlashCommandNamedArgument . fromProps ( {
name : 'return' ,
description : 'Whether the return will be simple, a list, or a dict.' ,
typeList : [ ARGUMENT _TYPE . STRING ] ,
defaultValue : 'simple' ,
enumList : [ 'simple' , 'list' , 'dict' ] ,
} ) ,
] ,
helpString : `
< div >
Gets the state of the specified prompt entries .
< / d i v >
< div >
If < code > return < / c o d e > i s < c o d e > s i m p l e < / c o d e > ( d e f a u l t ) t h e n t h e r e t u r n w i l l b e a s i n g l e v a l u e i f o n l y o n e v a l u e w a s r e t r i e v e d ; o t h e r w i s e u s e s a d i c t ( i f t h e i d e n t i f i e r p a r a m e t e r w a s u s e d ) o r a l i s t .
< / d i v >
` ,
} ) ) ;
2024-06-29 00:25:10 +02:00
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 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 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 12:18:06 +03:00
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'pick-icon' ,
callback : async ( ) => ( ( await showFontAwesomePicker ( ) ) ? ? false ) . toString ( ) ,
2024-07-16 09:28:33 -04: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 12:02:15 +03: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 12:18:06 +03: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 12:02:15 +03: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 13:09:10 +03: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 12:02:15 +03: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 12:18:06 +03: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 12:02:15 +03: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 12:18:06 +03: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 12:02:15 +03: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 21:31:58 +03: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-11-23 18:10:03 +02:00
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'upper' ,
aliases : [ 'uppercase' , 'to-upper' ] ,
callback : ( _ , text ) => typeof text === 'string' ? text . toUpperCase ( ) : '' ,
returns : 'uppercase string' ,
unnamedArgumentList : [
new SlashCommandArgument (
'string' , [ ARGUMENT _TYPE . STRING ] , true , false ,
) ,
] ,
helpString : 'Converts the provided string to uppercase.' ,
} ) ) ;
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'lower' ,
aliases : [ 'lowercase' , 'to-lower' ] ,
callback : ( _ , text ) => typeof text === 'string' ? text . toLowerCase ( ) : '' ,
returns : 'lowercase string' ,
unnamedArgumentList : [
new SlashCommandArgument (
'string' , [ ARGUMENT _TYPE . STRING ] , true , false ,
) ,
] ,
helpString : 'Converts the provided string to lowercase.' ,
} ) ) ;
2024-12-14 19:25:09 -06:00
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'substr' ,
aliases : [ 'substring' ] ,
callback : ( arg , text ) => typeof text === 'string' ? text . slice ( ... [ Number ( arg . start ) , arg . end && Number ( arg . end ) ] ) : '' ,
returns : 'substring' ,
namedArgumentList : [
new SlashCommandNamedArgument (
'start' , 'start index' , [ ARGUMENT _TYPE . NUMBER ] , false , false ,
) ,
new SlashCommandNamedArgument (
'end' , 'end index' , [ ARGUMENT _TYPE . NUMBER ] , false , false ,
) ,
] ,
unnamedArgumentList : [
new SlashCommandArgument (
'string' , [ ARGUMENT _TYPE . STRING ] , true , false ,
) ,
] ,
helpString : `
< div >
Extracts text from the provided string .
< / d i v >
< div >
If < code > start < /code> is omitted, it's treated as 0.<br / >
If < code > start < /code> < 0, the index is counted from the end of the string.<br / >
If < code > start < /code> >= the string's length, an empty string is returned.<br / >
If < code > end < / c o d e > i s o m i t t e d , o r i f < c o d e > e n d < / c o d e > > = t h e s t r i n g ' s l e n g t h , e x t r a c t s t o t h e e n d o f t h e s t r i n g . < b r / >
If < code > end < /code> < 0, the index is counted from the end of the string.<br / >
If < code > end < / c o d e > < = < c o d e > s t a r t < / c o d e > a f t e r n o r m a l i z i n g n e g a t i v e v a l u e s , a n e m p t y s t r i n g i s r e t u r n e d .
< / d i v >
< div >
< strong > Example : < / s t r o n g >
< pre > / l e t x T h e m o r n i n g i s u p o n u s . | | < / p r e >
< pre > / s u b s t r s t a r t = - 3 { { v a r : : x } } | / e c h o | / # u s . | | < / p r e >
< pre > / s u b s t r s t a r t = - 3 e n d = - 1 { { v a r : : x } } | / e c h o | / # u s | | < / p r e >
< pre > / s u b s t r e n d = - 1 { { v a r : : x } } | / e c h o | / # T h e m o r n i n g i s u p o n u s | | < / p r e >
< pre > / s u b s t r s t a r t = 4 e n d = - 1 { { v a r : : x } } | / e c h o | / # m o r n i n g i s u p o n u s | | < / p r e >
< / d i v >
` ,
} ) ) ;
2024-12-21 22:25:14 +02:00
SlashCommandParser . addCommandObject ( SlashCommand . fromProps ( {
name : 'is-mobile' ,
callback : ( ) => String ( isMobile ( ) ) ,
returns : ARGUMENT _TYPE . BOOLEAN ,
helpString : 'Returns true if the current device is a mobile device, false otherwise. Equivalent to <code>{{isMobile}}</code> macro.' ,
} ) ) ;
2024-04-19 14:19:57 +03:00
2024-06-29 00:25:10 +02:00
registerVariableCommands ( ) ;
}
2023-07-20 20:32:15 +03: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 01:44:13 +02:00
const SCRIPT _PROMPT _KEY = 'script_inject_' ;
2024-12-13 00:02:24 +02:00
/ * *
* Adds a new script injection to the chat .
* @ param { import ( './slash-commands/SlashCommand.js' ) . NamedArguments } args Named arguments
* @ param { import ( './slash-commands/SlashCommand.js' ) . UnnamedArguments } value Unnamed argument
* /
2023-12-07 13:35:48 +02:00
function injectCallback ( args , value ) {
2023-11-28 01:44:13 +02:00
const positions = {
'before' : extension _prompt _types . BEFORE _PROMPT ,
'after' : extension _prompt _types . IN _PROMPT ,
'chat' : extension _prompt _types . IN _CHAT ,
2024-07-28 00:26:44 +03:00
'none' : extension _prompt _types . NONE ,
2023-11-28 01:44:13 +02:00
} ;
2024-03-23 18:44:40 +02:00
const roles = {
'system' : extension _prompt _roles . SYSTEM ,
'user' : extension _prompt _roles . USER ,
'assistant' : extension _prompt _roles . ASSISTANT ,
} ;
2023-11-28 01:44:13 +02:00
2024-12-13 00:02:24 +02:00
const id = String ( args ? . id ) ;
const ephemeral = isTrueBoolean ( String ( args ? . ephemeral ) ) ;
2023-11-28 01:44:13 +02: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 13:35:48 +02:00
const position = positions [ positionValue ] ? ? positions [ defaultPosition ] ;
2023-11-28 01:44:13 +02:00
const depthValue = Number ( args ? . depth ) ? ? defaultDepth ;
const depth = isNaN ( depthValue ) ? defaultDepth : depthValue ;
2024-03-23 18:44:40 +02: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 ] ;
2024-12-13 00:02:24 +02:00
const scan = isTrueBoolean ( String ( args ? . scan ) ) ;
const filter = args ? . filter instanceof SlashCommandClosure ? args . filter . rawText : null ;
const filterFunction = args ? . filter instanceof SlashCommandClosure ? closureToFilter ( args . filter ) : null ;
2023-11-28 01:44:13 +02:00
value = value || '' ;
2024-12-13 01:20:43 +02:00
if ( args ? . filter && ! String ( filter ? ? '' ) . trim ( ) ) {
throw new Error ( 'Failed to parse the filter argument. Make sure it is a valid non-empty closure.' ) ;
}
2023-11-28 01:44:13 +02:00
const prefixedId = ` ${ SCRIPT _PROMPT _KEY } ${ id } ` ;
if ( ! chat _metadata . script _injects ) {
chat _metadata . script _injects = { } ;
}
2024-04-27 20:25:55 +03:00
if ( value ) {
2024-12-13 00:02:24 +02:00
const inject = { value , position , depth , scan , role , filter } ;
2024-04-27 20:25:55 +03:00
chat _metadata . script _injects [ id ] = inject ;
} else {
delete chat _metadata . script _injects [ id ] ;
}
2023-11-28 01:44:13 +02:00
2024-12-13 00:02:24 +02:00
setExtensionPrompt ( prefixedId , String ( value ) , position , depth , scan , role , filterFunction ) ;
2023-11-28 01:44:13 +02:00
saveMetadataDebounced ( ) ;
2024-05-23 02:34:13 +03:00
if ( ephemeral ) {
2024-05-24 21:53:29 +03:00
let deleted = false ;
2024-05-23 02:34:13 +03:00
const unsetInject = ( ) => {
2024-05-24 21:53:29 +03:00
if ( deleted ) {
return ;
}
2024-05-23 02:34:13 +03:00
console . log ( 'Removing ephemeral script injection' , id ) ;
delete chat _metadata . script _injects [ id ] ;
2024-12-13 00:02:24 +02:00
setExtensionPrompt ( prefixedId , '' , position , depth , scan , role , filterFunction ) ;
2024-05-24 21:53:29 +03:00
saveMetadataDebounced ( ) ;
deleted = true ;
2024-05-23 02:34:13 +03:00
} ;
eventSource . once ( event _types . GENERATION _ENDED , unsetInject ) ;
eventSource . once ( event _types . GENERATION _STOPPED , unsetInject ) ;
}
2023-11-28 01:44:13 +02:00
return '' ;
}
2024-09-08 18:24:12 +03: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 01:44:13 +02: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 18:24:12 +03: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 18:24:12 +03: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 01:44:13 +02:00
}
2024-06-05 21:20:55 +03: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 21:20:55 +03:00
* @ param { string } value Unnamed argument
* @ returns { string } Empty string
* /
2024-06-28 04:00:32 +02:00
function flushInjectsCallback ( _ , value ) {
2023-11-28 01:44:13 +02:00
if ( ! chat _metadata . script _injects ) {
return '' ;
}
2024-06-28 04:00:32 +02:00
const idArgument = value ;
2024-06-05 21:20:55 +03:00
2023-12-07 13:35:48 +02:00
for ( const [ id , inject ] of Object . entries ( chat _metadata . script _injects ) ) {
2024-06-05 21:20:55 +03:00
if ( idArgument && id !== idArgument ) {
continue ;
}
2023-11-28 01:44:13 +02:00
const prefixedId = ` ${ SCRIPT _PROMPT _KEY } ${ id } ` ;
2024-03-23 18:44:40 +02:00
setExtensionPrompt ( prefixedId , '' , inject . position , inject . depth , inject . scan , inject . role ) ;
2024-06-05 21:20:55 +03:00
delete chat _metadata . script _injects [ id ] ;
2023-11-28 01:44:13 +02:00
}
saveMetadataDebounced ( ) ;
return '' ;
}
2023-12-07 13:35:48 +02:00
export function processChatSlashCommands ( ) {
2023-11-28 01:44:13 +02: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 13:35:48 +02:00
delete context . extensionPrompts [ id ] ;
2023-11-28 01:44:13 +02:00
}
2023-12-07 13:35:48 +02:00
for ( const [ id , inject ] of Object . entries ( context . chatMetadata . script _injects ) ) {
2024-12-13 00:02:24 +02:00
/ * *
* Rehydrates a filter closure from a string .
* @ returns { SlashCommandClosure | null }
* /
function reviveFilterClosure ( ) {
if ( ! inject . filter ) {
return null ;
}
try {
return new SlashCommandParser ( ) . parse ( inject . filter , true ) ;
} catch ( error ) {
console . warn ( 'Failed to revive filter closure for script injection' , id , error ) ;
return null ;
}
}
2023-11-28 01:44:13 +02:00
const prefixedId = ` ${ SCRIPT _PROMPT _KEY } ${ id } ` ;
2024-12-13 00:02:24 +02:00
const filterClosure = reviveFilterClosure ( ) ;
const filter = filterClosure ? closureToFilter ( filterClosure ) : null ;
2023-11-28 01:44:13 +02:00
console . log ( 'Adding script injection' , id ) ;
2024-12-13 00:02:24 +02:00
setExtensionPrompt ( prefixedId , inject . value , inject . position , inject . depth , inject . scan , inject . role , filter ) ;
2023-11-28 01:44:13 +02:00
}
}
2023-07-20 20:32:15 +03:00
2023-12-07 13:35:48 +02:00
function setInputCallback ( _ , value ) {
2024-05-19 22:22:32 +03:00
$ ( '#send_textarea' ) . val ( value || '' ) [ 0 ] . dispatchEvent ( new Event ( 'input' , { bubbles : true } ) ) ;
2023-11-24 17:12:59 +02:00
return value ;
}
2023-12-07 13:35:48 +02:00
function trimStartCallback ( _ , value ) {
2023-11-26 13:55:22 +02:00
if ( ! value ) {
return '' ;
}
return trimToStartSentence ( value ) ;
}
2023-12-07 13:35:48 +02:00
function trimEndCallback ( _ , value ) {
2023-11-26 13:55:22 +02:00
if ( ! value ) {
return '' ;
}
return trimToEndSentence ( value ) ;
}
2024-04-13 21:33:19 +03:00
async function trimTokensCallback ( arg , value ) {
2023-11-26 13:55:22 +02: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 21:33:19 +03:00
const tokenCount = await getTokenCountAsync ( value ) ;
2023-11-26 13:55:22 +02: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-22 23:00:31 -06:00
const { text } = decodeTextTokens ( tokenizerId , sliceTokens ) ;
return text ;
2023-11-26 13:55:22 +02:00
} catch ( error ) {
console . warn ( 'WARN: Tokenization failed for /trimtokens command, returning original' , error ) ;
return value ;
}
}
2023-12-07 13:35:48 +02:00
async function buttonsCallback ( args , text ) {
2023-11-26 17:05:55 +02:00
try {
2024-06-19 01:40:22 +02:00
/** @type {string[]} */
2023-11-26 17:05:55 +02: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 17:05:55 +02: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 17:05:55 +02:00
const buttonContainer = document . createElement ( 'div' ) ;
2024-10-21 00:19:50 -07:00
buttonContainer . classList . add ( 'flex-container' , 'flexFlowColumn' , 'wide100p' ) ;
const scrollableContainer = document . createElement ( 'div' ) ;
2024-11-17 15:54:09 -08:00
scrollableContainer . classList . add ( 'scrollable-buttons-container' ) ;
2023-11-26 17:05:55 +02:00
2024-06-19 01:40:22 +02:00
for ( const [ result , button ] of resultToButtonMap ) {
2023-11-26 17:05:55 +02: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 17:05:55 +02:00
} ) ;
buttonElement . innerText = button ;
buttonContainer . appendChild ( buttonElement ) ;
}
2024-10-21 00:19:50 -07:00
scrollableContainer . appendChild ( buttonContainer ) ;
2023-11-26 17:05:55 +02:00
const popupContainer = document . createElement ( 'div' ) ;
popupContainer . innerHTML = safeValue ;
2024-10-21 00:19:50 -07:00
popupContainer . appendChild ( scrollableContainer ) ;
// Ensure the popup uses flex layout
popupContainer . style . display = 'flex' ;
popupContainer . style . flexDirection = 'column' ;
popupContainer . style . maxHeight = '80vh' ; // Limit the overall height of the popup
2024-06-19 01:40:22 +02:00
2024-10-23 23:00:08 +03:00
popup = new Popup ( popupContainer , POPUP _TYPE . TEXT , '' , { okButton : 'Cancel' , allowVerticalScrolling : true } ) ;
2024-06-19 01:40:22 +02:00
popup . show ( )
. then ( ( result => resolve ( typeof result === 'number' ? resultToButtonMap . get ( result ) ? ? '' : '' ) ) )
2023-11-26 17:05:55 +02:00
. catch ( ( ) => resolve ( '' ) ) ;
2023-12-02 21:11:06 +02:00
} ) ;
2023-11-26 17:05:55 +02:00
} catch {
return '' ;
}
}
2023-12-07 13:35:48 +02: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 13:35:48 +02:00
const popupOptions = {
2024-10-23 23:03:07 +03:00
allowVerticalScrolling : ! isFalseBoolean ( args ? . scroll ) ,
2023-12-06 19:26:29 -06: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 13:35:48 +02: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-06 19:26:29 -06: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 19:50:49 +02:00
}
2024-08-18 13:49:23 -05:00
async function getMessagesCallback ( args , value ) {
2023-11-24 17:12:59 +02:00
const includeNames = ! isFalseBoolean ( args ? . names ) ;
2024-05-13 21:36:55 +03:00
const includeHidden = isTrueBoolean ( args ? . hidden ) ;
const role = args ? . role ;
2023-11-24 17:12:59 +02:00
const range = stringToRange ( value , 0 , chat . length - 1 ) ;
if ( ! range ) {
2024-05-13 21:36:55 +03:00
console . warn ( ` WARN: Invalid range provided for /messages command: ${ value } ` ) ;
2023-11-24 17:12:59 +02:00
return '' ;
}
2024-05-13 21:36:55 +03:00
const filterByRole = ( mes ) => {
if ( ! role ) {
return true ;
}
2024-05-13 22:45:06 +03:00
const isNarrator = mes . extra ? . type === system _message _types . NARRATOR ;
2024-05-13 21:36:55 +03:00
if ( role === 'system' ) {
2024-05-13 22:45:06 +03:00
return isNarrator && ! mes . is _user ;
2024-05-13 21:36:55 +03:00
}
if ( role === 'assistant' ) {
2024-05-13 22:45:06 +03:00
return ! isNarrator && ! mes . is _user ;
2024-05-13 21:36:55 +03:00
}
if ( role === 'user' ) {
2024-05-13 22:45:06 +03:00
return ! isNarrator && mes . is _user ;
2024-05-13 21:36:55 +03:00
}
throw new Error ( ` Invalid role provided. Expected one of: system, assistant, user. Got: ${ role } ` ) ;
} ;
2024-08-18 13:49:23 -05: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 17:12:59 +02:00
}
2024-08-18 13:49:23 -05:00
if ( role && ! filterByRole ( msg ) ) {
console . debug ( ` /messages: Skipping message with ID ${ mesId } due to role filter ` ) ;
return null ;
2024-05-13 21:36:55 +03:00
}
2024-08-18 13:49:23 -05:00
if ( ! includeHidden && msg . is _system ) {
console . debug ( ` /messages: Skipping hidden message with ID ${ mesId } ` ) ;
return null ;
2023-11-26 01:15:19 +02:00
}
2024-08-18 13:49:23 -05:00
return includeNames ? ` ${ msg . name } : ${ msg . mes } ` : msg . mes ;
} ;
2024-08-18 15:37:31 -05:00
const messagePromises = [ ] ;
2024-08-18 13:49:23 -05:00
for ( let rInd = range . start ; rInd <= range . end ; ++ rInd )
2024-08-18 15:37:31 -05:00
messagePromises . push ( processMessage ( rInd ) ) ;
2024-08-18 13:49:23 -05:00
const messages = await Promise . all ( messagePromises ) ;
2024-08-18 14:44:48 -05:00
return messages . filter ( m => m !== null ) . join ( '\n\n' ) ;
2023-11-24 17:12:59 +02:00
}
2023-12-27 12:28:15 +00:00
async function runCallback ( args , name ) {
2023-11-24 15:58:00 +02:00
if ( ! name ) {
2024-05-12 15:15:05 -04:00
throw new Error ( 'No name provided for /run command' ) ;
}
2024-07-05 18:04:33 -04:00
if ( name instanceof SlashCommandClosure ) {
2024-07-23 16:27:27 -04:00
name . breakController = new SlashCommandBreakController ( ) ;
2024-07-12 15:05:39 -04:00
return ( await name . execute ( ) ) ? . pipe ;
2024-07-05 18:04:33 -04:00
}
2024-05-12 15:15:05 -04: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 12:31:27 -04:00
closure . breakController = new SlashCommandBreakController ( ) ;
2024-06-27 11:49:12 -04:00
if ( args . _debugController && ! closure . debugController ) {
closure . debugController = args . _debugController ;
}
while ( closure . providedArgumentList . pop ( ) ) ;
2024-05-19 22:22:32 +03:00
closure . argumentList . forEach ( arg => {
2024-05-12 15:15:05 -04: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 15:58:00 +02:00
}
2023-12-07 13:35:48 +02:00
if ( typeof window [ 'executeQuickReplyByName' ] !== 'function' ) {
2024-05-12 15:15:05 -04:00
throw new Error ( 'Quick Reply extension is not loaded' ) ;
2023-11-24 15:58:00 +02:00
}
try {
name = name . trim ( ) ;
2024-07-08 18:07:37 -04:00
/**@type {ExecuteSlashCommandsOptions} */
const options = {
abortController : args . _abortController ,
debugController : args . _debugController ,
} ;
return await window [ 'executeQuickReplyByName' ] ( name , args , options ) ;
2023-11-24 15:58:00 +02:00
} catch ( error ) {
2024-07-05 18:04:50 -04:00
throw new Error ( ` Error running Quick Reply " ${ name } ": ${ error . message } ` ) ;
2023-11-24 15:58:00 +02:00
}
}
2024-05-18 14:48:31 -04:00
/ * *
*
2024-06-21 20:04:55 +02:00
* @ param { import ( './slash-commands/SlashCommand.js' ) . NamedArguments } param0
2024-05-18 14:48:31 -04: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 14:48:31 -04: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-24 00:18:07 +02:00
}
2023-12-07 13:35:48 +02:00
async function delayCallback ( _ , amount ) {
2023-11-24 12:49:14 +02:00
if ( ! amount ) {
console . warn ( 'WARN: No amount provided for /delay command' ) ;
2024-06-14 17:48:41 -04:00
return '' ;
2023-11-24 12:49:14 +02:00
}
amount = Number ( amount ) ;
if ( isNaN ( amount ) ) {
amount = 0 ;
}
await delay ( amount ) ;
2024-06-14 17:48:41 -04:00
return '' ;
2023-11-24 12:49:14 +02:00
}
2023-12-07 13:35:48 +02:00
async function inputCallback ( args , prompt ) {
2023-12-06 19:26:29 -06:00
const safeValue = DOMPurify . sanitize ( prompt || '' ) ;
2023-12-07 13:44:49 +02:00
const defaultInput = args ? . default !== undefined && typeof args ? . default === 'string' ? args . default : '' ;
2023-12-06 19:26:29 -06:00
const popupOptions = {
large : isTrueBoolean ( args ? . large ) ,
wide : isTrueBoolean ( args ? . wide ) ,
2023-12-07 13:35:48 +02:00
okButton : args ? . okButton !== undefined && typeof args ? . okButton === 'string' ? args . okButton : 'Ok' ,
2023-12-06 19:26:29 -06:00
rows : args ? . rows !== undefined && typeof args ? . rows === 'string' ? isNaN ( Number ( args . rows ) ) ? 4 : Number ( args . rows ) : 4 ,
} ;
2023-11-24 12:49:14 +02: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 12:49:14 +02:00
await delay ( 1 ) ;
2024-06-23 18:16:15 +03:00
return String ( result || '' ) ;
2023-11-24 12:49:14 +02: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 16:04:48 +02: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 21:05:42 +03: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 16:04:48 +02: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 16:04:48 +02:00
function fuzzyCallback ( args , searchInValue ) {
if ( ! searchInValue ) {
2023-11-24 02:21:50 +02: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 17:41:49 +02:00
const list = JSON . parse ( resolveVariable ( args . list ) ) ;
2023-11-24 02:21:50 +02: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 02:21:50 +02: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 16:04:48 +02:00
if ( 'threshold' in args ) {
2024-07-11 21:05:42 +03:00
params . threshold = parseFloat ( args . threshold ) ;
2024-03-03 16:04:48 +02: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 16:04:48 +02:00
if ( params . threshold < 0 ) {
2024-03-03 03:43:44 +01:00
params . threshold = 0 ;
}
2024-03-03 16:04:48 +02:00
if ( params . threshold > 1 ) {
2024-03-03 03:43:44 +01:00
params . threshold = 1 ;
}
}
2024-07-11 21:05:42 +03: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 16:04:48 +02:00
if ( result . length > 0 ) {
2024-07-11 21:05:42 +03:00
console . info ( '/fuzzy: best matched' , result [ 0 ] . item ) ;
return result [ 0 ] . item ;
2024-03-03 03:43:44 +01:00
}
2024-07-11 21:05:42 +03: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 02:21:50 +02:00
} catch {
console . warn ( 'WARN: Invalid list argument provided for /fuzzy command' ) ;
return '' ;
}
}
2023-11-24 19:06:31 +02:00
function setEphemeralStopStrings ( value ) {
if ( typeof value === 'string' && value . length ) {
2023-11-24 00:51:27 +02:00
try {
2023-11-24 19:06:31 +02:00
const stopStrings = JSON . parse ( value ) ;
2023-11-24 00:51:27 +02:00
if ( Array . isArray ( stopStrings ) ) {
2023-12-06 19:26:29 -06:00
stopStrings . forEach ( stopString => addEphemeralStoppingString ( stopString ) ) ;
2023-11-24 00:51:27 +02:00
}
} catch {
// Do nothing
}
}
2023-11-24 19:06:31 +02:00
}
2023-12-07 13:35:48 +02:00
async function generateRawCallback ( args , value ) {
2023-11-24 19:06:31 +02:00
if ( ! value ) {
console . warn ( 'WARN: No argument provided for /genraw command' ) ;
2024-09-12 22:41:53 +02:00
return '' ;
2023-11-24 19:06:31 +02:00
}
// Prevent generate recursion
2024-05-19 22:22:32 +03:00
$ ( '#send_textarea' ) . val ( '' ) [ 0 ] . dispatchEvent ( new Event ( 'input' , { bubbles : true } ) ) ;
2023-11-24 19:06:31 +02:00
const lock = isTrueBoolean ( args ? . lock ) ;
2024-03-28 21:51:02 +02:00
const as = args ? . as || 'system' ;
const quietToLoud = as === 'char' ;
2024-03-31 19:42:12 +03:00
const systemPrompt = resolveVariable ( args ? . system ) || '' ;
2024-03-31 21:02:38 +03:00
const length = Number ( resolveVariable ( args ? . length ) ? ? 0 ) || 0 ;
2023-11-24 00:51:27 +02:00
2023-11-24 15:18:49 +02:00
try {
if ( lock ) {
deactivateSendButtons ( ) ;
}
2023-11-26 00:56:55 +02:00
setEphemeralStopStrings ( resolveVariable ( args ? . stop ) ) ;
2024-03-31 21:02:38 +03:00
const result = await generateRaw ( value , '' , isFalseBoolean ( args ? . instruct ) , quietToLoud , systemPrompt , length ) ;
2023-11-24 15:18:49 +02:00
return result ;
2024-09-12 22:41:53 +02:00
} catch ( err ) {
2024-09-13 11:12:41 +00:00
console . error ( 'Error on /genraw generation' , err ) ;
2024-09-13 11:22:12 +00:00
toastr . error ( err . message , 'API Error' , { preventDuplicates : true } ) ;
2023-11-24 15:18:49 +02:00
} finally {
if ( lock ) {
activateSendButtons ( ) ;
}
2023-11-24 19:06:31 +02:00
flushEphemeralStoppingStrings ( ) ;
2023-11-24 15:18:49 +02:00
}
2024-09-12 22:41:53 +02:00
return '' ;
2023-11-23 22:50:13 +02:00
}
2024-08-24 15:22:50 +03: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 13:35:48 +02:00
async function generateCallback ( args , value ) {
2023-11-22 00:39:17 +02:00
// Prevent generate recursion
2024-05-19 22:22:32 +03:00
$ ( '#send_textarea' ) . val ( '' ) [ 0 ] . dispatchEvent ( new Event ( 'input' , { bubbles : true } ) ) ;
2023-11-24 15:18:49 +02:00
const lock = isTrueBoolean ( args ? . lock ) ;
2024-03-30 18:13:55 +02:00
const as = args ? . as || 'system' ;
const quietToLoud = as === 'char' ;
2024-03-31 21:02:38 +03:00
const length = Number ( resolveVariable ( args ? . length ) ? ? 0 ) || 0 ;
2023-11-22 00:39:17 +02:00
2023-11-24 15:18:49 +02:00
try {
if ( lock ) {
deactivateSendButtons ( ) ;
}
2023-11-26 00:56:55 +02:00
setEphemeralStopStrings ( resolveVariable ( args ? . stop ) ) ;
2024-01-12 19:16:42 +02: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 15:18:49 +02:00
return result ;
2024-09-12 22:41:53 +02:00
} catch ( err ) {
2024-09-13 11:12:41 +00:00
console . error ( 'Error on /gen generation' , err ) ;
2024-09-13 11:22:12 +00:00
toastr . error ( err . message , 'API Error' , { preventDuplicates : true } ) ;
2023-11-24 15:18:49 +02:00
} finally {
if ( lock ) {
activateSendButtons ( ) ;
}
2023-11-24 19:06:31 +02:00
flushEphemeralStoppingStrings ( ) ;
2023-11-24 15:18:49 +02:00
}
2024-09-12 22:41:53 +02:00
return '' ;
2023-11-22 00:39:17 +02: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 13:35:48 +02: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-22 00:39:17 +02: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 01:26:15 +03: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 14:03:00 +03: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-22 00:39:17 +02: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-06 19:26:29 -06:00
switch ( severity ) {
case 'error' :
2024-09-06 18:55:50 +02:00
toast = toastr . error ( value , title , options ) ;
2023-12-06 19:26:29 -06:00
break ;
case 'warning' :
2024-09-06 18:55:50 +02:00
toast = toastr . warning ( value , title , options ) ;
2023-12-06 19:26:29 -06:00
break ;
case 'success' :
2024-09-06 18:55:50 +02:00
toast = toastr . success ( value , title , options ) ;
2023-12-06 19:26:29 -06:00
break ;
case 'info' :
default :
2024-09-06 18:55:50 +02:00
toast = toastr . info ( value , title , options ) ;
2023-12-06 19:26:29 -06: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-22 00:39:17 +02: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 13:35:48 +02:00
const lastMessage = chat [ chat . length - 1 ] ;
2023-11-22 01:26:17 +02:00
if ( ! lastMessage ) {
2023-12-02 13:04:51 -05:00
toastr . warning ( 'No messages to add swipes to.' ) ;
2024-06-14 17:48:41 -04:00
return '' ;
2023-11-22 01:26:17 +02:00
}
2024-08-09 00:37:26 +02:00
if ( ! value ) {
2023-11-22 01:26:17 +02:00
console . warn ( 'WARN: No argument provided for /addswipe command' ) ;
2024-06-14 17:48:41 -04:00
return '' ;
2023-11-22 01:26:17 +02:00
}
if ( lastMessage . is _user ) {
2023-12-02 13:04:51 -05:00
toastr . warning ( 'Can\'t add swipes to user messages.' ) ;
2024-06-14 17:48:41 -04:00
return '' ;
2023-11-22 01:26:17 +02:00
}
if ( lastMessage . is _system ) {
2023-12-02 13:04:51 -05:00
toastr . warning ( 'Can\'t add swipes to system messages.' ) ;
2024-06-14 17:48:41 -04:00
return '' ;
2023-11-22 01:26:17 +02:00
}
if ( lastMessage . extra ? . image ) {
2023-12-02 13:04:51 -05:00
toastr . warning ( 'Can\'t add swipes to message containing an image.' ) ;
2024-06-14 17:48:41 -04:00
return '' ;
2023-11-22 01:26:17 +02:00
}
if ( ! Array . isArray ( lastMessage . swipes ) ) {
2023-12-07 13:35:48 +02:00
lastMessage . swipes = [ lastMessage . mes ] ;
lastMessage . swipe _info = [ { } ] ;
2023-11-22 01:26:17 +02: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 01:26:17 +02:00
2024-08-09 00:37:26 +02:00
lastMessage . swipes . push ( value ) ;
2023-11-22 01:26:17 +02: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 01:26:17 +02:00
gen _id : Date . now ( ) ,
api : 'manual' ,
model : 'slash command' ,
2023-12-02 22:06:57 +02:00
} ,
2023-11-24 00:18:07 +02:00
} ) ;
2023-11-22 01:26:17 +02: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 01:26:17 +02:00
await saveChatConditional ( ) ;
await reloadCurrentChat ( ) ;
2024-06-14 17:48:41 -04:00
2024-08-09 00:37:26 +02:00
return String ( newSwipeId ) ;
2023-11-22 01:26:17 +02:00
}
2023-12-07 13:35:48 +02: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 23:28:11 +02:00
2024-09-01 23:12:33 +02:00
const newSwipeId = await deleteSwipe ( swipeId ) ;
2024-06-14 17:48:41 -04:00
2024-08-09 00:38:51 +02:00
return String ( newSwipeId ) ;
2023-11-21 23:28:11 +02:00
}
2024-06-05 21:37:33 +03:00
async function askCharacter ( args , text ) {
2023-10-16 02:12:12 -04:00
// Prevent generate recursion
2024-05-19 22:22:32 +03:00
$ ( '#send_textarea' ) . val ( '' ) [ 0 ] . dispatchEvent ( new Event ( 'input' , { bubbles : true } ) ) ;
2023-10-16 02:12:12 -04: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 17:48:41 -04:00
return '' ;
2023-10-16 02:12:12 -04: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 02:12:12 -04: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 13:04:51 -05:00
toastr . error ( 'Character not found.' ) ;
2024-06-14 17:48:41 -04:00
return '' ;
2023-10-16 02:12:12 -04:00
}
2024-09-29 02:14:06 +02:00
const chId = getCharIndex ( character ) ;
2024-09-12 20:13:05 +03: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 09:50:06 +00:00
2023-10-16 02:12:12 -04:00
// Override character and send a user message
2024-06-05 21:37:33 +03:00
setCharacterId ( String ( chId ) ) ;
2023-10-16 02:12:12 -04:00
2024-09-29 02:14:06 +02:00
const { name , force _avatar , original _avatar } = getNameAndAvatarForMessage ( character , args ? . name ) ;
2023-10-16 02:12:12 -04:00
2024-09-29 02:14:06 +02:00
setCharacterName ( name ) ;
2023-10-16 02:12:12 -04:00
const restoreCharacter = ( ) => {
2024-09-12 10:02:37 +00:00
if ( String ( this _chid ) !== String ( chId ) ) {
return ;
}
2024-09-29 19:12:26 +03:00
if ( prevChId !== undefined ) {
setCharacterId ( prevChId ) ;
setCharacterName ( characters [ prevChId ] . name ) ;
} else {
setCharacterId ( undefined ) ;
setCharacterName ( neutralCharacterName ) ;
}
2023-10-16 02:12:12 -04:00
// Only force the new avatar if the character name is the same
// This skips if an error was fired
2023-12-07 13:35:48 +02:00
const lastMessage = chat [ chat . length - 1 ] ;
2024-09-29 02:14:06 +02:00
if ( lastMessage && lastMessage ? . name === name ) {
2023-10-16 02:12:12 -04:00
lastMessage . force _avatar = force _avatar ;
lastMessage . original _avatar = original _avatar ;
}
2023-12-02 21:11:06 +02:00
} ;
2023-10-16 02:12:12 -04:00
2024-09-12 10:19:03 +03:00
let askResult = '' ;
2024-09-11 19:34:26 +03:00
// Run generate and restore previous character
2023-10-16 02:12:12 -04:00
try {
2024-09-12 10:02:37 +00:00
eventSource . once ( event _types . MESSAGE _RECEIVED , restoreCharacter ) ;
2024-09-29 02:14:06 +02:00
toastr . info ( ` Asking ${ name } something... ` ) ;
2024-09-12 10:19:03 +03:00
askResult = await Generate ( 'ask_command' ) ;
2024-09-11 19:34:26 +03:00
} catch ( error ) {
2024-09-12 10:02:37 +00:00
restoreCharacter ( ) ;
2024-09-11 19:34:26 +03:00
console . error ( 'Error running /ask command' , error ) ;
} finally {
2024-09-12 10:02:37 +00:00
if ( String ( this _chid ) === String ( prevChId ) ) {
2024-09-12 10:19:03 +03:00
await saveChatConditional ( ) ;
} else {
2024-09-11 19:34:26 +03:00
toastr . error ( 'It is strongly recommended to reload the page.' , 'Something went wrong' ) ;
}
2023-10-16 02:12:12 -04: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 02:12:12 -04:00
}
2023-12-07 13:35:48 +02:00
async function hideMessageCallback ( _ , arg ) {
2023-11-03 23:45:56 +02:00
if ( ! arg ) {
console . warn ( 'WARN: No argument provided for /hide command' ) ;
2024-06-14 17:48:41 -04:00
return '' ;
2023-11-03 23:45:56 +02:00
}
2023-11-14 21:37:37 +02:00
const range = stringToRange ( arg , 0 , chat . length - 1 ) ;
2023-11-03 23:45:56 +02:00
2023-11-14 21:37:37 +02:00
if ( ! range ) {
console . warn ( ` WARN: Invalid range provided for /hide command: ${ arg } ` ) ;
2024-06-14 17:48:41 -04:00
return '' ;
2023-11-03 23:45:56 +02:00
}
2024-04-12 16:03:50 +09:00
await hideChatMessageRange ( range . start , range . end , false ) ;
2024-06-14 17:48:41 -04:00
return '' ;
2023-11-03 23:45:56 +02:00
}
2023-12-07 13:35:48 +02:00
async function unhideMessageCallback ( _ , arg ) {
2023-11-03 23:45:56 +02:00
if ( ! arg ) {
console . warn ( 'WARN: No argument provided for /unhide command' ) ;
2023-11-27 02:18:36 +02:00
return '' ;
2023-11-03 23:45:56 +02:00
}
2023-11-14 21:37:37 +02:00
const range = stringToRange ( arg , 0 , chat . length - 1 ) ;
2023-11-03 23:45:56 +02:00
2023-11-14 21:37:37 +02:00
if ( ! range ) {
console . warn ( ` WARN: Invalid range provided for /unhide command: ${ arg } ` ) ;
2023-11-27 02:18:36 +02:00
return '' ;
2023-11-03 23:45:56 +02:00
}
2024-04-12 16:03:50 +09:00
await hideChatMessageRange ( range . start , range . end , true ) ;
2023-11-27 02:18:36 +02:00
return '' ;
2023-11-03 23:45:56 +02:00
}
2023-12-18 01:39:37 +02:00
/ * *
* Copium for running group actions when the member is offscreen .
* @ param { number } chid - character ID
2023-12-19 23:12:14 +02:00
* @ param { string } action - one of 'enable' , 'disable' , 'up' , 'down' , 'view' , 'remove'
2023-12-18 01:39:37 +02: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 23:12:14 +02:00
if ( $ ( paginationSelector ) . length ) {
$ ( paginationSelector ) . pagination ( 'go' , pageValue ) ;
}
2023-12-18 01:39:37 +02:00
}
}
2023-12-07 13:35:48 +02:00
async function disableGroupMemberCallback ( _ , arg ) {
2023-10-29 19:35:26 +02:00
if ( ! selected _group ) {
2023-12-02 13:04:51 -05:00
toastr . warning ( 'Cannot run /disable command outside of a group chat.' ) ;
2023-11-27 02:18:36 +02:00
return '' ;
2023-11-11 16:12:02 +02:00
}
2023-11-20 23:49:04 +02:00
const chid = findGroupMemberId ( arg ) ;
if ( chid === undefined ) {
console . warn ( ` WARN: No group member found for argument ${ arg } ` ) ;
2023-11-27 02:18:36 +02:00
return '' ;
2023-10-29 19:35:26 +02:00
}
2023-12-18 01:39:37 +02:00
performGroupMemberAction ( chid , 'disable' ) ;
2023-11-27 02:18:36 +02:00
return '' ;
2023-11-20 23:49:04 +02:00
}
2023-10-29 19:35:26 +02:00
2023-12-07 13:35:48 +02:00
async function enableGroupMemberCallback ( _ , arg ) {
2023-11-20 23:49:04 +02:00
if ( ! selected _group ) {
2023-12-02 13:04:51 -05:00
toastr . warning ( 'Cannot run /enable command outside of a group chat.' ) ;
2023-11-27 02:18:36 +02:00
return '' ;
2023-10-29 19:35:26 +02:00
}
2023-11-20 23:49:04 +02:00
const chid = findGroupMemberId ( arg ) ;
2023-10-29 19:35:26 +02:00
2023-11-20 23:49:04 +02:00
if ( chid === undefined ) {
console . warn ( ` WARN: No group member found for argument ${ arg } ` ) ;
2023-11-27 02:18:36 +02:00
return '' ;
2023-10-29 19:35:26 +02:00
}
2023-12-18 01:39:37 +02:00
performGroupMemberAction ( chid , 'enable' ) ;
2023-11-27 02:18:36 +02:00
return '' ;
2023-11-20 23:49:04 +02:00
}
2023-10-29 19:35:26 +02:00
2023-12-07 13:35:48 +02:00
async function moveGroupMemberUpCallback ( _ , arg ) {
2023-11-21 22:35:59 +02:00
if ( ! selected _group ) {
2023-12-02 13:04:51 -05:00
toastr . warning ( 'Cannot run /memberup command outside of a group chat.' ) ;
2023-11-27 02:18:36 +02:00
return '' ;
2023-11-21 22:35:59 +02:00
}
const chid = findGroupMemberId ( arg ) ;
if ( chid === undefined ) {
console . warn ( ` WARN: No group member found for argument ${ arg } ` ) ;
2023-11-27 02:18:36 +02:00
return '' ;
2023-11-21 22:35:59 +02:00
}
2023-12-18 01:39:37 +02:00
performGroupMemberAction ( chid , 'up' ) ;
2023-11-27 02:18:36 +02:00
return '' ;
2023-11-21 22:35:59 +02:00
}
2023-12-07 13:35:48 +02:00
async function moveGroupMemberDownCallback ( _ , arg ) {
2023-11-21 22:35:59 +02:00
if ( ! selected _group ) {
2023-12-02 13:04:51 -05:00
toastr . warning ( 'Cannot run /memberdown command outside of a group chat.' ) ;
2023-11-27 02:18:36 +02:00
return '' ;
2023-11-21 22:35:59 +02:00
}
const chid = findGroupMemberId ( arg ) ;
if ( chid === undefined ) {
console . warn ( ` WARN: No group member found for argument ${ arg } ` ) ;
2023-11-27 02:18:36 +02:00
return '' ;
2023-11-21 22:35:59 +02:00
}
2023-12-18 01:39:37 +02:00
performGroupMemberAction ( chid , 'down' ) ;
2023-11-27 02:18:36 +02:00
return '' ;
2023-11-21 22:35:59 +02:00
}
2023-12-07 13:35:48 +02:00
async function peekCallback ( _ , arg ) {
2023-11-21 22:35:59 +02:00
if ( ! selected _group ) {
2023-12-02 13:04:51 -05:00
toastr . warning ( 'Cannot run /peek command outside of a group chat.' ) ;
2023-11-27 02:18:36 +02:00
return '' ;
2023-11-21 22:35:59 +02:00
}
if ( is _group _generating ) {
2023-12-02 13:04:51 -05:00
toastr . warning ( 'Cannot run /peek command while the group reply is generating.' ) ;
2023-11-27 02:18:36 +02:00
return '' ;
2023-11-21 22:35:59 +02:00
}
const chid = findGroupMemberId ( arg ) ;
if ( chid === undefined ) {
console . warn ( ` WARN: No group member found for argument ${ arg } ` ) ;
2023-11-27 02:18:36 +02:00
return '' ;
2023-11-21 22:35:59 +02:00
}
2023-12-19 23:12:14 +02:00
performGroupMemberAction ( chid , 'view' ) ;
2023-11-27 02:18:36 +02:00
return '' ;
2023-11-21 22:35:59 +02:00
}
2023-12-07 13:35:48 +02:00
async function removeGroupMemberCallback ( _ , arg ) {
2023-11-21 22:35:59 +02:00
if ( ! selected _group ) {
2023-12-02 13:04:51 -05:00
toastr . warning ( 'Cannot run /memberremove command outside of a group chat.' ) ;
2023-11-27 02:18:36 +02:00
return '' ;
2023-11-21 22:35:59 +02:00
}
if ( is _group _generating ) {
2023-12-02 13:04:51 -05:00
toastr . warning ( 'Cannot run /memberremove command while the group reply is generating.' ) ;
2023-11-27 02:18:36 +02:00
return '' ;
2023-11-21 22:35:59 +02:00
}
const chid = findGroupMemberId ( arg ) ;
if ( chid === undefined ) {
console . warn ( ` WARN: No group member found for argument ${ arg } ` ) ;
2023-11-27 02:18:36 +02:00
return '' ;
2023-11-21 22:35:59 +02:00
}
2023-12-18 01:39:37 +02:00
performGroupMemberAction ( chid , 'remove' ) ;
2023-11-27 02:18:36 +02:00
return '' ;
2023-11-21 22:35:59 +02:00
}
2024-09-29 02:54:12 +02:00
async function addGroupMemberCallback ( _ , name ) {
2023-11-21 22:35:59 +02:00
if ( ! selected _group ) {
2023-12-02 13:04:51 -05:00
toastr . warning ( 'Cannot run /memberadd command outside of a group chat.' ) ;
2023-11-27 02:18:36 +02:00
return '' ;
2023-11-21 22:35:59 +02:00
}
2024-09-29 02:54:12 +02:00
if ( ! name ) {
2023-11-21 22:35:59 +02:00
console . warn ( 'WARN: No argument provided for /memberadd command' ) ;
2023-11-27 02:18:36 +02:00
return '' ;
2023-11-21 22:35:59 +02: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 02:18:36 +02:00
return '' ;
2023-11-21 22:35:59 +02: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 02:18:36 +02:00
return '' ;
2023-11-21 22:35:59 +02:00
}
const avatar = character . avatar ;
if ( group . members . includes ( avatar ) ) {
toastr . warning ( ` ${ character . name } is already a member of this group. ` ) ;
2023-11-27 02:18:36 +02:00
return '' ;
2023-11-21 22:35:59 +02:00
}
group . members . push ( avatar ) ;
await saveGroupChat ( selected _group , true ) ;
// Trigger to reload group UI
$ ( '#rm_button_selected_ch' ) . trigger ( 'click' ) ;
2023-11-27 02:18:36 +02:00
return character . name ;
2023-11-21 22:35:59 +02:00
}
2024-02-08 00:20:36 +02:00
async function triggerGenerationCallback ( args , value ) {
const shouldAwait = isTrueBoolean ( args ? . await ) ;
const outerPromise = new Promise ( ( outerResolve ) => setTimeout ( async ( ) => {
2023-12-02 22:34:46 +02: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-11 00:32:45 +03:00
outerResolve ( Promise . resolve ( '' ) ) ;
2023-12-02 22:34:46 +02:00
return '' ;
}
2023-10-29 19:35:26 +02:00
2023-12-02 22:34:46 +02:00
// Prevent generate recursion
2024-05-19 22:22:32 +03:00
$ ( '#send_textarea' ) . val ( '' ) [ 0 ] . dispatchEvent ( new Event ( 'input' , { bubbles : true } ) ) ;
2023-10-29 19:35:26 +02:00
2023-12-02 22:34:46 +02:00
let chid = undefined ;
2023-10-29 19:35:26 +02:00
2024-02-08 00:20:36 +02:00
if ( selected _group && value ) {
chid = findGroupMemberId ( value ) ;
2023-11-27 11:16:40 +02:00
2023-12-02 22:34:46 +02:00
if ( chid === undefined ) {
2024-02-08 00:20:36 +02:00
console . warn ( ` WARN: No group member found for argument ${ value } ` ) ;
2023-12-02 22:34:46 +02:00
}
2023-11-27 11:16:40 +02:00
}
2023-11-20 23:49:04 +02:00
2024-02-08 00:20:36 +02: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 22:34:46 +02:00
2023-11-27 02:18:36 +02:00
return '' ;
2023-10-29 19:35:26 +02:00
}
2024-05-06 01:18:59 +03: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 01:18:59 +03:00
return persona [ 0 ] ;
}
}
return null ;
}
2023-10-29 19:35:26 +02:00
2023-12-07 13:35:48 +02:00
async function sendUserMessageCallback ( args , text ) {
2023-07-26 21:00:36 +03:00
if ( ! text ) {
2024-09-30 18:30:09 +02:00
toastr . warning ( 'You must specify text to send' ) ;
2023-07-26 21:00:36 +03:00
return ;
}
text = text . trim ( ) ;
2024-03-27 19:40:34 +02:00
const compact = isTrueBoolean ( args ? . compact ) ;
2023-07-26 21:00:36 +03:00
const bias = extractMessageBias ( text ) ;
2024-06-29 02:52:30 +02:00
let insertAt = Number ( args ? . at ) ;
// Convert possible depth parameter to index
2024-12-04 12:53:34 +00:00
if ( ! isNaN ( insertAt ) && ( insertAt < 0 || Object . is ( insertAt , - 0 ) ) ) {
2024-06-29 02:52:30 +02:00
// 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 01:18:59 +03:00
2024-09-30 18:32:21 +02:00
let message ;
2024-05-06 01:18:59 +03:00
if ( 'name' in args ) {
2024-06-28 03:58:04 +02:00
const name = args . name || '' ;
2024-05-06 01:18:59 +03: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 01:18:59 +03:00
}
else {
2024-09-30 18:32:21 +02:00
message = await sendMessageAsUser ( text , bias , insertAt , compact ) ;
2024-05-06 01:18:59 +03:00
}
2024-10-01 00:41:14 +02:00
return await slashCommandReturnHelper . doReturn ( args . return ? ? 'none' , message , { objectToStringFunc : x => x . mes } ) ;
2023-07-26 21:00:36 +03:00
}
2023-12-07 13:35:48 +02:00
async function deleteMessagesByNameCallback ( _ , name ) {
2023-07-20 20:32:15 +03: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 20:32:15 +03: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 02:18:36 +02:00
return '' ;
2023-07-20 20:32:15 +03:00
}
2023-12-07 13:35:48 +02:00
async function goToCharacterCallback ( _ , name ) {
2023-07-20 20:32:15 +03: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 12:21:22 -04:00
setActiveGroup ( null ) ;
2024-09-29 02:36:33 +02:00
return character . name ;
2023-07-20 20:32:15 +03: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 20:32:15 +03:00
}
2024-09-29 02:36:33 +02:00
async function openChat ( chid ) {
2023-07-20 20:32:15 +03:00
resetSelectedGroup ( ) ;
2024-09-29 02:36:33 +02:00
setCharacterId ( chid ) ;
2023-11-27 02:18:36 +02:00
await delay ( 1 ) ;
await reloadCurrentChat ( ) ;
2023-07-20 20:32:15 +03:00
}
2024-07-08 18:43:55 +02:00
async function continueChatCallback ( args , prompt ) {
const shouldAwait = isTrueBoolean ( args ? . await ) ;
2023-12-02 22:34:46 +02:00
2024-07-08 20:12:52 +03:00
const outerPromise = new Promise ( async ( resolve , reject ) => {
2023-12-02 22:34:46 +02: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 20:12:52 +03:00
return reject ( ) ;
2023-12-02 22:34:46 +02:00
}
2024-07-08 20:12:52 +03: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 20:12:52 +03:00
} catch ( error ) {
console . error ( 'Error running /continue command:' , error ) ;
2024-10-01 14:14:19 +03:00
reject ( error ) ;
2024-07-08 20:12:52 +03:00
}
2024-07-08 18:43:55 +02:00
} ) ;
if ( shouldAwait ) {
await outerPromise ;
}
2023-12-02 20:12:36 +02:00
return '' ;
2023-07-20 20:32:15 +03:00
}
2023-12-07 13:35:48 +02:00
export async function generateSystemMessage ( _ , prompt ) {
2024-05-19 22:22:32 +03:00
$ ( '#send_textarea' ) . val ( '' ) [ 0 ] . dispatchEvent ( new Event ( 'input' , { bubbles : true } ) ) ;
2023-07-20 20:32:15 +03: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 17:48:41 -04:00
return '' ;
2023-07-20 20:32:15 +03:00
}
// Generate and regex the output if applicable
toastr . info ( 'Please wait' , 'Generating...' ) ;
2024-04-19 14:19:57 +03:00
let message = await generateQuietPrompt ( prompt , false , false ) ;
2023-07-20 20:32:15 +03:00
message = getRegexedString ( message , regex _placement . SLASH _COMMAND ) ;
sendNarratorMessage ( _ , message ) ;
2024-06-14 17:48:41 -04:00
return '' ;
2023-07-20 20:32:15 +03:00
}
2023-12-07 13:35:48 +02:00
function syncCallback ( ) {
2023-07-20 20:32:15 +03:00
$ ( '#sync_name_button' ) . trigger ( 'click' ) ;
2024-06-14 17:48:41 -04:00
return '' ;
2023-07-20 20:32:15 +03:00
}
2024-06-26 20:18:05 +03: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 17:48:41 -04:00
return '' ;
2023-07-20 20:32:15 +03:00
}
2023-12-07 13:35:48 +02:00
function setStoryModeCallback ( ) {
2023-07-20 20:32:15 +03:00
$ ( '#chat_display' ) . val ( chat _styles . DOCUMENT ) . trigger ( 'change' ) ;
2024-06-14 17:48:41 -04:00
return '' ;
2023-07-20 20:32:15 +03:00
}
2023-12-07 13:35:48 +02:00
function setBubbleModeCallback ( ) {
2023-07-20 20:32:15 +03:00
$ ( '#chat_display' ) . val ( chat _styles . BUBBLES ) . trigger ( 'change' ) ;
2024-06-14 17:48:41 -04:00
return '' ;
2023-07-20 20:32:15 +03:00
}
2023-12-07 13:35:48 +02:00
function setFlatModeCallback ( ) {
2023-07-20 20:32:15 +03:00
$ ( '#chat_display' ) . val ( chat _styles . DEFAULT ) . trigger ( 'change' ) ;
2024-06-14 17:48:41 -04:00
return '' ;
2023-07-20 20:32:15 +03:00
}
2024-06-11 01:00:13 +02:00
/ * *
2024-06-11 09:55:37 +03: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 17:48:41 -04:00
* @ returns { string }
2024-06-11 01:00:13 +02:00
* /
function setNameCallback ( { mode = 'all' } , name ) {
2023-07-20 20:32:15 +03:00
if ( ! name ) {
2024-06-11 09:55:37 +03:00
toastr . warning ( 'You must specify a name to change to' ) ;
2024-06-14 17:48:41 -04:00
return '' ;
2023-07-20 20:32:15 +03:00
}
2024-06-11 01:00:13 +02:00
if ( ! [ 'lookup' , 'temp' , 'all' ] . includes ( mode ) ) {
2024-06-11 09:55:37 +03:00
toastr . warning ( 'Mode must be one of "lookup", "temp" or "all"' ) ;
2024-06-14 17:48:41 -04:00
return '' ;
2024-06-11 01:00:13 +02:00
}
2023-07-20 20:32:15 +03: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 15:16:20 +02:00
retriggerFirstMessageOnEmptyChat ( ) ;
2024-06-14 17:48:41 -04:00
return '' ;
2024-06-11 01:00:13 +02:00
} else if ( mode === 'lookup' ) {
toastr . warning ( ` Persona ${ name } not found ` ) ;
2024-06-14 17:48:41 -04:00
return '' ;
2023-07-20 20:32:15 +03: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 17:48:41 -04:00
return '' ;
2023-07-20 20:32:15 +03:00
}
2023-12-07 13:35:48 +02:00
async function setNarratorName ( _ , text ) {
2023-07-20 20:32:15 +03:00
const name = text || NARRATOR _NAME _DEFAULT ;
2023-12-07 13:35:48 +02:00
chat _metadata [ NARRATOR _NAME _KEY ] = name ;
2023-07-20 20:32:15 +03:00
toastr . info ( ` System narrator name set to ${ name } ` ) ;
2023-08-23 10:32:48 +03:00
await saveChatConditional ( ) ;
2024-06-14 17:48:41 -04:00
return '' ;
2023-07-20 20:32:15 +03: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 13:35:48 +02:00
export async function sendMessageAs ( args , text ) {
2023-07-20 20:32:15 +03:00
if ( ! text ) {
2024-09-30 18:13:13 +02:00
toastr . warning ( 'You must specify text to send as' ) ;
2024-06-14 17:48:41 -04:00
return '' ;
2023-07-20 20:32:15 +03:00
}
2024-09-30 18:13:13 +02:00
let name = args . name ? . trim ( ) ;
2023-11-21 02:54:04 +02:00
let mesText ;
2023-07-20 20:32:15 +03:00
2024-09-30 18:13:13 +02:00
if ( ! name ) {
2024-04-28 03:53:17 +03: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 02:54:04 +02:00
}
2024-04-28 03:53:17 +03:00
name = name2 ;
2023-11-21 02:54:04 +02:00
}
2023-07-20 20:32:15 +03:00
2024-07-27 21:29:37 -07:00
mesText = text . trim ( ) ;
2023-07-20 20:32:15 +03: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 20:49:03 +02:00
const isSystem = bias && ! removeMacros ( mesText ) . length ;
2024-03-27 19:40:34 +02:00
const compact = isTrueBoolean ( args ? . compact ) ;
2023-07-20 20:32:15 +03: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 20:32:15 +03:00
const message = {
2024-09-29 17:32:18 +02:00
name : character ? . name || name || avatarCharName ,
2023-07-20 20:32:15 +03:00
is _user : false ,
is _system : isSystem ,
2023-08-22 18:13:03 +03:00
send _date : getMessageTimeStamp ( ) ,
2023-07-20 20:32:15 +03: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 19:40:34 +02:00
isSmallSys : compact ,
2024-06-26 19:48:45 +03:00
api : 'manual' ,
model : 'slash command' ,
2023-12-02 22:06:57 +02:00
} ,
2023-07-20 20:32:15 +03: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 19:48:45 +03: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
2024-12-04 12:53:34 +00:00
if ( ! isNaN ( insertAt ) && ( insertAt < 0 || Object . is ( insertAt , - 0 ) ) ) {
2024-06-29 02:52:30 +02:00
// 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 19:48:49 +02: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 17:48:41 -04:00
2024-09-30 23:32:24 +02:00
return await slashCommandReturnHelper . doReturn ( args . return ? ? 'none' , message , { objectToStringFunc : x => x . mes } ) ;
2023-07-20 20:32:15 +03:00
}
2023-12-07 13:35:48 +02:00
export async function sendNarratorMessage ( args , text ) {
2023-07-20 20:32:15 +03:00
if ( ! text ) {
2024-09-30 18:30:09 +02:00
toastr . warning ( 'You must specify text to send' ) ;
2024-06-14 17:48:41 -04:00
return '' ;
2023-07-20 20:32:15 +03:00
}
2023-12-07 13:35:48 +02:00
const name = chat _metadata [ NARRATOR _NAME _KEY ] || NARRATOR _NAME _DEFAULT ;
2023-07-20 20:32:15 +03:00
// Messages that do nothing but set bias will be hidden from the context
const bias = extractMessageBias ( text ) ;
2023-12-21 20:49:03 +02:00
const isSystem = bias && ! removeMacros ( text ) . length ;
2024-03-27 19:40:34 +02:00
const compact = isTrueBoolean ( args ? . compact ) ;
2023-07-20 20:32:15 +03:00
const message = {
name : name ,
is _user : false ,
is _system : isSystem ,
2023-08-22 18:13:03 +03:00
send _date : getMessageTimeStamp ( ) ,
2023-07-20 20:32:15 +03: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 19:40:34 +02:00
isSmallSys : compact ,
2024-06-26 19:48:45 +03:00
api : 'manual' ,
model : 'slash command' ,
2023-07-20 20:32:15 +03:00
} ,
} ;
2024-06-29 02:52:30 +02:00
let insertAt = Number ( args . at ) ;
// Convert possible depth parameter to index
2024-12-04 12:53:34 +00:00
if ( ! isNaN ( insertAt ) && ( insertAt < 0 || Object . is ( insertAt , - 0 ) ) ) {
2024-06-29 02:52:30 +02:00
// 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 19:48:49 +02: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 17:48:41 -04:00
2024-09-30 23:32:24 +02:00
return await slashCommandReturnHelper . doReturn ( args . return ? ? 'none' , message , { objectToStringFunc : x => x . mes } ) ;
2023-07-20 20:32:15 +03:00
}
2023-12-07 13:35:48 +02:00
export async function promptQuietForLoudResponse ( who , text ) {
2023-09-17 15:00:10 -04:00
let character _id = getContext ( ) . characterId ;
if ( who === 'sys' ) {
2023-12-02 13:04:51 -05:00
text = 'System: ' + text ;
2023-09-17 15:00:10 -04:00
} else if ( who === 'user' ) {
2023-12-02 13:04:51 -05:00
text = name1 + ': ' + text ;
2023-09-17 15:00:10 -04:00
} else if ( who === 'char' ) {
2023-12-07 13:35:48 +02:00
text = characters [ character _id ] . name + ': ' + text ;
2023-09-17 15:00:10 -04:00
} else if ( who === 'raw' ) {
2023-12-02 09:18:49 -05:00
// We don't need to modify the text
2023-09-17 15:00:10 -04:00
}
//text = `${text}${power_user.instruct.enabled ? '' : '\n'}${(power_user.always_force_name2 && who != 'raw') ? characters[character_id].name + ":" : ""}`
2024-04-19 14:19:57 +03:00
let reply = await generateQuietPrompt ( text , true , false ) ;
2023-09-17 15:00:10 -04:00
text = await getRegexedString ( reply , regex _placement . SLASH _COMMAND ) ;
const message = {
2023-12-07 13:35:48 +02:00
name : characters [ character _id ] . name ,
2023-09-17 15:00:10 -04: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 19:48:45 +03:00
api : 'manual' ,
model : 'slash command' ,
2023-09-17 15:00:10 -04: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 13:35:48 +02:00
async function sendCommentMessage ( args , text ) {
2023-07-20 20:32:15 +03:00
if ( ! text ) {
2024-09-30 18:30:09 +02:00
toastr . warning ( 'You must specify text to send' ) ;
2024-06-14 17:48:41 -04:00
return '' ;
2023-07-20 20:32:15 +03:00
}
2024-03-27 19:40:34 +02:00
const compact = isTrueBoolean ( args ? . compact ) ;
2023-07-20 20:32:15 +03:00
const message = {
name : COMMENT _NAME _DEFAULT ,
is _user : false ,
2023-08-26 19:26:23 +02:00
is _system : true ,
2023-08-22 18:13:03 +03:00
send _date : getMessageTimeStamp ( ) ,
2023-07-20 20:32:15 +03:00
mes : substituteParams ( text . trim ( ) ) ,
force _avatar : comment _avatar ,
extra : {
type : system _message _types . COMMENT ,
gen _id : Date . now ( ) ,
2024-03-27 19:40:34 +02:00
isSmallSys : compact ,
2024-06-26 19:48:45 +03:00
api : 'manual' ,
model : 'slash command' ,
2023-07-20 20:32:15 +03:00
} ,
} ;
2024-06-29 02:52:30 +02:00
let insertAt = Number ( args . at ) ;
// Convert possible depth parameter to index
2024-12-04 12:53:34 +00:00
if ( ! isNaN ( insertAt ) && ( insertAt < 0 || Object . is ( insertAt , - 0 ) ) ) {
2024-06-29 02:52:30 +02:00
// 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 19:48:49 +02: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 17:48:41 -04:00
2024-09-30 23:32:24 +02:00
return await slashCommandReturnHelper . doReturn ( args . return ? ? 'none' , message , { objectToStringFunc : x => x . mes } ) ;
2023-07-20 20:32:15 +03:00
}
2023-11-04 21:35:50 +02:00
/ * *
* Displays a help message from the slash command
* @ param { any } _ Unused
* @ param { string } type Type of help to display
* /
2023-12-07 13:35:48 +02:00
function helpCommandCallback ( _ , type ) {
2023-11-04 21:35:50 +02:00
switch ( type ? . trim ( ) ? . toLowerCase ( ) ) {
2023-07-20 20:32:15 +03:00
case 'slash' :
2023-11-04 21:35:50 +02:00
case 'commands' :
case 'slashes' :
case 'slash commands' :
2023-07-20 20:32:15 +03:00
case '1' :
sendSystemMessage ( system _message _types . SLASH _COMMANDS ) ;
break ;
case 'format' :
2023-11-04 21:35:50 +02:00
case 'formatting' :
case 'formats' :
case 'chat formatting' :
2023-07-20 20:32:15 +03:00
case '2' :
sendSystemMessage ( system _message _types . FORMATTING ) ;
break ;
case 'hotkeys' :
2023-11-04 21:35:50 +02:00
case 'hotkey' :
2023-07-20 20:32:15 +03:00
case '3' :
sendSystemMessage ( system _message _types . HOTKEYS ) ;
break ;
case 'macros' :
2023-11-04 21:35:50 +02:00
case 'macro' :
2023-07-20 20:32:15 +03:00
case '4' :
sendSystemMessage ( system _message _types . MACROS ) ;
break ;
default :
sendSystemMessage ( system _message _types . HELP ) ;
break ;
}
2024-06-14 17:48:41 -04:00
return '' ;
2023-07-20 20:32:15 +03:00
}
2023-08-07 22:21:10 +03:00
$ ( document ) . on ( 'click' , '[data-displayHelp]' , function ( e ) {
e . preventDefault ( ) ;
const page = String ( $ ( this ) . data ( 'displayhelp' ) ) ;
helpCommandCallback ( null , page ) ;
} ) ;
2023-07-20 20:32:15 +03:00
2023-12-07 13:35:48 +02:00
function setBackgroundCallback ( _ , bg ) {
2023-07-20 20:32:15 +03:00
if ( ! bg ) {
2024-04-12 08:21:17 +09: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 20:32:15 +03:00
}
2023-11-14 21:43:08 +02:00
2023-07-20 20:32:15 +03:00
console . log ( 'Set background to ' + bg ) ;
2023-12-02 13:04:51 -05:00
const bgElements = Array . from ( document . querySelectorAll ( '.bg_example' ) ) . map ( ( x ) => ( { element : x , bgfile : x . getAttribute ( 'bgfile' ) } ) ) ;
2023-11-14 21:43:08 +02:00
2023-12-07 13:35:48 +02:00
const fuse = new Fuse ( bgElements , { keys : [ 'bgfile' ] } ) ;
2023-11-14 21:43:08 +02:00
const result = fuse . search ( bg ) ;
if ( ! result . length ) {
toastr . error ( ` No background found with name " ${ bg } " ` ) ;
2024-06-14 17:48:41 -04:00
return '' ;
2023-11-14 21:43:08 +02:00
}
2023-12-07 13:35:48 +02:00
const bgElement = result [ 0 ] . item . element ;
2023-11-14 21:43:08 +02:00
if ( bgElement instanceof HTMLElement ) {
bgElement . click ( ) ;
2023-07-20 20:32:15 +03:00
}
2024-06-14 17:48:41 -04:00
return '' ;
2023-07-20 20:32:15 +03:00
}
2024-03-15 16:08:22 +02: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 13:30:26 +03:00
* @ param { boolean } quiet - Whether to suppress toasts
2024-06-21 21:24:37 +02:00
*
2024-10-01 15:02:14 +03:00
* @ returns { { control : HTMLSelectElement | HTMLInputElement , options : HTMLOptionElement [ ] } ? } An array of objects representing the available model options , or null if not supported
2024-03-15 16:08:22 +02:00
* /
2024-09-07 13:30:26 +03:00
function getModelOptions ( quiet ) {
const nullResult = { control : null , options : null } ;
2024-03-15 16:08:22 +02:00
const modelSelectMap = [
2024-12-13 01:12:10 +02:00
{ id : 'generic_model_textgenerationwebui' , api : 'textgenerationwebui' , type : textgen _types . GENERIC } ,
2024-10-01 15:02:14 +03:00
{ id : 'custom_model_textgenerationwebui' , api : 'textgenerationwebui' , type : textgen _types . OOBA } ,
2024-03-15 16:08:22 +02: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-02 22:40:40 +00:00
{ id : 'vllm_model' , api : 'textgenerationwebui' , type : textgen _types . VLLM } ,
2024-03-15 16:08:22 +02:00
{ id : 'aphrodite_model' , api : 'textgenerationwebui' , type : textgen _types . APHRODITE } ,
{ id : 'ollama_model' , api : 'textgenerationwebui' , type : textgen _types . OLLAMA } ,
2024-09-08 22:23:25 +03:00
{ id : 'tabby_model' , api : 'textgenerationwebui' , type : textgen _types . TABBY } ,
2024-10-02 06:55:32 +10:00
{ id : 'featherless_model' , api : 'textgenerationwebui' , type : textgen _types . FEATHERLESS } ,
2024-03-15 16:08:22 +02: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 15:02:14 +03:00
{ id : 'custom_model_id' , api : 'openai' , type : chat _completion _sources . CUSTOM } ,
2024-04-02 00:20:17 +03:00
{ id : 'model_cohere_select' , api : 'openai' , type : chat _completion _sources . COHERE } ,
2024-04-20 00:09:38 +03:00
{ id : 'model_perplexity_select' , api : 'openai' , type : chat _completion _sources . PERPLEXITY } ,
2024-05-05 18:53:12 +03:00
{ id : 'model_groq_select' , api : 'openai' , type : chat _completion _sources . GROQ } ,
2024-10-26 16:42:09 +13:00
{ id : 'model_nanogpt_select' , api : 'openai' , type : chat _completion _sources . NANOGPT } ,
2024-06-28 00:51:09 +03:00
{ id : 'model_01ai_select' , api : 'openai' , type : chat _completion _sources . ZEROONEAI } ,
2024-08-08 00:08:03 -04:00
{ id : 'model_blockentropy_select' , api : 'openai' , type : chat _completion _sources . BLOCKENTROPY } ,
2024-12-29 20:38:13 +02:00
{ id : 'model_deepseek_select' , api : 'openai' , type : chat _completion _sources . DEEPSEEK } ,
2024-03-15 16:08:22 +02: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 23:46:03 +03:00
return null ;
2024-03-15 16:08:22 +02:00
}
}
const apiSubType = getSubType ( ) ;
const modelSelectItem = modelSelectMap . find ( x => x . api == main _api && x . type == apiSubType ) ? . id ;
if ( ! modelSelectItem ) {
2024-09-07 13:30:26 +03:00
! quiet && toastr . info ( 'Setting a model for your API is not supported or not implemented yet.' ) ;
return nullResult ;
2024-03-15 16:08:22 +02:00
}
const modelSelectControl = document . getElementById ( modelSelectItem ) ;
2024-10-01 15:02:14 +03:00
if ( ! ( modelSelectControl instanceof HTMLSelectElement ) && ! ( modelSelectControl instanceof HTMLInputElement ) ) {
2024-09-07 13:30:26 +03:00
! quiet && toastr . error ( ` Model select control not found: ${ main _api } [ ${ apiSubType } ] ` ) ;
return nullResult ;
2024-03-15 16:08:22 +02:00
}
2024-10-01 15:02:14 +03: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 23:09:06 +03: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 23:09:06 +03:00
function modelCallback ( args , model ) {
2024-09-07 13:30:26 +03: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 16:08:22 +02:00
2024-04-03 00:52:30 +03:00
model = String ( model || '' ) . trim ( ) ;
if ( ! model ) {
return modelSelectControl . value ;
2024-03-15 16:08:22 +02:00
}
2024-04-03 00:52:30 +03:00
console . log ( 'Set model to ' + model ) ;
2024-10-01 15:02:14 +03: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 16:08:22 +02: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 23:09:06 +03:00
! quiet && toastr . success ( ` Model set to " ${ newSelectedOption . text } " ` ) ;
2024-04-03 00:52:30 +03:00
return newSelectedOption . value ;
2024-03-15 16:08:22 +02:00
} else {
2024-09-07 13:30:26 +03:00
! quiet && toastr . warning ( ` No model found with name " ${ model } " ` ) ;
2024-04-03 00:52:30 +03:00
return '' ;
2024-03-15 16:08:22 +02:00
}
}
2024-12-05 12:38:21 -06:00
/ * *
* Gets the 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
2024-12-05 22:06:16 +02:00
* @ param { string } args . return The type of return value to use ( simple , list , dict )
2024-12-05 12:38:21 -06:00
* @ returns { Object } An object containing the states of the requested prompt entries
* /
function getPromptEntryCallback ( args ) {
const prompts = promptManager . serviceSettings . prompts ;
let returnType = args . return ? ? 'simple' ;
function parseArgs ( arg ) {
// Arg is already an array
if ( Array . isArray ( arg ) ) {
return arg ;
}
const list = [ ] ;
try {
// Arg is a JSON-stringified array
const parsedArg = JSON . parse ( arg ) ;
list . push ( ... Array . isArray ( parsedArg ) ? parsedArg : [ arg ] ) ;
} catch {
// Arg is a string
list . push ( arg ) ;
}
return list ;
}
let identifiersList = parseArgs ( args . identifier ) ;
let nameList = parseArgs ( args . name ) ;
// Check if identifiers exists in prompt, else remove from list
if ( identifiersList . length !== 0 ) {
identifiersList = identifiersList . filter ( identifier => prompts . some ( prompt => prompt . identifier === identifier ) ) ;
}
if ( nameList . length !== 0 ) {
nameList . forEach ( name => {
let identifiers = prompts
. filter ( entry => entry . name === name )
. map ( entry => entry . identifier ) ;
identifiersList = identifiersList . concat ( identifiers ) ;
} ) ;
}
// Get the state for each prompt entry
let promptStates = new Map ( ) ;
identifiersList . forEach ( identifier => {
const promptOrderEntry = promptManager . getPromptOrderEntry ( promptManager . activeCharacter , identifier ) ;
if ( promptOrderEntry ) {
promptStates . set ( identifier , promptOrderEntry . enabled ) ;
}
} ) ;
// If return is simple (default) but more than one prompt state was retrieved, then change return type
if ( returnType === 'simple' && promptStates . size > 1 ) {
returnType = args . identifier ? 'dict' : 'list' ;
}
const result = ( ( ) => {
if ( returnType === 'list' ) return [ ... promptStates . values ( ) ] ;
if ( returnType === 'dict' ) return Object . fromEntries ( promptStates ) ;
return [ ... promptStates . values ( ) ] [ 0 ] ;
} ) ( ) ;
return result ;
}
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 prompts = promptManager . serviceSettings . prompts ;
2024-06-16 13:56:43 +03:00
function parseArgs ( arg ) {
2024-09-29 12:48:40 +03:00
// Arg is already an array
if ( Array . isArray ( arg ) ) {
return arg ;
}
2024-06-16 13:56:43 +03:00
const list = [ ] ;
try {
2024-09-29 12:48:40 +03:00
// Arg is a JSON-stringified array
2024-06-16 13:56:43 +03:00
const parsedArg = JSON . parse ( arg ) ;
list . push ( ... Array . isArray ( parsedArg ) ? parsedArg : [ arg ] ) ;
} catch {
2024-09-29 12:48:40 +03:00
// Arg is a string
2024-06-16 13:56:43 +03: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 13:56:43 +03: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 13:56:43 +03: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 13:56:43 +03: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 13:56:43 +03: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 13:56:43 +03:00
if ( isTrueBoolean ( targetState ) ) {
return true ;
}
2024-06-16 04:37:42 +02:00
2024-06-16 13:56:43 +03: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 15:15:05 -04:00
2024-08-18 12:02:15 +03: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 13:09:10 +03:00
* @ param { string ? } [ args . quiet = false ] - whether to suppress toasts
2024-08-18 12:02:15 +03:00
* @ param { string } url - the API URL to set
* @ returns { Promise < string > }
* /
2024-09-07 13:09:10 +03:00
async function setApiUrlCallback ( { api = null , connect = 'true' , quiet = 'false' } , url ) {
const isQuiet = isTrueBoolean ( quiet ) ;
2024-08-18 12:18:06 +03:00
const autoConnect = isTrueBoolean ( connect ) ;
2024-08-18 12:02:15 +03: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 12:18:06 +03: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 12:02:15 +03:00
$ ( '#custom_api_url_text' ) . val ( url ) . trigger ( 'input' ) ;
2024-08-18 12:18:06 +03:00
if ( autoConnect ) {
2024-08-18 12:02:15 +03:00
$ ( '#api_button_openai' ) . trigger ( 'click' ) ;
}
return url ;
}
2024-08-18 12:18:06 +03: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 12:02:15 +03: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 13:09:10 +03:00
! isQuiet && toastr . warning ( ` API ' ${ api } ' is not a valid text_gen API. ` ) ;
2024-08-18 12:02:15 +03:00
return '' ;
}
if ( ! api && ! Object . values ( textgen _types ) . includes ( textgenerationwebui _settings . type ) ) {
2024-09-07 13:09:10 +03: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 12:02:15 +03:00
return '' ;
}
2024-08-18 12:18:06 +03:00
if ( api && url && autoConnect && api !== textgenerationwebui _settings . type ) {
2024-09-07 13:09:10 +03: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 12:02:15 +03:00
return '' ;
}
const type = api || textgenerationwebui _settings . type ;
const inputSelector = SERVER _INPUTS [ type ] ;
if ( ! inputSelector ) {
2024-09-07 13:09:10 +03:00
! isQuiet && toastr . warning ( ` API ' ${ type } ' does not have a server url input. ` ) ;
2024-08-18 12:02:15 +03: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 12:18:06 +03:00
if ( autoConnect ) {
2024-08-18 12:02:15 +03: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 21:31:58 +03: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 15:15:05 -04:00
export let isExecutingCommandsFromChatInput = false ;
export let commandsFromChatInputAbortController ;
2023-11-23 22:36:48 +02:00
/ * *
2024-05-12 15:15:05 -04:00
* Show command execution pause / stop buttons next to chat input .
2023-11-23 22:36:48 +02:00
* /
2024-05-12 15:15:05 -04:00
export function activateScriptButtons ( ) {
document . querySelector ( '#form_sheld' ) . classList . add ( 'isExecutingCommandsFromChatInput' ) ;
}
2023-07-20 20:32:15 +03:00
2024-05-12 15:15:05 -04:00
/ * *
* Hide command execution pause / stop buttons next to chat input .
* /
export function deactivateScriptButtons ( ) {
document . querySelector ( '#form_sheld' ) . classList . remove ( 'isExecutingCommandsFromChatInput' ) ;
}
2023-11-24 00:18:07 +02:00
2024-05-12 15:15:05 -04: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-24 00:18:07 +02:00
}
}
2024-05-12 15:15:05 -04:00
}
2023-07-20 20:32:15 +03:00
2024-05-12 15:15:05 -04:00
/ * *
* Stop command execution . Only for commands executed via chat input .
* /
export function stopScriptExecution ( ) {
commandsFromChatInputAbortController ? . abort ( 'Clicked stop button' ) ;
}
2023-07-20 20:32:15 +03:00
2024-05-12 15:15:05 -04: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 20:32:15 +03:00
2024-05-12 15:15:05 -04: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 06:23:34 -04:00
* @ prop { { [ id : PARSER _FLAG ] : boolean } } [ parserFlags ] ( null ) Parser flags to apply
2024-05-12 15:15:05 -04:00
* @ prop { SlashCommandAbortController } [ abortController ] ( null ) Controller used to abort or pause command execution
2024-06-18 14:29:29 -04:00
* @ prop { SlashCommandDebugController } [ debugController ] ( null ) Controller used to control debug execution
2024-05-12 15:15:05 -04:00
* @ prop { ( done : number , total : number ) => void } [ onProgress ] ( null ) Callback to handle progress events
2024-07-08 18:07:37 -04:00
* @ prop { string } [ source ] ( null ) String indicating where the code come from ( e . g . , QR name )
2024-05-12 15:15:05 -04:00
* /
2023-07-20 20:32:15 +03:00
2024-05-12 15:15:05 -04:00
/ * *
* @ typedef ExecuteSlashCommandsOnChatInputOptions
* @ prop { SlashCommandScope } [ scope ] ( null ) The scope to be used when executing the commands .
2024-05-19 06:23:34 -04:00
* @ prop { { [ id : PARSER _FLAG ] : boolean } } [ parserFlags ] ( null ) Parser flags to apply
2024-05-12 15:15:05 -04:00
* @ prop { boolean } [ clearChatInput ] ( false ) Whether to clear the chat input textarea
2024-07-08 18:07:37 -04:00
* @ prop { string } [ source ] ( null ) String indicating where the code come from ( e . g . , QR name )
2024-05-12 15:15:05 -04:00
* /
2023-07-20 20:32:15 +03:00
2024-05-12 15:15:05 -04: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 20:32:15 +03:00
2024-05-12 15:15:05 -04:00
options = Object . assign ( {
scope : null ,
parserFlags : null ,
clearChatInput : false ,
2024-07-08 18:07:37 -04:00
source : null ,
2024-05-12 15:15:05 -04:00
} , options ) ;
2024-03-25 14:22:39 +02:00
2024-05-12 15:15:05 -04:00
isExecutingCommandsFromChatInput = true ;
commandsFromChatInputAbortController ? . abort ( 'processCommands was called' ) ;
activateScriptButtons ( ) ;
2023-11-04 13:33:09 +02:00
2024-05-12 15:15:05 -04:00
/**@type {HTMLTextAreaElement}*/
const ta = document . querySelector ( '#send_textarea' ) ;
2023-11-23 22:56:52 +02:00
2024-05-12 15:15:05 -04:00
if ( options . clearChatInput ) {
ta . value = '' ;
2024-05-19 22:22:32 +03:00
ta . dispatchEvent ( new Event ( 'input' , { bubbles : true } ) ) ;
2024-05-12 15:15:05 -04:00
}
2023-11-25 18:18:57 +02:00
2024-05-12 15:15:05 -04: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 01:56:43 +02:00
2024-05-12 15:15:05 -04:00
/**@type {SlashCommandClosureResult} */
let result = null ;
2024-07-26 22:20:21 -04:00
let currentProgress = 0 ;
2024-05-12 15:15:05 -04:00
try {
commandsFromChatInputAbortController = new SlashCommandAbortController ( ) ;
result = await executeSlashCommandsWithOptions ( text , {
abortController : commandsFromChatInputAbortController ,
2024-07-22 18:28:30 -04:00
onProgress : ( done , total ) => {
2024-07-26 22:20:21 -04:00
const newProgress = done / total ;
if ( newProgress > currentProgress ) {
2024-07-22 18:28:30 -04:00
currentProgress = newProgress ;
2024-07-26 22:20:21 -04:00
ta . style . setProperty ( '--prog' , ` ${ newProgress * 100 } % ` ) ;
2024-07-22 18:28:30 -04:00
}
} ,
2024-06-14 17:48:41 -04:00
parserFlags : options . parserFlags ,
scope : options . scope ,
2024-07-08 18:07:37 -04:00
source : options . source ,
2024-05-12 15:15:05 -04:00
} ) ;
if ( commandsFromChatInputAbortController . signal . aborted ) {
document . querySelector ( '#form_sheld' ) . classList . add ( 'script_aborted' ) ;
} else {
document . querySelector ( '#form_sheld' ) . classList . add ( 'script_success' ) ;
2023-11-24 00:18:07 +02:00
}
2024-05-12 15:15:05 -04:00
} catch ( e ) {
document . querySelector ( '#form_sheld' ) . classList . add ( 'script_error' ) ;
result = new SlashCommandClosureResult ( ) ;
result . isError = true ;
2024-06-28 10:22:26 +00:00
result . errorMessage = e . message || 'An unknown error occurred' ;
2024-05-18 15:41:02 +03:00
if ( e . cause !== 'abort' ) {
2024-07-05 18:05:22 -04: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 15:41:02 +03:00
}
2024-05-12 15:15:05 -04:00
} finally {
2024-05-19 22:22:32 +03:00
delay ( 1000 ) . then ( ( ) => clearCommandProgressDebounced ( ) ) ;
2023-11-24 00:18:07 +02:00
2024-05-12 15:15:05 -04:00
commandsFromChatInputAbortController = null ;
deactivateScriptButtons ( ) ;
isExecutingCommandsFromChatInput = false ;
}
return result ;
}
2024-02-03 02:06:49 +02:00
2024-05-12 15:15:05 -04: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 14:29:29 -04:00
debugController : null ,
2024-05-12 15:15:05 -04:00
onProgress : null ,
2024-07-08 18:07:37 -04:00
source : null ,
2024-05-12 15:15:05 -04:00
} , options ) ;
2023-11-23 22:56:52 +02:00
2024-05-12 15:15:05 -04:00
let closure ;
try {
2024-05-18 14:48:31 -04:00
closure = parser . parse ( text , true , options . parserFlags , options . abortController ? ? new SlashCommandAbortController ( ) ) ;
2024-05-12 15:15:05 -04:00
closure . scope . parent = options . scope ;
closure . onProgress = options . onProgress ;
2024-06-18 14:29:29 -04:00
closure . debugController = options . debugController ;
2024-07-08 18:07:37 -04:00
closure . source = options . source ;
2024-05-12 15:15:05 -04: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 22:22:32 +03:00
{ escapeHtml : false , timeOut : 10000 , onclick : ( ) => callPopup ( toast , 'text' ) } ,
2024-05-12 15:15:05 -04:00
) ;
const result = new SlashCommandClosureResult ( ) ;
return result ;
} else {
throw e ;
2024-01-26 18:58:44 +00:00
}
2024-05-12 15:15:05 -04:00
}
2024-01-26 18:58:44 +00:00
2024-05-12 15:15:05 -04:00
try {
const result = await closure . execute ( ) ;
2024-05-18 14:48:31 -04:00
if ( result . isAborted && ! result . isQuietlyAborted ) {
2024-05-12 15:15:05 -04:00
toastr . warning ( result . abortReason , 'Command execution aborted' ) ;
2024-05-19 07:34:09 -04:00
closure . abortController . signal . isQuiet = true ;
2023-07-20 20:32:15 +03:00
}
2024-05-12 15:15:05 -04:00
return result ;
} catch ( e ) {
if ( options . handleExecutionErrors ) {
2024-07-05 18:05:22 -04: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 15:15:05 -04:00
const result = new SlashCommandClosureResult ( ) ;
result . isError = true ;
result . errorMessage = e . message ;
return result ;
} else {
throw e ;
2023-07-20 20:32:15 +03:00
}
}
}
2024-04-19 22:46:49 +03:00
/ * *
2024-05-12 15:15:05 -04: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 17:48:41 -04:00
* @ param { { [ id : PARSER _FLAG ] : boolean } } parserFlags Parser flags to apply
2024-05-12 15:15:05 -04: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 22:46:49 +03:00
* /
2024-05-12 15:15:05 -04: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 01:21:20 +02:00
} ) ;
}
2024-05-12 15:15:05 -04: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 16:53:39 -04:00
* @ returns { Promise < AutoComplete > }
2024-05-12 15:15:05 -04:00
* /
export async function setSlashCommandAutoComplete ( textarea , isFloating = false ) {
2024-05-19 22:22:32 +03: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 15:15:05 -04:00
const parser = new SlashCommandParser ( ) ;
const ac = new AutoComplete (
textarea ,
( ) => ac . text [ 0 ] == '/' ,
2024-05-19 22:22:32 +03:00
async ( text , index ) => await parser . getNameAt ( text , index ) ,
2024-05-12 15:15:05 -04:00
isFloating ,
) ;
2024-07-04 16:53:39 -04:00
return ac ;
2024-05-12 15:15:05 -04: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 21:11:06 +02:00
} ) ;