11'use client'
22
3- import { useState } from 'react'
3+ import { useMemo , useState } from 'react'
44import { createLogger } from '@sim/logger'
5- import { Eye , EyeOff } from 'lucide-react'
5+ import { Eye , EyeOff , Search } from 'lucide-react'
66import { useParams } from 'next/navigation'
77import {
88 Button ,
@@ -30,6 +30,7 @@ import {
3030 PerplexityIcon ,
3131 SerperIcon ,
3232} from '@/components/icons'
33+ import { Input } from '@/components/ui'
3334import { BYOKKeySkeleton } from '@/app/workspace/[workspaceId]/settings/components/byok/byok-skeleton'
3435import {
3536 type BYOKKey ,
@@ -164,13 +165,26 @@ export function BYOK() {
164165 const upsertKey = useUpsertBYOKKey ( )
165166 const deleteKey = useDeleteBYOKKey ( )
166167
168+ const [ searchTerm , setSearchTerm ] = useState ( '' )
167169 const [ editingProvider , setEditingProvider ] = useState < BYOKProviderId | null > ( null )
168170 const [ apiKeyInput , setApiKeyInput ] = useState ( '' )
169171 const [ showApiKey , setShowApiKey ] = useState ( false )
170172 const [ error , setError ] = useState < string | null > ( null )
171173
172174 const [ deleteConfirmProvider , setDeleteConfirmProvider ] = useState < BYOKProviderId | null > ( null )
173175
176+ const filteredProviders = useMemo ( ( ) => {
177+ if ( ! searchTerm . trim ( ) ) return PROVIDERS
178+ const searchLower = searchTerm . toLowerCase ( )
179+ return PROVIDERS . filter (
180+ ( p ) =>
181+ p . name . toLowerCase ( ) . includes ( searchLower ) ||
182+ p . description . toLowerCase ( ) . includes ( searchLower )
183+ )
184+ } , [ searchTerm ] )
185+
186+ const showNoResults = searchTerm . trim ( ) && filteredProviders . length === 0
187+
174188 const getKeyForProvider = ( providerId : BYOKProviderId ) : BYOKKey | undefined => {
175189 return keys . find ( ( k ) => k . providerId === providerId )
176190 }
@@ -219,6 +233,22 @@ export function BYOK() {
219233 return (
220234 < >
221235 < div className = 'flex h-full flex-col gap-[18px]' >
236+ < div className = 'flex items-center gap-[8px]' >
237+ < div className = 'flex flex-1 items-center gap-[8px] rounded-[8px] border border-[var(--border)] bg-transparent px-[8px] py-[5px] transition-colors duration-100 dark:bg-[var(--surface-4)] dark:hover:border-[var(--border-1)] dark:hover:bg-[var(--surface-5)]' >
238+ < Search
239+ className = 'h-[14px] w-[14px] flex-shrink-0 text-[var(--text-tertiary)]'
240+ strokeWidth = { 2 }
241+ />
242+ < Input
243+ placeholder = 'Search providers...'
244+ value = { searchTerm }
245+ onChange = { ( e ) => setSearchTerm ( e . target . value ) }
246+ disabled = { isLoading }
247+ className = 'h-auto flex-1 border-0 bg-transparent p-0 font-base leading-none placeholder:text-[var(--text-tertiary)] focus-visible:ring-0 focus-visible:ring-offset-0'
248+ />
249+ </ div >
250+ </ div >
251+
222252 < p className = 'text-[14px] text-[var(--text-secondary)]' >
223253 Use your own API keys for hosted model providers.
224254 </ p >
@@ -232,7 +262,7 @@ export function BYOK() {
232262 </ div >
233263 ) : (
234264 < div className = 'flex flex-col gap-[8px]' >
235- { PROVIDERS . map ( ( provider ) => {
265+ { filteredProviders . map ( ( provider ) => {
236266 const existingKey = getKeyForProvider ( provider . id )
237267 const Icon = provider . icon
238268
@@ -274,6 +304,11 @@ export function BYOK() {
274304 </ div >
275305 )
276306 } ) }
307+ { showNoResults && (
308+ < div className = 'py-[16px] text-center text-[14px] text-[var(--text-muted)]' >
309+ No providers found matching "{ searchTerm } "
310+ </ div >
311+ ) }
277312 </ div >
278313 ) }
279314 </ div >
0 commit comments