zubair5544 commited on
Commit
cfbf2e8
·
verified ·
1 Parent(s): 55480de

Upload folder using huggingface_hub

Browse files
Files changed (1) hide show
  1. index.html +617 -563
index.html CHANGED
@@ -1,561 +1,565 @@
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>LTX-Video Inference Studio</title>
7
- <!-- Phosphor Icons for modern, clean iconography -->
8
- <script src="https://unpkg.com/@phosphor-icons/web"></script>
9
-
10
- <style>
11
- /* --- CSS VARIABLES & RESET --- */
12
- :root {
13
- --bg-dark: #0f1115;
14
- --bg-panel: #181b21;
15
- --bg-input: #232730;
16
- --primary: #6366f1; /* Indigo */
17
- --primary-hover: #4f46e5;
18
- --accent: #ec4899; /* Pink */
19
- --text-main: #ffffff;
20
- --text-muted: #9ca3af;
21
- --border: #2d313a;
22
- --success: #10b981;
23
- --terminal-bg: #000000;
24
- --font-mono: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;
25
- --font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
26
- --radius-md: 12px;
27
- --radius-lg: 16px;
28
- --shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.5);
29
- }
30
-
31
- * {
32
- box-sizing: border-box;
33
- margin: 0;
34
- padding: 0;
35
- outline: none;
36
- }
37
-
38
- body {
39
- background-color: var(--bg-dark);
40
- color: var(--text-main);
41
- font-family: var(--font-sans);
42
- height: 100vh;
43
- display: flex;
44
- flex-direction: column;
45
- overflow: hidden;
46
- }
47
-
48
- /* --- HEADER --- */
49
- header {
50
- height: 60px;
51
- background-color: var(--bg-panel);
52
- border-bottom: 1px solid var(--border);
53
- display: flex;
54
- align-items: center;
55
- padding: 0 24px;
56
- justify-content: space-between;
57
- }
58
-
59
- .brand {
60
- display: flex;
61
- align-items: center;
62
- gap: 12px;
63
- font-weight: 700;
64
- font-size: 1.1rem;
65
- letter-spacing: -0.5px;
66
- }
67
-
68
- .brand i {
69
- color: var(--primary);
70
- font-size: 1.5rem;
71
- }
72
-
73
- .status-badge {
74
- display: flex;
75
- align-items: center;
76
- gap: 8px;
77
- font-size: 0.85rem;
78
- padding: 6px 12px;
79
- background: rgba(16, 185, 129, 0.1);
80
- color: var(--success);
81
- border-radius: 20px;
82
- border: 1px solid rgba(16, 185, 129, 0.2);
83
- }
84
-
85
- .status-dot {
86
- width: 8px;
87
- height: 8px;
88
- background-color: var(--success);
89
- border-radius: 50%;
90
- animation: pulse 2s infinite;
91
- }
92
-
93
- @keyframes pulse {
94
- 0% { box-shadow: 0 0 0 0 rgba(16, 185, 129, 0.4); }
95
- 70% { box-shadow: 0 0 0 6px rgba(16, 185, 129, 0); }
96
- 100% { box-shadow: 0 0 0 0 rgba(16, 185, 129, 0); }
97
- }
98
 
99
- /* --- MAIN LAYOUT --- */
100
- main {
101
- flex: 1;
102
- display: grid;
103
- grid-template-columns: 350px 1fr;
104
- height: calc(100vh - 60px);
105
- }
106
-
107
- /* --- SIDEBAR (CONTROLS) --- */
108
- aside {
109
- background-color: var(--bg-panel);
110
- border-right: 1px solid var(--border);
111
- padding: 24px;
112
- overflow-y: auto;
113
- display: flex;
114
- flex-direction: column;
115
- gap: 24px;
116
- }
117
-
118
- .control-group {
119
- display: flex;
120
- flex-direction: column;
121
- gap: 12px;
122
- }
123
-
124
- .control-group label {
125
- font-size: 0.85rem;
126
- font-weight: 600;
127
- color: var(--text-muted);
128
- display: flex;
129
- justify-content: space-between;
130
- }
131
-
132
- .value-display {
133
- color: var(--primary);
134
- }
135
-
136
- /* Inputs */
137
- input[type="text"], textarea, select {
138
- background-color: var(--bg-input);
139
- border: 1px solid var(--border);
140
- color: var(--text-main);
141
- padding: 12px;
142
- border-radius: var(--radius-md);
143
- font-family: var(--font-sans);
144
- font-size: 0.95rem;
145
- transition: border-color 0.2s;
146
- }
147
-
148
- input[type="text"]:focus, textarea:focus, select:focus {
149
- border-color: var(--primary);
150
- }
151
-
152
- textarea {
153
- resize: vertical;
154
- min-height: 100px;
155
- }
156
-
157
- /* Range Slider */
158
- input[type="range"] {
159
- -webkit-appearance: none;
160
- width: 100%;
161
- height: 6px;
162
- background: var(--bg-input);
163
- border-radius: 3px;
164
- outline: none;
165
- }
166
-
167
- input[type="range"]::-webkit-slider-thumb {
168
- -webkit-appearance: none;
169
- width: 18px;
170
- height: 18px;
171
- background: var(--primary);
172
- border-radius: 50%;
173
- cursor: pointer;
174
- transition: transform 0.1s;
175
- }
176
-
177
- input[type="range"]::-webkit-slider-thumb:hover {
178
- transform: scale(1.1);
179
- }
180
-
181
- /* Buttons */
182
- .btn {
183
- padding: 12px 20px;
184
- border-radius: var(--radius-md);
185
- border: none;
186
- font-weight: 600;
187
- cursor: pointer;
188
- display: flex;
189
- align-items: center;
190
- justify-content: center;
191
- gap: 8px;
192
- transition: all 0.2s;
193
- font-size: 0.95rem;
194
- }
195
-
196
- .btn-primary {
197
- background-color: var(--primary);
198
- color: white;
199
- box-shadow: 0 4px 12px rgba(99, 102, 241, 0.3);
200
- }
201
-
202
- .btn-primary:hover {
203
- background-color: var(--primary-hover);
204
- transform: translateY(-1px);
205
- }
206
-
207
- .btn-primary:active {
208
- transform: translateY(0);
209
- }
210
-
211
- .btn-primary:disabled {
212
- background-color: var(--bg-input);
213
- color: var(--text-muted);
214
- cursor: not-allowed;
215
- box-shadow: none;
216
- }
217
-
218
- .btn-secondary {
219
- background-color: var(--bg-input);
220
- color: var(--text-main);
221
- border: 1px solid var(--border);
222
- }
223
-
224
- .btn-secondary:hover {
225
- background-color: var(--border);
226
- }
227
-
228
- /* --- PREVIEW AREA --- */
229
- .preview-area {
230
- background-color: #000;
231
- position: relative;
232
- display: flex;
233
- flex-direction: column;
234
- align-items: center;
235
- justify-content: center;
236
- padding: 40px;
237
- background-image:
238
- radial-gradient(circle at 10% 20%, rgba(99, 102, 241, 0.1) 0%, transparent 20%),
239
- radial-gradient(circle at 90% 80%, rgba(236, 72, 153, 0.1) 0%, transparent 20%);
240
- }
241
-
242
- .video-container {
243
- width: 100%;
244
- max-width: 800px;
245
- aspect-ratio: 16/9;
246
- background-color: #1a1a1a;
247
- border-radius: var(--radius-lg);
248
- box-shadow: var(--shadow-lg);
249
- overflow: hidden;
250
- position: relative;
251
- display: flex;
252
- align-items: center;
253
- justify-content: center;
254
- }
255
-
256
- video {
257
- width: 100%;
258
- height: 100%;
259
- object-fit: contain;
260
- }
261
-
262
- /* Placeholder State */
263
- .placeholder-state {
264
- text-align: center;
265
- color: var(--text-muted);
266
- }
267
-
268
- .placeholder-state i {
269
- font-size: 4rem;
270
- margin-bottom: 16px;
271
- opacity: 0.5;
272
- }
273
-
274
- /* Loading Overlay */
275
- .loading-overlay {
276
- position: absolute;
277
- top: 0;
278
- left: 0;
279
- width: 100%;
280
- height: 100%;
281
- background: rgba(0, 0, 0, 0.85);
282
- backdrop-filter: blur(5px);
283
- display: flex;
284
- flex-direction: column;
285
- align-items: center;
286
- justify-content: center;
287
- z-index: 10;
288
- opacity: 0;
289
- pointer-events: none;
290
- transition: opacity 0.3s;
291
- }
292
-
293
- .loading-overlay.active {
294
- opacity: 1;
295
- pointer-events: all;
296
- }
297
-
298
- .loader-ring {
299
- width: 60px;
300
- height: 60px;
301
- border: 4px solid var(--bg-input);
302
- border-top: 4px solid var(--primary);
303
- border-radius: 50%;
304
- animation: spin 1s linear infinite;
305
- margin-bottom: 20px;
306
- }
307
-
308
- @keyframes spin {
309
- 0% { transform: rotate(0deg); }
310
- 100% { transform: rotate(360deg); }
311
- }
312
-
313
- .loading-text {
314
- font-family: var(--font-mono);
315
- font-size: 0.9rem;
316
- color: var(--text-main);
317
- text-align: center;
318
- }
319
-
320
- /* --- TERMINAL / CONSOLE --- */
321
- .console-panel {
322
- position: absolute;
323
- bottom: 24px;
324
- right: 24px;
325
- width: 600px;
326
- height: 300px;
327
- background-color: var(--terminal-bg);
328
- border-radius: var(--radius-md);
329
- border: 1px solid var(--border);
330
- display: flex;
331
- flex-direction: column;
332
- box-shadow: var(--shadow-lg);
333
- font-family: var(--font-mono);
334
- font-size: 0.8rem;
335
- overflow: hidden;
336
- transition: transform 0.3s ease, opacity 0.3s ease;
337
- }
338
-
339
- .console-panel.hidden {
340
- transform: translateY(20px);
341
- opacity: 0;
342
- pointer-events: none;
343
- }
344
-
345
- .console-header {
346
- background-color: #1f2937;
347
- padding: 8px 16px;
348
- display: flex;
349
- justify-content: space-between;
350
- align-items: center;
351
- border-bottom: 1px solid #374151;
352
- color: #9ca3af;
353
- }
354
-
355
- .console-body {
356
- flex: 1;
357
- padding: 16px;
358
- overflow-y: auto;
359
- color: #10b981;
360
- }
361
-
362
- .console-line {
363
- margin-bottom: 4px;
364
- line-height: 1.4;
365
- }
366
- .console-line.error { color: #ef4444; }
367
- .console-line.warn { color: #f59e0b; }
368
- .console-line.info { color: #3b82f6; }
369
-
370
- /* --- GALLERY --- */
371
- .gallery-section {
372
- position: absolute;
373
- top: 24px;
374
- right: 24px;
375
- display: flex;
376
- flex-direction: column;
377
- gap: 10px;
378
- align-items: flex-end;
379
- }
380
-
381
- .gallery-item {
382
- width: 120px;
383
- height: 68px;
384
- background-color: #222;
385
- border-radius: 8px;
386
- overflow: hidden;
387
- cursor: pointer;
388
- border: 2px solid transparent;
389
- transition: all 0.2s;
390
- position: relative;
391
- }
392
-
393
- .gallery-item:hover {
394
- border-color: var(--primary);
395
- transform: scale(1.05);
396
- }
397
-
398
- .gallery-item img {
399
- width: 100%;
400
- height: 100%;
401
- object-fit: cover;
402
- }
403
-
404
- .gallery-item .badge {
405
- position: absolute;
406
- bottom: 0;
407
- left: 0;
408
- right: 0;
409
- background: rgba(0,0,0,0.7);
410
- font-size: 0.6rem;
411
- padding: 2px 4px;
412
- text-align: center;
413
- }
414
-
415
- /* --- RESPONSIVE --- */
416
- @media (max-width: 900px) {
417
- main {
418
- grid-template-columns: 1fr;
419
- grid-template-rows: auto 1fr;
420
- height: auto;
421
- overflow-y: auto;
422
- }
423
-
424
- aside {
425
- border-right: none;
426
- border-bottom: 1px solid var(--border);
427
- }
428
-
429
- .console-panel {
430
- width: 90%;
431
- right: 5%;
432
- bottom: 10px;
433
- }
434
-
435
- .gallery-section {
436
- display: none; /* Hide gallery on mobile to save space */
437
- }
438
- }
439
-
440
- .footer-link {
441
- font-size: 0.75rem;
442
- color: var(--text-muted);
443
- text-decoration: none;
444
- display: flex;
445
- align-items: center;
446
- gap: 4px;
447
- }
448
-
449
- .footer-link:hover {
450
- color: var(--text-main);
451
- }
452
- </style>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
453
  </head>
 
454
  <body>
455
 
456
- <header>
457
- <div class="brand">
458
- <i class="ph ph-film-strip"></i>
459
- <span>LTX-Video <span style="font-weight:400; opacity: 0.7;">Studio</span></span>
460
- </div>
461
- <div class="status-badge">
462
- <div class="status-dot"></div>
463
- <span id="status-text">GPU Ready</span>
464
- </div>
465
- </header>
466
-
467
- <main>
468
- <!-- CONTROLS SIDEBAR -->
469
- <aside>
470
- <div class="control-group">
471
- <label for="prompt-input">Prompt</label>
472
- <textarea id="prompt-input" placeholder="A cinematic shot of a cyberpunk city street at night with neon lights reflecting on wet pavement..."></textarea>
473
- </div>
474
-
475
- <div class="control-group">
476
- <label>Negative Prompt</label>
477
- <input type="text" id="neg-prompt" value="blurry, low quality, distorted, watermark">
478
- </div>
479
-
480
- <div class="control-group">
481
- <label>
482
  Duration (Frames)
483
  <span class="value-display" id="duration-val">129</span>
484
  </label>
485
- <input type="range" id="duration" min="16" max="129" step="1" value="129">
486
- </div>
487
 
488
- <div class="control-group">
489
- <label>
490
  Resolution
491
  <span class="value-display">512x896</span>
492
  </label>
493
- <select id="resolution">
494
  <option value="512x896">512x896 (Native)</option>
495
  <option value="768x1344">768x1344 (HD)</option>
496
  </select>
497
- </div>
498
 
499
- <div class="control-group">
500
- <label>
501
  Seed
502
  <button class="btn-secondary" style="padding: 4px 8px; font-size: 0.7rem;" id="random-seed">Random</button>
503
  </label>
504
- <input type="text" id="seed" value="-1">
505
- </div>
506
 
507
- <div class="control-group" style="margin-top: auto;">
508
- <button id="generate-btn" class="btn btn-primary">
509
  <i class="ph ph-play-circle"></i>
510
  Generate Video
511
  </button>
512
- </div>
513
- </aside>
514
 
515
- <!-- PREVIEW AREA -->
516
- <section class="preview-area">
517
-
518
- <div class="video-container">
519
- <!-- Placeholder -->
520
- <div id="placeholder" class="placeholder-state">
521
- <i class="ph ph-video-camera"></i>
522
- <p>Enter a prompt and click Generate to start inference.</p>
523
- </div>
524
-
525
- <!-- Hidden Video Element -->
526
- <video id="main-video" controls loop playsinline style="display: none;"></video>
527
-
528
- <!-- Loading Overlay -->
529
- <div id="loading-overlay" class="loading-overlay">
530
- <div class="loader-ring"></div>
531
- <div class="loading-text" id="loading-status">Initializing Environment...</div>
532
- </div>
533
- </div>
534
-
535
- <!-- Gallery -->
536
- <div class="gallery-section" id="gallery">
537
- <!-- Items will be injected here -->
538
- </div>
539
-
540
- <!-- Terminal Console -->
541
- <div class="console-panel" id="console-panel">
542
- <div class="console-header">
543
- <span><i class="ph ph-terminal-window"></i> Inference Console</span>
544
- <button class="btn-secondary" style="padding: 2px 6px; font-size: 0.7rem;" id="clear-console">Clear</button>
545
- </div>
546
- <div class="console-body" id="console-body">
547
- <div class="console-line info">> System initialized.</div>
548
- <div class="console-line">> Waiting for user input...</div>
549
- </div>
550
- </div>
551
- </section>
552
- </main>
553
-
554
- <script>
555
- /**
556
- * LTX-Video Simulation Logic
557
- * Handles UI interactions, simulated terminal output, and model loading states.
558
- */
 
 
 
559
 
560
  // --- DOM Elements ---
561
  const generateBtn = document.getElementById('generate-btn');
@@ -566,6 +570,8 @@
566
  const seedInput = document.getElementById('seed');
567
  const randomSeedBtn = document.getElementById('random-seed');
568
  const mainVideo = document.getElementById('main-video');
 
 
569
  const placeholder = document.getElementById('placeholder');
570
  const loadingOverlay = document.getElementById('loading-overlay');
571
  const loadingStatus = document.getElementById('loading-status');
@@ -578,12 +584,12 @@
578
  // --- State ---
579
  let isGenerating = false;
580
  let isModelLoaded = false;
 
581
 
582
  // --- Utilities ---
583
  const log = (msg, type = 'info') => {
584
  const line = document.createElement('div');
585
  line.className = `console-line ${type}`;
586
- // Add timestamp
587
  const time = new Date().toLocaleTimeString('en-US', {hour12: false, hour: "numeric", minute: "numeric", second: "numeric"});
588
  line.textContent = `[${time}] ${msg}`;
589
  consoleBody.appendChild(line);
@@ -594,10 +600,6 @@
594
  consolePanel.classList.remove('hidden');
595
  };
596
 
597
- const hideConsole = () => {
598
- // Keep it visible but maybe fade out slightly? No, let's keep it visible for "Colab" feel.
599
- };
600
-
601
  // --- Event Listeners ---
602
 
603
  // Duration Slider Update
@@ -695,7 +697,7 @@
695
  'transformers',
696
  'accelerate',
697
  'safetensors',
698
- 'xformers', // Optional, for optimization
699
  'opencv-python'
700
  ];
701
 
@@ -710,7 +712,6 @@
710
  }
711
 
712
  const pkg = packages[pIndex];
713
- // Randomize speed
714
  const delay = Math.random() * 800 + 400;
715
 
716
  setTimeout(() => {
@@ -725,20 +726,22 @@
725
  loadingOverlay.classList.remove('active');
726
  log('Inference complete. Saving video...', 'success');
727
 
728
- // Generate a fake video using a reliable placeholder or a short loop
729
- // Since we can't actually generate a 4GB video in JS, we use a high-quality placeholder
730
- // or a procedurally generated canvas. Let's use a nice Unsplash video loop for realism.
731
- const randomId = Math.floor(Math.random() * 1000);
732
- // Using a tech/cyberpunk themed video loop from Pexels (free CDN)
733
- const videoUrl = `https://videos.pexels.com/video-files/3129671/3129671-uhd_2560_1440_25fps.mp4`;
734
-
735
- // Or a generic abstract loop if that fails, but let's try this one.
736
- // Fallback to a placeholder image if video fails, but let's try video first.
737
 
738
- mainVideo.src = videoUrl;
739
- mainVideo.style.display = 'block';
 
740
  placeholder.style.display = 'none';
741
-
 
 
 
742
  // Reset UI
743
  isGenerating = false;
744
  generateBtn.disabled = false;
@@ -747,16 +750,72 @@
747
  log('Video rendered successfully.', 'success');
748
  statusText.textContent = "Inference Complete";
749
 
750
- addToGallery(videoUrl, prompt);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
751
  }
752
 
753
  function addToGallery(url, prompt) {
754
  const item = document.createElement('div');
755
  item.className = 'gallery-item';
756
 
757
- // Create thumbnail
758
  const thumb = document.createElement('img');
759
- thumb.src = url; // Using same URL for thumbnail
760
 
761
  const badge = document.createElement('div');
762
  badge.className = 'badge';
@@ -765,16 +824,12 @@
765
  item.appendChild(thumb);
766
  item.appendChild(badge);
767
 
768
- // Click to load back into main player
769
  item.addEventListener('click', () => {
770
- mainVideo.src = url;
771
- mainVideo.play();
772
- placeholder.style.display = 'none';
773
- mainVideo.style.display = 'block';
774
  log('Loaded video from gallery.', 'info');
775
  });
776
 
777
- // Insert at top
778
  gallery.insertBefore(item, gallery.firstChild);
779
  }
780
 
@@ -787,7 +842,6 @@
787
  setTimeout(() => {
788
  log('Connecting to Hugging Face Hub...', 'info');
789
  }, 500);
790
-
791
- </script>
792
  </body>
793
  </html>
 
1
  <!DOCTYPE html>
2
  <html lang="en">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
 
4
+ <head>
5
+ <meta charset="UTF-8">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <title>LTX-Video Inference Studio</title>
8
+ <!-- Phosphor Icons for modern, clean iconography -->
9
+ <script src="https://unpkg.com/@phosphor-icons/web"></script>
10
+
11
+ <style>
12
+ /* --- CSS VARIABLES & RESET --- */
13
+ :root {
14
+ --bg-dark: #0f1115;
15
+ --bg-panel: #181b21;
16
+ --bg-input: #232730;
17
+ --primary: #6366f1;
18
+ --primary-hover: #4f46e5;
19
+ --accent: #ec4899;
20
+ --text-main: #ffffff;
21
+ --text-muted: #9ca3af;
22
+ --border: #2d313a;
23
+ --success: #10b981;
24
+ --terminal-bg: #000000;
25
+ --font-mono: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;
26
+ --font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
27
+ --radius-md: 12px;
28
+ --radius-lg: 16px;
29
+ --shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.5);
30
+ }
31
+
32
+ * {
33
+ box-sizing: border-box;
34
+ margin: 0;
35
+ padding: 0;
36
+ outline: none;
37
+ }
38
+
39
+ body {
40
+ background-color: var(--bg-dark);
41
+ color: var(--text-main);
42
+ font-family: var(--font-sans);
43
+ height: 100vh;
44
+ display: flex;
45
+ flex-direction: column;
46
+ overflow: hidden;
47
+ }
48
+
49
+ /* --- HEADER --- */
50
+ header {
51
+ height: 60px;
52
+ background-color: var(--bg-panel);
53
+ border-bottom: 1px solid var(--border);
54
+ display: flex;
55
+ align-items: center;
56
+ padding: 0 24px;
57
+ justify-content: space-between;
58
+ }
59
+
60
+ .brand {
61
+ display: flex;
62
+ align-items: center;
63
+ gap: 12px;
64
+ font-weight: 700;
65
+ font-size: 1.1rem;
66
+ letter-spacing: -0.5px;
67
+ }
68
+
69
+ .brand i {
70
+ color: var(--primary);
71
+ font-size: 1.5rem;
72
+ }
73
+
74
+ .status-badge {
75
+ display: flex;
76
+ align-items: center;
77
+ gap: 8px;
78
+ font-size: 0.85rem;
79
+ padding: 6px 12px;
80
+ background: rgba(16, 185, 129, 0.1);
81
+ color: var(--success);
82
+ border-radius: 20px;
83
+ border: 1px solid rgba(16, 185, 129, 0.2);
84
+ }
85
+
86
+ .status-dot {
87
+ width: 8px;
88
+ height: 8px;
89
+ background-color: var(--success);
90
+ border-radius: 50%;
91
+ animation: pulse 2s infinite;
92
+ }
93
+
94
+ @keyframes pulse {
95
+ 0% {
96
+ box-shadow: 0 0 0 0 rgba(16, 185, 129, 0.4);
97
+ }
98
+
99
+ 70% {
100
+ box-shadow: 0 0 0 6px rgba(16, 185, 129, 0);
101
+ }
102
+
103
+ 100% {
104
+ box-shadow: 0 0 0 0 rgba(16, 185, 129, 0);
105
+ }
106
+ }
107
+
108
+ /* --- MAIN LAYOUT --- */
109
+ main {
110
+ flex: 1;
111
+ display: grid;
112
+ grid-template-columns: 350px 1fr;
113
+ height: calc(100vh - 60px);
114
+ }
115
+
116
+ /* --- SIDEBAR (CONTROLS) --- */
117
+ aside {
118
+ background-color: var(--bg-panel);
119
+ border-right: 1px solid var(--border);
120
+ padding: 24px;
121
+ overflow-y: auto;
122
+ display: flex;
123
+ flex-direction: column;
124
+ gap: 24px;
125
+ }
126
+
127
+ .control-group {
128
+ display: flex;
129
+ flex-direction: column;
130
+ gap: 12px;
131
+ }
132
+
133
+ .control-group label {
134
+ font-size: 0.85rem;
135
+ font-weight: 600;
136
+ color: var(--text-muted);
137
+ display: flex;
138
+ justify-content: space-between;
139
+ }
140
+
141
+ .value-display {
142
+ color: var(--primary);
143
+ }
144
+
145
+ /* Inputs */
146
+ input[type="text"],
147
+ textarea,
148
+ select {
149
+ background-color: var(--bg-input);
150
+ border: 1px solid var(--border);
151
+ color: var(--text-main);
152
+ padding: 12px;
153
+ border-radius: var(--radius-md);
154
+ font-family: var(--font-sans);
155
+ font-size: 0.95rem;
156
+ transition: border-color 0.2s;
157
+ }
158
+
159
+ input[type="text"]:focus,
160
+ textarea:focus,
161
+ select:focus {
162
+ border-color: var(--primary);
163
+ }
164
+
165
+ textarea {
166
+ resize: vertical;
167
+ min-height: 100px;
168
+ }
169
+
170
+ /* Range Slider */
171
+ input[type="range"] {
172
+ -webkit-appearance: none;
173
+ width: 100%;
174
+ height: 6px;
175
+ background: var(--bg-input);
176
+ border-radius: 3px;
177
+ outline: none;
178
+ }
179
+
180
+ input[type="range"]::-webkit-slider-thumb {
181
+ -webkit-appearance: none;
182
+ width: 18px;
183
+ height: 18px;
184
+ background: var(--primary);
185
+ border-radius: 50%;
186
+ cursor: pointer;
187
+ transition: transform 0.1s;
188
+ }
189
+
190
+ input[type="range"]::-webkit-slider-thumb:hover {
191
+ transform: scale(1.1);
192
+ }
193
+
194
+ /* Buttons */
195
+ .btn {
196
+ padding: 12px 20px;
197
+ border-radius: var(--radius-md);
198
+ border: none;
199
+ font-weight: 600;
200
+ cursor: pointer;
201
+ display: flex;
202
+ align-items: center;
203
+ justify-content: center;
204
+ gap: 8px;
205
+ transition: all 0.2s;
206
+ font-size: 0.95rem;
207
+ }
208
+
209
+ .btn-primary {
210
+ background-color: var(--primary);
211
+ color: white;
212
+ box-shadow: 0 4px 12px rgba(99, 102, 241, 0.3);
213
+ }
214
+
215
+ .btn-primary:hover {
216
+ background-color: var(--primary-hover);
217
+ transform: translateY(-1px);
218
+ }
219
+
220
+ .btn-primary:active {
221
+ transform: translateY(0);
222
+ }
223
+
224
+ .btn-primary:disabled {
225
+ background-color: var(--bg-input);
226
+ color: var(--text-muted);
227
+ cursor: not-allowed;
228
+ box-shadow: none;
229
+ }
230
+
231
+ .btn-secondary {
232
+ background-color: var(--bg-input);
233
+ color: var(--text-main);
234
+ border: 1px solid var(--border);
235
+ }
236
+
237
+ .btn-secondary:hover {
238
+ background-color: var(--border);
239
+ }
240
+
241
+ /* --- PREVIEW AREA --- */
242
+ .preview-area {
243
+ background-color: #000;
244
+ position: relative;
245
+ display: flex;
246
+ flex-direction: column;
247
+ align-items: center;
248
+ justify-content: center;
249
+ padding: 40px;
250
+ background-image:
251
+ radial-gradient(circle at 10% 20%, rgba(99, 102, 241, 0.1) 0%, transparent 20%),
252
+ radial-gradient(circle at 90% 80%, rgba(236, 72, 153, 0.1) 0%, transparent 20%);
253
+ }
254
+
255
+ .video-container {
256
+ width: 100%;
257
+ max-width: 800px;
258
+ aspect-ratio: 16/9;
259
+ background-color: #1a1a1a;
260
+ border-radius: var(--radius-lg);
261
+ box-shadow: var(--shadow-lg);
262
+ overflow: hidden;
263
+ position: relative;
264
+ display: flex;
265
+ align-items: center;
266
+ justify-content: center;
267
+ }
268
+
269
+ video, canvas {
270
+ width: 100%;
271
+ height: 100%;
272
+ object-fit: contain;
273
+ }
274
+
275
+ /* Placeholder State */
276
+ .placeholder-state {
277
+ text-align: center;
278
+ color: var(--text-muted);
279
+ }
280
+
281
+ .placeholder-state i {
282
+ font-size: 4rem;
283
+ margin-bottom: 16px;
284
+ opacity: 0.5;
285
+ }
286
+
287
+ /* Loading Overlay */
288
+ .loading-overlay {
289
+ position: absolute;
290
+ top: 0;
291
+ left: 0;
292
+ width: 100%;
293
+ height: 100%;
294
+ background: rgba(0, 0, 0, 0.85);
295
+ backdrop-filter: blur(5px);
296
+ display: flex;
297
+ flex-direction: column;
298
+ align-items: center;
299
+ justify-content: center;
300
+ z-index: 10;
301
+ opacity: 0;
302
+ pointer-events: none;
303
+ transition: opacity 0.3s;
304
+ }
305
+
306
+ .loading-overlay.active {
307
+ opacity: 1;
308
+ pointer-events: all;
309
+ }
310
+
311
+ .loader-ring {
312
+ width: 60px;
313
+ height: 60px;
314
+ border: 4px solid var(--bg-input);
315
+ border-top: 4px solid var(--primary);
316
+ border-radius: 50%;
317
+ animation: spin 1s linear infinite;
318
+ margin-bottom: 20px;
319
+ }
320
+
321
+ @keyframes spin {
322
+ 0% { transform: rotate(0deg); }
323
+ 100% { transform: rotate(360deg); }
324
+ }
325
+
326
+ .loading-text {
327
+ font-family: var(--font-mono);
328
+ font-size: 0.9rem;
329
+ color: var(--text-main);
330
+ text-align: center;
331
+ }
332
+
333
+ /* --- TERMINAL / CONSOLE --- */
334
+ .console-panel {
335
+ position: absolute;
336
+ bottom: 24px;
337
+ right: 24px;
338
+ width: 600px;
339
+ height: 300px;
340
+ background-color: var(--terminal-bg);
341
+ border-radius: var(--radius-md);
342
+ border: 1px solid var(--border);
343
+ display: flex;
344
+ flex-direction: column;
345
+ box-shadow: var(--shadow-lg);
346
+ font-family: var(--font-mono);
347
+ font-size: 0.8rem;
348
+ overflow: hidden;
349
+ transition: transform 0.3s ease, opacity 0.3s ease;
350
+ }
351
+
352
+ .console-panel.hidden {
353
+ transform: translateY(20px);
354
+ opacity: 0;
355
+ pointer-events: none;
356
+ }
357
+
358
+ .console-header {
359
+ background-color: #1f2937;
360
+ padding: 8px 16px;
361
+ display: flex;
362
+ justify-content: space-between;
363
+ align-items: center;
364
+ border-bottom: 1px solid #374151;
365
+ color: #9ca3af;
366
+ }
367
+
368
+ .console-body {
369
+ flex: 1;
370
+ padding: 16px;
371
+ overflow-y: auto;
372
+ color: #10b981;
373
+ }
374
+
375
+ .console-line {
376
+ margin-bottom: 4px;
377
+ line-height: 1.4;
378
+ }
379
+
380
+ .console-line.error { color: #ef4444; }
381
+ .console-line.warn { color: #f59e0b; }
382
+ .console-line.info { color: #3b82f6; }
383
+
384
+ /* --- GALLERY --- */
385
+ .gallery-section {
386
+ position: absolute;
387
+ top: 24px;
388
+ right: 24px;
389
+ display: flex;
390
+ flex-direction: column;
391
+ gap: 10px;
392
+ align-items: flex-end;
393
+ }
394
+
395
+ .gallery-item {
396
+ width: 120px;
397
+ height: 68px;
398
+ background-color: #222;
399
+ border-radius: 8px;
400
+ overflow: hidden;
401
+ cursor: pointer;
402
+ border: 2px solid transparent;
403
+ transition: all 0.2s;
404
+ position: relative;
405
+ }
406
+
407
+ .gallery-item:hover {
408
+ border-color: var(--primary);
409
+ transform: scale(1.05);
410
+ }
411
+
412
+ .gallery-item img {
413
+ width: 100%;
414
+ height: 100%;
415
+ object-fit: cover;
416
+ }
417
+
418
+ .gallery-item .badge {
419
+ position: absolute;
420
+ bottom: 0;
421
+ left: 0;
422
+ right: 0;
423
+ background: rgba(0, 0, 0, 0.7);
424
+ font-size: 0.6rem;
425
+ padding: 2px 4px;
426
+ text-align: center;
427
+ }
428
+
429
+ /* --- RESPONSIVE --- */
430
+ @media (max-width: 900px) {
431
+ main {
432
+ grid-template-columns: 1fr;
433
+ grid-template-rows: auto 1fr;
434
+ height: auto;
435
+ overflow-y: auto;
436
+ }
437
+
438
+ aside { border-right: none; border-bottom: 1px solid var(--border); }
439
+ .console-panel { width: 90%; right: 5%; bottom: 10px; }
440
+ .gallery-section { display: none; }
441
+ }
442
+
443
+ .footer-link {
444
+ font-size: 0.75rem;
445
+ color: var(--text-muted);
446
+ text-decoration: none;
447
+ display: flex;
448
+ align-items: center;
449
+ gap: 4px;
450
+ }
451
+ .footer-link:hover { color: var(--text-main); }
452
+ </style>
453
  </head>
454
+
455
  <body>
456
 
457
+ <header>
458
+ <div class="brand">
459
+ <i class="ph ph-film-strip"></i>
460
+ <span>LTX-Video <span style="font-weight:400; opacity: 0.7;">Studio</span></span>
461
+ </div>
462
+ <div class="status-badge">
463
+ <div class="status-dot"></div>
464
+ <span id="status-text">GPU Ready</span>
465
+ </div>
466
+ </header>
467
+
468
+ <main>
469
+ <!-- CONTROLS SIDEBAR -->
470
+ <aside>
471
+ <div class="control-group">
472
+ <label for="prompt-input">Prompt</label>
473
+ <textarea id="prompt-input" placeholder="A cinematic shot of a cyberpunk city street at night with neon lights reflecting on wet pavement..."></textarea>
474
+ </div>
475
+
476
+ <div class="control-group">
477
+ <label>Negative Prompt</label>
478
+ <input type="text" id="neg-prompt" value="blurry, low quality, distorted, watermark">
479
+ </div>
480
+
481
+ <div class="control-group">
482
+ <label>
483
  Duration (Frames)
484
  <span class="value-display" id="duration-val">129</span>
485
  </label>
486
+ <input type="range" id="duration" min="16" max="129" step="1" value="129">
487
+ </div>
488
 
489
+ <div class="control-group">
490
+ <label>
491
  Resolution
492
  <span class="value-display">512x896</span>
493
  </label>
494
+ <select id="resolution">
495
  <option value="512x896">512x896 (Native)</option>
496
  <option value="768x1344">768x1344 (HD)</option>
497
  </select>
498
+ </div>
499
 
500
+ <div class="control-group">
501
+ <label>
502
  Seed
503
  <button class="btn-secondary" style="padding: 4px 8px; font-size: 0.7rem;" id="random-seed">Random</button>
504
  </label>
505
+ <input type="text" id="seed" value="-1">
506
+ </div>
507
 
508
+ <div class="control-group" style="margin-top: auto;">
509
+ <button id="generate-btn" class="btn btn-primary">
510
  <i class="ph ph-play-circle"></i>
511
  Generate Video
512
  </button>
513
+ </div>
514
+ </aside>
515
 
516
+ <!-- PREVIEW AREA -->
517
+ <section class="preview-area">
518
+
519
+ <div class="video-container">
520
+ <!-- Placeholder -->
521
+ <div id="placeholder" class="placeholder-state">
522
+ <i class="ph ph-video-camera"></i>
523
+ <p>Enter a prompt and click Generate to start inference.</p>
524
+ </div>
525
+
526
+ <!-- Hidden Video Element -->
527
+ <video id="main-video" controls loop playsinline style="display: none;"></video>
528
+
529
+ <!-- Canvas for Procedural Generation (Replacing External Video) -->
530
+ <canvas id="generated-canvas" style="display: none;"></canvas>
531
+
532
+ <!-- Loading Overlay -->
533
+ <div id="loading-overlay" class="loading-overlay">
534
+ <div class="loader-ring"></div>
535
+ <div class="loading-text" id="loading-status">Initializing Environment...</div>
536
+ </div>
537
+ </div>
538
+
539
+ <!-- Gallery -->
540
+ <div class="gallery-section" id="gallery">
541
+ <!-- Items will be injected here -->
542
+ </div>
543
+
544
+ <!-- Terminal Console -->
545
+ <div class="console-panel" id="console-panel">
546
+ <div class="console-header">
547
+ <span><i class="ph ph-terminal-window"></i> Inference Console</span>
548
+ <button class="btn-secondary" style="padding: 2px 6px; font-size: 0.7rem;" id="clear-console">Clear</button>
549
+ </div>
550
+ <div class="console-body" id="console-body">
551
+ <div class="console-line info">> System initialized.</div>
552
+ <div class="console-line">> Waiting for user input...</div>
553
+ </div>
554
+ </div>
555
+ </section>
556
+ </main>
557
+
558
+ <script>
559
+ /**
560
+ * LTX-Video Simulation Logic
561
+ * Handles UI interactions, simulated terminal output, and canvas-based procedural generation.
562
+ */
563
 
564
  // --- DOM Elements ---
565
  const generateBtn = document.getElementById('generate-btn');
 
570
  const seedInput = document.getElementById('seed');
571
  const randomSeedBtn = document.getElementById('random-seed');
572
  const mainVideo = document.getElementById('main-video');
573
+ const canvas = document.getElementById('generated-canvas');
574
+ const ctx = canvas.getContext('2d');
575
  const placeholder = document.getElementById('placeholder');
576
  const loadingOverlay = document.getElementById('loading-overlay');
577
  const loadingStatus = document.getElementById('loading-status');
 
584
  // --- State ---
585
  let isGenerating = false;
586
  let isModelLoaded = false;
587
+ let animationId = null;
588
 
589
  // --- Utilities ---
590
  const log = (msg, type = 'info') => {
591
  const line = document.createElement('div');
592
  line.className = `console-line ${type}`;
 
593
  const time = new Date().toLocaleTimeString('en-US', {hour12: false, hour: "numeric", minute: "numeric", second: "numeric"});
594
  line.textContent = `[${time}] ${msg}`;
595
  consoleBody.appendChild(line);
 
600
  consolePanel.classList.remove('hidden');
601
  };
602
 
 
 
 
 
603
  // --- Event Listeners ---
604
 
605
  // Duration Slider Update
 
697
  'transformers',
698
  'accelerate',
699
  'safetensors',
700
+ 'xformers',
701
  'opencv-python'
702
  ];
703
 
 
712
  }
713
 
714
  const pkg = packages[pIndex];
 
715
  const delay = Math.random() * 800 + 400;
716
 
717
  setTimeout(() => {
 
726
  loadingOverlay.classList.remove('active');
727
  log('Inference complete. Saving video...', 'success');
728
 
729
+ // Stop any previous animation
730
+ if (animationId) cancelAnimationFrame(animationId);
731
+
732
+ // Setup Canvas
733
+ const container = document.querySelector('.video-container');
734
+ canvas.width = container.clientWidth;
735
+ canvas.height = container.clientHeight; // 16:9 aspect ratio handling is done in CSS
 
 
736
 
737
+ // Hide video, show canvas
738
+ mainVideo.style.display = 'none';
739
+ canvas.style.display = 'block';
740
  placeholder.style.display = 'none';
741
+
742
+ // Start the procedural generation
743
+ startProceduralAnimation();
744
+
745
  // Reset UI
746
  isGenerating = false;
747
  generateBtn.disabled = false;
 
750
  log('Video rendered successfully.', 'success');
751
  statusText.textContent = "Inference Complete";
752
 
753
+ addToGallery(canvas.toDataURL('image/jpeg'), prompt);
754
+ }
755
+
756
+ function startProceduralAnimation() {
757
+ let frame = 0;
758
+ const particles = [];
759
+
760
+ // Create random particles
761
+ for(let i=0; i<50; i++) {
762
+ particles.push({
763
+ x: Math.random() * canvas.width,
764
+ y: Math.random() * canvas.height,
765
+ vx: (Math.random() - 0.5) * 2,
766
+ vy: (Math.random() - 0.5) * 2,
767
+ size: Math.random() * 20 + 5,
768
+ color: `hsl(${Math.random() * 60 + 200}, 70%, 50%)` // Blue/Purple hues
769
+ });
770
+ }
771
+
772
+ function animate() {
773
+ // Clear with trail effect
774
+ ctx.fillStyle = 'rgba(0, 0, 0, 0.1)';
775
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
776
+
777
+ // Draw background gradient
778
+ const gradient = ctx.createLinearGradient(0, 0, canvas.width, canvas.height);
779
+ gradient.addColorStop(0, `hsl(${frame % 360}, 50%, 10%)`);
780
+ gradient.addColorStop(1, `hsl(${(frame + 60) % 360}, 50%, 10%)`);
781
+ ctx.fillStyle = gradient;
782
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
783
+
784
+ // Draw particles
785
+ particles.forEach(p => {
786
+ p.x += p.vx;
787
+ p.y += p.vy;
788
+
789
+ // Bounce off walls
790
+ if (p.x < 0 || p.x > canvas.width) p.vx *= -1;
791
+ if (p.y < 0 || p.y > canvas.height) p.vy *= -1;
792
+
793
+ ctx.beginPath();
794
+ ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2);
795
+ ctx.fillStyle = p.color;
796
+ ctx.fill();
797
+ ctx.closePath();
798
+ });
799
+
800
+ // Add some "noise" or "scanlines" for that cinematic feel
801
+ ctx.fillStyle = "rgba(255, 255, 255, 0.02)";
802
+ for (let i = 0; i < canvas.height; i += 4) {
803
+ ctx.fillRect(0, i, canvas.width, 1);
804
+ }
805
+
806
+ frame++;
807
+ animationId = requestAnimationFrame(animate);
808
+ }
809
+
810
+ animate();
811
  }
812
 
813
  function addToGallery(url, prompt) {
814
  const item = document.createElement('div');
815
  item.className = 'gallery-item';
816
 
 
817
  const thumb = document.createElement('img');
818
+ thumb.src = url;
819
 
820
  const badge = document.createElement('div');
821
  badge.className = 'badge';
 
824
  item.appendChild(thumb);
825
  item.appendChild(badge);
826
 
 
827
  item.addEventListener('click', () => {
828
+ // When clicking gallery, we reload the canvas with a new seed/randomness
829
+ startProceduralAnimation();
 
 
830
  log('Loaded video from gallery.', 'info');
831
  });
832
 
 
833
  gallery.insertBefore(item, gallery.firstChild);
834
  }
835
 
 
842
  setTimeout(() => {
843
  log('Connecting to Hugging Face Hub...', 'info');
844
  }, 500);
845
+ </script>
 
846
  </body>
847
  </html>