Search
Fuzzy search
The built-in fuzzySearch function is exported for standalone use:
import { fuzzySearch, highlightMatches } from '@macrulez/vue-command-palette'
const results = fuzzySearch('git cm', commands)
// sorted by score: exact → prefix → substring → fuzzy
// Render highlighted label in a custom slot
const vnode = highlightMatches(command.label, result.matches)
// returns a VNode: <span>git <mark class="vcp-match">c</mark>o<mark class="vcp-match">m</mark>mit</span>
// Highlight ranges for results that came from an external source
// (async groups, pages, modes — these are already highlighted internally):
import { getMatchRanges } from '@macrulez/vue-command-palette'
const ranges = getMatchRanges('al', 'Alan Turing') // → [[0, 1]]Scoring table
| Match type | Score |
|---|---|
| Exact match | 100 |
| Prefix match | 80 |
| Substring (contains) | 60 |
| Fuzzy (all chars in order) | 1 – 40 (penalised by character gaps) |
| No match | −1 (excluded from results) |
The engine checks label, all keywords[], and all aliases[]. The highest score across all fields wins. Commands where disabled: true or enabled() returns false are excluded before scoring.
Diacritic normalization
Strings are normalized with NFD Unicode decomposition before comparison, so accents are ignored:
fuzzySearch('cafe', [{ id: '1', label: 'Café', perform: () => {} }]) // → match
fuzzySearch('muller', [{ id: '2', label: 'Müller', perform: () => {} }]) // → matchOnly diacritics are stripped (
é→e,ü→u). Letters that have no canonical decomposition — such asß— are left as-is, soßdoes not matchss.
Modes / scopes
Define prefix-activated scopes (like VS Code's > commands or @ symbols). When the query starts with a mode's prefix, the prefix is stripped, the placeholder switches, a chip appears, and results come from the mode's onSearch (or, if omitted, the regular fuzzy search over the stripped query).
<CommandPalette
:modes="[
{ prefix: '>', label: 'Run', placeholder: 'Run a command…', onSearch: searchCommands },
{ prefix: '@', label: 'People', placeholder: 'Find a person…', onSearch: searchPeople },
]"
/>type PaletteMode = {
prefix: string
placeholder?: string
label?: string
onSearch?: (query: string) => Command[] | Promise<Command[]>
}Backspace over the prefix exits the mode. Results are debounced 200 ms.
Async search
Each group can provide an onSearch callback that returns dynamic commands for a given query. Useful for searching external APIs, databases, or documentation.
useRegisterGroup({
id: 'docs-search',
label: 'Documentation',
commands: [], // static commands (can be empty for search-only groups)
onSearch: async (query: string) => {
const results = await searchDocs(query)
return results.slice(0, 5).map((doc) => ({
id: `doc-${doc.slug}`,
label: doc.title,
description: doc.excerpt,
icon: '📄',
perform: () => window.open(doc.url, '_blank'),
}))
},
})- Debounced by 200 ms to avoid excessive requests
- An unobtrusive spinner appears in the input corner while the request is in flight — already-shown results stay visible (no blanking). The centered loading text only appears when there is nothing to show yet.
- Async results are merged with sync results and re-sorted by score
- Empty query clears async results immediately (no debounce)
- A plugin-level
onSearchoption provides one global async source (not tied to a group), merged the same way:tsapp.use(VCommandPalettePlugin, { onSearch: (q) => api.search(q) })
Custom search strategy
Replace the built-in fuzzy engine with any scorer — for example Fuse.js. The function receives the query and all available commands and returns ranked SearchResult[] (highest score first). The store still assigns groupId to each result afterwards.
import Fuse from 'fuse.js'
import type { SearchFn } from '@macrulez/vue-command-palette'
const fuseSearch: SearchFn = (query, commands) => {
const fuse = new Fuse(commands, {
keys: ['label', 'description', 'keywords'],
includeScore: true,
})
return fuse.search(query).map((r) => ({
command: r.item,
score: 1 - (r.score ?? 0),
matches: [],
}))
}
app.use(VCommandPalettePlugin, { search: fuseSearch })