diff --git a/public/scripts/preset-manager.js b/public/scripts/preset-manager.js
index d751c5803..9043e8a91 100644
--- a/public/scripts/preset-manager.js
+++ b/public/scripts/preset-manager.js
@@ -22,6 +22,8 @@ import { kai_settings } from './kai-settings.js';
import { context_presets, getContextSettings, power_user } from './power-user.js';
import { SlashCommand } from './slash-commands/SlashCommand.js';
import { ARGUMENT_TYPE, SlashCommandArgument } from './slash-commands/SlashCommandArgument.js';
+import { enumIcons } from './slash-commands/SlashCommandCommonEnumsProvider.js';
+import { SlashCommandEnumValue, enumTypes } from './slash-commands/SlashCommandEnumValue.js';
import { SlashCommandParser } from './slash-commands/SlashCommandParser.js';
import {
textgenerationwebui_preset_names,
@@ -482,11 +484,12 @@ export async function initPresetManager() {
SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'preset',
callback: presetCommandCallback,
returns: 'current preset',
- namedArgumentList: [],
unnamedArgumentList: [
- new SlashCommandArgument(
- 'name', [ARGUMENT_TYPE.STRING], false,
- ),
+ SlashCommandArgument.fromProps({
+ description: 'name',
+ typeList: [ARGUMENT_TYPE.STRING],
+ enumProvider: () => getPresetManager().getAllPresets().map(preset => new SlashCommandEnumValue(preset, null, enumTypes.enum, enumIcons.preset)),
+ }),
],
helpString: `
diff --git a/public/scripts/scrapers.js b/public/scripts/scrapers.js
index aa34eb3a0..86d2ba567 100644
--- a/public/scripts/scrapers.js
+++ b/public/scripts/scrapers.js
@@ -433,6 +433,24 @@ class FandomScraper {
}
}
+const iso6391Codes = [
+ 'aa', 'ab', 'ae', 'af', 'ak', 'am', 'an', 'ar', 'as', 'av', 'ay', 'az',
+ 'ba', 'be', 'bg', 'bh', 'bi', 'bm', 'bn', 'bo', 'br', 'bs', 'ca', 'ce',
+ 'ch', 'co', 'cr', 'cs', 'cu', 'cv', 'cy', 'da', 'de', 'dv', 'dz', 'ee',
+ 'el', 'en', 'eo', 'es', 'et', 'eu', 'fa', 'ff', 'fi', 'fj', 'fo', 'fr',
+ 'fy', 'ga', 'gd', 'gl', 'gn', 'gu', 'gv', 'ha', 'he', 'hi', 'ho', 'hr',
+ 'ht', 'hu', 'hy', 'hz', 'ia', 'id', 'ie', 'ig', 'ii', 'ik', 'io', 'is',
+ 'it', 'iu', 'ja', 'jv', 'ka', 'kg', 'ki', 'kj', 'kk', 'kl', 'km', 'kn',
+ 'ko', 'kr', 'ks', 'ku', 'kv', 'kw', 'ky', 'la', 'lb', 'lg', 'li', 'ln',
+ 'lo', 'lt', 'lu', 'lv', 'mg', 'mh', 'mi', 'mk', 'ml', 'mn', 'mr', 'ms',
+ 'mt', 'my', 'na', 'nb', 'nd', 'ne', 'ng', 'nl', 'nn', 'no', 'nr', 'nv',
+ 'ny', 'oc', 'oj', 'om', 'or', 'os', 'pa', 'pi', 'pl', 'ps', 'pt', 'qu',
+ 'rm', 'rn', 'ro', 'ru', 'rw', 'sa', 'sc', 'sd', 'se', 'sg', 'si', 'sk',
+ 'sl', 'sm', 'sn', 'so', 'sq', 'sr', 'ss', 'st', 'su', 'sv', 'sw', 'ta',
+ 'te', 'tg', 'th', 'ti', 'tk', 'tl', 'tn', 'to', 'tr', 'ts', 'tt', 'tw',
+ 'ty', 'ug', 'uk', 'ur', 'uz', 've', 'vi', 'vo', 'wa', 'wo', 'xh', 'yi',
+ 'yo', 'za', 'zh', 'zu'];
+
/**
* Scrape transcript from a YouTube video.
* @implements {Scraper}
@@ -464,7 +482,7 @@ class YouTubeScraper {
helpString: 'Scrape a transcript from a YouTube video by ID or URL.',
returns: ARGUMENT_TYPE.STRING,
namedArgumentList: [
- new SlashCommandNamedArgument('lang', 'ISO 639-1 language code of the transcript, e.g. "en"', ARGUMENT_TYPE.STRING, false, false, ''),
+ new SlashCommandNamedArgument('lang', 'ISO 639-1 language code of the transcript, e.g. "en"', ARGUMENT_TYPE.STRING, false, false, '', iso6391Codes),
],
unnamedArgumentList: [
new SlashCommandArgument('URL or ID of the YouTube video', ARGUMENT_TYPE.STRING, true, false),
@@ -514,7 +532,7 @@ class YouTubeScraper {
}
const toast = toastr.info('Working, please wait...');
- const { transcript, id } = await this.getScript(videoUrl, lang);
+ const { transcript, id } = await this.getScript(String(videoUrl), lang);
toastr.clear(toast);
const file = new File([transcript], `YouTube - ${id} - ${Date.now()}.txt`, { type: 'text/plain' });
diff --git a/public/scripts/slash-commands.js b/public/scripts/slash-commands.js
index 3672a954d..a48805863 100644
--- a/public/scripts/slash-commands.js
+++ b/public/scripts/slash-commands.js
@@ -42,9 +42,9 @@ import { PARSER_FLAG, SlashCommandParser } from './slash-commands/SlashCommandPa
import { SlashCommandParserError } from './slash-commands/SlashCommandParserError.js';
import { getMessageTimeStamp } from './RossAscends-mods.js';
import { hideChatMessageRange } from './chats.js';
-import { getContext, saveMetadataDebounced } from './extensions.js';
+import { extension_settings, getContext, saveMetadataDebounced } from './extensions.js';
import { getRegexedString, regex_placement } from './extensions/regex/engine.js';
-import { findGroupMemberId, groups, is_group_generating, openGroupById, resetSelectedGroup, saveGroupChat, selected_group } from './group-chats.js';
+import { findGroupMemberId, getGroupMembers, groups, is_group_generating, openGroupById, resetSelectedGroup, saveGroupChat, selected_group } from './group-chats.js';
import { chat_completion_sources, oai_settings, setupChatCompletionPromptManager } from './openai.js';
import { autoSelectPersona, retriggerFirstMessageOnEmptyChat, user_avatar } from './personas.js';
import { addEphemeralStoppingString, chat_styles, flushEphemeralStoppingStrings, power_user } from './power-user.js';
@@ -61,8 +61,9 @@ 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';
-import { SlashCommandEnumValue } from './slash-commands/SlashCommandEnumValue.js';
+import { SlashCommandEnumValue, enumTypes } from './slash-commands/SlashCommandEnumValue.js';
import { POPUP_TYPE, Popup, callGenericPopup } from './popup.js';
+import { commonEnumProviders, enumIcons } from './slash-commands/SlashCommandCommonEnumsProvider.js';
export {
executeSlashCommands, executeSlashCommandsWithOptions, getSlashCommandsHelp, registerSlashCommand,
};
@@ -78,9 +79,16 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
name: '?',
callback: helpCommandCallback,
aliases: ['help'],
- unnamedArgumentList: [new SlashCommandArgument(
- 'help topic', ARGUMENT_TYPE.STRING, false, false, null, ['slash', 'format', 'hotkeys', 'macros'],
- )],
+ unnamedArgumentList: [SlashCommandArgument.fromProps({
+ description: 'help topic',
+ typeList: [ARGUMENT_TYPE.STRING],
+ 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({
@@ -93,9 +101,12 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
),
],
unnamedArgumentList: [
- new SlashCommandArgument(
- 'persona name', [ARGUMENT_TYPE.STRING], true,
- ),
+ 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.',
aliases: ['name'],
@@ -122,9 +133,8 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
typeList: [ARGUMENT_TYPE.STRING],
isRequired: true,
enumProvider: () => [...document.querySelectorAll('.bg_example')]
- .map((it) => new SlashCommandEnumValue(it.getAttribute('bgfile')))
- .filter(it => it.value?.length)
- ,
+ .map(it => new SlashCommandEnumValue(it.getAttribute('bgfile')))
+ .filter(it => it.value?.length),
}),
],
helpString: `
@@ -145,16 +155,22 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
name: 'sendas',
callback: sendMessageAs,
namedArgumentList: [
+ SlashCommandNamedArgument.fromProps({
+ name: 'name',
+ description: 'Character name',
+ typeList: [ARGUMENT_TYPE.STRING],
+ isRequired: true,
+ enumProvider: commonEnumProviders.characters('character'),
+ forceEnum: false,
+ }),
new SlashCommandNamedArgument(
- 'name', 'Character name', [ARGUMENT_TYPE.STRING], true,
- ),
- new SlashCommandNamedArgument(
- 'compact', 'Use compact layout', [ARGUMENT_TYPE.BOOLEAN], false, false, 'false', ['true', 'false'],
+ 'compact', 'Use compact layout', [ARGUMENT_TYPE.BOOLEAN], false, false, 'false',
),
SlashCommandNamedArgument.fromProps({
name: 'at',
description: 'position to insert the message',
typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.VARIABLE_NAME],
+ enumProvider: commonEnumProviders.messages({ allowIdAfter: true, allowVars: true }),
}),
],
unnamedArgumentList: [
@@ -197,6 +213,7 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
name: 'at',
description: 'position to insert the message',
typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.VARIABLE_NAME],
+ enumProvider: commonEnumProviders.messages({ allowIdAfter: true, allowVars: true }),
}),
],
unnamedArgumentList: [
@@ -250,6 +267,7 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
name: 'at',
description: 'position to insert the message',
typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.VARIABLE_NAME],
+ enumProvider: commonEnumProviders.messages({ allowIdAfter: true, allowVars: true }),
}),
],
unnamedArgumentList: [
@@ -333,10 +351,7 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
description: 'name',
typeList: [ARGUMENT_TYPE.STRING],
isRequired: true,
- enumProvider: () => [
- ...characters.map(it => new SlashCommandEnumValue(it.name, null, 'qr', 'C')),
- ...groups.map(it => new SlashCommandEnumValue(it.name, null, 'variable', 'G')),
- ],
+ enumProvider: commonEnumProviders.characters('all'),
}),
],
helpString: 'Opens up a chat with the character or group by its name',
@@ -384,7 +399,7 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
description: 'character name',
typeList: [ARGUMENT_TYPE.STRING],
isRequired: true,
- enumProvider: () => characters.map(it => new SlashCommandEnumValue(it.name, null, 'qr', 'C')),
+ enumProvider: commonEnumProviders.characters('character'),
}),
],
unnamedArgumentList: [
@@ -399,9 +414,12 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
callback: deleteMessagesByNameCallback,
namedArgumentList: [],
unnamedArgumentList: [
- new SlashCommandArgument(
- 'name', [ARGUMENT_TYPE.STRING], true,
- ),
+ SlashCommandArgument.fromProps({
+ description: 'name',
+ typeList: [ARGUMENT_TYPE.STRING],
+ isRequired: true,
+ enumProvider: commonEnumProviders.characters('character'),
+ }),
],
aliases: ['cancel'],
helpString: `
@@ -434,15 +452,18 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
name: 'at',
description: 'position to insert the message',
typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.VARIABLE_NAME],
+ enumProvider: commonEnumProviders.messages({ allowIdAfter: true, allowVars: true }),
+ }),
+ SlashCommandNamedArgument.fromProps({
+ name: 'name',
+ description: 'display name',
+ typeList: [ARGUMENT_TYPE.STRING, ARGUMENT_TYPE.VARIABLE_NAME],
+ defaultValue: '{{user}}',
+ enumProvider: () => [
+ ...commonEnumProviders.characters('character')(),
+ ...commonEnumProviders.variables('all')().map(x => { x.description = 'Variable'; return x; }),
+ ],
}),
- new SlashCommandNamedArgument(
- 'name',
- 'display name',
- [ARGUMENT_TYPE.STRING, ARGUMENT_TYPE.VARIABLE_NAME],
- false,
- false,
- '{{user}}',
- ),
],
unnamedArgumentList: [
new SlashCommandArgument(
@@ -487,6 +508,14 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
'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: `
Triggers a message generation. If in group, can trigger a message for the specified group member index or name.
@@ -500,9 +529,12 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
name: 'hide',
callback: hideMessageCallback,
unnamedArgumentList: [
- new SlashCommandArgument(
- 'message index or range', [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.RANGE], true,
- ),
+ 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.',
}));
@@ -510,9 +542,12 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
name: 'unhide',
callback: unhideMessageCallback,
unnamedArgumentList: [
- new SlashCommandArgument(
- 'message index or range', [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.RANGE], true,
- ),
+ 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.',
}));
@@ -521,9 +556,12 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
callback: disableGroupMemberCallback,
aliases: ['disable', 'disablemember', 'memberdisable'],
unnamedArgumentList: [
- new SlashCommandArgument(
- 'member index or name', [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.STRING], true,
- ),
+ 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.',
}));
@@ -532,9 +570,12 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
aliases: ['enable', 'enablemember', 'memberenable'],
callback: enableGroupMemberCallback,
unnamedArgumentList: [
- new SlashCommandArgument(
- 'member index or name', [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.STRING], true,
- ),
+ 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.',
}));
@@ -543,9 +584,12 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
callback: addGroupMemberCallback,
aliases: ['addmember', 'memberadd'],
unnamedArgumentList: [
- new SlashCommandArgument(
- 'character name', [ARGUMENT_TYPE.STRING], true,
- ),
+ SlashCommandArgument.fromProps({
+ description: 'character name',
+ typeList: [ARGUMENT_TYPE.STRING],
+ isRequired: true,
+ enumProvider: () => selected_group ? commonEnumProviders.characters('character')() : [],
+ }),
],
helpString: `
@@ -566,9 +610,12 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
callback: removeGroupMemberCallback,
aliases: ['removemember', 'memberremove'],
unnamedArgumentList: [
- new SlashCommandArgument(
- 'member index or name', [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.STRING], true,
- ),
+ SlashCommandArgument.fromProps({
+ description: 'member index (starts with 0) or name',
+ typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.STRING],
+ isRequired: true,
+ enumProvider: commonEnumProviders.groupMembers(),
+ }),
],
helpString: `
@@ -590,9 +637,12 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
callback: moveGroupMemberUpCallback,
aliases: ['upmember', 'memberup'],
unnamedArgumentList: [
- new SlashCommandArgument(
- 'member index or name', [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.STRING], true,
- ),
+ 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.',
}));
@@ -601,9 +651,12 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
callback: moveGroupMemberDownCallback,
aliases: ['downmember', 'memberdown'],
unnamedArgumentList: [
- new SlashCommandArgument(
- 'member index or name', [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.STRING], true,
- ),
+ 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.',
}));
@@ -611,9 +664,12 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
name: 'peek',
callback: peekCallback,
unnamedArgumentList: [
- new SlashCommandArgument(
- 'message index or range', [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.RANGE], false, true,
- ),
+ SlashCommandArgument.fromProps({
+ description: 'member index (starts with 0) or name',
+ typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.STRING],
+ isRequired: true,
+ enumProvider: commonEnumProviders.groupMembers(),
+ }),
],
helpString: `
@@ -623,12 +679,8 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
Examples:
-
-
/peek 5
- Shows the character card for the 5th message.
-
- -
-
/peek 2-5
- Shows the character cards for messages 2 through 5.
+ /peek Gloria
+ Shows the character card for the character named "Gloria".
@@ -639,9 +691,14 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
callback: deleteSwipeCallback,
aliases: ['swipedel'],
unnamedArgumentList: [
- new SlashCommandArgument(
- '1-based swipe id', [ARGUMENT_TYPE.NUMBER],
- ),
+ 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: `
@@ -670,9 +727,18 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
new SlashCommandNamedArgument(
'title', 'title of the toast message', [ARGUMENT_TYPE.STRING], false,
),
- new SlashCommandNamedArgument(
- 'severity', 'severity level of the toast message', [ARGUMENT_TYPE.STRING], false, false, null, ['info', 'warning', 'error', 'success'],
- ),
+ 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, '✅'),
+ ],
+ }),
],
unnamedArgumentList: [
new SlashCommandArgument(
@@ -699,17 +765,28 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
returns: 'generated text',
namedArgumentList: [
new SlashCommandNamedArgument(
- 'lock', 'lock user input during generation', [ARGUMENT_TYPE.BOOLEAN], false, false, null, ['on', 'off'],
- ),
- new SlashCommandNamedArgument(
- 'name', 'in-prompt name for instruct mode', [ARGUMENT_TYPE.STRING], false, false, 'System',
+ 'lock', 'lock user input during generation', [ARGUMENT_TYPE.BOOLEAN], false, false, null, commonEnumProviders.boolean('onOff')(),
),
+ SlashCommandNamedArgument.fromProps({
+ name: 'name',
+ description: 'in-prompt name for instruct mode',
+ typeList: [ARGUMENT_TYPE.STRING],
+ defaultValue: 'System',
+ enumProvider: () => [...commonEnumProviders.characters('character')(), new SlashCommandEnumValue('System', null, enumTypes.enum, enumIcons.assistant)],
+ forceEnum: false,
+ }),
new SlashCommandNamedArgument(
'length', 'API response length in tokens', [ARGUMENT_TYPE.NUMBER], false,
),
- new SlashCommandNamedArgument(
- 'as', 'role of the output prompt', [ARGUMENT_TYPE.STRING], false, false, 'system', ['system', 'char'],
- ),
+ 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(
@@ -731,17 +808,23 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
returns: 'generated text',
namedArgumentList: [
new SlashCommandNamedArgument(
- 'lock', 'lock user input during generation', [ARGUMENT_TYPE.BOOLEAN], false, false, null, ['on', 'off'],
+ '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', ['on', 'off'],
+ '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,
),
- new SlashCommandNamedArgument(
- 'as', 'role of the output prompt', [ARGUMENT_TYPE.STRING], false, false, 'system', ['system', 'char'],
- ),
+ 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,
),
@@ -792,7 +875,6 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
description: 'Whether to suppress the toast message notifying about the /abort call.',
typeList: [ARGUMENT_TYPE.BOOLEAN],
defaultValue: 'true',
- enumList: ['true', 'false'],
}),
],
unnamedArgumentList: [
@@ -845,7 +927,12 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
}));
SlashCommandParser.addCommandObject(SlashCommand.fromProps({
name: 'pass',
- callback: (_, arg) => arg,
+ 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(
@@ -898,10 +985,10 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
'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', ['on', 'off'],
+ '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', ['on', 'off'],
+ '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,
@@ -933,9 +1020,15 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
),
],
unnamedArgumentList: [
- new SlashCommandArgument(
- 'scoped variable or qr label', [ARGUMENT_TYPE.VARIABLE_NAME, ARGUMENT_TYPE.STRING], true,
- ),
+ SlashCommandArgument.fromProps({
+ description: 'scoped variable or qr label',
+ typeList: [ARGUMENT_TYPE.VARIABLE_NAME, ARGUMENT_TYPE.STRING],
+ isRequired: true,
+ enumProvider: () => [
+ ...commonEnumProviders.variables('scope')(),
+ ...(typeof window['qrEnumProviderExecutables'] === 'function') ? window['qrEnumProviderExecutables']() : [],
+ ],
+ }),
],
helpString: `
@@ -950,19 +1043,29 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
aliases: ['message'],
namedArgumentList: [
new SlashCommandNamedArgument(
- 'names', 'show message author names', [ARGUMENT_TYPE.BOOLEAN], false, false, 'off', ['off', 'on'],
+ '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', ['off', 'on'],
- ),
- new SlashCommandNamedArgument(
- 'role', 'filter messages by role', [ARGUMENT_TYPE.STRING], false, false, null, ['system', 'assistant', 'user'],
+ '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: [
- new SlashCommandArgument(
- 'message index or range', [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.RANGE], false, true,
- ),
+ 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: `
@@ -1018,10 +1121,10 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
returns: 'popup text',
namedArgumentList: [
new SlashCommandNamedArgument(
- 'large', 'show large popup', [ARGUMENT_TYPE.BOOLEAN], false, false, null, ['on', 'off'],
+ 'large', 'show large popup', [ARGUMENT_TYPE.BOOLEAN], false, false, null, commonEnumProviders.boolean('onOff')(),
),
new SlashCommandNamedArgument(
- 'wide', 'show wide popup', [ARGUMENT_TYPE.BOOLEAN], false, false, null, ['on', 'off'],
+ 'wide', 'show wide popup', [ARGUMENT_TYPE.BOOLEAN], false, false, null, commonEnumProviders.boolean('onOff')(),
),
new SlashCommandNamedArgument(
'okButton', 'text for the OK button', [ARGUMENT_TYPE.STRING], false,
@@ -1084,9 +1187,16 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
new SlashCommandNamedArgument(
'limit', 'number of tokens to keep', [ARGUMENT_TYPE.NUMBER], true,
),
- new SlashCommandNamedArgument(
- 'direction', 'trim direction', [ARGUMENT_TYPE.STRING], true, false, null, ['start', 'end'],
- ),
+ 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(
@@ -1145,9 +1255,17 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
name: 'inject',
callback: injectCallback,
namedArgumentList: [
- new SlashCommandNamedArgument(
- 'id', 'injection ID', [ARGUMENT_TYPE.STRING], true,
- ),
+ SlashCommandNamedArgument.fromProps({
+ name: 'id',
+ description: 'injection ID or variable name pointing to ID',
+ typeList: [ARGUMENT_TYPE.STRING, ARGUMENT_TYPE.VARIABLE_NAME],
+ isRequired: true,
+
+ enumProvider: () => [
+ ...commonEnumProviders.injects(),
+ ...commonEnumProviders.variables('all')().map(x => { x.description = 'Variable'; return x; }),
+ ],
+ }),
new SlashCommandNamedArgument(
'position', 'injection position', [ARGUMENT_TYPE.STRING], false, false, 'after', ['before', 'after', 'chat'],
),
@@ -1157,11 +1275,19 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
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(
- 'role', 'role for in-chat injections', [ARGUMENT_TYPE.STRING], false, false, 'system', ['system', 'user', 'assistant'],
- ),
- new SlashCommandNamedArgument(
- 'ephemeral', 'remove injection after generation', [ARGUMENT_TYPE.BOOLEAN], false, false, 'false', ['true', 'false'],
+ 'ephemeral', 'remove injection after generation', [ARGUMENT_TYPE.BOOLEAN], false, false, 'false',
),
],
unnamedArgumentList: [
@@ -1180,16 +1306,25 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
name: 'flushinject',
aliases: ['flushinjects'],
unnamedArgumentList: [
- new SlashCommandArgument(
- 'injection ID or a variable name pointing to ID', [ARGUMENT_TYPE.STRING, ARGUMENT_TYPE.VARIABLE_NAME], false, false, '',
- ),
+ SlashCommandArgument.fromProps({
+ description: 'injection ID or a variable name pointing to ID',
+ typeList: [ARGUMENT_TYPE.STRING, ARGUMENT_TYPE.VARIABLE_NAME],
+ defaultValue: '',
+ enumProvider: () => [
+ ...commonEnumProviders.injects(),
+ ...commonEnumProviders.variables('all')().map(x => { x.description = 'Variable'; return x; }),
+ ],
+ }),
],
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) => getTokenCountAsync(text),
+ 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(
@@ -1203,9 +1338,11 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
callback: modelCallback,
returns: 'current model',
unnamedArgumentList: [
- new SlashCommandArgument(
- 'model name', [ARGUMENT_TYPE.STRING], false,
- ),
+ SlashCommandArgument.fromProps({
+ description: 'model name',
+ typeList: [ARGUMENT_TYPE.STRING],
+ enumProvider: () => getModelOptions()?.options.map(option => new SlashCommandEnumValue(option.value, option.value !== option.text ? option.text : null)),
+ }),
],
helpString: 'Sets the model for the current API. Gets the current model name if no argument is provided.',
}));
@@ -1214,12 +1351,28 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
aliases: ['setpromptentries'],
callback: setPromptEntryCallback,
namedArgumentList: [
- new SlashCommandNamedArgument(
- 'identifier', 'Prompt entry identifier(s) to target', [ARGUMENT_TYPE.STRING, ARGUMENT_TYPE.LIST], false, true,
- ),
- new SlashCommandNamedArgument(
- 'name', 'Prompt entry name(s) to target', [ARGUMENT_TYPE.STRING, ARGUMENT_TYPE.LIST], false, true,
- ),
+ SlashCommandNamedArgument.fromProps({
+ name: 'identifier',
+ description: 'Prompt entry identifier(s) to target',
+ typeList: [ARGUMENT_TYPE.STRING, ARGUMENT_TYPE.LIST],
+ acceptsMultiple: true,
+ enumProvider: () => {
+ const promptManager = setupChatCompletionPromptManager(oai_settings);
+ const prompts = promptManager.serviceSettings.prompts;
+ return prompts.map(prompt => new SlashCommandEnumValue(prompt.identifier, prompt.name, enumTypes.enum));
+ },
+ }),
+ SlashCommandNamedArgument.fromProps({
+ name: 'name',
+ description: 'Prompt entry name(s) to target',
+ typeList: [ARGUMENT_TYPE.STRING, ARGUMENT_TYPE.LIST],
+ acceptsMultiple: true,
+ enumProvider: () => {
+ const promptManager = setupChatCompletionPromptManager(oai_settings);
+ const prompts = promptManager.serviceSettings.prompts;
+ return prompts.map(prompt => new SlashCommandEnumValue(prompt.name, prompt.identifier, enumTypes.enum));
+ },
+ }),
],
unnamedArgumentList: [
SlashCommandArgument.fromProps({
@@ -1228,7 +1381,7 @@ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
isRequired: true,
acceptsMultiple: false,
defaultValue: 'toggle', // unnamed arguments don't support default values yet
- enumList: ['on', 'off', 'toggle'],
+ enumList: commonEnumProviders.boolean('onOffToggle')(),
}),
],
helpString: 'Sets the specified prompt manager entry/entries on or off.',
@@ -1505,7 +1658,7 @@ async function popupCallback(args, value) {
await delay(1);
await callGenericPopup(safeValue, POPUP_TYPE.TEXT, '', popupOptions);
await delay(1);
- return value;
+ return String(value);
}
function getMessagesCallback(args, value) {
@@ -1609,13 +1762,13 @@ async function runCallback(args, name) {
/**
*
- * @param {object} param0
- * @param {SlashCommandAbortController} param0._abortController
- * @param {string} [param0.quiet]
+ * @param {import('./slash-commands/SlashCommand.js').NamedArguments} param0
* @param {string} [reason]
*/
function abortCallback({ _abortController, quiet }, reason) {
+ if (quiet instanceof SlashCommandClosure) throw new Error('argument \'quiet\' cannot be a closure for command /abort');
_abortController.abort((reason ?? '').toString().length == 0 ? '/abort command executed' : reason, !isFalseBoolean(quiet ?? 'true'));
+ return '';
}
async function delayCallback(_, amount) {
@@ -1644,9 +1797,9 @@ async function inputCallback(args, prompt) {
};
// Do not remove this delay, otherwise the prompt will not show up
await delay(1);
- const result = await callPopup(safeValue, 'input', defaultInput, popupOptions);
+ const result = await callGenericPopup(safeValue, POPUP_TYPE.INPUT, defaultInput, popupOptions);
await delay(1);
- return result || '';
+ return String(result || '');
}
/**
@@ -2766,12 +2919,11 @@ function setBackgroundCallback(_, bg) {
}
/**
- * Sets a model for the current API.
- * @param {object} _ Unused
- * @param {string} model New model name
- * @returns {string} New or existing model name
+ * Retrieves the available model options based on the currently selected main API and its subtype
+ *
+ * @returns {{control: HTMLSelectElement, options: HTMLOptionElement[]}?} An array of objects representing the available model options, or null if not supported
*/
-function modelCallback(_, model) {
+function getModelOptions() {
const modelSelectMap = [
{ id: 'model_togetherai_select', api: 'textgenerationwebui', type: textgen_types.TOGETHERAI },
{ id: 'openrouter_model', api: 'textgenerationwebui', type: textgen_types.OPENROUTER },
@@ -2812,17 +2964,33 @@ function modelCallback(_, model) {
if (!modelSelectItem) {
toastr.info('Setting a model for your API is not supported or not implemented yet.');
- return '';
+ return null;
}
const modelSelectControl = document.getElementById(modelSelectItem);
if (!(modelSelectControl instanceof HTMLSelectElement)) {
toastr.error(`Model select control not found: ${main_api}[${apiSubType}]`);
- return '';
+ return null;
}
const options = Array.from(modelSelectControl.options);
+ return { control: modelSelectControl, options };
+}
+
+/**
+ * Sets a model for the current API.
+ * @param {object} _ Unused
+ * @param {string} model New model name
+ * @returns {string} New or existing model name
+ */
+function modelCallback(_, model) {
+ const { control: modelSelectControl, options } = getModelOptions();
+
+ // If no model was found, the reason was already logged, we just return here
+ if (options === null) {
+ return '';
+ }
if (!options.length) {
toastr.warning('No model options found. Check your API settings.');
diff --git a/public/scripts/slash-commands/SlashCommand.js b/public/scripts/slash-commands/SlashCommand.js
index d04934924..d0821d4d9 100644
--- a/public/scripts/slash-commands/SlashCommand.js
+++ b/public/scripts/slash-commands/SlashCommand.js
@@ -175,6 +175,11 @@ export class SlashCommand {
}
li.append(specs);
}
+ const stopgap = document.createElement('span'); {
+ stopgap.classList.add('stopgap');
+ stopgap.textContent = '';
+ li.append(stopgap);
+ }
const help = document.createElement('span'); {
help.classList.add('help');
const content = document.createElement('span'); {
diff --git a/public/scripts/slash-commands/SlashCommandArgument.js b/public/scripts/slash-commands/SlashCommandArgument.js
index 324d5b9d6..e27831a0c 100644
--- a/public/scripts/slash-commands/SlashCommandArgument.js
+++ b/public/scripts/slash-commands/SlashCommandArgument.js
@@ -1,9 +1,8 @@
import { SlashCommandClosure } from './SlashCommandClosure.js';
+import { commonEnumProviders } from './SlashCommandCommonEnumsProvider.js';
import { SlashCommandEnumValue } from './SlashCommandEnumValue.js';
import { SlashCommandExecutor } from './SlashCommandExecutor.js';
-
-
/**@readonly*/
/**@enum {string}*/
export const ARGUMENT_TYPE = {
@@ -18,20 +17,18 @@ export const ARGUMENT_TYPE = {
'DICTIONARY': 'dictionary',
};
-
-
export class SlashCommandArgument {
/**
* Creates an unnamed argument from a properties object.
* @param {Object} props
* @param {string} props.description description of the argument
- * @param {ARGUMENT_TYPE|ARGUMENT_TYPE[]} props.typeList default: ARGUMENT_TYPE.STRING - list of accepted types (from ARGUMENT_TYPE)
- * @param {boolean} [props.isRequired] default: false - whether the argument is required (false = optional argument)
- * @param {boolean} [props.acceptsMultiple] default: false - whether argument accepts multiple values
- * @param {string|SlashCommandClosure} [props.defaultValue] default value if no value is provided
- * @param {string|SlashCommandEnumValue|(string|SlashCommandEnumValue)[]} [props.enumList] list of accepted values
- * @param {(executor:SlashCommandExecutor)=>SlashCommandEnumValue[]} [props.enumProvider] function that returns auto complete options
- * @param {boolean} [props.forceEnum] default: true - whether the input must match one of the enum values
+ * @param {ARGUMENT_TYPE|ARGUMENT_TYPE[]} [props.typeList=[ARGUMENT_TYPE.STRING]] default: ARGUMENT_TYPE.STRING - list of accepted types (from ARGUMENT_TYPE)
+ * @param {boolean} [props.isRequired=false] default: false - whether the argument is required (false = optional argument)
+ * @param {boolean} [props.acceptsMultiple=false] default: false - whether argument accepts multiple values
+ * @param {string|SlashCommandClosure} [props.defaultValue=null] default value if no value is provided
+ * @param {string|SlashCommandEnumValue|(string|SlashCommandEnumValue)[]} [props.enumList=[]] list of accepted values
+ * @param {(executor:SlashCommandExecutor)=>SlashCommandEnumValue[]} [props.enumProvider=null] function that returns auto complete options
+ * @param {boolean} [props.forceEnum=false] default: false - whether the input must match one of the enum values
*/
static fromProps(props) {
return new SlashCommandArgument(
@@ -42,13 +39,10 @@ export class SlashCommandArgument {
props.defaultValue ?? null,
props.enumList ?? [],
props.enumProvider ?? null,
- props.forceEnum ?? true,
+ props.forceEnum ?? false,
);
}
-
-
-
/**@type {string}*/ description;
/**@type {ARGUMENT_TYPE[]}*/ typeList = [];
/**@type {boolean}*/ isRequired = false;
@@ -56,8 +50,7 @@ export class SlashCommandArgument {
/**@type {string|SlashCommandClosure}*/ defaultValue;
/**@type {SlashCommandEnumValue[]}*/ enumList = [];
/**@type {(executor:SlashCommandExecutor)=>SlashCommandEnumValue[]}*/ enumProvider = null;
- /**@type {boolean}*/ forceEnum = true;
-
+ /**@type {boolean}*/ forceEnum = false;
/**
* @param {string} description
@@ -78,25 +71,26 @@ export class SlashCommandArgument {
});
this.enumProvider = enumProvider;
this.forceEnum = forceEnum;
+
+ // If no enums were set explictly and the type is one where we know possible enum values, we set them here
+ if (!this.enumList.length && this.typeList.includes(ARGUMENT_TYPE.BOOLEAN)) this.enumList = commonEnumProviders.boolean()();
}
}
-
-
export class SlashCommandNamedArgument extends SlashCommandArgument {
/**
* Creates an unnamed argument from a properties object.
* @param {Object} props
* @param {string} props.name the argument's name
- * @param {string[]} [props.aliasList] list of aliases
* @param {string} props.description description of the argument
- * @param {ARGUMENT_TYPE|ARGUMENT_TYPE[]} props.typeList default: ARGUMENT_TYPE.STRING - list of accepted types (from ARGUMENT_TYPE)
- * @param {boolean} [props.isRequired] default: false - whether the argument is required (false = optional argument)
- * @param {boolean} [props.acceptsMultiple] default: false - whether argument accepts multiple values
- * @param {string|SlashCommandClosure} [props.defaultValue] default value if no value is provided
- * @param {string|SlashCommandEnumValue|(string|SlashCommandEnumValue)[]} [props.enumList] list of accepted values
- * @param {(executor:SlashCommandExecutor)=>SlashCommandEnumValue[]} [props.enumProvider] function that returns auto complete options
- * @param {boolean} [props.forceEnum] default: true - whether the input must match one of the enum values
+ * @param {string[]} [props.aliasList=[]] list of aliases
+ * @param {ARGUMENT_TYPE|ARGUMENT_TYPE[]} [props.typeList=[ARGUMENT_TYPE.STRING]] default: ARGUMENT_TYPE.STRING - list of accepted types (from ARGUMENT_TYPE)
+ * @param {boolean} [props.isRequired=false] default: false - whether the argument is required (false = optional argument)
+ * @param {boolean} [props.acceptsMultiple=false] default: false - whether argument accepts multiple values
+ * @param {string|SlashCommandClosure} [props.defaultValue=null] default value if no value is provided
+ * @param {string|SlashCommandEnumValue|(string|SlashCommandEnumValue)[]} [props.enumList=[]] list of accepted values
+ * @param {(executor:SlashCommandExecutor)=>SlashCommandEnumValue[]} [props.enumProvider=null] function that returns auto complete options
+ * @param {boolean} [props.forceEnum=true] default: true - whether the input must match one of the enum values
*/
static fromProps(props) {
return new SlashCommandNamedArgument(
@@ -113,21 +107,20 @@ export class SlashCommandNamedArgument extends SlashCommandArgument {
);
}
-
-
-
/**@type {string}*/ name;
/**@type {string[]}*/ aliasList = [];
-
/**
* @param {string} name
* @param {string} description
* @param {ARGUMENT_TYPE|ARGUMENT_TYPE[]} types
- * @param {string|SlashCommandClosure} defaultValue
- * @param {string|SlashCommandEnumValue|(string|SlashCommandEnumValue)[]} enums
- * @param {(executor:SlashCommandExecutor)=>SlashCommandEnumValue[]} enumProvider function that returns auto complete options
- * @param {boolean} forceEnum
+ * @param {boolean} [isRequired=false]
+ * @param {boolean} [acceptsMultiple=false]
+ * @param {string|SlashCommandClosure} [defaultValue=null]
+ * @param {string|SlashCommandEnumValue|(string|SlashCommandEnumValue)[]} [enums=[]]
+ * @param {string[]} [aliases=[]]
+ * @param {(executor:SlashCommandExecutor)=>SlashCommandEnumValue[]} [enumProvider=null] function that returns auto complete options
+ * @param {boolean} [forceEnum=true]
*/
constructor(name, description, types, isRequired = false, acceptsMultiple = false, defaultValue = null, enums = [], aliases = [], enumProvider = null, forceEnum = true) {
super(description, types, isRequired, acceptsMultiple, defaultValue, enums, enumProvider, forceEnum);
diff --git a/public/scripts/slash-commands/SlashCommandClosure.js b/public/scripts/slash-commands/SlashCommandClosure.js
index 89eda369a..6f62cc69b 100644
--- a/public/scripts/slash-commands/SlashCommandClosure.js
+++ b/public/scripts/slash-commands/SlashCommandClosure.js
@@ -93,7 +93,7 @@ export class SlashCommandClosure {
/**
*
- * @returns Promise
+ * @returns {Promise}
*/
async execute() {
const closure = this.getCopy();
diff --git a/public/scripts/slash-commands/SlashCommandCommonEnumsProvider.js b/public/scripts/slash-commands/SlashCommandCommonEnumsProvider.js
new file mode 100644
index 000000000..683f28ffa
--- /dev/null
+++ b/public/scripts/slash-commands/SlashCommandCommonEnumsProvider.js
@@ -0,0 +1,232 @@
+import { chat_metadata, characters, substituteParams, chat, extension_prompt_roles, extension_prompt_types } from "../../script.js";
+import { extension_settings } from "../extensions.js";
+import { getGroupMembers, groups, selected_group } from "../group-chats.js";
+import { power_user } from "../power-user.js";
+import { searchCharByName, getTagsList, tags } from "../tags.js";
+import { SlashCommandClosure } from "./SlashCommandClosure.js";
+import { SlashCommandEnumValue, enumTypes } from "./SlashCommandEnumValue.js";
+import { SlashCommandExecutor } from "./SlashCommandExecutor.js";
+
+/**
+ * A collection of regularly used enum icons
+ */
+export const enumIcons = {
+ default: '◊',
+
+ // Variables
+ variable: '𝑥',
+ localVariable: 'L',
+ globalVariable: 'G',
+ scopeVariable: 'S',
+
+ // Common types
+ character: '👤',
+ group: '🧑🤝🧑',
+ persona: '🧙♂️',
+ qr: 'QR',
+ closure: '𝑓',
+ macro: '{{',
+ tag: '🏷️',
+ world: '🌐',
+ preset: '⚙️',
+ file: '📄',
+ message: '💬',
+ voice: '🎤',
+
+ true: '✔️',
+ false: '❌',
+
+ // Value types
+ boolean: '🔲',
+ string: '📝',
+ number: '1️⃣',
+ array: '[]',
+ enum: '📚',
+ dictionary: '{}',
+
+ // Roles
+ system: '⚙️',
+ user: '👤',
+ assistant: '🤖',
+
+ // WI Icons
+ constant: '🔵',
+ normal: '🟢',
+ disabled: '❌',
+ vectorized: '🔗',
+
+ /**
+ * Returns the appropriate state icon based on a boolean
+ *
+ * @param {boolean} state - The state to determine the icon for
+ * @returns {string} The corresponding state icon
+ */
+ getStateIcon: (state) => {
+ return state ? enumIcons.true : enumIcons.false;
+ },
+
+ /**
+ * Returns the appropriate WI icon based on the entry
+ *
+ * @param {Object} entry - WI entry
+ * @returns {string} The corresponding WI icon
+ */
+ getWiStatusIcon: (entry) => {
+ if (entry.constant) return enumIcons.constant;
+ if (entry.disable) return enumIcons.disabled;
+ if (entry.vectorized) return enumIcons.vectorized;
+ return enumIcons.normal;
+ },
+
+ /**
+ * Returns the appropriate icon based on the role
+ *
+ * @param {extension_prompt_roles} role - The role to get the icon for
+ * @returns {string} The corresponding icon
+ */
+ getRoleIcon: (role) => {
+ switch (role) {
+ case extension_prompt_roles.SYSTEM: return enumIcons.system;
+ case extension_prompt_roles.USER: return enumIcons.user;
+ case extension_prompt_roles.ASSISTANT: return enumIcons.assistant;
+ default: return enumIcons.default;
+ }
+ },
+
+ /**
+ * A function to get the data type icon
+ *
+ * @param {string} type - The type of the data
+ * @returns {string} The corresponding data type icon
+ */
+ getDataTypeIcon: (type) => {
+ // Remove possible nullable types definition to match type icon
+ type = type.replace(/\?$/, '');
+ return enumIcons[type] ?? enumIcons.default;
+ }
+}
+
+/**
+ * A collection of common enum providers
+ *
+ * Can be used on `SlashCommandNamedArgument` and `SlashCommandArgument` and their `enumProvider` property.
+ */
+export const commonEnumProviders = {
+ /**
+ * Enum values for booleans. Either using true/false or on/off
+ * Optionally supports "toggle".
+ *
+ * @param {('onOff'|'onOffToggle'|'trueFalse')?} [mode='trueFalse'] - The mode to use. Default is 'trueFalse'.
+ * @returns {() => SlashCommandEnumValue[]}
+ */
+ boolean: (mode = 'trueFalse') => () => {
+ switch (mode) {
+ case 'onOff': return [new SlashCommandEnumValue('on', null, 'macro', enumIcons.true), new SlashCommandEnumValue('off', null, 'macro', enumIcons.false)];
+ case 'onOffToggle': return [new SlashCommandEnumValue('on', null, 'macro', enumIcons.true), new SlashCommandEnumValue('off', null, 'macro', enumIcons.false), new SlashCommandEnumValue('toggle', null, 'macro', enumIcons.boolean)];
+ case 'trueFalse': return [new SlashCommandEnumValue('true', null, 'macro', enumIcons.true), new SlashCommandEnumValue('false', null, 'macro', enumIcons.false)];
+ default: throw new Error(`Invalid boolean enum provider mode: ${mode}`);
+ }
+ },
+
+ /**
+ * All possible variable names
+ *
+ * Can be filtered by `type` to only show global or local variables
+ *
+ * @param {...('global'|'local'|'scope'|'all')} type - The type of variables to include in the array. Can be 'all', 'global', or 'local'.
+ * @returns {() => SlashCommandEnumValue[]}
+ */
+ variables: (...type) => () => {
+ const types = type.flat();
+ const isAll = types.includes('all');
+ return [
+ ...isAll || types.includes('global') ? Object.keys(extension_settings.variables.global ?? []).map(name => new SlashCommandEnumValue(name, null, enumTypes.macro, enumIcons.globalVariable)) : [],
+ ...isAll || types.includes('local') ? Object.keys(chat_metadata.variables ?? []).map(name => new SlashCommandEnumValue(name, null, enumTypes.name, enumIcons.localVariable)) : [],
+ ...isAll || types.includes('scope') ? [].map(name => new SlashCommandEnumValue(name, null, enumTypes.variable, enumIcons.scopeVariable)) : [], // TODO: Add scoped variables here, Lenny
+ ]
+ },
+
+ /**
+ * All possible char entities, like characters and groups. Can be filtered down to just one type.
+ *
+ * @param {('all' | 'character' | 'group')?} [mode='all'] - Which type to return
+ * @returns {() => SlashCommandEnumValue[]}
+ */
+ characters: (mode = 'all') => () => {
+ return [
+ ...['all', 'character'].includes(mode) ? characters.map(char => new SlashCommandEnumValue(char.name, null, enumTypes.name, enumIcons.character)) : [],
+ ...['all', 'group'].includes(mode) ? groups.map(group => new SlashCommandEnumValue(group.name, null, enumTypes.qr, enumIcons.group)) : [],
+ ];
+ },
+
+ /**
+ * All group members of the given group, or default the current active one
+ *
+ * @param {string?} groupId - The id of the group - pass in `undefined` to use the current active group
+ * @returns {() =>SlashCommandEnumValue[]}
+ */
+ groupMembers: (groupId = undefined) => () => getGroupMembers(groupId).map((character, index) => new SlashCommandEnumValue(String(index), character.name, enumTypes.enum, enumIcons.character)),
+
+ /**
+ * All possible personas
+ *
+ * @returns {SlashCommandEnumValue[]}
+ */
+ personas: () => Object.values(power_user.personas).map(persona => new SlashCommandEnumValue(persona, null, enumTypes.name, enumIcons.persona)),
+
+ /**
+ * All possible tags for a given char/group entity
+ *
+ * @param {('all' | 'existing' | 'not-existing')?} [mode='all'] - Which types of tags to show
+ * @returns {() => SlashCommandEnumValue[]}
+ */
+ tagsForChar: (mode = 'all') => (/** @type {SlashCommandExecutor} */ executor) => {
+ // Try to see if we can find the char during execution to filter down the tags list some more. Otherwise take all tags.
+ const charName = executor.namedArgumentList.find(it => it.name == 'name')?.value;
+ if (charName instanceof SlashCommandClosure) throw new Error('Argument \'name\' does not support closures');
+ const key = searchCharByName(substituteParams(charName), { suppressLogging: true });
+ const assigned = key ? getTagsList(key) : [];
+ return tags.filter(it => !key || mode === 'all' || mode === 'existing' && assigned.includes(it) || mode === 'not-existing' && !assigned.includes(it))
+ .map(tag => new SlashCommandEnumValue(tag.name, null, enumTypes.command, enumIcons.tag));
+ },
+
+ /**
+ * All messages in the current chat, returning the message id
+ *
+ * Optionally supports variable names, and/or a placeholder for the last/new message id
+ *
+ * @param {object} [options={}] - Optional arguments
+ * @param {boolean} [options.allowIdAfter=false] - Whether to add an enum option for the new message id after the last message
+ * @param {boolean} [options.allowVars=false] - Whether to add enum option for variable names
+ * @returns {() => SlashCommandEnumValue[]}
+ */
+ messages: ({ allowIdAfter = false, allowVars = false } = {}) => () => {
+ return [
+ ...chat.map((message, index) => new SlashCommandEnumValue(String(index), `${message.name}: ${message.mes}`, enumTypes.number, message.is_user ? enumIcons.user : message.is_system ? enumIcons.system : enumIcons.assistant)),
+ ...allowIdAfter ? [new SlashCommandEnumValue(String(chat.length), '>> After Last Message >>', enumTypes.enum, '➕')] : [],
+ ...allowVars ? commonEnumProviders.variables('all')() : [],
+ ];
+ },
+
+ /**
+ * All existing worlds / lorebooks
+ *
+ * @returns {SlashCommandEnumValue[]}
+ */
+ worlds: () => $('#world_info').children().toArray().map(x => new SlashCommandEnumValue(x.textContent, null, enumTypes.name, enumIcons.world)),
+
+ /**
+ * All existing injects for the current chat
+ *
+ * @returns {SlashCommandEnumValue[]}
+ */
+ injects: () => {
+ if (!chat_metadata.script_injects || !Object.keys(chat_metadata.script_injects).length) return [];
+ return Object.entries(chat_metadata.script_injects)
+ .map(([id, inject]) => {
+ const positionName = (Object.entries(extension_prompt_types)).find(([_, value]) => value === inject.position)?.[0] ?? 'unknown';
+ return new SlashCommandEnumValue(id, `${enumIcons.getRoleIcon(inject.role ?? extension_prompt_roles.SYSTEM)}[Inject](${positionName}, depth: ${inject.depth}, scan: ${inject.scan ?? false}) ${inject.value}`,
+ enumTypes.enum, '💉');
+ });
+ },
+};
diff --git a/public/scripts/slash-commands/SlashCommandEnumValue.js b/public/scripts/slash-commands/SlashCommandEnumValue.js
index 1fa610c56..973d1be99 100644
--- a/public/scripts/slash-commands/SlashCommandEnumValue.js
+++ b/public/scripts/slash-commands/SlashCommandEnumValue.js
@@ -1,13 +1,63 @@
+
+/**
+ * @typedef {'enum' | 'command' | 'namedArgument' | 'variable' | 'qr' | 'macro' | 'number' | 'name'} EnumType
+ */
+
+/**
+ * Collection of the enum types that can be used with `SlashCommandEnumValue`
+ *
+ * Contains documentation on which color this will result to
+ */
+export const enumTypes = {
+ /** 'enum' - [string] - light orange @type {EnumType} */
+ enum: 'enum',
+ /** 'command' - [cmd] - light yellow @type {EnumType} */
+ command: 'command',
+ /** 'namedArgument' - [argName] - sky blue @type {EnumType} */
+ namedArgument: 'namedArgument',
+ /** 'variable' - [punctuationL1] - pink @type {EnumType} */
+ variable: 'variable',
+ /** 'qr' - [variable] - light blue @type {EnumType} */
+ qr: 'qr',
+ /** 'macro' - [variableLanguage] - blue @type {EnumType} */
+ macro: 'macro',
+ /** 'number' - [number] - light green @type {EnumType} */
+ number: 'number',
+ /** 'name' - [type] - forest green @type {EnumType} */
+ name: 'name',
+
+ /**
+ * Gets the value of the enum type based on the provided index
+ *
+ * Can be used to get differing colors or even random colors, by providing the index of a unique set
+ *
+ * @param {number?} index - The index used to retrieve the enum type
+ * @return {EnumType} The enum type corresponding to the index
+ */
+ getBasedOnIndex(index) {
+ const keys = Object.keys(this);
+ return this[keys[(index ?? 0) % keys.length]];
+ }
+}
+
export class SlashCommandEnumValue {
/**@type {string}*/ value;
/**@type {string}*/ description;
- /**@type {string}*/ type = 'enum';
+ /**@type {EnumType}*/ type = 'enum';
/**@type {string}*/ typeIcon = '◊';
+ /**
+ * A constructor for creating a SlashCommandEnumValue instance.
+ *
+ * @param {string} value - The value
+ * @param {string?} description - Optional description, displayed in a second line
+ * @param {EnumType?} type - type of the enum (defining its color)
+ * @param {string} typeIcon - The icon to display (Can be pulled from `enumIcons` for common ones)
+ */
constructor(value, description = null, type = 'enum', typeIcon = '◊') {
this.value = value;
this.description = description;
- this.type = type;
+ this.type = type ?? 'enum';
this.typeIcon = typeIcon;
}
diff --git a/public/scripts/slash-commands/SlashCommandParser.js b/public/scripts/slash-commands/SlashCommandParser.js
index 8dffa666e..1b6f65761 100644
--- a/public/scripts/slash-commands/SlashCommandParser.js
+++ b/public/scripts/slash-commands/SlashCommandParser.js
@@ -17,6 +17,10 @@ import { SlashCommandAutoCompleteNameResult } from './SlashCommandAutoCompleteNa
import { SlashCommandUnnamedArgumentAssignment } from './SlashCommandUnnamedArgumentAssignment.js';
import { SlashCommandEnumValue } from './SlashCommandEnumValue.js';
import { MacroAutoCompleteOption } from '../autocomplete/MacroAutoCompleteOption.js';
+import { commonEnumProviders } from './SlashCommandCommonEnumsProvider.js';
+
+/** @typedef {import('./SlashCommand.js').NamedArgumentsCapture} NamedArgumentsCapture */
+/** @typedef {import('./SlashCommand.js').NamedArguments} NamedArguments */
/**@readonly*/
/**@enum {Number}*/
@@ -32,7 +36,7 @@ export class SlashCommandParser {
/**
* @deprecated Use SlashCommandParser.addCommandObject() instead.
* @param {string} command Command name
- * @param {(namedArguments:Object., unnamedArguments:string|SlashCommandClosure|(string|SlashCommandClosure)[])=>string|SlashCommandClosure|void|Promise} callback The function to execute when the command is called
+ * @param {(namedArguments:NamedArguments|NamedArgumentsCapture, unnamedArguments:string|SlashCommandClosure|(string|SlashCommandClosure)[])=>string|SlashCommandClosure|Promise} callback callback The function to execute when the command is called
* @param {string[]} aliases List of alternative command names
* @param {string} helpString Help text shown in autocomplete and command browser
*/
@@ -131,7 +135,7 @@ export class SlashCommandParser {
description: 'The state of the parser flag to set.',
typeList: [ARGUMENT_TYPE.BOOLEAN],
defaultValue: 'on',
- enumList: ['on', 'off'],
+ enumList: commonEnumProviders.boolean('onOff')(),
}),
],
splitUnnamedArgument: true,
diff --git a/public/scripts/tags.js b/public/scripts/tags.js
index 24a57a9c0..eb32cc646 100644
--- a/public/scripts/tags.js
+++ b/public/scripts/tags.js
@@ -9,6 +9,8 @@ import {
eventSource,
event_types,
DEFAULT_PRINT_TIMEOUT,
+ substituteParams,
+ printCharacters,
} from '../script.js';
// eslint-disable-next-line no-unused-vars
import { FILTER_TYPES, FILTER_STATES, DEFAULT_FILTER_STATE, isFilterState, FilterHelper } from './filters.js';
@@ -25,6 +27,7 @@ import { debounce_timeout } from './constants.js';
import { INTERACTABLE_CONTROL_CLASS } from './keyboard.js';
import { SlashCommandEnumValue } from './slash-commands/SlashCommandEnumValue.js';
import { SlashCommandExecutor } from './slash-commands/SlashCommandExecutor.js';
+import { commonEnumProviders } from './slash-commands/SlashCommandCommonEnumsProvider.js';
import { renderTemplateAsync } from './templates.js';
export {
@@ -491,6 +494,27 @@ export function getTagKeyForEntityElement(element) {
return undefined;
}
+/**
+ * Gets the key for char/group by searching based on the name or avatar. If none can be found, a toastr will be shown and null returned.
+ * This function is mostly used in slash commands.
+ *
+ * @param {string?} [charName] The optionally provided char name
+ * @param {object} [options] - Optional arguments
+ * @param {boolean} [options.suppressLogging=false] - Whether to suppress the toastr warning
+ * @returns {string?} - The char/group key, or null if none found
+ */
+export function searchCharByName(charName, { suppressLogging = false } = {}) {
+ const entity = charName
+ ? (characters.find(x => x.name === charName) || groups.find(x => x.name == charName))
+ : (selected_group ? groups.find(x => x.id == selected_group) : characters[this_chid]);
+ const key = getTagKeyForEntity(entity);
+ if (!key) {
+ if (!suppressLogging) toastr.warning(`Character ${charName} not found.`);
+ return null;
+ }
+ return key;
+}
+
/**
* Adds one or more tags to a given entity
*
@@ -752,7 +776,7 @@ async function handleTagImport(character, { forceShow = false } = {}) {
async function showTagImportPopup(character, existingTags, newTags, folderTags) {
/** @type {{[key: string]: import('./popup.js').CustomPopupButton}} */
const importButtons = {
- NONE: { result: 2, text: 'Import None', },
+ NONE: { result: 2, text: 'Import None' },
ALL: { result: 3, text: 'Import All' },
EXISTING: { result: 4, text: 'Import Existing' },
};
@@ -1768,22 +1792,6 @@ function printViewTagList(tagContainer, empty = true) {
}
function registerTagsSlashCommands() {
- /**
- * Gets the key for char/group for a slash command. If none can be found, a toastr will be shown and null returned.
- * @param {string?} [charName] The optionally provided char name
- * @returns {string?} - The char/group key, or null if none found
- */
- function paraGetCharKey(charName) {
- const entity = charName
- ? (characters.find(x => x.name === charName) || groups.find(x => x.name == charName))
- : (selected_group ? groups.find(x => x.id == selected_group) : characters[this_chid]);
- const key = getTagKeyForEntity(entity);
- if (!key) {
- toastr.warning(`Character ${charName} not found.`);
- return null;
- }
- return key;
- }
/**
* Gets a tag by its name. Optionally can create the tag if it does not exist.
* @param {string} tagName - The name of the tag
@@ -1812,11 +1820,12 @@ function registerTagsSlashCommands() {
returns: 'true/false - Whether the tag was added or was assigned already',
/** @param {{name: string}} namedArgs @param {string} tagName @returns {string} */
callback: ({ name }, tagName) => {
- const key = paraGetCharKey(name);
+ const key = searchCharByName(name);
if (!key) return 'false';
const tag = paraGetTag(tagName, { allowCreate: true });
if (!tag) return 'false';
const result = addTagsToEntity(tag, key);
+ printCharacters();
return String(result);
},
namedArgumentList: [
@@ -1824,22 +1833,14 @@ function registerTagsSlashCommands() {
description: 'Character name',
typeList: [ARGUMENT_TYPE.STRING],
defaultValue: '{{char}}',
- enumProvider: ()=>[
- ...characters.map(it=>new SlashCommandEnumValue(it.name, null, 'qr', 'C')),
- ...groups.map(it=>new SlashCommandEnumValue(it.name, null, 'variable', 'G')),
- ],
+ enumProvider: commonEnumProviders.characters(),
}),
],
unnamedArgumentList: [
SlashCommandArgument.fromProps({ description: 'tag name',
typeList: [ARGUMENT_TYPE.STRING],
isRequired: true,
- enumProvider: (executor)=>{
- const key = paraGetCharKey(/**@type {string}*/(executor.namedArgumentList.find(it=>it.name == 'name')?.value));
- if (!key) return tags.map(it=>new SlashCommandEnumValue(it.name, it.title));
- const assigned = getTagsList(key);
- return tags.filter(it=>!assigned.includes(it)).map(it=>new SlashCommandEnumValue(it.name, it.title));
- },
+ enumProvider: commonEnumProviders.tagsForChar('not-existing'),
forceEnum: false,
}),
],
@@ -1864,11 +1865,12 @@ function registerTagsSlashCommands() {
returns: 'true/false - Whether the tag was removed or wasn\'t assigned already',
/** @param {{name: string}} namedArgs @param {string} tagName @returns {string} */
callback: ({ name }, tagName) => {
- const key = paraGetCharKey(name);
+ const key = searchCharByName(name);
if (!key) return 'false';
const tag = paraGetTag(tagName);
if (!tag) return 'false';
const result = removeTagFromEntity(tag, key);
+ printCharacters();
return String(result);
},
namedArgumentList: [
@@ -1876,10 +1878,7 @@ function registerTagsSlashCommands() {
description: 'Character name',
typeList: [ARGUMENT_TYPE.STRING],
defaultValue: '{{char}}',
- enumProvider: ()=>[
- ...characters.map(it=>new SlashCommandEnumValue(it.name, null, 'qr', 'C')),
- ...groups.map(it=>new SlashCommandEnumValue(it.name, null, 'variable', 'G')),
- ],
+ enumProvider: commonEnumProviders.characters(),
}),
],
unnamedArgumentList: [
@@ -1887,11 +1886,7 @@ function registerTagsSlashCommands() {
typeList: [ARGUMENT_TYPE.STRING],
isRequired: true,
/**@param {SlashCommandExecutor} executor */
- enumProvider: (executor)=>{
- const key = paraGetCharKey(/**@type {string}*/(executor.namedArgumentList.find(it=>it.name == 'name')?.value));
- if (!key) return tags.map(it=>new SlashCommandEnumValue(it.name, it.title));
- return getTagsList(key).map(it=>new SlashCommandEnumValue(it.name, it.title));
- },
+ enumProvider: commonEnumProviders.tagsForChar('existing'),
}),
],
helpString: `
@@ -1914,17 +1909,29 @@ function registerTagsSlashCommands() {
returns: 'true/false - Whether the given tag name is assigned to the character',
/** @param {{name: string}} namedArgs @param {string} tagName @returns {string} */
callback: ({ name }, tagName) => {
- const key = paraGetCharKey(name);
+ const key = searchCharByName(name);
if (!key) return 'false';
const tag = paraGetTag(tagName);
if (!tag) return 'false';
return String(tag_map[key].includes(tag.id));
},
namedArgumentList: [
- new SlashCommandNamedArgument('name', 'Character name', [ARGUMENT_TYPE.STRING], false, false, '{{char}}'),
+ SlashCommandNamedArgument.fromProps({
+ name: 'name',
+ description: 'Character name',
+ typeList: [ARGUMENT_TYPE.STRING],
+ defaultValue: '{{char}}',
+ enumProvider: commonEnumProviders.characters(),
+ }),
],
unnamedArgumentList: [
- new SlashCommandArgument('tag name', [ARGUMENT_TYPE.STRING], true),
+ SlashCommandArgument.fromProps({
+ description: 'tag name',
+ typeList: [ARGUMENT_TYPE.STRING],
+ isRequired: true,
+ /**@param {SlashCommandExecutor} executor */
+ enumProvider: commonEnumProviders.tagsForChar('all'),
+ }),
],
helpString: `
@@ -1946,13 +1953,19 @@ function registerTagsSlashCommands() {
returns: 'Comma-separated list of all assigned tags',
/** @param {{name: string}} namedArgs @returns {string} */
callback: ({ name }) => {
- const key = paraGetCharKey(name);
+ const key = searchCharByName(name);
if (!key) return '';
const tags = getTagsList(key);
return tags.map(x => x.name).join(', ');
},
namedArgumentList: [
- new SlashCommandNamedArgument('name', 'Character name', [ARGUMENT_TYPE.STRING], false, false, '{{char}}'),
+ SlashCommandNamedArgument.fromProps({
+ name: 'name',
+ description: 'Character name',
+ typeList: [ARGUMENT_TYPE.STRING],
+ defaultValue: '{{char}}',
+ enumProvider: commonEnumProviders.characters(),
+ }),
],
helpString: `
diff --git a/public/scripts/variables.js b/public/scripts/variables.js
index de8ff390b..3117f07ec 100644
--- a/public/scripts/variables.js
+++ b/public/scripts/variables.js
@@ -1,16 +1,20 @@
import { chat_metadata, getCurrentChatId, saveSettingsDebounced, sendSystemMessage, system_message_types } from '../script.js';
import { extension_settings, saveMetadataDebounced } from './extensions.js';
-import { executeSlashCommands, executeSlashCommandsWithOptions } from './slash-commands.js';
+import { executeSlashCommandsWithOptions } from './slash-commands.js';
import { SlashCommand } from './slash-commands/SlashCommand.js';
import { SlashCommandAbortController } from './slash-commands/SlashCommandAbortController.js';
import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from './slash-commands/SlashCommandArgument.js';
import { SlashCommandClosure } from './slash-commands/SlashCommandClosure.js';
import { SlashCommandClosureResult } from './slash-commands/SlashCommandClosureResult.js';
+import { commonEnumProviders } from './slash-commands/SlashCommandCommonEnumsProvider.js';
import { SlashCommandEnumValue } from './slash-commands/SlashCommandEnumValue.js';
import { PARSER_FLAG, SlashCommandParser } from './slash-commands/SlashCommandParser.js';
import { SlashCommandScope } from './slash-commands/SlashCommandScope.js';
import { isFalseBoolean } from './utils.js';
+/** @typedef {import('./slash-commands/SlashCommandParser.js').NamedArguments} NamedArguments */
+/** @typedef {import('./slash-commands/SlashCommand.js').UnnamedArguments} UnnamedArguments */
+
const MAX_LOOPS = 100;
function getLocalVariable(name, args = {}) {
@@ -116,6 +120,7 @@ function setGlobalVariable(name, value, args = {}) {
extension_settings.variables.global[name] = value;
}
saveSettingsDebounced();
+ return value;
}
function addLocalVariable(name, value) {
@@ -314,14 +319,16 @@ function listVariablesCallback() {
const htmlMessage = DOMPurify.sanitize(converter.makeHtml(message));
sendSystemMessage(system_message_types.GENERIC, htmlMessage);
+ return '';
}
/**
*
- * @param {import('./slash-commands/SlashCommand.js').NamedArguments} args
+ * @param {NamedArguments} args
* @param {(string|SlashCommandClosure)[]} value
*/
async function whileCallback(args, value) {
+ if (args.guard instanceof SlashCommandClosure) throw new Error('argument \'guard\' cannot be a closure for command /while');
const isGuardOff = isFalseBoolean(args.guard);
const iterations = isGuardOff ? Number.MAX_SAFE_INTEGER : MAX_LOOPS;
/**@type {string|SlashCommandClosure} */
@@ -360,11 +367,12 @@ async function whileCallback(args, value) {
/**
*
- * @param {import('./slash-commands/SlashCommand.js').NamedArguments} args
- * @param {import('./slash-commands/SlashCommand.js').UnnamedArguments} value
+ * @param {NamedArguments} args
+ * @param {UnnamedArguments} value
* @returns
*/
async function timesCallback(args, value) {
+ if (args.guard instanceof SlashCommandClosure) throw new Error('argument \'guard\' cannot be a closure for command /while');
let repeats;
let command;
if (Array.isArray(value)) {
@@ -398,7 +406,7 @@ async function timesCallback(args, value) {
/**
*
- * @param {import('./slash-commands/SlashCommand.js').NamedArguments} args
+ * @param {NamedArguments} args
* @param {(string|SlashCommandClosure)[]} value
*/
async function ifCallback(args, value) {
@@ -765,13 +773,13 @@ function randValuesCallback(from, to, args) {
if (args.round == 'floor') {
return Math.floor(value);
}
- return String(value);
+ return value;
}
/**
* Declare a new variable in the current scope.
- * @param {{_scope:SlashCommandScope, key?:string}} args Named arguments.
- * @param {String|[String, SlashCommandClosure]} value Name and optional value for the variable.
+ * @param {NamedArguments} args Named arguments.
+ * @param {string|SlashCommandClosure|(string|SlashCommandClosure)[]} value Name and optional value for the variable.
* @returns The variable's value
*/
function letCallback(args, value) {
@@ -784,7 +792,9 @@ function letCallback(args, value) {
const val = value;
args._scope.letVariable(key, val);
return val;
- } else if (value.includes(' ')) {
+ }
+ if (value instanceof SlashCommandClosure) throw new Error('/let unnamed argument does not support closures if no key is provided');
+ if (value.includes(' ')) {
const key = value.split(' ')[0];
const val = value.split(' ').slice(1).join(' ');
args._scope.letVariable(key, val);
@@ -795,7 +805,7 @@ function letCallback(args, value) {
/**
* Set or retrieve a variable in the current scope or nearest ancestor scope.
- * @param {{_hasUnnamedArgument:boolean, _scope:SlashCommandScope, key?:string, index?:string|number}} args Named arguments.
+ * @param {NamedArguments} args Named arguments.
* @param {string|SlashCommandClosure|(string|SlashCommandClosure)[]} value Name and optional value for the variable.
* @returns The variable's value
*/
@@ -822,7 +832,7 @@ function varCallback(args, value) {
}
/**
- * @param {import('./slash-commands/SlashCommand.js').NamedArguments} args
+ * @param {NamedArguments} args
* @param {SlashCommandClosure} value
* @returns {string}
*/
@@ -834,8 +844,8 @@ function closureSerializeCallback(args, value) {
}
/**
- * @param {import('./slash-commands/SlashCommand.js').NamedArguments} args
- * @param {import('./slash-commands/SlashCommand.js').UnnamedArguments} value
+ * @param {NamedArguments} args
+ * @param {UnnamedArguments} value
* @returns {SlashCommandClosure}
*/
function closureDeserializeCallback(args, value) {
@@ -846,17 +856,24 @@ function closureDeserializeCallback(args, value) {
}
export function registerVariableCommands() {
- SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'listvar',
+ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
+ name: 'listvar',
callback: listVariablesCallback,
helpString: 'List registered chat variables.',
}));
- SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'setvar',
- callback: (args, value) => setLocalVariable(args.key || args.name, value, args),
+ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
+ name: 'setvar',
+ callback: (args, value) => String(setLocalVariable(args.key || args.name, value, args)),
returns: 'the set variable value',
namedArgumentList: [
- new SlashCommandNamedArgument(
- 'key', 'variable name', [ARGUMENT_TYPE.VARIABLE_NAME], true,
- ),
+ SlashCommandNamedArgument.fromProps({
+ name: 'key',
+ description: 'variable name',
+ typeList: [ARGUMENT_TYPE.VARIABLE_NAME],
+ isRequired: true,
+ enumProvider: commonEnumProviders.variables('local'),
+ forceEnum: false,
+ }),
new SlashCommandNamedArgument(
'index', 'list index', [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.STRING], false,
),
@@ -880,21 +897,28 @@ export function registerVariableCommands() {
`,
}));
- SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'getvar',
- callback: (args, value) => getLocalVariable(value, args),
+ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
+ name: 'getvar',
+ callback: (args, value) => String(getLocalVariable(value, args)),
returns: 'the variable value',
namedArgumentList: [
- new SlashCommandNamedArgument(
- 'key', 'variable name', [ARGUMENT_TYPE.VARIABLE_NAME], false,
- ),
+ SlashCommandNamedArgument.fromProps({
+ name: 'key',
+ description: 'variable name',
+ typeList: [ARGUMENT_TYPE.VARIABLE_NAME],
+ enumProvider: commonEnumProviders.variables('local'),
+ }),
new SlashCommandNamedArgument(
'index', 'list index', [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.STRING], false,
),
],
unnamedArgumentList: [
- new SlashCommandArgument(
- 'key', [ARGUMENT_TYPE.VARIABLE_NAME], false,
- ),
+ SlashCommandArgument.fromProps({
+ description: 'key',
+ typeList: [ARGUMENT_TYPE.VARIABLE_NAME],
+ isRequired: false,
+ enumProvider: commonEnumProviders.variables('local'),
+ }),
],
helpString: `
@@ -916,13 +940,19 @@ export function registerVariableCommands() {
`,
}));
- SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'addvar',
- callback: (args, value) => addLocalVariable(args.key || args.name, value),
+ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
+ name: 'addvar',
+ callback: (args, value) => String(addLocalVariable(args.key || args.name, value)),
returns: 'the new variable value',
namedArgumentList: [
- new SlashCommandNamedArgument(
- 'key', 'variable name', [ARGUMENT_TYPE.VARIABLE_NAME], true,
- ),
+ SlashCommandNamedArgument.fromProps({
+ name: 'key',
+ description: 'variable name',
+ typeList: [ARGUMENT_TYPE.VARIABLE_NAME],
+ isRequired: true,
+ enumProvider: commonEnumProviders.variables('local'),
+ forceEnum: false,
+ }),
],
unnamedArgumentList: [
new SlashCommandArgument(
@@ -943,13 +973,19 @@ export function registerVariableCommands() {
`,
}));
- SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'setglobalvar',
- callback: (args, value) => setGlobalVariable(args.key || args.name, value, args),
+ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
+ name: 'setglobalvar',
+ callback: (args, value) => String(setGlobalVariable(args.key || args.name, value, args)),
returns: 'the set global variable value',
namedArgumentList: [
- new SlashCommandNamedArgument(
- 'key', 'variable name', [ARGUMENT_TYPE.VARIABLE_NAME], true,
- ),
+ SlashCommandNamedArgument.fromProps({
+ name: 'key',
+ description: 'variable name',
+ typeList: [ARGUMENT_TYPE.VARIABLE_NAME],
+ isRequired: true,
+ enumProvider: commonEnumProviders.variables('global'),
+ forceEnum: false,
+ }),
new SlashCommandNamedArgument(
'index', 'list index', [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.STRING], false,
),
@@ -973,21 +1009,27 @@ export function registerVariableCommands() {
`,
}));
- SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'getglobalvar',
- callback: (args, value) => getGlobalVariable(value, args),
+ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
+ name: 'getglobalvar',
+ callback: (args, value) => String(getGlobalVariable(value, args)),
returns: 'global variable value',
namedArgumentList: [
- new SlashCommandNamedArgument(
- 'key', 'variable name', [ARGUMENT_TYPE.VARIABLE_NAME], false,
- ),
+ SlashCommandNamedArgument.fromProps({
+ name: 'key',
+ description: 'variable name',
+ typeList: [ARGUMENT_TYPE.VARIABLE_NAME],
+ enumProvider: commonEnumProviders.variables('global'),
+ }),
new SlashCommandNamedArgument(
'index', 'list index', [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.STRING], false,
),
],
unnamedArgumentList: [
- new SlashCommandArgument(
- 'key', [ARGUMENT_TYPE.VARIABLE_NAME], false,
- ),
+ SlashCommandArgument.fromProps({
+ description: 'key',
+ typeList: [ARGUMENT_TYPE.VARIABLE_NAME],
+ enumProvider: commonEnumProviders.variables('global'),
+ }),
],
helpString: `
@@ -1009,13 +1051,19 @@ export function registerVariableCommands() {
`,
}));
- SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'addglobalvar',
- callback: (args, value) => addGlobalVariable(args.key || args.name, value),
+ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
+ name: 'addglobalvar',
+ callback: (args, value) => String(addGlobalVariable(args.key || args.name, value)),
returns: 'the new variable value',
namedArgumentList: [
- new SlashCommandNamedArgument(
- 'key', 'variable name', [ARGUMENT_TYPE.VARIABLE_NAME], true,
- ),
+ SlashCommandNamedArgument.fromProps({
+ name: 'key',
+ description: 'variable name',
+ typeList: [ARGUMENT_TYPE.VARIABLE_NAME],
+ isRequired: true,
+ enumProvider: commonEnumProviders.variables('global'),
+ forceEnum: false,
+ }),
],
unnamedArgumentList: [
new SlashCommandArgument(
@@ -1036,13 +1084,19 @@ export function registerVariableCommands() {
`,
}));
- SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'incvar',
- callback: (_, value) => incrementLocalVariable(value),
+ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
+ name: 'incvar',
+ callback: (_, value) => String(incrementLocalVariable(value)),
returns: 'the new variable value',
unnamedArgumentList: [
- new SlashCommandArgument(
- 'key', [ARGUMENT_TYPE.VARIABLE_NAME], true,
- ),
+ SlashCommandNamedArgument.fromProps({
+ name: 'key',
+ description: 'variable name',
+ typeList: [ARGUMENT_TYPE.VARIABLE_NAME],
+ isRequired: true,
+ enumProvider: commonEnumProviders.variables('local'),
+ forceEnum: false,
+ }),
],
helpString: `
@@ -1058,13 +1112,19 @@ export function registerVariableCommands() {
`,
}));
- SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'decvar',
- callback: (_, value) => decrementLocalVariable(value),
+ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
+ name: 'decvar',
+ callback: (_, value) => String(decrementLocalVariable(value)),
returns: 'the new variable value',
unnamedArgumentList: [
- new SlashCommandArgument(
- 'key', [ARGUMENT_TYPE.VARIABLE_NAME], true,
- ),
+ SlashCommandNamedArgument.fromProps({
+ name: 'key',
+ description: 'variable name',
+ typeList: [ARGUMENT_TYPE.VARIABLE_NAME],
+ isRequired: true,
+ enumProvider: commonEnumProviders.variables('local'),
+ forceEnum: false,
+ }),
],
helpString: `
@@ -1080,13 +1140,19 @@ export function registerVariableCommands() {
`,
}));
- SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'incglobalvar',
- callback: (_, value) => incrementGlobalVariable(value),
+ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
+ name: 'incglobalvar',
+ callback: (_, value) => String(incrementGlobalVariable(value)),
returns: 'the new variable value',
unnamedArgumentList: [
- new SlashCommandArgument(
- 'key', [ARGUMENT_TYPE.VARIABLE_NAME], true,
- ),
+ SlashCommandNamedArgument.fromProps({
+ name: 'key',
+ description: 'variable name',
+ typeList: [ARGUMENT_TYPE.VARIABLE_NAME],
+ isRequired: true,
+ enumProvider: commonEnumProviders.variables('global'),
+ forceEnum: false,
+ }),
],
helpString: `
@@ -1102,13 +1168,19 @@ export function registerVariableCommands() {
`,
}));
- SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'decglobalvar',
- callback: (_, value) => decrementGlobalVariable(value),
+ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
+ name: 'decglobalvar',
+ callback: (_, value) => String(decrementGlobalVariable(value)),
returns: 'the new variable value',
unnamedArgumentList: [
- new SlashCommandArgument(
- 'key', [ARGUMENT_TYPE.VARIABLE_NAME], true,
- ),
+ SlashCommandNamedArgument.fromProps({
+ name: 'key',
+ description: 'variable name',
+ typeList: [ARGUMENT_TYPE.VARIABLE_NAME],
+ isRequired: true,
+ enumProvider: commonEnumProviders.variables('global'),
+ forceEnum: false,
+ }),
],
helpString: `
@@ -1124,26 +1196,37 @@ export function registerVariableCommands() {
`,
}));
- SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'if',
+ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
+ name: 'if',
callback: ifCallback,
returns: 'result of the executed command ("then" or "else")',
namedArgumentList: [
- new SlashCommandNamedArgument(
- 'left', 'left operand', [ARGUMENT_TYPE.VARIABLE_NAME, ARGUMENT_TYPE.STRING, ARGUMENT_TYPE.NUMBER], true,
- ),
- new SlashCommandNamedArgument(
- 'right', 'right operand', [ARGUMENT_TYPE.VARIABLE_NAME, ARGUMENT_TYPE.STRING, ARGUMENT_TYPE.NUMBER], true,
- ),
+ SlashCommandNamedArgument.fromProps({
+ name: 'left',
+ description: 'left operand',
+ typeList: [ARGUMENT_TYPE.VARIABLE_NAME, ARGUMENT_TYPE.STRING, ARGUMENT_TYPE.NUMBER],
+ isRequired: true,
+ enumProvider: commonEnumProviders.variables('all'),
+ forceEnum: false,
+ }),
+ SlashCommandNamedArgument.fromProps({
+ name: 'right',
+ description: 'right operand',
+ typeList: [ARGUMENT_TYPE.VARIABLE_NAME, ARGUMENT_TYPE.STRING, ARGUMENT_TYPE.NUMBER],
+ isRequired: true,
+ enumProvider: commonEnumProviders.variables('all'),
+ forceEnum: false,
+ }),
new SlashCommandNamedArgument(
'rule', 'comparison rule', [ARGUMENT_TYPE.STRING], true, false, null, [
- new SlashCommandEnumValue('gt', 'a > b'),
+ new SlashCommandEnumValue('gt', 'a > b'),
new SlashCommandEnumValue('gte', 'a >= b'),
- new SlashCommandEnumValue('lt', 'a < b'),
+ new SlashCommandEnumValue('lt', 'a < b'),
new SlashCommandEnumValue('lte', 'a <= b'),
- new SlashCommandEnumValue('eq', 'a == b'),
+ new SlashCommandEnumValue('eq', 'a == b'),
new SlashCommandEnumValue('neq', 'a !== b'),
new SlashCommandEnumValue('not', '!a'),
- new SlashCommandEnumValue('in', 'a includes b'),
+ new SlashCommandEnumValue('in', 'a includes b'),
new SlashCommandEnumValue('nin', 'a not includes b'),
],
),
@@ -1191,31 +1274,42 @@ export function registerVariableCommands() {
`,
}));
- SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'while',
+ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
+ name: 'while',
callback: whileCallback,
returns: 'result of the last executed command',
namedArgumentList: [
- new SlashCommandNamedArgument(
- 'left', 'left operand', [ARGUMENT_TYPE.VARIABLE_NAME, ARGUMENT_TYPE.STRING, ARGUMENT_TYPE.NUMBER], true,
- ),
- new SlashCommandNamedArgument(
- 'right', 'right operand', [ARGUMENT_TYPE.VARIABLE_NAME, ARGUMENT_TYPE.STRING, ARGUMENT_TYPE.NUMBER], true,
- ),
+ SlashCommandNamedArgument.fromProps({
+ name: 'left',
+ description: 'left operand',
+ typeList: [ARGUMENT_TYPE.VARIABLE_NAME, ARGUMENT_TYPE.STRING, ARGUMENT_TYPE.NUMBER],
+ isRequired: true,
+ enumProvider: commonEnumProviders.variables('all'),
+ forceEnum: false,
+ }),
+ SlashCommandNamedArgument.fromProps({
+ name: 'right',
+ description: 'right operand',
+ typeList: [ARGUMENT_TYPE.VARIABLE_NAME, ARGUMENT_TYPE.STRING, ARGUMENT_TYPE.NUMBER],
+ isRequired: true,
+ enumProvider: commonEnumProviders.variables('all'),
+ forceEnum: false,
+ }),
new SlashCommandNamedArgument(
'rule', 'comparison rule', [ARGUMENT_TYPE.STRING], true, false, null, [
- new SlashCommandEnumValue('gt', 'a > b'),
+ new SlashCommandEnumValue('gt', 'a > b'),
new SlashCommandEnumValue('gte', 'a >= b'),
- new SlashCommandEnumValue('lt', 'a < b'),
+ new SlashCommandEnumValue('lt', 'a < b'),
new SlashCommandEnumValue('lte', 'a <= b'),
- new SlashCommandEnumValue('eq', 'a == b'),
+ new SlashCommandEnumValue('eq', 'a == b'),
new SlashCommandEnumValue('neq', 'a !== b'),
new SlashCommandEnumValue('not', '!a'),
- new SlashCommandEnumValue('in', 'a includes b'),
+ new SlashCommandEnumValue('in', 'a includes b'),
new SlashCommandEnumValue('nin', 'a not includes b'),
],
),
new SlashCommandNamedArgument(
- 'guard', 'disable loop iteration limit', [ARGUMENT_TYPE.STRING], false, false, null, ['off'],
+ 'guard', 'disable loop iteration limit', [ARGUMENT_TYPE.STRING], false, false, null, commonEnumProviders.boolean('onOff')(),
),
],
unnamedArgumentList: [
@@ -1260,7 +1354,8 @@ export function registerVariableCommands() {
`,
}));
- SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'times',
+ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
+ name: 'times',
callback: timesCallback,
returns: 'result of the last executed command',
namedArgumentList: [],
@@ -1299,13 +1394,16 @@ export function registerVariableCommands() {
`,
}));
- SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'flushvar',
- callback: (_, value) => deleteLocalVariable(value),
- namedArgumentList: [],
+ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
+ name: 'flushvar',
+ callback: async (_, value) => deleteLocalVariable(value instanceof SlashCommandClosure ? (await value.execute())?.pipe : String(value)),
unnamedArgumentList: [
- new SlashCommandArgument(
- 'key', [ARGUMENT_TYPE.VARIABLE_NAME], true,
- ),
+ SlashCommandNamedArgument.fromProps({
+ name: 'key',
+ description: 'variable name or closure that returns a variable name',
+ typeList: [ARGUMENT_TYPE.VARIABLE_NAME, ARGUMENT_TYPE.CLOSURE],
+ enumProvider: commonEnumProviders.variables('local'),
+ }),
],
helpString: `
@@ -1321,13 +1419,17 @@ export function registerVariableCommands() {
`,
}));
- SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'flushglobalvar',
- callback: (_, value) => deleteGlobalVariable(value),
+ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
+ name: 'flushglobalvar',
+ callback: async (_, value) => deleteGlobalVariable(value instanceof SlashCommandClosure ? (await value.execute())?.pipe : String(value)),
namedArgumentList: [],
unnamedArgumentList: [
- new SlashCommandArgument(
- 'key', [ARGUMENT_TYPE.VARIABLE_NAME], true,
- ),
+ SlashCommandNamedArgument.fromProps({
+ name: 'key',
+ description: 'variable name or closure that returns a variable name',
+ typeList: [ARGUMENT_TYPE.VARIABLE_NAME, ARGUMENT_TYPE.CLOSURE],
+ enumProvider: commonEnumProviders.variables('global'),
+ }),
],
helpString: `
@@ -1344,13 +1446,19 @@ export function registerVariableCommands() {
`,
}));
- SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'add',
+ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
+ name: 'add',
callback: addValuesCallback,
returns: 'sum of the provided values',
unnamedArgumentList: [
- new SlashCommandArgument(
- 'values', [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.VARIABLE_NAME], true, true,
- ),
+ SlashCommandArgument.fromProps({
+ description: 'values to sum',
+ typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.VARIABLE_NAME],
+ isRequired: true,
+ acceptsMultiple: true,
+ enumProvider: commonEnumProviders.variables('all'),
+ forceEnum: false,
+ }),
],
helpString: `
@@ -1367,16 +1475,19 @@ export function registerVariableCommands() {
`,
}));
- SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'mul',
+ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
+ name: 'mul',
callback: (args, value) => mulValuesCallback(args, value),
- result: 'product of the provided values',
+ returns: 'product of the provided values',
unnamedArgumentList: [
- new SlashCommandArgument(
- 'values to multiply',
- [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.VARIABLE_NAME],
- true,
- true,
- ),
+ SlashCommandArgument.fromProps({
+ description: 'values to multiply',
+ typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.VARIABLE_NAME],
+ isRequired: true,
+ acceptsMultiple: true,
+ enumProvider: commonEnumProviders.variables('all'),
+ forceEnum: false,
+ }),
],
helpString: `
@@ -1392,16 +1503,19 @@ export function registerVariableCommands() {
`,
}));
- SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'max',
+ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
+ name: 'max',
callback: maxValuesCallback,
returns: 'maximum value of the set of values',
unnamedArgumentList: [
- new SlashCommandArgument(
- 'values',
- [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.VARIABLE_NAME],
- true,
- true,
- ),
+ SlashCommandArgument.fromProps({
+ description: 'values to find the max',
+ typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.VARIABLE_NAME],
+ isRequired: true,
+ acceptsMultiple: true,
+ enumProvider: commonEnumProviders.variables('all'),
+ forceEnum: false,
+ }),
],
helpString: `
@@ -1417,13 +1531,19 @@ export function registerVariableCommands() {
`,
}));
- SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'min',
+ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
+ name: 'min',
callback: minValuesCallback,
returns: 'minimum value of the set of values',
unnamedArgumentList: [
- new SlashCommandArgument(
- 'values', [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.VARIABLE_NAME], true, true,
- ),
+ SlashCommandArgument.fromProps({
+ description: 'values to find the min',
+ typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.VARIABLE_NAME],
+ isRequired: true,
+ acceptsMultiple: true,
+ enumProvider: commonEnumProviders.variables('all'),
+ forceEnum: false,
+ }),
],
helpString: `
@@ -1440,13 +1560,19 @@ export function registerVariableCommands() {
`,
}));
- SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'sub',
+ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
+ name: 'sub',
callback: subValuesCallback,
returns: 'difference of the provided values',
unnamedArgumentList: [
- new SlashCommandArgument(
- 'values', [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.VARIABLE_NAME], true, true,
- ),
+ SlashCommandArgument.fromProps({
+ description: 'values to find the difference',
+ typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.VARIABLE_NAME],
+ isRequired: true,
+ acceptsMultiple: true,
+ enumProvider: commonEnumProviders.variables('all'),
+ forceEnum: false,
+ }),
],
helpString: `
@@ -1463,16 +1589,25 @@ export function registerVariableCommands() {
`,
}));
- SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'div',
+ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
+ name: 'div',
callback: divValuesCallback,
returns: 'result of division',
unnamedArgumentList: [
- new SlashCommandArgument(
- 'dividend', [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.VARIABLE_NAME], true,
- ),
- new SlashCommandArgument(
- 'divisor', [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.VARIABLE_NAME], true,
- ),
+ SlashCommandArgument.fromProps({
+ description: 'dividend',
+ typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.VARIABLE_NAME],
+ isRequired: true,
+ enumProvider: commonEnumProviders.variables('all'),
+ forceEnum: false,
+ }),
+ SlashCommandArgument.fromProps({
+ description: 'divisor',
+ typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.VARIABLE_NAME],
+ isRequired: true,
+ enumProvider: commonEnumProviders.variables('all'),
+ forceEnum: false,
+ }),
],
helpString: `
@@ -1489,16 +1624,25 @@ export function registerVariableCommands() {
`,
}));
- SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'mod',
+ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
+ name: 'mod',
callback: modValuesCallback,
returns: 'result of modulo operation',
unnamedArgumentList: [
- new SlashCommandArgument(
- 'dividend', [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.VARIABLE_NAME], true,
- ),
- new SlashCommandArgument(
- 'divisor', [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.VARIABLE_NAME], true,
- ),
+ SlashCommandArgument.fromProps({
+ description: 'dividend',
+ typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.VARIABLE_NAME],
+ isRequired: true,
+ enumProvider: commonEnumProviders.variables('all'),
+ forceEnum: false,
+ }),
+ SlashCommandArgument.fromProps({
+ description: 'divisor',
+ typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.VARIABLE_NAME],
+ isRequired: true,
+ enumProvider: commonEnumProviders.variables('all'),
+ forceEnum: false,
+ }),
],
helpString: `
@@ -1515,16 +1659,25 @@ export function registerVariableCommands() {
`,
}));
- SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'pow',
+ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
+ name: 'pow',
callback: powValuesCallback,
returns: 'result of power operation',
unnamedArgumentList: [
- new SlashCommandArgument(
- 'base', [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.VARIABLE_NAME], true,
- ),
- new SlashCommandArgument(
- 'exponent', [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.VARIABLE_NAME], true,
- ),
+ SlashCommandArgument.fromProps({
+ description: 'base',
+ typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.VARIABLE_NAME],
+ isRequired: true,
+ enumProvider: commonEnumProviders.variables('all'),
+ forceEnum: false,
+ }),
+ SlashCommandArgument.fromProps({
+ description: 'exponent',
+ typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.VARIABLE_NAME],
+ isRequired: true,
+ enumProvider: commonEnumProviders.variables('all'),
+ forceEnum: false,
+ }),
],
helpString: `
@@ -1541,13 +1694,18 @@ export function registerVariableCommands() {
`,
}));
- SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'sin',
+ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
+ name: 'sin',
callback: sinValuesCallback,
returns: 'sine of the provided value',
unnamedArgumentList: [
- new SlashCommandArgument(
- 'value', [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.VARIABLE_NAME], true,
- ),
+ SlashCommandArgument.fromProps({
+ description: 'value',
+ typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.VARIABLE_NAME],
+ isRequired: true,
+ enumProvider: commonEnumProviders.variables('all'),
+ forceEnum: false,
+ }),
],
helpString: `
@@ -1564,13 +1722,18 @@ export function registerVariableCommands() {
`,
}));
- SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'cos',
+ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
+ name: 'cos',
callback: cosValuesCallback,
returns: 'cosine of the provided value',
unnamedArgumentList: [
- new SlashCommandArgument(
- 'value', [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.VARIABLE_NAME], true,
- ),
+ SlashCommandArgument.fromProps({
+ description: 'value',
+ typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.VARIABLE_NAME],
+ isRequired: true,
+ enumProvider: commonEnumProviders.variables('all'),
+ forceEnum: false,
+ }),
],
helpString: `
@@ -1587,14 +1750,19 @@ export function registerVariableCommands() {
`,
}));
- SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'log',
+ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
+ name: 'log',
callback: logValuesCallback,
returns: 'log of the provided value',
namedArgumentList: [],
unnamedArgumentList: [
- new SlashCommandArgument(
- 'value', [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.VARIABLE_NAME], true,
- ),
+ SlashCommandArgument.fromProps({
+ description: 'value',
+ typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.VARIABLE_NAME],
+ isRequired: true,
+ enumProvider: commonEnumProviders.variables('all'),
+ forceEnum: false,
+ }),
],
helpString: `
@@ -1611,13 +1779,18 @@ export function registerVariableCommands() {
`,
}));
- SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'abs',
+ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
+ name: 'abs',
callback: absValuesCallback,
returns: 'absolute value of the provided value',
unnamedArgumentList: [
- new SlashCommandArgument(
- 'value', [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.VARIABLE_NAME], true,
- ),
+ SlashCommandArgument.fromProps({
+ description: 'value',
+ typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.VARIABLE_NAME],
+ isRequired: true,
+ enumProvider: commonEnumProviders.variables('all'),
+ forceEnum: false,
+ }),
],
helpString: `
@@ -1634,13 +1807,18 @@ export function registerVariableCommands() {
`,
}));
- SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'sqrt',
+ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
+ name: 'sqrt',
callback: sqrtValuesCallback,
returns: 'square root of the provided value',
unnamedArgumentList: [
- new SlashCommandArgument(
- 'value', [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.VARIABLE_NAME], true,
- ),
+ SlashCommandArgument.fromProps({
+ description: 'value',
+ typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.VARIABLE_NAME],
+ isRequired: true,
+ enumProvider: commonEnumProviders.variables('all'),
+ forceEnum: false,
+ }),
],
helpString: `
@@ -1657,13 +1835,18 @@ export function registerVariableCommands() {
`,
}));
- SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'round',
+ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
+ name: 'round',
callback: roundValuesCallback,
returns: 'rounded value',
unnamedArgumentList: [
- new SlashCommandArgument(
- 'value', [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.VARIABLE_NAME], true,
- ),
+ SlashCommandArgument.fromProps({
+ description: 'value',
+ typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.VARIABLE_NAME],
+ isRequired: true,
+ enumProvider: commonEnumProviders.variables('all'),
+ forceEnum: false,
+ }),
],
helpString: `
@@ -1680,13 +1863,18 @@ export function registerVariableCommands() {
`,
}));
- SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'len',
- callback: (_, value) => lenValuesCallback(value),
+ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
+ name: 'len',
+ callback: (_, value) => String(lenValuesCallback(value)),
returns: 'length of the provided value',
unnamedArgumentList: [
- new SlashCommandArgument(
- 'value', [ARGUMENT_TYPE.STRING, ARGUMENT_TYPE.VARIABLE_NAME], true,
- ),
+ SlashCommandArgument.fromProps({
+ description: 'value',
+ typeList: [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.VARIABLE_NAME],
+ isRequired: true,
+ enumProvider: commonEnumProviders.variables('all'),
+ forceEnum: false,
+ }),
],
helpString: `
@@ -1702,8 +1890,9 @@ export function registerVariableCommands() {
`,
}));
- SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'rand',
- callback: (args, value) => randValuesCallback(Number(args.from ?? 0), Number(args.to ?? (value.length ? value : 1)), args),
+ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
+ name: 'rand',
+ callback: (args, value) => String(randValuesCallback(Number(args.from ?? 0), Number(args.to ?? (value ? value : 1)), args)),
returns: 'random number',
namedArgumentList: [
new SlashCommandNamedArgument(
@@ -1755,13 +1944,18 @@ export function registerVariableCommands() {
`,
}));
- SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'var',
- callback: (args, value) => varCallback(args, value),
+ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
+ name: 'var',
+ callback: (/** @type {NamedArguments} */ args, value) => varCallback(args, value),
returns: 'the variable value',
namedArgumentList: [
- new SlashCommandNamedArgument(
- 'key', 'variable name; forces setting the variable, even if no value is provided', [ARGUMENT_TYPE.VARIABLE_NAME], false,
- ),
+ SlashCommandNamedArgument.fromProps({
+ name: 'key',
+ description: 'variable name; forces setting the variable, even if no value is provided',
+ typeList: [ARGUMENT_TYPE.VARIABLE_NAME],
+ enumProvider: commonEnumProviders.variables('scope'),
+ forceEnum: false,
+ }),
new SlashCommandNamedArgument(
'index',
'optional index for list or dictionary',
@@ -1771,12 +1965,12 @@ export function registerVariableCommands() {
),
],
unnamedArgumentList: [
- new SlashCommandArgument(
- 'variable name',
- [ARGUMENT_TYPE.VARIABLE_NAME],
- false, // isRequired
- false, // acceptsMultiple
- ),
+ SlashCommandArgument.fromProps({
+ description: 'variable name',
+ typeList: [ARGUMENT_TYPE.VARIABLE_NAME],
+ enumProvider: commonEnumProviders.variables('scope'),
+ forceEnum: false,
+ }),
new SlashCommandArgument(
'variable value',
[ARGUMENT_TYPE.STRING, ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.BOOLEAN, ARGUMENT_TYPE.LIST, ARGUMENT_TYPE.DICTIONARY, ARGUMENT_TYPE.CLOSURE],
@@ -1802,18 +1996,26 @@ export function registerVariableCommands() {