Handle stale cache
| @@ -330,12 +330,6 @@ const debug_functions = []; | |||
| 330 | 330 | ||
| 331 | const setHotswapsDebounced = debounce(favsToHotswap); | 331 | const setHotswapsDebounced = debounce(favsToHotswap); |
| 332 | 332 | ||
| 333 | const fuzzySearchCharactersCache = new Map(); | ||
| 334 | const fuzzySearchWorldInfoCache = new Map(); | ||
| 335 | const fuzzySearchPersonasCache = new Map(); | ||
| 336 | const fuzzySearchTagsCache = new Map(); | ||
| 337 | const fuzzySearchGroupsCache = new Map(); | ||
| 338 | |||
| 339 | function playMessageSound() { | 333 | function playMessageSound() { |
| 340 | if (!power_user.play_message_sound) { | 334 | if (!power_user.play_message_sound) { |
| 341 | return; | 335 | return; |
| @@ -1830,32 +1824,70 @@ async function loadContextSettings() { | |||
| 1830 | }); | 1824 | }); |
| 1831 | } | 1825 | } |
| 1832 | 1826 | ||
| 1827 | // Create separate caches for each type of fuzzy search | ||
| 1828 | const fuzzySearchCaches = { | ||
| 1829 | characters: { keyHash: null, resultMap: new Map() }, | ||
| 1830 | worldInfo: { keyHash: null, resultMap: new Map() }, | ||
| 1831 | personas: { keyHash: null, resultMap: new Map() }, | ||
| 1832 | tags: { keyHash: null, resultMap: new Map() }, | ||
| 1833 | groups: { keyHash: null, resultMap: new Map() }, | ||
| 1834 | }; | ||
| 1835 | |||
| 1836 | /** | ||
| 1837 | * Generates a hash of the Fuse configuration keys | ||
| 1838 | * @param {Array<{name: string, weight: number, getFn?: Function}>} keys | ||
| 1839 | * @returns {number} Hash of the keys configuration | ||
| 1840 | */ | ||
| 1841 | function hashFuseKeys(keys) { | ||
| 1842 | // Convert keys to a stable string representation | ||
| 1843 | const keyString = keys.map(k => { | ||
| 1844 | const getFnString = k.getFn ? k.getFn.toString() : ''; | ||
| 1845 | return `${k.name}:${k.weight}:${getFnString}`; | ||
| 1846 | }).join('|'); | ||
| 1847 | |||
| 1848 | return getStringHash(keyString); | ||
| 1849 | } | ||
| 1850 | |||
| 1833 | /** | 1851 | /** |
| 1834 | * Fuzzy search characters by a search term | 1852 | * Fuzzy search characters by a search term with caching |
| 1835 | * @param {string} searchValue - The search term | 1853 | * @param {string} searchValue - The search term |
| 1836 | * @returns {import('fuse.js').FuseResult<any>[]} Results as items with their score | 1854 | * @returns {import('fuse.js').FuseResult<any>[]} Results as items with their score |
| 1837 | */ | 1855 | */ |
| 1838 | export function fuzzySearchCharacters(searchValue) { | 1856 | export function fuzzySearchCharacters(searchValue) { |
| 1857 | const fuseKeys = [ | ||
| 1858 | { name: 'data.name', weight: 20 }, | ||
| 1859 | { name: '#tags', weight: 10, getFn: (character) => getTagsList(character.avatar).map(x => x.name).join('||') }, | ||
| 1860 | { name: 'data.description', weight: 3 }, | ||
| 1861 | { name: 'data.mes_example', weight: 3 }, | ||
| 1862 | { name: 'data.scenario', weight: 2 }, | ||
| 1863 | { name: 'data.personality', weight: 2 }, | ||
| 1864 | { name: 'data.first_mes', weight: 2 }, | ||
| 1865 | { name: 'data.creator_notes', weight: 2 }, | ||
| 1866 | { name: 'data.creator', weight: 1 }, | ||
| 1867 | { name: 'data.tags', weight: 1 }, | ||
| 1868 | { name: 'data.alternate_greetings', weight: 1 }, | ||
| 1869 | ]; | ||
| 1870 | |||
| 1871 | // Generate hash of current keys configuration | ||
| 1872 | const currentKeyHash = hashFuseKeys(fuseKeys); | ||
| 1873 | const cache = fuzzySearchCaches.characters; | ||
| 1839 | 1874 | ||
| 1840 | if (fuzzySearchCharactersCache.has(searchValue)) { | 1875 | // Check if we have a cached result for this search value |
| 1841 | return fuzzySearchCharactersCache.get(searchValue); | 1876 | if (cache.keyHash === currentKeyHash && cache.resultMap.has(searchValue)) { |
| 1877 | console.debug('Using cached fuzzy search results for ' + searchValue); | ||
| 1878 | return cache.resultMap.get(searchValue); | ||
| 1842 | } | 1879 | } |
| 1843 | 1880 | ||
| 1844 | // @ts-ignore | 1881 | // If key configuration changed, clear the cache |
| 1882 | if (cache.keyHash !== currentKeyHash) { | ||
| 1883 | console.debug('Fuse keys changed, clearing cache'); | ||
| 1884 | cache.keyHash = currentKeyHash; | ||
| 1885 | cache.resultMap.clear(); | ||
| 1886 | } | ||
| 1887 | |||
| 1888 | // Create new Fuse instance and perform search | ||
| 1845 | const fuse = new Fuse(characters, { | 1889 | const fuse = new Fuse(characters, { |
| 1846 | keys: [ | 1890 | keys: fuseKeys, |
| 1847 | { name: 'data.name', weight: 20 }, | ||
| 1848 | { name: '#tags', weight: 10, getFn: (character) => getTagsList(character.avatar).map(x => x.name).join('||') }, | ||
| 1849 | { name: 'data.description', weight: 3 }, | ||
| 1850 | { name: 'data.mes_example', weight: 3 }, | ||
| 1851 | { name: 'data.scenario', weight: 2 }, | ||
| 1852 | { name: 'data.personality', weight: 2 }, | ||
| 1853 | { name: 'data.first_mes', weight: 2 }, | ||
| 1854 | { name: 'data.creator_notes', weight: 2 }, | ||
| 1855 | { name: 'data.creator', weight: 1 }, | ||
| 1856 | { name: 'data.tags', weight: 1 }, | ||
| 1857 | { name: 'data.alternate_greetings', weight: 1 }, | ||
| 1858 | ], | ||
| 1859 | includeScore: true, | 1891 | includeScore: true, |
| 1860 | ignoreLocation: true, | 1892 | ignoreLocation: true, |
| 1861 | useExtendedSearch: true, | 1893 | useExtendedSearch: true, |
| @@ -1863,8 +1895,11 @@ export function fuzzySearchCharacters(searchValue) { | |||
| 1863 | }); | 1895 | }); |
| 1864 | 1896 | ||
| 1865 | const results = fuse.search(searchValue); | 1897 | const results = fuse.search(searchValue); |
| 1898 | |||
| 1899 | // Cache the results | ||
| 1900 | cache.resultMap.set(searchValue, results); | ||
| 1901 | |||
| 1866 | console.debug('Characters fuzzy search results for ' + searchValue, results); | 1902 | console.debug('Characters fuzzy search results for ' + searchValue, results); |
| 1867 | fuzzySearchCharactersCache.set(searchValue, results); | ||
| 1868 | return results; | 1903 | return results; |
| 1869 | } | 1904 | } |
| 1870 | 1905 | ||
| @@ -1875,21 +1910,34 @@ export function fuzzySearchCharacters(searchValue) { | |||
| 1875 | * @returns {import('fuse.js').FuseResult<any>[]} Results as items with their score | 1910 | * @returns {import('fuse.js').FuseResult<any>[]} Results as items with their score |
| 1876 | */ | 1911 | */ |
| 1877 | export function fuzzySearchWorldInfo(data, searchValue) { | 1912 | export function fuzzySearchWorldInfo(data, searchValue) { |
| 1878 | if (fuzzySearchWorldInfoCache.has(searchValue)) { | 1913 | const fuseKeys = [ |
| 1879 | return fuzzySearchWorldInfoCache.get(searchValue); | 1914 | { name: 'key', weight: 20 }, |
| 1915 | { name: 'group', weight: 15 }, | ||
| 1916 | { name: 'comment', weight: 10 }, | ||
| 1917 | { name: 'keysecondary', weight: 10 }, | ||
| 1918 | { name: 'content', weight: 3 }, | ||
| 1919 | { name: 'uid', weight: 1 }, | ||
| 1920 | { name: 'automationId', weight: 1 }, | ||
| 1921 | ]; | ||
| 1922 | |||
| 1923 | const currentKeyHash = hashFuseKeys(fuseKeys); | ||
| 1924 | const cache = fuzzySearchCaches.worldInfo; | ||
| 1925 | |||
| 1926 | // Check cache for existing results | ||
| 1927 | if (cache.keyHash === currentKeyHash && cache.resultMap.has(searchValue)) { | ||
| 1928 | console.debug('Using cached WI fuzzy search results for ' + searchValue); | ||
| 1929 | return cache.resultMap.get(searchValue); | ||
| 1930 | } | ||
| 1931 | |||
| 1932 | // Clear cache if keys changed | ||
| 1933 | if (cache.keyHash !== currentKeyHash) { | ||
| 1934 | console.debug('WI Fuse keys changed, clearing cache'); | ||
| 1935 | cache.keyHash = currentKeyHash; | ||
| 1936 | cache.resultMap.clear(); | ||
| 1880 | } | 1937 | } |
| 1881 | 1938 | ||
| 1882 | // @ts-ignore | ||
| 1883 | const fuse = new Fuse(data, { | 1939 | const fuse = new Fuse(data, { |
| 1884 | keys: [ | 1940 | keys: fuseKeys, |
| 1885 | { name: 'key', weight: 20 }, | ||
| 1886 | { name: 'group', weight: 15 }, | ||
| 1887 | { name: 'comment', weight: 10 }, | ||
| 1888 | { name: 'keysecondary', weight: 10 }, | ||
| 1889 | { name: 'content', weight: 3 }, | ||
| 1890 | { name: 'uid', weight: 1 }, | ||
| 1891 | { name: 'automationId', weight: 1 }, | ||
| 1892 | ], | ||
| 1893 | includeScore: true, | 1941 | includeScore: true, |
| 1894 | ignoreLocation: true, | 1942 | ignoreLocation: true, |
| 1895 | useExtendedSearch: true, | 1943 | useExtendedSearch: true, |
| @@ -1897,8 +1945,9 @@ export function fuzzySearchWorldInfo(data, searchValue) { | |||
| 1897 | }); | 1945 | }); |
| 1898 | 1946 | ||
| 1899 | const results = fuse.search(searchValue); | 1947 | const results = fuse.search(searchValue); |
| 1948 | cache.resultMap.set(searchValue, results); | ||
| 1949 | |||
| 1900 | console.debug('World Info fuzzy search results for ' + searchValue, results); | 1950 | console.debug('World Info fuzzy search results for ' + searchValue, results); |
| 1901 | fuzzySearchWorldInfoCache.set(searchValue, results); | ||
| 1902 | return results; | 1951 | return results; |
| 1903 | } | 1952 | } |
| 1904 | 1953 | ||
| @@ -1909,17 +1958,31 @@ export function fuzzySearchWorldInfo(data, searchValue) { | |||
| 1909 | * @returns {import('fuse.js').FuseResult<any>[]} Results as items with their score | 1958 | * @returns {import('fuse.js').FuseResult<any>[]} Results as items with their score |
| 1910 | */ | 1959 | */ |
| 1911 | export function fuzzySearchPersonas(data, searchValue) { | 1960 | export function fuzzySearchPersonas(data, searchValue) { |
| 1912 | if (fuzzySearchPersonasCache.has(searchValue)) { | 1961 | data = data.map(x => ({ key: x, name: power_user.personas[x] ?? '', description: power_user.persona_descriptions[x]?.description ?? '' })); |
| 1913 | return fuzzySearchPersonasCache.get(searchValue); | 1962 | |
| 1963 | const fuseKeys = [ | ||
| 1964 | { name: 'name', weight: 20 }, | ||
| 1965 | { name: 'description', weight: 3 }, | ||
| 1966 | ]; | ||
| 1967 | |||
| 1968 | const currentKeyHash = hashFuseKeys(fuseKeys); | ||
| 1969 | const cache = fuzzySearchCaches.personas; | ||
| 1970 | |||
| 1971 | // Check cache for existing results | ||
| 1972 | if (cache.keyHash === currentKeyHash && cache.resultMap.has(searchValue)) { | ||
| 1973 | console.debug('Using cached personas fuzzy search results for ' + searchValue); | ||
| 1974 | return cache.resultMap.get(searchValue); | ||
| 1975 | } | ||
| 1976 | |||
| 1977 | // Clear cache if keys changed | ||
| 1978 | if (cache.keyHash !== currentKeyHash) { | ||
| 1979 | console.debug('Personas Fuse keys changed, clearing cache'); | ||
| 1980 | cache.keyHash = currentKeyHash; | ||
| 1981 | cache.resultMap.clear(); | ||
| 1914 | } | 1982 | } |
| 1915 | 1983 | ||
| 1916 | data = data.map(x => ({ key: x, name: power_user.personas[x] ?? '', description: power_user.persona_descriptions[x]?.description ?? '' })); | ||
| 1917 | // @ts-ignore | ||
| 1918 | const fuse = new Fuse(data, { | 1984 | const fuse = new Fuse(data, { |
| 1919 | keys: [ | 1985 | keys: fuseKeys, |
| 1920 | { name: 'name', weight: 20 }, | ||
| 1921 | { name: 'description', weight: 3 }, | ||
| 1922 | ], | ||
| 1923 | includeScore: true, | 1986 | includeScore: true, |
| 1924 | ignoreLocation: true, | 1987 | ignoreLocation: true, |
| 1925 | useExtendedSearch: true, | 1988 | useExtendedSearch: true, |
| @@ -1927,8 +1990,9 @@ export function fuzzySearchPersonas(data, searchValue) { | |||
| 1927 | }); | 1990 | }); |
| 1928 | 1991 | ||
| 1929 | const results = fuse.search(searchValue); | 1992 | const results = fuse.search(searchValue); |
| 1993 | cache.resultMap.set(searchValue, results); | ||
| 1994 | |||
| 1930 | console.debug('Personas fuzzy search results for ' + searchValue, results); | 1995 | console.debug('Personas fuzzy search results for ' + searchValue, results); |
| 1931 | fuzzySearchPersonasCache.set(searchValue, results); | ||
| 1932 | return results; | 1996 | return results; |
| 1933 | } | 1997 | } |
| 1934 | 1998 | ||
| @@ -1938,15 +2002,28 @@ export function fuzzySearchPersonas(data, searchValue) { | |||
| 1938 | * @returns {import('fuse.js').FuseResult<any>[]} Results as items with their score | 2002 | * @returns {import('fuse.js').FuseResult<any>[]} Results as items with their score |
| 1939 | */ | 2003 | */ |
| 1940 | export function fuzzySearchTags(searchValue) { | 2004 | export function fuzzySearchTags(searchValue) { |
| 1941 | if (fuzzySearchTagsCache.has(searchValue)) { | 2005 | const fuseKeys = [ |
| 1942 | return fuzzySearchTagsCache.get(searchValue); | 2006 | { name: 'name', weight: 1 }, |
| 2007 | ]; | ||
| 2008 | |||
| 2009 | const currentKeyHash = hashFuseKeys(fuseKeys); | ||
| 2010 | const cache = fuzzySearchCaches.tags; | ||
| 2011 | |||
| 2012 | // Check cache for existing results | ||
| 2013 | if (cache.keyHash === currentKeyHash && cache.resultMap.has(searchValue)) { | ||
| 2014 | console.debug('Using cached tags fuzzy search results for ' + searchValue); | ||
| 2015 | return cache.resultMap.get(searchValue); | ||
| 2016 | } | ||
| 2017 | |||
| 2018 | // Clear cache if keys changed | ||
| 2019 | if (cache.keyHash !== currentKeyHash) { | ||
| 2020 | console.debug('Tags Fuse keys changed, clearing cache'); | ||
| 2021 | cache.keyHash = currentKeyHash; | ||
| 2022 | cache.resultMap.clear(); | ||
| 1943 | } | 2023 | } |
| 1944 | 2024 | ||
| 1945 | // @ts-ignore | ||
| 1946 | const fuse = new Fuse(tags, { | 2025 | const fuse = new Fuse(tags, { |
| 1947 | keys: [ | 2026 | keys: fuseKeys, |
| 1948 | { name: 'name', weight: 1 }, | ||
| 1949 | ], | ||
| 1950 | includeScore: true, | 2027 | includeScore: true, |
| 1951 | ignoreLocation: true, | 2028 | ignoreLocation: true, |
| 1952 | useExtendedSearch: true, | 2029 | useExtendedSearch: true, |
| @@ -1954,8 +2031,9 @@ export function fuzzySearchTags(searchValue) { | |||
| 1954 | }); | 2031 | }); |
| 1955 | 2032 | ||
| 1956 | const results = fuse.search(searchValue); | 2033 | const results = fuse.search(searchValue); |
| 2034 | cache.resultMap.set(searchValue, results); | ||
| 2035 | |||
| 1957 | console.debug('Tags fuzzy search results for ' + searchValue, results); | 2036 | console.debug('Tags fuzzy search results for ' + searchValue, results); |
| 1958 | fuzzySearchTagsCache.set(searchValue, results); | ||
| 1959 | return results; | 2037 | return results; |
| 1960 | } | 2038 | } |
| 1961 | 2039 | ||
| @@ -1965,18 +2043,31 @@ export function fuzzySearchTags(searchValue) { | |||
| 1965 | * @returns {import('fuse.js').FuseResult<any>[]} Results as items with their score | 2043 | * @returns {import('fuse.js').FuseResult<any>[]} Results as items with their score |
| 1966 | */ | 2044 | */ |
| 1967 | export function fuzzySearchGroups(searchValue) { | 2045 | export function fuzzySearchGroups(searchValue) { |
| 1968 | if (fuzzySearchGroupsCache.has(searchValue)) { | 2046 | const fuseKeys = [ |
| 1969 | return fuzzySearchGroupsCache.get(searchValue); | 2047 | { name: 'name', weight: 20 }, |
| 2048 | { name: 'members', weight: 15 }, | ||
| 2049 | { name: '#tags', weight: 10, getFn: (group) => getTagsList(group.id).map(x => x.name).join('||') }, | ||
| 2050 | { name: 'id', weight: 1 }, | ||
| 2051 | ]; | ||
| 2052 | |||
| 2053 | const currentKeyHash = hashFuseKeys(fuseKeys); | ||
| 2054 | const cache = fuzzySearchCaches.groups; | ||
| 2055 | |||
| 2056 | // Check cache for existing results | ||
| 2057 | if (cache.keyHash === currentKeyHash && cache.resultMap.has(searchValue)) { | ||
| 2058 | console.debug('Using cached groups fuzzy search results for ' + searchValue); | ||
| 2059 | return cache.resultMap.get(searchValue); | ||
| 2060 | } | ||
| 2061 | |||
| 2062 | // Clear cache if keys changed | ||
| 2063 | if (cache.keyHash !== currentKeyHash) { | ||
| 2064 | console.debug('Groups Fuse keys changed, clearing cache'); | ||
| 2065 | cache.keyHash = currentKeyHash; | ||
| 2066 | cache.resultMap.clear(); | ||
| 1970 | } | 2067 | } |
| 1971 | 2068 | ||
| 1972 | // @ts-ignore | ||
| 1973 | const fuse = new Fuse(groups, { | 2069 | const fuse = new Fuse(groups, { |
| 1974 | keys: [ | 2070 | keys: fuseKeys, |
| 1975 | { name: 'name', weight: 20 }, | ||
| 1976 | { name: 'members', weight: 15 }, | ||
| 1977 | { name: '#tags', weight: 10, getFn: (group) => getTagsList(group.id).map(x => x.name).join('||') }, | ||
| 1978 | { name: 'id', weight: 1 }, | ||
| 1979 | ], | ||
| 1980 | includeScore: true, | 2071 | includeScore: true, |
| 1981 | ignoreLocation: true, | 2072 | ignoreLocation: true, |
| 1982 | useExtendedSearch: true, | 2073 | useExtendedSearch: true, |
| @@ -1984,12 +2075,23 @@ export function fuzzySearchGroups(searchValue) { | |||
| 1984 | }); | 2075 | }); |
| 1985 | 2076 | ||
| 1986 | const results = fuse.search(searchValue); | 2077 | const results = fuse.search(searchValue); |
| 2078 | cache.resultMap.set(searchValue, results); | ||
| 2079 | |||
| 1987 | console.debug('Groups fuzzy search results for ' + searchValue, results); | 2080 | console.debug('Groups fuzzy search results for ' + searchValue, results); |
| 1988 | fuzzySearchGroupsCache.set(searchValue, results); | ||
| 1989 | return results; | 2081 | return results; |
| 1990 | } | 2082 | } |
| 1991 | 2083 | ||
| 1992 | /** | 2084 | /** |
| 2085 | * Clears all fuzzy search caches | ||
| 2086 | */ | ||
| 2087 | export function clearFuzzySearchCaches() { | ||
| 2088 | for (const cache of Object.values(fuzzySearchCaches)) { | ||
| 2089 | cache.keyHash = null; | ||
| 2090 | cache.resultMap.clear(); | ||
| 2091 | } | ||
| 2092 | } | ||
| 2093 | |||
| 2094 | /** | ||
| 1993 | * Renders a story string template with the given parameters. | 2095 | * Renders a story string template with the given parameters. |
| 1994 | * @param {object} params Template parameters. | 2096 | * @param {object} params Template parameters. |
| 1995 | * @returns {string} The rendered story string. | 2097 | * @returns {string} The rendered story string. |