wip: upgrade vp, proper exact match

This commit is contained in:
taskylizard 2025-05-02 22:22:43 +00:00
parent 2606a39f8d
commit 588db44742
No known key found for this signature in database
GPG key ID: 1820131ED1A24120
2 changed files with 423 additions and 236 deletions

View file

@ -249,12 +249,12 @@ const searchIndex = computedAsync(async () =>
fields: ['title', 'titles', 'text'], fields: ['title', 'titles', 'text'],
storeFields: ['title', 'titles'], storeFields: ['title', 'titles'],
searchOptions: { searchOptions: {
// words of >=8 characters get an allowable edit distance of 1 // Conditional fuzzy search based on matchExact value
fuzzy: 0.07, fuzzy: matchExact.value ? false : 0.07,
// perform prefix search (i.e. "monk" matches "monkfish") only with terms greater than 3 chars // Conditional prefix search based on matchExact value
prefix: (term) => term.length > 3, prefix: matchExact.value ? false : (term) => term.length > 3,
// max edit distance of 1 no matter length of an individual term // Disable max fuzzy if match exact is enabled
maxFuzzy: 1, maxFuzzy: matchExact.value ? 0 : 1,
boost: { title: 4, text: 2, titles: 1 }, boost: { title: 4, text: 2, titles: 1 },
...(theme.value.search?.provider === 'local' && ...(theme.value.search?.provider === 'local' &&
theme.value.search.options?.miniSearch?.searchOptions) theme.value.search.options?.miniSearch?.searchOptions)
@ -283,9 +283,9 @@ const showDetailedList = useLocalStorage(
theme.value.search.options?.detailedView === true theme.value.search.options?.detailedView === true
) )
const useExactMatch = useLocalStorage( const matchExact = useLocalStorage(
'vitepress:local-search-exact-match', 'vitepress:local-search-match-exact',
false false // disabled by default
) )
const disableDetailedView = computed(() => { const disableDetailedView = computed(() => {
@ -333,13 +333,9 @@ debouncedWatch(
searchIndex.value, searchIndex.value,
filterText.value, filterText.value,
showDetailedList.value, showDetailedList.value,
useExactMatch.value matchExact.value
] as const, ] as const,
async ( async ([index, filterTextValue, showDetailedListValue], old, onCleanup) => {
[index, filterTextValue, showDetailedListValue, useExactMatchValue],
old,
onCleanup
) => {
if (old?.[0] !== index) { if (old?.[0] !== index) {
// in case of hmr // in case of hmr
cache.clear() cache.clear()
@ -353,23 +349,9 @@ debouncedWatch(
if (!index) return if (!index) return
// Search // Search
if (useExactMatchValue && filterTextValue.trim()) {
// Perform exact match search
results.value = index
.search(filterTextValue, {
fuzzy: 0, // Disable fuzzy matching
prefix: false, // Disable prefix matching
combineWith: 'AND', // Require all terms to match
...(theme.value.search?.provider === 'local' &&
theme.value.search.options?.miniSearch?.searchOptions)
})
.slice(0, 16) as (SearchResult & Result)[]
} else {
// Use default search behavior
results.value = index results.value = index
.search(filterTextValue) .search(filterTextValue)
.slice(0, 16) as (SearchResult & Result)[] .slice(0, 16) as (SearchResult & Result)[]
}
enableNoResults.value = true enableNoResults.value = true
// Highlighting // Highlighting
@ -441,9 +423,8 @@ debouncedWatch(
}) })
}) })
const excerpts = Array.from( const excerpts = el.value?.querySelectorAll('.result .excerpt') ?? []
resultsEl.value?.querySelectorAll('.result .excerpt') ?? [] // @ts-expect-error
)
for (const excerpt of excerpts) { for (const excerpt of excerpts) {
excerpt excerpt
.querySelector('mark[data-markjs="true"]') .querySelector('mark[data-markjs="true"]')
@ -555,7 +536,7 @@ const defaultTranslations: { modal: ModalTranslations } = {
resetButtonTitle: 'Reset search', resetButtonTitle: 'Reset search',
backButtonTitle: 'Close search', backButtonTitle: 'Close search',
noResultsText: 'No results for', noResultsText: 'No results for',
exactMatchTitle: 'Toggle exact match', exactMatchTitle: 'Match exact phrases',
footer: { footer: {
selectText: 'to select', selectText: 'to select',
selectKeyAriaLabel: 'enter', selectKeyAriaLabel: 'enter',
@ -699,11 +680,11 @@ function onMouseMove(e: MouseEvent) {
<button <button
class="exact-match-button" class="exact-match-button"
type="button" type="button"
:class="{ 'exact-match-active': useExactMatch }" :class="{ 'exact-match-active': matchExact }"
:title="translate('modal.exactMatchTitle')" :title="translate('modal.exactMatchTitle')"
@click="useExactMatch = !useExactMatch" @click="matchExact = !matchExact"
> >
<span class="vpi-quote local-search-icon" /> <span class="vpi-exact-match local-search-icon" />
</button> </button>
<button <button
@ -869,9 +850,16 @@ function onMouseMove(e: MouseEvent) {
border-color: var(--vp-c-brand-1); border-color: var(--vp-c-brand-1);
} }
.vpi-quote { .vpi-exact-match {
--icon: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIGNsYXNzPSJsdWNpZGUgbHVjaWRlLWFzdGVyaXNrLWljb24gbHVjaWRlLWFzdGVyaXNrIj48cGF0aCBkPSJNMTIgNnYxMiIvPjxwYXRoIGQ9Ik0xNy4xOTYgOSA2LjgwNCAxNSIvPjxwYXRoIGQ9Im02LjgwNCA5IDEwLjM5MiA2Ii8+PC9zdmc+'); --icon: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIGNsYXNzPSJsdWNpZGUgbHVjaWRlLXJlZ2V4LWljb24gbHVjaWRlLXJlZ2V4Ij48cGF0aCBkPSJNMTcgM3YxMCIvPjxwYXRoIGQ9Im0xMi42NyA1LjUgOC42NiA1Ii8+PHBhdGggZD0ibTEyLjY3IDEwLjUgOC42Ni01Ii8+PHBhdGggZD0iTTkgMTdhMiAyIDAgMCAwLTItMkg1YTIgMiAwIDAgMC0yIDJ2MmEyIDIgMCAwIDAgMiAyaDJhMiAyIDAgMCAwIDItMnYtMnoiLz48L3N2Zz4=');
} }
.exact-match-button.exact-match-active {
color: var(--vp-c-brand-1);
background-color: rgba(var(--vp-c-brand-1), 0.1);
border-radius: 4px;
}
.local-search-icon { .local-search-icon {
display: block; display: block;
font-size: 18px; font-size: 18px;

589
pnpm-lock.yaml generated

File diff suppressed because it is too large Load diff