imaginova1 / app.py
Prak2005's picture
Update app.py
3208271 verified
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"""<div class='character-counter {color_class}'>
<span class='count'>{count} characters</span>
<span class='advice'>{advice}</span>
</div>"""
# 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"<span class='example-tag'>{ex}</span>" for ex in info.get('examples', [])])
recommended_models = ""
if 'recommended_models' in info:
model_list = [f"<span class='recommended-model'>{model}</span>" for model in info['recommended_models']]
recommended_models = f"""
<div class="recommended-models">
<p>✓ Recommended models: {", ".join(model_list)}</p>
</div>
"""
return f"""<div class="info-card">
<h3>{info['icon']} {key}</h3>
<p>{info['description']}</p>
<p class="prompt-hint">💡 {info['prompt_hint']}</p>
<div class="examples-section">
<p>Examples: {examples}</p>
</div>
{recommended_models}
</div>"""
# 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"<span class='technical-term'>{term}</span>" for term in info['technical_terms']])
technical_terms = f"""
<div class="technical-terms">
<p>Keywords: {terms}</p>
</div>
"""
return f"""<div class="info-card">
<h3>{info['icon']} {key}</h3>
<p>{info['description']}</p>
<p class="examples-text">Inspired by: {info['examples']}</p>
{technical_terms}
</div>"""
# 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 = '<span class="badge badge-success">Ultra Fast</span>'
elif info.get('speed') == 'fast':
speed_badge = '<span class="badge badge-success">Fast</span>'
elif info.get('speed') == 'medium':
speed_badge = '<span class="badge badge-warning">Medium</span>'
elif info.get('speed') == 'slow':
speed_badge = '<span class="badge badge-error">Slower</span>'
# Create quality badge
quality_badge = ""
if info.get('quality') == 'premium':
quality_badge = '<span class="badge badge-success">Premium Quality</span>'
elif info.get('quality') == 'excellent':
quality_badge = '<span class="badge badge-success">Excellent Quality</span>'
elif info.get('quality') == 'very good':
quality_badge = '<span class="badge badge-success">Very Good Quality</span>'
elif info.get('quality') == 'good':
quality_badge = '<span class="badge badge-info">Good Quality</span>'
elif info.get('quality') == 'specialized':
quality_badge = '<span class="badge badge-info">Specialized</span>'
elif info.get('quality') == 'artistic':
quality_badge = '<span class="badge badge-info">Artistic</span>'
# Create recommended for badges
recommended_badges = ""
if 'recommended_for' in info:
badges = " ".join([f'<span class="badge badge-primary">{item}</span>' for item in info['recommended_for'][:3]])
recommended_badges = f"""<div class="recommended-for">
<p>Ideal for: {badges}</p>
</div>"""
# Create strengths and weaknesses section
strengths_weaknesses = ""
if 'strengths' in info and 'weaknesses' in info:
strengths_weaknesses = f"""<div class="strengths-weaknesses">
<p><span class="highlight positive">✓ Strengths:</span> {info['strengths']}</p>
<p><span class="highlight negative">✗ Limitations:</span> {info['weaknesses']}</p>
</div>"""
return f"""<div class="model-info">
<h3>{info['icon']} {info['display_name']} {speed_badge} {quality_badge}</h3>
<p>{info['description']}</p>
{strengths_weaknesses}
{recommended_badges}
<div class="model-id">{key}</div>
</div>"""
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"""<div class="info-card mood-card">
<h3>{info['icon']} {key}</h3>
<p>{info['description']}</p>
<p class="color-info">🎨 {info['color_palette']}</p>
<p class="lighting-info">💡 {info['lighting']}</p>
</div>"""
# 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"""<div class="status-message {status_class}">
<span class="icon">{icon}</span>
<span>{message}</span>
</div>"""
# 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 = """<div class="parameters-container">
<div class="parameters-title">Generated with these parameters:</div>
<div class="parameters-grid">"""
# Add creation type pill
if creation_key and creation_key in CREATION_TYPES:
html += f"""<div class="parameter-pill">
<span class="icon">{CREATION_TYPES[creation_key]['icon']}</span>
<span class="label">{creation_key}</span>
</div>"""
# Add art style pill
if art_key and art_key in ART_STYLES:
html += f"""<div class="parameter-pill">
<span class="icon">{ART_STYLES[art_key]['icon']}</span>
<span class="label">{art_key}</span>
</div>"""
# Add mood pill
if mood_key and mood_key in MOODS:
html += f"""<div class="parameter-pill">
<span class="icon">{MOODS[mood_key]['icon']}</span>
<span class="label">{mood_key}</span>
</div>"""
# Add aspect ratio pill
if aspect_key and aspect_key in ASPECT_RATIOS:
html += f"""<div class="parameter-pill">
<span class="icon">{ASPECT_RATIOS[aspect_key]['icon']}</span>
<span class="label">{aspect_key}</span>
</div>"""
# Add color palette pill
if palette_key and palette_key in COLOR_PALETTES:
html += f"""<div class="parameter-pill">
<span class="icon">{COLOR_PALETTES[palette_key]['icon']}</span>
<span class="label">{palette_key}</span>
</div>"""
# Add special effect pill
if effect_key and effect_key in SPECIAL_EFFECTS and effect_key != "None":
html += f"""<div class="parameter-pill">
<span class="icon">{SPECIAL_EFFECTS[effect_key]['icon']}</span>
<span class="label">{effect_key}</span>
</div>"""
# Add model pill
html += f"""<div class="parameter-pill model-pill">
<span class="icon">{model_icon}</span>
<span class="label">{model_display_name}</span>
</div>"""
# Close container
html += """</div>
<div class="generation-time">
<span class="timestamp">Generated on {timestamp}</span>
</div>
</div>""".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"<system>\n{system_prompt}\n</system>\n\n<user>\n{user_prompt}\n</user>\n\n<assistant>",
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("<assistant>"):
enhanced = enhanced[len("<assistant>"):].strip()
if enhanced.endswith("</assistant>"):
enhanced = enhanced[:-len("</assistant>")].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(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"""
<h1>{APP_CONFIG["name"]}</h1>
<div class="app-tagline">{APP_CONFIG["tagline"]}</div>
<p>{APP_CONFIG["description"]}</p>
""")
# 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("""
<div class="feature-grid">
<div class="feature-card">
<div class="feature-icon">🧙</div>
<h3 class="feature-title">Fantasy Worlds</h3>
<p class="feature-description">Explore magical realms, mythical creatures, and enchanted landscapes from your imagination.</p>
</div>
<div class="feature-card">
<div class="feature-icon">🚀</div>
<h3 class="feature-title">Sci-Fi Visions</h3>
<p class="feature-description">Create futuristic technology, alien worlds, and space adventures in stunning detail.</p>
</div>
<div class="feature-card">
<div class="feature-icon">🌌</div>
<h3 class="feature-title">Cosmic Dreams</h3>
<p class="feature-description">Visualize galaxies, nebulae, and celestial phenomena beyond human imagination.</p>
</div>
<div class="feature-card">
<div class="feature-icon">👤</div>
<h3 class="feature-title">Character Design</h3>
<p class="feature-description">Bring unique characters to life for stories, games, or personal projects.</p>
</div>
<div class="feature-card">
<div class="feature-icon">🏙️</div>
<h3 class="feature-title">Cityscape Concepts</h3>
<p class="feature-description">Design futuristic cities, cyberpunk streets, or nostalgic urban environments.</p>
</div>
<div class="feature-card">
<div class="feature-icon">🎨</div>
<h3 class="feature-title">Artistic Styles</h3>
<p class="feature-description">Experiment with different art styles from renaissance to modern abstract expressions.</p>
</div>
</div>
""")
# 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("""
<div class="tips-card">
<h4>✨ Guided Creation Mode</h4>
<p>Answer the questions below to create a detailed prompt. Our AI will help you transform your ideas into amazing images.</p>
</div>
""")
# 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"""
<div style="text-align: center; padding: 1rem 0;">
<h2 style="margin-bottom: 0.5rem;">{APP_CONFIG["name"]} v{APP_CONFIG["version"]}</h2>
<p style="margin-bottom: 2rem;">{APP_CONFIG["tagline"]}</p>
<div style="max-width: 800px; margin: 0 auto; text-align: left;">
<h3>About This Application</h3>
<p>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.</p>
<h3>Features</h3>
<ul>
<li><strong>Multiple AI Models:</strong> Access to several state-of-the-art image generation models</li>
<li><strong>Creative Control:</strong> Customize your images with different styles, moods, and special effects</li>
<li><strong>Prompt Enhancement:</strong> AI-powered prompt improvement for better results</li>
<li><strong>Guided Creation:</strong> Step-by-step assistance to build detailed prompts</li>
<li><strong>Image History:</strong> Save and organize your creations</li>
<li><strong>Batch Generation:</strong> Create multiple variations of your ideas</li>
</ul>
<h3>Credits</h3>
<p>Powered by Hugging Face's Diffusion Models and built with Gradio.</p>
<p>Created with ❤️ for the creative community.</p>
</div>
</div>
""")
# 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"""<div class="status-message status-success">
<span class="icon">✅</span>
<span>Generated {len(images)} variations successfully. Seeds: {seeds_text}</span>
</div>"""
else:
info_html = """<div class="status-message status-error">
<span class="icon">❌</span>
<span>Failed to generate variations. Please try again.</span>
</div>"""
# Switch to variations tab
image_tabs.select("variations-tab")
return images, info_html
except Exception as e:
error_html = f"""<div class="status-message status-error">
<span class="icon">❌</span>
<span>Error generating variations: {str(e)}</span>
</div>"""
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 = """<div class="empty-state">
<div class="icon">🖼️</div>
<h3>No Images Yet</h3>
<p>Your created images will appear here. Start generating to build your collection!</p>
</div>"""
return [], info_html
# Extract images
images = [entry.get("image") for entry in history if "image" in entry]
info_html = f"""<div class="status-info">
<p>Showing {len(images)} images from your history</p>
</div>"""
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 = """<div class="status-success">
<span class="icon">✅</span>
<span>History cleared successfully</span>
</div>"""
else:
info_html = """<div class="status-error">
<span class="icon">❌</span>
<span>Failed to clear history</span>
</div>"""
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 = """<div class="empty-state">
<div class="icon">❤️</div>
<h3>No Favorites Yet</h3>
<p>Your favorite images will appear here. Add images to favorites to build your collection!</p>
</div>"""
return [], info_html
# Extract images
images = [entry.get("image") for entry in favorites if "image" in entry]
info_html = f"""<div class="status-info">
<p>Showing {len(images)} favorite images</p>
</div>"""
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 """<div class="status-error">
<span class="icon">❌</span>
<span>No image to add to favorites</span>
</div>"""
success = current_user.add_to_favorites(image_id)
if success:
return """<div class="status-success">
<span class="icon">✅</span>
<span>Image added to favorites</span>
</div>"""
else:
return """<div class="status-warning">
<span class="icon">⚠️</span>
<span>Image already in favorites or not found</span>
</div>"""
# 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 HTML with embedded JavaScript
if theme == "Dark":
return gr.HTML("""
<script>
document.body.classList.add('dark-theme');
document.body.classList.remove('light-theme');
</script>
""")
elif theme == "Light":
return gr.HTML("""
<script>
document.body.classList.remove('dark-theme');
document.body.classList.add('light-theme');
</script>
""")
else: # Auto
return gr.HTML("""
<script>
// 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');
}
});
</script>
""")
# 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 """<div class="status-success">
<span class="icon">✅</span>
<span>Default settings saved successfully</span>
</div>"""
else:
return """<div class="status-error">
<span class="icon">❌</span>
<span>Failed to save default settings</span>
</div>"""
# 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()