feat: implement fuzzy matching for autocomplete components and add Fuse.js for improved search functionality
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { cn, fuzzyMatch } from '@/lib/utils';
|
||||
import { useConfigStore } from '@/stores/useConfigStore';
|
||||
import { useAgentsStore } from '@/stores/useAgentsStore';
|
||||
import { ScrollableOverlay } from '@/components/ui/ScrollableOverlay';
|
||||
@@ -62,9 +62,9 @@ export const AgentMentionAutocomplete = React.forwardRef<AgentMentionAutocomplet
|
||||
};
|
||||
});
|
||||
|
||||
const normalizedQuery = searchQuery.trim().toLowerCase();
|
||||
const normalizedQuery = searchQuery.trim();
|
||||
const matches = normalizedQuery.length
|
||||
? filtered.filter((agent) => agent.name.toLowerCase().includes(normalizedQuery))
|
||||
? filtered.filter((agent) => fuzzyMatch(agent.name, normalizedQuery))
|
||||
: filtered;
|
||||
|
||||
matches.sort((a, b) => a.name.localeCompare(b.name));
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import React from 'react';
|
||||
import { RiCommandLine, RiFileLine, RiFlashlightLine, RiRefreshLine, RiScissorsLine, RiTerminalBoxLine, RiArrowGoBackLine, RiArrowGoForwardLine, RiTimeLine } from '@remixicon/react';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { opencodeClient } from '@/lib/opencode/client';
|
||||
import { cn, fuzzyMatch } from '@/lib/utils';
|
||||
import { useSessionStore } from '@/stores/useSessionStore';
|
||||
import { useCommandsStore } from '@/stores/useCommandsStore';
|
||||
import { useShallow } from 'zustand/react/shallow';
|
||||
@@ -113,8 +112,8 @@ export const CommandAutocomplete = React.forwardRef<CommandAutocompleteHandle, C
|
||||
const allowInitCommand = !hasMessagesInCurrentSession;
|
||||
const filtered = (searchQuery
|
||||
? allCommands.filter(cmd =>
|
||||
cmd.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||
(cmd.description && cmd.description.toLowerCase().includes(searchQuery.toLowerCase()))
|
||||
fuzzyMatch(cmd.name, searchQuery) ||
|
||||
(cmd.description && fuzzyMatch(cmd.description, searchQuery))
|
||||
)
|
||||
: allCommands).filter(cmd => allowInitCommand || cmd.name !== 'init');
|
||||
|
||||
@@ -148,8 +147,8 @@ export const CommandAutocomplete = React.forwardRef<CommandAutocompleteHandle, C
|
||||
|
||||
const filtered = (searchQuery
|
||||
? builtInCommands.filter(cmd =>
|
||||
cmd.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||
(cmd.description && cmd.description.toLowerCase().includes(searchQuery.toLowerCase()))
|
||||
fuzzyMatch(cmd.name, searchQuery) ||
|
||||
(cmd.description && fuzzyMatch(cmd.description, searchQuery))
|
||||
)
|
||||
: builtInCommands).filter(cmd => allowInitCommand || cmd.name !== 'init');
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ import { getAgentColor } from '@/lib/agentColors';
|
||||
import { useDeviceInfo } from '@/lib/device';
|
||||
import { calculateEditPermissionUIState, type BashPermissionSetting } from '@/lib/permissions/editPermissionDefaults';
|
||||
import { getEditModeColors } from '@/lib/permissions/editModeColors';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { cn, fuzzyMatch } from '@/lib/utils';
|
||||
import { useContextStore } from '@/stores/contextStore';
|
||||
import { useConfigStore } from '@/stores/useConfigStore';
|
||||
import { useSessionStore } from '@/stores/useSessionStore';
|
||||
@@ -1286,19 +1286,19 @@ export const ModelControls: React.FC<ModelControlsProps> = ({ className }) => {
|
||||
const renderMobileModelPanel = () => {
|
||||
if (!isCompact) return null;
|
||||
|
||||
const normalizedQuery = mobileModelQuery.trim().toLowerCase();
|
||||
const normalizedQuery = mobileModelQuery.trim();
|
||||
const filteredProviders = providers
|
||||
.map((provider) => {
|
||||
const providerModels = Array.isArray(provider.models) ? provider.models : [];
|
||||
const matchesProvider = normalizedQuery.length === 0
|
||||
? true
|
||||
: provider.name.toLowerCase().includes(normalizedQuery) || provider.id.toLowerCase().includes(normalizedQuery);
|
||||
: fuzzyMatch(provider.name, normalizedQuery) || fuzzyMatch(provider.id, normalizedQuery);
|
||||
const matchingModels = normalizedQuery.length === 0
|
||||
? providerModels
|
||||
: providerModels.filter((model: ProviderModel) => {
|
||||
const name = getModelDisplayName(model).toLowerCase();
|
||||
const id = typeof model.id === 'string' ? model.id.toLowerCase() : '';
|
||||
return name.includes(normalizedQuery) || id.includes(normalizedQuery);
|
||||
const name = getModelDisplayName(model);
|
||||
const id = typeof model.id === 'string' ? model.id : '';
|
||||
return fuzzyMatch(name, normalizedQuery) || fuzzyMatch(id, normalizedQuery);
|
||||
});
|
||||
return { provider, providerModels: matchingModels, matchesProvider };
|
||||
})
|
||||
@@ -1928,13 +1928,12 @@ export const ModelControls: React.FC<ModelControlsProps> = ({ className }) => {
|
||||
);
|
||||
};
|
||||
|
||||
// Filter models based on search query
|
||||
// Filter models based on search query (fuzzy match)
|
||||
const filterByQuery = (modelName: string, providerName: string, query: string) => {
|
||||
if (!query.trim()) return true;
|
||||
const lowerQuery = query.toLowerCase();
|
||||
return (
|
||||
modelName.toLowerCase().includes(lowerQuery) ||
|
||||
providerName.toLowerCase().includes(lowerQuery)
|
||||
fuzzyMatch(modelName, query) ||
|
||||
fuzzyMatch(providerName, query)
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { cn, fuzzyMatch } from '@/lib/utils';
|
||||
import { useSkillsStore } from '@/stores/useSkillsStore';
|
||||
import { ScrollableOverlay } from '@/components/ui/ScrollableOverlay';
|
||||
|
||||
@@ -36,9 +36,9 @@ export const SkillAutocomplete = React.forwardRef<SkillAutocompleteHandle, Skill
|
||||
}, [loadSkills]);
|
||||
|
||||
React.useEffect(() => {
|
||||
const normalizedQuery = searchQuery.trim().toLowerCase();
|
||||
const normalizedQuery = searchQuery.trim();
|
||||
const matches = normalizedQuery.length
|
||||
? skills.filter((skill) => skill.name.toLowerCase().includes(normalizedQuery))
|
||||
? skills.filter((skill) => fuzzyMatch(skill.name, normalizedQuery))
|
||||
: skills;
|
||||
|
||||
const sorted = [...matches].sort((a, b) => {
|
||||
|
||||
Reference in New Issue
Block a user