import gradio as gr import asyncio from playwright.async_api import async_playwright from bs4 import BeautifulSoup import re import zipfile import io # ========================================== # 🔧 Backend Logic # ========================================== async def scan_page_for_components(url): components = [] async with async_playwright() as p: browser = await p.chromium.launch(headless=True) context = await browser.new_context( viewport={'width': 1920, 'height': 1080}, user_agent='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36' ) page = await context.new_page() try: await page.goto(url, wait_until="networkidle", timeout=30000) await page.evaluate("window.scrollTo(0, document.body.scrollHeight)") await page.wait_for_timeout(800) await page.evaluate("window.scrollTo(0, 0)") html_content = await page.content() soup = BeautifulSoup(html_content, 'html.parser') # 1. Buttons for btn in soup.find_all(['button', 'a'], class_=re.compile(r'btn|button', re.I)): txt = btn.get_text(strip=True) if txt and len(txt) < 40: components.append({'type': '🔘 Button', 'html': str(btn), 'classes': btn.get('class', []), 'preview': txt}) # 2. Cards for card in soup.find_all(class_=re.compile(r'card|product|item|post|entry', re.I)): if card.find('img') or card.find(['h1','h2','h3','h4']): if not any(c.get('class') and 'card' in str(c['class']).lower() for c in card.parents): components.append({'type': '🃏 Card', 'html': str(card), 'classes': card.get('class', []), 'preview': 'Card'}) # 3. Navbars for nav in soup.find_all(['nav', 'header'], class_=re.compile(r'nav|header|menu', re.I)): components.append({'type': '🧭 Navbar', 'html': str(nav), 'classes': nav.get('class', []), 'preview': 'Navigation'}) # 4. Hero Sections for sec in soup.find_all('section'): if sec.get('class') and any('hero' in str(c).lower() for c in sec.get('class')): components.append({'type': '🎨 Hero', 'html': str(sec), 'classes': sec.get('class', []), 'preview': 'Hero Section'}) except Exception as e: components.append({'type': '⚠️ Error', 'html': f'

{str(e)}

', 'classes': [], 'preview': 'Failed'}) finally: await browser.close() return components def generate_react_code(html_str, classes): clean = html_str.replace('class=', 'className=').replace('for=', 'htmlFor=') classes_comment = ' '.join(classes) return ( f"import React from 'react';\n\n" f"export default function ExtractedComponent() {{\n" f" return (\n" f" <>\n" f" {{/* Detected classes: {classes_comment} */}}\n" f" {clean}\n" f" \n" f" );\n" f"}}" ) def create_zip_file(components): zip_buffer = io.BytesIO() with zipfile.ZipFile(zip_buffer, "w", zipfile.ZIP_DEFLATED) as zip_file: for i, comp in enumerate(components): zip_file.writestr(f"component_{i+1}.html", comp['html']) zip_file.writestr(f"component_{i+1}.jsx", generate_react_code(comp['html'], comp['classes'])) zip_buffer.seek(0) return zip_buffer # ========================================== # 🎨 Gradio UI # ========================================== with gr.Blocks(title="UI Extractor Pro") as demo: gr.Markdown("# 🛠️ UI Component Extractor") gr.Markdown("أدخل رابط موقع لاستخراج المكونات، معاينتها، وتحميل كود React/HTML جاهز.") with gr.Row(): url_input = gr.Textbox(label="🔗 Website URL", placeholder="https://example.com", scale=3) extract_btn = gr.Button("🔍 Extract Components", variant="primary", scale=1) status_box = gr.Textbox(label="Status", interactive=False, value="Ready") components_state = gr.State([]) with gr.Row(): with gr.Column(scale=1): gr.Markdown("### 📦 Detected Components") preview_selector = gr.Radio(label="Click to Preview", choices=["No components yet"], interactive=True) download_selector = gr.CheckboxGroup(label="Select for ZIP Download", choices=["No components yet"], interactive=True) download_btn = gr.Button("📥 Download Selected as ZIP", variant="secondary") output_file = gr.File(label="Download", interactive=False) with gr.Column(scale=2): gr.Markdown("### 👁️ Live Preview & Code") with gr.Tabs(): with gr.Tab("Live Preview"): preview_html = gr.HTML(label="Component Preview", elem_id="preview-box") with gr.Tab("React Code"): code_display = gr.Code(label="React Component", language="javascript", lines=20) # ========================================== # 🔄 Event Handlers (Fixed: No yield, only return) # ========================================== async def on_extract(url): if not url.startswith('http'): return "❌ الرجاء إدخال رابط صحيح", [], gr.Radio(choices=["Invalid URL"]), gr.CheckboxGroup(choices=["Invalid URL"]), "", "", None try: results = await scan_page_for_components(url) if not results: return "⚠️ لم يتم العثور على مكونات.", [], gr.Radio(choices=["No results"]), gr.CheckboxGroup(choices=["No results"]), "", "", None choices = [f"{c['type']} - {c['preview']}" for c in results] first = results[0] safe_preview = f"""
{first['html']}
""" return ( f"✅ تم العثور على {len(results)} مكونات.", results, gr.Radio(choices=choices, value=choices[0]), gr.CheckboxGroup(choices=choices), safe_preview, generate_react_code(first['html'], first['classes']), None ) except Exception as e: return f"❌ خطأ: {str(e)}", [], gr.Radio(choices=["Error"]), gr.CheckboxGroup(choices=["Error"]), "", "", None async def on_preview_select(selected_name, components): if not selected_name or not components: return "", "" comp = next((c for c in components if f"{c['type']} - {c['preview']}" == selected_name), None) if not comp: return "", "" safe_preview = f"""
{comp['html']}
""" return safe_preview, generate_react_code(comp['html'], comp['classes']) async def on_download(selected_names, components): if not selected_names or not components: return None selected_comps = [c for c in components if f"{c['type']} - {c['preview']}" in selected_names] if not selected_comps: return None return create_zip_file(selected_comps) # ربط الأحداث extract_btn.click( fn=on_extract, inputs=url_input, outputs=[status_box, components_state, preview_selector, download_selector, preview_html, code_display, output_file] ) preview_selector.change( fn=on_preview_select, inputs=[preview_selector, components_state], outputs=[preview_html, code_display] ) download_btn.click( fn=on_download, inputs=[download_selector, components_state], outputs=output_file ) if __name__ == "__main__": demo.launch(server_name="0.0.0.0", server_port=7860, theme=gr.themes.Soft())