diff --git a/apps/ui/src/components/views/board-view/shared/model-selector.tsx b/apps/ui/src/components/views/board-view/shared/model-selector.tsx index 323190c8..dc95f39f 100644 --- a/apps/ui/src/components/views/board-view/shared/model-selector.tsx +++ b/apps/ui/src/components/views/board-view/shared/model-selector.tsx @@ -4,7 +4,6 @@ import { Badge } from '@/components/ui/badge'; import { Brain, AlertTriangle } from 'lucide-react'; import { AnthropicIcon, CursorIcon, OpenAIIcon } from '@/components/ui/provider-icon'; import { cn } from '@/lib/utils'; -import type { ModelAlias } from '@/store/app-store'; import { useAppStore } from '@/store/app-store'; import { useSetupStore } from '@/store/setup-store'; import { getModelProvider, PROVIDER_PREFIXES, stripProviderPrefix } from '@automaker/types'; @@ -19,6 +18,10 @@ interface ModelSelectorProps { testIdPrefix?: string; } +const CODEX_EMPTY_AVAILABLE_MESSAGE = 'No Codex models available'; +const CODEX_EMPTY_ENABLED_MESSAGE = + 'No Codex models enabled. Enable models in Settings → AI Providers.'; + export function ModelSelector({ selectedModel, onModelSelect, @@ -27,6 +30,8 @@ export function ModelSelector({ const { enabledCursorModels, cursorDefaultModel, + enabledCodexModels, + codexDefaultModel, codexModels, codexModelsLoading, codexModelsError, @@ -49,8 +54,10 @@ export function ModelSelector({ } }, [isCodexAvailable, codexModels.length, codexModelsLoading, fetchCodexModels]); + const enabledCodexModelIds = new Set(enabledCodexModels); + // Transform codex models from store to ModelOption format - const dynamicCodexModels: ModelOption[] = codexModels.map((model) => { + const codexModelOptions: ModelOption[] = codexModels.map((model) => { // Infer badge based on tier let badge: string | undefined; if (model.tier === 'premium') badge = 'Premium'; @@ -67,6 +74,10 @@ export function ModelSelector({ }; }); + const enabledCodexModelOptions = codexModelOptions.filter((model) => + enabledCodexModelIds.has(model.id) + ); + // Filter Cursor models based on enabled models from global settings const filteredCursorModels = CURSOR_MODELS.filter((model) => { // Extract the cursor model ID from the prefixed ID (e.g., "cursor-auto" -> "auto") @@ -74,21 +85,36 @@ export function ModelSelector({ return enabledCursorModels.includes(cursorModelId as any); }); + const hasEnabledCodexModels = enabledCodexModelOptions.length > 0; + const codexDefaultSelection = + codexModelOptions.find((model) => model.id === codexDefaultModel)?.id || + enabledCodexModelOptions[0]?.id || + codexModelOptions[0]?.id; + const handleProviderChange = (provider: ModelProvider) => { if (provider === 'cursor' && selectedProvider !== 'cursor') { // Switch to Cursor's default model (from global settings) onModelSelect(`${PROVIDER_PREFIXES.cursor}${cursorDefaultModel}`); } else if (provider === 'codex' && selectedProvider !== 'codex') { - // Switch to Codex's default model (use isDefault flag from dynamic models) - const defaultModel = codexModels.find((m) => m.isDefault); - const defaultModelId = defaultModel?.id || codexModels[0]?.id || 'codex-gpt-5.2-codex'; - onModelSelect(defaultModelId); + // Switch to Codex's default model (from global settings) + if (codexDefaultSelection) { + onModelSelect(codexDefaultSelection); + } } else if (provider === 'claude' && selectedProvider !== 'claude') { // Switch to Claude's default model onModelSelect('sonnet'); } }; + const showCodexAvailableEmpty = + !codexModelsLoading && !codexModelsError && codexModelOptions.length === 0; + const showCodexEnabledEmpty = + !codexModelsLoading && + !codexModelsError && + codexModelOptions.length > 0 && + !hasEnabledCodexModels; + const showCodexList = !codexModelsLoading && !codexModelsError && hasEnabledCodexModels; + return (
{/* Provider Selection */} @@ -272,7 +298,7 @@ export function ModelSelector({
{/* Loading state */} - {codexModelsLoading && dynamicCodexModels.length === 0 && ( + {codexModelsLoading && codexModelOptions.length === 0 && (
Loading models... @@ -297,15 +323,21 @@ export function ModelSelector({ )} {/* Model list */} - {!codexModelsLoading && !codexModelsError && dynamicCodexModels.length === 0 && ( + {showCodexAvailableEmpty && (
- No Codex models available + {CODEX_EMPTY_AVAILABLE_MESSAGE}
)} - {!codexModelsLoading && dynamicCodexModels.length > 0 && ( + {showCodexEnabledEmpty && ( +
+ {CODEX_EMPTY_ENABLED_MESSAGE} +
+ )} + + {showCodexList && (
- {dynamicCodexModels.map((option) => { + {enabledCodexModelOptions.map((option) => { const isSelected = selectedModel === option.id; return (