import { useNavigate } from "@tanstack/react-router"; import { ChevronRight, GiftIcon, Sparkles, CheckCircle, AlertCircle, XCircle, Loader2, Settings, GlobeIcon, } from "lucide-react"; import { providerSettingsRoute } from "@/routes/settings/providers/$provider"; import SetupProviderCard from "@/components/SetupProviderCard"; import { useState, useEffect, useCallback } from "react"; import { IpcClient } from "@/ipc/ipc_client"; import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, } from "@/components/ui/accordion"; import { Button } from "@/components/ui/button"; import { cn } from "@/lib/utils"; import { NodeSystemInfo } from "@/ipc/ipc_types"; import { usePostHog } from "posthog-js/react"; import { useLanguageModelProviders } from "@/hooks/useLanguageModelProviders"; import { useScrollAndNavigateTo } from "@/hooks/useScrollAndNavigateTo"; // @ts-ignore import logo from "../../assets/logo.svg"; type NodeInstallStep = | "install" | "waiting-for-continue" | "continue-processing" | "finished-checking"; export function SetupBanner() { const posthog = usePostHog(); const navigate = useNavigate(); const { isAnyProviderSetup, isLoading: loading } = useLanguageModelProviders(); const [nodeSystemInfo, setNodeSystemInfo] = useState( null, ); const [nodeCheckError, setNodeCheckError] = useState(false); const [nodeInstallStep, setNodeInstallStep] = useState("install"); const checkNode = useCallback(async () => { try { setNodeCheckError(false); const status = await IpcClient.getInstance().getNodejsStatus(); setNodeSystemInfo(status); } catch (error) { console.error("Failed to check Node.js status:", error); setNodeSystemInfo(null); setNodeCheckError(true); } }, [setNodeSystemInfo, setNodeCheckError]); useEffect(() => { checkNode(); }, [checkNode]); const settingsScrollAndNavigateTo = useScrollAndNavigateTo("/settings", { behavior: "smooth", block: "start", }); const handleGoogleSetupClick = () => { posthog.capture("setup-flow:ai-provider-setup:google:click"); navigate({ to: providerSettingsRoute.id, params: { provider: "google" }, }); }; const handleOpenRouterSetupClick = () => { posthog.capture("setup-flow:ai-provider-setup:openrouter:click"); navigate({ to: providerSettingsRoute.id, params: { provider: "openrouter" }, }); }; const handleDyadProSetupClick = () => { posthog.capture("setup-flow:ai-provider-setup:dyad:click"); IpcClient.getInstance().openExternalUrl( "https://www.dyad.sh/pro?utm_source=dyad-app&utm_medium=app&utm_campaign=setup-banner", ); }; const handleOtherProvidersClick = () => { posthog.capture("setup-flow:ai-provider-setup:other:click"); settingsScrollAndNavigateTo("provider-settings"); }; const handleNodeInstallClick = useCallback(async () => { posthog.capture("setup-flow:start-node-install-click"); setNodeInstallStep("waiting-for-continue"); IpcClient.getInstance().openExternalUrl(nodeSystemInfo!.nodeDownloadUrl); }, [nodeSystemInfo, setNodeInstallStep]); const finishNodeInstall = useCallback(async () => { posthog.capture("setup-flow:continue-node-install-click"); setNodeInstallStep("continue-processing"); await IpcClient.getInstance().reloadEnvPath(); await checkNode(); setNodeInstallStep("finished-checking"); }, [checkNode, setNodeInstallStep]); // We only check for node version because pnpm is not required for the app to run. const isNodeSetupComplete = Boolean(nodeSystemInfo?.nodeVersion); const itemsNeedAction: string[] = []; if (!isNodeSetupComplete && nodeSystemInfo) { itemsNeedAction.push("node-setup"); } if (!isAnyProviderSetup() && !loading) { itemsNeedAction.push("ai-setup"); } if (itemsNeedAction.length === 0) { return (

Build your dream app

); } const bannerClasses = cn( "w-full mb-6 border rounded-xl shadow-sm overflow-hidden", "border-zinc-200 dark:border-zinc-700", ); const getStatusIcon = (isComplete: boolean, hasError: boolean = false) => { if (hasError) { return ; } return isComplete ? ( ) : ( ); }; return ( <>

Setup Dyad

{getStatusIcon(isNodeSetupComplete, nodeCheckError)} 1. Install Node.js (App Runtime)
{nodeCheckError && (

Error checking Node.js status. Try installing Node.js.

)} {isNodeSetupComplete ? (

Node.js ({nodeSystemInfo!.nodeVersion}) installed.{" "} {nodeSystemInfo!.pnpmVersion && ( {" "} (optional) pnpm ({nodeSystemInfo!.pnpmVersion}) installed. )}

) : (

Node.js is required to run apps locally.

{nodeInstallStep === "waiting-for-continue" && (

After you have installed Node.js, click "Continue". If the installer didn't work, try{" "} { IpcClient.getInstance().openExternalUrl( "https://nodejs.org/en/download", ); }} > more download options .

)}
)}
{getStatusIcon(isAnyProviderSetup())} 2. Setup AI Model Access

Connect your preferred AI provider to start generating code.

} title="Setup Google Gemini API Key" subtitle={ <> Use Google Gemini for free } /> } title="Setup OpenRouter API Key" subtitle={ <> Free models available } /> } title="Setup Dyad Pro" subtitle={ <> Access all AI models with one plan } />

Setup other AI providers

OpenAI, Anthropic, OpenRouter and more

); } function NodeJsHelpCallout() { return ( ); } function NodeInstallButton({ nodeInstallStep, handleNodeInstallClick, finishNodeInstall, }: { nodeInstallStep: NodeInstallStep; handleNodeInstallClick: () => void; finishNodeInstall: () => void; }) { switch (nodeInstallStep) { case "install": return ( ); case "continue-processing": return ( ); case "waiting-for-continue": return ( ); case "finished-checking": return (
Node.js not detected. Closing and re-opening Dyad usually fixes this.
); default: const _exhaustiveCheck: never = nodeInstallStep; } } export const OpenRouterSetupBanner = ({ className, }: { className?: string; }) => { const posthog = usePostHog(); const navigate = useNavigate(); return ( { posthog.capture("setup-flow:ai-provider-setup:openrouter:click"); navigate({ to: providerSettingsRoute.id, params: { provider: "openrouter" }, }); }} tabIndex={0} leadingIcon={ } title="Setup OpenRouter API Key" subtitle={ <> Free models available } /> ); };