Move shallow toggle to config.yaml
| @@ -1,8 +1,6 @@ | |||
| 1 | # -- DATA CONFIGURATION -- | 1 | # -- DATA CONFIGURATION -- |
| 2 | # Root directory for user data storage | 2 | # Root directory for user data storage |
| 3 | dataRoot: ./data | 3 | dataRoot: ./data |
| 4 | # The maximum amount of memory that parsed character cards can use in MB | ||
| 5 | cardsCacheCapacity: 100 | ||
| 6 | # -- SERVER CONFIGURATION -- | 4 | # -- SERVER CONFIGURATION -- |
| 7 | # Listen for incoming connections | 5 | # Listen for incoming connections |
| 8 | listen: false | 6 | listen: false |
| @@ -135,6 +133,14 @@ thumbnails: | |||
| 135 | # Maximum thumbnail dimensions per type [width, height] | 133 | # Maximum thumbnail dimensions per type [width, height] |
| 136 | dimensions: { 'bg': [160, 90], 'avatar': [96, 144] } | 134 | dimensions: { 'bg': [160, 90], 'avatar': [96, 144] } |
| 137 | 135 | ||
| 136 | # PERFORMANCE-RELATED CONFIGURATION | ||
| 137 | performance: | ||
| 138 | # Enables lazy loading of character cards. Improves performances with large card libraries. | ||
| 139 | # May have compatibility issues with some extensions. | ||
| 140 | lazyLoadCharacters: false | ||
| 141 | # The maximum amount of memory that parsed character cards can use in MB | ||
| 142 | cardsCacheCapacity: 100 | ||
| 143 | |||
| 138 | # Allow secret keys exposure via API | 144 | # Allow secret keys exposure via API |
| 139 | allowKeysExposure: false | 145 | allowKeysExposure: false |
| 140 | # Skip new default content checks | 146 | # Skip new default content checks |
| @@ -96,6 +96,11 @@ const keyMigrationMap = [ | |||
| 96 | newKey: 'logging.minLogLevel', | 96 | newKey: 'logging.minLogLevel', |
| 97 | migrate: (value) => value, | 97 | migrate: (value) => value, |
| 98 | }, | 98 | }, |
| 99 | { | ||
| 100 | oldKey: 'cardsCacheCapacity', | ||
| 101 | newKey: 'performance.cardsCacheCapacity', | ||
| 102 | migrate: (value) => value, | ||
| 103 | }, | ||
| 99 | // uncomment one release after 1.12.13 | 104 | // uncomment one release after 1.12.13 |
| 100 | /* | 105 | /* |
| 101 | { | 106 | { |
| @@ -4424,13 +4424,6 @@ | |||
| 4424 | <option data-i18n="tag_import_existing" value="4">Existing</option> | 4424 | <option data-i18n="tag_import_existing" value="4">Existing</option> |
| 4425 | </select> | 4425 | </select> |
| 4426 | </div> | 4426 | </div> |
| 4427 | <label class="checkbox_label" for="shallow_characters" data-i18n="[title]Experimental feature. May behave unstable and have compatibility issues." title="Experimental feature. May behave unstable and have compatibility issues."> | ||
| 4428 | <input id="shallow_characters" type="checkbox" /> | ||
| 4429 | <small data-i18n="Lazy Load Characters"> | ||
| 4430 | Lazy Load Characters | ||
| 4431 | </small> | ||
| 4432 | <i class="fa-solid fa-flask"></i> | ||
| 4433 | </label> | ||
| 4434 | <label class="checkbox_label" for="fuzzy_search_checkbox" title="Use fuzzy matching, and search characters in the list by all data fields, not just by a name substring." data-i18n="[title]Use fuzzy matching, and search characters in the list by all data fields, not just by a name substring"> | 4427 | <label class="checkbox_label" for="fuzzy_search_checkbox" title="Use fuzzy matching, and search characters in the list by all data fields, not just by a name substring." data-i18n="[title]Use fuzzy matching, and search characters in the list by all data fields, not just by a name substring"> |
| 4435 | <input id="fuzzy_search_checkbox" type="checkbox" /> | 4428 | <input id="fuzzy_search_checkbox" type="checkbox" /> |
| 4436 | <small data-i18n="Advanced Character Search">Advanced Character Search</small> | 4429 | <small data-i18n="Advanced Character Search">Advanced Character Search</small> |
| @@ -1780,9 +1780,7 @@ export async function getCharacters() { | |||
| 1780 | const response = await fetch('/api/characters/all', { | 1780 | const response = await fetch('/api/characters/all', { |
| 1781 | method: 'POST', | 1781 | method: 'POST', |
| 1782 | headers: getRequestHeaders(), | 1782 | headers: getRequestHeaders(), |
| 1783 | body: JSON.stringify({ | 1783 | body: JSON.stringify({}), |
| 1784 | shallow: power_user.shallow_characters, | ||
| 1785 | }), | ||
| 1786 | }); | 1784 | }); |
| 1787 | if (response.ok === true) { | 1785 | if (response.ok === true) { |
| 1788 | characters.splice(0, characters.length); | 1786 | characters.splice(0, characters.length); |
| @@ -6718,6 +6716,7 @@ export async function unshallowCharacter(characterId) { | |||
| 6718 | return; | 6716 | return; |
| 6719 | } | 6717 | } |
| 6720 | 6718 | ||
| 6719 | /** @type {import('./scripts/char-data.js').v1CharData} */ | ||
| 6721 | const character = characters[characterId]; | 6720 | const character = characters[characterId]; |
| 6722 | if (!character) { | 6721 | if (!character) { |
| 6723 | console.warn('Character not found:', characterId); | 6722 | console.warn('Character not found:', characterId); |
| @@ -113,5 +113,6 @@ | |||
| 113 | * @property {string} chat - name of the current chat file chat | 113 | * @property {string} chat - name of the current chat file chat |
| 114 | * @property {string} avatar - file name of the avatar image (acts as a unique identifier) | 114 | * @property {string} avatar - file name of the avatar image (acts as a unique identifier) |
| 115 | * @property {string} json_data - the full raw JSON data of the character | 115 | * @property {string} json_data - the full raw JSON data of the character |
| 116 | * @property {boolean?} shallow - if the data is shallow (lazy-loaded) | ||
| 116 | */ | 117 | */ |
| 117 | export default 0;// now this file is a module | 118 | export default 0;// now this file is a module |
| @@ -314,7 +314,6 @@ let power_user = { | |||
| 314 | forbid_external_media: true, | 314 | forbid_external_media: true, |
| 315 | external_media_allowed_overrides: [], | 315 | external_media_allowed_overrides: [], |
| 316 | external_media_forbidden_overrides: [], | 316 | external_media_forbidden_overrides: [], |
| 317 | shallow_characters: false, | ||
| 318 | }; | 317 | }; |
| 319 | 318 | ||
| 320 | let themes = []; | 319 | let themes = []; |
| @@ -1596,7 +1595,6 @@ async function loadPowerUserSettings(settings, data) { | |||
| 1596 | $('#auto-connect-checkbox').prop('checked', power_user.auto_connect); | 1595 | $('#auto-connect-checkbox').prop('checked', power_user.auto_connect); |
| 1597 | $('#auto-load-chat-checkbox').prop('checked', power_user.auto_load_chat); | 1596 | $('#auto-load-chat-checkbox').prop('checked', power_user.auto_load_chat); |
| 1598 | $('#forbid_external_media').prop('checked', power_user.forbid_external_media); | 1597 | $('#forbid_external_media').prop('checked', power_user.forbid_external_media); |
| 1599 | $('#shallow_characters').prop('checked', power_user.shallow_characters); | ||
| 1600 | 1598 | ||
| 1601 | for (const theme of themes) { | 1599 | for (const theme of themes) { |
| 1602 | const option = document.createElement('option'); | 1600 | const option = document.createElement('option'); |
| @@ -3890,12 +3888,6 @@ $(document).ready(() => { | |||
| 3890 | await exportTheme(); | 3888 | await exportTheme(); |
| 3891 | }); | 3889 | }); |
| 3892 | 3890 | ||
| 3893 | $('#shallow_characters').on('input', function () { | ||
| 3894 | power_user.shallow_characters = !!$(this).prop('checked'); | ||
| 3895 | saveSettingsDebounced(); | ||
| 3896 | toastr.info('Reload the page for this setting to take effect'); | ||
| 3897 | }); | ||
| 3898 | |||
| 3899 | $(document).on('click', '#debug_table [data-debug-function]', function () { | 3891 | $(document).on('click', '#debug_table [data-debug-function]', function () { |
| 3900 | const functionId = $(this).data('debug-function'); | 3892 | const functionId = $(this).data('debug-function'); |
| 3901 | const functionRecord = debug_functions.find(f => f.functionId === functionId); | 3893 | const functionRecord = debug_functions.find(f => f.functionId === functionId); |
| @@ -24,11 +24,13 @@ import { importRisuSprites } from './sprites.js'; | |||
| 24 | const defaultAvatarPath = './public/img/ai4.png'; | 24 | const defaultAvatarPath = './public/img/ai4.png'; |
| 25 | 25 | ||
| 26 | // KV-store for parsed character data | 26 | // KV-store for parsed character data |
| 27 | const cacheCapacity = Number(getConfigValue('cardsCacheCapacity', 100, 'number')); // MB | 27 | const cacheCapacity = Number(getConfigValue('performance.cardsCacheCapacity', 100, 'number')); // MB |
| 28 | // With 100 MB limit it would take roughly 3000 characters to reach this limit | 28 | // With 100 MB limit it would take roughly 3000 characters to reach this limit |
| 29 | const characterDataCache = new MemoryLimitedMap(1024 * 1024 * cacheCapacity); | 29 | const characterDataCache = new MemoryLimitedMap(1024 * 1024 * cacheCapacity); |
| 30 | // Some Android devices require tighter memory management | 30 | // Some Android devices require tighter memory management |
| 31 | const isAndroid = process.platform === 'android'; | 31 | const isAndroid = process.platform === 'android'; |
| 32 | // Use shallow character data for the character list | ||
| 33 | const useShallowCharacters = !!getConfigValue('performance.lazyLoadCharacters', false, 'boolean'); | ||
| 32 | 34 | ||
| 33 | /** | 35 | /** |
| 34 | * Reads the character card from the specified image file. | 36 | * Reads the character card from the specified image file. |
| @@ -207,6 +209,7 @@ const calculateDataSize = (data) => { | |||
| 207 | */ | 209 | */ |
| 208 | const toShallow = (character) => { | 210 | const toShallow = (character) => { |
| 209 | return { | 211 | return { |
| 212 | shallow: true, | ||
| 210 | name: character.name, | 213 | name: character.name, |
| 211 | avatar: character.avatar, | 214 | avatar: character.avatar, |
| 212 | chat: character.chat, | 215 | chat: character.chat, |
| @@ -225,7 +228,6 @@ const toShallow = (character) => { | |||
| 225 | fav: _.get(character, 'data.extensions.fav', false), | 228 | fav: _.get(character, 'data.extensions.fav', false), |
| 226 | }, | 229 | }, |
| 227 | }, | 230 | }, |
| 228 | shallow: true, | ||
| 229 | }; | 231 | }; |
| 230 | }; | 232 | }; |
| 231 | 233 | ||
| @@ -1022,10 +1024,9 @@ router.post('/delete', jsonParser, validateAvatarUrlMiddleware, async function ( | |||
| 1022 | */ | 1024 | */ |
| 1023 | router.post('/all', jsonParser, async function (request, response) { | 1025 | router.post('/all', jsonParser, async function (request, response) { |
| 1024 | try { | 1026 | try { |
| 1025 | const shallow = !!request.body.shallow; | ||
| 1026 | const files = fs.readdirSync(request.user.directories.characters); | 1027 | const files = fs.readdirSync(request.user.directories.characters); |
| 1027 | const pngFiles = files.filter(file => file.endsWith('.png')); | 1028 | const pngFiles = files.filter(file => file.endsWith('.png')); |
| 1028 | const processingPromises = pngFiles.map(file => processCharacter(file, request.user.directories, { shallow })); | 1029 | const processingPromises = pngFiles.map(file => processCharacter(file, request.user.directories, { shallow: useShallowCharacters })); |
| 1029 | const data = (await Promise.all(processingPromises)).filter(c => c.name); | 1030 | const data = (await Promise.all(processingPromises)).filter(c => c.name); |
| 1030 | return response.send(data); | 1031 | return response.send(data); |
| 1031 | } catch (err) { | 1032 | } catch (err) { |