2024-06-21 20:04:55 +02:00
|
|
|
|
import { chat_metadata, characters, substituteParams, chat, extension_prompt_roles, extension_prompt_types } from "../../script.js";
|
2024-06-17 03:30:52 +02:00
|
|
|
|
import { extension_settings } from "../extensions.js";
|
2024-06-21 20:04:55 +02:00
|
|
|
|
import { getGroupMembers, groups, selected_group } from "../group-chats.js";
|
|
|
|
|
import { power_user } from "../power-user.js";
|
2024-06-17 03:30:52 +02:00
|
|
|
|
import { searchCharByName, getTagsList, tags } from "../tags.js";
|
2024-06-20 20:33:45 +02:00
|
|
|
|
import { SlashCommandClosure } from "./SlashCommandClosure.js";
|
|
|
|
|
import { SlashCommandEnumValue, enumTypes } from "./SlashCommandEnumValue.js";
|
2024-06-17 03:30:52 +02:00
|
|
|
|
import { SlashCommandExecutor } from "./SlashCommandExecutor.js";
|
|
|
|
|
|
2024-06-20 20:33:45 +02:00
|
|
|
|
/**
|
|
|
|
|
* A collection of regularly used enum icons
|
|
|
|
|
*/
|
|
|
|
|
export const enumIcons = {
|
|
|
|
|
default: '◊',
|
|
|
|
|
|
|
|
|
|
// Variables
|
2024-06-22 01:04:03 +02:00
|
|
|
|
variable: '𝑥',
|
2024-06-20 20:33:45 +02:00
|
|
|
|
localVariable: 'L',
|
|
|
|
|
globalVariable: 'G',
|
|
|
|
|
scopeVariable: 'S',
|
|
|
|
|
|
|
|
|
|
// Common types
|
|
|
|
|
character: '👤',
|
|
|
|
|
group: '🧑🤝🧑',
|
2024-06-21 20:04:55 +02:00
|
|
|
|
persona: '🧙♂️',
|
|
|
|
|
qr: 'QR',
|
2024-06-22 01:04:03 +02:00
|
|
|
|
closure: '𝑓',
|
|
|
|
|
macro: '{{',
|
2024-06-20 20:33:45 +02:00
|
|
|
|
tag: '🏷️',
|
|
|
|
|
world: '🌐',
|
|
|
|
|
preset: '⚙️',
|
|
|
|
|
file: '📄',
|
2024-06-21 20:04:55 +02:00
|
|
|
|
message: '💬',
|
2024-06-23 14:43:57 +02:00
|
|
|
|
voice: '🎤',
|
2024-06-20 20:33:45 +02:00
|
|
|
|
|
|
|
|
|
true: '✔️',
|
|
|
|
|
false: '❌',
|
|
|
|
|
|
|
|
|
|
// Value types
|
|
|
|
|
boolean: '🔲',
|
|
|
|
|
string: '📝',
|
|
|
|
|
number: '1️⃣',
|
2024-06-22 01:04:03 +02:00
|
|
|
|
array: '[]',
|
2024-06-20 20:33:45 +02:00
|
|
|
|
enum: '📚',
|
2024-06-22 01:04:03 +02:00
|
|
|
|
dictionary: '{}',
|
2024-06-20 20:33:45 +02:00
|
|
|
|
|
|
|
|
|
// Roles
|
|
|
|
|
system: '⚙️',
|
|
|
|
|
user: '👤',
|
|
|
|
|
assistant: '🤖',
|
|
|
|
|
|
|
|
|
|
// WI Icons
|
|
|
|
|
constant: '🔵',
|
|
|
|
|
normal: '🟢',
|
|
|
|
|
disabled: '❌',
|
|
|
|
|
vectorized: '🔗',
|
|
|
|
|
|
2024-06-21 20:04:55 +02:00
|
|
|
|
/**
|
|
|
|
|
* Returns the appropriate state icon based on a boolean
|
|
|
|
|
*
|
|
|
|
|
* @param {boolean} state - The state to determine the icon for
|
|
|
|
|
* @returns {string} The corresponding state icon
|
|
|
|
|
*/
|
|
|
|
|
getStateIcon: (state) => {
|
|
|
|
|
return state ? enumIcons.true : enumIcons.false;
|
|
|
|
|
},
|
|
|
|
|
|
2024-06-20 20:33:45 +02:00
|
|
|
|
/**
|
|
|
|
|
* Returns the appropriate WI icon based on the entry
|
|
|
|
|
*
|
|
|
|
|
* @param {Object} entry - WI entry
|
|
|
|
|
* @returns {string} The corresponding WI icon
|
|
|
|
|
*/
|
|
|
|
|
getWiStatusIcon: (entry) => {
|
|
|
|
|
if (entry.constant) return enumIcons.constant;
|
|
|
|
|
if (entry.disable) return enumIcons.disabled;
|
|
|
|
|
if (entry.vectorized) return enumIcons.vectorized;
|
|
|
|
|
return enumIcons.normal;
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Returns the appropriate icon based on the role
|
|
|
|
|
*
|
|
|
|
|
* @param {extension_prompt_roles} role - The role to get the icon for
|
|
|
|
|
* @returns {string} The corresponding icon
|
|
|
|
|
*/
|
|
|
|
|
getRoleIcon: (role) => {
|
|
|
|
|
switch (role) {
|
|
|
|
|
case extension_prompt_roles.SYSTEM: return enumIcons.system;
|
|
|
|
|
case extension_prompt_roles.USER: return enumIcons.user;
|
|
|
|
|
case extension_prompt_roles.ASSISTANT: return enumIcons.assistant;
|
|
|
|
|
default: return enumIcons.default;
|
|
|
|
|
}
|
|
|
|
|
},
|
2024-06-21 20:04:55 +02:00
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* A function to get the data type icon
|
|
|
|
|
*
|
|
|
|
|
* @param {string} type - The type of the data
|
|
|
|
|
* @returns {string} The corresponding data type icon
|
|
|
|
|
*/
|
|
|
|
|
getDataTypeIcon: (type) => {
|
|
|
|
|
// Remove possible nullable types definition to match type icon
|
|
|
|
|
type = type.replace(/\?$/, '');
|
|
|
|
|
return enumIcons[type] ?? enumIcons.default;
|
|
|
|
|
}
|
2024-06-20 20:33:45 +02:00
|
|
|
|
}
|
|
|
|
|
|
2024-06-17 03:30:52 +02:00
|
|
|
|
/**
|
|
|
|
|
* A collection of common enum providers
|
|
|
|
|
*
|
|
|
|
|
* Can be used on `SlashCommandNamedArgument` and `SlashCommandArgument` and their `enumProvider` property.
|
|
|
|
|
*/
|
|
|
|
|
export const commonEnumProviders = {
|
2024-06-20 20:33:45 +02:00
|
|
|
|
/**
|
|
|
|
|
* Enum values for booleans. Either using true/false or on/off
|
|
|
|
|
* Optionally supports "toggle".
|
2024-06-21 20:04:55 +02:00
|
|
|
|
*
|
2024-06-20 20:33:45 +02:00
|
|
|
|
* @param {('onOff'|'onOffToggle'|'trueFalse')?} [mode='trueFalse'] - The mode to use. Default is 'trueFalse'.
|
|
|
|
|
* @returns {() => SlashCommandEnumValue[]}
|
|
|
|
|
*/
|
|
|
|
|
boolean: (mode = 'trueFalse') => () => {
|
|
|
|
|
switch (mode) {
|
|
|
|
|
case 'onOff': return [new SlashCommandEnumValue('on', null, 'macro', enumIcons.true), new SlashCommandEnumValue('off', null, 'macro', enumIcons.false)];
|
|
|
|
|
case 'onOffToggle': return [new SlashCommandEnumValue('on', null, 'macro', enumIcons.true), new SlashCommandEnumValue('off', null, 'macro', enumIcons.false), new SlashCommandEnumValue('toggle', null, 'macro', enumIcons.boolean)];
|
|
|
|
|
case 'trueFalse': return [new SlashCommandEnumValue('true', null, 'macro', enumIcons.true), new SlashCommandEnumValue('false', null, 'macro', enumIcons.false)];
|
|
|
|
|
default: throw new Error(`Invalid boolean enum provider mode: ${mode}`);
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
2024-06-17 03:30:52 +02:00
|
|
|
|
/**
|
|
|
|
|
* All possible variable names
|
|
|
|
|
*
|
|
|
|
|
* Can be filtered by `type` to only show global or local variables
|
|
|
|
|
*
|
|
|
|
|
* @param {...('global'|'local'|'scope'|'all')} type - The type of variables to include in the array. Can be 'all', 'global', or 'local'.
|
|
|
|
|
* @returns {() => SlashCommandEnumValue[]}
|
|
|
|
|
*/
|
|
|
|
|
variables: (...type) => () => {
|
|
|
|
|
const types = type.flat();
|
|
|
|
|
const isAll = types.includes('all');
|
|
|
|
|
return [
|
2024-06-21 20:04:55 +02:00
|
|
|
|
...isAll || types.includes('global') ? Object.keys(extension_settings.variables.global ?? []).map(name => new SlashCommandEnumValue(name, null, enumTypes.macro, enumIcons.globalVariable)) : [],
|
|
|
|
|
...isAll || types.includes('local') ? Object.keys(chat_metadata.variables ?? []).map(name => new SlashCommandEnumValue(name, null, enumTypes.name, enumIcons.localVariable)) : [],
|
|
|
|
|
...isAll || types.includes('scope') ? [].map(name => new SlashCommandEnumValue(name, null, enumTypes.variable, enumIcons.scopeVariable)) : [], // TODO: Add scoped variables here, Lenny
|
2024-06-17 03:30:52 +02:00
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
/**
|
2024-06-17 07:04:10 +02:00
|
|
|
|
* All possible char entities, like characters and groups. Can be filtered down to just one type.
|
2024-06-17 03:30:52 +02:00
|
|
|
|
*
|
2024-06-17 07:04:10 +02:00
|
|
|
|
* @param {('all' | 'character' | 'group')?} [mode='all'] - Which type to return
|
|
|
|
|
* @returns {() => SlashCommandEnumValue[]}
|
2024-06-17 03:30:52 +02:00
|
|
|
|
*/
|
2024-06-21 20:04:55 +02:00
|
|
|
|
characters: (mode = 'all') => () => {
|
2024-06-17 07:04:10 +02:00
|
|
|
|
return [
|
2024-06-21 20:04:55 +02:00
|
|
|
|
...['all', 'character'].includes(mode) ? characters.map(char => new SlashCommandEnumValue(char.name, null, enumTypes.name, enumIcons.character)) : [],
|
|
|
|
|
...['all', 'group'].includes(mode) ? groups.map(group => new SlashCommandEnumValue(group.name, null, enumTypes.qr, enumIcons.group)) : [],
|
2024-06-17 07:04:10 +02:00
|
|
|
|
];
|
|
|
|
|
},
|
2024-06-17 03:30:52 +02:00
|
|
|
|
|
2024-06-21 20:04:55 +02:00
|
|
|
|
/**
|
|
|
|
|
* All group members of the given group, or default the current active one
|
|
|
|
|
*
|
|
|
|
|
* @param {string?} groupId - The id of the group - pass in `undefined` to use the current active group
|
|
|
|
|
* @returns {() =>SlashCommandEnumValue[]}
|
|
|
|
|
*/
|
|
|
|
|
groupMembers: (groupId = undefined) => () => getGroupMembers(groupId).map((character, index) => new SlashCommandEnumValue(String(index), character.name, enumTypes.enum, enumIcons.character)),
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* All possible personas
|
|
|
|
|
*
|
|
|
|
|
* @returns {SlashCommandEnumValue[]}
|
|
|
|
|
*/
|
|
|
|
|
personas: () => Object.values(power_user.personas).map(persona => new SlashCommandEnumValue(persona, null, enumTypes.name, enumIcons.persona)),
|
|
|
|
|
|
2024-06-17 03:30:52 +02:00
|
|
|
|
/**
|
|
|
|
|
* All possible tags for a given char/group entity
|
|
|
|
|
*
|
2024-06-17 07:04:10 +02:00
|
|
|
|
* @param {('all' | 'existing' | 'not-existing')?} [mode='all'] - Which types of tags to show
|
2024-06-17 03:30:52 +02:00
|
|
|
|
* @returns {() => SlashCommandEnumValue[]}
|
|
|
|
|
*/
|
2024-06-20 20:33:45 +02:00
|
|
|
|
tagsForChar: (mode = 'all') => (/** @type {SlashCommandExecutor} */ executor) => {
|
2024-06-17 03:30:52 +02:00
|
|
|
|
// Try to see if we can find the char during execution to filter down the tags list some more. Otherwise take all tags.
|
2024-06-20 20:33:45 +02:00
|
|
|
|
const charName = executor.namedArgumentList.find(it => it.name == 'name')?.value;
|
|
|
|
|
if (charName instanceof SlashCommandClosure) throw new Error('Argument \'name\' does not support closures');
|
|
|
|
|
const key = searchCharByName(substituteParams(charName), { suppressLogging: true });
|
2024-06-17 03:30:52 +02:00
|
|
|
|
const assigned = key ? getTagsList(key) : [];
|
|
|
|
|
return tags.filter(it => !key || mode === 'all' || mode === 'existing' && assigned.includes(it) || mode === 'not-existing' && !assigned.includes(it))
|
2024-06-21 20:04:55 +02:00
|
|
|
|
.map(tag => new SlashCommandEnumValue(tag.name, null, enumTypes.command, enumIcons.tag));
|
2024-06-17 03:30:52 +02:00
|
|
|
|
},
|
|
|
|
|
|
2024-06-20 20:33:45 +02:00
|
|
|
|
/**
|
|
|
|
|
* All messages in the current chat, returning the message id
|
2024-06-21 20:04:55 +02:00
|
|
|
|
*
|
|
|
|
|
* Optionally supports variable names, and/or a placeholder for the last/new message id
|
|
|
|
|
*
|
|
|
|
|
* @param {object} [options={}] - Optional arguments
|
|
|
|
|
* @param {boolean} [options.allowIdAfter=false] - Whether to add an enum option for the new message id after the last message
|
|
|
|
|
* @param {boolean} [options.allowVars=false] - Whether to add enum option for variable names
|
|
|
|
|
* @returns {() => SlashCommandEnumValue[]}
|
2024-06-20 20:33:45 +02:00
|
|
|
|
*/
|
2024-06-21 20:04:55 +02:00
|
|
|
|
messages: ({ allowIdAfter = false, allowVars = false } = {}) => () => {
|
|
|
|
|
return [
|
|
|
|
|
...chat.map((message, index) => new SlashCommandEnumValue(String(index), `${message.name}: ${message.mes}`, enumTypes.number, message.is_user ? enumIcons.user : message.is_system ? enumIcons.system : enumIcons.assistant)),
|
|
|
|
|
...allowIdAfter ? [new SlashCommandEnumValue(String(chat.length), '>> After Last Message >>', enumTypes.enum, '➕')] : [],
|
|
|
|
|
...allowVars ? commonEnumProviders.variables('all')() : [],
|
|
|
|
|
];
|
|
|
|
|
},
|
2024-06-20 20:33:45 +02:00
|
|
|
|
|
2024-06-17 03:30:52 +02:00
|
|
|
|
/**
|
|
|
|
|
* All existing worlds / lorebooks
|
|
|
|
|
*
|
|
|
|
|
* @returns {SlashCommandEnumValue[]}
|
|
|
|
|
*/
|
2024-06-20 20:33:45 +02:00
|
|
|
|
worlds: () => $('#world_info').children().toArray().map(x => new SlashCommandEnumValue(x.textContent, null, enumTypes.name, enumIcons.world)),
|
2024-06-17 03:30:52 +02:00
|
|
|
|
|
2024-06-21 20:04:55 +02:00
|
|
|
|
/**
|
|
|
|
|
* All existing injects for the current chat
|
|
|
|
|
*
|
|
|
|
|
* @returns {SlashCommandEnumValue[]}
|
|
|
|
|
*/
|
|
|
|
|
injects: () => {
|
|
|
|
|
if (!chat_metadata.script_injects || !Object.keys(chat_metadata.script_injects).length) return [];
|
|
|
|
|
return Object.entries(chat_metadata.script_injects)
|
|
|
|
|
.map(([id, inject]) => {
|
|
|
|
|
const positionName = (Object.entries(extension_prompt_types)).find(([_, value]) => value === inject.position)?.[0] ?? 'unknown';
|
|
|
|
|
return new SlashCommandEnumValue(id, `${enumIcons.getRoleIcon(inject.role ?? extension_prompt_roles.SYSTEM)}[Inject](${positionName}, depth: ${inject.depth}, scan: ${inject.scan ?? false}) ${inject.value}`,
|
|
|
|
|
enumTypes.enum, '💉');
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
};
|