| |
| |
| |
| |
| |
| |
|
|
| import gradio as gr |
| from huggingface_hub import InferenceClient |
| import json |
| import zipfile |
| import io |
| import re |
|
|
| |
| |
| 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 |
| ) |
| |
| |
| 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נסה תיאור יותר מפורט או לחץ שוב." |
|
|
| |
| 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", "") |
| |
| |
| 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) |
| |
| |
| 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}' |
| |
| |
| 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 |
|
|
| |
| 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() |
|
|
| |
| 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)}" |
|
|
| |
| 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 |
|
|
| |
| 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) |
| |
| |
| generate_button.click( |
| fn=lambda desc, color, style, pages, state: ( |
| *generate_website(desc, color, style, pages), |
| |
| ), |
| 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] |
| ) |
| |
| |
| 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 כדי לשמור.") |
|
|
| |
| if __name__ == "__main__": |
| iface.launch( |
| server_name="0.0.0.0", |
| server_port=7860, |
| share=False, |
| debug=True |
| ) |