tchung1970 Claude Opus 4.5 commited on
Commit
803c754
·
1 Parent(s): 850d0d4

Revamp UI to match Z-Image Apple-inspired design

Browse files

- Two-column horizontal layout with fixed 550px input column
- Large prompt textbox with character counter
- Aspect ratio dropdown with 2K resolutions (default 2:3 1344x2048)
- Removed input images and prompt upsampling features
- Apple-style theming with dark mode support
- Added CLAUDE.md for Claude Code guidance

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>

Files changed (2) hide show
  1. CLAUDE.md +46 -0
  2. app.py +385 -147
CLAUDE.md ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## Project Overview
6
+
7
+ This is a Hugging Face Spaces demo for FLUX.2 [dev], a 32B parameter rectified flow model for generating, editing, and combining images based on text instructions. It uses Black Forest Labs' FLUX.2-dev model.
8
+
9
+ ## Running the Application
10
+
11
+ ```bash
12
+ # Install dependencies
13
+ pip install -r requirements.txt
14
+
15
+ # Run the Gradio app
16
+ python app.py
17
+ ```
18
+
19
+ The app runs on Hugging Face Spaces with ZeroGPU infrastructure. Requires `HF_TOKEN` environment variable for the VLM-based prompt upsampling feature.
20
+
21
+ ## Architecture
22
+
23
+ ### Main Components
24
+
25
+ - **app.py**: Gradio web interface and inference pipeline
26
+ - Loads FLUX.2 transformer without text encoder (uses remote encoding)
27
+ - Uses `spaces.aoti_blocks_load()` to load pre-compiled transformer blocks from HF hub
28
+ - Main inference flow: prompt upsampling (optional) → remote text encoding → GPU image generation
29
+
30
+ - **optimization.py**: AOT compilation utilities for transformer blocks
31
+ - Defines dynamic shapes for variable-length image sequences
32
+ - Contains inductor configs for Triton compilation with cudagraphs
33
+
34
+ ### Key Pipeline Details
35
+
36
+ 1. **Text Encoding**: Offloaded to remote Gradio client (`multimodalart/mistral-text-encoder`) - runs on CPU, network-bound
37
+ 2. **Prompt Upsampling**: Uses ERNIE-4.5-VL via Hugging Face Inference API - two modes:
38
+ - Text-only: Enhances prompts with visual details
39
+ - Image+text: Converts editing requests into concise instructions
40
+ 3. **Image Generation**: GPU-bound, uses `@spaces.GPU` decorator with dynamic duration based on number of input images and inference steps
41
+
42
+ ### Configuration Constants
43
+
44
+ - `MAX_IMAGE_SIZE`: 1024
45
+ - `dtype`: torch.bfloat16
46
+ - Dimensions auto-adjust to uploaded image aspect ratio (multiples of 8, min 256, max 1024)
app.py CHANGED
@@ -2,6 +2,7 @@ import os
2
  import subprocess
3
  import sys
4
  import io
 
5
  import gradio as gr
6
  import numpy as np
7
  import random
@@ -187,44 +188,39 @@ def generate_image(prompt_embeds, image_list, width, height, num_inference_steps
187
  image = pipe(**pipe_kwargs).images[0]
188
  return image
189
 
190
- def infer(prompt, input_images=None, seed=42, randomize_seed=False, width=1024, height=1024, num_inference_steps=50, guidance_scale=2.5, prompt_upsampling=False, progress=gr.Progress(track_tqdm=True)):
191
-
 
 
 
 
 
 
 
 
192
  if randomize_seed:
193
  seed = random.randint(0, MAX_SEED)
194
-
195
- # Prepare image list (convert None or empty gallery to None)
196
- image_list = None
197
- if input_images is not None and len(input_images) > 0:
198
- image_list = []
199
- for item in input_images:
200
- image_list.append(item[0])
201
-
202
- # 1. Upsampling (Network bound - No GPU needed)
203
- final_prompt = prompt
204
- if prompt_upsampling:
205
- progress(0.05, desc="Upsampling prompt...")
206
- final_prompt = upsample_prompt_logic(prompt, image_list)
207
- print(f"Original Prompt: {prompt}")
208
- print(f"Upsampled Prompt: {final_prompt}")
209
-
210
- # 2. Text Encoding (Network bound - No GPU needed)
211
  progress(0.1, desc="Encoding prompt...")
212
- # This returns CPU tensors
213
- prompt_embeds = remote_text_encoder(final_prompt)
214
-
215
- # 3. Image Generation (GPU bound)
216
  progress(0.3, desc="Waiting for GPU...")
217
  image = generate_image(
218
- prompt_embeds,
219
- image_list,
220
- width,
221
- height,
222
- num_inference_steps,
223
- guidance_scale,
224
- seed,
225
  progress
226
  )
227
-
228
  return image, seed
229
 
230
  examples = [
@@ -239,132 +235,374 @@ examples_images = [
239
  ["The person from image 1 is petting the cat from image 2, the bird from image 3 is next to them", ["woman1.webp", "cat_window.webp", "bird.webp"]]
240
  ]
241
 
242
- css="""
243
- #col-container {
244
- margin: 0 auto;
245
- max-width: 1200px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
246
  }
247
- .gallery-container img{
248
- object-fit: contain;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
249
  }
250
  """
251
 
252
- with gr.Blocks() as demo:
253
-
254
- with gr.Column(elem_id="col-container"):
255
- gr.Markdown(f"""# FLUX.2 [dev]
256
- FLUX.2 [dev] is a 32B model rectified flow capable of generating, editing and combining images based on text instructions model [[model](https://huggingface.co/black-forest-labs/FLUX.2-dev)], [[blog](https://bfl.ai/blog/flux-2)]
257
- """)
258
- with gr.Row():
259
- with gr.Column():
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
260
  with gr.Row():
261
- prompt = gr.Text(
262
- label="Prompt",
263
- show_label=False,
264
- max_lines=2,
265
- placeholder="Enter your prompt",
266
- container=False,
267
- scale=3
268
- )
269
-
270
- run_button = gr.Button("Run", scale=1)
271
-
272
- with gr.Accordion("Input image(s) (optional)", open=True):
273
- input_images = gr.Gallery(
274
- label="Input Image(s)",
275
- type="pil",
276
- columns=3,
277
- rows=1,
278
- )
279
-
280
- with gr.Accordion("Advanced Settings", open=False):
281
- prompt_upsampling = gr.Checkbox(
282
- label="Prompt Upsampling",
283
- value=True,
284
- info="Automatically enhance the prompt using a VLM"
285
- )
286
-
287
- seed = gr.Slider(
288
- label="Seed",
289
- minimum=0,
290
- maximum=MAX_SEED,
291
  step=1,
292
- value=0,
293
  )
294
-
295
- randomize_seed = gr.Checkbox(label="Randomize seed", value=True)
296
-
297
- with gr.Row():
298
-
299
- width = gr.Slider(
300
- label="Width",
301
- minimum=256,
302
- maximum=MAX_IMAGE_SIZE,
303
- step=8,
304
- value=1024,
305
- )
306
-
307
- height = gr.Slider(
308
- label="Height",
309
- minimum=256,
310
- maximum=MAX_IMAGE_SIZE,
311
- step=8,
312
- value=1024,
313
- )
314
-
315
- with gr.Row():
316
-
317
- num_inference_steps = gr.Slider(
318
- label="Number of inference steps",
319
- minimum=1,
320
- maximum=100,
321
- step=1,
322
- value=30,
323
- )
324
-
325
- guidance_scale = gr.Slider(
326
- label="Guidance scale",
327
- minimum=0.0,
328
- maximum=10.0,
329
- step=0.1,
330
- value=4,
331
- )
332
-
333
-
334
- with gr.Column():
335
- result = gr.Image(label="Result", show_label=False)
336
-
337
-
338
- gr.Examples(
339
- examples=examples,
340
- fn=infer,
341
- inputs=[prompt],
342
- outputs=[result, seed],
343
- cache_examples=True,
344
- cache_mode="lazy"
345
- )
346
 
347
- gr.Examples(
348
- examples=examples_images,
349
- fn=infer,
350
- inputs=[prompt, input_images],
351
- outputs=[result, seed],
352
- cache_examples=True,
353
- cache_mode="lazy"
354
- )
355
-
356
- # Auto-update dimensions when images are uploaded
357
- input_images.upload(
358
- fn=update_dimensions_from_image,
359
- inputs=[input_images],
360
- outputs=[width, height]
361
- )
362
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
363
  gr.on(
364
- triggers=[run_button.click, prompt.submit],
365
  fn=infer,
366
- inputs=[prompt, input_images, seed, randomize_seed, width, height, num_inference_steps, guidance_scale, prompt_upsampling],
367
- outputs=[result, seed]
 
368
  )
369
 
370
- demo.launch(css=css)
 
 
 
 
2
  import subprocess
3
  import sys
4
  import io
5
+ import re
6
  import gradio as gr
7
  import numpy as np
8
  import random
 
188
  image = pipe(**pipe_kwargs).images[0]
189
  return image
190
 
191
+ def parse_aspect_ratio(aspect_ratio_str):
192
+ """Parse aspect ratio string to get width and height."""
193
+ # Extract dimensions from format like "1:1 (1024x1024)"
194
+ match = re.search(r'\((\d+)x(\d+)\)', aspect_ratio_str)
195
+ if match:
196
+ return int(match.group(1)), int(match.group(2))
197
+ return 1024, 1024 # Default
198
+
199
+ def infer(prompt, aspect_ratio="1:1 (1024x1024)", seed=42, randomize_seed=False, num_inference_steps=30, guidance_scale=4.0, progress=gr.Progress(track_tqdm=True)):
200
+
201
  if randomize_seed:
202
  seed = random.randint(0, MAX_SEED)
203
+
204
+ # Parse aspect ratio to get width and height
205
+ width, height = parse_aspect_ratio(aspect_ratio)
206
+
207
+ # Text Encoding (Network bound - No GPU needed)
 
 
 
 
 
 
 
 
 
 
 
 
208
  progress(0.1, desc="Encoding prompt...")
209
+ prompt_embeds = remote_text_encoder(prompt)
210
+
211
+ # Image Generation (GPU bound)
 
212
  progress(0.3, desc="Waiting for GPU...")
213
  image = generate_image(
214
+ prompt_embeds,
215
+ None, # No input images
216
+ width,
217
+ height,
218
+ num_inference_steps,
219
+ guidance_scale,
220
+ seed,
221
  progress
222
  )
223
+
224
  return image, seed
225
 
226
  examples = [
 
235
  ["The person from image 1 is petting the cat from image 2, the bird from image 3 is next to them", ["woman1.webp", "cat_window.webp", "bird.webp"]]
236
  ]
237
 
238
+ # Apple-inspired CSS styling
239
+ css = """
240
+ /* Global container styling */
241
+ .gradio-container {
242
+ max-width: 85vw !important;
243
+ margin: 0 auto !important;
244
+ font-family: -apple-system, BlinkMacSystemFont, 'Inter', 'SF Pro Display', sans-serif !important;
245
+ }
246
+
247
+ /* Main row - horizontal layout */
248
+ #main-row {
249
+ display: flex !important;
250
+ flex-direction: row !important;
251
+ flex-wrap: nowrap !important;
252
+ gap: 24px !important;
253
+ align-items: flex-start !important;
254
+ }
255
+
256
+ /* Input section - fixed width */
257
+ #input-column {
258
+ background: #ffffff !important;
259
+ border-radius: 18px !important;
260
+ padding: 32px !important;
261
+ box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08) !important;
262
+ width: 550px !important;
263
+ min-width: 550px !important;
264
+ max-width: 550px !important;
265
+ flex: 0 0 550px !important;
266
+ }
267
+
268
+ /* Output section - flexible */
269
+ #output-column {
270
+ flex: 1 1 auto !important;
271
+ min-height: 80vh !important;
272
+ max-height: 90vh !important;
273
+ display: flex !important;
274
+ flex-direction: column !important;
275
+ }
276
+
277
+ /* Header styling */
278
+ .header-container {
279
+ text-align: center;
280
+ margin-bottom: 24px;
281
+ }
282
+
283
+ .main-title {
284
+ font-size: 32px !important;
285
+ font-weight: 600 !important;
286
+ letter-spacing: -0.02em !important;
287
+ color: #1d1d1f !important;
288
+ margin: 0 !important;
289
+ }
290
+
291
+ /* Prompt textbox */
292
+ #prompt-textbox textarea {
293
+ min-height: 400px !important;
294
+ max-height: 500px !important;
295
+ border-radius: 12px !important;
296
+ border: 1px solid #d2d2d7 !important;
297
+ padding: 16px !important;
298
+ font-size: 15px !important;
299
+ line-height: 1.5 !important;
300
+ resize: vertical !important;
301
+ }
302
+
303
+ #prompt-textbox textarea:focus {
304
+ border-color: #0071e3 !important;
305
+ box-shadow: 0 0 0 4px rgba(0, 113, 227, 0.15) !important;
306
+ outline: none !important;
307
+ }
308
+
309
+ /* Character counter */
310
+ .char-counter {
311
+ text-align: center;
312
+ font-size: 13px;
313
+ color: #86868b;
314
+ margin-top: 8px;
315
+ margin-bottom: 16px;
316
+ }
317
+
318
+ .char-counter.warning {
319
+ color: #ff9500;
320
+ }
321
+
322
+ .char-counter.limit {
323
+ color: #ff3b30;
324
+ }
325
+
326
+ /* Generate button */
327
+ button.primary {
328
+ background: #0071e3 !important;
329
+ border: none !important;
330
+ border-radius: 980px !important;
331
+ padding: 12px 32px !important;
332
+ font-size: 17px !important;
333
+ font-weight: 500 !important;
334
+ color: white !important;
335
+ cursor: pointer !important;
336
+ transition: all 0.2s ease !important;
337
+ width: 100% !important;
338
+ margin-top: 16px !important;
339
+ }
340
+
341
+ button.primary:hover {
342
+ background: #0077ED !important;
343
+ transform: scale(1.02) !important;
344
+ }
345
+
346
+ /* Accordion styling */
347
+ .accordion {
348
+ border: 1px solid #d2d2d7 !important;
349
+ border-radius: 12px !important;
350
+ margin-top: 16px !important;
351
+ }
352
+
353
+ /* Gallery styling */
354
+ .gallery-container img {
355
+ object-fit: contain !important;
356
+ }
357
+
358
+ /* Output image */
359
+ #output-column .image-container {
360
+ border-radius: 18px !important;
361
+ overflow: hidden !important;
362
+ box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08) !important;
363
+ }
364
+
365
+ /* Dark mode support */
366
+ .dark #input-column {
367
+ background: #1d1d1f !important;
368
+ box-shadow: 0 2px 12px rgba(0, 0, 0, 0.4) !important;
369
  }
370
+
371
+ .dark .main-title {
372
+ color: #f5f5f7 !important;
373
+ }
374
+
375
+ .dark #prompt-textbox textarea {
376
+ background: #2d2d2f !important;
377
+ border-color: #424245 !important;
378
+ color: #f5f5f7 !important;
379
+ }
380
+
381
+ .dark #prompt-textbox textarea:focus {
382
+ border-color: #0071e3 !important;
383
+ }
384
+
385
+ .dark .char-counter {
386
+ color: #a1a1a6 !important;
387
+ }
388
+
389
+ /* Responsive adjustments */
390
+ @media (max-width: 1200px) {
391
+ #main-row {
392
+ flex-direction: column !important;
393
+ flex-wrap: wrap !important;
394
+ }
395
+
396
+ #input-column {
397
+ width: 100% !important;
398
+ min-width: 100% !important;
399
+ max-width: 100% !important;
400
+ flex: 1 1 100% !important;
401
+ }
402
+
403
+ #output-column {
404
+ width: 100% !important;
405
+ min-height: 50vh !important;
406
+ }
407
  }
408
  """
409
 
410
+ # JavaScript for layout control and character counter
411
+ js_code = """
412
+ function() {
413
+ // Force horizontal layout
414
+ function forceHorizontalLayout() {
415
+ const mainRow = document.getElementById('main-row');
416
+ if (mainRow) {
417
+ mainRow.style.display = 'flex';
418
+ mainRow.style.flexDirection = 'row';
419
+ mainRow.style.flexWrap = 'nowrap';
420
+ }
421
+
422
+ const inputCol = document.getElementById('input-column');
423
+ if (inputCol) {
424
+ inputCol.style.flex = '0 0 550px';
425
+ inputCol.style.width = '550px';
426
+ inputCol.style.minWidth = '550px';
427
+ inputCol.style.maxWidth = '550px';
428
+ }
429
+
430
+ const outputCol = document.getElementById('output-column');
431
+ if (outputCol) {
432
+ outputCol.style.flex = '1 1 auto';
433
+ }
434
+ }
435
+
436
+ // Character counter setup
437
+ function setupCharCounter() {
438
+ const textbox = document.querySelector('#prompt-textbox textarea');
439
+ const counterDiv = document.querySelector('.char-counter');
440
+ const countSpan = document.getElementById('char-count');
441
+
442
+ if (textbox && countSpan && counterDiv) {
443
+ const updateCounter = () => {
444
+ const len = textbox.value.length;
445
+ countSpan.textContent = len;
446
+
447
+ counterDiv.classList.remove('warning', 'limit');
448
+ if (len >= 2000) {
449
+ counterDiv.classList.add('limit');
450
+ } else if (len >= 1800) {
451
+ counterDiv.classList.add('warning');
452
+ }
453
+ };
454
+
455
+ textbox.addEventListener('input', updateCounter);
456
+ updateCounter();
457
+ }
458
+ }
459
+
460
+ // Run on load and with slight delay for Gradio rendering
461
+ forceHorizontalLayout();
462
+ setupCharCounter();
463
+
464
+ setTimeout(() => {
465
+ forceHorizontalLayout();
466
+ setupCharCounter();
467
+ }, 500);
468
+
469
+ setTimeout(() => {
470
+ forceHorizontalLayout();
471
+ setupCharCounter();
472
+ }, 1500);
473
+ }
474
+ """
475
+
476
+ # Theme configuration
477
+ theme = gr.themes.Soft(
478
+ primary_hue=gr.themes.colors.blue,
479
+ secondary_hue=gr.themes.colors.slate,
480
+ spacing_size=gr.themes.sizes.spacing_lg,
481
+ radius_size=gr.themes.sizes.radius_lg,
482
+ font=[gr.themes.GoogleFont("Inter"), "SF Pro Display", "-apple-system", "BlinkMacSystemFont", "sans-serif"],
483
+ ).set(
484
+ body_background_fill='#f5f5f7',
485
+ body_background_fill_dark='#000000',
486
+ button_primary_background_fill='#0071e3',
487
+ button_primary_background_fill_hover='#0077ED',
488
+ block_background_fill='#ffffff',
489
+ block_background_fill_dark='#1d1d1f',
490
+ input_border_color='#d2d2d7',
491
+ input_border_color_dark='#424245',
492
+ input_shadow_focus='0 0 0 4px rgba(0, 113, 227, 0.15)',
493
+ )
494
+
495
+ with gr.Blocks(
496
+ title="FLUX.2 [dev]",
497
+ theme=theme,
498
+ css=css,
499
+ fill_height=False,
500
+ ) as demo:
501
+
502
+ # Two-column layout
503
+ with gr.Row(equal_height=False, elem_id="main-row"):
504
+
505
+ # LEFT COLUMN - Input Controls
506
+ with gr.Column(scale=0, min_width=550, elem_id="input-column"):
507
+
508
+ # Header
509
+ gr.HTML("""
510
+ <div class="header-container">
511
+ <h1 class="main-title">FLUX.2 [dev]</h1>
512
+ </div>
513
+ """)
514
+
515
+ # Prompt Textbox
516
+ prompt = gr.Textbox(
517
+ placeholder="Describe the image you want to create...",
518
+ lines=15,
519
+ max_lines=20,
520
+ max_length=2000,
521
+ label="Prompt",
522
+ show_label=True,
523
+ container=True,
524
+ autoscroll=False,
525
+ elem_id="prompt-textbox",
526
+ )
527
+
528
+ # Character Counter
529
+ char_counter = gr.HTML(
530
+ '<div class="char-counter"><span id="char-count">0</span> characters (max 2000)</div>'
531
+ )
532
+
533
+ # Aspect Ratio Dropdown
534
+ aspect_ratio = gr.Dropdown(
535
+ choices=[
536
+ "1:1 (2048x2048)",
537
+ "2:3 (1344x2048)",
538
+ "3:2 (2048x1344)",
539
+ "3:4 (1536x2048)",
540
+ "4:3 (2048x1536)",
541
+ "9:16 (1152x2048)",
542
+ "16:9 (2048x1152)",
543
+ ],
544
+ value="2:3 (1344x2048)",
545
+ label="Aspect Ratio",
546
+ show_label=True,
547
+ container=True,
548
+ )
549
+
550
+ # Advanced Settings accordion
551
+ with gr.Accordion("Advanced Settings", open=False):
552
+ seed = gr.Slider(
553
+ label="Seed",
554
+ minimum=0,
555
+ maximum=MAX_SEED,
556
+ step=1,
557
+ value=0,
558
+ )
559
+
560
+ randomize_seed = gr.Checkbox(label="Randomize seed", value=True)
561
+
562
  with gr.Row():
563
+ num_inference_steps = gr.Slider(
564
+ label="Number of inference steps",
565
+ minimum=1,
566
+ maximum=100,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
567
  step=1,
568
+ value=30,
569
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
570
 
571
+ guidance_scale = gr.Slider(
572
+ label="Guidance scale",
573
+ minimum=0.0,
574
+ maximum=10.0,
575
+ step=0.1,
576
+ value=4,
577
+ )
 
 
 
 
 
 
 
 
578
 
579
+ # Generate Button
580
+ generate_btn = gr.Button(
581
+ "Generate",
582
+ variant="primary",
583
+ size="lg",
584
+ elem_classes="primary"
585
+ )
586
+
587
+ # RIGHT COLUMN - Image Output
588
+ with gr.Column(scale=2, elem_id="output-column"):
589
+ result = gr.Image(
590
+ label="Result",
591
+ show_label=False,
592
+ type="pil",
593
+ format="png",
594
+ )
595
+
596
+ # Event handlers
597
  gr.on(
598
+ triggers=[generate_btn.click, prompt.submit],
599
  fn=infer,
600
+ inputs=[prompt, aspect_ratio, seed, randomize_seed, num_inference_steps, guidance_scale],
601
+ outputs=[result, seed],
602
+ show_progress="full"
603
  )
604
 
605
+ # Load JavaScript for layout control
606
+ demo.load(None, None, None, js=js_code)
607
+
608
+ demo.launch()