import { useEffect, useRef, useState } from 'react'; import { ChevronDown, Sparkles } from 'lucide-react'; import { findModel, inferProvider, modelCatalog, providerMeta, type ModelDefinition, } from '@shared/domain/models'; import { reasoningEffortOptions, type ReasoningEffort } from '@shared/domain/pattern'; import { ProviderIcon } from './ProviderIcons'; function TierBadge({ tier }: { tier: ModelDefinition['tier'] }) { if (!tier) { return null; } const styles = { premium: 'bg-amber-500/10 text-amber-400', standard: 'bg-zinc-700/50 text-zinc-500', fast: 'bg-emerald-500/10 text-emerald-400', }; return ( {tier} ); } interface ModelSelectProps { value: string; onChange: (model: string) => void; models?: ReadonlyArray; label?: string; disabled?: boolean; } export function ModelSelect({ value, onChange, models = modelCatalog, label = 'Model', disabled = false, }: ModelSelectProps) { const [open, setOpen] = useState(false); const containerRef = useRef(null); useEffect(() => { if (!open) { return; } function handleClick(event: MouseEvent) { if (containerRef.current && !containerRef.current.contains(event.target as Node)) { setOpen(false); } } document.addEventListener('mousedown', handleClick); return () => document.removeEventListener('mousedown', handleClick); }, [open]); const selected = findModel(value, models); const provider = selected?.provider ?? inferProvider(value); const groupedModels = providerMeta .map((providerGroup) => ({ ...providerGroup, models: models.filter((model) => model.provider === providerGroup.id), })) .filter((providerGroup) => providerGroup.models.length > 0); const otherModels = models.filter((model) => !model.provider); return ( ); } interface ReasoningEffortSelectProps { value?: ReasoningEffort; onChange: (value: ReasoningEffort) => void; supportedEfforts?: ReadonlyArray; label?: string; disabled?: boolean; } export function ReasoningEffortSelect({ value, onChange, supportedEfforts, label = 'Reasoning', disabled = false, }: ReasoningEffortSelectProps) { const options = supportedEfforts ? reasoningEffortOptions.filter((option) => supportedEfforts.includes(option.value)) : [...reasoningEffortOptions]; const selectedValue = value && options.some((option) => option.value === value) ? value : options[0]?.value; if (supportedEfforts && supportedEfforts.length === 0) { return ( ); } return ( ); }