artificialguybr commited on
Commit
421034f
·
1 Parent(s): 59cf60e

Simplify model-to-LoRA UX and unpin HF dependencies

Browse files
Files changed (2) hide show
  1. app.py +149 -75
  2. custom.css +34 -97
app.py CHANGED
@@ -33,13 +33,13 @@ DEFAULT_NEGATIVE = (
33
  )
34
 
35
  FAMILY_LABELS = {
36
- "all": "All",
37
  "sdxl": "SDXL",
38
  "sd15": "SD 1.5",
39
  "flux": "FLUX",
40
  "qwen-image": "Qwen-Image",
41
  "z-image": "Z-Image",
42
  "other": "Other",
 
43
  }
44
 
45
  FAMILY_BASE_MODELS = {
@@ -194,13 +194,10 @@ def filter_loras(family: str, search: str) -> list[dict[str, Any]]:
194
  if term and term not in haystack:
195
  continue
196
  filtered.append(row)
 
197
  return filtered
198
 
199
 
200
- def build_gallery_data(filtered: list[dict[str, Any]]) -> list[tuple[str, str]]:
201
- return [(cover_for_entry(item), gallery_caption(item)) for item in filtered]
202
-
203
-
204
  def family_defaults(family: str) -> dict[str, Any]:
205
  return FAMILY_DEFAULTS.get(family, FAMILY_DEFAULTS["other"])
206
 
@@ -209,37 +206,28 @@ def scheduler_interactive_for_family(family: str) -> bool:
209
  return family in {"sdxl", "sd15"}
210
 
211
 
212
- def refresh_gallery(family: str, search: str):
213
- filtered = filter_loras(family, search)
214
- gallery_items = build_gallery_data(filtered)
215
- repos = [item["repo"] for item in filtered]
216
- count_text = f"### {len(filtered)} LoRAs found"
217
-
218
- return (
219
- gr.update(value=gallery_items),
220
- repos,
221
- gr.update(value=""),
222
- gr.update(value=""),
223
- count_text,
224
- None,
225
- )
226
 
227
 
228
- def update_selection(evt: gr.SelectData, filtered_repos: list[str]):
229
- if evt.index is None or evt.index >= len(filtered_repos):
230
  return (
231
- gr.update(),
232
  gr.update(value=""),
233
- None,
234
- gr.update(),
235
- gr.update(),
236
  gr.update(),
237
  gr.update(),
238
  gr.update(),
239
  gr.update(),
 
 
240
  )
241
 
242
- selected_repo = filtered_repos[evt.index]
243
  selected = LORA_BY_REPO[selected_repo]
244
  family = selected.get("family", "other")
245
  defaults = family_defaults(family)
@@ -261,8 +249,9 @@ def update_selection(evt: gr.SelectData, filtered_repos: list[str]):
261
  scheduler_value = "DPM++ 2M SDE Karras" if scheduler_enabled else "Auto"
262
 
263
  return (
264
- gr.update(placeholder=placeholder),
265
  info,
 
266
  selected_repo,
267
  gr.update(value=defaults["steps"]),
268
  gr.update(value=defaults["cfg"]),
@@ -273,6 +262,41 @@ def update_selection(evt: gr.SelectData, filtered_repos: list[str]):
273
  )
274
 
275
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
276
  CURRENT_PIPE: DiffusionPipeline | None = None
277
  CURRENT_BASE_MODEL = ""
278
  CURRENT_LOADED_REPO = ""
@@ -422,87 +446,137 @@ def run_lora(
422
  return result.images[0]
423
 
424
 
425
- INITIAL_FAMILY = "all"
426
  INITIAL_FILTERED = filter_loras(INITIAL_FAMILY, "")
 
427
 
428
  with gr.Blocks(theme=gr.themes.Soft(), css_paths="custom.css") as app:
429
  gr.Markdown("# artificialguybr Multi-LoRA Playground")
430
  gr.Markdown(
431
- "Generate images with LoRAs across SD 1.5, SDXL, FLUX, Qwen-Image and Z-Image. "
432
- "Pick a LoRA from the gallery, write a prompt and generate."
433
  )
434
 
435
- selected_repo = gr.State(None)
436
- filtered_repos = gr.State([item["repo"] for item in INITIAL_FILTERED])
437
 
438
  with gr.Row():
439
- with gr.Column(scale=2):
440
- result = gr.Image(label="Generated Image", height=768)
441
- generate_button = gr.Button("Generate", variant="primary")
442
-
443
  with gr.Column(scale=1):
444
  family_filter = gr.Dropdown(
445
- label="Model Family",
446
- choices=[(label, key) for key, label in FAMILY_LABELS.items()],
447
  value=INITIAL_FAMILY,
448
  )
449
- search_box = gr.Textbox(label="Search LoRA", placeholder="Type a repo or style name")
450
- gallery = gr.Gallery(
451
- value=build_gallery_data(INITIAL_FILTERED),
452
- label="LoRA Gallery",
453
- allow_preview=False,
454
- columns=2,
455
- rows=8,
456
- height=700,
457
  )
458
- catalog_stats = gr.Markdown(f"### {len(INITIAL_FILTERED)} LoRAs found")
459
-
460
- with gr.Row():
461
- with gr.Column():
 
 
 
462
  selected_info = gr.Markdown("")
 
 
 
463
  prompt = gr.Textbox(
464
- label="Prompt", lines=3, placeholder="Select a LoRA in the gallery first"
 
 
465
  )
466
  negative_prompt = gr.Textbox(
467
  label="Negative Prompt",
468
  lines=2,
469
  value=DEFAULT_NEGATIVE,
470
  )
471
-
472
- with gr.Column():
473
- with gr.Row():
474
- cfg_scale = gr.Slider(label="CFG Scale", minimum=1, maximum=20, step=0.5, value=7.0)
475
- steps = gr.Slider(label="Steps", minimum=1, maximum=100, step=1, value=30)
476
- with gr.Row():
477
- width = gr.Slider(label="Width", minimum=256, maximum=1536, step=8, value=1024)
478
- height = gr.Slider(label="Height", minimum=256, maximum=1536, step=8, value=1024)
479
- with gr.Row():
480
- seed = gr.Slider(label="Seed", minimum=0, maximum=2**32 - 1, step=1, value=0, randomize=True)
481
- lora_scale = gr.Slider(label="LoRA Scale", minimum=0, maximum=1.5, step=0.01, value=1.0)
482
- scheduler = gr.Dropdown(
483
- label="Scheduler (SD only)",
484
- choices=SCHEDULER_CHOICES,
485
- value="Auto",
486
- interactive=False,
 
 
487
  )
 
 
 
 
 
 
 
488
 
489
  family_filter.change(
490
- fn=refresh_gallery,
491
  inputs=[family_filter, search_box],
492
- outputs=[gallery, filtered_repos, prompt, selected_info, catalog_stats, selected_repo],
 
 
 
 
 
 
 
 
 
 
 
 
 
493
  )
494
  search_box.change(
495
- fn=refresh_gallery,
496
  inputs=[family_filter, search_box],
497
- outputs=[gallery, filtered_repos, prompt, selected_info, catalog_stats, selected_repo],
 
 
 
 
 
 
 
 
 
 
 
 
 
498
  )
499
-
500
- gallery.select(
501
- fn=update_selection,
502
- inputs=[filtered_repos],
503
  outputs=[
 
 
504
  prompt,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
505
  selected_info,
 
506
  selected_repo,
507
  steps,
508
  cfg_scale,
 
33
  )
34
 
35
  FAMILY_LABELS = {
 
36
  "sdxl": "SDXL",
37
  "sd15": "SD 1.5",
38
  "flux": "FLUX",
39
  "qwen-image": "Qwen-Image",
40
  "z-image": "Z-Image",
41
  "other": "Other",
42
+ "all": "All",
43
  }
44
 
45
  FAMILY_BASE_MODELS = {
 
194
  if term and term not in haystack:
195
  continue
196
  filtered.append(row)
197
+ filtered.sort(key=lambda item: item.get("title", "").lower())
198
  return filtered
199
 
200
 
 
 
 
 
201
  def family_defaults(family: str) -> dict[str, Any]:
202
  return FAMILY_DEFAULTS.get(family, FAMILY_DEFAULTS["other"])
203
 
 
206
  return family in {"sdxl", "sd15"}
207
 
208
 
209
+ def lora_dropdown_label(entry: dict[str, Any]) -> str:
210
+ short_repo = entry.get("repo", "").split("/", 1)[-1]
211
+ trigger = (entry.get("trigger_word") or "").strip()
212
+ if trigger:
213
+ return f"{entry['title']} · {short_repo} · trigger: {trigger}"
214
+ return f"{entry['title']} · {short_repo}"
 
 
 
 
 
 
 
 
215
 
216
 
217
+ def selected_payload(selected_repo: str):
218
+ if not selected_repo:
219
  return (
220
+ gr.update(value=None),
221
  gr.update(value=""),
222
+ gr.update(placeholder="Select a LoRA first"),
 
 
223
  gr.update(),
224
  gr.update(),
225
  gr.update(),
226
  gr.update(),
227
+ gr.update(value="Auto", interactive=False),
228
+ gr.update(value=1.0),
229
  )
230
 
 
231
  selected = LORA_BY_REPO[selected_repo]
232
  family = selected.get("family", "other")
233
  defaults = family_defaults(family)
 
249
  scheduler_value = "DPM++ 2M SDE Karras" if scheduler_enabled else "Auto"
250
 
251
  return (
252
+ gr.update(value=cover_for_entry(selected)),
253
  info,
254
+ gr.update(placeholder=placeholder),
255
  selected_repo,
256
  gr.update(value=defaults["steps"]),
257
  gr.update(value=defaults["cfg"]),
 
262
  )
263
 
264
 
265
+ def refresh_lora_selector(family: str, search: str):
266
+ filtered = filter_loras(family, search)
267
+ choices = [(lora_dropdown_label(item), item["repo"]) for item in filtered]
268
+ selected_repo = filtered[0]["repo"] if filtered else None
269
+ count_text = f"### {len(filtered)} LoRAs found in {family_to_label(family)}"
270
+
271
+ (
272
+ preview_update,
273
+ info_update,
274
+ prompt_update,
275
+ selected_repo_value,
276
+ steps_update,
277
+ cfg_update,
278
+ width_update,
279
+ height_update,
280
+ scheduler_update,
281
+ lora_scale_update,
282
+ ) = selected_payload(selected_repo)
283
+
284
+ return (
285
+ gr.update(choices=choices, value=selected_repo),
286
+ count_text,
287
+ preview_update,
288
+ info_update,
289
+ prompt_update,
290
+ selected_repo_value,
291
+ steps_update,
292
+ cfg_update,
293
+ width_update,
294
+ height_update,
295
+ scheduler_update,
296
+ lora_scale_update,
297
+ )
298
+
299
+
300
  CURRENT_PIPE: DiffusionPipeline | None = None
301
  CURRENT_BASE_MODEL = ""
302
  CURRENT_LOADED_REPO = ""
 
446
  return result.images[0]
447
 
448
 
449
+ INITIAL_FAMILY = "sdxl"
450
  INITIAL_FILTERED = filter_loras(INITIAL_FAMILY, "")
451
+ INITIAL_SELECTED = INITIAL_FILTERED[0]["repo"] if INITIAL_FILTERED else None
452
 
453
  with gr.Blocks(theme=gr.themes.Soft(), css_paths="custom.css") as app:
454
  gr.Markdown("# artificialguybr Multi-LoRA Playground")
455
  gr.Markdown(
456
+ "Fluxo recomendado: **1) escolha a família de modelo** **2) escolha a LoRA** → "
457
+ "**3) escreva o prompt** **4) gere**."
458
  )
459
 
460
+ selected_repo = gr.State(INITIAL_SELECTED)
 
461
 
462
  with gr.Row():
 
 
 
 
463
  with gr.Column(scale=1):
464
  family_filter = gr.Dropdown(
465
+ label="1) Família do Modelo",
466
+ choices=[(label, key) for key, label in FAMILY_LABELS.items() if key != "all"],
467
  value=INITIAL_FAMILY,
468
  )
469
+ search_box = gr.Textbox(
470
+ label="Filtro de LoRA",
471
+ placeholder="Ex: pixel, logo, anime, analog..."
 
 
 
 
 
472
  )
473
+ lora_selector = gr.Dropdown(
474
+ label="2) Escolha a LoRA",
475
+ choices=[(lora_dropdown_label(item), item["repo"]) for item in INITIAL_FILTERED],
476
+ value=INITIAL_SELECTED,
477
+ interactive=True,
478
+ )
479
+ catalog_stats = gr.Markdown(f"### {len(INITIAL_FILTERED)} LoRAs found in SDXL")
480
  selected_info = gr.Markdown("")
481
+ lora_preview = gr.Image(label="Preview da LoRA", height=320)
482
+
483
+ with gr.Column(scale=2):
484
  prompt = gr.Textbox(
485
+ label="3) Prompt",
486
+ lines=3,
487
+ placeholder="Select a LoRA first",
488
  )
489
  negative_prompt = gr.Textbox(
490
  label="Negative Prompt",
491
  lines=2,
492
  value=DEFAULT_NEGATIVE,
493
  )
494
+ generate_button = gr.Button("4) Generate", variant="primary")
495
+ result = gr.Image(label="Generated Image", height=820)
496
+
497
+ with gr.Accordion("Advanced settings", open=False):
498
+ with gr.Row():
499
+ cfg_scale = gr.Slider(label="CFG Scale", minimum=1, maximum=20, step=0.5, value=7.0)
500
+ steps = gr.Slider(label="Steps", minimum=1, maximum=100, step=1, value=30)
501
+ with gr.Row():
502
+ width = gr.Slider(label="Width", minimum=256, maximum=1536, step=8, value=1024)
503
+ height = gr.Slider(label="Height", minimum=256, maximum=1536, step=8, value=1024)
504
+ with gr.Row():
505
+ seed = gr.Slider(
506
+ label="Seed",
507
+ minimum=0,
508
+ maximum=2**32 - 1,
509
+ step=1,
510
+ value=0,
511
+ randomize=True,
512
  )
513
+ lora_scale = gr.Slider(label="LoRA Scale", minimum=0, maximum=1.5, step=0.01, value=1.0)
514
+ scheduler = gr.Dropdown(
515
+ label="Scheduler (SD only)",
516
+ choices=SCHEDULER_CHOICES,
517
+ value="Auto",
518
+ interactive=False,
519
+ )
520
 
521
  family_filter.change(
522
+ fn=refresh_lora_selector,
523
  inputs=[family_filter, search_box],
524
+ outputs=[
525
+ lora_selector,
526
+ catalog_stats,
527
+ lora_preview,
528
+ selected_info,
529
+ prompt,
530
+ selected_repo,
531
+ steps,
532
+ cfg_scale,
533
+ width,
534
+ height,
535
+ scheduler,
536
+ lora_scale,
537
+ ],
538
  )
539
  search_box.change(
540
+ fn=refresh_lora_selector,
541
  inputs=[family_filter, search_box],
542
+ outputs=[
543
+ lora_selector,
544
+ catalog_stats,
545
+ lora_preview,
546
+ selected_info,
547
+ prompt,
548
+ selected_repo,
549
+ steps,
550
+ cfg_scale,
551
+ width,
552
+ height,
553
+ scheduler,
554
+ lora_scale,
555
+ ],
556
  )
557
+ lora_selector.change(
558
+ fn=selected_payload,
559
+ inputs=[lora_selector],
 
560
  outputs=[
561
+ lora_preview,
562
+ selected_info,
563
  prompt,
564
+ selected_repo,
565
+ steps,
566
+ cfg_scale,
567
+ width,
568
+ height,
569
+ scheduler,
570
+ lora_scale,
571
+ ],
572
+ )
573
+ app.load(
574
+ fn=selected_payload,
575
+ inputs=[lora_selector],
576
+ outputs=[
577
+ lora_preview,
578
  selected_info,
579
+ prompt,
580
  selected_repo,
581
  steps,
582
  cfg_scale,
custom.css CHANGED
@@ -1,112 +1,49 @@
1
- /* Global styles */
2
- body {
3
- font-family: 'Arial', sans-serif;
4
- line-height: 1.6;
5
- color: #333;
6
- background-color: #f5f5f5;
 
7
  }
8
 
9
- /* Title styles */
10
- #title {
11
- text-align: center;
12
- padding: 20px 0;
13
- background-color: #3498db;
14
- color: white;
15
- margin-bottom: 20px;
16
  }
17
 
18
- #title h1 {
19
- font-size: 2.5em;
20
- margin: 0;
21
  }
22
 
23
- /* Input styles */
24
- #prompt input, #negative_prompt textarea {
25
- width: 100%;
26
- border-radius: 4px;
27
- border: 1px solid #ddd;
28
- padding: 10px;
29
- font-size: 1em;
30
- transition: border-color 0.3s ease;
31
  }
32
 
33
- #prompt input:focus, #negative_prompt textarea:focus {
34
- border-color: #3498db;
35
- outline: none;
 
36
  }
37
 
38
- /* Button styles */
39
- #generate_button {
40
- width: 100%;
41
- margin-top: 1em;
42
- border-radius: 4px;
43
- background-color: #2ecc71;
44
- color: white;
45
- font-size: 1.1em;
46
- padding: 10px;
47
- border: none;
48
- cursor: pointer;
49
- transition: background-color 0.3s ease;
50
  }
51
 
52
- #generate_button:hover {
53
- background-color: #27ae60;
 
54
  }
55
 
56
- /* Gallery styles */
57
- #gallery {
58
- display: flex;
59
- flex-wrap: wrap;
60
- justify-content: space-around;
61
- max-width: 100%;
62
- margin-top: 20px;
63
- }
64
-
65
- #gallery .grid-wrap {
66
- min-height: 150px;
67
- max-width: 150px;
68
- max-height: 150px;
69
- margin: 10px;
70
- border-radius: 4px;
71
- overflow: hidden;
72
- box-shadow: 0 2px 5px rgba(0,0,0,0.1);
73
- transition: transform 0.3s ease;
74
- }
75
-
76
- #gallery .grid-wrap:hover {
77
- transform: scale(1.05);
78
- }
79
-
80
- /* Slider styles */
81
- .gradio-slider {
82
- margin-bottom: 15px;
83
- }
84
-
85
- .gradio-slider input[type="range"] {
86
- width: 100%;
87
- }
88
-
89
- /* Dropdown styles */
90
- .gradio-dropdown select {
91
- width: 100%;
92
- padding: 8px;
93
- border-radius: 4px;
94
- border: 1px solid #ddd;
95
- font-size: 1em;
96
- }
97
-
98
- /* Result image styles */
99
- #result img {
100
- max-width: 100%;
101
- border-radius: 4px;
102
- box-shadow: 0 4px 8px rgba(0,0,0,0.1);
103
- }
104
-
105
- /* Responsive design */
106
- @media (max-width: 768px) {
107
- #gallery .grid-wrap {
108
- min-height: 100px;
109
- max-width: 100px;
110
- max-height: 100px;
111
- }
112
  }
 
1
+ :root {
2
+ --bg: #f3f6fb;
3
+ --panel: #ffffff;
4
+ --text: #0f172a;
5
+ --muted: #475569;
6
+ --border: #dbe3ef;
7
+ --accent: #0ea5e9;
8
  }
9
 
10
+ body,
11
+ .gradio-container {
12
+ background: radial-gradient(circle at 10% 10%, #e7f2ff 0%, #f3f6fb 45%, #eef2ff 100%);
13
+ color: var(--text);
 
 
 
14
  }
15
 
16
+ .gradio-container {
17
+ max-width: 1320px !important;
 
18
  }
19
 
20
+ .block,
21
+ .gr-box,
22
+ .gr-form,
23
+ .gr-panel {
24
+ border-radius: 14px !important;
25
+ border: 1px solid var(--border) !important;
26
+ background: var(--panel) !important;
 
27
  }
28
 
29
+ textarea,
30
+ input,
31
+ select {
32
+ border-radius: 10px !important;
33
  }
34
 
35
+ button.primary,
36
+ .gr-button-primary {
37
+ background: linear-gradient(90deg, #0284c7, #0ea5e9) !important;
38
+ border: none !important;
 
 
 
 
 
 
 
 
39
  }
40
 
41
+ button.primary:hover,
42
+ .gr-button-primary:hover {
43
+ filter: brightness(1.05);
44
  }
45
 
46
+ .gr-markdown p,
47
+ .gr-markdown li {
48
+ color: var(--muted);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49
  }