prithivMLmods commited on
Commit
aee27da
·
verified ·
1 Parent(s): 8a4b4ee

— add index.html and serves it

Browse files
Files changed (1) hide show
  1. index.html +997 -0
index.html ADDED
@@ -0,0 +1,997 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Wan2.2-I2V-Fast</title>
7
+ <link href="https://fonts.googleapis.com/css2?family=Ubuntu:wght@300;400;500;700&display=swap" rel="stylesheet">
8
+ <style>
9
+ :root {
10
+ --ub-aubergine: #2C001E;
11
+ --ub-aubergine-dark: #1f0015;
12
+ --ub-orange: #E95420;
13
+ --ub-orange-hover: #c4461a;
14
+ --ub-panel: #3D3D3D;
15
+ --ub-border: rgba(255,255,255,0.1);
16
+ --ub-text: #FFFFFF;
17
+ --ub-muted: #b0b0b0;
18
+ --ub-input: #2b2b2b;
19
+ --panel-radius: 8px;
20
+ }
21
+ * { box-sizing:border-box; font-family:'Ubuntu',sans-serif; margin:0; padding:0; }
22
+ body {
23
+ background: var(--ub-aubergine);
24
+ color: var(--ub-text);
25
+ min-height: 100vh;
26
+ display: flex;
27
+ flex-direction: column;
28
+ }
29
+
30
+ /* ── TOPBAR ── */
31
+ .topbar {
32
+ background: var(--ub-aubergine-dark);
33
+ padding: 0 24px;
34
+ border-bottom: 1px solid var(--ub-border);
35
+ display: flex;
36
+ align-items: center;
37
+ justify-content: space-between;
38
+ height: 52px;
39
+ flex-shrink: 0;
40
+ }
41
+ .topbar-title {
42
+ font-weight: 700;
43
+ letter-spacing: .5px;
44
+ color: var(--ub-orange);
45
+ font-size: 15px;
46
+ }
47
+ .topbar-examples-btn {
48
+ display: flex;
49
+ align-items: center;
50
+ gap: 6px;
51
+ background: rgba(233,84,32,.12);
52
+ border: 1px solid rgba(233,84,32,.35);
53
+ color: var(--ub-orange);
54
+ padding: 6px 14px;
55
+ border-radius: 20px;
56
+ font-size: 12px;
57
+ font-weight: 600;
58
+ cursor: pointer;
59
+ transition: background .2s, border-color .2s, transform .15s;
60
+ font-family: 'Ubuntu', sans-serif;
61
+ letter-spacing: .3px;
62
+ white-space: nowrap;
63
+ }
64
+ .topbar-examples-btn:hover {
65
+ background: rgba(233,84,32,.22);
66
+ border-color: var(--ub-orange);
67
+ transform: translateY(-1px);
68
+ }
69
+ .btn-thumbs { display:flex; gap:3px; margin-right:2px; }
70
+ .btn-thumb {
71
+ width:20px; height:20px; border-radius:3px;
72
+ object-fit:cover; border:1px solid rgba(233,84,32,.4); display:block;
73
+ }
74
+ .topbar-right { display:flex; align-items:center; gap:10px; }
75
+ .topbar-github-btn {
76
+ display: flex;
77
+ align-items: center;
78
+ gap: 6px;
79
+ background: rgba(255,255,255,.06);
80
+ border: 1px solid rgba(255,255,255,.18);
81
+ color: #e0e0e0;
82
+ padding: 6px 13px;
83
+ border-radius: 20px;
84
+ font-size: 12px;
85
+ font-weight: 600;
86
+ cursor: pointer;
87
+ text-decoration: none;
88
+ transition: background .2s, border-color .2s, color .2s, transform .15s;
89
+ font-family: 'Ubuntu', sans-serif;
90
+ letter-spacing: .3px;
91
+ white-space: nowrap;
92
+ }
93
+ .topbar-github-btn:hover {
94
+ background: rgba(255,255,255,.13);
95
+ border-color: rgba(255,255,255,.4);
96
+ color: #ffffff;
97
+ transform: translateY(-1px);
98
+ }
99
+ .topbar-github-btn svg { flex-shrink:0; }
100
+
101
+ /* ── CONTAINER ── */
102
+ .container {
103
+ max-width: 1300px;
104
+ margin: 0 auto;
105
+ padding: 30px 20px;
106
+ flex: 1;
107
+ width: 100%;
108
+ }
109
+ .header-text { text-align:center; margin-bottom:30px; }
110
+ .header-text h1 { font-size:2.2rem; margin-bottom:10px; }
111
+ .header-text p { color:var(--ub-muted); font-size:14px; line-height:1.6; }
112
+ .badge {
113
+ display:inline-block;
114
+ background:rgba(233,84,32,.15);
115
+ border:1px solid rgba(233,84,32,.4);
116
+ color:var(--ub-orange);
117
+ font-size:12px; padding:3px 10px;
118
+ border-radius:20px; margin-top:8px; font-weight:500;
119
+ }
120
+
121
+ /* ── MAIN LAYOUT ── */
122
+ .layout {
123
+ display: grid;
124
+ grid-template-columns: 440px 1fr;
125
+ gap: 24px;
126
+ align-items: stretch;
127
+ height: 720px;
128
+ }
129
+ .panel {
130
+ background: var(--ub-panel);
131
+ border-radius: var(--panel-radius);
132
+ box-shadow: 0 8px 24px rgba(0,0,0,.25);
133
+ display: flex;
134
+ flex-direction: column;
135
+ overflow: hidden;
136
+ height: 100%;
137
+ }
138
+ .panel-header {
139
+ padding: 16px 20px;
140
+ background: rgba(0,0,0,.2);
141
+ border-bottom: 1px solid var(--ub-border);
142
+ font-weight: 500;
143
+ font-size: 1.05rem;
144
+ flex-shrink: 0;
145
+ display: flex;
146
+ justify-content: space-between;
147
+ align-items: center;
148
+ }
149
+ .panel-body-scroll {
150
+ flex: 1;
151
+ padding: 20px;
152
+ overflow-y: auto;
153
+ display: flex;
154
+ flex-direction: column;
155
+ }
156
+
157
+ /* ── FORMS ── */
158
+ .form-group { margin-bottom:18px; flex-shrink:0; }
159
+ .label {
160
+ display:block; font-weight:500; font-size:13px;
161
+ color:var(--ub-muted); margin-bottom:7px;
162
+ text-transform:uppercase; letter-spacing:.5px;
163
+ }
164
+ .textarea, .input {
165
+ width:100%;
166
+ background:var(--ub-input);
167
+ border:1px solid var(--ub-border);
168
+ color:var(--ub-text);
169
+ padding:11px 12px; border-radius:4px;
170
+ outline:none; font-size:14px;
171
+ font-family:'Ubuntu',sans-serif;
172
+ transition:border-color .2s;
173
+ }
174
+ .textarea:focus, .input:focus { border-color:var(--ub-orange); }
175
+ .textarea { min-height:80px; resize:vertical; line-height:1.5; }
176
+
177
+ /* ── UPLOAD ZONE ── */
178
+ .upload-zone {
179
+ background:var(--ub-input);
180
+ border:1.5px dashed rgba(255,255,255,.2);
181
+ border-radius:4px;
182
+ cursor:pointer;
183
+ transition:background .2s, border-color .2s;
184
+ position:relative; overflow:hidden;
185
+ aspect-ratio:16/9;
186
+ display:flex; align-items:center; justify-content:center;
187
+ }
188
+ .upload-zone:hover, .upload-zone.dragover {
189
+ border-color:var(--ub-orange);
190
+ background:rgba(233,84,32,.05);
191
+ }
192
+ .upload-zone input[type="file"] { display:none; }
193
+ .upload-placeholder {
194
+ display:flex; flex-direction:column;
195
+ align-items:center; gap:10px;
196
+ color:var(--ub-muted); pointer-events:none;
197
+ }
198
+ .upload-placeholder svg { opacity:.5; }
199
+ .upload-placeholder span { font-size:13px; }
200
+ .preview-img {
201
+ position:absolute; inset:0;
202
+ width:100%; height:100%;
203
+ object-fit:contain; display:none; background:#111;
204
+ }
205
+ .remove-img-btn {
206
+ position:absolute; top:8px; right:8px;
207
+ background:rgba(0,0,0,.7); border:none; color:white;
208
+ border-radius:50%; width:26px; height:26px;
209
+ display:none; align-items:center; justify-content:center;
210
+ cursor:pointer; font-size:14px; z-index:5;
211
+ transition:background .2s;
212
+ }
213
+ .remove-img-btn:hover { background:var(--ub-orange); }
214
+
215
+ /* ── SLIDER INPUT ── */
216
+ .slider-wrap { display:flex; align-items:center; gap:12px; }
217
+ .slider-wrap input[type="range"] {
218
+ flex:1; -webkit-appearance:none;
219
+ height:4px; background:var(--ub-input);
220
+ border-radius:2px; outline:none; cursor:pointer;
221
+ border:1px solid var(--ub-border);
222
+ }
223
+ .slider-wrap input[type="range"]::-webkit-slider-thumb {
224
+ -webkit-appearance:none;
225
+ width:16px; height:16px; border-radius:50%;
226
+ background:var(--ub-orange); cursor:pointer;
227
+ }
228
+ .slider-val {
229
+ min-width:40px; text-align:right;
230
+ font-size:13px; color:var(--ub-text); font-weight:500;
231
+ }
232
+
233
+ /* ── ADVANCED ACCORDION ── */
234
+ .advanced-toggle {
235
+ width:100%; background:none;
236
+ border:none; border-top:1px solid var(--ub-border);
237
+ color:var(--ub-orange); text-align:left;
238
+ padding:12px 0; font-weight:500; font-size:13px;
239
+ cursor:pointer; display:flex;
240
+ justify-content:space-between; align-items:center;
241
+ flex-shrink:0; margin-top:4px;
242
+ }
243
+ .adv-icon { font-weight:bold; font-size:18px; line-height:1; }
244
+ .advanced-body { display:none; padding-top:12px; flex-shrink:0; }
245
+ .advanced-body.open { display:block; }
246
+ .grid-2 { display:grid; grid-template-columns:1fr 1fr; gap:14px; }
247
+
248
+ /* ── STATUS LOG ── */
249
+ .status-container {
250
+ margin-top:16px; margin-bottom:16px;
251
+ border:1px solid var(--ub-border);
252
+ border-radius:4px; background:#200014;
253
+ display:flex; flex-direction:column;
254
+ flex:1; min-height:80px; max-height:150px;
255
+ }
256
+ .status-header {
257
+ padding:7px 12px; font-size:10px; font-weight:700;
258
+ color:var(--ub-muted); background:rgba(0,0,0,.4);
259
+ border-bottom:1px solid var(--ub-border);
260
+ text-transform:uppercase; letter-spacing:.6px; flex-shrink:0;
261
+ }
262
+ .status-log {
263
+ padding:8px 10px;
264
+ font-family:'Courier New',Courier,monospace;
265
+ font-size:11.5px; color:#eee; overflow-y:auto;
266
+ flex:1; display:flex; flex-direction:column; gap:3px;
267
+ }
268
+ .log-time { color:#666; margin-right:7px; }
269
+ .log-info { color:#5bc0eb; }
270
+ .log-success { color:#9bc53d; }
271
+ .log-error { color:#ff5e5b; }
272
+ .log-warn { color:#f4c842; }
273
+
274
+ /* ── BUTTONS ── */
275
+ .btn {
276
+ width:100%; padding:13px; border:none; border-radius:4px;
277
+ font-size:15px; font-weight:700; cursor:pointer;
278
+ transition:opacity .2s, background .2s; flex-shrink:0;
279
+ letter-spacing:.3px; font-family:'Ubuntu',sans-serif;
280
+ }
281
+ .btn-primary {
282
+ background:var(--ub-orange); color:white;
283
+ box-shadow:0 4px 14px rgba(233,84,32,.3);
284
+ }
285
+ .btn-primary:hover { background:var(--ub-orange-hover); }
286
+ .btn:disabled { opacity:.55; cursor:not-allowed; }
287
+
288
+ .action-icon {
289
+ display:none; background:none; border:none;
290
+ color:var(--ub-muted); cursor:pointer; padding:4px;
291
+ transition:color .2s;
292
+ }
293
+ .action-icon:hover { color:var(--ub-orange); }
294
+
295
+ /* ── OUTPUT PANEL ── */
296
+ .panel-body-output {
297
+ flex:1; display:flex; flex-direction:column;
298
+ padding:0; position:relative;
299
+ }
300
+ .output-stage {
301
+ position:absolute; top:0; left:0; right:0; bottom:0;
302
+ background:#0d0008; overflow:hidden;
303
+ display:flex; align-items:center; justify-content:center;
304
+ }
305
+ .output-empty { color:var(--ub-muted); text-align:center; z-index:1; }
306
+ .output-empty svg { opacity:.3; margin-bottom:12px; }
307
+ .output-empty div { font-size:14px; }
308
+ .output-video {
309
+ position:absolute; top:0; left:0;
310
+ width:100%; height:100%;
311
+ object-fit:contain; display:none; background:#000;
312
+ }
313
+
314
+ /* ── LOADER ── */
315
+ .loader {
316
+ position:absolute; inset:0;
317
+ background:rgba(15,0,10,.78);
318
+ backdrop-filter:blur(6px);
319
+ display:none; flex-direction:column;
320
+ align-items:center; justify-content:center; z-index:20;
321
+ }
322
+ .spinner-single {
323
+ width:52px; height:52px;
324
+ border:3px solid rgba(255,255,255,.08);
325
+ border-top-color:var(--ub-orange);
326
+ border-radius:50%;
327
+ animation:spin 1s cubic-bezier(.4,0,.2,1) infinite;
328
+ margin-bottom:18px;
329
+ }
330
+ .loader-text {
331
+ font-weight:500; font-size:14px; color:#fff;
332
+ letter-spacing:1px;
333
+ animation:pulse 1.5s ease-in-out infinite;
334
+ }
335
+ .loader-sub { font-size:12px; color:var(--ub-muted); margin-top:6px; }
336
+ @keyframes pulse { 0%,100%{opacity:1} 50%{opacity:.45} }
337
+ @keyframes spin { to{transform:rotate(360deg)} }
338
+
339
+ .seed-badge {
340
+ display:none; position:absolute; bottom:12px; right:12px;
341
+ background:rgba(0,0,0,.65); color:#ccc;
342
+ padding:5px 10px; border-radius:20px;
343
+ font-size:12px; backdrop-filter:blur(4px); z-index:5;
344
+ }
345
+ .checkbox-label {
346
+ display:flex; align-items:center; gap:8px;
347
+ font-size:14px; color:var(--ub-text); cursor:pointer;
348
+ }
349
+ .checkbox-label input {
350
+ cursor:pointer; accent-color:var(--ub-orange); width:15px; height:15px;
351
+ }
352
+
353
+ /* ── FRAME UPSCALE SECTION ── */
354
+ .frames-section { margin-top:36px; }
355
+ .frames-section-header {
356
+ display:flex; align-items:center; gap:12px;
357
+ border-bottom:1px solid var(--ub-border);
358
+ padding-bottom:12px; margin-bottom:20px;
359
+ }
360
+ .frames-section-header h3 { font-size:1.1rem; color:var(--ub-text); }
361
+ .frames-badge {
362
+ background:rgba(155,197,61,.15);
363
+ border:1px solid rgba(155,197,61,.35);
364
+ color:#9bc53d;
365
+ font-size:11px; font-weight:600;
366
+ padding:3px 9px; border-radius:12px;
367
+ }
368
+ .frames-grid {
369
+ display:grid;
370
+ grid-template-columns:repeat(auto-fill,minmax(320px,1fr));
371
+ gap:22px;
372
+ }
373
+
374
+ /* ── COMPARISON CARD ── */
375
+ .cmp-card {
376
+ background:var(--ub-panel);
377
+ border-radius:var(--panel-radius);
378
+ overflow:hidden;
379
+ border:1px solid var(--ub-border);
380
+ box-shadow:0 4px 16px rgba(0,0,0,.25);
381
+ }
382
+ .cmp-card-header {
383
+ display:flex; align-items:center;
384
+ justify-content:space-between;
385
+ padding:10px 14px;
386
+ background:rgba(0,0,0,.25);
387
+ border-bottom:1px solid var(--ub-border);
388
+ font-size:13px; font-weight:500;
389
+ }
390
+ .cmp-label { color:var(--ub-text); }
391
+ .cmp-dl-btn {
392
+ display:flex; align-items:center; gap:5px;
393
+ background:rgba(233,84,32,.15);
394
+ border:1px solid rgba(233,84,32,.35);
395
+ color:var(--ub-orange);
396
+ padding:4px 10px; border-radius:12px;
397
+ font-size:11px; font-weight:600;
398
+ cursor:pointer; transition:background .2s;
399
+ font-family:'Ubuntu',sans-serif;
400
+ }
401
+ .cmp-dl-btn:hover { background:rgba(233,84,32,.3); }
402
+
403
+ /* ── SLIDER COMPARE ── */
404
+ .cmp-stage {
405
+ position:relative;
406
+ width:100%;
407
+ aspect-ratio:16/9;
408
+ overflow:hidden;
409
+ cursor:ew-resize;
410
+ user-select:none;
411
+ background:#111;
412
+ }
413
+ .cmp-img {
414
+ position:absolute; top:0; left:0;
415
+ width:100%; height:100%;
416
+ object-fit:contain;
417
+ }
418
+ .cmp-img-before { z-index:1; }
419
+ .cmp-img-after {
420
+ z-index:2;
421
+ clip-path:inset(0 50% 0 0);
422
+ }
423
+ .cmp-divider {
424
+ position:absolute; top:0; bottom:0;
425
+ left:50%;
426
+ width:2px;
427
+ background:var(--ub-orange);
428
+ z-index:10;
429
+ pointer-events:none;
430
+ }
431
+ .cmp-handle {
432
+ position:absolute; top:50%;
433
+ left:50%;
434
+ transform:translate(-50%,-50%);
435
+ width:32px; height:32px;
436
+ border-radius:50%;
437
+ background:var(--ub-orange);
438
+ z-index:11;
439
+ display:flex; align-items:center; justify-content:center;
440
+ pointer-events:none;
441
+ box-shadow:0 2px 8px rgba(0,0,0,.5);
442
+ }
443
+ .cmp-handle svg { flex-shrink:0; }
444
+ .cmp-pill {
445
+ position:absolute; bottom:10px;
446
+ background:rgba(0,0,0,.65);
447
+ color:#fff; font-size:10px; font-weight:700;
448
+ padding:3px 8px; border-radius:10px;
449
+ z-index:12; pointer-events:none;
450
+ backdrop-filter:blur(3px);
451
+ letter-spacing:.4px;
452
+ }
453
+ .cmp-pill-left { left:10px; }
454
+ .cmp-pill-right { right:10px; color:#9bc53d; }
455
+
456
+ /* ── EXAMPLES SECTION ── */
457
+ .examples-section { margin-top:40px; scroll-margin-top:20px; }
458
+ .examples-section-header {
459
+ display:flex; align-items:center; justify-content:space-between;
460
+ border-bottom:1px solid var(--ub-border);
461
+ padding-bottom:12px; margin-bottom:20px;
462
+ }
463
+ .examples-section-header h3 { font-size:1.1rem; color:var(--ub-text); }
464
+ .examples-grid {
465
+ display:grid;
466
+ grid-template-columns:repeat(auto-fill,minmax(260px,1fr));
467
+ gap:18px;
468
+ }
469
+ .ex-card {
470
+ background:var(--ub-panel); border-radius:6px;
471
+ overflow:hidden; cursor:pointer;
472
+ transition:transform .2s, box-shadow .2s, border-color .2s;
473
+ border:1px solid var(--ub-border);
474
+ }
475
+ .ex-card:hover {
476
+ transform:translateY(-4px);
477
+ box-shadow:0 10px 28px rgba(0,0,0,.4);
478
+ border-color:rgba(233,84,32,.5);
479
+ }
480
+ .ex-img-wrap {
481
+ width:100%; aspect-ratio:16/9;
482
+ background:#1a001a; overflow:hidden; position:relative;
483
+ }
484
+ .ex-img-wrap.loading::before {
485
+ content:''; position:absolute; inset:0;
486
+ background:linear-gradient(90deg,#2c001e 25%,#3d1030 50%,#2c001e 75%);
487
+ background-size:200% 100%;
488
+ animation:shimmer 1.5s infinite; z-index:1;
489
+ }
490
+ @keyframes shimmer {
491
+ 0%{background-position:-200% 0} 100%{background-position:200% 0}
492
+ }
493
+ .ex-img-wrap img {
494
+ width:100%; height:100%; object-fit:cover; display:block;
495
+ transition:transform .3s; position:relative; z-index:2;
496
+ }
497
+ .ex-card:hover .ex-img-wrap img { transform:scale(1.04); }
498
+ .ex-use-badge {
499
+ position:absolute; top:8px; left:8px; z-index:3;
500
+ background:var(--ub-orange); color:white;
501
+ font-size:10px; font-weight:700; padding:3px 8px; border-radius:10px;
502
+ opacity:0; transition:opacity .2s; pointer-events:none;
503
+ }
504
+ .ex-card:hover .ex-use-badge { opacity:1; }
505
+ .ex-card p {
506
+ padding:10px 12px 13px; font-size:12px;
507
+ color:var(--ub-muted); line-height:1.5;
508
+ display:-webkit-box; -webkit-line-clamp:3;
509
+ -webkit-box-orient:vertical; overflow:hidden;
510
+ }
511
+
512
+ @media(max-width:900px) {
513
+ .layout { grid-template-columns:1fr; height:auto; }
514
+ .panel-body-output { height:380px; flex:none; }
515
+ .output-stage { position:relative; height:100%; }
516
+ .btn-thumbs { display:none; }
517
+ .frames-grid { grid-template-columns:1fr; }
518
+ }
519
+ </style>
520
+ </head>
521
+ <body>
522
+
523
+ <div class="topbar">
524
+ <span class="topbar-title">Wan2.2-I2V-Fast — Lightning LoRA · FP8 · AoT Compiled</span>
525
+ <div class="topbar-right">
526
+
527
+ <a class="topbar-github-btn"
528
+ href="https://github.com/PRITHIVSAKTHIUR/wan2.2-i2v-fast"
529
+ target="_blank" rel="noopener noreferrer" title="View source on GitHub">
530
+ <svg width="15" height="15" fill="currentColor" viewBox="0 0 24 24">
531
+ <path d="M12 0C5.37 0 0 5.37 0 12c0 5.31 3.435 9.795 8.205 11.385
532
+ .6.105.825-.255.825-.57 0-.285-.015-1.23-.015-2.235
533
+ -3.015.555-3.795-.735-4.035-1.41-.135-.345-.72-1.41-1.23-1.695
534
+ -.42-.225-1.02-.78-.015-.795.945-.015 1.62.87 1.845 1.23
535
+ 1.08 1.815 2.805 1.305 3.495.99.105-.78.42-1.305.765-1.605
536
+ -2.67-.3-5.46-1.335-5.46-5.925 0-1.305.465-2.385 1.23-3.225
537
+ -.12-.3-.54-1.53.12-3.18 0 0 1.005-.315 3.3 1.23
538
+ .96-.27 1.98-.405 3-.405s2.04.135 3 .405c2.295-1.56 3.3-1.23
539
+ 3.3-1.23.66 1.65.24 2.88.12 3.18.765.84 1.23 1.905 1.23 3.225
540
+ 0 4.605-2.805 5.625-5.475 5.925.435.375.81 1.095.81 2.22
541
+ 0 1.605-.015 2.895-.015 3.3 0 .315.225.69.825.57
542
+ A12.02 12.02 0 0 0 24 12c0-6.63-5.37-12-12-12z"/>
543
+ </svg>
544
+ GitHub
545
+ </a>
546
+
547
+ <button class="topbar-examples-btn" id="examplesScrollBtn" title="Browse examples">
548
+ <div class="btn-thumbs" id="btnThumbs"></div>
549
+ <svg width="14" height="14" fill="none" stroke="currentColor" stroke-width="2"
550
+ stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 24 24" style="margin-right:4px;">
551
+ <rect x="3" y="3" width="7" height="7" rx="1"></rect>
552
+ <rect x="14" y="3" width="7" height="7" rx="1"></rect>
553
+ <rect x="3" y="14" width="7" height="7" rx="1"></rect>
554
+ <rect x="14" y="14" width="7" height="7" rx="1"></rect>
555
+ </svg>
556
+ Examples
557
+ </button>
558
+
559
+ </div>
560
+ </div>
561
+
562
+ <div class="container">
563
+ <div class="header-text">
564
+ <h1>Wan2.2 Image-to-Video</h1>
565
+ <p>Upload an image and describe the animation. Generates video in 4–8 steps with Lightning LoRA.<br>
566
+ Frames are automatically extracted &amp; upscaled with RealESRGAN ×2.</p>
567
+ <span class="badge">14B · FP8 Quantized · ZeroGPU · RealESRGAN Upscale</span>
568
+ </div>
569
+
570
+ <div class="layout">
571
+ <!-- Left: Settings -->
572
+ <div class="panel">
573
+ <div class="panel-header">Settings</div>
574
+ <div class="panel-body-scroll">
575
+
576
+ <div class="form-group">
577
+ <label class="label">Input Image</label>
578
+ <div class="upload-zone" id="dropZone">
579
+ <input type="file" id="fileInput" accept="image/*" />
580
+ <div class="upload-placeholder" id="uploadPlaceholder">
581
+ <svg width="40" height="40" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24">
582
+ <path d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
583
+ </svg>
584
+ <span>Click or drag &amp; drop an image</span>
585
+ </div>
586
+ <img id="previewImg" class="preview-img" alt="Preview" />
587
+ <button id="removeImgBtn" class="remove-img-btn" title="Remove">×</button>
588
+ </div>
589
+ </div>
590
+
591
+ <div class="form-group">
592
+ <label class="label">Prompt</label>
593
+ <textarea id="promptInput" class="textarea" placeholder="Describe the animation...">[[DEFAULT_PROMPT]]</textarea>
594
+ </div>
595
+
596
+ <div class="form-group">
597
+ <label class="label">Duration — <span id="durationVal">3.5</span>s</label>
598
+ <div class="slider-wrap">
599
+ <input type="range" id="duration"
600
+ min="[[MIN_DURATION]]" max="[[MAX_DURATION]]"
601
+ step="0.1" value="3.5"
602
+ oninput="document.getElementById('durationVal').textContent=parseFloat(this.value).toFixed(1)">
603
+ <span class="slider-val">[[MIN_DURATION]]–[[MAX_DURATION]]s</span>
604
+ </div>
605
+ </div>
606
+
607
+ <button class="advanced-toggle" id="advToggle">
608
+ <span>Advanced Settings</span>
609
+ <span class="adv-icon" id="advIcon">+</span>
610
+ </button>
611
+ <div class="advanced-body" id="advBody">
612
+ <div class="form-group">
613
+ <label class="label">Negative Prompt</label>
614
+ <textarea id="negPrompt" class="textarea" style="min-height:60px;font-size:12px;">[[DEFAULT_NEG]]</textarea>
615
+ </div>
616
+ <div class="grid-2">
617
+ <div class="form-group">
618
+ <label class="label">Steps</label>
619
+ <input type="number" id="steps" class="input" value="4" min="1" max="30">
620
+ </div>
621
+ <div class="form-group">
622
+ <label class="label">Seed</label>
623
+ <input type="number" id="seed" class="input" value="42">
624
+ </div>
625
+ <div class="form-group">
626
+ <label class="label">Guidance (Stage 1)</label>
627
+ <input type="number" id="guidance" class="input" value="1.0" step="0.5" min="0" max="10">
628
+ </div>
629
+ <div class="form-group">
630
+ <label class="label">Guidance (Stage 2)</label>
631
+ <input type="number" id="guidance2" class="input" value="1.0" step="0.5" min="0" max="10">
632
+ </div>
633
+ <div class="form-group" style="grid-column:span 2">
634
+ <label class="checkbox-label">
635
+ <input type="checkbox" id="randomize" checked> Randomize Seed
636
+ </label>
637
+ </div>
638
+ </div>
639
+ </div>
640
+
641
+ <div class="status-container">
642
+ <div class="status-header">Execution Log</div>
643
+ <div class="status-log" id="statusLog">
644
+ <div><span class="log-time">[[[DEVICE_LABEL]]]</span><span>System ready...</span></div>
645
+ </div>
646
+ </div>
647
+
648
+ <button class="btn btn-primary" id="runBtn">Generate Video</button>
649
+ </div>
650
+ </div>
651
+
652
+ <!-- Right: Output -->
653
+ <div class="panel">
654
+ <div class="panel-header">
655
+ <span>Output</span>
656
+ <button id="downloadBtn" class="action-icon" title="Download Video">
657
+ <svg width="22" height="22" fill="none" stroke="currentColor" stroke-width="2"
658
+ stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 24 24">
659
+ <path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4"></path>
660
+ <polyline points="7 10 12 15 17 10"></polyline>
661
+ <line x1="12" y1="15" x2="12" y2="3"></line>
662
+ </svg>
663
+ </button>
664
+ </div>
665
+ <div class="panel-body-output">
666
+ <div class="output-stage" id="outputStage">
667
+ <div class="output-empty" id="outputEmpty">
668
+ <svg width="52" height="52" fill="none" stroke="currentColor" stroke-width="1.2" viewBox="0 0 24 24">
669
+ <polygon points="23 7 16 12 23 17 23 7"></polygon>
670
+ <rect x="1" y="5" width="15" height="14" rx="2" ry="2"></rect>
671
+ </svg>
672
+ <div>Video will appear here</div>
673
+ </div>
674
+ <video id="outputVideo" class="output-video" controls autoplay loop></video>
675
+ <div class="seed-badge" id="seedBadge"></div>
676
+ <div class="loader" id="loader">
677
+ <div class="spinner-single"></div>
678
+ <div class="loader-text">Generating video + upscaling frames...</div>
679
+ <div class="loader-sub">This may take a minute</div>
680
+ </div>
681
+ </div>
682
+ </div>
683
+ </div>
684
+ </div>
685
+
686
+ <!-- Frame Upscale Results -->
687
+ <div class="frames-section" id="framesSection" style="display:none;">
688
+ <div class="frames-section-header">
689
+ <h3>Frame Upscale Results</h3>
690
+ <span class="frames-badge" id="framesBadge">RealESRGAN ×2</span>
691
+ </div>
692
+ <div class="frames-grid" id="framesGrid"></div>
693
+ </div>
694
+
695
+ <!-- Examples -->
696
+ <div class="examples-section" id="examplesSection">
697
+ <div class="examples-section-header">
698
+ <h3>Examples</h3>
699
+ </div>
700
+ <div class="examples-grid" id="examplesGrid"></div>
701
+ </div>
702
+ </div>
703
+
704
+ <script>
705
+ // ---------------------------------------------------------------------------
706
+ // Runtime constants injected by Python at serve time
707
+ // ---------------------------------------------------------------------------
708
+ const examples = [[EXAMPLES_JSON]];
709
+
710
+ // ---------------------------------------------------------------------------
711
+ // State & DOM refs
712
+ // ---------------------------------------------------------------------------
713
+ let currentFile = null;
714
+ let currentFilename = "";
715
+
716
+ const dropZone = document.getElementById('dropZone');
717
+ const fileInput = document.getElementById('fileInput');
718
+ const uploadPlaceholder = document.getElementById('uploadPlaceholder');
719
+ const previewImg = document.getElementById('previewImg');
720
+ const removeImgBtn = document.getElementById('removeImgBtn');
721
+ const promptInput = document.getElementById('promptInput');
722
+ const runBtn = document.getElementById('runBtn');
723
+ const downloadBtn = document.getElementById('downloadBtn');
724
+ const statusLog = document.getElementById('statusLog');
725
+ const outputVideo = document.getElementById('outputVideo');
726
+ const outputEmpty = document.getElementById('outputEmpty');
727
+ const loader = document.getElementById('loader');
728
+ const seedBadge = document.getElementById('seedBadge');
729
+ const framesSection = document.getElementById('framesSection');
730
+ const framesGrid = document.getElementById('framesGrid');
731
+ const examplesSection = document.getElementById('examplesSection');
732
+
733
+ // ── Scroll to examples ──
734
+ document.getElementById('examplesScrollBtn').onclick = () =>
735
+ examplesSection.scrollIntoView({behavior:'smooth', block:'start'});
736
+
737
+ // ── Topbar thumbnails ──
738
+ const btnThumbs = document.getElementById('btnThumbs');
739
+ examples.slice(0, 3).forEach(ex => {
740
+ const img = document.createElement('img');
741
+ img.src = ex.url; img.className = 'btn-thumb'; img.alt = '';
742
+ img.onerror = () => { img.style.display = 'none'; };
743
+ btnThumbs.appendChild(img);
744
+ });
745
+
746
+ // ── Log helper ──
747
+ function logMsg(msg, cls = '') {
748
+ const d = document.createElement('div');
749
+ const t = new Date().toLocaleTimeString('en-US', {hour12: false});
750
+ d.innerHTML = `<span class="log-time">[${t}]</span><span class="${cls}">${msg}</span>`;
751
+ statusLog.appendChild(d);
752
+ statusLog.scrollTop = statusLog.scrollHeight;
753
+ }
754
+
755
+ // ── Advanced accordion ──
756
+ document.getElementById('advToggle').onclick = function () {
757
+ const b = document.getElementById('advBody');
758
+ b.classList.toggle('open');
759
+ document.getElementById('advIcon').innerText = b.classList.contains('open') ? '−' : '+';
760
+ };
761
+
762
+ // ── Image upload ──
763
+ function setImage(file) {
764
+ if (!file) return;
765
+ currentFile = file;
766
+ previewImg.src = URL.createObjectURL(file);
767
+ previewImg.style.display = 'block';
768
+ uploadPlaceholder.style.display = 'none';
769
+ removeImgBtn.style.display = 'flex';
770
+ logMsg(`Image loaded: ${file.name}`, 'log-info');
771
+ }
772
+
773
+ function clearImage() {
774
+ currentFile = null;
775
+ previewImg.src = '';
776
+ previewImg.style.display = 'none';
777
+ uploadPlaceholder.style.display = 'flex';
778
+ removeImgBtn.style.display = 'none';
779
+ fileInput.value = '';
780
+ }
781
+
782
+ dropZone.onclick = e => {
783
+ if (e.target === dropZone || e.target === uploadPlaceholder || uploadPlaceholder.contains(e.target))
784
+ fileInput.click();
785
+ };
786
+ fileInput.onchange = e => { if (e.target.files[0]) setImage(e.target.files[0]); fileInput.value = ''; };
787
+ dropZone.ondragover = e => { e.preventDefault(); dropZone.classList.add('dragover'); };
788
+ dropZone.ondragleave = () => dropZone.classList.remove('dragover');
789
+ dropZone.ondrop = e => {
790
+ e.preventDefault(); dropZone.classList.remove('dragover');
791
+ if (e.dataTransfer.files[0]) setImage(e.dataTransfer.files[0]);
792
+ };
793
+ removeImgBtn.onclick = e => { e.stopPropagation(); clearImage(); };
794
+
795
+ // ── Download video ──
796
+ downloadBtn.onclick = () => {
797
+ if (!currentFilename) return;
798
+ const a = document.createElement('a');
799
+ a.href = `/download/${currentFilename}`;
800
+ a.download = currentFilename;
801
+ a.click();
802
+ };
803
+
804
+ // ── Comparison slider logic ──
805
+ function initCompare(stage) {
806
+ const after = stage.querySelector('.cmp-img-after');
807
+ const divider = stage.querySelector('.cmp-divider');
808
+ const handle = stage.querySelector('.cmp-handle');
809
+ let dragging = false;
810
+
811
+ function setPos(x) {
812
+ const rect = stage.getBoundingClientRect();
813
+ const pct = Math.max(0, Math.min(1, (x - rect.left) / rect.width));
814
+ const pctPx = (pct * 100).toFixed(2);
815
+ after.style.clipPath = `inset(0 ${(100 - pct * 100).toFixed(2)}% 0 0)`;
816
+ divider.style.left = pctPx + '%';
817
+ handle.style.left = pctPx + '%';
818
+ }
819
+
820
+ stage.addEventListener('mousedown', e => { dragging = true; setPos(e.clientX); });
821
+ window.addEventListener('mouseup', () => { dragging = false; });
822
+ window.addEventListener('mousemove', e => { if (dragging) setPos(e.clientX); });
823
+
824
+ stage.addEventListener('touchstart', e => { dragging = true; setPos(e.touches[0].clientX); }, {passive: true});
825
+ window.addEventListener('touchend', () => { dragging = false; });
826
+ window.addEventListener('touchmove', e => { if (dragging) setPos(e.touches[0].clientX); }, {passive: true});
827
+ }
828
+
829
+ // ── Build frame comparison cards ──
830
+ function buildFrameCards(frameResults) {
831
+ framesGrid.innerHTML = '';
832
+ if (!frameResults || frameResults.length === 0) return;
833
+
834
+ framesSection.style.display = 'block';
835
+ document.getElementById('framesBadge').textContent =
836
+ `RealESRGAN ×2 — ${frameResults.length} frame${frameResults.length > 1 ? 's' : ''}`;
837
+
838
+ frameResults.forEach(fr => {
839
+ const card = document.createElement('div');
840
+ card.className = 'cmp-card';
841
+
842
+ const hdr = document.createElement('div');
843
+ hdr.className = 'cmp-card-header';
844
+ hdr.innerHTML = `
845
+ <span class="cmp-label">${fr.label}</span>
846
+ <button class="cmp-dl-btn" onclick="dlFrame('${fr.upscaled_file}')">
847
+ <svg width="12" height="12" fill="none" stroke="currentColor" stroke-width="2.5"
848
+ stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 24 24">
849
+ <path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4"></path>
850
+ <polyline points="7 10 12 15 17 10"></polyline>
851
+ <line x1="12" y1="15" x2="12" y2="3"></line>
852
+ </svg>
853
+ Download upscaled
854
+ </button>`;
855
+
856
+ const stage = document.createElement('div');
857
+ stage.className = 'cmp-stage';
858
+ stage.innerHTML = `
859
+ <img class="cmp-img cmp-img-before" src="${fr.orig_url}" alt="Original">
860
+ <img class="cmp-img cmp-img-after" src="${fr.upscaled_url}" alt="Upscaled">
861
+ <div class="cmp-divider"></div>
862
+ <div class="cmp-handle">
863
+ <svg width="16" height="16" fill="none" stroke="white" stroke-width="2.5"
864
+ stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 24 24">
865
+ <polyline points="15 18 9 12 15 6"></polyline>
866
+ <polyline points="9 18 3 12 9 6" style="transform:translateX(6px)"></polyline>
867
+ </svg>
868
+ </div>
869
+ <span class="cmp-pill cmp-pill-left">ORIGINAL</span>
870
+ <span class="cmp-pill cmp-pill-right">UPSCALED ×2</span>`;
871
+
872
+ card.appendChild(hdr);
873
+ card.appendChild(stage);
874
+ framesGrid.appendChild(card);
875
+ initCompare(stage);
876
+ });
877
+ }
878
+
879
+ function dlFrame(filename) {
880
+ const a = document.createElement('a');
881
+ a.href = `/download-frame/${filename}`;
882
+ a.download = filename;
883
+ a.click();
884
+ }
885
+
886
+ // ── Generate ──
887
+ runBtn.onclick = async () => {
888
+ const prompt = promptInput.value.trim();
889
+ if (!prompt) { logMsg('Prompt is required.', 'log-error'); return; }
890
+ if (!currentFile) { logMsg('Please upload an input image first.', 'log-error'); return; }
891
+
892
+ logMsg('Initializing generation...', 'log-info');
893
+
894
+ const fd = new FormData();
895
+ fd.append('prompt', prompt);
896
+ fd.append('negative_prompt', document.getElementById('negPrompt').value);
897
+ fd.append('seed', document.getElementById('seed').value);
898
+ fd.append('randomize_seed', document.getElementById('randomize').checked);
899
+ fd.append('duration', document.getElementById('duration').value);
900
+ fd.append('steps', document.getElementById('steps').value);
901
+ fd.append('guidance_scale', document.getElementById('guidance').value);
902
+ fd.append('guidance_scale_2', document.getElementById('guidance2').value);
903
+ fd.append('image', currentFile);
904
+
905
+ loader.style.display = 'flex';
906
+ runBtn.disabled = true;
907
+ downloadBtn.style.display = 'none';
908
+ seedBadge.style.display = 'none';
909
+ outputVideo.style.display = 'none';
910
+ outputEmpty.style.display = 'flex';
911
+ framesSection.style.display = 'none';
912
+
913
+ logMsg('Sending to Wan 2.2 I2V pipeline (FP8 + Lightning LoRA + RealESRGAN)...', 'log-info');
914
+
915
+ try {
916
+ const res = await fetch('/api/generate', {method: 'POST', body: fd});
917
+ const data = await res.json();
918
+
919
+ if (data.success) {
920
+ logMsg(`Video done! Seed: ${data.seed}`, 'log-success');
921
+ logMsg(`Upscaled ${(data.frame_results || []).length} frame(s) with RealESRGAN ×2`, 'log-success');
922
+ currentFilename = data.filename;
923
+
924
+ outputVideo.src = data.url;
925
+ outputVideo.style.display = 'block';
926
+ outputEmpty.style.display = 'none';
927
+ downloadBtn.style.display = 'block';
928
+ seedBadge.innerText = `Seed: ${data.seed}`;
929
+ seedBadge.style.display = 'block';
930
+
931
+ buildFrameCards(data.frame_results || []);
932
+ } else {
933
+ logMsg('Error: ' + data.error, 'log-error');
934
+ }
935
+ } catch (e) {
936
+ logMsg('Network or server error.', 'log-error');
937
+ } finally {
938
+ loader.style.display = 'none';
939
+ runBtn.disabled = false;
940
+ logMsg('Ready for next input.', '');
941
+ }
942
+ };
943
+
944
+ // ── Load example ──
945
+ async function loadExample(url, prompt) {
946
+ clearImage();
947
+ promptInput.value = prompt;
948
+ logMsg('Loading example image...', 'log-info');
949
+ try {
950
+ const res = await fetch(url);
951
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
952
+ const blob = await res.blob();
953
+ const filename = url.split('/').pop();
954
+ setImage(new File([blob], filename, {type: blob.type || 'image/jpeg'}));
955
+ window.scrollTo({top: 0, behavior: 'smooth'});
956
+ logMsg('Example loaded — ready to generate.', 'log-success');
957
+ } catch (e) {
958
+ logMsg(`Failed to load example: ${e.message}`, 'log-error');
959
+ }
960
+ }
961
+
962
+ // ── Build example cards ──
963
+ const exGrid = document.getElementById('examplesGrid');
964
+ examples.forEach(ex => {
965
+ const card = document.createElement('div');
966
+ card.className = 'ex-card';
967
+
968
+ const wrap = document.createElement('div');
969
+ wrap.className = 'ex-img-wrap loading';
970
+
971
+ const img = document.createElement('img');
972
+ img.alt = 'Example'; img.loading = 'lazy';
973
+ img.onload = () => { wrap.classList.remove('loading'); };
974
+ img.onerror = () => {
975
+ wrap.classList.remove('loading');
976
+ wrap.innerHTML = `<div style="display:flex;align-items:center;justify-content:center;height:100%;color:#555;font-size:12px;">No preview</div>`;
977
+ };
978
+ img.src = ex.url;
979
+
980
+ const badge = document.createElement('span');
981
+ badge.className = 'ex-use-badge';
982
+ badge.textContent = 'Use this';
983
+
984
+ wrap.appendChild(img);
985
+ wrap.appendChild(badge);
986
+
987
+ const p = document.createElement('p');
988
+ p.textContent = ex.prompt;
989
+
990
+ card.appendChild(wrap);
991
+ card.appendChild(p);
992
+ card.onclick = () => loadExample(ex.url, ex.prompt);
993
+ exGrid.appendChild(card);
994
+ });
995
+ </script>
996
+ </body>
997
+ </html>