From 6c3d3aa111deafa6ddba743830daf3ce56b65691 Mon Sep 17 00:00:00 2001 From: Kacper Date: Mon, 29 Dec 2025 21:30:06 +0100 Subject: [PATCH] feat: Unify AI provider settings tabs with consistent design MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add CursorCliStatus component matching Claude's card design - Add authentication status display to Claude CLI status card - Add skeleton loading states for both Claude and Cursor tabs - Add usage info banners (Primary Provider / Board View Only) - Remove duplicate auth status from API Keys section - Update Model Configuration card to use unified styling 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .../api-keys/api-keys-section.tsx | 10 +- .../cli-status/claude-cli-status.tsx | 107 +++++++- .../cli-status/cursor-cli-status.tsx | 239 ++++++++++++++++++ .../providers/claude-settings-tab.tsx | 15 ++ .../providers/cursor-settings-tab.tsx | 164 ++++++------ 5 files changed, 430 insertions(+), 105 deletions(-) create mode 100644 apps/ui/src/components/views/settings-view/cli-status/cursor-cli-status.tsx diff --git a/apps/ui/src/components/views/settings-view/api-keys/api-keys-section.tsx b/apps/ui/src/components/views/settings-view/api-keys/api-keys-section.tsx index 63d4e2e7..e0261e97 100644 --- a/apps/ui/src/components/views/settings-view/api-keys/api-keys-section.tsx +++ b/apps/ui/src/components/views/settings-view/api-keys/api-keys-section.tsx @@ -4,7 +4,6 @@ import { Button } from '@/components/ui/button'; import { Key, CheckCircle2, Settings, Trash2, Loader2 } from 'lucide-react'; import { ApiKeyField } from './api-key-field'; import { buildProviderConfigs } from '@/config/api-providers'; -import { AuthenticationStatusDisplay } from './authentication-status-display'; import { SecurityNotice } from './security-notice'; import { useApiKeyManagement } from './hooks/use-api-key-management'; import { cn } from '@/lib/utils'; @@ -19,7 +18,7 @@ export function ApiKeysSection() { const [isDeletingAnthropicKey, setIsDeletingAnthropicKey] = useState(false); const navigate = useNavigate(); - const { providerConfigParams, apiKeyStatus, handleSave, saved } = useApiKeyManagement(); + const { providerConfigParams, handleSave, saved } = useApiKeyManagement(); const providerConfigs = buildProviderConfigs(providerConfigParams); @@ -84,13 +83,6 @@ export function ApiKeysSection() { ))} - {/* Authentication Status Display */} - - {/* Security Notice */} diff --git a/apps/ui/src/components/views/settings-view/cli-status/claude-cli-status.tsx b/apps/ui/src/components/views/settings-view/cli-status/claude-cli-status.tsx index 8046d451..c808c37a 100644 --- a/apps/ui/src/components/views/settings-view/cli-status/claude-cli-status.tsx +++ b/apps/ui/src/components/views/settings-view/cli-status/claude-cli-status.tsx @@ -1,16 +1,86 @@ import { Button } from '@/components/ui/button'; -import { Terminal, CheckCircle2, AlertCircle, RefreshCw } from 'lucide-react'; +import { Terminal, CheckCircle2, AlertCircle, RefreshCw, XCircle } from 'lucide-react'; import { cn } from '@/lib/utils'; import type { CliStatus } from '../shared/types'; +import type { ClaudeAuthStatus } from '@/store/setup-store'; interface CliStatusProps { status: CliStatus | null; + authStatus?: ClaudeAuthStatus | null; isChecking: boolean; onRefresh: () => void; } -export function ClaudeCliStatus({ status, isChecking, onRefresh }: CliStatusProps) { - if (!status) return null; +function getAuthMethodLabel(method: string): string { + switch (method) { + case 'oauth_token': + return 'OAuth Token (Subscription)'; + case 'oauth_token_env': + return 'OAuth Token (Environment)'; + case 'api_key': + return 'API Key'; + case 'api_key_env': + return 'API Key (Environment)'; + case 'credentials_file': + return 'Credentials File'; + case 'cli_authenticated': + return 'CLI Authentication'; + default: + return method || 'Unknown'; + } +} + +function SkeletonPulse({ className }: { className?: string }) { + return
; +} + +function ClaudeCliStatusSkeleton() { + return ( +
+
+
+
+ + +
+ +
+
+ +
+
+
+ {/* Installation status skeleton */} +
+ +
+ + + +
+
+ {/* Auth status skeleton */} +
+ +
+ + +
+
+
+
+ ); +} + +export function ClaudeCliStatus({ status, authStatus, isChecking, onRefresh }: CliStatusProps) { + if (!status) return ; return (
+ {/* Authentication Status */} + {authStatus?.authenticated ? ( +
+
+ +
+
+

Authenticated

+
+

+ Method:{' '} + {getAuthMethodLabel(authStatus.method)} +

+
+
+
+ ) : ( +
+
+ +
+
+

Not Authenticated

+

+ Run claude login{' '} + or set an API key to authenticate. +

+
+
+ )} + {status.recommendation && (

{status.recommendation}

)} diff --git a/apps/ui/src/components/views/settings-view/cli-status/cursor-cli-status.tsx b/apps/ui/src/components/views/settings-view/cli-status/cursor-cli-status.tsx new file mode 100644 index 00000000..cf7b842d --- /dev/null +++ b/apps/ui/src/components/views/settings-view/cli-status/cursor-cli-status.tsx @@ -0,0 +1,239 @@ +import { Button } from '@/components/ui/button'; +import { Terminal, CheckCircle2, AlertCircle, RefreshCw, XCircle } from 'lucide-react'; +import { cn } from '@/lib/utils'; + +interface CursorStatus { + installed: boolean; + version?: string; + authenticated: boolean; + method?: string; +} + +interface CursorCliStatusProps { + status: CursorStatus | null; + isChecking: boolean; + onRefresh: () => void; +} + +function SkeletonPulse({ className }: { className?: string }) { + return
; +} + +export function CursorCliStatusSkeleton() { + return ( +
+
+
+
+ + +
+ +
+
+ +
+
+
+ {/* Installation status skeleton */} +
+ +
+ + +
+
+ {/* Auth status skeleton */} +
+ +
+ + +
+
+
+
+ ); +} + +export function ModelConfigSkeleton() { + return ( +
+
+
+ + +
+
+ +
+
+
+ {/* Default Model skeleton */} +
+ + +
+ {/* Available Models skeleton */} +
+ +
+ {[1, 2, 3, 4].map((i) => ( +
+
+ +
+ + +
+
+ +
+ ))} +
+
+
+
+ ); +} + +export function CursorCliStatus({ status, isChecking, onRefresh }: CursorCliStatusProps) { + if (!status) return ; + + return ( +
+
+
+
+
+ +
+

Cursor CLI

+
+ +
+

+ Cursor CLI enables AI-powered code editing using Cursor's models. +

+
+
+ {status.installed ? ( +
+ {/* Installation Status - Success */} +
+
+ +
+
+

Cursor CLI Installed

+
+ {status.version && ( +

+ Version: {status.version} +

+ )} +
+
+
+ + {/* Authentication Status */} + {status.authenticated ? ( +
+
+ +
+
+

Authenticated

+
+

+ Method:{' '} + + {status.method === 'api_key' ? 'API Key' : 'Browser Login'} + +

+
+
+
+ ) : ( +
+
+ +
+
+

Not Authenticated

+

+ Run cursor auth{' '} + to authenticate with Cursor. +

+
+
+ )} +
+ ) : ( +
+
+
+ +
+
+

Cursor CLI Not Detected

+

+ Install Cursor CLI to use Cursor models in AutoMaker. +

+
+
+ +
+ )} +
+
+ ); +} diff --git a/apps/ui/src/components/views/settings-view/providers/claude-settings-tab.tsx b/apps/ui/src/components/views/settings-view/providers/claude-settings-tab.tsx index df69cc82..57b2fe97 100644 --- a/apps/ui/src/components/views/settings-view/providers/claude-settings-tab.tsx +++ b/apps/ui/src/components/views/settings-view/providers/claude-settings-tab.tsx @@ -1,11 +1,14 @@ import { useAppStore } from '@/store/app-store'; +import { useSetupStore } from '@/store/setup-store'; import { useCliStatus } from '../hooks/use-cli-status'; import { ClaudeCliStatus } from '../cli-status/claude-cli-status'; import { ClaudeMdSettings } from '../claude/claude-md-settings'; import { ClaudeUsageSection } from '../api-keys/claude-usage-section'; +import { Info } from 'lucide-react'; export function ClaudeSettingsTab() { const { apiKeys, autoLoadClaudeMd, setAutoLoadClaudeMd } = useAppStore(); + const { claudeAuthStatus } = useSetupStore(); // Use CLI status hook const { claudeCliStatus, isCheckingClaudeCli, handleRefreshClaudeCli } = useCliStatus(); @@ -18,8 +21,20 @@ export function ClaudeSettingsTab() { return (
+ {/* Usage Info */} +
+ +
+ Primary Provider +

+ Claude is used throughout the app including chat, analysis, and agent tasks. +

+
+
+ diff --git a/apps/ui/src/components/views/settings-view/providers/cursor-settings-tab.tsx b/apps/ui/src/components/views/settings-view/providers/cursor-settings-tab.tsx index 56e51da1..6b6f1841 100644 --- a/apps/ui/src/components/views/settings-view/providers/cursor-settings-tab.tsx +++ b/apps/ui/src/components/views/settings-view/providers/cursor-settings-tab.tsx @@ -1,6 +1,4 @@ import { useState, useEffect, useCallback } from 'react'; -import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; -import { Button } from '@/components/ui/button'; import { Label } from '@/components/ui/label'; import { Badge } from '@/components/ui/badge'; import { Checkbox } from '@/components/ui/checkbox'; @@ -11,12 +9,18 @@ import { SelectTrigger, SelectValue, } from '@/components/ui/select'; -import { Terminal, CheckCircle2, XCircle, Loader2, RefreshCw, ExternalLink } from 'lucide-react'; +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; @@ -137,87 +141,63 @@ export function CursorSettingsTab() { if (isLoading) { return ( -
- +
+ {/* Usage Info skeleton */} +
+ +
+ Board View Only +

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

+
+
+ +
); } return (
- {/* Status Card */} - - - - - Cursor CLI Status - - - - {/* Installation */} -
- Installation - {status?.installed ? ( -
- - v{status.version} -
- ) : ( -
- - Not installed -
- )} -
+ {/* Usage Info */} +
+ +
+ Board View Only +

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

+
+
- {/* Authentication */} -
- Authentication - {status?.authenticated ? ( -
- - - {status.method === 'api_key' ? 'API Key' : 'Browser Login'} - -
- ) : ( -
- - Not authenticated -
- )} -
- - {/* Actions */} -
- - {!status?.installed && ( - - )} -
-
-
+ {/* CLI Status */} + {/* Model Configuration */} {status?.installed && currentProject && ( - - - Model Configuration - +
+
+
+
+ +
+

+ Model Configuration +

+
+

Configure which Cursor models are available and set the default - - - +

+
+
{/* Default Model */}
@@ -261,7 +241,7 @@ export function CursorSettingsTab() { return (
- - - )} - - {/* Not Installed State */} - {!status?.installed && ( - - - -

Cursor CLI is not installed.

-

Install it to use Cursor models in AutoMaker.

-
-
+
+
)} {/* No Project Selected */} {status?.installed && !currentProject && ( - - - -

No project selected.

+
+
+
+ +
+

No project selected

Select a project to configure Cursor models.

- - +
+
)}
);