tchung1970 Claude Opus 4.5 commited on
Commit
cc5f21f
·
1 Parent(s): 6212e3f

Update UI to match Qwen-Image styling

Browse files

- Add gr.themes.Soft() theme with Apple-style design
- Two-column horizontal layout with dark mode support
- Custom CSS for panel styling, borders, and shadows
- JavaScript for enforcing horizontal layout
- Blue accent colors (#0071e3) and Inter font

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

Files changed (1) hide show
  1. app_diffusers.py +630 -93
app_diffusers.py CHANGED
@@ -13,7 +13,6 @@ from diffusers import OvisImagePipeline
13
 
14
  logging.set_verbosity_error()
15
 
16
- # DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
17
  MAX_SEED = np.iinfo(np.int32).max
18
 
19
  device = "cuda"
@@ -21,123 +20,661 @@ _dtype = torch.bfloat16
21
  hf_token = os.getenv("HF_TOKEN")
22
 
23
  pipe = OvisImagePipeline.from_pretrained(
24
- "AIDC-AI/Ovis-Image-7B",
25
- token=hf_token,
26
  torch_dtype=torch.bfloat16
27
  )
28
  pipe.to("cuda")
29
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
  @spaces.GPU(duration=75)
31
- def generate(prompt, img_height=1024, img_width=1024, seed=42, steps=50, guidance_scale=5.0):
32
- print(f'inference with prompt : {prompt}, size: {img_height}x{img_width}, seed : {seed}, step : {steps}, cfg : {guidance_scale}')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
  generator = torch.Generator().manual_seed(seed)
34
  image = pipe(
35
- prompt,
36
- negative_prompt="",
37
- height=img_height,
38
- width=img_width,
39
- num_inference_steps=steps,
40
  true_cfg_scale=guidance_scale,
41
  generator=generator,
42
  ).images[0]
43
- return image
44
 
45
- examples = [
46
- "Child lying on bed with balloons, streamers, and plush toys, surreal dreamlike scene. Close-up, centered on the child and surrounding objects. Soft, diffused lighting.",
47
- "A trendy, playful 3D render of the text \"OVIS-IMAGE\" designed as glossy, inflated balloon letters. The typography is bubbly and rounded. The material is a shiny, reflective plastic in vibrant Alibaba Orange. The text is floating in a bright, clean white studio space with soft pastel lighting. There are subtle reflections of the studio softboxes on the curves of the balloons. High-fashion retail aesthetic, pop art style, C4D render, cute and energetic.",
48
- "一张写实风格的现代化教室场景摄影图。画面焦点集中在前方墙壁正中央的一块洁白明亮的白板上,白板上用清晰、工整的黑色马克笔手写体写着多行文字:最上方是巨大的英文标题“OVIS-IMAGE”,紧接着下方依次分行写着中文说明:“7B文生图模型”、“无需专有模型即可实现双语渲染”、“可在消费级显卡部署”。白板下方的铝合金笔槽内摆放着几支直立或横躺的彩色白板笔(蓝、红、黄、绿)。前景是两位背对镜头的学生(呈虚化状态,营造景深感),坐在木质课桌前,右侧课桌上还放着一块折叠的棕色毛巾。左侧隐约可见部分绿色黑板,顶部有一盏明亮的长条日光灯提供照明。整体光线柔和自然,构图对称,对焦精准在白板文字上,营造出真实的教育和技术演示氛围。",
49
- "微距摄影,一只鲜艳的红色瓢虫停留在翠绿的叶子上,特写镜头。叶子表面分布着晶莹剔透的水珠(晨露),瓢虫红色的甲壳光滑如镜,反射着柔和的自然光,能够清晰看到瓢虫腿部和触角的细微纹理。背景是梦幻的绿色虚化(散景),突显主体,极度逼真,8k分辨率,高清晰度,电影级质感。",
50
- ]
51
 
52
- css="""
53
- #col-container {
54
- margin: 0 auto;
55
- max-width: 520px;
56
- }
57
- """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58
 
59
- with gr.Blocks(css=css) as demo:
60
-
61
- with gr.Column(elem_id="col-container"):
62
- gr.Markdown(f"""# Ovis-Image
63
- Built upon [Ovis-U1](https://huggingface.co/spaces/AIDC-AI/Ovis-U1-3B), Ovis-Image is a 7B text-to-image model specifically optimized for high-quality text rendering under stringent computational constraints.
64
-
65
- [[code](https://github.com/AIDC-AI/Ovis-Image)] [[model](https://huggingface.co/AIDC-AI/Ovis-Image-7B)]
66
- """)
67
-
68
- with gr.Row():
69
-
70
- prompt = gr.Text(
71
  label="Prompt",
72
- show_label=False,
73
- max_lines=1,
74
- placeholder="Enter your prompt here",
75
- container=False,
 
 
 
 
 
 
 
 
 
 
 
 
 
76
  )
77
-
78
- run_button = gr.Button("Run", scale=0)
79
-
80
- result = gr.Image(label="Result", show_label=False)
81
-
82
- with gr.Accordion("Advanced Settings", open=False):
83
- with gr.Row():
84
-
85
- img_height = gr.Slider(
86
- label="Image Height",
87
- minimum=256,
88
- maximum=2048,
89
- step=32,
90
- value=1024,
91
- )
92
-
93
- img_width = gr.Slider(
94
- label="Image Width",
95
- minimum=256,
96
- maximum=2048,
97
- step=32,
98
- value=1024,
99
- )
100
-
101
- with gr.Row():
102
-
103
- guidance_scale = gr.Slider(
104
- label="Guidance Scale",
105
- minimum=1,
106
- maximum=14,
107
- step=0.1,
108
- value=5.0,
109
- )
110
-
111
- num_inference_steps = gr.Slider(
112
- label="Number of inference steps",
113
- minimum=1,
114
- maximum=100,
115
- step=1,
116
- value=50,
117
- )
118
 
 
 
 
 
 
 
 
 
119
  seed = gr.Slider(
120
  label="Seed",
121
  minimum=0,
122
  maximum=MAX_SEED,
123
  step=1,
124
- value=42,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
125
  )
126
-
127
- gr.Examples(
128
- examples = examples,
129
- fn = generate,
130
- inputs = [prompt],
131
- outputs = [result],
132
- cache_examples="lazy"
133
- )
134
 
 
135
  gr.on(
136
  triggers=[run_button.click, prompt.submit],
137
- fn = generate,
138
- inputs = [prompt, img_height, img_width, seed, num_inference_steps, guidance_scale],
139
- outputs = [result]
 
 
 
 
 
 
 
140
  )
141
 
142
  if __name__ == '__main__':
143
- demo.launch()
 
13
 
14
  logging.set_verbosity_error()
15
 
 
16
  MAX_SEED = np.iinfo(np.int32).max
17
 
18
  device = "cuda"
 
20
  hf_token = os.getenv("HF_TOKEN")
21
 
22
  pipe = OvisImagePipeline.from_pretrained(
23
+ "AIDC-AI/Ovis-Image-7B",
24
+ token=hf_token,
25
  torch_dtype=torch.bfloat16
26
  )
27
  pipe.to("cuda")
28
 
29
+ examples = [
30
+ "Five shimmering goldfish weave through crevices between stones; four are red-and-white, while one is silver-white. By the pond's edge, a golden shaded British Shorthair cat watches them intently, counting on blind luck. Watercolor style.",
31
+ "Solar punk vehicle in a bustling city",
32
+ "An anthropomorphic cat riding a Harley Davidson in Arizona with sunglasses and a leather jacket",
33
+ "An elderly woman poses for a high fashion photoshoot in colorful, patterned clothes with a cyberpunk 2077 vibe",
34
+ ]
35
+
36
+ def get_image_size(aspect_ratio):
37
+ """Converts aspect ratio string to width, height tuple."""
38
+ if "(" in aspect_ratio and "x" in aspect_ratio:
39
+ try:
40
+ res_part = aspect_ratio.split("(")[1].split(")")[0]
41
+ width, height = res_part.split("x")
42
+ return int(width), int(height)
43
+ except:
44
+ pass
45
+ return 1024, 1024
46
+
47
+ apple_css = """
48
+ /* Global Styles */
49
+ .gradio-container {
50
+ max-width: 85vw !important;
51
+ margin: 0 auto !important;
52
+ padding: 48px 20px !important;
53
+ font-family: -apple-system, BlinkMacSystemFont, 'Inter', 'Segoe UI', 'Roboto', sans-serif !important;
54
+ }
55
+
56
+ /* Disable all transitions globally to prevent layout shifts */
57
+ * {
58
+ transition: none !important;
59
+ animation: none !important;
60
+ }
61
+
62
+ /* Header */
63
+ .header-container {
64
+ text-align: left;
65
+ margin-bottom: 24px;
66
+ }
67
+
68
+ .main-title {
69
+ font-size: 32px !important;
70
+ font-weight: 600 !important;
71
+ letter-spacing: -0.02em !important;
72
+ line-height: 1.07 !important;
73
+ color: #1d1d1f !important;
74
+ margin: 0 0 16px 0 !important;
75
+ }
76
+
77
+ .subtitle {
78
+ font-size: 21px !important;
79
+ font-weight: 400 !important;
80
+ line-height: 1.38 !important;
81
+ color: #6e6e73 !important;
82
+ margin: 0 0 24px 0 !important;
83
+ }
84
+
85
+ .attribution-link {
86
+ display: inline-block;
87
+ font-size: 14px !important;
88
+ color: #0071e3 !important;
89
+ text-decoration: none !important;
90
+ font-weight: 400 !important;
91
+ transition: color 0.2s ease !important;
92
+ }
93
+
94
+ .attribution-link:hover {
95
+ color: #0077ed !important;
96
+ text-decoration: underline !important;
97
+ }
98
+
99
+ /* Input Section */
100
+ .input-section {
101
+ background: #ffffff;
102
+ border-radius: 18px;
103
+ padding: 32px;
104
+ box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
105
+ }
106
+
107
+ /* Textbox */
108
+ textarea {
109
+ font-size: 17px !important;
110
+ line-height: 1.47 !important;
111
+ border-radius: 12px !important;
112
+ border: 1px solid #d2d2d7 !important;
113
+ padding: 12px 16px !important;
114
+ background: #ffffff !important;
115
+ font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif !important;
116
+ min-height: 200px !important;
117
+ max-height: 400px !important;
118
+ height: 200px !important;
119
+ resize: vertical !important;
120
+ overflow-y: auto !important;
121
+ margin-bottom: 16px !important;
122
+ }
123
+
124
+ textarea:focus {
125
+ border-color: #0071e3 !important;
126
+ box-shadow: 0 0 0 4px rgba(0, 113, 227, 0.15) !important;
127
+ outline: none !important;
128
+ }
129
+
130
+ textarea::placeholder {
131
+ color: #86868b !important;
132
+ }
133
+
134
+ /* Button */
135
+ button.primary {
136
+ font-size: 17px !important;
137
+ font-weight: 400 !important;
138
+ padding: 12px 32px !important;
139
+ border-radius: 980px !important;
140
+ background: #0071e3 !important;
141
+ border: none !important;
142
+ color: #ffffff !important;
143
+ min-height: 44px !important;
144
+ letter-spacing: -0.01em !important;
145
+ cursor: pointer !important;
146
+ }
147
+
148
+ button.primary:hover {
149
+ background: #0077ed !important;
150
+ }
151
+
152
+ button.primary:active {
153
+ opacity: 0.9 !important;
154
+ }
155
+
156
+ /* Output Section */
157
+ div.output-section {
158
+ background: #ffffff;
159
+ border-radius: 18px;
160
+ padding: 32px;
161
+ box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
162
+ overflow: hidden;
163
+ display: flex;
164
+ align-items: center;
165
+ justify-content: center;
166
+ min-height: 80vh;
167
+ max-height: 90vh;
168
+ will-change: auto;
169
+ position: relative;
170
+ }
171
+
172
+ .output-section * {
173
+ transform: none !important;
174
+ transition: none !important;
175
+ animation: none !important;
176
+ }
177
+
178
+ .output-section img {
179
+ border-radius: 12px !important;
180
+ max-width: 100% !important;
181
+ max-height: 85vh !important;
182
+ width: auto !important;
183
+ height: auto !important;
184
+ object-fit: contain !important;
185
+ transform: none !important;
186
+ transition: none !important;
187
+ animation: none !important;
188
+ backface-visibility: hidden;
189
+ -webkit-backface-visibility: hidden;
190
+ }
191
+
192
+ /* Make progress/generation area fill more space */
193
+ .output-section > div {
194
+ width: 100% !important;
195
+ min-height: 75vh !important;
196
+ max-height: 85vh !important;
197
+ display: flex !important;
198
+ align-items: center !important;
199
+ justify-content: center !important;
200
+ }
201
+
202
+ .output-section > div > div {
203
+ min-height: 75vh !important;
204
+ max-height: 85vh !important;
205
+ width: 100% !important;
206
+ display: flex !important;
207
+ align-items: center !important;
208
+ justify-content: center !important;
209
+ }
210
+
211
+ .output-section * {
212
+ max-width: 100% !important;
213
+ }
214
+
215
+ /* Footer */
216
+ .footer-text {
217
+ text-align: center;
218
+ margin-top: 48px;
219
+ font-size: 14px !important;
220
+ color: #86868b !important;
221
+ line-height: 1.43 !important;
222
+ }
223
+
224
+ /* Progress */
225
+ .progress-bar {
226
+ background: #0071e3 !important;
227
+ border-radius: 4px !important;
228
+ }
229
+
230
+ /* Dark Mode */
231
+ .dark .main-title {
232
+ color: #ffffff !important;
233
+ }
234
+
235
+ .dark .subtitle {
236
+ color: #a1a1a6 !important;
237
+ }
238
+
239
+ .input-section .main-title {
240
+ color: #ffffff !important;
241
+ }
242
+
243
+ .dark .input-section .main-title {
244
+ color: #f5f5f7 !important;
245
+ }
246
+
247
+ .dark .input-section,
248
+ .dark .output-section {
249
+ background: #1d1d1f;
250
+ box-shadow: 0 2px 12px rgba(0, 0, 0, 0.4);
251
+ }
252
+
253
+ .dark textarea {
254
+ background: #1d1d1f !important;
255
+ border-color: #424245 !important;
256
+ color: #f5f5f7 !important;
257
+ }
258
+
259
+ .dark textarea::placeholder {
260
+ color: #86868b !important;
261
+ }
262
+
263
+ /* Inline labels */
264
+ label.inline-label {
265
+ display: flex !important;
266
+ align-items: center !important;
267
+ min-width: 120px !important;
268
+ margin: 0 !important;
269
+ padding: 0 12px 0 0 !important;
270
+ font-weight: 400 !important;
271
+ font-size: 14px !important;
272
+ color: #1d1d1f !important;
273
+ }
274
+
275
+ /* Fix column width to prevent shrinking - target Gradio's generated structure */
276
+ .input-section {
277
+ min-width: 550px !important;
278
+ max-width: 550px !important;
279
+ width: 550px !important;
280
+ flex-shrink: 0 !important;
281
+ flex-grow: 0 !important;
282
+ }
283
+
284
+ /* Lock the output section to fill remaining space */
285
+ .output-section {
286
+ flex-grow: 1 !important;
287
+ flex-shrink: 0 !important;
288
+ flex-basis: auto !important;
289
+ }
290
+
291
+ /* Prevent Gradio columns from flexing */
292
+ .gradio-column {
293
+ flex-shrink: 0 !important;
294
+ }
295
+
296
+ /* Stabilize row layout - force horizontal layout with maximum specificity */
297
+ .gradio-row,
298
+ div.gradio-row,
299
+ .gradio-container .gradio-row,
300
+ .gradio-container > .gradio-row,
301
+ .gradio-container div.gradio-row {
302
+ align-items: flex-start !important;
303
+ flex-direction: row !important;
304
+ display: flex !important;
305
+ flex-wrap: nowrap !important;
306
+ width: 100% !important;
307
+ }
308
+
309
+ /* Force columns to stay inline */
310
+ .gradio-row > .gradio-column,
311
+ .gradio-row > div {
312
+ display: inline-flex !important;
313
+ vertical-align: top !important;
314
+ }
315
+
316
+ /* First column - input section */
317
+ .gradio-row > .gradio-column:first-child,
318
+ .gradio-row > div:first-child {
319
+ width: 550px !important;
320
+ min-width: 550px !important;
321
+ max-width: 550px !important;
322
+ flex: 0 0 550px !important;
323
+ }
324
+
325
+ /* Second column - output section */
326
+ .gradio-row > .gradio-column:last-child,
327
+ .gradio-row > div:last-child {
328
+ flex: 1 1 auto !important;
329
+ min-width: 0 !important;
330
+ }
331
+
332
+ /* Lock textbox container size */
333
+ .input-section .gr-textbox,
334
+ .input-section label[for] {
335
+ width: 100% !important;
336
+ }
337
+
338
+ /* Prevent form from expanding */
339
+ .input-section form {
340
+ width: 100% !important;
341
+ max-width: 100% !important;
342
+ }
343
+
344
+ /* Ensure seed input always visible */
345
+ .input-section input[type="number"] {
346
+ display: block !important;
347
+ visibility: visible !important;
348
+ }
349
+
350
+ /* Hide progress indicator in input section - target specific progress elements */
351
+ .input-section .progress-container,
352
+ .input-section [class*="progress-bar"],
353
+ .input-section [class*="progress-text"],
354
+ .input-section [class*="progress-level"],
355
+ .input-section .progress,
356
+ .input-section .eta-bar {
357
+ display: none !important;
358
+ visibility: hidden !important;
359
+ height: 0 !important;
360
+ overflow: hidden !important;
361
+ }
362
+
363
+ /* Override ALL responsive behavior - force horizontal layout at ALL viewport sizes */
364
+ @media (max-width: 2000px) {
365
+ .gradio-row,
366
+ div.gradio-row,
367
+ .gradio-container .gradio-row,
368
+ .gradio-container > .gradio-row {
369
+ flex-direction: row !important;
370
+ flex-wrap: nowrap !important;
371
+ display: flex !important;
372
+ }
373
+
374
+ .gradio-row > .gradio-column,
375
+ .gradio-row > div {
376
+ display: inline-flex !important;
377
+ }
378
+
379
+ .gradio-row > .gradio-column:first-child,
380
+ .gradio-row > div:first-child {
381
+ width: 550px !important;
382
+ min-width: 550px !important;
383
+ max-width: 550px !important;
384
+ flex: 0 0 550px !important;
385
+ }
386
+
387
+ .gradio-row > .gradio-column:last-child,
388
+ .gradio-row > div:last-child {
389
+ flex: 1 1 auto !important;
390
+ min-width: 0 !important;
391
+ }
392
+ }
393
+
394
+ /* Responsive text sizing only */
395
+ @media (max-width: 734px) {
396
+ .main-title {
397
+ font-size: 40px !important;
398
+ }
399
+
400
+ .subtitle {
401
+ font-size: 19px !important;
402
+ }
403
+
404
+ .gradio-container {
405
+ padding: 32px 16px !important;
406
+ }
407
+
408
+ .input-section,
409
+ .output-section {
410
+ padding: 24px !important;
411
+ }
412
+
413
+ /* FORCE horizontal layout even on mobile */
414
+ .gradio-row,
415
+ div.gradio-row {
416
+ flex-direction: row !important;
417
+ flex-wrap: nowrap !important;
418
+ }
419
+ }
420
+
421
+ /* Remove default Gradio styling */
422
+ .contain {
423
+ padding: 0 !important;
424
+ }
425
+
426
+ /* Hide Gradio footer */
427
+ footer {
428
+ display: none !important;
429
+ }
430
+
431
+ .footer {
432
+ display: none !important;
433
+ }
434
+
435
+ /* Target main app container */
436
+ #root, #app {
437
+ width: 100% !important;
438
+ max-width: none !important;
439
+ }
440
+ """
441
+
442
+ # JavaScript to force horizontal layout
443
+ js_code = """
444
+ function() {
445
+ function forceHorizontalLayout() {
446
+ // Set container width
447
+ const container = document.querySelector('.gradio-container');
448
+ if (container) {
449
+ container.style.maxWidth = '85vw';
450
+ container.style.width = '85vw';
451
+ }
452
+
453
+ // Target the main row specifically
454
+ const mainRow = document.getElementById('main-row');
455
+ if (mainRow) {
456
+ mainRow.style.flexDirection = 'row';
457
+ mainRow.style.flexWrap = 'nowrap';
458
+ mainRow.style.display = 'flex';
459
+ mainRow.style.width = '100%';
460
+ }
461
+
462
+ // Force ALL rows to stay horizontal
463
+ const rows = document.querySelectorAll('.gradio-row');
464
+ rows.forEach(row => {
465
+ row.style.flexDirection = 'row';
466
+ row.style.flexWrap = 'nowrap';
467
+ row.style.display = 'flex';
468
+ });
469
+
470
+ // Target specific columns
471
+ const inputCol = document.getElementById('input-column');
472
+ if (inputCol) {
473
+ inputCol.style.width = '550px';
474
+ inputCol.style.minWidth = '550px';
475
+ inputCol.style.maxWidth = '550px';
476
+ inputCol.style.flex = '0 0 550px';
477
+ inputCol.style.display = 'inline-flex';
478
+ inputCol.style.flexDirection = 'column';
479
+ }
480
+
481
+ const outputCol = document.getElementById('output-column');
482
+ if (outputCol) {
483
+ outputCol.style.flex = '1 1 auto';
484
+ outputCol.style.minWidth = '0';
485
+ outputCol.style.display = 'inline-flex';
486
+ outputCol.style.flexDirection = 'column';
487
+ }
488
+
489
+ // Fallback: force all column children of rows
490
+ const columns = document.querySelectorAll('.gradio-row > .gradio-column, .gradio-row > div');
491
+ columns.forEach((col, index) => {
492
+ if (index === 0) {
493
+ col.style.width = '550px';
494
+ col.style.minWidth = '550px';
495
+ col.style.maxWidth = '550px';
496
+ col.style.flex = '0 0 550px';
497
+ } else if (index === 1) {
498
+ col.style.flex = '1 1 auto';
499
+ col.style.minWidth = '0';
500
+ }
501
+ col.style.display = 'inline-flex';
502
+ });
503
+ }
504
+
505
+ // Run immediately
506
+ forceHorizontalLayout();
507
+
508
+ // Run again after delays to override Gradio's dynamic changes
509
+ setTimeout(forceHorizontalLayout, 100);
510
+ setTimeout(forceHorizontalLayout, 500);
511
+ setTimeout(forceHorizontalLayout, 1000);
512
+ setTimeout(forceHorizontalLayout, 2000);
513
+
514
+ // Set up mutation observer to reapply on DOM changes
515
+ const observer = new MutationObserver(forceHorizontalLayout);
516
+ observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });
517
+ }
518
+ """
519
+
520
  @spaces.GPU(duration=75)
521
+ def infer(
522
+ prompt,
523
+ seed=42,
524
+ randomize_seed=False,
525
+ aspect_ratio="1:1 (1024x1024)",
526
+ guidance_scale=5.0,
527
+ num_inference_steps=50,
528
+ progress=gr.Progress(track_tqdm=True),
529
+ ):
530
+ """Generates an image using the Ovis-Image pipeline."""
531
+ if randomize_seed:
532
+ seed = random.randint(0, MAX_SEED)
533
+
534
+ width, height = get_image_size(aspect_ratio)
535
+
536
+ print(f'inference with prompt: {prompt}, size: {height}x{width}, seed: {seed}, steps: {num_inference_steps}, cfg: {guidance_scale}')
537
+
538
  generator = torch.Generator().manual_seed(seed)
539
  image = pipe(
540
+ prompt,
541
+ negative_prompt="",
542
+ height=height,
543
+ width=width,
544
+ num_inference_steps=num_inference_steps,
545
  true_cfg_scale=guidance_scale,
546
  generator=generator,
547
  ).images[0]
 
548
 
549
+ return image, seed
 
 
 
 
 
550
 
551
+ with gr.Blocks(
552
+ title="Ovis-Image",
553
+ fill_height=False,
554
+ theme=gr.themes.Soft(
555
+ primary_hue=gr.themes.colors.blue,
556
+ secondary_hue=gr.themes.colors.slate,
557
+ neutral_hue=gr.themes.colors.gray,
558
+ spacing_size=gr.themes.sizes.spacing_lg,
559
+ radius_size=gr.themes.sizes.radius_lg,
560
+ text_size=gr.themes.sizes.text_md,
561
+ font=[gr.themes.GoogleFont("Inter"), "SF Pro Display", "-apple-system", "BlinkMacSystemFont", "system-ui", "sans-serif"],
562
+ font_mono=[gr.themes.GoogleFont("JetBrains Mono"), "SF Mono", "ui-monospace", "monospace"],
563
+ ).set(
564
+ body_background_fill='#f5f5f7',
565
+ body_background_fill_dark='#000000',
566
+ button_primary_background_fill='#0071e3',
567
+ button_primary_background_fill_hover='#0077ed',
568
+ button_primary_text_color='#ffffff',
569
+ block_background_fill='#ffffff',
570
+ block_background_fill_dark='#1d1d1f',
571
+ block_border_width='0px',
572
+ block_shadow='0 2px 12px rgba(0, 0, 0, 0.08)',
573
+ block_shadow_dark='0 2px 12px rgba(0, 0, 0, 0.4)',
574
+ input_background_fill='#ffffff',
575
+ input_background_fill_dark='#1d1d1f',
576
+ input_border_width='1px',
577
+ input_border_color='#d2d2d7',
578
+ input_border_color_dark='#424245',
579
+ input_shadow='none',
580
+ input_shadow_focus='0 0 0 4px rgba(0, 113, 227, 0.15)',
581
+ ),
582
+ css=apple_css,
583
+ js=js_code,
584
+ ) as demo:
585
+ # Two-column layout - variant='panel' prevents responsive stacking
586
+ with gr.Row(equal_height=False, variant="panel", elem_id="main-row"):
587
+ # Left column - Input controls (fixed width)
588
+ with gr.Column(scale=0, min_width=550, elem_classes="input-section", elem_id="input-column"):
589
+ # Title above prompt box
590
+ gr.HTML("""
591
+ <div class="header-container">
592
+ <h1 class="main-title">Ovis-Image</h1>
593
+ </div>
594
+ """)
595
 
596
+ prompt = gr.Textbox(
597
+ placeholder="Describe the image you want to create...",
598
+ value=examples[0],
599
+ lines=7,
600
+ max_lines=7,
 
 
 
 
 
 
 
601
  label="Prompt",
602
+ show_label=True,
603
+ container=True,
604
+ autoscroll=False,
605
+ )
606
+
607
+ aspect_ratio = gr.Dropdown(
608
+ choices=[
609
+ "1:1 (1024x1024)",
610
+ "4:3 (1024x768)",
611
+ "3:4 (768x1024)",
612
+ "16:9 (1024x576)",
613
+ "9:16 (576x1024)",
614
+ ],
615
+ value="1:1 (1024x1024)",
616
+ label="Aspect Ratio",
617
+ show_label=True,
618
+ container=True,
619
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
620
 
621
+ run_button = gr.Button(
622
+ "Generate",
623
+ variant="primary",
624
+ size="lg",
625
+ elem_classes="primary"
626
+ )
627
+
628
+ # Hidden advanced settings (still functional but not visible)
629
  seed = gr.Slider(
630
  label="Seed",
631
  minimum=0,
632
  maximum=MAX_SEED,
633
  step=1,
634
+ value=0,
635
+ visible=False
636
+ )
637
+ randomize_seed = gr.Checkbox(label="Randomize seed", value=True, visible=False)
638
+ guidance_scale = gr.Slider(
639
+ label="Guidance scale",
640
+ minimum=0.0,
641
+ maximum=14.0,
642
+ step=0.1,
643
+ value=5.0,
644
+ visible=False
645
+ )
646
+ num_inference_steps = gr.Slider(
647
+ label="Number of inference steps",
648
+ minimum=1,
649
+ maximum=100,
650
+ step=1,
651
+ value=50,
652
+ visible=False
653
+ )
654
+
655
+ # Right column - Image output
656
+ with gr.Column(scale=2, elem_classes="output-section", elem_id="output-column"):
657
+ result = gr.Image(
658
+ label="Result",
659
+ show_label=False,
660
+ type="pil",
661
+ format="png",
662
  )
 
 
 
 
 
 
 
 
663
 
664
+ # Event handlers - using gr.on() like original Qwen-Image
665
  gr.on(
666
  triggers=[run_button.click, prompt.submit],
667
+ fn=infer,
668
+ inputs=[
669
+ prompt,
670
+ seed,
671
+ randomize_seed,
672
+ aspect_ratio,
673
+ guidance_scale,
674
+ num_inference_steps,
675
+ ],
676
+ outputs=[result, seed],
677
  )
678
 
679
  if __name__ == '__main__':
680
+ demo.launch()