From 87df0fc1dcec91ad036fc34a83610d5e7ef5bd0b Mon Sep 17 00:00:00 2001 From: SillyLossy Date: Fri, 21 Apr 2023 17:50:46 +0300 Subject: [PATCH] Add slash command parser --- public/index.html | 1 + public/script.js | 24 +++---- public/scripts/slash-commands.js | 117 +++++++++++++++++++++++++++++++ 3 files changed, 129 insertions(+), 13 deletions(-) create mode 100644 public/scripts/slash-commands.js diff --git a/public/index.html b/public/index.html index a875e4ede..f11c83841 100644 --- a/public/index.html +++ b/public/index.html @@ -48,6 +48,7 @@ + SillyTavern diff --git a/public/script.js b/public/script.js index 0769ea982..3b43ee520 100644 --- a/public/script.js +++ b/public/script.js @@ -91,6 +91,7 @@ import { import { debounce, delay } from "./scripts/utils.js"; import { extension_settings, loadExtensionSettings } from "./scripts/extensions.js"; +import { executeSlashCommands } from "./scripts/slash-commands.js"; //exporting functions and vars for mods export { @@ -1031,23 +1032,20 @@ function getStoppingStrings(isImpersonate, addSpace) { return [addSpace ? `${result} ` : result]; } -function getSlashCommand(message, type) { +function processCommands(message, type) { if (type == "regenerate" || type == "swipe") { return null; } - const commandMap = { - "/?": system_message_types.HELP, - "/help": system_message_types.HELP - }; + const result = executeSlashCommands(message); + $("#send_textarea").val(result.newText).trigger('input'); - const activationText = message.trim().toLowerCase(); - - if (Object.keys(commandMap).includes(activationText)) { - return commandMap[activationText]; + // interrupt generation if the input was nothing but a command + if (message.length > 0 && result.newText.length === 0) { + return true; } - return null; + return result.interrupt; } function sendSystemMessage(type, text) { @@ -1311,11 +1309,11 @@ async function Generate(type, automatic_trigger, force_name2) { const isImpersonate = type == "impersonate"; message_already_generated = isImpersonate ? `${name1}: ` : `${name2}: `; - const slashCommand = getSlashCommand($("#send_textarea").val(), type); + const interruptedByCommand = processCommands($("#send_textarea").val(), type); - if (slashCommand == system_message_types.HELP) { - sendSystemMessage(system_message_types.HELP); + if (interruptedByCommand) { $("#send_textarea").val('').trigger('input'); + is_send_press = false; return; } diff --git a/public/scripts/slash-commands.js b/public/scripts/slash-commands.js new file mode 100644 index 000000000..595e1b6a0 --- /dev/null +++ b/public/scripts/slash-commands.js @@ -0,0 +1,117 @@ +import { + sendSystemMessage, + system_message_types +} from "../script.js"; +export { + executeSlashCommands, + registerSlashCommand, +} + +class SlashCommandParser { + constructor() { + this.commands = {}; + } + + addCommand(command, callback, aliases, helpString = '', interruptsGeneration = false, purgeFromMessage = true) { + const fnObj = { callback, helpString, interruptsGeneration, purgeFromMessage }; + this.commands[command] = fnObj; + + if (Array.isArray(aliases)) { + aliases.forEach((alias) => { + this.commands[alias] = fnObj; + }); + } + } + + parse(text) { + const firstSpace = text.indexOf(' '); + const command = firstSpace !== -1 ? text.substring(1, firstSpace) : text.substring(1); + const args = firstSpace !== -1 ? text.substring(firstSpace + 1) : ''; + const argObj = {}; + let unnamedArg; + + if (args.length > 0) { + const argsArray = args.split(' '); + for (let arg of argsArray) { + const equalsIndex = arg.indexOf('='); + if (equalsIndex !== -1) { + const key = arg.substring(0, equalsIndex); + const value = arg.substring(equalsIndex + 1); + argObj[key] = value; + } + else { + break; + } + } + + unnamedArg = argsArray.slice(Object.keys(argObj).length).join(' '); + } + + if (this.commands[command]) { + return { command: this.commands[command], args: argObj, value: unnamedArg }; + } + + return false; + } +} + +const parser = new SlashCommandParser(); +const registerSlashCommand = parser.addCommand.bind(parser); + +parser.addCommand('help', helpCommandCallback, ['?'], 'Displays a help information', true, true); +parser.addCommand('bg', setBackgroundCallback, ['background'], 'Sets a background', false, true); + +function helpCommandCallback() { + sendSystemMessage(system_message_types.HELP); +} + +function setBackgroundCallback(_, bg) { + if (!bg) { + return; + } + console.log('Set background to ' + bg); + const bgElement = $(`.bg_example[bgfile^="${bg.trim()}"`); + + if (bgElement.length) { + bgElement.get(0).click(); + } +} + +function executeSlashCommands(text) { + if (!text) { + return false; + } + + const lines = text.split('\n'); + const linesToRemove = []; + + let interrupt = false; + + for (let index = 0; index < lines.length; index++) { + const trimmedLine = lines[index].trim(); + + if (!trimmedLine.startsWith('/')) { + continue; + } + + const result = parser.parse(trimmedLine); + + if (!result) { + continue; + } + + result.command.callback(result.args, result.value); + + if (result.command.interruptsGeneration) { + interrupt = true; + } + + if (result.command.purgeFromMessage) { + linesToRemove.push(lines[index]); + } + } + + const newText = lines.filter(x => linesToRemove.indexOf(x) === -1).join('\n'); + + return { interrupt, newText }; +} \ No newline at end of file