/** * Settings Panel Component * Model settings and parameters with DYNAMIC thinking controls from backend API */ import { useState } from 'react'; import { useQuery } from '@tanstack/react-query'; import { RefreshCw, ChevronDown, ChevronRight, AlertCircle, Loader2 } from 'lucide-react'; import { useSettings, useI18n } from '@/contexts'; import { fetchModels } from '@/api'; import { useModelCapabilities } from '@/hooks/useModelCapabilities'; import type { ThinkingLevel } from '@/types'; import styles from './SettingsPanel.module.css'; export function SettingsPanel() { const { t } = useI18n(); const { settings, dispatch, selectedModel, setSelectedModel } = useSettings(); const [expandedSections, setExpandedSections] = useState>({ model: true, thinking: true, params: true, tools: true, system: false, }); // Fetch models const { data: modelsData, isLoading: modelsLoading, refetch } = useQuery({ queryKey: ['models'], queryFn: fetchModels, staleTime: 60000, }); // Fetch model capabilities from backend (single source of truth) const { getModelCategory, getModelCapabilities, isLoading: capabilitiesLoading } = useModelCapabilities(); const models = modelsData?.data || []; const category = getModelCategory(selectedModel); const capabilities = getModelCapabilities(selectedModel); const toggleSection = (section: string) => { setExpandedSections(prev => ({ ...prev, [section]: !prev[section] })); }; // Build thinking level options from capabilities const buildLevelOptions = (): { value: ThinkingLevel | ''; label: string }[] => { if (capabilities?.thinkingType !== 'level' || !capabilities.levels) { return []; } const options: { value: ThinkingLevel | ''; label: string }[] = [ { value: '', label: t.settingsPanel.unspecified } ]; for (const level of capabilities.levels) { options.push({ value: level as ThinkingLevel, label: level.charAt(0).toUpperCase() + level.slice(1) }); } return options; }; const levelOptions = buildLevelOptions(); // Get budget slider range from capabilities const getBudgetRange = () => { if (capabilities?.budgetRange) { return { min: capabilities.budgetRange[0], max: capabilities.budgetRange[1] }; } return { min: 512, max: 24576 }; }; return (
{/* Model Selection */} toggleSection('model')} >
{/* Thinking Settings - Dynamic UI based on backend capabilities */} toggleSection('thinking')} > {capabilitiesLoading ? (
{t.common.loading}
) : capabilities?.thinkingType === 'level' ? ( /* Level selector (Gemini 3) */
{category} {t.settingsPanel.supportsLevels.replace('{count}', String(capabilities.levels?.length || 0))}
) : capabilities?.thinkingType === 'budget' ? ( /* Budget controls (Gemini 2.5) */ <> dispatch({ type: 'SET_ENABLE_THINKING', payload: checked }) } /> {(capabilities.alwaysOn || settings.enableThinking) && ( <> dispatch({ type: 'SET_ENABLE_MANUAL_BUDGET', payload: checked }) } /> {settings.enableManualBudget && ( dispatch({ type: 'SET_THINKING_BUDGET', payload: value }) } /> )} )} ) : ( /* No thinking support */
)}
{/* Parameters */} toggleSection('params')} > dispatch({ type: 'SET_TEMPERATURE', payload: value }) } /> dispatch({ type: 'SET_MAX_TOKENS', payload: value }) } /> dispatch({ type: 'SET_TOP_P', payload: value }) } /> {/* Tools */} toggleSection('tools')} > dispatch({ type: 'SET_ENABLE_GOOGLE_SEARCH', payload: checked }) } /> {/* System Prompt */} toggleSection('system')} >