mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-06-05 21:59:27 +02:00
* set isForced to true on input * make floating auto-complete follow horizontal scrolling * add callable closure vars * changes to /let and /var for callable closures * fix error message * fix scope for closure arguments * if should return the pipe result from closures * use /run to call closures and no arguments on immediate closures * throw exception from QRs window-function if no match * when to show autocomplete vs info only * autocomplete positioning * autocomplete styling * add theming to autocomplete (theme, dark, light) * improve autocomplete show/hide logic and editor selection * use blur tint color instead of chat tint color and use blur setting * cleanup and docs * use scope macros for QR args * add enter to select autocomplete * fix no executor found * cleanup and comment * fix alias list in help string * fallback to empty string piped value if null or undefined * fix typo * blur textarea on ctrl+enter execute (and refocus after) * stop executeSlashCommand if parser throws * move /let and /var callbacks into functions * switch textarea to monospace when value starts with slash * add double pipe a pipe breaker * fix /? slash * remove some logging * add "/:name" as shorthand for "/run name" after all * move shit around * fix error message * use testRunShorthandEnd * use parseQuotedValue and parseValue to determine name for "/:" QR labels and set names can include spaces * add some adjustments to make autocomplete work properly some hint in there about "/:" would still be nice * add autocomplete style selector * only strip quotes from subcommand if they are at both ends * fix JSDoc * escaping * allow open quotes on dry run * throwing shit at the wall for /: autocomplete * escapes only for symbols * clean up autocomplete * improve performance * fix scope macros * remove unescaping of pipes * fix macros in scope copy * fix "/? slash" * don't run parser for getNameAt if text has not changed * fix options filter * re-enable blur listener * restore selection on non-replace select * fix for escaping first character of value * add support for {{pipe}} and {{var::}} closures * add index support to var macro * add scoped var macro to macro help * more escape fixes * reduce autocomplete render debounce * cleanup * restore old escape handling and parser flag for strict escaping * fix "no match" autocomplete message * add dummy commands for comments and parser flag * fix type annotations * somewhat safer macro replacements * fix autocomplete select on blank / "no match" * fix cutting off handled part in substitution * add parser flag REPLACE_GETVAR Replaces all {{getvar::}} and {{getglobalvar::}} macros with {{var::}}. Inserts a series of command executors before the command with the macros that: - save {{pipe}} to a var - call /getvar or /getglobalvar to get the variable used in the macro - call /let to save the retrieved variable - return the saved {{pipe}} value This helps to avoid double-substitutions when the var values contain text that could be interpreted as macros. * remove old parser * fix send on enter when no match * deal with pipes in quoted values (loose escaping) * add default parser flags to user settings * allow quoted values in unnamed argument * set parser flag without explicit state to "on" * add click hint on parser error toast * dirty more detailed cmd defs * remove name from unnamed arg * move autocomplete into class and floating with details * replace jQuery's trigger('input') on #send_textarea with native events because jQuery does not dispatch the native event * fix ctrl+space * fix arrow navigation * add comments * fix pointer block * add static fromProps * fix up dummy commands * migrate all commands to addCommandObject * remove commented comment command * fix alias in details * add range as argument type * switch to addCommandObject * switch to addCommandObject * fix height * fix floating details position on left * re-enable blur event * use auto width for full details on floating autocomplete * auto-size floating full details * fix typo * re-enable blur listener * don't prevent enter when selected item is fully typed out * add autocomplete details tooltips * add language to slash command examples * move makeItem into option and command and fix click select * use autocomplete parts in /? slash * fix alias formatting * add language to slash command examples * fix details position on initial input history * small screen styles * replace registerSlashCommand with detailed declarations * put name on first line * add missing returns * fix missing comma * fix alias display in autocomplete list * remove args from help string * move parser settings to its own section * jsdoc * hljs stscript lang * add hljs to autocomplete help examples * add missing import * apply autocomplete colors to stscript codeblocks (hljs) * add fromProps * cache autocomplete elements * towards generic autocomplete * remove unused imports * fix blanks * add return types * re-enable blur * fix blank check * Caption messages by id * add aborting command execution * fix return type * fix chat input font reset * add slash command progress indicator * add missing return * mark registerSlashCommand deprecated * why?? * separate abort logic for commands * remove parsing of quoted values from unnamed arg * add adjustable autocomplete width * revert stop button pulse * add progress and pause/abort to QR editor * add resize event on autocomplete width change * add key= argument to all get vars * refactoring * introduce NamedArgumentAsignment * add TODOs * refactoring * record start and end of named arg assignment * refactoring * prevent duplicate calls to show * refactoring * remove macro ac * add secondary autocomplete and enum descriptions * add syntax highlighting to QR editor * add enum descriptions to /while * add /let key=... to scope variable names * add unnamed argument assignment class and unnamed argument splitting * fix QR editor style * remove dash before autocomplete help text * add autocomplete for unnamed enums * fix remaining dom after holding backslash * fix for unnamed enums * fix autocomplete for /parser-flag * add parser-flag enum help * fix type annotations * fix autocomplete result for /: * add colored autocomplete type icons * collapse second line autocomplete help if empty * mark optional named args in autocomplete * fix when what * remove duplicate debug buttons * dispatch input on autocomplete select * prevent grow from editor syntax layer * add auto-adjust qr editor caret color * remove text-shadow from autocomplete * join value strings in /let and /var * add /abort syntax highlight * fix attempting secondary result when there is none * rename settings headers and split autocomplete / stscript * add parser flag tooltips * add tooltips to chat width stops * fix typo * return clone of help item * fix enum string * don't make optional notice for autocomplete arguments smaller * avoid scrollbar in chat input * add rudimentary macro autocomplete * strip macro from helptext * finally remove closure delimiters around root * cleanup * fix index stuff for removed closure delimiters * fix type hint * add child commands to progress indicator * include sub-separator in macro autocomplete * remove all mentions of interruptsGeneration and purge * remove unused imports * fix syntax highlight with newline at end of input * cleanup select pointer events * coalesce onProgress call * add regex to STscript syntax highlighting * fix closure end * fix autocomplete type icon alignment * adjustments for small screens * fix removing wrong element * add missing "at=" arg to /sys, /comment, /sendas * add font scale setting for autocomplete * add target=_blank for parser flag links * fix for searching enums * remove REGEXP_MODE from hljs just causes trouble * fix autocomplete in closures * fix typo * fix type hint * Get rid of scroll bar on load * Add type hint for /send name argument. Fix 'at' types * Add 'negative' arg hint to /sd command * reenable blur event * Allow /summarize to process any text * Compact layout of script toggles * Expand CSS by default * fix double ranger indicator and adjust to narrow container * make custom css input fill available vertical space * reduce scroll lag * use default cursor on scrollbar * Clean-up module loading in index.html * fix tab indent with hljs --------- Co-authored-by: Cohee <18619528+Cohee1207@users.noreply.github.com>
254 lines
7.4 KiB
JavaScript
254 lines
7.4 KiB
JavaScript
import { getRequestHeaders, substituteParams } from '../../../../script.js';
|
|
import { executeSlashCommands, executeSlashCommandsOnChatInput, executeSlashCommandsWithOptions } from '../../../slash-commands.js';
|
|
import { SlashCommandScope } from '../../../slash-commands/SlashCommandScope.js';
|
|
import { debounceAsync, warn } from '../index.js';
|
|
import { QuickReply } from './QuickReply.js';
|
|
|
|
export class QuickReplySet {
|
|
/**@type {QuickReplySet[]}*/ static list = [];
|
|
|
|
|
|
static from(props) {
|
|
props.qrList = []; //props.qrList?.map(it=>QuickReply.from(it));
|
|
const instance = Object.assign(new this(), props);
|
|
// instance.init();
|
|
return instance;
|
|
}
|
|
|
|
/**
|
|
* @param {String} name - name of the QuickReplySet
|
|
*/
|
|
static get(name) {
|
|
return this.list.find(it=>it.name == name);
|
|
}
|
|
|
|
|
|
|
|
|
|
/**@type {String}*/ name;
|
|
/**@type {Boolean}*/ disableSend = false;
|
|
/**@type {Boolean}*/ placeBeforeInput = false;
|
|
/**@type {Boolean}*/ injectInput = false;
|
|
/**@type {QuickReply[]}*/ qrList = [];
|
|
|
|
/**@type {Number}*/ idIndex = 0;
|
|
|
|
/**@type {Boolean}*/ isDeleted = false;
|
|
|
|
/**@type {Function}*/ save;
|
|
|
|
/**@type {HTMLElement}*/ dom;
|
|
/**@type {HTMLElement}*/ settingsDom;
|
|
|
|
|
|
|
|
|
|
constructor() {
|
|
this.save = debounceAsync(()=>this.performSave(), 200);
|
|
}
|
|
|
|
init() {
|
|
this.qrList.forEach(qr=>this.hookQuickReply(qr));
|
|
}
|
|
|
|
|
|
|
|
|
|
unrender() {
|
|
this.dom?.remove();
|
|
this.dom = null;
|
|
}
|
|
render() {
|
|
this.unrender();
|
|
if (!this.dom) {
|
|
const root = document.createElement('div'); {
|
|
this.dom = root;
|
|
root.classList.add('qr--buttons');
|
|
this.qrList.filter(qr=>!qr.isHidden).forEach(qr=>{
|
|
root.append(qr.render());
|
|
});
|
|
}
|
|
}
|
|
return this.dom;
|
|
}
|
|
rerender() {
|
|
if (!this.dom) return;
|
|
this.dom.innerHTML = '';
|
|
this.qrList.filter(qr=>!qr.isHidden).forEach(qr=>{
|
|
this.dom.append(qr.render());
|
|
});
|
|
}
|
|
|
|
|
|
|
|
|
|
renderSettings() {
|
|
if (!this.settingsDom) {
|
|
this.settingsDom = document.createElement('div'); {
|
|
this.settingsDom.classList.add('qr--set-qrListContents');
|
|
this.qrList.forEach((qr,idx)=>{
|
|
this.renderSettingsItem(qr, idx);
|
|
});
|
|
}
|
|
}
|
|
return this.settingsDom;
|
|
}
|
|
renderSettingsItem(qr, idx) {
|
|
this.settingsDom.append(qr.renderSettings(idx));
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
*
|
|
* @param {QuickReply} qr The QR to execute.
|
|
* @param {object} options
|
|
* @param {string} [options.message] (null) altered message to be used
|
|
* @param {boolean} [options.isAutoExecute] (false) whether the execution is triggered by auto execute
|
|
* @param {boolean} [options.isEditor] (false) whether the execution is triggered by the QR editor
|
|
* @param {boolean} [options.isRun] (false) whether the execution is triggered by /run or /: (window.executeQuickReplyByName)
|
|
* @param {SlashCommandScope} [options.scope] (null) scope to be used when running the command
|
|
* @returns
|
|
*/
|
|
async executeWithOptions(qr, options = {}) {
|
|
options = Object.assign({
|
|
message:null,
|
|
isAutoExecute:false,
|
|
isEditor:false,
|
|
isRun:false,
|
|
scope:null,
|
|
}, options);
|
|
/**@type {HTMLTextAreaElement}*/
|
|
const ta = document.querySelector('#send_textarea');
|
|
const finalMessage = options.message ?? qr.message;
|
|
let input = ta.value;
|
|
if (!options.isAutoExecute && !options.isEditor && !options.isRun && this.injectInput && input.length > 0) {
|
|
if (this.placeBeforeInput) {
|
|
input = `${finalMessage} ${input}`;
|
|
} else {
|
|
input = `${input} ${finalMessage}`;
|
|
}
|
|
} else {
|
|
input = `${finalMessage} `;
|
|
}
|
|
|
|
if (input[0] == '/' && !this.disableSend) {
|
|
let result;
|
|
if (options.isAutoExecute || options.isRun) {
|
|
result = await executeSlashCommandsWithOptions(input, {
|
|
handleParserErrors: true,
|
|
scope: options.scope,
|
|
});
|
|
} else if (options.isEditor) {
|
|
result = await executeSlashCommandsWithOptions(input, {
|
|
handleParserErrors: false,
|
|
scope: options.scope,
|
|
abortController: qr.abortController,
|
|
onProgress: (done, total) => qr.updateEditorProgress(done, total),
|
|
});
|
|
} else {
|
|
result = await executeSlashCommandsOnChatInput(input, {
|
|
scope: options.scope,
|
|
});
|
|
}
|
|
return typeof result === 'object' ? result?.pipe : '';
|
|
}
|
|
|
|
ta.value = substituteParams(input);
|
|
ta.focus();
|
|
|
|
if (!this.disableSend) {
|
|
// @ts-ignore
|
|
document.querySelector('#send_but').click();
|
|
}
|
|
}
|
|
/**
|
|
* @param {QuickReply} qr
|
|
* @param {String} [message] - optional altered message to be used
|
|
* @param {SlashCommandScope} [scope] - optional scope to be used when running the command
|
|
*/
|
|
async execute(qr, message = null, isAutoExecute = false, scope = null) {
|
|
return this.executeWithOptions(qr, {
|
|
message,
|
|
isAutoExecute,
|
|
scope,
|
|
});
|
|
}
|
|
|
|
|
|
|
|
|
|
addQuickReply() {
|
|
const id = Math.max(this.idIndex, this.qrList.reduce((max,qr)=>Math.max(max,qr.id),0)) + 1;
|
|
this.idIndex = id + 1;
|
|
const qr = QuickReply.from({ id });
|
|
this.qrList.push(qr);
|
|
this.hookQuickReply(qr);
|
|
if (this.settingsDom) {
|
|
this.renderSettingsItem(qr, this.qrList.length - 1);
|
|
}
|
|
if (this.dom) {
|
|
this.dom.append(qr.render());
|
|
}
|
|
this.save();
|
|
return qr;
|
|
}
|
|
|
|
hookQuickReply(qr) {
|
|
qr.onExecute = (_, options)=>this.executeWithOptions(qr, options);
|
|
qr.onDelete = ()=>this.removeQuickReply(qr);
|
|
qr.onUpdate = ()=>this.save();
|
|
}
|
|
|
|
removeQuickReply(qr) {
|
|
this.qrList.splice(this.qrList.indexOf(qr), 1);
|
|
this.save();
|
|
}
|
|
|
|
|
|
toJSON() {
|
|
return {
|
|
version: 2,
|
|
name: this.name,
|
|
disableSend: this.disableSend,
|
|
placeBeforeInput: this.placeBeforeInput,
|
|
injectInput: this.injectInput,
|
|
qrList: this.qrList,
|
|
idIndex: this.idIndex,
|
|
};
|
|
}
|
|
|
|
|
|
async performSave() {
|
|
const response = await fetch('/api/quick-replies/save', {
|
|
method: 'POST',
|
|
headers: getRequestHeaders(),
|
|
body: JSON.stringify(this),
|
|
});
|
|
|
|
if (response.ok) {
|
|
this.rerender();
|
|
} else {
|
|
warn(`Failed to save Quick Reply Set: ${this.name}`);
|
|
}
|
|
}
|
|
|
|
async delete() {
|
|
const response = await fetch('/api/quick-replies/delete', {
|
|
method: 'POST',
|
|
headers: getRequestHeaders(),
|
|
body: JSON.stringify(this),
|
|
});
|
|
|
|
if (response.ok) {
|
|
this.unrender();
|
|
const idx = QuickReplySet.list.indexOf(this);
|
|
QuickReplySet.list.splice(idx, 1);
|
|
this.isDeleted = true;
|
|
} else {
|
|
warn(`Failed to delete Quick Reply Set: ${this.name}`);
|
|
}
|
|
}
|
|
}
|