StablecogAPI / dapp.py
MySafeCode's picture
Update dapp.py
4afccf1 verified
import os
import json
import requests
import gradio as gr
from datetime import datetime
import time
# API Configuration
API_KEY = os.getenv('StableCogKey')
API_HOST = 'https://api.stablecog.com'
headers = {
'Authorization': f'Bearer {API_KEY}',
'Content-Type': 'application/json'
}
# Global state for outputs pagination
current_outputs = []
current_page = 0
page_size = 10
# ========== MODEL AND SCHEDULER MANAGEMENT ==========
def get_model_list():
"""Get list of models for dropdown"""
try:
url = f'{API_HOST}/v1/image/generation/models'
response = requests.get(url, headers=headers, timeout=10)
if response.status_code == 200:
models = response.json().get('models', [])
# Sort: default first, then by name
models.sort(key=lambda x: (not x.get('is_default', False), x.get('name', '')))
return [(f"{m['name']}", m['id']) for m in models]
except Exception as e:
print(f"Error fetching models: {e}")
# Fallback to a working model from your successful response
return [
("SDXL 1.0", "0a99668b-45bd-4f7e-aa9c-f9aaa41ef13b"),
("SDXL Lightning", "22b0857d-7edc-4d00-9cd9-45aa509db093")
]
def get_scheduler_list():
"""Get list of schedulers"""
# From your successful response, use the actual scheduler IDs
return [
("Euler", "af2679a4-dbbb-4950-8c06-c3bb15416ef6"),
("Euler A", "6fb13a76-990d-49df-a2ab-7d9d22c33e3d"),
("DDIM", "c5a0bad3-bd9d-4c5c-96e3-9d8e8c0c7a6b"),
("DPMSolver++", "e9c3a7b3-2b5c-4a5d-8e2d-7b1c9a3d4e5f"),
("DPM++ 2M Karras", "f1c3a7b3-2b5c-4a5d-8e2d-7b1c9a3d4e5f")
]
# Initialize model and scheduler lists
MODELS = get_model_list()
SCHEDULERS = get_scheduler_list()
# Create mapping dictionaries
MODEL_MAP = {name: id for name, id in MODELS}
SCHEDULER_MAP = {name: id for name, id in SCHEDULERS}
# Default values
DEFAULT_MODEL = MODELS[0][0] if MODELS else "SDXL 1.0"
DEFAULT_SCHEDULER = SCHEDULERS[0][0] if SCHEDULERS else "Euler"
# ========== CUSTOM DARK THEME CSS ==========
CUSTOM_CSS = """
/* Dark theme background */
.gradio-container {
background: linear-gradient(135deg, #0a0a2a 0%, #1a1a3a 100%) !important;
min-height: 100vh !important;
padding: 20px !important;
}
/* Main container */
.container {
background: rgba(255, 255, 255, 0.05) !important;
backdrop-filter: blur(10px) !important;
border-radius: 20px !important;
padding: 30px !important;
border: 1px solid rgba(255, 255, 255, 0.1) !important;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3) !important;
max-width: 1400px !important;
margin: 0 auto !important;
}
/* Tab styling */
.tabs {
background: rgba(0, 0, 0, 0.3) !important;
border-radius: 10px !important;
padding: 5px !important;
margin-bottom: 20px !important;
}
.tab-nav {
background: rgba(255, 255, 255, 0.1) !important;
border-radius: 8px !important;
}
.tab-button {
background: transparent !important;
color: #aaa !important;
border: none !important;
}
.tab-button.selected {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
color: white !important;
font-weight: bold !important;
}
/* Button styling */
button {
border-radius: 8px !important;
border: none !important;
transition: all 0.2s !important;
}
button.primary {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
color: white !important;
font-weight: bold !important;
}
button.secondary {
background: rgba(255, 255, 255, 0.1) !important;
color: white !important;
border: 1px solid rgba(255, 255, 255, 0.2) !important;
}
button:hover {
transform: translateY(-2px) !important;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3) !important;
}
/* Input styling */
input, textarea, .gradio-dropdown {
background: rgba(255, 255, 255, 0.05) !important;
border: 1px solid rgba(255, 255, 255, 0.1) !important;
color: white !important;
border-radius: 8px !important;
}
input:focus, textarea:focus, .gradio-dropdown:focus-within {
border-color: #667eea !important;
box-shadow: 0 0 0 2px rgba(102, 126, 234, 0.2) !important;
outline: none !important;
}
/* Label styling */
label {
color: #ddd !important;
font-weight: 500 !important;
}
/* Textbox and code styling */
.gradio-textbox, .gradio-code {
background: rgba(0, 0, 0, 0.3) !important;
border: 1px solid rgba(255, 255, 255, 0.1) !important;
color: #eee !important;
}
/* Slider styling */
.gr-slider > .range {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
}
.gr-slider > .range-container {
background: rgba(255, 255, 255, 0.1) !important;
}
/* Group styling */
.gr-group {
background: rgba(255, 255, 255, 0.05) !important;
border: 1px solid rgba(255, 255, 255, 0.1) !important;
border-radius: 12px !important;
padding: 20px !important;
margin-bottom: 20px !important;
}
/* Markdown styling */
.gr-markdown h1, .gr-markdown h2, .gr-markdown h3 {
color: white !important;
}
.gr-markdown p {
color: #bbb !important;
}
/* Accordion styling */
.gr-accordion {
background: rgba(255, 255, 255, 0.05) !important;
border: 1px solid rgba(255, 255, 255, 0.1) !important;
border-radius: 8px !important;
}
/* Custom scrollbar */
::-webkit-scrollbar {
width: 8px;
height: 8px;
}
::-webkit-scrollbar-track {
background: rgba(255, 255, 255, 0.05);
border-radius: 4px;
}
::-webkit-scrollbar-thumb {
background: rgba(102, 126, 234, 0.5);
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background: rgba(102, 126, 234, 0.8);
}
/* Header styling */
header {
background: transparent !important;
border-bottom: none !important;
}
/* Footer hiding */
footer {
display: none !important;
}
"""
# ========== MODELS TAB ==========
def get_models():
"""Fetch and display available models"""
try:
url = f'{API_HOST}/v1/image/generation/models'
response = requests.get(url, headers=headers, timeout=10)
if response.status_code == 200:
data = response.json()
models = data.get('models', [])
# Format display
display_text = f"📊 Found {len(models)} models\n"
display_text += f"⏰ {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n\n"
for i, model in enumerate(models, 1):
name = model.get('name', 'Unknown')
model_type = model.get('type', 'unknown')
description = model.get('description', 'No description')
display_text += f"{i}. 🔹 **{name}**\n"
display_text += f" 📝 {description}\n"
display_text += f" 🏷️ Type: {model_type}\n"
display_text += f" 🌍 Public: {'✅' if model.get('is_public') else '❌'}\n"
display_text += f" ⭐ Default: {'✅' if model.get('is_default') else '❌'}\n"
display_text += f" 👥 Community: {'✅' if model.get('is_community') else '❌'}\n"
display_text += "─" * 40 + "\n"
return display_text, str(data)
else:
return f"❌ Error {response.status_code}", f"Error: {response.text}"
except Exception as e:
return f"❌ Error: {str(e)}", "No data"
# ========== GENERATE TAB ==========
def generate_image(prompt, negative_prompt, model_name, width, height,
num_outputs, guidance_scale, inference_steps,
scheduler_name, seed, init_image_url, prompt_strength):
"""Generate images using StableCog API"""
try:
url = f'{API_HOST}/v1/image/generation/create'
# Get actual IDs from maps
model_id = MODEL_MAP.get(model_name, MODELS[0][1] if MODELS else "0a99668b-45bd-4f7e-aa9c-f9aaa41ef13b")
scheduler_id = SCHEDULER_MAP.get(scheduler_name, "af2679a4-dbbb-4950-8c06-c3bb15416ef6")
# Prepare request data
data = {
"prompt": prompt,
"model_id": model_id,
"width": int(width),
"height": int(height),
"num_outputs": int(num_outputs),
"guidance_scale": float(guidance_scale),
"inference_steps": int(inference_steps),
"scheduler_id": scheduler_id,
}
# Add optional fields if provided
if negative_prompt and negative_prompt.strip():
data["negative_prompt"] = negative_prompt.strip()
if seed and seed.strip():
try:
data["seed"] = int(seed.strip())
except:
# Generate random seed if invalid
import random
data["seed"] = random.randint(1, 1000000000)
if init_image_url and init_image_url.strip():
data["init_image_url"] = init_image_url.strip()
if prompt_strength is not None:
data["prompt_strength"] = float(prompt_strength)
# Show loading state
start_time = time.time()
# Make API request
response = requests.post(
url,
json=data,
headers=headers,
timeout=60 # Longer timeout for generation
)
generation_time = time.time() - start_time
if response.status_code == 200:
result = response.json()
outputs = result.get('outputs', [])
remaining_credits = result.get('remaining_credits', 0)
settings = result.get('settings', {})
# Format response
display_text = f"✅ Generation successful!\n"
display_text += f"🪙 Remaining credits: {remaining_credits}\n"
display_text += f"🖼️ Generated {len(outputs)} image(s)\n"
display_text += f"⏱️ Generation time: {generation_time:.1f}s\n"
display_text += f"⏰ {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n\n"
# Get image URLs for gallery
image_urls = []
for i, output in enumerate(outputs, 1):
output_id = output.get('id', 'N/A')
image_url = output.get('url', '')
display_text += f"{i}. 🔹 Output ID: {output_id}\n"
display_text += f" 📷 URL: {image_url}\n"
if image_url:
image_urls.append(image_url)
# Create gallery HTML
gallery_html = ""
if image_urls:
gallery_html = create_gallery_html(image_urls, "🎨 Generated Images")
# Show settings used
display_text += "\n⚙️ Settings used:\n"
for key, value in settings.items():
display_text += f" {key}: {value}\n"
return display_text, gallery_html, str(result)
else:
error_msg = f"❌ Generation failed: {response.status_code}"
try:
error_detail = response.json()
error_msg += f"\nDetails: {error_detail.get('error', str(error_detail))}"
except:
error_msg += f"\nResponse: {response.text[:200]}..."
return error_msg, "", error_msg
except Exception as e:
error_msg = f"❌ Error: {str(e)}"
return error_msg, "", error_msg
# ========== OUTPUTS TAB ==========
def create_gallery_html(image_urls, title="Gallery"):
"""Create HTML gallery with lightbox"""
if not image_urls:
return """
<div style='
text-align: center;
padding: 60px;
color: #888;
background: rgba(255,255,255,0.05);
border-radius: 12px;
border: 2px dashed rgba(255,255,255,0.1);
margin: 20px 0;
'>
<div style='font-size: 48px; margin-bottom: 20px;'>🎨</div>
<div style='font-size: 18px; font-weight: bold;'>No images to display</div>
<div style='margin-top: 10px; opacity: 0.7;'>Generate some images first!</div>
</div>
"""
html = f"""
<style>
.image-gallery {{
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 15px;
margin-bottom: 20px;
}}
.image-card {{
border-radius: 10px;
overflow: hidden;
background: rgba(255,255,255,0.1);
padding: 10px;
cursor: pointer;
transition: transform 0.2s, box-shadow 0.2s;
border: 1px solid rgba(255,255,255,0.1);
}}
.image-card:hover {{
transform: scale(1.02);
background: rgba(255,255,255,0.15);
box-shadow: 0 5px 15px rgba(0,0,0,0.3);
border-color: #667eea;
}}
.image-card img {{
width: 100%;
height: 200px;
object-fit: cover;
border-radius: 8px;
}}
.image-meta {{
margin-top: 8px;
font-size: 12px;
line-height: 1.4;
color: rgba(255,255,255,0.9);
}}
.lightbox {{
display: none;
position: fixed;
z-index: 9999;
left: 0;
top: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.95);
justify-content: center;
align-items: center;
animation: fadeIn 0.3s;
}}
@keyframes fadeIn {{
from {{ opacity: 0; }}
to {{ opacity: 1; }}
}}
.lightbox img {{
max-width: 95%;
max-height: 95%;
border-radius: 10px;
box-shadow: 0 20px 60px rgba(0,0,0,0.5);
animation: zoomIn 0.3s;
}}
@keyframes zoomIn {{
from {{ transform: scale(0.9); opacity: 0; }}
to {{ transform: scale(1); opacity: 1; }}
}}
.lightbox.active {{
display: flex;
}}
.close-btn {{
position: absolute;
top: 20px;
right: 30px;
color: white;
font-size: 40px;
font-weight: bold;
cursor: pointer;
z-index: 10000;
opacity: 0.8;
transition: opacity 0.2s;
background: rgba(0,0,0,0.5);
width: 50px;
height: 50px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
}}
.close-btn:hover {{
opacity: 1;
background: rgba(0,0,0,0.7);
}}
.pagination {{
display: flex;
justify-content: center;
gap: 10px;
margin-top: 20px;
}}
.page-btn {{
padding: 8px 16px;
background: rgba(255,255,255,0.1);
border: 1px solid rgba(255,255,255,0.2);
border-radius: 8px;
color: white;
cursor: pointer;
transition: background 0.2s;
}}
.page-btn:hover {{
background: rgba(255,255,255,0.2);
}}
.page-btn.disabled {{
opacity: 0.5;
cursor: not-allowed;
}}
.page-info {{
display: flex;
align-items: center;
padding: 8px 16px;
color: white;
font-weight: bold;
background: rgba(255,255,255,0.05);
border-radius: 8px;
}}
.gallery-title {{
color: white;
font-size: 24px;
margin-bottom: 20px;
padding-bottom: 10px;
border-bottom: 2px solid rgba(102, 126, 234, 0.5);
}}
</style>
<div class="lightbox" id="lightbox" onclick="closeLightbox()">
<span class="close-btn" onclick="closeLightbox()">&times;</span>
<img id="lightbox-img" onclick="event.stopPropagation()">
</div>
<script>
function openLightbox(imgSrc) {{
document.getElementById('lightbox-img').src = imgSrc;
document.getElementById('lightbox').classList.add('active');
document.body.style.overflow = 'hidden';
}}
function closeLightbox() {{
document.getElementById('lightbox').classList.remove('active');
document.body.style.overflow = 'auto';
}}
// Close lightbox on ESC key
document.addEventListener('keydown', function(e) {{
if (e.key === 'Escape') closeLightbox();
}});
</script>
<div class="gallery-title">{title}</div>
<div class="image-gallery">
"""
for i, image_url in enumerate(image_urls):
html += f"""
<div class="image-card" onclick="openLightbox('{image_url}')">
<img src="{image_url}" alt="Image {i+1}" onerror="this.src='https://via.placeholder.com/200x200/333/666?text=Image+Error'">
<div class="image-meta">
<div>🖼️ Image #{i+1}</div>
<div>📏 Click to enlarge</div>
</div>
</div>
"""
html += "</div>"
return html
def fetch_outputs():
"""Fetch outputs from API"""
global current_outputs
try:
url = f'{API_HOST}/v1/image/generation/outputs'
response = requests.get(url, headers=headers, timeout=10)
if response.status_code == 200:
data = response.json()
current_outputs = data.get('outputs', [])
return data
else:
return None
except:
return None
def update_outputs_display():
"""Update outputs display with current page"""
global current_outputs, current_page, page_size
if not current_outputs:
return "📭 No outputs found. Generate some images first!", """
<div style='
text-align: center;
padding: 60px;
color: #888;
background: rgba(255,255,255,0.05);
border-radius: 12px;
border: 2px dashed rgba(255,255,255,0.1);
margin: 20px 0;
'>
<div style='font-size: 48px; margin-bottom: 20px;'>🎨</div>
<div style='font-size: 18px; font-weight: bold;'>No images found yet</div>
<div style='margin-top: 10px; opacity: 0.7;'>Try generating some images in the Generate tab!</div>
</div>
""", "[]"
total = len(current_outputs)
total_pages = (total + page_size - 1) // page_size # Ceiling division
# Calculate page bounds
start_idx = current_page * page_size
end_idx = min(start_idx + page_size, total)
page_outputs = current_outputs[start_idx:end_idx]
# Format display
display_text = f"📊 Total outputs: {total}\n"
display_text += f"📄 Page {current_page + 1} of {total_pages}\n"
display_text += f"🖼️ Showing {start_idx + 1}-{end_idx} of {total}\n"
display_text += f"⏰ {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n\n"
# Get image URLs for gallery
image_urls = []
for idx, output in enumerate(page_outputs, start=start_idx + 1):
output_id = output.get('id', 'N/A')
created_at = output.get('created_at', 'N/A')
model_name = output.get('model_name', 'Unknown')
# Gallery status
gallery_status = output.get('gallery_status', 'not_submitted')
gallery_emoji = {
'not_submitted': '🔒',
'submitted': '📤',
'approved': '✅',
'rejected': '❌'
}.get(gallery_status, '❓')
# Favorites
is_favorited = output.get('is_favorited', False)
favorite_emoji = '❤️' if is_favorited else '🤍'
# Format timestamp
if created_at != 'N/A':
try:
dt = datetime.fromisoformat(created_at.replace('Z', '+00:00'))
created_date = dt.strftime('%Y-%m-%d')
created_time = dt.strftime('%H:%M')
display_date = f"{created_date} {created_time}"
except:
display_date = created_at
else:
display_date = 'Unknown'
# Get image URL
image_url = output.get('image_url')
if not image_url:
image_urls_list = output.get('image_urls', [])
if image_urls_list and isinstance(image_urls_list, list) and len(image_urls_list) > 0:
image_url = image_urls_list[0]
if image_url:
image_urls.append(image_url)
display_text += f"{idx}. 🔹 **Output {output_id[:8]}...**\n"
display_text += f" 🕒 {display_date}\n"
display_text += f" 🤖 {model_name}\n"
display_text += f" 🖼️ {gallery_emoji} {gallery_status}\n"
display_text += f" {favorite_emoji} Favorite\n"
# Show generation details if available
generation = output.get('generation', {})
if generation:
prompt = generation.get('prompt', 'No prompt')
if len(prompt) > 50:
prompt = prompt[:50] + '...'
display_text += f" 📝 Prompt: {prompt}\n"
else:
display_text += f"{idx}. ⚠️ No image data\n"
display_text += "─" * 40 + "\n"
# Create gallery HTML
gallery_html = create_gallery_html(image_urls, f"🎨 Your Generated Images (Page {current_page + 1}/{total_pages})")
# Add pagination controls
gallery_html += f"""
<div class="pagination">
<button class="page-btn {'disabled' if current_page == 0 else ''}"
onclick="{'return false;' if current_page == 0 else f'window.paginationButtonClick({current_page - 1})'}">
◀ Previous
</button>
<div class="page-info">
Page {current_page + 1} of {total_pages}
</div>
<button class="page-btn {'disabled' if current_page >= total_pages - 1 else ''}"
onclick="{'return false;' if current_page >= total_pages - 1 else f'window.paginationButtonClick({current_page + 1})'}">
Next ▶
</button>
</div>
<script>
window.paginationButtonClick = function(page) {{
const event = new CustomEvent('gradio_pagination', {{ detail: {{ page: page }} }});
document.dispatchEvent(event);
}}
</script>
"""
return display_text, gallery_html, str(current_outputs)
def load_outputs():
"""Load outputs from API and display first page"""
global current_page
current_page = 0
data = fetch_outputs()
if data:
return update_outputs_display()
else:
return "❌ Failed to load outputs", """
<div style='
color: #ff6b6b;
padding: 30px;
background: rgba(255,107,107,0.1);
border-radius: 12px;
border: 1px solid rgba(255,107,107,0.3);
text-align: center;
'>
<div style='font-size: 24px; margin-bottom: 10px;'>⚠️</div>
<div style='font-size: 16px; font-weight: bold;'>Failed to load outputs</div>
<div style='margin-top: 10px; opacity: 0.8;'>Check your API key and connection</div>
</div>
""", "[]"
def next_page():
"""Go to next page"""
global current_page
if current_outputs:
total_pages = (len(current_outputs) + page_size - 1) // page_size
if current_page < total_pages - 1:
current_page += 1
return update_outputs_display()
def prev_page():
"""Go to previous page"""
global current_page
if current_page > 0:
current_page -= 1
return update_outputs_display()
# ========== CREATE INTERFACE ==========
with gr.Blocks(title="StableCog Image Generator") as demo:
# Add custom CSS directly as HTML
gr.HTML(f"<style>{CUSTOM_CSS}</style>")
gr.Markdown("""
<div class="container">
<h1 style="color: white; text-align: center; margin-bottom: 10px;">🎨 StableCog Image Generator</h1>
<p style="color: #bbb; text-align: center; margin-bottom: 30px;">Create, browse, and manage your AI-generated images</p>
""")
with gr.Tabs():
# ========== GENERATE TAB ==========
with gr.Tab("✨ Generate", id="generate"):
with gr.Row():
with gr.Column(scale=1):
with gr.Group():
gr.Markdown("### 🎯 Prompt Settings")
prompt = gr.Textbox(
label="Prompt",
placeholder="A beautiful sunset over mountains, digital art...",
lines=3,
value="A majestic dragon flying over a fantasy castle, digital art, epic lighting"
)
negative_prompt = gr.Textbox(
label="Negative Prompt (Optional)",
placeholder="blurry, low quality, distorted...",
lines=2,
value="blurry, distorted, low quality, ugly"
)
init_image_url = gr.Textbox(
label="Init Image URL (Optional - for img2img)",
placeholder="https://example.com/image.jpg",
lines=1
)
prompt_strength = gr.Slider(
label="Prompt Strength (for img2img)",
minimum=0.0,
maximum=1.0,
value=0.8,
step=0.1,
visible=False
)
# Show prompt strength only when init image is provided
init_image_url.change(
lambda x: gr.update(visible=bool(x and x.strip())),
inputs=[init_image_url],
outputs=[prompt_strength]
)
with gr.Group():
gr.Markdown("### ⚙️ Generation Settings")
with gr.Row():
model_dropdown = gr.Dropdown(
label="Model",
choices=[m[0] for m in MODELS],
value=DEFAULT_MODEL,
info="Select which AI model to use"
)
with gr.Row():
width = gr.Slider(
label="Width",
minimum=256,
maximum=1024,
value=768,
step=8,
info="Image width (pixels)"
)
height = gr.Slider(
label="Height",
minimum=256,
maximum=1024,
value=768,
step=8,
info="Image height (pixels)"
)
num_outputs = gr.Slider(
label="Number of Images",
minimum=1,
maximum=4,
value=1,
step=1,
info="How many images to generate (uses more credits)"
)
guidance_scale = gr.Slider(
label="Guidance Scale",
minimum=1.0,
maximum=20.0,
value=7.0,
step=0.5,
info="How closely to follow the prompt"
)
inference_steps = gr.Slider(
label="Inference Steps",
minimum=10,
maximum=50,
value=30,
step=1,
info="More steps = more detail but slower"
)
with gr.Row():
scheduler_dropdown = gr.Dropdown(
label="Scheduler",
choices=[s[0] for s in SCHEDULERS],
value=DEFAULT_SCHEDULER,
info="Diffusion sampling method"
)
seed = gr.Textbox(
label="Seed (Optional)",
placeholder="Leave empty for random",
lines=1,
info="Same seed + same settings = same image"
)
with gr.Column(scale=1):
generate_btn = gr.Button(
"🚀 Generate Image",
variant="primary",
size="lg",
scale=1
)
with gr.Group():
gr.Markdown("### 📊 Generation Results")
generate_output = gr.Textbox(
label="Status & Details",
lines=15,
interactive=False,
value="Ready to generate! Enter a prompt and click the button above."
)
with gr.Group():
gr.Markdown("### 🖼️ Generated Images")
generate_gallery = gr.HTML(
label="",
value="""
<div style='
text-align: center;
padding: 40px;
color: #888;
background: rgba(255,255,255,0.05);
border-radius: 12px;
'>
<div style='font-size: 36px; margin-bottom: 10px;'>🖼️</div>
<div>Images will appear here after generation</div>
</div>
"""
)
with gr.Group():
with gr.Accordion("📋 Raw API Response", open=False):
generate_raw = gr.Code(
label="",
language="json",
lines=10,
interactive=False,
value="// API response will appear here"
)
# Connect generate button
generate_btn.click(
generate_image,
inputs=[
prompt, negative_prompt, model_dropdown, width, height,
num_outputs, guidance_scale, inference_steps,
scheduler_dropdown, seed, init_image_url, prompt_strength
],
outputs=[generate_output, generate_gallery, generate_raw]
)
# ========== OUTPUTS TAB ==========
with gr.Tab("🖼️ My Outputs", id="outputs"):
with gr.Row():
with gr.Column(scale=1):
outputs_display = gr.Textbox(
label="Output Details",
lines=25,
value="Click 'Load My Outputs' to see your generated images."
)
with gr.Column(scale=2):
outputs_gallery = gr.HTML(
label="Image Gallery",
value="""
<div style='
text-align: center;
padding: 60px;
color: #888;
background: rgba(255,255,255,0.05);
border-radius: 12px;
border: 2px dashed rgba(255,255,255,0.1);
'>
<div style='font-size: 48px; margin-bottom: 20px;'>🎨</div>
<div style='font-size: 18px; font-weight: bold;'>Your images will appear here</div>
<div style='margin-top: 10px; opacity: 0.7;'>Click 'Load My Outputs' to see your generated images</div>
</div>
"""
)
with gr.Row():
with gr.Accordion("📋 Raw JSON Data", open=False):
outputs_raw = gr.Code(
label="",
language="json",
lines=10,
interactive=False,
value="// Your outputs data will appear here"
)
with gr.Row():
load_outputs_btn = gr.Button