ysharma HF Staff commited on
Commit
89704c1
·
verified ·
1 Parent(s): ecd9353

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +104 -189
app.py CHANGED
@@ -153,22 +153,24 @@ HTML_TEMPLATE = """
153
  <div class="cv-wrap">
154
  {{#if value.img}}
155
  <img class="cv-img" src="{{value.img}}">
156
- <div class="cv-scanline"></div>
157
  {{else}}
158
  <div class="cv-empty">
159
- <div class="cv-reticle"><div class="cv-reticle-inner"></div></div>
 
 
 
160
  <p class="cv-empty-title">No image loaded</p>
161
- <p class="cv-empty-sub">Upload an image on the left, then hover here to orbit</p>
162
  </div>
163
  {{/if}}
164
 
165
  <div class="cv-hud">
166
  <div class="cv-readout">
167
- <span class="cv-lbl">AZ</span><span class="cv-val">${value.az}&deg;</span>
168
- <span class="cv-sep">&middot;</span>
169
- <span class="cv-lbl">EL</span><span class="cv-val">${value.el}&deg;</span>
170
- <span class="cv-sep">&middot;</span>
171
- <span class="cv-lbl">DIST</span><span class="cv-val">${value.dist}&times;</span>
172
  </div>
173
  <div class="cv-controls">
174
  <div class="cv-dpad">
@@ -179,8 +181,8 @@ HTML_TEMPLATE = """
179
  <button class="cv-btn cv-down" data-action="el-minus" title="Lower">&#9660;</button>
180
  </div>
181
  <div class="cv-zoom">
182
- <button class="cv-zbtn" data-action="dist-minus" title="Zoom In">&#xFF0B;</button>
183
- <button class="cv-zbtn" data-action="dist-plus" title="Zoom Out">&#xFF0D;</button>
184
  </div>
185
  </div>
186
  </div>
@@ -192,129 +194,123 @@ CSS_TEMPLATE = """
192
 
193
  .cv-wrap {
194
  position: relative;
195
- width: 100%; height: 540px;
196
- background: #08080f;
197
- border: 1px solid rgba(34,211,238,0.15);
198
- border-radius: 18px;
199
  overflow: hidden;
200
  display: flex; align-items: center; justify-content: center;
201
- font-family: 'IBM Plex Mono', 'Courier New', monospace;
202
  }
203
 
204
  .cv-img {
205
  max-width: 100%; max-height: 100%;
206
- object-fit: contain; border-radius: 4px; display: block;
207
- }
208
-
209
- .cv-scanline {
210
- position: absolute; inset: 0;
211
- background: repeating-linear-gradient(
212
- to bottom,
213
- transparent 0px, transparent 3px,
214
- rgba(0,0,0,0.05) 3px, rgba(0,0,0,0.05) 4px
215
- );
216
- pointer-events: none; border-radius: inherit;
217
  }
218
 
 
219
  .cv-empty {
220
  text-align: center; user-select: none;
221
- display: flex; flex-direction: column; align-items: center; gap: 10px;
222
- }
223
- .cv-reticle {
224
- width: 72px; height: 72px;
225
- border: 2px solid rgba(34,211,238,0.3); border-radius: 50%;
226
- display: flex; align-items: center; justify-content: center;
227
- position: relative; margin-bottom: 4px;
228
  }
229
- .cv-reticle::before, .cv-reticle::after {
230
- content: ''; position: absolute; background: rgba(34,211,238,0.25);
231
- }
232
- .cv-reticle::before { width: 1px; height: 100%; }
233
- .cv-reticle::after { width: 100%; height: 1px; }
234
- .cv-reticle-inner {
235
- width: 12px; height: 12px; border-radius: 50%;
236
- background: rgba(34,211,238,0.6); z-index: 1;
237
  }
238
  .cv-empty-title {
239
- font-size: 11px; letter-spacing: 0.08em; text-transform: uppercase;
240
- color: rgba(255,255,255,0.45);
241
  }
242
  .cv-empty-sub {
243
- font-size: 11px; color: rgba(255,255,255,0.2);
244
- max-width: 240px; line-height: 1.6;
245
  }
246
 
247
- /* HUD: hidden by default, shown on CSS :hover — survives template re-renders */
248
  .cv-hud {
249
- position: absolute; bottom: 16px; right: 16px;
250
  display: flex; flex-direction: column; align-items: flex-end; gap: 8px;
251
- opacity: 0; transition: opacity 0.2s ease; pointer-events: auto;
252
  }
253
  .cv-wrap:hover .cv-hud { opacity: 1; }
254
 
 
255
  .cv-readout {
256
- display: flex; align-items: center; gap: 6px;
257
- background: rgba(5,5,15,0.88); border: 1px solid rgba(34,211,238,0.3);
258
- border-radius: 8px; padding: 5px 14px;
259
- font-size: 11px; letter-spacing: 0.08em; white-space: nowrap;
260
- backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
261
- }
262
- .cv-lbl { color: rgba(34,211,238,0.5); font-size: 9px; text-transform: uppercase; }
263
- .cv-val { color: #22d3ee; font-weight: 600; }
264
- .cv-sep { color: rgba(255,255,255,0.15); }
265
-
 
 
266
  .cv-controls {
267
- display: flex; align-items: center; gap: 10px;
268
- background: rgba(5,5,15,0.82); border: 1px solid rgba(255,255,255,0.07);
269
- border-radius: 14px; padding: 10px 12px;
270
- backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px);
 
271
  }
272
 
 
273
  .cv-dpad {
274
  display: grid;
275
- grid-template-columns: repeat(3, 36px);
276
- grid-template-rows: repeat(3, 36px);
277
- gap: 3px;
278
  }
279
  .cv-btn {
280
- width: 36px; height: 36px;
281
- border: 1px solid rgba(255,255,255,0.08); border-radius: 8px;
282
- background: rgba(255,255,255,0.05); color: rgba(255,255,255,0.65);
283
- font-size: 11px; cursor: pointer;
 
 
284
  display: flex; align-items: center; justify-content: center;
285
- transition: background .12s, border-color .12s, transform .1s, color .12s;
286
  padding: 0; line-height: 1;
287
  }
288
  .cv-btn:hover {
289
- background: rgba(34,211,238,0.2); border-color: rgba(34,211,238,0.5);
290
- color: #22d3ee; transform: scale(1.12);
 
 
291
  }
292
- .cv-btn:active { transform: scale(0.9); }
293
 
294
  .cv-up { grid-column:2; grid-row:1; }
295
  .cv-left { grid-column:1; grid-row:2; }
296
  .cv-dot {
297
- grid-column:2; grid-row:2; width:36px; height:36px; border-radius:50%;
298
- background: rgba(34,211,238,0.07); border: 1px solid rgba(34,211,238,0.12);
 
299
  }
300
  .cv-right { grid-column:3; grid-row:2; }
301
  .cv-down { grid-column:2; grid-row:3; }
302
 
303
- .cv-zoom { display: flex; flex-direction: column; gap: 4px; }
 
304
  .cv-zbtn {
305
- width: 36px; height: 42px;
306
- border: 1px solid rgba(255,255,255,0.08); border-radius: 8px;
307
- background: rgba(255,255,255,0.05); color: rgba(255,255,255,0.65);
308
- font-size: 16px; font-weight: 500; cursor: pointer;
 
 
309
  display: flex; align-items: center; justify-content: center;
310
- transition: background .12s, border-color .12s, transform .1s, color .12s;
311
  padding: 0; line-height: 1;
312
  }
313
  .cv-zbtn:hover {
314
- background: rgba(251,191,36,0.2); border-color: rgba(251,191,36,0.5);
315
- color: #fbbf24; transform: scale(1.12);
 
 
316
  }
317
- .cv-zbtn:active { transform: scale(0.9); }
318
  """
319
 
320
  # BUG FIX: distance uses shiftDist() to jump between the exact snap points
@@ -357,117 +353,37 @@ element.addEventListener('click', function(e) {
357
 
358
  # ── Global Gradio CSS ──────────────────────────────────────────────────────────
359
  GLOBAL_CSS = """
360
- @import url('https://fonts.googleapis.com/css2?family=Oxanium:wght@300;400;500;600;700&family=IBM+Plex+Mono:wght@400;500;600&display=swap');
361
-
362
- body,
363
- .gradio-container,
364
- .gradio-container > .main,
365
- footer { background: #06060d !important; }
366
-
367
- .gradio-container {
368
- font-family: 'Oxanium', sans-serif !important;
369
- max-width: 1120px !important;
370
- margin: 0 auto !important;
371
- padding: 0 20px 40px !important;
372
- }
373
-
374
- .app-heading {
375
- text-align: center;
376
- padding: 36px 0 16px;
377
- }
378
  .app-heading h1 {
379
- font-family: 'Oxanium', sans-serif;
380
- font-size: clamp(28px, 4vw, 44px);
381
  font-weight: 700;
382
- letter-spacing: -0.025em;
383
- background: linear-gradient(125deg, #22d3ee 0%, #818cf8 55%, #c084fc 100%);
384
- -webkit-background-clip: text;
385
- -webkit-text-fill-color: transparent;
386
- background-clip: text;
387
- line-height: 1.1;
388
- margin: 0 0 10px;
389
  }
390
  .app-heading p {
391
- font-family: 'IBM Plex Mono', monospace;
392
- font-size: 11px;
393
- color: rgba(255,255,255,0.28);
394
- letter-spacing: 0.14em;
395
- text-transform: uppercase;
396
  }
397
 
398
- .gradio-container .block,
399
- .gradio-container .form {
400
- background: rgba(255,255,255,0.025) !important;
401
- border: 1px solid rgba(255,255,255,0.06) !important;
402
- border-radius: 14px !important;
403
- box-shadow: none !important;
404
- }
405
-
406
- label > span,
407
- .label-wrap span {
408
- font-family: 'IBM Plex Mono', monospace !important;
409
- font-size: 10px !important;
410
- text-transform: uppercase !important;
411
- letter-spacing: 0.1em !important;
412
- color: rgba(255,255,255,0.32) !important;
413
- }
414
-
415
- textarea,
416
- input[type=text],
417
- input[type=number] {
418
- background: rgba(255,255,255,0.04) !important;
419
- border: 1px solid rgba(255,255,255,0.08) !important;
420
- color: rgba(255,255,255,0.8) !important;
421
- border-radius: 8px !important;
422
- font-family: 'IBM Plex Mono', monospace !important;
423
- font-size: 11px !important;
424
- }
425
- textarea:focus, input:focus {
426
- border-color: rgba(34,211,238,0.4) !important;
427
- box-shadow: 0 0 0 2px rgba(34,211,238,0.06) !important;
428
- }
429
-
430
- input[type=range] { accent-color: #22d3ee !important; }
431
-
432
- .accordion > button,
433
- details > summary {
434
- background: rgba(255,255,255,0.03) !important;
435
- border-radius: 8px !important;
436
- color: rgba(255,255,255,0.5) !important;
437
- font-family: 'IBM Plex Mono', monospace !important;
438
- font-size: 11px !important;
439
- letter-spacing: 0.06em !important;
440
- }
441
-
442
- input[type=checkbox] { accent-color: #22d3ee; }
443
-
444
- .gradio-container h3 {
445
- font-family: 'Oxanium', sans-serif;
446
- font-size: 12px; font-weight: 600;
447
- letter-spacing: 0.1em; text-transform: uppercase;
448
- color: rgba(255,255,255,0.35);
449
- margin-bottom: 6px;
450
- }
451
-
452
- .status-box textarea {
453
- color: rgba(34,211,238,0.8) !important;
454
- font-size: 11px !important;
455
  }
456
 
 
457
  .section-divider {
458
  border: none;
459
- border-top: 1px solid rgba(255,255,255,0.05);
460
- margin: 8px 0;
461
  }
462
-
463
- .gallery-item { border-radius: 8px !important; overflow: hidden; }
464
  """
465
 
466
- GRADIO_THEME = gr.themes.Base(
467
- primary_hue="cyan",
468
- neutral_hue="slate",
469
- font=["Oxanium", "sans-serif"],
470
- )
471
 
472
 
473
  # ── App ────────────────────────────────────────────────────────────────────────
@@ -479,7 +395,7 @@ def create_app():
479
  gr.HTML("""
480
  <div class="app-heading">
481
  <h1>3D Camera View Generator</h1>
482
- <p>Qwen Image Edit &nbsp;&middot;&nbsp; Lightning LoRA &nbsp;&middot;&nbsp; Multi-Angle LoRA</p>
483
  </div>
484
  """)
485
 
@@ -511,7 +427,7 @@ def create_app():
511
 
512
  # ── Right column ─────────────────────────────────────────────────
513
  with gr.Column(scale=5, min_width=400):
514
- gr.Markdown("### Camera View — hover to reveal orbit controls")
515
 
516
  # FIX: plain gr.HTML with dict value — no subclass, no inspect error
517
  cam_view = gr.HTML(
@@ -531,14 +447,13 @@ def create_app():
531
  )
532
 
533
  gr.HTML('<hr class="section-divider">')
534
- gr.Markdown("### Generated Views")
535
 
536
  gallery_state = gr.State([])
537
  gallery = gr.Gallery(
538
- label="",
539
- show_label=False,
540
- columns=6,
541
- height=190,
542
  object_fit="cover",
543
  allow_preview=True,
544
  )
 
153
  <div class="cv-wrap">
154
  {{#if value.img}}
155
  <img class="cv-img" src="{{value.img}}">
 
156
  {{else}}
157
  <div class="cv-empty">
158
+ <svg class="cv-empty-icon" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.25">
159
+ <path stroke-linecap="round" stroke-linejoin="round" d="M6.827 6.175A2.31 2.31 0 015.186 7.23c-.38.054-.757.112-1.134.175C2.999 7.58 2.25 8.507 2.25 9.574V18a2.25 2.25 0 002.25 2.25h15A2.25 2.25 0 0021.75 18V9.574c0-1.067-.75-1.994-1.802-2.169a47.865 47.865 0 00-1.134-.175 2.31 2.31 0 01-1.64-1.055l-.822-1.316a2.192 2.192 0 00-1.736-1.039 48.774 48.774 0 00-5.232 0 2.192 2.192 0 00-1.736 1.039l-.821 1.316z" />
160
+ <path stroke-linecap="round" stroke-linejoin="round" d="M16.5 12.75a4.5 4.5 0 11-9 0 4.5 4.5 0 019 0zM18.75 10.5h.008v.008h-.008V10.5z" />
161
+ </svg>
162
  <p class="cv-empty-title">No image loaded</p>
163
+ <p class="cv-empty-sub">Upload an image on the left, then hover here to see camera controls</p>
164
  </div>
165
  {{/if}}
166
 
167
  <div class="cv-hud">
168
  <div class="cv-readout">
169
+ <span class="cv-lbl">Az</span><span class="cv-val">${value.az}&deg;</span>
170
+ <span class="cv-sep">/</span>
171
+ <span class="cv-lbl">El</span><span class="cv-val">${value.el}&deg;</span>
172
+ <span class="cv-sep">/</span>
173
+ <span class="cv-lbl">Dist</span><span class="cv-val">${value.dist}&times;</span>
174
  </div>
175
  <div class="cv-controls">
176
  <div class="cv-dpad">
 
181
  <button class="cv-btn cv-down" data-action="el-minus" title="Lower">&#9660;</button>
182
  </div>
183
  <div class="cv-zoom">
184
+ <button class="cv-zbtn" data-action="dist-minus" title="Zoom In">+</button>
185
+ <button class="cv-zbtn" data-action="dist-plus" title="Zoom Out">&minus;</button>
186
  </div>
187
  </div>
188
  </div>
 
194
 
195
  .cv-wrap {
196
  position: relative;
197
+ width: 100%; height: 500px;
198
+ background: var(--background-fill-secondary, #f9fafb);
199
+ border: 1px solid var(--border-color-primary, #e5e7eb);
200
+ border-radius: var(--radius-lg, 10px);
201
  overflow: hidden;
202
  display: flex; align-items: center; justify-content: center;
 
203
  }
204
 
205
  .cv-img {
206
  max-width: 100%; max-height: 100%;
207
+ object-fit: contain; display: block;
 
 
 
 
 
 
 
 
 
 
208
  }
209
 
210
+ /* empty state */
211
  .cv-empty {
212
  text-align: center; user-select: none;
213
+ display: flex; flex-direction: column; align-items: center; gap: 12px;
214
+ color: var(--body-text-color-subdued, #9ca3af);
 
 
 
 
 
215
  }
216
+ .cv-empty-icon {
217
+ width: 56px; height: 56px; opacity: 0.3;
 
 
 
 
 
 
218
  }
219
  .cv-empty-title {
220
+ font-size: 14px; font-weight: 500;
221
+ color: var(--body-text-color-subdued, #6b7280);
222
  }
223
  .cv-empty-sub {
224
+ font-size: 13px; max-width: 220px; line-height: 1.6;
225
+ color: var(--body-text-color-subdued, #9ca3af);
226
  }
227
 
228
+ /* HUD hidden until hover, pure CSS */
229
  .cv-hud {
230
+ position: absolute; bottom: 14px; right: 14px;
231
  display: flex; flex-direction: column; align-items: flex-end; gap: 8px;
232
+ opacity: 0; transition: opacity 0.18s ease; pointer-events: auto;
233
  }
234
  .cv-wrap:hover .cv-hud { opacity: 1; }
235
 
236
+ /* coordinate readout */
237
  .cv-readout {
238
+ display: flex; align-items: center; gap: 8px;
239
+ background: var(--background-fill-primary, #ffffff);
240
+ border: 1px solid var(--border-color-primary, #e5e7eb);
241
+ border-radius: 6px; padding: 5px 12px;
242
+ font-size: 12px; white-space: nowrap;
243
+ box-shadow: 0 1px 4px rgba(0,0,0,0.08);
244
+ }
245
+ .cv-lbl { color: var(--body-text-color-subdued, #9ca3af); font-size: 11px; }
246
+ .cv-val { color: var(--body-text-color, #111827); font-weight: 600; font-variant-numeric: tabular-nums; }
247
+ .cv-sep { color: var(--border-color-primary, #d1d5db); }
248
+
249
+ /* controls panel */
250
  .cv-controls {
251
+ display: flex; align-items: center; gap: 8px;
252
+ background: var(--background-fill-primary, #ffffff);
253
+ border: 1px solid var(--border-color-primary, #e5e7eb);
254
+ border-radius: 10px; padding: 8px 10px;
255
+ box-shadow: 0 2px 8px rgba(0,0,0,0.08);
256
  }
257
 
258
+ /* d-pad */
259
  .cv-dpad {
260
  display: grid;
261
+ grid-template-columns: repeat(3, 32px);
262
+ grid-template-rows: repeat(3, 32px);
263
+ gap: 2px;
264
  }
265
  .cv-btn {
266
+ width: 32px; height: 32px;
267
+ border: 1px solid var(--border-color-primary, #e5e7eb);
268
+ border-radius: 6px;
269
+ background: var(--background-fill-primary, #ffffff);
270
+ color: var(--body-text-color-subdued, #6b7280);
271
+ font-size: 10px; cursor: pointer;
272
  display: flex; align-items: center; justify-content: center;
273
+ transition: background 0.1s, border-color 0.1s, color 0.1s, transform 0.08s;
274
  padding: 0; line-height: 1;
275
  }
276
  .cv-btn:hover {
277
+ background: var(--color-accent-soft, #eff6ff);
278
+ border-color: var(--color-accent, #3b82f6);
279
+ color: var(--color-accent, #3b82f6);
280
+ transform: scale(1.1);
281
  }
282
+ .cv-btn:active { transform: scale(0.93); }
283
 
284
  .cv-up { grid-column:2; grid-row:1; }
285
  .cv-left { grid-column:1; grid-row:2; }
286
  .cv-dot {
287
+ grid-column:2; grid-row:2; width:32px; height:32px; border-radius:50%;
288
+ background: var(--background-fill-secondary, #f3f4f6);
289
+ border: 1px solid var(--border-color-primary, #e5e7eb);
290
  }
291
  .cv-right { grid-column:3; grid-row:2; }
292
  .cv-down { grid-column:2; grid-row:3; }
293
 
294
+ /* zoom */
295
+ .cv-zoom { display: flex; flex-direction: column; gap: 3px; }
296
  .cv-zbtn {
297
+ width: 32px; height: 37px;
298
+ border: 1px solid var(--border-color-primary, #e5e7eb);
299
+ border-radius: 6px;
300
+ background: var(--background-fill-primary, #ffffff);
301
+ color: var(--body-text-color-subdued, #6b7280);
302
+ font-size: 15px; font-weight: 500; cursor: pointer;
303
  display: flex; align-items: center; justify-content: center;
304
+ transition: background 0.1s, border-color 0.1s, color 0.1s, transform 0.08s;
305
  padding: 0; line-height: 1;
306
  }
307
  .cv-zbtn:hover {
308
+ background: var(--color-accent-soft, #eff6ff);
309
+ border-color: var(--color-accent, #3b82f6);
310
+ color: var(--color-accent, #3b82f6);
311
+ transform: scale(1.1);
312
  }
313
+ .cv-zbtn:active { transform: scale(0.93); }
314
  """
315
 
316
  # BUG FIX: distance uses shiftDist() to jump between the exact snap points
 
353
 
354
  # ── Global Gradio CSS ──────────────────────────────────────────────────────────
355
  GLOBAL_CSS = """
356
+ /* Header */
357
+ .app-heading { padding: 20px 0 4px; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
358
  .app-heading h1 {
359
+ font-size: clamp(22px, 3vw, 32px);
 
360
  font-weight: 700;
361
+ letter-spacing: -0.01em;
362
+ margin: 0 0 4px;
 
 
 
 
 
363
  }
364
  .app-heading p {
365
+ font-size: 13px;
366
+ opacity: 0.5;
367
+ margin: 0;
 
 
368
  }
369
 
370
+ /* Gallery: let it expand to fit its content instead of being fixed-height */
371
+ .gallery-container,
372
+ .gallery-container > .gallery,
373
+ .gallery-container > .gallery > .grid-wrap {
374
+ height: auto !important;
375
+ min-height: 180px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
376
  }
377
 
378
+ /* Section divider */
379
  .section-divider {
380
  border: none;
381
+ border-top: 1px solid var(--border-color-primary);
382
+ margin: 12px 0 4px;
383
  }
 
 
384
  """
385
 
386
+ GRADIO_THEME = gr.themes.Default()
 
 
 
 
387
 
388
 
389
  # ── App ────────────────────────────────────────────────────────────────────────
 
395
  gr.HTML("""
396
  <div class="app-heading">
397
  <h1>3D Camera View Generator</h1>
398
+ <p>Qwen Image Edit · Lightning LoRA · Multi-Angle LoRA</p>
399
  </div>
400
  """)
401
 
 
427
 
428
  # ── Right column ─────────────────────────────────────────────────
429
  with gr.Column(scale=5, min_width=400):
430
+ gr.Markdown("**Camera View** — hover to reveal controls")
431
 
432
  # FIX: plain gr.HTML with dict value — no subclass, no inspect error
433
  cam_view = gr.HTML(
 
447
  )
448
 
449
  gr.HTML('<hr class="section-divider">')
 
450
 
451
  gallery_state = gr.State([])
452
  gallery = gr.Gallery(
453
+ label="Generated Views",
454
+ show_label=True,
455
+ columns=4,
456
+ height="auto",
457
  object_fit="cover",
458
  allow_preview=True,
459
  )