diff --git a/invokeai/frontend/web/src/common/components/Picker/Picker.tsx b/invokeai/frontend/web/src/common/components/Picker/Picker.tsx index cdfe8d1cdc..3b48369923 100644 --- a/invokeai/frontend/web/src/common/components/Picker/Picker.tsx +++ b/invokeai/frontend/web/src/common/components/Picker/Picker.tsx @@ -34,6 +34,22 @@ const DefaultGroupHeaderComponent = ({ id }: { id: string }) => { return {id}; }; +const DefaultNoOptionsFallback = () => { + return ( + + No options available + + ); +}; + +const DefaultNoMatchesFallback = () => { + return ( + + No matching options + + ); +}; + export type PickerProps = { options: (T | Group)[]; getOptionId: (option: T) => string; @@ -49,15 +65,21 @@ export type PickerProps = { GroupHeaderComponent?: React.ComponentType<{ group: Group }>; }; -export const getRegex = (searchTerm: string) => - new RegExp( - searchTerm - .trim() - .replace(/[-[\]{}()*+!<=:?./\\^$|#,]/g, '') - .split(' ') - .join('.*'), - 'gi' - ); +export const getRegex = (searchTerm: string) => { + const terms = searchTerm + .trim() + .replace(/[-[\]{}()*+!<=:?./\\^$|#,]/g, '') + .split(' ') + .filter((term) => term.length > 0); + + if (terms.length === 0) { + return new RegExp('', 'gi'); + } + + // Create positive lookaheads for each term - matches in any order + const pattern = terms.map((term) => `(?=.*${term})`).join(''); + return new RegExp(`${pattern}.+`, 'i'); +}; const getFirstOption = (options: (T | Group)[]): T | undefined => { const firstOptionOrGroup = options[0]; @@ -121,8 +143,8 @@ export const Picker = typedMemo((props: PickerProps) => { handleRef, isMatch, getIsDisabled, - noMatchesFallback, - noOptionsFallback, + noMatchesFallback = , + noOptionsFallback = , onClose, onSelect, selectedItem, diff --git a/invokeai/frontend/web/src/features/settingsAccordions/components/GenerationSettingsAccordion/GenerationSettingsAccordion.tsx b/invokeai/frontend/web/src/features/settingsAccordions/components/GenerationSettingsAccordion/GenerationSettingsAccordion.tsx index 561c03930f..738a21e742 100644 --- a/invokeai/frontend/web/src/features/settingsAccordions/components/GenerationSettingsAccordion/GenerationSettingsAccordion.tsx +++ b/invokeai/frontend/web/src/features/settingsAccordions/components/GenerationSettingsAccordion/GenerationSettingsAccordion.tsx @@ -216,12 +216,10 @@ const MainModelPicker = memo(() => { getOptionId={getOptionId} onSelect={onSelect} selectedItem={modelConfig} - getIsDisabled={getIsDisabled} + // getIsDisabled={getIsDisabled} isMatch={isMatch} OptionComponent={PickerItemComponent} GroupHeaderComponent={PickerGroupHeaderComponent} - noOptionsFallback={{t('common.noOptions')}} - noMatchesFallback={{t('common.noMatches')}} /> @@ -233,7 +231,7 @@ MainModelPicker.displayName = 'MainModelPicker'; const PickerGroupHeaderComponent = memo( ({ group }: { group: Group }) => { return ( - + {group.data.name} @@ -276,18 +274,11 @@ const BASE_KEYWORDS: { [key in BaseModelType]?: string[] } = { const isMatch = (model: AnyModelConfig, searchTerm: string) => { const regex = getRegex(searchTerm); + const bases = BASE_KEYWORDS[model.base] ?? [model.base]; + const testString = + `${model.name} ${bases.join(' ')} ${model.type} ${model.description ?? ''} ${model.format}`.toLowerCase(); - if ( - model.name.toLowerCase().includes(searchTerm) || - regex.test(model.name) || - (BASE_KEYWORDS[model.base] ?? [model.base]).some((kw) => kw.toLowerCase().includes(searchTerm) || regex.test(kw)) || - model.type.toLowerCase().includes(searchTerm) || - regex.test(model.type) || - (model.description ?? '').toLowerCase().includes(searchTerm) || - regex.test(model.description ?? '') || - model.format.toLowerCase().includes(searchTerm) || - regex.test(model.format) - ) { + if (testString.includes(searchTerm) || regex.test(testString)) { return true; }