import { useT } from "@agent-native/core/client/i18n"; import { IconX, IconCheck, IconLoader2, IconDatabase, IconCloud, IconChevronRight, } from "@tabler/icons-react"; import { useState, useRef, useCallback } from "react"; interface CloudUpgradeProps { title?: string; description?: string; onClose?: () => void; } interface Provider { id: string; name: string; description: string; urlPrefix: string; needsAuthToken: boolean; steps: string[]; } const PROVIDERS: Provider[] = [ { id: "turso", name: "Turso", description: "cloudSqliteEdge", urlPrefix: "libsql://", needsAuthToken: true, steps: [ "Install CLI: curl -sSfL https://get.tur.so/install.sh | bash", "Sign up / login: turso auth login (opens browser)", "Create a database: turso db create my-app", "Copy the URL: turso db show my-app --url → starts with libsql://", "Create an auth token: turso db tokens create my-app → paste below", ], }, { id: "neon", name: "Neon", description: "cloudServerlessPostgres", urlPrefix: "postgres://", needsAuthToken: false, steps: [ "Go to console.neon.tech and sign up or log in", 'Click "New Project" → pick a name and region → click Create', "On the project dashboard, find the Connection Details panel", 'Select "Connection string" tab → copy the postgres://... URL', "Paste the full connection string (includes password) below", ], }, { id: "supabase", name: "Supabase", description: "cloudOpenSourceFirebaseAlternative", urlPrefix: "postgres://", needsAuthToken: false, steps: [ "Go to supabase.com/dashboard and sign up or log in", 'Click "New Project" → set a name and database password → click Create', "Wait for the project to finish provisioning (~30 seconds)", "Go to Project Settings → Database → Connection string", 'Select "URI" tab → copy the postgres://... string (replace [YOUR-PASSWORD] with your DB password)', ], }, { id: "d1", name: "Cloudflare D1", // i18n-ignore stable provider name description: "cloudCloudflareSqliteEdge", urlPrefix: "d1://", needsAuthToken: true, steps: [ "Go to dash.cloudflare.com → Workers & Pages → D1 SQL Database", 'Click "Create" → name your database → click Create', "Copy the Database ID from the database overview page", "For the auth token: go to My Profile → API Tokens → Create Token", 'Select "Edit Cloudflare Workers" template → Create Token → copy it', "Paste as: d1:// with the API token below", ], }, ]; export function CloudUpgrade({ title = "Share Publicly", description = "To share content publicly, connect a cloud database.", onClose, }: CloudUpgradeProps) { const t = useT(); const [selectedProvider, setSelectedProvider] = useState(null); const [dbUrl, setDbUrl] = useState(""); const [authToken, setAuthToken] = useState(""); const [status, setStatus] = useState< "idle" | "saving" | "polling" | "success" | "error" >("idle"); const [errorMsg, setErrorMsg] = useState(""); const connectingRef = useRef(false); const provider = PROVIDERS.find((p) => p.id === selectedProvider); const handleConnect = useCallback(async () => { if (connectingRef.current) return; connectingRef.current = true; if (!dbUrl.trim()) { setErrorMsg( "Database settings are deployment-level. Configure DATABASE_URL with your host and redeploy the app.", ); setStatus("error"); connectingRef.current = false; return; } try { setStatus("error"); setErrorMsg(""); throw new Error( "Database settings are deployment-level. Configure DATABASE_URL and DATABASE_AUTH_TOKEN with your host, redeploy, then check sharing again.", ); } catch (e) { setErrorMsg( e instanceof Error ? e.message : t("raw.cloudConnectionFailed"), ); setStatus("error"); } finally { connectingRef.current = false; } }, [dbUrl, authToken, t]); const isConnecting = status === "saving" || status === "polling"; return (
{/* Header */}

{title}

{onClose && ( )}

{description}

{/* Provider selection */}
{PROVIDERS.map((p) => ( ))}
{/* Provider setup steps */} {provider && (

{t("raw.cloudSetupSteps")}

    {provider.steps.map((step, i) => (
  1. {step}
  2. ))}
)} {/* Credential inputs */}
setDbUrl(e.target.value)} disabled={isConnecting} className="w-full rounded-md border border-zinc-300 bg-white px-3 py-2 text-sm text-zinc-900 placeholder:text-zinc-400 focus:border-blue-500 focus:outline-none focus:ring-1 focus:ring-blue-500 disabled:opacity-50 dark:border-zinc-700 dark:bg-zinc-900 dark:text-zinc-100 dark:placeholder:text-zinc-500" />
{(!provider || provider.needsAuthToken) && (
setAuthToken(e.target.value)} disabled={isConnecting} className="w-full rounded-md border border-zinc-300 bg-white px-3 py-2 text-sm text-zinc-900 placeholder:text-zinc-400 focus:border-blue-500 focus:outline-none focus:ring-1 focus:ring-blue-500 disabled:opacity-50 dark:border-zinc-700 dark:bg-zinc-900 dark:text-zinc-100 dark:placeholder:text-zinc-500" />
)}
{/* Error message */} {status === "error" && errorMsg && (

{errorMsg}

)} {/* Success message */} {status === "success" && (
{t("raw.cloudConnectedReloading")}
)} {/* Connect button */}
); }