mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-02 08:33:36 +00:00
feat: Unify AI provider settings tabs with consistent design
- 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 <noreply@anthropic.com>
This commit is contained in:
@@ -4,7 +4,6 @@ import { Button } from '@/components/ui/button';
|
|||||||
import { Key, CheckCircle2, Settings, Trash2, Loader2 } from 'lucide-react';
|
import { Key, CheckCircle2, Settings, Trash2, Loader2 } from 'lucide-react';
|
||||||
import { ApiKeyField } from './api-key-field';
|
import { ApiKeyField } from './api-key-field';
|
||||||
import { buildProviderConfigs } from '@/config/api-providers';
|
import { buildProviderConfigs } from '@/config/api-providers';
|
||||||
import { AuthenticationStatusDisplay } from './authentication-status-display';
|
|
||||||
import { SecurityNotice } from './security-notice';
|
import { SecurityNotice } from './security-notice';
|
||||||
import { useApiKeyManagement } from './hooks/use-api-key-management';
|
import { useApiKeyManagement } from './hooks/use-api-key-management';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
@@ -19,7 +18,7 @@ export function ApiKeysSection() {
|
|||||||
const [isDeletingAnthropicKey, setIsDeletingAnthropicKey] = useState(false);
|
const [isDeletingAnthropicKey, setIsDeletingAnthropicKey] = useState(false);
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
const { providerConfigParams, apiKeyStatus, handleSave, saved } = useApiKeyManagement();
|
const { providerConfigParams, handleSave, saved } = useApiKeyManagement();
|
||||||
|
|
||||||
const providerConfigs = buildProviderConfigs(providerConfigParams);
|
const providerConfigs = buildProviderConfigs(providerConfigParams);
|
||||||
|
|
||||||
@@ -84,13 +83,6 @@ export function ApiKeysSection() {
|
|||||||
<ApiKeyField key={provider.key} config={provider} />
|
<ApiKeyField key={provider.key} config={provider} />
|
||||||
))}
|
))}
|
||||||
|
|
||||||
{/* Authentication Status Display */}
|
|
||||||
<AuthenticationStatusDisplay
|
|
||||||
claudeAuthStatus={claudeAuthStatus}
|
|
||||||
apiKeyStatus={apiKeyStatus}
|
|
||||||
apiKeys={apiKeys}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{/* Security Notice */}
|
{/* Security Notice */}
|
||||||
<SecurityNotice />
|
<SecurityNotice />
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +1,86 @@
|
|||||||
import { Button } from '@/components/ui/button';
|
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 { cn } from '@/lib/utils';
|
||||||
import type { CliStatus } from '../shared/types';
|
import type { CliStatus } from '../shared/types';
|
||||||
|
import type { ClaudeAuthStatus } from '@/store/setup-store';
|
||||||
|
|
||||||
interface CliStatusProps {
|
interface CliStatusProps {
|
||||||
status: CliStatus | null;
|
status: CliStatus | null;
|
||||||
|
authStatus?: ClaudeAuthStatus | null;
|
||||||
isChecking: boolean;
|
isChecking: boolean;
|
||||||
onRefresh: () => void;
|
onRefresh: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ClaudeCliStatus({ status, isChecking, onRefresh }: CliStatusProps) {
|
function getAuthMethodLabel(method: string): string {
|
||||||
if (!status) return null;
|
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 <div className={cn('animate-pulse bg-muted/50 rounded', className)} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
function ClaudeCliStatusSkeleton() {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
'rounded-2xl overflow-hidden',
|
||||||
|
'border border-border/50',
|
||||||
|
'bg-gradient-to-br from-card/90 via-card/70 to-card/80 backdrop-blur-xl',
|
||||||
|
'shadow-sm shadow-black/5'
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div className="p-6 border-b border-border/50 bg-gradient-to-r from-transparent via-accent/5 to-transparent">
|
||||||
|
<div className="flex items-center justify-between mb-2">
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<SkeletonPulse className="w-9 h-9 rounded-xl" />
|
||||||
|
<SkeletonPulse className="h-6 w-36" />
|
||||||
|
</div>
|
||||||
|
<SkeletonPulse className="w-9 h-9 rounded-lg" />
|
||||||
|
</div>
|
||||||
|
<div className="ml-12">
|
||||||
|
<SkeletonPulse className="h-4 w-80" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="p-6 space-y-4">
|
||||||
|
{/* Installation status skeleton */}
|
||||||
|
<div className="flex items-center gap-3 p-4 rounded-xl border border-border/30 bg-muted/10">
|
||||||
|
<SkeletonPulse className="w-10 h-10 rounded-xl" />
|
||||||
|
<div className="flex-1 space-y-2">
|
||||||
|
<SkeletonPulse className="h-4 w-40" />
|
||||||
|
<SkeletonPulse className="h-3 w-32" />
|
||||||
|
<SkeletonPulse className="h-3 w-48" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/* Auth status skeleton */}
|
||||||
|
<div className="flex items-center gap-3 p-4 rounded-xl border border-border/30 bg-muted/10">
|
||||||
|
<SkeletonPulse className="w-10 h-10 rounded-xl" />
|
||||||
|
<div className="flex-1 space-y-2">
|
||||||
|
<SkeletonPulse className="h-4 w-28" />
|
||||||
|
<SkeletonPulse className="h-3 w-36" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ClaudeCliStatus({ status, authStatus, isChecking, onRefresh }: CliStatusProps) {
|
||||||
|
if (!status) return <ClaudeCliStatusSkeleton />;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@@ -80,6 +150,37 @@ export function ClaudeCliStatus({ status, isChecking, onRefresh }: CliStatusProp
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{/* Authentication Status */}
|
||||||
|
{authStatus?.authenticated ? (
|
||||||
|
<div className="flex items-center gap-3 p-4 rounded-xl bg-emerald-500/10 border border-emerald-500/20">
|
||||||
|
<div className="w-10 h-10 rounded-xl bg-emerald-500/15 flex items-center justify-center border border-emerald-500/20 shrink-0">
|
||||||
|
<CheckCircle2 className="w-5 h-5 text-emerald-500" />
|
||||||
|
</div>
|
||||||
|
<div className="flex-1 min-w-0">
|
||||||
|
<p className="text-sm font-medium text-emerald-400">Authenticated</p>
|
||||||
|
<div className="text-xs text-emerald-400/70 mt-1.5">
|
||||||
|
<p>
|
||||||
|
Method:{' '}
|
||||||
|
<span className="font-mono">{getAuthMethodLabel(authStatus.method)}</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="flex items-start gap-3 p-4 rounded-xl bg-amber-500/10 border border-amber-500/20">
|
||||||
|
<div className="w-10 h-10 rounded-xl bg-amber-500/15 flex items-center justify-center border border-amber-500/20 shrink-0 mt-0.5">
|
||||||
|
<XCircle className="w-5 h-5 text-amber-500" />
|
||||||
|
</div>
|
||||||
|
<div className="flex-1">
|
||||||
|
<p className="text-sm font-medium text-amber-400">Not Authenticated</p>
|
||||||
|
<p className="text-xs text-amber-400/70 mt-1">
|
||||||
|
Run <code className="font-mono bg-amber-500/10 px-1 rounded">claude login</code>{' '}
|
||||||
|
or set an API key to authenticate.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{status.recommendation && (
|
{status.recommendation && (
|
||||||
<p className="text-xs text-muted-foreground/70 ml-1">{status.recommendation}</p>
|
<p className="text-xs text-muted-foreground/70 ml-1">{status.recommendation}</p>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -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 <div className={cn('animate-pulse bg-muted/50 rounded', className)} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function CursorCliStatusSkeleton() {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
'rounded-2xl overflow-hidden',
|
||||||
|
'border border-border/50',
|
||||||
|
'bg-gradient-to-br from-card/90 via-card/70 to-card/80 backdrop-blur-xl',
|
||||||
|
'shadow-sm shadow-black/5'
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div className="p-6 border-b border-border/50 bg-gradient-to-r from-transparent via-accent/5 to-transparent">
|
||||||
|
<div className="flex items-center justify-between mb-2">
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<SkeletonPulse className="w-9 h-9 rounded-xl" />
|
||||||
|
<SkeletonPulse className="h-6 w-28" />
|
||||||
|
</div>
|
||||||
|
<SkeletonPulse className="w-9 h-9 rounded-lg" />
|
||||||
|
</div>
|
||||||
|
<div className="ml-12">
|
||||||
|
<SkeletonPulse className="h-4 w-72" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="p-6 space-y-4">
|
||||||
|
{/* Installation status skeleton */}
|
||||||
|
<div className="flex items-center gap-3 p-4 rounded-xl border border-border/30 bg-muted/10">
|
||||||
|
<SkeletonPulse className="w-10 h-10 rounded-xl" />
|
||||||
|
<div className="flex-1 space-y-2">
|
||||||
|
<SkeletonPulse className="h-4 w-36" />
|
||||||
|
<SkeletonPulse className="h-3 w-28" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/* Auth status skeleton */}
|
||||||
|
<div className="flex items-center gap-3 p-4 rounded-xl border border-border/30 bg-muted/10">
|
||||||
|
<SkeletonPulse className="w-10 h-10 rounded-xl" />
|
||||||
|
<div className="flex-1 space-y-2">
|
||||||
|
<SkeletonPulse className="h-4 w-28" />
|
||||||
|
<SkeletonPulse className="h-3 w-32" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ModelConfigSkeleton() {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
'rounded-2xl overflow-hidden',
|
||||||
|
'border border-border/50',
|
||||||
|
'bg-gradient-to-br from-card/90 via-card/70 to-card/80 backdrop-blur-xl',
|
||||||
|
'shadow-sm shadow-black/5'
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div className="p-6 border-b border-border/50 bg-gradient-to-r from-transparent via-accent/5 to-transparent">
|
||||||
|
<div className="flex items-center gap-3 mb-2">
|
||||||
|
<SkeletonPulse className="w-9 h-9 rounded-xl" />
|
||||||
|
<SkeletonPulse className="h-6 w-40" />
|
||||||
|
</div>
|
||||||
|
<div className="ml-12">
|
||||||
|
<SkeletonPulse className="h-4 w-72" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="p-6 space-y-6">
|
||||||
|
{/* Default Model skeleton */}
|
||||||
|
<div className="space-y-2">
|
||||||
|
<SkeletonPulse className="h-4 w-24" />
|
||||||
|
<SkeletonPulse className="h-10 w-full rounded-md" />
|
||||||
|
</div>
|
||||||
|
{/* Available Models skeleton */}
|
||||||
|
<div className="space-y-3">
|
||||||
|
<SkeletonPulse className="h-4 w-32" />
|
||||||
|
<div className="grid gap-3">
|
||||||
|
{[1, 2, 3, 4].map((i) => (
|
||||||
|
<div
|
||||||
|
key={i}
|
||||||
|
className="flex items-center justify-between p-3 rounded-xl border border-border/30 bg-muted/10"
|
||||||
|
>
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<SkeletonPulse className="w-5 h-5 rounded" />
|
||||||
|
<div className="space-y-1.5">
|
||||||
|
<SkeletonPulse className="h-4 w-32" />
|
||||||
|
<SkeletonPulse className="h-3 w-48" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<SkeletonPulse className="h-5 w-12 rounded-full" />
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function CursorCliStatus({ status, isChecking, onRefresh }: CursorCliStatusProps) {
|
||||||
|
if (!status) return <CursorCliStatusSkeleton />;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
'rounded-2xl overflow-hidden',
|
||||||
|
'border border-border/50',
|
||||||
|
'bg-gradient-to-br from-card/90 via-card/70 to-card/80 backdrop-blur-xl',
|
||||||
|
'shadow-sm shadow-black/5'
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div className="p-6 border-b border-border/50 bg-gradient-to-r from-transparent via-accent/5 to-transparent">
|
||||||
|
<div className="flex items-center justify-between mb-2">
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<div className="w-9 h-9 rounded-xl bg-gradient-to-br from-brand-500/20 to-brand-600/10 flex items-center justify-center border border-brand-500/20">
|
||||||
|
<Terminal className="w-5 h-5 text-brand-500" />
|
||||||
|
</div>
|
||||||
|
<h2 className="text-lg font-semibold text-foreground tracking-tight">Cursor CLI</h2>
|
||||||
|
</div>
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="icon"
|
||||||
|
onClick={onRefresh}
|
||||||
|
disabled={isChecking}
|
||||||
|
data-testid="refresh-cursor-cli"
|
||||||
|
title="Refresh Cursor CLI detection"
|
||||||
|
className={cn(
|
||||||
|
'h-9 w-9 rounded-lg',
|
||||||
|
'hover:bg-accent/50 hover:scale-105',
|
||||||
|
'transition-all duration-200'
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<RefreshCw className={cn('w-4 h-4', isChecking && 'animate-spin')} />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<p className="text-sm text-muted-foreground/80 ml-12">
|
||||||
|
Cursor CLI enables AI-powered code editing using Cursor's models.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="p-6 space-y-4">
|
||||||
|
{status.installed ? (
|
||||||
|
<div className="space-y-3">
|
||||||
|
{/* Installation Status - Success */}
|
||||||
|
<div className="flex items-center gap-3 p-4 rounded-xl bg-emerald-500/10 border border-emerald-500/20">
|
||||||
|
<div className="w-10 h-10 rounded-xl bg-emerald-500/15 flex items-center justify-center border border-emerald-500/20 shrink-0">
|
||||||
|
<CheckCircle2 className="w-5 h-5 text-emerald-500" />
|
||||||
|
</div>
|
||||||
|
<div className="flex-1 min-w-0">
|
||||||
|
<p className="text-sm font-medium text-emerald-400">Cursor CLI Installed</p>
|
||||||
|
<div className="text-xs text-emerald-400/70 mt-1.5 space-y-0.5">
|
||||||
|
{status.version && (
|
||||||
|
<p>
|
||||||
|
Version: <span className="font-mono">{status.version}</span>
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Authentication Status */}
|
||||||
|
{status.authenticated ? (
|
||||||
|
<div className="flex items-center gap-3 p-4 rounded-xl bg-emerald-500/10 border border-emerald-500/20">
|
||||||
|
<div className="w-10 h-10 rounded-xl bg-emerald-500/15 flex items-center justify-center border border-emerald-500/20 shrink-0">
|
||||||
|
<CheckCircle2 className="w-5 h-5 text-emerald-500" />
|
||||||
|
</div>
|
||||||
|
<div className="flex-1 min-w-0">
|
||||||
|
<p className="text-sm font-medium text-emerald-400">Authenticated</p>
|
||||||
|
<div className="text-xs text-emerald-400/70 mt-1.5">
|
||||||
|
<p>
|
||||||
|
Method:{' '}
|
||||||
|
<span className="font-mono">
|
||||||
|
{status.method === 'api_key' ? 'API Key' : 'Browser Login'}
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="flex items-start gap-3 p-4 rounded-xl bg-amber-500/10 border border-amber-500/20">
|
||||||
|
<div className="w-10 h-10 rounded-xl bg-amber-500/15 flex items-center justify-center border border-amber-500/20 shrink-0 mt-0.5">
|
||||||
|
<XCircle className="w-5 h-5 text-amber-500" />
|
||||||
|
</div>
|
||||||
|
<div className="flex-1">
|
||||||
|
<p className="text-sm font-medium text-amber-400">Not Authenticated</p>
|
||||||
|
<p className="text-xs text-amber-400/70 mt-1">
|
||||||
|
Run <code className="font-mono bg-amber-500/10 px-1 rounded">cursor auth</code>{' '}
|
||||||
|
to authenticate with Cursor.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="space-y-4">
|
||||||
|
<div className="flex items-start gap-3 p-4 rounded-xl bg-amber-500/10 border border-amber-500/20">
|
||||||
|
<div className="w-10 h-10 rounded-xl bg-amber-500/15 flex items-center justify-center border border-amber-500/20 shrink-0 mt-0.5">
|
||||||
|
<AlertCircle className="w-5 h-5 text-amber-500" />
|
||||||
|
</div>
|
||||||
|
<div className="flex-1">
|
||||||
|
<p className="text-sm font-medium text-amber-400">Cursor CLI Not Detected</p>
|
||||||
|
<p className="text-xs text-amber-400/70 mt-1">
|
||||||
|
Install Cursor CLI to use Cursor models in AutoMaker.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-3">
|
||||||
|
<p className="text-xs font-medium text-foreground/80">Installation:</p>
|
||||||
|
<a
|
||||||
|
href="https://cursor.com/docs/cli"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="inline-flex items-center gap-2 text-xs text-brand-400 hover:text-brand-300 transition-colors"
|
||||||
|
>
|
||||||
|
View installation guide →
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,11 +1,14 @@
|
|||||||
import { useAppStore } from '@/store/app-store';
|
import { useAppStore } from '@/store/app-store';
|
||||||
|
import { useSetupStore } from '@/store/setup-store';
|
||||||
import { useCliStatus } from '../hooks/use-cli-status';
|
import { useCliStatus } from '../hooks/use-cli-status';
|
||||||
import { ClaudeCliStatus } from '../cli-status/claude-cli-status';
|
import { ClaudeCliStatus } from '../cli-status/claude-cli-status';
|
||||||
import { ClaudeMdSettings } from '../claude/claude-md-settings';
|
import { ClaudeMdSettings } from '../claude/claude-md-settings';
|
||||||
import { ClaudeUsageSection } from '../api-keys/claude-usage-section';
|
import { ClaudeUsageSection } from '../api-keys/claude-usage-section';
|
||||||
|
import { Info } from 'lucide-react';
|
||||||
|
|
||||||
export function ClaudeSettingsTab() {
|
export function ClaudeSettingsTab() {
|
||||||
const { apiKeys, autoLoadClaudeMd, setAutoLoadClaudeMd } = useAppStore();
|
const { apiKeys, autoLoadClaudeMd, setAutoLoadClaudeMd } = useAppStore();
|
||||||
|
const { claudeAuthStatus } = useSetupStore();
|
||||||
|
|
||||||
// Use CLI status hook
|
// Use CLI status hook
|
||||||
const { claudeCliStatus, isCheckingClaudeCli, handleRefreshClaudeCli } = useCliStatus();
|
const { claudeCliStatus, isCheckingClaudeCli, handleRefreshClaudeCli } = useCliStatus();
|
||||||
@@ -18,8 +21,20 @@ export function ClaudeSettingsTab() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
|
{/* Usage Info */}
|
||||||
|
<div className="flex items-start gap-3 p-4 rounded-xl bg-blue-500/10 border border-blue-500/20">
|
||||||
|
<Info className="w-5 h-5 text-blue-400 shrink-0 mt-0.5" />
|
||||||
|
<div className="text-sm text-blue-400/90">
|
||||||
|
<span className="font-medium">Primary Provider</span>
|
||||||
|
<p className="text-xs text-blue-400/70 mt-1">
|
||||||
|
Claude is used throughout the app including chat, analysis, and agent tasks.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<ClaudeCliStatus
|
<ClaudeCliStatus
|
||||||
status={claudeCliStatus}
|
status={claudeCliStatus}
|
||||||
|
authStatus={claudeAuthStatus}
|
||||||
isChecking={isCheckingClaudeCli}
|
isChecking={isCheckingClaudeCli}
|
||||||
onRefresh={handleRefreshClaudeCli}
|
onRefresh={handleRefreshClaudeCli}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
import { useState, useEffect, useCallback } from 'react';
|
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 { Label } from '@/components/ui/label';
|
||||||
import { Badge } from '@/components/ui/badge';
|
import { Badge } from '@/components/ui/badge';
|
||||||
import { Checkbox } from '@/components/ui/checkbox';
|
import { Checkbox } from '@/components/ui/checkbox';
|
||||||
@@ -11,12 +9,18 @@ import {
|
|||||||
SelectTrigger,
|
SelectTrigger,
|
||||||
SelectValue,
|
SelectValue,
|
||||||
} from '@/components/ui/select';
|
} 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 { toast } from 'sonner';
|
||||||
import { getHttpApiClient } from '@/lib/http-api-client';
|
import { getHttpApiClient } from '@/lib/http-api-client';
|
||||||
import { useAppStore } from '@/store/app-store';
|
import { useAppStore } from '@/store/app-store';
|
||||||
|
import { cn } from '@/lib/utils';
|
||||||
import type { CursorModelId, CursorModelConfig, CursorCliConfig } from '@automaker/types';
|
import type { CursorModelId, CursorModelConfig, CursorCliConfig } from '@automaker/types';
|
||||||
import { CURSOR_MODEL_MAP } from '@automaker/types';
|
import { CURSOR_MODEL_MAP } from '@automaker/types';
|
||||||
|
import {
|
||||||
|
CursorCliStatus,
|
||||||
|
CursorCliStatusSkeleton,
|
||||||
|
ModelConfigSkeleton,
|
||||||
|
} from '../cli-status/cursor-cli-status';
|
||||||
|
|
||||||
interface CursorStatus {
|
interface CursorStatus {
|
||||||
installed: boolean;
|
installed: boolean;
|
||||||
@@ -137,87 +141,63 @@ export function CursorSettingsTab() {
|
|||||||
|
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center justify-center py-12">
|
<div className="space-y-6">
|
||||||
<Loader2 className="w-6 h-6 animate-spin text-muted-foreground" />
|
{/* Usage Info skeleton */}
|
||||||
|
<div className="flex items-start gap-3 p-4 rounded-xl bg-amber-500/10 border border-amber-500/20">
|
||||||
|
<Info className="w-5 h-5 text-amber-400 shrink-0 mt-0.5" />
|
||||||
|
<div className="text-sm text-amber-400/90">
|
||||||
|
<span className="font-medium">Board View Only</span>
|
||||||
|
<p className="text-xs text-amber-400/70 mt-1">
|
||||||
|
Cursor is currently only available for the Kanban board agent tasks.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<CursorCliStatusSkeleton />
|
||||||
|
<ModelConfigSkeleton />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
{/* Status Card */}
|
{/* Usage Info */}
|
||||||
<Card>
|
<div className="flex items-start gap-3 p-4 rounded-xl bg-amber-500/10 border border-amber-500/20">
|
||||||
<CardHeader>
|
<Info className="w-5 h-5 text-amber-400 shrink-0 mt-0.5" />
|
||||||
<CardTitle className="flex items-center gap-2 text-lg">
|
<div className="text-sm text-amber-400/90">
|
||||||
<Terminal className="w-5 h-5" />
|
<span className="font-medium">Board View Only</span>
|
||||||
Cursor CLI Status
|
<p className="text-xs text-amber-400/70 mt-1">
|
||||||
</CardTitle>
|
Cursor is currently only available for the Kanban board agent tasks.
|
||||||
</CardHeader>
|
</p>
|
||||||
<CardContent className="space-y-4">
|
</div>
|
||||||
{/* Installation */}
|
</div>
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<span className="text-sm">Installation</span>
|
|
||||||
{status?.installed ? (
|
|
||||||
<div className="flex items-center gap-2 text-green-600 dark:text-green-400">
|
|
||||||
<CheckCircle2 className="w-4 h-4" />
|
|
||||||
<span className="text-xs font-mono">v{status.version}</span>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div className="flex items-center gap-2 text-destructive">
|
|
||||||
<XCircle className="w-4 h-4" />
|
|
||||||
<span className="text-xs">Not installed</span>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Authentication */}
|
{/* CLI Status */}
|
||||||
<div className="flex items-center justify-between">
|
<CursorCliStatus status={status} isChecking={isLoading} onRefresh={loadData} />
|
||||||
<span className="text-sm">Authentication</span>
|
|
||||||
{status?.authenticated ? (
|
|
||||||
<div className="flex items-center gap-2 text-green-600 dark:text-green-400">
|
|
||||||
<CheckCircle2 className="w-4 h-4" />
|
|
||||||
<span className="text-xs capitalize">
|
|
||||||
{status.method === 'api_key' ? 'API Key' : 'Browser Login'}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div className="flex items-center gap-2 text-amber-600 dark:text-amber-400">
|
|
||||||
<XCircle className="w-4 h-4" />
|
|
||||||
<span className="text-xs">Not authenticated</span>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Actions */}
|
|
||||||
<div className="flex gap-2 pt-2">
|
|
||||||
<Button variant="outline" size="sm" onClick={loadData}>
|
|
||||||
<RefreshCw className="w-4 h-4 mr-2" />
|
|
||||||
Refresh Status
|
|
||||||
</Button>
|
|
||||||
{!status?.installed && (
|
|
||||||
<Button
|
|
||||||
variant="outline"
|
|
||||||
size="sm"
|
|
||||||
onClick={() => window.open('https://cursor.com/docs/cli', '_blank')}
|
|
||||||
>
|
|
||||||
Installation Guide
|
|
||||||
<ExternalLink className="w-4 h-4 ml-2" />
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
{/* Model Configuration */}
|
{/* Model Configuration */}
|
||||||
{status?.installed && currentProject && (
|
{status?.installed && currentProject && (
|
||||||
<Card>
|
<div
|
||||||
<CardHeader>
|
className={cn(
|
||||||
<CardTitle className="text-lg">Model Configuration</CardTitle>
|
'rounded-2xl overflow-hidden',
|
||||||
<CardDescription>
|
'border border-border/50',
|
||||||
|
'bg-gradient-to-br from-card/90 via-card/70 to-card/80 backdrop-blur-xl',
|
||||||
|
'shadow-sm shadow-black/5'
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div className="p-6 border-b border-border/50 bg-gradient-to-r from-transparent via-accent/5 to-transparent">
|
||||||
|
<div className="flex items-center gap-3 mb-2">
|
||||||
|
<div className="w-9 h-9 rounded-xl bg-gradient-to-br from-brand-500/20 to-brand-600/10 flex items-center justify-center border border-brand-500/20">
|
||||||
|
<Terminal className="w-5 h-5 text-brand-500" />
|
||||||
|
</div>
|
||||||
|
<h2 className="text-lg font-semibold text-foreground tracking-tight">
|
||||||
|
Model Configuration
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
<p className="text-sm text-muted-foreground/80 ml-12">
|
||||||
Configure which Cursor models are available and set the default
|
Configure which Cursor models are available and set the default
|
||||||
</CardDescription>
|
</p>
|
||||||
</CardHeader>
|
</div>
|
||||||
<CardContent className="space-y-6">
|
<div className="p-6 space-y-6">
|
||||||
{/* Default Model */}
|
{/* Default Model */}
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label>Default Model</Label>
|
<Label>Default Model</Label>
|
||||||
@@ -261,7 +241,7 @@ export function CursorSettingsTab() {
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={model.id}
|
key={model.id}
|
||||||
className="flex items-center justify-between p-3 rounded-lg border bg-card"
|
className="flex items-center justify-between p-3 rounded-xl border border-border/50 bg-card/50 hover:bg-accent/30 transition-colors"
|
||||||
>
|
>
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<Checkbox
|
<Checkbox
|
||||||
@@ -289,30 +269,28 @@ export function CursorSettingsTab() {
|
|||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</div>
|
||||||
</Card>
|
</div>
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Not Installed State */}
|
|
||||||
{!status?.installed && (
|
|
||||||
<Card>
|
|
||||||
<CardContent className="py-8 text-center text-muted-foreground">
|
|
||||||
<Terminal className="w-12 h-12 mx-auto mb-4 opacity-50" />
|
|
||||||
<p>Cursor CLI is not installed.</p>
|
|
||||||
<p className="text-sm mt-2">Install it to use Cursor models in AutoMaker.</p>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* No Project Selected */}
|
{/* No Project Selected */}
|
||||||
{status?.installed && !currentProject && (
|
{status?.installed && !currentProject && (
|
||||||
<Card>
|
<div
|
||||||
<CardContent className="py-8 text-center text-muted-foreground">
|
className={cn(
|
||||||
<Terminal className="w-12 h-12 mx-auto mb-4 opacity-50" />
|
'rounded-2xl overflow-hidden',
|
||||||
<p>No project selected.</p>
|
'border border-border/50',
|
||||||
|
'bg-gradient-to-br from-card/90 via-card/70 to-card/80 backdrop-blur-xl',
|
||||||
|
'shadow-sm shadow-black/5'
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div className="p-8 text-center text-muted-foreground">
|
||||||
|
<div className="w-12 h-12 rounded-xl bg-gradient-to-br from-brand-500/10 to-brand-600/5 flex items-center justify-center border border-brand-500/10 mx-auto mb-4">
|
||||||
|
<Terminal className="w-6 h-6 text-brand-500/50" />
|
||||||
|
</div>
|
||||||
|
<p className="font-medium">No project selected</p>
|
||||||
<p className="text-sm mt-2">Select a project to configure Cursor models.</p>
|
<p className="text-sm mt-2">Select a project to configure Cursor models.</p>
|
||||||
</CardContent>
|
</div>
|
||||||
</Card>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user