import React from 'react'; import type { ComponentType } from 'react'; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, } from '@/components/ui/dropdown-menu'; import { Tooltip, TooltipTrigger, TooltipContent } from '@/components/ui/tooltip'; import { Input } from '@/components/ui/input'; import { RiAiAgentLine, RiArrowDownSLine, RiArrowRightSLine, RiBrainAi3Line, RiCheckboxCircleLine, RiCloseCircleLine, RiFileImageLine, RiFileMusicLine, RiFilePdfLine, RiFileVideoLine, RiPencilAiLine, RiQuestionLine, RiSearchLine, RiText, RiToolsLine } from '@remixicon/react'; import type { ModelMetadata } from '@/types'; import type { EditPermissionMode } from '@/stores/types/sessionTypes'; import { getEditModeColors } from '@/lib/permissions/editModeColors'; // eslint-disable-next-line @typescript-eslint/no-explicit-any type IconComponent = ComponentType; type ProviderModel = Record & { id?: string; name?: string }; import { useConfigStore } from '@/stores/useConfigStore'; import { useSessionStore } from '@/stores/useSessionStore'; import { useContextStore } from '@/stores/contextStore'; import { useDeviceInfo } from '@/lib/device'; import { useIsVSCodeRuntime } from '@/hooks/useRuntimeAPIs'; import { cn } from '@/lib/utils'; import { MobileOverlayPanel } from '@/components/ui/MobileOverlayPanel'; import { getAgentColor } from '@/lib/agentColors'; import { ProviderLogo } from '@/components/ui/ProviderLogo'; import { calculateEditPermissionUIState, type BashPermissionSetting } from '@/lib/permissions/editPermissionDefaults'; import { ScrollableOverlay } from '@/components/ui/ScrollableOverlay'; const isPrimaryMode = (mode?: string) => mode === 'primary' || mode === 'all' || mode === undefined || mode === null; interface CapabilityDefinition { key: 'tool_call' | 'reasoning'; icon: IconComponent; label: string; isActive: (metadata?: ModelMetadata) => boolean; } const CAPABILITY_DEFINITIONS: CapabilityDefinition[] = [ { key: 'tool_call', icon: RiToolsLine, label: 'Tool calling', isActive: (metadata) => metadata?.tool_call === true, }, { key: 'reasoning', icon: RiBrainAi3Line, label: 'Reasoning', isActive: (metadata) => metadata?.reasoning === true, }, ]; interface ModalityIconDefinition { icon: IconComponent; label: string; } type ModalityIcon = { key: string; icon: IconComponent; label: string; }; type ModelApplyResult = 'applied' | 'provider-missing' | 'model-missing'; const MODALITY_ICON_MAP: Record = { text: { icon: RiText, label: 'Text' }, image: { icon: RiFileImageLine, label: 'Image' }, video: { icon: RiFileVideoLine, label: 'Video' }, audio: { icon: RiFileMusicLine, label: 'Audio' }, pdf: { icon: RiFilePdfLine, label: 'PDF' }, }; const normalizeModality = (value: string) => value.trim().toLowerCase(); const getModalityIcons = (metadata: ModelMetadata | undefined, direction: 'input' | 'output'): ModalityIcon[] => { const modalityList = direction === 'input' ? metadata?.modalities?.input : metadata?.modalities?.output; if (!Array.isArray(modalityList) || modalityList.length === 0) { return []; } const uniqueValues = Array.from(new Set(modalityList.map((item) => normalizeModality(item)))); return uniqueValues .map((modality) => { const definition = MODALITY_ICON_MAP[modality]; if (!definition) { return null; } return { key: modality, icon: definition.icon, label: definition.label, } satisfies ModalityIcon; }) .filter((entry): entry is ModalityIcon => Boolean(entry)); }; const COMPACT_NUMBER_FORMATTER = new Intl.NumberFormat('en-US', { notation: 'compact', compactDisplay: 'short', maximumFractionDigits: 1, minimumFractionDigits: 0, }); const CURRENCY_FORMATTER = new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD', maximumFractionDigits: 4, minimumFractionDigits: 2, }); const formatTokens = (value?: number | null) => { if (typeof value !== 'number' || Number.isNaN(value)) { return '—'; } if (value === 0) { return '0'; } const formatted = COMPACT_NUMBER_FORMATTER.format(value); return formatted.endsWith('.0') ? formatted.slice(0, -2) : formatted; }; const formatCost = (value?: number | null) => { if (typeof value !== 'number' || !Number.isFinite(value)) { return '—'; } return CURRENCY_FORMATTER.format(value); }; const getCapabilityIcons = (metadata?: ModelMetadata) => { return CAPABILITY_DEFINITIONS.filter((definition) => definition.isActive(metadata)).map((definition) => ({ key: definition.key, icon: definition.icon, label: definition.label, })); }; const formatKnowledge = (knowledge?: string) => { if (!knowledge) { return '—'; } const match = knowledge.match(/^(\d{4})-(\d{2})$/); if (match) { const year = Number.parseInt(match[1], 10); const monthIndex = Number.parseInt(match[2], 10) - 1; const knowledgeDate = new Date(Date.UTC(year, monthIndex, 1)); if (!Number.isNaN(knowledgeDate.getTime())) { return new Intl.DateTimeFormat('en-US', { month: 'short', year: 'numeric' }).format(knowledgeDate); } } return knowledge; }; const formatDate = (value?: string) => { if (!value) { return '—'; } const parsedDate = new Date(value); if (Number.isNaN(parsedDate.getTime())) { return value; } return new Intl.DateTimeFormat('en-US', { month: 'short', day: 'numeric', year: 'numeric', }).format(parsedDate); }; interface ModelControlsProps { className?: string; } export const ModelControls: React.FC = ({ className }) => { const { providers, agents, currentProviderId, currentModelId, currentAgentName, setProvider, setModel, setAgent, getCurrentProvider, getModelMetadata, getCurrentAgent, } = useConfigStore(); const { currentSessionId, messages, saveSessionAgentSelection, getSessionAgentSelection, saveAgentModelForSession, getAgentModelForSession, analyzeAndSaveExternalSessionChoices, getSessionAgentEditMode, setSessionAgentEditMode, } = useSessionStore(); const contextHydrated = useContextStore((state) => state.hasHydrated); const { isMobile } = useDeviceInfo(); const isVSCodeRuntime = useIsVSCodeRuntime(); const isCompact = isMobile || isVSCodeRuntime; const [activeMobilePanel, setActiveMobilePanel] = React.useState<'model' | 'agent' | null>(null); const [mobileTooltipOpen, setMobileTooltipOpen] = React.useState<'model' | 'agent' | null>(null); const [mobileModelQuery, setMobileModelQuery] = React.useState(''); const closeMobilePanel = React.useCallback(() => setActiveMobilePanel(null), []); const closeMobileTooltip = React.useCallback(() => setMobileTooltipOpen(null), []); const longPressTimerRef = React.useRef(undefined); const [expandedMobileProviders, setExpandedMobileProviders] = React.useState>(() => { const initial = new Set(); if (currentProviderId) { initial.add(currentProviderId); } return initial; }); const [mobileEditOptionsOpen, setMobileEditOptionsOpen] = React.useState(false); const [agentMenuOpen, setAgentMenuOpen] = React.useState(false); const [desktopEditOptionsOpen, setDesktopEditOptionsOpen] = React.useState(false); const desktopEditOptionsId = React.useId(); React.useEffect(() => { if (activeMobilePanel === 'model') { setExpandedMobileProviders(() => { const initial = new Set(); if (currentProviderId) { initial.add(currentProviderId); } return initial; }); } }, [activeMobilePanel, currentProviderId]); React.useEffect(() => { if (activeMobilePanel !== 'agent') { setMobileEditOptionsOpen(false); } if (activeMobilePanel !== 'model') { setMobileModelQuery(''); } }, [activeMobilePanel]); React.useEffect(() => { if (!agentMenuOpen) { setDesktopEditOptionsOpen(false); } }, [agentMenuOpen]); const currentAgent = getCurrentAgent?.(); const agentPermissionRaw = currentAgent?.permission?.edit; let agentDefaultEditMode: EditPermissionMode = 'ask'; if (agentPermissionRaw === 'allow' || agentPermissionRaw === 'ask' || agentPermissionRaw === 'deny' || agentPermissionRaw === 'full') { agentDefaultEditMode = agentPermissionRaw; } const editToolConfigured = currentAgent ? (currentAgent.tools?.['edit'] !== false) : false; if (!currentAgent || !editToolConfigured) { agentDefaultEditMode = 'deny'; } const agentWebfetchPermission = currentAgent?.permission?.webfetch; const agentBashPermission = currentAgent?.permission?.bash as BashPermissionSetting | undefined; const permissionUiState = React.useMemo(() => calculateEditPermissionUIState({ agentDefaultEditMode, webfetchPermission: agentWebfetchPermission, bashPermission: agentBashPermission, }), [agentDefaultEditMode, agentWebfetchPermission, agentBashPermission]); const { cascadeDefaultMode, modeAvailability, autoApproveAvailable } = permissionUiState; const selectionContextReady = Boolean(currentSessionId && currentAgentName); const sessionMode = selectionContextReady && currentSessionId && currentAgentName ? getSessionAgentEditMode(currentSessionId, currentAgentName, cascadeDefaultMode) : cascadeDefaultMode; const editModeShortLabels: Record = { ask: 'Ask before edits', allow: 'Approve edit tools', full: 'Approve every tool', deny: 'Editing disabled', }; const isModeDisabled = React.useCallback((mode: EditPermissionMode) => { return !modeAvailability[mode]; }, [modeAvailability]); const effectiveEditMode = React.useMemo(() => { if (!selectionContextReady) { return cascadeDefaultMode; } if (isModeDisabled(sessionMode) && sessionMode !== cascadeDefaultMode) { return cascadeDefaultMode; } return sessionMode; }, [cascadeDefaultMode, isModeDisabled, selectionContextReady, sessionMode]); const editPermissionOptions: Array<{ mode: EditPermissionMode; label: string; disabled: boolean }> = [ { mode: 'ask', label: editModeShortLabels.ask, disabled: isModeDisabled('ask') }, { mode: 'allow', label: editModeShortLabels.allow, disabled: isModeDisabled('allow') }, { mode: 'full', label: editModeShortLabels.full, disabled: isModeDisabled('full') }, ]; const activeEditModeColors = React.useMemo(() => getEditModeColors(effectiveEditMode), [effectiveEditMode]); const editToggleDisabled = !selectionContextReady || !autoApproveAvailable; React.useEffect(() => { if (editToggleDisabled) { setMobileEditOptionsOpen(false); setDesktopEditOptionsOpen(false); } }, [editToggleDisabled]); const buttonHeight = isCompact ? 'h-9' : 'h-8'; const editToggleIconClass = isCompact ? 'h-5 w-5' : 'h-4 w-4'; const controlIconSize = isCompact ? 'h-5 w-5' : 'h-4 w-4'; const controlTextSize = isCompact ? 'typography-micro' : 'typography-meta'; const inlineGapClass = isCompact ? 'gap-x-2' : 'gap-x-3'; const editPermissionMenuLabel = editModeShortLabels[effectiveEditMode]; const renderEditModeIcon = React.useCallback((mode: EditPermissionMode, iconClass = editToggleIconClass) => { const combinedClassName = cn(iconClass, 'flex-shrink-0'); const modeColors = getEditModeColors(mode); const iconColor = modeColors ? modeColors.text : 'var(--foreground)'; const iconStyle = { color: iconColor }; if (mode === 'full') { return ; } if (mode === 'allow') { return ; } if (mode === 'deny') { return ; } return ; }, [editToggleIconClass]); const handleEditPermissionSelect = React.useCallback((mode: EditPermissionMode) => { if (editToggleDisabled || !currentSessionId || !currentAgentName || isModeDisabled(mode)) { return; } setSessionAgentEditMode(currentSessionId, currentAgentName, mode, cascadeDefaultMode); setAgentMenuOpen(false); setMobileEditOptionsOpen(false); setDesktopEditOptionsOpen(false); }, [cascadeDefaultMode, editToggleDisabled, currentSessionId, currentAgentName, setSessionAgentEditMode, setAgentMenuOpen, setDesktopEditOptionsOpen, isModeDisabled]); const currentProvider = getCurrentProvider(); const models = Array.isArray(currentProvider?.models) ? currentProvider.models : []; const currentMetadata = currentProviderId && currentModelId ? getModelMetadata(currentProviderId, currentModelId) : undefined; const currentCapabilityIcons = getCapabilityIcons(currentMetadata); const inputModalityIcons = getModalityIcons(currentMetadata, 'input'); const outputModalityIcons = getModalityIcons(currentMetadata, 'output'); const costRows = [ { label: 'Input', value: formatCost(currentMetadata?.cost?.input) }, { label: 'Output', value: formatCost(currentMetadata?.cost?.output) }, { label: 'Cache read', value: formatCost(currentMetadata?.cost?.cache_read) }, { label: 'Cache write', value: formatCost(currentMetadata?.cost?.cache_write) }, ]; const limitRows = [ { label: 'Context', value: formatTokens(currentMetadata?.limit?.context) }, { label: 'Output', value: formatTokens(currentMetadata?.limit?.output) }, ]; const prevAgentNameRef = React.useRef(undefined); const currentSessionMessageCount = currentSessionId ? (messages.get(currentSessionId)?.length ?? -1) : -1; const sessionInitializationRef = React.useRef<{ sessionId: string; resolved: boolean; inFlight: boolean; } | null>(null); const tryApplyModelSelection = React.useCallback( (providerId: string, modelId: string, agentName?: string): ModelApplyResult => { if (!providerId || !modelId) { return 'model-missing'; } const provider = providers.find(p => p.id === providerId); if (!provider) { return 'provider-missing'; } const providerModels = Array.isArray(provider.models) ? provider.models : []; const modelExists = providerModels.find((m: ProviderModel) => m.id === modelId); if (!modelExists) { return 'model-missing'; } setProvider(providerId); setModel(modelId); if (currentSessionId && agentName) { saveAgentModelForSession(currentSessionId, agentName, providerId, modelId); } return 'applied'; }, [providers, setProvider, setModel, currentSessionId, saveAgentModelForSession], ); React.useEffect(() => { if (!currentSessionId) { sessionInitializationRef.current = null; return; } if (!contextHydrated || providers.length === 0 || agents.length === 0) { return; } if (!sessionInitializationRef.current || sessionInitializationRef.current.sessionId !== currentSessionId) { sessionInitializationRef.current = { sessionId: currentSessionId, resolved: false, inFlight: false }; } const state = sessionInitializationRef.current; if (!state || state.resolved || state.inFlight) { return; } let isCancelled = false; const finalize = () => { if (isCancelled) { return; } const refState = sessionInitializationRef.current; if (refState && refState.sessionId === currentSessionId) { refState.resolved = true; refState.inFlight = false; } }; const applySavedSelections = (): 'resolved' | 'waiting' | 'continue' => { const savedAgentName = getSessionAgentSelection(currentSessionId); if (savedAgentName) { if (currentAgentName !== savedAgentName) { setAgent(savedAgentName); } const savedModel = getAgentModelForSession(currentSessionId, savedAgentName); if (savedModel) { const result = tryApplyModelSelection(savedModel.providerId, savedModel.modelId, savedAgentName); if (result === 'applied') { return 'resolved'; } if (result === 'provider-missing') { return 'waiting'; } } else { return 'resolved'; } } for (const agent of agents) { const selection = getAgentModelForSession(currentSessionId, agent.name); if (!selection) { continue; } if (currentAgentName !== agent.name) { setAgent(agent.name); } saveSessionAgentSelection(currentSessionId, agent.name); const result = tryApplyModelSelection(selection.providerId, selection.modelId, agent.name); if (result === 'applied') { return 'resolved'; } if (result === 'provider-missing') { return 'waiting'; } } return 'continue'; }; const applyFallbackAgent = () => { if (agents.length === 0) { return; } const primaryAgents = agents.filter(agent => isPrimaryMode(agent.mode)); const fallbackAgent = agents.find(agent => agent.name === 'build') || primaryAgents[0] || agents[0]; if (!fallbackAgent) { return; } saveSessionAgentSelection(currentSessionId, fallbackAgent.name); if (currentAgentName !== fallbackAgent.name) { setAgent(fallbackAgent.name); } if (fallbackAgent.model?.providerID && fallbackAgent.model?.modelID) { tryApplyModelSelection(fallbackAgent.model.providerID, fallbackAgent.model.modelID, fallbackAgent.name); } }; const resolveSessionPreferences = async () => { try { const savedOutcome = applySavedSelections(); if (savedOutcome === 'resolved') { finalize(); return; } if (savedOutcome === 'waiting') { return; } if (currentSessionMessageCount === -1) { return; } if (currentSessionMessageCount > 0) { state.inFlight = true; try { const discoveredChoices = await analyzeAndSaveExternalSessionChoices(currentSessionId, agents); if (isCancelled) { return; } if (discoveredChoices.size > 0) { let latestAgent: string | null = null; let latestTimestamp = -Infinity; for (const [agentName, choice] of discoveredChoices) { if (choice.timestamp > latestTimestamp) { latestTimestamp = choice.timestamp; latestAgent = agentName; } } if (latestAgent) { saveSessionAgentSelection(currentSessionId, latestAgent); if (currentAgentName !== latestAgent) { setAgent(latestAgent); } const latestChoice = discoveredChoices.get(latestAgent); if (latestChoice) { const applyResult = tryApplyModelSelection( latestChoice.providerId, latestChoice.modelId, latestAgent, ); if (applyResult === 'applied') { finalize(); return; } if (applyResult === 'provider-missing') { return; } } else { finalize(); return; } } } } catch (error) { if (!isCancelled) { console.error('[ModelControls] Error resolving session from messages:', error); } } finally { const refState = sessionInitializationRef.current; if (!isCancelled && refState && refState.sessionId === currentSessionId) { refState.inFlight = false; } } } applyFallbackAgent(); finalize(); } catch (error) { if (!isCancelled) { console.error('[ModelControls] Error in session switch:', error); } } }; resolveSessionPreferences(); return () => { isCancelled = true; }; }, [ currentSessionId, currentSessionMessageCount, agents, currentAgentName, getSessionAgentSelection, getAgentModelForSession, setAgent, tryApplyModelSelection, analyzeAndSaveExternalSessionChoices, saveSessionAgentSelection, contextHydrated, providers, ]); React.useEffect(() => { if (!contextHydrated || !currentSessionId || providers.length === 0 || agents.length === 0) { return; } const savedAgentName = getSessionAgentSelection(currentSessionId); const preferredAgent = savedAgentName || currentAgentName; if (!preferredAgent) { return; } const preferredSelection = getAgentModelForSession(currentSessionId, preferredAgent); if (!preferredSelection) { return; } const provider = providers.find(p => p.id === preferredSelection.providerId); if (!provider) { return; } const modelExists = Array.isArray(provider.models) ? provider.models.some((m: ProviderModel) => m.id === preferredSelection.modelId) : false; if (!modelExists) { return; } const providerMatches = currentProviderId === preferredSelection.providerId; const modelMatches = currentModelId === preferredSelection.modelId; if (providerMatches && modelMatches) { return; } if (preferredAgent !== currentAgentName) { setAgent(preferredAgent); } tryApplyModelSelection(preferredSelection.providerId, preferredSelection.modelId, preferredAgent); }, [ contextHydrated, currentSessionId, currentAgentName, currentProviderId, currentModelId, providers, agents, getSessionAgentSelection, getAgentModelForSession, tryApplyModelSelection, setAgent, ]); React.useEffect(() => { if (!contextHydrated) { return; } const handleAgentSwitch = async () => { try { if (currentAgentName !== prevAgentNameRef.current) { prevAgentNameRef.current = currentAgentName; if (currentAgentName && currentSessionId) { await new Promise(resolve => setTimeout(resolve, 50)); const persistedChoice = getAgentModelForSession(currentSessionId, currentAgentName); if (persistedChoice) { const result = tryApplyModelSelection( persistedChoice.providerId, persistedChoice.modelId, currentAgentName, ); if (result === 'applied' || result === 'provider-missing') { return; } } const agent = agents.find(a => a.name === currentAgentName); if (agent?.model?.providerID && agent?.model?.modelID) { const result = tryApplyModelSelection( agent.model.providerID, agent.model.modelID, currentAgentName, ); if (result === 'provider-missing') { return; } } } } } catch (error) { console.error('[ModelControls] Agent change error:', error); } }; handleAgentSwitch(); }, [currentAgentName, currentSessionId, getAgentModelForSession, tryApplyModelSelection, agents, contextHydrated]); const handleAgentChange = (agentName: string) => { try { setAgent(agentName); setAgentMenuOpen(false); if (currentSessionId) { saveSessionAgentSelection(currentSessionId, agentName); } if (isCompact) { closeMobilePanel(); } } catch (error) { console.error('[ModelControls] Handle agent change error:', error); } }; const handleProviderAndModelChange = (providerId: string, modelId: string) => { try { const result = tryApplyModelSelection(providerId, modelId, currentAgentName || undefined); if (result !== 'applied') { if (result === 'provider-missing') { console.error('[ModelControls] Provider not available for selection:', providerId); } else if (result === 'model-missing') { console.error('[ModelControls] Model not available for selection:', { providerId, modelId }); } return; } if (isCompact) { closeMobilePanel(); } } catch (error) { console.error('[ModelControls] Handle model change error:', error); } }; const getModelDisplayName = (model: ProviderModel | undefined) => { const name = (typeof model?.name === 'string' ? model.name : (typeof model?.id === 'string' ? model.id : '')); if (name.length > 40) { return name.substring(0, 37) + '...'; } return name; }; const getProviderDisplayName = () => { const provider = providers.find(p => p.id === currentProviderId); return provider?.name || currentProviderId; }; const getCurrentModelDisplayName = () => { if (!currentProviderId || !currentModelId) return 'Not selected'; if (models.length === 0) return 'Not selected'; const currentModel = models.find((m: ProviderModel) => m.id === currentModelId); return getModelDisplayName(currentModel); }; const getAgentDisplayName = () => { if (!currentAgentName) { const primaryAgents = agents.filter(agent => isPrimaryMode(agent.mode)); const buildAgent = primaryAgents.find(agent => agent.name === 'build'); const defaultAgent = buildAgent || primaryAgents[0]; return defaultAgent ? capitalizeAgentName(defaultAgent.name) : 'Select Agent'; } const agent = agents.find(a => a.name === currentAgentName); return agent ? capitalizeAgentName(agent.name) : capitalizeAgentName(currentAgentName); }; const capitalizeAgentName = (name: string) => { return name.charAt(0).toUpperCase() + name.slice(1); }; const renderIconBadge = (IconComp: IconComponent, label: string, key: string) => ( ); const toggleMobileProviderExpansion = React.useCallback((providerId: string) => { setExpandedMobileProviders((prev) => { const next = new Set(prev); if (next.has(providerId)) { next.delete(providerId); } else { next.add(providerId); } return next; }); }, []); const handleLongPressStart = React.useCallback((type: 'model' | 'agent') => { if (longPressTimerRef.current) { clearTimeout(longPressTimerRef.current); } longPressTimerRef.current = setTimeout(() => { setMobileTooltipOpen(type); }, 500); }, []); const handleLongPressEnd = React.useCallback(() => { if (longPressTimerRef.current) { clearTimeout(longPressTimerRef.current); } }, []); React.useEffect(() => { return () => { if (longPressTimerRef.current) { clearTimeout(longPressTimerRef.current); } }; }, []); const renderMobileModelTooltip = () => { if (!isCompact || mobileTooltipOpen !== 'model') return null; return (
{}
Provider
{getProviderDisplayName()}
{} {currentCapabilityIcons.length > 0 && (
Capabilities
{currentCapabilityIcons.map(({ key, icon, label }) => (
{renderIconBadge(icon, label, `cap-${key}`)} {label}
))}
)} {} {(inputModalityIcons.length > 0 || outputModalityIcons.length > 0) && (
Modalities
{inputModalityIcons.length > 0 && (
Input
{inputModalityIcons.map(({ key, icon, label }) => renderIconBadge(icon, `${label} input`, `input-${key}`))}
)} {outputModalityIcons.length > 0 && (
Output
{outputModalityIcons.map(({ key, icon, label }) => renderIconBadge(icon, `${label} output`, `output-${key}`))}
)}
)} {}
Limits
Context {formatTokens(currentMetadata?.limit?.context)}
Output {formatTokens(currentMetadata?.limit?.output)}
{}
Metadata
Knowledge {formatKnowledge(currentMetadata?.knowledge)}
Release {formatDate(currentMetadata?.release_date)}
); }; const renderMobileAgentTooltip = () => { if (!isCompact || mobileTooltipOpen !== 'agent' || !currentAgent) return null; const enabledTools = Object.entries(currentAgent.tools || {}) .filter(([, enabled]) => enabled) .map(([tool]) => tool) .sort(); const hasCustomPrompt = Boolean(currentAgent.prompt && currentAgent.prompt.trim().length > 0); const hasModelConfig = currentAgent.model?.providerID && currentAgent.model?.modelID; const hasTemperatureOrTopP = currentAgent.temperature !== undefined || currentAgent.topP !== undefined; const getPermissionIcon = (permission?: string) => { const mode: EditPermissionMode = permission === 'full' || permission === 'allow' || permission === 'deny' ? permission : 'ask'; return renderEditModeIcon(mode, 'h-3.5 w-3.5'); }; const getPermissionLabel = (permission?: string) => { if (permission === 'full') return 'Full'; if (permission === 'allow') return 'Allow'; if (permission === 'deny') return 'Deny'; return 'Ask'; }; return (
{} {currentAgent.description && (
{currentAgent.description}
)} {}
Mode
{currentAgent.mode === 'primary' ? 'Primary' : currentAgent.mode === 'subagent' ? 'Subagent' : currentAgent.mode === 'all' ? 'All' : '—'}
{} {(hasModelConfig || hasTemperatureOrTopP) && (
Model
{hasModelConfig && (
{currentAgent.model!.providerID} / {currentAgent.model!.modelID}
)} {hasTemperatureOrTopP && (
{currentAgent.temperature !== undefined && (
Temperature {currentAgent.temperature}
)} {currentAgent.topP !== undefined && (
Top P {currentAgent.topP}
)}
)}
)} {}
Tools
{enabledTools.length > 0 ? (
{enabledTools.map((tool) => ( {tool} ))}
) : (
All enabled
)}
{}
Permissions
Edit
{getPermissionIcon(currentAgent.permission?.edit)} {getPermissionLabel(currentAgent.permission?.edit)}
Bash
{getPermissionIcon(typeof currentAgent.permission?.bash === 'string' ? currentAgent.permission.bash : undefined)} {getPermissionLabel(typeof currentAgent.permission?.bash === 'string' ? currentAgent.permission.bash : undefined)}
WebFetch
{getPermissionIcon(currentAgent.permission?.webfetch)} {getPermissionLabel(currentAgent.permission?.webfetch)}
{} {hasCustomPrompt && (
Custom Prompt
)}
); }; const renderMobileModelPanel = () => { if (!isCompact) return null; const normalizedQuery = mobileModelQuery.trim().toLowerCase(); const filteredProviders = providers .map((provider) => { const providerModels = Array.isArray(provider.models) ? provider.models : []; const matchesProvider = normalizedQuery.length === 0 ? true : provider.name.toLowerCase().includes(normalizedQuery) || provider.id.toLowerCase().includes(normalizedQuery); const matchingModels = normalizedQuery.length === 0 ? providerModels : providerModels.filter((model: ProviderModel) => { const name = getModelDisplayName(model).toLowerCase(); const id = typeof model.id === 'string' ? model.id.toLowerCase() : ''; return name.includes(normalizedQuery) || id.includes(normalizedQuery); }); return { provider, providerModels: matchingModels, matchesProvider }; }) .filter(({ matchesProvider, providerModels }) => matchesProvider || providerModels.length > 0); return (
setMobileModelQuery(event.target.value)} placeholder="Search providers or models" className="pl-7 h-8 typography-meta" /> {mobileModelQuery && ( )}
{filteredProviders.length === 0 && (
No providers or models match your search.
)} {filteredProviders.map(({ provider, providerModels }) => { if (providerModels.length === 0 && !normalizedQuery.length) { return null; } const isActiveProvider = provider.id === currentProviderId; const isExpanded = expandedMobileProviders.has(provider.id) || normalizedQuery.length > 0; return (
{isExpanded && providerModels.length > 0 && (
{providerModels.map((model: ProviderModel) => { const isSelected = isActiveProvider && model.id === currentModelId; const metadata = getModelMetadata(provider.id, model.id!); const capabilityIcons = getCapabilityIcons(metadata).slice(0, 3); const inputIcons = getModalityIcons(metadata, 'input'); return ( ); })}
)}
); })}
); }; const renderMobileAgentPanel = () => { if (!isCompact) return null; const primaryAgents = agents.filter(agent => isPrimaryMode(agent.mode)); return (
{primaryAgents.map((agent) => { const isSelected = agent.name === currentAgentName; const agentColor = getAgentColor(agent.name); return ( ); })}
{mobileEditOptionsOpen && !editToggleDisabled && (
{editPermissionOptions.map((option) => { const isSelected = option.mode === effectiveEditMode; const optionColors = getEditModeColors(option.mode); return ( ); })}
)}
); }; const renderModelTooltipContent = () => ( {currentMetadata ? (
{currentMetadata.name || getCurrentModelDisplayName()} {getProviderDisplayName()}
Capabilities
{currentCapabilityIcons.length > 0 ? ( currentCapabilityIcons.map(({ key, icon, label }) => renderIconBadge(icon, label, `cap-${key}`) ) ) : ( )}
Modalities
Input
{inputModalityIcons.length > 0 ? inputModalityIcons.map(({ key, icon, label }) => renderIconBadge(icon, `${label} input`, `input-${key}`) ) : }
Output
{outputModalityIcons.length > 0 ? outputModalityIcons.map(({ key, icon, label }) => renderIconBadge(icon, `${label} output`, `output-${key}`) ) : }
Cost ($/1M tokens) {costRows.map((row) => (
{row.label} {row.value}
))}
Limits {limitRows.map((row) => (
{row.label} {row.value}
))}
Metadata
Knowledge {formatKnowledge(currentMetadata.knowledge)}
Release {formatDate(currentMetadata.release_date)}
) : (
Model metadata unavailable.
)}
); const renderModelSelector = () => ( {!isCompact ? (
{currentProviderId ? ( <> ) : ( )} {getCurrentModelDisplayName()}
{providers.map((provider) => { const providerModels = Array.isArray(provider.models) ? provider.models : []; if (providerModels.length === 0) { return ( {provider.name} (No models) ); } return ( {provider.name} {providerModels.map((model: ProviderModel) => { const metadata = getModelMetadata(provider.id, model.id!); const capabilityIcons = getCapabilityIcons(metadata).map((icon) => ({ ...icon, id: `cap-${icon.key}`, })); const modalityIcons = [ ...getModalityIcons(metadata, 'input'), ...getModalityIcons(metadata, 'output'), ]; const uniqueModalityIcons = Array.from( new Map(modalityIcons.map((icon) => [icon.key, icon])).values() ).map((icon) => ({ ...icon, id: `mod-${icon.key}` })); const indicatorIcons = [...capabilityIcons, ...uniqueModalityIcons]; const contextTokens = formatTokens(metadata?.limit?.context); const outputTokens = formatTokens(metadata?.limit?.output); return ( { handleProviderAndModelChange(provider.id as string, model.id as string); }} >
{getModelDisplayName(model)} {metadata?.limit?.context || metadata?.limit?.output ? ( {metadata?.limit?.context ? `${contextTokens} ctx` : ''} {metadata?.limit?.context && metadata?.limit?.output ? ' • ' : ''} {metadata?.limit?.output ? `${outputTokens} out` : ''} ) : null}
{indicatorIcons.map(({ id, icon: Icon, label }) => ( ))}
); })}
); })}
) : ( )} {renderModelTooltipContent()}
); const renderAgentTooltipContent = () => { if (!currentAgent) { return (
No agent selected.
); } const enabledTools = Object.entries(currentAgent.tools || {}) .filter(([, enabled]) => enabled) .map(([tool]) => tool) .sort(); const hasCustomPrompt = Boolean(currentAgent.prompt && currentAgent.prompt.trim().length > 0); const hasModelConfig = currentAgent.model?.providerID && currentAgent.model?.modelID; const hasTemperatureOrTopP = currentAgent.temperature !== undefined || currentAgent.topP !== undefined; const getPermissionIcon = (permission?: string) => { const mode: EditPermissionMode = permission === 'full' || permission === 'allow' || permission === 'deny' ? permission : 'ask'; return renderEditModeIcon(mode, 'h-3.5 w-3.5'); }; const getPermissionLabel = (permission?: string) => { if (permission === 'full') return 'Full'; if (permission === 'allow') return 'Allow'; if (permission === 'deny') return 'Deny'; return 'Ask'; }; return (
{capitalizeAgentName(currentAgent.name)} {currentAgent.description && ( {currentAgent.description} )}
Mode {currentAgent.mode === 'primary' ? 'Primary' : currentAgent.mode === 'subagent' ? 'Subagent' : currentAgent.mode === 'all' ? 'All' : '—'}
{(hasModelConfig || hasTemperatureOrTopP) && (
Model {hasModelConfig ? ( {currentAgent.model!.providerID} / {currentAgent.model!.modelID} ) : ( )} {hasTemperatureOrTopP && (
{currentAgent.temperature !== undefined && (
Temperature {currentAgent.temperature}
)} {currentAgent.topP !== undefined && (
Top P {currentAgent.topP}
)}
)}
)}
Tools {enabledTools.length > 0 ? (
{enabledTools.map((tool) => ( {tool} ))}
) : ( All enabled )}
Permissions
Edit
{getPermissionIcon(currentAgent.permission?.edit)} {getPermissionLabel(currentAgent.permission?.edit)}
Bash
{getPermissionIcon(typeof currentAgent.permission?.bash === 'string' ? currentAgent.permission.bash : undefined)} {getPermissionLabel(typeof currentAgent.permission?.bash === 'string' ? currentAgent.permission.bash : undefined)}
WebFetch
{getPermissionIcon(currentAgent.permission?.webfetch)} {getPermissionLabel(currentAgent.permission?.webfetch)}
{hasCustomPrompt && (
Custom Prompt
)}
); }; const renderAgentSelector = () => { if (!isCompact) { return (
{getAgentDisplayName()}
{agents.filter(agent => isPrimaryMode(agent.mode)).map((agent) => ( handleAgentChange(agent.name)} >
{capitalizeAgentName(agent.name)}
{agent.description && ( {agent.description} )}
))}
{desktopEditOptionsOpen && !editToggleDisabled && (
{editPermissionOptions.map((option) => { const isSelected = option.mode === effectiveEditMode; const optionColors = getEditModeColors(option.mode); return ( ); })}
)}
{renderAgentTooltipContent()}
); } return ( ); }; const inlineClassName = cn('@container/model-controls flex items-center min-w-0', inlineGapClass, className); return ( <>
{renderModelSelector()}
{renderAgentSelector()}
{renderMobileModelPanel()} {renderMobileAgentPanel()} {renderMobileModelTooltip()} {renderMobileAgentTooltip()} ); };