tomiconic commited on
Commit
cd72379
Β·
verified Β·
1 Parent(s): e5a5926

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +333 -175
app.py CHANGED
@@ -5,7 +5,7 @@ from diffusers import StableDiffusionXLPipeline, DPMSolverMultistepScheduler
5
  from huggingface_hub import hf_hub_download
6
  import random
7
 
8
- # ── Model config ──────────────────────────────────────────────────────────────
9
  MODEL_REPO = "cyberdelia/CyberRealisticPony"
10
  MODEL_FILE = "CyberRealisticPony_V16.0_FP16.safetensors"
11
  PONY_POS = "score_9, score_8_up, score_7_up, "
@@ -14,37 +14,98 @@ PONY_NEG = "score_6, score_5, score_4, "
14
  print("Downloading model...")
15
  local_path = hf_hub_download(repo_id=MODEL_REPO, filename=MODEL_FILE)
16
  print("Loading pipeline...")
17
-
18
- pipe = StableDiffusionXLPipeline.from_single_file(
19
- local_path,
20
- torch_dtype=torch.float16,
21
- )
22
  pipe.scheduler = DPMSolverMultistepScheduler.from_config(
23
- pipe.scheduler.config,
24
- use_karras_sigmas=True,
25
  )
26
  pipe.enable_attention_slicing()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
 
28
- # Keep on CPU at startup β€” ZeroGPU allocates GPU per request
29
- print("Pipeline ready.")
 
 
 
 
 
 
 
 
 
 
 
 
30
 
31
  # ── Generation ────────────────────────────────────────────────────────────────
32
  @spaces.GPU(duration=180)
33
- def generate(prompt, negative_prompt, width, height, steps, guidance, seed, randomize):
 
 
34
  if not prompt.strip():
35
  raise gr.Error("Please enter a prompt.")
 
36
  if randomize:
37
  seed = random.randint(0, 2**32 - 1)
38
  seed = int(seed)
39
 
40
- # Move to GPU for this request
 
 
 
 
 
41
  pipe.to("cuda")
42
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
  generator = torch.Generator(device="cpu").manual_seed(seed)
44
 
45
  result = pipe(
46
- prompt=PONY_POS + prompt.strip(),
47
- negative_prompt=PONY_NEG + negative_prompt.strip(),
48
  width=int(width),
49
  height=int(height),
50
  num_inference_steps=int(steps),
@@ -53,236 +114,308 @@ def generate(prompt, negative_prompt, width, height, steps, guidance, seed, rand
53
  clip_skip=2,
54
  )
55
 
56
- # Move back to CPU to free GPU for other users
 
 
 
 
57
  pipe.to("cpu")
58
 
59
  return result.images[0], seed
60
 
61
  # ── CSS ───────────────────────────────────────────────────────────────────────
62
  css = """
63
- * { box-sizing: border-box; }
64
 
65
  body, .gradio-container {
66
- background: #0a0a0f !important;
67
  font-family: 'Inter', system-ui, -apple-system, sans-serif !important;
68
- max-width: 520px !important;
69
  margin: 0 auto !important;
 
70
  }
71
 
72
- .site-header {
73
- background: linear-gradient(160deg, #12001a, #1e0033, #0d001a);
74
- border: 1px solid #6f00ff44;
75
- border-radius: 20px;
76
- padding: 24px 20px 18px;
77
- text-align: center;
78
- margin-bottom: 12px;
79
- position: relative;
80
- overflow: hidden;
81
- }
82
- .site-header::before {
83
- content: '';
84
- position: absolute;
85
- top: -40px; left: -40px; right: -40px;
86
- height: 120px;
87
- background: radial-gradient(ellipse, #6f00ff22 0%, transparent 70%);
88
- }
89
- .site-header h1 {
90
- color: #ffffff;
91
- font-size: 1.7em;
92
- font-weight: 900;
93
- margin: 0 0 4px;
94
- letter-spacing: -1px;
95
- position: relative;
96
  }
97
- .site-header .sub {
98
- color: #9966cc;
99
- font-size: 0.78em;
100
- font-weight: 500;
101
- letter-spacing: 2px;
102
- text-transform: uppercase;
103
- position: relative;
104
  }
105
- .gpu-badge {
106
- display: inline-block;
107
- background: linear-gradient(90deg, #00c853, #00e676);
108
  color: #000;
109
- font-size: 0.68em;
110
- font-weight: 800;
111
- padding: 3px 12px;
112
  border-radius: 20px;
113
- margin-top: 10px;
114
  text-transform: uppercase;
115
- letter-spacing: 1.5px;
116
- position: relative;
117
  }
118
 
119
- .output-wrap {
120
- background: #0f0f1a;
121
- border: 1px solid #1e1e35;
 
122
  border-radius: 18px;
123
  overflow: hidden;
124
  margin-bottom: 10px;
125
- min-height: 340px;
126
  display: flex;
127
  align-items: center;
128
  justify-content: center;
 
129
  }
130
- .output-wrap img {
131
- width: 100%;
 
 
132
  border-radius: 18px;
133
  }
134
-
135
- .seed-display input {
136
- background: #0f0f1a !important;
137
- border: 1px solid #1e1e35 !important;
138
- border-radius: 10px !important;
139
- color: #5544aa !important;
140
- font-size: 0.8em !important;
141
- text-align: center !important;
142
- padding: 8px !important;
143
  }
144
 
145
- .sec-head {
146
- color: #7733cc;
147
- font-size: 0.7em;
148
- font-weight: 800;
149
- text-transform: uppercase;
150
- letter-spacing: 2px;
151
- margin: 16px 0 8px;
152
- padding-left: 2px;
153
- display: flex;
154
- align-items: center;
155
- gap: 6px;
156
  }
157
- .sec-head::after {
158
- content: '';
159
- flex: 1;
160
- height: 1px;
161
- background: linear-gradient(90deg, #331155, transparent);
 
 
162
  }
163
 
164
  textarea {
165
- background: #0f0f1a !important;
166
- border: 1px solid #221133 !important;
167
- border-radius: 12px !important;
168
- color: #d0c0ee !important;
169
- font-size: 15px !important;
170
  line-height: 1.5 !important;
171
- padding: 12px !important;
172
  resize: none !important;
 
 
173
  }
174
  textarea:focus {
175
- border-color: #7733cc !important;
176
- box-shadow: 0 0 0 3px #7733cc22 !important;
177
  outline: none !important;
 
 
178
  }
179
- textarea::placeholder { color: #443355 !important; }
180
 
181
- input[type=range] { accent-color: #7733cc !important; }
182
- .gradio-slider {
183
- background: #0f0f1a !important;
184
- border: 1px solid #1e1e35 !important;
185
- border-radius: 12px !important;
186
- padding: 12px 14px !important;
187
- margin-bottom: 8px !important;
 
188
  }
189
- .gradio-slider label span {
190
- color: #9966cc !important;
 
 
 
 
191
  font-size: 0.78em !important;
192
  font-weight: 600 !important;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
193
  text-transform: uppercase !important;
194
  letter-spacing: 1px !important;
195
  }
 
 
 
196
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
197
  input[type=number] {
198
- background: #0f0f1a !important;
199
- border: 1px solid #221133 !important;
200
  border-radius: 10px !important;
201
- color: #d0c0ee !important;
202
- font-size: 14px !important;
203
- padding: 10px !important;
 
204
  }
205
 
206
- .gradio-checkbox {
207
- background: #0f0f1a !important;
208
- border: 1px solid #1e1e35 !important;
209
- border-radius: 12px !important;
210
- padding: 10px 14px !important;
211
- }
212
  input[type=checkbox] {
213
- accent-color: #7733cc !important;
214
- width: 18px !important;
215
- height: 18px !important;
216
  }
217
-
218
- label span {
219
- color: #8855bb !important;
220
  font-size: 0.78em !important;
221
- font-weight: 700 !important;
222
- text-transform: uppercase !important;
223
- letter-spacing: 1px !important;
224
  }
225
 
226
- button.lg.primary {
227
- background: linear-gradient(135deg, #7733cc 0%, #5500aa 50%, #3d007a 100%) !important;
228
- border: 1px solid #9944ee !important;
229
- border-radius: 16px !important;
230
- color: #ffffff !important;
231
- font-size: 1.05em !important;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
232
  font-weight: 800 !important;
233
- padding: 18px !important;
234
  width: 100% !important;
235
- letter-spacing: 1px !important;
236
  text-transform: uppercase !important;
237
- box-shadow: 0 4px 24px #7733cc55 !important;
238
- transition: all 0.15s ease !important;
239
- margin-top: 6px !important;
240
  }
241
- button.lg.primary:hover {
242
- box-shadow: 0 6px 32px #7733cc88 !important;
243
  transform: translateY(-1px) !important;
244
  }
245
- button.lg.primary:active {
246
- transform: scale(0.98) translateY(0) !important;
247
- box-shadow: 0 2px 12px #7733cc44 !important;
 
 
 
 
 
 
 
 
 
248
  }
249
 
250
- footer { display: none !important; }
251
- .built-with { display: none !important; }
252
  """
253
 
254
  # ── UI ────────────────────────────────────────────────────────────────────────
255
- with gr.Blocks(css=css, title="CyberRealistic Pony") as demo:
256
 
 
257
  gr.HTML("""
258
- <div class="site-header">
259
- <h1>🐴 CyberRealistic Pony</h1>
260
- <div class="sub">v16.0 Β· SDXL Β· Pony XL</div>
261
- <div class="gpu-badge">⚑ ZeroGPU</div>
262
  </div>
263
  """)
264
 
265
- with gr.Group(elem_classes="output-wrap"):
266
- output_image = gr.Image(
267
- show_label=False,
268
- type="pil",
269
- height=400,
270
- )
 
271
 
 
272
  used_seed = gr.Number(
273
- label="Seed used β€” note this to recreate",
274
  interactive=False,
275
- elem_classes="seed-display",
276
  )
277
 
278
- gr.HTML('<div class="sec-head">✏️ Prompt</div>')
 
279
  prompt = gr.Textbox(
280
  show_label=False,
281
- placeholder="a woman in a futuristic city, cinematic lighting, highly detailed, photorealistic",
282
  lines=3,
 
283
  )
284
 
285
- gr.HTML('<div class="sec-head">🚫 Negative</div>')
 
286
  negative_prompt = gr.Textbox(
287
  show_label=False,
288
  value=(
@@ -292,27 +425,52 @@ with gr.Blocks(css=css, title="CyberRealistic Pony") as demo:
292
  "unnatural body, error, extra limb, missing limbs"
293
  ),
294
  lines=2,
 
295
  )
296
 
297
- gr.HTML('<div class="sec-head">πŸ“ Size</div>')
298
- with gr.Row():
299
- width = gr.Slider(512, 896, value=832, step=64, label="Width")
300
- height = gr.Slider(512, 1152, value=1024, step=64, label="Height")
301
-
302
- gr.HTML('<div class="sec-head">βš™οΈ Sampling</div>')
303
- steps = gr.Slider(20, 60, value=30, step=1, label="Steps")
304
- guidance = gr.Slider(1.0, 12.0, value=5.0, step=0.5, label="CFG Scale")
305
 
306
- gr.HTML('<div class="sec-head">🎲 Seed</div>')
307
- with gr.Row():
308
- seed = gr.Number(label="Seed", value=42, precision=0, minimum=0, maximum=2**32-1, scale=3)
309
- randomize_seed = gr.Checkbox(label="Random", value=True, scale=1)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
310
 
311
- generate_btn = gr.Button("⚑ Generate", variant="primary", size="lg")
 
312
 
313
  generate_btn.click(
314
  fn=generate,
315
- inputs=[prompt, negative_prompt, width, height, steps, guidance, seed, randomize_seed],
 
316
  outputs=[output_image, used_seed],
317
  )
318
 
 
5
  from huggingface_hub import hf_hub_download
6
  import random
7
 
8
+ # ── Model ─────────────────────────────────────────────────────────────────────
9
  MODEL_REPO = "cyberdelia/CyberRealisticPony"
10
  MODEL_FILE = "CyberRealisticPony_V16.0_FP16.safetensors"
11
  PONY_POS = "score_9, score_8_up, score_7_up, "
 
14
  print("Downloading model...")
15
  local_path = hf_hub_download(repo_id=MODEL_REPO, filename=MODEL_FILE)
16
  print("Loading pipeline...")
17
+ pipe = StableDiffusionXLPipeline.from_single_file(local_path, torch_dtype=torch.float16)
 
 
 
 
18
  pipe.scheduler = DPMSolverMultistepScheduler.from_config(
19
+ pipe.scheduler.config, use_karras_sigmas=True
 
20
  )
21
  pipe.enable_attention_slicing()
22
+ print("Ready.")
23
+
24
+ # ── Style presets (Fooocus-style) ─────────────────────────────────────────────
25
+ STYLES = {
26
+ "None": {
27
+ "pos": "",
28
+ "neg": "",
29
+ },
30
+ "πŸ“Έ Photorealistic": {
31
+ "pos": "RAW photo, photorealistic, hyperrealistic, 8k uhd, dslr, high quality, film grain, Fujifilm XT3, sharp focus, detailed skin texture, natural lighting, ",
32
+ "neg": "painting, illustration, cartoon, anime, render, cgi, drawing, ",
33
+ },
34
+ "🎬 Cinematic": {
35
+ "pos": "cinematic shot, movie scene, dramatic lighting, shallow depth of field, anamorphic lens, film grain, color graded, professional cinematography, epic composition, ",
36
+ "neg": "static, flat lighting, amateur, snapshot, ",
37
+ },
38
+ "πŸ–ΌοΈ Portrait": {
39
+ "pos": "professional portrait, studio lighting, bokeh background, sharp eyes, detailed face, perfect skin, 85mm lens, f/1.8, headshot, ",
40
+ "neg": "wide angle, fish eye, distorted face, bad eyes, cropped head, ",
41
+ },
42
+ "πŸŒ† Neon City": {
43
+ "pos": "cyberpunk city, neon lights, rain reflections on pavement, night scene, atmospheric fog, vibrant colors, futuristic architecture, blade runner aesthetic, ",
44
+ "neg": "daytime, rural, nature, warm tones, bright sunshine, ",
45
+ },
46
+ "🎨 Oil Painting": {
47
+ "pos": "oil painting, impressionist, visible brushstrokes, canvas texture, artistic masterpiece, museum quality, rich colors, painted by a master, ",
48
+ "neg": "photo, photograph, digital art, flat colors, ",
49
+ },
50
+ "✨ Fantasy Epic": {
51
+ "pos": "fantasy art, epic scene, magical atmosphere, dramatic clouds, volumetric lighting, highly detailed, concept art, artstation, 4k, ",
52
+ "neg": "modern, contemporary, mundane, boring, flat, ",
53
+ },
54
+ }
55
 
56
+ # ── LoRA presets ──────────────────────────────────────────────────────────────
57
+ LORAS = {
58
+ "None": None,
59
+ "βœ‹ Perfect Hands": {
60
+ "repo": "WolfAether21/PONY-DIFFUSION-SDXL-LORA",
61
+ "file": "Perfect Hands v2.safetensors",
62
+ "default_strength": 0.7,
63
+ },
64
+ "πŸ” Detail Enhancer": {
65
+ "repo": "WolfAether21/PONY-DIFFUSION-SDXL-LORA",
66
+ "file": "SDXL Detail.safetensors",
67
+ "default_strength": 0.6,
68
+ },
69
+ }
70
 
71
  # ── Generation ────────────────────────────────────────────────────────────────
72
  @spaces.GPU(duration=180)
73
+ def generate(prompt, negative_prompt, style, lora_name, lora_strength,
74
+ width, height, steps, guidance, seed, randomize):
75
+
76
  if not prompt.strip():
77
  raise gr.Error("Please enter a prompt.")
78
+
79
  if randomize:
80
  seed = random.randint(0, 2**32 - 1)
81
  seed = int(seed)
82
 
83
+ # Build final prompt with style
84
+ style_data = STYLES.get(style, STYLES["None"])
85
+ final_pos = PONY_POS + style_data["pos"] + prompt.strip()
86
+ final_neg = PONY_NEG + style_data["neg"] + negative_prompt.strip()
87
+
88
+ # Move to GPU
89
  pipe.to("cuda")
90
 
91
+ # Load LoRA if selected
92
+ lora_loaded = False
93
+ lora_data = LORAS.get(lora_name)
94
+ if lora_data:
95
+ try:
96
+ lora_path = hf_hub_download(repo_id=lora_data["repo"], filename=lora_data["file"])
97
+ pipe.load_lora_weights(lora_path)
98
+ pipe.fuse_lora(lora_scale=float(lora_strength))
99
+ lora_loaded = True
100
+ print(f"LoRA loaded: {lora_name} @ {lora_strength}")
101
+ except Exception as e:
102
+ print(f"LoRA load failed, continuing without: {e}")
103
+
104
  generator = torch.Generator(device="cpu").manual_seed(seed)
105
 
106
  result = pipe(
107
+ prompt=final_pos,
108
+ negative_prompt=final_neg,
109
  width=int(width),
110
  height=int(height),
111
  num_inference_steps=int(steps),
 
114
  clip_skip=2,
115
  )
116
 
117
+ # Unload LoRA and free GPU
118
+ if lora_loaded:
119
+ pipe.unfuse_lora()
120
+ pipe.unload_lora_weights()
121
+
122
  pipe.to("cpu")
123
 
124
  return result.images[0], seed
125
 
126
  # ── CSS ───────────────────────────────────────────────────────────────────────
127
  css = """
128
+ * { box-sizing: border-box; margin: 0; padding: 0; }
129
 
130
  body, .gradio-container {
131
+ background: #080810 !important;
132
  font-family: 'Inter', system-ui, -apple-system, sans-serif !important;
133
+ max-width: 480px !important;
134
  margin: 0 auto !important;
135
+ padding: 10px !important;
136
  }
137
 
138
+ /* ── Top bar ── */
139
+ .topbar {
140
+ display: flex;
141
+ align-items: center;
142
+ justify-content: space-between;
143
+ padding: 12px 4px;
144
+ margin-bottom: 10px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
145
  }
146
+ .topbar-title {
147
+ color: #fff;
148
+ font-size: 1.0em;
149
+ font-weight: 800;
150
+ letter-spacing: -0.3px;
 
 
151
  }
152
+ .topbar-badge {
153
+ background: #1aff7a;
 
154
  color: #000;
155
+ font-size: 0.6em;
156
+ font-weight: 900;
157
+ padding: 3px 10px;
158
  border-radius: 20px;
159
+ letter-spacing: 1px;
160
  text-transform: uppercase;
 
 
161
  }
162
 
163
+ /* ── Output image ── */
164
+ .img-output {
165
+ background: #0e0e18;
166
+ border: 1px solid #1c1c2e;
167
  border-radius: 18px;
168
  overflow: hidden;
169
  margin-bottom: 10px;
170
+ aspect-ratio: 3/4;
171
  display: flex;
172
  align-items: center;
173
  justify-content: center;
174
+ position: relative;
175
  }
176
+ .img-output img {
177
+ width: 100% !important;
178
+ height: 100% !important;
179
+ object-fit: contain;
180
  border-radius: 18px;
181
  }
182
+ /* Empty state placeholder */
183
+ .img-output .empty {
184
+ color: #2a2a3e;
185
+ font-size: 3em;
 
 
 
 
 
186
  }
187
 
188
+ /* ── Prompt area ── */
189
+ .prompt-wrap {
190
+ background: #0e0e18;
191
+ border: 1px solid #1c1c2e;
192
+ border-radius: 14px;
193
+ padding: 12px;
194
+ margin-bottom: 8px;
195
+ transition: border-color 0.2s;
196
+ }
197
+ .prompt-wrap:focus-within {
198
+ border-color: #5533aa;
199
  }
200
+ .prompt-label {
201
+ color: #44334a;
202
+ font-size: 0.65em;
203
+ font-weight: 700;
204
+ text-transform: uppercase;
205
+ letter-spacing: 1.5px;
206
+ margin-bottom: 6px;
207
  }
208
 
209
  textarea {
210
+ background: transparent !important;
211
+ border: none !important;
212
+ border-radius: 0 !important;
213
+ color: #c8b8e8 !important;
214
+ font-size: 14px !important;
215
  line-height: 1.5 !important;
216
+ padding: 0 !important;
217
  resize: none !important;
218
+ box-shadow: none !important;
219
+ width: 100% !important;
220
  }
221
  textarea:focus {
 
 
222
  outline: none !important;
223
+ box-shadow: none !important;
224
+ border: none !important;
225
  }
226
+ textarea::placeholder { color: #2e2640 !important; }
227
 
228
+ /* ── Style pills ── */
229
+ .style-pills {
230
+ margin-bottom: 8px;
231
+ }
232
+ .style-pills .gr-radio-group {
233
+ display: flex !important;
234
+ flex-wrap: wrap !important;
235
+ gap: 6px !important;
236
  }
237
+ .style-pills label {
238
+ background: #0e0e18 !important;
239
+ border: 1px solid #1c1c2e !important;
240
+ border-radius: 20px !important;
241
+ color: #7766aa !important;
242
+ cursor: pointer !important;
243
  font-size: 0.78em !important;
244
  font-weight: 600 !important;
245
+ padding: 6px 14px !important;
246
+ transition: all 0.15s !important;
247
+ white-space: nowrap !important;
248
+ }
249
+ .style-pills label:has(input:checked) {
250
+ background: #1e0e3e !important;
251
+ border-color: #6633cc !important;
252
+ color: #cc99ff !important;
253
+ }
254
+ .style-pills input[type=radio] { display: none !important; }
255
+
256
+ /* ── Accordion ── */
257
+ .gradio-accordion {
258
+ background: #0e0e18 !important;
259
+ border: 1px solid #1c1c2e !important;
260
+ border-radius: 14px !important;
261
+ margin-bottom: 8px !important;
262
+ overflow: hidden !important;
263
+ }
264
+ .gradio-accordion > .label-wrap {
265
+ padding: 12px 16px !important;
266
+ color: #7766aa !important;
267
+ font-size: 0.78em !important;
268
+ font-weight: 700 !important;
269
  text-transform: uppercase !important;
270
  letter-spacing: 1px !important;
271
  }
272
+ .gradio-accordion > .label-wrap:hover {
273
+ color: #aa88ff !important;
274
+ }
275
 
276
+ /* ── Sliders ── */
277
+ .gradio-slider {
278
+ padding: 4px 0 !important;
279
+ background: transparent !important;
280
+ border: none !important;
281
+ }
282
+ input[type=range] {
283
+ accent-color: #6633cc !important;
284
+ width: 100% !important;
285
+ height: 3px !important;
286
+ }
287
+ .gradio-slider span {
288
+ color: #7766aa !important;
289
+ font-size: 0.75em !important;
290
+ font-weight: 600 !important;
291
+ }
292
+
293
+ /* ── Number input ── */
294
  input[type=number] {
295
+ background: #0a0a14 !important;
296
+ border: 1px solid #1c1c2e !important;
297
  border-radius: 10px !important;
298
+ color: #aa88ff !important;
299
+ font-size: 13px !important;
300
+ padding: 8px 10px !important;
301
+ width: 100% !important;
302
  }
303
 
304
+ /* ── Checkbox ── */
 
 
 
 
 
305
  input[type=checkbox] {
306
+ accent-color: #6633cc !important;
307
+ width: 16px !important;
308
+ height: 16px !important;
309
  }
310
+ .gradio-checkbox label span {
311
+ color: #7766aa !important;
 
312
  font-size: 0.78em !important;
313
+ font-weight: 600 !important;
 
 
314
  }
315
 
316
+ /* ── Dropdown (LoRA) ── */
317
+ .gradio-dropdown {
318
+ background: #0a0a14 !important;
319
+ border: 1px solid #1c1c2e !important;
320
+ border-radius: 10px !important;
321
+ }
322
+ .gradio-dropdown select, .gradio-dropdown input {
323
+ background: transparent !important;
324
+ color: #aa88ff !important;
325
+ font-size: 13px !important;
326
+ }
327
+
328
+ /* ── Seed row ── */
329
+ .seed-row {
330
+ display: flex;
331
+ align-items: center;
332
+ gap: 10px;
333
+ margin-top: 4px;
334
+ }
335
+ .seed-out input {
336
+ background: #0a0a14 !important;
337
+ border: 1px solid #111120 !important;
338
+ border-radius: 8px !important;
339
+ color: #332255 !important;
340
+ font-size: 0.75em !important;
341
+ text-align: center !important;
342
+ padding: 6px !important;
343
+ }
344
+
345
+ /* ── Generate button ── */
346
+ .gen-btn button {
347
+ background: linear-gradient(135deg, #5522aa 0%, #3a1580 100%) !important;
348
+ border: 1px solid #7744cc !important;
349
+ border-radius: 14px !important;
350
+ color: #fff !important;
351
+ font-size: 0.95em !important;
352
  font-weight: 800 !important;
353
+ padding: 16px !important;
354
  width: 100% !important;
355
+ letter-spacing: 1.5px !important;
356
  text-transform: uppercase !important;
357
+ box-shadow: 0 4px 20px #5522aa44 !important;
358
+ transition: all 0.15s !important;
359
+ margin-top: 4px !important;
360
  }
361
+ .gen-btn button:hover {
362
+ box-shadow: 0 6px 28px #5522aa88 !important;
363
  transform: translateY(-1px) !important;
364
  }
365
+ .gen-btn button:active {
366
+ transform: scale(0.98) !important;
367
+ box-shadow: 0 2px 10px #5522aa33 !important;
368
+ }
369
+
370
+ /* ── Labels ── */
371
+ label > span:first-child {
372
+ color: #55446a !important;
373
+ font-size: 0.72em !important;
374
+ font-weight: 700 !important;
375
+ text-transform: uppercase !important;
376
+ letter-spacing: 1px !important;
377
  }
378
 
379
+ footer, .built-with, .svelte-1ipelgc { display: none !important; }
 
380
  """
381
 
382
  # ── UI ────────────────────────────────────────────────────────────────────────
383
+ with gr.Blocks(css=css, title="ImageGen") as demo:
384
 
385
+ # Top bar
386
  gr.HTML("""
387
+ <div class="topbar">
388
+ <span class="topbar-title">🐴 CyberRealistic Pony</span>
389
+ <span class="topbar-badge">⚑ GPU</span>
 
390
  </div>
391
  """)
392
 
393
+ # Output
394
+ output_image = gr.Image(
395
+ show_label=False,
396
+ type="pil",
397
+ height=440,
398
+ elem_classes="img-output",
399
+ )
400
 
401
+ # Seed used (small, below image)
402
  used_seed = gr.Number(
403
+ label="Last seed",
404
  interactive=False,
405
+ elem_classes="seed-out",
406
  )
407
 
408
+ # Prompt
409
+ gr.HTML('<div class="prompt-label">Prompt</div>')
410
  prompt = gr.Textbox(
411
  show_label=False,
412
+ placeholder="describe your image...",
413
  lines=3,
414
+ elem_classes="prompt-wrap",
415
  )
416
 
417
+ # Negative
418
+ gr.HTML('<div class="prompt-label" style="margin-top:8px">Negative</div>')
419
  negative_prompt = gr.Textbox(
420
  show_label=False,
421
  value=(
 
425
  "unnatural body, error, extra limb, missing limbs"
426
  ),
427
  lines=2,
428
+ elem_classes="prompt-wrap",
429
  )
430
 
431
+ # Style presets
432
+ gr.HTML('<div class="prompt-label" style="margin-top:8px">Style</div>')
433
+ style = gr.Radio(
434
+ choices=list(STYLES.keys()),
435
+ value="None",
436
+ show_label=False,
437
+ elem_classes="style-pills",
438
+ )
439
 
440
+ # Advanced settings accordion
441
+ with gr.Accordion("βš™οΈ Advanced Settings", open=False):
442
+ gr.HTML('<div style="height:8px"></div>')
443
+
444
+ with gr.Row():
445
+ width = gr.Slider(512, 896, value=832, step=64, label="Width")
446
+ height = gr.Slider(512, 1152, value=1024, step=64, label="Height")
447
+
448
+ steps = gr.Slider(20, 60, value=30, step=1, label="Steps")
449
+ guidance = gr.Slider(1.0, 12.0, value=5.0, step=0.5, label="CFG Scale")
450
+
451
+ with gr.Row():
452
+ seed = gr.Number(label="Seed", value=42, precision=0,
453
+ minimum=0, maximum=2**32-1, scale=3)
454
+ randomize_seed = gr.Checkbox(label="Random seed", value=True, scale=1)
455
+
456
+ # LoRA accordion
457
+ with gr.Accordion("🎨 LoRA", open=False):
458
+ gr.HTML('<div style="height:8px"></div>')
459
+ gr.HTML('<div style="color:#554466;font-size:0.72em;margin-bottom:10px;">Add a LoRA to tweak style or fix common issues like hands.</div>')
460
+ lora_name = gr.Dropdown(
461
+ choices=list(LORAS.keys()),
462
+ value="None",
463
+ label="LoRA",
464
+ )
465
+ lora_strength = gr.Slider(0.1, 1.0, value=0.7, step=0.05, label="LoRA Strength")
466
 
467
+ # Generate
468
+ generate_btn = gr.Button("Generate ✦", variant="primary", size="lg", elem_classes="gen-btn")
469
 
470
  generate_btn.click(
471
  fn=generate,
472
+ inputs=[prompt, negative_prompt, style, lora_name, lora_strength,
473
+ width, height, steps, guidance, seed, randomize_seed],
474
  outputs=[output_image, used_seed],
475
  )
476