import React, { useCallback, useEffect, useMemo, useState } from "react"; import { Check, ChevronDown } from "lucide-react"; import { Trans, useTranslation } from "react-i18next"; import { useServerPlatform } from "../../../../hooks/useServerPlatform"; import SessionProviderLogo from "../../../llm-logo-provider/SessionProviderLogo"; import { CLAUDE_MODELS, CURSOR_MODELS, CODEX_MODELS, GEMINI_MODELS, PROVIDERS, } from "../../../../../shared/modelConstants"; import type { ProjectSession, LLMProvider } from "../../../../types/app"; import { NextTaskBanner } from "../../../task-master"; import { Dialog, DialogTrigger, DialogContent, DialogTitle, Command, CommandInput, CommandList, CommandEmpty, CommandGroup, CommandItem, Card, } from "../../../../shared/view/ui"; const MOD_KEY = typeof navigator !== "undefined" && /Mac|iPhone|iPad/.test(navigator.platform) ? "⌘" : "Ctrl"; type ProviderSelectionEmptyStateProps = { selectedSession: ProjectSession | null; currentSessionId: string | null; provider: LLMProvider; setProvider: (next: LLMProvider) => void; textareaRef: React.RefObject; claudeModel: string; setClaudeModel: (model: string) => void; cursorModel: string; setCursorModel: (model: string) => void; codexModel: string; setCodexModel: (model: string) => void; geminiModel: string; setGeminiModel: (model: string) => void; tasksEnabled: boolean; isTaskMasterInstalled: boolean | null; onShowAllTasks?: (() => void) | null; setInput: React.Dispatch>; }; type ProviderGroup = { id: LLMProvider; name: string; models: { value: string; label: string }[]; }; const PROVIDER_GROUPS: ProviderGroup[] = PROVIDERS.map((p) => ({ id: p.id as LLMProvider, name: p.name, models: p.models.OPTIONS, })); function getModelConfig(p: LLMProvider) { if (p === "claude") return CLAUDE_MODELS; if (p === "codex") return CODEX_MODELS; if (p === "gemini") return GEMINI_MODELS; return CURSOR_MODELS; } function getCurrentModel( p: LLMProvider, c: string, cu: string, co: string, g: string, ) { if (p === "claude") return c; if (p === "codex") return co; if (p === "gemini") return g; return cu; } function getProviderDisplayName(p: LLMProvider) { if (p === "claude") return "Claude"; if (p === "cursor") return "Cursor"; if (p === "codex") return "Codex"; return "Gemini"; } export default function ProviderSelectionEmptyState({ selectedSession, currentSessionId, provider, setProvider, textareaRef, claudeModel, setClaudeModel, cursorModel, setCursorModel, codexModel, setCodexModel, geminiModel, setGeminiModel, tasksEnabled, isTaskMasterInstalled, onShowAllTasks, setInput, }: ProviderSelectionEmptyStateProps) { const { t } = useTranslation("chat"); const { isWindowsServer } = useServerPlatform(); const [dialogOpen, setDialogOpen] = useState(false); const visibleProviderGroups = useMemo( () => (isWindowsServer ? PROVIDER_GROUPS.filter((p) => p.id !== "cursor") : PROVIDER_GROUPS), [isWindowsServer], ); useEffect(() => { if (isWindowsServer && provider === "cursor") { setProvider("claude"); localStorage.setItem("selected-provider", "claude"); } }, [isWindowsServer, provider, setProvider]); const nextTaskPrompt = t("tasks.nextTaskPrompt", { defaultValue: "Start the next task", }); const currentModel = getCurrentModel( provider, claudeModel, cursorModel, codexModel, geminiModel, ); const currentModelLabel = useMemo(() => { const config = getModelConfig(provider); const found = config.OPTIONS.find( (o: { value: string; label: string }) => o.value === currentModel, ); return found?.label || currentModel; }, [provider, currentModel]); const setModelForProvider = useCallback( (providerId: LLMProvider, modelValue: string) => { if (providerId === "claude") { setClaudeModel(modelValue); localStorage.setItem("claude-model", modelValue); } else if (providerId === "codex") { setCodexModel(modelValue); localStorage.setItem("codex-model", modelValue); } else if (providerId === "gemini") { setGeminiModel(modelValue); localStorage.setItem("gemini-model", modelValue); } else { setCursorModel(modelValue); localStorage.setItem("cursor-model", modelValue); } }, [setClaudeModel, setCursorModel, setCodexModel, setGeminiModel], ); const handleModelSelect = useCallback( (providerId: LLMProvider, modelValue: string) => { setProvider(providerId); localStorage.setItem("selected-provider", providerId); setModelForProvider(providerId, modelValue); setDialogOpen(false); setTimeout(() => textareaRef.current?.focus(), 100); }, [setProvider, setModelForProvider, textareaRef], ); if (!selectedSession && !currentSessionId) { return (

{t("providerSelection.title")}

{t("providerSelection.description")}

{getProviderDisplayName(provider)} · {currentModelLabel}

{t("providerSelection.clickToChange", { defaultValue: "Click to change model", })}

Model Selector {t("providerSelection.noModelsFound", { defaultValue: "No models found.", })} {visibleProviderGroups.map((group, idx) => ( 0 ? "border-t border-border/40 [&_[cmdk-group-heading]]:mt-1 [&_[cmdk-group-heading]]:uppercase [&_[cmdk-group-heading]]:tracking-wider" : "[&_[cmdk-group-heading]]:uppercase [&_[cmdk-group-heading]]:tracking-wider" } heading={ {group.name} } > {group.models.map((model) => { const isSelected = provider === group.id && currentModel === model.value; return ( handleModelSelect(group.id, model.value)} className="ml-4 border-l border-border/40 pl-4" > {model.label} {isSelected && ( )} ); })} ))}

{ { claude: t("providerSelection.readyPrompt.claude", { model: claudeModel, }), cursor: t("providerSelection.readyPrompt.cursor", { model: cursorModel, }), codex: t("providerSelection.readyPrompt.codex", { model: codexModel, }), gemini: t("providerSelection.readyPrompt.gemini", { model: geminiModel, }), }[provider] }

), }} />

{provider && tasksEnabled && isTaskMasterInstalled && (
setInput(nextTaskPrompt)} onShowAllTasks={onShowAllTasks} />
)}
); } if (selectedSession) { return (

{t("session.continue.title")}

{t("session.continue.description")}

{tasksEnabled && isTaskMasterInstalled && (
setInput(nextTaskPrompt)} onShowAllTasks={onShowAllTasks} />
)}
); } return null; }