diff --git a/app/components/chat/ModelSelector.tsx b/app/components/chat/ModelSelector.tsx index 521ccac3..918b681d 100644 --- a/app/components/chat/ModelSelector.tsx +++ b/app/components/chat/ModelSelector.tsx @@ -1,6 +1,7 @@ import type { ProviderInfo } from '~/types/model'; -import { useEffect } from 'react'; +import { useEffect, useState, useRef } from 'react'; import type { ModelInfo } from '~/lib/modules/llm/types'; +import { classNames } from '~/utils/classNames'; interface ModelSelectorProps { model?: string; @@ -22,12 +23,30 @@ export const ModelSelector = ({ providerList, modelLoading, }: ModelSelectorProps) => { - // Load enabled providers from cookies + const [modelSearchQuery, setModelSearchQuery] = useState(''); + const [isModelDropdownOpen, setIsModelDropdownOpen] = useState(false); + const searchInputRef = useRef(null); + + // Filter models based on search query + const filteredModels = [...modelList] + .filter((e) => e.provider === provider?.name && e.name) + .filter( + (model) => + model.label.toLowerCase().includes(modelSearchQuery.toLowerCase()) || + model.name.toLowerCase().includes(modelSearchQuery.toLowerCase()), + ); + + // Focus search input when dropdown opens + useEffect(() => { + if (isModelDropdownOpen && searchInputRef.current) { + searchInputRef.current.focus(); + } + }, [isModelDropdownOpen]); // Update enabled providers when cookies change useEffect(() => { // If current provider is disabled, switch to first enabled provider - if (providerList.length == 0) { + if (providerList.length === 0) { return; } @@ -80,27 +99,91 @@ export const ModelSelector = ({ ))} - setModelSearchQuery(e.target.value)} + placeholder="Search models..." + className={classNames( + 'w-full pl-8 pr-3 py-1.5 rounded-md text-sm', + 'bg-bolt-elements-background-depth-2 border border-bolt-elements-borderColor', + 'text-bolt-elements-textPrimary placeholder:text-bolt-elements-textTertiary', + 'focus:outline-none focus:ring-2 focus:ring-bolt-elements-focus', + 'transition-all', + )} + onClick={(e) => e.stopPropagation()} + /> +
+ +
+ + + +
+ {modelLoading === 'all' || modelLoading === provider?.name ? ( +
Loading...
+ ) : filteredModels.length === 0 ? ( +
No models found
+ ) : ( + filteredModels.map((modelOption, index) => ( +
{ + e.stopPropagation(); + setModel?.(modelOption.name); + setIsModelDropdownOpen(false); + setModelSearchQuery(''); + }} + > + {modelOption.label} +
+ )) + )} +
+ )} - + ); };