Spaces:
Sleeping
Sleeping
| import gradio as gr | |
| import json | |
| import os | |
| from PIL import Image | |
| import tempfile | |
| from utils.extractor import DesignTokenExtractor | |
| from utils.token_generator import TokenCodeGenerator | |
| def create_token_preview(tokens): | |
| """Create HTML preview of extracted tokens""" | |
| html = """ | |
| <div style="font-family: system-ui, sans-serif; padding: 20px; background: #f9fafb; border-radius: 8px;"> | |
| <h3 style="margin-top: 0; color: #1f2937;">Extracted Design Tokens</h3> | |
| """ | |
| # Color palette preview | |
| if 'colors' in tokens and tokens['colors']: | |
| html += """ | |
| <div style="margin-bottom: 24px;"> | |
| <h4 style="color: #6b7280; font-size: 14px; text-transform: uppercase; letter-spacing: 0.05em;">Colors</h4> | |
| <div style="display: flex; gap: 12px; flex-wrap: wrap;"> | |
| """ | |
| for name, color in tokens['colors'].items(): | |
| html += f""" | |
| <div style="text-align: center;"> | |
| <div style="width: 80px; height: 80px; background: {color['hex']}; | |
| border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1);"></div> | |
| <div style="margin-top: 8px;"> | |
| <div style="font-size: 12px; font-weight: 600; color: #374151;">{name}</div> | |
| <div style="font-size: 11px; color: #9ca3af;">{color['hex']}</div> | |
| <div style="font-size: 10px; color: #9ca3af;">{int(color.get('proportion', 0) * 100)}%</div> | |
| </div> | |
| </div> | |
| """ | |
| html += "</div></div>" | |
| # Spacing preview | |
| if 'spacing' in tokens and tokens['spacing']: | |
| html += """ | |
| <div style="margin-bottom: 24px;"> | |
| <h4 style="color: #6b7280; font-size: 14px; text-transform: uppercase; letter-spacing: 0.05em;">Spacing</h4> | |
| <div style="display: flex; gap: 16px; align-items: flex-end;"> | |
| """ | |
| for name, value in tokens['spacing'].items(): | |
| try: | |
| height = value.replace('px', '') | |
| html += f""" | |
| <div style="text-align: center;"> | |
| <div style="width: 60px; height: {height}px; background: #3b82f6; | |
| border-radius: 4px; opacity: 0.8;"></div> | |
| <div style="margin-top: 8px;"> | |
| <div style="font-size: 12px; font-weight: 600; color: #374151;">{name}</div> | |
| <div style="font-size: 11px; color: #9ca3af;">{value}</div> | |
| </div> | |
| </div> | |
| """ | |
| except: | |
| pass | |
| html += "</div></div>" | |
| # Typography preview | |
| if 'typography' in tokens and tokens['typography']: | |
| html += """ | |
| <div style="margin-bottom: 24px;"> | |
| <h4 style="color: #6b7280; font-size: 14px; text-transform: uppercase; letter-spacing: 0.05em;">Typography</h4> | |
| """ | |
| for name, props in tokens['typography'].items(): | |
| size = props.get('size', '16px') | |
| weight = props.get('weight', '400') | |
| family = props.get('family', 'sans-serif') | |
| html += f""" | |
| <div style="margin-bottom: 12px; padding: 12px; background: white; border-radius: 6px;"> | |
| <div style="font-size: {size}; font-weight: {weight}; font-family: {family}; color: #1f2937;"> | |
| Sample {name.title()} Text | |
| </div> | |
| <div style="font-size: 11px; color: #9ca3af; margin-top: 4px;"> | |
| {family} • {size} • Weight {weight} | |
| </div> | |
| </div> | |
| """ | |
| html += "</div>" | |
| html += "</div>" | |
| return html | |
| def process_screenshot(image, output_format, progress=gr.Progress()): | |
| """Process uploaded screenshot and extract design tokens""" | |
| if image is None: | |
| return None, "Please upload a screenshot", None | |
| extractor = DesignTokenExtractor() | |
| generator = TokenCodeGenerator() | |
| try: | |
| progress(0.1, desc="Initializing extraction...") | |
| # Resize image if needed | |
| image = extractor.resize_for_processing(image) | |
| # Save temporary file for colorgram | |
| with tempfile.NamedTemporaryFile(suffix='.png', delete=False) as tmp: | |
| temp_path = tmp.name | |
| image.save(temp_path) | |
| progress(0.3, desc="Extracting colors...") | |
| colors = extractor.extract_colors(temp_path) | |
| progress(0.5, desc="Detecting spacing...") | |
| spacing = extractor.detect_spacing(image) | |
| progress(0.6, desc="Analyzing typography...") | |
| typography = extractor.detect_typography(image) | |
| progress(0.7, desc="Analyzing components...") | |
| components = extractor.analyze_components(image) | |
| # Combine all tokens | |
| tokens = { | |
| "colors": colors, | |
| "spacing": spacing, | |
| "typography": typography, | |
| "components": components | |
| } | |
| progress(0.8, desc="Generating code...") | |
| # Generate output based on selected format | |
| if output_format == "CSS Variables": | |
| code_output = generator.generate_css_variables(tokens) | |
| file_ext = "css" | |
| elif output_format == "Tailwind Config": | |
| code_output = generator.generate_tailwind_config(tokens) | |
| file_ext = "js" | |
| elif output_format == "JSON Tokens": | |
| code_output = generator.generate_json_tokens(tokens) | |
| file_ext = "json" | |
| elif output_format == "Style Dictionary": | |
| code_output = generator.generate_style_dictionary(tokens) | |
| file_ext = "json" | |
| elif output_format == "SCSS Variables": | |
| code_output = generator.generate_scss_variables(tokens) | |
| file_ext = "scss" | |
| else: | |
| code_output = json.dumps(tokens, indent=2) | |
| file_ext = "json" | |
| # Save output file | |
| output_filename = f"design_tokens.{file_ext}" | |
| with open(output_filename, "w") as f: | |
| f.write(code_output) | |
| # Clean up temp file | |
| try: | |
| os.unlink(temp_path) | |
| except: | |
| pass | |
| progress(1.0, desc="Complete!") | |
| # Create preview visualization | |
| preview_html = create_token_preview(tokens) | |
| return preview_html, code_output, output_filename | |
| except Exception as e: | |
| return None, f"Error processing screenshot: {str(e)}", None | |
| def create_gradio_app(): | |
| """Create the main Gradio application""" | |
| with gr.Blocks( | |
| title="Design Token Extractor", | |
| theme=gr.themes.Soft(), | |
| css=""" | |
| .gradio-container { | |
| font-family: 'Inter', system-ui, sans-serif; | |
| } | |
| .gr-button-primary { | |
| background-color: #3b82f6 !important; | |
| } | |
| """ | |
| ) as app: | |
| gr.Markdown( | |
| """ | |
| # 🎨 Design Token Extractor | |
| Transform UI screenshots into structured design token libraries using AI-powered analysis. | |
| Upload a screenshot to automatically extract colors, spacing, typography, and component tokens. | |
| --- | |
| """ | |
| ) | |
| with gr.Row(): | |
| with gr.Column(scale=1): | |
| input_image = gr.Image( | |
| label="Upload UI Screenshot", | |
| type="pil", | |
| sources=["upload", "clipboard"], | |
| height=400 | |
| ) | |
| output_format = gr.Radio( | |
| choices=[ | |
| "CSS Variables", | |
| "Tailwind Config", | |
| "JSON Tokens", | |
| "Style Dictionary", | |
| "SCSS Variables" | |
| ], | |
| value="CSS Variables", | |
| label="Output Format", | |
| info="Choose the format for your design tokens" | |
| ) | |
| extract_btn = gr.Button( | |
| "🚀 Extract Design Tokens", | |
| variant="primary", | |
| size="lg" | |
| ) | |
| gr.Markdown( | |
| """ | |
| ### Tips for best results: | |
| - Use high-quality screenshots (min 800px width) | |
| - Include various UI elements for comprehensive extraction | |
| - Screenshots with clear color hierarchy work best | |
| - Ensure good contrast between elements | |
| """ | |
| ) | |
| with gr.Column(scale=1): | |
| preview = gr.HTML( | |
| label="Token Preview", | |
| value="<div style='padding: 20px; text-align: center; color: #9ca3af;'>Upload a screenshot to see extracted tokens</div>" | |
| ) | |
| code_output = gr.Code( | |
| label="Generated Code", | |
| language="css", | |
| lines=20, | |
| value="// Your design tokens will appear here" | |
| ) | |
| download_file = gr.File( | |
| label="Download Tokens", | |
| visible=True | |
| ) | |
| # Examples section commented out until example images are available | |
| # gr.Markdown("### Example Screenshots") | |
| # gr.Examples( | |
| # examples=[ | |
| # ["examples/dashboard.png", "CSS Variables"], | |
| # ["examples/landing_page.png", "Tailwind Config"], | |
| # ["examples/mobile_app.png", "JSON Tokens"] | |
| # ], | |
| # inputs=[input_image, output_format], | |
| # cache_examples=False | |
| # ) | |
| # Connect the extraction function | |
| extract_btn.click( | |
| fn=process_screenshot, | |
| inputs=[input_image, output_format], | |
| outputs=[preview, code_output, download_file] | |
| ) | |
| # Add footer | |
| gr.Markdown( | |
| """ | |
| --- | |
| ### Features: | |
| - **Color Extraction**: Identifies dominant colors and creates semantic color roles | |
| - **Spacing Detection**: Analyzes layout patterns to extract consistent spacing values | |
| - **Typography Analysis**: Detects font styles and creates text hierarchy tokens | |
| - **Multiple Output Formats**: Export to CSS, Tailwind, JSON, Style Dictionary, or SCSS | |
| Built with ❤️ using Gradio and computer vision models | |
| """ | |
| ) | |
| return app | |
| if __name__ == "__main__": | |
| app = create_gradio_app() | |
| app.launch( | |
| server_name="0.0.0.0", | |
| server_port=7860, | |
| share=True, | |
| show_error=True | |
| ) |