import os import uuid import shutil import traceback import json import gradio as gr from fastapi import FastAPI, File, UploadFile from fastapi.responses import JSONResponse, FileResponse from fastapi.middleware.cors import CORSMiddleware from visualization import process_wireframe from CodingModule import HTMLGenerator # ----------------------------------------------------------------------------- # FASTAPI BACKEND # ----------------------------------------------------------------------------- api = FastAPI() api.add_middleware( CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) TEMP_DIR = "./temp" OUTPUT_DIR = "./output" os.makedirs(TEMP_DIR, exist_ok=True) os.makedirs(OUTPUT_DIR, exist_ok=True) @api.get("/") def health_check(): return {"status": "ok"} @api.post("/process-wireframe") async def process_wireframe_api(image: UploadFile = File(...)): """ Firebase / Programmatic endpoint Returns JSON + HTML content directly. """ file_id = str(uuid.uuid4()) temp_path = os.path.join(TEMP_DIR, f"{file_id}_{image.filename}") try: with open(temp_path, "wb") as f: shutil.copyfileobj(image.file, f) results = process_wireframe( image_path=temp_path, save_json=True, save_html=False, show_visualization=False ) if not results or not results.get('normalized_elements'): return JSONResponse( status_code=400, content={"error": "No elements detected"} ) json_path = results.get("json_path") # Load JSON content json_content = None if json_path and os.path.exists(json_path): with open(json_path, "r") as f: json_content = json.load(f) # Generate styled HTML html_path = None try: html_generator = HTMLGenerator(json_path) html_filename = f"{file_id}_styled.html" html_path = os.path.join(OUTPUT_DIR, html_filename) html_generator.generate_html(html_path) except: html_path = results.get("html_path") html_content = None if html_path and os.path.exists(html_path): with open(html_path, "r", encoding="utf-8") as f: html_content = f.read() elements = ( results.get("normalized_elements") or json_content.get("normalized_elements") or json_content.get("elements") or [] ) return { "success": True, "json_data": json_content, "html_content": html_content, "total_elements": len(elements), "normalized_elements": elements } except Exception as e: traceback.print_exc() return JSONResponse(status_code=500, content={"error": str(e)}) finally: if os.path.exists(temp_path): os.remove(temp_path) @api.get("/view-html/{filename}") async def serve_output_file(filename: str): """ Serve stored HTML/JSON files. """ file_path = os.path.join(OUTPUT_DIR, filename) if not os.path.exists(file_path): return JSONResponse(status_code=404, content={"error": "File not found"}) if filename.endswith(".html"): return FileResponse(file_path, media_type="text/html") elif filename.endswith(".json"): return FileResponse(file_path, media_type="application/json") return FileResponse(file_path) # ----------------------------------------------------------------------------- # GRADIO UI LOGIC # ----------------------------------------------------------------------------- def gradio_process(image): if image is None: return "Please upload an image", None, None file_id = str(uuid.uuid4()) temp_path = os.path.join(TEMP_DIR, f"{file_id}.png") try: image.save(temp_path) except Exception as e: return f"Error saving: {str(e)}", None, None try: results = process_wireframe( image_path=temp_path, save_json=True, save_html=False, show_visualization=False ) if not results.get("normalized_elements"): return "No elements detected", None, None json_path = results["json_path"] # Generate styled HTML try: html_generator = HTMLGenerator(json_path) html_filename = f"{file_id}_styled.html" html_path = os.path.join(OUTPUT_DIR, html_filename) html_generator.generate_html(html_path) except: traceback.print_exc() html_path = results.get("html_path") num = len(results["normalized_elements"]) status = f"Detected {num} elements\n" status += f"JSON: {os.path.basename(json_path)}\n" status += f"HTML: {os.path.basename(html_path)}" return status, json_path, html_path except Exception as e: traceback.print_exc() return f"Error: {str(e)}", None, None finally: if os.path.exists(temp_path): os.remove(temp_path) # ----------------------------------------------------------------------------- # GRADIO UI (updated for Gradio 6 compliance) # ----------------------------------------------------------------------------- with gr.Blocks(title="Wireframe Layout Normalizer") as demo: gr.Markdown("# Wireframe Layout Normalizer") gr.Markdown("Upload a wireframe image to extract and normalize UI layout elements.") with gr.Row(): with gr.Column(): image_input = gr.Image(type="pil", label="Upload Wireframe Image") process_btn = gr.Button("Process Wireframe") with gr.Column(): status_output = gr.Textbox(label="Status") json_output = gr.File(label="JSON Output") html_output = gr.File(label="HTML Preview") process_btn.click( fn=gradio_process, inputs=image_input, outputs=[status_output, json_output, html_output], ) # ----------------------------------------------------------------------------- # FINAL APP MOUNT (fixes reload + /new spam) # ----------------------------------------------------------------------------- app = gr.mount_gradio_app( api, demo, path="/gradio", theme=gr.themes.Soft() ) if __name__ == "__main__": import uvicorn uvicorn.run(app, host="0.0.0.0", port=7860)