"use client"; import { useState, useCallback } from "react"; import { Button } from "@/components/ui/button"; import { HotkeyButton } from "@/components/ui/hotkey-button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from "@/components/ui/dialog"; import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from "@/components/ui/card"; import { useAppStore } from "@/store/app-store"; import { getElectronAPI } from "@/lib/electron"; import { initializeProject } from "@/lib/project-init"; import { FolderOpen, Plus, Folder, Clock, Sparkles, MessageSquare, ChevronDown, Loader2, } from "lucide-react"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; import { toast } from "sonner"; export function WelcomeView() { const { projects, addProject, setCurrentProject, setCurrentView } = useAppStore(); const [showNewProjectDialog, setShowNewProjectDialog] = useState(false); const [newProjectName, setNewProjectName] = useState(""); const [newProjectPath, setNewProjectPath] = useState(""); const [isCreating, setIsCreating] = useState(false); const [isOpening, setIsOpening] = useState(false); const [showInitDialog, setShowInitDialog] = useState(false); const [isAnalyzing, setIsAnalyzing] = useState(false); const [initStatus, setInitStatus] = useState<{ isNewProject: boolean; createdFiles: string[]; projectName: string; projectPath: string; } | null>(null); /** * Kick off project analysis agent to analyze the codebase */ const analyzeProject = useCallback(async (projectPath: string) => { const api = getElectronAPI(); if (!api.autoMode?.analyzeProject) { console.log("[Welcome] Auto mode API not available, skipping analysis"); return; } setIsAnalyzing(true); try { console.log("[Welcome] Starting project analysis for:", projectPath); const result = await api.autoMode.analyzeProject(projectPath); if (result.success) { toast.success("Project analyzed", { description: "AI agent has analyzed your project structure", }); } else { console.error("[Welcome] Project analysis failed:", result.error); } } catch (error) { console.error("[Welcome] Failed to analyze project:", error); } finally { setIsAnalyzing(false); } }, []); /** * Initialize project and optionally kick off project analysis agent */ const initializeAndOpenProject = useCallback( async (path: string, name: string) => { setIsOpening(true); try { // Initialize the .automaker directory structure const initResult = await initializeProject(path); if (!initResult.success) { toast.error("Failed to initialize project", { description: initResult.error || "Unknown error occurred", }); return; } const project = { id: `project-${Date.now()}`, name, path, lastOpened: new Date().toISOString(), }; addProject(project); setCurrentProject(project); // Show initialization dialog if files were created if (initResult.createdFiles && initResult.createdFiles.length > 0) { setInitStatus({ isNewProject: initResult.isNewProject, createdFiles: initResult.createdFiles, projectName: name, projectPath: path, }); setShowInitDialog(true); // Kick off agent to analyze the project and update app_spec.txt console.log( "[Welcome] Project initialized, created files:", initResult.createdFiles ); console.log("[Welcome] Kicking off project analysis agent..."); // Start analysis in background (don't await, let it run async) analyzeProject(path); } else { toast.success("Project opened", { description: `Opened ${name}`, }); } } catch (error) { console.error("[Welcome] Failed to open project:", error); toast.error("Failed to open project", { description: error instanceof Error ? error.message : "Unknown error", }); } finally { setIsOpening(false); } }, [addProject, setCurrentProject, analyzeProject] ); const handleOpenProject = useCallback(async () => { const api = getElectronAPI(); const result = await api.openDirectory(); if (!result.canceled && result.filePaths[0]) { const path = result.filePaths[0]; // Extract folder name from path (works on both Windows and Mac/Linux) const name = path.split(/[/\\]/).filter(Boolean).pop() || "Untitled Project"; await initializeAndOpenProject(path, name); } }, [initializeAndOpenProject]); /** * Handle clicking on a recent project */ const handleRecentProjectClick = useCallback( async (project: { id: string; name: string; path: string }) => { await initializeAndOpenProject(project.path, project.name); }, [initializeAndOpenProject] ); const handleNewProject = () => { setNewProjectName(""); setNewProjectPath(""); setShowNewProjectDialog(true); }; const handleInteractiveMode = () => { setCurrentView("interview"); }; const handleSelectDirectory = async () => { const api = getElectronAPI(); const result = await api.openDirectory(); if (!result.canceled && result.filePaths[0]) { setNewProjectPath(result.filePaths[0]); } }; const handleCreateProject = async () => { if (!newProjectName || !newProjectPath) return; setIsCreating(true); try { const api = getElectronAPI(); const projectPath = `${newProjectPath}/${newProjectName}`; // Create project directory await api.mkdir(projectPath); // Initialize .automaker directory with all necessary files const initResult = await initializeProject(projectPath); if (!initResult.success) { toast.error("Failed to initialize project", { description: initResult.error || "Unknown error occurred", }); return; } // Update the app_spec.txt with the project name await api.writeFile( `${projectPath}/.automaker/app_spec.txt`, ` ${newProjectName} Describe your project here. This file will be analyzed by an AI agent to understand your project structure and tech stack. ` ); const project = { id: `project-${Date.now()}`, name: newProjectName, path: projectPath, lastOpened: new Date().toISOString(), }; addProject(project); setCurrentProject(project); setShowNewProjectDialog(false); toast.success("Project created", { description: `Created ${newProjectName} with .automaker directory`, }); // Set init status to show the dialog setInitStatus({ isNewProject: true, createdFiles: initResult.createdFiles || [], projectName: newProjectName, projectPath: projectPath, }); setShowInitDialog(true); } catch (error) { console.error("Failed to create project:", error); toast.error("Failed to create project", { description: error instanceof Error ? error.message : "Unknown error", }); } finally { setIsCreating(false); } }; const recentProjects = [...projects] .sort((a, b) => { const dateA = a.lastOpened ? new Date(a.lastOpened).getTime() : 0; const dateB = b.lastOpened ? new Date(b.lastOpened).getTime() : 0; return dateB - dateA; }) .slice(0, 5); return (
{/* Header Section */}
Automaker Logo

Welcome to Automaker

Your autonomous AI development studio

{/* Content Area */}
{/* Quick Actions */}

New Project

Create a new project from scratch with AI-powered development

Quick Setup Interactive Mode

Open Project

Open an existing project folder to continue working

{/* Recent Projects */} {recentProjects.length > 0 && (

Recent Projects

{recentProjects.map((project) => (
handleRecentProjectClick(project)} data-testid={`recent-project-${project.id}`} >

{project.name}

{project.path}

{project.lastOpened && (

{new Date( project.lastOpened ).toLocaleDateString()}

)}
))}
)} {/* Empty State for No Projects */} {recentProjects.length === 0 && (

No projects yet

Get started by creating a new project or opening an existing one

)}
{/* New Project Dialog */} Create New Project Set up a new project directory with initial configuration files.
setNewProjectName(e.target.value)} className="bg-input border-border text-foreground placeholder:text-muted-foreground" data-testid="project-name-input" />
setNewProjectPath(e.target.value)} className="flex-1 bg-input border-border text-foreground placeholder:text-muted-foreground" data-testid="project-path-input" />
{isCreating ? "Creating..." : "Create Project"}
{/* Project Initialization Dialog */} {initStatus?.isNewProject ? "Project Initialized" : "Project Updated"} {initStatus?.isNewProject ? `Created .automaker directory structure for ${initStatus?.projectName}` : `Updated missing files in .automaker for ${initStatus?.projectName}`}

Created files:

    {initStatus?.createdFiles.map((file) => (
  • {file}
  • ))}
{initStatus?.isNewProject && (
{isAnalyzing ? (

AI agent is analyzing your project structure...

) : (

Tip: Edit the{" "} app_spec.txt {" "} file to describe your project. The AI agent will use this to understand your project structure.

)}
)}
{/* Loading overlay when opening project */} {isOpening && (

Initializing project...

)}
); }