Merge pull request #2601 from SillyTavern/wi-blackbox-be-gone WI blackbox begone (Export, document and refactor a bit of the WI API)
Signed| @@ -18,33 +18,13 @@ import { SlashCommandClosure } from './slash-commands/SlashCommandClosure.js'; | ||
| 18 | 18 | import { callGenericPopup, Popup, POPUP_TYPE } from './popup.js'; |
| 19 | 19 | import { StructuredCloneMap } from './util/StructuredCloneMap.js'; |
| 20 | 20 | |
| 21 | 21 | export const world_info_insertion_strategy = { |
| 22 | - world_info, | |
| 23 | - world_info_budget, | |
| 24 | - world_info_depth, | |
| 25 | - world_info_min_activations, | |
| 26 | - world_info_min_activations_depth_max, | |
| 27 | - world_info_include_names, | |
| 28 | - world_info_recursive, | |
| 29 | - world_info_overflow_alert, | |
| 30 | - world_info_case_sensitive, | |
| 31 | - world_info_match_whole_words, | |
| 32 | - world_info_character_strategy, | |
| 33 | - world_info_budget_cap, | |
| 34 | - world_names, | |
| 35 | - checkWorldInfo, | |
| 36 | - deleteWorldInfo, | |
| 37 | - setWorldInfoSettings, | |
| 38 | - getWorldInfoPrompt, | |
| 39 | -}; | |
| 40 | - | |
| 41 | -const world_info_insertion_strategy = { | |
| 42 | 22 | evenly: 0, |
| 43 | 23 | character_first: 1, |
| 44 | 24 | global_first: 2, |
| 45 | 25 | }; |
| 46 | 26 | |
| 47 | 27 | export const world_info_logic = { |
| 48 | 28 | AND_ANY: 0, |
| 49 | 29 | NOT_ALL: 1, |
| 50 | 30 | NOT_ANY: 2, |
| @@ -54,7 +34,7 @@ const world_info_logic = { | ||
| 54 | 34 | /** |
| 55 | 35 | * @enum {number} Possible states of the WI evaluation |
| 56 | 36 | */ |
| 57 | 37 | export const scan_state = { |
| 58 | 38 | /** |
| 59 | 39 | * The scan will be stopped. |
| 60 | 40 | */ |
| @@ -75,23 +55,23 @@ const scan_state = { | ||
| 75 | 55 | |
| 76 | 56 | const WI_ENTRY_EDIT_TEMPLATE = $('#entry_edit_template .world_entry'); |
| 77 | 57 | |
| 78 | 58 | export let world_info = {}; |
| 79 | 59 | export let selected_world_info = []; |
| 80 | 60 | /** @type {string[]} */ |
| 81 | 61 | export let world_names; |
| 82 | 62 | export let world_info_depth = 2; |
| 83 | 63 | export let world_info_min_activations = 0; // if > 0, will continue seeking chat until minimum world infos are activated |
| 84 | 64 | export let world_info_min_activations_depth_max = 0; // used when (world_info_min_activations > 0) |
| 85 | 65 | |
| 86 | 66 | export let world_info_budget = 25; |
| 87 | 67 | export let world_info_include_names = true; |
| 88 | 68 | export let world_info_recursive = false; |
| 89 | 69 | export let world_info_overflow_alert = false; |
| 90 | 70 | export let world_info_case_sensitive = false; |
| 91 | 71 | export let world_info_match_whole_words = false; |
| 92 | 72 | export let world_info_use_group_scoring = false; |
| 93 | 73 | export let world_info_character_strategy = world_info_insertion_strategy.character_first; |
| 94 | 74 | export let world_info_budget_cap = 0; |
| 95 | 75 | const saveWorldDebounced = debounce(async (name, data) => await _save(name, data), debounce_timeout.relaxed); |
| 96 | 76 | const saveSettingsDebounced = debounce(() => { |
| 97 | 77 | Object.assign(world_info, { globalSelect: selected_world_info }); |
| @@ -101,13 +81,13 @@ const sortFn = (a, b) => b.order - a.order; | ||
| 101 | 81 | let updateEditor = (navigation, flashOnNav = true) => { console.debug('Triggered WI navigation', navigation, flashOnNav); }; |
| 102 | 82 | |
| 103 | 83 | // Do not optimize. updateEditor is a function that is updated by the displayWorldEntries with new data. |
| 104 | 84 | export const worldInfoFilter = new FilterHelper(() => updateEditor()); |
| 105 | 85 | export const SORT_ORDER_KEY = 'world_info_sort_order'; |
| 106 | 86 | export const METADATA_KEY = 'world_info'; |
| 107 | 87 | |
| 108 | 88 | export const DEFAULT_DEPTH = 4; |
| 109 | 89 | export const DEFAULT_WEIGHT = 100; |
| 110 | 90 | export const MAX_SCAN_DEPTH = 1000; |
| 111 | 91 | const KNOWN_DECORATORS = ['@@activate', '@@dont_activate']; |
| 112 | 92 | |
| 113 | 93 | // Typedef area |
| @@ -732,7 +712,7 @@ export function getWorldInfoSettings() { | ||
| 732 | 712 | }; |
| 733 | 713 | } |
| 734 | 714 | |
| 735 | 715 | export const world_info_position = { |
| 736 | 716 | before: 0, |
| 737 | 717 | after: 1, |
| 738 | 718 | ANTop: 2, |
| @@ -747,8 +727,18 @@ export const wi_anchor_position = { | ||
| 747 | 727 | after: 1, |
| 748 | 728 | }; |
| 749 | 729 | |
| 750 | -/** @type {StructuredCloneMap<string,object>} */ | |
| 730 | +/** | |
| 751 | -const worldInfoCache = new StructuredCloneMap({ cloneOnGet: true, cloneOnSet: false }); | |
| 731 | + * The cache of all world info data that was loaded from the backend. | |
| 732 | + * | |
| 733 | + * Calling `loadWorldInfo` will fill this cache and utilize this cache, so should be the preferred way to load any world info data. | |
| 734 | + * Only use the cache directly if you need synchronous access. | |
| 735 | + * | |
| 736 | + * This will return a deep clone of the data, so no way to modify the data without actually saving it. | |
| 737 | + * Should generally be only used for readonly access. | |
| 738 | + * | |
| 739 | + * @type {StructuredCloneMap<string,object>} | |
| 740 | + * */ | |
| 741 | +export const worldInfoCache = new StructuredCloneMap({ cloneOnGet: true, cloneOnSet: false }); | |
| 752 | 742 | |
| 753 | 743 | /** |
| 754 | 744 | * Gets the world info based on chat messages. |
| @@ -758,7 +748,7 @@ const worldInfoCache = new StructuredCloneMap({ cloneOnGet: true, cloneOnSet: fa | ||
| 758 | 748 | * @typedef {{worldInfoString: string, worldInfoBefore: string, worldInfoAfter: string, worldInfoExamples: any[], worldInfoDepth: any[]}} WIPromptResult |
| 759 | 749 | * @returns {Promise<WIPromptResult>} The world info string and depth. |
| 760 | 750 | */ |
| 761 | 751 | export async function getWorldInfoPrompt(chat, maxContext, isDryRun) { |
| 762 | 752 | let worldInfoString = '', worldInfoBefore = '', worldInfoAfter = ''; |
| 763 | 753 | |
| 764 | 754 | const activatedWorldInfo = await checkWorldInfo(chat, maxContext, isDryRun); |
| @@ -780,7 +770,7 @@ async function getWorldInfoPrompt(chat, maxContext, isDryRun) { | ||
| 780 | 770 | }; |
| 781 | 771 | } |
| 782 | 772 | |
| 783 | 773 | export function setWorldInfoSettings(settings, data) { |
| 784 | 774 | if (settings.world_info_depth !== undefined) |
| 785 | 775 | world_info_depth = Number(settings.world_info_depth); |
| 786 | 776 | if (settings.world_info_min_activations !== undefined) |
| @@ -916,7 +906,7 @@ function registerWorldInfoSlashCommands() { | ||
| 916 | 906 | return ''; |
| 917 | 907 | } |
| 918 | 908 | |
| 919 | 909 | const data = await loadWorldInfoDataloadWorldInfo(file); |
| 920 | 910 | |
| 921 | 911 | if (!data || !('entries' in data)) { |
| 922 | 912 | toastr.warning('World Info file has an invalid format'); |
| @@ -965,7 +955,7 @@ function registerWorldInfoSlashCommands() { | ||
| 965 | 955 | return ''; |
| 966 | 956 | } |
| 967 | 957 | |
| 968 | 958 | if (typeof newEntryTemplatenewWorldInfoEntryTemplate[field] === 'boolean') { |
| 969 | 959 | const isTrue = isTrueBoolean(value); |
| 970 | 960 | const isFalse = isFalseBoolean(value); |
| 971 | 961 | |
| @@ -1016,7 +1006,7 @@ function registerWorldInfoSlashCommands() { | ||
| 1016 | 1006 | return ''; |
| 1017 | 1007 | } |
| 1018 | 1008 | |
| 1019 | 1009 | if (newEntryTemplatenewWorldInfoEntryTemplate[field] === undefined) { |
| 1020 | 1010 | toastr.warning('Valid field name is required'); |
| 1021 | 1011 | return ''; |
| 1022 | 1012 | } |
| @@ -1038,7 +1028,7 @@ function registerWorldInfoSlashCommands() { | ||
| 1038 | 1028 | const file = args.file; |
| 1039 | 1029 | const key = args.key; |
| 1040 | 1030 | |
| 1041 | 1031 | const data = await loadWorldInfoDataloadWorldInfo(file); |
| 1042 | 1032 | |
| 1043 | 1033 | if (!data || !('entries' in data)) { |
| 1044 | 1034 | toastr.warning('Valid World Info file name is required'); |
| @@ -1075,7 +1065,7 @@ function registerWorldInfoSlashCommands() { | ||
| 1075 | 1065 | |
| 1076 | 1066 | value = value.replace(/\\([{}|])/g, '$1'); |
| 1077 | 1067 | |
| 1078 | 1068 | const data = await loadWorldInfoDataloadWorldInfo(file); |
| 1079 | 1069 | |
| 1080 | 1070 | if (!data || !('entries' in data)) { |
| 1081 | 1071 | toastr.warning('Valid World Info file name is required'); |
| @@ -1089,7 +1079,7 @@ function registerWorldInfoSlashCommands() { | ||
| 1089 | 1079 | return ''; |
| 1090 | 1080 | } |
| 1091 | 1081 | |
| 1092 | 1082 | if (newEntryTemplatenewWorldInfoEntryTemplate[field] === undefined) { |
| 1093 | 1083 | toastr.warning('Valid field name is required'); |
| 1094 | 1084 | return ''; |
| 1095 | 1085 | } |
| @@ -1104,8 +1094,8 @@ function registerWorldInfoSlashCommands() { | ||
| 1104 | 1094 | entry[field] = value; |
| 1105 | 1095 | } |
| 1106 | 1096 | |
| 1107 | 1097 | if (originalDataKeyMaporiginalWIDataKeyMap[field]) { |
| 1108 | 1098 | setOriginalDataValuesetWIOriginalDataValue(data, uid, originalDataKeyMaporiginalWIDataKeyMap[field], entry[field]); |
| 1109 | 1099 | } |
| 1110 | 1100 | |
| 1111 | 1101 | await saveWorldInfo(file, data); |
| @@ -1226,7 +1216,7 @@ function registerWorldInfoSlashCommands() { | ||
| 1226 | 1216 | /** A collection of local enum providers for this context of world info */ |
| 1227 | 1217 | const localEnumProviders = { |
| 1228 | 1218 | /** All possible fields that can be set in a WI entry */ |
| 1229 | 1219 | wiEntryFields: () => Object.entries(newEntryDefinitionnewWorldInfoEntryDefinition).map(([key, value]) => |
| 1230 | 1220 | new SlashCommandEnumValue(key, `[${value.type}] default: ${(typeof value.default === 'string' ? `'${value.default}'` : value.default)}`, |
| 1231 | 1221 | enumTypes.enum, enumIcons.getDataTypeIcon(value.type))), |
| 1232 | 1222 | |
| @@ -1566,18 +1556,32 @@ function registerWorldInfoSlashCommands() { | ||
| 1566 | 1556 | })); |
| 1567 | 1557 | } |
| 1568 | 1558 | |
| 1569 | -// World Info Editor | |
| 1559 | + | |
| 1570 | -async function showWorldEditor(name) { | |
| 1560 | +/** | |
| 1561 | + * Loads the given world into the World Editor. | |
| 1562 | + * | |
| 1563 | + * @param {string} name - The name of the world | |
| 1564 | + * @return {Promise<void>} A promise that resolves when the world editor is loaded | |
| 1565 | + */ | |
| 1566 | +export async function showWorldEditor(name) { | |
| 1571 | 1567 | if (!name) { |
| 1572 | 1568 | hideWorldEditor(); |
| 1573 | 1569 | return; |
| 1574 | 1570 | } |
| 1575 | 1571 | |
| 1576 | 1572 | const wiData = await loadWorldInfoDataloadWorldInfo(name); |
| 1577 | 1573 | displayWorldEntries(name, wiData); |
| 1578 | 1574 | } |
| 1579 | 1575 | |
| 1580 | -async function loadWorldInfoData(name) { | |
| 1576 | +/** | |
| 1577 | + * Loads world info from the backend. | |
| 1578 | + * | |
| 1579 | + * This function will return from `worldInfoCache` if it has already been loaded before. | |
| 1580 | + * | |
| 1581 | + * @param {string} name - The name of the world to load | |
| 1582 | + * @return {Promise<Object|null>} A promise that resolves to the loaded world information, or null if the request fails. | |
| 1583 | + */ | |
| 1584 | +export async function loadWorldInfo(name) { | |
| 1581 | 1585 | if (!name) { |
| 1582 | 1586 | return; |
| 1583 | 1587 | } |
| @@ -1635,14 +1639,18 @@ function getWIElement(name) { | ||
| 1635 | 1639 | } |
| 1636 | 1640 | |
| 1637 | 1641 | /** |
| 1642 | + * Sorts the given data based on the selected sort option | |
| 1643 | + * | |
| 1638 | 1644 | * @param {any[]} data WI entries |
| 1645 | + * @param {object} [options={}] - Optional arguments | |
| 1646 | + * @param {{sortField?: string, sortOrder?: string, sortRule?: string}} [options.customSort={}] - Custom sort options, instead of the chosen UI sort | |
| 1639 | 1647 | * @returns {any[]} Sorted data |
| 1640 | 1648 | */ |
| 1641 | -function sortEntries(data) { | |
| 1649 | +export function sortWorldInfoEntries(data, { customSort = null } = {}) { | |
| 1642 | 1650 | const option = $('#world_info_sort_order').find(':selected'); |
| 1643 | 1651 | const sortField = customSort?.sortField ?? option.data('field'); |
| 1644 | 1652 | const sortOrder = customSort?.sortOrder ?? option.data('order'); |
| 1645 | 1653 | const sortRule = customSort?.sortRule ?? option.data('rule'); |
| 1646 | 1654 | const orderSign = sortOrder === 'asc' ? 1 : -1; |
| 1647 | 1655 | |
| 1648 | 1656 | if (!data.length) return data; |
| @@ -1801,7 +1809,7 @@ function displayWorldEntries(name, data, navigation = navigation_option.none, fl | ||
| 1801 | 1809 | |
| 1802 | 1810 | // Apply the filter and do the chosen sorting |
| 1803 | 1811 | entriesArray = worldInfoFilter.applyFilters(entriesArray); |
| 1804 | 1812 | entriesArray = sortEntriessortWorldInfoEntries(entriesArray); |
| 1805 | 1813 | |
| 1806 | 1814 | // Cache keys |
| 1807 | 1815 | const keys = entriesArray.flatMap(entry => [...entry.key, ...entry.keysecondary]); |
| @@ -1919,7 +1927,7 @@ function displayWorldEntries(name, data, navigation = navigation_option.none, fl | ||
| 1919 | 1927 | for (const entry of Object.values(data.entries)) { |
| 1920 | 1928 | if (!entry.comment && Array.isArray(entry.key) && entry.key.length > 0) { |
| 1921 | 1929 | entry.comment = entry.key[0]; |
| 1922 | 1930 | setOriginalDataValuesetWIOriginalDataValue(data, entry.uid, 'comment', entry.comment); |
| 1923 | 1931 | counter++; |
| 1924 | 1932 | } |
| 1925 | 1933 | } |
| @@ -1954,7 +1962,7 @@ function displayWorldEntries(name, data, navigation = navigation_option.none, fl | ||
| 1954 | 1962 | |
| 1955 | 1963 | // We need to sort the entries here, as the data source isn't sorted |
| 1956 | 1964 | const entries = Object.values(data.entries); |
| 1957 | 1965 | sortEntriessortWorldInfoEntries(entries); |
| 1958 | 1966 | |
| 1959 | 1967 | let updated = 0, current = start; |
| 1960 | 1968 | for (const entry of entries) { |
| @@ -1962,7 +1970,7 @@ function displayWorldEntries(name, data, navigation = navigation_option.none, fl | ||
| 1962 | 1970 | if (entry.order === newOrder) continue; |
| 1963 | 1971 | |
| 1964 | 1972 | entry.order = newOrder; |
| 1965 | 1973 | setOriginalDataValuesetWIOriginalDataValue(data, entry.order, 'order', entry.order); |
| 1966 | 1974 | updated++; |
| 1967 | 1975 | } |
| 1968 | 1976 | |
| @@ -2025,7 +2033,7 @@ function displayWorldEntries(name, data, navigation = navigation_option.none, fl | ||
| 2025 | 2033 | } |
| 2026 | 2034 | |
| 2027 | 2035 | item.displayIndex = minDisplayIndex + index; |
| 2028 | 2036 | setOriginalDataValuesetWIOriginalDataValue(data, uid, 'extensions.display_index', item.displayIndex); |
| 2029 | 2037 | }); |
| 2030 | 2038 | |
| 2031 | 2039 | console.table(Object.keys(data.entries).map(uid => data.entries[uid]).map(x => ({ uid: x.uid, key: x.key.join(','), displayIndex: x.displayIndex }))); |
| @@ -2036,7 +2044,7 @@ function displayWorldEntries(name, data, navigation = navigation_option.none, fl | ||
| 2036 | 2044 | //$("#world_popup_entries_list").disableSelection(); |
| 2037 | 2045 | } |
| 2038 | 2046 | |
| 2039 | 2047 | export const originalDataKeyMaporiginalWIDataKeyMap = { |
| 2040 | 2048 | 'displayIndex': 'extensions.display_index', |
| 2041 | 2049 | 'excludeRecursion': 'extensions.exclude_recursion', |
| 2042 | 2050 | 'preventRecursion': 'extensions.prevent_recursion', |
| @@ -2087,7 +2095,17 @@ function verifyWorldInfoSearchSortRule() { | ||
| 2087 | 2095 | } |
| 2088 | 2096 | } |
| 2089 | 2097 | |
| 2090 | -function setOriginalDataValue(data, uid, key, value) { | |
| 2098 | +/** | |
| 2099 | + * Sets the value of a specific key in the original data entry corresponding to the given uid | |
| 2100 | + * This needs to be called whenever you update JSON data fields. | |
| 2101 | + * Use `originalWIDataKeyMap` to find the correct value to be set. | |
| 2102 | + * | |
| 2103 | + * @param {object} data - The data object containing the original data entries. | |
| 2104 | + * @param {string} uid - The unique identifier of the data entry. | |
| 2105 | + * @param {string} key - The key of the value to be set. | |
| 2106 | + * @param {any} value - The value to be set. | |
| 2107 | + */ | |
| 2108 | +export function setWIOriginalDataValue(data, uid, key, value) { | |
| 2091 | 2109 | if (data.originalData && Array.isArray(data.originalData.entries)) { |
| 2092 | 2110 | let originalEntry = data.originalData.entries.find(x => x.uid === uid); |
| 2093 | 2111 | |
| @@ -2099,7 +2117,13 @@ function setOriginalDataValue(data, uid, key, value) { | ||
| 2099 | 2117 | } |
| 2100 | 2118 | } |
| 2101 | 2119 | |
| 2102 | -function deleteOriginalDataValue(data, uid) { | |
| 2120 | +/** | |
| 2121 | + * Deletes the original data entry corresponding to the given uid from the provided data object | |
| 2122 | + * | |
| 2123 | + * @param {object} data - The data object containing the original data entries | |
| 2124 | + * @param {string} uid - The unique identifier of the data entry to be deleted | |
| 2125 | + */ | |
| 2126 | +export function deleteWIOriginalDataValue(data, uid) { | |
| 2103 | 2127 | if (data.originalData && Array.isArray(data.originalData.entries)) { |
| 2104 | 2128 | const originalIndex = data.originalData.entries.findIndex(x => x.uid === uid); |
| 2105 | 2129 | |
| @@ -2120,7 +2144,7 @@ function deleteOriginalDataValue(data, uid) { | ||
| 2120 | 2144 | * @param {string} input - One or multiple keywords or regexes, separated by commas |
| 2121 | 2145 | * @returns {string[]} An array of keywords and regexes |
| 2122 | 2146 | */ |
| 2123 | 2147 | export function splitKeywordsAndRegexes(input) { |
| 2124 | 2148 | /** @type {string[]} */ |
| 2125 | 2149 | let keywordsAndRegexes = []; |
| 2126 | 2150 | |
| @@ -2224,7 +2248,7 @@ function isValidRegex(input) { | ||
| 2224 | 2248 | * @param {string} input - A delimited regex string |
| 2225 | 2249 | * @returns {RegExp|null} The regex object, or null if not a valid regex |
| 2226 | 2250 | */ |
| 2227 | 2251 | export function parseRegexFromString(input) { |
| 2228 | 2252 | // Extracting the regex pattern and flags |
| 2229 | 2253 | let match = input.match(/^\/([\w\W]+?)\/([gimsuy]*)$/); |
| 2230 | 2254 | if (!match) { |
| @@ -2310,7 +2334,7 @@ function getWorldEntry(name, data, entry) { | ||
| 2310 | 2334 | !skipReset && resetScrollHeight(this); |
| 2311 | 2335 | if (!noSave) { |
| 2312 | 2336 | data.entries[uid][entryPropName] = keys; |
| 2313 | 2337 | setOriginalDataValuesetWIOriginalDataValue(data, uid, originalDataValueName, data.entries[uid][entryPropName]); |
| 2314 | 2338 | await saveWorldInfo(name, data); |
| 2315 | 2339 | } |
| 2316 | 2340 | }); |
| @@ -2348,7 +2372,7 @@ function getWorldEntry(name, data, entry) { | ||
| 2348 | 2372 | !skipReset && resetScrollHeight(this); |
| 2349 | 2373 | if (!noSave) { |
| 2350 | 2374 | data.entries[uid][entryPropName] = splitKeywordsAndRegexes(value); |
| 2351 | 2375 | setOriginalDataValuesetWIOriginalDataValue(data, uid, originalDataValueName, data.entries[uid][entryPropName]); |
| 2352 | 2376 | await saveWorldInfo(name, data); |
| 2353 | 2377 | } |
| 2354 | 2378 | }); |
| @@ -2392,7 +2416,7 @@ function getWorldEntry(name, data, entry) { | ||
| 2392 | 2416 | const uid = $(this).data('uid'); |
| 2393 | 2417 | const value = Number($(this).val()); |
| 2394 | 2418 | data.entries[uid].selectiveLogic = !isNaN(value) ? value : world_info_logic.AND_ANY; |
| 2395 | 2419 | setOriginalDataValuesetWIOriginalDataValue(data, uid, 'selectiveLogic', data.entries[uid].selectiveLogic); |
| 2396 | 2420 | await saveWorldInfo(name, data); |
| 2397 | 2421 | }); |
| 2398 | 2422 | |
| @@ -2441,7 +2465,7 @@ function getWorldEntry(name, data, entry) { | ||
| 2441 | 2465 | } |
| 2442 | 2466 | } |
| 2443 | 2467 | |
| 2444 | 2468 | setOriginalDataValuesetWIOriginalDataValue(data, uid, 'character_filter', data.entries[uid].characterFilter); |
| 2445 | 2469 | await saveWorldInfo(name, data); |
| 2446 | 2470 | }); |
| 2447 | 2471 | characterExclusionInput.prop('checked', entry.characterFilter?.isExclude ?? false).trigger('input'); |
| @@ -2503,7 +2527,7 @@ function getWorldEntry(name, data, entry) { | ||
| 2503 | 2527 | }, |
| 2504 | 2528 | ); |
| 2505 | 2529 | } |
| 2506 | 2530 | setOriginalDataValuesetWIOriginalDataValue(data, uid, 'character_filter', data.entries[uid].characterFilter); |
| 2507 | 2531 | await saveWorldInfo(name, data); |
| 2508 | 2532 | }); |
| 2509 | 2533 | |
| @@ -2517,7 +2541,7 @@ function getWorldEntry(name, data, entry) { | ||
| 2517 | 2541 | !skipReset && resetScrollHeight(this); |
| 2518 | 2542 | data.entries[uid].comment = value; |
| 2519 | 2543 | |
| 2520 | 2544 | setOriginalDataValuesetWIOriginalDataValue(data, uid, 'comment', data.entries[uid].comment); |
| 2521 | 2545 | await saveWorldInfo(name, data); |
| 2522 | 2546 | }); |
| 2523 | 2547 | commentToggle.data('uid', entry.uid); |
| @@ -2552,7 +2576,7 @@ function getWorldEntry(name, data, entry) { | ||
| 2552 | 2576 | const value = $(this).val(); |
| 2553 | 2577 | data.entries[uid].content = value; |
| 2554 | 2578 | |
| 2555 | 2579 | setOriginalDataValuesetWIOriginalDataValue(data, uid, 'content', data.entries[uid].content); |
| 2556 | 2580 | await saveWorldInfo(name, data); |
| 2557 | 2581 | |
| 2558 | 2582 | if (skipCount) { |
| @@ -2582,7 +2606,7 @@ function getWorldEntry(name, data, entry) { | ||
| 2582 | 2606 | const value = $(this).prop('checked'); |
| 2583 | 2607 | data.entries[uid].selective = value; |
| 2584 | 2608 | |
| 2585 | 2609 | setOriginalDataValuesetWIOriginalDataValue(data, uid, 'selective', data.entries[uid].selective); |
| 2586 | 2610 | await saveWorldInfo(name, data); |
| 2587 | 2611 | |
| 2588 | 2612 | const keysecondary = $(this) |
| @@ -2631,7 +2655,7 @@ function getWorldEntry(name, data, entry) { | ||
| 2631 | 2655 | |
| 2632 | 2656 | data.entries[uid].order = !isNaN(value) ? value : 0; |
| 2633 | 2657 | updatePosOrdDisplay(uid); |
| 2634 | 2658 | setOriginalDataValuesetWIOriginalDataValue(data, uid, 'insertion_order', data.entries[uid].order); |
| 2635 | 2659 | await saveWorldInfo(name, data); |
| 2636 | 2660 | }); |
| 2637 | 2661 | orderInput.val(entry.order).trigger('input'); |
| @@ -2645,7 +2669,7 @@ function getWorldEntry(name, data, entry) { | ||
| 2645 | 2669 | const value = String($(this).val()).trim(); |
| 2646 | 2670 | |
| 2647 | 2671 | data.entries[uid].group = value; |
| 2648 | 2672 | setOriginalDataValuesetWIOriginalDataValue(data, uid, 'extensions.group', data.entries[uid].group); |
| 2649 | 2673 | await saveWorldInfo(name, data); |
| 2650 | 2674 | }); |
| 2651 | 2675 | groupInput.val(entry.group ?? '').trigger('input'); |
| @@ -2658,7 +2682,7 @@ function getWorldEntry(name, data, entry) { | ||
| 2658 | 2682 | const uid = $(this).data('uid'); |
| 2659 | 2683 | const value = $(this).prop('checked'); |
| 2660 | 2684 | data.entries[uid].groupOverride = value; |
| 2661 | 2685 | setOriginalDataValuesetWIOriginalDataValue(data, uid, 'extensions.group_override', data.entries[uid].groupOverride); |
| 2662 | 2686 | await saveWorldInfo(name, data); |
| 2663 | 2687 | }); |
| 2664 | 2688 | groupOverrideInput.prop('checked', entry.groupOverride).trigger('input'); |
| @@ -2682,7 +2706,7 @@ function getWorldEntry(name, data, entry) { | ||
| 2682 | 2706 | } |
| 2683 | 2707 | |
| 2684 | 2708 | data.entries[uid].groupWeight = !isNaN(value) ? Math.abs(value) : 1; |
| 2685 | 2709 | setOriginalDataValuesetWIOriginalDataValue(data, uid, 'extensions.group_weight', data.entries[uid].groupWeight); |
| 2686 | 2710 | await saveWorldInfo(name, data); |
| 2687 | 2711 | }); |
| 2688 | 2712 | groupWeightInput.val(entry.groupWeight ?? DEFAULT_WEIGHT).trigger('input'); |
| @@ -2695,7 +2719,7 @@ function getWorldEntry(name, data, entry) { | ||
| 2695 | 2719 | const value = Number($(this).val()); |
| 2696 | 2720 | data.entries[uid].sticky = !isNaN(value) ? value : null; |
| 2697 | 2721 | |
| 2698 | 2722 | setOriginalDataValuesetWIOriginalDataValue(data, uid, 'extensions.sticky', data.entries[uid].sticky); |
| 2699 | 2723 | await saveWorldInfo(name, data); |
| 2700 | 2724 | }); |
| 2701 | 2725 | sticky.val(entry.sticky > 0 ? entry.sticky : '').trigger('input'); |
| @@ -2708,7 +2732,7 @@ function getWorldEntry(name, data, entry) { | ||
| 2708 | 2732 | const value = Number($(this).val()); |
| 2709 | 2733 | data.entries[uid].cooldown = !isNaN(value) ? value : null; |
| 2710 | 2734 | |
| 2711 | 2735 | setOriginalDataValuesetWIOriginalDataValue(data, uid, 'extensions.cooldown', data.entries[uid].cooldown); |
| 2712 | 2736 | await saveWorldInfo(name, data); |
| 2713 | 2737 | }); |
| 2714 | 2738 | cooldown.val(entry.cooldown > 0 ? entry.cooldown : '').trigger('input'); |
| @@ -2721,7 +2745,7 @@ function getWorldEntry(name, data, entry) { | ||
| 2721 | 2745 | const value = Number($(this).val()); |
| 2722 | 2746 | data.entries[uid].delay = !isNaN(value) ? value : null; |
| 2723 | 2747 | |
| 2724 | 2748 | setOriginalDataValuesetWIOriginalDataValue(data, uid, 'extensions.delay', data.entries[uid].delay); |
| 2725 | 2749 | await saveWorldInfo(name, data); |
| 2726 | 2750 | }); |
| 2727 | 2751 | delay.val(entry.delay > 0 ? entry.delay : '').trigger('input'); |
| @@ -2741,7 +2765,7 @@ function getWorldEntry(name, data, entry) { | ||
| 2741 | 2765 | |
| 2742 | 2766 | data.entries[uid].depth = !isNaN(value) ? value : 0; |
| 2743 | 2767 | updatePosOrdDisplay(uid); |
| 2744 | 2768 | setOriginalDataValuesetWIOriginalDataValue(data, uid, 'extensions.depth', data.entries[uid].depth); |
| 2745 | 2769 | await saveWorldInfo(name, data); |
| 2746 | 2770 | }); |
| 2747 | 2771 | depthInput.val(entry.depth ?? DEFAULT_DEPTH).trigger('input'); |
| @@ -2769,7 +2793,7 @@ function getWorldEntry(name, data, entry) { | ||
| 2769 | 2793 | } |
| 2770 | 2794 | } |
| 2771 | 2795 | |
| 2772 | 2796 | setOriginalDataValuesetWIOriginalDataValue(data, uid, 'extensions.probability', data.entries[uid].probability); |
| 2773 | 2797 | await saveWorldInfo(name, data); |
| 2774 | 2798 | }); |
| 2775 | 2799 | probabilityInput.val(entry.probability).trigger('input'); |
| @@ -2836,10 +2860,10 @@ function getWorldEntry(name, data, entry) { | ||
| 2836 | 2860 | } |
| 2837 | 2861 | updatePosOrdDisplay(uid); |
| 2838 | 2862 | // Spec v2 only supports before_char and after_char |
| 2839 | 2863 | setOriginalDataValuesetWIOriginalDataValue(data, uid, 'position', data.entries[uid].position == 0 ? 'before_char' : 'after_char'); |
| 2840 | 2864 | // Write the original value as extensions field |
| 2841 | 2865 | setOriginalDataValuesetWIOriginalDataValue(data, uid, 'extensions.position', data.entries[uid].position); |
| 2842 | 2866 | setOriginalDataValuesetWIOriginalDataValue(data, uid, 'extensions.role', data.entries[uid].role); |
| 2843 | 2867 | await saveWorldInfo(name, data); |
| 2844 | 2868 | }); |
| 2845 | 2869 | |
| @@ -2881,36 +2905,36 @@ function getWorldEntry(name, data, entry) { | ||
| 2881 | 2905 | data.entries[uid].constant = true; |
| 2882 | 2906 | data.entries[uid].disable = false; |
| 2883 | 2907 | data.entries[uid].vectorized = false; |
| 2884 | 2908 | setOriginalDataValuesetWIOriginalDataValue(data, uid, 'enabled', true); |
| 2885 | 2909 | setOriginalDataValuesetWIOriginalDataValue(data, uid, 'constant', true); |
| 2886 | 2910 | setOriginalDataValuesetWIOriginalDataValue(data, uid, 'extensions.vectorized', false); |
| 2887 | 2911 | template.removeClass('disabledWIEntry'); |
| 2888 | 2912 | break; |
| 2889 | 2913 | case 'normal': |
| 2890 | 2914 | data.entries[uid].constant = false; |
| 2891 | 2915 | data.entries[uid].disable = false; |
| 2892 | 2916 | data.entries[uid].vectorized = false; |
| 2893 | 2917 | setOriginalDataValuesetWIOriginalDataValue(data, uid, 'enabled', true); |
| 2894 | 2918 | setOriginalDataValuesetWIOriginalDataValue(data, uid, 'constant', false); |
| 2895 | 2919 | setOriginalDataValuesetWIOriginalDataValue(data, uid, 'extensions.vectorized', false); |
| 2896 | 2920 | template.removeClass('disabledWIEntry'); |
| 2897 | 2921 | break; |
| 2898 | 2922 | case 'vectorized': |
| 2899 | 2923 | data.entries[uid].constant = false; |
| 2900 | 2924 | data.entries[uid].disable = false; |
| 2901 | 2925 | data.entries[uid].vectorized = true; |
| 2902 | 2926 | setOriginalDataValuesetWIOriginalDataValue(data, uid, 'enabled', true); |
| 2903 | 2927 | setOriginalDataValuesetWIOriginalDataValue(data, uid, 'constant', false); |
| 2904 | 2928 | setOriginalDataValuesetWIOriginalDataValue(data, uid, 'extensions.vectorized', true); |
| 2905 | 2929 | template.removeClass('disabledWIEntry'); |
| 2906 | 2930 | break; |
| 2907 | 2931 | case 'disabled': |
| 2908 | 2932 | data.entries[uid].constant = false; |
| 2909 | 2933 | data.entries[uid].disable = true; |
| 2910 | 2934 | data.entries[uid].vectorized = false; |
| 2911 | 2935 | setOriginalDataValuesetWIOriginalDataValue(data, uid, 'enabled', false); |
| 2912 | 2936 | setOriginalDataValuesetWIOriginalDataValue(data, uid, 'constant', false); |
| 2913 | 2937 | setOriginalDataValuesetWIOriginalDataValue(data, uid, 'extensions.vectorized', false); |
| 2914 | 2938 | template.addClass('disabledWIEntry'); |
| 2915 | 2939 | break; |
| 2916 | 2940 | } |
| @@ -2941,7 +2965,7 @@ function getWorldEntry(name, data, entry) { | ||
| 2941 | 2965 | const uid = $(this).data('uid'); |
| 2942 | 2966 | const value = $(this).prop('checked'); |
| 2943 | 2967 | data.entries[uid].excludeRecursion = value; |
| 2944 | 2968 | setOriginalDataValuesetWIOriginalDataValue(data, uid, 'extensions.exclude_recursion', data.entries[uid].excludeRecursion); |
| 2945 | 2969 | await saveWorldInfo(name, data); |
| 2946 | 2970 | }); |
| 2947 | 2971 | excludeRecursionInput.prop('checked', entry.excludeRecursion).trigger('input'); |
| @@ -2953,7 +2977,7 @@ function getWorldEntry(name, data, entry) { | ||
| 2953 | 2977 | const uid = $(this).data('uid'); |
| 2954 | 2978 | const value = $(this).prop('checked'); |
| 2955 | 2979 | data.entries[uid].preventRecursion = value; |
| 2956 | 2980 | setOriginalDataValuesetWIOriginalDataValue(data, uid, 'extensions.prevent_recursion', data.entries[uid].preventRecursion); |
| 2957 | 2981 | await saveWorldInfo(name, data); |
| 2958 | 2982 | }); |
| 2959 | 2983 | preventRecursionInput.prop('checked', entry.preventRecursion).trigger('input'); |
| @@ -2965,7 +2989,7 @@ function getWorldEntry(name, data, entry) { | ||
| 2965 | 2989 | const uid = $(this).data('uid'); |
| 2966 | 2990 | const value = $(this).prop('checked'); |
| 2967 | 2991 | data.entries[uid].delayUntilRecursion = value; |
| 2968 | 2992 | setOriginalDataValuesetWIOriginalDataValue(data, uid, 'extensions.delay_until_recursion', data.entries[uid].delayUntilRecursion); |
| 2969 | 2993 | await saveWorldInfo(name, data); |
| 2970 | 2994 | }); |
| 2971 | 2995 | delayUntilRecursionInput.prop('checked', entry.delayUntilRecursion).trigger('input'); |
| @@ -2985,10 +3009,12 @@ function getWorldEntry(name, data, entry) { | ||
| 2985 | 3009 | // delete button |
| 2986 | 3010 | const deleteButton = template.find('.delete_entry_button'); |
| 2987 | 3011 | deleteButton.data('uid', entry.uid); |
| 2988 | 3012 | deleteButton.on('click', async function (e) { |
| 3013 | + e.stopPropagation(); | |
| 2989 | 3014 | const uid = $(this).data('uid'); |
| 2990 | 3015 | const deleted = await deleteWorldInfoEntry(data, uid); |
| 2991 | - deleteOriginalDataValue(data, uid); | |
| 3016 | + if (!deleted) return; | |
| 3017 | + deleteWIOriginalDataValue(data, uid); | |
| 2992 | 3018 | await saveWorldInfo(name, data); |
| 2993 | 3019 | updateEditor(navigation_option.previous); |
| 2994 | 3020 | }); |
| @@ -3015,7 +3041,7 @@ function getWorldEntry(name, data, entry) { | ||
| 3015 | 3041 | } |
| 3016 | 3042 | |
| 3017 | 3043 | data.entries[uid].scanDepth = !isEmpty && !isNaN(value) && value >= 0 && value <= MAX_SCAN_DEPTH ? Math.floor(value) : null; |
| 3018 | 3044 | setOriginalDataValuesetWIOriginalDataValue(data, uid, 'extensions.scan_depth', data.entries[uid].scanDepth); |
| 3019 | 3045 | await saveWorldInfo(name, data); |
| 3020 | 3046 | }); |
| 3021 | 3047 | scanDepthInput.val(entry.scanDepth ?? null).trigger('input'); |
| @@ -3028,7 +3054,7 @@ function getWorldEntry(name, data, entry) { | ||
| 3028 | 3054 | const value = $(this).val(); |
| 3029 | 3055 | |
| 3030 | 3056 | data.entries[uid].caseSensitive = value === 'null' ? null : value === 'true'; |
| 3031 | 3057 | setOriginalDataValuesetWIOriginalDataValue(data, uid, 'extensions.case_sensitive', data.entries[uid].caseSensitive); |
| 3032 | 3058 | await saveWorldInfo(name, data); |
| 3033 | 3059 | }); |
| 3034 | 3060 | caseSensitiveSelect.val((entry.caseSensitive === null || entry.caseSensitive === undefined) ? 'null' : entry.caseSensitive ? 'true' : 'false').trigger('input'); |
| @@ -3041,7 +3067,7 @@ function getWorldEntry(name, data, entry) { | ||
| 3041 | 3067 | const value = $(this).val(); |
| 3042 | 3068 | |
| 3043 | 3069 | data.entries[uid].matchWholeWords = value === 'null' ? null : value === 'true'; |
| 3044 | 3070 | setOriginalDataValuesetWIOriginalDataValue(data, uid, 'extensions.match_whole_words', data.entries[uid].matchWholeWords); |
| 3045 | 3071 | await saveWorldInfo(name, data); |
| 3046 | 3072 | }); |
| 3047 | 3073 | matchWholeWordsSelect.val((entry.matchWholeWords === null || entry.matchWholeWords === undefined) ? 'null' : entry.matchWholeWords ? 'true' : 'false').trigger('input'); |
| @@ -3054,7 +3080,7 @@ function getWorldEntry(name, data, entry) { | ||
| 3054 | 3080 | const value = $(this).val(); |
| 3055 | 3081 | |
| 3056 | 3082 | data.entries[uid].useGroupScoring = value === 'null' ? null : value === 'true'; |
| 3057 | 3083 | setOriginalDataValuesetWIOriginalDataValue(data, uid, 'extensions.use_group_scoring', data.entries[uid].useGroupScoring); |
| 3058 | 3084 | await saveWorldInfo(name, data); |
| 3059 | 3085 | }); |
| 3060 | 3086 | useGroupScoringSelect.val((entry.useGroupScoring === null || entry.useGroupScoring === undefined) ? 'null' : entry.useGroupScoring ? 'true' : 'false').trigger('input'); |
| @@ -3067,7 +3093,7 @@ function getWorldEntry(name, data, entry) { | ||
| 3067 | 3093 | const value = $(this).val(); |
| 3068 | 3094 | |
| 3069 | 3095 | data.entries[uid].automationId = value; |
| 3070 | 3096 | setOriginalDataValuesetWIOriginalDataValue(data, uid, 'extensions.automation_id', data.entries[uid].automationId); |
| 3071 | 3097 | await saveWorldInfo(name, data); |
| 3072 | 3098 | }); |
| 3073 | 3099 | automationIdInput.val(entry.automationId ?? '').trigger('input'); |
| @@ -3200,12 +3226,12 @@ function createEntryInputAutocomplete(input, callback, { allowMultiple = false } | ||
| 3200 | 3226 | |
| 3201 | 3227 | |
| 3202 | 3228 | /** |
| 3203 | 3229 | * DuplicatedDuplicate a WI entry by copying all of its properties and assigning a new uid |
| 3204 | 3230 | * @param {*} data - The data of the book |
| 3205 | 3231 | * @param {number} uid - The uid of the entry to copy in this book |
| 3206 | 3232 | * @returns {*} The new WI duplicated entry |
| 3207 | 3233 | */ |
| 3208 | 3234 | export function duplicateWorldInfoEntry(data, uid) { |
| 3209 | 3235 | if (!data || !('entries' in data) || !data.entries[uid]) { |
| 3210 | 3236 | return; |
| 3211 | 3237 | } |
| @@ -3225,17 +3251,22 @@ function duplicateWorldInfoEntry(data, uid) { | ||
| 3225 | 3251 | * Deletes a WI entry, with a user confirmation dialog |
| 3226 | 3252 | * @param {*[]} data - The data of the book |
| 3227 | 3253 | * @param {number} uid - The uid of the entry to copy in this book |
| 3254 | + * @param {object} [options={}] - Optional arguments | |
| 3255 | + * @param {boolean} [options.silent=false] - Whether to prompt the user for deletion or just do it | |
| 3256 | + * @returns {Promise<boolean>} Whether the entry deletion was successful | |
| 3228 | 3257 | */ |
| 3229 | 3258 | export async function deleteWorldInfoEntry(data, uid, { silent = false } = {}) { |
| 3230 | 3259 | if (!data || !('entries' in data)) { |
| 3231 | 3260 | return; |
| 3232 | 3261 | } |
| 3233 | 3262 | |
| 3234 | 3263 | ifconst (!confirmation = silent || await Popup.show.confirm(`Delete the entry with UID: ${uid}?`, 'This action is irreversible!`)') {; |
| 3235 | - throw new Error('User cancelled deletion'); | |
| 3264 | + if (!confirmation) { | |
| 3265 | + return false; | |
| 3236 | 3266 | } |
| 3237 | 3267 | |
| 3238 | 3268 | delete data.entries[uid]; |
| 3269 | + return true; | |
| 3239 | 3270 | } |
| 3240 | 3271 | |
| 3241 | 3272 | /** |
| @@ -3245,7 +3276,7 @@ function deleteWorldInfoEntry(data, uid) { | ||
| 3245 | 3276 | * |
| 3246 | 3277 | * @type {{[key: string]: { default: any, type: string }}} |
| 3247 | 3278 | */ |
| 3248 | 3279 | export const newEntryDefinitionnewWorldInfoEntryDefinition = { |
| 3249 | 3280 | key: { default: [], type: 'array' }, |
| 3250 | 3281 | keysecondary: { default: [], type: 'array' }, |
| 3251 | 3282 | comment: { default: '', type: 'string' }, |
| @@ -3278,8 +3309,8 @@ const newEntryDefinition = { | ||
| 3278 | 3309 | delay: { default: null, type: 'number?' }, |
| 3279 | 3310 | }; |
| 3280 | 3311 | |
| 3281 | 3312 | export const newEntryTemplatenewWorldInfoEntryTemplate = Object.fromEntries( |
| 3282 | 3313 | Object.entries(newEntryDefinitionnewWorldInfoEntryDefinition).map(([key, value]) => [key, value.default]), |
| 3283 | 3314 | ); |
| 3284 | 3315 | |
| 3285 | 3316 | /** |
| @@ -3288,7 +3319,7 @@ const newEntryTemplate = Object.fromEntries( | ||
| 3288 | 3319 | * @param {any} data WI data |
| 3289 | 3320 | * @returns {object | undefined} New entry object or undefined if failed |
| 3290 | 3321 | */ |
| 3291 | 3322 | export function createWorldInfoEntry(_name, data) { |
| 3292 | 3323 | const newUid = getFreeWorldEntryUid(data); |
| 3293 | 3324 | |
| 3294 | 3325 | if (!Number.isInteger(newUid)) { |
| @@ -3296,7 +3327,7 @@ function createWorldInfoEntry(_name, data) { | ||
| 3296 | 3327 | return; |
| 3297 | 3328 | } |
| 3298 | 3329 | |
| 3299 | 3330 | const newEntry = { uid: newUid, ...structuredClone(newEntryTemplatenewWorldInfoEntryTemplate) }; |
| 3300 | 3331 | data.entries[newUid] = newEntry; |
| 3301 | 3332 | |
| 3302 | 3333 | return newEntry; |
| @@ -3314,7 +3345,21 @@ async function _save(name, data) { | ||
| 3314 | 3345 | eventSource.emit(event_types.WORLDINFO_UPDATED, name, data); |
| 3315 | 3346 | } |
| 3316 | 3347 | |
| 3317 | -async function saveWorldInfo(name, data, immediately = false) { | |
| 3348 | + | |
| 3349 | +/** | |
| 3350 | + * Saves the world info | |
| 3351 | + * | |
| 3352 | + * This will also refresh the `worldInfoCache`. | |
| 3353 | + * Note, for performance reasons the saved cache will not make a deep clone of the data. | |
| 3354 | + * It is your responsibility to not modify the saved data object after calling this function, or there will be data inconsistencies. | |
| 3355 | + * Call `loadWorldInfoData` or query directly from cache if you need the object again. | |
| 3356 | + * | |
| 3357 | + * @param {string} name - The name of the world info | |
| 3358 | + * @param {any} data - The data to be saved | |
| 3359 | + * @param {boolean} [immediately=false] - Whether to save immediately or use debouncing | |
| 3360 | + * @return {Promise<void>} A promise that resolves when the world info is saved | |
| 3361 | + */ | |
| 3362 | +export async function saveWorldInfo(name, data, immediately = false) { | |
| 3318 | 3363 | if (!name || !data) { |
| 3319 | 3364 | return; |
| 3320 | 3365 | } |
| @@ -3371,7 +3416,7 @@ async function renameWorldInfo(name, data) { | ||
| 3371 | 3416 | * @param {string} worldInfoName - The name of the world info to delete |
| 3372 | 3417 | * @returns {Promise<boolean>} A promise that resolves to true if the world info was successfully deleted, false otherwise |
| 3373 | 3418 | */ |
| 3374 | 3419 | export async function deleteWorldInfo(worldInfoName) { |
| 3375 | 3420 | if (!world_names.includes(worldInfoName)) { |
| 3376 | 3421 | return false; |
| 3377 | 3422 | } |
| @@ -3406,7 +3451,7 @@ async function deleteWorldInfo(worldInfoName) { | ||
| 3406 | 3451 | return true; |
| 3407 | 3452 | } |
| 3408 | 3453 | |
| 3409 | 3454 | export function getFreeWorldEntryUid(data) { |
| 3410 | 3455 | if (!data || !('entries' in data)) { |
| 3411 | 3456 | return null; |
| 3412 | 3457 | } |
| @@ -3422,7 +3467,7 @@ function getFreeWorldEntryUid(data) { | ||
| 3422 | 3467 | return null; |
| 3423 | 3468 | } |
| 3424 | 3469 | |
| 3425 | 3470 | export function getFreeWorldName() { |
| 3426 | 3471 | const MAX_FREE_NAME = 100_000; |
| 3427 | 3472 | for (let index = 1; index < MAX_FREE_NAME; index++) { |
| 3428 | 3473 | const newName = `New World (${index})`; |
| @@ -3444,7 +3489,7 @@ function getFreeWorldName() { | ||
| 3444 | 3489 | * @param {boolean} [options.interactive=false] - Whether to show a confirmation dialog when overwriting an existing world |
| 3445 | 3490 | * @returns {Promise<boolean>} - True if the world info was successfully created, false otherwise |
| 3446 | 3491 | */ |
| 3447 | 3492 | export async function createNewWorldInfo(worldName, { interactive = false } = {}) { |
| 3448 | 3493 | const worldInfoTemplate = { entries: {} }; |
| 3449 | 3494 | |
| 3450 | 3495 | if (!worldName) { |
| @@ -3505,7 +3550,7 @@ async function getCharacterLore() { | ||
| 3505 | 3550 | continue; |
| 3506 | 3551 | } |
| 3507 | 3552 | |
| 3508 | 3553 | const data = await loadWorldInfoDataloadWorldInfo(worldName); |
| 3509 | 3554 | const newEntries = data ? Object.keys(data.entries).map((x) => data.entries[x]).map(({ uid, ...rest }) => ({ uid, world: worldName, ...rest })) : []; |
| 3510 | 3555 | entries = entries.concat(newEntries); |
| 3511 | 3556 | |
| @@ -3525,7 +3570,7 @@ async function getGlobalLore() { | ||
| 3525 | 3570 | |
| 3526 | 3571 | let entries = []; |
| 3527 | 3572 | for (const worldName of selected_world_info) { |
| 3528 | 3573 | const data = await loadWorldInfoDataloadWorldInfo(worldName); |
| 3529 | 3574 | const newEntries = data ? Object.keys(data.entries).map((x) => data.entries[x]).map(({ uid, ...rest }) => ({ uid, world: worldName, ...rest })) : []; |
| 3530 | 3575 | entries = entries.concat(newEntries); |
| 3531 | 3576 | } |
| @@ -3547,7 +3592,7 @@ async function getChatLore() { | ||
| 3547 | 3592 | return []; |
| 3548 | 3593 | } |
| 3549 | 3594 | |
| 3550 | 3595 | const data = await loadWorldInfoDataloadWorldInfo(chatWorld); |
| 3551 | 3596 | const entries = data ? Object.keys(data.entries).map((x) => data.entries[x]).map(({ uid, ...rest }) => ({ uid, world: chatWorld, ...rest })) : []; |
| 3552 | 3597 | |
| 3553 | 3598 | console.debug(`[WI] Chat lore has ${entries.length} entries`, [chatWorld]); |
| @@ -3663,7 +3708,7 @@ function parseDecorators(content) { | ||
| 3663 | 3708 | * @typedef {{ worldInfoBefore: string, worldInfoAfter: string, EMEntries: any[], WIDepthEntries: any[], allActivatedEntries: Set<any> }} WIActivated |
| 3664 | 3709 | * @returns {Promise<WIActivated>} The world info activated. |
| 3665 | 3710 | */ |
| 3666 | 3711 | export async function checkWorldInfo(chat, maxContext, isDryRun) { |
| 3667 | 3712 | const context = getContext(); |
| 3668 | 3713 | const buffer = new WorldInfoBuffer(chat); |
| 3669 | 3714 | |
| @@ -4233,7 +4278,7 @@ function convertAgnaiMemoryBook(inputObj) { | ||
| 4233 | 4278 | |
| 4234 | 4279 | inputObj.entries.forEach((entry, index) => { |
| 4235 | 4280 | outputObj.entries[index] = { |
| 4236 | 4281 | ...newEntryTemplatenewWorldInfoEntryTemplate, |
| 4237 | 4282 | uid: index, |
| 4238 | 4283 | key: entry.keywords, |
| 4239 | 4284 | keysecondary: [], |
| @@ -4275,7 +4320,7 @@ function convertRisuLorebook(inputObj) { | ||
| 4275 | 4320 | |
| 4276 | 4321 | inputObj.data.forEach((entry, index) => { |
| 4277 | 4322 | outputObj.entries[index] = { |
| 4278 | 4323 | ...newEntryTemplatenewWorldInfoEntryTemplate, |
| 4279 | 4324 | uid: index, |
| 4280 | 4325 | key: entry.key.split(',').map(x => x.trim()), |
| 4281 | 4326 | keysecondary: entry.secondkey ? entry.secondkey.split(',').map(x => x.trim()) : [], |
| @@ -4322,7 +4367,7 @@ function convertNovelLorebook(inputObj) { | ||
| 4322 | 4367 | const addMemo = displayName !== undefined && displayName.trim() !== ''; |
| 4323 | 4368 | |
| 4324 | 4369 | outputObj.entries[index] = { |
| 4325 | 4370 | ...newEntryTemplatenewWorldInfoEntryTemplate, |
| 4326 | 4371 | uid: index, |
| 4327 | 4372 | key: entry.keys, |
| 4328 | 4373 | keysecondary: [], |
| @@ -4369,7 +4414,7 @@ function convertCharacterBook(characterBook) { | ||
| 4369 | 4414 | } |
| 4370 | 4415 | |
| 4371 | 4416 | result.entries[entry.id] = { |
| 4372 | 4417 | ...newEntryTemplatenewWorldInfoEntryTemplate, |
| 4373 | 4418 | uid: entry.id, |
| 4374 | 4419 | key: entry.keys, |
| 4375 | 4420 | keysecondary: entry.secondary_keys || [], |
| @@ -4499,7 +4544,7 @@ export async function importEmbeddedWorldInfo(skipPopup = false) { | ||
| 4499 | 4544 | setWorldInfoButtonClass(chid, true); |
| 4500 | 4545 | } |
| 4501 | 4546 | |
| 4502 | 4547 | export function onWorldInfoChange(args, text) { |
| 4503 | 4548 | if (args !== '__notSlashCommand__') { // if it's a slash command |
| 4504 | 4549 | const silent = isTrueBoolean(args.silent); |
| 4505 | 4550 | if (text.trim() !== '') { // and args are provided |
| @@ -4650,7 +4695,7 @@ export async function importWorldInfo(file) { | ||
| 4650 | 4695 | }); |
| 4651 | 4696 | } |
| 4652 | 4697 | |
| 4653 | 4698 | export function assignLorebookToChat() { |
| 4654 | 4699 | const selectedName = chat_metadata[METADATA_KEY]; |
| 4655 | 4700 | const template = $('#chat_world_template .chat_world').clone(); |
| 4656 | 4701 | |