mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-02-13 10:42:55 +01:00
add /import to import closures from other QRs
This commit is contained in:
parent
5df932a76d
commit
977d98e7e8
@ -1,8 +1,11 @@
|
|||||||
import { SlashCommand } from '../../../slash-commands/SlashCommand.js';
|
import { SlashCommand } from '../../../slash-commands/SlashCommand.js';
|
||||||
import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from '../../../slash-commands/SlashCommandArgument.js';
|
import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from '../../../slash-commands/SlashCommandArgument.js';
|
||||||
|
import { SlashCommandClosure } from '../../../slash-commands/SlashCommandClosure.js';
|
||||||
import { enumIcons } from '../../../slash-commands/SlashCommandCommonEnumsProvider.js';
|
import { enumIcons } from '../../../slash-commands/SlashCommandCommonEnumsProvider.js';
|
||||||
|
import { SlashCommandDebugController } from '../../../slash-commands/SlashCommandDebugController.js';
|
||||||
import { SlashCommandEnumValue, enumTypes } from '../../../slash-commands/SlashCommandEnumValue.js';
|
import { SlashCommandEnumValue, enumTypes } from '../../../slash-commands/SlashCommandEnumValue.js';
|
||||||
import { SlashCommandParser } from '../../../slash-commands/SlashCommandParser.js';
|
import { SlashCommandParser } from '../../../slash-commands/SlashCommandParser.js';
|
||||||
|
import { SlashCommandScope } from '../../../slash-commands/SlashCommandScope.js';
|
||||||
import { isTrueBoolean } from '../../../utils.js';
|
import { isTrueBoolean } from '../../../utils.js';
|
||||||
// eslint-disable-next-line no-unused-vars
|
// eslint-disable-next-line no-unused-vars
|
||||||
import { QuickReplyApi } from '../api/QuickReplyApi.js';
|
import { QuickReplyApi } from '../api/QuickReplyApi.js';
|
||||||
@ -554,6 +557,106 @@ export class SlashCommandHandler {
|
|||||||
</div>
|
</div>
|
||||||
`,
|
`,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'import',
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {{_scope:SlashCommandScope, _debugController:SlashCommandDebugController, from:string}} args
|
||||||
|
* @param {string} value
|
||||||
|
*/
|
||||||
|
callback: (args, value) => {
|
||||||
|
if (!args.from) throw new Error('/import requires from= to be set.');
|
||||||
|
if (!value) throw new Error('/import requires the unnamed argument to be set.');
|
||||||
|
let qr = [...this.api.listGlobalSets(), ...this.api.listChatSets()]
|
||||||
|
.map(it=>this.api.getSetByName(it)?.qrList ?? [])
|
||||||
|
.flat()
|
||||||
|
.find(it=>it.label == args.from)
|
||||||
|
;
|
||||||
|
if (!qr) {
|
||||||
|
let [setName, ...qrNameParts] = args.from.split('.');
|
||||||
|
let qrName = qrNameParts.join('.');
|
||||||
|
let qrs = QuickReplySet.get(setName);
|
||||||
|
if (qrs) {
|
||||||
|
qr = qrs.qrList.find(it=>it.label == qrName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (qr) {
|
||||||
|
const parser = new SlashCommandParser();
|
||||||
|
const closure = parser.parse(qr.message, true, [], null, args._debugController);
|
||||||
|
if (args._debugController) {
|
||||||
|
closure.source = args.from;
|
||||||
|
}
|
||||||
|
const testCandidates = (executor)=>{
|
||||||
|
return (
|
||||||
|
executor.namedArgumentList.find(arg=>arg.name == 'key')
|
||||||
|
&& executor.unnamedArgumentList.length > 0
|
||||||
|
&& executor.unnamedArgumentList[0].value instanceof SlashCommandClosure
|
||||||
|
) || (
|
||||||
|
!executor.namedArgumentList.find(arg=>arg.name == 'key')
|
||||||
|
&& executor.unnamedArgumentList.length > 1
|
||||||
|
&& executor.unnamedArgumentList[1].value instanceof SlashCommandClosure
|
||||||
|
);
|
||||||
|
};
|
||||||
|
const candidates = closure.executorList
|
||||||
|
.filter(executor=>['let', 'var'].includes(executor.command.name))
|
||||||
|
.filter(testCandidates)
|
||||||
|
.map(executor=>({
|
||||||
|
key: executor.namedArgumentList.find(arg=>arg.name == 'key')?.value ?? executor.unnamedArgumentList[0].value,
|
||||||
|
value: executor.unnamedArgumentList[executor.namedArgumentList.find(arg=>arg.name == 'key') ? 0 : 1].value,
|
||||||
|
}))
|
||||||
|
;
|
||||||
|
for (let i = 0; i < value.length; i++) {
|
||||||
|
const srcName = value[i];
|
||||||
|
let dstName = srcName;
|
||||||
|
if (i + 2 < value.length && value[i + 1] == 'as') {
|
||||||
|
dstName = value[i + 2];
|
||||||
|
i += 2;
|
||||||
|
}
|
||||||
|
const pick = candidates.find(it=>it.key == srcName);
|
||||||
|
if (!pick) throw new Error(`No scoped closure named "${srcName}" found in "${args.from}"`);
|
||||||
|
if (args._scope.existsVariableInScope(dstName)) {
|
||||||
|
args._scope.setVariable(dstName, pick.value);
|
||||||
|
} else {
|
||||||
|
args._scope.letVariable(dstName, pick.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new Error(`No Quick Reply found for "${name}".`);
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
},
|
||||||
|
namedArgumentList: [
|
||||||
|
SlashCommandNamedArgument.fromProps({ name: 'from',
|
||||||
|
description: 'Quick Reply to import from (QRSet.QRLabel)',
|
||||||
|
typeList: ARGUMENT_TYPE.STRING,
|
||||||
|
isRequired: true,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
unnamedArgumentList: [
|
||||||
|
SlashCommandArgument.fromProps({ description: 'what to import (x or x as y)',
|
||||||
|
acceptsMultiple: true,
|
||||||
|
typeList: ARGUMENT_TYPE.STRING,
|
||||||
|
isRequired: true,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
splitUnnamedArgument: true,
|
||||||
|
helpString: `
|
||||||
|
<div>
|
||||||
|
Import one or more closures from another Quick Reply.
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
Only imports closures that are directly assigned a scoped variable via <code>/let</code> or <code>/var</code>.
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<strong>Examples:</strong>
|
||||||
|
<ul>
|
||||||
|
<li><pre><code>/import from=LibraryQrSet.FooBar foo |\n/:foo</code></pre></li>
|
||||||
|
<li><pre><code>/import from=LibraryQrSet.FooBar\n\tfoo\n\tbar\n|\n/:foo |\n/:bar</code></pre></li>
|
||||||
|
<li><pre><code>/import from=LibraryQrSet.FooBar\n\tfoo as x\n\tbar as y\n|\n/:x |\n/:y</code></pre></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
`,
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -861,6 +861,17 @@ export class SlashCommandParser {
|
|||||||
} else if (typeof cmd.unnamedArgumentList[0]?.value == 'string') {
|
} else if (typeof cmd.unnamedArgumentList[0]?.value == 'string') {
|
||||||
this.scope.variableNames.push(cmd.unnamedArgumentList[0].value);
|
this.scope.variableNames.push(cmd.unnamedArgumentList[0].value);
|
||||||
}
|
}
|
||||||
|
} else if (cmd.name == 'import') {
|
||||||
|
const value = /**@type {string[]}*/(cmd.unnamedArgumentList.map(it=>it.value));
|
||||||
|
for (let i = 0; i < value.length; i++) {
|
||||||
|
const srcName = value[i];
|
||||||
|
let dstName = srcName;
|
||||||
|
if (i + 2 < value.length && value[i + 1] == 'as') {
|
||||||
|
dstName = value[i + 2];
|
||||||
|
i += 2;
|
||||||
|
}
|
||||||
|
this.scope.variableNames.push(dstName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (this.testCommandEnd()) {
|
if (this.testCommandEnd()) {
|
||||||
|
@ -1872,6 +1872,7 @@ body[data-stscript-style] .hljs.language-stscript {
|
|||||||
>code {
|
>code {
|
||||||
display: block;
|
display: block;
|
||||||
padding: 1px;
|
padding: 1px;
|
||||||
|
tab-size: 4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user