mirror of
https://github.com/siteboon/claudecodeui.git
synced 2026-03-31 10:47:25 +00:00
168 lines
6.3 KiB
TypeScript
168 lines
6.3 KiB
TypeScript
import { ExternalLink, KeyRound, X } from 'lucide-react';
|
|
import StandaloneShell from '../../standalone-shell/view/StandaloneShell';
|
|
import { IS_PLATFORM } from '../../../constants/config';
|
|
import type { CliProvider } from '../types';
|
|
|
|
type LoginModalProject = {
|
|
name?: string;
|
|
displayName?: string;
|
|
fullPath?: string;
|
|
path?: string;
|
|
[key: string]: unknown;
|
|
};
|
|
|
|
type ProviderLoginModalProps = {
|
|
isOpen: boolean;
|
|
onClose: () => void;
|
|
provider?: CliProvider;
|
|
project?: LoginModalProject | null;
|
|
onComplete?: (exitCode: number) => void;
|
|
customCommand?: string;
|
|
isAuthenticated?: boolean;
|
|
};
|
|
|
|
const getProviderCommand = ({
|
|
provider,
|
|
customCommand,
|
|
isAuthenticated: _isAuthenticated,
|
|
}: {
|
|
provider: CliProvider;
|
|
customCommand?: string;
|
|
isAuthenticated: boolean;
|
|
}) => {
|
|
if (customCommand) {
|
|
return customCommand;
|
|
}
|
|
|
|
if (provider === 'claude') {
|
|
return 'claude --dangerously-skip-permissions /login';
|
|
}
|
|
|
|
if (provider === 'cursor') {
|
|
return 'cursor-agent login';
|
|
}
|
|
|
|
if (provider === 'codex') {
|
|
return IS_PLATFORM ? 'codex login --device-auth' : 'codex login';
|
|
}
|
|
|
|
return 'gemini status';
|
|
};
|
|
|
|
const getProviderTitle = (provider: CliProvider) => {
|
|
if (provider === 'claude') return 'Claude CLI Login';
|
|
if (provider === 'cursor') return 'Cursor CLI Login';
|
|
if (provider === 'codex') return 'Codex CLI Login';
|
|
return 'Gemini CLI Configuration';
|
|
};
|
|
|
|
const normalizeProject = (project?: LoginModalProject | null) => {
|
|
const normalizedName = project?.name || 'default';
|
|
const normalizedFullPath = project?.fullPath ?? project?.path ?? (IS_PLATFORM ? '/workspace' : '');
|
|
|
|
return {
|
|
name: normalizedName,
|
|
displayName: project?.displayName || normalizedName,
|
|
fullPath: normalizedFullPath,
|
|
path: project?.path ?? normalizedFullPath,
|
|
};
|
|
};
|
|
|
|
export default function ProviderLoginModal({
|
|
isOpen,
|
|
onClose,
|
|
provider = 'claude',
|
|
project = null,
|
|
onComplete,
|
|
customCommand,
|
|
isAuthenticated = false,
|
|
}: ProviderLoginModalProps) {
|
|
if (!isOpen) {
|
|
return null;
|
|
}
|
|
|
|
const command = getProviderCommand({ provider, customCommand, isAuthenticated });
|
|
const title = getProviderTitle(provider);
|
|
const shellProject = normalizeProject(project);
|
|
|
|
const handleComplete = (exitCode: number) => {
|
|
onComplete?.(exitCode);
|
|
// Keep the modal open so users can read terminal output before closing.
|
|
};
|
|
|
|
return (
|
|
<div className="fixed inset-0 z-[9999] flex items-center justify-center bg-black bg-opacity-50 max-md:items-stretch max-md:justify-stretch">
|
|
<div className="flex h-3/4 w-full max-w-4xl flex-col rounded-lg bg-white shadow-xl dark:bg-gray-800 max-md:m-0 max-md:h-full max-md:max-w-none max-md:rounded-none md:m-4 md:h-3/4 md:max-w-4xl md:rounded-lg">
|
|
<div className="flex items-center justify-between border-b border-gray-200 p-4 dark:border-gray-700">
|
|
<h3 className="text-lg font-semibold text-gray-900 dark:text-white">{title}</h3>
|
|
<button
|
|
onClick={onClose}
|
|
className="text-gray-400 transition-colors hover:text-gray-600 dark:hover:text-gray-300"
|
|
aria-label="Close login modal"
|
|
>
|
|
<X className="h-6 w-6" />
|
|
</button>
|
|
</div>
|
|
|
|
<div className="flex-1 overflow-hidden">
|
|
{provider === 'gemini' ? (
|
|
<div className="flex h-full flex-col items-center justify-center bg-gray-50 p-8 text-center dark:bg-gray-900/50">
|
|
<div className="mb-6 flex h-16 w-16 items-center justify-center rounded-full bg-blue-100 dark:bg-blue-900/30">
|
|
<KeyRound className="h-8 w-8 text-blue-600 dark:text-blue-400" />
|
|
</div>
|
|
|
|
<h4 className="mb-3 text-xl font-medium text-gray-900 dark:text-white">Setup Gemini API Access</h4>
|
|
|
|
<p className="mb-8 max-w-md text-gray-600 dark:text-gray-400">
|
|
The Gemini CLI requires an API key to function. Configure it in your terminal first.
|
|
</p>
|
|
|
|
<div className="w-full max-w-lg rounded-xl border border-gray-200 bg-white p-6 text-left shadow-sm dark:border-gray-700 dark:bg-gray-800">
|
|
<ol className="space-y-4">
|
|
<li className="flex gap-4">
|
|
<div className="flex h-6 w-6 flex-shrink-0 items-center justify-center rounded-full bg-blue-100 text-sm font-medium text-blue-600 dark:bg-blue-900/50 dark:text-blue-400">
|
|
1
|
|
</div>
|
|
<div>
|
|
<p className="mb-1 text-sm font-medium text-gray-900 dark:text-white">Get your API key</p>
|
|
<a
|
|
href="https://aistudio.google.com/app/apikey"
|
|
target="_blank"
|
|
rel="noreferrer"
|
|
className="flex inline-flex items-center gap-1 text-sm text-blue-600 hover:underline dark:text-blue-400"
|
|
>
|
|
Google AI Studio <ExternalLink className="h-3 w-3" />
|
|
</a>
|
|
</div>
|
|
</li>
|
|
<li className="flex gap-4">
|
|
<div className="flex h-6 w-6 flex-shrink-0 items-center justify-center rounded-full bg-blue-100 text-sm font-medium text-blue-600 dark:bg-blue-900/50 dark:text-blue-400">
|
|
2
|
|
</div>
|
|
<div>
|
|
<p className="mb-1 text-sm font-medium text-gray-900 dark:text-white">Run configuration</p>
|
|
<p className="mb-2 text-sm text-gray-600 dark:text-gray-400">Open your terminal and run:</p>
|
|
<code className="block rounded bg-gray-100 px-3 py-2 font-mono text-sm text-pink-600 dark:bg-gray-900 dark:text-pink-400">
|
|
gemini config set api_key YOUR_KEY
|
|
</code>
|
|
</div>
|
|
</li>
|
|
</ol>
|
|
</div>
|
|
|
|
<button
|
|
onClick={onClose}
|
|
className="mt-8 rounded-lg bg-blue-600 px-6 py-2.5 font-medium text-white transition-colors hover:bg-blue-700"
|
|
>
|
|
Done
|
|
</button>
|
|
</div>
|
|
) : (
|
|
<StandaloneShell project={shellProject} command={command} onComplete={handleComplete} minimal={true} />
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|