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""" + + """ + + return f"""
+

{info['icon']} {key}

+

{info['description']}

+

๐Ÿ’ก {info['prompt_hint']}

+
+

Examples: {examples}

+
+ {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""" +
+

Keywords: {terms}

+
+ """ + + 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