Ivaylo77 commited on
Commit
3011c06
·
verified ·
1 Parent(s): 1d8383a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +410 -194
app.py CHANGED
@@ -1,8 +1,226 @@
 
 
 
 
 
1
  import torch
2
  import spaces
3
  import gradio as gr
4
- from diffusers import DiffusionPipeline
 
 
5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6
  # Load the pipeline once at startup
7
  print("Loading Z-Image-Turbo pipeline...")
8
  pipe = DiffusionPipeline.from_pretrained(
@@ -19,241 +237,239 @@ pipe.to("cuda")
19
  print("Pipeline loaded!")
20
 
21
  @spaces.GPU
22
- def generate_image(prompt, height, width, num_inference_steps, seed, randomize_seed, progress=gr.Progress(track_tqdm=True)):
23
- """Generate an image from the given prompt."""
 
 
 
 
 
 
 
 
 
 
 
24
  if randomize_seed:
25
- seed = torch.randint(0, 2**32 - 1, (1,)).item()
26
-
27
- generator = torch.Generator("cuda").manual_seed(int(seed))
28
- image = pipe(
29
- prompt=prompt,
30
- height=int(height),
31
- width=int(width),
32
- num_inference_steps=int(num_inference_steps),
33
- guidance_scale=0.0,
34
- generator=generator,
35
- ).images[0]
36
-
37
- return image, seed
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
 
39
  # Example prompts
40
  examples = [
41
-
42
-
43
-
44
-
45
-
46
  ]
47
 
48
- # Custom theme with modern aesthetics (Gradio 6)
49
- custom_theme = gr.themes.Soft(
50
- primary_hue="green",
51
- secondary_hue="amber",
52
- neutral_hue="slate",
53
- font=gr.themes.GoogleFont("Inter"),
54
- text_size="lg",
55
- spacing_size="md",
56
- radius_size="lg"
57
- ).set(
58
- button_primary_background_fill="*primary_500",
59
- button_primary_background_fill_hover="*primary_600",
60
- block_title_text_weight="600",
61
- )
62
-
63
  # Build the Gradio interface
64
- with gr.Blocks(fill_height=True) as demo:
65
- # Header
 
 
66
  gr.Markdown(
67
  """
68
- # Z-Image-Turbo
 
 
 
 
69
  """,
70
- elem_classes="header-text"
71
  )
72
 
73
- with gr.Row(equal_height=False):
74
- # Left column - Input controls
75
- with gr.Column(scale=1, min_width=600):
76
  prompt = gr.Textbox(
77
- label="✨ Your Prompt",
78
- placeholder="Describe the image you want to create...",
79
- lines=5,
80
- max_lines=10,
81
- autofocus=True,
82
  )
83
-
84
- with gr.Accordion("⚙️ Advanced Settings", open=True):
85
- with gr.Row():
86
- height = gr.Slider(
87
- minimum=512,
88
- maximum=2048,
89
- value=1024,
90
- step=64,
91
- label="Height",
92
- info="Pixels_______________________"
93
- )
94
- width = gr.Slider(
95
- minimum=512,
96
- maximum=2048,
97
- value=1024,
98
- step=64,
99
- label="Width",
100
- info="Pixels________________________"
101
- )
102
-
 
 
 
 
103
  num_inference_steps = gr.Slider(
104
  minimum=1,
105
  maximum=20,
106
  value=9,
107
  step=1,
108
  label="Inference Steps",
109
- info="9 steps = 8 DiT (recommended)"
110
  )
111
-
112
- with gr.Row():
113
- randomize_seed = gr.Checkbox(
114
- label="🎲 Random Seed",
115
- value=True,
116
- )
117
- seed = gr.Number(
118
- label="Seed",
119
- value=42,
120
- precision=0,
121
- visible=False,
122
- )
123
-
124
- def toggle_seed(randomize):
125
- return gr.Number(visible=not randomize)
126
-
127
- randomize_seed.change(
128
- toggle_seed,
129
- inputs=[randomize_seed],
130
- outputs=[seed]
131
- )
132
-
133
- generate_btn = gr.Button(
134
- "🚀 Generate Image",
135
- variant="primary",
136
- size="lg",
137
- scale=1
138
  )
139
-
140
- # Example prompts
141
- gr.Examples(
142
- examples=examples,
143
- inputs=[prompt],
144
- label=" ",
145
- examples_per_page=5,
 
146
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
147
 
148
- # Right column - Output
149
- with gr.Column(scale=1, min_width=320):
150
- output_image = gr.Image(
151
- label="Generated Image",
152
- type="pil",
153
- format="png",
154
- show_label=False,
155
- height=600,
156
- buttons=["download", "share"],
157
  )
158
-
159
- used_seed = gr.Number(
160
- label="🎲 Seed Used",
161
  interactive=False,
162
- container=True,
 
 
 
 
 
 
 
 
 
163
  )
164
 
165
- # Footer credits
 
 
 
 
 
 
166
  gr.Markdown(
167
  """
168
- ---
169
- <div style="text-align: center; opacity: 0.7; font-size: 0.9em; margin-top: 1rem;">
170
- <strong>Model:</strong> <a href="https://huggingface.co/Tongyi-MAI/Z-Image-Turbo" target="_blank">Tongyi-MAI/Z-Image-Turbo</a> (Apache 2.0 License) •
171
-
172
-
173
-
174
  </div>
175
  """,
176
- elem_classes="footer-text"
177
  )
178
 
179
  # Connect the generate button
180
  generate_btn.click(
181
  fn=generate_image,
182
- inputs=[prompt, height, width, num_inference_steps, seed, randomize_seed],
183
- outputs=[output_image, used_seed],
 
 
 
 
184
  )
185
 
186
  # Also allow generating by pressing Enter in the prompt box
187
  prompt.submit(
188
  fn=generate_image,
189
- inputs=[prompt, height, width, num_inference_steps, seed, randomize_seed],
190
- outputs=[output_image, used_seed],
 
 
 
 
 
 
 
 
 
 
191
  )
192
 
193
  if __name__ == "__main__":
194
- demo.launch(
195
- theme=custom_theme,
196
- css="""
197
- .header-text h1 {
198
- font-size: 2.5rem !important;
199
- font-weight: 700 !important;
200
- margin-bottom: 0.5rem !important;
201
- background: linear-gradient(135deg, #fbbf24 0%, #f59e0b 100%);
202
- -webkit-background-clip: text;
203
- -webkit-text-fill-color: transparent;
204
- background-clip: text;
205
- }
206
-
207
- .header-text p {
208
- font-size: 1.1rem !important;
209
- color: #64748b !important;
210
- margin-top: 0 !important;
211
- }
212
-
213
- .footer-text {
214
- padding: 1rem 0;
215
- }
216
-
217
- .footer-text a {
218
- color: #f59e0b !important;
219
- text-decoration: none !important;
220
- font-weight: 500;
221
- }
222
-
223
- .footer-text a:hover {
224
- text-decoration: underline !important;
225
- }
226
-
227
- /* Mobile optimizations */
228
- @media (max-width: 768px) {
229
- .header-text h1 {
230
- font-size: 1.8rem !important;
231
- }
232
-
233
- .header-text p {
234
- font-size: 1rem !important;
235
- }
236
- }
237
-
238
- /* Smooth transitions */
239
- button, .gr-button {
240
- transition: all 0.2s ease !important;
241
- }
242
-
243
- button:hover, .gr-button:hover {
244
- transform: translateY(-1px);
245
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15) !important;
246
- }
247
-
248
- /* Better spacing */
249
- .gradio-container {
250
- max-width: 1400px !important;
251
- margin: 0 auto !important;
252
- }
253
- """,
254
- footer_links=[
255
- "api",
256
- "gradio"
257
- ],
258
- mcp_server=True
259
- )
 
1
+ import io
2
+ import tempfile
3
+ import zipfile
4
+
5
+ import random
6
  import torch
7
  import spaces
8
  import gradio as gr
9
+ from diffusers import ZImagePipeline
10
+ MAX_SEED = 2**32 - 1
11
+
12
 
13
+ # ===== Custom aesthetic =====
14
+ # Neo-noir dusk palette with cyan + amber accents, glass panels, and subtle grain.
15
+ CUSTOM_CSS = """
16
+ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap');
17
+ :root {
18
+ /* Light Mode (Professional & Clean) */
19
+ --bg: #fdfdfd;
20
+ --panel: rgba(255, 255, 255, 0.95);
21
+ --card: #ffffff;
22
+ --border: #e5e7eb;
23
+ --border-hover: #d1d5db;
24
+ --text: #111827;
25
+ --text-secondary: #4b5563;
26
+ --muted: #9ca3af;
27
+ --accent: #0f172a; /* Dark sleek accent for professionalism */
28
+ --accent-hover: #1e293b;
29
+ --accent-text: #ffffff;
30
+ --primary-gradient: linear-gradient(135deg, #0f172a 0%, #334155 100%);
31
+ --glow: 0 0 0 transparent;
32
+ --shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
33
+ --shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.05), 0 2px 4px -1px rgba(0, 0, 0, 0.03);
34
+ --shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.05), 0 4px 6px -2px rgba(0, 0, 0, 0.03);
35
+ --radius: 12px;
36
+ --input-bg: #ffffff;
37
+ --input-border: #e2e8f0;
38
+ --checkbox-bg: #f1f5f9;
39
+ --body-bg: #f8fafc; /* Very subtle cool gray */
40
+ --font-heading: 'Inter', -apple-system, sans-serif;
41
+ --font-body: 'Inter', -apple-system, sans-serif;
42
+ }
43
+ .dark {
44
+ /* Dark Mode (Neo-Noir Polished) */
45
+ --bg: #05080f;
46
+ --panel: rgba(12, 18, 32, 0.85);
47
+ --card: rgba(18, 28, 46, 0.70);
48
+ --border: rgba(36, 224, 194, 0.15);
49
+ --border-hover: rgba(36, 224, 194, 0.3);
50
+ --text: #e9f3ff;
51
+ --text-secondary: #94a3b8;
52
+ --muted: #64748b;
53
+ --accent: #24e0c2;
54
+ --accent-hover: #18cdb0;
55
+ --accent-text: #041019;
56
+ --primary-gradient: linear-gradient(120deg, #24e0c2 0%, #ffb347 100%);
57
+ --glow: 0 8px 32px rgba(36, 224, 194, 0.12);
58
+ --shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.2);
59
+ --shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.3);
60
+ --shadow-lg: 0 20px 40px -5px rgba(0, 0, 0, 0.4);
61
+ --radius: 16px;
62
+ --input-bg: rgba(255,255,255,0.03);
63
+ --input-border: rgba(255,255,255,0.08);
64
+ --checkbox-bg: #0d1829;
65
+ --body-bg: radial-gradient(circle at 20% 20%, rgba(36, 224, 194, 0.06), transparent 35%),
66
+ radial-gradient(circle at 82% 12%, rgba(0, 156, 196, 0.06), transparent 35%),
67
+ linear-gradient(145deg, #05080f 0%, #080f1e 100%);
68
+ --font-heading: 'Inter', -apple-system, sans-serif;
69
+ --font-body: 'Inter', -apple-system, sans-serif;
70
+ }
71
+ body, .gradio-container {
72
+ font-family: var(--font-body) !important;
73
+ background: var(--body-bg) !important;
74
+ color: var(--text);
75
+ min-height: 100vh;
76
+ }
77
+ /* Titles & Typography */
78
+ .gradio-container .prose h1,
79
+ .gradio-container .prose h2,
80
+ .gradio-container .prose h3 {
81
+ font-family: var(--font-heading);
82
+ letter-spacing: -0.025em;
83
+ font-weight: 700;
84
+ color: var(--text);
85
+ }
86
+ .gradio-container .prose h1 {
87
+ font-size: 2.25rem;
88
+ margin-bottom: 0.5rem;
89
+ background: var(--primary-gradient);
90
+ -webkit-background-clip: text;
91
+ -webkit-text-fill-color: transparent;
92
+ background-clip: text;
93
+ display: inline-block;
94
+ }
95
+ .gradio-container * { letter-spacing: -0.01em; }
96
+ /* Panels & Cards */
97
+ .gr-block, .gr-panel, .gr-group {
98
+ background: var(--panel);
99
+ border: 1px solid var(--border);
100
+ border-radius: var(--radius);
101
+ box-shadow: var(--shadow-sm);
102
+ backdrop-filter: blur(8px);
103
+ transition: box-shadow 0.2s ease, border-color 0.2s ease;
104
+ }
105
+ .hero-card {
106
+ background: var(--card);
107
+ border: 1px solid var(--border);
108
+ padding: 24px;
109
+ border-radius: var(--radius);
110
+ box-shadow: var(--shadow-md);
111
+ position: relative;
112
+ overflow: hidden;
113
+ }
114
+ .tagline {
115
+ display: inline-flex;
116
+ align-items: center;
117
+ gap: 8px;
118
+ padding: 6px 14px;
119
+ background: var(--input-bg);
120
+ border: 1px solid var(--border);
121
+ border-radius: 999px;
122
+ font-size: 0.875rem;
123
+ font-weight: 500;
124
+ color: var(--text-secondary);
125
+ margin-bottom: 12px;
126
+ }
127
+ .hero-card p {
128
+ color: var(--text-secondary);
129
+ font-size: 1.05rem;
130
+ line-height: 1.6;
131
+ max-width: 65ch;
132
+ }
133
+ /* Inputs */
134
+ textarea, input:not([type='checkbox']):not([type='radio']),
135
+ .gr-input, .gr-textbox, .gr-number, .gr-slider input {
136
+ background: var(--input-bg) !important;
137
+ border: 1px solid var(--input-border) !important;
138
+ border-radius: 10px !important;
139
+ color: var(--text) !important;
140
+ font-family: var(--font-body);
141
+ transition: all 0.2s ease;
142
+ }
143
+ textarea:focus, input:focus, .gr-input:focus-within {
144
+ border-color: var(--text-secondary) !important;
145
+ box-shadow: 0 0 0 2px rgba(var(--accent), 0.1);
146
+ }
147
+ label, .gr-box label {
148
+ color: var(--text-secondary) !important;
149
+ font-weight: 600;
150
+ font-size: 0.875rem;
151
+ margin-bottom: 6px;
152
+ text-transform: none !important;
153
+ }
154
+ /* Sliders */
155
+ .gr-slider input[type='range'] {
156
+ accent-color: var(--accent);
157
+ }
158
+ /* Buttons */
159
+ .gr-button-primary, button.primary {
160
+ background: var(--primary-gradient) !important;
161
+ color: var(--accent-text) !important;
162
+ font-weight: 600 !important;
163
+ border: 1px solid rgba(255,255,255,0.1) !important;
164
+ box-shadow: var(--shadow-md);
165
+ border-radius: 10px !important;
166
+ padding: 10px 24px;
167
+ transition: transform 0.1s, box-shadow 0.2s;
168
+ }
169
+ .gr-button-primary:hover {
170
+ transform: translateY(-1px);
171
+ box-shadow: var(--shadow-lg);
172
+ filter: brightness(1.1);
173
+ }
174
+ .gr-button-secondary, button.secondary, .gr-downloadbutton {
175
+ background: var(--input-bg) !important;
176
+ border: 1px solid var(--border) !important;
177
+ color: var(--text) !important;
178
+ font-weight: 500;
179
+ border-radius: 10px !important;
180
+ box-shadow: var(--shadow-sm);
181
+ }
182
+ .gr-button-secondary:hover {
183
+ border-color: var(--border-hover) !important;
184
+ background: var(--card) !important;
185
+ }
186
+ .gr-downloadbutton, .gr-downloadbutton > button { width: 100%; }
187
+ /* Gallery */
188
+ .gr-gallery {
189
+ background: var(--input-bg);
190
+ border-radius: var(--radius);
191
+ border: 1px solid var(--border);
192
+ padding: 8px;
193
+ }
194
+ .gr-gallery .thumbnail-item {
195
+ border-radius: 8px;
196
+ overflow: hidden;
197
+ box-shadow: var(--shadow-sm);
198
+ border: 1px solid transparent;
199
+ transition: all 0.2s;
200
+ }
201
+ .gr-gallery .thumbnail-item:hover {
202
+ box-shadow: var(--shadow-md);
203
+ transform: scale(1.02);
204
+ }
205
+ .gr-gallery img { object-fit: cover; }
206
+ /* Footer */
207
+ .footer-note {
208
+ color: var(--muted);
209
+ font-size: 0.875rem;
210
+ text-align: center;
211
+ margin-top: 2rem;
212
+ opacity: 0.8;
213
+ }
214
+ .footer-note a {
215
+ color: var(--text-secondary);
216
+ text-decoration: none;
217
+ border-bottom: 1px dotted var(--muted);
218
+ }
219
+ .footer-note a:hover {
220
+ color: var(--accent);
221
+ border-bottom-style: solid;
222
+ }
223
+ """
224
  # Load the pipeline once at startup
225
  print("Loading Z-Image-Turbo pipeline...")
226
  pipe = DiffusionPipeline.from_pretrained(
 
237
  print("Pipeline loaded!")
238
 
239
  @spaces.GPU
240
+ def generate_image(
241
+ prompt,
242
+ negative_prompt,
243
+ height,
244
+ width,
245
+ images_count,
246
+ num_inference_steps,
247
+ guidance_scale,
248
+ seed,
249
+ randomize_seed,
250
+ progress=gr.Progress(track_tqdm=True),
251
+ ):
252
+ """Generate N images using a deterministic seed cascade (x1..xN)."""
253
  if randomize_seed:
254
+ seed = random.randint(0, MAX_SEED)
255
+
256
+ base_seed = int(seed) % MAX_SEED
257
+ if base_seed < 0:
258
+ base_seed += MAX_SEED
259
+
260
+ # Cap to prevent excessive VRAM usage / latency spikes on the demo space
261
+ images_count = max(1, min(int(images_count), 12))
262
+
263
+ seeds = [(base_seed * i) % MAX_SEED for i in range(1, images_count + 1)]
264
+
265
+ neg_prompt = None
266
+ if isinstance(negative_prompt, str) and negative_prompt.strip():
267
+ neg_prompt = negative_prompt
268
+
269
+ images = []
270
+ image_paths = []
271
+ for s in seeds:
272
+ generator = torch.Generator("cuda").manual_seed(int(s))
273
+ image = pipe(
274
+ prompt=prompt,
275
+ negative_prompt=neg_prompt,
276
+ height=int(height),
277
+ width=int(width),
278
+ num_inference_steps=int(num_inference_steps),
279
+ guidance_scale=float(guidance_scale), # 0.0 is recommended default for Turbo
280
+ generator=generator,
281
+ ).images[0]
282
+ images.append(image)
283
+ tmp_img = tempfile.NamedTemporaryFile(delete=False, suffix=".png")
284
+ image.save(tmp_img.name, format="PNG")
285
+ image_paths.append(tmp_img.name)
286
+
287
+ return images, ", ".join(str(s) for s in seeds), image_paths, base_seed
288
+
289
+
290
+
291
+ def append_history(new_images, history):
292
+ """Append new images to the history state."""
293
+ if history is None:
294
+ history = []
295
+ updated_history = history + new_images
296
+ return updated_history, updated_history
297
+
298
+
299
+ def package_zip(image_paths):
300
+ """Pack the current image list into a ZIP file for download."""
301
+ if not image_paths:
302
+ raise gr.Error("No images in history to download.")
303
+
304
+ tmp = tempfile.NamedTemporaryFile(delete=False, suffix=".zip")
305
+ with zipfile.ZipFile(tmp, "w", zipfile.ZIP_DEFLATED) as zf:
306
+ for idx, path in enumerate(image_paths, start=1):
307
+ # Store as image_001.png, image_002.png, ...
308
+ zf.write(path, arcname=f"image_{idx:03d}.png")
309
+
310
+ tmp.flush()
311
+ return tmp.name
312
+
313
 
314
  # Example prompts
315
  examples = [
316
+ ["Astronaut riding a horse on Mars, cinematic lighting, sci-fi concept art, highly detailed"],
317
+ ["Portrait of a wise old wizard with a long white beard, holding a glowing crystal staff, magical forest background"],
 
 
 
318
  ]
319
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
320
  # Build the Gradio interface
321
+ # Build the Gradio interface
322
+ with gr.Blocks(title="Z-Image-Turbo Demo", css=CUSTOM_CSS, analytics_enabled=False) as demo:
323
+ image_state = gr.State([])
324
+ history_state = gr.State([])
325
  gr.Markdown(
326
  """
327
+ <div class="hero-card">
328
+ <div class="tagline">⚡ Turbo diffusion · 8 steps · CUDA ready</div>
329
+ <h1>Z‑Image Turbo Studio</h1>
330
+ <p>Draft up to twelve stylized candidates in one pass. Neo‑noir gradients, glass panels, and crisp typography keep the tooling out of your way while you explore ideas.</p>
331
+ </div>
332
  """,
333
+ sanitize_html=False,
334
  )
335
 
336
+ with gr.Row():
337
+ with gr.Column(scale=1):
 
338
  prompt = gr.Textbox(
339
+ label="Prompt",
340
+ placeholder="e.g. bioluminescent reef city at dusk, cinematic, anamorphic glow",
341
+ lines=4,
 
 
342
  )
343
+
344
+ negative_prompt = gr.Textbox(
345
+ label="Negative Prompt",
346
+ placeholder="noise, blur, extra limbs, text watermark",
347
+ lines=3,
348
+ )
349
+
350
+ with gr.Row():
351
+ height = gr.Slider(
352
+ minimum=512,
353
+ maximum=2048,
354
+ value=1024,
355
+ step=64,
356
+ label="Height",
357
+ )
358
+ width = gr.Slider(
359
+ minimum=512,
360
+ maximum=2048,
361
+ value=1024,
362
+ step=64,
363
+ label="Width",
364
+ )
365
+
366
+ with gr.Row():
367
  num_inference_steps = gr.Slider(
368
  minimum=1,
369
  maximum=20,
370
  value=9,
371
  step=1,
372
  label="Inference Steps",
373
+ info="9 steps 8 DiT forwards",
374
  )
375
+
376
+ images_count = gr.Slider(
377
+ minimum=1,
378
+ maximum=12,
379
+ value=4,
380
+ step=1,
381
+ label="Images",
382
+ info="1–12 (higher counts use more VRAM)",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
383
  )
384
+
385
+ guidance_scale = gr.Slider(
386
+ minimum=0.0,
387
+ maximum=7.0,
388
+ value=0.0,
389
+ step=0.1,
390
+ label="CFG Guidance Scale",
391
+ info="0 = no CFG (recommended for Turbo models)",
392
  )
393
+
394
+ with gr.Row():
395
+ seed = gr.Number(
396
+ label="Base Seed",
397
+ value=42,
398
+ precision=0,
399
+ )
400
+ randomize_seed = gr.Checkbox(
401
+ label="Randomize",
402
+ value=True,
403
+ interactive=True,
404
+ )
405
+
406
+ generate_btn = gr.Button("🚀 Generate", variant="primary", size="lg")
407
 
408
+ with gr.Column(scale=1):
409
+ output_images = gr.Gallery(
410
+ label="Generated Grid",
411
+ columns=4,
412
+ rows=None,
413
+ preview=True,
 
 
 
414
  )
415
+ used_seeds = gr.Textbox(
416
+ label="Seed Cascade (x1 · x2 · ... · xN)",
 
417
  interactive=False,
418
+ )
419
+ history_gallery = gr.Gallery(
420
+ label="History",
421
+ columns=6,
422
+ rows=None,
423
+ preview=True,
424
+ object_fit="cover"
425
+ )
426
+ download_btn = gr.DownloadButton(
427
+ label="📦 Download All History (ZIP)",
428
  )
429
 
430
+ gr.Markdown("### 💡 Quick Prompts")
431
+ gr.Examples(
432
+ examples=examples,
433
+ inputs=[prompt],
434
+ cache_examples=False,
435
+ )
436
+
437
  gr.Markdown(
438
  """
439
+ <div class="footer-note">
440
+ Model: Tongyi-MAI/Z-Image-Turbo (Apache 2.0). Demo by <a href="https://z-image-turbo.tech" target="_blank">https://z-image-turbo.tech</a>
 
 
 
 
441
  </div>
442
  """,
443
+ sanitize_html=False,
444
  )
445
 
446
  # Connect the generate button
447
  generate_btn.click(
448
  fn=generate_image,
449
+ inputs=[prompt, negative_prompt, height, width, images_count, num_inference_steps, guidance_scale, seed, randomize_seed],
450
+ outputs=[output_images, used_seeds, image_state, seed],
451
+ ).success(
452
+ fn=append_history,
453
+ inputs=[image_state, history_state],
454
+ outputs=[history_state, history_gallery],
455
  )
456
 
457
  # Also allow generating by pressing Enter in the prompt box
458
  prompt.submit(
459
  fn=generate_image,
460
+ inputs=[prompt, negative_prompt, height, width, images_count, num_inference_steps, guidance_scale, seed, randomize_seed],
461
+ outputs=[output_images, used_seeds, image_state, seed],
462
+ ).success(
463
+ fn=append_history,
464
+ inputs=[image_state, history_state],
465
+ outputs=[history_state, history_gallery],
466
+ )
467
+
468
+ download_btn.click(
469
+ fn=package_zip,
470
+ inputs=[history_state],
471
+ outputs=[download_btn],
472
  )
473
 
474
  if __name__ == "__main__":
475
+ demo.launch(mcp_server=True, show_error=True)