diff --git "a/app.py" "b/app.py"
new file mode 100644--- /dev/null
+++ "b/app.py"
@@ -0,0 +1,4522 @@
+import gradio as gr
+import logging
+import sys
+import random
+import time
+import os
+import json
+import uuid
+import base64
+from datetime import datetime
+from huggingface_hub import InferenceClient
+from PIL import Image, ImageOps, ImageEnhance, ImageFilter
+import io
+import math
+import hashlib
+import tempfile
+import re
+import numpy as np
+from pathlib import Path
+
+# =============== SETUP LOGGING ===============
+logging.basicConfig(
+ level=logging.INFO,
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
+ handlers=[logging.StreamHandler(sys.stdout)]
+)
+logger = logging.getLogger("imaginova")
+
+# =============== APP CONFIGURATION ===============
+APP_CONFIG = {
+ "name": "Imaginova",
+ "tagline": "Where Imagination Becomes Reality",
+ "version": "3.0.0",
+ "description": "Create magnificent AI-generated artwork from your imagination",
+ "max_history": 50, # Maximum number of images to store in history
+ "cache_enabled": True, # Enable caching to improve performance
+ "enable_analytics": False, # Anonymous usage statistics
+ "default_theme": "auto", # Default theme (light, dark, or auto)
+ "huggingface_space": True, # Running in HF Spaces environment
+ "enable_guided_creation": True, # Enable guided creative mode
+ "max_batch_size": 4, # Maximum images in batch generation
+ "enable_image_editing": True, # Enable image editing features
+ "data_dir": "imaginova_data", # Directory to store persistent data
+}
+
+# Create data directory if it doesn't exist
+os.makedirs(APP_CONFIG["data_dir"], exist_ok=True)
+
+# =============== MODEL CLIENTS SETUP ===============
+def setup_client(api_key=None, provider=None):
+ """Initialize and return API client"""
+ try:
+ if provider:
+ client = InferenceClient(provider=provider, api_key=api_key)
+ logger.info(f"{provider} client initialized successfully")
+ else:
+ client = InferenceClient(api_key=api_key)
+ logger.info("Hugging Face client initialized successfully")
+ return client
+ except Exception as e:
+ logger.error(f"Error initializing client: {str(e)}")
+ return None
+
+# Initialize clients
+try:
+ # For Hugging Face Spaces, we can use the internal API
+ if APP_CONFIG["huggingface_space"]:
+ # In Hugging Face Spaces, no API key is needed
+ hf_client = InferenceClient()
+ logger.info("Hugging Face client created successfully in Spaces environment")
+ else:
+ # Replace with your actual HF API key for local development
+ hf_api_key = os.getenv("HF_API_KEY", None)
+ hf_client = setup_client(hf_api_key)
+ logger.info("Hugging Face client created successfully")
+
+ # Set up prompt enhancer client if API key is provided
+ enhancer_api_key = os.getenv("ENHANCER_API_KEY", None)
+ try:
+ enhancer_client = setup_client(enhancer_api_key, "text-generation")
+ use_ai_enhancer = True
+ logger.info("Prompt enhancer client created successfully")
+ except Exception as e:
+ logger.warning(f"Prompt enhancer not available: {str(e)}. Will use fallback enhancement.")
+ enhancer_client = None
+ use_ai_enhancer = False
+except Exception as e:
+ logger.error(f"Failed to create Hugging Face client: {str(e)}")
+ hf_client = None
+ enhancer_client = None
+ use_ai_enhancer = False
+
+# =============== DATA MODELS ===============
+
+# Image Models with friendly names, descriptions and icons
+IMAGE_MODELS = {
+ "stabilityai/stable-diffusion-xl-base-1.0": {
+ "display_name": "Nova XL",
+ "description": "Premium quality with exceptional detail and composition",
+ "icon": "โญ",
+ "speed": "slow",
+ "quality": "excellent",
+ "strengths": "Composition, detail, realism",
+ "weaknesses": "Generation time, resource intensive",
+ "category": "premium",
+ "recommended_for": ["portraits", "landscapes", "detailed scenes", "professional work"]
+ },
+ "runwayml/stable-diffusion-v1-5": {
+ "display_name": "Nova Fast",
+ "description": "Quick generation with good all-around results",
+ "icon": "๐",
+ "speed": "fast",
+ "quality": "good",
+ "strengths": "Speed, versatility, community support",
+ "weaknesses": "Less sophisticated than Nova XL",
+ "category": "standard",
+ "recommended_for": ["quick sketches", "iterations", "simpler scenes"]
+ },
+ "stabilityai/stable-diffusion-2-1": {
+ "display_name": "Nova Balance",
+ "description": "Balanced approach between speed and quality",
+ "icon": "โจ",
+ "speed": "medium",
+ "quality": "very good",
+ "strengths": "Human faces, consistency, balanced",
+ "weaknesses": "Less stylistic range than Nova Fast",
+ "category": "standard",
+ "recommended_for": ["portraits", "consistent style", "balanced workflow"]
+ },
+ "prompthero/openjourney": {
+ "display_name": "Nova Art",
+ "description": "Stylized artistic results with vibrant imagination",
+ "icon": "๐จ",
+ "speed": "medium",
+ "quality": "stylized",
+ "strengths": "Artistic style, vibrant colors",
+ "weaknesses": "Less photorealistic, inconsistent faces",
+ "category": "specialized",
+ "recommended_for": ["creative artworks", "stylized illustrations", "fantasy concepts"]
+ },
+ "dreamlike-art/dreamlike-diffusion-1.0": {
+ "display_name": "Nova Dream",
+ "description": "Dreamy, ethereal aesthetics with artistic flair",
+ "icon": "๐ซ",
+ "speed": "medium",
+ "quality": "artistic",
+ "strengths": "Dreamy atmospheres, artistic flair",
+ "weaknesses": "Less precise control, abstract results",
+ "category": "specialized",
+ "recommended_for": ["dreamy scenes", "abstract concepts", "atmospheric images"]
+ },
+ "cjwbw/anything-v3-better-vae": {
+ "display_name": "Nova Anime",
+ "description": "Specialized for anime and illustration styles",
+ "icon": "๐",
+ "speed": "fast",
+ "quality": "specialized",
+ "strengths": "Anime style, illustrations, characters",
+ "weaknesses": "Not suitable for photorealism",
+ "category": "specialized",
+ "recommended_for": ["anime characters", "manga style", "cartoon illustrations"]
+ },
+ "eimiss/EimisAnimeDiffusion_1.0v": {
+ "display_name": "Nova Toon",
+ "description": "Enhanced anime and cartoon renderer with vibrant colors",
+ "icon": "๐บ",
+ "speed": "medium",
+ "quality": "specialized",
+ "strengths": "Vibrant anime, consistent character design",
+ "weaknesses": "Limited to cartoon styles",
+ "category": "specialized",
+ "recommended_for": ["vivid anime", "cartoon characters", "stylized portraits"]
+ },
+ "thibaud/sdxl-lightning": {
+ "display_name": "Nova Lightning",
+ "description": "Ultra-fast version of Nova XL with impressive speed",
+ "icon": "โก",
+ "speed": "very fast",
+ "quality": "good",
+ "strengths": "Extreme speed, good quality",
+ "weaknesses": "Less detail than Nova XL",
+ "category": "premium",
+ "recommended_for": ["quick iterations", "rapid prototyping", "multiple variations"]
+ },
+ "stabilityai/sdxl-turbo": {
+ "display_name": "Nova Turbo",
+ "description": "Accelerated high-quality generation with fewer steps",
+ "icon": "๐ฑ",
+ "speed": "fast",
+ "quality": "premium",
+ "strengths": "Speed with premium quality",
+ "weaknesses": "Slightly less detailed than Nova XL",
+ "category": "premium",
+ "recommended_for": ["production quality with speed", "professional work", "detailed concepts"]
+ },
+ "SG161222/Realistic_Vision_V5.1": {
+ "display_name": "Nova Reality",
+ "description": "Specialized in photorealistic people and scenes",
+ "icon": "๐๏ธ",
+ "speed": "medium",
+ "quality": "specialized",
+ "strengths": "Realistic humans, lifelike scenes",
+ "weaknesses": "Less versatile for other styles",
+ "category": "specialized",
+ "recommended_for": ["realistic portraits", "photorealistic scenes", "human figures"]
+ }
+}
+
+# Creation types with icons and detailed descriptions
+CREATION_TYPES = {
+ "Realistic Photo": {
+ "description": "Create a photorealistic image with natural details and lighting",
+ "icon": "๐ท",
+ "prompt_hint": "Try to include details about lighting, time of day, and environment",
+ "examples": ["portrait photography", "landscape photo", "product photography"],
+ "recommended_models": ["Nova XL", "Nova Reality", "Nova Balance"]
+ },
+ "Digital Art": {
+ "description": "Create colorful digital artwork with clean lines and vibrant colors",
+ "icon": "๐๏ธ",
+ "prompt_hint": "Consider specifying color palette and mood for better results",
+ "examples": ["digital landscape art", "character concept", "futuristic city"],
+ "recommended_models": ["Nova Fast", "Nova Art", "Nova XL"]
+ },
+ "Fantasy Illustration": {
+ "description": "Create magical and fantastical scenes with otherworldly elements",
+ "icon": "๐ง",
+ "prompt_hint": "Describe magical elements, creatures, and environments in detail",
+ "examples": ["dragon's lair", "fairy forest", "wizard tower"],
+ "recommended_models": ["Nova Art", "Nova Dream", "Nova XL"]
+ },
+ "Concept Art": {
+ "description": "Create professional concept art for characters, environments or objects",
+ "icon": "๐ฎ",
+ "prompt_hint": "Include details about perspective, purpose, and design influences",
+ "examples": ["sci-fi vehicle", "fantasy character design", "alien landscape"],
+ "recommended_models": ["Nova XL", "Nova Art", "Nova Turbo"]
+ },
+ "Anime/Manga": {
+ "description": "Create Japanese anime or manga style illustration",
+ "icon": "๐",
+ "prompt_hint": "Specify anime aesthetics like shading style and character features",
+ "examples": ["anime portrait", "manga action scene", "chibi character"],
+ "recommended_models": ["Nova Anime", "Nova Toon", "Nova Art"]
+ },
+ "Oil Painting": {
+ "description": "Create an image with oil painting textures and artistic brushstrokes",
+ "icon": "๐ผ๏ธ",
+ "prompt_hint": "Consider describing texture, brushwork style, and canvas feel",
+ "examples": ["oil portrait", "impressionist landscape", "still life painting"],
+ "recommended_models": ["Nova XL", "Nova Dream", "Nova Art"]
+ },
+ "Watercolor": {
+ "description": "Create a soft watercolor illustration with subtle color blending",
+ "icon": "๐ง",
+ "prompt_hint": "Mention color blending, paper texture, and watercolor-specific effects",
+ "examples": ["watercolor landscape", "botanical illustration", "watercolor portrait"],
+ "recommended_models": ["Nova Dream", "Nova XL", "Nova Art"]
+ },
+ "Sketch": {
+ "description": "Create a detailed sketch or drawing with line art focus",
+ "icon": "โ๏ธ",
+ "prompt_hint": "Describe line weight, hatching style, and sketch medium (pencil, charcoal, etc.)",
+ "examples": ["pencil portrait", "architectural sketch", "nature study drawing"],
+ "recommended_models": ["Nova Balance", "Nova Fast", "Nova XL"]
+ },
+ "3D Rendering": {
+ "description": "Create an image that looks like a 3D rendered scene with realistic lighting",
+ "icon": "๐ป",
+ "prompt_hint": "Include details about lighting setup, materials, and camera perspective",
+ "examples": ["3D product render", "architectural visualization", "game environment"],
+ "recommended_models": ["Nova XL", "Nova Reality", "Nova Balance"]
+ },
+ "Pixel Art": {
+ "description": "Create retro-style pixel art with limited color palette",
+ "icon": "๐พ",
+ "prompt_hint": "Specify resolution, color limitations, and pixel art style (e.g., 16-bit, 8-bit)",
+ "examples": ["pixel game character", "isometric pixel scene", "pixel landscape"],
+ "recommended_models": ["Nova Fast", "Nova Art", "Nova Balance"]
+ },
+ "Isometric Art": {
+ "description": "Create isometric style illustrations with geometric perspective",
+ "icon": "๐",
+ "prompt_hint": "Describe the scene elements, color scheme, and isometric perspective",
+ "examples": ["isometric room", "isometric city", "isometric landscape"],
+ "recommended_models": ["Nova Fast", "Nova Balance", "Nova Art"]
+ },
+ "Cinematic Scene": {
+ "description": "Create a dramatic scene with cinematic lighting and composition",
+ "icon": "๐ฌ",
+ "prompt_hint": "Specify camera angle, lighting setup, and film style references",
+ "examples": ["sci-fi movie scene", "dramatic character moment", "epic landscape shot"],
+ "recommended_models": ["Nova XL", "Nova Turbo", "Nova Reality"]
+ },
+ "Character Portrait": {
+ "description": "Create a focused portrait of a character with detailed features",
+ "icon": "๐ค",
+ "prompt_hint": "Describe facial features, expression, clothing, and character background",
+ "examples": ["fantasy character", "sci-fi hero", "historical figure"],
+ "recommended_models": ["Nova XL", "Nova Reality", "Nova Anime"]
+ },
+ "Landscape": {
+ "description": "Create a stunning natural or urban landscape with atmospheric elements",
+ "icon": "๐๏ธ",
+ "prompt_hint": "Include details about terrain, weather, time of day, and focal points",
+ "examples": ["mountain vista", "coastal sunset", "futuristic cityscape"],
+ "recommended_models": ["Nova XL", "Nova Turbo", "Nova Dream"]
+ },
+ "Abstract Composition": {
+ "description": "Create non-representational art with focus on shapes, colors and textures",
+ "icon": "๐ณ",
+ "prompt_hint": "Describe mood, color relationships, textures, and compositional elements",
+ "examples": ["geometric abstraction", "fluid color composition", "textural abstract"],
+ "recommended_models": ["Nova Dream", "Nova Art", "Nova XL"]
+ },
+ "Product Visualization": {
+ "description": "Create professional product imagery with studio-quality lighting",
+ "icon": "๐ฆ",
+ "prompt_hint": "Describe the product details, materials, lighting setup, and background",
+ "examples": ["gadget render", "fashion accessory", "food photography"],
+ "recommended_models": ["Nova XL", "Nova Reality", "Nova Turbo"]
+ }
+}
+
+# Art styles with icons and detailed descriptions
+ART_STYLES = {
+ "Photorealistic": {
+ "description": "Detailed realistic style that resembles a photograph with accurate lighting and textures",
+ "icon": "๐ธ",
+ "examples": "Works by Chuck Close, Richard Estes, or modern 3D renderings",
+ "technical_terms": ["hyperdetailed", "photographic", "lifelike", "realistic lighting"]
+ },
+ "Impressionist": {
+ "description": "Soft brushstrokes that capture light and atmosphere over precise details, like Monet",
+ "icon": "๐",
+ "examples": "Claude Monet, Pierre-Auguste Renoir, Camille Pissarro",
+ "technical_terms": ["impressionism", "plein air", "visible brushstrokes", "light emphasis"]
+ },
+ "Surrealist": {
+ "description": "Dreamlike quality with impossible or irrational scenes, like Salvador Dali",
+ "icon": "๐",
+ "examples": "Salvador Dali, Renรฉ Magritte, Frida Kahlo",
+ "technical_terms": ["surrealism", "dreamlike", "symbolic", "juxtaposition"]
+ },
+ "Pop Art": {
+ "description": "Bold colors, sharp lines and popular culture references, like Andy Warhol",
+ "icon": "๐ญ",
+ "examples": "Andy Warhol, Roy Lichtenstein, Keith Haring",
+ "technical_terms": ["pop art", "halftone dots", "bold outlines", "bright flat colors"]
+ },
+ "Minimalist": {
+ "description": "Simplified forms, limited color palette, and clean composition with minimal elements",
+ "icon": "โฌ",
+ "examples": "Piet Mondrian, Kazimir Malevich, Agnes Martin",
+ "technical_terms": ["minimalism", "geometric", "simplicity", "negative space"]
+ },
+ "Abstract": {
+ "description": "Non-representational style using shapes, colors, and forms to express ideas",
+ "icon": "๐ถ",
+ "examples": "Wassily Kandinsky, Jackson Pollock, Mark Rothko",
+ "technical_terms": ["abstract", "non-figurative", "expressive", "color field"]
+ },
+ "Cubist": {
+ "description": "Geometric shapes and multiple perspectives shown simultaneously, like Picasso",
+ "icon": "๐",
+ "examples": "Pablo Picasso, Georges Braque, Juan Gris",
+ "technical_terms": ["cubism", "geometric", "multiple perspectives", "fragmented"]
+ },
+ "Art Nouveau": {
+ "description": "Ornate, flowing lines inspired by natural forms with decorative elegance",
+ "icon": "๐ฟ",
+ "examples": "Alphonse Mucha, Gustav Klimt, Antoni Gaudรญ",
+ "technical_terms": ["art nouveau", "organic curves", "decorative", "floral motifs"]
+ },
+ "Gothic": {
+ "description": "Dark, medieval-inspired aesthetic with dramatic lighting and architectural elements",
+ "icon": "๐ฐ",
+ "examples": "Zdzisลaw Beksiลski, H.R. Giger, medieval architecture",
+ "technical_terms": ["gothic", "medieval", "dark fantasy", "ornate details"]
+ },
+ "Cyberpunk": {
+ "description": "Futuristic dystopian style with neon colors, technology, and urban decay",
+ "icon": "๐ค",
+ "examples": "Blade Runner, Ghost in the Shell, Akira",
+ "technical_terms": ["cyberpunk", "neon", "high-tech low-life", "dystopian"]
+ },
+ "Steampunk": {
+ "description": "Victorian-era aesthetic combined with steam-powered technology and brass elements",
+ "icon": "โ๏ธ",
+ "examples": "Works by James Ng, Keith Thompson, retrofuturistic Jules Verne adaptations",
+ "technical_terms": ["steampunk", "brass", "victorian", "mechanical"]
+ },
+ "Retro/Vintage": {
+ "description": "Nostalgic style reminiscent of past decades with period-appropriate elements",
+ "icon": "๐บ",
+ "examples": "1950s advertisements, vintage travel posters, pulp magazine covers",
+ "technical_terms": ["retro", "vintage", "nostalgic", "aged texture"]
+ },
+ "Art Deco": {
+ "description": "Geometric patterns, bold colors, and luxurious materials in a symmetrical style",
+ "icon": "๐ข",
+ "examples": "Works from the 1920s-30s, Chrysler Building, Tamara de Lempicka paintings",
+ "technical_terms": ["art deco", "geometric", "luxurious", "symmetrical"]
+ },
+ "Baroque": {
+ "description": "Dramatic, ornate style with rich details, contrast, and dynamic composition",
+ "icon": "๐",
+ "examples": "Caravaggio, Rembrandt, Peter Paul Rubens",
+ "technical_terms": ["baroque", "chiaroscuro", "ornate", "dramatic lighting"]
+ },
+ "Ukiyo-e": {
+ "description": "Traditional Japanese woodblock print style with flat areas of color and strong outlines",
+ "icon": "๐",
+ "examples": "Hokusai's Great Wave, Hiroshige's landscapes, traditional Japanese prints",
+ "technical_terms": ["ukiyo-e", "woodblock print", "japanese", "flat color"]
+ },
+ "Comic Book": {
+ "description": "Bold outlines, bright colors, and action-oriented composition like classic comics",
+ "icon": "๐ฅ",
+ "examples": "Jack Kirby, Steve Ditko, modern Marvel/DC art styles",
+ "technical_terms": ["comic art", "bold outlines", "dynamic", "halftone"]
+ },
+ "Psychedelic": {
+ "description": "Vibrant, swirling colors with abstract patterns inspired by 1960s art",
+ "icon": "๐",
+ "examples": "1960s concert posters, Peter Max, Alex Grey",
+ "technical_terms": ["psychedelic", "vibrant", "swirling", "fractal patterns"]
+ },
+ "Vaporwave": {
+ "description": "Glitch aesthetics with pastel colors, 80s/90s nostalgia and digital elements",
+ "icon": "๐ผ",
+ "examples": "Retrowave aesthetics, 80s digital graphics, glitch art",
+ "technical_terms": ["vaporwave", "retrowave", "glitch", "pastel"]
+ },
+ "Studio Ghibli": {
+ "description": "Whimsical, detailed animation style inspired by Japanese animated films",
+ "icon": "๐",
+ "examples": "Spirited Away, My Neighbor Totoro, Howl's Moving Castle",
+ "technical_terms": ["anime", "ghibli", "miyazaki", "hand-drawn animation"]
+ },
+ "Hyperrealism": {
+ "description": "Extremely detailed realism that exceeds photograph-like precision",
+ "icon": "๐",
+ "examples": "Works by Roberto Bernardi, Denis Peterson, Gottfried Helnwein",
+ "technical_terms": ["hyperrealism", "photorealistic", "extreme detail", "precision"]
+ },
+ "Stained Glass": {
+ "description": "Design resembling stained glass with defined segments and vivid colors",
+ "icon": "๐ช",
+ "examples": "Medieval cathedral windows, Tiffany lamps, modern stained glass art",
+ "technical_terms": ["stained glass", "leaded", "translucent", "sectioned"]
+ },
+ "Synthwave": {
+ "description": "80s-inspired futuristic style with neon grids, sunset gradients and retro elements",
+ "icon": "๐",
+ "examples": "Retrowave artwork, 80s sci-fi aesthetics, digital sunset landscapes",
+ "technical_terms": ["synthwave", "retrowave", "neon", "grid", "80s futurism"]
+ },
+ "Low Poly": {
+ "description": "3D style with visible polygonal faces and geometric simplification",
+ "icon": "๐",
+ "examples": "Low polygon 3D models, indie game art, geometric illustrations",
+ "technical_terms": ["low poly", "polygonal", "faceted", "geometric"]
+ },
+ "Watercolor Sketch": {
+ "description": "Combination of line drawing with soft watercolor washes and bleeding colors",
+ "icon": "๐จ",
+ "examples": "Urban sketchers, travel journals, architectural sketches with color",
+ "technical_terms": ["urban sketch", "line and wash", "watercolor sketch", "loose style"]
+ },
+ "Digital Painting": {
+ "description": "Digital art that imitates traditional painting techniques with modern tools",
+ "icon": "๐ฑ๏ธ",
+ "examples": "Contemporary digital artists, concept art, digital illustrations",
+ "technical_terms": ["digital painting", "brushwork", "textured", "painterly digital"]
+ },
+ "Cel Shaded": {
+ "description": "Comic or animation style with flat colors and defined outlines",
+ "icon": "๐ญ",
+ "examples": "Western animation, comic books, video game cel-shading",
+ "technical_terms": ["cel shading", "toon shading", "flat color", "outlined"]
+ },
+ "Cinematic": {
+ "description": "Movie-like composition with dramatic lighting and film color grading",
+ "icon": "๐๏ธ",
+ "examples": "Film stills, cinematography, movie posters",
+ "technical_terms": ["cinematic", "film grain", "color grading", "composition"]
+ },
+ "Renaissance": {
+ "description": "Classical style with realistic proportions, perspective and religious themes",
+ "icon": "๐๏ธ",
+ "examples": "Leonardo da Vinci, Michelangelo, Raphael",
+ "technical_terms": ["renaissance", "classical", "sfumato", "perspective"]
+ },
+ "Folk Art": {
+ "description": "Traditional decorative style with cultural motifs and simplified forms",
+ "icon": "๐งถ",
+ "examples": "Various cultural folk art traditions, primitive art",
+ "technical_terms": ["folk art", "naive art", "traditional", "decorative"]
+ },
+ "Pointillism": {
+ "description": "Style using small distinct dots of color to form images",
+ "icon": "๐",
+ "examples": "Georges Seurat, Paul Signac",
+ "technical_terms": ["pointillism", "stippling", "dot technique", "optical mixing"]
+ },
+ "Bauhaus": {
+ "description": "Modernist style with geometric shapes, primary colors and functional design",
+ "icon": "๐ท",
+ "examples": "Wassily Kandinsky's Bauhaus works, Josef Albers, Lรกszlรณ Moholy-Nagy",
+ "technical_terms": ["bauhaus", "modernist", "functional", "geometric"]
+ }
+}
+
+# Moods with icons and descriptions
+MOODS = {
+ "Happy": {
+ "description": "Bright, cheerful atmosphere with warm colors",
+ "icon": "๐",
+ "color_palette": "Warm and vibrant colors: yellows, bright oranges, light blues",
+ "lighting": "Bright, open lighting with minimal shadows"
+ },
+ "Sad": {
+ "description": "Melancholic atmosphere with muted colors",
+ "icon": "๐ข",
+ "color_palette": "Muted blues, grays, desaturated colors, cool tones",
+ "lighting": "Soft, subdued lighting with long shadows"
+ },
+ "Mysterious": {
+ "description": "Enigmatic atmosphere with shadows and hidden elements",
+ "icon": "๐ฎ",
+ "color_palette": "Deep purples, dark blues, hints of teal, selective lighting",
+ "lighting": "Dramatic, directional lighting with strong shadows"
+ },
+ "Peaceful": {
+ "description": "Serene, calm atmosphere with gentle lighting",
+ "icon": "๐๏ธ",
+ "color_palette": "Soft blues, gentle greens, pastel colors, balanced light",
+ "lighting": "Soft, diffused lighting with minimal contrast"
+ },
+ "Tense": {
+ "description": "Suspenseful atmosphere with dramatic lighting",
+ "icon": "๐ฐ",
+ "color_palette": "High contrast, dark shadows, selective reds, strong highlights",
+ "lighting": "Harsh, directional lighting with strong shadows"
+ },
+ "Whimsical": {
+ "description": "Playful, imaginative atmosphere with fanciful elements",
+ "icon": "๐ฆ",
+ "color_palette": "Pastels, candy colors, unexpected color combinations",
+ "lighting": "Magical, glowing lighting with soft sparkles"
+ },
+ "Dark": {
+ "description": "Gloomy atmosphere with deep shadows and low lighting",
+ "icon": "๐",
+ "color_palette": "Dark blues, blacks, deep greens, minimal highlights",
+ "lighting": "Low-key lighting with strong shadows and minimal highlights"
+ },
+ "Energetic": {
+ "description": "Dynamic, vibrant atmosphere with strong colors and movement",
+ "icon": "โก",
+ "color_palette": "Saturated primary colors, bold contrasts, vibrant hues",
+ "lighting": "Bold, dynamic lighting with sharp contrasts"
+ },
+ "Romantic": {
+ "description": "Soft, dreamy atmosphere with warm, gentle lighting",
+ "icon": "โค๏ธ",
+ "color_palette": "Soft pinks, gentle reds, golden highlights, warm tones",
+ "lighting": "Warm, soft lighting with a gentle glow"
+ },
+ "Epic": {
+ "description": "Grand, impressive atmosphere with dramatic scale and lighting",
+ "icon": "๐๏ธ",
+ "color_palette": "Bold colors, dramatic contrast, atmospheric lighting, expansive scale",
+ "lighting": "Dramatic, cinematic lighting with god rays and atmosphere"
+ },
+ "Ethereal": {
+ "description": "Delicate, otherworldly atmosphere with a sense of transcendence",
+ "icon": "โจ",
+ "color_palette": "Soft whites, pale blues, gentle pastels, luminous tones",
+ "lighting": "Glowing, diffused lighting with light particles"
+ },
+ "Nostalgic": {
+ "description": "Warm, reminiscent atmosphere evoking memories and sentiment",
+ "icon": "๐ฐ๏ธ",
+ "color_palette": "Warm ambers, faded colors, slightly desaturated tones",
+ "lighting": "Golden hour lighting or vintage film effects"
+ },
+ "Dramatic": {
+ "description": "Bold, emotionally charged atmosphere with strong contrasts",
+ "icon": "๐ญ",
+ "color_palette": "Strong contrasts, selective color highlights, bold tones",
+ "lighting": "Strong directional lighting with pronounced shadows"
+ },
+ "Serene": {
+ "description": "Tranquil, meditative atmosphere with balanced elements",
+ "icon": "๐ง",
+ "color_palette": "Balanced harmonious colors, natural tones, soft transitions",
+ "lighting": "Even, gentle lighting with subtle gradients"
+ },
+ "Chaotic": {
+ "description": "Disorderly, energetic atmosphere with clashing elements",
+ "icon": "๐ฅ",
+ "color_palette": "Clashing colors, high contrast, unpredictable combinations",
+ "lighting": "Uneven, conflicting lighting from multiple sources"
+ },
+ "Futuristic": {
+ "description": "Advanced technological atmosphere with sleek, modern elements",
+ "icon": "๐",
+ "color_palette": "Blues, cyans, whites, metallics, with accent neons",
+ "lighting": "High-tech lighting, glows, volumetric beams"
+ },
+ "Organic": {
+ "description": "Natural, living atmosphere with biological elements and growth",
+ "icon": "๐ฑ",
+ "color_palette": "Natural greens, browns, living colors, earthy tones",
+ "lighting": "Soft, natural lighting with dappled shadows and highlights"
+ },
+ "Magical": {
+ "description": "Enchanted atmosphere with mystical elements and wonder",
+ "icon": "โจ",
+ "color_palette": "Glowing purples, blues, with golden accents and sparkles",
+ "lighting": "Magical glow, light particles, soft radiance from magical elements"
+ },
+ "Cozy": {
+ "description": "Warm, comfortable atmosphere that evokes feelings of safety and comfort",
+ "icon": "๐ก",
+ "color_palette": "Warm oranges, browns, reds, soft yellows, comforting tones",
+ "lighting": "Warm, soft lighting like firelight or sunset glow"
+ },
+ "Elegant": {
+ "description": "Refined, sophisticated atmosphere with tasteful elements",
+ "icon": "๐",
+ "color_palette": "Subdued luxurious colors, golds, deep blues, soft whites",
+ "lighting": "Carefully balanced, flattering lighting with subtle highlights"
+ }
+}
+
+# New aspect ratios with descriptions and use cases
+ASPECT_RATIOS = {
+ "1:1 (Square)": {
+ "dimensions": (512, 512),
+ "description": "Perfect square format",
+ "use_cases": "Social media posts, profile pictures, album covers",
+ "icon": "โฌ"
+ },
+ "4:3 (Classic)": {
+ "dimensions": (576, 432),
+ "description": "Traditional TV and monitor ratio",
+ "use_cases": "Desktop wallpapers, presentations, classic photo prints",
+ "icon": "๐บ"
+ },
+ "3:2 (Photo)": {
+ "dimensions": (576, 384),
+ "description": "Traditional photography ratio",
+ "use_cases": "Professional photography, prints, postcards",
+ "icon": "๐ท"
+ },
+ "16:9 (Widescreen)": {
+ "dimensions": (576, 324),
+ "description": "Modern video and display format",
+ "use_cases": "Widescreen wallpapers, video thumbnails, presentations",
+ "icon": "๐ฅ๏ธ"
+ },
+ "2:1 (Panoramic)": {
+ "dimensions": (640, 320),
+ "description": "Wide panoramic format",
+ "use_cases": "Landscape photography, banner images, wide displays",
+ "icon": "๐"
+ },
+ "9:16 (Portrait)": {
+ "dimensions": (384, 680),
+ "description": "Vertical mobile format",
+ "use_cases": "Mobile wallpapers, stories, vertical videos",
+ "icon": "๐ฑ"
+ },
+ "3:4 (Portrait)": {
+ "dimensions": (384, 512),
+ "description": "Traditional portrait ratio",
+ "use_cases": "Portraits, book covers, magazine covers",
+ "icon": "๐"
+ },
+ "2:3 (Portrait Photo)": {
+ "dimensions": (384, 576),
+ "description": "Portrait photography ratio",
+ "use_cases": "Portrait prints, phone wallpapers, book covers",
+ "icon": "๐ฑ"
+ },
+ "21:9 (Ultrawide)": {
+ "dimensions": (640, 272),
+ "description": "Ultra-widescreen cinematic format",
+ "use_cases": "Cinematic scenes, movie stills, ultrawide displays",
+ "icon": "๐ฌ"
+ }
+}
+
+# Color palettes with descriptions and sample colors
+COLOR_PALETTES = {
+ "Vibrant": {
+ "description": "Bold, energetic colors with high saturation",
+ "colors": ["#FF4500", "#FFD700", "#00FF7F", "#1E90FF", "#FF1493"],
+ "icon": "๐",
+ "mood_association": ["Happy", "Energetic", "Whimsical"]
+ },
+ "Pastel": {
+ "description": "Soft, light colors with low saturation",
+ "colors": ["#FFB6C1", "#E6E6FA", "#B0E0E6", "#FFDAB9", "#98FB98"],
+ "icon": "๐ญ",
+ "mood_association": ["Peaceful", "Whimsical", "Romantic"]
+ },
+ "Monochrome": {
+ "description": "Various shades and tones of a single color",
+ "colors": ["#000000", "#333333", "#666666", "#999999", "#CCCCCC"],
+ "icon": "โซ",
+ "mood_association": ["Elegant", "Mysterious", "Dramatic"]
+ },
+ "Earthy": {
+ "description": "Natural tones inspired by nature and earth",
+ "colors": ["#8B4513", "#A0522D", "#556B2F", "#BDB76B", "#F5DEB3"],
+ "icon": "๐",
+ "mood_association": ["Peaceful", "Organic", "Serene"]
+ },
+ "Neon": {
+ "description": "Bright, glowing colors with high intensity",
+ "colors": ["#FF00FF", "#00FFFF", "#FF9900", "#39FF14", "#FE01B1"],
+ "icon": "๐ซ",
+ "mood_association": ["Energetic", "Futuristic", "Chaotic"]
+ },
+ "Vintage": {
+ "description": "Faded, nostalgic colors with a retro feel",
+ "colors": ["#D3A588", "#C3B091", "#8F9E8B", "#535657", "#A87C6D"],
+ "icon": "๐ฐ๏ธ",
+ "mood_association": ["Nostalgic", "Romantic", "Cozy"]
+ },
+ "Cool": {
+ "description": "Blues, greens, and purples with a calm feel",
+ "colors": ["#4682B4", "#5F9EA0", "#6495ED", "#483D8B", "#008080"],
+ "icon": "โ๏ธ",
+ "mood_association": ["Peaceful", "Mysterious", "Serene"]
+ },
+ "Warm": {
+ "description": "Reds, oranges, and yellows with a warm feel",
+ "colors": ["#CD5C5C", "#F08080", "#FA8072", "#FFA07A", "#FFDAB9"],
+ "icon": "๐ฅ",
+ "mood_association": ["Happy", "Energetic", "Romantic"]
+ },
+ "Cyberpunk": {
+ "description": "Neon colors against dark backgrounds with high contrast",
+ "colors": ["#FF00FF", "#00FFFF", "#101010", "#FE9A00", "#121634"],
+ "icon": "๐พ",
+ "mood_association": ["Futuristic", "Mysterious", "Dramatic"]
+ },
+ "Minimal": {
+ "description": "Limited color palette with blacks, whites, and accents",
+ "colors": ["#FFFFFF", "#000000", "#DDDDDD", "#333333", "#FF3366"],
+ "icon": "๐ฒ",
+ "mood_association": ["Elegant", "Dramatic", "Serene"]
+ }
+}
+
+# Special effects with descriptions and parameters
+SPECIAL_EFFECTS = {
+ "None": {
+ "description": "No special effects applied",
+ "icon": "โญ",
+ "intensity_range": (0, 0),
+ "default_intensity": 0
+ },
+ "Glow": {
+ "description": "Adds a subtle luminous glow to bright areas",
+ "icon": "โจ",
+ "intensity_range": (0.1, 1.0),
+ "default_intensity": 0.5
+ },
+ "Film Grain": {
+ "description": "Adds a textured grain similar to analog film",
+ "icon": "๐๏ธ",
+ "intensity_range": (0.1, 1.0),
+ "default_intensity": 0.3
+ },
+ "Vignette": {
+ "description": "Darkens the edges of the image for focus",
+ "icon": "โซ",
+ "intensity_range": (0.1, 0.8),
+ "default_intensity": 0.4
+ },
+ "Chromatic Aberration": {
+ "description": "Creates subtle color fringing on edges",
+ "icon": "๐",
+ "intensity_range": (0.1, 0.5),
+ "default_intensity": 0.2
+ },
+ "Retro Wave": {
+ "description": "80s style grid and neon effects",
+ "icon": "๐บ",
+ "intensity_range": (0.2, 1.0),
+ "default_intensity": 0.7
+ },
+ "Dream Blur": {
+ "description": "Soft gaussian blur with glow for dreamy effect",
+ "icon": "๐ญ",
+ "intensity_range": (0.1, 0.6),
+ "default_intensity": 0.3
+ },
+ "Duotone": {
+ "description": "Two-color overlay effect",
+ "icon": "๐",
+ "intensity_range": (0.3, 1.0),
+ "default_intensity": 0.7
+ },
+ "Halftone": {
+ "description": "Comic-style dots pattern effect",
+ "icon": "๐พ",
+ "intensity_range": (0.2, 0.8),
+ "default_intensity": 0.5
+ },
+ "Noir": {
+ "description": "High contrast black and white with film noir look",
+ "icon": "๐ต๏ธ",
+ "intensity_range": (0.4, 1.0),
+ "default_intensity": 0.7
+ }
+}
+
+# Example prompts with rich metadata and additional parameters
+EXAMPLE_PROMPTS = [
+ {
+ "text": "A serene lake at sunset with mountains in the background and a small wooden boat floating nearby",
+ "thumbnail_desc": "Peaceful lake scene at sunset",
+ "creation_type": "Realistic Photo",
+ "art_style": "Photorealistic",
+ "mood": "Peaceful",
+ "aspect_ratio": "16:9 (Widescreen)",
+ "color_palette": "Warm",
+ "special_effect": "None",
+ "tags": ["nature", "landscape", "water", "sunset"]
+ },
+ {
+ "text": "A futuristic cityscape with flying cars, neon lights, and tall skyscrapers under a night sky with two moons",
+ "thumbnail_desc": "Futuristic city with flying cars",
+ "creation_type": "Concept Art",
+ "art_style": "Cyberpunk",
+ "mood": "Mysterious",
+ "aspect_ratio": "16:9 (Widescreen)",
+ "color_palette": "Cyberpunk",
+ "special_effect": "Glow",
+ "tags": ["scifi", "future", "urban", "night"]
+ },
+ {
+ "text": "A close-up portrait of an elderly craftsman with weathered hands working on an intricate wooden carving in his workshop",
+ "thumbnail_desc": "Elderly craftsman working with wood",
+ "creation_type": "Oil Painting",
+ "art_style": "Hyperrealism",
+ "mood": "Peaceful",
+ "aspect_ratio": "3:4 (Portrait)",
+ "color_palette": "Earthy",
+ "special_effect": "Vignette",
+ "tags": ["portrait", "craftsmanship", "elderly", "detail"]
+ },
+ {
+ "text": "A magical forest with glowing mushrooms, fairy lights, and a small cottage with smoke coming from the chimney",
+ "thumbnail_desc": "Magical forest with glowing elements",
+ "creation_type": "Fantasy Illustration",
+ "art_style": "Studio Ghibli",
+ "mood": "Whimsical",
+ "aspect_ratio": "3:2 (Photo)",
+ "color_palette": "Vibrant",
+ "special_effect": "Glow",
+ "tags": ["fantasy", "forest", "magic", "cottage"]
+ },
+ {
+ "text": "A cybernetic samurai with glowing blue circuits standing in a rainy Tokyo street at night",
+ "thumbnail_desc": "Cybernetic samurai in rainy Tokyo",
+ "creation_type": "Digital Art",
+ "art_style": "Cyberpunk",
+ "mood": "Dark",
+ "aspect_ratio": "2:3 (Portrait Photo)",
+ "color_palette": "Cyberpunk",
+ "special_effect": "Chromatic Aberration",
+ "tags": ["character", "cyberpunk", "samurai", "rain"]
+ },
+ {
+ "text": "A cute cat with dragon wings and tiny horns sleeping on a pile of gold coins",
+ "thumbnail_desc": "Cat with dragon features on gold",
+ "creation_type": "Fantasy Illustration",
+ "art_style": "Comic Book",
+ "mood": "Whimsical",
+ "aspect_ratio": "1:1 (Square)",
+ "color_palette": "Vibrant",
+ "special_effect": "None",
+ "tags": ["creature", "fantasy", "cute", "treasure"]
+ },
+ {
+ "text": "An ancient temple covered in vines and moss, partially sunken into a crystal-clear cenote in the jungle",
+ "thumbnail_desc": "Ancient temple in jungle cenote",
+ "creation_type": "Concept Art",
+ "art_style": "Photorealistic",
+ "mood": "Mysterious",
+ "aspect_ratio": "16:9 (Widescreen)",
+ "color_palette": "Earthy",
+ "special_effect": "Vignette",
+ "tags": ["architecture", "ruins", "jungle", "water"]
+ },
+ {
+ "text": "A cozy coffee shop interior with rain falling outside the windows, soft lighting, and a few people reading books",
+ "thumbnail_desc": "Cozy rainy day coffee shop",
+ "creation_type": "Digital Art",
+ "art_style": "Impressionist",
+ "mood": "Cozy",
+ "aspect_ratio": "3:2 (Photo)",
+ "color_palette": "Warm",
+ "special_effect": "Dream Blur",
+ "tags": ["interior", "rainy", "cozy", "urban"]
+ },
+ {
+ "text": "An astronaut standing on a distant planet with multiple ringed moons in the sky and alien vegetation around",
+ "thumbnail_desc": "Astronaut on alien planet",
+ "creation_type": "Cinematic Scene",
+ "art_style": "Photorealistic",
+ "mood": "Epic",
+ "aspect_ratio": "21:9 (Ultrawide)",
+ "color_palette": "Cool",
+ "special_effect": "Film Grain",
+ "tags": ["scifi", "space", "exploration", "alien"]
+ },
+ {
+ "text": "A stylish cyberpunk street fashion model with neon accessories and holographic clothing in a futuristic marketplace",
+ "thumbnail_desc": "Cyberpunk street fashion",
+ "creation_type": "Character Portrait",
+ "art_style": "Cyberpunk",
+ "mood": "Futuristic",
+ "aspect_ratio": "2:3 (Portrait Photo)",
+ "color_palette": "Neon",
+ "special_effect": "Chromatic Aberration",
+ "tags": ["fashion", "character", "cyberpunk", "future"]
+ },
+ {
+ "text": "An underwater city with bioluminescent buildings and mermaid-like inhabitants swimming between coral structures",
+ "thumbnail_desc": "Bioluminescent underwater city",
+ "creation_type": "Fantasy Illustration",
+ "art_style": "Digital Painting",
+ "mood": "Mysterious",
+ "aspect_ratio": "16:9 (Widescreen)",
+ "color_palette": "Cool",
+ "special_effect": "Glow",
+ "tags": ["underwater", "fantasy", "city", "merfolk"]
+ },
+ {
+ "text": "A tranquil Japanese zen garden with carefully raked sand, moss-covered stones, and maple trees in autumn colors",
+ "thumbnail_desc": "Japanese zen garden in autumn",
+ "creation_type": "Realistic Photo",
+ "art_style": "Photorealistic",
+ "mood": "Peaceful",
+ "aspect_ratio": "3:2 (Photo)",
+ "color_palette": "Earthy",
+ "special_effect": "None",
+ "tags": ["garden", "japanese", "zen", "autumn"]
+ },
+ {
+ "text": "A steampunk airship battle among the clouds with brass and copper vessels exchanging cannon fire",
+ "thumbnail_desc": "Steampunk airship battle",
+ "creation_type": "Concept Art",
+ "art_style": "Steampunk",
+ "mood": "Dramatic",
+ "aspect_ratio": "16:9 (Widescreen)",
+ "color_palette": "Vintage",
+ "special_effect": "Film Grain",
+ "tags": ["steampunk", "battle", "airship", "clouds"]
+ }
+]
+
+# =============== HELPER FUNCTIONS ===============
+
+# Format dropdown choices with icons for better UI
+def format_dropdown_choices(options_dict):
+ """Format dropdown choices with icons for better UI"""
+ return [f"{options_dict[key].get('icon', 'โข')} {key}" for key in options_dict.keys()]
+
+# Extract the key from a formatted choice with icon
+def extract_key(formatted_choice):
+ """Extract the key from a formatted choice with icon"""
+ if not formatted_choice:
+ return None
+ # Skip the icon and space at the beginning
+ parts = formatted_choice.split(' ', 1)
+ if len(parts) > 1:
+ return parts[1]
+ return formatted_choice
+
+# Function to load example prompt
+def load_example(example_index):
+ """Load a pre-defined example prompt"""
+ if example_index < 0 or example_index >= len(EXAMPLE_PROMPTS):
+ return "", "", "", "", "", "", ""
+
+ example = EXAMPLE_PROMPTS[example_index]
+ creation = f"{CREATION_TYPES[example['creation_type']]['icon']} {example['creation_type']}"
+ art = f"{ART_STYLES[example['art_style']]['icon']} {example['art_style']}"
+ mood = f"{MOODS[example['mood']]['icon']} {example['mood']}"
+ aspect = f"{ASPECT_RATIOS[example['aspect_ratio']]['icon']} {example['aspect_ratio']}"
+ color_palette = f"{COLOR_PALETTES[example['color_palette']]['icon']} {example['color_palette']}"
+ special_effect = f"{SPECIAL_EFFECTS[example['special_effect']]['icon']} {example['special_effect']}"
+
+ return example["text"], creation, art, mood, aspect, color_palette, special_effect
+
+# Get model key from formatted display name
+def get_model_key_from_display_name(formatted_name):
+ """Get the model ID from its formatted display name"""
+ if not formatted_name:
+ return list(IMAGE_MODELS.keys())[0] # Default to first model
+
+ # Extract display name without the icon
+ display_name = extract_key(formatted_name)
+
+ # Find the corresponding key by display name
+ for key, info in IMAGE_MODELS.items():
+ if info['display_name'] == display_name:
+ return key
+
+ # Default to first model if not found
+ return list(IMAGE_MODELS.keys())[0]
+
+# Helper function to update character count with color coding
+def update_char_count(text):
+ """Update character count with color coding based on length"""
+ count = len(text) if text else 0
+ if count == 0:
+ color_class = ""
+ advice = "Start typing to create your prompt"
+ elif count < 20:
+ color_class = "warning"
+ advice = "Your prompt is too short. Add more details for better results"
+ elif count > 300:
+ color_class = "info"
+ advice = "Great level of detail! You can still add more if needed"
+ elif count > 500:
+ color_class = "warning"
+ advice = "Your prompt is getting very long. Consider focusing on key details"
+ elif count > 700:
+ color_class = "error"
+ advice = "Very long prompts may be truncated. Try to be more concise"
+ else:
+ color_class = "success"
+ advice = "Good prompt length with useful details"
+
+ return f"""
+ {count} characters
+ {advice}
+
"""
+
+# Helper function to update creation type info
+def update_creation_info(choice):
+ """Update creation type info display based on selection"""
+ key = extract_key(choice)
+ if not key or key not in CREATION_TYPES:
+ return ""
+
+ info = CREATION_TYPES[key]
+ examples = ", ".join([f"{ex}" for ex in info.get('examples', [])])
+
+ recommended_models = ""
+ if 'recommended_models' in info:
+ model_list = [f"{model}" for model in info['recommended_models']]
+ recommended_models = f"""
+
+
โ Recommended models: {", ".join(model_list)}
+
+ """
+
+ return f"""
+
{info['icon']} {key}
+
{info['description']}
+
๐ก {info['prompt_hint']}
+
+ {recommended_models}
+
"""
+
+# Helper function to update art style info
+def update_art_style_info(choice):
+ """Update art style info display based on selection"""
+ key = extract_key(choice)
+ if not key or key not in ART_STYLES:
+ return ""
+
+ info = ART_STYLES[key]
+ technical_terms = ""
+ if 'technical_terms' in info:
+ terms = ", ".join([f"{term}" for term in info['technical_terms']])
+ technical_terms = f"""
+
+ """
+
+ return f"""
+
{info['icon']} {key}
+
{info['description']}
+
Inspired by: {info['examples']}
+ {technical_terms}
+
"""
+
+# Helper function to update model info
+def update_model_info(formatted_choice):
+ """Update model info display based on selection"""
+ if not formatted_choice:
+ return ""
+
+ # Extract display name without the icon
+ display_name = extract_key(formatted_choice)
+
+ # Find the corresponding key and info
+ for key, info in IMAGE_MODELS.items():
+ if info['display_name'] == display_name:
+ # Create speed badge
+ speed_badge = ""
+ if info.get('speed') == 'very fast':
+ speed_badge = 'Ultra Fast'
+ elif info.get('speed') == 'fast':
+ speed_badge = 'Fast'
+ elif info.get('speed') == 'medium':
+ speed_badge = 'Medium'
+ elif info.get('speed') == 'slow':
+ speed_badge = 'Slower'
+
+ # Create quality badge
+ quality_badge = ""
+ if info.get('quality') == 'premium':
+ quality_badge = 'Premium Quality'
+ elif info.get('quality') == 'excellent':
+ quality_badge = 'Excellent Quality'
+ elif info.get('quality') == 'very good':
+ quality_badge = 'Very Good Quality'
+ elif info.get('quality') == 'good':
+ quality_badge = 'Good Quality'
+ elif info.get('quality') == 'specialized':
+ quality_badge = 'Specialized'
+ elif info.get('quality') == 'artistic':
+ quality_badge = 'Artistic'
+
+ # Create recommended for badges
+ recommended_badges = ""
+ if 'recommended_for' in info:
+ badges = " ".join([f'{item}' for item in info['recommended_for'][:3]])
+ recommended_badges = f""""""
+
+ # Create strengths and weaknesses section
+ strengths_weaknesses = ""
+ if 'strengths' in info and 'weaknesses' in info:
+ strengths_weaknesses = f"""
+
โ Strengths: {info['strengths']}
+
โ Limitations: {info['weaknesses']}
+
"""
+
+ return f"""
+
{info['icon']} {info['display_name']} {speed_badge} {quality_badge}
+
{info['description']}
+ {strengths_weaknesses}
+ {recommended_badges}
+
{key}
+
"""
+
+ return ""
+
+# Helper function to update mood info
+def update_mood_info(choice):
+ """Update mood info display based on selection"""
+ key = extract_key(choice)
+ if not key or key not in MOODS:
+ return ""
+
+ info = MOODS[key]
+
+ return f"""
+
{info['icon']} {key}
+
{info['description']}
+
๐จ {info['color_palette']}
+
๐ก {info['lighting']}
+
"""
+
+# Helper function to update status message with styling
+def update_status(message, is_error=False, is_warning=False, is_info=False):
+ """Update status message with appropriate styling"""
+ if is_error:
+ status_class = "status-error"
+ icon = "โ"
+ elif is_warning:
+ status_class = "status-warning"
+ icon = "โ ๏ธ"
+ elif is_info:
+ status_class = "status-info"
+ icon = "โน๏ธ"
+ else:
+ status_class = "status-success"
+ icon = "โ
"
+
+ return f"""
+ {icon}
+ {message}
+
"""
+
+# Helper function to format parameters display as pills
+def format_parameters(creation_type_val, art_style_val, mood_val, aspect_ratio_val, color_palette_val, special_effect_val, model_name):
+ """Format generation parameters as readable badges/pills"""
+ creation_key = extract_key(creation_type_val)
+ art_key = extract_key(art_style_val)
+ mood_key = extract_key(mood_val)
+ aspect_key = extract_key(aspect_ratio_val)
+ palette_key = extract_key(color_palette_val)
+ effect_key = extract_key(special_effect_val)
+
+ # Get model info
+ model_display_name = "Unknown Model"
+ model_id = ""
+ model_icon = "๐ค"
+ for key, info in IMAGE_MODELS.items():
+ if info['display_name'] == extract_key(model_name):
+ model_display_name = info['display_name']
+ model_id = key
+ model_icon = info['icon']
+ break
+
+ html = """
+
Generated with these parameters:
+
"""
+
+ # Add creation type pill
+ if creation_key and creation_key in CREATION_TYPES:
+ html += f"""
+ {CREATION_TYPES[creation_key]['icon']}
+ {creation_key}
+
"""
+
+ # Add art style pill
+ if art_key and art_key in ART_STYLES:
+ html += f"""
+ {ART_STYLES[art_key]['icon']}
+ {art_key}
+
"""
+
+ # Add mood pill
+ if mood_key and mood_key in MOODS:
+ html += f"""
+ {MOODS[mood_key]['icon']}
+ {mood_key}
+
"""
+
+ # Add aspect ratio pill
+ if aspect_key and aspect_key in ASPECT_RATIOS:
+ html += f"""
+ {ASPECT_RATIOS[aspect_key]['icon']}
+ {aspect_key}
+
"""
+
+ # Add color palette pill
+ if palette_key and palette_key in COLOR_PALETTES:
+ html += f"""
+ {COLOR_PALETTES[palette_key]['icon']}
+ {palette_key}
+
"""
+
+ # Add special effect pill
+ if effect_key and effect_key in SPECIAL_EFFECTS and effect_key != "None":
+ html += f"""
+ {SPECIAL_EFFECTS[effect_key]['icon']}
+ {effect_key}
+
"""
+
+ # Add model pill
+ html += f"""
+ {model_icon}
+ {model_display_name}
+
"""
+
+ # Close container
+ html += """
+
+ Generated on {timestamp}
+
+
""".replace("{timestamp}", datetime.now().strftime("%Y-%m-%d at %H:%M:%S"))
+
+ return html
+
+# Helper function to cache generation results
+# Helper function to cache generation results
+def get_cache_key(description, model_key, creation_type, art_style, mood, aspect_ratio, color_palette, special_effect):
+ """Generate a unique cache key for generation parameters"""
+ # Create a string representation of all parameters
+ param_string = f"{description}|{model_key}|{creation_type}|{art_style}|{mood}|{aspect_ratio}|{color_palette}|{special_effect}"
+ # Create a hash of the parameters for a shorter key
+ return hashlib.md5(param_string.encode('utf-8')).hexdigest()
+
+# Create a simple cache for generated images
+IMAGE_CACHE = {}
+def cache_image(key, image, enhanced_prompt, parameters_html, seed=None):
+ """Cache an image with its metadata"""
+ if not APP_CONFIG["cache_enabled"]:
+ return
+
+ # Limit cache size
+ if len(IMAGE_CACHE) > 50: # Limit to last 50 images
+ # Remove oldest entry
+ oldest_key = next(iter(IMAGE_CACHE))
+ IMAGE_CACHE.pop(oldest_key)
+
+ # Save to cache
+ IMAGE_CACHE[key] = {
+ "image": image,
+ "enhanced_prompt": enhanced_prompt,
+ "parameters_html": parameters_html,
+ "timestamp": datetime.now().isoformat(),
+ "seed": seed
+ }
+
+def get_cached_image(key):
+ """Get an image from cache if it exists"""
+ if not APP_CONFIG["cache_enabled"] or key not in IMAGE_CACHE:
+ return None
+ return IMAGE_CACHE[key]
+
+# =============== PROMPT ENHANCEMENT LOGIC ===============
+
+# Function to enhance prompt with AI model
+def enhance_prompt_with_ai(user_input, creation_type, art_style, mood, color_palette=None, special_effect=None):
+ """
+ Enhance user input with AI model to create detailed image generation prompts
+ Args:
+ user_input (str): User's original description
+ creation_type (str): Selected creation type (e.g., "Digital Art")
+ art_style (str): Selected art style (e.g., "Photorealistic")
+ mood (str): Selected mood (e.g., "Peaceful")
+ color_palette (str): Selected color palette (e.g., "Vibrant")
+ special_effect (str): Selected special effect (e.g., "Glow")
+ Returns:
+ str: Enhanced prompt optimized for image generation
+ """
+ try:
+ if not use_ai_enhancer or enhancer_client is None:
+ logger.warning("AI enhancer not available, using fallback")
+ return enhance_prompt_fallback(user_input, creation_type, art_style, mood, color_palette, special_effect)
+
+ logger.info(f"Enhancing prompt with AI for: {creation_type}, {art_style}, {mood}")
+
+ # Enhanced system prompt with detailed instructions
+ system_prompt = """You are a world-class prompt engineer who specializes in creating detailed, effective prompts for text-to-image AI models.
+Your task is to transform a user's description into a comprehensive, detailed image generation prompt that will create stunning visuals. Consider all the provided elements and combine them into a cohesive, detailed prompt.
+MOST IMPORTANTLY - ADD LOGICAL DETAILS:
+- Analyze what the user wants and add logical details that would make the scene realistic or coherent
+- Think about environment, lighting, perspective, time of day, weather, and other contextual elements
+- Create a vivid, imaginable scene with spatial relationships clearly defined
+PROMPT STRUCTURE GUIDELINES:
+1. Start with the core subject and its primary characteristics
+2. Add environment and setting details
+3. Describe lighting, atmosphere, and mood
+4. Include specific visual style and artistic technique references
+5. Add technical quality terms (8K, detailed, masterful, etc.)
+FORMAT YOUR RESPONSE AS A SINGLE PARAGRAPH with no additional comments, explanations, or bullet points. Use natural language without awkward comma separations. Aim for 75-175 words.
+AVOID:
+- Do not include quotation marks in your response
+- Do not preface with "here's a prompt" or similar text
+- Do not use placeholders
+- Do not add negative prompts
+- Do not write in list format or use bullet points
+Respond only with the enhanced prompt and nothing else."""
+
+ # Get creation type description
+ creation_info = CREATION_TYPES.get(creation_type, {"description": "Create a detailed image", "icon": "๐จ"})
+ creation_description = creation_info["description"]
+
+ # Get art style description
+ style_info = ART_STYLES.get(art_style, {"description": "with detailed and professional quality", "icon": "๐๏ธ"})
+ style_description = style_info["description"]
+ style_technical_terms = ", ".join(style_info.get("technical_terms", []))
+
+ # Get mood description
+ mood_info = MOODS.get(mood, {"description": "atmospheric", "icon": "โจ"})
+ mood_description = mood_info["description"]
+ mood_lighting = mood_info.get("lighting", "")
+
+ # Get color palette description if provided
+ color_palette_description = ""
+ if color_palette and color_palette in COLOR_PALETTES:
+ palette_info = COLOR_PALETTES[color_palette]
+ color_palette_description = f"Color Palette: {color_palette} - {palette_info['description']}"
+
+ # Get special effect description if provided
+ special_effect_description = ""
+ if special_effect and special_effect in SPECIAL_EFFECTS and special_effect != "None":
+ effect_info = SPECIAL_EFFECTS[special_effect]
+ special_effect_description = f"Special Effect: {special_effect} - {effect_info['description']}"
+
+ # Prepare the user prompt for AI enhancer
+ user_prompt = f"""Description: {user_input}
+Creation Type: {creation_type} - {creation_description}
+Art Style: {art_style} - {style_description}
+Technical Style Terms: {style_technical_terms}
+Mood: {mood} - {mood_description}
+Lighting: {mood_lighting}
+{color_palette_description}
+{special_effect_description}
+Please create a comprehensive, detailed image generation prompt that combines all these elements."""
+
+ try:
+ # Request enhancement from AI model
+ response = enhancer_client.text_generation(
+ prompt=f"\n{system_prompt}\n\n\n\n{user_prompt}\n\n\n",
+ max_new_tokens=500,
+ temperature=0.7, # Slight creativity while maintaining coherence
+ top_p=0.95,
+ repetition_penalty=1.1
+ )
+
+ # Extract enhanced prompt
+ enhanced = response.strip()
+ if enhanced.startswith(""):
+ enhanced = enhanced[len(""):].strip()
+ if enhanced.endswith(""):
+ enhanced = enhanced[:-len("")].strip()
+
+ logger.info(f"AI enhanced prompt: {enhanced[:100]}...")
+ return enhanced if enhanced else user_input
+ except Exception as e:
+ logger.error(f"Error during AI enhancement: {str(e)}")
+ return enhance_prompt_fallback(user_input, creation_type, art_style, mood, color_palette, special_effect)
+ except Exception as e:
+ logger.error(f"Error in AI enhancement: {str(e)}")
+ return enhance_prompt_fallback(user_input, creation_type, art_style, mood, color_palette, special_effect)
+
+# Fallback prompt enhancement without AI
+def enhance_prompt_fallback(user_input, creation_type, art_style, mood, color_palette=None, special_effect=None):
+ """
+ Enhance user input without requiring AI using rule-based enhancement
+ Args:
+ user_input (str): User's original description
+ creation_type (str): Selected creation type (e.g., "Digital Art")
+ art_style (str): Selected art style (e.g., "Photorealistic")
+ mood (str): Selected mood (e.g., "Peaceful")
+ color_palette (str): Selected color palette (e.g., "Vibrant")
+ special_effect (str): Selected special effect (e.g., "Glow")
+ Returns:
+ str: Enhanced prompt using predefined rules and templates
+ """
+ logger.info(f"Using fallback enhancement for: {user_input[:50]}...")
+
+ # Quality terms by creation type
+ quality_terms = {
+ "Realistic Photo": [
+ "photorealistic", "high resolution", "detailed",
+ "natural lighting", "sharp focus", "professional photography",
+ "crisp details", "realistic textures", "DSLR photo", "high-definition"
+ ],
+ "Digital Art": [
+ "vibrant colors", "clean lines", "digital illustration",
+ "polished", "professional digital art", "detailed rendering",
+ "digital painting", "colorful", "vector-like precision", "crisp"
+ ],
+ "Fantasy Illustration": [
+ "magical atmosphere", "fantasy art", "detailed illustration",
+ "epic", "otherworldly", "imaginative scene",
+ "fantasy environment", "magical lighting", "mythical qualities"
+ ],
+ "Concept Art": [
+ "professional concept art", "detailed design", "conceptual illustration",
+ "industry standard", "visual development", "production artwork",
+ "concept design", "detailed environment", "character design"
+ ],
+ "Anime/Manga": [
+ "anime style", "manga illustration", "cel shaded",
+ "Japanese animation", "2D character art", "anime aesthetic",
+ "clean linework", "anime proportions", "stylized features"
+ ],
+ "Oil Painting": [
+ "oil on canvas", "textured brushwork", "rich colors",
+ "traditional painting", "artistic brushstrokes", "gallery quality",
+ "glazed layers", "impasto technique", "classical painting style"
+ ],
+ "Watercolor": [
+ "watercolor painting", "soft color bleeding", "delicate washes",
+ "transparent layers", "loose brushwork", "gentle transitions",
+ "watercolor paper texture", "wet-on-wet technique", "fluid color blending"
+ ],
+ "Sketch": [
+ "detailed sketch", "pencil drawing", "line art",
+ "hand-drawn", "fine details", "shading techniques",
+ "graphite", "charcoal texture", "gestural lines"
+ ],
+ "3D Rendering": [
+ "3D render", "volumetric lighting", "ray tracing",
+ "3D modeling", "realistic textures", "computer graphics",
+ "physically based rendering", "global illumination", "ambient occlusion"
+ ],
+ "Pixel Art": [
+ "pixel art", "8-bit style", "retro game aesthetic",
+ "limited color palette", "pixelated", "nostalgic game art",
+ "16-bit look", "pixel perfect", "dithering effects"
+ ],
+ "Character Portrait": [
+ "character portrait", "detailed features", "expressive pose",
+ "personality captured", "dynamic lighting", "professional portrait",
+ "character design", "emotive", "high-quality portrait"
+ ],
+ "Landscape": [
+ "scenic vista", "atmospheric lighting", "panoramic view",
+ "environmental details", "natural beauty", "grand landscape",
+ "scenic composition", "depth of field", "natural environment"
+ ],
+ "Abstract Composition": [
+ "abstract composition", "non-representational", "artistic expression",
+ "color theory", "visual rhythm", "compositional balance",
+ "abstract forms", "artistic creativity", "non-figurative"
+ ],
+ "Product Visualization": [
+ "product showcase", "studio lighting", "professional product shot",
+ "commercial quality", "advertising aesthetic", "product details",
+ "pristine rendering", "marketing visualization", "product photography"
+ ]
+ }
+
+ # Get art style technical terms
+ style_terms = []
+ if art_style in ART_STYLES and "technical_terms" in ART_STYLES[art_style]:
+ style_terms = ART_STYLES[art_style]["technical_terms"]
+
+ # Get mood lighting and color palette
+ mood_lighting = ""
+ if mood in MOODS:
+ mood_lighting = MOODS[mood].get("lighting", "")
+
+ # Get color palette terms
+ palette_terms = []
+ if color_palette and color_palette in COLOR_PALETTES:
+ palette_info = COLOR_PALETTES[color_palette]
+ palette_terms = [color_palette.lower(), palette_info["description"].lower()]
+
+ # Special effect terms
+ effect_terms = []
+ if special_effect and special_effect in SPECIAL_EFFECTS and special_effect != "None":
+ effect_info = SPECIAL_EFFECTS[special_effect]
+ effect_terms = [special_effect.lower(), effect_info["description"].lower()]
+
+ # Get terms for the specific creation type, or use generic terms
+ type_terms = quality_terms.get(creation_type, [
+ "high quality", "detailed", "professional", "masterful", "high resolution", "sharp details"
+ ])
+
+ # Common quality terms enhanced with trending and technical terms
+ common_terms = [
+ "8K resolution", "highly detailed", "professional", "masterpiece",
+ "trending on artstation", "award winning", "stunning", "intricate details",
+ "perfect composition", "cinematic lighting", "ultra realistic"
+ ]
+
+ # Get style modifier
+ style_modifier = f"{art_style} style, {', '.join(style_terms[:3])}" if style_terms else f"{art_style} style"
+
+ # Get mood modifier
+ mood_modifier = f"{mood} mood, {mood_lighting}" if mood_lighting else f"{mood} mood"
+
+ # Color palette modifier
+ palette_modifier = f"{', '.join(palette_terms[:2])}" if palette_terms else ""
+
+ # Effect modifier
+ effect_modifier = f"{', '.join(effect_terms[:1])}" if effect_terms else ""
+
+ # Basic prompt structure - core subject and style elements
+ prompt_parts = [
+ user_input,
+ style_modifier,
+ mood_modifier
+ ]
+
+ if palette_modifier:
+ prompt_parts.append(palette_modifier)
+
+ if effect_modifier:
+ prompt_parts.append(effect_modifier)
+
+ # Add randomly selected quality terms for variety
+ selected_type_terms = random.sample(type_terms, min(3, len(type_terms)))
+ selected_common_terms = random.sample(common_terms, min(3, len(common_terms)))
+
+ # Combine terms
+ quality_description = ", ".join(selected_type_terms + selected_common_terms)
+
+ # Final enhanced prompt
+ enhanced_prompt = f"{', '.join(prompt_parts)}, {quality_description}"
+
+ logger.info(f"Fallback enhanced prompt: {enhanced_prompt[:100]}...")
+ return enhanced_prompt
+
+# =============== IMAGE GENERATION FUNCTIONS ===============
+
+# Apply special effects to an image
+def apply_special_effects(image, special_effect, intensity=None):
+ """Apply special visual effects to an image"""
+ if not special_effect or special_effect == "None" or image is None:
+ return image
+
+ if special_effect not in SPECIAL_EFFECTS:
+ return image
+
+ effect_info = SPECIAL_EFFECTS[special_effect]
+
+ # Use default intensity if not specified
+ if intensity is None:
+ intensity = effect_info["default_intensity"]
+
+ # Ensure intensity is within range
+ min_intensity, max_intensity = effect_info["intensity_range"]
+ intensity = max(min_intensity, min(max_intensity, intensity))
+
+ try:
+ # Apply the selected effect
+ if special_effect == "Glow":
+ # Create a blurred version for the glow effect
+ glow_image = image.copy()
+ glow_image = glow_image.filter(ImageFilter.GaussianBlur(radius=10 * intensity))
+
+ # Blend the original with the blurred version
+ return Image.blend(image, glow_image, intensity * 0.5)
+
+ elif special_effect == "Film Grain":
+ # Create a noise layer
+ width, height = image.size
+ noise = Image.new('L', (width, height))
+ noise_data = np.random.randint(0, 255, (height, width), dtype=np.uint8)
+ # Adjust noise intensity
+ noise_data = np.clip(noise_data * intensity, 0, 255).astype(np.uint8)
+ noise.putdata([x for x in noise_data.flatten()])
+
+ # Convert image to RGBA if it isn't already
+ if image.mode != 'RGBA':
+ image = image.convert('RGBA')
+
+ # Create an alpha mask for the noise
+ noise_mask = Image.new('L', (width, height), 255)
+
+ # Apply noise with screen blending mode
+ noise_overlay = Image.new('RGBA', (width, height))
+ for x in range(width):
+ for y in range(height):
+ r, g, b, a = image.getpixel((x, y))
+ noise_val = noise_data[y, x]
+ # Screen blend mode
+ nr = min(255, r + (noise_val * intensity * 0.1))
+ ng = min(255, g + (noise_val * intensity * 0.1))
+ nb = min(255, b + (noise_val * intensity * 0.1))
+ noise_overlay.putpixel((x, y), (int(nr), int(ng), int(nb), a))
+
+ # Blend the original with the noise overlay
+ result = Image.alpha_composite(image, noise_overlay)
+ return result.convert('RGB')
+
+ elif special_effect == "Vignette":
+ # Create a radial gradient for vignette
+ width, height = image.size
+ vignette = Image.new('L', (width, height), 255)
+
+ # Calculate the dimensions of the ellipse
+ ellipse_w = width * 1.5
+ ellipse_h = height * 1.5
+ ellipse_x = (width - ellipse_w) / 2
+ ellipse_y = (height - ellipse_h) / 2
+
+ # Create a draw object
+ from PIL import ImageDraw
+ draw = ImageDraw.Draw(vignette)
+
+ # Draw the gradient
+ for i in range(min(width, height) // 2):
+ # Scale the ellipse size
+ factor = 1 - (i / (min(width, height) / 2))
+ e_w = ellipse_w * factor
+ e_h = ellipse_h * factor
+ e_x = (width - e_w) / 2
+ e_y = (height - e_h) / 2
+
+ # Calculate the brightness (darker toward edges)
+ brightness = int(255 * (1 - i / (min(width, height) / 2) * intensity))
+
+ # Draw an ellipse with the current brightness
+ draw.ellipse((e_x, e_y, e_x + e_w, e_y + e_h), fill=brightness)
+
+ # Apply the vignette
+ result = image.copy()
+ result.putalpha(vignette)
+ # Convert back to RGB
+ result = result.convert('RGB')
+ return result
+
+ elif special_effect == "Chromatic Aberration":
+ # Split into RGB channels
+ r, g, b = image.split()
+
+ # Shift red channel to the right and blue channel to the left
+ shift_amount = int(5 * intensity)
+ r = ImageChops.offset(r, shift_amount, 0)
+ b = ImageChops.offset(b, -shift_amount, 0)
+
+ # Merge channels back
+ return Image.merge("RGB", (r, g, b))
+
+ elif special_effect == "Dream Blur":
+ # Apply a soft Gaussian blur
+ blurred = image.filter(ImageFilter.GaussianBlur(radius=intensity * 3))
+
+ # Increase brightness slightly
+ enhancer = ImageEnhance.Brightness(blurred)
+ brightened = enhancer.enhance(1 + intensity * 0.2)
+
+ # Increase contrast slightly
+ enhancer = ImageEnhance.Contrast(brightened)
+ contrasted = enhancer.enhance(1 + intensity * 0.1)
+
+ return contrasted
+
+ elif special_effect == "Duotone":
+ # Convert to grayscale
+ grayscale = image.convert('L')
+
+ # Define two colors for duotone effect
+ highlight_color = (52, 143, 235) # Blue
+ shadow_color = (145, 39, 143) # Purple
+
+ # Create duotone image
+ duotone = Image.new('RGB', image.size)
+
+ # For each pixel, blend between the shadow and highlight colors
+ # based on the grayscale value
+ for x in range(image.width):
+ for y in range(image.height):
+ gray_value = grayscale.getpixel((x, y)) / 255.0
+ r = int(shadow_color[0] * (1 - gray_value) + highlight_color[0] * gray_value)
+ g = int(shadow_color[1] * (1 - gray_value) + highlight_color[1] * gray_value)
+ b = int(shadow_color[2] * (1 - gray_value) + highlight_color[2] * gray_value)
+ duotone.putpixel((x, y), (r, g, b))
+
+ # Blend with original based on intensity
+ return Image.blend(image, duotone, intensity)
+
+ elif special_effect == "Halftone":
+ # This is a simplified halftone effect
+ # Convert to grayscale
+ grayscale = image.convert('L')
+
+ # Resize down and then back up to create the dot pattern
+ dot_size = max(1, int(10 * intensity))
+ small = grayscale.resize((image.width // dot_size, image.height // dot_size), Image.NEAREST)
+ halftone = small.resize(image.size, Image.NEAREST)
+
+ # Convert back to RGB
+ halftone = halftone.convert('RGB')
+
+ # Blend with original based on intensity
+ return Image.blend(image, halftone, intensity * 0.7)
+
+ elif special_effect == "Noir":
+ # Convert to high contrast black and white
+ grayscale = image.convert('L')
+
+ # Increase contrast
+ enhancer = ImageEnhance.Contrast(grayscale)
+ noir = enhancer.enhance(1.5 + intensity)
+
+ # Convert back to RGB
+ noir = noir.convert('RGB')
+
+ return noir
+
+ elif special_effect == "Retro Wave":
+ # This is a simplified retro wave effect with purple/pink gradient
+ width, height = image.size
+
+ # Create base gradient
+ gradient = Image.new('RGB', (width, height))
+ for y in range(height):
+ # Calculate color gradient from pink to purple to blue
+ r = int(255 - (y/height) * 150)
+ g = int(50 + (y/height) * 50)
+ b = int(150 + (y/height) * 105)
+
+ for x in range(width):
+ gradient.putpixel((x, y), (r, g, b))
+
+ # Blend gradient with original image
+ result = Image.blend(image, gradient, intensity * 0.4)
+
+ # Add scanlines for retro effect
+ scanlines = Image.new('L', (width, height), 255)
+ draw = ImageDraw.Draw(scanlines)
+
+ # Draw horizontal lines
+ line_spacing = max(2, int(4 * (1/intensity)))
+ for y in range(0, height, line_spacing):
+ draw.line([(0, y), (width, y)], fill=150)
+
+ # Apply scanlines with opacity based on intensity
+ result.putalpha(scanlines)
+ result = result.convert('RGB')
+
+ return result
+
+ # Default case - return original image
+ return image
+
+ except Exception as e:
+ logger.error(f"Error applying special effect {special_effect}: {str(e)}")
+ return image
+
+# Generate image function with loading state handling and retry mechanism
+def generate_image(description, model_key, creation_type=None, art_style=None, mood=None,
+ aspect_ratio=None, color_palette=None, special_effect=None, seed=None, retries=1):
+ """
+ Generate image based on user inputs by enhancing prompt and calling image model API
+ Args:
+ description (str): User's original description
+ model_key (str): Model identifier
+ creation_type (str, optional): Creation type
+ art_style (str, optional): Art style
+ mood (str, optional): Mood
+ aspect_ratio (str, optional): Aspect ratio
+ color_palette (str, optional): Color palette
+ special_effect (str, optional): Special effect
+ seed (int, optional): Random seed for reproducibility
+ retries (int): Number of retries if generation fails
+ Returns:
+ tuple: (image, status_message, enhanced_prompt, parameters_html, seed)
+ """
+ try:
+ # Validate input
+ if not description or not description.strip():
+ return None, "Please enter a description for your image", "", "", None
+
+ logger.info(f"Generating image with model: {model_key}")
+
+ # Check if we have a cached result
+ cache_key = get_cache_key(description, model_key, creation_type, art_style, mood, aspect_ratio, color_palette, special_effect)
+ cached = get_cached_image(cache_key)
+ if cached:
+ logger.info(f"Using cached image for: {description[:50]}...")
+ return cached["image"], "Image retrieved from cache", cached["enhanced_prompt"], cached["parameters_html"], cached["seed"]
+
+ # Extract aspect ratio dimensions
+ width, height = (512, 512) # Default
+ if aspect_ratio in ASPECT_RATIOS:
+ width, height = ASPECT_RATIOS[aspect_ratio]["dimensions"]
+
+ # Enhance prompt with AI or fallback
+ enhanced_prompt = enhance_prompt_with_ai(
+ description,
+ creation_type,
+ art_style,
+ mood,
+ color_palette,
+ special_effect
+ )
+
+ # Validate client availability
+ if hf_client is None:
+ logger.error("Hugging Face client not available")
+ return None, "Error: Unable to connect to image generation service. Please try again later.", enhanced_prompt, "", None
+
+ # Add negative prompt to avoid common issues
+ negative_prompt = "low quality, blurry, distorted, deformed, disfigured, bad anatomy, watermark, signature, text, poorly drawn, amateur, ugly"
+
+ try:
+ # Generate image with progress tracking
+ logger.info(f"Sending request to model {model_key} with prompt: {enhanced_prompt[:100]}...")
+
+ # Log start time for performance tracking
+ start_time = time.time()
+
+ # Set generation parameters
+ params = {
+ "negative_prompt": negative_prompt,
+ "width": width,
+ "height": height
+ }
+
+ # Add seed if provided
+ if seed is not None:
+ params["seed"] = seed
+ else:
+ # Generate random seed
+ seed = random.randint(0, 2147483647)
+ params["seed"] = seed
+
+ # Generate the image
+ image = hf_client.text_to_image(
+ prompt=enhanced_prompt,
+ model=model_key,
+ **params
+ )
+
+ # Apply special effects if requested
+ if special_effect and special_effect != "None":
+ image = apply_special_effects(image, special_effect)
+
+ # Calculate generation time
+ generation_time = time.time() - start_time
+ logger.info(f"Image generated successfully in {generation_time:.2f} seconds")
+
+ # Format parameters for display
+ model_info = ""
+ for key, info in IMAGE_MODELS.items():
+ if key == model_key:
+ model_info = f"{info['icon']} {info['display_name']}"
+ break
+
+ # Create parameters display
+ parameters_html = format_parameters(
+ f"{CREATION_TYPES.get(creation_type, {}).get('icon', 'โข')} {creation_type}" if creation_type else "",
+ f"{ART_STYLES.get(art_style, {}).get('icon', 'โข')} {art_style}" if art_style else "",
+ f"{MOODS.get(mood, {}).get('icon', 'โข')} {mood}" if mood else "",
+ f"{ASPECT_RATIOS.get(aspect_ratio, {}).get('icon', 'โข')} {aspect_ratio}" if aspect_ratio else "",
+ f"{COLOR_PALETTES.get(color_palette, {}).get('icon', 'โข')} {color_palette}" if color_palette else "",
+ f"{SPECIAL_EFFECTS.get(special_effect, {}).get('icon', 'โข')} {special_effect}" if special_effect else "",
+ model_info
+ )
+
+ # Cache the result
+ cache_image(cache_key, image, enhanced_prompt, parameters_html, seed)
+
+ # Success message with generation details
+ if use_ai_enhancer:
+ enhancement_method = "AI-enhanced prompt"
+ else:
+ enhancement_method = "rule-based prompt enhancement"
+
+ model_display_name = "Unknown Model"
+ for key, info in IMAGE_MODELS.items():
+ if key == model_key:
+ model_display_name = info['display_name']
+ break
+
+ success_message = f"Image created successfully in {generation_time:.1f}s using {model_display_name} with {enhancement_method} (seed: {seed})"
+
+ return image, success_message, enhanced_prompt, parameters_html, seed
+
+ except Exception as e:
+ error_message = str(e)
+ logger.error(f"Error during image generation: {error_message}")
+
+ # Retry logic for transient errors
+ if retries > 0:
+ logger.info(f"Retrying image generation, {retries} attempts remaining")
+ time.sleep(1) # Small delay before retry
+ return generate_image(description, model_key, creation_type, art_style, mood,
+ aspect_ratio, color_palette, special_effect, seed, retries - 1)
+
+ # Format user-friendly error message
+ if "429" in error_message:
+ friendly_error = "Server is currently busy. Please try again in a few moments."
+ elif "401" in error_message or "403" in error_message:
+ friendly_error = "Authentication error with the image service. Please check API settings."
+ elif "timeout" in error_message.lower():
+ friendly_error = "Request timed out. The server might be under heavy load."
+ else:
+ friendly_error = f"Error generating image: {error_message}"
+
+ return None, friendly_error, enhanced_prompt, "", None
+ except Exception as e:
+ logger.error(f"Unexpected error in generate_image: {str(e)}")
+ return None, f"Unexpected error: {str(e)}", "", "", None
+
+# Wrapper function for generate_image with status updates
+# Wrapper function for generate_image with status updates (continued)
+def generate_with_status(description, creation_type_val, art_style_val, mood_val,
+ aspect_ratio_val, color_palette_val, special_effect_val, model_name, seed=None):
+ """
+ Wrapper for generate_image that handles UI status updates and parameter formatting
+ Returns:
+ tuple: (image, status_html, enhanced_prompt, parameters_html, seed)
+ """
+ # Check if description is empty
+ if not description or not description.strip():
+ return None, update_status("Please enter a description", is_error=True), "", "", None
+
+ # Extract keys from formatted values
+ creation_key = extract_key(creation_type_val)
+ art_key = extract_key(art_style_val)
+ mood_key = extract_key(mood_val)
+ aspect_key = extract_key(aspect_ratio_val)
+ palette_key = extract_key(color_palette_val)
+ effect_key = extract_key(special_effect_val)
+
+ # Get model key from formatted name
+ model_key = get_model_key_from_display_name(model_name)
+ if not model_key:
+ return None, update_status("Invalid model selection", is_error=True), "", "", None
+
+ try:
+ # Generate the image
+ image, message, enhanced_prompt, parameters_html, used_seed = generate_image(
+ description, model_key, creation_key, art_key, mood_key,
+ aspect_key, palette_key, effect_key, seed
+ )
+
+ if image is None:
+ return None, update_status(message, is_error=True), "", "", None
+
+ # Success message
+ success_message = update_status(message)
+ return image, success_message, enhanced_prompt, parameters_html, used_seed
+
+ except Exception as e:
+ error_message = str(e)
+ logger.error(f"Error in generate_with_status: {error_message}")
+ return None, update_status(f"Error: {error_message}", is_error=True), "", "", None
+
+# Batch generation function
+def generate_variations(description, model_key, creation_type=None, art_style=None, mood=None,
+ aspect_ratio=None, color_palette=None, special_effect=None, num_images=4):
+ """
+ Generate multiple variations of an image with different seeds
+ Args:
+ description (str): User's original description
+ model_key (str): Model identifier
+ creation_type (str, optional): Creation type
+ art_style (str, optional): Art style
+ mood (str, optional): Mood
+ aspect_ratio (str, optional): Aspect ratio
+ color_palette (str, optional): Color palette
+ special_effect (str, optional): Special effect
+ num_images (int): Number of variations to generate
+ Returns:
+ list: List of tuples (image, seed) for each variation
+ """
+ results = []
+ num_images = min(num_images, APP_CONFIG["max_batch_size"]) # Limit to max batch size
+
+ # Generate varied seeds
+ import time
+ base_seed = int(time.time()) % 10000
+ seeds = [base_seed + i * 1000 for i in range(num_images)]
+
+ # Generate images in sequence
+ for i, seed in enumerate(seeds):
+ try:
+ image, _, _, _, used_seed = generate_image(
+ description, model_key, creation_type, art_style, mood,
+ aspect_ratio, color_palette, special_effect, seed, retries=0
+ )
+
+ if image:
+ results.append((image, used_seed))
+
+ except Exception as e:
+ logger.error(f"Error generating variation {i+1}: {str(e)}")
+ continue
+
+ return results
+
+# =============== USER PROFILE AND HISTORY MANAGEMENT ===============
+
+# User profile management
+class UserProfile:
+ """User profile management with preferences and history"""
+
+ def __init__(self, user_id=None):
+ """Initialize user profile"""
+ self.user_id = user_id or str(uuid.uuid4())
+ self.preferences = {
+ "theme": APP_CONFIG["default_theme"],
+ "default_model": "stabilityai/stable-diffusion-xl-base-1.0",
+ "default_creation_type": "Digital Art",
+ "default_aspect_ratio": "1:1 (Square)",
+ "show_tips": True,
+ "advanced_mode": False
+ }
+ self.history = []
+ self.favorites = []
+ self.load()
+
+ def save(self):
+ """Save user profile to disk"""
+ try:
+ data_dir = os.path.join(APP_CONFIG["data_dir"], "users")
+ os.makedirs(data_dir, exist_ok=True)
+
+ filename = os.path.join(data_dir, f"{self.user_id}.json")
+
+ with open(filename, 'w') as f:
+ # Create serializable history (convert PIL images to base64)
+ serializable_history = []
+ for entry in self.history:
+ if 'image' in entry and entry['image'] is not None:
+ # Convert PIL image to base64
+ img_buffer = io.BytesIO()
+ entry['image'].save(img_buffer, format="PNG")
+ img_base64 = base64.b64encode(img_buffer.getvalue()).decode('utf-8')
+
+ # Create copy of entry with base64 image
+ entry_copy = entry.copy()
+ entry_copy['image_data'] = img_base64
+ entry_copy.pop('image', None) # Remove PIL image
+ serializable_history.append(entry_copy)
+ else:
+ entry_copy = entry.copy()
+ entry_copy.pop('image', None) # Remove PIL image if any
+ serializable_history.append(entry_copy)
+
+ # Create serializable favorites
+ serializable_favorites = []
+ for entry in self.favorites:
+ if 'image' in entry and entry['image'] is not None:
+ # Convert PIL image to base64
+ img_buffer = io.BytesIO()
+ entry['image'].save(img_buffer, format="PNG")
+ img_base64 = base64.b64encode(img_buffer.getvalue()).decode('utf-8')
+
+ # Create copy of entry with base64 image
+ entry_copy = entry.copy()
+ entry_copy['image_data'] = img_base64
+ entry_copy.pop('image', None) # Remove PIL image
+ serializable_favorites.append(entry_copy)
+ else:
+ entry_copy = entry.copy()
+ entry_copy.pop('image', None) # Remove PIL image if any
+ serializable_favorites.append(entry_copy)
+
+ # Create serializable profile
+ profile_data = {
+ 'user_id': self.user_id,
+ 'preferences': self.preferences,
+ 'history': serializable_history,
+ 'favorites': serializable_favorites
+ }
+
+ json.dump(profile_data, f, indent=2)
+ logger.info(f"Saved user profile: {self.user_id}")
+ return True
+ except Exception as e:
+ logger.error(f"Error saving user profile: {str(e)}")
+ return False
+
+ def load(self):
+ """Load user profile from disk"""
+ try:
+ filename = os.path.join(APP_CONFIG["data_dir"], "users", f"{self.user_id}.json")
+
+ if not os.path.exists(filename):
+ logger.info(f"User profile doesn't exist: {self.user_id}")
+ return False
+
+ with open(filename, 'r') as f:
+ data = json.load(f)
+
+ # Load profile data
+ self.preferences = data.get('preferences', self.preferences)
+
+ # Load history and convert base64 back to PIL images
+ self.history = []
+ for entry in data.get('history', []):
+ if 'image_data' in entry:
+ # Convert base64 back to PIL image
+ img_data = base64.b64decode(entry['image_data'])
+ img = Image.open(io.BytesIO(img_data))
+
+ # Create entry with PIL image
+ entry_copy = entry.copy()
+ entry_copy['image'] = img
+ entry_copy.pop('image_data', None) # Remove base64 data
+ self.history.append(entry_copy)
+ else:
+ self.history.append(entry)
+
+ # Load favorites
+ self.favorites = []
+ for entry in data.get('favorites', []):
+ if 'image_data' in entry:
+ # Convert base64 back to PIL image
+ img_data = base64.b64decode(entry['image_data'])
+ img = Image.open(io.BytesIO(img_data))
+
+ # Create entry with PIL image
+ entry_copy = entry.copy()
+ entry_copy['image'] = img
+ entry_copy.pop('image_data', None) # Remove base64 data
+ self.favorites.append(entry_copy)
+ else:
+ self.favorites.append(entry)
+
+ logger.info(f"Loaded user profile: {self.user_id}")
+ return True
+ except Exception as e:
+ logger.error(f"Error loading user profile: {str(e)}")
+ return False
+
+ def add_to_history(self, image, description, enhanced_prompt, parameters, seed=None):
+ """Add an image to user history"""
+ if image is None:
+ return False
+
+ # Create history entry
+ entry = {
+ 'id': str(uuid.uuid4()),
+ 'timestamp': datetime.now().isoformat(),
+ 'image': image,
+ 'description': description,
+ 'enhanced_prompt': enhanced_prompt,
+ 'parameters': parameters,
+ 'seed': seed
+ }
+
+ # Add to history
+ self.history.insert(0, entry)
+
+ # Limit history size
+ if len(self.history) > APP_CONFIG["max_history"]:
+ self.history = self.history[:APP_CONFIG["max_history"]]
+
+ # Save profile
+ self.save()
+ return True
+
+ def add_to_favorites(self, image_id):
+ """Add an image from history to favorites"""
+ # Find image in history
+ for entry in self.history:
+ if entry.get('id') == image_id:
+ # Check if already in favorites
+ if any(fav.get('id') == image_id for fav in self.favorites):
+ return False
+
+ # Add to favorites
+ self.favorites.insert(0, entry)
+ # Save profile
+ self.save()
+ return True
+
+ return False
+
+ def remove_from_favorites(self, image_id):
+ """Remove an image from favorites"""
+ for i, entry in enumerate(self.favorites):
+ if entry.get('id') == image_id:
+ self.favorites.pop(i)
+ self.save()
+ return True
+
+ return False
+
+ def get_history(self):
+ """Get user history"""
+ return self.history
+
+ def get_favorites(self):
+ """Get user favorites"""
+ return self.favorites
+
+ def clear_history(self):
+ """Clear user history"""
+ self.history = []
+ self.save()
+ return True
+
+ def update_preferences(self, preferences):
+ """Update user preferences"""
+ self.preferences.update(preferences)
+ self.save()
+ return True
+
+# Initialize user profile
+current_user = UserProfile()
+
+# =============== UI CREATION FUNCTIONS ===============
+
+# Create modern UI with improved UX
+def create_ui():
+ """Create the application UI"""
+ # Set page title and favicon
+ gr.Blocks.load_config(analytics_enabled=APP_CONFIG["enable_analytics"])
+
+ # Define CSS for enhanced UI
+ css = """
+/* Variables for light/dark theme customization */
+:root {
+ /* Light Theme (Default) */
+ --primary-color: #6A24FE;
+ --primary-hover: #5615ED;
+ --secondary-color: #FF4593;
+ --secondary-hover: #E32D7B;
+ --tertiary-color: #2ECADE;
+ --tertiary-hover: #22A8BC;
+ --accent-color: #FFA03C;
+ --accent-hover: #E98321;
+
+ --background-color: #F9FAFF;
+ --card-color: #FFFFFF;
+ --card-hover: #F5F7FF;
+
+ --text-color: #1A1A2E;
+ --text-muted: #64748B;
+ --text-light: #94A3B8;
+
+ --border-color: #E2E8F0;
+ --border-hover: #CBD5E1;
+
+ --error-color: #E11D48;
+ --warning-color: #F59E0B;
+ --success-color: #10B981;
+ --info-color: #3B82F6;
+
+ --shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
+ --shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
+ --shadow-md: 0 6px 10px -2px rgba(0, 0, 0, 0.12), 0 3px 6px -2px rgba(0, 0, 0, 0.08);
+ --shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
+ --shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
+
+ --radius-sm: 0.375rem;
+ --radius: 0.5rem;
+ --radius-md: 0.625rem;
+ --radius-lg: 0.75rem;
+ --radius-xl: 1rem;
+ --radius-2xl: 1.5rem;
+ --radius-3xl: 2rem;
+ --radius-full: 9999px;
+
+ --font-sans: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
+ --font-display: 'Plus Jakarta Sans', 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
+ --font-mono: 'JetBrains Mono', 'SF Mono', 'Roboto Mono', Menlo, Consolas, monospace;
+
+ --header-height: 4rem;
+
+ /* Theme transition */
+ --transition-normal: 0.2s ease;
+ --transition-slow: 0.35s ease;
+}
+
+/* Dark Theme */
+.dark-theme {
+ --primary-color: #8B5CF6;
+ --primary-hover: #7C3AED;
+ --secondary-color: #EC4899;
+ --secondary-hover: #DB2777;
+ --tertiary-color: #06B6D4;
+ --tertiary-hover: #0891B2;
+ --accent-color: #F97316;
+ --accent-hover: #EA580C;
+
+ --background-color: #0F172A;
+ --card-color: #1E293B;
+ --card-hover: #263449;
+
+ --text-color: #F8FAFC;
+ --text-muted: #94A3B8;
+ --text-light: #CBD5E1;
+
+ --border-color: #334155;
+ --border-hover: #475569;
+
+ --shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.2);
+ --shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.3), 0 2px 4px -1px rgba(0, 0, 0, 0.2);
+ --shadow-md: 0 6px 10px -2px rgba(0, 0, 0, 0.4), 0 3px 6px -2px rgba(0, 0, 0, 0.25);
+ --shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.4), 0 4px 6px -2px rgba(0, 0, 0, 0.2);
+ --shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, 0.4), 0 10px 10px -5px rgba(0, 0, 0, 0.2);
+}
+
+/* Global styles and resets */
+body, html {
+ font-family: var(--font-sans);
+ color: var(--text-color);
+ background-color: var(--background-color);
+ line-height: 1.5;
+ margin: 0;
+ padding: 0;
+ transition: background-color var(--transition-normal), color var(--transition-normal);
+}
+
+/* Container with responsive padding */
+.container {
+ max-width: 1500px;
+ margin: 0 auto;
+ padding: 1rem;
+}
+
+@media (max-width: 768px) {
+ .container {
+ padding: 0.75rem;
+ }
+}
+
+@media (max-width: 640px) {
+ .container {
+ padding: 0.5rem;
+ }
+}
+
+/* Add custom fonts */
+@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Plus+Jakarta+Sans:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600&display=swap');
+
+/* Card styling with elevation and hover effects */
+.gr-panel, div.gradio-box {
+ border-radius: var(--radius-lg) !important;
+ border: 1px solid var(--border-color) !important;
+ box-shadow: var(--shadow) !important;
+ overflow: hidden;
+ transition: transform 0.2s, box-shadow 0.2s, background-color var(--transition-normal), border-color var(--transition-normal);
+ background-color: var(--card-color) !important;
+}
+
+.gr-panel:hover, div.gradio-box:hover {
+ transform: translateY(-2px);
+ box-shadow: var(--shadow-lg) !important;
+ border-color: var(--border-hover) !important;
+}
+
+/* Primary Button styling with gradient and hover states */
+button.primary {
+ background: linear-gradient(135deg, var(--primary-color), var(--secondary-color)) !important;
+ color: white !important;
+ border: none !important;
+ border-radius: var(--radius) !important;
+ font-weight: 600 !important;
+ letter-spacing: 0.025em !important;
+ padding: 0.75rem 1.5rem !important;
+ transition: all 0.3s ease !important;
+ box-shadow: var(--shadow-sm) !important;
+ outline: none !important;
+ text-transform: none !important;
+ position: relative;
+ overflow: hidden;
+ z-index: 1;
+}
+
+button.primary::before {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background: linear-gradient(135deg, var(--secondary-color), var(--primary-color));
+ z-index: -1;
+ opacity: 0;
+ transition: opacity 0.3s ease;
+}
+
+button.primary:hover {
+ transform: translateY(-1px);
+ box-shadow: var(--shadow) !important;
+}
+
+button.primary:hover::before {
+ opacity: 1;
+}
+
+button.primary:active {
+ transform: translateY(0);
+}
+
+button.primary[disabled], button.primary[disabled]:hover {
+ opacity: 0.5;
+ cursor: not-allowed;
+ transform: none;
+}
+
+/* Secondary button styling */
+button.secondary {
+ background-color: transparent !important;
+ color: var(--primary-color) !important;
+ border: 1px solid var(--primary-color) !important;
+ border-radius: var(--radius) !important;
+ font-weight: 500 !important;
+ padding: 0.625rem 1.25rem !important;
+ transition: all 0.2s ease !important;
+ text-transform: none !important;
+}
+
+button.secondary:hover {
+ background-color: rgba(106, 36, 254, 0.08) !important;
+ border-color: var(--primary-hover) !important;
+ transform: translateY(-1px);
+}
+
+/* Ghost button styling */
+button.ghost {
+ background-color: transparent !important;
+ color: var(--text-color) !important;
+ border: 1px solid var(--border-color) !important;
+ border-radius: var(--radius) !important;
+ font-weight: 500 !important;
+ padding: 0.625rem 1.25rem !important;
+ transition: all 0.2s ease !important;
+ text-transform: none !important;
+}
+
+button.ghost:hover {
+ background-color: rgba(0, 0, 0, 0.04) !important;
+ border-color: var(--border-hover) !important;
+ transform: translateY(-1px);
+}
+
+.dark-theme button.ghost:hover {
+ background-color: rgba(255, 255, 255, 0.04) !important;
+}
+
+/* Style for the example buttons */
+.example-button {
+ font-size: 0.875rem !important;
+ padding: 0.75rem 1rem !important;
+ background-color: var(--card-color) !important;
+ border: 1px solid var(--border-color) !important;
+ border-radius: var(--radius-lg) !important;
+ transition: all 0.2s !important;
+ text-align: left !important;
+ justify-content: flex-start !important;
+ height: auto !important;
+ text-overflow: ellipsis;
+ overflow: hidden;
+ white-space: nowrap;
+ width: 100%;
+ color: var(--text-color) !important;
+ box-shadow: var(--shadow-sm) !important;
+}
+
+.example-button:hover {
+ background-color: var(--card-hover) !important;
+ border-color: var(--primary-color) !important;
+ transform: translateY(-2px);
+ box-shadow: var(--shadow) !important;
+}
+
+/* Form controls styling */
+.gr-input, .gr-textarea, .gr-dropdown {
+ border-radius: var(--radius) !important;
+ border: 1px solid var(--border-color) !important;
+ transition: border-color 0.2s, box-shadow 0.2s, background-color var(--transition-normal), color var(--transition-normal) !important;
+ font-family: var(--font-sans) !important;
+ color: var(--text-color) !important;
+ background-color: var(--card-color) !important;
+}
+
+.gr-input:focus, .gr-textarea:focus, .gr-dropdown:focus-within {
+ border-color: var(--primary-color) !important;
+ box-shadow: 0 0 0 3px rgba(106, 36, 254, 0.2) !important;
+ outline: none !important;
+}
+
+.dark-theme .gr-input:focus, .dark-theme .gr-textarea:focus, .dark-theme .gr-dropdown:focus-within {
+ box-shadow: 0 0 0 3px rgba(139, 92, 246, 0.3) !important;
+}
+
+.gr-form {
+ gap: 1rem !important;
+}
+
+.gr-input-label, .gr-dropdown-label, .gr-textarea-label, .gr-checkbox-label, .gr-radio-label {
+ font-size: 0.875rem !important;
+ font-weight: 500 !important;
+ color: var(--text-color) !important;
+ margin-bottom: 0.25rem !important;
+ transition: color var(--transition-normal);
+}
+
+/* Input placeholder styling */
+.gr-input::placeholder, .gr-textarea::placeholder {
+ color: var(--text-muted) !important;
+ opacity: 0.7;
+}
+
+/* Input and textarea styling */
+textarea, input[type="text"], input[type="number"], input[type="email"], input[type="password"] {
+ border-radius: var(--radius) !important;
+ border: 1px solid var(--border-color) !important;
+ padding: 0.75rem 1rem !important;
+ transition: border-color 0.2s, box-shadow 0.2s, background-color var(--transition-normal), color var(--transition-normal) !important;
+ font-family: var(--font-sans) !important;
+ background-color: var(--card-color) !important;
+ color: var(--text-color) !important;
+}
+
+textarea:focus, input[type="text"]:focus, input[type="number"]:focus, input[type="email"]:focus, input[type="password"]:focus {
+ border-color: var(--primary-color) !important;
+ box-shadow: 0 0 0 3px rgba(106, 36, 254, 0.2) !important;
+ outline: none !important;
+}
+
+.dark-theme textarea:focus, .dark-theme input[type="text"]:focus, .dark-theme input[type="number"]:focus,
+.dark-theme input[type="email"]:focus, .dark-theme input[type="password"]:focus {
+ box-shadow: 0 0 0 3px rgba(139, 92, 246, 0.3) !important;
+}
+
+/* Dropdown styling */
+.gr-dropdown {
+ border-radius: var(--radius) !important;
+ border: 1px solid var(--border-color) !important;
+ background-color: var(--card-color) !important;
+ color: var(--text-color) !important;
+ transition: border-color 0.2s, box-shadow 0.2s, background-color var(--transition-normal), color var(--transition-normal) !important;
+}
+
+.gr-dropdown > div {
+ border-radius: var(--radius) !important;
+ min-height: 38px !important;
+}
+
+.gr-dropdown > div > span {
+ font-size: 0.9375rem !important;
+ color: var(--text-color) !important;
+}
+
+/* Dropdown menu styling */
+.gr-dropdown ul {
+ background-color: var(--card-color) !important;
+ border: 1px solid var(--border-color) !important;
+ border-radius: var(--radius) !important;
+ box-shadow: var(--shadow) !important;
+ transition: background-color var(--transition-normal), border-color var(--transition-normal) !important;
+}
+
+.gr-dropdown ul li {
+ padding: 0.5rem 0.75rem !important;
+ color: var(--text-color) !important;
+ transition: background-color var(--transition-normal), color var(--transition-normal) !important;
+}
+
+.gr-dropdown ul li:hover {
+ background-color: rgba(106, 36, 254, 0.08) !important;
+}
+
+.dark-theme .gr-dropdown ul li:hover {
+ background-color: rgba(139, 92, 246, 0.15) !important;
+}
+
+/* App header with animation and brand styling */
+.app-header {
+ text-align: center;
+ padding: 2.5rem 1.5rem 2rem;
+ margin-bottom: 2rem;
+ background: linear-gradient(135deg, rgba(106, 36, 254, 0.08), rgba(255, 69, 147, 0.08));
+ border-radius: var(--radius-xl);
+ position: relative;
+ overflow: hidden;
+ transition: background var(--transition-normal);
+}
+
+.dark-theme .app-header {
+ background: linear-gradient(135deg, rgba(139, 92, 246, 0.12), rgba(236, 72, 153, 0.12));
+}
+
+.app-header::before {
+ content: '';
+ position: absolute;
+ top: -50px;
+ left: -50px;
+ right: -50px;
+ height: 100px;
+ background: linear-gradient(135deg, rgba(106, 36, 254, 0.2), rgba(255, 69, 147, 0.2));
+ transform: rotate(-5deg);
+ z-index: 0;
+ transition: background var(--transition-normal);
+}
+
+.dark-theme .app-header::before {
+ background: linear-gradient(135deg, rgba(139, 92, 246, 0.25), rgba(236, 72, 153, 0.25));
+}
+
+.app-header::after {
+ content: '';
+ position: absolute;
+ bottom: -50px;
+ left: -50px;
+ right: -50px;
+ height: 70px;
+ background: linear-gradient(135deg, rgba(255, 69, 147, 0.1), rgba(106, 36, 254, 0.1));
+ transform: rotate(3deg);
+ z-index: 0;
+ transition: background var(--transition-normal);
+}
+
+.dark-theme .app-header::after {
+ background: linear-gradient(135deg, rgba(236, 72, 153, 0.15), rgba(139, 92, 246, 0.15));
+}
+
+.app-header h1 {
+ font-family: var(--font-display) !important;
+ font-size: 3.5rem !important;
+ font-weight: 800 !important;
+ background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ margin-bottom: 0.5rem !important;
+ position: relative;
+ z-index: 1;
+ letter-spacing: -0.03em;
+ transition: background var(--transition-normal);
+}
+
+.dark-theme .app-header h1 {
+ background: linear-gradient(135deg, #A78BFA, #F472B6);
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+}
+
+.app-header p {
+ font-size: 1.25rem !important;
+ color: var(--text-color);
+ opacity: 0.9;
+ max-width: 42rem;
+ margin: 0 auto;
+ position: relative;
+ z-index: 1;
+ transition: color var(--transition-normal);
+}
+
+.app-tagline {
+ font-family: var(--font-display) !important;
+ font-weight: 600;
+ font-size: 1.5rem !important;
+ background: linear-gradient(135deg, var(--secondary-color), var(--primary-color));
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ margin-bottom: 1rem !important;
+ transition: background var(--transition-normal);
+}
+
+.dark-theme .app-tagline {
+ background: linear-gradient(135deg, #F472B6, #A78BFA);
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+}
+
+/* Responsive header */
+@media (max-width: 768px) {
+ .app-header h1 {
+ font-size: 2.5rem !important;
+ }
+ .app-tagline {
+ font-size: 1.25rem !important;
+ }
+ .app-header p {
+ font-size: 1rem !important;
+ }
+}
+
+@media (max-width: 640px) {
+ .app-header h1 {
+ font-size: 2rem !important;
+ }
+ .app-header p {
+ font-size: 0.875rem !important;
+ }
+}
+
+/* Gallery with grid layout and responsive design */
+.gallery {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
+ gap: 1rem;
+ margin: 1.5rem 0;
+}
+
+@media (max-width: 768px) {
+ .gallery {
+ grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
+ gap: 0.75rem;
+ }
+}
+
+@media (max-width: 480px) {
+ .gallery {
+ grid-template-columns: repeat(auto-fill, minmax(130px, 1fr));
+ gap: 0.5rem;
+ }
+}
+
+.gallery-item {
+ border-radius: var(--radius-lg);
+ overflow: hidden;
+ cursor: pointer;
+ border: 2px solid transparent;
+ transition: all 0.3s;
+ display: flex;
+ flex-direction: column;
+ background-color: var(--card-color);
+ box-shadow: var(--shadow-sm);
+ position: relative;
+}
+
+.gallery-item:hover {
+ transform: translateY(-3px) scale(1.02);
+ border-color: var(--primary-color);
+ box-shadow: var(--shadow-md);
+}
+
+.dark-theme .gallery-item:hover {
+ border-color: var(--primary-color);
+}
+
+.gallery-item:active {
+ transform: translateY(-1px) scale(1.01);
+}
+
+.gallery-item-image {
+ width: 100%;
+ aspect-ratio: 1;
+ background-color: rgba(0, 0, 0, 0.04);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ color: var(--text-muted);
+ font-size: 1.5rem;
+ border-bottom: 1px solid var(--border-color);
+ transition: background-color var(--transition-normal), border-color var(--transition-normal);
+}
+
+.dark-theme .gallery-item-image {
+ background-color: rgba(255, 255, 255, 0.04);
+}
+
+.gallery-item-caption {
+ padding: 0.75rem;
+ font-size: 0.75rem;
+ line-height: 1.25;
+ color: var(--text-color);
+ overflow: hidden;
+ display: -webkit-box;
+ -webkit-line-clamp: 2;
+ -webkit-box-orient: vertical;
+ transition: color var(--transition-normal);
+}
+
+.gallery-item-labels {
+ display: flex;
+ gap: 0.25rem;
+ padding: 0 0.5rem 0.5rem;
+}
+
+.gallery-item-label {
+ font-size: 0.625rem;
+ padding: 0.125rem 0.375rem;
+ border-radius: var(--radius-full);
+ background-color: rgba(106, 36, 254, 0.1);
+ color: var(--primary-color);
+ white-space: nowrap;
+ transition: background-color var(--transition-normal), color var(--transition-normal);
+}
+
+.dark-theme .gallery-item-label {
+ background-color: rgba(139, 92, 246, 0.2);
+ color: #A78BFA;
+}
+
+/* Loading indicator with animation */
+.loading-indicator {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ height: 100%;
+ width: 100%;
+ position: absolute;
+ top: 0;
+ left: 0;
+ background-color: rgba(255, 255, 255, 0.85);
+ z-index: 1000;
+ backdrop-filter: blur(3px);
+ border-radius: var(--radius-lg);
+ transition: background-color var(--transition-normal);
+}
+
+.dark-theme .loading-indicator {
+ background-color: rgba(15, 23, 42, 0.85);
+}
+
+.spinner {
+ width: 40px;
+ height: 40px;
+ border: 3px solid rgba(106, 36, 254, 0.2);
+ border-radius: 50%;
+ border-top-color: var(--primary-color);
+ animation: spin 1s linear infinite;
+ transition: border-color var(--transition-normal);
+}
+
+.dark-theme .spinner {
+ border: 3px solid rgba(139, 92, 246, 0.3);
+ border-top-color: var(--primary-color);
+}
+
+@keyframes spin {
+ to {
+ transform: rotate(360deg);
+ }
+}
+
+/* Progress bars with animations */
+.progress-container {
+ width: 100%;
+ height: 8px;
+ background-color: rgba(106, 36, 254, 0.1);
+ border-radius: var(--radius-full);
+ overflow: hidden;
+ margin: 0.75rem 0;
+ transition: background-color var(--transition-normal);
+}
+
+.dark-theme .progress-container {
+ background-color: rgba(139, 92, 246, 0.15);
+}
+
+.progress-bar {
+ height: 100%;
+ background: linear-gradient(to right, var(--primary-color), var(--secondary-color));
+ width: 0%;
+ transition: width 0.3s ease, background var(--transition-normal);
+ border-radius: var(--radius-full);
+ position: relative;
+ overflow: hidden;
+}
+
+.dark-theme .progress-bar {
+ background: linear-gradient(to right, #8B5CF6, #EC4899);
+}
+
+.progress-bar::after {
+ content: "";
+ position: absolute;
+ top: 0;
+ left: 0;
+ bottom: 0;
+ right: 0;
+ background-image: linear-gradient(
+ -45deg,
+ rgba(255, 255, 255, 0.2) 25%,
+ transparent 25%,
+ transparent 50%,
+ rgba(255, 255, 255, 0.2) 50%,
+ rgba(255, 255, 255, 0.2) 75%,
+ transparent 75%,
+ transparent
+ );
+ background-size: 50px 50px;
+ animation: move 2s linear infinite;
+ border-radius: var(--radius-full);
+ overflow: hidden;
+ z-index: 1;
+}
+
+@keyframes move {
+ 0% {
+ background-position: 0 0;
+ }
+ 100% {
+ background-position: 50px 50px;
+ }
+}
+
+/* Status message with icons */
+.status-message {
+ padding: 1rem 1.25rem;
+ border-radius: var(--radius-lg);
+ margin: 1rem 0;
+ font-size: 0.875rem;
+ display: flex;
+ align-items: center;
+ position: relative;
+ transition: background-color var(--transition-normal), color var(--transition-normal);
+}
+
+.status-message .icon {
+ margin-right: 0.75rem;
+ font-size: 1.25rem;
+ flex-shrink: 0;
+}
+
+.status-success {
+ background-color: rgba(16, 185, 129, 0.1);
+ color: var(--success-color);
+ border-left: 3px solid var(--success-color);
+}
+
+.dark-theme .status-success {
+ background-color: rgba(16, 185, 129, 0.15);
+}
+
+.status-error {
+ background-color: rgba(225, 29, 72, 0.1);
+ color: var(--error-color);
+ border-left: 3px solid var(--error-color);
+}
+
+.dark-theme .status-error {
+ background-color: rgba(225, 29, 72, 0.15);
+}
+
+.status-warning {
+ background-color: rgba(245, 158, 11, 0.1);
+ color: var(--warning-color);
+ border-left: 3px solid var(--warning-color);
+}
+
+.dark-theme .status-warning {
+ background-color: rgba(245, 158, 11, 0.15);
+}
+
+.status-info {
+ background-color: rgba(59, 130, 246, 0.1);
+ color: var(--info-color);
+ border-left: 3px solid var(--info-color);
+}
+
+.dark-theme .status-info {
+ background-color: rgba(59, 130, 246, 0.15);
+}
+
+/* Tabs styling with active indicators */
+.tabs {
+ display: flex;
+ border-bottom: 2px solid var(--border-color);
+ margin-bottom: 1.5rem;
+ transition: border-color var(--transition-normal);
+}
+
+.tab {
+ padding: 0.875rem 1.25rem;
+ cursor: pointer;
+ border-bottom: 2px solid transparent;
+ font-weight: 600;
+ font-size: 0.9375rem;
+ color: var(--text-muted);
+ transition: all 0.2s;
+ margin-bottom: -2px;
+ position: relative;
+ z-index: 1;
+}
+
+.tab:hover {
+ color: var(--primary-color);
+}
+
+.tab.active {
+ color: var(--primary-color);
+ border-bottom-color: var(--primary-color);
+}
+
+/* Badges for status indicators */
+.badge {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ border-radius: 9999px;
+ padding: 0.25rem 0.625rem;
+ font-size: 0.75rem;
+ font-weight: 600;
+ text-transform: uppercase;
+ letter-spacing: 0.025em;
+}
+
+.badge-success {
+ background-color: rgba(16, 185, 129, 0.1);
+ color: var(--success-color);
+}
+
+.dark-theme .badge-success {
+ background-color: rgba(16, 185, 129, 0.2);
+}
+
+.badge-warning {
+ background-color: rgba(245, 158, 11, 0.1);
+ color: var(--warning-color);
+}
+
+.dark-theme .badge-warning {
+ background-color: rgba(245, 158, 11, 0.2);
+}
+
+.badge-error {
+ background-color: rgba(225, 29, 72, 0.1);
+ color: var(--error-color);
+}
+
+.dark-theme .badge-error {
+ background-color: rgba(225, 29, 72, 0.2);
+}
+
+.badge-info {
+ background-color: rgba(59, 130, 246, 0.1);
+ color: var(--info-color);
+}
+
+.dark-theme .badge-info {
+ background-color: rgba(59, 130, 246, 0.2);
+}
+
+.badge-primary {
+ background-color: rgba(106, 36, 254, 0.1);
+ color: var(--primary-color);
+}
+
+.dark-theme .badge-primary {
+ background-color: rgba(139, 92, 246, 0.2);
+ color: #A78BFA;
+}
+
+/* Character counter with advice */
+.character-counter {
+ display: flex;
+ flex-direction: column;
+ font-size: 0.75rem;
+ color: var(--text-muted);
+ margin-top: 0.25rem;
+ transition: color 0.2s;
+}
+
+.character-counter .count {
+ font-weight: 500;
+}
+
+.character-counter .advice {
+ font-size: 0.7rem;
+ opacity: 0.9;
+ margin-top: 0.25rem;
+}
+
+.character-counter.warning {
+ color: var(--warning-color);
+}
+
+.character-counter.error {
+ color: var(--error-color);
+}
+
+.character-counter.success {
+ color: var(--success-color);
+}
+
+.character-counter.info {
+ color: var(--info-color);
+}
+
+/* Creativity slider */
+.creativity-slider-container {
+ padding: 1rem;
+ background: linear-gradient(to right, rgba(16, 185, 129, 0.1), rgba(59, 130, 246, 0.1), rgba(139, 92, 246, 0.1));
+ border-radius: var(--radius-lg);
+ margin: 1rem 0;
+}
+
+.creativity-slider-container .label {
+ display: flex;
+ justify-content: space-between;
+ font-size: 0.875rem;
+ font-weight: 500;
+ margin-bottom: 0.5rem;
+}
+
+.creativity-slider-container .label .value {
+ font-weight: 600;
+ color: var(--primary-color);
+}
+
+/* Model info card */
+.model-info {
+ background-color: rgba(106, 36, 254, 0.05);
+ border-left: 3px solid var(--primary-color);
+ padding: 1rem 1.25rem;
+ border-radius: 0 var(--radius) var(--radius) 0;
+ margin: 1rem 0;
+ transition: background-color var(--transition-normal);
+}
+
+.dark-theme .model-info {
+ background-color: rgba(139, 92, 246, 0.1);
+}
+
+.model-info h3 {
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+ margin-top: 0;
+ margin-bottom: 0.5rem;
+ font-size: 1rem;
+ font-weight: 600;
+ color: var(--primary-color);
+}
+
+.model-info p {
+ margin: 0 0 0.75rem 0;
+ font-size: 0.875rem;
+ color: var(--text-color);
+}
+
+.model-info .model-id {
+ font-size: 0.75rem;
+ color: var(--text-muted);
+ font-family: var(--font-mono);
+ background-color: rgba(0, 0, 0, 0.03);
+ padding: 0.25rem 0.5rem;
+ border-radius: 4px;
+ margin-top: 0.5rem;
+ word-break: break-all;
+}
+
+.dark-theme .model-info .model-id {
+ background-color: rgba(255, 255, 255, 0.05);
+}
+
+.model-info .strengths-weaknesses {
+ margin-top: 0.5rem;
+ font-size: 0.8125rem;
+}
+
+.model-info .highlight {
+ font-weight: 600;
+}
+
+.model-info .highlight.positive {
+ color: var(--success-color);
+}
+
+.model-info .highlight.negative {
+ color: var(--warning-color);
+}
+
+.model-info .recommended-for {
+ margin-top: 0.75rem;
+ display: flex;
+ flex-wrap: wrap;
+ gap: 0.5rem;
+ align-items: center;
+}
+
+.model-info .recommended-for p {
+ margin: 0;
+ font-size: 0.8125rem;
+ display: flex;
+ flex-wrap: wrap;
+ gap: 0.5rem;
+ align-items: center;
+}
+
+/* Parameter pills */
+.parameters-container {
+ background-color: var(--card-color);
+ border-radius: var(--radius-lg);
+ padding: 1rem;
+ margin: 1rem 0;
+ border: 1px solid var(--border-color);
+ transition: background-color var(--transition-normal), border-color var(--transition-normal);
+}
+
+.parameters-title {
+ font-weight: 600;
+ margin-bottom: 0.75rem;
+ font-size: 0.9375rem;
+ color: var(--text-color);
+}
+
+.parameters-grid {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 0.5rem;
+ margin-bottom: 0.75rem;
+}
+
+.parameter-pill {
+ display: inline-flex;
+ align-items: center;
+ background-color: rgba(106, 36, 254, 0.08);
+ color: var(--primary-color);
+ border-radius: var(--radius-full);
+ padding: 0.25rem 0.75rem;
+ font-size: 0.75rem;
+ font-weight: 500;
+ user-select: none;
+ transition: background-color var(--transition-normal), color var(--transition-normal);
+}
+
+.dark-theme .parameter-pill {
+ background-color: rgba(139, 92, 246, 0.12);
+ color: #A78BFA;
+}
+
+.parameter-pill .icon {
+ margin-right: 0.25rem;
+}
+
+.parameter-pill.model-pill {
+ background-color: rgba(59, 130, 246, 0.08);
+ color: var(--info-color);
+}
+
+.dark-theme .parameter-pill.model-pill {
+ background-color: rgba(59, 130, 246, 0.12);
+ color: #60A5FA;
+}
+
+.generation-time {
+ font-size: 0.75rem;
+ color: var(--text-muted);
+ margin-top: 0.5rem;
+}
+
+/* Tips card */
+.tips-card {
+ background-color: rgba(59, 130, 246, 0.05);
+ border-radius: var(--radius-lg);
+ padding: 1rem 1.25rem;
+ margin: 1rem 0;
+ border-left: 3px solid var(--info-color);
+ transition: background-color var(--transition-normal);
+}
+
+.dark-theme .tips-card {
+ background-color: rgba(59, 130, 246, 0.1);
+}
+
+.tips-card h4 {
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+ margin-top: 0;
+ margin-bottom: 0.75rem;
+ font-size: 1rem;
+ color: var(--info-color);
+}
+
+.tips-card ul {
+ margin: 0;
+ padding-left: 1.5rem;
+}
+
+.tips-card li {
+ margin-bottom: 0.5rem;
+ font-size: 0.875rem;
+}
+
+.tips-card li:last-child {
+ margin-bottom: 0;
+}
+
+/* Theme toggle switch */
+.theme-toggle {
+ position: fixed;
+ bottom: 1.5rem;
+ right: 1.5rem;
+ background-color: var(--card-color);
+ border-radius: var(--radius-full);
+ box-shadow: var(--shadow-lg);
+ padding: 0.625rem;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ z-index: 1000;
+ cursor: pointer;
+ border: 1px solid var(--border-color);
+ transition: background-color var(--transition-normal), border-color var(--transition-normal);
+}
+
+.theme-toggle:hover {
+ transform: translateY(-2px);
+ box-shadow: var(--shadow-xl);
+}
+
+.theme-toggle-icon {
+ font-size: 1.25rem;
+ color: var(--text-color);
+ transition: color var(--transition-normal);
+}
+
+/* Tooltip */
+.tooltip {
+ position: relative;
+ display: inline-block;
+}
+
+.tooltip .tooltip-text {
+ visibility: hidden;
+ background-color: var(--text-color);
+ color: white;
+ text-align: center;
+ border-radius: var(--radius);
+ padding: 0.5rem 0.75rem;
+ position: absolute;
+ z-index: 1;
+ bottom: 125%;
+ left: 50%;
+ margin-left: -60px;
+ opacity: 0;
+ transition: opacity 0.3s;
+ font-size: 0.75rem;
+ width: 120px;
+ pointer-events: none;
+}
+
+.tooltip .tooltip-text::after {
+ content: "";
+ position: absolute;
+ top: 100%;
+ left: 50%;
+ margin-left: -5px;
+ border-width: 5px;
+ border-style: solid;
+ border-color: var(--text-color) transparent transparent transparent;
+}
+
+.tooltip:hover .tooltip-text {
+ visibility: visible;
+ opacity: 1;
+}
+
+/* Image grid for gallery view */
+.image-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
+ gap: 1rem;
+ margin: 1.5rem 0;
+}
+
+@media (max-width: 768px) {
+ .image-grid {
+ grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
+ gap: 0.75rem;
+ }
+}
+
+@media (max-width: 480px) {
+ .image-grid {
+ grid-template-columns: repeat(auto-fill, minmax(130px, 1fr));
+ gap: 0.5rem;
+ }
+}
+
+/* Empty state illustrations */
+.empty-state {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ padding: 3rem 1.5rem;
+ text-align: center;
+ background-color: var(--card-color);
+ border-radius: var(--radius-lg);
+ border: 1px dashed var(--border-color);
+ margin: 1.5rem 0;
+ transition: background-color var(--transition-normal), border-color var(--transition-normal);
+}
+
+.empty-state .icon {
+ font-size: 3rem;
+ margin-bottom: 1.5rem;
+ color: var(--text-muted);
+}
+
+.empty-state h3 {
+ margin: 0 0 0.75rem 0;
+ font-size: 1.25rem;
+ color: var(--text-color);
+}
+
+.empty-state p {
+ margin: 0 0 1.5rem 0;
+ font-size: 0.9375rem;
+ color: var(--text-muted);
+ max-width: 500px;
+}
+
+/* Feature cards */
+.feature-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
+ gap: 1.5rem;
+ margin: 2rem 0;
+}
+
+@media (max-width: 768px) {
+ .feature-grid {
+ grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
+ gap: 1rem;
+ }
+}
+
+.feature-card {
+ background-color: var(--card-color);
+ border-radius: var(--radius-lg);
+ padding: 1.5rem;
+ box-shadow: var(--shadow);
+ transition: transform 0.2s, box-shadow 0.2s, background-color var(--transition-normal);
+ display: flex;
+ flex-direction: column;
+ border: 1px solid var(--border-color);
+}
+
+.feature-card:hover {
+ transform: translateY(-5px);
+ box-shadow: var(--shadow-lg);
+}
+
+.feature-icon {
+ font-size: 2.5rem;
+ margin-bottom: 1rem;
+ color: var(--primary-color);
+}
+
+.feature-title {
+ font-size: 1.125rem;
+ font-weight: 700;
+ margin: 0 0 0.75rem 0;
+ color: var(--text-color);
+}
+
+.feature-description {
+ font-size: 0.875rem;
+ color: var(--text-muted);
+ margin: 0;
+ flex-grow: 1;
+}
+
+/* Responsive layout adjustments */
+@media (max-width: 1200px) {
+ h1 {
+ font-size: 2.25rem !important;
+ }
+
+ h2 {
+ font-size: 1.75rem !important;
+ }
+
+ h3 {
+ font-size: 1.375rem !important;
+ }
+}
+
+@media (max-width: 768px) {
+ button.primary, button.secondary {
+ padding: 0.625rem 1.25rem !important;
+ font-size: 0.9375rem !important;
+ }
+
+ .app-header {
+ padding: 2rem 1rem 1.5rem;
+ }
+
+ .gallery-item-image {
+ aspect-ratio: 1;
+ }
+}
+
+@media (max-width: 640px) {
+ .tabs {
+ overflow-x: auto;
+ -webkit-overflow-scrolling: touch;
+ white-space: nowrap;
+ padding-bottom: 0.25rem;
+ }
+
+ .tab {
+ padding: 0.75rem 1rem;
+ }
+}
+"""
+
+ # Create new interface
+ with gr.Blocks(title=APP_CONFIG["name"], css=css) as interface:
+ # Header with branding
+ with gr.Row(elem_classes="app-header"):
+ with gr.Column():
+ gr.HTML(f"""
+ {APP_CONFIG["name"]}
+ {APP_CONFIG["tagline"]}
+ {APP_CONFIG["description"]}
+ """)
+
+ # Main content area - tabs for different sections
+ with gr.Tabs() as tabs:
+ # Create tab
+ with gr.TabItem("Create", id="create-tab") as create_tab:
+ with gr.Row(equal_height=False):
+ # Left column - Input controls
+ with gr.Column(scale=1):
+ # Description input with character counter
+ with gr.Group():
+ description_input = gr.Textbox(
+ label="Describe what you want to see",
+ placeholder="Be detailed and specific about colors, composition, lighting, and subject...",
+ lines=4,
+ max_lines=8,
+ elem_id="description-input"
+ )
+
+ # Character counter with dynamic updates
+ char_counter = gr.HTML(
+ value=update_char_count(""),
+ elem_classes="char-counter-container"
+ )
+
+ # Settings accordion
+ with gr.Accordion("Image Settings", open=True):
+ # Creation Type and Art Style in one row
+ with gr.Row():
+ # Creation type dropdown with icons
+ creation_type = gr.Dropdown(
+ choices=format_dropdown_choices(CREATION_TYPES),
+ value=f"{CREATION_TYPES['Digital Art']['icon']} Digital Art",
+ label="Creation Type",
+ elem_classes="enhanced-dropdown"
+ )
+
+ # Art style dropdown with icons
+ art_style = gr.Dropdown(
+ choices=format_dropdown_choices(ART_STYLES),
+ value=f"{ART_STYLES['Photorealistic']['icon']} Photorealistic",
+ label="Art Style",
+ elem_classes="enhanced-dropdown"
+ )
+
+ # Mood and Model in one row
+ with gr.Row():
+ # Mood dropdown with icons
+ mood_dropdown = gr.Dropdown(
+ choices=format_dropdown_choices(MOODS),
+ value=f"{MOODS['Peaceful']['icon']} Peaceful",
+ label="Mood",
+ elem_classes="enhanced-dropdown"
+ )
+
+ # Model selector with display names
+ formatted_models = [f"{info['icon']} {info['display_name']}" for model_key, info in IMAGE_MODELS.items()]
+ model_selector = gr.Dropdown(
+ choices=formatted_models,
+ value=f"{IMAGE_MODELS['stabilityai/stable-diffusion-xl-base-1.0']['icon']} {IMAGE_MODELS['stabilityai/stable-diffusion-xl-base-1.0']['display_name']}",
+ label="Model",
+ elem_classes="enhanced-dropdown"
+ )
+
+ # Advanced options
+ with gr.Accordion("Advanced Options", open=False):
+ with gr.Row():
+ # Aspect ratio dropdown
+ aspect_ratio = gr.Dropdown(
+ choices=format_dropdown_choices(ASPECT_RATIOS),
+ value=f"{ASPECT_RATIOS['1:1 (Square)']['icon']} 1:1 (Square)",
+ label="Aspect Ratio",
+ elem_classes="enhanced-dropdown"
+ )
+
+ # Seed input
+ seed_input = gr.Number(
+ label="Seed (optional)",
+ value=None,
+ precision=0,
+ elem_id="seed-input"
+ )
+
+ with gr.Row():
+ # Color palette dropdown
+ color_palette = gr.Dropdown(
+ choices=format_dropdown_choices(COLOR_PALETTES),
+ value=None,
+ label="Color Palette (optional)",
+ elem_classes="enhanced-dropdown"
+ )
+ # Special effects dropdown
+ special_effect = gr.Dropdown(
+ choices=format_dropdown_choices(SPECIAL_EFFECTS),
+ value=f"{SPECIAL_EFFECTS['None']['icon']} None",
+ label="Special Effect (optional)",
+ elem_classes="enhanced-dropdown"
+ )
+
+ # Examples gallery with clear visual structure
+ with gr.Accordion("Try an Example", open=True):
+ # Gallery of examples
+ with gr.Row(elem_classes="gallery"):
+ for i, example in enumerate(EXAMPLE_PROMPTS[:8]): # Show first 8 examples
+ with gr.Column(elem_classes="gallery-item"):
+ # Example card with visual element and caption
+ example_btn = gr.Button(
+ example["thumbnail_desc"],
+ elem_classes="example-button"
+ )
+
+ # Event handler for example selection
+ example_btn.click(
+ fn=lambda idx=i: load_example(idx),
+ outputs=[description_input, creation_type, art_style, mood_dropdown,
+ aspect_ratio, color_palette, special_effect]
+ )
+
+ # Generate buttons with batch option
+ with gr.Row():
+ # Single image generation
+ generate_btn = gr.Button("โจ Generate Image", variant="primary", elem_classes="primary", elem_id="generate-btn")
+
+ # Generate multiple variations
+ variations_btn = gr.Button("๐ Generate 4 Variations", elem_classes="secondary", elem_id="variations-btn")
+
+ # Generation status message
+ generation_status = gr.HTML(value="", elem_classes="generation-status")
+
+ # Right column - Output display
+ with gr.Column(scale=1):
+ # Tabs for single image and variations
+ with gr.Tabs() as image_tabs:
+ # Single image tab
+ with gr.TabItem("Image", id="single-tab"):
+ # Image display area with placeholder
+ with gr.Group(elem_classes="output-container"):
+ image_output = gr.Image(
+ label="Generated Image",
+ elem_id="image-output",
+ type="pil",
+ height=512,
+ interactive=False
+ )
+
+ # Action buttons for generated image
+ with gr.Row():
+ # Download button
+ download_btn = gr.Button("๐พ Save Image", elem_classes="secondary")
+
+ # Add to favorites
+ favorite_btn = gr.Button("โค๏ธ Add to Favorites", elem_classes="secondary")
+
+ # Generate variations of this image
+ more_variations_btn = gr.Button("๐ More Like This", elem_classes="secondary")
+
+ # Image generation details
+ with gr.Accordion("Image Details", open=True):
+ parameters_display = gr.HTML(value="")
+
+ # Enhanced prompt display
+ with gr.Accordion("AI-Enhanced Prompt", open=False):
+ prompt_output = gr.Textbox(
+ label="AI-Enhanced Prompt Used",
+ lines=5,
+ elem_id="prompt-output",
+ elem_classes="prompt-display"
+ )
+
+ # Variations tab
+ with gr.TabItem("Variations", id="variations-tab"):
+ # Variations gallery
+ with gr.Group(elem_classes="variations-gallery"):
+ variations_gallery = gr.Gallery(
+ label="Image Variations",
+ elem_id="variations-gallery",
+ columns=2,
+ rows=2,
+ height=512,
+ object_fit="contain"
+ )
+
+ # Variations info
+ variations_info = gr.HTML(value="")
+
+ # Information panels for selected options
+ with gr.Group(elem_classes="info-panels"):
+ # Creation type info
+ creation_info = gr.HTML(value="", elem_classes="creation-info")
+
+ # Art style info
+ art_info = gr.HTML(value="", elem_classes="art-info")
+
+ # Model info panel
+ model_info = gr.HTML(value="", elem_classes="model-info")
+
+ # Mood info
+ mood_info = gr.HTML(value="", elem_classes="mood-info")
+
+ # Gallery tab with history and favorites
+ with gr.TabItem("My Creations", id="gallery-tab") as gallery_tab:
+ with gr.Tabs() as gallery_tabs:
+ # History tab
+ with gr.TabItem("History", id="history-tab"):
+ # History toolbar
+ with gr.Row():
+ refresh_history_btn = gr.Button("๐ Refresh", elem_classes="secondary")
+ clear_history_btn = gr.Button("๐๏ธ Clear History", elem_classes="secondary")
+
+ # History gallery
+ history_gallery = gr.Gallery(
+ label="Your Generated Images",
+ elem_id="history-gallery",
+ columns=4,
+ rows=3,
+ height=600,
+ object_fit="contain"
+ )
+
+ # History info
+ history_info = gr.HTML(value="")
+
+ # Favorites tab
+ with gr.TabItem("Favorites", id="favorites-tab"):
+ # Favorites toolbar
+ with gr.Row():
+ refresh_favorites_btn = gr.Button("๐ Refresh", elem_classes="secondary")
+
+ # Favorites gallery
+ favorites_gallery = gr.Gallery(
+ label="Your Favorite Images",
+ elem_id="favorites-gallery",
+ columns=4,
+ rows=3,
+ height=600,
+ object_fit="contain"
+ )
+
+ # Favorites info
+ favorites_info = gr.HTML(value="")
+
+ # Explore tab with creative examples and guided mode
+ with gr.TabItem("Explore", id="explore-tab") as explore_tab:
+ with gr.Tabs() as explore_tabs:
+ # Creative prompts tab
+ with gr.TabItem("Inspiration", id="inspiration-tab"):
+ gr.HTML("""
+
+
+
๐ง
+
Fantasy Worlds
+
Explore magical realms, mythical creatures, and enchanted landscapes from your imagination.
+
+
+
๐
+
Sci-Fi Visions
+
Create futuristic technology, alien worlds, and space adventures in stunning detail.
+
+
+
๐
+
Cosmic Dreams
+
Visualize galaxies, nebulae, and celestial phenomena beyond human imagination.
+
+
+
๐ค
+
Character Design
+
Bring unique characters to life for stories, games, or personal projects.
+
+
+
๐๏ธ
+
Cityscape Concepts
+
Design futuristic cities, cyberpunk streets, or nostalgic urban environments.
+
+
+
๐จ
+
Artistic Styles
+
Experiment with different art styles from renaissance to modern abstract expressions.
+
+
+ """)
+
+ # Creative prompts gallery
+ creative_prompts = gr.Gallery(
+ label="Trending Creations",
+ elem_id="creative-prompts",
+ columns=3,
+ rows=2,
+ height=500,
+ object_fit="contain"
+ )
+
+ # Guided creation tab
+ with gr.TabItem("Guided Creation", id="guided-tab"):
+ gr.HTML("""
+
+
โจ Guided Creation Mode
+
Answer the questions below to create a detailed prompt. Our AI will help you transform your ideas into amazing images.
+
+ """)
+
+ # Step-by-step guided prompt creation
+ with gr.Group():
+ # Subject
+ guided_subject = gr.Textbox(
+ label="What is the main subject?",
+ placeholder="Examples: a majestic dragon, a cybernetic samurai, a cozy cottage...",
+ elem_id="guided-subject"
+ )
+
+ # Setting
+ guided_setting = gr.Textbox(
+ label="Where is it located? (Setting/Environment)",
+ placeholder="Examples: in a misty forest, on a distant planet, in a futuristic city...",
+ elem_id="guided-setting"
+ )
+
+ # Lighting and atmosphere
+ guided_lighting = gr.Textbox(
+ label="Describe the lighting and atmosphere",
+ placeholder="Examples: sunset with golden rays, blue moonlight, neon lights...",
+ elem_id="guided-lighting"
+ )
+
+ # Additional details
+ guided_details = gr.Textbox(
+ label="Additional details (optional)",
+ placeholder="Examples: wearing a red cloak, with glowing eyes, surrounded by flowers...",
+ elem_id="guided-details"
+ )
+
+ # Style preferences
+ with gr.Row():
+ guided_art_style = gr.Dropdown(
+ choices=format_dropdown_choices(ART_STYLES),
+ value=f"{ART_STYLES['Photorealistic']['icon']} Photorealistic",
+ label="Art Style",
+ elem_classes="enhanced-dropdown"
+ )
+
+ guided_mood = gr.Dropdown(
+ choices=format_dropdown_choices(MOODS),
+ value=f"{MOODS['Peaceful']['icon']} Peaceful",
+ label="Mood",
+ elem_classes="enhanced-dropdown"
+ )
+
+ # Build prompt button
+ build_prompt_btn = gr.Button("๐ช Build My Prompt", variant="primary", elem_classes="primary")
+
+ # Generated prompt preview
+ guided_preview = gr.Textbox(
+ label="Your Generated Prompt",
+ lines=4,
+ elem_id="guided-preview"
+ )
+
+ # Use this prompt button
+ use_prompt_btn = gr.Button("โ
Use This Prompt", elem_classes="secondary")
+
+ # Settings tab
+ with gr.TabItem("Settings", id="settings-tab") as settings_tab:
+ with gr.Tabs() as settings_tabs:
+ # Appearance settings
+ with gr.TabItem("Appearance", id="appearance-tab"):
+ # Theme selector
+ theme_selector = gr.Radio(
+ choices=["Light", "Dark", "Auto"],
+ value=current_user.preferences["theme"],
+ label="Theme",
+ elem_id="theme-selector"
+ )
+
+ # Apply button
+ apply_theme_btn = gr.Button("Apply Theme", elem_classes="secondary")
+
+ # Defaults settings
+ with gr.TabItem("Defaults", id="defaults-tab"):
+ # Default model
+ default_model = gr.Dropdown(
+ choices=formatted_models,
+ value=f"{IMAGE_MODELS[current_user.preferences['default_model']]['icon']} {IMAGE_MODELS[current_user.preferences['default_model']]['display_name']}",
+ label="Default Model",
+ elem_classes="enhanced-dropdown"
+ )
+
+ # Default creation type
+ default_creation_type = gr.Dropdown(
+ choices=format_dropdown_choices(CREATION_TYPES),
+ value=f"{CREATION_TYPES[current_user.preferences['default_creation_type']]['icon']} {current_user.preferences['default_creation_type']}",
+ label="Default Creation Type",
+ elem_classes="enhanced-dropdown"
+ )
+
+ # Default aspect ratio
+ default_aspect_ratio = gr.Dropdown(
+ choices=format_dropdown_choices(ASPECT_RATIOS),
+ value=f"{ASPECT_RATIOS[current_user.preferences['default_aspect_ratio']]['icon']} {current_user.preferences['default_aspect_ratio']}",
+ label="Default Aspect Ratio",
+ elem_classes="enhanced-dropdown"
+ )
+
+ # Show tips
+ show_tips = gr.Checkbox(
+ value=current_user.preferences["show_tips"],
+ label="Show Tips and Suggestions",
+ elem_id="show-tips"
+ )
+
+ # Advanced mode
+ advanced_mode = gr.Checkbox(
+ value=current_user.preferences["advanced_mode"],
+ label="Advanced Mode (Show all options by default)",
+ elem_id="advanced-mode"
+ )
+
+ # Save defaults button
+ save_defaults_btn = gr.Button("Save Defaults", elem_classes="secondary")
+
+ # About tab
+ with gr.TabItem("About", id="about-tab"):
+ gr.HTML(f"""
+
+
{APP_CONFIG["name"]} v{APP_CONFIG["version"]}
+
{APP_CONFIG["tagline"]}
+
+
+
About This Application
+
Imaginova is a powerful AI image generation platform that helps you bring your creative visions to life.
+ Built with advanced diffusion models, Imaginova transforms your text descriptions into stunning,
+ high-quality images across various styles and artistic mediums.
+
+
Features
+
+ - Multiple AI Models: Access to several state-of-the-art image generation models
+ - Creative Control: Customize your images with different styles, moods, and special effects
+ - Prompt Enhancement: AI-powered prompt improvement for better results
+ - Guided Creation: Step-by-step assistance to build detailed prompts
+ - Image History: Save and organize your creations
+ - Batch Generation: Create multiple variations of your ideas
+
+
+
Credits
+
Powered by Hugging Face's Diffusion Models and built with Gradio.
+
Created with โค๏ธ for the creative community.
+
+
+ """)
+
+ # Hidden elements for state management
+ current_seed = gr.Number(value=None, visible=False)
+ current_image_id = gr.Textbox(value="", visible=False)
+
+ # Event handlers
+
+ # Character counter update
+ description_input.change(
+ fn=update_char_count,
+ inputs=description_input,
+ outputs=char_counter
+ )
+
+ # Info panels updates
+ creation_type.change(
+ fn=update_creation_info,
+ inputs=creation_type,
+ outputs=creation_info
+ )
+
+ art_style.change(
+ fn=update_art_style_info,
+ inputs=art_style,
+ outputs=art_info
+ )
+
+ model_selector.change(
+ fn=update_model_info,
+ inputs=model_selector,
+ outputs=model_info
+ )
+
+ mood_dropdown.change(
+ fn=update_mood_info,
+ inputs=mood_dropdown,
+ outputs=mood_info
+ )
+
+ # Generate image button
+ generate_btn.click(
+ fn=generate_with_status,
+ inputs=[
+ description_input,
+ creation_type,
+ art_style,
+ mood_dropdown,
+ aspect_ratio,
+ color_palette,
+ special_effect,
+ model_selector,
+ seed_input
+ ],
+ outputs=[
+ image_output,
+ generation_status,
+ prompt_output,
+ parameters_display,
+ current_seed
+ ]
+ )
+
+ # Add generated image to history
+ def add_to_history_handler(image, description, prompt, parameters, seed):
+ """Add the generated image to user history"""
+ if image is None:
+ return gr.HTML.update(value="No image to save")
+
+ # Add to history
+ image_id = str(uuid.uuid4())
+ success = current_user.add_to_history(image, description, prompt, parameters, seed)
+
+ if success:
+ return gr.Textbox.update(value=image_id)
+ else:
+ return gr.Textbox.update(value="")
+
+ # After successful generation, add to history
+ image_output.change(
+ fn=add_to_history_handler,
+ inputs=[
+ image_output,
+ description_input,
+ prompt_output,
+ parameters_display,
+ current_seed
+ ],
+ outputs=[current_image_id]
+ )
+
+ # Generate variations
+ def generate_variations_handler(description, creation_type_val, art_style_val, mood_val,
+ aspect_ratio_val, color_palette_val, special_effect_val, model_name):
+ """Generate multiple variations of an image"""
+ try:
+ # Extract keys from formatted values
+ creation_key = extract_key(creation_type_val)
+ art_key = extract_key(art_style_val)
+ mood_key = extract_key(mood_val)
+ aspect_key = extract_key(aspect_ratio_val)
+ palette_key = extract_key(color_palette_val)
+ effect_key = extract_key(special_effect_val)
+
+ # Get model key
+ model_key = get_model_key_from_display_name(model_name)
+
+ # Generate variations (4 by default)
+ results = generate_variations(
+ description, model_key, creation_key, art_key, mood_key,
+ aspect_key, palette_key, effect_key, num_images=4
+ )
+
+ # Extract images and seeds
+ images = [img for img, _ in results]
+ seeds = [seed for _, seed in results]
+
+ # Create info text
+ if images:
+ seeds_text = ", ".join([str(s) for s in seeds])
+ info_html = f"""
+ โ
+ Generated {len(images)} variations successfully. Seeds: {seeds_text}
+
"""
+ else:
+ info_html = """
+ โ
+ Failed to generate variations. Please try again.
+
"""
+
+ # Switch to variations tab
+ image_tabs.select("variations-tab")
+
+ return images, info_html
+
+ except Exception as e:
+ error_html = f"""
+ โ
+ Error generating variations: {str(e)}
+
"""
+ return [], error_html
+
+ # Variations button click
+ variations_btn.click(
+ fn=generate_variations_handler,
+ inputs=[
+ description_input,
+ creation_type,
+ art_style,
+ mood_dropdown,
+ aspect_ratio,
+ color_palette,
+ special_effect,
+ model_selector
+ ],
+ outputs=[
+ variations_gallery,
+ variations_info
+ ]
+ )
+
+ # Guided creation handlers
+ def build_guided_prompt(subject, setting, lighting, details, art_style_val, mood_val):
+ """Build a detailed prompt from guided inputs"""
+ if not subject:
+ return "Please describe the main subject first"
+
+ # Extract keys
+ art_key = extract_key(art_style_val)
+ mood_key = extract_key(mood_val)
+
+ # Get art style info
+ art_style_desc = ""
+ if art_key and art_key in ART_STYLES:
+ art_info = ART_STYLES[art_key]
+ style_terms = ", ".join(art_info.get("technical_terms", [])[:2])
+ art_style_desc = f"in {art_key} style, {style_terms}"
+
+ # Get mood info
+ mood_desc = ""
+ if mood_key and mood_key in MOODS:
+ mood_info = MOODS[mood_key]
+ mood_desc = f"with {mood_key.lower()} mood, {mood_info.get('lighting', '')}"
+
+ # Build prompt parts
+ prompt_parts = []
+
+ # Main subject
+ prompt_parts.append(subject.strip())
+
+ # Setting/environment
+ if setting:
+ prompt_parts.append(setting.strip())
+
+ # Lighting and atmosphere
+ if lighting:
+ prompt_parts.append(lighting.strip())
+
+ # Additional details
+ if details:
+ prompt_parts.append(details.strip())
+
+ # Style and mood
+ if art_style_desc:
+ prompt_parts.append(art_style_desc)
+
+ if mood_desc:
+ prompt_parts.append(mood_desc)
+
+ # Quality terms
+ quality_terms = "highly detailed, masterful, 8K resolution, professional, sharp focus, high quality"
+ prompt_parts.append(quality_terms)
+
+ # Build final prompt
+ prompt = ", ".join(prompt_parts)
+
+ return prompt
+
+ # Build prompt button
+ build_prompt_btn.click(
+ fn=build_guided_prompt,
+ inputs=[
+ guided_subject,
+ guided_setting,
+ guided_lighting,
+ guided_details,
+ guided_art_style,
+ guided_mood
+ ],
+ outputs=[guided_preview]
+ )
+
+ # Use guided prompt
+ def use_guided_prompt(prompt):
+ """Use the guided prompt in the main create tab"""
+ # Switch to create tab
+ tabs.select("create-tab")
+
+ return prompt
+
+ # Use prompt button
+ use_prompt_btn.click(
+ fn=use_guided_prompt,
+ inputs=[guided_preview],
+ outputs=[description_input]
+ )
+
+ # History and favorites handlers
+ def load_history():
+ """Load user history for gallery display"""
+ history = current_user.get_history()
+
+ if not history:
+ info_html = """
+
๐ผ๏ธ
+
No Images Yet
+
Your created images will appear here. Start generating to build your collection!
+
"""
+ return [], info_html
+
+ # Extract images
+ images = [entry.get("image") for entry in history if "image" in entry]
+
+ info_html = f"""
+
Showing {len(images)} images from your history
+
"""
+
+ return images, info_html
+
+ # Load history on tab click
+ gallery_tab.select(
+ fn=load_history,
+ inputs=[],
+ outputs=[history_gallery, history_info]
+ )
+
+ # Refresh history button
+ refresh_history_btn.click(
+ fn=load_history,
+ inputs=[],
+ outputs=[history_gallery, history_info]
+ )
+
+ # Clear history button
+ def clear_history():
+ """Clear user history"""
+ success = current_user.clear_history()
+
+ if success:
+ info_html = """
+ โ
+ History cleared successfully
+
"""
+ else:
+ info_html = """
+ โ
+ Failed to clear history
+
"""
+
+ return [], info_html
+
+ # Clear history button
+ clear_history_btn.click(
+ fn=clear_history,
+ inputs=[],
+ outputs=[history_gallery, history_info]
+ )
+
+ # Load favorites
+ def load_favorites():
+ """Load user favorites for gallery display"""
+ favorites = current_user.get_favorites()
+
+ if not favorites:
+ info_html = """
+
โค๏ธ
+
No Favorites Yet
+
Your favorite images will appear here. Add images to favorites to build your collection!
+
"""
+ return [], info_html
+
+ # Extract images
+ images = [entry.get("image") for entry in favorites if "image" in entry]
+
+ info_html = f"""
+
Showing {len(images)} favorite images
+
"""
+
+ return images, info_html
+
+ # Refresh favorites button
+ refresh_favorites_btn.click(
+ fn=load_favorites,
+ inputs=[],
+ outputs=[favorites_gallery, favorites_info]
+ )
+
+ # Add to favorites
+ def add_to_favorites(image_id):
+ """Add an image to favorites"""
+ if not image_id:
+ return """
+ โ
+ No image to add to favorites
+
"""
+
+ success = current_user.add_to_favorites(image_id)
+
+ if success:
+ return """
+ โ
+ Image added to favorites
+
"""
+ else:
+ return """
+ โ ๏ธ
+ Image already in favorites or not found
+
"""
+
+ # Add to favorites button
+ favorite_btn.click(
+ fn=add_to_favorites,
+ inputs=[current_image_id],
+ outputs=[generation_status]
+ )
+
+ # Update theme
+ def update_theme(theme):
+ """Update user theme preference"""
+ if theme not in ["Light", "Dark", "Auto"]:
+ theme = "Light"
+
+ current_user.update_preferences({"theme": theme})
+
+ # Return JavaScript to update theme
+ if theme == "Dark":
+ return """
+ document.body.classList.add('dark-theme');
+ document.body.classList.remove('light-theme');
+ """
+ elif theme == "Light":
+ return """
+ document.body.classList.remove('dark-theme');
+ document.body.classList.add('light-theme');
+ """
+ else: # Auto
+ return """
+ // Check system preference
+ if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
+ document.body.classList.add('dark-theme');
+ document.body.classList.remove('light-theme');
+ } else {
+ document.body.classList.remove('dark-theme');
+ document.body.classList.add('light-theme');
+ }
+
+ // Listen for system changes
+ window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', e => {
+ if (e.matches) {
+ document.body.classList.add('dark-theme');
+ document.body.classList.remove('light-theme');
+ } else {
+ document.body.classList.remove('dark-theme');
+ document.body.classList.add('light-theme');
+ }
+ });
+ """
+
+ # Apply theme button
+ apply_theme_btn.click(
+ fn=update_theme,
+ inputs=[theme_selector],
+ outputs=[gr.Javascript()]
+ )
+
+ # Update default settings
+ def update_defaults(model, creation_type, aspect_ratio, show_tips, advanced_mode):
+ """Update user default settings"""
+ # Extract values
+ model_key = get_model_key_from_display_name(model)
+ creation_key = extract_key(creation_type)
+ aspect_key = extract_key(aspect_ratio)
+
+ # Update preferences
+ preferences = {
+ "default_model": model_key,
+ "default_creation_type": creation_key,
+ "default_aspect_ratio": aspect_key,
+ "show_tips": show_tips,
+ "advanced_mode": advanced_mode
+ }
+
+ success = current_user.update_preferences(preferences)
+
+ if success:
+ return """
+ โ
+ Default settings saved successfully
+
"""
+ else:
+ return """
+ โ
+ Failed to save default settings
+
"""
+
+ # Save defaults button
+ save_defaults_btn.click(
+ fn=update_defaults,
+ inputs=[
+ default_model,
+ default_creation_type,
+ default_aspect_ratio,
+ show_tips,
+ advanced_mode
+ ],
+ outputs=[gr.HTML()]
+ )
+
+ # Load default values on page load
+ # Load default values on page load
+ interface.load(
+ fn=lambda: (
+ update_creation_info(f"{CREATION_TYPES['Digital Art']['icon']} Digital Art"),
+ update_art_style_info(f"{ART_STYLES['Photorealistic']['icon']} Photorealistic"),
+ update_model_info(f"{IMAGE_MODELS['stabilityai/stable-diffusion-xl-base-1.0']['icon']} {IMAGE_MODELS['stabilityai/stable-diffusion-xl-base-1.0']['display_name']}"),
+ update_mood_info(f"{MOODS['Peaceful']['icon']} Peaceful"),
+ # Initial theme setting based on user preference
+ update_theme(current_user.preferences["theme"])
+ ),
+ outputs=[creation_info, art_info, model_info, mood_info, gr.Javascript()]
+ )
+
+ return interface
+
+# =============== MAIN APPLICATION FLOW ===============
+
+def main():
+ """Main application entry point - creates UI and sets up event handlers"""
+ logger.info(f"Starting {APP_CONFIG['name']} v{APP_CONFIG['version']}")
+
+ # Create the UI components
+ interface = create_ui()
+
+ # Launch the interface
+ launch_kwargs = {}
+
+ # If running in Hugging Face Spaces
+ if APP_CONFIG["huggingface_space"]:
+ launch_kwargs.update({
+ "share": False,
+ "server_name": "0.0.0.0",
+ "server_port": 7860
+ })
+ else:
+ # For local development
+ launch_kwargs.update({
+ "share": False,
+ "server_name": "127.0.0.1",
+ "server_port": 7860
+ })
+
+ try:
+ interface.launch(**launch_kwargs)
+ except Exception as e:
+ logger.error(f"Error launching interface: {str(e)}")
+ interface.launch() # Fallback to default launch
+
+# Entry point
+if __name__ == "__main__":
+ main()
\ No newline at end of file