prithivMLmods commited on
Commit
3c358cc
·
1 Parent(s): 5cf87ba

update app [cleaned] ✅ (#6)

Browse files

- update app [cleaned] ✅ (1f086007a8b80d41b735c1f6d835472f809c2920)

Files changed (1) hide show
  1. app.py +46 -79
app.py CHANGED
@@ -43,10 +43,6 @@ try:
43
  except Exception as e:
44
  print(f"Warning: Could not set FA3 processor: {e}")
45
 
46
- # ═══════════════════════════════════════════
47
- # Example Configuration & Helpers
48
- # ═══════════════════════════════════════════
49
-
50
  EXAMPLES_CONFIG = [
51
  {
52
  "images": ["examples/1.jpg"],
@@ -147,10 +143,6 @@ print("Building example thumbnails...")
147
  EXAMPLE_CARDS_HTML = build_example_cards_html()
148
  print(f"Built {len(EXAMPLES_CONFIG)} example cards.")
149
 
150
- # ═══════════════════════════════════════════
151
- # Inference Helpers
152
- # ═══════════════════════════════════════════
153
-
154
 
155
  def b64_to_pil_list(b64_json_str):
156
  if not b64_json_str or b64_json_str.strip() in ("", "[]"):
@@ -216,10 +208,6 @@ def infer(images_b64_json, prompt, seed, randomize_seed, guidance_scale, steps,
216
  torch.cuda.empty_cache()
217
 
218
 
219
- # ═══════════════════════════════════════════
220
- # CSS
221
- # ═══════════════════════════════════════════
222
-
223
  css = r"""
224
  @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600&display=swap');
225
  *{box-sizing:border-box;margin:0;padding:0}
@@ -252,9 +240,9 @@ footer{display:none!important}
252
  }
253
  .app-header-left{display:flex;align-items:center;gap:12px}
254
  .app-logo{
255
- width:36px;height:36px;background:linear-gradient(135deg,#FF4500,#FF6633,#FF8C66);
256
  border-radius:10px;display:flex;align-items:center;justify-content:center;
257
- box-shadow:0 4px 12px rgba(255,69,0,.35);
258
  }
259
  .app-logo svg{width:20px;height:20px;fill:#fff;flex-shrink:0}
260
  .app-title{
@@ -263,7 +251,7 @@ footer{display:none!important}
263
  }
264
  .app-badge{
265
  font-size:11px;font-weight:600;padding:3px 10px;border-radius:20px;
266
- background:rgba(255,69,0,.15);color:#FF6633;border:1px solid rgba(255,69,0,.25);letter-spacing:.3px;
267
  }
268
  .app-badge.fast{background:rgba(34,197,94,.12);color:#4ade80;border:1px solid rgba(34,197,94,.25)}
269
 
@@ -279,8 +267,8 @@ footer{display:none!important}
279
  font-family:'Inter',sans-serif;color:#ffffff!important;-webkit-text-fill-color:#ffffff!important;
280
  transition:all .15s ease;
281
  }
282
- .modern-tb-btn:hover{background:rgba(255,69,0,.15);border-color:rgba(255,69,0,.3)}
283
- .modern-tb-btn:active,.modern-tb-btn.active{background:rgba(255,69,0,.25);border-color:rgba(255,69,0,.45)}
284
  .modern-tb-btn .tb-label{font-size:13px;color:#ffffff!important;-webkit-text-fill-color:#ffffff!important;font-weight:600}
285
  .modern-tb-btn .tb-svg{width:15px;height:15px;flex-shrink:0;color:#ffffff!important}
286
  .modern-tb-btn .tb-svg,
@@ -299,16 +287,16 @@ body:not(.dark) .modern-tb-btn .tb-svg,body:not(.dark) .modern-tb-btn .tb-svg *{
299
  .app-main-right{width:420px;display:flex;flex-direction:column;flex-shrink:0;background:#18181b}
300
 
301
  #gallery-drop-zone{position:relative;background:#09090b;min-height:440px;overflow:auto}
302
- #gallery-drop-zone.drag-over{outline:2px solid #FF4500;outline-offset:-2px;background:rgba(255,69,0,.04)}
303
 
304
  .upload-prompt-modern{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);z-index:20}
305
  .upload-click-area{
306
  display:flex;flex-direction:column;align-items:center;justify-content:center;
307
  cursor:pointer;padding:36px 52px;border:2px dashed #3f3f46;border-radius:16px;
308
- background:rgba(255,69,0,.03);transition:all .2s ease;gap:8px;
309
  }
310
- .upload-click-area:hover{background:rgba(255,69,0,.08);border-color:#FF4500;transform:scale(1.03)}
311
- .upload-click-area:active{background:rgba(255,69,0,.12);transform:scale(.98)}
312
  .upload-click-area svg{width:80px;height:80px}
313
  .upload-main-text{color:#71717a;font-size:14px;font-weight:500;margin-top:4px}
314
  .upload-sub-text{color:#52525b;font-size:12px}
@@ -322,10 +310,10 @@ body:not(.dark) .modern-tb-btn .tb-svg,body:not(.dark) .modern-tb-btn .tb-svg *{
322
  cursor:pointer;border:2px solid #27272a;transition:all .2s ease;background:#18181b;
323
  }
324
  .gallery-thumb:hover{border-color:#3f3f46;transform:translateY(-2px);box-shadow:0 4px 12px rgba(0,0,0,.4)}
325
- .gallery-thumb.selected{border-color:#FF4500!important;box-shadow:0 0 0 3px rgba(255,69,0,.2)}
326
  .gallery-thumb img{width:100%;height:100%;object-fit:cover}
327
  .thumb-badge{
328
- position:absolute;top:6px;left:6px;background:#FF4500;color:#fff;
329
  padding:2px 8px;border-radius:4px;font-family:'JetBrains Mono',monospace;font-size:11px;font-weight:600;
330
  }
331
  .thumb-remove{
@@ -334,21 +322,21 @@ body:not(.dark) .modern-tb-btn .tb-svg,body:not(.dark) .modern-tb-btn .tb-svg *{
334
  display:none;align-items:center;justify-content:center;font-size:12px;transition:all .15s;line-height:1;
335
  }
336
  .gallery-thumb:hover .thumb-remove{display:flex}
337
- .thumb-remove:hover{background:#FF4500;border-color:#FF4500}
338
  .gallery-add-card{
339
  aspect-ratio:1;border-radius:10px;border:2px dashed #3f3f46;
340
  display:flex;flex-direction:column;align-items:center;justify-content:center;
341
- cursor:pointer;transition:all .2s ease;background:rgba(255,69,0,.03);gap:4px;
342
  }
343
- .gallery-add-card:hover{border-color:#FF4500;background:rgba(255,69,0,.08)}
344
  .gallery-add-card .add-icon{font-size:28px;color:#71717a;font-weight:300}
345
  .gallery-add-card .add-text{font-size:12px;color:#71717a;font-weight:500}
346
 
347
  .hint-bar{
348
- background:rgba(255,69,0,.06);border-top:1px solid #27272a;border-bottom:1px solid #27272a;
349
  padding:10px 20px;font-size:13px;color:#a1a1aa;line-height:1.7;
350
  }
351
- .hint-bar b{color:#FF8C66;font-weight:600}
352
  .hint-bar kbd{
353
  display:inline-block;padding:1px 6px;background:#27272a;border:1px solid #3f3f46;
354
  border-radius:4px;font-family:'JetBrains Mono',monospace;font-size:11px;color:#a1a1aa;
@@ -362,11 +350,11 @@ body:not(.dark) .modern-tb-btn .tb-svg,body:not(.dark) .modern-tb-btn .tb-svg *{
362
  .suggestions-wrap{display:flex;flex-wrap:wrap;gap:6px}
363
  .suggestion-chip{
364
  display:inline-flex;align-items:center;gap:4px;padding:5px 12px;
365
- background:rgba(255,69,0,.08);border:1px solid rgba(255,69,0,.2);border-radius:20px;
366
- color:#FF8C66;font-size:12px;font-weight:500;font-family:'Inter',sans-serif;
367
  cursor:pointer;transition:all .15s;white-space:nowrap;
368
  }
369
- .suggestion-chip:hover{background:rgba(255,69,0,.15);border-color:rgba(255,69,0,.35);color:#FF6633;transform:translateY(-1px)}
370
 
371
  .examples-section{border-top:1px solid #27272a;padding:12px 16px}
372
  .examples-scroll{display:flex;gap:10px;overflow-x:auto;padding-bottom:8px}
@@ -378,7 +366,7 @@ body:not(.dark) .modern-tb-btn .tb-svg,body:not(.dark) .modern-tb-btn .tb-svg *{
378
  flex-shrink:0;width:210px;background:#09090b;border:1px solid #27272a;
379
  border-radius:10px;overflow:hidden;cursor:pointer;transition:all .2s ease;
380
  }
381
- .example-card:hover{border-color:#FF4500;transform:translateY(-2px);box-shadow:0 4px 12px rgba(255,69,0,.15)}
382
  .example-card.loading{opacity:.5;pointer-events:none}
383
  .example-thumbs{display:flex;height:110px;overflow:hidden;background:#18181b}
384
  .example-thumbs img{flex:1;object-fit:cover;min-width:0;border-bottom:1px solid #27272a}
@@ -388,8 +376,8 @@ body:not(.dark) .modern-tb-btn .tb-svg,body:not(.dark) .modern-tb-btn .tb-svg *{
388
  }
389
  .example-meta{padding:6px 10px;display:flex;align-items:center;gap:6px}
390
  .example-badge{
391
- display:inline-flex;padding:2px 7px;background:rgba(255,69,0,.1);border-radius:4px;
392
- font-size:10px;font-weight:600;color:#FF6633;font-family:'JetBrains Mono',monospace;white-space:nowrap;
393
  }
394
  .example-prompt-text{
395
  padding:0 10px 8px;font-size:11px;color:#a1a1aa;line-height:1.4;
@@ -408,7 +396,7 @@ body:not(.dark) .modern-tb-btn .tb-svg,body:not(.dark) .modern-tb-btn .tb-svg *{
408
  padding:10px 14px;font-family:'Inter',sans-serif;font-size:14px;color:#e4e4e7;
409
  resize:vertical;outline:none;min-height:42px;transition:border-color .2s;
410
  }
411
- .modern-textarea:focus{border-color:#FF4500;box-shadow:0 0 0 3px rgba(255,69,0,.15)}
412
  .modern-textarea::placeholder{color:#3f3f46}
413
  .modern-textarea.error-flash{
414
  border-color:#ef4444!important;box-shadow:0 0 0 3px rgba(239,68,68,.2)!important;animation:shake .4s ease;
@@ -431,16 +419,16 @@ body:not(.dark) .modern-tb-btn .tb-svg,body:not(.dark) .modern-tb-btn .tb-svg *{
431
 
432
  .btn-run{
433
  display:flex;align-items:center;justify-content:center;gap:8px;width:100%;
434
- background:linear-gradient(135deg,#FF4500,#E63E00);border:none;border-radius:10px;
435
  padding:12px 24px;cursor:pointer;font-size:15px;font-weight:600;font-family:'Inter',sans-serif;
436
  color:#ffffff!important;-webkit-text-fill-color:#ffffff!important;transition:all .2s ease;letter-spacing:-.2px;
437
- box-shadow:0 4px 16px rgba(255,69,0,.3),inset 0 1px 0 rgba(255,255,255,.1);
438
  }
439
  .btn-run:hover{
440
- background:linear-gradient(135deg,#FF6633,#FF4500);transform:translateY(-1px);
441
- box-shadow:0 6px 24px rgba(255,69,0,.45),inset 0 1px 0 rgba(255,255,255,.15);
442
  }
443
- .btn-run:active{transform:translateY(0);box-shadow:0 2px 8px rgba(255,69,0,.3)}
444
  .btn-run svg{width:18px;height:18px;fill:#ffffff!important}
445
  .btn-run svg path{fill:#ffffff!important}
446
  #custom-run-btn,#custom-run-btn *,#custom-run-btn span,#custom-run-btn svg,
@@ -469,13 +457,13 @@ body:not(.dark) #custom-run-btn *{color:#ffffff!important;-webkit-text-fill-colo
469
  .output-frame .out-body img{max-width:100%;max-height:460px;image-rendering:auto}
470
  .output-frame .out-placeholder{color:#3f3f46;font-size:13px;text-align:center;padding:20px}
471
  .out-download-btn{
472
- display:none;align-items:center;justify-content:center;background:rgba(255,69,0,.1);
473
- border:1px solid rgba(255,69,0,.2);border-radius:6px;cursor:pointer;padding:3px 10px;
474
- font-size:11px;font-weight:500;color:#FF8C66!important;gap:4px;height:24px;transition:all .15s;
475
  }
476
- .out-download-btn:hover{background:rgba(255,69,0,.2);border-color:rgba(255,69,0,.35);color:#ffffff!important}
477
  .out-download-btn.visible{display:inline-flex}
478
- .out-download-btn svg{width:12px;height:12px;fill:#FF8C66}
479
 
480
  .modern-loader{
481
  display:none;position:absolute;top:0;left:0;right:0;bottom:0;background:rgba(9,9,11,.92);
@@ -483,14 +471,14 @@ body:not(.dark) #custom-run-btn *{color:#ffffff!important;-webkit-text-fill-colo
483
  }
484
  .modern-loader.active{display:flex}
485
  .modern-loader .loader-spinner{
486
- width:36px;height:36px;border:3px solid #27272a;border-top-color:#FF4500;
487
  border-radius:50%;animation:spin .8s linear infinite;
488
  }
489
  @keyframes spin{to{transform:rotate(360deg)}}
490
  .modern-loader .loader-text{font-size:13px;color:#a1a1aa;font-weight:500}
491
  .loader-bar-track{width:200px;height:4px;background:#27272a;border-radius:2px;overflow:hidden}
492
  .loader-bar-fill{
493
- height:100%;background:linear-gradient(90deg,#FF4500,#FF6633,#FF4500);
494
  background-size:200% 100%;animation:shimmer 1.5s ease-in-out infinite;border-radius:2px;
495
  }
496
  @keyframes shimmer{0%{background-position:200% 0}100%{background-position:-200% 0}}
@@ -508,13 +496,13 @@ body:not(.dark) #custom-run-btn *{color:#ffffff!important;-webkit-text-fill-colo
508
  border-radius:3px;outline:none;min-width:0;
509
  }
510
  .slider-row input[type="range"]::-webkit-slider-thumb{
511
- -webkit-appearance:none;width:16px;height:16px;background:linear-gradient(135deg,#FF4500,#E63E00);
512
- border-radius:50%;cursor:pointer;box-shadow:0 2px 6px rgba(255,69,0,.4);transition:transform .15s;
513
  }
514
  .slider-row input[type="range"]::-webkit-slider-thumb:hover{transform:scale(1.2)}
515
  .slider-row input[type="range"]::-moz-range-thumb{
516
- width:16px;height:16px;background:linear-gradient(135deg,#FF4500,#E63E00);
517
- border-radius:50%;cursor:pointer;border:none;box-shadow:0 2px 6px rgba(255,69,0,.4);
518
  }
519
  .slider-row .slider-val{
520
  min-width:52px;text-align:right;font-family:'JetBrains Mono',monospace;font-size:12px;
@@ -522,7 +510,7 @@ body:not(.dark) #custom-run-btn *{color:#ffffff!important;-webkit-text-fill-colo
522
  border-radius:6px;color:#a1a1aa;flex-shrink:0;
523
  }
524
  .checkbox-row{display:flex;align-items:center;gap:8px;font-size:13px;color:#a1a1aa}
525
- .checkbox-row input[type="checkbox"]{accent-color:#FF4500;width:16px;height:16px;cursor:pointer}
526
  .checkbox-row label{color:#a1a1aa;font-size:13px;cursor:pointer}
527
 
528
  .app-statusbar{
@@ -535,11 +523,11 @@ body:not(.dark) #custom-run-btn *{color:#ffffff!important;-webkit-text-fill-colo
535
  }
536
  .app-statusbar .sb-section.sb-fixed{
537
  flex:0 0 auto;min-width:90px;text-align:center;justify-content:center;
538
- padding:3px 12px;background:rgba(255,69,0,.08);border-radius:6px;color:#FF6633;font-weight:500;
539
  }
540
 
541
  .exp-note{padding:10px 20px;font-size:12px;color:#52525b;border-top:1px solid #27272a;text-align:center}
542
- .exp-note a{color:#FF6633;text-decoration:none}
543
  .exp-note a:hover{text-decoration:underline}
544
 
545
  .dark .app-shell{background:#18181b}
@@ -548,7 +536,7 @@ body:not(.dark) #custom-run-btn *{color:#ffffff!important;-webkit-text-fill-colo
548
  .dark .settings-group{background:#18181b}
549
  .dark .output-frame .out-title{color:#ffffff!important}
550
  .dark .output-frame .out-title span{color:#ffffff!important}
551
- .dark .out-download-btn{color:#FF8C66!important}
552
  .dark .out-download-btn:hover{color:#ffffff!important}
553
 
554
  ::-webkit-scrollbar{width:8px;height:8px}
@@ -563,10 +551,6 @@ body:not(.dark) #custom-run-btn *{color:#ffffff!important;-webkit-text-fill-colo
563
  }
564
  """
565
 
566
- # ═══════════════════════════════════════════
567
- # JavaScript
568
- # ═══════════════════════════════════════════
569
-
570
  gallery_js = r"""
571
  () => {
572
  function init() {
@@ -597,7 +581,6 @@ function init() {
597
  let selectedIdx = -1;
598
  let toastTimer = null;
599
 
600
- /* ── Toast ── */
601
  function showToast(message, type) {
602
  let toast = document.getElementById('app-toast');
603
  if (!toast) {
@@ -762,7 +745,6 @@ function init() {
762
  if (promptInput) { promptInput.value = text; syncPromptToGradio(); }
763
  };
764
 
765
- /* ── Example card click → trigger Python callback ── */
766
  document.querySelectorAll('.example-card[data-idx]').forEach(card => {
767
  card.addEventListener('click', () => {
768
  const idx = card.getAttribute('data-idx');
@@ -785,7 +767,6 @@ function init() {
785
  });
786
  });
787
 
788
- /* ── Slider sync ── */
789
  function syncSlider(customId, gradioId) {
790
  const slider = document.getElementById(customId);
791
  const valSpan = document.getElementById(customId + '-val');
@@ -918,7 +899,6 @@ function watchSeed() {
918
  }
919
  watchSeed();
920
 
921
- /* ── Watch for example load results from Python ── */
922
  function watchExampleResults() {
923
  const container = document.getElementById('example-result-data');
924
  if (!container) { setTimeout(watchExampleResults, 500); return; }
@@ -965,10 +945,6 @@ watchExampleResults();
965
  }
966
  """
967
 
968
- # ═══════════════════════════════════════════
969
- # SVG Definitions (no emojis)
970
- # ═══════════════════════════════════════════
971
-
972
  DOWNLOAD_SVG = '<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M12 16l-5-5h3V4h4v7h3l-5 5z"/><path d="M20 18H4v2h16v-2z"/></svg>'
973
 
974
  UPLOAD_SVG = '<svg class="tb-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/></svg>'
@@ -979,13 +955,8 @@ CLEAR_SVG = '<svg class="tb-svg" viewBox="0 0 24 24" fill="none" stroke="current
979
 
980
  FIRE_LOGO_SVG = '<svg viewBox="0 0 24 24" fill="white" xmlns="http://www.w3.org/2000/svg"><path d="M12 23c-3.6 0-8-2.69-8-7.5 0-3.5 3-6.5 4.5-8 .27-.27.75-.08.75.28v2.44c0 .42.5.63.72.28C12.28 7.5 13 3 13 1c0-.42.48-.64.8-.35C18 4.5 20 9 20 12c0 5.5-3.5 11-8 11z"/></svg>'
981
 
982
- # ═══════════════════════════════════════════
983
- # Gradio App
984
- # ═══════════════════════════════════════════
985
-
986
  with gr.Blocks() as demo:
987
 
988
- # ── Hidden Gradio components ──
989
  hidden_images_b64 = gr.Textbox(value="[]", elem_id="hidden-images-b64", elem_classes="hidden-input", container=False)
990
  prompt = gr.Textbox(value="", elem_id="prompt-gradio-input", elem_classes="hidden-input", container=False)
991
  seed = gr.Slider(minimum=0, maximum=MAX_SEED, step=1, value=0, elem_id="gradio-seed", elem_classes="hidden-input", container=False)
@@ -994,12 +965,10 @@ with gr.Blocks() as demo:
994
  steps = gr.Slider(minimum=1, maximum=50, step=1, value=4, elem_id="gradio-steps", elem_classes="hidden-input", container=False)
995
  result = gr.Image(elem_id="gradio-result", elem_classes="hidden-input", container=False, format="png")
996
 
997
- # ── Hidden example loading components ──
998
  example_idx = gr.Textbox(value="", elem_id="example-idx-input", elem_classes="hidden-input", container=False)
999
  example_result = gr.Textbox(value="", elem_id="example-result-data", elem_classes="hidden-input", container=False)
1000
  example_load_btn = gr.Button("Load Example", elem_id="example-load-btn")
1001
 
1002
- # ── Main HTML UI ──
1003
  gr.HTML(f"""
1004
  <div class="app-shell">
1005
 
@@ -1032,9 +1001,9 @@ with gr.Blocks() as demo:
1032
  <div id="upload-prompt" class="upload-prompt-modern">
1033
  <div id="upload-click-area" class="upload-click-area">
1034
  <svg viewBox="0 0 80 80" fill="none" xmlns="http://www.w3.org/2000/svg">
1035
- <rect x="8" y="14" width="64" height="52" rx="6" fill="none" stroke="#FF4500" stroke-width="2" stroke-dasharray="4 3"/>
1036
- <polygon points="12,62 30,40 42,50 54,34 68,62" fill="rgba(255,69,0,0.15)" stroke="#FF4500" stroke-width="1.5"/>
1037
- <circle cx="28" cy="30" r="6" fill="rgba(255,69,0,0.2)" stroke="#FF4500" stroke-width="1.5"/>
1038
  </svg>
1039
  <span class="upload-main-text">Click or drag images here</span>
1040
  <span class="upload-sub-text">Supports multiple images for reference-based editing</span>
@@ -1158,7 +1127,6 @@ with gr.Blocks() as demo:
1158
  demo.load(fn=None, js=gallery_js)
1159
  demo.load(fn=None, js=wire_outputs_js)
1160
 
1161
- # ── Run inference ──
1162
  run_btn.click(
1163
  fn=infer,
1164
  inputs=[hidden_images_b64, prompt, seed, randomize_seed, guidance_scale, steps],
@@ -1173,7 +1141,6 @@ with gr.Blocks() as demo:
1173
  }""",
1174
  )
1175
 
1176
- # ── Load example (bypasses queue for instant response) ──
1177
  example_load_btn.click(
1178
  fn=load_example_data,
1179
  inputs=[example_idx],
 
43
  except Exception as e:
44
  print(f"Warning: Could not set FA3 processor: {e}")
45
 
 
 
 
 
46
  EXAMPLES_CONFIG = [
47
  {
48
  "images": ["examples/1.jpg"],
 
143
  EXAMPLE_CARDS_HTML = build_example_cards_html()
144
  print(f"Built {len(EXAMPLES_CONFIG)} example cards.")
145
 
 
 
 
 
146
 
147
  def b64_to_pil_list(b64_json_str):
148
  if not b64_json_str or b64_json_str.strip() in ("", "[]"):
 
208
  torch.cuda.empty_cache()
209
 
210
 
 
 
 
 
211
  css = r"""
212
  @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600&display=swap');
213
  *{box-sizing:border-box;margin:0;padding:0}
 
240
  }
241
  .app-header-left{display:flex;align-items:center;gap:12px}
242
  .app-logo{
243
+ width:36px;height:36px;background:linear-gradient(135deg,#1E90FF,#47A3FF,#7CB8FF);
244
  border-radius:10px;display:flex;align-items:center;justify-content:center;
245
+ box-shadow:0 4px 12px rgba(30,144,255,.35);
246
  }
247
  .app-logo svg{width:20px;height:20px;fill:#fff;flex-shrink:0}
248
  .app-title{
 
251
  }
252
  .app-badge{
253
  font-size:11px;font-weight:600;padding:3px 10px;border-radius:20px;
254
+ background:rgba(30,144,255,.15);color:#47A3FF;border:1px solid rgba(30,144,255,.25);letter-spacing:.3px;
255
  }
256
  .app-badge.fast{background:rgba(34,197,94,.12);color:#4ade80;border:1px solid rgba(34,197,94,.25)}
257
 
 
267
  font-family:'Inter',sans-serif;color:#ffffff!important;-webkit-text-fill-color:#ffffff!important;
268
  transition:all .15s ease;
269
  }
270
+ .modern-tb-btn:hover{background:rgba(30,144,255,.15);border-color:rgba(30,144,255,.3)}
271
+ .modern-tb-btn:active,.modern-tb-btn.active{background:rgba(30,144,255,.25);border-color:rgba(30,144,255,.45)}
272
  .modern-tb-btn .tb-label{font-size:13px;color:#ffffff!important;-webkit-text-fill-color:#ffffff!important;font-weight:600}
273
  .modern-tb-btn .tb-svg{width:15px;height:15px;flex-shrink:0;color:#ffffff!important}
274
  .modern-tb-btn .tb-svg,
 
287
  .app-main-right{width:420px;display:flex;flex-direction:column;flex-shrink:0;background:#18181b}
288
 
289
  #gallery-drop-zone{position:relative;background:#09090b;min-height:440px;overflow:auto}
290
+ #gallery-drop-zone.drag-over{outline:2px solid #1E90FF;outline-offset:-2px;background:rgba(30,144,255,.04)}
291
 
292
  .upload-prompt-modern{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);z-index:20}
293
  .upload-click-area{
294
  display:flex;flex-direction:column;align-items:center;justify-content:center;
295
  cursor:pointer;padding:36px 52px;border:2px dashed #3f3f46;border-radius:16px;
296
+ background:rgba(30,144,255,.03);transition:all .2s ease;gap:8px;
297
  }
298
+ .upload-click-area:hover{background:rgba(30,144,255,.08);border-color:#1E90FF;transform:scale(1.03)}
299
+ .upload-click-area:active{background:rgba(30,144,255,.12);transform:scale(.98)}
300
  .upload-click-area svg{width:80px;height:80px}
301
  .upload-main-text{color:#71717a;font-size:14px;font-weight:500;margin-top:4px}
302
  .upload-sub-text{color:#52525b;font-size:12px}
 
310
  cursor:pointer;border:2px solid #27272a;transition:all .2s ease;background:#18181b;
311
  }
312
  .gallery-thumb:hover{border-color:#3f3f46;transform:translateY(-2px);box-shadow:0 4px 12px rgba(0,0,0,.4)}
313
+ .gallery-thumb.selected{border-color:#1E90FF!important;box-shadow:0 0 0 3px rgba(30,144,255,.2)}
314
  .gallery-thumb img{width:100%;height:100%;object-fit:cover}
315
  .thumb-badge{
316
+ position:absolute;top:6px;left:6px;background:#1E90FF;color:#fff;
317
  padding:2px 8px;border-radius:4px;font-family:'JetBrains Mono',monospace;font-size:11px;font-weight:600;
318
  }
319
  .thumb-remove{
 
322
  display:none;align-items:center;justify-content:center;font-size:12px;transition:all .15s;line-height:1;
323
  }
324
  .gallery-thumb:hover .thumb-remove{display:flex}
325
+ .thumb-remove:hover{background:#1E90FF;border-color:#1E90FF}
326
  .gallery-add-card{
327
  aspect-ratio:1;border-radius:10px;border:2px dashed #3f3f46;
328
  display:flex;flex-direction:column;align-items:center;justify-content:center;
329
+ cursor:pointer;transition:all .2s ease;background:rgba(30,144,255,.03);gap:4px;
330
  }
331
+ .gallery-add-card:hover{border-color:#1E90FF;background:rgba(30,144,255,.08)}
332
  .gallery-add-card .add-icon{font-size:28px;color:#71717a;font-weight:300}
333
  .gallery-add-card .add-text{font-size:12px;color:#71717a;font-weight:500}
334
 
335
  .hint-bar{
336
+ background:rgba(30,144,255,.06);border-top:1px solid #27272a;border-bottom:1px solid #27272a;
337
  padding:10px 20px;font-size:13px;color:#a1a1aa;line-height:1.7;
338
  }
339
+ .hint-bar b{color:#7CB8FF;font-weight:600}
340
  .hint-bar kbd{
341
  display:inline-block;padding:1px 6px;background:#27272a;border:1px solid #3f3f46;
342
  border-radius:4px;font-family:'JetBrains Mono',monospace;font-size:11px;color:#a1a1aa;
 
350
  .suggestions-wrap{display:flex;flex-wrap:wrap;gap:6px}
351
  .suggestion-chip{
352
  display:inline-flex;align-items:center;gap:4px;padding:5px 12px;
353
+ background:rgba(30,144,255,.08);border:1px solid rgba(30,144,255,.2);border-radius:20px;
354
+ color:#7CB8FF;font-size:12px;font-weight:500;font-family:'Inter',sans-serif;
355
  cursor:pointer;transition:all .15s;white-space:nowrap;
356
  }
357
+ .suggestion-chip:hover{background:rgba(30,144,255,.15);border-color:rgba(30,144,255,.35);color:#47A3FF;transform:translateY(-1px)}
358
 
359
  .examples-section{border-top:1px solid #27272a;padding:12px 16px}
360
  .examples-scroll{display:flex;gap:10px;overflow-x:auto;padding-bottom:8px}
 
366
  flex-shrink:0;width:210px;background:#09090b;border:1px solid #27272a;
367
  border-radius:10px;overflow:hidden;cursor:pointer;transition:all .2s ease;
368
  }
369
+ .example-card:hover{border-color:#1E90FF;transform:translateY(-2px);box-shadow:0 4px 12px rgba(30,144,255,.15)}
370
  .example-card.loading{opacity:.5;pointer-events:none}
371
  .example-thumbs{display:flex;height:110px;overflow:hidden;background:#18181b}
372
  .example-thumbs img{flex:1;object-fit:cover;min-width:0;border-bottom:1px solid #27272a}
 
376
  }
377
  .example-meta{padding:6px 10px;display:flex;align-items:center;gap:6px}
378
  .example-badge{
379
+ display:inline-flex;padding:2px 7px;background:rgba(30,144,255,.1);border-radius:4px;
380
+ font-size:10px;font-weight:600;color:#47A3FF;font-family:'JetBrains Mono',monospace;white-space:nowrap;
381
  }
382
  .example-prompt-text{
383
  padding:0 10px 8px;font-size:11px;color:#a1a1aa;line-height:1.4;
 
396
  padding:10px 14px;font-family:'Inter',sans-serif;font-size:14px;color:#e4e4e7;
397
  resize:vertical;outline:none;min-height:42px;transition:border-color .2s;
398
  }
399
+ .modern-textarea:focus{border-color:#1E90FF;box-shadow:0 0 0 3px rgba(30,144,255,.15)}
400
  .modern-textarea::placeholder{color:#3f3f46}
401
  .modern-textarea.error-flash{
402
  border-color:#ef4444!important;box-shadow:0 0 0 3px rgba(239,68,68,.2)!important;animation:shake .4s ease;
 
419
 
420
  .btn-run{
421
  display:flex;align-items:center;justify-content:center;gap:8px;width:100%;
422
+ background:linear-gradient(135deg,#1E90FF,#1873CC);border:none;border-radius:10px;
423
  padding:12px 24px;cursor:pointer;font-size:15px;font-weight:600;font-family:'Inter',sans-serif;
424
  color:#ffffff!important;-webkit-text-fill-color:#ffffff!important;transition:all .2s ease;letter-spacing:-.2px;
425
+ box-shadow:0 4px 16px rgba(30,144,255,.3),inset 0 1px 0 rgba(255,255,255,.1);
426
  }
427
  .btn-run:hover{
428
+ background:linear-gradient(135deg,#47A3FF,#1E90FF);transform:translateY(-1px);
429
+ box-shadow:0 6px 24px rgba(30,144,255,.45),inset 0 1px 0 rgba(255,255,255,.15);
430
  }
431
+ .btn-run:active{transform:translateY(0);box-shadow:0 2px 8px rgba(30,144,255,.3)}
432
  .btn-run svg{width:18px;height:18px;fill:#ffffff!important}
433
  .btn-run svg path{fill:#ffffff!important}
434
  #custom-run-btn,#custom-run-btn *,#custom-run-btn span,#custom-run-btn svg,
 
457
  .output-frame .out-body img{max-width:100%;max-height:460px;image-rendering:auto}
458
  .output-frame .out-placeholder{color:#3f3f46;font-size:13px;text-align:center;padding:20px}
459
  .out-download-btn{
460
+ display:none;align-items:center;justify-content:center;background:rgba(30,144,255,.1);
461
+ border:1px solid rgba(30,144,255,.2);border-radius:6px;cursor:pointer;padding:3px 10px;
462
+ font-size:11px;font-weight:500;color:#7CB8FF!important;gap:4px;height:24px;transition:all .15s;
463
  }
464
+ .out-download-btn:hover{background:rgba(30,144,255,.2);border-color:rgba(30,144,255,.35);color:#ffffff!important}
465
  .out-download-btn.visible{display:inline-flex}
466
+ .out-download-btn svg{width:12px;height:12px;fill:#7CB8FF}
467
 
468
  .modern-loader{
469
  display:none;position:absolute;top:0;left:0;right:0;bottom:0;background:rgba(9,9,11,.92);
 
471
  }
472
  .modern-loader.active{display:flex}
473
  .modern-loader .loader-spinner{
474
+ width:36px;height:36px;border:3px solid #27272a;border-top-color:#1E90FF;
475
  border-radius:50%;animation:spin .8s linear infinite;
476
  }
477
  @keyframes spin{to{transform:rotate(360deg)}}
478
  .modern-loader .loader-text{font-size:13px;color:#a1a1aa;font-weight:500}
479
  .loader-bar-track{width:200px;height:4px;background:#27272a;border-radius:2px;overflow:hidden}
480
  .loader-bar-fill{
481
+ height:100%;background:linear-gradient(90deg,#1E90FF,#47A3FF,#1E90FF);
482
  background-size:200% 100%;animation:shimmer 1.5s ease-in-out infinite;border-radius:2px;
483
  }
484
  @keyframes shimmer{0%{background-position:200% 0}100%{background-position:-200% 0}}
 
496
  border-radius:3px;outline:none;min-width:0;
497
  }
498
  .slider-row input[type="range"]::-webkit-slider-thumb{
499
+ -webkit-appearance:none;width:16px;height:16px;background:linear-gradient(135deg,#1E90FF,#1873CC);
500
+ border-radius:50%;cursor:pointer;box-shadow:0 2px 6px rgba(30,144,255,.4);transition:transform .15s;
501
  }
502
  .slider-row input[type="range"]::-webkit-slider-thumb:hover{transform:scale(1.2)}
503
  .slider-row input[type="range"]::-moz-range-thumb{
504
+ width:16px;height:16px;background:linear-gradient(135deg,#1E90FF,#1873CC);
505
+ border-radius:50%;cursor:pointer;border:none;box-shadow:0 2px 6px rgba(30,144,255,.4);
506
  }
507
  .slider-row .slider-val{
508
  min-width:52px;text-align:right;font-family:'JetBrains Mono',monospace;font-size:12px;
 
510
  border-radius:6px;color:#a1a1aa;flex-shrink:0;
511
  }
512
  .checkbox-row{display:flex;align-items:center;gap:8px;font-size:13px;color:#a1a1aa}
513
+ .checkbox-row input[type="checkbox"]{accent-color:#1E90FF;width:16px;height:16px;cursor:pointer}
514
  .checkbox-row label{color:#a1a1aa;font-size:13px;cursor:pointer}
515
 
516
  .app-statusbar{
 
523
  }
524
  .app-statusbar .sb-section.sb-fixed{
525
  flex:0 0 auto;min-width:90px;text-align:center;justify-content:center;
526
+ padding:3px 12px;background:rgba(30,144,255,.08);border-radius:6px;color:#47A3FF;font-weight:500;
527
  }
528
 
529
  .exp-note{padding:10px 20px;font-size:12px;color:#52525b;border-top:1px solid #27272a;text-align:center}
530
+ .exp-note a{color:#47A3FF;text-decoration:none}
531
  .exp-note a:hover{text-decoration:underline}
532
 
533
  .dark .app-shell{background:#18181b}
 
536
  .dark .settings-group{background:#18181b}
537
  .dark .output-frame .out-title{color:#ffffff!important}
538
  .dark .output-frame .out-title span{color:#ffffff!important}
539
+ .dark .out-download-btn{color:#7CB8FF!important}
540
  .dark .out-download-btn:hover{color:#ffffff!important}
541
 
542
  ::-webkit-scrollbar{width:8px;height:8px}
 
551
  }
552
  """
553
 
 
 
 
 
554
  gallery_js = r"""
555
  () => {
556
  function init() {
 
581
  let selectedIdx = -1;
582
  let toastTimer = null;
583
 
 
584
  function showToast(message, type) {
585
  let toast = document.getElementById('app-toast');
586
  if (!toast) {
 
745
  if (promptInput) { promptInput.value = text; syncPromptToGradio(); }
746
  };
747
 
 
748
  document.querySelectorAll('.example-card[data-idx]').forEach(card => {
749
  card.addEventListener('click', () => {
750
  const idx = card.getAttribute('data-idx');
 
767
  });
768
  });
769
 
 
770
  function syncSlider(customId, gradioId) {
771
  const slider = document.getElementById(customId);
772
  const valSpan = document.getElementById(customId + '-val');
 
899
  }
900
  watchSeed();
901
 
 
902
  function watchExampleResults() {
903
  const container = document.getElementById('example-result-data');
904
  if (!container) { setTimeout(watchExampleResults, 500); return; }
 
945
  }
946
  """
947
 
 
 
 
 
948
  DOWNLOAD_SVG = '<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M12 16l-5-5h3V4h4v7h3l-5 5z"/><path d="M20 18H4v2h16v-2z"/></svg>'
949
 
950
  UPLOAD_SVG = '<svg class="tb-svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/></svg>'
 
955
 
956
  FIRE_LOGO_SVG = '<svg viewBox="0 0 24 24" fill="white" xmlns="http://www.w3.org/2000/svg"><path d="M12 23c-3.6 0-8-2.69-8-7.5 0-3.5 3-6.5 4.5-8 .27-.27.75-.08.75.28v2.44c0 .42.5.63.72.28C12.28 7.5 13 3 13 1c0-.42.48-.64.8-.35C18 4.5 20 9 20 12c0 5.5-3.5 11-8 11z"/></svg>'
957
 
 
 
 
 
958
  with gr.Blocks() as demo:
959
 
 
960
  hidden_images_b64 = gr.Textbox(value="[]", elem_id="hidden-images-b64", elem_classes="hidden-input", container=False)
961
  prompt = gr.Textbox(value="", elem_id="prompt-gradio-input", elem_classes="hidden-input", container=False)
962
  seed = gr.Slider(minimum=0, maximum=MAX_SEED, step=1, value=0, elem_id="gradio-seed", elem_classes="hidden-input", container=False)
 
965
  steps = gr.Slider(minimum=1, maximum=50, step=1, value=4, elem_id="gradio-steps", elem_classes="hidden-input", container=False)
966
  result = gr.Image(elem_id="gradio-result", elem_classes="hidden-input", container=False, format="png")
967
 
 
968
  example_idx = gr.Textbox(value="", elem_id="example-idx-input", elem_classes="hidden-input", container=False)
969
  example_result = gr.Textbox(value="", elem_id="example-result-data", elem_classes="hidden-input", container=False)
970
  example_load_btn = gr.Button("Load Example", elem_id="example-load-btn")
971
 
 
972
  gr.HTML(f"""
973
  <div class="app-shell">
974
 
 
1001
  <div id="upload-prompt" class="upload-prompt-modern">
1002
  <div id="upload-click-area" class="upload-click-area">
1003
  <svg viewBox="0 0 80 80" fill="none" xmlns="http://www.w3.org/2000/svg">
1004
+ <rect x="8" y="14" width="64" height="52" rx="6" fill="none" stroke="#1E90FF" stroke-width="2" stroke-dasharray="4 3"/>
1005
+ <polygon points="12,62 30,40 42,50 54,34 68,62" fill="rgba(30,144,255,0.15)" stroke="#1E90FF" stroke-width="1.5"/>
1006
+ <circle cx="28" cy="30" r="6" fill="rgba(30,144,255,0.2)" stroke="#1E90FF" stroke-width="1.5"/>
1007
  </svg>
1008
  <span class="upload-main-text">Click or drag images here</span>
1009
  <span class="upload-sub-text">Supports multiple images for reference-based editing</span>
 
1127
  demo.load(fn=None, js=gallery_js)
1128
  demo.load(fn=None, js=wire_outputs_js)
1129
 
 
1130
  run_btn.click(
1131
  fn=infer,
1132
  inputs=[hidden_images_b64, prompt, seed, randomize_seed, guidance_scale, steps],
 
1141
  }""",
1142
  )
1143
 
 
1144
  example_load_btn.click(
1145
  fn=load_example_data,
1146
  inputs=[example_idx],