import { useState, useEffect, useCallback } from 'react' import { useProjects, useFeatures } from './hooks/useProjects' import { useProjectWebSocket } from './hooks/useWebSocket' import { useFeatureSound } from './hooks/useFeatureSound' const STORAGE_KEY = 'autonomous-coder-selected-project' import { ProjectSelector } from './components/ProjectSelector' import { KanbanBoard } from './components/KanbanBoard' import { AgentControl } from './components/AgentControl' import { ProgressDashboard } from './components/ProgressDashboard' import { SetupWizard } from './components/SetupWizard' import { AddFeatureForm } from './components/AddFeatureForm' import { FeatureModal } from './components/FeatureModal' import { DebugLogViewer } from './components/DebugLogViewer' import { Plus, Loader2 } from 'lucide-react' import type { Feature } from './lib/types' function App() { // Initialize selected project from localStorage const [selectedProject, setSelectedProject] = useState(() => { try { return localStorage.getItem(STORAGE_KEY) } catch { return null } }) const [showAddFeature, setShowAddFeature] = useState(false) const [selectedFeature, setSelectedFeature] = useState(null) const [setupComplete, setSetupComplete] = useState(true) // Start optimistic const [debugOpen, setDebugOpen] = useState(false) const { data: projects, isLoading: projectsLoading } = useProjects() const { data: features } = useFeatures(selectedProject) const wsState = useProjectWebSocket(selectedProject) // Play sounds when features move between columns useFeatureSound(features) // Persist selected project to localStorage const handleSelectProject = useCallback((project: string | null) => { setSelectedProject(project) try { if (project) { localStorage.setItem(STORAGE_KEY, project) } else { localStorage.removeItem(STORAGE_KEY) } } catch { // localStorage not available } }, []) // Validate stored project exists (clear if project was deleted) useEffect(() => { if (selectedProject && projects && !projects.some(p => p.name === selectedProject)) { handleSelectProject(null) } }, [selectedProject, projects, handleSelectProject]) // Keyboard shortcuts useEffect(() => { const handleKeyDown = (e: KeyboardEvent) => { // Ignore if user is typing in an input if (e.target instanceof HTMLInputElement || e.target instanceof HTMLTextAreaElement) { return } // D : Toggle debug window if (e.key === 'd' || e.key === 'D') { e.preventDefault() setDebugOpen(prev => !prev) } // N : Add new feature (when project selected) if ((e.key === 'n' || e.key === 'N') && selectedProject) { e.preventDefault() setShowAddFeature(true) } // Escape : Close modals if (e.key === 'Escape') { if (showAddFeature) { setShowAddFeature(false) } else if (selectedFeature) { setSelectedFeature(null) } else if (debugOpen) { setDebugOpen(false) } } } window.addEventListener('keydown', handleKeyDown) return () => window.removeEventListener('keydown', handleKeyDown) }, [selectedProject, showAddFeature, selectedFeature, debugOpen]) // Combine WebSocket progress with feature data const progress = wsState.progress.total > 0 ? wsState.progress : { passing: features?.done.length ?? 0, total: (features?.pending.length ?? 0) + (features?.in_progress.length ?? 0) + (features?.done.length ?? 0), percentage: 0, } if (progress.total > 0 && progress.percentage === 0) { progress.percentage = Math.round((progress.passing / progress.total) * 100 * 10) / 10 } if (!setupComplete) { return setSetupComplete(true)} /> } return (
{/* Header */}
{/* Logo and Title */}

Autonomous Coder

{/* Controls */}
{selectedProject && ( <> )}
{/* Main Content */}
{!selectedProject ? (

Welcome to Autonomous Coder

Select a project from the dropdown above or create a new one to get started.

) : (
{/* Progress Dashboard */} {/* Initializing Features State - show when agent is running but no features yet */} {features && features.pending.length === 0 && features.in_progress.length === 0 && features.done.length === 0 && wsState.agentStatus === 'running' && (

Initializing Features...

The agent is reading your spec and creating features. This may take a moment.

)} {/* Kanban Board */}
)}
{/* Add Feature Modal */} {showAddFeature && selectedProject && ( setShowAddFeature(false)} /> )} {/* Feature Detail Modal */} {selectedFeature && selectedProject && ( setSelectedFeature(null)} /> )} {/* Debug Log Viewer - fixed to bottom */} {selectedProject && ( setDebugOpen(!debugOpen)} onClear={wsState.clearLogs} /> )}
) } export default App