diff --git a/public/script.js b/public/script.js
index aa6e5f16b..7666b500b 100644
--- a/public/script.js
+++ b/public/script.js
@@ -101,6 +101,7 @@ import {
proxies,
loadProxyPresets,
selected_proxy,
+ initOpenai,
} from './scripts/openai.js';
import {
@@ -155,7 +156,6 @@ import {
ensureImageFormatSupported,
flashHighlight,
isTrueBoolean,
- debouncedThrottle,
} from './scripts/utils.js';
import { debounce_timeout } from './scripts/constants.js';
@@ -915,6 +915,7 @@ async function firstLoadInit() {
initKeyboard();
initDynamicStyles();
initTags();
+ initOpenai();
await getUserAvatars(true, user_avatar);
await getCharacters();
await getBackgrounds();
@@ -9233,11 +9234,12 @@ jQuery(async function () {
*/
function autoFitEditTextArea(e) {
scroll_holder = chatElement[0].scrollTop;
- e.style.height = '0';
- e.style.height = `${e.scrollHeight + 4}px`;
+ e.style.height = '0px';
+ const newHeight = e.scrollHeight + 4;
+ e.style.height = `${newHeight}px`;
is_use_scroll_holder = true;
}
- const autoFitEditTextAreaDebounced = debouncedThrottle(autoFitEditTextArea, debounce_timeout.standard);
+ const autoFitEditTextAreaDebounced = debounce(autoFitEditTextArea, debounce_timeout.short);
document.addEventListener('input', e => {
if (e.target instanceof HTMLTextAreaElement && e.target.classList.contains('edit_textarea')) {
const immediately = e.target.scrollHeight > e.target.offsetHeight || e.target.value === '';
diff --git a/public/scripts/RossAscends-mods.js b/public/scripts/RossAscends-mods.js
index dceba9063..e248ca713 100644
--- a/public/scripts/RossAscends-mods.js
+++ b/public/scripts/RossAscends-mods.js
@@ -696,18 +696,18 @@ const isFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1;
function autoFitSendTextArea() {
const originalScrollBottom = chatBlock.scrollHeight - (chatBlock.scrollTop + chatBlock.offsetHeight);
if (Math.ceil(sendTextArea.scrollHeight + 3) >= Math.floor(sendTextArea.offsetHeight)) {
- // Needs to be pulled dynamically because it is affected by font size changes
- const sendTextAreaMinHeight = window.getComputedStyle(sendTextArea).getPropertyValue('min-height');
+ const sendTextAreaMinHeight = '0px';
sendTextArea.style.height = sendTextAreaMinHeight;
}
- sendTextArea.style.height = sendTextArea.scrollHeight + 3 + 'px';
+ const newHeight = sendTextArea.scrollHeight + 3;
+ sendTextArea.style.height = `${newHeight}px`;
if (!isFirefox) {
const newScrollTop = Math.round(chatBlock.scrollHeight - (chatBlock.offsetHeight + originalScrollBottom));
chatBlock.scrollTop = newScrollTop;
}
}
-export const autoFitSendTextAreaDebounced = debounce(autoFitSendTextArea);
+export const autoFitSendTextAreaDebounced = debounce(autoFitSendTextArea, debounce_timeout.short);
// ---------------------------------------------------
@@ -882,7 +882,8 @@ export function initRossMods() {
});
$(sendTextArea).on('input', () => {
- if (sendTextArea.scrollHeight > sendTextArea.offsetHeight || sendTextArea.value === '') {
+ const scrollbarShown = sendTextArea.clientWidth < sendTextArea.offsetWidth && sendTextArea.offsetHeight >= window.innerHeight / 2;
+ if ((sendTextArea.scrollHeight > sendTextArea.offsetHeight && !scrollbarShown) || sendTextArea.value === '') {
autoFitSendTextArea();
} else {
autoFitSendTextAreaDebounced();
diff --git a/public/scripts/extensions/caption/index.js b/public/scripts/extensions/caption/index.js
index b47f73a2f..dbfbc0d1d 100644
--- a/public/scripts/extensions/caption/index.js
+++ b/public/scripts/extensions/caption/index.js
@@ -333,8 +333,9 @@ async function getCaptionForFile(file, prompt, quiet) {
return caption;
}
catch (error) {
- toastr.error('Failed to caption image.');
- console.log(error);
+ const errorMessage = error.message || 'Unknown error';
+ toastr.error(errorMessage, "Failed to caption image.");
+ console.error(error);
return '';
}
finally {
diff --git a/public/scripts/extensions/memory/index.js b/public/scripts/extensions/memory/index.js
index 2bb5c1269..0bc2ff577 100644
--- a/public/scripts/extensions/memory/index.js
+++ b/public/scripts/extensions/memory/index.js
@@ -914,7 +914,7 @@ jQuery(async function () {
await addExtensionControls();
loadSettings();
- eventSource.on(event_types.MESSAGE_RECEIVED, onChatEvent);
+ eventSource.makeLast(event_types.CHARACTER_MESSAGE_RENDERED, onChatEvent);
eventSource.on(event_types.MESSAGE_DELETED, onChatEvent);
eventSource.on(event_types.MESSAGE_EDITED, onChatEvent);
eventSource.on(event_types.MESSAGE_SWIPED, onChatEvent);
diff --git a/public/scripts/extensions/quick-reply/index.js b/public/scripts/extensions/quick-reply/index.js
index b0f496126..ab6044bf0 100644
--- a/public/scripts/extensions/quick-reply/index.js
+++ b/public/scripts/extensions/quick-reply/index.js
@@ -239,7 +239,7 @@ eventSource.on(event_types.CHAT_CHANGED, (...args)=>executeIfReadyElseQueue(onCh
const onUserMessage = async () => {
await autoExec.handleUser();
};
-eventSource.on(event_types.USER_MESSAGE_RENDERED, (...args)=>executeIfReadyElseQueue(onUserMessage, args));
+eventSource.makeFirst(event_types.USER_MESSAGE_RENDERED, (...args)=>executeIfReadyElseQueue(onUserMessage, args));
const onAiMessage = async (messageId) => {
if (['...'].includes(chat[messageId]?.mes)) {
@@ -249,7 +249,7 @@ const onAiMessage = async (messageId) => {
await autoExec.handleAi();
};
-eventSource.on(event_types.CHARACTER_MESSAGE_RENDERED, (...args)=>executeIfReadyElseQueue(onAiMessage, args));
+eventSource.makeFirst(event_types.CHARACTER_MESSAGE_RENDERED, (...args)=>executeIfReadyElseQueue(onAiMessage, args));
const onGroupMemberDraft = async () => {
await autoExec.handleGroupMemberDraft();
diff --git a/public/scripts/openai.js b/public/scripts/openai.js
index 391c0ee19..ba5cbcee1 100644
--- a/public/scripts/openai.js
+++ b/public/scripts/openai.js
@@ -4744,22 +4744,23 @@ function runProxyCallback(_, value) {
return foundName;
}
-SlashCommandParser.addCommandObject(SlashCommand.fromProps({
- name: 'proxy',
- callback: runProxyCallback,
- returns: 'current proxy',
- namedArgumentList: [],
- unnamedArgumentList: [
- SlashCommandArgument.fromProps({
- description: 'name',
- typeList: [ARGUMENT_TYPE.STRING],
- isRequired: true,
- enumProvider: () => proxies.map(preset => new SlashCommandEnumValue(preset.name, preset.url)),
- }),
- ],
- helpString: 'Sets a proxy preset by name.',
-}));
-
+export function initOpenai() {
+ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
+ name: 'proxy',
+ callback: runProxyCallback,
+ returns: 'current proxy',
+ namedArgumentList: [],
+ unnamedArgumentList: [
+ SlashCommandArgument.fromProps({
+ description: 'name',
+ typeList: [ARGUMENT_TYPE.STRING],
+ isRequired: true,
+ enumProvider: () => proxies.map(preset => new SlashCommandEnumValue(preset.name, preset.url)),
+ }),
+ ],
+ helpString: 'Sets a proxy preset by name.',
+ }));
+}
$(document).ready(async function () {
$('#test_api_button').on('click', testApiConnection);
diff --git a/public/scripts/popup.js b/public/scripts/popup.js
index 8def3d352..c1cfc942a 100644
--- a/public/scripts/popup.js
+++ b/public/scripts/popup.js
@@ -194,7 +194,7 @@ export class Popup {
const buttonElement = document.createElement('div');
buttonElement.classList.add('menu_button', 'popup-button-custom', 'result-control');
buttonElement.classList.add(...(button.classes ?? []));
- buttonElement.dataset.result = String(button.result ?? undefined);
+ buttonElement.dataset.result = String(button.result); // This is expected to also write 'null' or 'staging', to indicate cancel and no action respectively
buttonElement.textContent = button.text;
buttonElement.dataset.i18n = buttonElement.textContent;
buttonElement.tabIndex = 0;
@@ -317,9 +317,14 @@ export class Popup {
// Bind event listeners for all result controls to their defined event type
this.dlg.querySelectorAll('[data-result]').forEach(resultControl => {
if (!(resultControl instanceof HTMLElement)) return;
- const result = Number(resultControl.dataset.result);
- if (String(undefined) === String(resultControl.dataset.result)) return;
- if (isNaN(result)) throw new Error('Invalid result control. Result must be a number. ' + resultControl.dataset.result);
+ // If no value was set, we exit out and don't bind an action
+ if (String(resultControl.dataset.result) === String(undefined)) return;
+
+ // Make sure that both `POPUP_RESULT` numbers and also `null` as 'cancelled' are supported
+ const result = String(resultControl.dataset.result) === String(null) ? null
+ : Number(resultControl.dataset.result);
+
+ if (result !== null && isNaN(result)) throw new Error('Invalid result control. Result must be a number. ' + resultControl.dataset.result);
const type = resultControl.dataset.resultEvent || 'click';
resultControl.addEventListener(type, async () => await this.complete(result));
});
diff --git a/public/scripts/power-user.js b/public/scripts/power-user.js
index f57de63ff..08fbfb504 100644
--- a/public/scripts/power-user.js
+++ b/public/scripts/power-user.js
@@ -2734,45 +2734,26 @@ async function doDelMode(_, text) {
return '';
}
- //first enter delmode
- $('#option_delete_mes').trigger('click', { fromSlashCommand: true });
-
- //parse valid args
- if (text) {
- await delay(300); //same as above, need event signal for 'entered del mode'
- console.debug('parsing msgs to del');
- let numMesToDel = Number(text);
- let lastMesID = Number($('#chat .mes').last().attr('mesid'));
- let oldestMesIDToDel = lastMesID - numMesToDel + 1;
-
- if (oldestMesIDToDel < 0) {
- toastr.warning(`Cannot delete more than ${chat.length} messages.`);
- return '';
- }
-
- let oldestMesToDel = $('#chat').find(`.mes[mesid=${oldestMesIDToDel}]`);
-
- if (!oldestMesIDToDel && lastMesID > 0) {
- oldestMesToDel = await loadUntilMesId(oldestMesIDToDel);
-
- if (!oldestMesToDel || !oldestMesToDel.length) {
- return '';
- }
- }
-
- let oldestDelMesCheckbox = $(oldestMesToDel).find('.del_checkbox');
- let newLastMesID = oldestMesIDToDel - 1;
- console.debug(`DelMesReport -- numMesToDel: ${numMesToDel}, lastMesID: ${lastMesID}, oldestMesIDToDel:${oldestMesIDToDel}, newLastMesID: ${newLastMesID}`);
- oldestDelMesCheckbox.trigger('click');
- let trueNumberOfDeletedMessage = lastMesID - oldestMesIDToDel + 1;
-
- //await delay(1)
- $('#dialogue_del_mes_ok').trigger('click');
- toastr.success(`Deleted ${trueNumberOfDeletedMessage} messages.`);
+ // Just enter the delete mode.
+ if (!text) {
+ $('#option_delete_mes').trigger('click', { fromSlashCommand: true });
return '';
}
- return '';
+ const count = Number(text);
+
+ // Nothing to delete.
+ if (count < 1) {
+ return '';
+ }
+
+ if (count > chat.length) {
+ toastr.warning(`Cannot delete more than ${chat.length} messages.`);
+ return '';
+ }
+
+ const range = `${chat.length - count}-${chat.length - 1}`;
+ return doMesCut(_, range);
}
function doResetPanels() {
diff --git a/public/scripts/preset-manager.js b/public/scripts/preset-manager.js
index 90fe232ba..5d25140dc 100644
--- a/public/scripts/preset-manager.js
+++ b/public/scripts/preset-manager.js
@@ -1,6 +1,5 @@
import {
amount_gen,
- callPopup,
characters,
eventSource,
event_types,
@@ -19,6 +18,7 @@ import {
import { groups, selected_group } from './group-chats.js';
import { instruct_presets } from './instruct-mode.js';
import { kai_settings } from './kai-settings.js';
+import { Popup } from './popup.js';
import { context_presets, getContextSettings, power_user } from './power-user.js';
import { SlashCommand } from './slash-commands/SlashCommand.js';
import { ARGUMENT_TYPE, SlashCommandArgument } from './slash-commands/SlashCommandArgument.js';
@@ -165,11 +165,8 @@ class PresetManager {
async savePresetAs() {
const inputValue = this.getSelectedPresetName();
- const popupText = `
-
Preset name:
- ${!this.isNonGenericApi() ? 'Hint: Use a character/group name to bind preset to a specific chat.
' : ''}`;
- const name = await callPopup(popupText, 'input', inputValue);
-
+ const popupText = !this.isNonGenericApi() ? 'Hint: Use a character/group name to bind preset to a specific chat.
' : '';
+ const name = await Popup.show.input('Preset name:', popupText, inputValue);
if (!name) {
console.log('Preset name not provided');
return;
@@ -372,7 +369,7 @@ class PresetManager {
if (Object.keys(preset_names).length) {
const nextPresetName = Object.keys(preset_names)[0];
const newValue = preset_names[nextPresetName];
- $(this.select).find(`option[value="${newValue}"]`).attr('selected', true);
+ $(this.select).find(`option[value="${newValue}"]`).attr('selected', 'true');
$(this.select).trigger('change');
}
@@ -597,8 +594,7 @@ export async function initPresetManager() {
return;
}
- const confirm = await callPopup('Delete the preset? This action is irreversible and your current settings will be overwritten.', 'confirm');
-
+ const confirm = await Popup.show.confirm('Delete the preset?', 'This action is irreversible and your current settings will be overwritten.');
if (!confirm) {
return;
}
@@ -641,8 +637,7 @@ export async function initPresetManager() {
return;
}
- const confirm = await callPopup('Are you sure?
Resetting a default preset will restore the default settings.', 'confirm');
-
+ const confirm = await Popup.show.confirm('Are you sure?', 'Resetting a default preset will restore the default settings.');
if (!confirm) {
return;
}
@@ -653,8 +648,7 @@ export async function initPresetManager() {
presetManager.selectPreset(option);
toastr.success('Default preset restored');
} else {
- const confirm = await callPopup('Are you sure?
Resetting a custom preset will restore to the last saved state.', 'confirm');
-
+ const confirm = await Popup.show.confirm('Are you sure?', 'Resetting a custom preset will restore to the last saved state.');
if (!confirm) {
return;
}
diff --git a/public/scripts/slash-commands.js b/public/scripts/slash-commands.js
index 533ca5e36..71d8c894a 100644
--- a/public/scripts/slash-commands.js
+++ b/public/scripts/slash-commands.js
@@ -952,14 +952,36 @@ export function initDefaultSlashCommands() {
SlashCommandParser.addCommandObject(SlashCommand.fromProps({
name: 'fuzzy',
callback: fuzzyCallback,
- returns: 'first matching item',
+ returns: 'matching item',
namedArgumentList: [
- new SlashCommandNamedArgument(
- 'list', 'list of items to match against', [ARGUMENT_TYPE.LIST], true,
- ),
- new SlashCommandNamedArgument(
- 'threshold', 'fuzzy match threshold (0.0 to 1.0)', [ARGUMENT_TYPE.NUMBER], false, false, '0.4',
- ),
+ SlashCommandNamedArgument.fromProps({
+ name: 'list',
+ description: 'list of items to match against',
+ acceptsMultiple: false,
+ isRequired: true,
+ typeList: [ARGUMENT_TYPE.LIST, ARGUMENT_TYPE.VARIABLE_NAME],
+ enumProvider: commonEnumProviders.variables('all'),
+ }),
+ SlashCommandNamedArgument.fromProps({
+ name: 'threshold',
+ description: 'fuzzy match threshold (0.0 to 1.0)',
+ typeList: [ARGUMENT_TYPE.NUMBER],
+ isRequired: false,
+ defaultValue: '0.4',
+ acceptsMultiple: false,
+ }),
+ SlashCommandNamedArgument.fromProps({
+ name: 'mode',
+ description: 'fuzzy match mode',
+ typeList: [ARGUMENT_TYPE.STRING],
+ isRequired: false,
+ defaultValue: 'first',
+ acceptsMultiple: false,
+ enumList: [
+ new SlashCommandEnumValue('first', 'first match below the threshold', enumTypes.enum, enumIcons.default),
+ new SlashCommandEnumValue('best', 'best match below the threshold', enumTypes.enum, enumIcons.default),
+ ],
+ }),
],
unnamedArgumentList: [
new SlashCommandArgument(
@@ -976,6 +998,13 @@ export function initDefaultSlashCommands() {
A low value (min 0.0) means the match is very strict.
At 1.0 (max) the match is very loose and will match anything.
+
+ The optional
mode
argument allows to control the behavior when multiple items match the text.
+
+ first
(default) returns the first match below the threshold.
+ best
returns the best match below the threshold.
+
+
The returned value passes to the next command through the pipe.
@@ -1865,7 +1894,7 @@ async function inputCallback(args, prompt) {
* @param {FuzzyCommandArgs} args - arguments containing "list" (JSON array) and optionaly "threshold" (float between 0.0 and 1.0)
* @param {string} searchInValue - the string where items of list are searched
* @returns {string} - the matched item from the list
- * @typedef {{list: string, threshold: string}} FuzzyCommandArgs - arguments for /fuzzy command
+ * @typedef {{list: string, threshold: string, mode:string}} FuzzyCommandArgs - arguments for /fuzzy command
* @example /fuzzy list=["down","left","up","right"] "he looks up" | /echo // should return "up"
* @link https://www.fusejs.io/
*/
@@ -1895,7 +1924,7 @@ function fuzzyCallback(args, searchInValue) {
};
// threshold determines how strict is the match, low threshold value is very strict, at 1 (nearly?) everything matches
if ('threshold' in args) {
- params.threshold = parseFloat(resolveVariable(args.threshold));
+ params.threshold = parseFloat(args.threshold);
if (isNaN(params.threshold)) {
console.warn('WARN: \'threshold\' argument must be a float between 0.0 and 1.0 for /fuzzy command');
return '';
@@ -1908,16 +1937,42 @@ function fuzzyCallback(args, searchInValue) {
}
}
- const fuse = new Fuse([searchInValue], params);
- // each item in the "list" is searched within "search_item", if any matches it returns the matched "item"
- for (const searchItem of list) {
- const result = fuse.search(searchItem);
- if (result.length > 0) {
- console.info('fuzzyCallback Matched: ' + searchItem);
- return searchItem;
+ function getFirstMatch() {
+ const fuse = new Fuse([searchInValue], params);
+ // each item in the "list" is searched within "search_item", if any matches it returns the matched "item"
+ for (const searchItem of list) {
+ const result = fuse.search(searchItem);
+ console.debug('/fuzzy: result', result);
+ if (result.length > 0) {
+ console.info('/fuzzy: first matched', searchItem);
+ return searchItem;
+ }
}
+
+ console.info('/fuzzy: no match');
+ return '';
+ }
+
+ function getBestMatch() {
+ const fuse = new Fuse(list, params);
+ const result = fuse.search(searchInValue);
+ console.debug('/fuzzy: result', result);
+ if (result.length > 0) {
+ console.info('/fuzzy: best matched', result[0].item);
+ return result[0].item;
+ }
+
+ console.info('/fuzzy: no match');
+ return '';
+ }
+
+ switch (String(args.mode).trim().toLowerCase()) {
+ case 'best':
+ return getBestMatch();
+ case 'first':
+ default:
+ return getFirstMatch();
}
- return '';
} catch {
console.warn('WARN: Invalid list argument provided for /fuzzy command');
return '';
diff --git a/public/scripts/slash-commands/SlashCommandCommonEnumsProvider.js b/public/scripts/slash-commands/SlashCommandCommonEnumsProvider.js
index 683f28ffa..0d9aae5c6 100644
--- a/public/scripts/slash-commands/SlashCommandCommonEnumsProvider.js
+++ b/public/scripts/slash-commands/SlashCommandCommonEnumsProvider.js
@@ -1,11 +1,11 @@
-import { chat_metadata, characters, substituteParams, chat, extension_prompt_roles, extension_prompt_types } from "../../script.js";
-import { extension_settings } from "../extensions.js";
-import { getGroupMembers, groups, selected_group } from "../group-chats.js";
-import { power_user } from "../power-user.js";
-import { searchCharByName, getTagsList, tags } from "../tags.js";
-import { SlashCommandClosure } from "./SlashCommandClosure.js";
-import { SlashCommandEnumValue, enumTypes } from "./SlashCommandEnumValue.js";
-import { SlashCommandExecutor } from "./SlashCommandExecutor.js";
+import { chat_metadata, characters, substituteParams, chat, extension_prompt_roles, extension_prompt_types } from '../../script.js';
+import { extension_settings } from '../extensions.js';
+import { getGroupMembers, groups } from '../group-chats.js';
+import { power_user } from '../power-user.js';
+import { searchCharByName, getTagsList, tags } from '../tags.js';
+import { world_names } from '../world-info.js';
+import { SlashCommandClosure } from './SlashCommandClosure.js';
+import { SlashCommandEnumValue, enumTypes } from './SlashCommandEnumValue.js';
/**
* A collection of regularly used enum icons
@@ -103,8 +103,8 @@ export const enumIcons = {
// Remove possible nullable types definition to match type icon
type = type.replace(/\?$/, '');
return enumIcons[type] ?? enumIcons.default;
- }
-}
+ },
+};
/**
* A collection of common enum providers
@@ -143,7 +143,7 @@ export const commonEnumProviders = {
...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
- ]
+ ];
},
/**
@@ -180,7 +180,7 @@ export const commonEnumProviders = {
* @param {('all' | 'existing' | 'not-existing')?} [mode='all'] - Which types of tags to show
* @returns {() => SlashCommandEnumValue[]}
*/
- tagsForChar: (mode = 'all') => (/** @type {SlashCommandExecutor} */ executor) => {
+ tagsForChar: (mode = 'all') => (/** @type {import('./SlashCommandExecutor.js').SlashCommandExecutor} */ executor) => {
// Try to see if we can find the char during execution to filter down the tags list some more. Otherwise take all tags.
const charName = executor.namedArgumentList.find(it => it.name == 'name')?.value;
if (charName instanceof SlashCommandClosure) throw new Error('Argument \'name\' does not support closures');
@@ -213,7 +213,7 @@ export const commonEnumProviders = {
*
* @returns {SlashCommandEnumValue[]}
*/
- worlds: () => $('#world_info').children().toArray().map(x => new SlashCommandEnumValue(x.textContent, null, enumTypes.name, enumIcons.world)),
+ worlds: () => world_names.map(worldName => new SlashCommandEnumValue(worldName, null, enumTypes.name, enumIcons.world)),
/**
* All existing injects for the current chat
diff --git a/public/scripts/world-info.js b/public/scripts/world-info.js
index 355ac676b..d497e8020 100644
--- a/public/scripts/world-info.js
+++ b/public/scripts/world-info.js
@@ -14,7 +14,6 @@ import { SlashCommand } from './slash-commands/SlashCommand.js';
import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from './slash-commands/SlashCommandArgument.js';
import { SlashCommandEnumValue, enumTypes } from './slash-commands/SlashCommandEnumValue.js';
import { commonEnumProviders, enumIcons } from './slash-commands/SlashCommandCommonEnumsProvider.js';
-import { SlashCommandExecutor } from './slash-commands/SlashCommandExecutor.js';
import { SlashCommandClosure } from './slash-commands/SlashCommandClosure.js';
import { callGenericPopup, Popup, POPUP_TYPE } from './popup.js';
@@ -1215,7 +1214,7 @@ function registerWorldInfoSlashCommands() {
enumTypes.enum, enumIcons.getDataTypeIcon(value.type))),
/** All existing UIDs based on the file argument as world name */
- wiUids: (/** @type {SlashCommandExecutor} */ executor) => {
+ wiUids: (/** @type {import('./slash-commands/SlashCommandExecutor.js').SlashCommandExecutor} */ executor) => {
const file = executor.namedArgumentList.find(it => it.name == 'file')?.value;
if (file instanceof SlashCommandClosure) throw new Error('Argument \'file\' does not support closures');
// Try find world from cache
@@ -3161,7 +3160,8 @@ function duplicateWorldInfoEntry(data, uid) {
}
// Exclude uid and gather the rest of the properties
- const { uid: _, ...originalData } = data.entries[uid];
+ const originalData = Object.assign({}, data.entries[uid]);
+ delete originalData.uid;
// Create new entry and copy over data
const entry = createWorldInfoEntry(data.name, data);
@@ -4326,8 +4326,9 @@ function onWorldInfoChange(args, text) {
$('#world_info').val(null).trigger('change');
}
} else { //if it's a pointer selection
- let tempWorldInfo = [];
- let selectedWorlds = $('#world_info').val().map((e) => Number(e)).filter((e) => !isNaN(e));
+ const tempWorldInfo = [];
+ const val = $('#world_info').val();
+ const selectedWorlds = (Array.isArray(val) ? val : [val]).map((e) => Number(e)).filter((e) => !isNaN(e));
if (selectedWorlds.length > 0) {
selectedWorlds.forEach((worldIndex) => {
const existingWorldName = world_names[worldIndex];