Move entity results above actions in command palette and tighten fuzzy matching
- Reorder result hierarchy: Pages → Entities → Actions → Settings (doc files and database records now appear right after page matches) - Disable fuzzy matching for terms under 5 characters to prevent false positives like "test" matching "text" (all SMS pages) - Prefix matching still works for short terms (e.g. "mail" → MailHog) Bunker Admin
This commit is contained in:
parent
46fc92fab8
commit
41d86782b4
@ -194,7 +194,7 @@ export default function CommandPalette() {
|
|||||||
}, [query, recentItems, allItems, favoriteCommandItems]);
|
}, [query, recentItems, allItems, favoriteCommandItems]);
|
||||||
|
|
||||||
// Flatten all results into a single list for keyboard navigation.
|
// Flatten all results into a single list for keyboard navigation.
|
||||||
// Order: Favorites → Recent → Pages → Actions → Entities → Settings
|
// Order: Favorites → Recent → Pages → Entities → Actions → Settings
|
||||||
const flatList = useMemo((): FlatItem[] => {
|
const flatList = useMemo((): FlatItem[] => {
|
||||||
const items: FlatItem[] = [];
|
const items: FlatItem[] = [];
|
||||||
if (!query) {
|
if (!query) {
|
||||||
@ -207,14 +207,15 @@ export default function CommandPalette() {
|
|||||||
} else if (parsed.showScopeList) {
|
} else if (parsed.showScopeList) {
|
||||||
// No items when showing scope selector
|
// No items when showing scope selector
|
||||||
} else {
|
} else {
|
||||||
// Pages (navigation) first, then actions
|
|
||||||
const pages = commandResults.filter((i) => i.category === 'navigation');
|
const pages = commandResults.filter((i) => i.category === 'navigation');
|
||||||
const actions = commandResults.filter((i) => i.category === 'action');
|
const actions = commandResults.filter((i) => i.category === 'action');
|
||||||
const settings = commandResults.filter((i) => i.category === 'settings');
|
const settings = commandResults.filter((i) => i.category === 'settings');
|
||||||
|
// Pages first (exact name matches ranked highest by MiniSearch)
|
||||||
for (const item of pages) items.push({ type: 'command', item });
|
for (const item of pages) items.push({ type: 'command', item });
|
||||||
for (const item of actions) items.push({ type: 'command', item });
|
// Entities next (database records like doc files, campaigns, users)
|
||||||
// Entities in the middle
|
|
||||||
for (const item of entityResults) items.push({ type: 'entity', item });
|
for (const item of entityResults) items.push({ type: 'entity', item });
|
||||||
|
// Actions after entities
|
||||||
|
for (const item of actions) items.push({ type: 'command', item });
|
||||||
// Settings last
|
// Settings last
|
||||||
for (const item of settings) items.push({ type: 'command', item });
|
for (const item of settings) items.push({ type: 'command', item });
|
||||||
}
|
}
|
||||||
@ -268,6 +269,7 @@ export default function CommandPalette() {
|
|||||||
|
|
||||||
// Group commands by category for display, in priority order:
|
// Group commands by category for display, in priority order:
|
||||||
// Pages (navigation) → Actions → Settings (least frequent)
|
// Pages (navigation) → Actions → Settings (least frequent)
|
||||||
|
// Entities are interleaved between pages and actions in the render phase.
|
||||||
// NOTE: These hooks must stay above the early return to satisfy Rules of Hooks
|
// NOTE: These hooks must stay above the early return to satisfy Rules of Hooks
|
||||||
const CATEGORY_ORDER: (CommandCategory | 'favorites' | 'recent')[] = [
|
const CATEGORY_ORDER: (CommandCategory | 'favorites' | 'recent')[] = [
|
||||||
'favorites', 'recent', 'navigation', 'action', 'settings',
|
'favorites', 'recent', 'navigation', 'action', 'settings',
|
||||||
@ -451,15 +453,15 @@ export default function CommandPalette() {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Render in priority order: Pages → Actions → Entities → Settings
|
{/* Render in priority order: Pages → Entities → Actions → Settings
|
||||||
For no-query mode: Favorites → Recent (no entities) */}
|
For no-query mode: Favorites → Recent (no entities) */}
|
||||||
{(() => {
|
{(() => {
|
||||||
const sections: React.ReactNode[] = [];
|
const sections: React.ReactNode[] = [];
|
||||||
const commandEntries = Array.from(groupedCommands.entries());
|
const commandEntries = Array.from(groupedCommands.entries());
|
||||||
|
|
||||||
// Render command groups that come before entities (everything except settings)
|
// Pages (navigation) first — skip actions and settings for now
|
||||||
for (const [groupKey, items] of commandEntries) {
|
for (const [groupKey, items] of commandEntries) {
|
||||||
if (groupKey === 'settings') continue; // settings rendered after entities
|
if (groupKey === 'settings' || groupKey === 'action') continue;
|
||||||
sections.push(
|
sections.push(
|
||||||
<CommandGroupSection
|
<CommandGroupSection
|
||||||
key={groupKey}
|
key={groupKey}
|
||||||
@ -476,7 +478,7 @@ export default function CommandPalette() {
|
|||||||
flatIndex += items.length;
|
flatIndex += items.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Entity groups (only when searching)
|
// Entities right after pages (doc files, campaigns, users, etc.)
|
||||||
if (query && !parsed.showScopeList) {
|
if (query && !parsed.showScopeList) {
|
||||||
for (const [entityType, items] of groupedEntities.entries()) {
|
for (const [entityType, items] of groupedEntities.entries()) {
|
||||||
sections.push(
|
sections.push(
|
||||||
@ -495,6 +497,25 @@ export default function CommandPalette() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Actions after entities
|
||||||
|
const actionItems = groupedCommands.get('action');
|
||||||
|
if (actionItems) {
|
||||||
|
sections.push(
|
||||||
|
<CommandGroupSection
|
||||||
|
key="action"
|
||||||
|
groupKey="action"
|
||||||
|
items={actionItems}
|
||||||
|
flatIndexRef={{ current: flatIndex }}
|
||||||
|
selectedIndex={selectedIndex}
|
||||||
|
favoritePaths={favoritePaths}
|
||||||
|
token={token}
|
||||||
|
onSelect={handleSelect}
|
||||||
|
onHover={setSelectedIndex}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
flatIndex += actionItems.length;
|
||||||
|
}
|
||||||
|
|
||||||
// Settings last
|
// Settings last
|
||||||
const settingsItems = groupedCommands.get('settings');
|
const settingsItems = groupedCommands.get('settings');
|
||||||
if (settingsItems) {
|
if (settingsItems) {
|
||||||
|
|||||||
@ -87,7 +87,9 @@ export function useCommandIndex() {
|
|||||||
searchOptions: {
|
searchOptions: {
|
||||||
boost: { title: 10, keywordsJoined: 3, group: 1 },
|
boost: { title: 10, keywordsJoined: 3, group: 1 },
|
||||||
prefix: true,
|
prefix: true,
|
||||||
fuzzy: 0.2,
|
// Only allow fuzzy matching for terms 5+ chars to prevent
|
||||||
|
// false positives like "test" → "text"
|
||||||
|
fuzzy: (term) => (term.length >= 5 ? 0.2 : false),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user