import { useState, useEffect } from "react"; import { IconLoader2, IconCopy, IconCheck, IconExternalLink, IconArrowRight } from "@tabler/icons-react"; import { Button } from "@/components/ui/button"; import { KimiMascot } from "./KimiMascot"; import { bridge, Events } from "@/services"; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from "@/components/ui/alert-dialog"; interface LoginScreenProps { onLoginSuccess: () => void; onSkip: () => void; } type LoginState = "idle" | "pending" | "error"; function isPaymentRequiredError(error: string | null): boolean { if (!error) return false; return error.includes("402") || error.toLowerCase().includes("payment required"); } export function LoginScreen({ onLoginSuccess, onSkip }: LoginScreenProps) { const [state, setState] = useState("idle"); const [url, setUrl] = useState(null); const [error, setError] = useState(null); const [copied, setCopied] = useState(false); const [showSubscribeDialog, setShowSubscribeDialog] = useState(false); useEffect(() => { return bridge.on<{ url: string }>(Events.LoginUrl, ({ url }) => { setUrl(url); }); }, []); const handleLogin = async () => { setState("pending"); setUrl(null); setError(null); try { const result = await bridge.login(); if (result.success) { onLoginSuccess(); } else { const errorMessage = result.error || "Login failed"; if (isPaymentRequiredError(errorMessage)) { setShowSubscribeDialog(true); setState("idle"); } else { setState("error"); setError(errorMessage); } } } catch (err) { const errorMessage = err instanceof Error ? err.message : String(err); if (isPaymentRequiredError(errorMessage)) { setShowSubscribeDialog(true); setState("idle"); } else { setState("error"); setError(errorMessage); } } }; const handleSubscribe = () => { // TODO(region-split): derive this from the region profile's siteBase // (`https://www.kimi.ai/code` for overseas logins). The webview cannot // resolve the region itself — @moonshot-ai/kimi-code-oauth is not a // webview dependency and its region resolver is Node-only — so the // extension host needs to hand the site URL over the bridge first. window.open("https://www.kimi.com/code", "_blank"); setShowSubscribeDialog(false); }; const handleCopyUrl = async () => { if (!url) return; await navigator.clipboard.writeText(url); setCopied(true); setTimeout(() => setCopied(false), 2000); }; if (state === "pending") { return (
Waiting for authentication…

A browser window should open automatically. Complete the sign-in process there.

{url && (

If the browser didn't open, visit this URL:

{url}
Open in browser
)}
); } return ( <>

Welcome to Kimi Code

Use Kimi Code with your Kimi account subscription or your existing API setup.

{error && (

{error}

)}

Use your Kimi account and Kimi Code subscription.

Use your existing API key configuration.

Subscription Required Your account does not have an active Kimi Code subscription. Please subscribe to continue using Kimi Code with your account. setShowSubscribeDialog(false)}>Skip Subscribe ); }