fix: ensure custom model entry is always visible in model selector (#662)

The merged PR (#661) injected custom entries into filteredModels, but
cmdk auto-scrolls to its first selected CommandItem, pushing the custom
entry out of view. Fix by using forceMount on a separate CommandGroup
and resetting scroll to top on every keystroke via requestAnimationFrame.
This commit is contained in:
shivammittal274
2026-04-09 02:40:38 +05:30
committed by GitHub
parent 33617ba9e7
commit 412386b489

View File

@@ -8,7 +8,7 @@ import {
Loader2, Loader2,
XCircle, XCircle,
} from 'lucide-react' } from 'lucide-react'
import { type FC, useEffect, useMemo, useState } from 'react' import { type FC, useEffect, useMemo, useRef, useState } from 'react'
import { useForm } from 'react-hook-form' import { useForm } from 'react-hook-form'
import { z } from 'zod/v3' import { z } from 'zod/v3'
import { Button } from '@/components/ui/button' import { Button } from '@/components/ui/button'
@@ -223,6 +223,7 @@ export const NewProviderDialog: FC<NewProviderDialogProps> = ({
const [testResult, setTestResult] = useState<TestResult | null>(null) const [testResult, setTestResult] = useState<TestResult | null>(null)
const [modelPickerOpen, setModelPickerOpen] = useState(false) const [modelPickerOpen, setModelPickerOpen] = useState(false)
const [modelSearch, setModelSearch] = useState('') const [modelSearch, setModelSearch] = useState('')
const modelListRef = useRef<HTMLDivElement>(null)
const { supports } = useCapabilities() const { supports } = useCapabilities()
const { baseUrl: agentServerUrl } = useAgentServerUrl() const { baseUrl: agentServerUrl } = useAgentServerUrl()
const kimiLaunch = useKimiLaunch() const kimiLaunch = useKimiLaunch()
@@ -305,14 +306,12 @@ export const NewProviderDialog: FC<NewProviderDialogProps> = ({
[modelInfoList], [modelInfoList],
) )
const filteredModels = useMemo(() => { const filteredModels = modelSearch
if (!modelSearch) return modelInfoList ? modelFuse.search(modelSearch).map((r) => r.item)
const fuzzyResults = modelFuse.search(modelSearch).map((r) => r.item) : modelInfoList
const hasExactMatch = fuzzyResults.some((m) => m.modelId === modelSearch)
if (hasExactMatch) return fuzzyResults const showCustomEntry =
const customEntry = { modelId: modelSearch, contextLength: 0 } modelSearch && !filteredModels.some((m) => m.modelId === modelSearch)
return [customEntry, ...fuzzyResults]
}, [modelSearch, modelFuse, modelInfoList])
// Handle provider type change (user-initiated via Select) // Handle provider type change (user-initiated via Select)
const handleTypeChange = (newType: ProviderType) => { const handleTypeChange = (newType: ProviderType) => {
@@ -899,7 +898,12 @@ export const NewProviderDialog: FC<NewProviderDialogProps> = ({
<CommandInput <CommandInput
placeholder="Search models..." placeholder="Search models..."
value={modelSearch} value={modelSearch}
onValueChange={setModelSearch} onValueChange={(v) => {
setModelSearch(v)
requestAnimationFrame(() => {
modelListRef.current?.scrollTo(0, 0)
})
}}
onKeyDown={(e) => { onKeyDown={(e) => {
if (e.key === 'Enter' && modelSearch) { if (e.key === 'Enter' && modelSearch) {
e.preventDefault() e.preventDefault()
@@ -917,11 +921,36 @@ export const NewProviderDialog: FC<NewProviderDialogProps> = ({
} }
}} }}
/> />
<CommandList> <CommandList ref={modelListRef}>
<CommandEmpty> <CommandEmpty>
No models found. Press Enter to use &quot; No models found. Press Enter to use &quot;
{modelSearch}&quot; {modelSearch}&quot;
</CommandEmpty> </CommandEmpty>
{showCustomEntry && (
<CommandGroup forceMount>
<CommandItem
forceMount
value={`custom:${modelSearch}`}
onSelect={() => {
form.setValue('modelId', modelSearch)
track(MODEL_SELECTED_EVENT, {
provider_type: watchedType,
model_id: modelSearch,
is_custom_model: true,
})
setModelPickerOpen(false)
setModelSearch('')
}}
>
<span className="flex-1 truncate">
{modelSearch}
</span>
{field.value === modelSearch && (
<Check className="ml-2 h-4 w-4 shrink-0" />
)}
</CommandItem>
</CommandGroup>
)}
{filteredModels.length > 0 && ( {filteredModels.length > 0 && (
<CommandGroup> <CommandGroup>
{filteredModels.map((model) => ( {filteredModels.map((model) => (