"use client"; import React, { useState, useRef, useEffect } from 'react'; import { useRouter } from 'next/navigation'; type UI_STATE = 'INPUT_REPO' | 'LOADING_REPO' | 'REPO_DASHBOARD' | 'DONE'; interface GitHubIssue { title: string; number: number; url: string; author: string; created_at: string; body_snippet: string; } interface RepoFile { path: string; size: number; type: string; } export default function Home() { const router = useRouter(); const [uiState, setUiState] = useState('INPUT_REPO'); const [repoUrl, setRepoUrl] = useState(""); const [issueUrl, setIssueUrl] = useState(""); const [runConfidence, setRunConfidence] = useState(true); const [repoInfo, setRepoInfo] = useState<{ tree: RepoFile[], issues: GitHubIssue[] } | null>(null); const [selectedFilePath, setSelectedFilePath] = useState(null); const [selectedFileContent, setSelectedFileContent] = useState(""); const [isLoadingFile, setIsLoadingFile] = useState(false); const [statusName, setStatusName] = useState(""); const [statusMessage, setStatusMessage] = useState(""); const [running, setRunning] = useState(false); const [error, setError] = useState(""); const [streamChunks, setStreamChunks] = useState([]); const [result, setResult] = useState(null); const [analyzingFiles, setAnalyzingFiles] = useState([]); const [terminalExpanded, setTerminalExpanded] = useState(true); const [completedSteps, setCompletedSteps] = useState<{step: string, message: string, status: string}[]>([]); // When results arrive, show fixed files inline const [fixedFileView, setFixedFileView] = useState<{path: string, content: string} | null>(null); const [sessionId, setSessionId] = useState(""); const [feedback, setFeedback] = useState(""); const logsEndRef = useRef(null); useEffect(() => { if (logsEndRef.current) { logsEndRef.current.scrollIntoView({ behavior: "smooth" }); } }, [streamChunks, statusMessage, running]); // Handle path detection in status messages to show "live context" useEffect(() => { if (statusMessage) { // Simple path detection (looks for strings with / or .py, .js, etc.) const pathRegex = /([a-zA-Z0-9._\-/]+\.(py|js|tsx|ts|html|css|json|md))/g; const matches = statusMessage.match(pathRegex); if (matches) { setAnalyzingFiles(prev => [...new Set([...prev, ...matches])]); // Auto-focus the first detected path if it exists in our tree const validPath = matches.find(p => repoInfo?.tree.some(f => f.path === p)); if (validPath && validPath !== selectedFilePath) { handleFileClick(validPath); } } } }, [statusMessage]); const handleFetchRepo = () => { if (!repoUrl) return; setError(""); try { // Handle full GitHub URL: https://github.com/owner/repo const url = new URL(repoUrl.trim().replace(/\/+$/, '')); const parts = url.pathname.replace(/^\//, '').split('/'); if (parts.length >= 2 && parts[0] && parts[1]) { router.push(`/${parts[0]}/${parts[1]}`); return; } } catch { // Not a full URL — try treating as "owner/repo" const parts = repoUrl.trim().replace(/^github\.com\//, '').split('/'); if (parts.length >= 2 && parts[0] && parts[1]) { router.push(`/${parts[0]}/${parts[1]}`); return; } } setError("Please enter a valid GitHub URL (e.g. https://github.com/owner/repo)"); }; const handleFileClick = async (path: string) => { setSelectedFilePath(path); setIsLoadingFile(true); try { const res = await fetch(`/api/file_content?repo_url=${encodeURIComponent(repoUrl)}&file_path=${encodeURIComponent(path)}`); if (!res.ok) throw new Error("Failed to fetch file content"); const data = await res.json(); setSelectedFileContent(data.content); } catch (err) { setSelectedFileContent("// Error loading file content..."); } finally { setIsLoadingFile(false); } }; const handleAnalyze = (selectedIssueUrl?: string) => { const finalIssueUrl = selectedIssueUrl || issueUrl; if (!finalIssueUrl || !repoUrl) { setError("Please provide an Issue URL"); return; } setRunning(true); setError(""); setResult(null); setStreamChunks([]); setAnalyzingFiles([]); setCompletedSteps([]); setFixedFileView(null); setTerminalExpanded(true); setStatusName("Starting"); setStatusMessage("Connecting to FixFlow API..."); const params = new URLSearchParams({ issue_url: finalIssueUrl, repo_url: repoUrl, run_confidence: runConfidence.toString(), }); const eventSource = new EventSource(`/api/analyze?${params.toString()}`); setupEventSource(eventSource); }; const handleRefine = () => { if (!feedback || !sessionId) return; setRunning(true); setError(""); setStreamChunks([]); setAnalyzingFiles([]); setCompletedSteps([]); setFixedFileView(null); setTerminalExpanded(true); setStatusName("Refining"); setStatusMessage("Sending feedback to FixFlow..."); const params = new URLSearchParams({ session_id: sessionId, feedback: feedback, }); setFeedback(""); const eventSource = new EventSource(`/api/refine?${params.toString()}`); setupEventSource(eventSource); }; const setupEventSource = (eventSource: EventSource) => { eventSource.addEventListener("status", (e: Event) => { const data = JSON.parse((e as MessageEvent).data); const { step, status, message } = data; setStatusName(step); setStatusMessage(message); if (status === "complete" || status === "error") { setCompletedSteps(prev => [...prev, { step, status, message }]); } }); eventSource.addEventListener("stream", (e: Event) => { const data = JSON.parse((e as MessageEvent).data); setStreamChunks((prev) => [...prev, data.chunk]); }); eventSource.addEventListener("done", (e: Event) => { const data = JSON.parse((e as MessageEvent).data); const r = data.result; setResult(r); if (data.session_id) setSessionId(data.session_id); // Auto-load the first fixed file into the editor so the user sees the change if (r?.fixed_files) { const paths = Object.keys(r.fixed_files); if (paths.length > 0) { const firstPath = paths[0]; setFixedFileView({ path: firstPath, content: r.fixed_files[firstPath] }); setSelectedFilePath(firstPath); } } }); eventSource.addEventListener("error", (e: Event) => { const data = JSON.parse((e as MessageEvent).data); setError(data.error || "Unknown stream error"); setRunning(false); eventSource.close(); }); eventSource.addEventListener("eof", () => { setRunning(false); setStatusName("Done"); setStatusMessage("Fix generated — Review the highlighted file in the editor."); setCompletedSteps(prev => [...prev, { step: "done", status: "complete", message: "Fix generated — review the highlighted file in the editor!" }]); eventSource.close(); // Stay on REPO_DASHBOARD — the fix is shown in the editor }); eventSource.addEventListener("heartbeat", () => {}); eventSource.onerror = (e) => { console.error("SSE Error:", e); }; }; const handleOpenPR = async () => { if (!result) return; try { const res = await fetch("/api/pr", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ repo_url: repoUrl, title: result.issue_title, body: result.fix_explanation + "\n\n---\n*Generated autonomously by FixFlow*", fixed_files: result.fixed_files }) }); const data = await res.json(); if (!res.ok) throw new Error(data.detail || "Failed to create PR"); window.open(data.url, "_blank"); } catch (err: any) { alert("Error: " + err.message); } }; // ── Render Helpers ──────────────────────────────────────────────────────── const renderInputRepo = () => ( <> {/* Hero Section */}
AI-Powered • Autonomous • Production-Ready

Fix Bugs Autonomously

FixFlow analyzes your repository, understands issues deeply, and generates production-ready fixes with pull requests—all automatically.

{/* Stats Grid */}
98%
Success Rate
2.4min
Avg Fix Time
1,247
Bugs Fixed
24/7
Uptime
{/* Main Input Panel */}

🚀 Connect Repository

System Online

Enter a GitHub repository URL to begin autonomous bug analysis and resolution.

setRepoUrl(e.target.value)} onKeyDown={(e) => e.key === 'Enter' && handleFetchRepo()} />
{error && (
❌ {error}
)} {/* Quick Examples */}
Try Popular Repositories
{['vercel/next.js', 'facebook/react', 'microsoft/vscode', 'nodejs/node'].map(repo => ( ))}
{/* Features Grid */}
🧠

Deep Code Analysis

Advanced AI models understand your codebase structure, dependencies, and context to identify root causes.

Lightning Fast

Average fix generation in under 3 minutes. From issue detection to pull request creation.

🔒

Production Safe

Every fix is validated, tested, and reviewed before creating a pull request to your repository.

); const renderLoadingRepo = () => (

Indexing Repository...

We're fetching the file tree and discovering open issues from GitHub.

); const renderRepoDashboard = () => { if (!repoInfo) return null; return (
{/* Left Pane: Explorer with analyzing-glow */}

📁 EXPLORER

{repoInfo.tree.map((file, i) => { const isAnalyzing = analyzingFiles.includes(file.path); return (
handleFileClick(file.path)} className={isAnalyzing ? 'analyzing-glow' : ''} style={{ padding: '6px 8px', cursor: 'pointer', borderRadius: '4px', marginBottom: '2px', backgroundColor: selectedFilePath === file.path ? 'rgba(139, 92, 246, 0.15)' : 'transparent', color: selectedFilePath === file.path ? 'var(--primary)' : 'inherit', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis', transition: 'all 0.3s ease' }} > {isAnalyzing ? '🧠' : '📄'} {file.path}
); })}
{/* Center Pane: Editor + Integrated Step Tracker */}
{/* Editor */}
{/* Show streaming code in editor during step 4 */} {running && (statusName === '4_fix' || statusName === '4_refine') ? '🔧 Generating Fix...' : selectedFilePath || 'Editor'}
{/* Priority 1: Fix just generated — show it in the editor with green */} {fixedFileView && !running ? (
                    {fixedFileView.content}
                  
) : running && (statusName === '4_fix' || statusName === '4_refine') && streamChunks.length > 0 ? ( /* Priority 2: Stream the fix being generated live */
                    {streamChunks.join('')}
                     
                    
) : ( /* Default: show selected file */
                    {selectedFileContent || '// Select a file to browse source code'}
                  
)}
{/* Bottom Step Progress Tracker */}
setTerminalExpanded(!terminalExpanded)}>
{running && } {statusName ? `AGENT — [${statusName.toUpperCase()}]` : 'FIXFLOW AGENT CONSOLE'}
{terminalExpanded ? '▼' : '▲ Show Progress'}
{/* Completed steps */} {completedSteps.map((s, i) => (
{s.status === 'error' ? '✗' : '✓'} {s.message}
))} {/* Current running step */} {running && statusMessage && (
{statusMessage}
)}
{/* Right Pane: Discovery OR Result Actions */}
{result && !running ? ( /* ── Result Actions Panel ── */ <>

FIX READY

{result.bug_summary?.slice(0, 120)}...

{/* Fixed files list — click to view each */}
CHANGED FILES:
{Object.keys(result.fixed_files || {}).map((path) => (
setFixedFileView({ path, content: result.fixed_files[path] })} style={{ padding: '6px 10px', borderRadius: 6, marginBottom: 6, cursor: 'pointer', fontSize: '0.75rem', fontFamily: 'monospace', backgroundColor: fixedFileView?.path === path ? 'rgba(163, 190, 140, 0.15)' : 'rgba(255,255,255,0.04)', color: fixedFileView?.path === path ? '#a3be8c' : 'var(--text-muted)', border: fixedFileView?.path === path ? '1px solid rgba(163,190,140,0.3)' : '1px solid transparent', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }} > 📝 {path}
))}
{/* Action Buttons */}
setFeedback(e.target.value)} onKeyDown={(e) => e.key === 'Enter' && handleRefine()} />
) : ( /* ── Discovery Panel ── */ <>

🐛 DISCOVERY {!running && ( )}

{repoInfo.issues.map((issue) => (
!running && handleAnalyze(issue.url)} >
#{issue.number} {issue.title}
@{issue.author}
))}
{!running && (
setIssueUrl(e.target.value)} />
)} )}
); }; const renderResult = () => (

✅ Fix Successfully Generated

{result.bug_summary}

Analysis & Methodology

{result.root_cause_analysis}

Unified Diff

{result.diff_formatted.split('\n').map((line: string, i: number) => { let className = ""; if (line.startsWith('+') && !line.startsWith('+++')) className = "diff-add"; else if (line.startsWith('-') && !line.startsWith('---')) className = "diff-remove"; else if (line.startsWith('@@') || line.startsWith('---') || line.startsWith('+++')) className = "diff-header"; return
{line}
})}
setFeedback(e.target.value)} onKeyDown={(e) => e.key === 'Enter' && handleRefine()} style={{ flex: 1 }} />
); return (
{/* Vercel-style top bar */}
🔧 FixFlow
API Online
AI
{/* Main content area */}
{uiState === 'INPUT_REPO' && renderInputRepo()} {uiState === 'LOADING_REPO' && renderLoadingRepo()} {uiState === 'REPO_DASHBOARD' && renderRepoDashboard()} {uiState === 'DONE' && renderResult()}
); }