yeye / app.py
mgbam's picture
Update app.py
185e6e8 verified
import os
import uuid
import base64
import tempfile
from typing import Dict, List, Optional, Tuple
import gradio as gr
# Import from our modularized files
from config import (
AVAILABLE_MODELS, DEFAULT_MODEL, THEME_CONFIGS, DEMO_LIST,
HTML_SYSTEM_PROMPT, get_saved_theme, save_theme_preference
)
from utils import (
get_inference_client, remove_code_block, extract_text_from_file,
create_multimodal_message, apply_search_replace_changes,
cleanup_all_temp_media, reap_old_media, get_gradio_language
)
from web_utils import extract_website_content, enhance_query_with_search
from media_generation import (
generate_image_with_qwen, generate_image_to_image, generate_video_from_image,
generate_video_from_text, generate_music_from_text
)
from code_processing import (
is_streamlit_code, is_gradio_code, extract_html_document,
parse_transformers_js_output, format_transformers_js_output, build_transformers_inline_html,
parse_svelte_output, format_svelte_output,
parse_multipage_html_output, format_multipage_output,
validate_and_autofix_files, inline_multipage_into_single_preview,
apply_generated_media_to_html
)
from sandbox import send_to_sandbox, send_streamlit_to_stlite, send_gradio_to_lite
from generation_engine import generation_code
# Initialize theme
current_theme_name = get_saved_theme()
current_theme = THEME_CONFIGS[current_theme_name]["theme"]
# =============================================================================
# PROFESSIONAL UI FUNCTIONS
# =============================================================================
def get_model_info_html(model):
"""Generate HTML for model information"""
vision_badge = '<span class="feature-badge vision">πŸ‘οΈ Vision</span>' if model.get('supports_vision') else ''
category_color = {
'General': '#3b82f6',
'Code Specialist': '#10b981',
'Vision-Language': '#f59e0b',
'Premium': '#8b5cf6'
}.get(model.get('category', 'General'), '#6b7280')
return f"""
<div class="model-info">
<div class="model-header">
<div class="model-name">{model['name']}</div>
<span class="category-badge" style="background-color: {category_color}">
{model.get('category', 'General')}
</span>
</div>
<div class="model-description">{model.get('description', '')}</div>
<div class="model-features">
{vision_badge}
<span class="feature-badge">πŸš€ Latest</span>
</div>
</div>
"""
def generate_code_stats(code, language):
"""Generate code statistics HTML"""
if not code:
return "<div class='code-stats'>No code generated</div>"
try:
lines = len(code.split('\n'))
chars = len(code)
words = len(code.split())
# Language-specific analysis
if language == "html":
tags = len([m for m in code.split('<') if m.strip()])
stats_content = f"""
<div class='stats-grid'>
<div class='stat-item'>
<span class='stat-number'>{lines}</span>
<span class='stat-label'>Lines</span>
</div>
<div class='stat-item'>
<span class='stat-number'>{tags}</span>
<span class='stat-label'>HTML Tags</span>
</div>
<div class='stat-item'>
<span class='stat-number'>{chars}</span>
<span class='stat-label'>Characters</span>
</div>
<div class='stat-item'>
<span class='stat-number'>{round(chars/1024, 1)}KB</span>
<span class='stat-label'>Size</span>
</div>
</div>
"""
else:
stats_content = f"""
<div class='stats-grid'>
<div class='stat-item'>
<span class='stat-number'>{lines}</span>
<span class='stat-label'>Lines</span>
</div>
<div class='stat-item'>
<span class='stat-number'>{words}</span>
<span class='stat-label'>Words</span>
</div>
<div class='stat-item'>
<span class='stat-number'>{chars}</span>
<span class='stat-label'>Characters</span>
</div>
<div class='stat-item'>
<span class='stat-number'>{language.upper()}</span>
<span class='stat-label'>Language</span>
</div>
</div>
"""
return f"<div class='code-stats'>{stats_content}</div>"
except Exception:
return "<div class='code-stats'>Unable to analyze code</div>"
def handle_model_change(model_selection):
"""Handle model selection change"""
try:
# Extract model name from selection
model_name = model_selection.split(" (")[0]
# Find the model
selected_model = None
for model in AVAILABLE_MODELS:
if model['name'] == model_name:
selected_model = model
break
if selected_model:
model_info_html = get_model_info_html(selected_model)
return selected_model, model_info_html
return DEFAULT_MODEL, get_model_info_html(DEFAULT_MODEL)
except Exception:
return DEFAULT_MODEL, get_model_info_html(DEFAULT_MODEL)
def update_image_input_visibility(model):
"""Update image input visibility based on selected model"""
return gr.update(visible=model.get('supports_vision', False))
def clear_history():
"""Clear all inputs and history"""
return [], [], None, "", ""
def demo_card_click(demo_index):
"""Handle demo card click"""
if 0 <= demo_index < len(DEMO_LIST):
return DEMO_LIST[demo_index]['description']
return ""
# =============================================================================
# CUSTOM CSS
# =============================================================================
CUSTOM_CSS = """
/* Modern Professional Styling */
.gradio-container {
max-width: none !important;
padding: 0 !important;
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
}
.app-header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 24px;
border-radius: 0 0 16px 16px;
margin-bottom: 24px;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
}
.header-content {
display: flex;
justify-content: space-between;
align-items: center;
max-width: 1400px;
margin: 0 auto;
}
.logo-section {
display: flex;
align-items: center;
gap: 16px;
}
.logo {
font-size: 2.5rem;
font-weight: bold;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
}
.app-title h1 {
font-size: 2.2rem;
margin: 0;
font-weight: 700;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
}
.app-title p {
margin: 8px 0 0 0;
opacity: 0.9;
font-size: 1.1rem;
font-weight: 300;
}
.status-indicators {
display: flex;
flex-direction: column;
gap: 12px;
}
.status-item {
display: flex;
align-items: center;
gap: 10px;
font-size: 0.95rem;
font-weight: 500;
}
.status-dot {
width: 10px;
height: 10px;
border-radius: 50%;
background: #10b981;
box-shadow: 0 0 0 2px rgba(16, 185, 129, 0.3);
}
.status-dot.active {
animation: pulse 2s infinite;
}
@keyframes pulse {
0%, 100% {
opacity: 1;
transform: scale(1);
}
50% {
opacity: 0.7;
transform: scale(1.1);
}
}
.sidebar {
background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);
padding: 24px;
border-radius: 16px;
border: 1px solid #e2e8f0;
height: fit-content;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.05);
}
.main-content {
padding-left: 24px;
}
.control-group {
margin-bottom: 20px;
background: white;
padding: 20px;
border-radius: 12px;
border: 1px solid #e5e7eb;
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.05);
}
.group-title {
font-weight: 600;
font-size: 1rem;
color: #1f2937;
margin-bottom: 16px;
display: flex;
align-items: center;
gap: 8px;
border-bottom: 2px solid #f3f4f6;
padding-bottom: 8px;
}
.quick-start-btn {
width: 100%;
margin-bottom: 8px;
text-align: left;
justify-content: flex-start;
transition: all 0.2s ease;
border-radius: 8px;
}
.quick-start-btn:hover {
transform: translateX(4px);
background: linear-gradient(135deg, #f3f4f6 0%, #e5e7eb 100%);
}
.input-group {
background: white;
padding: 28px;
border-radius: 16px;
border: 1px solid #e2e8f0;
margin-bottom: 24px;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.05);
}
.main-input textarea {
font-size: 16px !important;
line-height: 1.6 !important;
border-radius: 12px !important;
border: 2px solid #e5e7eb !important;
transition: border-color 0.2s ease !important;
}
.main-input textarea:focus {
border-color: #3b82f6 !important;
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1) !important;
}
.generate-btn {
background: linear-gradient(135deg, #10b981 0%, #059669 100%) !important;
border: none !important;
font-weight: 600 !important;
font-size: 1.1rem !important;
padding: 18px 36px !important;
border-radius: 12px !important;
transition: all 0.3s ease !important;
}
.generate-btn:hover {
transform: translateY(-2px) !important;
box-shadow: 0 12px 28px rgba(16, 185, 129, 0.35) !important;
background: linear-gradient(135deg, #059669 0%, #047857 100%) !important;
}
.output-tabs {
background: white;
border-radius: 16px;
overflow: hidden;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.05);
border: 1px solid #e5e7eb;
}
.preview-container iframe {
border-radius: 12px;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
border: 1px solid #e5e7eb;
}
.code-editor {
border-radius: 12px !important;
border: 1px solid #e2e8f0 !important;
background: #fafafa !important;
}
.code-sidebar {
padding-left: 20px;
}
.code-stats {
background: white;
padding: 20px;
border-radius: 12px;
border: 1px solid #e5e7eb;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
}
.stats-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 16px;
}
.stat-item {
text-align: center;
padding: 12px;
background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);
border-radius: 8px;
border: 1px solid #e5e7eb;
}
.stat-number {
display: block;
font-size: 1.5rem;
font-weight: 700;
color: #1f2937;
margin-bottom: 4px;
}
.stat-label {
font-size: 0.75rem;
color: #6b7280;
text-transform: uppercase;
letter-spacing: 0.1em;
font-weight: 500;
}
.model-info {
background: white;
padding: 16px;
border-radius: 10px;
border: 1px solid #e5e7eb;
margin-top: 12px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
}
.model-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
}
.model-name {
font-weight: 600;
color: #1f2937;
font-size: 1rem;
}
.category-badge {
padding: 4px 12px;
border-radius: 16px;
font-size: 0.75rem;
font-weight: 600;
color: white;
text-transform: uppercase;
letter-spacing: 0.05em;
}
.model-description {
font-size: 0.9rem;
color: #6b7280;
margin-bottom: 12px;
line-height: 1.5;
}
.model-features {
display: flex;
gap: 6px;
flex-wrap: wrap;
}
.feature-badge {
padding: 3px 8px;
border-radius: 10px;
font-size: 0.7rem;
font-weight: 500;
background: #f3f4f6;
color: #374151;
}
.feature-badge.vision {
background: #fef3c7;
color: #92400e;
}
.preview-message {
text-align: center;
padding: 60px 24px;
color: #6b7280;
background: linear-gradient(135deg, #f9fafb 0%, #f3f4f6 100%);
border-radius: 12px;
border: 2px dashed #d1d5db;
font-size: 1.1rem;
font-weight: 500;
}
.error-message {
text-align: center;
padding: 24px;
color: #dc2626;
background: linear-gradient(135deg, #fef2f2 0%, #fee2e2 100%);
border: 1px solid #fecaca;
border-radius: 12px;
font-weight: 500;
}
.preview-controls {
background: #f8fafc;
padding: 12px 20px;
border-radius: 8px 8px 0 0;
border-bottom: 1px solid #e5e7eb;
}
.preview-info {
display: flex;
justify-content: center;
gap: 24px;
font-size: 0.85rem;
color: #6b7280;
}
.info-item {
display: flex;
align-items: center;
gap: 6px;
}
/* Responsive design */
@media (max-width: 768px) {
.header-content {
flex-direction: column;
gap: 20px;
text-align: center;
}
.stats-grid {
grid-template-columns: 1fr;
}
.main-content {
padding-left: 0;
margin-top: 24px;
}
.status-indicators {
flex-direction: row;
justify-content: center;
}
}
/* Animation improvements */
.control-group {
transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.control-group:hover {
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
"""
# =============================================================================
# MAIN APPLICATION
# =============================================================================
def create_professional_ui():
"""Create the professional AnyCoder UI"""
with gr.Blocks(
title="AnyCoder - Professional AI Development Suite",
theme=current_theme,
css=CUSTOM_CSS,
head="""
<meta name="description" content="AnyCoder - Professional AI Development Suite for creating modern applications">
<meta name="keywords" content="AI, code generation, web development, automation">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
"""
) as demo:
# State management
history = gr.State([])
setting = gr.State({"system": HTML_SYSTEM_PROMPT})
current_model = gr.State(DEFAULT_MODEL)
session_state = gr.State({"session_id": str(uuid.uuid4())})
# Header
with gr.Row(elem_classes=["header-row"]):
with gr.Column(scale=3):
gr.HTML("""
<div class="app-header">
<div class="header-content">
<div class="logo-section">
<div class="logo">⚑</div>
<div class="app-title">
<h1>AnyCoder</h1>
<p>Professional AI Development Suite</p>
</div>
</div>
<div class="status-indicators">
<div class="status-item">
<span class="status-dot active"></span>
<span>AI Models Ready</span>
</div>
<div class="status-item">
<span class="status-dot active"></span>
<span>Media Generation Active</span>
</div>
</div>
</div>
</div>
""")
with gr.Column(scale=1, min_width=200):
with gr.Row():
login_button = gr.LoginButton(scale=1, size="sm")
# Main interface
with gr.Row():
# Left sidebar - Controls
with gr.Column(scale=1, elem_classes=["sidebar"]):
# Model Selection
with gr.Group(elem_classes=["control-group"]):
gr.HTML('<div class="group-title">πŸ€– AI Model</div>')
model_dropdown = gr.Dropdown(
choices=[f"{model['name']} ({model['category']})" for model in AVAILABLE_MODELS],
value=f"{DEFAULT_MODEL['name']} ({DEFAULT_MODEL['category']})",
label="Select Model",
container=False
)
model_info = gr.HTML(get_model_info_html(DEFAULT_MODEL))
# Project Configuration
with gr.Group(elem_classes=["control-group"]):
gr.HTML('<div class="group-title">βš™οΈ Project Settings</div>')
language_dropdown = gr.Dropdown(
choices=[
"html", "streamlit", "gradio", "python",
"transformers.js", "svelte", "javascript", "css"
],
value="html",
label="Project Type",
container=False
)
search_toggle = gr.Checkbox(
label="πŸ” Enable Web Search",
value=False,
info="Include web search for current information",
container=False
)
# Media Generation Controls
with gr.Group(elem_classes=["control-group"]):
gr.HTML('<div class="group-title">🎨 Media Generation</div>')
enable_images = gr.Checkbox(
label="Generate Images (text β†’ image)",
value=False,
container=False
)
enable_image_to_image = gr.Checkbox(
label="Transform Images (image β†’ image)",
value=False,
container=False
)
enable_video = gr.Checkbox(
label="Generate Videos (text β†’ video)",
value=False,
container=False
)
enable_music = gr.Checkbox(
label="Generate Music (text β†’ music)",
value=False,
container=False
)
# Input Sources
with gr.Group(elem_classes=["control-group"]):
gr.HTML('<div class="group-title">πŸ“ Input Sources</div>')
file_input = gr.File(
label="Reference File",
file_types=[".pdf", ".txt", ".md", ".csv", ".docx", ".jpg", ".jpeg", ".png"],
container=False
)
website_url_input = gr.Textbox(
label="Website URL (for redesign)",
placeholder="https://example.com",
container=False
)
image_input = gr.Image(
label="Design Reference Image",
visible=DEFAULT_MODEL.get('supports_vision', False),
container=False
)
# Quick Start Examples
with gr.Group(elem_classes=["control-group"]):
gr.HTML('<div class="group-title">πŸš€ Quick Start</div>')
quick_start_buttons = []
for i, demo in enumerate(DEMO_LIST):
btn = gr.Button(
demo["title"],
variant="secondary",
size="sm",
elem_classes=["quick-start-btn"]
)
quick_start_buttons.append(btn)
# Main content area
with gr.Column(scale=3, elem_classes=["main-content"]):
# Input area
with gr.Group(elem_classes=["input-group"]):
input_textbox = gr.Textbox(
label="What would you like to build?",
placeholder="Describe your application in detail... (e.g., 'Create a modern dashboard with charts and user management')",
lines=4,
container=False,
elem_classes=["main-input"]
)
with gr.Row():
generate_btn = gr.Button(
"πŸš€ Generate Application",
variant="primary",
scale=3,
size="lg",
elem_classes=["generate-btn"]
)
clear_btn = gr.Button(
"πŸ—‘οΈ Clear",
variant="secondary",
scale=1,
size="lg"
)
# Output area with professional tabs
with gr.Tabs(elem_classes=["output-tabs"]):
# Preview Tab
with gr.Tab("πŸ–₯️ Live Preview"):
preview_controls = gr.HTML("""
<div class="preview-controls">
<div class="preview-info">
<span class="info-item">πŸ“± Responsive Design</span>
<span class="info-item">⚑ Real-time Updates</span>
<span class="info-item">πŸ”’ Sandboxed Environment</span>
</div>
</div>
""")
sandbox = gr.HTML(
"<div class='preview-message'>Ready to generate your application</div>",
elem_classes=["preview-container"]
)
# Code Tab
with gr.Tab("πŸ’» Source Code"):
with gr.Row():
with gr.Column(scale=4):
code_output = gr.Code(
language="html",
lines=25,
interactive=True,
label="Generated Code",
elem_classes=["code-editor"]
)
with gr.Column(scale=1, elem_classes=["code-sidebar"]):
# Code stats
with gr.Group():
gr.HTML('<div class="group-title">πŸ“Š Code Statistics</div>')
code_stats = gr.HTML(
"<div class='code-stats'>Ready to generate...</div>",
elem_classes=["code-stats"]
)
# Hidden components for functionality
history_output = gr.Chatbot(show_label=False, height=400, type="messages", visible=False)
# =============================================================================
# EVENT HANDLERS
# =============================================================================
# Model selection handler
model_dropdown.change(
handle_model_change,
inputs=[model_dropdown],
outputs=[current_model, model_info]
)
# Update image input visibility based on model
current_model.change(
update_image_input_visibility,
inputs=[current_model],
outputs=[image_input]
)
# Generation handler
generate_btn.click(
generation_code,
inputs=[
input_textbox,
image_input,
gr.State(None), # generation_image_input placeholder
file_input,
website_url_input,
setting,
history,
current_model,
search_toggle,
language_dropdown,
gr.State("auto"), # provider_state
enable_images,
enable_image_to_image,
gr.State(""), # image_to_image_prompt placeholder
gr.State(""), # text_to_image_prompt placeholder
gr.State(False), # enable_image_to_video placeholder
gr.State(""), # image_to_video_prompt placeholder
enable_video,
gr.State(""), # text_to_video_prompt placeholder
enable_music,
gr.State("") # text_to_music_prompt placeholder
],
outputs=[code_output, history, sandbox, history_output]
).then(
generate_code_stats,
inputs=[code_output, language_dropdown],
outputs=[code_stats]
)
# Enter key in input triggers generation
input_textbox.submit(
generation_code,
inputs=[
input_textbox,
image_input,
gr.State(None),
file_input,
website_url_input,
setting,
history,
current_model,
search_toggle,
language_dropdown,
gr.State("auto"),
enable_images,
enable_image_to_image,
gr.State(""),
gr.State(""),
gr.State(False),
gr.State(""),
enable_video,
gr.State(""),
enable_music,
gr.State("")
],
outputs=[code_output, history, sandbox, history_output]
).then(
generate_code_stats,
inputs=[code_output, language_dropdown],
outputs=[code_stats]
)
# Clear handler
clear_btn.click(
clear_history,
outputs=[history, history_output, file_input, website_url_input, input_textbox]
).then(
lambda: [
gr.update(value="", language="html"),
"<div class='preview-message'>Ready to generate your next application</div>",
"<div class='code-stats'>Ready to generate...</div>"
],
outputs=[code_output, sandbox, code_stats]
)
# Quick start button handlers
for i, btn in enumerate(quick_start_buttons):
if i < len(DEMO_LIST):
btn.click(
lambda demo_idx=i: DEMO_LIST[demo_idx]['description'],
outputs=[input_textbox]
)
# Language change updates code editor
language_dropdown.change(
lambda lang: gr.update(language=get_gradio_language(lang)),
inputs=[language_dropdown],
outputs=[code_output]
)
return demo
# =============================================================================
# MAIN ENTRY POINT
# =============================================================================
def main():
"""Main application entry point"""
print("πŸš€ Starting AnyCoder Professional AI Development Suite...")
# Setup cleanup
cleanup_all_temp_media()
reap_old_media()
# Create and launch the UI
demo = create_professional_ui()
demo.queue(api_open=False, default_concurrency_limit=20).launch(
show_api=False,
share=False,
server_name="0.0.0.0",
server_port=7860,
show_error=True
)
if __name__ == "__main__":
main()