diff --git a/public/scripts/authors-note.js b/public/scripts/authors-note.js index fbc249aa2..a0307afee 100644 --- a/public/scripts/authors-note.js +++ b/public/scripts/authors-note.js @@ -12,6 +12,9 @@ import { extension_settings, getContext, saveMetadataDebounced } from './extensi import { registerSlashCommand } from './slash-commands.js'; import { getCharaFilename, debounce, delay } from './utils.js'; import { getTokenCountAsync } from './tokenizers.js'; +import { SlashCommandParser } from './slash-commands/SlashCommandParser.js'; +import { SlashCommand } from './slash-commands/SlashCommand.js'; +import { ARGUMENT_TYPE, SlashCommandArgument } from './slash-commands/SlashCommandArgument.js'; export { MODULE_NAME as NOTE_MODULE_NAME }; const MODULE_NAME = '2_floating_prompt'; // <= Deliberate, for sorting lower than memory @@ -454,9 +457,59 @@ export function initAuthorsNote() { }); $('#option_toggle_AN').on('click', onANMenuItemClick); - registerSlashCommand('note', setNoteTextCommand, [], '(text) – sets an author\'s note for the currently selected chat', true, true); - registerSlashCommand('depth', setNoteDepthCommand, [], '(number) – sets an author\'s note depth for in-chat positioning', true, true); - registerSlashCommand('freq', setNoteIntervalCommand, ['interval'], '(number) – sets an author\'s note insertion frequency', true, true); - registerSlashCommand('pos', setNotePositionCommand, ['position'], '(chat or scenario) – sets an author\'s note position', true, true); + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'note', + callback: setNoteTextCommand, + unnamedArgumentList: [ + new SlashCommandArgument( + 'text', [ARGUMENT_TYPE.STRING], true, + ), + ], + helpString: ` +
+ Sets an author's note for the currently selected chat. +
+ `, + })); + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'depth', + callback: setNoteDepthCommand, + unnamedArgumentList: [ + new SlashCommandArgument( + 'number', [ARGUMENT_TYPE.NUMBER], true, + ), + ], + helpString: ` +
+ Sets an author's note depth for in-chat positioning. +
+ `, + })); + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'freq', + callback: setNoteIntervalCommand, + namedArgumentList: [], + unnamedArgumentList: [ + new SlashCommandArgument( + 'number', [ARGUMENT_TYPE.NUMBER], true, + ), + ], + helpString: ` +
+ Sets an author's note insertion frequency. +
+ `, + })); + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'pos', + callback: setNotePositionCommand, + namedArgumentList: [], + unnamedArgumentList: [ + new SlashCommandArgument( + 'position', [ARGUMENT_TYPE.STRING], true, false, null, ['chat', 'scenario'], + ), + ], + helpString: ` +
+ Sets an author's note position. +
+ `, + })); eventSource.on(event_types.CHAT_CHANGED, onChatChanged); } diff --git a/public/scripts/backgrounds.js b/public/scripts/backgrounds.js index e462cb8f7..b3c1356c0 100644 --- a/public/scripts/backgrounds.js +++ b/public/scripts/backgrounds.js @@ -1,6 +1,8 @@ import { callPopup, chat_metadata, eventSource, event_types, generateQuietPrompt, getCurrentChatId, getRequestHeaders, getThumbnailUrl, saveSettingsDebounced } from '../script.js'; import { saveMetadataDebounced } from './extensions.js'; import { registerSlashCommand } from './slash-commands.js'; +import { SlashCommand } from './slash-commands/SlashCommand.js'; +import { SlashCommandParser } from './slash-commands/SlashCommandParser.js'; import { stringFormat } from './utils.js'; const BG_METADATA_KEY = 'custom_background'; @@ -481,7 +483,26 @@ export function initBackgrounds() { $('#auto_background').on('click', autoBackgroundCommand); $('#add_bg_button').on('change', onBackgroundUploadSelected); $('#bg-filter').on('input', onBackgroundFilterInput); - registerSlashCommand('lockbg', onLockBackgroundClick, ['bglock'], '– locks a background for the currently selected chat', true, true); - registerSlashCommand('unlockbg', onUnlockBackgroundClick, ['bgunlock'], '– unlocks a background for the currently selected chat', true, true); - registerSlashCommand('autobg', autoBackgroundCommand, ['bgauto'], '– automatically changes the background based on the chat context using the AI request prompt', true, true); + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'lockbg', + callback: onLockBackgroundClick, + aliases: ['bglock'], + helpString: 'Locks a background for the currently selected chat', + interruptsGeneration: true, + purgeFromMessage: true, + })); + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'unlockbg', + callback: onUnlockBackgroundClick, + aliases: ['bgunlock'], + helpString: 'Unlocks a background for the currently selected chat', + interruptsGeneration: true, + purgeFromMessage: true, + })); + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'autobg', + callback: autoBackgroundCommand, + aliases: ['bgauto'], + helpString: 'Automatically changes the background based on the chat context using the AI request prompt', + interruptsGeneration: true, + purgeFromMessage: true, + })); + } diff --git a/public/scripts/extensions/attachments/index.js b/public/scripts/extensions/attachments/index.js index ab3db9419..26e16cbaa 100644 --- a/public/scripts/extensions/attachments/index.js +++ b/public/scripts/extensions/attachments/index.js @@ -1,9 +1,19 @@ import { renderExtensionTemplateAsync } from '../../extensions.js'; import { registerSlashCommand } from '../../slash-commands.js'; +import { SlashCommand } from '../../slash-commands/SlashCommand.js'; +import { SlashCommandParser } from '../../slash-commands/SlashCommandParser.js'; jQuery(async () => { const buttons = await renderExtensionTemplateAsync('attachments', 'buttons', {}); $('#extensionsMenu').prepend(buttons); - registerSlashCommand('db', () => document.getElementById('manageAttachments')?.click(), ['databank', 'data-bank'], '– open the data bank', true, true); + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ + name: 'db', + callback: () => document.getElementById('manageAttachments')?.click(), + aliases: ['databank', 'data-bank'], + helpString: 'Open the data bank', + interruptsGeneration: true, + purgeFromMessage: true, + })); + }); diff --git a/public/scripts/extensions/caption/index.js b/public/scripts/extensions/caption/index.js index 572d02217..f197ba154 100644 --- a/public/scripts/extensions/caption/index.js +++ b/public/scripts/extensions/caption/index.js @@ -6,6 +6,9 @@ import { SECRET_KEYS, secret_state } from '../../secrets.js'; import { getMultimodalCaption } from '../shared.js'; import { textgen_types, textgenerationwebui_settings } from '../../textgen-settings.js'; import { registerSlashCommand } from '../../slash-commands.js'; +import { SlashCommandParser } from '../../slash-commands/SlashCommandParser.js'; +import { SlashCommand } from '../../slash-commands/SlashCommand.js'; +import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from '../../slash-commands/SlashCommandArgument.js'; export { MODULE_NAME }; const MODULE_NAME = 'caption'; @@ -492,5 +495,28 @@ jQuery(function () { saveSettingsDebounced(); }); - registerSlashCommand('caption', captionCommandCallback, [], 'quiet=true/false [prompt] - caption an image with an optional prompt and passes the caption down the pipe. Only multimodal sources support custom prompts. Set the "quiet" argument to true to suppress sending a captioned message, default: false.', true, true); + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'caption', + callback: captionCommandCallback, + namedArgumentList: [ + new SlashCommandNamedArgument( + 'quiet', 'suppress sending a captioned message', [ARGUMENT_TYPE.BOOLEAN], false, false, 'false', ['true', 'false'], + ), + ], + unnamedArgumentList: [ + new SlashCommandArgument( + 'prompt', [ARGUMENT_TYPE.STRING], false, + ), + ], + helpString: ` +
+ Caption an image with an optional prompt and passes the caption down the pipe. +
+
+ Only multimodal sources support custom prompts. +
+
+ Set the "quiet" argument to true to suppress sending a captioned message, default: false. +
+ `, + })); }); diff --git a/public/scripts/extensions/expressions/index.js b/public/scripts/extensions/expressions/index.js index 8ff0ac935..abe3bf249 100644 --- a/public/scripts/extensions/expressions/index.js +++ b/public/scripts/extensions/expressions/index.js @@ -6,6 +6,9 @@ import { registerSlashCommand } from '../../slash-commands.js'; import { onlyUnique, debounce, getCharaFilename, trimToEndSentence, trimToStartSentence } from '../../utils.js'; import { hideMutedSprites } from '../../group-chats.js'; import { isJsonSchemaSupported } from '../../textgen-settings.js'; +import { SlashCommandParser } from '../../slash-commands/SlashCommandParser.js'; +import { SlashCommand } from '../../slash-commands/SlashCommand.js'; +import { ARGUMENT_TYPE, SlashCommandArgument } from '../../slash-commands/SlashCommandArgument.js'; export { MODULE_NAME }; const MODULE_NAME = 'expressions'; @@ -1965,9 +1968,68 @@ function migrateSettings() { }); eventSource.on(event_types.MOVABLE_PANELS_RESET, updateVisualNovelModeDebounced); eventSource.on(event_types.GROUP_UPDATED, updateVisualNovelModeDebounced); - registerSlashCommand('sprite', setSpriteSlashCommand, ['emote'], '(spriteId) – force sets the sprite for the current character', true, true); - registerSlashCommand('spriteoverride', setSpriteSetCommand, ['costume'], '(optional folder) – sets an override sprite folder for the current character. If the name starts with a slash or a backslash, selects a sub-folder in the character-named folder. Empty value to reset to default.', true, true); - registerSlashCommand('lastsprite', (_, value) => lastExpression[value.trim()] ?? '', [], '(charName) – Returns the last set sprite / expression for the named character.', true, true); - registerSlashCommand('th', toggleTalkingHeadCommand, ['talkinghead'], '– Character Expressions: toggles Image Type - talkinghead (extras) on/off.', true, true); - registerSlashCommand('classify', classifyCommand, [], '(text) – performs an emotion classification of the given text and returns a label.', true, true); + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'sprite', + aliases: ['emote'], + callback: setSpriteSlashCommand, + unnamedArgumentList: [ + new SlashCommandArgument( + 'spriteId', [ARGUMENT_TYPE.STRING], true, + ), + ], + helpString: 'Force sets the sprite for the current character.', + interruptsGeneration: true, + purgeFromMessage: true, + })); + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'spriteoverride', + aliases: ['costume'], + callback: setSpriteSetCommand, + unnamedArgumentList: [ + new SlashCommandArgument( + 'optional folder', [ARGUMENT_TYPE.STRING], false, + ), + ], + helpString: 'Sets an override sprite folder for the current character. If the name starts with a slash or a backslash, selects a sub-folder in the character-named folder. Empty value to reset to default.', + interruptsGeneration: true, + purgeFromMessage: true, + })); + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'lastsprite', + callback: (_, value) => lastExpression[value.trim()] ?? '', + unnamedArgumentList: [ + new SlashCommandArgument( + 'charName', [ARGUMENT_TYPE.STRING], true, + ), + ], + helpString: 'Returns the last set sprite / expression for the named character.', + interruptsGeneration: true, + purgeFromMessage: true, + })); + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'th', + callback: toggleTalkingHeadCommand, + aliases: ['talkinghead'], + helpString: 'Character Expressions: toggles Image Type - talkinghead (extras) on/off.', + interruptsGeneration: true, + purgeFromMessage: true, + })); + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'classify', + callback: classifyCommand, + unnamedArgumentList: [ + new SlashCommandArgument( + 'text', [ARGUMENT_TYPE.STRING], true, + ), + ], + returns: 'emotion classification label for the given text', + helpString: ` +
+ Performs an emotion classification of the given text and returns a label. +
+
+ Example: + +
+ `, + })); })(); diff --git a/public/scripts/extensions/gallery/index.js b/public/scripts/extensions/gallery/index.js index 06d62d0a4..c1a7089f5 100644 --- a/public/scripts/extensions/gallery/index.js +++ b/public/scripts/extensions/gallery/index.js @@ -9,6 +9,9 @@ import { loadFileToDocument, delay } from '../../utils.js'; import { loadMovingUIState } from '../../power-user.js'; import { dragElement } from '../../RossAscends-mods.js'; import { registerSlashCommand } from '../../slash-commands.js'; +import { SlashCommandParser } from '../../slash-commands/SlashCommandParser.js'; +import { SlashCommand } from '../../slash-commands/SlashCommand.js'; +import { ARGUMENT_TYPE, SlashCommandNamedArgument } from '../../slash-commands/SlashCommandArgument.js'; const extensionName = 'gallery'; const extensionFolderPath = `scripts/extensions/${extensionName}/`; @@ -415,8 +418,29 @@ function viewWithDragbox(items) { // Registers a simple command for opening the char gallery. -registerSlashCommand('show-gallery', showGalleryCommand, ['sg'], '– shows the gallery', true, true); -registerSlashCommand('list-gallery', listGalleryCommand, ['lg'], '[optional char=charName] [optional group=groupName] – list images in the gallery of the current char / group or a specified char / group', true, true); +SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'show-gallery', + aliases: ['sg'], + callback: showGalleryCommand, + helpString: 'Shows the gallery.', + interruptsGeneration: true, + purgeFromMessage: true, +})); +SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'list-gallery', + aliases: ['lg'], + callback: listGalleryCommand, + namedArgumentList: [ + new SlashCommandNamedArgument( + 'char', 'character name', [ARGUMENT_TYPE.STRING], false, + ), + new SlashCommandNamedArgument( + 'group', 'group name', [ARGUMENT_TYPE.STRING], false, + ), + ], + helpString: 'List images in the gallery of the current char / group or a specified char / group.', + interruptsGeneration: true, + purgeFromMessage: true, +})); + function showGalleryCommand(args) { showCharGallery(); diff --git a/public/scripts/extensions/memory/index.js b/public/scripts/extensions/memory/index.js index cb6bf95d5..b98311f07 100644 --- a/public/scripts/extensions/memory/index.js +++ b/public/scripts/extensions/memory/index.js @@ -20,6 +20,8 @@ import { registerSlashCommand } from '../../slash-commands.js'; import { loadMovingUIState } from '../../power-user.js'; import { dragElement } from '../../RossAscends-mods.js'; import { getTextTokens, getTokenCountAsync, tokenizers } from '../../tokenizers.js'; +import { SlashCommandParser } from '../../slash-commands/SlashCommandParser.js'; +import { SlashCommand } from '../../slash-commands/SlashCommand.js'; export { MODULE_NAME }; const MODULE_NAME = '1_memory'; @@ -864,5 +866,10 @@ jQuery(async function () { eventSource.on(event_types.MESSAGE_EDITED, onChatEvent); eventSource.on(event_types.MESSAGE_SWIPED, onChatEvent); eventSource.on(event_types.CHAT_CHANGED, onChatEvent); - registerSlashCommand('summarize', forceSummarizeChat, [], '– forces the summarization of the current chat using the Main API', true, true); + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'summarize', + callback: forceSummarizeChat, + helpString: 'Forces the summarization of the current chat using the Main API.', + interruptsGeneration: true, + purgeFromMessage: true, + })); }); diff --git a/public/scripts/extensions/quick-reply/src/SlashCommandHandler.js b/public/scripts/extensions/quick-reply/src/SlashCommandHandler.js index 45c120dcf..c22782103 100644 --- a/public/scripts/extensions/quick-reply/src/SlashCommandHandler.js +++ b/public/scripts/extensions/quick-reply/src/SlashCommandHandler.js @@ -1,4 +1,7 @@ import { registerSlashCommand } from '../../../slash-commands.js'; +import { SlashCommand } from '../../../slash-commands/SlashCommand.js'; +import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from '../../../slash-commands/SlashCommandArgument.js'; +import { SlashCommandParser } from '../../../slash-commands/SlashCommandParser.js'; import { isTrueBoolean } from '../../../utils.js'; // eslint-disable-next-line no-unused-vars import { QuickReplyApi } from '../api/QuickReplyApi.js'; @@ -17,46 +20,347 @@ export class SlashCommandHandler { init() { - registerSlashCommand('qr', (_, value) => this.executeQuickReplyByIndex(Number(value)), [], '(number) – activates the specified Quick Reply', true, true); - registerSlashCommand('qrset', ()=>toastr.warning('The command /qrset has been deprecated. Use /qr-set, /qr-set-on, and /qr-set-off instead.'), [], 'DEPRECATED – The command /qrset has been deprecated. Use /qr-set, /qr-set-on, and /qr-set-off instead.', true, true); - registerSlashCommand('qr-set', (args, value)=>this.toggleGlobalSet(value, args), [], '[visible=true] (number) – toggle global QR set', true, true); - registerSlashCommand('qr-set-on', (args, value)=>this.addGlobalSet(value, args), [], '[visible=true] (number) – activate global QR set', true, true); - registerSlashCommand('qr-set-off', (_, value)=>this.removeGlobalSet(value), [], '(number) – deactivate global QR set', true, true); - registerSlashCommand('qr-chat-set', (args, value)=>this.toggleChatSet(value, args), [], '[visible=true] (number) – toggle chat QR set', true, true); - registerSlashCommand('qr-chat-set-on', (args, value)=>this.addChatSet(value, args), [], '[visible=true] (number) – activate chat QR set', true, true); - registerSlashCommand('qr-chat-set-off', (_, value)=>this.removeChatSet(value), [], '(number) – deactivate chat QR set', true, true); - registerSlashCommand('qr-set-list', (_, value)=>this.listSets(value ?? 'all'), [], '(all|global|chat) – gets a list of the names of all quick reply sets', true, true); - registerSlashCommand('qr-list', (_, value)=>this.listQuickReplies(value), [], '(set name) – gets a list of the names of all quick replies in this quick reply set', true, true); + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'qr', + callback: (_, value) => this.executeQuickReplyByIndex(Number(value)), + unnamedArgumentList: [ + new SlashCommandArgument( + 'number', [ARGUMENT_TYPE.NUMBER], true, + ), + ], + helpString: 'Activates the specified Quick Reply', + })); + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'qrset', + callback: () => toastr.warning('The command /qrset has been deprecated. Use /qr-set, /qr-set-on, and /qr-set-off instead.'), + helpString: 'DEPRECATED – The command /qrset has been deprecated. Use /qr-set, /qr-set-on, and /qr-set-off instead.', + })); + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'qr-set', + callback: (args, value) => this.toggleGlobalSet(value, args), + namedArgumentList: [ + new SlashCommandNamedArgument( + 'visible', 'set visibility', [ARGUMENT_TYPE.BOOLEAN], false, false, 'true', + ), + ], + unnamedArgumentList: [ + new SlashCommandArgument( + 'QR set name', [ARGUMENT_TYPE.STRING], true, + ), + ], + helpString: 'Toggle global QR set', + interruptsGeneration: true, + purgeFromMessage: true, + })); + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'qr-set-on', + callback: (args, value) => this.addGlobalSet(value, args), + namedArgumentList: [ + new SlashCommandNamedArgument( + 'visible', 'set visibility', [ARGUMENT_TYPE.BOOLEAN], false, false, 'true', + ), + ], + unnamedArgumentList: [ + new SlashCommandArgument( + 'QR set name', [ARGUMENT_TYPE.STRING], true, + ), + ], + helpString: 'Activate global QR set', + interruptsGeneration: true, + purgeFromMessage: true, + })); + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'qr-set-off', + callback: (_, value) => this.removeGlobalSet(value), + unnamedArgumentList: [ + new SlashCommandArgument( + 'QR set name', [ARGUMENT_TYPE.STRING], true, + ), + ], + helpString: 'Deactivate global QR set', + interruptsGeneration: true, + purgeFromMessage: true, + })); + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'qr-chat-set', + callback: (args, value) => this.toggleChatSet(value, args), + namedArgumentList: [ + new SlashCommandNamedArgument( + 'visible', 'set visibility', [ARGUMENT_TYPE.BOOLEAN], false, false, 'true', + ), + ], + unnamedArgumentList: [ + new SlashCommandArgument( + 'QR set name', [ARGUMENT_TYPE.STRING], true, + ), + ], + helpString: 'Toggle chat QR set', + interruptsGeneration: true, + purgeFromMessage: true, + })); - const qrArgs = ` - label - string - text on the button, e.g., label=MyButton - set - string - name of the QR set, e.g., set=PresetName1 - hidden - bool - whether the button should be hidden, e.g., hidden=true - startup - bool - auto execute on app startup, e.g., startup=true - user - bool - auto execute on user message, e.g., user=true - bot - bool - auto execute on AI message, e.g., bot=true - load - bool - auto execute on chat load, e.g., load=true - group - bool - auto execute on group member selection, e.g., group=true - title - string - title / tooltip to be shown on button, e.g., title="My Fancy Button" - `.trim(); - const qrUpdateArgs = ` - newlabel - string - new text for the button, e.g. newlabel=MyRenamedButton - ${qrArgs} - `.trim(); - registerSlashCommand('qr-create', (args, message)=>this.createQuickReply(args, message), [], `[arguments] (message)\n arguments:\n ${qrArgs} – creates a new Quick Reply, example: /qr-create set=MyPreset label=MyButton /echo 123`, true, true); - registerSlashCommand('qr-update', (args, message)=>this.updateQuickReply(args, message), [], `[arguments] (message)\n arguments:\n ${qrUpdateArgs} – updates Quick Reply, example: /qr-update set=MyPreset label=MyButton newlabel=MyRenamedButton /echo 123`, true, true); - registerSlashCommand('qr-delete', (args, name)=>this.deleteQuickReply(args, name), [], 'set=string [label] – deletes Quick Reply', true, true); - registerSlashCommand('qr-contextadd', (args, name)=>this.createContextItem(args, name), [], 'set=string label=string [chain=false] (preset name) – add context menu preset to a QR, example: /qr-contextadd set=MyPreset label=MyButton chain=true MyOtherPreset', true, true); - registerSlashCommand('qr-contextdel', (args, name)=>this.deleteContextItem(args, name), [], 'set=string label=string (preset name) – remove context menu preset from a QR, example: /qr-contextdel set=MyPreset label=MyButton MyOtherPreset', true, true); - registerSlashCommand('qr-contextclear', (args, label)=>this.clearContextMenu(args, label), [], 'set=string (label) – remove all context menu presets from a QR, example: /qr-contextclear set=MyPreset MyButton', true, true); - const presetArgs = ` - nosend - bool - disable send / insert in user input (invalid for slash commands) - before - bool - place QR before user input - inject - bool - inject user input automatically (if disabled use {{input}}) - `.trim(); - registerSlashCommand('qr-set-create', (args, name)=>this.createSet(name, args), ['qr-presetadd'], `[arguments] (name)\n arguments:\n ${presetArgs} – create a new preset (overrides existing ones), example: /qr-set-add MyNewPreset`, true, true); - registerSlashCommand('qr-set-update', (args, name)=>this.updateSet(name, args), ['qr-presetupdate'], `[arguments] (name)\n arguments:\n ${presetArgs} – update an existing preset, example: /qr-set-update enabled=false MyPreset`, true, true); - registerSlashCommand('qr-set-delete', (args, name)=>this.deleteSet(name), ['qr-presetdelete'], `(name)\n arguments:\n ${presetArgs} – delete an existing preset, example: /qr-set-delete MyPreset`, true, true); + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'qr-chat-set-on', + callback: (args, value) => this.addChatSet(value, args), + namedArgumentList: [ + new SlashCommandNamedArgument( + 'visible', 'whether the QR set should be visible', [ARGUMENT_TYPE.BOOLEAN], false, false, 'true', ['true', 'false'], + ), + ], + unnamedArgumentList: [ + new SlashCommandArgument( + 'QR set name', [ARGUMENT_TYPE.STRING], true, + ), + ], + helpString: 'Activate chat QR set', + interruptsGeneration: true, + purgeFromMessage: true, + })); + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'qr-chat-set-off', + callback: (_, value) => this.removeChatSet(value), + unnamedArgumentList: [ + new SlashCommandArgument( + 'QR set name', [ARGUMENT_TYPE.STRING], true, + ), + ], + helpString: 'Deactivate chat QR set', + interruptsGeneration: true, + purgeFromMessage: true, + })); + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'qr-set-list', + callback: (_, value) => this.listSets(value ?? 'all'), + namedArgumentList: [], + unnamedArgumentList: [ + new SlashCommandArgument( + 'set type', [ARGUMENT_TYPE.STRING], false, false, null, ['all', 'global', 'chat'], + ), + ], + helpString: 'Gets a list of the names of all quick reply sets.', + })); + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'qr-list', + callback: (_, value) => this.listQuickReplies(value), + namedArgumentList: [], + unnamedArgumentList: [ + new SlashCommandArgument( + 'set name', [ARGUMENT_TYPE.STRING], true, + ), + ], + helpString: 'Gets a list of the names of all quick replies in this quick reply set.', + })); + + const qrArgs = [ + new SlashCommandNamedArgument('label', 'text on the button, e.g., label=MyButton', [ARGUMENT_TYPE.STRING]), + new SlashCommandNamedArgument('set', 'name of the QR set, e.g., set=PresetName1', [ARGUMENT_TYPE.STRING]), + new SlashCommandNamedArgument('hidden', 'whether the button should be hidden, e.g., hidden=true', [ARGUMENT_TYPE.BOOLEAN], false, false, 'false'), + new SlashCommandNamedArgument('startup', 'auto execute on app startup, e.g., startup=true', [ARGUMENT_TYPE.BOOLEAN], false, false, 'false'), + new SlashCommandNamedArgument('user', 'auto execute on user message, e.g., user=true', [ARGUMENT_TYPE.BOOLEAN], false, false, 'false'), + new SlashCommandNamedArgument('bot', 'auto execute on AI message, e.g., bot=true', [ARGUMENT_TYPE.BOOLEAN], false, false, 'false'), + new SlashCommandNamedArgument('load', 'auto execute on chat load, e.g., load=true', [ARGUMENT_TYPE.BOOLEAN], false, false, 'false'), + new SlashCommandNamedArgument('group', 'auto execute on group member selection, e.g., group=true', [ARGUMENT_TYPE.BOOLEAN], false, false, 'false'), + new SlashCommandNamedArgument('title', 'title / tooltip to be shown on button, e.g., title="My Fancy Button"', [ARGUMENT_TYPE.STRING], false), + ]; + const qrUpdateArgs = [ + new SlashCommandNamedArgument('newlabel', 'new text for the button', [ARGUMENT_TYPE.STRING], false), + ]; + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'qr-create', + callback: (args, message) => this.createQuickReply(args, message), + namedArgumentList: qrArgs, + unnamedArgumentList: [ + new SlashCommandArgument( + 'command', [ARGUMENT_TYPE.STRING], true, + ), + ], + helpString: ` +
Creates a new Quick Reply.
+
+ Example: + +
+
+ Arguments: +
${qrArgs}
+
+ `, + })); + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'qr-update', + callback: (args, message) => this.updateQuickReply(args, message), + returns: 'updated quick reply', + namedArgumentList: [...qrUpdateArgs, ...qrArgs], + helpString: ` +
+ Updates Quick Reply. +
+
+ Example: + +
+ `, + })); + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'qr-delete', + callback: (args, name) => this.deleteQuickReply(args, name), + namedArgumentList: [ + new SlashCommandNamedArgument( + 'set', 'Quick Reply set', [ARGUMENT_TYPE.STRING], true, + ), + new SlashCommandNamedArgument( + 'label', 'Quick Reply label', [ARGUMENT_TYPE.STRING], false, + ), + ], + helpString: 'Deletes a Quick Reply from the specified set. If no label is provided, the entire set is deleted.', + })); + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'qr-contextadd', + callback: (args, name) => this.createContextItem(args, name), + namedArgumentList: [ + new SlashCommandNamedArgument( + 'set', 'string', [ARGUMENT_TYPE.STRING], true, + ), + new SlashCommandNamedArgument( + 'label', 'string', [ARGUMENT_TYPE.STRING], true, + ), + new SlashCommandNamedArgument( + 'chain', 'boolean', [ARGUMENT_TYPE.BOOLEAN], false, false, 'false', + ), + ], + unnamedArgumentList: [ + new SlashCommandArgument( + 'preset name', [ARGUMENT_TYPE.STRING], true, + ), + ], + helpString: ` +
+ Add context menu preset to a QR. +
+
+ Example: + +
+ `, + })); + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'qr-contextdel', + callback: (args, name) => this.deleteContextItem(args, name), + namedArgumentList: [ + new SlashCommandNamedArgument( + 'set', 'string', [ARGUMENT_TYPE.STRING], true, + ), + new SlashCommandNamedArgument( + 'label', 'string', [ARGUMENT_TYPE.STRING], true, + ), + ], + unnamedArgumentList: [ + new SlashCommandArgument( + 'preset name', [ARGUMENT_TYPE.STRING], true, + ), + ], + helpString: ` +
+ Remove context menu preset from a QR. +
+
+ Example: + +
+ `, + })); + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'qr-contextclear', + callback: (args, label) => this.clearContextMenu(args, label), + namedArgumentList: [ + new SlashCommandNamedArgument( + 'set', 'context menu preset name', [ARGUMENT_TYPE.STRING], true, + ), + ], + unnamedArgumentList: [ + new SlashCommandArgument( + 'label', [ARGUMENT_TYPE.STRING], true, + ), + ], + helpString: ` +
+ Remove all context menu presets from a QR. +
+
+ Example: + +
+ `, + purgeFromMessage: true, + interruptsGeneration: true, + })); + + const presetArgs = [ + new SlashCommandNamedArgument('nosend', 'disable send / insert in user input (invalid for slash commands)', [ARGUMENT_TYPE.BOOLEAN], false), + new SlashCommandNamedArgument('before', 'place QR before user input', [ARGUMENT_TYPE.BOOLEAN], false), + new SlashCommandNamedArgument('inject', 'inject user input automatically (if disabled use {{input}})', [ARGUMENT_TYPE.BOOLEAN], false), + ]; + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'qr-set-create', + callback: (args, name) => this.createSet(name, args), + aliases: ['qr-presetadd'], + namedArgumentList: presetArgs, + unnamedArgumentList: [ + new SlashCommandArgument( + 'name', [ARGUMENT_TYPE.STRING], true, + ), + ], + helpString: ` +
+ Create a new preset (overrides existing ones). +
+
+ Example: + +
+ `, + })); + + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'qr-set-update', + callback: (args, name) => this.updateSet(name, args), + aliases: ['qr-presetupdate'], + namedArgumentList: presetArgs, + unnamedArgumentList: [ + new SlashCommandArgument('name', [ARGUMENT_TYPE.STRING], true), + ], + helpString: ` +
+ Update an existing preset. +
+
+ Example: +
/qr-set-update enabled=false MyPreset
+
+ `, + })); + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'qr-set-delete', + callback: (args, name) => this.deleteSet(name), + aliases: ['qr-presetdelete'], + unnamedArgumentList: [ + new SlashCommandArgument('name', [ARGUMENT_TYPE.STRING], true), + ], + helpString: ` +
+ Delete an existing preset. +
+
+ Example: +
/qr-set-delete MyPreset
+
+ `, + })); } diff --git a/public/scripts/extensions/regex/index.js b/public/scripts/extensions/regex/index.js index 5115af276..fd4714154 100644 --- a/public/scripts/extensions/regex/index.js +++ b/public/scripts/extensions/regex/index.js @@ -1,6 +1,9 @@ import { callPopup, getCurrentChatId, reloadCurrentChat, saveSettingsDebounced } from '../../../script.js'; import { extension_settings, renderExtensionTemplateAsync } from '../../extensions.js'; import { registerSlashCommand } from '../../slash-commands.js'; +import { SlashCommand } from '../../slash-commands/SlashCommand.js'; +import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from '../../slash-commands/SlashCommandArgument.js'; +import { SlashCommandParser } from '../../slash-commands/SlashCommandParser.js'; import { download, getFileText, getSortableDelay, uuidv4 } from '../../utils.js'; import { resolveVariable } from '../../variables.js'; import { regex_placement, runRegexScript } from './engine.js'; @@ -353,5 +356,19 @@ jQuery(async () => { await loadRegexScripts(); $('#saved_regex_scripts').sortable('enable'); - registerSlashCommand('regex', runRegexCallback, [], '(name=scriptName [input]) – runs a Regex extension script by name on the provided string. The script must be enabled.', true, true); + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'regex', + callback: runRegexCallback, + namedArgumentList: [ + new SlashCommandNamedArgument( + 'name', 'script name', [ARGUMENT_TYPE.STRING], true, + ), + ], + unnamedArgumentList: [ + new SlashCommandArgument( + 'input', [ARGUMENT_TYPE.STRING], false, + ), + ], + helpString: 'Runs a Regex extension script by name on the provided string. The script must be enabled.', + })); + }); diff --git a/public/scripts/extensions/stable-diffusion/index.js b/public/scripts/extensions/stable-diffusion/index.js index 88fdbad40..bd49e52bf 100644 --- a/public/scripts/extensions/stable-diffusion/index.js +++ b/public/scripts/extensions/stable-diffusion/index.js @@ -26,6 +26,9 @@ import { SECRET_KEYS, secret_state } from '../../secrets.js'; import { getNovelUnlimitedImageGeneration, getNovelAnlas, loadNovelSubscriptionData } from '../../nai-settings.js'; import { getMultimodalCaption } from '../shared.js'; import { registerSlashCommand } from '../../slash-commands.js'; +import { SlashCommandParser } from '../../slash-commands/SlashCommandParser.js'; +import { SlashCommand } from '../../slash-commands/SlashCommand.js'; +import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from '../../slash-commands/SlashCommandArgument.js'; export { MODULE_NAME }; // Wraps a string into monospace font-face span @@ -3025,8 +3028,42 @@ $('#sd_dropdown [id]').on('click', function () { }); jQuery(async () => { - registerSlashCommand('imagine', generatePicture, ['sd', 'img', 'image'], helpString, true, true); - registerSlashCommand('imagine-comfy-workflow', changeComfyWorkflow, ['icw'], '(workflowName) - change the workflow to be used for image generation with ComfyUI, e.g. /imagine-comfy-workflow MyWorkflow'); + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'imagine', + callback: generatePicture, + aliases: ['sd', 'img', 'image'], + namedArgumentList: [ + new SlashCommandNamedArgument( + 'quiet', 'whether to post the generated image to chat', [ARGUMENT_TYPE.BOOLEAN], false, false, 'false', ['false', 'true'], + ), + ], + unnamedArgumentList: [ + new SlashCommandArgument( + 'argument', [ARGUMENT_TYPE.STRING], false, false, null, Object.values(triggerWords).flat(), + ), + ], + interruptsGeneration: true, + purgeFromMessage: true, + helpString: ` +
+ Requests to generate an image and posts it to chat (unless quiet=true argument is specified). Supported arguments: ${Object.values(triggerWords).flat().join(', ')}. +
+
+ Anything else would trigger a "free mode" to make generate whatever you prompted. Example: /imagine apple tree would generate a picture of an apple tree. Returns a link to the generated image. +
+ `, + })); + + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'imagine-comfy-workflow', + callback: changeComfyWorkflow, + aliases: ['icw'], + unnamedArgumentList: [ + new SlashCommandArgument( + 'workflowName', [ARGUMENT_TYPE.STRING], true, + ), + ], + helpString: '(workflowName) - change the workflow to be used for image generation with ComfyUI, e.g.
/imagine-comfy-workflow MyWorkflow
', + })); + const template = await renderExtensionTemplateAsync('stable-diffusion', 'settings', defaultSettings); $('#extensions_settings').append(template); diff --git a/public/scripts/extensions/token-counter/index.js b/public/scripts/extensions/token-counter/index.js index d9231dac1..91c6f32ef 100644 --- a/public/scripts/extensions/token-counter/index.js +++ b/public/scripts/extensions/token-counter/index.js @@ -1,6 +1,8 @@ import { callPopup, main_api } from '../../../script.js'; import { getContext } from '../../extensions.js'; import { registerSlashCommand } from '../../slash-commands.js'; +import { SlashCommand } from '../../slash-commands/SlashCommand.js'; +import { SlashCommandParser } from '../../slash-commands/SlashCommandParser.js'; import { getFriendlyTokenizerName, getTextTokens, getTokenCountAsync, tokenizers } from '../../tokenizers.js'; import { resetScrollHeight, debounce } from '../../utils.js'; @@ -131,5 +133,11 @@ jQuery(() => { `; $('#extensionsMenu').prepend(buttonHtml); $('#token_counter').on('click', doTokenCounter); - registerSlashCommand('count', doCount, [], '– counts the number of tokens in the current chat', true, false); + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'count', + callback: doCount, + helpString: 'Counts the number of tokens in the current chat.', + interruptsGeneration: true, + purgeFromMessage: false, + })); + }); diff --git a/public/scripts/extensions/tts/index.js b/public/scripts/extensions/tts/index.js index 6203bd213..b64eaf20a 100644 --- a/public/scripts/extensions/tts/index.js +++ b/public/scripts/extensions/tts/index.js @@ -13,6 +13,9 @@ import { OpenAITtsProvider } from './openai.js'; import { XTTSTtsProvider } from './xtts.js'; import { AllTalkTtsProvider } from './alltalk.js'; import { SpeechT5TtsProvider } from './speecht5.js'; +import { SlashCommandParser } from '../../slash-commands/SlashCommandParser.js'; +import { SlashCommand } from '../../slash-commands/SlashCommand.js'; +import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from '../../slash-commands/SlashCommandArgument.js'; export { talkingAnimation }; const UPDATE_INTERVAL = 1000; @@ -1063,6 +1066,37 @@ $(document).ready(function () { eventSource.on(event_types.GROUP_UPDATED, onChatChanged); eventSource.on(event_types.MESSAGE_SENT, onMessageEvent); eventSource.on(event_types.MESSAGE_RECEIVED, onMessageEvent); - registerSlashCommand('speak', onNarrateText, ['narrate', 'tts'], '(text) – narrate any text using currently selected character\'s voice. Use voice="Character Name" argument to set other voice from the voice map, example: /speak voice="Donald Duck" Quack!', true, true); + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ + name: 'speak', + callback: onNarrateText, + aliases: ['narrate', 'tts'], + namedArgumentList: [ + new SlashCommandNamedArgument( + 'voice', 'character voice name', [ARGUMENT_TYPE.STRING], false, + ), + ], + unnamedArgumentList: [ + new SlashCommandArgument( + 'text', [ARGUMENT_TYPE.STRING], true, + ), + ], + helpString: ` +
+ Narrate any text using currently selected character's voice. +
+
+ Use voice="Character Name" argument to set other voice from the voice map. +
+
+ Example: + +
+ `, + })); + document.body.appendChild(audioElement); }); diff --git a/public/scripts/openai.js b/public/scripts/openai.js index 10b15326d..6682568d9 100644 --- a/public/scripts/openai.js +++ b/public/scripts/openai.js @@ -66,6 +66,9 @@ import { } from './instruct-mode.js'; import { isMobile } from './RossAscends-mods.js'; import { saveLogprobsForActiveMessage } from './logprobs.js'; +import { SlashCommandParser } from './slash-commands/SlashCommandParser.js'; +import { SlashCommand } from './slash-commands/SlashCommand.js'; +import { ARGUMENT_TYPE, SlashCommandArgument } from './slash-commands/SlashCommandArgument.js'; export { openai_messages_count, @@ -4292,7 +4295,17 @@ function runProxyCallback(_, value) { return foundName; } -registerSlashCommand('proxy', runProxyCallback, [], '(name) – sets a proxy preset by name'); +SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'proxy', + callback: runProxyCallback, + namedArgumentList: [], + unnamedArgumentList: [ + new SlashCommandArgument( + 'name', [ARGUMENT_TYPE.STRING], true, + ), + ], + helpString: 'Sets a proxy preset by name.', +})); + $(document).ready(async function () { $('#test_api_button').on('click', testApiConnection); diff --git a/public/scripts/power-user.js b/public/scripts/power-user.js index d74b0c064..1cab70e15 100644 --- a/public/scripts/power-user.js +++ b/public/scripts/power-user.js @@ -41,7 +41,9 @@ import { BIAS_CACHE } from './logit-bias.js'; import { renderTemplateAsync } from './templates.js'; import { countOccurrences, debounce, delay, download, getFileText, isOdd, resetScrollHeight, shuffle, sortMoments, stringToRange, timestampToMoment } from './utils.js'; -import { PARSER_FLAG } from './slash-commands/SlashCommandParser.js'; +import { PARSER_FLAG, SlashCommandParser } from './slash-commands/SlashCommandParser.js'; +import { SlashCommand } from './slash-commands/SlashCommand.js'; +import { ARGUMENT_TYPE, SlashCommandArgument } from './slash-commands/SlashCommandArgument.js'; export { loadPowerUserSettings, @@ -3622,13 +3624,96 @@ $(document).ready(() => { browser_has_focus = false; }); - registerSlashCommand('vn', toggleWaifu, [], '– swaps Visual Novel Mode On/Off', false, true); - registerSlashCommand('newchat', doNewChat, [], '– start a new chat with current character', true, true); - registerSlashCommand('random', doRandomChat, [], '(optional tag name) – start a new chat with a random character. If an argument is provided, only considers characters that have the specified tag.', true, true); - registerSlashCommand('delmode', doDelMode, ['del'], '(optional number) – enter message deletion mode, and auto-deletes last N messages if numeric argument is provided', true, true); - registerSlashCommand('cut', doMesCut, [], '(number or range) – cuts the specified message or continuous chunk from the chat, e.g. /cut 0-10. Ranges are inclusive! Returns the text of cut messages separated by a newline.', true, true); - registerSlashCommand('resetpanels', doResetPanels, ['resetui'], '– resets UI panels to original state.', true, true); - registerSlashCommand('bgcol', setAvgBG, [], '– WIP test of auto-bg avg coloring', true, true); - registerSlashCommand('theme', setThemeCallback, [], '(name) – sets a UI theme by name', true, true); - registerSlashCommand('movingui', setmovingUIPreset, [], '(name) – activates a movingUI preset by name', true, true); + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'vn', + callback: toggleWaifu, + helpString: 'Swaps Visual Novel Mode On/Off', + interruptsGeneration: false, + purgeFromMessage: true, + })); + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'newchat', + callback: doNewChat, + helpString: 'Start a new chat with the current character', + interruptsGeneration: true, + purgeFromMessage: true, + })); + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'random', + callback: doRandomChat, + interruptsGeneration: true, + purgeFromMessage: true, + unnamedArgumentList: [ + new SlashCommandArgument( + 'optional tag name', [ARGUMENT_TYPE.STRING], false, + ), + ], + helpString: 'Start a new chat with a random character. If an argument is provided, only considers characters that have the specified tag.', + })); + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'delmode', + callback: doDelMode, + aliases: ['del'], + interruptsGeneration: true, + purgeFromMessage: true, + unnamedArgumentList: [ + new SlashCommandArgument( + 'optional number', [ARGUMENT_TYPE.NUMBER], false, + ), + ], + helpString: 'Enter message deletion mode, and auto-deletes last N messages if numeric argument is provided.', + })); + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'cut', + callback: doMesCut, + returns: 'the text of cut messages separated by a newline', + unnamedArgumentList: [ + new SlashCommandArgument( + 'number or range', [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.RANGE], true, + ), + ], + helpString: ` +
+ Cuts the specified message or continuous chunk from the chat. +
+
+ Ranges are inclusive! +
+
+ Example: + +
+ `, + aliases: [], + interruptsGeneration: true, + purgeFromMessage: true, + })); + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'resetpanels', + callback: doResetPanels, + helpString: 'resets UI panels to original state', + aliases: ['resetui'], + interruptsGeneration: true, + purgeFromMessage: true, + })); + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'bgcol', + callback: setAvgBG, + helpString: '– WIP test of auto-bg avg coloring', + })); + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'theme', + callback: setThemeCallback, + unnamedArgumentList: [ + new SlashCommandArgument( + 'name', [ARGUMENT_TYPE.STRING], true, + ), + ], + helpString: 'sets a UI theme by name', + })); + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'movingui', + callback: setmovingUIPreset, + unnamedArgumentList: [ + new SlashCommandArgument( + 'name', [ARGUMENT_TYPE.STRING], true, + ), + ], + helpString: 'activates a movingUI preset by name', + })); }); diff --git a/public/scripts/preset-manager.js b/public/scripts/preset-manager.js index 1a28f075c..d9bdf7e47 100644 --- a/public/scripts/preset-manager.js +++ b/public/scripts/preset-manager.js @@ -21,6 +21,9 @@ import { instruct_presets } from './instruct-mode.js'; import { kai_settings } from './kai-settings.js'; import { context_presets, getContextSettings, power_user } from './power-user.js'; import { registerSlashCommand } from './slash-commands.js'; +import { SlashCommand } from './slash-commands/SlashCommand.js'; +import { ARGUMENT_TYPE, SlashCommandArgument } from './slash-commands/SlashCommandArgument.js'; +import { SlashCommandParser } from './slash-commands/SlashCommandParser.js'; import { textgenerationwebui_preset_names, textgenerationwebui_presets, @@ -470,7 +473,33 @@ async function waitForConnection() { export async function initPresetManager() { eventSource.on(event_types.CHAT_CHANGED, autoSelectPreset); registerPresetManagers(); - registerSlashCommand('preset', presetCommandCallback, [], '(name) – sets a preset by name for the current API. Gets the current preset if no name is provided', true, true); + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ + name: 'preset', + callback: presetCommandCallback, + namedArgumentList: [], + unnamedArgumentList: [ + new SlashCommandArgument( + 'name', [ARGUMENT_TYPE.STRING], false, + ), + ], + helpString: ` +
+ Sets a preset by name for the current API. Gets the current preset if no name is provided. +
+
+ Example: + +
+ `, + })); + $(document).on('click', '[data-preset-manager-update]', async function () { const apiId = $(this).data('preset-manager-update'); diff --git a/public/scripts/world-info.js b/public/scripts/world-info.js index 767f923d0..7199d29e2 100644 --- a/public/scripts/world-info.js +++ b/public/scripts/world-info.js @@ -9,6 +9,9 @@ import { getTokenCountAsync } from './tokenizers.js'; import { power_user } from './power-user.js'; import { getTagKeyForEntity } from './tags.js'; import { resolveVariable } from './variables.js'; +import { SlashCommandParser } from './slash-commands/SlashCommandParser.js'; +import { SlashCommand } from './slash-commands/SlashCommand.js'; +import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from './slash-commands/SlashCommandArgument.js'; export { world_info, @@ -590,12 +593,166 @@ function registerWorldInfoSlashCommands() { return ''; } - registerSlashCommand('world', onWorldInfoChange, [], '[optional state=off|toggle] [optional silent=true] (optional name) – sets active World, or unsets if no args provided, use state=off and state=toggle to deactivate or toggle a World, use silent=true to suppress toast messages', true, true); - registerSlashCommand('getchatbook', getChatBookCallback, ['getchatlore', 'getchatwi'], '– get a name of the chat-bound lorebook or create a new one if was unbound, and pass it down the pipe', true, true); - registerSlashCommand('findentry', findBookEntryCallback, ['findlore', 'findwi'], '(file=bookName field=field [texts]) – find a UID of the record from the specified book using the fuzzy match of a field value (default: key) and pass it down the pipe, e.g. /findentry file=chatLore field=key Shadowfang', true, true); - registerSlashCommand('getentryfield', getEntryFieldCallback, ['getlorefield', 'getwifield'], '(file=bookName field=field [UID]) – get a field value (default: content) of the record with the UID from the specified book and pass it down the pipe, e.g. /getentryfield file=chatLore field=content 123', true, true); - registerSlashCommand('createentry', createEntryCallback, ['createlore', 'createwi'], '(file=bookName key=key [content]) – create a new record in the specified book with the key and content (both are optional) and pass the UID down the pipe, e.g. /createentry file=chatLore key=Shadowfang The sword of the king', true, true); - registerSlashCommand('setentryfield', setEntryFieldCallback, ['setlorefield', 'setwifield'], '(file=bookName uid=UID field=field [value]) – set a field value (default: content) of the record with the UID from the specified book. To set multiple values for key fields, use comma-delimited list as a value, e.g. /setentryfield file=chatLore uid=123 field=key Shadowfang,sword,weapon', true, true); + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'world', + callback: onWorldInfoChange, + namedArgumentList: [ + new SlashCommandNamedArgument( + 'state', 'set world state', [ARGUMENT_TYPE.STRING], false, false, null, ['off', 'toggle'], + ), + new SlashCommandNamedArgument( + 'silent', 'suppress toast messages', [ARGUMENT_TYPE.BOOLEAN], false, + ), + ], + unnamedArgumentList: [ + new SlashCommandArgument( + 'name', [ARGUMENT_TYPE.STRING], false, + ), + ], + helpString: ` +
+ Sets active World, or unsets if no args provided, use state=off and state=toggle to deactivate or toggle a World, use silent=true to suppress toast messages. +
+ `, + aliases: [], + interruptsGeneration: true, + purgeFromMessage: true, + })); + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'getchatbook', + callback: getChatBookCallback, + helpString: 'Get a name of the chat-bound lorebook or create a new one if was unbound, and pass it down the pipe.', + aliases: ['getchatlore', 'getchatwi'], + interruptsGeneration: true, + purgeFromMessage: true, + })); + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'findentry', + aliases: ['findlore', 'findwi'], + callback: findBookEntryCallback, + namedArgumentList: [ + new SlashCommandNamedArgument( + 'file', 'bookName', ARGUMENT_TYPE.STRING, true, + ), + new SlashCommandNamedArgument( + 'field', 'field value for fuzzy match (default: key)', ARGUMENT_TYPE.STRING, false, false, 'key', + ), + ], + unnamedArgumentList: [ + new SlashCommandArgument( + 'texts', ARGUMENT_TYPE.STRING, true, true, + ), + ], + helpString: ` +
+ Find a UID of the record from the specified book using the fuzzy match of a field value (default: key) and pass it down the pipe. +
+
+ Example: + +
+ `, + interruptsGeneration: true, + purgeFromMessage: true, + })); + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'getentryfield', + aliases: ['getlorefield', 'getwifield'], + callback: getEntryFieldCallback, + namedArgumentList: [ + new SlashCommandNamedArgument( + 'file', 'bookName', ARGUMENT_TYPE.STRING, true, + ), + new SlashCommandNamedArgument( + 'field', 'field to retrieve (default: content)', ARGUMENT_TYPE.STRING, false, false, 'content', + ), + ], + unnamedArgumentList: [ + new SlashCommandArgument( + 'UID', ARGUMENT_TYPE.STRING, true, + ), + ], + helpString: ` +
+ Get a field value (default: content) of the record with the UID from the specified book and pass it down the pipe. +
+
+ Example: + +
+ `, + interruptsGeneration: true, + purgeFromMessage: true, + })); + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'createentry', + callback: createEntryCallback, + aliases: ['createlore', 'createwi'], + returns: 'UID of the new record', + namedArgumentList: [ + new SlashCommandNamedArgument( + 'file', 'book name', [ARGUMENT_TYPE.STRING], true, + ), + new SlashCommandNamedArgument( + 'key', 'record key', [ARGUMENT_TYPE.STRING], false, + ), + ], + unnamedArgumentList: [ + new SlashCommandArgument( + 'content', [ARGUMENT_TYPE.STRING], false, + ), + ], + helpString: ` +
+ Create a new record in the specified book with the key and content (both are optional) and pass the UID down the pipe. +
+
+ Example: + +
+ `, + })); + SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'setentryfield', + callback: setEntryFieldCallback, + aliases: ['setlorefield', 'setwifield'], + namedArgumentList: [ + new SlashCommandNamedArgument( + 'file', 'book name', [ARGUMENT_TYPE.STRING], true, + ), + new SlashCommandNamedArgument( + 'uid', 'record UID', [ARGUMENT_TYPE.STRING], true, + ), + new SlashCommandNamedArgument( + 'field', 'field name', [ARGUMENT_TYPE.STRING], true, false, 'content', + ), + ], + unnamedArgumentList: [ + new SlashCommandArgument( + 'value', [ARGUMENT_TYPE.STRING], true, + ), + ], + helpString: ` +
+ Set a field value (default: content) of the record with the UID from the specified book. To set multiple values for key fields, use comma-delimited list as a value. +
+
+ Example: + +
+ `, + })); + } // World Info Editor