Merge branch 'staging' into proxy-confirm

22831d55175696e67373a2e4288ce580dd1a4243

Cohee <18619528+Cohee1207@users.noreply.github.com>

12 files changed, +155 -114Ignore whitespace
public/script.js+6 -4
@@ -101,6 +101,7 @@ import {
101 proxies,101 proxies,
102 loadProxyPresets,102 loadProxyPresets,
103 selected_proxy,103 selected_proxy,
104 initOpenai,
104} from './scripts/openai.js';105} from './scripts/openai.js';
105106
106import {107import {
@@ -155,7 +156,6 @@ import {
155 ensureImageFormatSupported,156 ensureImageFormatSupported,
156 flashHighlight,157 flashHighlight,
157 isTrueBoolean,158 isTrueBoolean,
158 debouncedThrottle,
159} from './scripts/utils.js';159} from './scripts/utils.js';
160import { debounce_timeout } from './scripts/constants.js';160import { debounce_timeout } from './scripts/constants.js';
161161
@@ -915,6 +915,7 @@ async function firstLoadInit() {
915 initKeyboard();915 initKeyboard();
916 initDynamicStyles();916 initDynamicStyles();
917 initTags();917 initTags();
918 initOpenai();
918 await getUserAvatars(true, user_avatar);919 await getUserAvatars(true, user_avatar);
919 await getCharacters();920 await getCharacters();
920 await getBackgrounds();921 await getBackgrounds();
@@ -9233,11 +9234,12 @@ jQuery(async function () {
9233 */9234 */
9234 function autoFitEditTextArea(e) {9235 function autoFitEditTextArea(e) {
9235 scroll_holder = chatElement[0].scrollTop;9236 scroll_holder = chatElement[0].scrollTop;
9236 e.style.height = '0';9237 e.style.height = '0px';
9237 e.style.height = `${e.scrollHeight + 4}px`;9238 const newHeight = e.scrollHeight + 4;
9239 e.style.height = `${newHeight}px`;
9238 is_use_scroll_holder = true;9240 is_use_scroll_holder = true;
9239 }9241 }
9240 const autoFitEditTextAreaDebounced = debouncedThrottle(autoFitEditTextArea, debounce_timeout.standard);9242 const autoFitEditTextAreaDebounced = debounce(autoFitEditTextArea, debounce_timeout.short);
9241 document.addEventListener('input', e => {9243 document.addEventListener('input', e => {
9242 if (e.target instanceof HTMLTextAreaElement && e.target.classList.contains('edit_textarea')) {9244 if (e.target instanceof HTMLTextAreaElement && e.target.classList.contains('edit_textarea')) {
9243 const immediately = e.target.scrollHeight > e.target.offsetHeight || e.target.value === '';9245 const immediately = e.target.scrollHeight > e.target.offsetHeight || e.target.value === '';
public/scripts/RossAscends-mods.js+6 -5
@@ -696,18 +696,18 @@ const isFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1;
696function autoFitSendTextArea() {696function autoFitSendTextArea() {
697 const originalScrollBottom = chatBlock.scrollHeight - (chatBlock.scrollTop + chatBlock.offsetHeight);697 const originalScrollBottom = chatBlock.scrollHeight - (chatBlock.scrollTop + chatBlock.offsetHeight);
698 if (Math.ceil(sendTextArea.scrollHeight + 3) >= Math.floor(sendTextArea.offsetHeight)) {698 if (Math.ceil(sendTextArea.scrollHeight + 3) >= Math.floor(sendTextArea.offsetHeight)) {
699 // Needs to be pulled dynamically because it is affected by font size changes699 const sendTextAreaMinHeight = '0px';
700 const sendTextAreaMinHeight = window.getComputedStyle(sendTextArea).getPropertyValue('min-height');
701 sendTextArea.style.height = sendTextAreaMinHeight;700 sendTextArea.style.height = sendTextAreaMinHeight;
702 }701 }
703 sendTextArea.style.height = sendTextArea.scrollHeight + 3 + 'px';702 const newHeight = sendTextArea.scrollHeight + 3;
703 sendTextArea.style.height = `${newHeight}px`;
704704
705 if (!isFirefox) {705 if (!isFirefox) {
706 const newScrollTop = Math.round(chatBlock.scrollHeight - (chatBlock.offsetHeight + originalScrollBottom));706 const newScrollTop = Math.round(chatBlock.scrollHeight - (chatBlock.offsetHeight + originalScrollBottom));
707 chatBlock.scrollTop = newScrollTop;707 chatBlock.scrollTop = newScrollTop;
708 }708 }
709}709}
710export const autoFitSendTextAreaDebounced = debounce(autoFitSendTextArea);710export const autoFitSendTextAreaDebounced = debounce(autoFitSendTextArea, debounce_timeout.short);
711711
712// ---------------------------------------------------712// ---------------------------------------------------
713713
@@ -882,7 +882,8 @@ export function initRossMods() {
882 });882 });
883883
884 $(sendTextArea).on('input', () => {884 $(sendTextArea).on('input', () => {
885 if (sendTextArea.scrollHeight > sendTextArea.offsetHeight || sendTextArea.value === '') {885 const scrollbarShown = sendTextArea.clientWidth < sendTextArea.offsetWidth && sendTextArea.offsetHeight >= window.innerHeight / 2;
886 if ((sendTextArea.scrollHeight > sendTextArea.offsetHeight && !scrollbarShown) || sendTextArea.value === '') {
886 autoFitSendTextArea();887 autoFitSendTextArea();
887 } else {888 } else {
888 autoFitSendTextAreaDebounced();889 autoFitSendTextAreaDebounced();
public/scripts/extensions/caption/index.js+3 -2
@@ -333,8 +333,9 @@ async function getCaptionForFile(file, prompt, quiet) {
333 return caption;333 return caption;
334 }334 }
335 catch (error) {335 catch (error) {
336 toastr.error('Failed to caption image.');336 const errorMessage = error.message || 'Unknown error';
337 console.log(error);337 toastr.error(errorMessage, "Failed to caption image.");
338 console.error(error);
338 return '';339 return '';
339 }340 }
340 finally {341 finally {
public/scripts/extensions/memory/index.js+1 -1
@@ -914,7 +914,7 @@ jQuery(async function () {
914914
915 await addExtensionControls();915 await addExtensionControls();
916 loadSettings();916 loadSettings();
917 eventSource.on(event_types.MESSAGE_RECEIVED, onChatEvent);917 eventSource.makeLast(event_types.CHARACTER_MESSAGE_RENDERED, onChatEvent);
918 eventSource.on(event_types.MESSAGE_DELETED, onChatEvent);918 eventSource.on(event_types.MESSAGE_DELETED, onChatEvent);
919 eventSource.on(event_types.MESSAGE_EDITED, onChatEvent);919 eventSource.on(event_types.MESSAGE_EDITED, onChatEvent);
920 eventSource.on(event_types.MESSAGE_SWIPED, onChatEvent);920 eventSource.on(event_types.MESSAGE_SWIPED, onChatEvent);
public/scripts/extensions/quick-reply/index.js+2 -2
@@ -239,7 +239,7 @@ eventSource.on(event_types.CHAT_CHANGED, (...args)=>executeIfReadyElseQueue(onCh
239const onUserMessage = async () => {239const onUserMessage = async () => {
240 await autoExec.handleUser();240 await autoExec.handleUser();
241};241};
242eventSource.on(event_types.USER_MESSAGE_RENDERED, (...args)=>executeIfReadyElseQueue(onUserMessage, args));242eventSource.makeFirst(event_types.USER_MESSAGE_RENDERED, (...args)=>executeIfReadyElseQueue(onUserMessage, args));
243243
244const onAiMessage = async (messageId) => {244const onAiMessage = async (messageId) => {
245 if (['...'].includes(chat[messageId]?.mes)) {245 if (['...'].includes(chat[messageId]?.mes)) {
@@ -249,7 +249,7 @@ const onAiMessage = async (messageId) => {
249249
250 await autoExec.handleAi();250 await autoExec.handleAi();
251};251};
252eventSource.on(event_types.CHARACTER_MESSAGE_RENDERED, (...args)=>executeIfReadyElseQueue(onAiMessage, args));252eventSource.makeFirst(event_types.CHARACTER_MESSAGE_RENDERED, (...args)=>executeIfReadyElseQueue(onAiMessage, args));
253253
254const onGroupMemberDraft = async () => {254const onGroupMemberDraft = async () => {
255 await autoExec.handleGroupMemberDraft();255 await autoExec.handleGroupMemberDraft();
public/scripts/openai.js+17 -16
@@ -4744,22 +4744,23 @@ function runProxyCallback(_, value) {
4744 return foundName;4744 return foundName;
4745}4745}
47464746
4747SlashCommandParser.addCommandObject(SlashCommand.fromProps({4747export function initOpenai() {
4748 name: 'proxy',4748 SlashCommandParser.addCommandObject(SlashCommand.fromProps({
4749 callback: runProxyCallback,4749 name: 'proxy',
4750 returns: 'current proxy',4750 callback: runProxyCallback,
4751 namedArgumentList: [],4751 returns: 'current proxy',
4752 unnamedArgumentList: [4752 namedArgumentList: [],
4753 SlashCommandArgument.fromProps({4753 unnamedArgumentList: [
4754 description: 'name',4754 SlashCommandArgument.fromProps({
4755 typeList: [ARGUMENT_TYPE.STRING],4755 description: 'name',
4756 isRequired: true,4756 typeList: [ARGUMENT_TYPE.STRING],
4757 enumProvider: () => proxies.map(preset => new SlashCommandEnumValue(preset.name, preset.url)),4757 isRequired: true,
4758 }),4758 enumProvider: () => proxies.map(preset => new SlashCommandEnumValue(preset.name, preset.url)),
4759 ],4759 }),
4760 helpString: 'Sets a proxy preset by name.',4760 ],
4761}));4761 helpString: 'Sets a proxy preset by name.',
47624762 }));
4763}
47634764
4764$(document).ready(async function () {4765$(document).ready(async function () {
4765 $('#test_api_button').on('click', testApiConnection);4766 $('#test_api_button').on('click', testApiConnection);
public/scripts/popup.js+9 -4
@@ -194,7 +194,7 @@ export class Popup {
194 const buttonElement = document.createElement('div');194 const buttonElement = document.createElement('div');
195 buttonElement.classList.add('menu_button', 'popup-button-custom', 'result-control');195 buttonElement.classList.add('menu_button', 'popup-button-custom', 'result-control');
196 buttonElement.classList.add(...(button.classes ?? []));196 buttonElement.classList.add(...(button.classes ?? []));
197 buttonElement.dataset.result = String(button.result ?? undefined);197 buttonElement.dataset.result = String(button.result); // This is expected to also write 'null' or 'staging', to indicate cancel and no action respectively
198 buttonElement.textContent = button.text;198 buttonElement.textContent = button.text;
199 buttonElement.dataset.i18n = buttonElement.textContent;199 buttonElement.dataset.i18n = buttonElement.textContent;
200 buttonElement.tabIndex = 0;200 buttonElement.tabIndex = 0;
@@ -317,9 +317,14 @@ export class Popup {
317 // Bind event listeners for all result controls to their defined event type317 // Bind event listeners for all result controls to their defined event type
318 this.dlg.querySelectorAll('[data-result]').forEach(resultControl => {318 this.dlg.querySelectorAll('[data-result]').forEach(resultControl => {
319 if (!(resultControl instanceof HTMLElement)) return;319 if (!(resultControl instanceof HTMLElement)) return;
320 const result = Number(resultControl.dataset.result);320 // If no value was set, we exit out and don't bind an action
321 if (String(undefined) === String(resultControl.dataset.result)) return;321 if (String(resultControl.dataset.result) === String(undefined)) return;
322 if (isNaN(result)) throw new Error('Invalid result control. Result must be a number. ' + resultControl.dataset.result);322
323 // Make sure that both `POPUP_RESULT` numbers and also `null` as 'cancelled' are supported
324 const result = String(resultControl.dataset.result) === String(null) ? null
325 : Number(resultControl.dataset.result);
326
327 if (result !== null && isNaN(result)) throw new Error('Invalid result control. Result must be a number. ' + resultControl.dataset.result);
323 const type = resultControl.dataset.resultEvent || 'click';328 const type = resultControl.dataset.resultEvent || 'click';
324 resultControl.addEventListener(type, async () => await this.complete(result));329 resultControl.addEventListener(type, async () => await this.complete(result));
325 });330 });
public/scripts/power-user.js+14 -33
@@ -2734,45 +2734,26 @@ async function doDelMode(_, text) {
2734 return '';2734 return '';
2735 }2735 }
27362736
2737 //first enter delmode2737 // Just enter the delete mode.
2738 $('#option_delete_mes').trigger('click', { fromSlashCommand: true });2738 if (!text) {
27392739 $('#option_delete_mes').trigger('click', { fromSlashCommand: true });
2740 //parse valid args2740 return '';
2741 if (text) {2741 }
2742 await delay(300); //same as above, need event signal for 'entered del mode'
2743 console.debug('parsing msgs to del');
2744 let numMesToDel = Number(text);
2745 let lastMesID = Number($('#chat .mes').last().attr('mesid'));
2746 let oldestMesIDToDel = lastMesID - numMesToDel + 1;
2747
2748 if (oldestMesIDToDel < 0) {
2749 toastr.warning(`Cannot delete more than ${chat.length} messages.`);
2750 return '';
2751 }
2752
2753 let oldestMesToDel = $('#chat').find(`.mes[mesid=${oldestMesIDToDel}]`);
27542742
2755 if (!oldestMesIDToDel && lastMesID > 0) {2743 const count = Number(text);
2756 oldestMesToDel = await loadUntilMesId(oldestMesIDToDel);
27572744
2758 if (!oldestMesToDel || !oldestMesToDel.length) {2745 // Nothing to delete.
2759 return '';2746 if (count < 1) {
2760 }2747 return '';
2761 }2748 }
2762
2763 let oldestDelMesCheckbox = $(oldestMesToDel).find('.del_checkbox');
2764 let newLastMesID = oldestMesIDToDel - 1;
2765 console.debug(`DelMesReport -- numMesToDel: ${numMesToDel}, lastMesID: ${lastMesID}, oldestMesIDToDel:${oldestMesIDToDel}, newLastMesID: ${newLastMesID}`);
2766 oldestDelMesCheckbox.trigger('click');
2767 let trueNumberOfDeletedMessage = lastMesID - oldestMesIDToDel + 1;
27682749
2769 //await delay(1)2750 if (count > chat.length) {
2770 $('#dialogue_del_mes_ok').trigger('click');2751 toastr.warning(`Cannot delete more than ${chat.length} messages.`);
2771 toastr.success(`Deleted ${trueNumberOfDeletedMessage} messages.`);
2772 return '';2752 return '';
2773 }2753 }
27742754
2775 return '';2755 const range = `${chat.length - count}-${chat.length - 1}`;
2756 return doMesCut(_, range);
2776}2757}
27772758
2778function doResetPanels() {2759function doResetPanels() {
public/scripts/preset-manager.js+7 -13
@@ -1,6 +1,5 @@
1import {1import {
2 amount_gen,2 amount_gen,
3 callPopup,
4 characters,3 characters,
5 eventSource,4 eventSource,
6 event_types,5 event_types,
@@ -19,6 +18,7 @@ import {
19import { groups, selected_group } from './group-chats.js';18import { groups, selected_group } from './group-chats.js';
20import { instruct_presets } from './instruct-mode.js';19import { instruct_presets } from './instruct-mode.js';
21import { kai_settings } from './kai-settings.js';20import { kai_settings } from './kai-settings.js';
21import { Popup } from './popup.js';
22import { context_presets, getContextSettings, power_user } from './power-user.js';22import { context_presets, getContextSettings, power_user } from './power-user.js';
23import { SlashCommand } from './slash-commands/SlashCommand.js';23import { SlashCommand } from './slash-commands/SlashCommand.js';
24import { ARGUMENT_TYPE, SlashCommandArgument } from './slash-commands/SlashCommandArgument.js';24import { ARGUMENT_TYPE, SlashCommandArgument } from './slash-commands/SlashCommandArgument.js';
@@ -165,11 +165,8 @@ class PresetManager {
165165
166 async savePresetAs() {166 async savePresetAs() {
167 const inputValue = this.getSelectedPresetName();167 const inputValue = this.getSelectedPresetName();
168 const popupText = `168 const popupText = !this.isNonGenericApi() ? '<h4>Hint: Use a character/group name to bind preset to a specific chat.</h4>' : '';
169 <h3>Preset name:</h3>169 const name = await Popup.show.input('Preset name:', popupText, inputValue);
170 ${!this.isNonGenericApi() ? '<h4>Hint: Use a character/group name to bind preset to a specific chat.</h4>' : ''}`;
171 const name = await callPopup(popupText, 'input', inputValue);
172
173 if (!name) {170 if (!name) {
174 console.log('Preset name not provided');171 console.log('Preset name not provided');
175 return;172 return;
@@ -372,7 +369,7 @@ class PresetManager {
372 if (Object.keys(preset_names).length) {369 if (Object.keys(preset_names).length) {
373 const nextPresetName = Object.keys(preset_names)[0];370 const nextPresetName = Object.keys(preset_names)[0];
374 const newValue = preset_names[nextPresetName];371 const newValue = preset_names[nextPresetName];
375 $(this.select).find(`option[value="${newValue}"]`).attr('selected', true);372 $(this.select).find(`option[value="${newValue}"]`).attr('selected', 'true');
376 $(this.select).trigger('change');373 $(this.select).trigger('change');
377 }374 }
378375
@@ -597,8 +594,7 @@ export async function initPresetManager() {
597 return;594 return;
598 }595 }
599596
600 const confirm = await callPopup('Delete the preset? This action is irreversible and your current settings will be overwritten.', 'confirm');597 const confirm = await Popup.show.confirm('Delete the preset?', 'This action is irreversible and your current settings will be overwritten.');
601
602 if (!confirm) {598 if (!confirm) {
603 return;599 return;
604 }600 }
@@ -641,8 +637,7 @@ export async function initPresetManager() {
641 return;637 return;
642 }638 }
643639
644 const confirm = await callPopup('<h3>Are you sure?</h3>Resetting a <b>default preset</b> will restore the default settings.', 'confirm');640 const confirm = await Popup.show.confirm('Are you sure?', 'Resetting a <b>default preset</b> will restore the default settings.');
645
646 if (!confirm) {641 if (!confirm) {
647 return;642 return;
648 }643 }
@@ -653,8 +648,7 @@ export async function initPresetManager() {
653 presetManager.selectPreset(option);648 presetManager.selectPreset(option);
654 toastr.success('Default preset restored');649 toastr.success('Default preset restored');
655 } else {650 } else {
656 const confirm = await callPopup('<h3>Are you sure?</h3>Resetting a <b>custom preset</b> will restore to the last saved state.', 'confirm');651 const confirm = await Popup.show.confirm('Are you sure?', 'Resetting a <b>custom preset</b> will restore to the last saved state.');
657
658 if (!confirm) {652 if (!confirm) {
659 return;653 return;
660 }654 }
public/scripts/slash-commands.js+71 -16
@@ -952,14 +952,36 @@ export function initDefaultSlashCommands() {
952 SlashCommandParser.addCommandObject(SlashCommand.fromProps({952 SlashCommandParser.addCommandObject(SlashCommand.fromProps({
953 name: 'fuzzy',953 name: 'fuzzy',
954 callback: fuzzyCallback,954 callback: fuzzyCallback,
955 returns: 'first matching item',955 returns: 'matching item',
956 namedArgumentList: [956 namedArgumentList: [
957 new SlashCommandNamedArgument(957 SlashCommandNamedArgument.fromProps({
958 'list', 'list of items to match against', [ARGUMENT_TYPE.LIST], true,958 name: 'list',
959 ),959 description: 'list of items to match against',
960 new SlashCommandNamedArgument(960 acceptsMultiple: false,
961 'threshold', 'fuzzy match threshold (0.0 to 1.0)', [ARGUMENT_TYPE.NUMBER], false, false, '0.4',961 isRequired: true,
962 ),962 typeList: [ARGUMENT_TYPE.LIST, ARGUMENT_TYPE.VARIABLE_NAME],
963 enumProvider: commonEnumProviders.variables('all'),
964 }),
965 SlashCommandNamedArgument.fromProps({
966 name: 'threshold',
967 description: 'fuzzy match threshold (0.0 to 1.0)',
968 typeList: [ARGUMENT_TYPE.NUMBER],
969 isRequired: false,
970 defaultValue: '0.4',
971 acceptsMultiple: false,
972 }),
973 SlashCommandNamedArgument.fromProps({
974 name: 'mode',
975 description: 'fuzzy match mode',
976 typeList: [ARGUMENT_TYPE.STRING],
977 isRequired: false,
978 defaultValue: 'first',
979 acceptsMultiple: false,
980 enumList: [
981 new SlashCommandEnumValue('first', 'first match below the threshold', enumTypes.enum, enumIcons.default),
982 new SlashCommandEnumValue('best', 'best match below the threshold', enumTypes.enum, enumIcons.default),
983 ],
984 }),
963 ],985 ],
964 unnamedArgumentList: [986 unnamedArgumentList: [
965 new SlashCommandArgument(987 new SlashCommandArgument(
@@ -977,6 +999,13 @@ export function initDefaultSlashCommands() {
977 At 1.0 (max) the match is very loose and will match anything.999 At 1.0 (max) the match is very loose and will match anything.
978 </div>1000 </div>
979 <div>1001 <div>
1002 The optional <code>mode</code> argument allows to control the behavior when multiple items match the text.
1003 <ul>
1004 <li><code>first</code> (default) returns the first match below the threshold.</li>
1005 <li><code>best</code> returns the best match below the threshold.</li>
1006 </ul>
1007 </div>
1008 <div>
980 The returned value passes to the next command through the pipe.1009 The returned value passes to the next command through the pipe.
981 </div>1010 </div>
982 <div>1011 <div>
@@ -1865,7 +1894,7 @@ async function inputCallback(args, prompt) {
1865 * @param {FuzzyCommandArgs} args - arguments containing "list" (JSON array) and optionaly "threshold" (float between 0.0 and 1.0)1894 * @param {FuzzyCommandArgs} args - arguments containing "list" (JSON array) and optionaly "threshold" (float between 0.0 and 1.0)
1866 * @param {string} searchInValue - the string where items of list are searched1895 * @param {string} searchInValue - the string where items of list are searched
1867 * @returns {string} - the matched item from the list1896 * @returns {string} - the matched item from the list
1868 * @typedef {{list: string, threshold: string}} FuzzyCommandArgs - arguments for /fuzzy command1897 * @typedef {{list: string, threshold: string, mode:string}} FuzzyCommandArgs - arguments for /fuzzy command
1869 * @example /fuzzy list=["down","left","up","right"] "he looks up" | /echo // should return "up"1898 * @example /fuzzy list=["down","left","up","right"] "he looks up" | /echo // should return "up"
1870 * @link https://www.fusejs.io/1899 * @link https://www.fusejs.io/
1871 */1900 */
@@ -1895,7 +1924,7 @@ function fuzzyCallback(args, searchInValue) {
1895 };1924 };
1896 // threshold determines how strict is the match, low threshold value is very strict, at 1 (nearly?) everything matches1925 // threshold determines how strict is the match, low threshold value is very strict, at 1 (nearly?) everything matches
1897 if ('threshold' in args) {1926 if ('threshold' in args) {
1898 params.threshold = parseFloat(resolveVariable(args.threshold));1927 params.threshold = parseFloat(args.threshold);
1899 if (isNaN(params.threshold)) {1928 if (isNaN(params.threshold)) {
1900 console.warn('WARN: \'threshold\' argument must be a float between 0.0 and 1.0 for /fuzzy command');1929 console.warn('WARN: \'threshold\' argument must be a float between 0.0 and 1.0 for /fuzzy command');
1901 return '';1930 return '';
@@ -1908,16 +1937,42 @@ function fuzzyCallback(args, searchInValue) {
1908 }1937 }
1909 }1938 }
19101939
1911 const fuse = new Fuse([searchInValue], params);1940 function getFirstMatch() {
1912 // each item in the "list" is searched within "search_item", if any matches it returns the matched "item"1941 const fuse = new Fuse([searchInValue], params);
1913 for (const searchItem of list) {1942 // each item in the "list" is searched within "search_item", if any matches it returns the matched "item"
1914 const result = fuse.search(searchItem);1943 for (const searchItem of list) {
1944 const result = fuse.search(searchItem);
1945 console.debug('/fuzzy: result', result);
1946 if (result.length > 0) {
1947 console.info('/fuzzy: first matched', searchItem);
1948 return searchItem;
1949 }
1950 }
1951
1952 console.info('/fuzzy: no match');
1953 return '';
1954 }
1955
1956 function getBestMatch() {
1957 const fuse = new Fuse(list, params);
1958 const result = fuse.search(searchInValue);
1959 console.debug('/fuzzy: result', result);
1915 if (result.length > 0) {1960 if (result.length > 0) {
1916 console.info('fuzzyCallback Matched: ' + searchItem);1961 console.info('/fuzzy: best matched', result[0].item);
1917 return searchItem;1962 return result[0].item;
1918 }1963 }
1964
1965 console.info('/fuzzy: no match');
1966 return '';
1967 }
1968
1969 switch (String(args.mode).trim().toLowerCase()) {
1970 case 'best':
1971 return getBestMatch();
1972 case 'first':
1973 default:
1974 return getFirstMatch();
1919 }1975 }
1920 return '';
1921 } catch {1976 } catch {
1922 console.warn('WARN: Invalid list argument provided for /fuzzy command');1977 console.warn('WARN: Invalid list argument provided for /fuzzy command');
1923 return '';1978 return '';
public/scripts/slash-commands/SlashCommandCommonEnumsProvider.js+13 -13
@@ -1,11 +1,11 @@
1import { chat_metadata, characters, substituteParams, chat, extension_prompt_roles, extension_prompt_types } from "../../script.js";1import { chat_metadata, characters, substituteParams, chat, extension_prompt_roles, extension_prompt_types } from '../../script.js';
2import { extension_settings } from "../extensions.js";2import { extension_settings } from '../extensions.js';
3import { getGroupMembers, groups, selected_group } from "../group-chats.js";3import { getGroupMembers, groups } from '../group-chats.js';
4import { power_user } from "../power-user.js";4import { power_user } from '../power-user.js';
5import { searchCharByName, getTagsList, tags } from "../tags.js";5import { searchCharByName, getTagsList, tags } from '../tags.js';
6import { SlashCommandClosure } from "./SlashCommandClosure.js";6import { world_names } from '../world-info.js';
7import { SlashCommandEnumValue, enumTypes } from "./SlashCommandEnumValue.js";7import { SlashCommandClosure } from './SlashCommandClosure.js';
8import { SlashCommandExecutor } from "./SlashCommandExecutor.js";8import { SlashCommandEnumValue, enumTypes } from './SlashCommandEnumValue.js';
99
10/**10/**
11 * A collection of regularly used enum icons11 * A collection of regularly used enum icons
@@ -103,8 +103,8 @@ export const enumIcons = {
103 // Remove possible nullable types definition to match type icon103 // Remove possible nullable types definition to match type icon
104 type = type.replace(/\?$/, '');104 type = type.replace(/\?$/, '');
105 return enumIcons[type] ?? enumIcons.default;105 return enumIcons[type] ?? enumIcons.default;
106 }106 },
107}107};
108108
109/**109/**
110 * A collection of common enum providers110 * A collection of common enum providers
@@ -143,7 +143,7 @@ export const commonEnumProviders = {
143 ...isAll || types.includes('global') ? Object.keys(extension_settings.variables.global ?? []).map(name => new SlashCommandEnumValue(name, null, enumTypes.macro, enumIcons.globalVariable)) : [],143 ...isAll || types.includes('global') ? Object.keys(extension_settings.variables.global ?? []).map(name => new SlashCommandEnumValue(name, null, enumTypes.macro, enumIcons.globalVariable)) : [],
144 ...isAll || types.includes('local') ? Object.keys(chat_metadata.variables ?? []).map(name => new SlashCommandEnumValue(name, null, enumTypes.name, enumIcons.localVariable)) : [],144 ...isAll || types.includes('local') ? Object.keys(chat_metadata.variables ?? []).map(name => new SlashCommandEnumValue(name, null, enumTypes.name, enumIcons.localVariable)) : [],
145 ...isAll || types.includes('scope') ? [].map(name => new SlashCommandEnumValue(name, null, enumTypes.variable, enumIcons.scopeVariable)) : [], // TODO: Add scoped variables here, Lenny145 ...isAll || types.includes('scope') ? [].map(name => new SlashCommandEnumValue(name, null, enumTypes.variable, enumIcons.scopeVariable)) : [], // TODO: Add scoped variables here, Lenny
146 ]146 ];
147 },147 },
148148
149 /**149 /**
@@ -180,7 +180,7 @@ export const commonEnumProviders = {
180 * @param {('all' | 'existing' | 'not-existing')?} [mode='all'] - Which types of tags to show180 * @param {('all' | 'existing' | 'not-existing')?} [mode='all'] - Which types of tags to show
181 * @returns {() => SlashCommandEnumValue[]}181 * @returns {() => SlashCommandEnumValue[]}
182 */182 */
183 tagsForChar: (mode = 'all') => (/** @type {SlashCommandExecutor} */ executor) => {183 tagsForChar: (mode = 'all') => (/** @type {import('./SlashCommandExecutor.js').SlashCommandExecutor} */ executor) => {
184 // Try to see if we can find the char during execution to filter down the tags list some more. Otherwise take all tags.184 // Try to see if we can find the char during execution to filter down the tags list some more. Otherwise take all tags.
185 const charName = executor.namedArgumentList.find(it => it.name == 'name')?.value;185 const charName = executor.namedArgumentList.find(it => it.name == 'name')?.value;
186 if (charName instanceof SlashCommandClosure) throw new Error('Argument \'name\' does not support closures');186 if (charName instanceof SlashCommandClosure) throw new Error('Argument \'name\' does not support closures');
@@ -213,7 +213,7 @@ export const commonEnumProviders = {
213 *213 *
214 * @returns {SlashCommandEnumValue[]}214 * @returns {SlashCommandEnumValue[]}
215 */215 */
216 worlds: () => $('#world_info').children().toArray().map(x => new SlashCommandEnumValue(x.textContent, null, enumTypes.name, enumIcons.world)),216 worlds: () => world_names.map(worldName => new SlashCommandEnumValue(worldName, null, enumTypes.name, enumIcons.world)),
217217
218 /**218 /**
219 * All existing injects for the current chat219 * All existing injects for the current chat
public/scripts/world-info.js+6 -5
@@ -14,7 +14,6 @@ import { SlashCommand } from './slash-commands/SlashCommand.js';
14import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from './slash-commands/SlashCommandArgument.js';14import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from './slash-commands/SlashCommandArgument.js';
15import { SlashCommandEnumValue, enumTypes } from './slash-commands/SlashCommandEnumValue.js';15import { SlashCommandEnumValue, enumTypes } from './slash-commands/SlashCommandEnumValue.js';
16import { commonEnumProviders, enumIcons } from './slash-commands/SlashCommandCommonEnumsProvider.js';16import { commonEnumProviders, enumIcons } from './slash-commands/SlashCommandCommonEnumsProvider.js';
17import { SlashCommandExecutor } from './slash-commands/SlashCommandExecutor.js';
18import { SlashCommandClosure } from './slash-commands/SlashCommandClosure.js';17import { SlashCommandClosure } from './slash-commands/SlashCommandClosure.js';
19import { callGenericPopup, Popup, POPUP_TYPE } from './popup.js';18import { callGenericPopup, Popup, POPUP_TYPE } from './popup.js';
2019
@@ -1215,7 +1214,7 @@ function registerWorldInfoSlashCommands() {
1215 enumTypes.enum, enumIcons.getDataTypeIcon(value.type))),1214 enumTypes.enum, enumIcons.getDataTypeIcon(value.type))),
12161215
1217 /** All existing UIDs based on the file argument as world name */1216 /** All existing UIDs based on the file argument as world name */
1218 wiUids: (/** @type {SlashCommandExecutor} */ executor) => {1217 wiUids: (/** @type {import('./slash-commands/SlashCommandExecutor.js').SlashCommandExecutor} */ executor) => {
1219 const file = executor.namedArgumentList.find(it => it.name == 'file')?.value;1218 const file = executor.namedArgumentList.find(it => it.name == 'file')?.value;
1220 if (file instanceof SlashCommandClosure) throw new Error('Argument \'file\' does not support closures');1219 if (file instanceof SlashCommandClosure) throw new Error('Argument \'file\' does not support closures');
1221 // Try find world from cache1220 // Try find world from cache
@@ -3161,7 +3160,8 @@ function duplicateWorldInfoEntry(data, uid) {
3161 }3160 }
31623161
3163 // Exclude uid and gather the rest of the properties3162 // Exclude uid and gather the rest of the properties
3164 const { uid: _, ...originalData } = data.entries[uid];3163 const originalData = Object.assign({}, data.entries[uid]);
3164 delete originalData.uid;
31653165
3166 // Create new entry and copy over data3166 // Create new entry and copy over data
3167 const entry = createWorldInfoEntry(data.name, data);3167 const entry = createWorldInfoEntry(data.name, data);
@@ -4326,8 +4326,9 @@ function onWorldInfoChange(args, text) {
4326 $('#world_info').val(null).trigger('change');4326 $('#world_info').val(null).trigger('change');
4327 }4327 }
4328 } else { //if it's a pointer selection4328 } else { //if it's a pointer selection
4329 let tempWorldInfo = [];4329 const tempWorldInfo = [];
4330 let selectedWorlds = $('#world_info').val().map((e) => Number(e)).filter((e) => !isNaN(e));4330 const val = $('#world_info').val();
4331 const selectedWorlds = (Array.isArray(val) ? val : [val]).map((e) => Number(e)).filter((e) => !isNaN(e));
4331 if (selectedWorlds.length > 0) {4332 if (selectedWorlds.length > 0) {
4332 selectedWorlds.forEach((worldIndex) => {4333 selectedWorlds.forEach((worldIndex) => {
4333 const existingWorldName = world_names[worldIndex];4334 const existingWorldName = world_names[worldIndex];