Export and doc most of wi functions
| @@ -18,33 +18,13 @@ 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 | import { StructuredCloneMap } from './util/StructuredCloneMap.js'; |
| 20 | 20 | ||
| 21 | export { | 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 | evenly: 0, | 22 | evenly: 0, |
| 43 | character_first: 1, | 23 | character_first: 1, |
| 44 | global_first: 2, | 24 | global_first: 2, |
| 45 | }; | 25 | }; |
| 46 | 26 | ||
| 47 | const world_info_logic = { | 27 | export const world_info_logic = { |
| 48 | AND_ANY: 0, | 28 | AND_ANY: 0, |
| 49 | NOT_ALL: 1, | 29 | NOT_ALL: 1, |
| 50 | NOT_ANY: 2, | 30 | NOT_ANY: 2, |
| @@ -54,7 +34,7 @@ const world_info_logic = { | |||
| 54 | /** | 34 | /** |
| 55 | * @enum {number} Possible states of the WI evaluation | 35 | * @enum {number} Possible states of the WI evaluation |
| 56 | */ | 36 | */ |
| 57 | const scan_state = { | 37 | export const scan_state = { |
| 58 | /** | 38 | /** |
| 59 | * The scan will be stopped. | 39 | * The scan will be stopped. |
| 60 | */ | 40 | */ |
| @@ -75,23 +55,23 @@ const scan_state = { | |||
| 75 | 55 | ||
| 76 | const WI_ENTRY_EDIT_TEMPLATE = $('#entry_edit_template .world_entry'); | 56 | const WI_ENTRY_EDIT_TEMPLATE = $('#entry_edit_template .world_entry'); |
| 77 | 57 | ||
| 78 | let world_info = {}; | 58 | export let world_info = {}; |
| 79 | let selected_world_info = []; | 59 | export let selected_world_info = []; |
| 80 | /** @type {string[]} */ | 60 | /** @type {string[]} */ |
| 81 | let world_names; | 61 | export let world_names; |
| 82 | let world_info_depth = 2; | 62 | export let world_info_depth = 2; |
| 83 | let world_info_min_activations = 0; // if > 0, will continue seeking chat until minimum world infos are activated | 63 | export let world_info_min_activations = 0; // if > 0, will continue seeking chat until minimum world infos are activated |
| 84 | let world_info_min_activations_depth_max = 0; // used when (world_info_min_activations > 0) | 64 | export let world_info_min_activations_depth_max = 0; // used when (world_info_min_activations > 0) |
| 85 | 65 | ||
| 86 | let world_info_budget = 25; | 66 | export let world_info_budget = 25; |
| 87 | let world_info_include_names = true; | 67 | export let world_info_include_names = true; |
| 88 | let world_info_recursive = false; | 68 | export let world_info_recursive = false; |
| 89 | let world_info_overflow_alert = false; | 69 | export let world_info_overflow_alert = false; |
| 90 | let world_info_case_sensitive = false; | 70 | export let world_info_case_sensitive = false; |
| 91 | let world_info_match_whole_words = false; | 71 | export let world_info_match_whole_words = false; |
| 92 | let world_info_use_group_scoring = false; | 72 | export let world_info_use_group_scoring = false; |
| 93 | let world_info_character_strategy = world_info_insertion_strategy.character_first; | 73 | export let world_info_character_strategy = world_info_insertion_strategy.character_first; |
| 94 | let world_info_budget_cap = 0; | 74 | export let world_info_budget_cap = 0; |
| 95 | const saveWorldDebounced = debounce(async (name, data) => await _save(name, data), debounce_timeout.relaxed); | 75 | const saveWorldDebounced = debounce(async (name, data) => await _save(name, data), debounce_timeout.relaxed); |
| 96 | const saveSettingsDebounced = debounce(() => { | 76 | const saveSettingsDebounced = debounce(() => { |
| 97 | Object.assign(world_info, { globalSelect: selected_world_info }); | 77 | Object.assign(world_info, { globalSelect: selected_world_info }); |
| @@ -101,13 +81,13 @@ const sortFn = (a, b) => b.order - a.order; | |||
| 101 | let updateEditor = (navigation, flashOnNav = true) => { console.debug('Triggered WI navigation', navigation, flashOnNav); }; | 81 | let updateEditor = (navigation, flashOnNav = true) => { console.debug('Triggered WI navigation', navigation, flashOnNav); }; |
| 102 | 82 | ||
| 103 | // Do not optimize. updateEditor is a function that is updated by the displayWorldEntries with new data. | 83 | // Do not optimize. updateEditor is a function that is updated by the displayWorldEntries with new data. |
| 104 | const worldInfoFilter = new FilterHelper(() => updateEditor()); | 84 | export const worldInfoFilter = new FilterHelper(() => updateEditor()); |
| 105 | const SORT_ORDER_KEY = 'world_info_sort_order'; | 85 | export const SORT_ORDER_KEY = 'world_info_sort_order'; |
| 106 | const METADATA_KEY = 'world_info'; | 86 | export const METADATA_KEY = 'world_info'; |
| 107 | 87 | ||
| 108 | const DEFAULT_DEPTH = 4; | 88 | export const DEFAULT_DEPTH = 4; |
| 109 | const DEFAULT_WEIGHT = 100; | 89 | export const DEFAULT_WEIGHT = 100; |
| 110 | const MAX_SCAN_DEPTH = 1000; | 90 | export const MAX_SCAN_DEPTH = 1000; |
| 111 | const KNOWN_DECORATORS = ['@@activate', '@@dont_activate']; | 91 | const KNOWN_DECORATORS = ['@@activate', '@@dont_activate']; |
| 112 | 92 | ||
| 113 | // Typedef area | 93 | // Typedef area |
| @@ -732,7 +712,7 @@ export function getWorldInfoSettings() { | |||
| 732 | }; | 712 | }; |
| 733 | } | 713 | } |
| 734 | 714 | ||
| 735 | const world_info_position = { | 715 | export const world_info_position = { |
| 736 | before: 0, | 716 | before: 0, |
| 737 | after: 1, | 717 | after: 1, |
| 738 | ANTop: 2, | 718 | ANTop: 2, |
| @@ -747,8 +727,18 @@ export const wi_anchor_position = { | |||
| 747 | after: 1, | 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 | * Gets the world info based on chat messages. | 744 | * Gets the world info based on chat messages. |
| @@ -758,7 +748,7 @@ const worldInfoCache = new StructuredCloneMap({ cloneOnGet: true, cloneOnSet: fa | |||
| 758 | * @typedef {{worldInfoString: string, worldInfoBefore: string, worldInfoAfter: string, worldInfoExamples: any[], worldInfoDepth: any[]}} WIPromptResult | 748 | * @typedef {{worldInfoString: string, worldInfoBefore: string, worldInfoAfter: string, worldInfoExamples: any[], worldInfoDepth: any[]}} WIPromptResult |
| 759 | * @returns {Promise<WIPromptResult>} The world info string and depth. | 749 | * @returns {Promise<WIPromptResult>} The world info string and depth. |
| 760 | */ | 750 | */ |
| 761 | async function getWorldInfoPrompt(chat, maxContext, isDryRun) { | 751 | export async function getWorldInfoPrompt(chat, maxContext, isDryRun) { |
| 762 | let worldInfoString = '', worldInfoBefore = '', worldInfoAfter = ''; | 752 | let worldInfoString = '', worldInfoBefore = '', worldInfoAfter = ''; |
| 763 | 753 | ||
| 764 | const activatedWorldInfo = await checkWorldInfo(chat, maxContext, isDryRun); | 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 | function setWorldInfoSettings(settings, data) { | 773 | export function setWorldInfoSettings(settings, data) { |
| 784 | if (settings.world_info_depth !== undefined) | 774 | if (settings.world_info_depth !== undefined) |
| 785 | world_info_depth = Number(settings.world_info_depth); | 775 | world_info_depth = Number(settings.world_info_depth); |
| 786 | if (settings.world_info_min_activations !== undefined) | 776 | if (settings.world_info_min_activations !== undefined) |
| @@ -916,7 +906,7 @@ function registerWorldInfoSlashCommands() { | |||
| 916 | return ''; | 906 | return ''; |
| 917 | } | 907 | } |
| 918 | 908 | ||
| 919 | const data = await loadWorldInfoData(file); | 909 | const data = await loadWorldInfo(file); |
| 920 | 910 | ||
| 921 | if (!data || !('entries' in data)) { | 911 | if (!data || !('entries' in data)) { |
| 922 | toastr.warning('World Info file has an invalid format'); | 912 | toastr.warning('World Info file has an invalid format'); |
| @@ -965,7 +955,7 @@ function registerWorldInfoSlashCommands() { | |||
| 965 | return ''; | 955 | return ''; |
| 966 | } | 956 | } |
| 967 | 957 | ||
| 968 | if (typeof newEntryTemplate[field] === 'boolean') { | 958 | if (typeof newWorldInfoEntryTemplate[field] === 'boolean') { |
| 969 | const isTrue = isTrueBoolean(value); | 959 | const isTrue = isTrueBoolean(value); |
| 970 | const isFalse = isFalseBoolean(value); | 960 | const isFalse = isFalseBoolean(value); |
| 971 | 961 | ||
| @@ -1016,7 +1006,7 @@ function registerWorldInfoSlashCommands() { | |||
| 1016 | return ''; | 1006 | return ''; |
| 1017 | } | 1007 | } |
| 1018 | 1008 | ||
| 1019 | if (newEntryTemplate[field] === undefined) { | 1009 | if (newWorldInfoEntryTemplate[field] === undefined) { |
| 1020 | toastr.warning('Valid field name is required'); | 1010 | toastr.warning('Valid field name is required'); |
| 1021 | return ''; | 1011 | return ''; |
| 1022 | } | 1012 | } |
| @@ -1038,7 +1028,7 @@ function registerWorldInfoSlashCommands() { | |||
| 1038 | const file = args.file; | 1028 | const file = args.file; |
| 1039 | const key = args.key; | 1029 | const key = args.key; |
| 1040 | 1030 | ||
| 1041 | const data = await loadWorldInfoData(file); | 1031 | const data = await loadWorldInfo(file); |
| 1042 | 1032 | ||
| 1043 | if (!data || !('entries' in data)) { | 1033 | if (!data || !('entries' in data)) { |
| 1044 | toastr.warning('Valid World Info file name is required'); | 1034 | toastr.warning('Valid World Info file name is required'); |
| @@ -1075,7 +1065,7 @@ function registerWorldInfoSlashCommands() { | |||
| 1075 | 1065 | ||
| 1076 | value = value.replace(/\\([{}|])/g, '$1'); | 1066 | value = value.replace(/\\([{}|])/g, '$1'); |
| 1077 | 1067 | ||
| 1078 | const data = await loadWorldInfoData(file); | 1068 | const data = await loadWorldInfo(file); |
| 1079 | 1069 | ||
| 1080 | if (!data || !('entries' in data)) { | 1070 | if (!data || !('entries' in data)) { |
| 1081 | toastr.warning('Valid World Info file name is required'); | 1071 | toastr.warning('Valid World Info file name is required'); |
| @@ -1089,7 +1079,7 @@ function registerWorldInfoSlashCommands() { | |||
| 1089 | return ''; | 1079 | return ''; |
| 1090 | } | 1080 | } |
| 1091 | 1081 | ||
| 1092 | if (newEntryTemplate[field] === undefined) { | 1082 | if (newWorldInfoEntryTemplate[field] === undefined) { |
| 1093 | toastr.warning('Valid field name is required'); | 1083 | toastr.warning('Valid field name is required'); |
| 1094 | return ''; | 1084 | return ''; |
| 1095 | } | 1085 | } |
| @@ -1104,8 +1094,8 @@ function registerWorldInfoSlashCommands() { | |||
| 1104 | entry[field] = value; | 1094 | entry[field] = value; |
| 1105 | } | 1095 | } |
| 1106 | 1096 | ||
| 1107 | if (originalDataKeyMap[field]) { | 1097 | if (originalWIDataKeyMap[field]) { |
| 1108 | setOriginalDataValue(data, uid, originalDataKeyMap[field], entry[field]); | 1098 | setWIOriginalDataValue(data, uid, originalWIDataKeyMap[field], entry[field]); |
| 1109 | } | 1099 | } |
| 1110 | 1100 | ||
| 1111 | await saveWorldInfo(file, data); | 1101 | await saveWorldInfo(file, data); |
| @@ -1226,7 +1216,7 @@ function registerWorldInfoSlashCommands() { | |||
| 1226 | /** A collection of local enum providers for this context of world info */ | 1216 | /** A collection of local enum providers for this context of world info */ |
| 1227 | const localEnumProviders = { | 1217 | const localEnumProviders = { |
| 1228 | /** All possible fields that can be set in a WI entry */ | 1218 | /** All possible fields that can be set in a WI entry */ |
| 1229 | wiEntryFields: () => Object.entries(newEntryDefinition).map(([key, value]) => | 1219 | wiEntryFields: () => Object.entries(newWorldInfoEntryDefinition).map(([key, value]) => |
| 1230 | new SlashCommandEnumValue(key, `[${value.type}] default: ${(typeof value.default === 'string' ? `'${value.default}'` : value.default)}`, | 1220 | new SlashCommandEnumValue(key, `[${value.type}] default: ${(typeof value.default === 'string' ? `'${value.default}'` : value.default)}`, |
| 1231 | enumTypes.enum, enumIcons.getDataTypeIcon(value.type))), | 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 | if (!name) { | 1567 | if (!name) { |
| 1572 | hideWorldEditor(); | 1568 | hideWorldEditor(); |
| 1573 | return; | 1569 | return; |
| 1574 | } | 1570 | } |
| 1575 | 1571 | ||
| 1576 | const wiData = await loadWorldInfoData(name); | 1572 | const wiData = await loadWorldInfo(name); |
| 1577 | displayWorldEntries(name, wiData); | 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 | if (!name) { | 1585 | if (!name) { |
| 1582 | return; | 1586 | return; |
| 1583 | } | 1587 | } |
| @@ -1635,10 +1639,12 @@ function getWIElement(name) { | |||
| 1635 | } | 1639 | } |
| 1636 | 1640 | ||
| 1637 | /** | 1641 | /** |
| 1642 | * Sorts the given data based on the selected sort option | ||
| 1643 | * | ||
| 1638 | * @param {any[]} data WI entries | 1644 | * @param {any[]} data WI entries |
| 1639 | * @returns {any[]} Sorted data | 1645 | * @returns {any[]} Sorted data |
| 1640 | */ | 1646 | */ |
| 1641 | function sortEntries(data) { | 1647 | export function sortWorldInfoEntries(data) { |
| 1642 | const option = $('#world_info_sort_order').find(':selected'); | 1648 | const option = $('#world_info_sort_order').find(':selected'); |
| 1643 | const sortField = option.data('field'); | 1649 | const sortField = option.data('field'); |
| 1644 | const sortOrder = option.data('order'); | 1650 | const sortOrder = option.data('order'); |
| @@ -1801,7 +1807,7 @@ function displayWorldEntries(name, data, navigation = navigation_option.none, fl | |||
| 1801 | 1807 | ||
| 1802 | // Apply the filter and do the chosen sorting | 1808 | // Apply the filter and do the chosen sorting |
| 1803 | entriesArray = worldInfoFilter.applyFilters(entriesArray); | 1809 | entriesArray = worldInfoFilter.applyFilters(entriesArray); |
| 1804 | entriesArray = sortEntries(entriesArray); | 1810 | entriesArray = sortWorldInfoEntries(entriesArray); |
| 1805 | 1811 | ||
| 1806 | // Cache keys | 1812 | // Cache keys |
| 1807 | const keys = entriesArray.flatMap(entry => [...entry.key, ...entry.keysecondary]); | 1813 | const keys = entriesArray.flatMap(entry => [...entry.key, ...entry.keysecondary]); |
| @@ -1919,7 +1925,7 @@ function displayWorldEntries(name, data, navigation = navigation_option.none, fl | |||
| 1919 | for (const entry of Object.values(data.entries)) { | 1925 | for (const entry of Object.values(data.entries)) { |
| 1920 | if (!entry.comment && Array.isArray(entry.key) && entry.key.length > 0) { | 1926 | if (!entry.comment && Array.isArray(entry.key) && entry.key.length > 0) { |
| 1921 | entry.comment = entry.key[0]; | 1927 | entry.comment = entry.key[0]; |
| 1922 | setOriginalDataValue(data, entry.uid, 'comment', entry.comment); | 1928 | setWIOriginalDataValue(data, entry.uid, 'comment', entry.comment); |
| 1923 | counter++; | 1929 | counter++; |
| 1924 | } | 1930 | } |
| 1925 | } | 1931 | } |
| @@ -1954,7 +1960,7 @@ function displayWorldEntries(name, data, navigation = navigation_option.none, fl | |||
| 1954 | 1960 | ||
| 1955 | // We need to sort the entries here, as the data source isn't sorted | 1961 | // We need to sort the entries here, as the data source isn't sorted |
| 1956 | const entries = Object.values(data.entries); | 1962 | const entries = Object.values(data.entries); |
| 1957 | sortEntries(entries); | 1963 | sortWorldInfoEntries(entries); |
| 1958 | 1964 | ||
| 1959 | let updated = 0, current = start; | 1965 | let updated = 0, current = start; |
| 1960 | for (const entry of entries) { | 1966 | for (const entry of entries) { |
| @@ -1962,7 +1968,7 @@ function displayWorldEntries(name, data, navigation = navigation_option.none, fl | |||
| 1962 | if (entry.order === newOrder) continue; | 1968 | if (entry.order === newOrder) continue; |
| 1963 | 1969 | ||
| 1964 | entry.order = newOrder; | 1970 | entry.order = newOrder; |
| 1965 | setOriginalDataValue(data, entry.order, 'order', entry.order); | 1971 | setWIOriginalDataValue(data, entry.order, 'order', entry.order); |
| 1966 | updated++; | 1972 | updated++; |
| 1967 | } | 1973 | } |
| 1968 | 1974 | ||
| @@ -2025,7 +2031,7 @@ function displayWorldEntries(name, data, navigation = navigation_option.none, fl | |||
| 2025 | } | 2031 | } |
| 2026 | 2032 | ||
| 2027 | item.displayIndex = minDisplayIndex + index; | 2033 | item.displayIndex = minDisplayIndex + index; |
| 2028 | setOriginalDataValue(data, uid, 'extensions.display_index', item.displayIndex); | 2034 | setWIOriginalDataValue(data, uid, 'extensions.display_index', item.displayIndex); |
| 2029 | }); | 2035 | }); |
| 2030 | 2036 | ||
| 2031 | console.table(Object.keys(data.entries).map(uid => data.entries[uid]).map(x => ({ uid: x.uid, key: x.key.join(','), displayIndex: x.displayIndex }))); | 2037 | 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 +2042,7 @@ function displayWorldEntries(name, data, navigation = navigation_option.none, fl | |||
| 2036 | //$("#world_popup_entries_list").disableSelection(); | 2042 | //$("#world_popup_entries_list").disableSelection(); |
| 2037 | } | 2043 | } |
| 2038 | 2044 | ||
| 2039 | const originalDataKeyMap = { | 2045 | export const originalWIDataKeyMap = { |
| 2040 | 'displayIndex': 'extensions.display_index', | 2046 | 'displayIndex': 'extensions.display_index', |
| 2041 | 'excludeRecursion': 'extensions.exclude_recursion', | 2047 | 'excludeRecursion': 'extensions.exclude_recursion', |
| 2042 | 'preventRecursion': 'extensions.prevent_recursion', | 2048 | 'preventRecursion': 'extensions.prevent_recursion', |
| @@ -2087,7 +2093,17 @@ function verifyWorldInfoSearchSortRule() { | |||
| 2087 | } | 2093 | } |
| 2088 | } | 2094 | } |
| 2089 | 2095 | ||
| 2090 | function setOriginalDataValue(data, uid, key, value) { | 2096 | /** |
| 2097 | * Sets the value of a specific key in the original data entry corresponding to the given uid | ||
| 2098 | * This needs to be called whenever you update JSON data fields. | ||
| 2099 | * Use `originalWIDataKeyMap` to find the correct value to be set. | ||
| 2100 | * | ||
| 2101 | * @param {object} data - The data object containing the original data entries. | ||
| 2102 | * @param {string} uid - The unique identifier of the data entry. | ||
| 2103 | * @param {string} key - The key of the value to be set. | ||
| 2104 | * @param {any} value - The value to be set. | ||
| 2105 | */ | ||
| 2106 | export function setWIOriginalDataValue(data, uid, key, value) { | ||
| 2091 | if (data.originalData && Array.isArray(data.originalData.entries)) { | 2107 | if (data.originalData && Array.isArray(data.originalData.entries)) { |
| 2092 | let originalEntry = data.originalData.entries.find(x => x.uid === uid); | 2108 | let originalEntry = data.originalData.entries.find(x => x.uid === uid); |
| 2093 | 2109 | ||
| @@ -2099,7 +2115,7 @@ function setOriginalDataValue(data, uid, key, value) { | |||
| 2099 | } | 2115 | } |
| 2100 | } | 2116 | } |
| 2101 | 2117 | ||
| 2102 | function deleteOriginalDataValue(data, uid) { | 2118 | function deleteWIOriginalDataValue(data, uid) { |
| 2103 | if (data.originalData && Array.isArray(data.originalData.entries)) { | 2119 | if (data.originalData && Array.isArray(data.originalData.entries)) { |
| 2104 | const originalIndex = data.originalData.entries.findIndex(x => x.uid === uid); | 2120 | const originalIndex = data.originalData.entries.findIndex(x => x.uid === uid); |
| 2105 | 2121 | ||
| @@ -2120,7 +2136,7 @@ function deleteOriginalDataValue(data, uid) { | |||
| 2120 | * @param {string} input - One or multiple keywords or regexes, separated by commas | 2136 | * @param {string} input - One or multiple keywords or regexes, separated by commas |
| 2121 | * @returns {string[]} An array of keywords and regexes | 2137 | * @returns {string[]} An array of keywords and regexes |
| 2122 | */ | 2138 | */ |
| 2123 | function splitKeywordsAndRegexes(input) { | 2139 | export function splitKeywordsAndRegexes(input) { |
| 2124 | /** @type {string[]} */ | 2140 | /** @type {string[]} */ |
| 2125 | let keywordsAndRegexes = []; | 2141 | let keywordsAndRegexes = []; |
| 2126 | 2142 | ||
| @@ -2224,7 +2240,7 @@ function isValidRegex(input) { | |||
| 2224 | * @param {string} input - A delimited regex string | 2240 | * @param {string} input - A delimited regex string |
| 2225 | * @returns {RegExp|null} The regex object, or null if not a valid regex | 2241 | * @returns {RegExp|null} The regex object, or null if not a valid regex |
| 2226 | */ | 2242 | */ |
| 2227 | function parseRegexFromString(input) { | 2243 | export function parseRegexFromString(input) { |
| 2228 | // Extracting the regex pattern and flags | 2244 | // Extracting the regex pattern and flags |
| 2229 | let match = input.match(/^\/([\w\W]+?)\/([gimsuy]*)$/); | 2245 | let match = input.match(/^\/([\w\W]+?)\/([gimsuy]*)$/); |
| 2230 | if (!match) { | 2246 | if (!match) { |
| @@ -2310,7 +2326,7 @@ function getWorldEntry(name, data, entry) { | |||
| 2310 | !skipReset && resetScrollHeight(this); | 2326 | !skipReset && resetScrollHeight(this); |
| 2311 | if (!noSave) { | 2327 | if (!noSave) { |
| 2312 | data.entries[uid][entryPropName] = keys; | 2328 | data.entries[uid][entryPropName] = keys; |
| 2313 | setOriginalDataValue(data, uid, originalDataValueName, data.entries[uid][entryPropName]); | 2329 | setWIOriginalDataValue(data, uid, originalDataValueName, data.entries[uid][entryPropName]); |
| 2314 | await saveWorldInfo(name, data); | 2330 | await saveWorldInfo(name, data); |
| 2315 | } | 2331 | } |
| 2316 | }); | 2332 | }); |
| @@ -2348,7 +2364,7 @@ function getWorldEntry(name, data, entry) { | |||
| 2348 | !skipReset && resetScrollHeight(this); | 2364 | !skipReset && resetScrollHeight(this); |
| 2349 | if (!noSave) { | 2365 | if (!noSave) { |
| 2350 | data.entries[uid][entryPropName] = splitKeywordsAndRegexes(value); | 2366 | data.entries[uid][entryPropName] = splitKeywordsAndRegexes(value); |
| 2351 | setOriginalDataValue(data, uid, originalDataValueName, data.entries[uid][entryPropName]); | 2367 | setWIOriginalDataValue(data, uid, originalDataValueName, data.entries[uid][entryPropName]); |
| 2352 | await saveWorldInfo(name, data); | 2368 | await saveWorldInfo(name, data); |
| 2353 | } | 2369 | } |
| 2354 | }); | 2370 | }); |
| @@ -2392,7 +2408,7 @@ function getWorldEntry(name, data, entry) { | |||
| 2392 | const uid = $(this).data('uid'); | 2408 | const uid = $(this).data('uid'); |
| 2393 | const value = Number($(this).val()); | 2409 | const value = Number($(this).val()); |
| 2394 | data.entries[uid].selectiveLogic = !isNaN(value) ? value : world_info_logic.AND_ANY; | 2410 | data.entries[uid].selectiveLogic = !isNaN(value) ? value : world_info_logic.AND_ANY; |
| 2395 | setOriginalDataValue(data, uid, 'selectiveLogic', data.entries[uid].selectiveLogic); | 2411 | setWIOriginalDataValue(data, uid, 'selectiveLogic', data.entries[uid].selectiveLogic); |
| 2396 | await saveWorldInfo(name, data); | 2412 | await saveWorldInfo(name, data); |
| 2397 | }); | 2413 | }); |
| 2398 | 2414 | ||
| @@ -2441,7 +2457,7 @@ function getWorldEntry(name, data, entry) { | |||
| 2441 | } | 2457 | } |
| 2442 | } | 2458 | } |
| 2443 | 2459 | ||
| 2444 | setOriginalDataValue(data, uid, 'character_filter', data.entries[uid].characterFilter); | 2460 | setWIOriginalDataValue(data, uid, 'character_filter', data.entries[uid].characterFilter); |
| 2445 | await saveWorldInfo(name, data); | 2461 | await saveWorldInfo(name, data); |
| 2446 | }); | 2462 | }); |
| 2447 | characterExclusionInput.prop('checked', entry.characterFilter?.isExclude ?? false).trigger('input'); | 2463 | characterExclusionInput.prop('checked', entry.characterFilter?.isExclude ?? false).trigger('input'); |
| @@ -2503,7 +2519,7 @@ function getWorldEntry(name, data, entry) { | |||
| 2503 | }, | 2519 | }, |
| 2504 | ); | 2520 | ); |
| 2505 | } | 2521 | } |
| 2506 | setOriginalDataValue(data, uid, 'character_filter', data.entries[uid].characterFilter); | 2522 | setWIOriginalDataValue(data, uid, 'character_filter', data.entries[uid].characterFilter); |
| 2507 | await saveWorldInfo(name, data); | 2523 | await saveWorldInfo(name, data); |
| 2508 | }); | 2524 | }); |
| 2509 | 2525 | ||
| @@ -2517,7 +2533,7 @@ function getWorldEntry(name, data, entry) { | |||
| 2517 | !skipReset && resetScrollHeight(this); | 2533 | !skipReset && resetScrollHeight(this); |
| 2518 | data.entries[uid].comment = value; | 2534 | data.entries[uid].comment = value; |
| 2519 | 2535 | ||
| 2520 | setOriginalDataValue(data, uid, 'comment', data.entries[uid].comment); | 2536 | setWIOriginalDataValue(data, uid, 'comment', data.entries[uid].comment); |
| 2521 | await saveWorldInfo(name, data); | 2537 | await saveWorldInfo(name, data); |
| 2522 | }); | 2538 | }); |
| 2523 | commentToggle.data('uid', entry.uid); | 2539 | commentToggle.data('uid', entry.uid); |
| @@ -2552,7 +2568,7 @@ function getWorldEntry(name, data, entry) { | |||
| 2552 | const value = $(this).val(); | 2568 | const value = $(this).val(); |
| 2553 | data.entries[uid].content = value; | 2569 | data.entries[uid].content = value; |
| 2554 | 2570 | ||
| 2555 | setOriginalDataValue(data, uid, 'content', data.entries[uid].content); | 2571 | setWIOriginalDataValue(data, uid, 'content', data.entries[uid].content); |
| 2556 | await saveWorldInfo(name, data); | 2572 | await saveWorldInfo(name, data); |
| 2557 | 2573 | ||
| 2558 | if (skipCount) { | 2574 | if (skipCount) { |
| @@ -2582,7 +2598,7 @@ function getWorldEntry(name, data, entry) { | |||
| 2582 | const value = $(this).prop('checked'); | 2598 | const value = $(this).prop('checked'); |
| 2583 | data.entries[uid].selective = value; | 2599 | data.entries[uid].selective = value; |
| 2584 | 2600 | ||
| 2585 | setOriginalDataValue(data, uid, 'selective', data.entries[uid].selective); | 2601 | setWIOriginalDataValue(data, uid, 'selective', data.entries[uid].selective); |
| 2586 | await saveWorldInfo(name, data); | 2602 | await saveWorldInfo(name, data); |
| 2587 | 2603 | ||
| 2588 | const keysecondary = $(this) | 2604 | const keysecondary = $(this) |
| @@ -2631,7 +2647,7 @@ function getWorldEntry(name, data, entry) { | |||
| 2631 | 2647 | ||
| 2632 | data.entries[uid].order = !isNaN(value) ? value : 0; | 2648 | data.entries[uid].order = !isNaN(value) ? value : 0; |
| 2633 | updatePosOrdDisplay(uid); | 2649 | updatePosOrdDisplay(uid); |
| 2634 | setOriginalDataValue(data, uid, 'insertion_order', data.entries[uid].order); | 2650 | setWIOriginalDataValue(data, uid, 'insertion_order', data.entries[uid].order); |
| 2635 | await saveWorldInfo(name, data); | 2651 | await saveWorldInfo(name, data); |
| 2636 | }); | 2652 | }); |
| 2637 | orderInput.val(entry.order).trigger('input'); | 2653 | orderInput.val(entry.order).trigger('input'); |
| @@ -2645,7 +2661,7 @@ function getWorldEntry(name, data, entry) { | |||
| 2645 | const value = String($(this).val()).trim(); | 2661 | const value = String($(this).val()).trim(); |
| 2646 | 2662 | ||
| 2647 | data.entries[uid].group = value; | 2663 | data.entries[uid].group = value; |
| 2648 | setOriginalDataValue(data, uid, 'extensions.group', data.entries[uid].group); | 2664 | setWIOriginalDataValue(data, uid, 'extensions.group', data.entries[uid].group); |
| 2649 | await saveWorldInfo(name, data); | 2665 | await saveWorldInfo(name, data); |
| 2650 | }); | 2666 | }); |
| 2651 | groupInput.val(entry.group ?? '').trigger('input'); | 2667 | groupInput.val(entry.group ?? '').trigger('input'); |
| @@ -2658,7 +2674,7 @@ function getWorldEntry(name, data, entry) { | |||
| 2658 | const uid = $(this).data('uid'); | 2674 | const uid = $(this).data('uid'); |
| 2659 | const value = $(this).prop('checked'); | 2675 | const value = $(this).prop('checked'); |
| 2660 | data.entries[uid].groupOverride = value; | 2676 | data.entries[uid].groupOverride = value; |
| 2661 | setOriginalDataValue(data, uid, 'extensions.group_override', data.entries[uid].groupOverride); | 2677 | setWIOriginalDataValue(data, uid, 'extensions.group_override', data.entries[uid].groupOverride); |
| 2662 | await saveWorldInfo(name, data); | 2678 | await saveWorldInfo(name, data); |
| 2663 | }); | 2679 | }); |
| 2664 | groupOverrideInput.prop('checked', entry.groupOverride).trigger('input'); | 2680 | groupOverrideInput.prop('checked', entry.groupOverride).trigger('input'); |
| @@ -2682,7 +2698,7 @@ function getWorldEntry(name, data, entry) { | |||
| 2682 | } | 2698 | } |
| 2683 | 2699 | ||
| 2684 | data.entries[uid].groupWeight = !isNaN(value) ? Math.abs(value) : 1; | 2700 | data.entries[uid].groupWeight = !isNaN(value) ? Math.abs(value) : 1; |
| 2685 | setOriginalDataValue(data, uid, 'extensions.group_weight', data.entries[uid].groupWeight); | 2701 | setWIOriginalDataValue(data, uid, 'extensions.group_weight', data.entries[uid].groupWeight); |
| 2686 | await saveWorldInfo(name, data); | 2702 | await saveWorldInfo(name, data); |
| 2687 | }); | 2703 | }); |
| 2688 | groupWeightInput.val(entry.groupWeight ?? DEFAULT_WEIGHT).trigger('input'); | 2704 | groupWeightInput.val(entry.groupWeight ?? DEFAULT_WEIGHT).trigger('input'); |
| @@ -2695,7 +2711,7 @@ function getWorldEntry(name, data, entry) { | |||
| 2695 | const value = Number($(this).val()); | 2711 | const value = Number($(this).val()); |
| 2696 | data.entries[uid].sticky = !isNaN(value) ? value : null; | 2712 | data.entries[uid].sticky = !isNaN(value) ? value : null; |
| 2697 | 2713 | ||
| 2698 | setOriginalDataValue(data, uid, 'extensions.sticky', data.entries[uid].sticky); | 2714 | setWIOriginalDataValue(data, uid, 'extensions.sticky', data.entries[uid].sticky); |
| 2699 | await saveWorldInfo(name, data); | 2715 | await saveWorldInfo(name, data); |
| 2700 | }); | 2716 | }); |
| 2701 | sticky.val(entry.sticky > 0 ? entry.sticky : '').trigger('input'); | 2717 | sticky.val(entry.sticky > 0 ? entry.sticky : '').trigger('input'); |
| @@ -2708,7 +2724,7 @@ function getWorldEntry(name, data, entry) { | |||
| 2708 | const value = Number($(this).val()); | 2724 | const value = Number($(this).val()); |
| 2709 | data.entries[uid].cooldown = !isNaN(value) ? value : null; | 2725 | data.entries[uid].cooldown = !isNaN(value) ? value : null; |
| 2710 | 2726 | ||
| 2711 | setOriginalDataValue(data, uid, 'extensions.cooldown', data.entries[uid].cooldown); | 2727 | setWIOriginalDataValue(data, uid, 'extensions.cooldown', data.entries[uid].cooldown); |
| 2712 | await saveWorldInfo(name, data); | 2728 | await saveWorldInfo(name, data); |
| 2713 | }); | 2729 | }); |
| 2714 | cooldown.val(entry.cooldown > 0 ? entry.cooldown : '').trigger('input'); | 2730 | cooldown.val(entry.cooldown > 0 ? entry.cooldown : '').trigger('input'); |
| @@ -2721,7 +2737,7 @@ function getWorldEntry(name, data, entry) { | |||
| 2721 | const value = Number($(this).val()); | 2737 | const value = Number($(this).val()); |
| 2722 | data.entries[uid].delay = !isNaN(value) ? value : null; | 2738 | data.entries[uid].delay = !isNaN(value) ? value : null; |
| 2723 | 2739 | ||
| 2724 | setOriginalDataValue(data, uid, 'extensions.delay', data.entries[uid].delay); | 2740 | setWIOriginalDataValue(data, uid, 'extensions.delay', data.entries[uid].delay); |
| 2725 | await saveWorldInfo(name, data); | 2741 | await saveWorldInfo(name, data); |
| 2726 | }); | 2742 | }); |
| 2727 | delay.val(entry.delay > 0 ? entry.delay : '').trigger('input'); | 2743 | delay.val(entry.delay > 0 ? entry.delay : '').trigger('input'); |
| @@ -2741,7 +2757,7 @@ function getWorldEntry(name, data, entry) { | |||
| 2741 | 2757 | ||
| 2742 | data.entries[uid].depth = !isNaN(value) ? value : 0; | 2758 | data.entries[uid].depth = !isNaN(value) ? value : 0; |
| 2743 | updatePosOrdDisplay(uid); | 2759 | updatePosOrdDisplay(uid); |
| 2744 | setOriginalDataValue(data, uid, 'extensions.depth', data.entries[uid].depth); | 2760 | setWIOriginalDataValue(data, uid, 'extensions.depth', data.entries[uid].depth); |
| 2745 | await saveWorldInfo(name, data); | 2761 | await saveWorldInfo(name, data); |
| 2746 | }); | 2762 | }); |
| 2747 | depthInput.val(entry.depth ?? DEFAULT_DEPTH).trigger('input'); | 2763 | depthInput.val(entry.depth ?? DEFAULT_DEPTH).trigger('input'); |
| @@ -2769,7 +2785,7 @@ function getWorldEntry(name, data, entry) { | |||
| 2769 | } | 2785 | } |
| 2770 | } | 2786 | } |
| 2771 | 2787 | ||
| 2772 | setOriginalDataValue(data, uid, 'extensions.probability', data.entries[uid].probability); | 2788 | setWIOriginalDataValue(data, uid, 'extensions.probability', data.entries[uid].probability); |
| 2773 | await saveWorldInfo(name, data); | 2789 | await saveWorldInfo(name, data); |
| 2774 | }); | 2790 | }); |
| 2775 | probabilityInput.val(entry.probability).trigger('input'); | 2791 | probabilityInput.val(entry.probability).trigger('input'); |
| @@ -2836,10 +2852,10 @@ function getWorldEntry(name, data, entry) { | |||
| 2836 | } | 2852 | } |
| 2837 | updatePosOrdDisplay(uid); | 2853 | updatePosOrdDisplay(uid); |
| 2838 | // Spec v2 only supports before_char and after_char | 2854 | // Spec v2 only supports before_char and after_char |
| 2839 | setOriginalDataValue(data, uid, 'position', data.entries[uid].position == 0 ? 'before_char' : 'after_char'); | 2855 | setWIOriginalDataValue(data, uid, 'position', data.entries[uid].position == 0 ? 'before_char' : 'after_char'); |
| 2840 | // Write the original value as extensions field | 2856 | // Write the original value as extensions field |
| 2841 | setOriginalDataValue(data, uid, 'extensions.position', data.entries[uid].position); | 2857 | setWIOriginalDataValue(data, uid, 'extensions.position', data.entries[uid].position); |
| 2842 | setOriginalDataValue(data, uid, 'extensions.role', data.entries[uid].role); | 2858 | setWIOriginalDataValue(data, uid, 'extensions.role', data.entries[uid].role); |
| 2843 | await saveWorldInfo(name, data); | 2859 | await saveWorldInfo(name, data); |
| 2844 | }); | 2860 | }); |
| 2845 | 2861 | ||
| @@ -2881,36 +2897,36 @@ function getWorldEntry(name, data, entry) { | |||
| 2881 | data.entries[uid].constant = true; | 2897 | data.entries[uid].constant = true; |
| 2882 | data.entries[uid].disable = false; | 2898 | data.entries[uid].disable = false; |
| 2883 | data.entries[uid].vectorized = false; | 2899 | data.entries[uid].vectorized = false; |
| 2884 | setOriginalDataValue(data, uid, 'enabled', true); | 2900 | setWIOriginalDataValue(data, uid, 'enabled', true); |
| 2885 | setOriginalDataValue(data, uid, 'constant', true); | 2901 | setWIOriginalDataValue(data, uid, 'constant', true); |
| 2886 | setOriginalDataValue(data, uid, 'extensions.vectorized', false); | 2902 | setWIOriginalDataValue(data, uid, 'extensions.vectorized', false); |
| 2887 | template.removeClass('disabledWIEntry'); | 2903 | template.removeClass('disabledWIEntry'); |
| 2888 | break; | 2904 | break; |
| 2889 | case 'normal': | 2905 | case 'normal': |
| 2890 | data.entries[uid].constant = false; | 2906 | data.entries[uid].constant = false; |
| 2891 | data.entries[uid].disable = false; | 2907 | data.entries[uid].disable = false; |
| 2892 | data.entries[uid].vectorized = false; | 2908 | data.entries[uid].vectorized = false; |
| 2893 | setOriginalDataValue(data, uid, 'enabled', true); | 2909 | setWIOriginalDataValue(data, uid, 'enabled', true); |
| 2894 | setOriginalDataValue(data, uid, 'constant', false); | 2910 | setWIOriginalDataValue(data, uid, 'constant', false); |
| 2895 | setOriginalDataValue(data, uid, 'extensions.vectorized', false); | 2911 | setWIOriginalDataValue(data, uid, 'extensions.vectorized', false); |
| 2896 | template.removeClass('disabledWIEntry'); | 2912 | template.removeClass('disabledWIEntry'); |
| 2897 | break; | 2913 | break; |
| 2898 | case 'vectorized': | 2914 | case 'vectorized': |
| 2899 | data.entries[uid].constant = false; | 2915 | data.entries[uid].constant = false; |
| 2900 | data.entries[uid].disable = false; | 2916 | data.entries[uid].disable = false; |
| 2901 | data.entries[uid].vectorized = true; | 2917 | data.entries[uid].vectorized = true; |
| 2902 | setOriginalDataValue(data, uid, 'enabled', true); | 2918 | setWIOriginalDataValue(data, uid, 'enabled', true); |
| 2903 | setOriginalDataValue(data, uid, 'constant', false); | 2919 | setWIOriginalDataValue(data, uid, 'constant', false); |
| 2904 | setOriginalDataValue(data, uid, 'extensions.vectorized', true); | 2920 | setWIOriginalDataValue(data, uid, 'extensions.vectorized', true); |
| 2905 | template.removeClass('disabledWIEntry'); | 2921 | template.removeClass('disabledWIEntry'); |
| 2906 | break; | 2922 | break; |
| 2907 | case 'disabled': | 2923 | case 'disabled': |
| 2908 | data.entries[uid].constant = false; | 2924 | data.entries[uid].constant = false; |
| 2909 | data.entries[uid].disable = true; | 2925 | data.entries[uid].disable = true; |
| 2910 | data.entries[uid].vectorized = false; | 2926 | data.entries[uid].vectorized = false; |
| 2911 | setOriginalDataValue(data, uid, 'enabled', false); | 2927 | setWIOriginalDataValue(data, uid, 'enabled', false); |
| 2912 | setOriginalDataValue(data, uid, 'constant', false); | 2928 | setWIOriginalDataValue(data, uid, 'constant', false); |
| 2913 | setOriginalDataValue(data, uid, 'extensions.vectorized', false); | 2929 | setWIOriginalDataValue(data, uid, 'extensions.vectorized', false); |
| 2914 | template.addClass('disabledWIEntry'); | 2930 | template.addClass('disabledWIEntry'); |
| 2915 | break; | 2931 | break; |
| 2916 | } | 2932 | } |
| @@ -2941,7 +2957,7 @@ function getWorldEntry(name, data, entry) { | |||
| 2941 | const uid = $(this).data('uid'); | 2957 | const uid = $(this).data('uid'); |
| 2942 | const value = $(this).prop('checked'); | 2958 | const value = $(this).prop('checked'); |
| 2943 | data.entries[uid].excludeRecursion = value; | 2959 | data.entries[uid].excludeRecursion = value; |
| 2944 | setOriginalDataValue(data, uid, 'extensions.exclude_recursion', data.entries[uid].excludeRecursion); | 2960 | setWIOriginalDataValue(data, uid, 'extensions.exclude_recursion', data.entries[uid].excludeRecursion); |
| 2945 | await saveWorldInfo(name, data); | 2961 | await saveWorldInfo(name, data); |
| 2946 | }); | 2962 | }); |
| 2947 | excludeRecursionInput.prop('checked', entry.excludeRecursion).trigger('input'); | 2963 | excludeRecursionInput.prop('checked', entry.excludeRecursion).trigger('input'); |
| @@ -2953,7 +2969,7 @@ function getWorldEntry(name, data, entry) { | |||
| 2953 | const uid = $(this).data('uid'); | 2969 | const uid = $(this).data('uid'); |
| 2954 | const value = $(this).prop('checked'); | 2970 | const value = $(this).prop('checked'); |
| 2955 | data.entries[uid].preventRecursion = value; | 2971 | data.entries[uid].preventRecursion = value; |
| 2956 | setOriginalDataValue(data, uid, 'extensions.prevent_recursion', data.entries[uid].preventRecursion); | 2972 | setWIOriginalDataValue(data, uid, 'extensions.prevent_recursion', data.entries[uid].preventRecursion); |
| 2957 | await saveWorldInfo(name, data); | 2973 | await saveWorldInfo(name, data); |
| 2958 | }); | 2974 | }); |
| 2959 | preventRecursionInput.prop('checked', entry.preventRecursion).trigger('input'); | 2975 | preventRecursionInput.prop('checked', entry.preventRecursion).trigger('input'); |
| @@ -2965,7 +2981,7 @@ function getWorldEntry(name, data, entry) { | |||
| 2965 | const uid = $(this).data('uid'); | 2981 | const uid = $(this).data('uid'); |
| 2966 | const value = $(this).prop('checked'); | 2982 | const value = $(this).prop('checked'); |
| 2967 | data.entries[uid].delayUntilRecursion = value; | 2983 | data.entries[uid].delayUntilRecursion = value; |
| 2968 | setOriginalDataValue(data, uid, 'extensions.delay_until_recursion', data.entries[uid].delayUntilRecursion); | 2984 | setWIOriginalDataValue(data, uid, 'extensions.delay_until_recursion', data.entries[uid].delayUntilRecursion); |
| 2969 | await saveWorldInfo(name, data); | 2985 | await saveWorldInfo(name, data); |
| 2970 | }); | 2986 | }); |
| 2971 | delayUntilRecursionInput.prop('checked', entry.delayUntilRecursion).trigger('input'); | 2987 | delayUntilRecursionInput.prop('checked', entry.delayUntilRecursion).trigger('input'); |
| @@ -2988,7 +3004,7 @@ function getWorldEntry(name, data, entry) { | |||
| 2988 | deleteButton.on('click', async function () { | 3004 | deleteButton.on('click', async function () { |
| 2989 | const uid = $(this).data('uid'); | 3005 | const uid = $(this).data('uid'); |
| 2990 | deleteWorldInfoEntry(data, uid); | 3006 | deleteWorldInfoEntry(data, uid); |
| 2991 | deleteOriginalDataValue(data, uid); | 3007 | deleteWIOriginalDataValue(data, uid); |
| 2992 | await saveWorldInfo(name, data); | 3008 | await saveWorldInfo(name, data); |
| 2993 | updateEditor(navigation_option.previous); | 3009 | updateEditor(navigation_option.previous); |
| 2994 | }); | 3010 | }); |
| @@ -3015,7 +3031,7 @@ function getWorldEntry(name, data, entry) { | |||
| 3015 | } | 3031 | } |
| 3016 | 3032 | ||
| 3017 | data.entries[uid].scanDepth = !isEmpty && !isNaN(value) && value >= 0 && value <= MAX_SCAN_DEPTH ? Math.floor(value) : null; | 3033 | data.entries[uid].scanDepth = !isEmpty && !isNaN(value) && value >= 0 && value <= MAX_SCAN_DEPTH ? Math.floor(value) : null; |
| 3018 | setOriginalDataValue(data, uid, 'extensions.scan_depth', data.entries[uid].scanDepth); | 3034 | setWIOriginalDataValue(data, uid, 'extensions.scan_depth', data.entries[uid].scanDepth); |
| 3019 | await saveWorldInfo(name, data); | 3035 | await saveWorldInfo(name, data); |
| 3020 | }); | 3036 | }); |
| 3021 | scanDepthInput.val(entry.scanDepth ?? null).trigger('input'); | 3037 | scanDepthInput.val(entry.scanDepth ?? null).trigger('input'); |
| @@ -3028,7 +3044,7 @@ function getWorldEntry(name, data, entry) { | |||
| 3028 | const value = $(this).val(); | 3044 | const value = $(this).val(); |
| 3029 | 3045 | ||
| 3030 | data.entries[uid].caseSensitive = value === 'null' ? null : value === 'true'; | 3046 | data.entries[uid].caseSensitive = value === 'null' ? null : value === 'true'; |
| 3031 | setOriginalDataValue(data, uid, 'extensions.case_sensitive', data.entries[uid].caseSensitive); | 3047 | setWIOriginalDataValue(data, uid, 'extensions.case_sensitive', data.entries[uid].caseSensitive); |
| 3032 | await saveWorldInfo(name, data); | 3048 | await saveWorldInfo(name, data); |
| 3033 | }); | 3049 | }); |
| 3034 | caseSensitiveSelect.val((entry.caseSensitive === null || entry.caseSensitive === undefined) ? 'null' : entry.caseSensitive ? 'true' : 'false').trigger('input'); | 3050 | caseSensitiveSelect.val((entry.caseSensitive === null || entry.caseSensitive === undefined) ? 'null' : entry.caseSensitive ? 'true' : 'false').trigger('input'); |
| @@ -3041,7 +3057,7 @@ function getWorldEntry(name, data, entry) { | |||
| 3041 | const value = $(this).val(); | 3057 | const value = $(this).val(); |
| 3042 | 3058 | ||
| 3043 | data.entries[uid].matchWholeWords = value === 'null' ? null : value === 'true'; | 3059 | data.entries[uid].matchWholeWords = value === 'null' ? null : value === 'true'; |
| 3044 | setOriginalDataValue(data, uid, 'extensions.match_whole_words', data.entries[uid].matchWholeWords); | 3060 | setWIOriginalDataValue(data, uid, 'extensions.match_whole_words', data.entries[uid].matchWholeWords); |
| 3045 | await saveWorldInfo(name, data); | 3061 | await saveWorldInfo(name, data); |
| 3046 | }); | 3062 | }); |
| 3047 | matchWholeWordsSelect.val((entry.matchWholeWords === null || entry.matchWholeWords === undefined) ? 'null' : entry.matchWholeWords ? 'true' : 'false').trigger('input'); | 3063 | matchWholeWordsSelect.val((entry.matchWholeWords === null || entry.matchWholeWords === undefined) ? 'null' : entry.matchWholeWords ? 'true' : 'false').trigger('input'); |
| @@ -3054,7 +3070,7 @@ function getWorldEntry(name, data, entry) { | |||
| 3054 | const value = $(this).val(); | 3070 | const value = $(this).val(); |
| 3055 | 3071 | ||
| 3056 | data.entries[uid].useGroupScoring = value === 'null' ? null : value === 'true'; | 3072 | data.entries[uid].useGroupScoring = value === 'null' ? null : value === 'true'; |
| 3057 | setOriginalDataValue(data, uid, 'extensions.use_group_scoring', data.entries[uid].useGroupScoring); | 3073 | setWIOriginalDataValue(data, uid, 'extensions.use_group_scoring', data.entries[uid].useGroupScoring); |
| 3058 | await saveWorldInfo(name, data); | 3074 | await saveWorldInfo(name, data); |
| 3059 | }); | 3075 | }); |
| 3060 | useGroupScoringSelect.val((entry.useGroupScoring === null || entry.useGroupScoring === undefined) ? 'null' : entry.useGroupScoring ? 'true' : 'false').trigger('input'); | 3076 | useGroupScoringSelect.val((entry.useGroupScoring === null || entry.useGroupScoring === undefined) ? 'null' : entry.useGroupScoring ? 'true' : 'false').trigger('input'); |
| @@ -3067,7 +3083,7 @@ function getWorldEntry(name, data, entry) { | |||
| 3067 | const value = $(this).val(); | 3083 | const value = $(this).val(); |
| 3068 | 3084 | ||
| 3069 | data.entries[uid].automationId = value; | 3085 | data.entries[uid].automationId = value; |
| 3070 | setOriginalDataValue(data, uid, 'extensions.automation_id', data.entries[uid].automationId); | 3086 | setWIOriginalDataValue(data, uid, 'extensions.automation_id', data.entries[uid].automationId); |
| 3071 | await saveWorldInfo(name, data); | 3087 | await saveWorldInfo(name, data); |
| 3072 | }); | 3088 | }); |
| 3073 | automationIdInput.val(entry.automationId ?? '').trigger('input'); | 3089 | automationIdInput.val(entry.automationId ?? '').trigger('input'); |
| @@ -3200,12 +3216,12 @@ function createEntryInputAutocomplete(input, callback, { allowMultiple = false } | |||
| 3200 | 3216 | ||
| 3201 | 3217 | ||
| 3202 | /** | 3218 | /** |
| 3203 | * Duplicated a WI entry by copying all of its properties and assigning a new uid | 3219 | * Duplicate a WI entry by copying all of its properties and assigning a new uid |
| 3204 | * @param {*} data - The data of the book | 3220 | * @param {*} data - The data of the book |
| 3205 | * @param {number} uid - The uid of the entry to copy in this book | 3221 | * @param {number} uid - The uid of the entry to copy in this book |
| 3206 | * @returns {*} The new WI duplicated entry | 3222 | * @returns {*} The new WI duplicated entry |
| 3207 | */ | 3223 | */ |
| 3208 | function duplicateWorldInfoEntry(data, uid) { | 3224 | export function duplicateWorldInfoEntry(data, uid) { |
| 3209 | if (!data || !('entries' in data) || !data.entries[uid]) { | 3225 | if (!data || !('entries' in data) || !data.entries[uid]) { |
| 3210 | return; | 3226 | return; |
| 3211 | } | 3227 | } |
| @@ -3226,7 +3242,7 @@ function duplicateWorldInfoEntry(data, uid) { | |||
| 3226 | * @param {*[]} data - The data of the book | 3242 | * @param {*[]} data - The data of the book |
| 3227 | * @param {number} uid - The uid of the entry to copy in this book | 3243 | * @param {number} uid - The uid of the entry to copy in this book |
| 3228 | */ | 3244 | */ |
| 3229 | function deleteWorldInfoEntry(data, uid) { | 3245 | export function deleteWorldInfoEntry(data, uid) { |
| 3230 | if (!data || !('entries' in data)) { | 3246 | if (!data || !('entries' in data)) { |
| 3231 | return; | 3247 | return; |
| 3232 | } | 3248 | } |
| @@ -3245,7 +3261,7 @@ function deleteWorldInfoEntry(data, uid) { | |||
| 3245 | * | 3261 | * |
| 3246 | * @type {{[key: string]: { default: any, type: string }}} | 3262 | * @type {{[key: string]: { default: any, type: string }}} |
| 3247 | */ | 3263 | */ |
| 3248 | const newEntryDefinition = { | 3264 | export const newWorldInfoEntryDefinition = { |
| 3249 | key: { default: [], type: 'array' }, | 3265 | key: { default: [], type: 'array' }, |
| 3250 | keysecondary: { default: [], type: 'array' }, | 3266 | keysecondary: { default: [], type: 'array' }, |
| 3251 | comment: { default: '', type: 'string' }, | 3267 | comment: { default: '', type: 'string' }, |
| @@ -3278,8 +3294,8 @@ const newEntryDefinition = { | |||
| 3278 | delay: { default: null, type: 'number?' }, | 3294 | delay: { default: null, type: 'number?' }, |
| 3279 | }; | 3295 | }; |
| 3280 | 3296 | ||
| 3281 | const newEntryTemplate = Object.fromEntries( | 3297 | export const newWorldInfoEntryTemplate = Object.fromEntries( |
| 3282 | Object.entries(newEntryDefinition).map(([key, value]) => [key, value.default]), | 3298 | Object.entries(newWorldInfoEntryDefinition).map(([key, value]) => [key, value.default]), |
| 3283 | ); | 3299 | ); |
| 3284 | 3300 | ||
| 3285 | /** | 3301 | /** |
| @@ -3288,7 +3304,7 @@ const newEntryTemplate = Object.fromEntries( | |||
| 3288 | * @param {any} data WI data | 3304 | * @param {any} data WI data |
| 3289 | * @returns {object | undefined} New entry object or undefined if failed | 3305 | * @returns {object | undefined} New entry object or undefined if failed |
| 3290 | */ | 3306 | */ |
| 3291 | function createWorldInfoEntry(_name, data) { | 3307 | export function createWorldInfoEntry(_name, data) { |
| 3292 | const newUid = getFreeWorldEntryUid(data); | 3308 | const newUid = getFreeWorldEntryUid(data); |
| 3293 | 3309 | ||
| 3294 | if (!Number.isInteger(newUid)) { | 3310 | if (!Number.isInteger(newUid)) { |
| @@ -3296,7 +3312,7 @@ function createWorldInfoEntry(_name, data) { | |||
| 3296 | return; | 3312 | return; |
| 3297 | } | 3313 | } |
| 3298 | 3314 | ||
| 3299 | const newEntry = { uid: newUid, ...structuredClone(newEntryTemplate) }; | 3315 | const newEntry = { uid: newUid, ...structuredClone(newWorldInfoEntryTemplate) }; |
| 3300 | data.entries[newUid] = newEntry; | 3316 | data.entries[newUid] = newEntry; |
| 3301 | 3317 | ||
| 3302 | return newEntry; | 3318 | return newEntry; |
| @@ -3314,7 +3330,21 @@ async function _save(name, data) { | |||
| 3314 | eventSource.emit(event_types.WORLDINFO_UPDATED, name, data); | 3330 | eventSource.emit(event_types.WORLDINFO_UPDATED, name, data); |
| 3315 | } | 3331 | } |
| 3316 | 3332 | ||
| 3317 | async function saveWorldInfo(name, data, immediately = false) { | 3333 | |
| 3334 | /** | ||
| 3335 | * Saves the world info | ||
| 3336 | * | ||
| 3337 | * This will also refresh the `worldInfoCache`. | ||
| 3338 | * Note, for performance reasons the saved cache will not make a deep clone of the data. | ||
| 3339 | * It is your responsibility to not modify the saved data object after calling this function, or there will be data inconsistencies. | ||
| 3340 | * Call `loadWorldInfoData` or query directly from cache if you need the object again. | ||
| 3341 | * | ||
| 3342 | * @param {string} name - The name of the world info | ||
| 3343 | * @param {any} data - The data to be saved | ||
| 3344 | * @param {boolean} [immediately=false] - Whether to save immediately or use debouncing | ||
| 3345 | * @return {Promise<void>} A promise that resolves when the world info is saved | ||
| 3346 | */ | ||
| 3347 | export async function saveWorldInfo(name, data, immediately = false) { | ||
| 3318 | if (!name || !data) { | 3348 | if (!name || !data) { |
| 3319 | return; | 3349 | return; |
| 3320 | } | 3350 | } |
| @@ -3371,7 +3401,7 @@ async function renameWorldInfo(name, data) { | |||
| 3371 | * @param {string} worldInfoName - The name of the world info to delete | 3401 | * @param {string} worldInfoName - The name of the world info to delete |
| 3372 | * @returns {Promise<boolean>} A promise that resolves to true if the world info was successfully deleted, false otherwise | 3402 | * @returns {Promise<boolean>} A promise that resolves to true if the world info was successfully deleted, false otherwise |
| 3373 | */ | 3403 | */ |
| 3374 | async function deleteWorldInfo(worldInfoName) { | 3404 | export async function deleteWorldInfo(worldInfoName) { |
| 3375 | if (!world_names.includes(worldInfoName)) { | 3405 | if (!world_names.includes(worldInfoName)) { |
| 3376 | return false; | 3406 | return false; |
| 3377 | } | 3407 | } |
| @@ -3406,7 +3436,7 @@ async function deleteWorldInfo(worldInfoName) { | |||
| 3406 | return true; | 3436 | return true; |
| 3407 | } | 3437 | } |
| 3408 | 3438 | ||
| 3409 | function getFreeWorldEntryUid(data) { | 3439 | export function getFreeWorldEntryUid(data) { |
| 3410 | if (!data || !('entries' in data)) { | 3440 | if (!data || !('entries' in data)) { |
| 3411 | return null; | 3441 | return null; |
| 3412 | } | 3442 | } |
| @@ -3422,7 +3452,7 @@ function getFreeWorldEntryUid(data) { | |||
| 3422 | return null; | 3452 | return null; |
| 3423 | } | 3453 | } |
| 3424 | 3454 | ||
| 3425 | function getFreeWorldName() { | 3455 | export function getFreeWorldName() { |
| 3426 | const MAX_FREE_NAME = 100_000; | 3456 | const MAX_FREE_NAME = 100_000; |
| 3427 | for (let index = 1; index < MAX_FREE_NAME; index++) { | 3457 | for (let index = 1; index < MAX_FREE_NAME; index++) { |
| 3428 | const newName = `New World (${index})`; | 3458 | const newName = `New World (${index})`; |
| @@ -3444,7 +3474,7 @@ function getFreeWorldName() { | |||
| 3444 | * @param {boolean} [options.interactive=false] - Whether to show a confirmation dialog when overwriting an existing world | 3474 | * @param {boolean} [options.interactive=false] - Whether to show a confirmation dialog when overwriting an existing world |
| 3445 | * @returns {Promise<boolean>} - True if the world info was successfully created, false otherwise | 3475 | * @returns {Promise<boolean>} - True if the world info was successfully created, false otherwise |
| 3446 | */ | 3476 | */ |
| 3447 | async function createNewWorldInfo(worldName, { interactive = false } = {}) { | 3477 | export async function createNewWorldInfo(worldName, { interactive = false } = {}) { |
| 3448 | const worldInfoTemplate = { entries: {} }; | 3478 | const worldInfoTemplate = { entries: {} }; |
| 3449 | 3479 | ||
| 3450 | if (!worldName) { | 3480 | if (!worldName) { |
| @@ -3505,7 +3535,7 @@ async function getCharacterLore() { | |||
| 3505 | continue; | 3535 | continue; |
| 3506 | } | 3536 | } |
| 3507 | 3537 | ||
| 3508 | const data = await loadWorldInfoData(worldName); | 3538 | const data = await loadWorldInfo(worldName); |
| 3509 | const newEntries = data ? Object.keys(data.entries).map((x) => data.entries[x]).map(({ uid, ...rest }) => ({ uid, world: worldName, ...rest })) : []; | 3539 | const newEntries = data ? Object.keys(data.entries).map((x) => data.entries[x]).map(({ uid, ...rest }) => ({ uid, world: worldName, ...rest })) : []; |
| 3510 | entries = entries.concat(newEntries); | 3540 | entries = entries.concat(newEntries); |
| 3511 | 3541 | ||
| @@ -3525,7 +3555,7 @@ async function getGlobalLore() { | |||
| 3525 | 3555 | ||
| 3526 | let entries = []; | 3556 | let entries = []; |
| 3527 | for (const worldName of selected_world_info) { | 3557 | for (const worldName of selected_world_info) { |
| 3528 | const data = await loadWorldInfoData(worldName); | 3558 | const data = await loadWorldInfo(worldName); |
| 3529 | const newEntries = data ? Object.keys(data.entries).map((x) => data.entries[x]).map(({ uid, ...rest }) => ({ uid, world: worldName, ...rest })) : []; | 3559 | const newEntries = data ? Object.keys(data.entries).map((x) => data.entries[x]).map(({ uid, ...rest }) => ({ uid, world: worldName, ...rest })) : []; |
| 3530 | entries = entries.concat(newEntries); | 3560 | entries = entries.concat(newEntries); |
| 3531 | } | 3561 | } |
| @@ -3547,7 +3577,7 @@ async function getChatLore() { | |||
| 3547 | return []; | 3577 | return []; |
| 3548 | } | 3578 | } |
| 3549 | 3579 | ||
| 3550 | const data = await loadWorldInfoData(chatWorld); | 3580 | const data = await loadWorldInfo(chatWorld); |
| 3551 | const entries = data ? Object.keys(data.entries).map((x) => data.entries[x]).map(({ uid, ...rest }) => ({ uid, world: chatWorld, ...rest })) : []; | 3581 | const entries = data ? Object.keys(data.entries).map((x) => data.entries[x]).map(({ uid, ...rest }) => ({ uid, world: chatWorld, ...rest })) : []; |
| 3552 | 3582 | ||
| 3553 | console.debug(`[WI] Chat lore has ${entries.length} entries`, [chatWorld]); | 3583 | console.debug(`[WI] Chat lore has ${entries.length} entries`, [chatWorld]); |
| @@ -3663,7 +3693,7 @@ function parseDecorators(content) { | |||
| 3663 | * @typedef {{ worldInfoBefore: string, worldInfoAfter: string, EMEntries: any[], WIDepthEntries: any[], allActivatedEntries: Set<any> }} WIActivated | 3693 | * @typedef {{ worldInfoBefore: string, worldInfoAfter: string, EMEntries: any[], WIDepthEntries: any[], allActivatedEntries: Set<any> }} WIActivated |
| 3664 | * @returns {Promise<WIActivated>} The world info activated. | 3694 | * @returns {Promise<WIActivated>} The world info activated. |
| 3665 | */ | 3695 | */ |
| 3666 | async function checkWorldInfo(chat, maxContext, isDryRun) { | 3696 | export async function checkWorldInfo(chat, maxContext, isDryRun) { |
| 3667 | const context = getContext(); | 3697 | const context = getContext(); |
| 3668 | const buffer = new WorldInfoBuffer(chat); | 3698 | const buffer = new WorldInfoBuffer(chat); |
| 3669 | 3699 | ||
| @@ -4233,7 +4263,7 @@ function convertAgnaiMemoryBook(inputObj) { | |||
| 4233 | 4263 | ||
| 4234 | inputObj.entries.forEach((entry, index) => { | 4264 | inputObj.entries.forEach((entry, index) => { |
| 4235 | outputObj.entries[index] = { | 4265 | outputObj.entries[index] = { |
| 4236 | ...newEntryTemplate, | 4266 | ...newWorldInfoEntryTemplate, |
| 4237 | uid: index, | 4267 | uid: index, |
| 4238 | key: entry.keywords, | 4268 | key: entry.keywords, |
| 4239 | keysecondary: [], | 4269 | keysecondary: [], |
| @@ -4275,7 +4305,7 @@ function convertRisuLorebook(inputObj) { | |||
| 4275 | 4305 | ||
| 4276 | inputObj.data.forEach((entry, index) => { | 4306 | inputObj.data.forEach((entry, index) => { |
| 4277 | outputObj.entries[index] = { | 4307 | outputObj.entries[index] = { |
| 4278 | ...newEntryTemplate, | 4308 | ...newWorldInfoEntryTemplate, |
| 4279 | uid: index, | 4309 | uid: index, |
| 4280 | key: entry.key.split(',').map(x => x.trim()), | 4310 | key: entry.key.split(',').map(x => x.trim()), |
| 4281 | keysecondary: entry.secondkey ? entry.secondkey.split(',').map(x => x.trim()) : [], | 4311 | keysecondary: entry.secondkey ? entry.secondkey.split(',').map(x => x.trim()) : [], |
| @@ -4322,7 +4352,7 @@ function convertNovelLorebook(inputObj) { | |||
| 4322 | const addMemo = displayName !== undefined && displayName.trim() !== ''; | 4352 | const addMemo = displayName !== undefined && displayName.trim() !== ''; |
| 4323 | 4353 | ||
| 4324 | outputObj.entries[index] = { | 4354 | outputObj.entries[index] = { |
| 4325 | ...newEntryTemplate, | 4355 | ...newWorldInfoEntryTemplate, |
| 4326 | uid: index, | 4356 | uid: index, |
| 4327 | key: entry.keys, | 4357 | key: entry.keys, |
| 4328 | keysecondary: [], | 4358 | keysecondary: [], |
| @@ -4369,7 +4399,7 @@ function convertCharacterBook(characterBook) { | |||
| 4369 | } | 4399 | } |
| 4370 | 4400 | ||
| 4371 | result.entries[entry.id] = { | 4401 | result.entries[entry.id] = { |
| 4372 | ...newEntryTemplate, | 4402 | ...newWorldInfoEntryTemplate, |
| 4373 | uid: entry.id, | 4403 | uid: entry.id, |
| 4374 | key: entry.keys, | 4404 | key: entry.keys, |
| 4375 | keysecondary: entry.secondary_keys || [], | 4405 | keysecondary: entry.secondary_keys || [], |
| @@ -4499,7 +4529,7 @@ export async function importEmbeddedWorldInfo(skipPopup = false) { | |||
| 4499 | setWorldInfoButtonClass(chid, true); | 4529 | setWorldInfoButtonClass(chid, true); |
| 4500 | } | 4530 | } |
| 4501 | 4531 | ||
| 4502 | function onWorldInfoChange(args, text) { | 4532 | export function onWorldInfoChange(args, text) { |
| 4503 | if (args !== '__notSlashCommand__') { // if it's a slash command | 4533 | if (args !== '__notSlashCommand__') { // if it's a slash command |
| 4504 | const silent = isTrueBoolean(args.silent); | 4534 | const silent = isTrueBoolean(args.silent); |
| 4505 | if (text.trim() !== '') { // and args are provided | 4535 | if (text.trim() !== '') { // and args are provided |
| @@ -4650,7 +4680,7 @@ export async function importWorldInfo(file) { | |||
| 4650 | }); | 4680 | }); |
| 4651 | } | 4681 | } |
| 4652 | 4682 | ||
| 4653 | function assignLorebookToChat() { | 4683 | export function assignLorebookToChat() { |
| 4654 | const selectedName = chat_metadata[METADATA_KEY]; | 4684 | const selectedName = chat_metadata[METADATA_KEY]; |
| 4655 | const template = $('#chat_world_template .chat_world').clone(); | 4685 | const template = $('#chat_world_template .chat_world').clone(); |
| 4656 | 4686 | ||