MySafeCode commited on
Commit
7e090a9
·
verified ·
1 Parent(s): 3978a3c

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +602 -106
app.py CHANGED
@@ -1,4 +1,5 @@
1
  import os
 
2
  import requests
3
  import gradio as gr
4
  from datetime import datetime
@@ -12,6 +13,59 @@ headers = {
12
  'Content-Type': 'application/json'
13
  }
14
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
  def get_models():
16
  """Fetch and display available models"""
17
  try:
@@ -47,133 +101,537 @@ def get_models():
47
  except Exception as e:
48
  return f"❌ Error: {str(e)}", "No data"
49
 
50
- def get_outputs():
51
- """Fetch and display recent outputs with images"""
 
 
 
52
  try:
53
- url = f'{API_HOST}/v1/image/generation/outputs'
54
- response = requests.get(url, headers=headers, timeout=10)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55
 
56
  if response.status_code == 200:
57
- data = response.json()
58
- outputs = data.get('outputs', [])
59
- total = data.get('total_count', 0)
60
- next_cursor = data.get('next', 'None')
61
-
62
- # Prepare display
63
- display_text = f"📊 Total outputs: {total}\n"
64
- display_text += f"📋 Showing: {len(outputs)} outputs\n"
65
- display_text += f"⏭️ Next cursor: {next_cursor}\n"
66
- display_text += f"⏰ {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n\n"
67
 
68
- # Create gallery HTML
69
- gallery_html = "<div style='display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 15px;'>"
 
 
 
70
 
 
 
71
  for i, output in enumerate(outputs, 1):
72
  output_id = output.get('id', 'N/A')
73
- short_id = output_id[:8] + '...' if len(output_id) > 8 else output_id
74
- created_at = output.get('created_at', 'N/A')
75
- status = output.get('status', 'unknown')
76
- model_name = output.get('model_name', 'Unknown')
77
-
78
- # Gallery status with emojis
79
- gallery_status = output.get('gallery_status', 'not_submitted')
80
- gallery_emoji = {
81
- 'not_submitted': '🔒',
82
- 'submitted': '📤',
83
- 'approved': '✅',
84
- 'rejected': '❌'
85
- }.get(gallery_status, '❓')
86
 
87
- # Favorites
88
- is_favorited = output.get('is_favorited', False)
89
- favorite_emoji = '❤️' if is_favorited else '🤍'
90
-
91
- # Format timestamp
92
- if created_at != 'N/A':
93
- try:
94
- dt = datetime.fromisoformat(created_at.replace('Z', '+00:00'))
95
- created_date = dt.strftime('%Y-%m-%d')
96
- created_time = dt.strftime('%H:%M')
97
- except:
98
- created_date = created_at
99
- created_time = ''
100
- else:
101
- created_date = 'Unknown'
102
- created_time = ''
103
-
104
- # Text display
105
- display_text += f"{i}. 🔹 **Output {short_id}**\n"
106
- display_text += f" 🕒 Created: {created_at}\n"
107
- display_text += f" 📊 Status: {status}\n"
108
- display_text += f" 🤖 Model: {model_name}\n"
109
- display_text += f" 🖼️ Gallery: {gallery_emoji} {gallery_status}\n"
110
- display_text += f" {favorite_emoji} Favorite\n"
111
-
112
- # Get image URL
113
- image_url = output.get('image_url')
114
- if not image_url:
115
- # Try to get from image_urls array
116
- image_urls = output.get('image_urls', [])
117
- if image_urls and isinstance(image_urls, list) and len(image_urls) > 0:
118
- image_url = image_urls[0]
119
 
120
  if image_url:
121
- # Add to gallery
122
- gallery_html += f"""
123
- <div style='border-radius: 10px; overflow: hidden; background: rgba(255,255,255,0.1); padding: 10px;'>
124
- <img src='{image_url}' style='width: 100%; height: 200px; object-fit: cover; border-radius: 8px;'>
125
- <div style='margin-top: 8px; font-size: 12px;'>
126
- <div>📅 {created_date}</div>
127
- <div>🤖 {model_name[:15]}{'...' if len(model_name) > 15 else ''}</div>
128
- <div>{gallery_emoji} {gallery_status}</div>
129
- <div>{favorite_emoji}</div>
130
- </div>
131
- </div>
132
- """
133
- display_text += f" 🖼️ Image URL: {image_url[:50]}...\n"
134
- else:
135
- display_text += f" 🖼️ No image available\n"
136
-
137
- # Show generation details if available
138
- generation = output.get('generation', {})
139
- if generation:
140
- prompt = generation.get('prompt', 'No prompt')
141
- if len(prompt) > 50:
142
- prompt = prompt[:50] + '...'
143
- display_text += f" 📝 Prompt: {prompt}\n"
144
-
145
- display_text += "─" * 40 + "\n"
146
 
147
- gallery_html += "</div>"
 
 
 
148
 
149
- if not outputs:
150
- display_text = "📭 No outputs found. Generate some images first!"
151
- gallery_html = "<div style='text-align: center; padding: 40px; color: #888;'>No images found</div>"
 
152
 
153
- return display_text, gallery_html, str(data)
154
 
155
  else:
156
- error_msg = f"❌ Error {response.status_code}"
157
- return error_msg, f"<div style='color: red; padding: 20px;'>{error_msg}</div>", f"Error: {response.text}"
158
 
159
  except Exception as e:
160
  error_msg = f"❌ Error: {str(e)}"
161
- return error_msg, f"<div style='color: red; padding: 20px;'>{error_msg}</div>", "No data"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
162
 
163
- # Create Gradio interface
164
- with gr.Blocks(title="StableCog Dashboard") as demo:
165
- gr.Markdown("# 🎯 StableCog Dashboard")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
166
 
167
  with gr.Tabs():
168
- with gr.Tab("🤖 Models"):
 
169
  with gr.Row():
170
- models_display = gr.Textbox(label="Available Models", lines=25)
171
- models_raw = gr.Code(label="Raw JSON", language="json", lines=25)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
172
 
173
- check_models_btn = gr.Button("🔄 Refresh Models", variant="primary")
174
- check_models_btn.click(get_models, outputs=[models_display, models_raw])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
175
 
176
- with gr.Tab("🖼️ Outputs"):
 
177
  with gr.Row():
178
  with gr.Column(scale=1):
179
  outputs_display = gr.Textbox(label="Output Details", lines=25)
@@ -181,10 +639,48 @@ with gr.Blocks(title="StableCog Dashboard") as demo:
181
  outputs_gallery = gr.HTML(label="Image Gallery")
182
 
183
  with gr.Row():
184
- outputs_raw = gr.Code(label="Raw JSON", language="json", lines=15)
 
 
 
 
 
185
 
186
- check_outputs_btn = gr.Button("🔄 Refresh Outputs", variant="primary")
187
- check_outputs_btn.click(get_outputs, outputs=[outputs_display, outputs_gallery, outputs_raw])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
188
 
189
  if __name__ == "__main__":
190
  demo.launch()
 
1
  import os
2
+ import json
3
  import requests
4
  import gradio as gr
5
  from datetime import datetime
 
13
  'Content-Type': 'application/json'
14
  }
15
 
16
+ # Global state for outputs pagination
17
+ current_outputs = []
18
+ current_page = 0
19
+ page_size = 10
20
+
21
+ # Default generation settings
22
+ DEFAULT_SETTINGS = {
23
+ "model_id": "048b4ea3-5586-47ed-900f-f4341c96bdb2", # SDXL 1.0
24
+ "width": 1024,
25
+ "height": 1024,
26
+ "num_outputs": 1,
27
+ "guidance_scale": 7.5,
28
+ "inference_steps": 30,
29
+ "scheduler_id": "euler",
30
+ "seed": None
31
+ }
32
+
33
+ # Get available models for dropdown
34
+ def get_model_list():
35
+ """Get list of models for dropdown"""
36
+ try:
37
+ url = f'{API_HOST}/v1/image/generation/models'
38
+ response = requests.get(url, headers=headers, timeout=10)
39
+ if response.status_code == 200:
40
+ models = response.json().get('models', [])
41
+ # Sort: default first, then by name
42
+ models.sort(key=lambda x: (not x.get('is_default', False), x.get('name', '')))
43
+ return [(m['name'], m['id']) for m in models]
44
+ except:
45
+ pass
46
+ return [("SDXL 1.0", "048b4ea3-5586-47ed-900f-f4341c96bdb2")]
47
+
48
+ # Get schedulers for dropdown
49
+ def get_scheduler_list():
50
+ """Get list of schedulers"""
51
+ return [
52
+ ("Euler", "euler"),
53
+ ("Euler A", "euler_a"),
54
+ ("DDIM", "ddim"),
55
+ ("DPMSolver++", "dpmpp_2m"),
56
+ ("DPM++ 2M Karras", "dpmpp_2m_karras"),
57
+ ("DPM++ SDE", "dpmpp_sde"),
58
+ ("DPM++ SDE Karras", "dpmpp_sde_karras"),
59
+ ("Heun", "heun"),
60
+ ("LMS", "lms"),
61
+ ("LMS Karras", "lms_karras")
62
+ ]
63
+
64
+ # Initialize model and scheduler lists
65
+ MODELS = get_model_list()
66
+ SCHEDULERS = get_scheduler_list()
67
+
68
+ # ========== MODELS TAB ==========
69
  def get_models():
70
  """Fetch and display available models"""
71
  try:
 
101
  except Exception as e:
102
  return f"❌ Error: {str(e)}", "No data"
103
 
104
+ # ========== GENERATE TAB ==========
105
+ def generate_image(prompt, negative_prompt, model_id, width, height,
106
+ num_outputs, guidance_scale, inference_steps,
107
+ scheduler_id, seed, init_image_url, prompt_strength):
108
+ """Generate images using StableCog API"""
109
  try:
110
+ url = f'{API_HOST}/v1/image/generation/create'
111
+
112
+ # Prepare request data
113
+ data = {
114
+ "prompt": prompt,
115
+ "model_id": model_id,
116
+ "width": int(width),
117
+ "height": int(height),
118
+ "num_outputs": int(num_outputs),
119
+ "guidance_scale": float(guidance_scale),
120
+ "inference_steps": int(inference_steps),
121
+ "scheduler_id": scheduler_id,
122
+ }
123
+
124
+ # Add optional fields if provided
125
+ if negative_prompt and negative_prompt.strip():
126
+ data["negative_prompt"] = negative_prompt.strip()
127
+
128
+ if seed and seed.strip():
129
+ try:
130
+ data["seed"] = int(seed.strip())
131
+ except:
132
+ pass
133
+
134
+ if init_image_url and init_image_url.strip():
135
+ data["init_image_url"] = init_image_url.strip()
136
+ if prompt_strength is not None:
137
+ data["prompt_strength"] = float(prompt_strength)
138
+
139
+ # Make API request
140
+ response = requests.post(
141
+ url,
142
+ data=json.dumps(data),
143
+ headers=headers,
144
+ timeout=30 # Longer timeout for generation
145
+ )
146
 
147
  if response.status_code == 200:
148
+ result = response.json()
149
+ outputs = result.get('outputs', [])
150
+ remaining_credits = result.get('remaining_credits', 0)
151
+ settings = result.get('settings', {})
 
 
 
 
 
 
152
 
153
+ # Format response
154
+ display_text = f" Generation successful!\n"
155
+ display_text += f"🪙 Remaining credits: {remaining_credits}\n"
156
+ display_text += f"🖼️ Generated {len(outputs)} image(s)\n"
157
+ display_text += f"⏰ {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n\n"
158
 
159
+ # Get image URLs for gallery
160
+ image_urls = []
161
  for i, output in enumerate(outputs, 1):
162
  output_id = output.get('id', 'N/A')
163
+ image_url = output.get('url', '')
 
 
 
 
 
 
 
 
 
 
 
 
164
 
165
+ display_text += f"{i}. Output ID: {output_id}\n"
166
+ display_text += f" 📷 URL: {image_url[:60]}...\n"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
167
 
168
  if image_url:
169
+ image_urls.append(image_url)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
170
 
171
+ # Create gallery HTML
172
+ gallery_html = ""
173
+ if image_urls:
174
+ gallery_html = create_gallery_html(image_urls, "Generated Images")
175
 
176
+ # Show settings used
177
+ display_text += "\n⚙️ Settings used:\n"
178
+ for key, value in settings.items():
179
+ display_text += f" {key}: {value}\n"
180
 
181
+ return display_text, gallery_html, str(result), None
182
 
183
  else:
184
+ error_msg = f"❌ Generation failed: {response.status_code}"
185
+ return error_msg, "", f"Error: {response.text}", None
186
 
187
  except Exception as e:
188
  error_msg = f"❌ Error: {str(e)}"
189
+ return error_msg, "", "No data", None
190
+
191
+ # ========== OUTPUTS TAB ==========
192
+ def fetch_outputs():
193
+ """Fetch outputs from API"""
194
+ global current_outputs
195
+ try:
196
+ url = f'{API_HOST}/v1/image/generation/outputs'
197
+ response = requests.get(url, headers=headers, timeout=10)
198
+
199
+ if response.status_code == 200:
200
+ data = response.json()
201
+ current_outputs = data.get('outputs', [])
202
+ return data
203
+ else:
204
+ return None
205
+ except:
206
+ return None
207
+
208
+ def create_gallery_html(image_urls, title="Gallery"):
209
+ """Create HTML gallery with lightbox"""
210
+ html = f"""
211
+ <style>
212
+ .image-gallery {{
213
+ display: grid;
214
+ grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
215
+ gap: 15px;
216
+ margin-bottom: 20px;
217
+ }}
218
+ .image-card {{
219
+ border-radius: 10px;
220
+ overflow: hidden;
221
+ background: rgba(255,255,255,0.1);
222
+ padding: 10px;
223
+ cursor: pointer;
224
+ transition: transform 0.2s;
225
+ }}
226
+ .image-card:hover {{
227
+ transform: scale(1.02);
228
+ background: rgba(255,255,255,0.15);
229
+ }}
230
+ .image-card img {{
231
+ width: 100%;
232
+ height: 200px;
233
+ object-fit: cover;
234
+ border-radius: 8px;
235
+ }}
236
+ .image-meta {{
237
+ margin-top: 8px;
238
+ font-size: 12px;
239
+ line-height: 1.4;
240
+ }}
241
+ .lightbox {{
242
+ display: none;
243
+ position: fixed;
244
+ z-index: 9999;
245
+ left: 0;
246
+ top: 0;
247
+ width: 100%;
248
+ height: 100%;
249
+ background: rgba(0,0,0,0.9);
250
+ justify-content: center;
251
+ align-items: center;
252
+ }}
253
+ .lightbox img {{
254
+ max-width: 90%;
255
+ max-height: 90%;
256
+ border-radius: 10px;
257
+ box-shadow: 0 20px 60px rgba(0,0,0,0.5);
258
+ }}
259
+ .lightbox.active {{
260
+ display: flex;
261
+ }}
262
+ .close-btn {{
263
+ position: absolute;
264
+ top: 20px;
265
+ right: 30px;
266
+ color: white;
267
+ font-size: 40px;
268
+ font-weight: bold;
269
+ cursor: pointer;
270
+ z-index: 10000;
271
+ }}
272
+ .pagination {{
273
+ display: flex;
274
+ justify-content: center;
275
+ gap: 10px;
276
+ margin-top: 20px;
277
+ }}
278
+ .page-btn {{
279
+ padding: 8px 16px;
280
+ background: rgba(255,255,255,0.1);
281
+ border: 1px solid rgba(255,255,255,0.2);
282
+ border-radius: 8px;
283
+ color: white;
284
+ cursor: pointer;
285
+ transition: background 0.2s;
286
+ }}
287
+ .page-btn:hover {{
288
+ background: rgba(255,255,255,0.2);
289
+ }}
290
+ .page-btn.disabled {{
291
+ opacity: 0.5;
292
+ cursor: not-allowed;
293
+ }}
294
+ .page-info {{
295
+ display: flex;
296
+ align-items: center;
297
+ padding: 8px 16px;
298
+ color: white;
299
+ }}
300
+ </style>
301
+
302
+ <div class="lightbox" id="lightbox" onclick="closeLightbox()">
303
+ <span class="close-btn" onclick="closeLightbox()">&times;</span>
304
+ <img id="lightbox-img" onclick="event.stopPropagation()">
305
+ </div>
306
+
307
+ <script>
308
+ function openLightbox(imgSrc) {{
309
+ document.getElementById('lightbox-img').src = imgSrc;
310
+ document.getElementById('lightbox').classList.add('active');
311
+ }}
312
+ function closeLightbox() {{
313
+ document.getElementById('lightbox').classList.remove('active');
314
+ }}
315
+ // Close lightbox on ESC key
316
+ document.addEventListener('keydown', function(e) {{
317
+ if (e.key === 'Escape') closeLightbox();
318
+ }});
319
+ </script>
320
+
321
+ <h3>{title}</h3>
322
+ <div class="image-gallery">
323
+ """
324
+
325
+ for i, image_url in enumerate(image_urls):
326
+ html += f"""
327
+ <div class="image-card" onclick="openLightbox('{image_url}')">
328
+ <img src="{image_url}" alt="Image {i+1}">
329
+ <div class="image-meta">
330
+ <div>Image #{i+1}</div>
331
+ </div>
332
+ </div>
333
+ """
334
+
335
+ html += "</div>"
336
+ return html
337
 
338
+ def update_outputs_display():
339
+ """Update outputs display with current page"""
340
+ global current_outputs, current_page, page_size
341
+
342
+ if not current_outputs:
343
+ return "📭 No outputs found. Generate some images first!", "<div style='text-align: center; padding: 40px; color: #888;'>No images found</div>", "[]"
344
+
345
+ total = len(current_outputs)
346
+ total_pages = (total + page_size - 1) // page_size # Ceiling division
347
+
348
+ # Calculate page bounds
349
+ start_idx = current_page * page_size
350
+ end_idx = min(start_idx + page_size, total)
351
+ page_outputs = current_outputs[start_idx:end_idx]
352
+
353
+ # Format display
354
+ display_text = f"📊 Total outputs: {total}\n"
355
+ display_text += f"📄 Page {current_page + 1} of {total_pages}\n"
356
+ display_text += f"🖼️ Showing {start_idx + 1}-{end_idx} of {total}\n"
357
+ display_text += f"⏰ {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n\n"
358
+
359
+ # Get image URLs for gallery
360
+ image_urls = []
361
+ for idx, output in enumerate(page_outputs, start=start_idx + 1):
362
+ output_id = output.get('id', 'N/A')
363
+ created_at = output.get('created_at', 'N/A')
364
+ model_name = output.get('model_name', 'Unknown')
365
+
366
+ # Gallery status
367
+ gallery_status = output.get('gallery_status', 'not_submitted')
368
+ gallery_emoji = {
369
+ 'not_submitted': '🔒',
370
+ 'submitted': '📤',
371
+ 'approved': '✅',
372
+ 'rejected': '❌'
373
+ }.get(gallery_status, '❓')
374
+
375
+ # Favorites
376
+ is_favorited = output.get('is_favorited', False)
377
+ favorite_emoji = '❤️' if is_favorited else '🤍'
378
+
379
+ # Format timestamp
380
+ if created_at != 'N/A':
381
+ try:
382
+ dt = datetime.fromisoformat(created_at.replace('Z', '+00:00'))
383
+ created_date = dt.strftime('%Y-%m-%d')
384
+ except:
385
+ created_date = created_at
386
+ else:
387
+ created_date = 'Unknown'
388
+
389
+ # Get image URL
390
+ image_url = output.get('image_url')
391
+ if not image_url:
392
+ image_urls_list = output.get('image_urls', [])
393
+ if image_urls_list and isinstance(image_urls_list, list) and len(image_urls_list) > 0:
394
+ image_url = image_urls_list[0]
395
+
396
+ if image_url:
397
+ image_urls.append(image_url)
398
+ display_text += f"{idx}. 🔹 **Output {output_id[:8]}...**\n"
399
+ display_text += f" 🕒 {created_at}\n"
400
+ display_text += f" 🤖 {model_name}\n"
401
+ display_text += f" 🖼️ {gallery_emoji} {gallery_status}\n"
402
+ display_text += f" {favorite_emoji} Favorite\n"
403
+
404
+ # Show generation details if available
405
+ generation = output.get('generation', {})
406
+ if generation:
407
+ prompt = generation.get('prompt', 'No prompt')
408
+ if len(prompt) > 50:
409
+ prompt = prompt[:50] + '...'
410
+ display_text += f" 📝 {prompt}\n"
411
+ else:
412
+ display_text += f"{idx}. ⚠️ No image data\n"
413
+
414
+ display_text += "─" * 40 + "\n"
415
+
416
+ # Create gallery HTML
417
+ gallery_html = create_gallery_html(image_urls, "Your Generated Images")
418
+
419
+ # Add pagination controls
420
+ gallery_html += f"""
421
+ <div class="pagination">
422
+ <button class="page-btn {'disabled' if current_page == 0 else ''}"
423
+ onclick="{'return false;' if current_page == 0 else f'window.paginationButtonClick({current_page - 1})'}">
424
+ ◀ Previous
425
+ </button>
426
+ <div class="page-info">
427
+ Page {current_page + 1} of {total_pages}
428
+ </div>
429
+ <button class="page-btn {'disabled' if current_page >= total_pages - 1 else ''}"
430
+ onclick="{'return false;' if current_page >= total_pages - 1 else f'window.paginationButtonClick({current_page + 1})'}">
431
+ Next ▶
432
+ </button>
433
+ </div>
434
+
435
+ <script>
436
+ window.paginationButtonClick = function(page) {{
437
+ const event = new CustomEvent('gradio_pagination', {{ detail: {{ page: page }} }});
438
+ document.dispatchEvent(event);
439
+ }}
440
+ </script>
441
+ """
442
+
443
+ return display_text, gallery_html, str(current_outputs)
444
+
445
+ def load_outputs():
446
+ """Load outputs from API and display first page"""
447
+ global current_page
448
+ current_page = 0
449
+ data = fetch_outputs()
450
+ if data:
451
+ return update_outputs_display()
452
+ else:
453
+ return "❌ Failed to load outputs", "<div style='color: red; padding: 20px;'>Failed to load outputs</div>", "[]"
454
+
455
+ def next_page():
456
+ """Go to next page"""
457
+ global current_page
458
+ if current_outputs:
459
+ total_pages = (len(current_outputs) + page_size - 1) // page_size
460
+ if current_page < total_pages - 1:
461
+ current_page += 1
462
+ return update_outputs_display()
463
+
464
+ def prev_page():
465
+ """Go to previous page"""
466
+ global current_page
467
+ if current_page > 0:
468
+ current_page -= 1
469
+ return update_outputs_display()
470
+
471
+ # ========== CREATE INTERFACE ==========
472
+ with gr.Blocks(title="StableCog Dashboard", theme=gr.themes.Soft()) as demo:
473
+ gr.Markdown("# 🎨 StableCog Image Generator")
474
 
475
  with gr.Tabs():
476
+ # ========== GENERATE TAB ==========
477
+ with gr.Tab("✨ Generate", id="generate"):
478
  with gr.Row():
479
+ with gr.Column(scale=1):
480
+ with gr.Group():
481
+ gr.Markdown("### 🎯 Prompt Settings")
482
+ prompt = gr.Textbox(
483
+ label="Prompt",
484
+ placeholder="Describe the image you want to generate...",
485
+ lines=3
486
+ )
487
+ negative_prompt = gr.Textbox(
488
+ label="Negative Prompt (Optional)",
489
+ placeholder="What to avoid in the image...",
490
+ lines=2
491
+ )
492
+ init_image_url = gr.Textbox(
493
+ label="Init Image URL (Optional)",
494
+ placeholder="https://example.com/image.jpg",
495
+ lines=1
496
+ )
497
+ prompt_strength = gr.Slider(
498
+ label="Prompt Strength (for img2img)",
499
+ minimum=0.0,
500
+ maximum=1.0,
501
+ value=0.8,
502
+ step=0.1,
503
+ visible=False
504
+ )
505
+
506
+ # Show prompt strength only when init image is provided
507
+ def toggle_prompt_strength(url):
508
+ return gr.update(visible=bool(url and url.strip()))
509
+
510
+ init_image_url.change(
511
+ toggle_prompt_strength,
512
+ inputs=[init_image_url],
513
+ outputs=[prompt_strength]
514
+ )
515
+
516
+ with gr.Group():
517
+ gr.Markdown("### ⚙️ Generation Settings")
518
+
519
+ with gr.Row():
520
+ model_id = gr.Dropdown(
521
+ label="Model",
522
+ choices=[m[0] for m in MODELS],
523
+ value=MODELS[0][0] if MODELS else "SDXL 1.0"
524
+ )
525
+ # Store actual model IDs
526
+ model_map = {m[0]: m[1] for m in MODELS}
527
+
528
+ with gr.Row():
529
+ width = gr.Slider(
530
+ label="Width",
531
+ minimum=256,
532
+ maximum=1024,
533
+ value=1024,
534
+ step=8
535
+ )
536
+ height = gr.Slider(
537
+ label="Height",
538
+ minimum=256,
539
+ maximum=1024,
540
+ value=1024,
541
+ step=8
542
+ )
543
+
544
+ num_outputs = gr.Slider(
545
+ label="Number of Images",
546
+ minimum=1,
547
+ maximum=4,
548
+ value=1,
549
+ step=1
550
+ )
551
+
552
+ guidance_scale = gr.Slider(
553
+ label="Guidance Scale",
554
+ minimum=1.0,
555
+ maximum=20.0,
556
+ value=7.5,
557
+ step=0.5
558
+ )
559
+
560
+ inference_steps = gr.Slider(
561
+ label="Inference Steps",
562
+ minimum=10,
563
+ maximum=50,
564
+ value=30,
565
+ step=1
566
+ )
567
+
568
+ with gr.Row():
569
+ scheduler_id = gr.Dropdown(
570
+ label="Scheduler",
571
+ choices=[s[0] for s in SCHEDULERS],
572
+ value="Euler"
573
+ )
574
+ # Store actual scheduler IDs
575
+ scheduler_map = {s[0]: s[1] for s in SCHEDULERS}
576
+
577
+ seed = gr.Textbox(
578
+ label="Seed (Optional)",
579
+ placeholder="Leave empty for random",
580
+ lines=1
581
+ )
582
+
583
+ with gr.Column(scale=1):
584
+ generate_btn = gr.Button(
585
+ "🚀 Generate Image",
586
+ variant="primary",
587
+ size="lg"
588
+ )
589
+
590
+ with gr.Group():
591
+ gr.Markdown("### 📊 Results")
592
+ generate_output = gr.Textbox(
593
+ label="Generation Result",
594
+ lines=15,
595
+ interactive=False
596
+ )
597
+
598
+ with gr.Group():
599
+ generate_gallery = gr.HTML(label="Generated Images")
600
+
601
+ with gr.Group():
602
+ generate_raw = gr.Code(
603
+ label="Raw API Response",
604
+ language="json",
605
+ lines=10,
606
+ interactive=False
607
+ )
608
 
609
+ # Connect generate button
610
+ def on_generate(prompt, negative_prompt, model_name, width, height,
611
+ num_outputs, guidance_scale, inference_steps,
612
+ scheduler_name, seed, init_image_url, prompt_strength):
613
+ # Get actual IDs from maps
614
+ actual_model_id = model_map.get(model_name, MODELS[0][1] if MODELS else DEFAULT_SETTINGS["model_id"])
615
+ actual_scheduler_id = scheduler_map.get(scheduler_name, "euler")
616
+
617
+ return generate_image(
618
+ prompt, negative_prompt, actual_model_id, width, height,
619
+ num_outputs, guidance_scale, inference_steps,
620
+ actual_scheduler_id, seed, init_image_url, prompt_strength
621
+ )
622
+
623
+ generate_btn.click(
624
+ on_generate,
625
+ inputs=[
626
+ prompt, negative_prompt, model_id, width, height,
627
+ num_outputs, guidance_scale, inference_steps,
628
+ scheduler_id, seed, init_image_url, prompt_strength
629
+ ],
630
+ outputs=[generate_output, generate_gallery, generate_raw]
631
+ )
632
 
633
+ # ========== OUTPUTS TAB ==========
634
+ with gr.Tab("🖼️ My Outputs", id="outputs"):
635
  with gr.Row():
636
  with gr.Column(scale=1):
637
  outputs_display = gr.Textbox(label="Output Details", lines=25)
 
639
  outputs_gallery = gr.HTML(label="Image Gallery")
640
 
641
  with gr.Row():
642
+ outputs_raw = gr.Code(label="Raw JSON", language="json", lines=10)
643
+
644
+ with gr.Row():
645
+ load_outputs_btn = gr.Button("🔄 Load My Outputs", variant="primary")
646
+ prev_page_btn = gr.Button("◀ Previous Page")
647
+ next_page_btn = gr.Button("Next Page ▶")
648
 
649
+ # Add pagination JavaScript
650
+ js = """
651
+ <script>
652
+ window.paginationButtonClick = function(page) {
653
+ const event = new CustomEvent('gradio_pagination', { detail: { page: page } });
654
+ document.dispatchEvent(event);
655
+ }
656
+ </script>
657
+ """
658
+ gr.HTML(js)
659
+
660
+ # Connect buttons
661
+ load_outputs_btn.click(
662
+ load_outputs,
663
+ outputs=[outputs_display, outputs_gallery, outputs_raw]
664
+ )
665
+
666
+ prev_page_btn.click(
667
+ prev_page,
668
+ outputs=[outputs_display, outputs_gallery, outputs_raw]
669
+ )
670
+
671
+ next_page_btn.click(
672
+ next_page,
673
+ outputs=[outputs_display, outputs_gallery, outputs_raw]
674
+ )
675
+
676
+ # ========== MODELS TAB ==========
677
+ with gr.Tab("🤖 Models", id="models"):
678
+ with gr.Row():
679
+ models_display = gr.Textbox(label="Available Models", lines=25)
680
+ models_raw = gr.Code(label="Raw JSON", language="json", lines=25)
681
+
682
+ check_models_btn = gr.Button("🔄 Refresh Models", variant="primary")
683
+ check_models_btn.click(get_models, outputs=[models_display, models_raw])
684
 
685
  if __name__ == "__main__":
686
  demo.launch()