Merge pull request #2542 from SillyTavern/wi-slash-commands-performance-improvements World Info: slash commands performance improvements
Signed| @@ -0,0 +1,56 @@ | |||
| 1 | /** | ||
| 2 | * A specialized Map class that provides consistent data storage by performing deep cloning of values. | ||
| 3 | * | ||
| 4 | * @template K, V | ||
| 5 | * @extends Map<K, V> | ||
| 6 | */ | ||
| 7 | export class StructuredCloneMap extends Map { | ||
| 8 | /** | ||
| 9 | * Constructs a new StructuredCloneMap. | ||
| 10 | * @param {object} options - Options for the map | ||
| 11 | * @param {boolean} options.cloneOnGet - Whether to clone the value when getting it from the map | ||
| 12 | * @param {boolean} options.cloneOnSet - Whether to clone the value when setting it in the map | ||
| 13 | */ | ||
| 14 | constructor({ cloneOnGet, cloneOnSet } = { cloneOnGet: true, cloneOnSet: true }) { | ||
| 15 | super(); | ||
| 16 | this.cloneOnGet = cloneOnGet; | ||
| 17 | this.cloneOnSet = cloneOnSet; | ||
| 18 | } | ||
| 19 | |||
| 20 | /** | ||
| 21 | * Adds a new element with a specified key and value to the Map. If an element with the same key already exists, the element will be updated. | ||
| 22 | * | ||
| 23 | * The set value will always be a deep clone of the provided value to provide consistent data storage. | ||
| 24 | * | ||
| 25 | * @param {K} key - The key to set | ||
| 26 | * @param {V} value - The value to set | ||
| 27 | * @returns {this} The updated map | ||
| 28 | */ | ||
| 29 | set(key, value) { | ||
| 30 | if (!this.cloneOnSet) { | ||
| 31 | return super.set(key, value); | ||
| 32 | } | ||
| 33 | |||
| 34 | const clonedValue = structuredClone(value); | ||
| 35 | super.set(key, clonedValue); | ||
| 36 | return this; | ||
| 37 | } | ||
| 38 | |||
| 39 | /** | ||
| 40 | * Returns a specified element from the Map object. | ||
| 41 | * If the value that is associated to the provided key is an object, then you will get a reference to that object and any change made to that object will effectively modify it inside the Map. | ||
| 42 | * | ||
| 43 | * The returned value will always be a deep clone of the cached value. | ||
| 44 | * | ||
| 45 | * @param {K} key - The key to get the value for | ||
| 46 | * @returns {V | undefined} Returns the element associated with the specified key. If no element is associated with the specified key, undefined is returned. | ||
| 47 | */ | ||
| 48 | get(key) { | ||
| 49 | if (!this.cloneOnGet) { | ||
| 50 | return super.get(key); | ||
| 51 | } | ||
| 52 | |||
| 53 | const value = super.get(key); | ||
| 54 | return structuredClone(value); | ||
| 55 | } | ||
| 56 | } | ||
| @@ -271,6 +271,13 @@ export function getStringHash(str, seed = 0) { | |||
| 271 | } | 271 | } |
| 272 | 272 | ||
| 273 | /** | 273 | /** |
| 274 | * Map of debounced functions to their timers. | ||
| 275 | * Weak map is used to avoid memory leaks. | ||
| 276 | * @type {WeakMap<function, any>} | ||
| 277 | */ | ||
| 278 | const debounceMap = new WeakMap(); | ||
| 279 | |||
| 280 | /** | ||
| 274 | * Creates a debounced function that delays invoking func until after wait milliseconds have elapsed since the last time the debounced function was invoked. | 281 | * Creates a debounced function that delays invoking func until after wait milliseconds have elapsed since the last time the debounced function was invoked. |
| 275 | * @param {function} func The function to debounce. | 282 | * @param {function} func The function to debounce. |
| 276 | * @param {debounce_timeout|number} [timeout=debounce_timeout.default] The timeout based on the common enum values, or in milliseconds. | 283 | * @param {debounce_timeout|number} [timeout=debounce_timeout.default] The timeout based on the common enum values, or in milliseconds. |
| @@ -278,10 +285,26 @@ export function getStringHash(str, seed = 0) { | |||
| 278 | */ | 285 | */ |
| 279 | export function debounce(func, timeout = debounce_timeout.standard) { | 286 | export function debounce(func, timeout = debounce_timeout.standard) { |
| 280 | let timer; | 287 | let timer; |
| 281 | return (...args) => { | 288 | let fn = (...args) => { |
| 282 | clearTimeout(timer); | 289 | clearTimeout(timer); |
| 283 | timer = setTimeout(() => { func.apply(this, args); }, timeout); | 290 | timer = setTimeout(() => { func.apply(this, args); }, timeout); |
| 291 | debounceMap.set(func, timer); | ||
| 292 | debounceMap.set(fn, timer); | ||
| 284 | }; | 293 | }; |
| 294 | |||
| 295 | return fn; | ||
| 296 | } | ||
| 297 | |||
| 298 | /** | ||
| 299 | * Cancels a scheduled debounced function. | ||
| 300 | * Does nothing if the function is not debounced or not scheduled. | ||
| 301 | * @param {function} func The function to cancel. Either the original or the debounced function. | ||
| 302 | */ | ||
| 303 | export function cancelDebounce(func) { | ||
| 304 | if (debounceMap.has(func)) { | ||
| 305 | clearTimeout(debounceMap.get(func)); | ||
| 306 | debounceMap.delete(func); | ||
| 307 | } | ||
| 285 | } | 308 | } |
| 286 | 309 | ||
| 287 | /** | 310 | /** |
| @@ -1,5 +1,5 @@ | |||
| 1 | import { saveSettings, callPopup, substituteParams, getRequestHeaders, chat_metadata, this_chid, characters, saveCharacterDebounced, menu_type, eventSource, event_types, getExtensionPromptByName, saveMetadata, getCurrentChatId, extension_prompt_roles } from '../script.js'; | 1 | import { saveSettings, callPopup, substituteParams, getRequestHeaders, chat_metadata, this_chid, characters, saveCharacterDebounced, menu_type, eventSource, event_types, getExtensionPromptByName, saveMetadata, getCurrentChatId, extension_prompt_roles } from '../script.js'; |
| 2 | import { download, debounce, initScrollHeight, resetScrollHeight, parseJsonFile, extractDataFromPng, getFileBuffer, getCharaFilename, getSortableDelay, escapeRegex, PAGINATION_TEMPLATE, navigation_option, waitUntilCondition, isTrueBoolean, setValueByPath, flashHighlight, select2ModifyOptions, getSelect2OptionId, dynamicSelect2DataViaAjax, highlightRegex, select2ChoiceClickSubscribe, isFalseBoolean, getSanitizedFilename, checkOverwriteExistingData, getStringHash, parseStringArray } from './utils.js'; | 2 | import { download, debounce, initScrollHeight, resetScrollHeight, parseJsonFile, extractDataFromPng, getFileBuffer, getCharaFilename, getSortableDelay, escapeRegex, PAGINATION_TEMPLATE, navigation_option, waitUntilCondition, isTrueBoolean, setValueByPath, flashHighlight, select2ModifyOptions, getSelect2OptionId, dynamicSelect2DataViaAjax, highlightRegex, select2ChoiceClickSubscribe, isFalseBoolean, getSanitizedFilename, checkOverwriteExistingData, getStringHash, parseStringArray, cancelDebounce } from './utils.js'; |
| 3 | import { extension_settings, getContext } from './extensions.js'; | 3 | import { extension_settings, getContext } from './extensions.js'; |
| 4 | import { NOTE_MODULE_NAME, metadata_keys, shouldWIAddPrompt } from './authors-note.js'; | 4 | import { NOTE_MODULE_NAME, metadata_keys, shouldWIAddPrompt } from './authors-note.js'; |
| 5 | import { isMobile } from './RossAscends-mods.js'; | 5 | import { isMobile } from './RossAscends-mods.js'; |
| @@ -16,6 +16,7 @@ import { SlashCommandEnumValue, enumTypes } from './slash-commands/SlashCommandE | |||
| 16 | import { commonEnumProviders, enumIcons } from './slash-commands/SlashCommandCommonEnumsProvider.js'; | 16 | import { commonEnumProviders, enumIcons } from './slash-commands/SlashCommandCommonEnumsProvider.js'; |
| 17 | import { SlashCommandClosure } from './slash-commands/SlashCommandClosure.js'; | 17 | import { SlashCommandClosure } from './slash-commands/SlashCommandClosure.js'; |
| 18 | import { callGenericPopup, Popup, POPUP_TYPE } from './popup.js'; | 18 | import { callGenericPopup, Popup, POPUP_TYPE } from './popup.js'; |
| 19 | import { StructuredCloneMap } from './util/StructuredCloneMap.js'; | ||
| 19 | 20 | ||
| 20 | export { | 21 | export { |
| 21 | world_info, | 22 | world_info, |
| @@ -746,7 +747,8 @@ export const wi_anchor_position = { | |||
| 746 | after: 1, | 747 | after: 1, |
| 747 | }; | 748 | }; |
| 748 | 749 | ||
| 749 | const worldInfoCache = new Map(); | 750 | /** @type {StructuredCloneMap<string,object>} */ |
| 751 | const worldInfoCache = new StructuredCloneMap({ cloneOnGet: true, cloneOnSet: false }); | ||
| 750 | 752 | ||
| 751 | /** | 753 | /** |
| 752 | * Gets the world info based on chat messages. | 754 | * Gets the world info based on chat messages. |
| @@ -885,9 +887,15 @@ function setWorldInfoSettings(settings, data) { | |||
| 885 | } | 887 | } |
| 886 | 888 | ||
| 887 | function registerWorldInfoSlashCommands() { | 889 | function registerWorldInfoSlashCommands() { |
| 888 | function reloadEditor(file) { | 890 | /** |
| 891 | * Reloads the editor with the specified world info file | ||
| 892 | * @param {string} file - The file to load in the editor | ||
| 893 | * @param {boolean} [loadIfNotSelected=false] - Indicates whether to load the file even if it's not currently selected | ||
| 894 | */ | ||
| 895 | function reloadEditor(file, loadIfNotSelected = false) { | ||
| 896 | const currentIndex = $('#world_editor_select').val(); | ||
| 889 | const selectedIndex = world_names.indexOf(file); | 897 | const selectedIndex = world_names.indexOf(file); |
| 890 | if (selectedIndex !== -1) { | 898 | if (selectedIndex !== -1 && (loadIfNotSelected || currentIndex === selectedIndex)) { |
| 891 | $('#world_editor_select').val(selectedIndex).trigger('change'); | 899 | $('#world_editor_select').val(selectedIndex).trigger('change'); |
| 892 | } | 900 | } |
| 893 | } | 901 | } |
| @@ -1049,7 +1057,7 @@ function registerWorldInfoSlashCommands() { | |||
| 1049 | entry.content = content; | 1057 | entry.content = content; |
| 1050 | } | 1058 | } |
| 1051 | 1059 | ||
| 1052 | await saveWorldInfo(file, data, true); | 1060 | await saveWorldInfo(file, data); |
| 1053 | reloadEditor(file); | 1061 | reloadEditor(file); |
| 1054 | 1062 | ||
| 1055 | return String(entry.uid); | 1063 | return String(entry.uid); |
| @@ -1100,7 +1108,7 @@ function registerWorldInfoSlashCommands() { | |||
| 1100 | setOriginalDataValue(data, uid, originalDataKeyMap[field], entry[field]); | 1108 | setOriginalDataValue(data, uid, originalDataKeyMap[field], entry[field]); |
| 1101 | } | 1109 | } |
| 1102 | 1110 | ||
| 1103 | await saveWorldInfo(file, data, true); | 1111 | await saveWorldInfo(file, data); |
| 1104 | reloadEditor(file); | 1112 | reloadEditor(file); |
| 1105 | return ''; | 1113 | return ''; |
| 1106 | } | 1114 | } |
| @@ -1840,7 +1848,7 @@ function displayWorldEntries(name, data, navigation = navigation_option.none, fl | |||
| 1840 | nextText: '>', | 1848 | nextText: '>', |
| 1841 | formatNavigator: PAGINATION_TEMPLATE, | 1849 | formatNavigator: PAGINATION_TEMPLATE, |
| 1842 | showNavigator: true, | 1850 | showNavigator: true, |
| 1843 | callback: function (/** @type {object[]} */ page) { | 1851 | callback: async function (/** @type {object[]} */ page) { |
| 1844 | // We save costly performance by removing all events before emptying. Because we know there are no relevant event handlers reacting on removing elements | 1852 | // We save costly performance by removing all events before emptying. Because we know there are no relevant event handlers reacting on removing elements |
| 1845 | // This prevents jQuery from actually going through all registered events on the controls for each entry when removing it | 1853 | // This prevents jQuery from actually going through all registered events on the controls for each entry when removing it |
| 1846 | worldEntriesList.find('*').off(); | 1854 | worldEntriesList.find('*').off(); |
| @@ -1926,7 +1934,7 @@ function displayWorldEntries(name, data, navigation = navigation_option.none, fl | |||
| 1926 | 1934 | ||
| 1927 | if (counter > 0) { | 1935 | if (counter > 0) { |
| 1928 | toastr.info(`Backfilled ${counter} titles`); | 1936 | toastr.info(`Backfilled ${counter} titles`); |
| 1929 | await saveWorldInfo(name, data, true); | 1937 | await saveWorldInfo(name, data); |
| 1930 | updateEditor(navigation_option.previous); | 1938 | updateEditor(navigation_option.previous); |
| 1931 | } | 1939 | } |
| 1932 | }); | 1940 | }); |
| @@ -2026,7 +2034,7 @@ function displayWorldEntries(name, data, navigation = navigation_option.none, fl | |||
| 2026 | 2034 | ||
| 2027 | console.table(Object.keys(data.entries).map(uid => data.entries[uid]).map(x => ({ uid: x.uid, key: x.key.join(','), displayIndex: x.displayIndex }))); | 2035 | console.table(Object.keys(data.entries).map(uid => data.entries[uid]).map(x => ({ uid: x.uid, key: x.key.join(','), displayIndex: x.displayIndex }))); |
| 2028 | 2036 | ||
| 2029 | await saveWorldInfo(name, data, true); | 2037 | await saveWorldInfo(name, data); |
| 2030 | }, | 2038 | }, |
| 2031 | }); | 2039 | }); |
| 2032 | //$("#world_popup_entries_list").disableSelection(); | 2040 | //$("#world_popup_entries_list").disableSelection(); |
| @@ -2298,7 +2306,7 @@ function getWorldEntry(name, data, entry) { | |||
| 2298 | templateResult: item => templateStyling(item, { searchStyle: true }), | 2306 | templateResult: item => templateStyling(item, { searchStyle: true }), |
| 2299 | templateSelection: item => templateStyling(item), | 2307 | templateSelection: item => templateStyling(item), |
| 2300 | }); | 2308 | }); |
| 2301 | input.on('change', function (_, { skipReset, noSave } = {}) { | 2309 | input.on('change', async function (_, { skipReset, noSave } = {}) { |
| 2302 | const uid = $(this).data('uid'); | 2310 | const uid = $(this).data('uid'); |
| 2303 | /** @type {string[]} */ | 2311 | /** @type {string[]} */ |
| 2304 | const keys = ($(this).select2('data')).map(x => x.text); | 2312 | const keys = ($(this).select2('data')).map(x => x.text); |
| @@ -2307,7 +2315,7 @@ function getWorldEntry(name, data, entry) { | |||
| 2307 | if (!noSave) { | 2315 | if (!noSave) { |
| 2308 | data.entries[uid][entryPropName] = keys; | 2316 | data.entries[uid][entryPropName] = keys; |
| 2309 | setOriginalDataValue(data, uid, originalDataValueName, data.entries[uid][entryPropName]); | 2317 | setOriginalDataValue(data, uid, originalDataValueName, data.entries[uid][entryPropName]); |
| 2310 | saveWorldInfo(name, data); | 2318 | await saveWorldInfo(name, data); |
| 2311 | } | 2319 | } |
| 2312 | }); | 2320 | }); |
| 2313 | input.on('select2:select', /** @type {function(*):void} */ event => updateWorldEntryKeyOptionsCache([event.params.data])); | 2321 | input.on('select2:select', /** @type {function(*):void} */ event => updateWorldEntryKeyOptionsCache([event.params.data])); |
| @@ -2338,14 +2346,14 @@ function getWorldEntry(name, data, entry) { | |||
| 2338 | template.find(`select[name="${entryPropName}"]`).hide(); | 2346 | template.find(`select[name="${entryPropName}"]`).hide(); |
| 2339 | input.show(); | 2347 | input.show(); |
| 2340 | 2348 | ||
| 2341 | input.on('input', function (_, { skipReset, noSave } = {}) { | 2349 | input.on('input', async function (_, { skipReset, noSave } = {}) { |
| 2342 | const uid = $(this).data('uid'); | 2350 | const uid = $(this).data('uid'); |
| 2343 | const value = String($(this).val()); | 2351 | const value = String($(this).val()); |
| 2344 | !skipReset && resetScrollHeight(this); | 2352 | !skipReset && resetScrollHeight(this); |
| 2345 | if (!noSave) { | 2353 | if (!noSave) { |
| 2346 | data.entries[uid][entryPropName] = splitKeywordsAndRegexes(value); | 2354 | data.entries[uid][entryPropName] = splitKeywordsAndRegexes(value); |
| 2347 | setOriginalDataValue(data, uid, originalDataValueName, data.entries[uid][entryPropName]); | 2355 | setOriginalDataValue(data, uid, originalDataValueName, data.entries[uid][entryPropName]); |
| 2348 | saveWorldInfo(name, data); | 2356 | await saveWorldInfo(name, data); |
| 2349 | } | 2357 | } |
| 2350 | }); | 2358 | }); |
| 2351 | input.val(entry[entryPropName].join(', ')).trigger('input', { skipReset: true }); | 2359 | input.val(entry[entryPropName].join(', ')).trigger('input', { skipReset: true }); |
| @@ -2384,12 +2392,12 @@ function getWorldEntry(name, data, entry) { | |||
| 2384 | event.stopPropagation(); | 2392 | event.stopPropagation(); |
| 2385 | }); | 2393 | }); |
| 2386 | 2394 | ||
| 2387 | selectiveLogicDropdown.on('input', function () { | 2395 | selectiveLogicDropdown.on('input', async function () { |
| 2388 | const uid = $(this).data('uid'); | 2396 | const uid = $(this).data('uid'); |
| 2389 | const value = Number($(this).val()); | 2397 | const value = Number($(this).val()); |
| 2390 | data.entries[uid].selectiveLogic = !isNaN(value) ? value : world_info_logic.AND_ANY; | 2398 | data.entries[uid].selectiveLogic = !isNaN(value) ? value : world_info_logic.AND_ANY; |
| 2391 | setOriginalDataValue(data, uid, 'selectiveLogic', data.entries[uid].selectiveLogic); | 2399 | setOriginalDataValue(data, uid, 'selectiveLogic', data.entries[uid].selectiveLogic); |
| 2392 | saveWorldInfo(name, data); | 2400 | await saveWorldInfo(name, data); |
| 2393 | }); | 2401 | }); |
| 2394 | 2402 | ||
| 2395 | template | 2403 | template |
| @@ -2404,7 +2412,7 @@ function getWorldEntry(name, data, entry) { | |||
| 2404 | // exclude characters checkbox | 2412 | // exclude characters checkbox |
| 2405 | const characterExclusionInput = template.find('input[name="character_exclusion"]'); | 2413 | const characterExclusionInput = template.find('input[name="character_exclusion"]'); |
| 2406 | characterExclusionInput.data('uid', entry.uid); | 2414 | characterExclusionInput.data('uid', entry.uid); |
| 2407 | characterExclusionInput.on('input', function () { | 2415 | characterExclusionInput.on('input', async function () { |
| 2408 | const uid = $(this).data('uid'); | 2416 | const uid = $(this).data('uid'); |
| 2409 | const value = $(this).prop('checked'); | 2417 | const value = $(this).prop('checked'); |
| 2410 | characterFilterLabel.text(value ? 'Exclude Character(s)' : 'Filter to Character(s)'); | 2418 | characterFilterLabel.text(value ? 'Exclude Character(s)' : 'Filter to Character(s)'); |
| @@ -2438,7 +2446,7 @@ function getWorldEntry(name, data, entry) { | |||
| 2438 | } | 2446 | } |
| 2439 | 2447 | ||
| 2440 | setOriginalDataValue(data, uid, 'character_filter', data.entries[uid].characterFilter); | 2448 | setOriginalDataValue(data, uid, 'character_filter', data.entries[uid].characterFilter); |
| 2441 | saveWorldInfo(name, data); | 2449 | await saveWorldInfo(name, data); |
| 2442 | }); | 2450 | }); |
| 2443 | characterExclusionInput.prop('checked', entry.characterFilter?.isExclude ?? false).trigger('input'); | 2451 | characterExclusionInput.prop('checked', entry.characterFilter?.isExclude ?? false).trigger('input'); |
| 2444 | 2452 | ||
| @@ -2500,24 +2508,24 @@ function getWorldEntry(name, data, entry) { | |||
| 2500 | ); | 2508 | ); |
| 2501 | } | 2509 | } |
| 2502 | setOriginalDataValue(data, uid, 'character_filter', data.entries[uid].characterFilter); | 2510 | setOriginalDataValue(data, uid, 'character_filter', data.entries[uid].characterFilter); |
| 2503 | saveWorldInfo(name, data); | 2511 | await saveWorldInfo(name, data); |
| 2504 | }); | 2512 | }); |
| 2505 | 2513 | ||
| 2506 | // comment | 2514 | // comment |
| 2507 | const commentInput = template.find('textarea[name="comment"]'); | 2515 | const commentInput = template.find('textarea[name="comment"]'); |
| 2508 | const commentToggle = template.find('input[name="addMemo"]'); | 2516 | const commentToggle = template.find('input[name="addMemo"]'); |
| 2509 | commentInput.data('uid', entry.uid); | 2517 | commentInput.data('uid', entry.uid); |
| 2510 | commentInput.on('input', function (_, { skipReset } = {}) { | 2518 | commentInput.on('input', async function (_, { skipReset } = {}) { |
| 2511 | const uid = $(this).data('uid'); | 2519 | const uid = $(this).data('uid'); |
| 2512 | const value = $(this).val(); | 2520 | const value = $(this).val(); |
| 2513 | !skipReset && resetScrollHeight(this); | 2521 | !skipReset && resetScrollHeight(this); |
| 2514 | data.entries[uid].comment = value; | 2522 | data.entries[uid].comment = value; |
| 2515 | 2523 | ||
| 2516 | setOriginalDataValue(data, uid, 'comment', data.entries[uid].comment); | 2524 | setOriginalDataValue(data, uid, 'comment', data.entries[uid].comment); |
| 2517 | saveWorldInfo(name, data); | 2525 | await saveWorldInfo(name, data); |
| 2518 | }); | 2526 | }); |
| 2519 | commentToggle.data('uid', entry.uid); | 2527 | commentToggle.data('uid', entry.uid); |
| 2520 | commentToggle.on('input', function () { | 2528 | commentToggle.on('input', async function () { |
| 2521 | const uid = $(this).data('uid'); | 2529 | const uid = $(this).data('uid'); |
| 2522 | const value = $(this).prop('checked'); | 2530 | const value = $(this).prop('checked'); |
| 2523 | //console.log(value) | 2531 | //console.log(value) |
| @@ -2525,7 +2533,7 @@ function getWorldEntry(name, data, entry) { | |||
| 2525 | .closest('.world_entry') | 2533 | .closest('.world_entry') |
| 2526 | .find('.commentContainer'); | 2534 | .find('.commentContainer'); |
| 2527 | data.entries[uid].addMemo = value; | 2535 | data.entries[uid].addMemo = value; |
| 2528 | saveWorldInfo(name, data); | 2536 | await saveWorldInfo(name, data); |
| 2529 | value ? commentContainer.show() : commentContainer.hide(); | 2537 | value ? commentContainer.show() : commentContainer.hide(); |
| 2530 | }); | 2538 | }); |
| 2531 | 2539 | ||
| @@ -2543,13 +2551,13 @@ function getWorldEntry(name, data, entry) { | |||
| 2543 | 2551 | ||
| 2544 | const contentInput = template.find('textarea[name="content"]'); | 2552 | const contentInput = template.find('textarea[name="content"]'); |
| 2545 | contentInput.data('uid', entry.uid); | 2553 | contentInput.data('uid', entry.uid); |
| 2546 | contentInput.on('input', function (_, { skipCount } = {}) { | 2554 | contentInput.on('input', async function (_, { skipCount } = {}) { |
| 2547 | const uid = $(this).data('uid'); | 2555 | const uid = $(this).data('uid'); |
| 2548 | const value = $(this).val(); | 2556 | const value = $(this).val(); |
| 2549 | data.entries[uid].content = value; | 2557 | data.entries[uid].content = value; |
| 2550 | 2558 | ||
| 2551 | setOriginalDataValue(data, uid, 'content', data.entries[uid].content); | 2559 | setOriginalDataValue(data, uid, 'content', data.entries[uid].content); |
| 2552 | saveWorldInfo(name, data); | 2560 | await saveWorldInfo(name, data); |
| 2553 | 2561 | ||
| 2554 | if (skipCount) { | 2562 | if (skipCount) { |
| 2555 | return; | 2563 | return; |
| @@ -2573,13 +2581,13 @@ function getWorldEntry(name, data, entry) { | |||
| 2573 | // selective | 2581 | // selective |
| 2574 | const selectiveInput = template.find('input[name="selective"]'); | 2582 | const selectiveInput = template.find('input[name="selective"]'); |
| 2575 | selectiveInput.data('uid', entry.uid); | 2583 | selectiveInput.data('uid', entry.uid); |
| 2576 | selectiveInput.on('input', function () { | 2584 | selectiveInput.on('input', async function () { |
| 2577 | const uid = $(this).data('uid'); | 2585 | const uid = $(this).data('uid'); |
| 2578 | const value = $(this).prop('checked'); | 2586 | const value = $(this).prop('checked'); |
| 2579 | data.entries[uid].selective = value; | 2587 | data.entries[uid].selective = value; |
| 2580 | 2588 | ||
| 2581 | setOriginalDataValue(data, uid, 'selective', data.entries[uid].selective); | 2589 | setOriginalDataValue(data, uid, 'selective', data.entries[uid].selective); |
| 2582 | saveWorldInfo(name, data); | 2590 | await saveWorldInfo(name, data); |
| 2583 | 2591 | ||
| 2584 | const keysecondary = $(this) | 2592 | const keysecondary = $(this) |
| 2585 | .closest('.world_entry') | 2593 | .closest('.world_entry') |
| @@ -2608,12 +2616,12 @@ function getWorldEntry(name, data, entry) { | |||
| 2608 | /* | 2616 | /* |
| 2609 | const constantInput = template.find('input[name="constant"]'); | 2617 | const constantInput = template.find('input[name="constant"]'); |
| 2610 | constantInput.data("uid", entry.uid); | 2618 | constantInput.data("uid", entry.uid); |
| 2611 | constantInput.on("input", function () { | 2619 | constantInput.on("input", async function () { |
| 2612 | const uid = $(this).data("uid"); | 2620 | const uid = $(this).data("uid"); |
| 2613 | const value = $(this).prop("checked"); | 2621 | const value = $(this).prop("checked"); |
| 2614 | data.entries[uid].constant = value; | 2622 | data.entries[uid].constant = value; |
| 2615 | setOriginalDataValue(data, uid, "constant", data.entries[uid].constant); | 2623 | setOriginalDataValue(data, uid, "constant", data.entries[uid].constant); |
| 2616 | saveWorldInfo(name, data); | 2624 | await saveWorldInfo(name, data); |
| 2617 | }); | 2625 | }); |
| 2618 | constantInput.prop("checked", entry.constant).trigger("input"); | 2626 | constantInput.prop("checked", entry.constant).trigger("input"); |
| 2619 | */ | 2627 | */ |
| @@ -2621,14 +2629,14 @@ function getWorldEntry(name, data, entry) { | |||
| 2621 | // order | 2629 | // order |
| 2622 | const orderInput = template.find('input[name="order"]'); | 2630 | const orderInput = template.find('input[name="order"]'); |
| 2623 | orderInput.data('uid', entry.uid); | 2631 | orderInput.data('uid', entry.uid); |
| 2624 | orderInput.on('input', function () { | 2632 | orderInput.on('input', async function () { |
| 2625 | const uid = $(this).data('uid'); | 2633 | const uid = $(this).data('uid'); |
| 2626 | const value = Number($(this).val()); | 2634 | const value = Number($(this).val()); |
| 2627 | 2635 | ||
| 2628 | data.entries[uid].order = !isNaN(value) ? value : 0; | 2636 | data.entries[uid].order = !isNaN(value) ? value : 0; |
| 2629 | updatePosOrdDisplay(uid); | 2637 | updatePosOrdDisplay(uid); |
| 2630 | setOriginalDataValue(data, uid, 'insertion_order', data.entries[uid].order); | 2638 | setOriginalDataValue(data, uid, 'insertion_order', data.entries[uid].order); |
| 2631 | saveWorldInfo(name, data); | 2639 | await saveWorldInfo(name, data); |
| 2632 | }); | 2640 | }); |
| 2633 | orderInput.val(entry.order).trigger('input'); | 2641 | orderInput.val(entry.order).trigger('input'); |
| 2634 | orderInput.css('width', 'calc(3em + 15px)'); | 2642 | orderInput.css('width', 'calc(3em + 15px)'); |
| @@ -2636,13 +2644,13 @@ function getWorldEntry(name, data, entry) { | |||
| 2636 | // group | 2644 | // group |
| 2637 | const groupInput = template.find('input[name="group"]'); | 2645 | const groupInput = template.find('input[name="group"]'); |
| 2638 | groupInput.data('uid', entry.uid); | 2646 | groupInput.data('uid', entry.uid); |
| 2639 | groupInput.on('input', function () { | 2647 | groupInput.on('input', async function () { |
| 2640 | const uid = $(this).data('uid'); | 2648 | const uid = $(this).data('uid'); |
| 2641 | const value = String($(this).val()).trim(); | 2649 | const value = String($(this).val()).trim(); |
| 2642 | 2650 | ||
| 2643 | data.entries[uid].group = value; | 2651 | data.entries[uid].group = value; |
| 2644 | setOriginalDataValue(data, uid, 'extensions.group', data.entries[uid].group); | 2652 | setOriginalDataValue(data, uid, 'extensions.group', data.entries[uid].group); |
| 2645 | saveWorldInfo(name, data); | 2653 | await saveWorldInfo(name, data); |
| 2646 | }); | 2654 | }); |
| 2647 | groupInput.val(entry.group ?? '').trigger('input'); | 2655 | groupInput.val(entry.group ?? '').trigger('input'); |
| 2648 | setTimeout(() => createEntryInputAutocomplete(groupInput, getInclusionGroupCallback(data), { allowMultiple: true }), 1); | 2656 | setTimeout(() => createEntryInputAutocomplete(groupInput, getInclusionGroupCallback(data), { allowMultiple: true }), 1); |
| @@ -2650,19 +2658,19 @@ function getWorldEntry(name, data, entry) { | |||
| 2650 | // inclusion priority | 2658 | // inclusion priority |
| 2651 | const groupOverrideInput = template.find('input[name="groupOverride"]'); | 2659 | const groupOverrideInput = template.find('input[name="groupOverride"]'); |
| 2652 | groupOverrideInput.data('uid', entry.uid); | 2660 | groupOverrideInput.data('uid', entry.uid); |
| 2653 | groupOverrideInput.on('input', function () { | 2661 | groupOverrideInput.on('input', async function () { |
| 2654 | const uid = $(this).data('uid'); | 2662 | const uid = $(this).data('uid'); |
| 2655 | const value = $(this).prop('checked'); | 2663 | const value = $(this).prop('checked'); |
| 2656 | data.entries[uid].groupOverride = value; | 2664 | data.entries[uid].groupOverride = value; |
| 2657 | setOriginalDataValue(data, uid, 'extensions.group_override', data.entries[uid].groupOverride); | 2665 | setOriginalDataValue(data, uid, 'extensions.group_override', data.entries[uid].groupOverride); |
| 2658 | saveWorldInfo(name, data); | 2666 | await saveWorldInfo(name, data); |
| 2659 | }); | 2667 | }); |
| 2660 | groupOverrideInput.prop('checked', entry.groupOverride).trigger('input'); | 2668 | groupOverrideInput.prop('checked', entry.groupOverride).trigger('input'); |
| 2661 | 2669 | ||
| 2662 | // group weight | 2670 | // group weight |
| 2663 | const groupWeightInput = template.find('input[name="groupWeight"]'); | 2671 | const groupWeightInput = template.find('input[name="groupWeight"]'); |
| 2664 | groupWeightInput.data('uid', entry.uid); | 2672 | groupWeightInput.data('uid', entry.uid); |
| 2665 | groupWeightInput.on('input', function () { | 2673 | groupWeightInput.on('input', async function () { |
| 2666 | const uid = $(this).data('uid'); | 2674 | const uid = $(this).data('uid'); |
| 2667 | let value = Number($(this).val()); | 2675 | let value = Number($(this).val()); |
| 2668 | const min = Number($(this).attr('min')); | 2676 | const min = Number($(this).attr('min')); |
| @@ -2679,46 +2687,46 @@ function getWorldEntry(name, data, entry) { | |||
| 2679 | 2687 | ||
| 2680 | data.entries[uid].groupWeight = !isNaN(value) ? Math.abs(value) : 1; | 2688 | data.entries[uid].groupWeight = !isNaN(value) ? Math.abs(value) : 1; |
| 2681 | setOriginalDataValue(data, uid, 'extensions.group_weight', data.entries[uid].groupWeight); | 2689 | setOriginalDataValue(data, uid, 'extensions.group_weight', data.entries[uid].groupWeight); |
| 2682 | saveWorldInfo(name, data); | 2690 | await saveWorldInfo(name, data); |
| 2683 | }); | 2691 | }); |
| 2684 | groupWeightInput.val(entry.groupWeight ?? DEFAULT_WEIGHT).trigger('input'); | 2692 | groupWeightInput.val(entry.groupWeight ?? DEFAULT_WEIGHT).trigger('input'); |
| 2685 | 2693 | ||
| 2686 | // sticky | 2694 | // sticky |
| 2687 | const sticky = template.find('input[name="sticky"]'); | 2695 | const sticky = template.find('input[name="sticky"]'); |
| 2688 | sticky.data('uid', entry.uid); | 2696 | sticky.data('uid', entry.uid); |
| 2689 | sticky.on('input', function () { | 2697 | sticky.on('input', async function () { |
| 2690 | const uid = $(this).data('uid'); | 2698 | const uid = $(this).data('uid'); |
| 2691 | const value = Number($(this).val()); | 2699 | const value = Number($(this).val()); |
| 2692 | data.entries[uid].sticky = !isNaN(value) ? value : null; | 2700 | data.entries[uid].sticky = !isNaN(value) ? value : null; |
| 2693 | 2701 | ||
| 2694 | setOriginalDataValue(data, uid, 'extensions.sticky', data.entries[uid].sticky); | 2702 | setOriginalDataValue(data, uid, 'extensions.sticky', data.entries[uid].sticky); |
| 2695 | saveWorldInfo(name, data); | 2703 | await saveWorldInfo(name, data); |
| 2696 | }); | 2704 | }); |
| 2697 | sticky.val(entry.sticky > 0 ? entry.sticky : '').trigger('input'); | 2705 | sticky.val(entry.sticky > 0 ? entry.sticky : '').trigger('input'); |
| 2698 | 2706 | ||
| 2699 | // cooldown | 2707 | // cooldown |
| 2700 | const cooldown = template.find('input[name="cooldown"]'); | 2708 | const cooldown = template.find('input[name="cooldown"]'); |
| 2701 | cooldown.data('uid', entry.uid); | 2709 | cooldown.data('uid', entry.uid); |
| 2702 | cooldown.on('input', function () { | 2710 | cooldown.on('input', async function () { |
| 2703 | const uid = $(this).data('uid'); | 2711 | const uid = $(this).data('uid'); |
| 2704 | const value = Number($(this).val()); | 2712 | const value = Number($(this).val()); |
| 2705 | data.entries[uid].cooldown = !isNaN(value) ? value : null; | 2713 | data.entries[uid].cooldown = !isNaN(value) ? value : null; |
| 2706 | 2714 | ||
| 2707 | setOriginalDataValue(data, uid, 'extensions.cooldown', data.entries[uid].cooldown); | 2715 | setOriginalDataValue(data, uid, 'extensions.cooldown', data.entries[uid].cooldown); |
| 2708 | saveWorldInfo(name, data); | 2716 | await saveWorldInfo(name, data); |
| 2709 | }); | 2717 | }); |
| 2710 | cooldown.val(entry.cooldown > 0 ? entry.cooldown : '').trigger('input'); | 2718 | cooldown.val(entry.cooldown > 0 ? entry.cooldown : '').trigger('input'); |
| 2711 | 2719 | ||
| 2712 | // delay | 2720 | // delay |
| 2713 | const delay = template.find('input[name="delay"]'); | 2721 | const delay = template.find('input[name="delay"]'); |
| 2714 | delay.data('uid', entry.uid); | 2722 | delay.data('uid', entry.uid); |
| 2715 | delay.on('input', function () { | 2723 | delay.on('input', async function () { |
| 2716 | const uid = $(this).data('uid'); | 2724 | const uid = $(this).data('uid'); |
| 2717 | const value = Number($(this).val()); | 2725 | const value = Number($(this).val()); |
| 2718 | data.entries[uid].delay = !isNaN(value) ? value : null; | 2726 | data.entries[uid].delay = !isNaN(value) ? value : null; |
| 2719 | 2727 | ||
| 2720 | setOriginalDataValue(data, uid, 'extensions.delay', data.entries[uid].delay); | 2728 | setOriginalDataValue(data, uid, 'extensions.delay', data.entries[uid].delay); |
| 2721 | saveWorldInfo(name, data); | 2729 | await saveWorldInfo(name, data); |
| 2722 | }); | 2730 | }); |
| 2723 | delay.val(entry.delay > 0 ? entry.delay : '').trigger('input'); | 2731 | delay.val(entry.delay > 0 ? entry.delay : '').trigger('input'); |
| 2724 | 2732 | ||
| @@ -2731,14 +2739,14 @@ function getWorldEntry(name, data, entry) { | |||
| 2731 | const depthInput = template.find('input[name="depth"]'); | 2739 | const depthInput = template.find('input[name="depth"]'); |
| 2732 | depthInput.data('uid', entry.uid); | 2740 | depthInput.data('uid', entry.uid); |
| 2733 | 2741 | ||
| 2734 | depthInput.on('input', function () { | 2742 | depthInput.on('input', async function () { |
| 2735 | const uid = $(this).data('uid'); | 2743 | const uid = $(this).data('uid'); |
| 2736 | const value = Number($(this).val()); | 2744 | const value = Number($(this).val()); |
| 2737 | 2745 | ||
| 2738 | data.entries[uid].depth = !isNaN(value) ? value : 0; | 2746 | data.entries[uid].depth = !isNaN(value) ? value : 0; |
| 2739 | updatePosOrdDisplay(uid); | 2747 | updatePosOrdDisplay(uid); |
| 2740 | setOriginalDataValue(data, uid, 'extensions.depth', data.entries[uid].depth); | 2748 | setOriginalDataValue(data, uid, 'extensions.depth', data.entries[uid].depth); |
| 2741 | saveWorldInfo(name, data); | 2749 | await saveWorldInfo(name, data); |
| 2742 | }); | 2750 | }); |
| 2743 | depthInput.val(entry.depth ?? DEFAULT_DEPTH).trigger('input'); | 2751 | depthInput.val(entry.depth ?? DEFAULT_DEPTH).trigger('input'); |
| 2744 | depthInput.css('width', 'calc(3em + 15px)'); | 2752 | depthInput.css('width', 'calc(3em + 15px)'); |
| @@ -2750,7 +2758,7 @@ function getWorldEntry(name, data, entry) { | |||
| 2750 | 2758 | ||
| 2751 | const probabilityInput = template.find('input[name="probability"]'); | 2759 | const probabilityInput = template.find('input[name="probability"]'); |
| 2752 | probabilityInput.data('uid', entry.uid); | 2760 | probabilityInput.data('uid', entry.uid); |
| 2753 | probabilityInput.on('input', function () { | 2761 | probabilityInput.on('input', async function () { |
| 2754 | const uid = $(this).data('uid'); | 2762 | const uid = $(this).data('uid'); |
| 2755 | const value = Number($(this).val()); | 2763 | const value = Number($(this).val()); |
| 2756 | 2764 | ||
| @@ -2766,7 +2774,7 @@ function getWorldEntry(name, data, entry) { | |||
| 2766 | } | 2774 | } |
| 2767 | 2775 | ||
| 2768 | setOriginalDataValue(data, uid, 'extensions.probability', data.entries[uid].probability); | 2776 | setOriginalDataValue(data, uid, 'extensions.probability', data.entries[uid].probability); |
| 2769 | saveWorldInfo(name, data); | 2777 | await saveWorldInfo(name, data); |
| 2770 | }); | 2778 | }); |
| 2771 | probabilityInput.val(entry.probability).trigger('input'); | 2779 | probabilityInput.val(entry.probability).trigger('input'); |
| 2772 | probabilityInput.css('width', 'calc(3em + 15px)'); | 2780 | probabilityInput.css('width', 'calc(3em + 15px)'); |
| @@ -2778,14 +2786,14 @@ function getWorldEntry(name, data, entry) { | |||
| 2778 | 2786 | ||
| 2779 | const probabilityToggle = template.find('input[name="useProbability"]'); | 2787 | const probabilityToggle = template.find('input[name="useProbability"]'); |
| 2780 | probabilityToggle.data('uid', entry.uid); | 2788 | probabilityToggle.data('uid', entry.uid); |
| 2781 | probabilityToggle.on('input', function () { | 2789 | probabilityToggle.on('input', async function () { |
| 2782 | const uid = $(this).data('uid'); | 2790 | const uid = $(this).data('uid'); |
| 2783 | const value = $(this).prop('checked'); | 2791 | const value = $(this).prop('checked'); |
| 2784 | data.entries[uid].useProbability = value; | 2792 | data.entries[uid].useProbability = value; |
| 2785 | const probabilityContainer = $(this) | 2793 | const probabilityContainer = $(this) |
| 2786 | .closest('.world_entry') | 2794 | .closest('.world_entry') |
| 2787 | .find('.probabilityContainer'); | 2795 | .find('.probabilityContainer'); |
| 2788 | saveWorldInfo(name, data); | 2796 | await saveWorldInfo(name, data); |
| 2789 | value ? probabilityContainer.show() : probabilityContainer.hide(); | 2797 | value ? probabilityContainer.show() : probabilityContainer.hide(); |
| 2790 | 2798 | ||
| 2791 | if (value && data.entries[uid].probability === null) { | 2799 | if (value && data.entries[uid].probability === null) { |
| @@ -2814,7 +2822,7 @@ function getWorldEntry(name, data, entry) { | |||
| 2814 | // Prevent closing the drawer on clicking the input | 2822 | // Prevent closing the drawer on clicking the input |
| 2815 | event.stopPropagation(); | 2823 | event.stopPropagation(); |
| 2816 | }); | 2824 | }); |
| 2817 | positionInput.on('input', function () { | 2825 | positionInput.on('input', async function () { |
| 2818 | const uid = $(this).data('uid'); | 2826 | const uid = $(this).data('uid'); |
| 2819 | const value = Number($(this).val()); | 2827 | const value = Number($(this).val()); |
| 2820 | data.entries[uid].position = !isNaN(value) ? value : 0; | 2828 | data.entries[uid].position = !isNaN(value) ? value : 0; |
| @@ -2836,7 +2844,7 @@ function getWorldEntry(name, data, entry) { | |||
| 2836 | // Write the original value as extensions field | 2844 | // Write the original value as extensions field |
| 2837 | setOriginalDataValue(data, uid, 'extensions.position', data.entries[uid].position); | 2845 | setOriginalDataValue(data, uid, 'extensions.position', data.entries[uid].position); |
| 2838 | setOriginalDataValue(data, uid, 'extensions.role', data.entries[uid].role); | 2846 | setOriginalDataValue(data, uid, 'extensions.role', data.entries[uid].role); |
| 2839 | saveWorldInfo(name, data); | 2847 | await saveWorldInfo(name, data); |
| 2840 | }); | 2848 | }); |
| 2841 | 2849 | ||
| 2842 | const roleValue = entry.position === world_info_position.atDepth ? String(entry.role ?? extension_prompt_roles.SYSTEM) : ''; | 2850 | const roleValue = entry.position === world_info_position.atDepth ? String(entry.role ?? extension_prompt_roles.SYSTEM) : ''; |
| @@ -2852,12 +2860,12 @@ function getWorldEntry(name, data, entry) { | |||
| 2852 | /* | 2860 | /* |
| 2853 | const disableInput = template.find('input[name="disable"]'); | 2861 | const disableInput = template.find('input[name="disable"]'); |
| 2854 | disableInput.data("uid", entry.uid); | 2862 | disableInput.data("uid", entry.uid); |
| 2855 | disableInput.on("input", function () { | 2863 | disableInput.on("input", async function () { |
| 2856 | const uid = $(this).data("uid"); | 2864 | const uid = $(this).data("uid"); |
| 2857 | const value = $(this).prop("checked"); | 2865 | const value = $(this).prop("checked"); |
| 2858 | data.entries[uid].disable = value; | 2866 | data.entries[uid].disable = value; |
| 2859 | setOriginalDataValue(data, uid, "enabled", !data.entries[uid].disable); | 2867 | setOriginalDataValue(data, uid, "enabled", !data.entries[uid].disable); |
| 2860 | saveWorldInfo(name, data); | 2868 | await saveWorldInfo(name, data); |
| 2861 | }); | 2869 | }); |
| 2862 | disableInput.prop("checked", entry.disable).trigger("input"); | 2870 | disableInput.prop("checked", entry.disable).trigger("input"); |
| 2863 | */ | 2871 | */ |
| @@ -2869,7 +2877,7 @@ function getWorldEntry(name, data, entry) { | |||
| 2869 | // Prevent closing the drawer on clicking the input | 2877 | // Prevent closing the drawer on clicking the input |
| 2870 | event.stopPropagation(); | 2878 | event.stopPropagation(); |
| 2871 | }); | 2879 | }); |
| 2872 | entryStateSelector.on('input', function () { | 2880 | entryStateSelector.on('input', async function () { |
| 2873 | const uid = entry.uid; | 2881 | const uid = entry.uid; |
| 2874 | const value = $(this).val(); | 2882 | const value = $(this).val(); |
| 2875 | switch (value) { | 2883 | switch (value) { |
| @@ -2910,7 +2918,7 @@ function getWorldEntry(name, data, entry) { | |||
| 2910 | template.addClass('disabledWIEntry'); | 2918 | template.addClass('disabledWIEntry'); |
| 2911 | break; | 2919 | break; |
| 2912 | } | 2920 | } |
| 2913 | saveWorldInfo(name, data); | 2921 | await saveWorldInfo(name, data); |
| 2914 | 2922 | ||
| 2915 | }); | 2923 | }); |
| 2916 | 2924 | ||
| @@ -2930,52 +2938,50 @@ function getWorldEntry(name, data, entry) { | |||
| 2930 | .prop('selected', true) | 2938 | .prop('selected', true) |
| 2931 | .trigger('input'); | 2939 | .trigger('input'); |
| 2932 | 2940 | ||
| 2933 | saveWorldInfo(name, data); | ||
| 2934 | |||
| 2935 | // exclude recursion | 2941 | // exclude recursion |
| 2936 | const excludeRecursionInput = template.find('input[name="exclude_recursion"]'); | 2942 | const excludeRecursionInput = template.find('input[name="exclude_recursion"]'); |
| 2937 | excludeRecursionInput.data('uid', entry.uid); | 2943 | excludeRecursionInput.data('uid', entry.uid); |
| 2938 | excludeRecursionInput.on('input', function () { | 2944 | excludeRecursionInput.on('input', async function () { |
| 2939 | const uid = $(this).data('uid'); | 2945 | const uid = $(this).data('uid'); |
| 2940 | const value = $(this).prop('checked'); | 2946 | const value = $(this).prop('checked'); |
| 2941 | data.entries[uid].excludeRecursion = value; | 2947 | data.entries[uid].excludeRecursion = value; |
| 2942 | setOriginalDataValue(data, uid, 'extensions.exclude_recursion', data.entries[uid].excludeRecursion); | 2948 | setOriginalDataValue(data, uid, 'extensions.exclude_recursion', data.entries[uid].excludeRecursion); |
| 2943 | saveWorldInfo(name, data); | 2949 | await saveWorldInfo(name, data); |
| 2944 | }); | 2950 | }); |
| 2945 | excludeRecursionInput.prop('checked', entry.excludeRecursion).trigger('input'); | 2951 | excludeRecursionInput.prop('checked', entry.excludeRecursion).trigger('input'); |
| 2946 | 2952 | ||
| 2947 | // prevent recursion | 2953 | // prevent recursion |
| 2948 | const preventRecursionInput = template.find('input[name="prevent_recursion"]'); | 2954 | const preventRecursionInput = template.find('input[name="prevent_recursion"]'); |
| 2949 | preventRecursionInput.data('uid', entry.uid); | 2955 | preventRecursionInput.data('uid', entry.uid); |
| 2950 | preventRecursionInput.on('input', function () { | 2956 | preventRecursionInput.on('input', async function () { |
| 2951 | const uid = $(this).data('uid'); | 2957 | const uid = $(this).data('uid'); |
| 2952 | const value = $(this).prop('checked'); | 2958 | const value = $(this).prop('checked'); |
| 2953 | data.entries[uid].preventRecursion = value; | 2959 | data.entries[uid].preventRecursion = value; |
| 2954 | setOriginalDataValue(data, uid, 'extensions.prevent_recursion', data.entries[uid].preventRecursion); | 2960 | setOriginalDataValue(data, uid, 'extensions.prevent_recursion', data.entries[uid].preventRecursion); |
| 2955 | saveWorldInfo(name, data); | 2961 | await saveWorldInfo(name, data); |
| 2956 | }); | 2962 | }); |
| 2957 | preventRecursionInput.prop('checked', entry.preventRecursion).trigger('input'); | 2963 | preventRecursionInput.prop('checked', entry.preventRecursion).trigger('input'); |
| 2958 | 2964 | ||
| 2959 | // delay until recursion | 2965 | // delay until recursion |
| 2960 | const delayUntilRecursionInput = template.find('input[name="delay_until_recursion"]'); | 2966 | const delayUntilRecursionInput = template.find('input[name="delay_until_recursion"]'); |
| 2961 | delayUntilRecursionInput.data('uid', entry.uid); | 2967 | delayUntilRecursionInput.data('uid', entry.uid); |
| 2962 | delayUntilRecursionInput.on('input', function () { | 2968 | delayUntilRecursionInput.on('input', async function () { |
| 2963 | const uid = $(this).data('uid'); | 2969 | const uid = $(this).data('uid'); |
| 2964 | const value = $(this).prop('checked'); | 2970 | const value = $(this).prop('checked'); |
| 2965 | data.entries[uid].delayUntilRecursion = value; | 2971 | data.entries[uid].delayUntilRecursion = value; |
| 2966 | setOriginalDataValue(data, uid, 'extensions.delay_until_recursion', data.entries[uid].delayUntilRecursion); | 2972 | setOriginalDataValue(data, uid, 'extensions.delay_until_recursion', data.entries[uid].delayUntilRecursion); |
| 2967 | saveWorldInfo(name, data); | 2973 | await saveWorldInfo(name, data); |
| 2968 | }); | 2974 | }); |
| 2969 | delayUntilRecursionInput.prop('checked', entry.delayUntilRecursion).trigger('input'); | 2975 | delayUntilRecursionInput.prop('checked', entry.delayUntilRecursion).trigger('input'); |
| 2970 | 2976 | ||
| 2971 | // duplicate button | 2977 | // duplicate button |
| 2972 | const duplicateButton = template.find('.duplicate_entry_button'); | 2978 | const duplicateButton = template.find('.duplicate_entry_button'); |
| 2973 | duplicateButton.data('uid', entry.uid); | 2979 | duplicateButton.data('uid', entry.uid); |
| 2974 | duplicateButton.on('click', function () { | 2980 | duplicateButton.on('click', async function () { |
| 2975 | const uid = $(this).data('uid'); | 2981 | const uid = $(this).data('uid'); |
| 2976 | const entry = duplicateWorldInfoEntry(data, uid); | 2982 | const entry = duplicateWorldInfoEntry(data, uid); |
| 2977 | if (entry) { | 2983 | if (entry) { |
| 2978 | saveWorldInfo(name, data); | 2984 | await saveWorldInfo(name, data); |
| 2979 | updateEditor(entry.uid); | 2985 | updateEditor(entry.uid); |
| 2980 | } | 2986 | } |
| 2981 | }); | 2987 | }); |
| @@ -2983,18 +2989,18 @@ function getWorldEntry(name, data, entry) { | |||
| 2983 | // delete button | 2989 | // delete button |
| 2984 | const deleteButton = template.find('.delete_entry_button'); | 2990 | const deleteButton = template.find('.delete_entry_button'); |
| 2985 | deleteButton.data('uid', entry.uid); | 2991 | deleteButton.data('uid', entry.uid); |
| 2986 | deleteButton.on('click', function () { | 2992 | deleteButton.on('click', async function () { |
| 2987 | const uid = $(this).data('uid'); | 2993 | const uid = $(this).data('uid'); |
| 2988 | deleteWorldInfoEntry(data, uid); | 2994 | deleteWorldInfoEntry(data, uid); |
| 2989 | deleteOriginalDataValue(data, uid); | 2995 | deleteOriginalDataValue(data, uid); |
| 2990 | saveWorldInfo(name, data); | 2996 | await saveWorldInfo(name, data); |
| 2991 | updateEditor(navigation_option.previous); | 2997 | updateEditor(navigation_option.previous); |
| 2992 | }); | 2998 | }); |
| 2993 | 2999 | ||
| 2994 | // scan depth | 3000 | // scan depth |
| 2995 | const scanDepthInput = template.find('input[name="scanDepth"]'); | 3001 | const scanDepthInput = template.find('input[name="scanDepth"]'); |
| 2996 | scanDepthInput.data('uid', entry.uid); | 3002 | scanDepthInput.data('uid', entry.uid); |
| 2997 | scanDepthInput.on('input', function () { | 3003 | scanDepthInput.on('input', async function () { |
| 2998 | const uid = $(this).data('uid'); | 3004 | const uid = $(this).data('uid'); |
| 2999 | const isEmpty = $(this).val() === ''; | 3005 | const isEmpty = $(this).val() === ''; |
| 3000 | const value = Number($(this).val()); | 3006 | const value = Number($(this).val()); |
| @@ -3014,59 +3020,59 @@ function getWorldEntry(name, data, entry) { | |||
| 3014 | 3020 | ||
| 3015 | data.entries[uid].scanDepth = !isEmpty && !isNaN(value) && value >= 0 && value <= MAX_SCAN_DEPTH ? Math.floor(value) : null; | 3021 | data.entries[uid].scanDepth = !isEmpty && !isNaN(value) && value >= 0 && value <= MAX_SCAN_DEPTH ? Math.floor(value) : null; |
| 3016 | setOriginalDataValue(data, uid, 'extensions.scan_depth', data.entries[uid].scanDepth); | 3022 | setOriginalDataValue(data, uid, 'extensions.scan_depth', data.entries[uid].scanDepth); |
| 3017 | saveWorldInfo(name, data); | 3023 | await saveWorldInfo(name, data); |
| 3018 | }); | 3024 | }); |
| 3019 | scanDepthInput.val(entry.scanDepth ?? null).trigger('input'); | 3025 | scanDepthInput.val(entry.scanDepth ?? null).trigger('input'); |
| 3020 | 3026 | ||
| 3021 | // case sensitive select | 3027 | // case sensitive select |
| 3022 | const caseSensitiveSelect = template.find('select[name="caseSensitive"]'); | 3028 | const caseSensitiveSelect = template.find('select[name="caseSensitive"]'); |
| 3023 | caseSensitiveSelect.data('uid', entry.uid); | 3029 | caseSensitiveSelect.data('uid', entry.uid); |
| 3024 | caseSensitiveSelect.on('input', function () { | 3030 | caseSensitiveSelect.on('input', async function () { |
| 3025 | const uid = $(this).data('uid'); | 3031 | const uid = $(this).data('uid'); |
| 3026 | const value = $(this).val(); | 3032 | const value = $(this).val(); |
| 3027 | 3033 | ||
| 3028 | data.entries[uid].caseSensitive = value === 'null' ? null : value === 'true'; | 3034 | data.entries[uid].caseSensitive = value === 'null' ? null : value === 'true'; |
| 3029 | setOriginalDataValue(data, uid, 'extensions.case_sensitive', data.entries[uid].caseSensitive); | 3035 | setOriginalDataValue(data, uid, 'extensions.case_sensitive', data.entries[uid].caseSensitive); |
| 3030 | saveWorldInfo(name, data); | 3036 | await saveWorldInfo(name, data); |
| 3031 | }); | 3037 | }); |
| 3032 | caseSensitiveSelect.val((entry.caseSensitive === null || entry.caseSensitive === undefined) ? 'null' : entry.caseSensitive ? 'true' : 'false').trigger('input'); | 3038 | caseSensitiveSelect.val((entry.caseSensitive === null || entry.caseSensitive === undefined) ? 'null' : entry.caseSensitive ? 'true' : 'false').trigger('input'); |
| 3033 | 3039 | ||
| 3034 | // match whole words select | 3040 | // match whole words select |
| 3035 | const matchWholeWordsSelect = template.find('select[name="matchWholeWords"]'); | 3041 | const matchWholeWordsSelect = template.find('select[name="matchWholeWords"]'); |
| 3036 | matchWholeWordsSelect.data('uid', entry.uid); | 3042 | matchWholeWordsSelect.data('uid', entry.uid); |
| 3037 | matchWholeWordsSelect.on('input', function () { | 3043 | matchWholeWordsSelect.on('input', async function () { |
| 3038 | const uid = $(this).data('uid'); | 3044 | const uid = $(this).data('uid'); |
| 3039 | const value = $(this).val(); | 3045 | const value = $(this).val(); |
| 3040 | 3046 | ||
| 3041 | data.entries[uid].matchWholeWords = value === 'null' ? null : value === 'true'; | 3047 | data.entries[uid].matchWholeWords = value === 'null' ? null : value === 'true'; |
| 3042 | setOriginalDataValue(data, uid, 'extensions.match_whole_words', data.entries[uid].matchWholeWords); | 3048 | setOriginalDataValue(data, uid, 'extensions.match_whole_words', data.entries[uid].matchWholeWords); |
| 3043 | saveWorldInfo(name, data); | 3049 | await saveWorldInfo(name, data); |
| 3044 | }); | 3050 | }); |
| 3045 | matchWholeWordsSelect.val((entry.matchWholeWords === null || entry.matchWholeWords === undefined) ? 'null' : entry.matchWholeWords ? 'true' : 'false').trigger('input'); | 3051 | matchWholeWordsSelect.val((entry.matchWholeWords === null || entry.matchWholeWords === undefined) ? 'null' : entry.matchWholeWords ? 'true' : 'false').trigger('input'); |
| 3046 | 3052 | ||
| 3047 | // use group scoring select | 3053 | // use group scoring select |
| 3048 | const useGroupScoringSelect = template.find('select[name="useGroupScoring"]'); | 3054 | const useGroupScoringSelect = template.find('select[name="useGroupScoring"]'); |
| 3049 | useGroupScoringSelect.data('uid', entry.uid); | 3055 | useGroupScoringSelect.data('uid', entry.uid); |
| 3050 | useGroupScoringSelect.on('input', function () { | 3056 | useGroupScoringSelect.on('input', async function () { |
| 3051 | const uid = $(this).data('uid'); | 3057 | const uid = $(this).data('uid'); |
| 3052 | const value = $(this).val(); | 3058 | const value = $(this).val(); |
| 3053 | 3059 | ||
| 3054 | data.entries[uid].useGroupScoring = value === 'null' ? null : value === 'true'; | 3060 | data.entries[uid].useGroupScoring = value === 'null' ? null : value === 'true'; |
| 3055 | setOriginalDataValue(data, uid, 'extensions.use_group_scoring', data.entries[uid].useGroupScoring); | 3061 | setOriginalDataValue(data, uid, 'extensions.use_group_scoring', data.entries[uid].useGroupScoring); |
| 3056 | saveWorldInfo(name, data); | 3062 | await saveWorldInfo(name, data); |
| 3057 | }); | 3063 | }); |
| 3058 | useGroupScoringSelect.val((entry.useGroupScoring === null || entry.useGroupScoring === undefined) ? 'null' : entry.useGroupScoring ? 'true' : 'false').trigger('input'); | 3064 | useGroupScoringSelect.val((entry.useGroupScoring === null || entry.useGroupScoring === undefined) ? 'null' : entry.useGroupScoring ? 'true' : 'false').trigger('input'); |
| 3059 | 3065 | ||
| 3060 | // automation id | 3066 | // automation id |
| 3061 | const automationIdInput = template.find('input[name="automationId"]'); | 3067 | const automationIdInput = template.find('input[name="automationId"]'); |
| 3062 | automationIdInput.data('uid', entry.uid); | 3068 | automationIdInput.data('uid', entry.uid); |
| 3063 | automationIdInput.on('input', function () { | 3069 | automationIdInput.on('input', async function () { |
| 3064 | const uid = $(this).data('uid'); | 3070 | const uid = $(this).data('uid'); |
| 3065 | const value = $(this).val(); | 3071 | const value = $(this).val(); |
| 3066 | 3072 | ||
| 3067 | data.entries[uid].automationId = value; | 3073 | data.entries[uid].automationId = value; |
| 3068 | setOriginalDataValue(data, uid, 'extensions.automation_id', data.entries[uid].automationId); | 3074 | setOriginalDataValue(data, uid, 'extensions.automation_id', data.entries[uid].automationId); |
| 3069 | saveWorldInfo(name, data); | 3075 | await saveWorldInfo(name, data); |
| 3070 | }); | 3076 | }); |
| 3071 | automationIdInput.val(entry.automationId ?? '').trigger('input'); | 3077 | automationIdInput.val(entry.automationId ?? '').trigger('input'); |
| 3072 | setTimeout(() => createEntryInputAutocomplete(automationIdInput, getAutomationIdCallback(data)), 1); | 3078 | setTimeout(() => createEntryInputAutocomplete(automationIdInput, getAutomationIdCallback(data)), 1); |
| @@ -3301,6 +3307,9 @@ function createWorldInfoEntry(_name, data) { | |||
| 3301 | } | 3307 | } |
| 3302 | 3308 | ||
| 3303 | async function _save(name, data) { | 3309 | async function _save(name, data) { |
| 3310 | // Prevent double saving if both immediate and debounced save are called | ||
| 3311 | cancelDebounce(saveWorldDebounced); | ||
| 3312 | |||
| 3304 | await fetch('/api/worldinfo/edit', { | 3313 | await fetch('/api/worldinfo/edit', { |
| 3305 | method: 'POST', | 3314 | method: 'POST', |
| 3306 | headers: getRequestHeaders(), | 3315 | headers: getRequestHeaders(), |
| @@ -3309,12 +3318,13 @@ async function _save(name, data) { | |||
| 3309 | eventSource.emit(event_types.WORLDINFO_UPDATED, name, data); | 3318 | eventSource.emit(event_types.WORLDINFO_UPDATED, name, data); |
| 3310 | } | 3319 | } |
| 3311 | 3320 | ||
| 3312 | async function saveWorldInfo(name, data, immediately) { | 3321 | async function saveWorldInfo(name, data, immediately = false) { |
| 3313 | if (!name || !data) { | 3322 | if (!name || !data) { |
| 3314 | return; | 3323 | return; |
| 3315 | } | 3324 | } |
| 3316 | 3325 | ||
| 3317 | worldInfoCache.delete(name); | 3326 | // Update cache immediately, so any future call can pull from this |
| 3327 | worldInfoCache.set(name, data); | ||
| 3318 | 3328 | ||
| 3319 | if (immediately) { | 3329 | if (immediately) { |
| 3320 | return await _save(name, data); | 3330 | return await _save(name, data); |