import React from 'react'; import { MobileOverlayPanel } from '@/components/ui/MobileOverlayPanel'; import { ProviderLogo } from '@/components/ui/ProviderLogo'; import { cn } from '@/lib/utils'; import { useConfigStore } from '@/stores/useConfigStore'; import { useSessionStore } from '@/stores/useSessionStore'; import { useContextStore } from '@/stores/contextStore'; import { useUIStore } from '@/stores/useUIStore'; import { useModelLists } from '@/hooks/useModelLists'; import { formatEffortLabel, getQuickEffortOptions, parseEffortVariant, } from './mobileControlsUtils'; const COMPACT_NUMBER_FORMATTER = new Intl.NumberFormat('en-US', { notation: 'compact', maximumFractionDigits: 1, }); const formatTokens = (value?: number | null) => { if (typeof value !== 'number' || Number.isNaN(value)) { return null; } if (value === 0) { return '0'; } const formatted = COMPACT_NUMBER_FORMATTER.format(value); return formatted.endsWith('.0') ? formatted.slice(0, -2) : formatted; }; interface UnifiedControlsDrawerProps { open: boolean; onClose: () => void; onOpenModel: () => void; onOpenEffort: () => void; } export const UnifiedControlsDrawer: React.FC = ({ open, onClose, onOpenModel, onOpenEffort, }) => { const { providers, currentProviderId, currentModelId, currentVariant, setProvider, setModel, setCurrentVariant, getCurrentModelVariants, getModelMetadata, } = useConfigStore(); const { addRecentModel, addRecentEffort, recentEfforts } = useUIStore(); const { recentModelsList } = useModelLists(); const { currentSessionId, saveAgentModelForSession, saveAgentModelVariantForSession, } = useSessionStore(); const sessionAgentName = useContextStore((state) => currentSessionId ? state.getSessionAgentSelection(currentSessionId) : null ); const uiAgentName = currentSessionId ? (sessionAgentName || null) : null; const recentModelsBase = recentModelsList.slice(0, 4); const hasCurrentInRecents = recentModelsBase.some( (entry) => entry.providerID === currentProviderId && entry.modelID === currentModelId ); // If current model not in recents, prepend it so it's always visible const recentModels = React.useMemo(() => { if (hasCurrentInRecents || !currentProviderId || !currentModelId) { return recentModelsBase; } const currentProvider = providers.find((p) => p.id === currentProviderId); const currentModel = currentProvider?.models?.find((m) => m.id === currentModelId); if (!currentModel) { return recentModelsBase; } return [ { providerID: currentProviderId, modelID: currentModelId, provider: currentProvider, model: currentModel }, ...recentModelsBase.slice(0, 3), ]; }, [recentModelsBase, hasCurrentInRecents, currentProviderId, currentModelId, providers]); const variants = getCurrentModelVariants(); const hasEffort = variants.length > 0; const effortKey = currentProviderId && currentModelId ? `${currentProviderId}/${currentModelId}` : null; const recentEffortsForModel = effortKey ? (recentEfforts[effortKey] ?? []) : []; const recentEffortOptions = recentEffortsForModel .map((variant) => parseEffortVariant(variant)) .filter((variant) => !variant || variants.includes(variant)); const fallbackEfforts = getQuickEffortOptions(variants); const baseEfforts = fallbackEfforts.length > 0 ? fallbackEfforts : recentEffortOptions; const quickEfforts = React.useMemo(() => { const base = baseEfforts.slice(0, 4); const orderedRecents = recentEffortOptions.slice().reverse(); for (const recent of orderedRecents) { if (base.some((entry) => entry === recent)) { continue; } base.unshift(recent); base.splice(4); } if (!base.some((entry) => entry === currentVariant)) { if (base.length > 0) { base[0] = currentVariant; } else { base.push(currentVariant); } } if (!base.some((entry) => entry === undefined)) { base.push(undefined); base.splice(4); } return base; }, [baseEfforts, currentVariant, recentEffortOptions]); const effortHasMore = variants.length + 1 > quickEfforts.length; const handleModelSelect = (providerId: string, modelId: string) => { const provider = providers.find((entry) => entry.id === providerId); if (!provider) { return; } const providerModels = Array.isArray(provider.models) ? provider.models : []; const modelExists = providerModels.some((model) => model.id === modelId); if (!modelExists) { return; } const isRecentAlready = recentModelsList.some( (entry) => entry.providerID === providerId && entry.modelID === modelId ); setProvider(providerId); setModel(modelId); if (!isRecentAlready) { addRecentModel(providerId, modelId); } if (currentSessionId && uiAgentName) { saveAgentModelForSession(currentSessionId, uiAgentName, providerId, modelId); } }; const handleEffortSelect = (variant: string | undefined) => { setCurrentVariant(variant); if (currentProviderId && currentModelId) { addRecentEffort(currentProviderId, currentModelId, variant); } if (currentSessionId && uiAgentName && currentProviderId && currentModelId) { saveAgentModelVariantForSession(currentSessionId, uiAgentName, currentProviderId, currentModelId, variant); } }; return (
Model
{recentModels.length === 0 && !hasCurrentInRecents && (
No recent models
)} {recentModels.map(({ providerID, modelID, model }) => { const isSelected = providerID === currentProviderId && modelID === currentModelId; const modelName = typeof model?.name === 'string' && model.name.trim().length > 0 ? model.name : modelID; const metadata = getModelMetadata(providerID, modelID); const ctxTokens = formatTokens(metadata?.limit?.context); const outTokens = formatTokens(metadata?.limit?.output); return ( ); })}
{hasEffort && (
Effort
{quickEfforts.map((variant) => { const isSelected = variant === currentVariant || (!variant && !currentVariant); return ( ); })} {effortHasMore && ( )}
)}
); }; export default UnifiedControlsDrawer;