Merge branch 'staging' into proxy-confirm
| @@ -101,6 +101,7 @@ import { | ||
| 101 | 101 | proxies, |
| 102 | 102 | loadProxyPresets, |
| 103 | 103 | selected_proxy, |
| 104 | + initOpenai, | |
| 104 | 105 | } from './scripts/openai.js'; |
| 105 | 106 | |
| 106 | 107 | import { |
| @@ -155,7 +156,6 @@ import { | ||
| 155 | 156 | ensureImageFormatSupported, |
| 156 | 157 | flashHighlight, |
| 157 | 158 | isTrueBoolean, |
| 158 | - debouncedThrottle, | |
| 159 | 159 | } from './scripts/utils.js'; |
| 160 | 160 | import { debounce_timeout } from './scripts/constants.js'; |
| 161 | 161 | |
| @@ -915,6 +915,7 @@ async function firstLoadInit() { | ||
| 915 | 915 | initKeyboard(); |
| 916 | 916 | initDynamicStyles(); |
| 917 | 917 | initTags(); |
| 918 | + initOpenai(); | |
| 918 | 919 | await getUserAvatars(true, user_avatar); |
| 919 | 920 | await getCharacters(); |
| 920 | 921 | await getBackgrounds(); |
| @@ -9233,11 +9234,12 @@ jQuery(async function () { | ||
| 9233 | 9234 | */ |
| 9234 | 9235 | function autoFitEditTextArea(e) { |
| 9235 | 9236 | scroll_holder = chatElement[0].scrollTop; |
| 9236 | 9237 | e.style.height = '00px'; |
| 9237 | 9238 | e.style.heightconst newHeight = `${e.scrollHeight + 4}px`; |
| 9239 | + e.style.height = `${newHeight}px`; | |
| 9238 | 9240 | is_use_scroll_holder = true; |
| 9239 | 9241 | } |
| 9240 | 9242 | const autoFitEditTextAreaDebounced = debouncedThrottledebounce(autoFitEditTextArea, debounce_timeout.standardshort); |
| 9241 | 9243 | document.addEventListener('input', e => { |
| 9242 | 9244 | if (e.target instanceof HTMLTextAreaElement && e.target.classList.contains('edit_textarea')) { |
| 9243 | 9245 | const immediately = e.target.scrollHeight > e.target.offsetHeight || e.target.value === ''; |
| @@ -696,18 +696,18 @@ const isFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1; | ||
| 696 | 696 | function autoFitSendTextArea() { |
| 697 | 697 | const originalScrollBottom = chatBlock.scrollHeight - (chatBlock.scrollTop + chatBlock.offsetHeight); |
| 698 | 698 | if (Math.ceil(sendTextArea.scrollHeight + 3) >= Math.floor(sendTextArea.offsetHeight)) { |
| 699 | - // Needs to be pulled dynamically because it is affected by font size changes | |
| 699 | + const sendTextAreaMinHeight = '0px'; | |
| 700 | - const sendTextAreaMinHeight = window.getComputedStyle(sendTextArea).getPropertyValue('min-height'); | |
| 701 | 700 | sendTextArea.style.height = sendTextAreaMinHeight; |
| 702 | 701 | } |
| 703 | 702 | sendTextArea.style.heightconst newHeight = sendTextArea.scrollHeight + 3 + 'px'; |
| 703 | + sendTextArea.style.height = `${newHeight}px`; | |
| 704 | 704 | |
| 705 | 705 | if (!isFirefox) { |
| 706 | 706 | const newScrollTop = Math.round(chatBlock.scrollHeight - (chatBlock.offsetHeight + originalScrollBottom)); |
| 707 | 707 | chatBlock.scrollTop = newScrollTop; |
| 708 | 708 | } |
| 709 | 709 | } |
| 710 | 710 | export const autoFitSendTextAreaDebounced = debounce(autoFitSendTextArea, debounce_timeout.short); |
| 711 | 711 | |
| 712 | 712 | // --------------------------------------------------- |
| 713 | 713 | |
| @@ -882,7 +882,8 @@ export function initRossMods() { | ||
| 882 | 882 | }); |
| 883 | 883 | |
| 884 | 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 | 887 | autoFitSendTextArea(); |
| 887 | 888 | } else { |
| 888 | 889 | autoFitSendTextAreaDebounced(); |
| @@ -333,8 +333,9 @@ async function getCaptionForFile(file, prompt, quiet) { | ||
| 333 | 333 | return caption; |
| 334 | 334 | } |
| 335 | 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 | 339 | return ''; |
| 339 | 340 | } |
| 340 | 341 | finally { |
| @@ -914,7 +914,7 @@ jQuery(async function () { | ||
| 914 | 914 | |
| 915 | 915 | await addExtensionControls(); |
| 916 | 916 | loadSettings(); |
| 917 | 917 | eventSource.onmakeLast(event_types.MESSAGE_RECEIVEDCHARACTER_MESSAGE_RENDERED, onChatEvent); |
| 918 | 918 | eventSource.on(event_types.MESSAGE_DELETED, onChatEvent); |
| 919 | 919 | eventSource.on(event_types.MESSAGE_EDITED, onChatEvent); |
| 920 | 920 | eventSource.on(event_types.MESSAGE_SWIPED, onChatEvent); |
| @@ -239,7 +239,7 @@ eventSource.on(event_types.CHAT_CHANGED, (...args)=>executeIfReadyElseQueue(onCh | ||
| 239 | 239 | const onUserMessage = async () => { |
| 240 | 240 | await autoExec.handleUser(); |
| 241 | 241 | }; |
| 242 | 242 | eventSource.onmakeFirst(event_types.USER_MESSAGE_RENDERED, (...args)=>executeIfReadyElseQueue(onUserMessage, args)); |
| 243 | 243 | |
| 244 | 244 | const onAiMessage = async (messageId) => { |
| 245 | 245 | if (['...'].includes(chat[messageId]?.mes)) { |
| @@ -249,7 +249,7 @@ const onAiMessage = async (messageId) => { | ||
| 249 | 249 | |
| 250 | 250 | await autoExec.handleAi(); |
| 251 | 251 | }; |
| 252 | 252 | eventSource.onmakeFirst(event_types.CHARACTER_MESSAGE_RENDERED, (...args)=>executeIfReadyElseQueue(onAiMessage, args)); |
| 253 | 253 | |
| 254 | 254 | const onGroupMemberDraft = async () => { |
| 255 | 255 | await autoExec.handleGroupMemberDraft(); |
| @@ -4744,22 +4744,23 @@ function runProxyCallback(_, value) { | ||
| 4744 | 4744 | return foundName; |
| 4745 | 4745 | } |
| 4746 | 4746 | |
| 4747 | -SlashCommandParser.addCommandObject(SlashCommand.fromProps({ | |
| 4747 | +export 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 | 4752 | unnamedArgumentList 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.', | |
| 4762 | - | |
| 4762 | + })); | |
| 4763 | +} | |
| 4763 | 4764 | |
| 4764 | 4765 | $(document).ready(async function () { |
| 4765 | 4766 | $('#test_api_button').on('click', testApiConnection); |
| @@ -194,7 +194,7 @@ export class Popup { | ||
| 194 | 194 | const buttonElement = document.createElement('div'); |
| 195 | 195 | buttonElement.classList.add('menu_button', 'popup-button-custom', 'result-control'); |
| 196 | 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 | 198 | buttonElement.textContent = button.text; |
| 199 | 199 | buttonElement.dataset.i18n = buttonElement.textContent; |
| 200 | 200 | buttonElement.tabIndex = 0; |
| @@ -317,9 +317,14 @@ export class Popup { | ||
| 317 | 317 | // Bind event listeners for all result controls to their defined event type |
| 318 | 318 | this.dlg.querySelectorAll('[data-result]').forEach(resultControl => { |
| 319 | 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 | 321 | if (String(undefinedresultControl.dataset.result) === String(resultControl.dataset.resultundefined)) 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 | 328 | const type = resultControl.dataset.resultEvent || 'click'; |
| 324 | 329 | resultControl.addEventListener(type, async () => await this.complete(result)); |
| 325 | 330 | }); |
| @@ -2734,45 +2734,26 @@ async function doDelMode(_, text) { | ||
| 2734 | 2734 | return ''; |
| 2735 | 2735 | } |
| 2736 | 2736 | |
| 2737 | 2737 | //first Just enter delmodethe delete mode. |
| 2738 | - $('#option_delete_mes').trigger('click', { fromSlashCommand: true }); | |
| 2738 | + if (!text) { | |
| 2739 | - | |
| 2739 | + $('#option_delete_mes').trigger('click', { fromSlashCommand: true }); | |
| 2740 | - //parse valid args | |
| 2740 | + 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}]`); | |
| 2754 | 2742 | |
| 2755 | - if (!oldestMesIDToDel && lastMesID > 0) { | |
| 2743 | + const count = Number(text); | |
| 2756 | - oldestMesToDel = await loadUntilMesId(oldestMesIDToDel); | |
| 2757 | 2744 | |
| 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; | |
| 2768 | 2749 | |
| 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 | 2752 | return ''; |
| 2773 | 2753 | } |
| 2774 | 2754 | |
| 2775 | - return ''; | |
| 2755 | + const range = `${chat.length - count}-${chat.length - 1}`; | |
| 2756 | + return doMesCut(_, range); | |
| 2776 | 2757 | } |
| 2777 | 2758 | |
| 2778 | 2759 | function doResetPanels() { |
| @@ -1,6 +1,5 @@ | ||
| 1 | 1 | import { |
| 2 | 2 | amount_gen, |
| 3 | - callPopup, | |
| 4 | 3 | characters, |
| 5 | 4 | eventSource, |
| 6 | 5 | event_types, |
| @@ -19,6 +18,7 @@ import { | ||
| 19 | 18 | import { groups, selected_group } from './group-chats.js'; |
| 20 | 19 | import { instruct_presets } from './instruct-mode.js'; |
| 21 | 20 | import { kai_settings } from './kai-settings.js'; |
| 21 | +import { Popup } from './popup.js'; | |
| 22 | 22 | import { context_presets, getContextSettings, power_user } from './power-user.js'; |
| 23 | 23 | import { SlashCommand } from './slash-commands/SlashCommand.js'; |
| 24 | 24 | import { ARGUMENT_TYPE, SlashCommandArgument } from './slash-commands/SlashCommandArgument.js'; |
| @@ -165,11 +165,8 @@ class PresetManager { | ||
| 165 | 165 | |
| 166 | 166 | async savePresetAs() { |
| 167 | 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 | 170 | if (!name) { |
| 174 | 171 | console.log('Preset name not provided'); |
| 175 | 172 | return; |
| @@ -372,7 +369,7 @@ class PresetManager { | ||
| 372 | 369 | if (Object.keys(preset_names).length) { |
| 373 | 370 | const nextPresetName = Object.keys(preset_names)[0]; |
| 374 | 371 | const newValue = preset_names[nextPresetName]; |
| 375 | 372 | $(this.select).find(`option[value="${newValue}"]`).attr('selected', 'true'); |
| 376 | 373 | $(this.select).trigger('change'); |
| 377 | 374 | } |
| 378 | 375 | |
| @@ -597,8 +594,7 @@ export async function initPresetManager() { | ||
| 597 | 594 | return; |
| 598 | 595 | } |
| 599 | 596 | |
| 600 | 597 | const confirm = await callPopupPopup.show.confirm('Delete the preset?', 'This action is irreversible and your current settings will be overwritten.', 'confirm'); |
| 601 | - | |
| 602 | 598 | if (!confirm) { |
| 603 | 599 | return; |
| 604 | 600 | } |
| @@ -641,8 +637,7 @@ export async function initPresetManager() { | ||
| 641 | 637 | return; |
| 642 | 638 | } |
| 643 | 639 | |
| 644 | 640 | const confirm = await callPopupPopup.show.confirm('<h3>Are you sure?</h3>', 'Resetting a <b>default preset</b> will restore the default settings.', 'confirm'); |
| 645 | - | |
| 646 | 641 | if (!confirm) { |
| 647 | 642 | return; |
| 648 | 643 | } |
| @@ -653,8 +648,7 @@ export async function initPresetManager() { | ||
| 653 | 648 | presetManager.selectPreset(option); |
| 654 | 649 | toastr.success('Default preset restored'); |
| 655 | 650 | } else { |
| 656 | 651 | const confirm = await callPopupPopup.show.confirm('<h3>Are you sure?</h3>', 'Resetting a <b>custom preset</b> will restore to the last saved state.', 'confirm'); |
| 657 | - | |
| 658 | 652 | if (!confirm) { |
| 659 | 653 | return; |
| 660 | 654 | } |
| @@ -952,14 +952,36 @@ export function initDefaultSlashCommands() { | ||
| 952 | 952 | SlashCommandParser.addCommandObject(SlashCommand.fromProps({ |
| 953 | 953 | name: 'fuzzy', |
| 954 | 954 | callback: fuzzyCallback, |
| 955 | 955 | returns: 'first matching item', |
| 956 | 956 | namedArgumentList: [ |
| 957 | 957 | new 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 | 986 | unnamedArgumentList: [ |
| 965 | 987 | new SlashCommandArgument( |
| @@ -977,6 +999,13 @@ export function initDefaultSlashCommands() { | ||
| 977 | 999 | At 1.0 (max) the match is very loose and will match anything. |
| 978 | 1000 | </div> |
| 979 | 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 | 1009 | The returned value passes to the next command through the pipe. |
| 981 | 1010 | </div> |
| 982 | 1011 | <div> |
| @@ -1865,7 +1894,7 @@ async function inputCallback(args, prompt) { | ||
| 1865 | 1894 | * @param {FuzzyCommandArgs} args - arguments containing "list" (JSON array) and optionaly "threshold" (float between 0.0 and 1.0) |
| 1866 | 1895 | * @param {string} searchInValue - the string where items of list are searched |
| 1867 | 1896 | * @returns {string} - the matched item from the list |
| 1868 | 1897 | * @typedef {{list: string, threshold: string, mode:string}} FuzzyCommandArgs - arguments for /fuzzy command |
| 1869 | 1898 | * @example /fuzzy list=["down","left","up","right"] "he looks up" | /echo // should return "up" |
| 1870 | 1899 | * @link https://www.fusejs.io/ |
| 1871 | 1900 | */ |
| @@ -1895,7 +1924,7 @@ function fuzzyCallback(args, searchInValue) { | ||
| 1895 | 1924 | }; |
| 1896 | 1925 | // threshold determines how strict is the match, low threshold value is very strict, at 1 (nearly?) everything matches |
| 1897 | 1926 | if ('threshold' in args) { |
| 1898 | 1927 | params.threshold = parseFloat(resolveVariable(args.threshold)); |
| 1899 | 1928 | if (isNaN(params.threshold)) { |
| 1900 | 1929 | console.warn('WARN: \'threshold\' argument must be a float between 0.0 and 1.0 for /fuzzy command'); |
| 1901 | 1930 | return ''; |
| @@ -1908,16 +1937,42 @@ function fuzzyCallback(args, searchInValue) { | ||
| 1908 | 1937 | } |
| 1909 | 1938 | } |
| 1910 | 1939 | |
| 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 | 1960 | if (result.length > 0) { |
| 1916 | 1961 | console.info('fuzzyCallback Matched/fuzzy: 'best +matched', searchItemresult[0].item); |
| 1917 | 1962 | return searchItemresult[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 | 1976 | } catch { |
| 1922 | 1977 | console.warn('WARN: Invalid list argument provided for /fuzzy command'); |
| 1923 | 1978 | return ''; |
| @@ -1,11 +1,11 @@ | ||
| 1 | 1 | import { chat_metadata, characters, substituteParams, chat, extension_prompt_roles, extension_prompt_types } from "'../../script.js"'; |
| 2 | 2 | import { extension_settings } from "'../extensions.js"'; |
| 3 | 3 | import { getGroupMembers, groups, selected_group } from "'../group-chats.js"'; |
| 4 | 4 | import { power_user } from "'../power-user.js"'; |
| 5 | 5 | import { searchCharByName, getTagsList, tags } from "'../tags.js"'; |
| 6 | 6 | import { SlashCommandClosureworld_names } from "'../SlashCommandClosureworld-info.js"'; |
| 7 | 7 | import { SlashCommandEnumValue, enumTypesSlashCommandClosure } from "'./SlashCommandEnumValueSlashCommandClosure.js"'; |
| 8 | 8 | import { SlashCommandExecutorSlashCommandEnumValue, enumTypes } from "'./SlashCommandExecutorSlashCommandEnumValue.js"'; |
| 9 | 9 | |
| 10 | 10 | /** |
| 11 | 11 | * A collection of regularly used enum icons |
| @@ -103,8 +103,8 @@ export const enumIcons = { | ||
| 103 | 103 | // Remove possible nullable types definition to match type icon |
| 104 | 104 | type = type.replace(/\?$/, ''); |
| 105 | 105 | return enumIcons[type] ?? enumIcons.default; |
| 106 | 106 | }, |
| 107 | 107 | }; |
| 108 | 108 | |
| 109 | 109 | /** |
| 110 | 110 | * A collection of common enum providers |
| @@ -143,7 +143,7 @@ export const commonEnumProviders = { | ||
| 143 | 143 | ...isAll || types.includes('global') ? Object.keys(extension_settings.variables.global ?? []).map(name => new SlashCommandEnumValue(name, null, enumTypes.macro, enumIcons.globalVariable)) : [], |
| 144 | 144 | ...isAll || types.includes('local') ? Object.keys(chat_metadata.variables ?? []).map(name => new SlashCommandEnumValue(name, null, enumTypes.name, enumIcons.localVariable)) : [], |
| 145 | 145 | ...isAll || types.includes('scope') ? [].map(name => new SlashCommandEnumValue(name, null, enumTypes.variable, enumIcons.scopeVariable)) : [], // TODO: Add scoped variables here, Lenny |
| 146 | 146 | ]; |
| 147 | 147 | }, |
| 148 | 148 | |
| 149 | 149 | /** |
| @@ -180,7 +180,7 @@ export const commonEnumProviders = { | ||
| 180 | 180 | * @param {('all' | 'existing' | 'not-existing')?} [mode='all'] - Which types of tags to show |
| 181 | 181 | * @returns {() => SlashCommandEnumValue[]} |
| 182 | 182 | */ |
| 183 | 183 | tagsForChar: (mode = 'all') => (/** @type {import('./SlashCommandExecutor.js').SlashCommandExecutor} */ executor) => { |
| 184 | 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 | 185 | const charName = executor.namedArgumentList.find(it => it.name == 'name')?.value; |
| 186 | 186 | if (charName instanceof SlashCommandClosure) throw new Error('Argument \'name\' does not support closures'); |
| @@ -213,7 +213,7 @@ export const commonEnumProviders = { | ||
| 213 | 213 | * |
| 214 | 214 | * @returns {SlashCommandEnumValue[]} |
| 215 | 215 | */ |
| 216 | 216 | worlds: () => $('#world_info').children().toArray()world_names.map(xworldName => new SlashCommandEnumValue(x.textContentworldName, null, enumTypes.name, enumIcons.world)), |
| 217 | 217 | |
| 218 | 218 | /** |
| 219 | 219 | * All existing injects for the current chat |
| @@ -14,7 +14,6 @@ import { SlashCommand } from './slash-commands/SlashCommand.js'; | ||
| 14 | 14 | import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from './slash-commands/SlashCommandArgument.js'; |
| 15 | 15 | import { SlashCommandEnumValue, enumTypes } from './slash-commands/SlashCommandEnumValue.js'; |
| 16 | 16 | import { commonEnumProviders, enumIcons } from './slash-commands/SlashCommandCommonEnumsProvider.js'; |
| 17 | -import { SlashCommandExecutor } from './slash-commands/SlashCommandExecutor.js'; | |
| 18 | 17 | import { SlashCommandClosure } from './slash-commands/SlashCommandClosure.js'; |
| 19 | 18 | import { callGenericPopup, Popup, POPUP_TYPE } from './popup.js'; |
| 20 | 19 | |
| @@ -1215,7 +1214,7 @@ function registerWorldInfoSlashCommands() { | ||
| 1215 | 1214 | enumTypes.enum, enumIcons.getDataTypeIcon(value.type))), |
| 1216 | 1215 | |
| 1217 | 1216 | /** All existing UIDs based on the file argument as world name */ |
| 1218 | 1217 | wiUids: (/** @type {import('./slash-commands/SlashCommandExecutor.js').SlashCommandExecutor} */ executor) => { |
| 1219 | 1218 | const file = executor.namedArgumentList.find(it => it.name == 'file')?.value; |
| 1220 | 1219 | if (file instanceof SlashCommandClosure) throw new Error('Argument \'file\' does not support closures'); |
| 1221 | 1220 | // Try find world from cache |
| @@ -3161,7 +3160,8 @@ function duplicateWorldInfoEntry(data, uid) { | ||
| 3161 | 3160 | } |
| 3162 | 3161 | |
| 3163 | 3162 | // Exclude uid and gather the rest of the properties |
| 3164 | 3163 | const { uid:originalData _,= ..Object.originalData assign({} =, data.entries[uid]); |
| 3164 | + delete originalData.uid; | |
| 3165 | 3165 | |
| 3166 | 3166 | // Create new entry and copy over data |
| 3167 | 3167 | const entry = createWorldInfoEntry(data.name, data); |
| @@ -4326,8 +4326,9 @@ function onWorldInfoChange(args, text) { | ||
| 4326 | 4326 | $('#world_info').val(null).trigger('change'); |
| 4327 | 4327 | } |
| 4328 | 4328 | } else { //if it's a pointer selection |
| 4329 | 4329 | letconst 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 | 4332 | if (selectedWorlds.length > 0) { |
| 4332 | 4333 | selectedWorlds.forEach((worldIndex) => { |
| 4333 | 4334 | const existingWorldName = world_names[worldIndex]; |