From d1cdd60883b3abf3030a7a114d8c31b41956ec43 Mon Sep 17 00:00:00 2001 From: Wolfsblvt Date: Tue, 30 Apr 2024 02:27:44 +0200 Subject: [PATCH] Scored search sorting for personas --- public/index.html | 1 + public/script.js | 59 +++++++++++++++++++++++++++++++----- public/scripts/filters.js | 11 ++++--- public/scripts/personas.js | 4 ++- public/scripts/power-user.js | 10 ++++-- public/scripts/world-info.js | 12 +++----- 6 files changed, 74 insertions(+), 23 deletions(-) diff --git a/public/index.html b/public/index.html index 3ecac3fe6..d2c7ef8ac 100644 --- a/public/index.html +++ b/public/index.html @@ -4186,6 +4186,7 @@ diff --git a/public/script.js b/public/script.js index f57aa157c..0634313fe 100644 --- a/public/script.js +++ b/public/script.js @@ -5914,17 +5914,16 @@ export async function getUserAvatars(doRender = true, openPageAt = '') { return []; } - allEntities.sort((a, b) => { - const aName = String(power_user.personas[a] || a); - const bName = String(power_user.personas[b] || b); - return power_user.persona_sort_order === 'asc' ? aName.localeCompare(bName) : bName.localeCompare(aName); - }); - if (!doRender) { return allEntities; } - const entities = personasFilter.applyFilters(allEntities); + // Before printing the personas, we check if we should enable/disable search sorting + verifyPersonaSearchSortRule(); + + let entities = personasFilter.applyFilters(allEntities); + entities = sortPersonas(entities); + const storageKey = 'Personas_PerPage'; const listId = '#user_avatar_block'; const perPage = Number(localStorage.getItem(storageKey)) || 5; @@ -5978,6 +5977,50 @@ function highlightSelectedAvatar() { $(`#user_avatar_block .avatar-container[imgfile="${user_avatar}"]`).addClass('selected'); } +/** + * Sort the given personas + * @param {string[]} personas - The persona names to sort + * @returns {string[]} The sorted persona names arrray, same reference as passed in + */ +function sortPersonas(personas) { + const option = $('#persona_sort_order').find(':selected'); + if (option.attr('value') === 'search') { + personas.sort((a, b) => { + const aScore = personasFilter.getScore(FILTER_TYPES.WORLD_INFO_SEARCH, a); + const bScore = personasFilter.getScore(FILTER_TYPES.WORLD_INFO_SEARCH, b); + return (aScore - bScore); + }); + } else { + personas.sort((a, b) => { + const aName = String(power_user.personas[a] || a); + const bName = String(power_user.personas[b] || b); + return power_user.persona_sort_order === 'asc' ? aName.localeCompare(bName) : bName.localeCompare(aName); + }); + } + + return personas; +} + +/** Checks the state of the current search, and adds/removes the search sorting option accordingly */ +function verifyPersonaSearchSortRule() { + const searchTerm = personasFilter.getFilterData(FILTER_TYPES.PERSONA_SEARCH); + const searchOption = $('#persona_sort_order option[value="search"]'); + const selector = $('#persona_sort_order'); + const isHidden = searchOption.attr('hidden') !== undefined; + + // If we have a search term, we are displaying the sorting option for it + if (searchTerm && isHidden) { + searchOption.removeAttr('hidden'); + selector.val(searchOption.attr('value')); + flashHighlight(selector); + } + // If search got cleared, we make sure to hide the option and go back to the one before + if (!searchTerm && !isHidden) { + searchOption.attr('hidden', ''); + selector.val(power_user.persona_sort_order); + } +} + /** * Gets a rendered avatar block. * @param {string} name Avatar file name @@ -8698,7 +8741,7 @@ jQuery(async function () { personasFilter.setFilterData(FILTER_TYPES.PERSONA_SEARCH, searchQuery); }); $('#persona_search_bar').on('input', function () { - const searchQuery = String($(this).val()).toLowerCase(); + const searchQuery = String($(this).val()); debouncedPersonaSearch(searchQuery); }); diff --git a/public/scripts/filters.js b/public/scripts/filters.js index b1460288e..5db6b9067 100644 --- a/public/scripts/filters.js +++ b/public/scripts/filters.js @@ -151,9 +151,7 @@ export class FilterHelper { } const fuzzySearchResults = fuzzySearchWorldInfo(data, term); - - var wiScoreMap = new Map(fuzzySearchResults.map(i => [i.item?.uid, i.score])); - this.cacheScores(FILTER_TYPES.WORLD_INFO_SEARCH, wiScoreMap); + this.cacheScores(FILTER_TYPES.WORLD_INFO_SEARCH, new Map(fuzzySearchResults.map(i => [i.item?.uid, i.score]))); const filteredData = data.filter(entity => fuzzySearchResults.find(x => x.item === entity)); return filteredData; @@ -172,7 +170,10 @@ export class FilterHelper { } const fuzzySearchResults = fuzzySearchPersonas(data, term); - return data.filter(entity => fuzzySearchResults.includes(entity)); + this.cacheScores(FILTER_TYPES.PERSONA_SEARCH, new Map(fuzzySearchResults.map(i => [i.item.key, i.score]))); + + const filteredData = data.filter(name => fuzzySearchResults.find(x => x.item.key === name)); + return filteredData; } /** @@ -362,7 +363,7 @@ export class FilterHelper { typeScores.set(uid, score); } this.scoreCache.set(type, typeScores); - console.debug('scores chached', type, typeScores); + console.debug('search scores chached', type, typeScores); } /** diff --git a/public/scripts/personas.js b/public/scripts/personas.js index e25190b29..bb5ab7209 100644 --- a/public/scripts/personas.js +++ b/public/scripts/personas.js @@ -635,7 +635,9 @@ export function initPersonas() { $('#personas_restore').on('click', () => $('#personas_restore_input').trigger('click')); $('#personas_restore_input').on('change', onPersonasRestoreInput); $('#persona_sort_order').val(power_user.persona_sort_order).on('input', function () { - power_user.persona_sort_order = String($(this).val()); + const value = String($(this).val()); + // Save sort order, but do not save search sorting, as this is a temporary sorting option + if (value !== 'search') power_user.persona_sort_order = value; getUserAvatars(true, user_avatar); saveSettingsDebounced(); }); diff --git a/public/scripts/power-user.js b/public/scripts/power-user.js index 6afd45192..4afd73f99 100644 --- a/public/scripts/power-user.js +++ b/public/scripts/power-user.js @@ -1912,8 +1912,14 @@ export function fuzzySearchWorldInfo(data, searchValue) { return results; } +/** + * Fuzzy search persona entries by a search term + * @param {*[]} data - persona data array + * @param {string} searchValue - The search term + * @returns {{item?: *, refIndex: number, score: number}[]} Results as items with their score + */ export function fuzzySearchPersonas(data, searchValue) { - data = data.map(x => ({ key: x, description: power_user.persona_descriptions[x]?.description ?? '', name: power_user.personas[x] ?? '' })); + data = data.map(x => ({ key: x, name: power_user.personas[x] ?? '', description: power_user.persona_descriptions[x]?.description ?? '' })); // @ts-ignore const fuse = new Fuse(data, { keys: [ @@ -1927,7 +1933,7 @@ export function fuzzySearchPersonas(data, searchValue) { const results = fuse.search(searchValue); console.debug('Personas fuzzy search results for ' + searchValue, results); - return results.map(x => x.item?.key); + return results; } export function fuzzySearchTags(searchValue) { diff --git a/public/scripts/world-info.js b/public/scripts/world-info.js index bf5f60bb5..11feb3b9d 100644 --- a/public/scripts/world-info.js +++ b/public/scripts/world-info.js @@ -681,7 +681,7 @@ function sortEntries(data) { if (!data.length) return data; // If we have a search term for WI, we are sorting by weighting scores - if ('search') { + if (sortRule === 'search') { data.sort((a, b) => { const aScore = worldInfoFilter.getScore(FILTER_TYPES.WORLD_INFO_SEARCH, a.uid); const bScore = worldInfoFilter.getScore(FILTER_TYPES.WORLD_INFO_SEARCH, b.uid); @@ -767,7 +767,7 @@ function displayWorldEntries(name, data, navigation = navigation_option.none) { } // Before printing the WI, we check if we should enable/disable search sorting - verifySearchSortRule(); + verifyWorldInfoSearchSortRule(); function getDataArray(callback) { // Convert the data.entries object into an array @@ -1011,13 +1011,13 @@ const originalDataKeyMap = { }; /** Checks the state of the current search, and adds/removes the search sorting option accordingly */ -function verifySearchSortRule() { +function verifyWorldInfoSearchSortRule() { const searchTerm = worldInfoFilter.getFilterData(FILTER_TYPES.WORLD_INFO_SEARCH); const searchOption = $('#world_info_sort_order option[data-rule="search"]'); const selector = $('#world_info_sort_order'); const isHidden = searchOption.attr('hidden') !== undefined; - // If we have a search term for WI, we are displaying the sorting option for it + // If we have a search term, we are displaying the sorting option for it if (searchTerm && isHidden) { searchOption.removeAttr('hidden'); selector.val(searchOption.attr('value') || '0'); @@ -3088,9 +3088,7 @@ jQuery(() => { $('#world_info_sort_order').on('change', function () { const value = String($(this).find(':selected').val()); // Save sort order, but do not save search sorting, as this is a temporary sorting option - if (value !== 'search') { - localStorage.setItem(SORT_ORDER_KEY, value); - } + if (value !== 'search') localStorage.setItem(SORT_ORDER_KEY, value); updateEditor(navigation_option.none); });