import gradio as gr import numpy as np import os import json import time from typing import Iterable from huggingface_hub import login from sentence_transformers import SentenceTransformer, util from gradio.themes.base import Base from gradio.themes.utils import colors, fonts, sizes # --- CONFIGURATION --- class Config: """Configuration settings for the application.""" EMBEDDING_MODEL_ID = "google/embeddinggemma-300M" PROMPT_NAME = "STS" TOP_K = 5 HF_TOKEN = os.getenv('HF_TOKEN') # --- COLOR DATA --- COLOR_DATA = [ {"name": "Crimson", "hex": "#DC143C", "description": "A deep, rich red color, leaning slightly towards purple."}, {"name": "Scarlet", "hex": "#FF2400", "description": "A brilliant, vivid red with a hint of orange."}, {"name": "Blood Red", "hex": "#8B0000", "description": "Dark, intense red color associated with danger and horror."}, {"name": "Coral", "hex": "#FF7F50", "description": "A vibrant pinkish-orange reminiscent of marine invertebrates."}, {"name": "Tangerine", "hex": "#F28500", "description": "A saturated, zesty orange, like the ripe citrus fruit."}, {"name": "Pumpkin", "hex": "#FF7518", "description": "Bright orange color associated with autumn and Halloween."}, {"name": "Gold", "hex": "#FFD700", "description": "A bright, metallic yellow associated with wealth and luxury."}, {"name": "Champagne", "hex": "#F7E7CE", "description": "Pale golden color associated with celebration and elegance."}, {"name": "Lemon Chiffon", "hex": "#FFFACD", "description": "A pale, light yellow, as soft and airy as the dessert."}, {"name": "Lime Green", "hex": "#32CD32", "description": "A bright green color, evoking freshness and zesty energy."}, {"name": "Forest Green", "hex": "#228B22", "description": "A dark, shaded green, like the canopy of a dense forest."}, {"name": "Mint", "hex": "#98FB98", "description": "Fresh, cool green color associated with tranquility and cleanliness."}, {"name": "Teal", "hex": "#008080", "description": "A medium blue-green color, often seen as sophisticated and calming."}, {"name": "Cyan", "hex": "#00FFFF", "description": "A vibrant greenish-blue, one of the primary subtractive colors."}, {"name": "Sky Blue", "hex": "#87CEEB", "description": "A light, pale blue, like the color of a clear daytime sky."}, {"name": "Royal Blue", "hex": "#4169E1", "description": "A deep, vivid blue that is both rich and bright."}, {"name": "Navy", "hex": "#000080", "description": "Dark blue color associated with professionalism and depth."}, {"name": "Indigo", "hex": "#4B0082", "description": "A deep, rich color between blue and violet in the spectrum."}, {"name": "Lavender", "hex": "#E6E6FA", "description": "A light, pale purple with a bluish hue, named after the flower."}, {"name": "Plum", "hex": "#DDA0DD", "description": "A reddish-purple color, like the ripe fruit it's named after."}, {"name": "Purple", "hex": "#800080", "description": "Deep purple color associated with royalty and mystery."}, {"name": "Magenta", "hex": "#FF00FF", "description": "A purplish-red color that lies between red and violet."}, {"name": "Hot Pink", "hex": "#FF69B4", "description": "A bright, vivid pink that is both bold and energetic."}, {"name": "Rose", "hex": "#FF007F", "description": "Bright pink color associated with romance and femininity."}, {"name": "Ivory", "hex": "#FFFFF0", "description": "An off-white color that resembles the material from tusks and teeth."}, {"name": "Cream", "hex": "#F5F5DC", "description": "Warm off-white color associated with comfort and elegance."}, {"name": "Beige", "hex": "#F5F5DC", "description": "A pale sandy fawn color, often used as a warm, neutral tone."}, {"name": "Taupe", "hex": "#483C32", "description": "A dark grayish-brown or brownish-gray color."}, {"name": "Coffee", "hex": "#6F4E37", "description": "Rich brown color associated with warmth and comfort."}, {"name": "Chocolate", "hex": "#7B3F00", "description": "Deep brown color associated with richness and indulgence."}, {"name": "Slate Gray", "hex": "#708090", "description": "A medium gray with a slight blue tinge, like the metamorphic rock."}, {"name": "Charcoal", "hex": "#36454F", "description": "A dark, almost black gray, like burnt wood."}, {"name": "Silver", "hex": "#C0C0C0", "description": "A metallic gray color that resembles polished silver."}, {"name": "Emerald", "hex": "#50C878", "description": "A brilliant green, named after the precious gemstone."}, {"name": "Sapphire", "hex": "#0F52BA", "description": "A deep, lustrous blue, reminiscent of the valuable gemstone."}, {"name": "Ruby", "hex": "#E0115F", "description": "A deep red color, inspired by the gemstone of the same name."}, {"name": "Turquoise", "hex": "#40E0D0", "description": "A greenish-blue color, often associated with tropical waters."}, {"name": "Bronze", "hex": "#CD7F32", "description": "A metallic brown color that resembles the alloy of copper and tin."}, {"name": "Neon Green", "hex": "#39FF14", "description": "Extremely bright green color associated with technology and cyberpunk."}, {"name": "Electric Blue", "hex": "#7DF9FF", "description": "Bright, vibrant blue color associated with energy and technology."}, {"name": "Sunset Orange", "hex": "#FF4500", "description": "Warm orange color reminiscent of beautiful sunsets."}, {"name": "Midnight Blue", "hex": "#191970", "description": "Very dark blue color associated with mystery and night."}, {"name": "Burgundy", "hex": "#800020", "description": "Dark red color associated with sophistication and luxury."}, {"name": "Peach", "hex": "#FFCBA4", "description": "Soft orange-pink color associated with warmth and gentleness."}, {"name": "Sage", "hex": "#9CAF88", "description": "Muted green color associated with wisdom and tranquility."}, {"name": "Dusty Rose", "hex": "#DCAE96", "description": "Muted pink color with vintage and romantic associations."}, {"name": "Steel Blue", "hex": "#4682B4", "description": "Blue-gray color associated with strength and industry."}, {"name": "Mauve", "hex": "#E0B0FF", "description": "Pale purple color associated with elegance and sophistication."}, {"name": "Rust", "hex": "#B7410E", "description": "Reddish-brown color associated with vintage and industrial themes."}, {"name": "Olive", "hex": "#808000", "description": "Yellow-green color associated with nature and earthiness."}, {"name": "Maroon", "hex": "#800000", "description": "Dark red color associated with depth and richness."}, ] # --- FONT DATA --- FONT_DATA = [ {"name": "Playfair Display", "family": "serif", "google_fonts_url": "https://fonts.googleapis.com/css2?family=Playfair+Display:wght@400;700&display=swap", "description": "Elegant, sophisticated, editorial, high-contrast serif with dramatic flair, perfect for luxury brands and fashion magazines"}, {"name": "Inter", "family": "sans-serif", "google_fonts_url": "https://fonts.googleapis.com/css2?family=Inter:wght@300;400;600&display=swap", "description": "Modern, clean, professional, highly legible sans-serif designed for digital interfaces and contemporary design"}, {"name": "Amatic SC", "family": "handwriting", "google_fonts_url": "https://fonts.googleapis.com/css2?family=Amatic+SC:wght@400;700&display=swap", "description": "Playful, casual, handwritten, fun, child-like, informal font perfect for creative and whimsical projects"}, {"name": "Crimson Text", "family": "serif", "google_fonts_url": "https://fonts.googleapis.com/css2?family=Crimson+Text:wght@400;600&display=swap", "description": "Classical, scholarly, academic, readable serif inspired by old-style typefaces, ideal for books and literature"}, {"name": "Roboto", "family": "sans-serif", "google_fonts_url": "https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500&display=swap", "description": "Friendly, approachable, geometric sans-serif with a mechanical skeleton, widely used in digital applications"}, {"name": "Dancing Script", "family": "script", "google_fonts_url": "https://fonts.googleapis.com/css2?family=Dancing+Script:wght@400;700&display=swap", "description": "Romantic, flowing, elegant script font perfect for wedding invitations, greeting cards, and feminine designs"}, {"name": "Oswald", "family": "sans-serif", "google_fonts_url": "https://fonts.googleapis.com/css2?family=Oswald:wght@300;400;600&display=swap", "description": "Bold, condensed, impactful sans-serif with strong presence, ideal for headlines and masculine designs"}, {"name": "Lora", "family": "serif", "google_fonts_url": "https://fonts.googleapis.com/css2?family=Lora:wght@400;600&display=swap", "description": "Warm, friendly, contemporary serif with calligraphic roots, perfect for body text and storytelling"}, {"name": "Montserrat", "family": "sans-serif", "google_fonts_url": "https://fonts.googleapis.com/css2?family=Montserrat:wght@300;400;600&display=swap", "description": "Urban, modern, versatile sans-serif inspired by Buenos Aires signage, great for branding and corporate use"}, {"name": "Pacifico", "family": "script", "google_fonts_url": "https://fonts.googleapis.com/css2?family=Pacifico&display=swap", "description": "Surfing, California, retro, casual script font with beach vibes and laid-back summer feeling"}, {"name": "Source Code Pro", "family": "monospace", "google_fonts_url": "https://fonts.googleapis.com/css2?family=Source+Code+Pro:wght@300;400;600&display=swap", "description": "Technical, programming, coding, monospaced font designed for developers and technical documentation"}, {"name": "Merriweather", "family": "serif", "google_fonts_url": "https://fonts.googleapis.com/css2?family=Merriweather:wght@300;400;700&display=swap", "description": "Traditional, readable, pleasant serif designed for comfortable reading on screens and in print"}, {"name": "Raleway", "family": "sans-serif", "google_fonts_url": "https://fonts.googleapis.com/css2?family=Raleway:wght@300;400;600&display=swap", "description": "Sophisticated, thin, elegant sans-serif with distinctive 'W', perfect for fashion and high-end design"}, {"name": "Poppins", "family": "sans-serif", "google_fonts_url": "https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;600&display=swap", "description": "Geometric, modern, friendly sans-serif with circular forms, popular for contemporary web design"}, {"name": "Open Sans", "family": "sans-serif", "google_fonts_url": "https://fonts.googleapis.com/css2?family=Open+Sans:wght@300;400;600&display=swap", "description": "Neutral, friendly, optimistic sans-serif designed for legibility across interfaces, print, and web"}, {"name": "Creepster", "family": "display", "google_fonts_url": "https://fonts.googleapis.com/css2?family=Creepster&display=swap", "description": "Horror, scary, Halloween, gothic font with dripping effect, perfect for spooky and thriller themes"}, {"name": "Orbitron", "family": "sans-serif", "google_fonts_url": "https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700&display=swap", "description": "Futuristic, sci-fi, technology, space-age font perfect for cyberpunk and futuristic designs"}, {"name": "Righteous", "family": "display", "google_fonts_url": "https://fonts.googleapis.com/css2?family=Righteous&display=swap", "description": "Futuristic, sci-fi, technology, bold display font with unique character shapes for modern designs"}, {"name": "Satisfy", "family": "script", "google_fonts_url": "https://fonts.googleapis.com/css2?family=Satisfy&display=swap", "description": "Casual, relaxed, handwritten script with natural flow, perfect for personal and informal communications"}, {"name": "Anton", "family": "sans-serif", "google_fonts_url": "https://fonts.googleapis.com/css2?family=Anton&display=swap", "description": "Bold, condensed, impactful sans-serif perfect for headlines, posters, and attention-grabbing text"}, {"name": "Courgette", "family": "script", "google_fonts_url": "https://fonts.googleapis.com/css2?family=Courgette&display=swap", "description": "French, bistro, café, elegant script font with continental European charm and sophistication"}, {"name": "Indie Flower", "family": "handwriting", "google_fonts_url": "https://fonts.googleapis.com/css2?family=Indie+Flower&display=swap", "description": "Indie, hipster, handwritten font with quirky personality, perfect for creative and artistic projects"}, {"name": "PT Serif", "family": "serif", "google_fonts_url": "https://fonts.googleapis.com/css2?family=PT+Serif:wght@400;700&display=swap", "description": "Russian, Cyrillic, transitional serif with excellent readability for both Latin and Cyrillic scripts"}, {"name": "Questrial", "family": "sans-serif", "google_fonts_url": "https://fonts.googleapis.com/css2?family=Questrial&display=swap", "description": "Simple, clean, minimal sans-serif with subtle quirks, perfect for modern and understated designs"}, {"name": "Bangers", "family": "display", "google_fonts_url": "https://fonts.googleapis.com/css2?family=Bangers&display=swap", "description": "Comic book, superhero, pop art font inspired by mid-20th century comic books and advertisements"}, {"name": "Sacramento", "family": "script", "google_fonts_url": "https://fonts.googleapis.com/css2?family=Sacramento&display=swap", "description": "Monoline, cursive script with vintage charm, perfect for elegant and sophisticated branding"}, {"name": "Bitter", "family": "serif", "google_fonts_url": "https://fonts.googleapis.com/css2?family=Bitter:wght@400;700&display=swap", "description": "Contemporary, slab serif with slight contrast, designed for comfortable reading in long texts"}, {"name": "Lobster", "family": "script", "google_fonts_url": "https://fonts.googleapis.com/css2?family=Lobster&display=swap", "description": "Bold, retro, vintage script font with a 1950s diner feel, perfect for nostalgic and Americana designs"}, {"name": "Fredoka One", "family": "display", "google_fonts_url": "https://fonts.googleapis.com/css2?family=Fredoka+One&display=swap", "description": "Friendly, rounded, playful display font perfect for children's content, toys, and fun applications"}, {"name": "Abril Fatface", "family": "serif", "google_fonts_url": "https://fonts.googleapis.com/css2?family=Abril+Fatface&display=swap", "description": "Bold, dramatic, high-contrast display serif inspired by French and Italian typography, perfect for headlines"}, {"name": "Great Vibes", "family": "script", "google_fonts_url": "https://fonts.googleapis.com/css2?family=Great+Vibes&display=swap", "description": "Elegant, formal, calligraphic script with sophisticated curves, ideal for luxury and premium branding"}, {"name": "Shadows Into Light", "family": "handwriting", "google_fonts_url": "https://fonts.googleapis.com/css2?family=Shadows+Into+Light&display=swap", "description": "Casual, handwritten, personal font that feels like natural handwriting with a marker or pen"}, {"name": "Libre Baskerville", "family": "serif", "google_fonts_url": "https://fonts.googleapis.com/css2?family=Libre+Baskerville:wght@400;700&display=swap", "description": "Classic, traditional, scholarly serif based on American Type Founder's Baskerville, perfect for academic texts"}, {"name": "Bungee", "family": "display", "google_fonts_url": "https://fonts.googleapis.com/css2?family=Bungee&display=swap", "description": "Urban, street art, graffiti-inspired display font perfect for hip-hop culture and urban designs"}, {"name": "Philosopher", "family": "sans-serif", "google_fonts_url": "https://fonts.googleapis.com/css2?family=Philosopher:wght@400;700&display=swap", "description": "Intellectual, academic, thoughtful sans-serif with classical proportions, perfect for philosophical content"}, {"name": "Cinzel", "family": "serif", "google_fonts_url": "https://fonts.googleapis.com/css2?family=Cinzel:wght@400;600&display=swap", "description": "Ancient, Roman, classical serif inspired by inscriptions, perfect for historical and luxury themes"}, ] # --- DYNAMIC MOOD THEME CLASS --- class MoodTheme(Base): """Dynamic theme class that can be configured with mood-based colors and fonts.""" def __init__( self, mood_colors: list = None, mood_fonts: list = None, theme_name: str = "MoodTheme", *, primary_hue: colors.Color | str = colors.blue, secondary_hue: colors.Color | str = colors.gray, neutral_hue: colors.Color | str = colors.gray, spacing_size: sizes.Size | str = sizes.spacing_md, radius_size: sizes.Size | str = sizes.radius_md, text_size: sizes.Size | str = sizes.text_md, font: fonts.Font | str | Iterable[fonts.Font | str] = ( fonts.GoogleFont("Inter"), "ui-sans-serif", "sans-serif", ), font_mono: fonts.Font | str | Iterable[fonts.Font | str] = ( fonts.GoogleFont("Source Code Pro"), "ui-monospace", "monospace", ), ): self.mood_colors = mood_colors or [] self.mood_fonts = mood_fonts or [] self.theme_name = theme_name # Map mood colors to existing Gradio colors if self.mood_colors: primary_color = self._map_hex_to_gradio_color(self.mood_colors[0]['hex']) if len(self.mood_colors) > 1: secondary_color = self._map_hex_to_gradio_color(self.mood_colors[1]['hex']) else: secondary_color = colors.gray if len(self.mood_colors) > 2: neutral_color = self._map_hex_to_gradio_color(self.mood_colors[2]['hex']) else: neutral_color = colors.blue else: primary_color = primary_hue secondary_color = secondary_hue neutral_color = neutral_hue # If mood fonts provided, use them if self.mood_fonts: primary_font = fonts.GoogleFont(self.mood_fonts[0]['name']) font = (primary_font, "ui-sans-serif", "sans-serif") if len(self.mood_fonts) > 1: mono_font = fonts.GoogleFont(self.mood_fonts[1]['name']) font_mono = (mono_font, "ui-monospace", "monospace") super().__init__( primary_hue=primary_color, secondary_hue=secondary_color, neutral_hue=neutral_color, spacing_size=spacing_size, radius_size=radius_size, text_size=text_size, font=font, font_mono=font_mono, ) # Apply additional mood-based styling that will persist in published theme self._apply_mood_styling() def _map_hex_to_gradio_color(self, hex_color: str): """Map hex colors to deliberately different Gradio colors.""" # Create a more aggressive mapping to ensure variety color_mapping = { # Reds - map to different red variants "#DC143C": colors.red, # Crimson "#FF2400": colors.rose, # Scarlet "#8B0000": colors.red, # Dark red # Oranges "#FF7F50": colors.orange, # Coral "#F28500": colors.amber, # Tangerine "#FF7518": colors.orange, # Pumpkin # Yellows/Golds "#FFD700": colors.yellow, # Gold "#F7E7CE": colors.amber, # Champagne "#FFFACD": colors.yellow, # Lemon Chiffon # Greens "#32CD32": colors.lime, # Lime Green "#228B22": colors.green, # Forest Green "#50C878": colors.emerald, # Emerald # Blues "#4169E1": colors.blue, # Royal Blue "#87CEEB": colors.sky, # Sky Blue "#0F52BA": colors.indigo, # Sapphire # Purples "#4B0082": colors.indigo, # Indigo "#800080": colors.purple, # Purple "#DDA0DD": colors.violet, # Plum # Pinks "#FF69B4": colors.pink, # Hot Pink "#FF00FF": colors.fuchsia, # Magenta # Teals/Cyans "#008080": colors.teal, # Teal "#40E0D0": colors.cyan, # Turquoise # Grays/Browns "#708090": colors.slate, # Slate Gray "#36454F": colors.gray, # Charcoal "#6F4E37": colors.stone, # Coffee } return color_mapping.get(hex_color, colors.blue) def _apply_mood_styling(self): """Apply additional styling based on mood colors and fonts.""" if self.mood_colors: # Use the actual hex colors for specific styling color1 = self.mood_colors[0]['hex'] color2 = self.mood_colors[1]['hex'] if len(self.mood_colors) > 1 else color1 color3 = self.mood_colors[2]['hex'] if len(self.mood_colors) > 2 else color2 # Apply styling that will be preserved in the published theme self.set( # Button styling button_primary_background_fill=color1, button_primary_background_fill_hover=color2, button_primary_text_color="white", button_secondary_background_fill=color3, button_secondary_text_color="white", # Block styling block_title_background_fill=color2, block_title_text_color="white", # Input styling input_border_color_focus=color1, slider_color=color1, ) # --- CORE LOGIC --- class MoodThemeGenerator: """Handles model loading, embedding generation, and theme creation.""" def __init__(self, config: Config, color_data: list, font_data: list): """Initializes the generator, logs in, and loads necessary assets.""" self.config = config self.color_data = color_data self.font_data = font_data self._login_to_hf() self.embedding_model = self._load_model() self.color_embeddings = self._precompute_color_embeddings() self.font_embeddings = self._precompute_font_embeddings() self.current_theme_data = None def _login_to_hf(self): """Logs into Hugging Face Hub if a token is provided.""" if self.config.HF_TOKEN: print("Logging into Hugging Face Hub...") login(token=self.config.HF_TOKEN) else: print("HF_TOKEN not found. Proceeding without login.") def _load_model(self) -> SentenceTransformer: """Loads the Sentence Transformer model.""" print(f"Initializing embedding model: {self.config.EMBEDDING_MODEL_ID}...") try: return SentenceTransformer(self.config.EMBEDDING_MODEL_ID) except Exception as e: print(f"Error loading model: {e}") raise def _precompute_color_embeddings(self) -> np.ndarray: """Generates and stores embeddings for the color descriptions.""" print("Pre-computing embeddings for color palette...") color_texts = [f"{color['name']}, {color['description']}" for color in self.color_data] return self.embedding_model.encode(color_texts, prompt_name=self.config.PROMPT_NAME) def _precompute_font_embeddings(self) -> np.ndarray: """Generates and stores embeddings for the font descriptions.""" print("Pre-computing embeddings for font palette...") font_texts = [f"{font['name']}, {font['description']}" for font in self.font_data] return self.embedding_model.encode(font_texts, prompt_name=self.config.PROMPT_NAME) def _get_text_color_for_bg(self, hex_color: str) -> str: """Calculate best text color for background.""" hex_color = hex_color.lstrip('#') try: r, g, b = tuple(int(hex_color[i:i+2], 16) for i in (0, 2, 4)) luminance = (0.299 * r + 0.587 * g + 0.114 * b) return '#000000' if luminance > 150 else '#FFFFFF' except (ValueError, IndexError): return '#000000' def _format_results_as_html(self, color_hits: list, font_hits: list) -> str: """Format both color and font results as HTML.""" html = "
Please enter a mood or description.
", "", "" # Generate embeddings for the mood mood_embedding = self.embedding_model.encode(mood_text, prompt_name=self.config.PROMPT_NAME) # Find matching colors and fonts color_hits = util.semantic_search(mood_embedding, self.color_embeddings, top_k=self.config.TOP_K)[0] font_hits = util.semantic_search(mood_embedding, self.font_embeddings, top_k=self.config.TOP_K)[0] # Extract the actual color and font data selected_colors = [self.color_data[hit['corpus_id']] for hit in color_hits] selected_fonts = [self.font_data[hit['corpus_id']] for hit in font_hits] # Store current theme data for publishing self.current_theme_data = { "mood": mood_text, "colors": selected_colors, "fonts": selected_fonts, "color_scores": [hit['score'] for hit in color_hits], "font_scores": [hit['score'] for hit in font_hits] } # Format results for display results_html = self._format_results_as_html(color_hits, font_hits) # Create dynamic CSS for theme application theme_css = self._create_theme_css(selected_colors, selected_fonts) # Create theme name suggestion theme_name = f"mood-{mood_text.lower().replace(' ', '-')[:20]}" return results_html, theme_css, theme_name def _create_theme_css(self, colors: list, fonts: list) -> str: """Create CSS to apply the theme dynamically.""" # Import fonts font_imports = [] seen_urls = set() for font in fonts: if font['google_fonts_url'] not in seen_urls: font_imports.append(f"@import url('{font['google_fonts_url']}');") seen_urls.add(font['google_fonts_url']) # Apply colors and fonts to various elements color1 = colors[0]['hex'] if colors else "#4169E1" color2 = colors[1]['hex'] if len(colors) > 1 else color1 color3 = colors[2]['hex'] if len(colors) > 2 else color2 font1 = fonts[0]['name'] if fonts else "Inter" font2 = fonts[1]['name'] if len(fonts) > 1 else font1 css = f""" """ return css def publish_theme(self, theme_name: str, version: str = "1.0.0") -> str: """Publish the current theme to HuggingFace Hub.""" if not self.current_theme_data: return "No theme data available. Please generate a theme first." if not self.config.HF_TOKEN: return "HuggingFace token not found. Please set HF_TOKEN environment variable." # DEBUG: Print what we're actually working with print("=== DEBUG INFO ===") print(f"Colors: {[(c['name'], c['hex']) for c in self.current_theme_data['colors'][:3]]}") print(f"Fonts: {[f['name'] for f in self.current_theme_data['fonts'][:2]]}") try: # Create theme with more distinct differentiation mood_theme = MoodTheme( mood_colors=self.current_theme_data['colors'][:3], mood_fonts=self.current_theme_data['fonts'][:2], theme_name=theme_name.replace('-', '_').replace(' ', '_') ) # Additional forced differentiation based on first color first_color = self.current_theme_data['colors'][0]['hex'] if first_color in ['#DC143C', '#FF2400', '#8B0000']: # Red family mood_theme.set(button_primary_background_fill="#8B0000") elif first_color in ['#FFD700', '#F7E7CE', '#FFFACD']: # Yellow/Gold family mood_theme.set(button_primary_background_fill="#DAA520") elif first_color in ['#228B22', '#32CD32', '#50C878']: # Green family mood_theme.set(button_primary_background_fill="#006400") elif first_color in ['#4169E1', '#87CEEB', '#0F52BA']: # Blue family mood_theme.set(button_primary_background_fill="#000080") else: mood_theme.set(button_primary_background_fill=first_color) clean_name = theme_name.lower().replace(' ', '-').replace('_', '-') repo_name = f"{clean_name}-{int(time.time())}" mood_theme.push_to_hub( repo_name=repo_name, version=version, hf_token=self.config.HF_TOKEN ) return f"Theme published: {repo_name}\nFirst color: {first_color}\nFirst font: {self.current_theme_data['fonts'][0]['name']}" except Exception as e: return f"Error publishing theme: {str(e)}" def clear_theme(self) -> tuple[str, str, str]: """Clear the current theme.""" self.current_theme_data = None return "", "", "" # --- GRADIO UI --- def create_ui(generator: MoodThemeGenerator): """Creates the Gradio web interface.""" with gr.Blocks(title="Mood Theme Generator") as demo: # Dynamic CSS output dynamic_css_output = gr.HTML() gr.Markdown(""" # Mood Theme Generator Transform your words into a complete design system! Describe any mood, scene, or feeling, and get a matching **color palette** and **font selection** that's automatically applied as a **custom Gradio theme**. **Features:** - AI-powered color palette generation - Semantic font matching - Real-time theme application - One-click theme publishing to HuggingFace Hub """) with gr.Row(): with gr.Column(scale=3): mood_input = gr.Textbox( value="Cozy autumn coffee shop with warm lighting", label="Describe Your Mood", info="Be as descriptive and creative as you like!", lines=2 ) with gr.Column(scale=1): generate_btn = gr.Button("Generate Theme", variant="primary", size="lg") clear_btn = gr.Button("Clear", variant="secondary") # Results display results_output = gr.HTML(label="Generated Theme Elements") # Theme management section with gr.Row(): with gr.Column(scale=2): theme_name_input = gr.Textbox( label="Theme Name", placeholder="my-awesome-theme", info="Name for your theme (will be used for HuggingFace repo)" ) with gr.Column(scale=1): version_input = gr.Textbox( value="1.0.0", label="Version", info="Semantic version" ) with gr.Column(scale=1): publish_btn = gr.Button("Publish Theme", variant="secondary") publish_output = gr.Textbox( label="Publishing Status", interactive=False, lines=3 ) # Examples gr.Examples( examples=[ "Cozy autumn coffee shop with warm lighting and vintage books", "Futuristic cyberpunk city with neon lights and dark atmosphere", "Peaceful zen garden with natural stones and flowing water", "Vibrant music festival with colorful stage lights and energy", "Elegant luxury hotel lobby with marble and gold accents", "Retro 80s arcade with bright colors and pixel art aesthetics", "Minimalist Scandinavian office with clean lines and natural wood", "Romantic Paris café with soft pastels and vintage charm", "Bold modern art gallery with striking contrasts and geometry", "Serene beach sunset with warm oranges and calm blues" ], inputs=[mood_input], outputs=[results_output, dynamic_css_output, theme_name_input], fn=generator.generate_mood_theme, run_on_click=True, label="Try These Examples" ) # Event handlers generate_btn.click( fn=generator.generate_mood_theme, inputs=[mood_input], outputs=[results_output, dynamic_css_output, theme_name_input] ) clear_btn.click( fn=generator.clear_theme, outputs=[results_output, dynamic_css_output, theme_name_input] ) mood_input.submit( fn=generator.generate_mood_theme, inputs=[mood_input], outputs=[results_output, dynamic_css_output, theme_name_input] ) publish_btn.click( fn=generator.publish_theme, inputs=[theme_name_input, version_input], outputs=[publish_output] ) gr.Markdown(""" --- ## How It Works This application uses [**EmbeddingGemma**](https://huggingface.co/google/embeddinggemma-300M) to understand the semantic meaning of your mood description and match it with: 1. **Colors**: A curated palette of colors with rich descriptions 2. **Fonts**: A collection of Google Fonts with personality profiles 3. **Theme Generation**: Automatic creation of a cohesive Gradio theme 4. **Publishing**: One-click publishing to HuggingFace Hub for reuse The magic happens through **semantic similarity search** - your words are converted into mathematical representations (embeddings) that capture meaning, not just keywords. **Theme Application**: The generated theme is applied in real-time using: - Primary color → Headers, primary buttons, accents - Secondary color → Borders, secondary elements - Tertiary color → Backgrounds, subtle highlights - Primary font → Headers, important text - Secondary font → Body text, inputs **Publishing**: Published themes can be reused in any Gradio app with: ```python theme = gr.Theme.from_hub("your-username/your-theme-name") with gr.Blocks(theme=theme) as demo: # Your app here ``` """) return demo if __name__ == "__main__": # Initialize the generator generator = MoodThemeGenerator( config=Config(), color_data=COLOR_DATA, font_data=FONT_DATA ) # Create and launch the UI demo = create_ui(generator) demo.launch()