Spaces:
Running
Running
# AI Image Creator: Enhanced UI and UX | |
# Part 1: Core Setup, Model Classes and API Configuration | |
import gradio as gr | |
import logging | |
import sys | |
import random | |
import time | |
import os | |
from huggingface_hub import InferenceClient | |
from PIL import Image | |
import io | |
import base64 | |
# Set up logging | |
logging.basicConfig( | |
level=logging.INFO, | |
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', | |
handlers=[logging.StreamHandler(sys.stdout)] | |
) | |
logger = logging.getLogger("ai_image_creator") | |
# =============== MODEL CLIENTS SETUP =============== | |
def setup_client(api_key, 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: | |
# Replace with your actual HF API key | |
hf_api_key = os.getenv("HF_API_KEY1") | |
hf_client = setup_client(hf_api_key) | |
logger.info("Hugging Face client created successfully") | |
# Set up Llama client if API key is provided | |
llama_api_key = os.getenv("HF_API_KEY2") # Replace with actual key if available | |
try: | |
llama_client = setup_client(llama_api_key, "sambanova") | |
use_llama = True | |
logger.info("Llama client created successfully") | |
except Exception as e: | |
logger.warning(f"Llama client not available: {str(e)}. Will use fallback enhancement.") | |
llama_client = None | |
use_llama = False | |
except Exception as e: | |
logger.error(f"Failed to create Hugging Face client: {str(e)}") | |
hf_client = None | |
llama_client = None | |
use_llama = False | |
# =============== DATA MODELS =============== | |
# Image Models with friendly names, descriptions and icons | |
IMAGE_MODELS = { | |
"stabilityai/stable-diffusion-xl-base-1.0": { | |
"display_name": "SDXL 1.0", | |
"description": "Best overall quality, slower generation", | |
"icon": "⭐", | |
"speed": "slow", | |
"quality": "excellent" | |
}, | |
"runwayml/stable-diffusion-v1-5": { | |
"display_name": "SD 1.5", | |
"description": "Good for general purpose, faster generation", | |
"icon": "🚀", | |
"speed": "fast", | |
"quality": "good" | |
}, | |
"stabilityai/stable-diffusion-2-1": { | |
"display_name": "SD 2.1", | |
"description": "Improved details, balanced speed and quality", | |
"icon": "✨", | |
"speed": "medium", | |
"quality": "very good" | |
}, | |
"prompthero/openjourney": { | |
"display_name": "OpenJourney", | |
"description": "Midjourney-like stylized results", | |
"icon": "🎨", | |
"speed": "medium", | |
"quality": "stylized" | |
}, | |
"dreamlike-art/dreamlike-diffusion-1.0": { | |
"display_name": "Dreamlike", | |
"description": "Artistic style with dreamy aesthetics", | |
"icon": "💫", | |
"speed": "medium", | |
"quality": "artistic" | |
} | |
} | |
# 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" | |
}, | |
"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" | |
}, | |
"Fantasy Illustration": { | |
"description": "Create magical and fantastical scenes with otherworldly elements", | |
"icon": "🧙", | |
"prompt_hint": "Describe magical elements, creatures, and environments in detail" | |
}, | |
"Concept Art": { | |
"description": "Create professional concept art for characters, environments or objects", | |
"icon": "🎮", | |
"prompt_hint": "Include details about perspective, purpose, and design influences" | |
}, | |
"Anime/Manga": { | |
"description": "Create Japanese anime or manga style illustration", | |
"icon": "🍙", | |
"prompt_hint": "Specify anime aesthetics like shading style and character features" | |
}, | |
"Oil Painting": { | |
"description": "Create an image with oil painting textures and artistic brushstrokes", | |
"icon": "🖼️", | |
"prompt_hint": "Consider describing texture, brushwork style, and canvas feel" | |
}, | |
"Watercolor": { | |
"description": "Create a soft watercolor illustration with subtle color blending", | |
"icon": "💧", | |
"prompt_hint": "Mention color blending, paper texture, and watercolor-specific effects" | |
}, | |
"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.)" | |
}, | |
"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" | |
}, | |
"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)" | |
} | |
} | |
# 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" | |
}, | |
"Impressionist": { | |
"description": "Soft brushstrokes that capture light and atmosphere over precise details, like Monet", | |
"icon": "🌈", | |
"examples": "Claude Monet, Pierre-Auguste Renoir, Camille Pissarro" | |
}, | |
"Surrealist": { | |
"description": "Dreamlike quality with impossible or irrational scenes, like Salvador Dali", | |
"icon": "🌀", | |
"examples": "Salvador Dali, René Magritte, Frida Kahlo" | |
}, | |
"Pop Art": { | |
"description": "Bold colors, sharp lines and popular culture references, like Andy Warhol", | |
"icon": "🎭", | |
"examples": "Andy Warhol, Roy Lichtenstein, Keith Haring" | |
}, | |
"Minimalist": { | |
"description": "Simplified forms, limited color palette, and clean composition with minimal elements", | |
"icon": "⬜", | |
"examples": "Piet Mondrian, Kazimir Malevich, Agnes Martin" | |
}, | |
"Abstract": { | |
"description": "Non-representational style using shapes, colors, and forms to express ideas", | |
"icon": "🔶", | |
"examples": "Wassily Kandinsky, Jackson Pollock, Mark Rothko" | |
}, | |
"Cubist": { | |
"description": "Geometric shapes and multiple perspectives shown simultaneously, like Picasso", | |
"icon": "📐", | |
"examples": "Pablo Picasso, Georges Braque, Juan Gris" | |
}, | |
"Art Nouveau": { | |
"description": "Ornate, flowing lines inspired by natural forms with decorative elegance", | |
"icon": "🌿", | |
"examples": "Alphonse Mucha, Gustav Klimt, Antoni Gaudí" | |
}, | |
"Gothic": { | |
"description": "Dark, medieval-inspired aesthetic with dramatic lighting and architectural elements", | |
"icon": "🏰", | |
"examples": "Zdzisław Beksiński, H.R. Giger, medieval architecture" | |
}, | |
"Cyberpunk": { | |
"description": "Futuristic dystopian style with neon colors, technology, and urban decay", | |
"icon": "🤖", | |
"examples": "Blade Runner, Ghost in the Shell, Akira" | |
}, | |
"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" | |
}, | |
"Retro/Vintage": { | |
"description": "Nostalgic style reminiscent of past decades with period-appropriate elements", | |
"icon": "📺", | |
"examples": "1950s advertisements, vintage travel posters, pulp magazine covers" | |
}, | |
"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" | |
}, | |
"Baroque": { | |
"description": "Dramatic, ornate style with rich details, contrast, and dynamic composition", | |
"icon": "👑", | |
"examples": "Caravaggio, Rembrandt, Peter Paul Rubens" | |
}, | |
"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" | |
}, | |
"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" | |
}, | |
"Psychedelic": { | |
"description": "Vibrant, swirling colors with abstract patterns inspired by 1960s art", | |
"icon": "🌈", | |
"examples": "1960s concert posters, Peter Max, Alex Grey" | |
}, | |
"Vaporwave": { | |
"description": "Glitch aesthetics with pastel colors, 80s/90s nostalgia and digital elements", | |
"icon": "📼", | |
"examples": "Retrowave aesthetics, 80s digital graphics, glitch art" | |
}, | |
"Studio Ghibli": { | |
"description": "Whimsical, detailed animation style inspired by Japanese animated films", | |
"icon": "🐉", | |
"examples": "Spirited Away, My Neighbor Totoro, Howl's Moving Castle" | |
}, | |
"Hyperrealism": { | |
"description": "Extremely detailed realism that exceeds photograph-like precision", | |
"icon": "🔍", | |
"examples": "Works by Roberto Bernardi, Denis Peterson, Gottfried Helnwein" | |
} | |
} | |
# 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" | |
}, | |
"Sad": { | |
"description": "Melancholic atmosphere with muted colors", | |
"icon": "😢", | |
"color_palette": "Muted blues, grays, desaturated colors, cool tones" | |
}, | |
"Mysterious": { | |
"description": "Enigmatic atmosphere with shadows and hidden elements", | |
"icon": "🔮", | |
"color_palette": "Deep purples, dark blues, hints of teal, selective lighting" | |
}, | |
"Peaceful": { | |
"description": "Serene, calm atmosphere with gentle lighting", | |
"icon": "🕊️", | |
"color_palette": "Soft blues, gentle greens, pastel colors, balanced light" | |
}, | |
"Tense": { | |
"description": "Suspenseful atmosphere with dramatic lighting", | |
"icon": "😰", | |
"color_palette": "High contrast, dark shadows, selective reds, strong highlights" | |
}, | |
"Whimsical": { | |
"description": "Playful, imaginative atmosphere with fanciful elements", | |
"icon": "🦄", | |
"color_palette": "Pastels, candy colors, unexpected color combinations" | |
}, | |
"Dark": { | |
"description": "Gloomy atmosphere with deep shadows and low lighting", | |
"icon": "🌑", | |
"color_palette": "Dark blues, blacks, deep greens, minimal highlights" | |
}, | |
"Energetic": { | |
"description": "Dynamic, vibrant atmosphere with strong colors and movement", | |
"icon": "⚡", | |
"color_palette": "Saturated primary colors, bold contrasts, vibrant hues" | |
}, | |
"Romantic": { | |
"description": "Soft, dreamy atmosphere with warm, gentle lighting", | |
"icon": "❤️", | |
"color_palette": "Soft pinks, gentle reds, golden highlights, warm tones" | |
}, | |
"Epic": { | |
"description": "Grand, impressive atmosphere with dramatic scale and lighting", | |
"icon": "🏔️", | |
"color_palette": "Bold colors, dramatic contrast, atmospheric lighting, expansive scale" | |
} | |
} | |
# Example prompts with rich metadata | |
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", | |
"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", | |
"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", | |
"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", | |
"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", | |
"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", | |
"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", | |
"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 in coffee shop", | |
"creation_type": "Digital Art", | |
"art_style": "Impressionist", | |
"mood": "Peaceful", | |
"tags": ["interior", "rainy", "cozy", "urban"] | |
} | |
] | |
# CSS for enhanced UI styling - Will be included in part 2 | |
# =============== HELPER FUNCTIONS =============== | |
# Helper function to format dropdown choices with icons | |
def format_dropdown_choices(options_dict): | |
return [f"{options_dict[key].get('icon', '•')} {key}" for key in options_dict.keys()] | |
# Helper function to extract the key from formatted choice | |
def extract_key(formatted_choice): | |
# 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): | |
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']}" | |
return example["text"], creation, art, mood | |
# Get model key from formatted display name | |
def get_model_key(formatted_name): | |
# Extract display name without the icon | |
if ' ' in formatted_name: | |
display_name = formatted_name.split(' ', 1)[1] | |
# Find the corresponding key | |
for key, info in IMAGE_MODELS.items(): | |
if info['display_name'] == display_name: | |
return key | |
return list(IMAGE_MODELS.keys())[0] # Default to first model if not found | |
# AI Image Creator: Enhanced UI and UX | |
# Part 2: Enhanced UI Components and Styling | |
# CSS for styling the interface - Comprehensive styling for modern UI | |
css = """ | |
/* Main theme colors with CSS variables for better customization */ | |
:root { | |
--primary-color: #4F46E5; | |
--primary-hover: #4338CA; | |
--secondary-color: #7C3AED; | |
--secondary-hover: #6D28D9; | |
--background-color: #F8FAFC; | |
--card-color: #FFFFFF; | |
--text-color: #1E293B; | |
--text-muted: #64748B; | |
--accent-color: #3B82F6; | |
--success-color: #10B981; | |
--success-hover: #059669; | |
--warning-color: #F59E0B; | |
--error-color: #EF4444; | |
--error-hover: #DC2626; | |
--border-color: #E2E8F0; | |
--border-hover: #CBD5E1; | |
--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-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05); | |
--radius-sm: 0.375rem; | |
--radius: 0.5rem; | |
--radius-lg: 0.75rem; | |
--radius-xl: 1rem; | |
--font-sans: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; | |
} | |
/* 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; | |
} | |
/* Container with responsive padding */ | |
.container { | |
max-width: 1400px; | |
margin: 0 auto; | |
padding: 1rem; | |
} | |
@media (max-width: 640px) { | |
.container { | |
padding: 0.5rem; | |
} | |
} | |
/* Card styling with elevation and hover effects */ | |
.gr-panel { | |
border-radius: var(--radius) !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(--card-color) !important; | |
} | |
.gr-panel:hover { | |
transform: translateY(-2px); | |
box-shadow: var(--shadow-lg) !important; | |
} | |
/* 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; | |
} | |
button.primary:hover { | |
transform: translateY(-1px); | |
box-shadow: var(--shadow) !important; | |
opacity: 0.9; | |
} | |
button.primary:active { | |
transform: translateY(0); | |
opacity: 0.8; | |
} | |
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(79, 70, 229, 0.05) !important; | |
border-color: var(--primary-hover) !important; | |
} | |
/* Style for the example buttons */ | |
.example-button { | |
font-size: 0.875rem !important; | |
padding: 0.5rem 0.75rem !important; | |
background-color: transparent !important; | |
border: 1px solid var(--border-color) !important; | |
border-radius: var(--radius) !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; | |
} | |
.example-button:hover { | |
background-color: rgba(79, 70, 229, 0.05) !important; | |
border-color: var(--primary-color) !important; | |
transform: translateY(-1px); | |
} | |
/* 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 !important; | |
font-family: var(--font-sans) !important; | |
color: var(--text-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(79, 70, 229, 0.2) !important; | |
outline: none !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; | |
} | |
/* 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"] { | |
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 !important; | |
font-family: var(--font-sans) !important; | |
} | |
textarea:focus, input[type="text"]:focus { | |
border-color: var(--primary-color) !important; | |
box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.2) !important; | |
outline: none !important; | |
} | |
/* Dropdown styling */ | |
.gr-dropdown { | |
border-radius: var(--radius) !important; | |
border: 1px solid var(--border-color) !important; | |
background-color: var(--card-color) !important; | |
} | |
.gr-dropdown > div { | |
border-radius: var(--radius) !important; | |
min-height: 38px !important; | |
} | |
.gr-dropdown > div > span { | |
font-size: 0.9375rem !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; | |
} | |
.gr-dropdown ul li { | |
padding: 0.5rem 0.75rem !important; | |
} | |
.gr-dropdown ul li:hover { | |
background-color: rgba(79, 70, 229, 0.05) !important; | |
} | |
/* Custom header with gradient background */ | |
.app-header { | |
text-align: center; | |
padding: 1.75rem 1rem; | |
margin-bottom: 2rem; | |
background: linear-gradient(135deg, rgba(79, 70, 229, 0.08), rgba(124, 58, 237, 0.08)); | |
border-radius: var(--radius-lg); | |
border-bottom: 3px solid var(--primary-color); | |
position: relative; | |
overflow: hidden; | |
} | |
.app-header::before { | |
content: ''; | |
position: absolute; | |
top: -50px; | |
left: -50px; | |
right: -50px; | |
height: 100px; | |
background: linear-gradient(135deg, rgba(79, 70, 229, 0.2), rgba(124, 58, 237, 0.2)); | |
transform: rotate(-5deg); | |
z-index: 0; | |
} | |
.app-header h1 { | |
font-size: 2.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; | |
} | |
.app-header p { | |
font-size: 1.25rem !important; | |
color: var(--text-color); | |
opacity: 0.8; | |
max-width: 40rem; | |
margin: 0 auto; | |
position: relative; | |
z-index: 1; | |
} | |
/* Responsive header */ | |
@media (max-width: 640px) { | |
.app-header h1 { | |
font-size: 2rem !important; | |
} | |
.app-header p { | |
font-size: 1rem !important; | |
} | |
} | |
/* Examples gallery with grid layout */ | |
.example-gallery { | |
display: grid; | |
grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); | |
gap: 1rem; | |
margin: 1rem 0; | |
} | |
.example-item { | |
border-radius: var(--radius); | |
overflow: hidden; | |
cursor: pointer; | |
border: 2px solid transparent; | |
transition: all 0.2s; | |
display: flex; | |
flex-direction: column; | |
background-color: var(--card-color); | |
} | |
.example-item:hover { | |
transform: translateY(-2px); | |
border-color: var(--accent-color); | |
box-shadow: var(--shadow); | |
} | |
.example-item:active { | |
transform: translateY(0); | |
} | |
.example-item-image { | |
width: 100%; | |
aspect-ratio: 1; | |
background-color: #f0f0f0; | |
display: flex; | |
align-items: center; | |
justify-content: center; | |
color: var(--text-muted); | |
font-size: 1.5rem; | |
border-bottom: 1px solid var(--border-color); | |
} | |
.example-item-caption { | |
padding: 0.5rem; | |
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; | |
} | |
/* 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.8); | |
z-index: 1000; | |
backdrop-filter: blur(2px); | |
border-radius: var(--radius); | |
} | |
.spinner { | |
width: 40px; | |
height: 40px; | |
border: 4px solid rgba(79, 70, 229, 0.2); | |
border-radius: 50%; | |
border-top-color: var(--primary-color); | |
animation: spin 1s linear infinite; | |
} | |
@keyframes spin { | |
to { | |
transform: rotate(360deg); | |
} | |
} | |
/* Info cards with subtle styling */ | |
.info-card { | |
background-color: var(--card-color); | |
border-radius: var(--radius); | |
padding: 1rem; | |
border: 1px solid var(--border-color); | |
margin-bottom: 1rem; | |
transition: all 0.2s; | |
} | |
.info-card:hover { | |
border-color: var(--border-hover); | |
box-shadow: var(--shadow-sm); | |
} | |
.info-card h3 { | |
margin-top: 0; | |
margin-bottom: 0.5rem; | |
font-size: 1rem; | |
font-weight: 600; | |
color: var(--primary-color); | |
display: flex; | |
align-items: center; | |
gap: 0.5rem; | |
} | |
.info-card p { | |
margin: 0; | |
font-size: 0.875rem; | |
color: var(--text-muted); | |
line-height: 1.5; | |
} | |
/* Model info panel with branded styling */ | |
.model-info { | |
background-color: rgba(79, 70, 229, 0.05); | |
border-left: 3px solid var(--primary-color); | |
padding: 0.75rem 1rem; | |
border-radius: 0 var(--radius) var(--radius) 0; | |
margin: 1rem 0; | |
} | |
.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.5rem 0; | |
font-size: 0.875rem; | |
color: var(--text-color); | |
} | |
.model-info .model-id { | |
font-size: 0.75rem; | |
color: var(--text-muted); | |
font-family: monospace; | |
background-color: rgba(0, 0, 0, 0.03); | |
padding: 0.25rem 0.5rem; | |
border-radius: 4px; | |
margin-top: 0.5rem; | |
word-break: break-all; | |
} | |
/* Parameter pills for displaying selections */ | |
.parameter-pill { | |
display: inline-flex; | |
align-items: center; | |
background-color: rgba(79, 70, 229, 0.1); | |
color: var(--primary-color); | |
border-radius: 16px; | |
padding: 0.25rem 0.75rem; | |
margin-right: 0.5rem; | |
margin-bottom: 0.5rem; | |
font-size: 0.75rem; | |
font-weight: 500; | |
user-select: none; | |
} | |
.parameter-pill .icon { | |
margin-right: 0.25rem; | |
} | |
/* Badge for showing model speed/quality */ | |
.badge { | |
display: inline-flex; | |
align-items: center; | |
justify-content: center; | |
border-radius: 9999px; | |
padding: 0.125rem 0.5rem; | |
font-size: 0.75rem; | |
font-weight: 500; | |
margin-left: 0.5rem; | |
} | |
.badge-success { | |
background-color: rgba(16, 185, 129, 0.1); | |
color: var(--success-color); | |
} | |
.badge-warning { | |
background-color: rgba(245, 158, 11, 0.1); | |
color: var(--warning-color); | |
} | |
.badge-error { | |
background-color: rgba(239, 68, 68, 0.1); | |
color: var(--error-color); | |
} | |
.badge-info { | |
background-color: rgba(59, 130, 246, 0.1); | |
color: var(--accent-color); | |
} | |
/* Enhanced accordion styling */ | |
.gr-accordion { | |
border: 1px solid var(--border-color) !important; | |
border-radius: var(--radius) !important; | |
margin-bottom: 1rem !important; | |
overflow: hidden; | |
} | |
.gr-accordion > div:first-child { | |
padding: 0.75rem 1rem !important; | |
background-color: rgba(0, 0, 0, 0.02) !important; | |
font-weight: 600 !important; | |
color: var(--text-color) !important; | |
font-size: 0.9375rem !important; | |
} | |
.gr-accordion > div:last-child { | |
padding: 1rem !important; | |
} | |
/* Tooltip styling */ | |
.tooltip { | |
position: relative; | |
display: inline-block; | |
cursor: help; | |
} | |
.tooltip .tooltiptext { | |
visibility: hidden; | |
width: 200px; | |
background-color: var(--text-color); | |
color: white; | |
text-align: center; | |
border-radius: var(--radius-sm); | |
padding: 0.5rem 0.75rem; | |
position: absolute; | |
z-index: 1000; | |
bottom: 125%; | |
left: 50%; | |
transform: translateX(-50%); | |
opacity: 0; | |
transition: opacity 0.3s; | |
font-size: 0.75rem; | |
box-shadow: var(--shadow); | |
pointer-events: none; | |
} | |
.tooltip .tooltiptext::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 .tooltiptext { | |
visibility: visible; | |
opacity: 1; | |
} | |
/* History item styling */ | |
.history-item { | |
display: flex; | |
align-items: center; | |
padding: 0.75rem; | |
border-radius: var(--radius); | |
margin-bottom: 0.75rem; | |
background-color: var(--card-color); | |
border: 1px solid var(--border-color); | |
cursor: pointer; | |
transition: all 0.2s; | |
} | |
.history-item:hover { | |
background-color: rgba(79, 70, 229, 0.05); | |
transform: translateY(-1px); | |
} | |
.history-item-image { | |
width: 48px; | |
height: 48px; | |
border-radius: var(--radius-sm); | |
object-fit: cover; | |
margin-right: 0.75rem; | |
background-color: #f0f0f0; | |
display: flex; | |
align-items: center; | |
justify-content: center; | |
color: var(--text-muted); | |
font-size: 1.25rem; | |
} | |
.history-item-content { | |
flex: 1; | |
overflow: hidden; | |
} | |
.history-item-title { | |
margin: 0; | |
font-size: 0.875rem; | |
font-weight: 500; | |
white-space: nowrap; | |
overflow: hidden; | |
text-overflow: ellipsis; | |
color: var(--text-color); | |
} | |
.history-item-subtitle { | |
margin: 0; | |
font-size: 0.75rem; | |
color: var(--text-muted); | |
margin-top: 0.25rem; | |
} | |
/* Tabs styling */ | |
.tabs { | |
display: flex; | |
border-bottom: 1px solid var(--border-color); | |
margin-bottom: 1rem; | |
} | |
.tab { | |
padding: 0.75rem 1rem; | |
cursor: pointer; | |
border-bottom: 2px solid transparent; | |
font-weight: 500; | |
font-size: 0.9375rem; | |
color: var(--text-muted); | |
transition: all 0.2s; | |
} | |
.tab:hover { | |
color: var(--primary-color); | |
} | |
.tab.active { | |
color: var(--primary-color); | |
border-bottom-color: var(--primary-color); | |
} | |
/* Progress bar */ | |
.progress-container { | |
width: 100%; | |
height: 8px; | |
background-color: rgba(79, 70, 229, 0.1); | |
border-radius: 4px; | |
overflow: hidden; | |
margin: 0.5rem 0; | |
} | |
.progress-bar { | |
height: 100%; | |
background: linear-gradient(to right, var(--primary-color), var(--secondary-color)); | |
width: 0%; | |
transition: width 0.3s ease; | |
border-radius: 4px; | |
} | |
/* Image output container */ | |
.image-output-container { | |
position: relative; | |
border-radius: var(--radius); | |
overflow: hidden; | |
transition: all 0.2s; | |
background-color: #f5f5f5; | |
min-height: 300px; | |
} | |
.image-output-container img { | |
width: 100%; | |
display: block; | |
border-radius: var(--radius); | |
} | |
.image-placeholder { | |
position: absolute; | |
top: 0; | |
left: 0; | |
width: 100%; | |
height: 100%; | |
display: flex; | |
flex-direction: column; | |
align-items: center; | |
justify-content: center; | |
color: var(--text-muted); | |
font-size: 1rem; | |
text-align: center; | |
padding: 1rem; | |
} | |
.image-placeholder .icon { | |
font-size: 3rem; | |
margin-bottom: 1rem; | |
opacity: 0.6; | |
} | |
/* Image controls overlay */ | |
.image-controls { | |
position: absolute; | |
bottom: 0; | |
left: 0; | |
right: 0; | |
padding: 0.75rem; | |
background: linear-gradient(to top, rgba(0,0,0,0.7), rgba(0,0,0,0)); | |
display: flex; | |
justify-content: flex-end; | |
opacity: 0; | |
transition: opacity 0.2s; | |
} | |
.image-output-container:hover .image-controls { | |
opacity: 1; | |
} | |
.image-control-button { | |
background-color: rgba(255, 255, 255, 0.9); | |
border: none; | |
border-radius: 50%; | |
width: 36px; | |
height: 36px; | |
display: flex; | |
align-items: center; | |
justify-content: center; | |
cursor: pointer; | |
margin-left: 0.5rem; | |
color: var(--text-color); | |
transition: all 0.2s; | |
} | |
.image-control-button:hover { | |
background-color: white; | |
transform: translateY(-2px); | |
box-shadow: var(--shadow); | |
} | |
/* Character counter */ | |
.character-counter { | |
text-align: right; | |
font-size: 0.75rem; | |
color: var(--text-muted); | |
margin-top: 0.25rem; | |
transition: color 0.2s; | |
} | |
.character-counter.warning { | |
color: var(--warning-color); | |
} | |
.character-counter.error { | |
color: var(--error-color); | |
} | |
/* Status message */ | |
.status-message { | |
padding: 0.75rem 1rem; | |
border-radius: var(--radius); | |
margin: 1rem 0; | |
font-size: 0.875rem; | |
display: flex; | |
align-items: center; | |
} | |
.status-message .icon { | |
margin-right: 0.75rem; | |
font-size: 1.25rem; | |
} | |
.status-success { | |
background-color: rgba(16, 185, 129, 0.1); | |
color: var(--success-color); | |
border-left: 3px solid var(--success-color); | |
} | |
.status-error { | |
background-color: rgba(239, 68, 68, 0.1); | |
color: var(--error-color); | |
border-left: 3px solid var(--error-color); | |
} | |
.status-warning { | |
background-color: rgba(245, 158, 11, 0.1); | |
color: var(--warning-color); | |
border-left: 3px solid var(--warning-color); | |
} | |
.status-info { | |
background-color: rgba(59, 130, 246, 0.1); | |
color: var(--accent-color); | |
border-left: 3px solid var(--accent-color); | |
} | |
/* Responsive adjustments */ | |
@media (max-width: 768px) { | |
.example-gallery { | |
grid-template-columns: repeat(auto-fill, minmax(110px, 1fr)); | |
} | |
.gr-panel { | |
padding: 0.75rem !important; | |
} | |
.gr-accordion > div:first-child { | |
padding: 0.625rem 0.75rem !important; | |
} | |
.gr-accordion > div:last-child { | |
padding: 0.75rem !important; | |
} | |
button.primary { | |
padding: 0.625rem 1.25rem !important; | |
} | |
} | |
""" | |
# =============== ENHANCED UI COMPONENTS =============== | |
# Function to create and show application UI | |
def create_ui(): | |
with gr.Blocks(title="", css=css) as interface: | |
# Custom header with branding | |
with gr.Row(elem_classes="app-header"): | |
with gr.Column(): | |
gr.HTML(""" | |
<h1>✨Memory Magic Studio</h1> | |
<p>Capture the magic of your imagination and watch it come to life.🪄</p> | |
""") | |
# Main content area | |
with gr.Row(equal_height=False): | |
# Left column - Input controls | |
with gr.Column(scale=1, min_width=380): | |
# Description input with character counter | |
with gr.Group(elem_classes="input-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="<div class='character-counter'>0 characters</div>", | |
elem_classes="char-counter-container" | |
) | |
# Creation settings with enhanced dropdowns | |
with gr.Group(elem_classes="settings-group"): | |
gr.HTML("<h3 style='margin-top: 0; font-size: 1rem; margin-bottom: 0.75rem;'>🛠️ Image Settings</h3>") | |
# 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" | |
) | |
# Examples gallery with clear visual structure | |
with gr.Group(elem_classes="examples-group"): | |
gr.HTML("<h3 style='margin-top: 0; font-size: 1rem; margin-bottom: 0.75rem;'>🌟 Try an example:</h3>") | |
# Gallery of examples | |
with gr.Row(elem_classes="example-gallery"): | |
for i, example in enumerate(EXAMPLE_PROMPTS): | |
with gr.Column(elem_classes="example-item"): | |
# Example card with visual element and caption | |
example_card = gr.Button( | |
example["thumbnail_desc"], | |
elem_classes="example-button" | |
) | |
# Event handler for example selection | |
example_card.click( | |
fn=lambda idx=i: load_example(idx), | |
outputs=[description_input, creation_type, art_style, mood_dropdown] | |
) | |
# Information panels for selected options | |
with gr.Group(elem_classes="info-panels"): | |
# Creation type info | |
creation_info = gr.HTML(value="", elem_classes="option-info") | |
# Art style info | |
art_info = gr.HTML(value="", elem_classes="option-info") | |
# Model info panel | |
model_info = gr.HTML(value="", elem_classes="option-info") | |
# Generate button with clear call to action | |
with gr.Group(elem_classes="action-group"): | |
# Generate button with progress indicator | |
generate_button = gr.Button( | |
"✨ Generate Image", | |
variant="primary", | |
elem_classes="primary", | |
elem_id="generate-button" | |
) | |
# Generation status message | |
generation_status = gr.HTML(value="", elem_classes="generation-status") | |
# Tips section in collapsible accordion | |
with gr.Accordion("📝 Tips for better results", open=True): | |
gr.HTML(""" | |
<div class="tips-container"> | |
<h3>Tips for better results:</h3> | |
<ul> | |
<li><strong>Be specific and detailed</strong> - Include information about subjects, environment, lighting, colors, perspective, time of day, etc.</li> | |
<li><strong>Use descriptive adjectives</strong> - Words like "vibrant", "moody", "ethereal", "weathered" help set the tone.</li> | |
<li><strong>Experiment with art styles</strong> - Different styles can dramatically change the look and feel.</li> | |
<li><strong>Combine with the right model</strong> - SDXL provides the highest quality but takes longer.</li> | |
<li><strong>Think about composition</strong> - Mention foreground/background elements and their relationships.</li> | |
</ul> | |
</div> | |
""") | |
# Right column - Output display | |
with gr.Column(scale=1, min_width=480): | |
# Image display area with placeholder | |
with gr.Group(elem_classes="output-container"): | |
# Output image with interactive elements | |
image_output = gr.Image( | |
label="Generated Image", | |
elem_id="image-output", | |
type="pil", | |
height=512 | |
) | |
# Image generation details | |
with gr.Accordion("Image Details", open=True): | |
parameters_display = gr.HTML(value="") | |
# Enhanced prompt display | |
with gr.Accordion("Enhanced Prompt", open=False): | |
prompt_output = gr.Textbox( | |
label="AI-Enhanced Prompt Used", | |
lines=5, | |
elem_id="prompt-output", | |
elem_classes="prompt-display" | |
) | |
# Technical details for advanced users | |
with gr.Accordion("Technical Details", open=False): | |
technical_info = gr.HTML(""" | |
<div class="technical-info"> | |
<h4>How Image Generation Works</h4> | |
<p>Images are generated using Stable Diffusion, a latent text-to-image diffusion model. Your text prompt is: | |
<ol> | |
<li>Enhanced with AI to add descriptive details and quality terms</li> | |
<li>Processed through a neural network that gradually transforms random noise into an image</li> | |
<li>Refined based on the parameters you select (model, style, mood)</li> | |
</ol> | |
</p> | |
<p>Different models have different strengths: | |
<ul> | |
<li><strong>SDXL 1.0</strong>: Highest quality, best composition and details</li> | |
<li><strong>SD 1.5</strong>: Faster generation, good for general purpose</li> | |
<li><strong>SD 2.1</strong>: Better with human faces, improved consistency</li> | |
<li><strong>OpenJourney</strong>: Midjourney-like stylized artistic results</li> | |
<li><strong>Dreamlike</strong>: Dreamy, ethereal aesthetic with artistic flair</li> | |
</ul> | |
</p> | |
</div> | |
""") | |
# Set up event handlers within the Blocks context | |
description_input.change( | |
fn=update_char_count, | |
inputs=description_input, | |
outputs=char_counter | |
) | |
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 | |
) | |
generate_button.click( | |
fn=generate_with_status, | |
inputs=[ | |
description_input, | |
creation_type, | |
art_style, | |
mood_dropdown, | |
model_selector | |
], | |
outputs=[ | |
image_output, | |
generation_status, | |
prompt_output, | |
parameters_display | |
] | |
) | |
# 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']}") | |
), | |
outputs=[creation_info, art_info, model_info] | |
) | |
return interface, description_input, creation_type, art_style, mood_dropdown, model_selector, generate_button, image_output, generation_status, prompt_output, parameters_display, char_counter, creation_info, art_info, model_info | |
# Helper function to update character count with color coding | |
def update_char_count(text): | |
count = len(text) | |
if count == 0: | |
color_class = "" | |
elif count < 20: | |
color_class = "warning" | |
elif count > 300: | |
color_class = "warning" | |
elif count > 500: | |
color_class = "error" | |
else: | |
color_class = "" | |
return f"<div class='character-counter {color_class}'>{count} characters</div>" | |
# Helper function to update creation type info | |
def update_creation_info(choice): | |
key = extract_key(choice) | |
if key in CREATION_TYPES: | |
info = CREATION_TYPES[key] | |
return f"""<div class="info-card"> | |
<h3>{info['icon']} {key}</h3> | |
<p>{info['description']}</p> | |
<p style="margin-top: 0.5rem; font-style: italic; opacity: 0.8;">💡 {info['prompt_hint']}</p> | |
</div>""" | |
return "" | |
# Helper function to update art style info | |
def update_art_style_info(choice): | |
key = extract_key(choice) | |
if key in ART_STYLES: | |
info = ART_STYLES[key] | |
return f"""<div class="info-card"> | |
<h3>{info['icon']} {key}</h3> | |
<p>{info['description']}</p> | |
<p style="margin-top: 0.5rem; font-style: italic; opacity: 0.8;">🎨 Examples: {info['examples']}</p> | |
</div>""" | |
return "" | |
# Helper function to update model info | |
def update_model_info(formatted_choice): | |
# Extract display name without the icon | |
if ' ' in formatted_choice: | |
display_name = formatted_choice.split(' ', 1)[1] | |
# 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') == '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') == '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') == 'stylized': | |
quality_badge = '<span class="badge badge-info">Stylized</span>' | |
elif info.get('quality') == 'artistic': | |
quality_badge = '<span class="badge badge-info">Artistic</span>' | |
return f"""<div class="model-info"> | |
<h3>{info['icon']} {info['display_name']} {speed_badge} {quality_badge}</h3> | |
<p>{info['description']}</p> | |
<div class="model-id">{key}</div> | |
</div>""" | |
return "" | |
# Helper function to update status message | |
def update_status(message, is_error=False, is_warning=False, is_info=False): | |
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, model_name): | |
creation_key = extract_key(creation_type_val) | |
art_key = extract_key(art_style_val) | |
mood_key = extract_key(mood_val) | |
# Get model info | |
model_display_name = "Unknown Model" | |
model_id = "" | |
model_icon = "🤖" | |
for key, info in IMAGE_MODELS.items(): | |
if f"{info['icon']} {info['display_name']}" == model_name: | |
model_display_name = info['display_name'] | |
model_id = key | |
model_icon = info['icon'] | |
break | |
html = """<div style="margin-bottom: 1rem;"> | |
<div style="font-weight: 500; margin-bottom: 0.75rem;">Generated with these parameters:</div> | |
<div style="display: flex; flex-wrap: wrap; gap: 0.5rem;">""" | |
# Add creation type pill | |
if creation_key in CREATION_TYPES: | |
html += f"""<div class="parameter-pill"> | |
<span class="icon">{CREATION_TYPES[creation_key]['icon']}</span> {creation_key} | |
</div>""" | |
# Add art style pill | |
if art_key in ART_STYLES: | |
html += f"""<div class="parameter-pill"> | |
<span class="icon">{ART_STYLES[art_key]['icon']}</span> {art_key} | |
</div>""" | |
# Add mood pill | |
if mood_key in MOODS: | |
html += f"""<div class="parameter-pill"> | |
<span class="icon">{MOODS[mood_key]['icon']}</span> {mood_key} | |
</div>""" | |
# Add model pill | |
html += f"""<div class="parameter-pill"> | |
<span class="icon">{model_icon}</span> {model_display_name} | |
</div>""" | |
# Close container | |
html += """</div> | |
<div style="margin-top: 1rem; font-size: 0.75rem; color: var(--text-muted);"> | |
Image generated on {timestamp} | |
</div> | |
</div>""".replace("{timestamp}", time.strftime("%Y-%m-%d at %H:%M:%S")) | |
return html | |
# AI Image Creator: Enhanced UI and UX | |
# Part 3: Processing Logic, Prompt Enhancement, and Application Flow | |
# =============== PROMPT ENHANCEMENT LOGIC =============== | |
# Function to enhance prompt with Llama 4 with improved logical understanding | |
def enhance_prompt_with_llama(user_input, creation_type, art_style, mood): | |
""" | |
Enhance user input with Llama 4 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") | |
Returns: | |
str: Enhanced prompt optimized for image generation | |
""" | |
try: | |
if not use_llama or llama_client is None: | |
logger.warning("Llama enhancement not available, using fallback") | |
return enhance_prompt_fallback(user_input, creation_type, art_style, mood) | |
logger.info(f"Enhancing prompt with Llama 4 for creation type: {creation_type}, art style: {art_style}") | |
# Enhanced Llama 4 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 simple description into a comprehensive, detailed image generation prompt that will create stunning visuals. Consider all the provided elements (description, creation type, art style, mood) 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 | |
- If describing something fantastical (e.g., "flying cat"), add logical details about how this could work (e.g., "a cat with majestic feathered wings spread wide") | |
- 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-150 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"] | |
# Get mood description | |
mood_info = MOODS.get(mood, {"description": "atmospheric", "icon": "✨"}) | |
mood_description = mood_info["description"] | |
# Prepare the user prompt for Llama | |
user_prompt = f"""Description: {user_input} | |
Creation Type: {creation_type} - {creation_description} | |
Art Style: {art_style} - {style_description} | |
Mood: {mood} - {mood_description} | |
Please create a comprehensive, detailed image generation prompt that combines all these elements.""" | |
try: | |
# Request enhancement from Llama 4 | |
completion = llama_client.chat.completions.create( | |
model="meta-llama/Llama-4-Scout-17B-16E-Instruct", | |
messages=[ | |
{"role": "system", "content": system_prompt}, | |
{"role": "user", "content": user_prompt} | |
], | |
max_tokens=500, | |
temperature=0.7, # Slight creativity while maintaining coherence | |
) | |
enhanced = completion.choices[0].message.content | |
logger.info(f"Llama 4 enhanced prompt: {enhanced[:100]}...") | |
return enhanced if enhanced else user_input | |
except Exception as e: | |
logger.error(f"Error during Llama enhancement: {str(e)}") | |
return enhance_prompt_fallback(user_input, creation_type, art_style, mood) | |
except Exception as e: | |
logger.error(f"Error in Llama enhancement: {str(e)}") | |
return enhance_prompt_fallback(user_input, creation_type, art_style, mood) | |
# Fallback prompt enhancement without Llama | |
def enhance_prompt_fallback(user_input, creation_type, art_style, mood): | |
""" | |
Enhance user input without requiring Llama API 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") | |
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" | |
], | |
"Digital Art": [ | |
"vibrant colors", "clean lines", "digital illustration", | |
"polished", "professional digital art", "detailed rendering", | |
"digital painting", "colorful", "vector-like precision" | |
], | |
"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" | |
] | |
} | |
# Style modifiers for different art styles - more detailed descriptions | |
style_modifiers = { | |
"Photorealistic": "highly detailed photorealistic style with perfect lighting, natural shadows, and lifelike textures", | |
"Impressionist": "impressionist style with visible brushstrokes capturing light and atmosphere over precise details, reminiscent of Claude Monet", | |
"Surrealist": "surrealist style with dreamlike and impossible elements, juxtaposed reality, inspired by Salvador Dali", | |
"Pop Art": "pop art style with bold colors, sharp lines, halftone patterns and cultural references, like Andy Warhol", | |
"Minimalist": "minimalist style with simplified forms, limited color palette, clean composition, and essential elements only", | |
"Abstract": "abstract style using non-representational shapes, colors, and forms to express emotion rather than reality", | |
"Cubist": "cubist style with geometric forms, multiple perspectives shown simultaneously, fractured surfaces, like Pablo Picasso", | |
"Art Nouveau": "art nouveau style with ornate flowing lines inspired by natural forms, decorative elegance, and organic shapes", | |
"Gothic": "gothic style with dark atmosphere, dramatic elements, pointed arches, and medieval-inspired architecture", | |
"Cyberpunk": "cyberpunk style with neon colors, high tech low life aesthetic, futuristic technology, and urban decay", | |
"Steampunk": "steampunk style with Victorian aesthetics, brass machinery, steam-powered technology, and retrofuturistic design", | |
"Retro/Vintage": "retro style with nostalgic elements from past decades, aged texture, and period-appropriate colors and design", | |
"Art Deco": "art deco style with geometric patterns, bold colors, symmetry, luxurious materials, and streamlined forms", | |
"Baroque": "baroque style with dramatic lighting, rich details, contrast, dynamic composition, and ornate decorations", | |
"Ukiyo-e": "ukiyo-e style Japanese woodblock print aesthetic with flat areas of color, strong outlines, and traditional subjects", | |
"Comic Book": "comic book style with bold outlines, vibrant colors, dynamic action poses, and expressive characters", | |
"Psychedelic": "psychedelic style with vibrant swirling colors, abstract patterns, distorted perspective, and 1960s-inspired visuals", | |
"Vaporwave": "vaporwave aesthetic with glitch art, pastel colors, 80s/90s nostalgia, ancient statues, and digital elements", | |
"Studio Ghibli": "Studio Ghibli anime style with whimsical detailed environments, soft colors, and charming character design", | |
"Hyperrealism": "hyperrealistic style with extreme detail beyond photography, perfect textures, and meticulous precision" | |
} | |
# Mood modifiers for different moods - enhanced descriptions | |
mood_modifiers = { | |
"Happy": "bright cheerful atmosphere with warm golden lighting, vibrant colors, and uplifting elements", | |
"Sad": "melancholic atmosphere with muted colors, soft shadows, and somber emotional tone", | |
"Mysterious": "enigmatic atmosphere with shadows, fog, hidden elements, and dramatic lighting contrasts", | |
"Peaceful": "serene calm atmosphere with gentle lighting, soft colors, and tranquil composition", | |
"Tense": "suspenseful atmosphere with dramatic lighting, stark contrasts, and unsettling composition", | |
"Whimsical": "playful whimsical atmosphere with imaginative elements, saturated colors, and fantastical details", | |
"Dark": "dark gloomy atmosphere with deep shadows, limited lighting, and ominous elements", | |
"Energetic": "dynamic vibrant atmosphere with strong colors, motion effects, and active composition", | |
"Romantic": "soft romantic atmosphere with dreamy lighting, gentle colors, and intimate ambiance", | |
"Epic": "grand epic atmosphere with dramatic scale, sweeping vistas, and majestic lighting" | |
} | |
# 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" | |
] | |
# Get style modifier | |
style_modifier = style_modifiers.get(art_style, "detailed professional style") | |
# Get mood modifier | |
mood_modifier = mood_modifiers.get(mood, "atmospheric") | |
# Basic prompt structure - core subject and style elements | |
prompt_parts = [ | |
user_input, | |
style_modifier, | |
mood_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 =============== | |
# Generate image function with loading state handling and retry mechanism | |
def generate_image(description, creation_type, art_style, mood, model_name, retries=1): | |
""" | |
Generate image based on user inputs by enhancing prompt and calling image model API | |
Args: | |
description (str): User's original description | |
creation_type (str): Selected creation type | |
art_style (str): Selected art style | |
mood (str): Selected mood | |
model_name (str): Model identifier | |
retries (int): Number of retries if generation fails | |
Returns: | |
tuple: (image, status_message, enhanced_prompt) | |
""" | |
try: | |
# Validate input | |
if not description.strip(): | |
return None, "Please enter a description for your image", "" | |
logger.info(f"Generating image with model: {model_name}") | |
# Enhance prompt with Llama or fallback | |
enhanced_prompt = enhance_prompt_with_llama(description, creation_type, art_style, mood) | |
# 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 | |
# 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_name} with prompt: {enhanced_prompt[:100]}...") | |
# Log start time for performance tracking | |
start_time = time.time() | |
# Generate the image | |
image = hf_client.text_to_image( | |
prompt=enhanced_prompt, | |
model=model_name, | |
negative_prompt=negative_prompt | |
) | |
# Calculate generation time | |
generation_time = time.time() - start_time | |
logger.info(f"Image generated successfully in {generation_time:.2f} seconds") | |
# Success message with generation details | |
if use_llama: | |
enhancement_method = "Llama 4 AI" | |
else: | |
enhancement_method = "rule-based enhancement" | |
success_message = f"Image created successfully in {generation_time:.1f}s using {model_name.split('/')[-1]} model and {enhancement_method}" | |
return image, success_message, enhanced_prompt | |
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, creation_type, art_style, mood, model_name, 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 | |
except Exception as e: | |
logger.error(f"Unexpected error in generate_image: {str(e)}") | |
return None, f"Unexpected error: {str(e)}", "" | |
# Wrapper function for generate_image with status updates | |
def generate_with_status(description, creation_type_val, art_style_val, mood_val, model_name): | |
""" | |
Wrapper for generate_image that handles UI status updates and parameter formatting | |
Args: | |
description (str): User's original description | |
creation_type_val (str): Formatted creation type with icon | |
art_style_val (str): Formatted art style with icon | |
mood_val (str): Formatted mood with icon | |
model_name (str): Formatted model name with icon | |
Returns: | |
tuple: (image, status_html, enhanced_prompt, parameters_html) | |
""" | |
# Check if description is empty | |
if not description.strip(): | |
return None, update_status("Please enter a description", is_error=True), "", "" | |
# 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) | |
# Get model key from formatted name | |
model_key = None | |
for key, info in IMAGE_MODELS.items(): | |
if f"{info['icon']} {info['display_name']}" == model_name: | |
model_key = key | |
break | |
if not model_key: | |
return None, update_status("Invalid model selection", is_error=True), "", "" | |
try: | |
# Generate the image | |
image, message, enhanced_prompt = generate_image( | |
description, creation_key, art_key, mood_key, model_key | |
) | |
if image is None: | |
return None, update_status(message, is_error=True), "", "" | |
# Format parameters display | |
params_html = format_parameters(creation_type_val, art_style_val, mood_val, model_name) | |
# Success message | |
success_message = update_status(message) | |
return image, success_message, enhanced_prompt, params_html | |
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), "", "" | |
# =============== MAIN APPLICATION FLOW =============== | |
def main(): | |
""" | |
Main application entry point - creates UI and sets up event handlers | |
""" | |
# Create the UI components - event handlers are now defined inside create_ui | |
interface, *_ = create_ui() | |
# Launch the interface with appropriate parameters for Gradio version | |
# Check Gradio version to decide on the correct parameters to use | |
try: | |
# Use simple parameters that work across versions | |
interface.launch( | |
share=False, # Set to True to create a public link | |
debug=False # Set to True for development | |
) | |
except Exception as e: | |
logger.error(f"Error launching Gradio interface: {str(e)}") | |
# Fallback to the most basic launch parameters | |
interface.launch() | |
# =============== APP EXECUTION =============== | |
if __name__ == "__main__": | |
# Start the application | |
main() |