import React, { useState, useRef, useCallback } from 'react';
import { Button } from './components/ui/button';
import { Card, CardContent, CardHeader, CardTitle } from './components/ui/card';
import { Textarea } from './components/ui/textarea';
import { Badge } from './components/ui/badge';
import { Separator } from './components/ui/separator';
import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetTrigger } from './components/ui/sheet';
import { ScrollArea } from './components/ui/scroll-area';
import { Alert, AlertDescription } from './components/ui/alert';
import { Tabs, TabsContent, TabsList, TabsTrigger } from './components/ui/tabs';
import {
Play,
Download,
Code2,
FileText,
Loader2,
AlertCircle,
Eye,
Sparkles,
Terminal
} from 'lucide-react';
import { toast, Toaster } from 'sonner';
import './App.css';
const BACKEND_URL = process.env.REACT_APP_BACKEND_URL || 'http://localhost:8001';
export default function App() {
const [prompt, setPrompt] = useState('');
const [output, setOutput] = useState('');
const [isGenerating, setIsGenerating] = useState(false);
const [parsedFiles, setParsedFiles] = useState({});
const [error, setError] = useState('');
const [previewUrl, setPreviewUrl] = useState('');
const eventSourceRef = useRef(null);
const createPreviewUrl = useCallback((files) => {
console.log('🔍 Creating preview for files:', Object.keys(files));
if (!files || Object.keys(files).length === 0) {
console.log('❌ No files provided for preview');
return '';
}
try {
// Check if this is a React project
const hasReactFiles = Object.keys(files).some(name =>
name.endsWith('.jsx') || name.endsWith('.tsx')
);
console.log('📱 Has React files:', hasReactFiles);
if (hasReactFiles) {
const previewUrl = createReactPreview(files);
console.log('⚛️ React preview URL created:', !!previewUrl);
return previewUrl;
}
// Handle HTML files
const htmlFile = files['index.html'] || Object.values(files).find(content =>
typeof content === 'string' && content.includes('')
);
if (htmlFile) {
console.log('🌐 Processing HTML file');
let htmlContent = htmlFile;
// Inject all CSS files
const cssFiles = Object.entries(files).filter(([name]) => name.endsWith('.css'));
if (cssFiles.length > 0) {
console.log('🎨 Injecting CSS files:', cssFiles.map(([name]) => name));
const allCSS = cssFiles.map(([, content]) => content).join('\n\n');
htmlContent = htmlContent.replace(
'',
`\n`
);
}
const blob = new Blob([htmlContent], { type: 'text/html' });
const url = URL.createObjectURL(blob);
console.log('✅ HTML preview URL created successfully');
return url;
}
console.log('❌ No valid HTML or React files found for preview');
return '';
} catch (error) {
console.error('❌ Error creating preview:', error);
toast.error(`Preview generation failed: ${error.message}`);
return '';
}
}, []);
const createReactPreview = (files) => {
console.log('⚛️ Creating React preview...');
try {
// Collect all CSS files
const cssFiles = Object.entries(files).filter(([name]) => name.endsWith('.css'));
const allCSS = cssFiles.map(([, content]) => content).join('\n\n');
console.log('🎨 Combined CSS length:', allCSS.length);
// Collect all JSX files
const jsxFiles = Object.entries(files).filter(([name]) =>
name.endsWith('.jsx') || name.endsWith('.tsx') || name.endsWith('.js')
);
console.log('📄 JSX files found:', jsxFiles.map(([name]) => name));
if (jsxFiles.length === 0) {
console.log('❌ No JSX files found');
return '';
}
// Get all component code
const allComponents = jsxFiles.map(([filename, content]) => {
const componentName = filename.split('/').pop().replace(/\.(jsx|tsx|js)$/, '') || 'Component';
// Simple cleanup - just remove imports and exports
let cleanContent = content
.replace(/^import\s+.*$/gm, '') // Remove imports
.replace(/^export\s+default\s+/gm, '') // Remove export default
.replace(/^export\s*\{[^}]*\}/gm, '') // Remove named exports
.trim();
return cleanContent;
}).join('\n\n');
// Create a simplified React preview with better error handling
const htmlWrapper = `
React Preview
`;
console.log('📦 Created HTML wrapper, length:', htmlWrapper.length);
const blob = new Blob([htmlWrapper], { type: 'text/html' });
const url = URL.createObjectURL(blob);
console.log('✅ React preview URL created successfully');
return url;
} catch (error) {
console.error('❌ Error in createReactPreview:', error);
toast.error(`React preview failed: ${error.message}`);
return '';
}
};
const handleGenerate = async () => {
if (!prompt.trim()) {
toast.error('Please enter a prompt');
return;
}
setIsGenerating(true);
setOutput('');
setParsedFiles({});
setError('');
setPreviewUrl('');
try {
const response = await fetch(`${BACKEND_URL}/api/generate`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ prompt }),
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const reader = response.body.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done) break;
const chunk = decoder.decode(value);
const lines = chunk.split('\n');
for (const line of lines) {
if (line.startsWith('data: ')) {
try {
const data = JSON.parse(line.slice(6));
if (data.error) {
setError(`${data.error}: ${data.details || 'Unknown error'}`);
toast.error(data.error);
continue;
}
if (data.type === 'content') {
setOutput(data.full_content || '');
} else if (data.type === 'complete') {
setParsedFiles(data.files || {});
if (data.files && Object.keys(data.files).length > 0) {
const url = createPreviewUrl(data.files);
setPreviewUrl(url);
toast.success(`Generated ${Object.keys(data.files).length} file(s)!`);
}
}
} catch (e) {
console.error('Error parsing SSE data:', e);
}
}
}
}
} catch (error) {
console.error('Generation error:', error);
setError(`Connection error: ${error.message}`);
toast.error('Failed to generate code. Please try again.');
} finally {
setIsGenerating(false);
}
};
const handleDownload = async () => {
if (Object.keys(parsedFiles).length === 0) {
toast.error('No files to download');
return;
}
try {
const response = await fetch(`${BACKEND_URL}/api/download`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(parsedFiles),
});
if (!response.ok) {
throw new Error('Download failed');
}
const blob = await response.blob();
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'generated-code.zip';
document.body.appendChild(a);
a.click();
window.URL.revokeObjectURL(url);
document.body.removeChild(a);
toast.success('Files downloaded successfully!');
} catch (error) {
console.error('Download error:', error);
toast.error('Failed to download files');
}
};
const getFileIcon = (filename) => {
if (filename.endsWith('.html')) return ;
if (filename.endsWith('.jsx') || filename.endsWith('.tsx')) return ;
if (filename.endsWith('.js')) return ;
if (filename.endsWith('.css')) return ;
return ;
};
const getLanguageFromFilename = (filename) => {
if (filename.endsWith('.html')) return 'html';
if (filename.endsWith('.jsx')) return 'jsx';
if (filename.endsWith('.tsx')) return 'tsx';
if (filename.endsWith('.css')) return 'css';
if (filename.endsWith('.js')) return 'javascript';
return 'text';
};
const copyToClipboard = async (content, filename) => {
try {
await navigator.clipboard.writeText(content);
toast.success(`${filename} copied to clipboard!`);
} catch (error) {
console.error('Failed to copy to clipboard:', error);
toast.error('Failed to copy to clipboard');
}
};
return (
{/* Header */}
Generate clean, production-ready code with live preview and instant download.
Powered by Qwen3 Coder via OpenRouter.
{/* Input Section */}
Code Generation
{/* Output Section */}
Generated Output
{Object.keys(parsedFiles).length > 0 && (
Generated Files
{Object.keys(parsedFiles).map((filename) => (
{getFileIcon(filename)}
{filename}
))}
{Object.entries(parsedFiles).map(([filename, content]) => (
{content}
))}
)}
{error && (
{error}
)}
{/* Show individual file blocks if files are parsed */}
{Object.keys(parsedFiles).length > 0 ? (
{Object.entries(parsedFiles).map(([filename, content]) => (
{getFileIcon(filename)}
{filename}
{content}
))}
) : (
{output ? (
{output}
) : (
{isGenerating ? (
Waiting for response...
) : (
'Generated code will appear here...'
)}
)}
)}
{Object.keys(parsedFiles).length > 0 && (
{Object.keys(parsedFiles).map((filename) => (
{getFileIcon(filename)}
{filename}
))}
)}
{/* Preview Section */}
Live Preview
{Object.keys(parsedFiles).length > 0 && (
)}
{/* Footer */}
Built with React + FastAPI • Powered by OpenRouter •
Ready for Hugging Face Spaces
);
}