diff --git a/public/css/mobile-styles.css b/public/css/mobile-styles.css index 1db0d9426..a53758a8f 100644 --- a/public/css/mobile-styles.css +++ b/public/css/mobile-styles.css @@ -63,15 +63,6 @@ display: none; } - /* #world_popup_header { - flex-direction: column; - align-items: flex-start; - } */ - - #world_popup_header .world_popup_expander { - display: none; - } - body { touch-action: none; overflow: hidden; @@ -416,4 +407,4 @@ #horde_model { height: unset; } -} \ No newline at end of file +} diff --git a/public/css/world-info.css b/public/css/world-info.css index d747d90b8..425b4f83b 100644 --- a/public/css/world-info.css +++ b/public/css/world-info.css @@ -41,39 +41,12 @@ user-select: none; } -.world_popup_logo_block { - display: flex; - align-items: center; -} - -#world_popup_header { - display: flex; - flex-direction: row; - align-items: center; -} - -#world_popup_header h3 { - margin: 0; -} - #form_rename_world { display: flex; align-items: center; gap: 5px; } -#form_rename_world input[type="submit"] { - cursor: pointer; -} - -#form_world_import { - display: none; -} - -#world_popup_header h5 { - display: inline-block; -} - .world_popup_expander { flex-grow: 1; } @@ -89,8 +62,8 @@ } #world_popup_entries_list:empty::before { - content: 'No entries exist. Try creating one!'; - font-size: calc(var(--mainFontSize) + .5rem); + content: 'No entries found.'; + font-size: calc(var(--mainFontSize) + .1rem); font-weight: bolder; width: 100%; height: 100%; @@ -183,3 +156,7 @@ white-space: nowrap; width: 10em; } + +#world_info_search { + width: 10em; +} diff --git a/public/index.html b/public/index.html index 252fc114f..59ea701aa 100644 --- a/public/index.html +++ b/public/index.html @@ -2506,35 +2506,25 @@ <div id="world_popup"> <hr> - <div id="world_popup_text"> - <div id="world_popup_header" class="flex-container flexGap5"> - <div class="world_popup_logo_block"> - <h3 data-i18n="Editor"> - Editor - <a href="https://docs.sillytavern.app/usage/core-concepts/worldinfo/#world-info-entry" class="notes-link" target="_blank"><span class="note-link-span">?</span></a> - </h3> - </div> - <div id="OpenAllWIEntries" class="menu_button fa-solid fa-expand" title="Open all Entries" data-i18n="[title]Open all Entries"></div> - <div id="CloseAllWIEntries" class="menu_button fa-solid fa-compress" title="Close all Entries" data-i18n="[title]Close all Entries"></div> - <div id="world_popup_new" class="menu_button fa-solid fa-plus" title="New Entry" data-i18n="[title]New Entry"></div> - <div class="flex-container"> - <form id="form_world_import" action="javascript:void(null);" method="post" enctype="multipart/form-data"> - <input type="file" id="world_import_file" accept=".json,.lorebook,.png" name="avatar" hidden> - </form> - <form id="form_rename_world" action="javascript:void(null);" method="post" enctype="multipart/form-data"> - <div id="world_create_button" class="menu_button fa-solid fa-globe fa-fw" title="Create" data-i18n="[title]Create"></div> - <div id="world_import_button" class="menu_button fa-solid fa-file-import fa-fw" title="Import World Info" data-i18n="[title]Import World Info"></div> - <div id="world_popup_export" class="menu_button fa-solid fa-file-export margin0 fa-fw" title="Export World Info" data-i18n="[title]Export World Info"></div> - <div id="world_popup_delete" class="menu_button fa-solid fa-trash-can redWarningBG margin0 fa-fw" title="Delete World Info" data-i18n="[title]Delete World Info"></div> - <small data-i18n="Editing:"> Editing:</small> - <select id="world_editor_select" class="margin0"> - <option value="" data-i18n="--- None ---">--- None ---</option> - </select> - <div id="world_popup_name_button" class="menu_button fa-pencil fa-solid" title="Rename World Info" data-i18n="[title]Rename World Info"></div> - <div id="world_info_pagination"></div> - </form> - </div> + <div class="flex-container alignitemscenter"> + <input type="file" id="world_import_file" accept=".json,.lorebook,.png" name="avatar" hidden> + <div id="world_create_button" class="menu_button menu_button_icon"> + <i class="fa-solid fa-globe"></i> + <span data-i18n="Create">Create</span> </div> + <small data-i18n="or">or</small> + <select id="world_editor_select" class="margin0"> + <option value="" data-i18n="--- Pick to Edit ---">--- Pick to Edit ---</option> + </select> + <div id="world_popup_name_button" class="menu_button fa-pencil fa-solid" title="Rename World Info" data-i18n="[title]Rename World Info"></div> + <div id="OpenAllWIEntries" class="menu_button fa-solid fa-expand" title="Open all Entries" data-i18n="[title]Open all Entries"></div> + <div id="CloseAllWIEntries" class="menu_button fa-solid fa-compress" title="Close all Entries" data-i18n="[title]Close all Entries"></div> + <div id="world_popup_new" class="menu_button fa-solid fa-plus" title="New Entry" data-i18n="[title]New Entry"></div> + <div id="world_import_button" class="menu_button fa-solid fa-file-import" title="Import World Info" data-i18n="[title]Import World Info"></div> + <div id="world_popup_export" class="menu_button fa-solid fa-file-export" title="Export World Info" data-i18n="[title]Export World Info"></div> + <div id="world_popup_delete" class="menu_button fa-solid fa-trash-can redWarningBG" title="Delete World Info" data-i18n="[title]Delete World Info"></div> + <input type="search" class="text_pole textarea_compact" data-i18n="[placeholder]Search..." id="world_info_search" placeholder="Search..."> + <div id="world_info_pagination"></div> </div> <div id="world_popup_entries_list"> @@ -4341,4 +4331,4 @@ </script> </body> -</html> \ No newline at end of file +</html> diff --git a/public/scripts/filters.js b/public/scripts/filters.js index fae9ef8dc..d0c5a7aeb 100644 --- a/public/scripts/filters.js +++ b/public/scripts/filters.js @@ -1,4 +1,4 @@ -import { fuzzySearchCharacters, fuzzySearchGroups, power_user } from "./power-user.js"; +import { fuzzySearchCharacters, fuzzySearchGroups, fuzzySearchWorldInfo, power_user } from "./power-user.js"; import { tag_map } from "./tags.js"; export const FILTER_TYPES = { @@ -6,6 +6,7 @@ export const FILTER_TYPES = { TAG: 'tag', FAV: 'fav', GROUP: 'group', + WORLD_INFO_SEARCH: 'world_info_search', }; export class FilterHelper { @@ -18,6 +19,7 @@ export class FilterHelper { [FILTER_TYPES.GROUP]: this.groupFilter.bind(this), [FILTER_TYPES.FAV]: this.favFilter.bind(this), [FILTER_TYPES.TAG]: this.tagFilter.bind(this), + [FILTER_TYPES.WORLD_INFO_SEARCH]: this.wiSearchFilter.bind(this), } filterData = { @@ -25,6 +27,18 @@ export class FilterHelper { [FILTER_TYPES.GROUP]: false, [FILTER_TYPES.FAV]: false, [FILTER_TYPES.TAG]: { excluded: [], selected: [] }, + [FILTER_TYPES.WORLD_INFO_SEARCH]: '', + } + + wiSearchFilter(data) { + const term = this.filterData[FILTER_TYPES.WORLD_INFO_SEARCH]; + + if (!term) { + return data; + } + + const fuzzySearchResults = fuzzySearchWorldInfo(data, term); + return data.filter(entity => fuzzySearchResults.includes(entity.uid)); } tagFilter(data) { @@ -108,12 +122,12 @@ export class FilterHelper { return data.filter(entity => getIsValidSearch(entity)); } - setFilterData(filterType, data) { + setFilterData(filterType, data, suppressDataChanged = false) { const oldData = this.filterData[filterType]; this.filterData[filterType] = data; // only trigger a data change if the data actually changed - if (JSON.stringify(oldData) !== JSON.stringify(data)) { + if (JSON.stringify(oldData) !== JSON.stringify(data) && !suppressDataChanged) { this.onDataChanged(); } } diff --git a/public/scripts/power-user.js b/public/scripts/power-user.js index 4cf06bbe1..3d4832bb1 100644 --- a/public/scripts/power-user.js +++ b/public/scripts/power-user.js @@ -947,6 +947,25 @@ export function fuzzySearchCharacters(searchValue) { return indices; } +export function fuzzySearchWorldInfo(data, searchValue) { + const fuse = new Fuse(data, { + keys: [ + { name: 'key', weight: 3 }, + { name: 'content', weight: 3 }, + { name: 'comment', weight: 2 }, + { name: 'keysecondary', weight: 2 }, + { name: 'uid', weight: 1 }, + ], + includeScore: true, + ignoreLocation: true, + threshold: 0.2, + }); + + const results = fuse.search(searchValue); + console.debug('World Info fuzzy search results for ' + searchValue, results); + return results.map(x => x.item?.uid); +} + export function fuzzySearchGroups(searchValue) { const fuse = new Fuse(groups, { keys: [ diff --git a/public/scripts/world-info.js b/public/scripts/world-info.js index c39456435..347045913 100644 --- a/public/scripts/world-info.js +++ b/public/scripts/world-info.js @@ -4,6 +4,7 @@ import { getContext } from "./extensions.js"; import { NOTE_MODULE_NAME, metadata_keys, shouldWIAddPrompt } from "./authors-note.js"; import { registerSlashCommand } from "./slash-commands.js"; import { deviceInfo } from "./RossAscends-mods.js"; +import { FILTER_TYPES, FilterHelper } from "./filters.js"; export { world_info, @@ -48,6 +49,9 @@ const sortFn = (a, b) => b.order - a.order; const navigation_option = { none: 0, previous: 1, last: 2, }; let updateEditor = (navigation) => { navigation; }; +// Do not optimize. updateEditor is a function that is updated by the displayWorldEntries with new data. +const worldInfoFilter = new FilterHelper(() => updateEditor()); + export function getWorldInfoSettings() { return { world_info, @@ -236,13 +240,13 @@ function displayWorldEntries(name, data, navigation = navigation_option.none) { $("#world_popup_export").off('click').on('click', nullWorldInfo); $("#world_popup_delete").off('click').on('click', nullWorldInfo); $("#world_popup_entries_list").hide(); - $("#world_info_pagination").pagination('destroy'); + $('#world_info_pagination').html(''); return; } function getDataArray(callback) { // Convert the data.entries object into an array - const entriesArray = Object.keys(data.entries).map(uid => { + let entriesArray = Object.keys(data.entries).map(uid => { const entry = data.entries[uid]; entry.displayIndex = entry.displayIndex ?? entry.uid; return entry; @@ -250,7 +254,9 @@ function displayWorldEntries(name, data, navigation = navigation_option.none) { // Sort the entries array by displayIndex and uid entriesArray.sort((a, b) => a.displayIndex - b.displayIndex || a.uid - b.uid); + entriesArray = worldInfoFilter.applyFilters(entriesArray); callback(entriesArray); + return entriesArray; } let startPage = 1; @@ -262,13 +268,14 @@ function displayWorldEntries(name, data, navigation = navigation_option.none) { const storageKey = 'WI_PerPage'; $("#world_info_pagination").pagination({ dataSource: getDataArray, - pageSize: Number(localStorage.getItem(storageKey)) || 10, - sizeChangerOptions: [10, 25, 50, 100], + pageSize: 25, + //pageSize: Number(localStorage.getItem(storageKey)) || 25, + //sizeChangerOptions: [10, 25, 50, 100], + //showSizeChanger: true, pageRange: 1, pageNumber: startPage, position: 'top', showPageNumbers: false, - showSizeChanger: true, prevText: '<', nextText: '>', formatNavigator: PAGINATION_TEMPLATE, @@ -1526,7 +1533,7 @@ jQuery(() => { await importWorldInfo(file); // Will allow to select the same file twice in a row - $("#form_world_import").trigger("reset"); + e.target.value = ''; }); $("#world_create_button").on('click', async () => { @@ -1539,6 +1546,8 @@ jQuery(() => { }); $("#world_editor_select").on('change', async () => { + $("#world_info_search").val(''); + worldInfoFilter.setFilterData(FILTER_TYPES.WORLD_INFO_SEARCH, '', true); const selectedIndex = $("#world_editor_select").find(":selected").val(); if (selectedIndex === "") { @@ -1619,23 +1628,10 @@ jQuery(() => { } }); - /* - $("#world_info").on('mousewheel', function (e) { - e.preventDefault(); - if ($(this).is(':animated')) { - return; //dont force multiple scroll animations - } - var wheelDelta = e.originalEvent.wheelDelta.toFixed(0); - var DeltaPosNeg = (wheelDelta >= 0) ? 1 : -1; //determine if scrolling up or down - var containerHeight = $(this).height().toFixed(0); - var optionHeight = $(this).find('option').first().height().toFixed(0); - var visibleOptions = (containerHeight / optionHeight).toFixed(0); //how many options we can see - var pixelsToScroll = (optionHeight * visibleOptions * DeltaPosNeg).toFixed(0); //scroll a full container height - var scrollTop = ($(this).scrollTop() - pixelsToScroll).toFixed(0); - - $(this).animate({ scrollTop: scrollTop }, 200); + $('#world_info_search').on('input', function () { + const term = $(this).val(); + worldInfoFilter.setFilterData(FILTER_TYPES.WORLD_INFO_SEARCH, term); }); - */ // Not needed on mobile if (deviceInfo.device.type === 'desktop') { diff --git a/public/style.css b/public/style.css index 8a6c26fd0..a69baa51d 100644 --- a/public/style.css +++ b/public/style.css @@ -1358,6 +1358,7 @@ input[type=search]::-webkit-search-cancel-button { border-radius: 50em; background: url('/img/times-circle.svg') no-repeat 50% 50%; background-size: contain; + backdrop-filter: invert(1) contrast(9); opacity: 0; pointer-events: none; cursor: pointer; @@ -3007,6 +3008,10 @@ a { gap: 5px; } +.menu_button_icon span { + font-size: calc(var(--mainFontSize) * 0.9); +} + /*------------ TOP SIDE SETTINGS ----------------*/ #top-settings-holder { @@ -3159,6 +3164,7 @@ a { .drawer-content select { width: 100%; + font-size: calc(var(--mainFontSize) * 0.9); } .settingsSectionWrap {