AA / app.py
nhkr's picture
Create app.py
74cee4d verified
# פרויקט Python מלא: מחולל אתרי אינטרנט AI אוטומטי להרצה ב-Hugging Face Spaces (Gradio)
# גרסה: 1.0
# תואם בדיוק לדרישות: מודל AI, Gradio, עורך קוד, תצוגה מקדימה, ניהול פרויקטים, מספר דפים, יצוא ZIP
# הסבר: הקוד משתמש ב-InferenceClient (huggingface_hub) למודל Qwen2.5-Coder-1.5B-Instruct – קל ומהיר ב-Spaces ללא צורך ב-GPU כבד.
# כל הקבצים נשמרים בזיכרון (State), תומך בעריכה, תצוגה מקדימה עם inline CSS/JS, ושמירה/טעינה כ-JSON + ZIP.
# הערות מפורטות בכל חלק – כפי שביקשת.
import gradio as gr
from huggingface_hub import InferenceClient
import json
import zipfile
import io
import re
# ====================== 1. מודל AI חזק (Code Generation) ======================
# מודל Hugging Face מתאים במיוחד ליצירת HTML/CSS/JS איכותי ומוכן להרצה
client = InferenceClient(model="Qwen/Qwen2.5-Coder-1.5B-Instruct")
def generate_website(description: str, primary_color: str, design_style: str, page_names_str: str):
"""
יוצר את כל הקבצים באמצעות מודל AI.
הפרומפט באנגלית (מודלים לקוד טובים יותר באנגלית) אבל התוצאה מותאמת לעברית/עיצוב.
"""
page_names = [p.strip() for p in page_names_str.split(",") if p.strip()]
if not page_names:
page_names = ["index"]
prompt = f"""You are an expert full-stack web developer. Create a complete, beautiful, responsive multi-page website.
Description: {description}
Primary color: {primary_color}
Design style: {design_style} (use Tailwind CSS via CDN + custom CSS)
Pages: {', '.join(page_names)}
Requirements:
- Use HTML5, Tailwind CSS CDN, modern design.
- Each HTML file must contain: <link rel="stylesheet" href="style.css"> and <script src="script.js"></script>
- Add nice navigation between pages.
- Include some JavaScript interactivity (buttons, smooth scroll, mobile menu).
- Make it production-ready and beautiful.
Return ONLY a valid JSON object (no extra text):
{{
"files": {{
"style.css": "FULL CSS CODE HERE",
"script.js": "FULL JS CODE HERE",
"index.html": "FULL HTML CODE HERE",
"about.html": "FULL HTML CODE HERE",
... (one file for each page)
}}
}}
"""
try:
response = client.text_generation(
prompt=prompt,
max_new_tokens=8192,
temperature=0.6,
top_p=0.9,
do_sample=True
)
# ניקוי אם המודל הוסיף markdown
response = response.strip()
if response.startswith("```json"):
response = response[7:]
if response.endswith("```"):
response = response[:-3]
data = json.loads(response)
files = data.get("files", {})
# בדיקת בטיחות – תמיד קיימים קבצים בסיסיים
if "style.css" not in files:
files["style.css"] = f"body {{ color: {primary_color}; font-family: system-ui; }}"
if "script.js" not in files:
files["script.js"] = "// JS interactivity\nconsole.log('Site loaded!');"
return files, "✅ האתר נוצר בהצלחה! ערוך, צפה והורד."
except Exception as e:
return {}, f"❌ שגיאה: {str(e)}\nנסה תיאור יותר מפורט או לחץ שוב."
# ====================== 2. הכנת תצוגה מקדימה (iframe-like עם inline) ======================
def prepare_preview_html(files: dict, page_name: str):
"""הופך את הדף ל-single HTML עם CSS + JS inline כדי שיעבוד בתוך Gradio."""
if not files or page_name not in files:
return "<h1 style='text-align:center;padding:50px;'>בחר דף להצגה</h1>"
html = files[page_name]
css = files.get("style.css", "")
js = files.get("script.js", "")
# הסרת קישורים חיצוניים (כדי שהכל יהיה inline)
html = re.sub(r'<link[^>]*href=["\']style\.css["\'][^>]*>', '', html, flags=re.IGNORECASE)
html = re.sub(r'<script[^>]*src=["\']script\.js["\'][^>]*></script>', '', html, flags=re.IGNORECASE)
# הוספת inline CSS
if re.search(r'</head>', html, re.IGNORECASE):
html = re.sub(r'(?i)</head>', f'<style>\n{css}\n</style></head>', html)
else:
html = f'<head><style>\n{css}\n</style></head>\n{html}'
# הוספת inline JS
if re.search(r'</body>', html, re.IGNORECASE):
html = re.sub(r'(?i)</body>', f'<script>\n{js}\n</script></body>', html)
else:
html += f'\n<script>\n{js}\n</script>'
return html
# ====================== 3. יצוא ZIP ======================
def create_zip_download(files: dict):
"""יוצר ZIP בזיכרון עם כל הקבצים הנפרדים."""
if not files:
return None
buffer = io.BytesIO()
with zipfile.ZipFile(buffer, "w", zipfile.ZIP_DEFLATED) as zf:
for filename, content in files.items():
zf.writestr(filename, content.encode("utf-8"))
buffer.seek(0)
return buffer.getvalue()
# ====================== 4. שמירה וטעינה של פרויקט (JSON) ======================
def save_project_json(files: dict):
if not files:
return None
return json.dumps(files, ensure_ascii=False, indent=2).encode("utf-8")
def load_project(uploaded_file):
"""טוען פרויקט JSON שהמשתמש העלה."""
if uploaded_file is None:
return {}, "לא נבחר קובץ"
try:
content = uploaded_file.decode("utf-8")
files = json.loads(content)
return files, "✅ פרויקט נטען בהצלחה!"
except Exception as e:
return {}, f"❌ שגיאה בטעינה: {str(e)}"
# ====================== 5. פונקציות עזר לעורך ======================
def update_editor(selected_file, current_files):
"""מעדכן את עורך הקוד לפי הקובץ שנבחר."""
if not current_files or selected_file not in current_files:
return "// אין קובץ", "javascript"
ext = selected_file.split(".")[-1].lower()
lang = "html" if ext == "html" else "css" if ext == "css" else "javascript"
return current_files[selected_file], lang
def save_edited_code(selected_file, new_code, current_files):
"""שומר שינויים מהעורך."""
if selected_file and current_files:
current_files[selected_file] = new_code
return current_files, "✅ שינויים נשמרו!", create_zip_download(current_files), save_project_json(current_files)
return current_files, "❌ לא נשמר", None, None
# ====================== 6. ממשק Gradio מלא ======================
with gr.Blocks(title="מחולל אתרי AI – Gradio", theme=gr.themes.Soft()) as iface:
gr.Markdown("# 🛠️ מחולל אתרי אינטרנט אוטומטי עם AI\n"
"תאר → בחר צבע וסגנון → יצור → ערוך → צפה → הורד ZIP\n"
"תומך ב-Landing Page, Blog, Portfolio, Dashboard ועוד!")
files_state = gr.State({}) # שומר את כל הקבצים בזיכרון
with gr.Tab("✨ יצירת אתר חדש"):
with gr.Row():
with gr.Column(scale=2):
description_input = gr.Textbox(
label="📝 תיאור האתר",
placeholder="אתר Landing Page לחנות בגדים אופנתית עם דף מוצרים, בלוג וטופס יצירת קשר...",
lines=4
)
primary_color_input = gr.ColorPicker(label="🎨 צבע עיקרי", value="#2563eb")
design_style_input = gr.Dropdown(
choices=["מודרני", "מינימליסטי", "כהה", "תוסס", "תאגידי", "פורטפוליו"],
label="🎨 סגנון עיצוב",
value="מודרני"
)
page_names_input = gr.Textbox(
label="📄 שמות הדפים (מופרדים בפסיקים)",
value="index,about,services,contact",
placeholder="index,blog,portfolio"
)
generate_button = gr.Button("🚀 יצור אתר!", variant="primary", size="large")
with gr.Column(scale=1):
status_output = gr.Textbox(label="סטטוס", interactive=False, value="מוכן ליצירה...")
gr.Markdown("### 👀 תצוגה מקדימה + עורך קוד")
with gr.Row():
with gr.Column(scale=1):
preview_page_dropdown = gr.Dropdown(label="בחר דף להצגה", choices=[], interactive=True)
live_preview = gr.HTML(
value="<div style='text-align:center;padding:80px;background:#f8fafc;border-radius:12px;'>התצוגה המקדימה תופיע כאן</div>",
label="תצוגה מקדימה (מתעדכנת אוטומטית)"
)
refresh_preview_btn = gr.Button("🔄 רענן תצוגה")
with gr.Column(scale=1):
file_dropdown = gr.Dropdown(label="בחר קובץ לעריכה", choices=[], interactive=True)
code_editor = gr.Code(
label="עורך קוד (HTML / CSS / JS)",
language="html",
lines=22,
interactive=True
)
save_edit_button = gr.Button("💾 שמור שינויים בקוד")
with gr.Tab("📁 ניהול פרויקטים & יצוא"):
gr.Markdown("### שמור / טען פרויקטים")
with gr.Row():
upload_project = gr.File(label="העלה פרויקט JSON קודם", file_types=[".json"], type="binary")
load_status = gr.Textbox(label="סטטוס טעינה", interactive=False)
with gr.Row():
save_json_button = gr.DownloadButton(label="💾 שמור פרויקט כ-JSON", value=None, visible=False)
download_zip_button = gr.DownloadButton(label="📦 הורד אתר מלא כ-ZIP", value=None, visible=False)
# ====================== אירועים (Events) ======================
generate_button.click(
fn=lambda desc, color, style, pages, state: (
*generate_website(desc, color, style, pages),
# outputs: files, status, file_dropdown, preview_page_dropdown, live_preview, zip_bytes, json_bytes
),
inputs=[description_input, primary_color_input, design_style_input, page_names_input, files_state],
outputs=[
files_state,
status_output,
file_dropdown,
preview_page_dropdown,
live_preview,
download_zip_button,
save_json_button
]
)
# עדכון עורך קוד
file_dropdown.change(
fn=update_editor,
inputs=[file_dropdown, files_state],
outputs=[code_editor]
)
# שמירת עריכה + עדכון כפתורי הורדה
save_edit_button.click(
fn=save_edited_code,
inputs=[file_dropdown, code_editor, files_state],
outputs=[files_state, status_output, download_zip_button, save_json_button]
)
# רענון תצוגה מקדימה
def refresh_preview(page, state):
return prepare_preview_html(state, page)
preview_page_dropdown.change(
fn=refresh_preview,
inputs=[preview_page_dropdown, files_state],
outputs=[live_preview]
)
refresh_preview_btn.click(
fn=refresh_preview,
inputs=[preview_page_dropdown, files_state],
outputs=[live_preview]
)
# טעינת פרויקט JSON
def handle_upload(uploaded, state):
new_files, msg = load_project(uploaded)
if new_files:
html_pages = [f for f in new_files if f.endswith(".html")]
all_files = list(new_files.keys())
return (
new_files,
msg,
gr.Dropdown(choices=all_files, value=all_files[0] if all_files else None),
gr.Dropdown(choices=html_pages, value=html_pages[0] if html_pages else None),
prepare_preview_html(new_files, html_pages[0] if html_pages else ""),
create_zip_download(new_files),
save_project_json(new_files)
)
return state, msg, gr.Dropdown(), gr.Dropdown(), live_preview.value, None, None
upload_project.upload(
fn=handle_upload,
inputs=[upload_project, files_state],
outputs=[
files_state,
load_status,
file_dropdown,
preview_page_dropdown,
live_preview,
download_zip_button,
save_json_button
]
)
gr.Markdown("---\n"
"**טיפים:**\n"
"• התצוגה המקדימה מראה דף אחד בכל פעם (קישורים לא עובדים כי אין שרת). הורד ZIP כדי לראות את כל האתר.\n"
"• אחרי עריכה – לחץ \"שמור שינויים\" ואז \"רענן תצוגה\".\n"
"• כל הפרויקטים נשמרים רק בדפדפן – הורד JSON כדי לשמור.")
# ====================== 7. הרצה ======================
if __name__ == "__main__":
iface.launch(
server_name="0.0.0.0",
server_port=7860,
share=False, # ב-Spaces זה אוטומטי
debug=True
)