yeye / app.py
mgbam's picture
Update app.py
185e6e8 verified
Raw History Blame Contribute Delete
28.8 kB
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()