multimodalart HF Staff commited on
Commit
a3b454b
·
verified ·
1 Parent(s): aa5e463

Create index.html

Browse files
Files changed (1) hide show
  1. index.html +502 -0
index.html ADDED
@@ -0,0 +1,502 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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>Real-time Video Generation</title>
7
+ <style>
8
+ * {
9
+ margin: 0;
10
+ padding: 0;
11
+ box-sizing: border-box;
12
+ }
13
+
14
+ body {
15
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
16
+ background: linear-gradient(135deg, #0f172a, #1e3a8a, #312e81);
17
+ color: #e2e8f0;
18
+ min-height: 100vh;
19
+ padding: 20px;
20
+ }
21
+
22
+ .container {
23
+ max-width: 1200px;
24
+ margin: 0 auto;
25
+ }
26
+
27
+ header {
28
+ text-align: center;
29
+ margin-bottom: 30px;
30
+ }
31
+
32
+ h1 {
33
+ font-size: 2rem;
34
+ margin-bottom: 10px;
35
+ background: linear-gradient(to right, #22d3ee, #3b82f6, #a855f7);
36
+ -webkit-background-clip: text;
37
+ background-clip: text;
38
+ color: transparent;
39
+ }
40
+
41
+ .subtitle {
42
+ color: #94a3b8;
43
+ font-size: 0.9rem;
44
+ }
45
+
46
+ /* Login Modal */
47
+ .login-modal {
48
+ position: fixed;
49
+ top: 0;
50
+ left: 0;
51
+ width: 100%;
52
+ height: 100%;
53
+ background: rgba(0, 0, 0, 0.8);
54
+ display: flex;
55
+ align-items: center;
56
+ justify-content: center;
57
+ z-index: 1000;
58
+ }
59
+
60
+ .login-box {
61
+ background: rgba(15, 23, 42, 0.95);
62
+ backdrop-filter: blur(10px);
63
+ border-radius: 12px;
64
+ border: 1px solid rgba(148, 163, 184, 0.2);
65
+ padding: 40px;
66
+ max-width: 500px;
67
+ width: 90%;
68
+ }
69
+
70
+ .login-box h2 {
71
+ margin-bottom: 10px;
72
+ color: #e2e8f0;
73
+ }
74
+
75
+ .login-box p {
76
+ color: #94a3b8;
77
+ margin-bottom: 20px;
78
+ font-size: 0.9rem;
79
+ }
80
+
81
+ .form-group {
82
+ margin-bottom: 20px;
83
+ }
84
+
85
+ label {
86
+ display: block;
87
+ font-size: 0.85rem;
88
+ color: #94a3b8;
89
+ margin-bottom: 8px;
90
+ }
91
+
92
+ input[type="password"],
93
+ input[type="text"] {
94
+ width: 100%;
95
+ padding: 12px;
96
+ background: rgba(51, 65, 85, 0.5);
97
+ border: 1px solid #475569;
98
+ border-radius: 6px;
99
+ color: #e2e8f0;
100
+ font-size: 0.9rem;
101
+ }
102
+
103
+ input:focus {
104
+ outline: none;
105
+ border-color: #3b82f6;
106
+ box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
107
+ }
108
+
109
+ .btn {
110
+ padding: 12px 24px;
111
+ border: none;
112
+ border-radius: 6px;
113
+ font-weight: 500;
114
+ cursor: pointer;
115
+ transition: all 0.2s;
116
+ width: 100%;
117
+ }
118
+
119
+ .btn-primary {
120
+ background: #10b981;
121
+ color: white;
122
+ box-shadow: 0 4px 12px rgba(16, 185, 129, 0.3);
123
+ }
124
+
125
+ .btn-primary:hover {
126
+ background: #059669;
127
+ }
128
+
129
+ .btn-primary:disabled {
130
+ opacity: 0.5;
131
+ cursor: not-allowed;
132
+ }
133
+
134
+ .error-box, .info-box, .warning-box {
135
+ border-radius: 6px;
136
+ padding: 12px;
137
+ margin-top: 15px;
138
+ font-size: 0.85rem;
139
+ }
140
+
141
+ .error-box {
142
+ background: rgba(239, 68, 68, 0.1);
143
+ border: 1px solid rgba(239, 68, 68, 0.3);
144
+ color: #fca5a5;
145
+ }
146
+
147
+ .info-box {
148
+ background: rgba(59, 130, 246, 0.1);
149
+ border: 1px solid rgba(59, 130, 246, 0.3);
150
+ color: #93c5fd;
151
+ }
152
+
153
+ .warning-box {
154
+ background: rgba(251, 191, 36, 0.1);
155
+ border: 1px solid rgba(251, 191, 36, 0.3);
156
+ color: #fcd34d;
157
+ }
158
+
159
+ .hidden {
160
+ display: none !important;
161
+ }
162
+
163
+ /* User Info Bar */
164
+ .user-bar {
165
+ background: rgba(15, 23, 42, 0.6);
166
+ backdrop-filter: blur(10px);
167
+ border-radius: 12px;
168
+ border: 1px solid rgba(148, 163, 184, 0.2);
169
+ padding: 15px 20px;
170
+ margin-bottom: 20px;
171
+ display: flex;
172
+ justify-content: space-between;
173
+ align-items: center;
174
+ }
175
+
176
+ .user-info {
177
+ display: flex;
178
+ align-items: center;
179
+ gap: 15px;
180
+ }
181
+
182
+ .user-avatar {
183
+ width: 40px;
184
+ height: 40px;
185
+ border-radius: 50%;
186
+ border: 2px solid #3b82f6;
187
+ }
188
+
189
+ .user-details h3 {
190
+ font-size: 1rem;
191
+ margin-bottom: 3px;
192
+ }
193
+
194
+ .user-badge {
195
+ display: inline-block;
196
+ padding: 2px 8px;
197
+ border-radius: 4px;
198
+ font-size: 0.7rem;
199
+ font-weight: 600;
200
+ text-transform: uppercase;
201
+ }
202
+
203
+ .badge-pro {
204
+ background: linear-gradient(135deg, #f59e0b, #d97706);
205
+ color: white;
206
+ }
207
+
208
+ .badge-free {
209
+ background: #475569;
210
+ color: #94a3b8;
211
+ }
212
+
213
+ .usage-info {
214
+ font-size: 0.85rem;
215
+ color: #94a3b8;
216
+ }
217
+
218
+ .btn-logout {
219
+ padding: 8px 16px;
220
+ background: #475569;
221
+ color: #e2e8f0;
222
+ border: none;
223
+ border-radius: 6px;
224
+ cursor: pointer;
225
+ font-size: 0.85rem;
226
+ }
227
+
228
+ .btn-logout:hover {
229
+ background: #64748b;
230
+ }
231
+
232
+ .limit-warning {
233
+ background: rgba(239, 68, 68, 0.1);
234
+ border: 1px solid rgba(239, 68, 68, 0.3);
235
+ border-radius: 12px;
236
+ padding: 20px;
237
+ margin-bottom: 20px;
238
+ text-align: center;
239
+ }
240
+
241
+ .limit-warning h3 {
242
+ color: #fca5a5;
243
+ margin-bottom: 10px;
244
+ }
245
+
246
+ .limit-warning p {
247
+ color: #94a3b8;
248
+ font-size: 0.9rem;
249
+ }
250
+
251
+ .upgrade-link {
252
+ color: #3b82f6;
253
+ text-decoration: none;
254
+ font-weight: 500;
255
+ }
256
+
257
+ .upgrade-link:hover {
258
+ text-decoration: underline;
259
+ }
260
+
261
+ /* Main app styles from original */
262
+ .grid {
263
+ display: grid;
264
+ grid-template-columns: 2fr 1fr;
265
+ gap: 20px;
266
+ }
267
+
268
+ .panel {
269
+ background: rgba(15, 23, 42, 0.6);
270
+ backdrop-filter: blur(10px);
271
+ border-radius: 12px;
272
+ border: 1px solid rgba(148, 163, 184, 0.2);
273
+ padding: 20px;
274
+ }
275
+
276
+ .video-container {
277
+ background: #000;
278
+ border-radius: 8px;
279
+ aspect-ratio: 832/480;
280
+ display: flex;
281
+ align-items: center;
282
+ justify-content: center;
283
+ position: relative;
284
+ overflow: hidden;
285
+ }
286
+
287
+ #outputCanvas {
288
+ max-width: 100%;
289
+ max-height: 100%;
290
+ object-fit: contain;
291
+ }
292
+
293
+ .placeholder {
294
+ text-align: center;
295
+ color: #475569;
296
+ }
297
+
298
+ /* Additional styles would continue here - abbreviated for space */
299
+
300
+ @media (max-width: 768px) {
301
+ .grid {
302
+ grid-template-columns: 1fr;
303
+ }
304
+
305
+ .user-bar {
306
+ flex-direction: column;
307
+ gap: 15px;
308
+ }
309
+ }
310
+ </style>
311
+ </head>
312
+ <body>
313
+ <div class="container">
314
+ {% if not authenticated %}
315
+ <!-- Login Modal -->
316
+ <div class="login-modal" id="loginModal">
317
+ <div class="login-box">
318
+ <h2>🤗 Sign in with Hugging Face</h2>
319
+ <p>Please enter your Hugging Face token to continue</p>
320
+
321
+ <div class="form-group">
322
+ <label>Hugging Face Token</label>
323
+ <input type="password" id="hfToken" placeholder="hf_...">
324
+ <small style="color: #64748b; font-size: 0.75rem; display: block; margin-top: 5px;">
325
+ Get your token from <a href="https://huggingface.co/settings/tokens" target="_blank" style="color: #3b82f6;">huggingface.co/settings/tokens</a>
326
+ </small>
327
+ </div>
328
+
329
+ <button class="btn btn-primary" onclick="login()" id="loginBtn">
330
+ Sign In
331
+ </button>
332
+
333
+ <div id="loginError" class="error-box hidden"></div>
334
+
335
+ <div class="info-box" style="margin-top: 20px;">
336
+ <strong>Free Users:</strong> 1 session per day<br>
337
+ <strong>PRO Users:</strong> 15 sessions per day
338
+ </div>
339
+ </div>
340
+ </div>
341
+ {% else %}
342
+
343
+ <!-- User Bar -->
344
+ <div class="user-bar">
345
+ <div class="user-info">
346
+ {% if user.avatar %}
347
+ <img src="{{ user.avatar }}" alt="Avatar" class="user-avatar">
348
+ {% endif %}
349
+ <div class="user-details">
350
+ <h3>{{ user.fullname }}</h3>
351
+ <span class="user-badge {% if user.is_pro %}badge-pro{% else %}badge-free{% endif %}">
352
+ {% if user.is_pro %}PRO{% else %}FREE{% endif %}
353
+ </span>
354
+ </div>
355
+ </div>
356
+ <div style="text-align: right;">
357
+ <div class="usage-info">Sessions: {{ sessions_used }}/{{ sessions_limit }} today</div>
358
+ <button class="btn-logout" onclick="logout()">Logout</button>
359
+ </div>
360
+ </div>
361
+
362
+ {% if not can_start %}
363
+ <!-- Limit Reached Warning -->
364
+ <div class="limit-warning">
365
+ <h3>⚠️ Daily Limit Reached</h3>
366
+ <p>You've used all {{ sessions_limit }} sessions for today. Come back tomorrow!</p>
367
+ {% if not user.is_pro %}
368
+ <p style="margin-top: 10px;">
369
+ Upgrade to <a href="https://huggingface.co/pricing" target="_blank" class="upgrade-link">Hugging Face PRO</a> for 15 sessions per day!
370
+ </p>
371
+ {% endif %}
372
+ </div>
373
+ {% endif %}
374
+
375
+ <header>
376
+ <h1>Real-time Video Generation</h1>
377
+ <p class="subtitle">Self-Forcing Diffusion with Dynamic Prompt Rewriting • 832×480 Fixed Resolution</p>
378
+ </header>
379
+
380
+ <!-- Original app content would go here -->
381
+ <div class="grid">
382
+ <div class="panel">
383
+ <div class="video-container">
384
+ <canvas id="outputCanvas"></canvas>
385
+ <div id="placeholder" class="placeholder">
386
+ <p>{% if can_start %}Click Start to begin generation{% else %}Daily limit reached{% endif %}</p>
387
+ </div>
388
+ </div>
389
+ </div>
390
+
391
+ <div class="panel">
392
+ <h2 style="margin-bottom: 20px;">Controls</h2>
393
+
394
+ <div class="form-group">
395
+ <label>FAL API Key</label>
396
+ <input type="password" id="apiKey" placeholder="YOUR_FAL_API_KEY" {% if not can_start %}disabled{% endif %}>
397
+ </div>
398
+
399
+ <div class="form-group">
400
+ <label>Prompt</label>
401
+ <textarea id="prompt" {% if not can_start %}disabled{% endif %}>A cat riding a skateboard through a neon city at night</textarea>
402
+ </div>
403
+
404
+ <button class="btn btn-primary" id="startBtn" onclick="startGeneration()" {% if not can_start %}disabled{% endif %}>
405
+ {% if can_start %}Start Generation{% else %}Limit Reached{% endif %}
406
+ </button>
407
+
408
+ <div id="statusMessage" class="info-box hidden"></div>
409
+ </div>
410
+ </div>
411
+ {% endif %}
412
+ </div>
413
+
414
+ <script>
415
+ {% if not authenticated %}
416
+ async function login() {
417
+ const token = document.getElementById('hfToken').value.trim();
418
+ const btn = document.getElementById('loginBtn');
419
+ const errorBox = document.getElementById('loginError');
420
+
421
+ if (!token) {
422
+ errorBox.textContent = 'Please enter your token';
423
+ errorBox.classList.remove('hidden');
424
+ return;
425
+ }
426
+
427
+ btn.disabled = true;
428
+ btn.textContent = 'Signing in...';
429
+ errorBox.classList.add('hidden');
430
+
431
+ try {
432
+ const response = await fetch('/api/login', {
433
+ method: 'POST',
434
+ headers: {'Content-Type': 'application/json'},
435
+ body: JSON.stringify({token})
436
+ });
437
+
438
+ const data = await response.json();
439
+
440
+ if (response.ok) {
441
+ window.location.reload();
442
+ } else {
443
+ errorBox.textContent = data.detail || 'Login failed';
444
+ errorBox.classList.remove('hidden');
445
+ btn.disabled = false;
446
+ btn.textContent = 'Sign In';
447
+ }
448
+ } catch (error) {
449
+ errorBox.textContent = 'Network error. Please try again.';
450
+ errorBox.classList.remove('hidden');
451
+ btn.disabled = false;
452
+ btn.textContent = 'Sign In';
453
+ }
454
+ }
455
+
456
+ document.getElementById('hfToken').addEventListener('keypress', (e) => {
457
+ if (e.key === 'Enter') login();
458
+ });
459
+ {% else %}
460
+ async function logout() {
461
+ await fetch('/api/logout', {method: 'POST'});
462
+ window.location.reload();
463
+ }
464
+
465
+ async function startGeneration() {
466
+ const btn = document.getElementById('startBtn');
467
+ const statusMsg = document.getElementById('statusMessage');
468
+
469
+ btn.disabled = true;
470
+ btn.textContent = 'Starting...';
471
+
472
+ try {
473
+ const response = await fetch('/api/start-session', {method: 'POST'});
474
+ const data = await response.json();
475
+
476
+ if (response.ok) {
477
+ statusMsg.className = 'info-box';
478
+ statusMsg.textContent = `✓ Session started! (${data.sessions_used}/${data.sessions_limit} used today)`;
479
+ statusMsg.classList.remove('hidden');
480
+
481
+ // Here you would initialize the actual video generation
482
+ // For now, just show success
483
+ btn.textContent = 'Session Active';
484
+ } else {
485
+ statusMsg.className = 'error-box';
486
+ statusMsg.textContent = data.detail || 'Failed to start session';
487
+ statusMsg.classList.remove('hidden');
488
+ btn.disabled = false;
489
+ btn.textContent = 'Start Generation';
490
+ }
491
+ } catch (error) {
492
+ statusMsg.className = 'error-box';
493
+ statusMsg.textContent = 'Network error. Please try again.';
494
+ statusMsg.classList.remove('hidden');
495
+ btn.disabled = false;
496
+ btn.textContent = 'Start Generation';
497
+ }
498
+ }
499
+ {% endif %}
500
+ </script>
501
+ </body>
502
+ </html>