import { useState, useEffect, useCallback } from 'react'; import { Label } from '@/components/ui/label'; import { Badge } from '@/components/ui/badge'; import { Checkbox } from '@/components/ui/checkbox'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select'; import { Terminal, Info } from 'lucide-react'; import { toast } from 'sonner'; import { getHttpApiClient } from '@/lib/http-api-client'; import { useAppStore } from '@/store/app-store'; import { cn } from '@/lib/utils'; import type { CursorModelId, CursorModelConfig, CursorCliConfig } from '@automaker/types'; import { CURSOR_MODEL_MAP } from '@automaker/types'; import { CursorCliStatus, CursorCliStatusSkeleton, ModelConfigSkeleton, } from '../cli-status/cursor-cli-status'; interface CursorStatus { installed: boolean; version?: string; authenticated: boolean; method?: string; } export function CursorSettingsTab() { const { currentProject } = useAppStore(); const [status, setStatus] = useState(null); const [config, setConfig] = useState(null); const [availableModels, setAvailableModels] = useState([]); const [isLoading, setIsLoading] = useState(true); const [isSaving, setIsSaving] = useState(false); const loadData = useCallback(async () => { setIsLoading(true); try { const api = getHttpApiClient(); const statusResult = await api.setup.getCursorStatus(); if (statusResult.success) { setStatus({ installed: statusResult.installed ?? false, version: statusResult.version ?? undefined, authenticated: statusResult.auth?.authenticated ?? false, method: statusResult.auth?.method, }); } // Only load config if we have a project path if (currentProject?.path) { const configResult = await api.setup.getCursorConfig(currentProject.path); if (configResult.success) { setConfig({ defaultModel: configResult.config?.defaultModel as CursorModelId | undefined, models: configResult.config?.models as CursorModelId[] | undefined, mcpServers: configResult.config?.mcpServers, rules: configResult.config?.rules, }); if (configResult.availableModels) { setAvailableModels(configResult.availableModels as CursorModelConfig[]); } else { setAvailableModels(Object.values(CURSOR_MODEL_MAP)); } } else { // Set defaults if no config setAvailableModels(Object.values(CURSOR_MODEL_MAP)); } } else { // No project, just show available models setAvailableModels(Object.values(CURSOR_MODEL_MAP)); } } catch (error) { console.error('Failed to load Cursor settings:', error); toast.error('Failed to load Cursor settings'); } finally { setIsLoading(false); } }, [currentProject?.path]); useEffect(() => { loadData(); }, [loadData]); const handleDefaultModelChange = async (model: CursorModelId) => { if (!currentProject?.path) { toast.error('No project selected'); return; } setIsSaving(true); try { const api = getHttpApiClient(); const result = await api.setup.setCursorDefaultModel(currentProject.path, model); if (result.success) { setConfig((prev) => (prev ? { ...prev, defaultModel: model } : { defaultModel: model })); toast.success('Default model updated'); } else { toast.error(result.error || 'Failed to update default model'); } } catch (error) { toast.error('Failed to update default model'); } finally { setIsSaving(false); } }; const handleModelToggle = async (model: CursorModelId, enabled: boolean) => { if (!currentProject?.path) { toast.error('No project selected'); return; } const currentModels = config?.models || ['auto']; const newModels = enabled ? [...currentModels, model] : currentModels.filter((m) => m !== model); setIsSaving(true); try { const api = getHttpApiClient(); const result = await api.setup.setCursorModels(currentProject.path, newModels); if (result.success) { setConfig((prev) => (prev ? { ...prev, models: newModels } : { models: newModels })); } else { toast.error(result.error || 'Failed to update models'); } } catch (error) { toast.error('Failed to update models'); } finally { setIsSaving(false); } }; if (isLoading) { return (
{/* Usage Info skeleton */}
Board View Only

Cursor is currently only available for the Kanban board agent tasks.

); } return (
{/* Usage Info */}
Board View Only

Cursor is currently only available for the Kanban board agent tasks.

{/* CLI Status */} {/* Model Configuration */} {status?.installed && currentProject && (

Model Configuration

Configure which Cursor models are available and set the default

{/* Default Model */}
{/* Enabled Models */}
{availableModels.map((model) => { const isEnabled = config?.models?.includes(model.id) ?? model.id === 'auto'; const isAuto = model.id === 'auto'; return (
handleModelToggle(model.id, !!checked)} disabled={isSaving || isAuto} />
{model.label} {model.hasThinking && ( Thinking )}

{model.description}

{model.tier}
); })}
)} {/* No Project Selected */} {status?.installed && !currentProject && (

No project selected

Select a project to configure Cursor models.

)}
); } export default CursorSettingsTab;