fresh-space-10 / app.py
AiCoderv2's picture
Update app.py
5f7c4f7 verified
import gradio as gr
import anthropic
import json
import os
import re
from typing import Tuple, Optional
import tempfile
import zipfile
from pathlib import Path
# Initialize Anthropic client
ANTHROPIC_API_KEY = os.getenv("ANTHROPIC_API_KEY")
client = anthropic.Anthropic(api_key=ANTHROPIC_API_KEY) if ANTHROPIC_API_KEY else None
def generate_website(description: str, style: str, features: list) -> Tuple[str, str, str]:
"""Generate website HTML, CSS, and JavaScript using Claude Sonnet 4.5"""
if not client:
return create_error_page("API Error: Please set ANTHROPIC_API_KEY environment variable")
# Create prompt for Claude
features_text = ", ".join(features) if features else "basic"
prompt = f"""
Create a complete, modern website based on this description: {description}
Style: {style}
Features to include: {features_text}
Please generate:
1. A complete HTML file with semantic HTML5 structure
2. Modern CSS with responsive design (using CSS Grid/Flexbox)
3. JavaScript for interactivity (if needed)
Requirements:
- Use modern design principles
- Make it fully responsive for mobile, tablet, and desktop
- Include smooth animations and transitions
- Use semantic HTML5 tags
- Add hover effects and micro-interactions
- Include proper meta tags for SEO
- Use a modern color scheme
- Add placeholder content that matches the description
- Make it production-ready
Return your response as a JSON object with this exact structure:
{{
"html": "<complete HTML code here>",
"css": "<complete CSS code here>",
"js": "<complete JavaScript code here>",
"title": "Website Title"
}}
Do not include any explanations outside the JSON. Just return the JSON object.
"""
try:
message = client.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=4000,
temperature=0.7,
messages=[{
"role": "user",
"content": prompt
}]
)
response_text = message.content[0].text
# Extract JSON from response
json_match = re.search(r'\{.*\}', response_text, re.DOTALL)
if json_match:
website_data = json.loads(json_match.group())
# Validate required keys
if all(key in website_data for key in ['html', 'css', 'js', 'title']):
return website_data['html'], website_data['css'], website_data['js']
else:
return create_error_page("Invalid response format from AI")
else:
return create_error_page("Could not parse AI response")
except Exception as e:
return create_error_page(f"Error generating website: {str(e)}")
def create_error_page(error_message: str) -> Tuple[str, str, str]:
"""Create an error page HTML"""
error_html = f"""
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Error</title>
<style>
body {{
font-family: Arial, sans-serif;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
}}
.error-container {{
text-align: center;
padding: 2rem;
background: rgba(255, 255, 255, 0.1);
border-radius: 10px;
backdrop-filter: blur(10px);
}}
h1 {{ font-size: 2rem; margin-bottom: 1rem; }}
p {{ font-size: 1.1rem; opacity: 0.9; }}
</style>
</head>
<body>
<div class="error-container">
<h1>⚠️ Error</h1>
<p>{error_message}</p>
</div>
</body>
</html>
"""
return error_html, "", ""
def create_preview(html: str, css: str, js: str) -> str:
"""Create a complete preview by combining HTML, CSS, and JS"""
# Create a complete HTML document with embedded CSS and JS
preview_html = f"""
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="AI Generated Website">
<title>AI Generated Website Preview</title>
<style>
{css}
</style>
</head>
<body>
{html}
<script>
{js}
</script>
</body>
</html>
"""
return preview_html
def download_website(html: str, css: str, js: str) -> str:
"""Create a zip file with the website files"""
with tempfile.TemporaryDirectory() as temp_dir:
temp_path = Path(temp_dir)
# Create index.html
index_content = f"""<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="AI Generated Website">
<title>AI Generated Website</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
{html}
<script src="script.js"></script>
</body>
</html>"""
with open(temp_path / "index.html", "w", encoding="utf-8") as f:
f.write(index_content)
# Create styles.css
with open(temp_path / "styles.css", "w", encoding="utf-8") as f:
f.write(css)
# Create script.js
with open(temp_path / "script.js", "w", encoding="utf-8") as f:
f.write(js)
# Create README.md
readme_content = """# AI Generated Website
This website was generated using AI Website Builder with Claude Sonnet 4.5.
## Files
- `index.html` - Main HTML file
- `styles.css` - Stylesheet
- `script.js` - JavaScript functionality
## Customization
Feel free to modify any of the files to customize your website.
"""
with open(temp_path / "README.md", "w", encoding="utf-8") as f:
f.write(readme_content)
# Create zip file
zip_path = temp_path / "website.zip"
with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zipf:
zipf.write(temp_path / "index.html", "index.html")
zipf.write(temp_path / "styles.css", "styles.css")
zipf.write(temp_path / "script.js", "script.js")
zipf.write(temp_path / "README.md", "README.md")
# Read zip file as bytes
with open(zip_path, "rb") as f:
zip_bytes = f.read()
return zip_bytes
# Custom CSS for the Gradio interface
custom_css = """
.main-container {
max-width: 1400px !important;
margin: 0 auto !important;
}
.header {
text-align: center;
padding: 2rem;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border-radius: 10px;
margin-bottom: 2rem;
}
.header h1 {
margin: 0;
font-size: 2.5rem;
font-weight: 700;
}
.header p {
margin: 0.5rem 0 0 0;
opacity: 0.9;
font-size: 1.1rem;
}
.generator-section {
background: #f8f9fa;
padding: 2rem;
border-radius: 10px;
margin-bottom: 2rem;
}
.preview-section {
background: white;
border: 1px solid #e0e0e0;
border-radius: 10px;
overflow: hidden;
}
.preview-frame {
width: 100%;
height: 600px;
border: none;
}
.code-editor {
font-family: 'Courier New', monospace;
font-size: 14px;
}
.tabs {
margin-top: 1rem;
}
.generate-btn {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
border: none !important;
font-weight: 600 !important;
padding: 0.75rem 2rem !important;
font-size: 1.1rem !important;
}
.generate-btn:hover {
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);
}
.feature-group {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 1rem;
margin-top: 1rem;
}
.feature-checkbox {
background: white;
padding: 1rem;
border-radius: 8px;
border: 2px solid #e0e0e0;
transition: all 0.3s ease;
}
.feature-checkbox:hover {
border-color: #667eea;
transform: translateY(-2px);
}
.feature-checkbox label {
font-weight: 500;
cursor: pointer;
}
@media (max-width: 768px) {
.header h1 {
font-size: 2rem;
}
.feature-group {
grid-template-columns: 1fr;
}
}
"""
# Create the Gradio interface
with gr.Blocks(css=custom_css, title="AI Website Builder") as demo:
# Header
gr.HTML("""
<div class="header">
<h1>🚀 AI Website Builder</h1>
<p>Create stunning websites instantly with Claude Sonnet 4.5</p>
<p><a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" style="color: white; text-decoration: underline;">Built with anycoder</a></p>
</div>
""")
with gr.Row():
# Left column - Generator controls
with gr.Column(scale=1):
with gr.Box(elem_classes=["generator-section"]):
gr.Markdown("## 📝 Website Description")
description = gr.Textbox(
label="Describe your website",
placeholder="e.g., A modern portfolio website for a web developer with projects, about section, and contact form",
lines=4,
max_lines=6
)
gr.Markdown("## 🎨 Style Preference")
style = gr.Dropdown(
choices=[
"Modern & Minimalist",
"Corporate & Professional",
"Creative & Colorful",
"Dark & Moody",
"Elegant & Sophisticated",
"Playful & Fun",
"Tech & Futuristic",
"Nature & Organic"
],
value="Modern & Minimalist",
label="Choose a style"
)
gr.Markdown("## ✨ Features")
with gr.Row(elem_classes=["feature-group"]):
with gr.Column():
responsive = gr.Checkbox(label="📱 Responsive Design", value=True)
animations = gr.Checkbox(label="🎬 Animations", value=True)
navbar = gr.Checkbox(label="🧭 Navigation Bar", value=True)
with gr.Column():
footer = gr.Checkbox(label="📄 Footer", value=True)
social = gr.Checkbox(label="🔗 Social Links", value=False)
contact = gr.Checkbox(label="📧 Contact Form", value=False)
with gr.Column():
gallery = gr.Checkbox(label="🖼️ Image Gallery", value=False)
blog = gr.Checkbox(label="📝 Blog Section", value=False)
dark_mode = gr.Checkbox(label="🌙 Dark Mode Toggle", value=False)
features = [responsive, animations, navbar, footer, social, contact, gallery, blog, dark_mode]
generate_btn = gr.Button(
"🚀 Generate Website",
variant="primary",
size="lg",
elem_classes=["generate-btn"]
)
# Right column - Preview and Code
with gr.Column(scale=2):
with gr.Tabs(elem_classes=["tabs"]) as tabs:
with gr.TabItem("🖥️ Live Preview"):
with gr.Box(elem_classes=["preview-section"]):
preview = gr.HTML(
value='<div style="padding: 3rem; text-align: center; color: #666;">Your website preview will appear here after generation</div>',
label="Preview"
)
with gr.TabItem("💻 Code Editor"):
with gr.Tabs():
with gr.TabItem("HTML"):
html_code = gr.Code(
language="html",
label="HTML Code",
elem_classes=["code-editor"],
lines=20
)
with gr.TabItem("CSS"):
css_code = gr.Code(
language="css",
label="CSS Code",
elem_classes=["code-editor"],
lines=20
)
with gr.TabItem("JavaScript"):
js_code = gr.Code(
language="javascript",
label="JavaScript Code",
elem_classes=["code-editor"],
lines=20
)
with gr.TabItem("⬇️ Download"):
gr.Markdown("### 📦 Download Your Website")
gr.Markdown("Click the button below to download a complete ZIP file with all website assets.")
download_btn = gr.DownloadButton(
"📥 Download Website",
variant="primary",
size="lg"
)
gr.Markdown("""
**What's included:**
- `index.html` - Main HTML file
- `styles.css` - Complete stylesheet
- `script.js` - JavaScript functionality
- `README.md` - Documentation
""")
# Store generated website data
website_state = gr.State({"html": "", "css": "", "js": ""})
# Event handlers
def generate_and_update(desc, style_choice, *feature_flags):
"""Generate website and update all components"""
if not desc.strip():
return (
'<div style="padding: 3rem; text-align: center; color: #e74c3c;">Please enter a website description</div>',
"",
"",
"",
None,
{"html": "", "css": "", "js": ""}
)
# Filter selected features
selected_features = []
feature_names = [
"Responsive Design", "Animations", "Navigation Bar",
"Footer", "Social Links", "Contact Form",
"Image Gallery", "Blog Section", "Dark Mode Toggle"
]
for i, flag in enumerate(feature_flags):
if flag:
selected_features.append(feature_names[i])
# Generate website
html, css, js = generate_website(desc, style_choice, selected_features)
# Create preview
preview_html = create_preview(html, css, js)
# Create download file
zip_file = download_website(html, css, js)
return (
preview_html,
html,
css,
js,
zip_file,
{"html": html, "css": css, "js": js}
)
# Wire up the generate button
generate_btn.click(
fn=generate_and_update,
inputs=[description, style] + features,
outputs=[
preview,
html_code,
css_code,
js_code,
download_btn,
website_state
]
)
# Add example descriptions
gr.Examples(
examples=[
[
"A professional portfolio website for a UX designer showcasing their best projects with case studies, testimonials, and contact information",
"Modern & Minimalist",
[True, True, True, True, False, True, False, False, False]
],
[
"A vibrant restaurant website with menu, online reservations, gallery of dishes, and location map",
"Creative & Colorful",
[True, True, True, True, True, True, True, False, False]
],
[
"A tech startup landing page with features, pricing plans, team section, and signup form",
"Tech & Futuristic",
[True, True, True, True, False, True, False, False, True]
],
[
"A photography portfolio with gallery, about section, services offered, and booking form",
"Elegant & Sophisticated",
[True, True, True, True, True, True, True, False, False]
]
],
inputs=[description, style] + features,
outputs=[preview, html_code, css_code, js_code, download_btn, website_state],
fn=generate_and_update,
cache_examples=False
)
# Launch the app
if __name__ == "__main__":
demo.launch(
share=False,
show_error=True,
show_api=False
)