Spaces:
Sleeping
Sleeping
| """ | |
| Hugging Face Spaces Entry Point | |
| Improved UI with better logging, status indicators, and results display. | |
| """ | |
| import gradio as gr | |
| import os | |
| import sys | |
| from datetime import datetime | |
| from workflow import create_workflow, run_workflow_step_1, resume_workflow | |
| # Add current directory to path | |
| sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) | |
| # Log file path | |
| LOG_FILE = "app.log" | |
| def clear_logs(): | |
| """Clear the log file for a fresh start.""" | |
| with open(LOG_FILE, "w") as f: | |
| f.write(f"===== New Session Started at {datetime.now()} =====\n") | |
| def start_test(figma_key, figma_id, url, hf_token): | |
| """Start the test and stop at the breakpoint.""" | |
| # Clear logs at start of new test | |
| clear_logs() | |
| print(f"\n{'='*60}") | |
| print(f"๐ STARTING NEW TEST") | |
| print(f"{'='*60}") | |
| print(f"โฐ Time: {datetime.now()}") | |
| print(f"๐ Website URL: {url}") | |
| print(f"๐ Figma File ID: {figma_id}") | |
| print(f"๐ Figma API Key: {'โ Provided' if figma_key else 'โ Missing'}") | |
| print(f"๐ค HF Token: {'โ Provided - AI Analysis will be enhanced' if hf_token else 'โ ๏ธ Not provided - Using basic comparison only'}") | |
| print(f"{'='*60}\n") | |
| execution_id = f"exec_{datetime.now().strftime('%Y%m%d_%H%M%S')}" | |
| thread_id = f"thread_{execution_id}" | |
| try: | |
| print(f"๐งต Thread ID: {thread_id}") | |
| print(f"\n๐ STEP 1: Capturing Screenshots...") | |
| print(f"-" * 40) | |
| state_snapshot = run_workflow_step_1(figma_id, figma_key, url, execution_id, thread_id, hf_token) | |
| if not state_snapshot or not state_snapshot.values: | |
| print("โ ERROR: Workflow returned empty state.") | |
| return "โ Error: Workflow failed to start or returned empty state.", None, get_current_logs() | |
| # Get captured screenshots info | |
| figma_screenshots = state_snapshot.values.get("figma_screenshots", {}) | |
| website_screenshots = state_snapshot.values.get("website_screenshots", {}) | |
| print(f"\n{'='*60}") | |
| print(f"โ STEP 1 COMPLETE!") | |
| print(f"{'='*60}") | |
| print(f"๐ธ Figma Screenshots: {len(figma_screenshots)}") | |
| for viewport, path in figma_screenshots.items(): | |
| print(f" โข {viewport}: {path}") | |
| print(f"๐ธ Website Screenshots: {len(website_screenshots)}") | |
| for viewport, path in website_screenshots.items(): | |
| print(f" โข {viewport}: {path}") | |
| print(f"\n๐ Click 'Step 2: Run AI Analysis' to compare images") | |
| print(f"{'='*60}\n") | |
| status = f"โ Step 1 Complete!\n" | |
| status += f"Thread ID: {thread_id}\n\n" | |
| status += f"๐ธ Screenshots Captured:\n" | |
| status += f" โข Figma: {len(figma_screenshots)} images\n" | |
| status += f" โข Website: {len(website_screenshots)} images\n\n" | |
| status += f"๐ค HF Token: {'Active' if hf_token else 'Not provided'}\n\n" | |
| status += f"๐ Click 'Step 2' button to run AI analysis" | |
| return status, thread_id, get_current_logs() | |
| except Exception as e: | |
| import traceback | |
| error_msg = f"โ Error: {str(e)}" | |
| print(f"\n{'='*60}") | |
| print(f"โ ERROR OCCURRED") | |
| print(f"{'='*60}") | |
| print(error_msg) | |
| traceback.print_exc() | |
| print(f"{'='*60}\n") | |
| return error_msg, None, get_current_logs() | |
| def continue_test(thread_id, hf_token): | |
| """Resume the test after human approval.""" | |
| if not thread_id: | |
| print("โ ERROR: No active thread found. Please run Step 1 first.") | |
| return "โ No active thread found. Please run Step 1 first.", get_current_logs() | |
| print(f"\n{'='*60}") | |
| print(f"๐ STEP 2: RUNNING AI ANALYSIS") | |
| print(f"{'='*60}") | |
| print(f"โฐ Time: {datetime.now()}") | |
| print(f"๐งต Thread ID: {thread_id}") | |
| print(f"๐ค HF Token: {'โ Using enhanced AI analysis' if hf_token else 'โ ๏ธ Using basic comparison'}") | |
| print(f"-" * 40) | |
| try: | |
| print("๐ Resuming workflow...") | |
| state_snapshot = resume_workflow(thread_id, user_approval=True) | |
| if not state_snapshot: | |
| print("โ ERROR: Workflow failed to resume.") | |
| return "โ Workflow failed to resume. Try running Step 1 again.", get_current_logs() | |
| # Extract results | |
| score = state_snapshot.values.get("similarity_score", 0) | |
| differences = state_snapshot.values.get("visual_differences", []) | |
| hf_analysis = state_snapshot.values.get("hf_analysis", {}) | |
| status = state_snapshot.values.get("status", "unknown") | |
| error_msg = state_snapshot.values.get("error_message", "") | |
| print(f"\n{'='*60}") | |
| print(f"๐ ANALYSIS RESULTS") | |
| print(f"{'='*60}") | |
| print(f"๐ Similarity Score: {score}/100") | |
| print(f"๐ Differences Found: {len(differences)}") | |
| print(f"๐ Status: {status}") | |
| if error_msg: | |
| print(f"โ ๏ธ Error: {error_msg}") | |
| # Show difference details | |
| if differences: | |
| print(f"\n๐ DIFFERENCE DETAILS:") | |
| print(f"-" * 40) | |
| for i, diff in enumerate(differences, 1): | |
| if isinstance(diff, dict): | |
| print(f"\n [{i}] {diff.get('title', 'Unknown')}") | |
| print(f" Category: {diff.get('category', 'N/A')}") | |
| print(f" Severity: {diff.get('severity', 'N/A')}") | |
| print(f" Viewport: {diff.get('viewport', 'N/A')}") | |
| if diff.get('description'): | |
| print(f" Description: {diff.get('description', '')[:100]}...") | |
| # Show HF Analysis results | |
| if hf_analysis: | |
| print(f"\n๐ค HF VISION ANALYSIS:") | |
| print(f"-" * 40) | |
| for viewport, analysis in hf_analysis.items(): | |
| print(f" {viewport.upper()}:") | |
| if isinstance(analysis, dict): | |
| for key, value in analysis.items(): | |
| print(f" โข {key}: {str(value)[:100]}") | |
| print(f"\n{'='*60}") | |
| print(f"โ ANALYSIS COMPLETE!") | |
| print(f"{'='*60}\n") | |
| # Build result string for UI | |
| result = f"{'='*50}\n" | |
| result += f"๐ ANALYSIS RESULTS\n" | |
| result += f"{'='*50}\n\n" | |
| result += f"๐ Similarity Score: {score}/100\n" | |
| result += f"๐ Differences Found: {len(differences)}\n" | |
| result += f"๐ Status: {status}\n\n" | |
| if differences: | |
| result += f"๐ DIFFERENCES:\n" | |
| result += f"-" * 30 + "\n" | |
| for i, diff in enumerate(differences[:10], 1): # Show first 10 | |
| if isinstance(diff, dict): | |
| result += f"\n[{i}] {diff.get('title', 'Unknown')}\n" | |
| result += f" Severity: {diff.get('severity', 'N/A')}\n" | |
| result += f" Category: {diff.get('category', 'N/A')}\n" | |
| if len(differences) > 10: | |
| result += f"\n... and {len(differences) - 10} more differences\n" | |
| if hf_analysis: | |
| result += f"\n๐ค AI INSIGHTS:\n" | |
| result += f"-" * 30 + "\n" | |
| for viewport, analysis in hf_analysis.items(): | |
| result += f"\n{viewport.upper()}:\n" | |
| if isinstance(analysis, dict): | |
| for key, value in list(analysis.items())[:3]: | |
| result += f" โข {key}: {str(value)[:80]}\n" | |
| return result, get_current_logs() | |
| except Exception as e: | |
| import traceback | |
| error_msg = f"โ Analysis Error: {str(e)}" | |
| print(f"\n{'='*60}") | |
| print(f"โ ERROR IN ANALYSIS") | |
| print(f"{'='*60}") | |
| print(error_msg) | |
| traceback.print_exc() | |
| print(f"{'='*60}\n") | |
| return error_msg, get_current_logs() | |
| def get_current_logs(): | |
| """Get current log contents.""" | |
| try: | |
| with open(LOG_FILE, "r") as f: | |
| return f.read() | |
| except: | |
| return "No logs available yet." | |
| # Gradio UI | |
| def create_interface(): | |
| with gr.Blocks(title="Advanced UI Regression Testing", theme=gr.themes.Soft()) as demo: | |
| gr.Markdown("# ๐ Advanced UI Regression Testing (LangGraph Powered)") | |
| gr.Markdown("Compare Figma designs with live websites to detect visual differences.") | |
| with gr.Row(): | |
| # LEFT COLUMN - Inputs | |
| with gr.Column(scale=1): | |
| gr.Markdown("### ๐ Configuration") | |
| f_key = gr.Textbox(label="Figma API Key", type="password", placeholder="Enter your Figma API key") | |
| f_id = gr.Textbox(label="Figma File ID", placeholder="e.g., ENieX2p3Gy3TAtxaB36cZA") | |
| w_url = gr.Textbox(label="Website URL", placeholder="https://your-website.com") | |
| hf_token = gr.Textbox(label="Hugging Face API Token (Optional)", type="password", | |
| placeholder="For enhanced AI analysis") | |
| gr.Markdown("### ๐ฎ Controls") | |
| start_btn = gr.Button("1๏ธโฃ Start Capture & Setup", variant="primary", size="lg") | |
| resume_btn = gr.Button("2๏ธโฃ Run AI Analysis", variant="secondary", size="lg") | |
| # Hidden state | |
| thread_id_state = gr.State() | |
| # RIGHT COLUMN - Results | |
| with gr.Column(scale=2): | |
| gr.Markdown("### ๐ Status & Results") | |
| status_out = gr.Textbox(label="Current Status", lines=8, interactive=False) | |
| gr.Markdown("### ๐ฏ Final Analysis Results") | |
| final_out = gr.Textbox(label="Analysis Results", lines=12, interactive=False) | |
| # BOTTOM - Logs | |
| gr.Markdown("### ๐ Execution Logs") | |
| gr.Markdown("*Logs refresh automatically every 2 seconds*") | |
| log_viewer = gr.Code(label="Live Logs", language="python", interactive=False, lines=20) | |
| refresh_logs = gr.Timer(value=2) | |
| # Clear button for logs | |
| clear_btn = gr.Button("๐๏ธ Clear Logs", variant="secondary", size="sm") | |
| # Event handlers | |
| start_btn.click( | |
| start_test, | |
| inputs=[f_key, f_id, w_url, hf_token], | |
| outputs=[status_out, thread_id_state, log_viewer] | |
| ) | |
| resume_btn.click( | |
| continue_test, | |
| inputs=[thread_id_state, hf_token], | |
| outputs=[final_out, log_viewer] | |
| ) | |
| clear_btn.click( | |
| lambda: (clear_logs(), "Logs cleared.", "", get_current_logs()), | |
| outputs=[status_out, final_out, log_viewer] | |
| ) | |
| refresh_logs.tick(get_current_logs, outputs=[log_viewer]) | |
| return demo | |
| if __name__ == "__main__": | |
| # Setup logging to file | |
| class Logger(object): | |
| def __init__(self): | |
| self.terminal = sys.stdout | |
| # Clear log file on startup | |
| with open(LOG_FILE, "w") as f: | |
| f.write(f"===== Application Startup at {datetime.now()} =====\n\n") | |
| self.log = open(LOG_FILE, "a") | |
| def write(self, message): | |
| self.terminal.write(message) | |
| self.log.write(message) | |
| self.log.flush() | |
| def flush(self): | |
| self.terminal.flush() | |
| self.log.flush() | |
| def isatty(self): | |
| return hasattr(self.terminal, 'isatty') and self.terminal.isatty() | |
| sys.stdout = Logger() | |
| sys.stderr = Logger() | |
| print(f"--- Application Started at {datetime.now()} ---") | |
| demo = create_interface() | |
| demo.launch(server_name="0.0.0.0", server_port=7860) |