mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-06-05 21:59:27 +02:00
Merge pull request #2731 from SillyTavern/fix-pipe-types
Add type conversion for /setvar commands with index
This commit is contained in:
@ -36,6 +36,8 @@ export const enumIcons = {
|
|||||||
|
|
||||||
true: '✔️',
|
true: '✔️',
|
||||||
false: '❌',
|
false: '❌',
|
||||||
|
null: '🚫',
|
||||||
|
undefined: '❓',
|
||||||
|
|
||||||
// Value types
|
// Value types
|
||||||
boolean: '🔲',
|
boolean: '🔲',
|
||||||
@ -230,4 +232,19 @@ export const commonEnumProviders = {
|
|||||||
enumTypes.enum, '💉');
|
enumTypes.enum, '💉');
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets somewhat recognizable STscript types.
|
||||||
|
*
|
||||||
|
* @returns {SlashCommandEnumValue[]}
|
||||||
|
*/
|
||||||
|
types: () => [
|
||||||
|
new SlashCommandEnumValue('string', null, enumTypes.type, enumIcons.string),
|
||||||
|
new SlashCommandEnumValue('number', null, enumTypes.type, enumIcons.number),
|
||||||
|
new SlashCommandEnumValue('boolean', null, enumTypes.type, enumIcons.boolean),
|
||||||
|
new SlashCommandEnumValue('array', null, enumTypes.type, enumIcons.array),
|
||||||
|
new SlashCommandEnumValue('object', null, enumTypes.type, enumIcons.dictionary),
|
||||||
|
new SlashCommandEnumValue('null', null, enumTypes.type, enumIcons.null),
|
||||||
|
new SlashCommandEnumValue('undefined', null, enumTypes.type, enumIcons.undefined),
|
||||||
|
],
|
||||||
};
|
};
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { SlashCommandClosure } from './SlashCommandClosure.js';
|
import { SlashCommandClosure } from './SlashCommandClosure.js';
|
||||||
|
import { convertValueType } from '../utils.js';
|
||||||
|
|
||||||
export class SlashCommandScope {
|
export class SlashCommandScope {
|
||||||
/**@type {string[]}*/ variableNames = [];
|
/**@type {string[]}*/ variableNames = [];
|
||||||
@ -55,7 +56,7 @@ export class SlashCommandScope {
|
|||||||
if (this.existsVariableInScope(key)) throw new SlashCommandScopeVariableExistsError(`Variable named "${key}" already exists.`);
|
if (this.existsVariableInScope(key)) throw new SlashCommandScopeVariableExistsError(`Variable named "${key}" already exists.`);
|
||||||
this.variables[key] = value;
|
this.variables[key] = value;
|
||||||
}
|
}
|
||||||
setVariable(key, value, index = null) {
|
setVariable(key, value, index = null, type = null) {
|
||||||
if (this.existsVariableInScope(key)) {
|
if (this.existsVariableInScope(key)) {
|
||||||
if (index !== null && index !== undefined) {
|
if (index !== null && index !== undefined) {
|
||||||
let v = this.variables[key];
|
let v = this.variables[key];
|
||||||
@ -63,13 +64,13 @@ export class SlashCommandScope {
|
|||||||
v = JSON.parse(v);
|
v = JSON.parse(v);
|
||||||
const numIndex = Number(index);
|
const numIndex = Number(index);
|
||||||
if (Number.isNaN(numIndex)) {
|
if (Number.isNaN(numIndex)) {
|
||||||
v[index] = value;
|
v[index] = convertValueType(value, type);
|
||||||
} else {
|
} else {
|
||||||
v[numIndex] = value;
|
v[numIndex] = convertValueType(value, type);
|
||||||
}
|
}
|
||||||
v = JSON.stringify(v);
|
v = JSON.stringify(v);
|
||||||
} catch {
|
} catch {
|
||||||
v[index] = value;
|
v[index] = convertValueType(value, type);
|
||||||
}
|
}
|
||||||
this.variables[key] = v;
|
this.variables[key] = v;
|
||||||
} else {
|
} else {
|
||||||
@ -78,7 +79,7 @@ export class SlashCommandScope {
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
if (this.parent) {
|
if (this.parent) {
|
||||||
return this.parent.setVariable(key, value, index);
|
return this.parent.setVariable(key, value, index, type);
|
||||||
}
|
}
|
||||||
throw new SlashCommandScopeVariableNotFoundError(`No such variable: "${key}"`);
|
throw new SlashCommandScopeVariableNotFoundError(`No such variable: "${key}"`);
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ import { isMobile } from './RossAscends-mods.js';
|
|||||||
import { collapseNewlines } from './power-user.js';
|
import { collapseNewlines } from './power-user.js';
|
||||||
import { debounce_timeout } from './constants.js';
|
import { debounce_timeout } from './constants.js';
|
||||||
import { Popup, POPUP_RESULT, POPUP_TYPE } from './popup.js';
|
import { Popup, POPUP_RESULT, POPUP_TYPE } from './popup.js';
|
||||||
|
import { SlashCommandClosure } from './slash-commands/SlashCommandClosure.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pagination status string template.
|
* Pagination status string template.
|
||||||
@ -33,6 +34,74 @@ export function isValidUrl(value) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts string to a value of a given type. Includes pythonista-friendly aliases.
|
||||||
|
* @param {string|SlashCommandClosure} value String value
|
||||||
|
* @param {string} type Type to convert to
|
||||||
|
* @returns {any} Converted value
|
||||||
|
*/
|
||||||
|
export function convertValueType(value, type) {
|
||||||
|
if (value instanceof SlashCommandClosure || typeof type !== 'string') {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (type.trim().toLowerCase()) {
|
||||||
|
case 'string':
|
||||||
|
case 'str':
|
||||||
|
return String(value);
|
||||||
|
|
||||||
|
case 'null':
|
||||||
|
return null;
|
||||||
|
|
||||||
|
case 'undefined':
|
||||||
|
case 'none':
|
||||||
|
return undefined;
|
||||||
|
|
||||||
|
case 'number':
|
||||||
|
return Number(value);
|
||||||
|
|
||||||
|
case 'int':
|
||||||
|
return parseInt(value, 10);
|
||||||
|
|
||||||
|
case 'float':
|
||||||
|
return parseFloat(value);
|
||||||
|
|
||||||
|
case 'boolean':
|
||||||
|
case 'bool':
|
||||||
|
return isTrueBoolean(value);
|
||||||
|
|
||||||
|
case 'list':
|
||||||
|
case 'array':
|
||||||
|
try {
|
||||||
|
const parsedArray = JSON.parse(value);
|
||||||
|
if (Array.isArray(parsedArray)) {
|
||||||
|
return parsedArray;
|
||||||
|
}
|
||||||
|
// The value is not an array
|
||||||
|
return [];
|
||||||
|
} catch {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'object':
|
||||||
|
case 'dict':
|
||||||
|
case 'dictionary':
|
||||||
|
try {
|
||||||
|
const parsedObject = JSON.parse(value);
|
||||||
|
if (typeof parsedObject === 'object') {
|
||||||
|
return parsedObject;
|
||||||
|
}
|
||||||
|
// The value is not an object
|
||||||
|
return {};
|
||||||
|
} catch {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses ranges like 10-20 or 10.
|
* Parses ranges like 10-20 or 10.
|
||||||
* Range is inclusive. Start must be less than end.
|
* Range is inclusive. Start must be less than end.
|
||||||
|
@ -11,7 +11,7 @@ import { commonEnumProviders, enumIcons } from './slash-commands/SlashCommandCom
|
|||||||
import { SlashCommandEnumValue, enumTypes } from './slash-commands/SlashCommandEnumValue.js';
|
import { SlashCommandEnumValue, enumTypes } from './slash-commands/SlashCommandEnumValue.js';
|
||||||
import { PARSER_FLAG, SlashCommandParser } from './slash-commands/SlashCommandParser.js';
|
import { PARSER_FLAG, SlashCommandParser } from './slash-commands/SlashCommandParser.js';
|
||||||
import { SlashCommandScope } from './slash-commands/SlashCommandScope.js';
|
import { SlashCommandScope } from './slash-commands/SlashCommandScope.js';
|
||||||
import { isFalseBoolean } from './utils.js';
|
import { isFalseBoolean, convertValueType } from './utils.js';
|
||||||
|
|
||||||
/** @typedef {import('./slash-commands/SlashCommandParser.js').NamedArguments} NamedArguments */
|
/** @typedef {import('./slash-commands/SlashCommandParser.js').NamedArguments} NamedArguments */
|
||||||
/** @typedef {import('./slash-commands/SlashCommand.js').UnnamedArguments} UnnamedArguments */
|
/** @typedef {import('./slash-commands/SlashCommand.js').UnnamedArguments} UnnamedArguments */
|
||||||
@ -57,12 +57,12 @@ function setLocalVariable(name, value, args = {}) {
|
|||||||
if (localVariable === null) {
|
if (localVariable === null) {
|
||||||
localVariable = {};
|
localVariable = {};
|
||||||
}
|
}
|
||||||
localVariable[args.index] = value;
|
localVariable[args.index] = convertValueType(value, args.as);
|
||||||
} else {
|
} else {
|
||||||
if (localVariable === null) {
|
if (localVariable === null) {
|
||||||
localVariable = [];
|
localVariable = [];
|
||||||
}
|
}
|
||||||
localVariable[numIndex] = value;
|
localVariable[numIndex] = convertValueType(value, args.as);
|
||||||
}
|
}
|
||||||
chat_metadata.variables[name] = JSON.stringify(localVariable);
|
chat_metadata.variables[name] = JSON.stringify(localVariable);
|
||||||
} catch {
|
} catch {
|
||||||
@ -106,12 +106,12 @@ function setGlobalVariable(name, value, args = {}) {
|
|||||||
if (globalVariable === null) {
|
if (globalVariable === null) {
|
||||||
globalVariable = {};
|
globalVariable = {};
|
||||||
}
|
}
|
||||||
globalVariable[args.index] = value;
|
globalVariable[args.index] = convertValueType(value, args.as);
|
||||||
} else {
|
} else {
|
||||||
if (globalVariable === null) {
|
if (globalVariable === null) {
|
||||||
globalVariable = [];
|
globalVariable = [];
|
||||||
}
|
}
|
||||||
globalVariable[numIndex] = value;
|
globalVariable[numIndex] = convertValueType(value, args.as);
|
||||||
}
|
}
|
||||||
extension_settings.variables.global[name] = JSON.stringify(globalVariable);
|
extension_settings.variables.global[name] = JSON.stringify(globalVariable);
|
||||||
} catch {
|
} catch {
|
||||||
@ -667,6 +667,7 @@ function parseNumericSeries(value, scope = null) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function performOperation(value, operation, singleOperand = false, scope = null) {
|
function performOperation(value, operation, singleOperand = false, scope = null) {
|
||||||
|
function getResult() {
|
||||||
if (!value) {
|
if (!value) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -686,6 +687,10 @@ function performOperation(value, operation, singleOperand = false, scope = null)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const result = getResult();
|
||||||
|
return String(result);
|
||||||
|
}
|
||||||
|
|
||||||
function addValuesCallback(args, value) {
|
function addValuesCallback(args, value) {
|
||||||
return performOperation(value, (array) => array.reduce((a, b) => a + b, 0), false, args._scope);
|
return performOperation(value, (array) => array.reduce((a, b) => a + b, 0), false, args._scope);
|
||||||
}
|
}
|
||||||
@ -836,7 +841,7 @@ function varCallback(args, value) {
|
|||||||
if (typeof key != 'string') throw new Error('Key must be a string');
|
if (typeof key != 'string') throw new Error('Key must be a string');
|
||||||
if (args._hasUnnamedArgument) {
|
if (args._hasUnnamedArgument) {
|
||||||
const val = typeof value[0] == 'string' ? value.join(' ') : value[0];
|
const val = typeof value[0] == 'string' ? value.join(' ') : value[0];
|
||||||
args._scope.setVariable(key, val, args.index);
|
args._scope.setVariable(key, val, args.index, args.as);
|
||||||
return val;
|
return val;
|
||||||
} else {
|
} else {
|
||||||
return args._scope.getVariable(key, args.index);
|
return args._scope.getVariable(key, args.index);
|
||||||
@ -846,7 +851,7 @@ function varCallback(args, value) {
|
|||||||
if (typeof key != 'string') throw new Error('Key must be a string');
|
if (typeof key != 'string') throw new Error('Key must be a string');
|
||||||
if (value.length > 0) {
|
if (value.length > 0) {
|
||||||
const val = typeof value[0] == 'string' ? value.join(' ') : value[0];
|
const val = typeof value[0] == 'string' ? value.join(' ') : value[0];
|
||||||
args._scope.setVariable(key, val, args.index);
|
args._scope.setVariable(key, val, args.index, args.as);
|
||||||
return val;
|
return val;
|
||||||
} else {
|
} else {
|
||||||
return args._scope.getVariable(key, args.index);
|
return args._scope.getVariable(key, args.index);
|
||||||
@ -901,6 +906,14 @@ export function registerVariableCommands() {
|
|||||||
new SlashCommandNamedArgument(
|
new SlashCommandNamedArgument(
|
||||||
'index', 'list index', [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.STRING], false,
|
'index', 'list index', [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.STRING], false,
|
||||||
),
|
),
|
||||||
|
SlashCommandNamedArgument.fromProps({
|
||||||
|
name: 'as',
|
||||||
|
description: 'change the type of the value when used with index',
|
||||||
|
forceEnum: true,
|
||||||
|
enumProvider: commonEnumProviders.types,
|
||||||
|
isRequired: false,
|
||||||
|
defaultValue: 'string',
|
||||||
|
}),
|
||||||
],
|
],
|
||||||
unnamedArgumentList: [
|
unnamedArgumentList: [
|
||||||
new SlashCommandArgument(
|
new SlashCommandArgument(
|
||||||
@ -910,6 +923,7 @@ export function registerVariableCommands() {
|
|||||||
helpString: `
|
helpString: `
|
||||||
<div>
|
<div>
|
||||||
Set a local variable value and pass it down the pipe. The <code>index</code> argument is optional.
|
Set a local variable value and pass it down the pipe. The <code>index</code> argument is optional.
|
||||||
|
To convert the value to a specific JSON type when using <code>index</code>, use the <code>as</code> argument.
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<strong>Example:</strong>
|
<strong>Example:</strong>
|
||||||
@ -917,6 +931,9 @@ export function registerVariableCommands() {
|
|||||||
<li>
|
<li>
|
||||||
<pre><code class="language-stscript">/setvar key=color green</code></pre>
|
<pre><code class="language-stscript">/setvar key=color green</code></pre>
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
<pre><code class="language-stscript">/setvar key=ages index=John as=number 21</code></pre>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
`,
|
`,
|
||||||
@ -1015,6 +1032,14 @@ export function registerVariableCommands() {
|
|||||||
new SlashCommandNamedArgument(
|
new SlashCommandNamedArgument(
|
||||||
'index', 'list index', [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.STRING], false,
|
'index', 'list index', [ARGUMENT_TYPE.NUMBER, ARGUMENT_TYPE.STRING], false,
|
||||||
),
|
),
|
||||||
|
SlashCommandNamedArgument.fromProps({
|
||||||
|
name: 'as',
|
||||||
|
description: 'change the type of the value when used with index',
|
||||||
|
forceEnum: true,
|
||||||
|
enumProvider: commonEnumProviders.types,
|
||||||
|
isRequired: false,
|
||||||
|
defaultValue: 'string',
|
||||||
|
}),
|
||||||
],
|
],
|
||||||
unnamedArgumentList: [
|
unnamedArgumentList: [
|
||||||
new SlashCommandArgument(
|
new SlashCommandArgument(
|
||||||
@ -1024,6 +1049,7 @@ export function registerVariableCommands() {
|
|||||||
helpString: `
|
helpString: `
|
||||||
<div>
|
<div>
|
||||||
Set a global variable value and pass it down the pipe. The <code>index</code> argument is optional.
|
Set a global variable value and pass it down the pipe. The <code>index</code> argument is optional.
|
||||||
|
To convert the value to a specific JSON type when using <code>index</code>, use the <code>as</code> argument.
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<strong>Example:</strong>
|
<strong>Example:</strong>
|
||||||
@ -1031,6 +1057,9 @@ export function registerVariableCommands() {
|
|||||||
<li>
|
<li>
|
||||||
<pre><code class="language-stscript">/setglobalvar key=color green</code></pre>
|
<pre><code class="language-stscript">/setglobalvar key=color green</code></pre>
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
<pre><code class="language-stscript">/setglobalvar key=ages index=John as=number 21</code></pre>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
`,
|
`,
|
||||||
@ -2030,6 +2059,14 @@ export function registerVariableCommands() {
|
|||||||
false, // isRequired
|
false, // isRequired
|
||||||
false, // acceptsMultiple
|
false, // acceptsMultiple
|
||||||
),
|
),
|
||||||
|
SlashCommandNamedArgument.fromProps({
|
||||||
|
name: 'as',
|
||||||
|
description: 'change the type of the value when used with index',
|
||||||
|
forceEnum: true,
|
||||||
|
enumProvider: commonEnumProviders.types,
|
||||||
|
isRequired: false,
|
||||||
|
defaultValue: 'string',
|
||||||
|
}),
|
||||||
],
|
],
|
||||||
unnamedArgumentList: [
|
unnamedArgumentList: [
|
||||||
SlashCommandArgument.fromProps({
|
SlashCommandArgument.fromProps({
|
||||||
@ -2049,7 +2086,8 @@ export function registerVariableCommands() {
|
|||||||
splitUnnamedArgumentCount: 1,
|
splitUnnamedArgumentCount: 1,
|
||||||
helpString: `
|
helpString: `
|
||||||
<div>
|
<div>
|
||||||
Get or set a variable.
|
Get or set a variable. Use <code>index</code> to access elements of a JSON-serialized list or dictionary.
|
||||||
|
To convert the value to a specific JSON type when using with <code>index</code>, use the <code>as</code> argument.
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<strong>Examples:</strong>
|
<strong>Examples:</strong>
|
||||||
@ -2060,6 +2098,9 @@ export function registerVariableCommands() {
|
|||||||
<li>
|
<li>
|
||||||
<pre><code class="language-stscript">/let x foo | /var key=x foo bar | /var x | /echo</code></pre>
|
<pre><code class="language-stscript">/let x foo | /var key=x foo bar | /var x | /echo</code></pre>
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
<pre><code class="language-stscript">/let x {} | /var index=cool as=number x 1337 | /echo {{var::x}}</code></pre>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
`,
|
`,
|
||||||
|
Reference in New Issue
Block a user