Mousco commited on
Commit
4e3d8a6
·
verified ·
1 Parent(s): 7b56591

Upload folder using huggingface_hub

Browse files
Files changed (1) hide show
  1. index.html +914 -834
index.html CHANGED
@@ -1,858 +1,938 @@
1
  <!DOCTYPE html>
2
  <html lang="fr">
3
- <head>
4
- <meta charset="UTF-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
6
- <title>Telegram Web (Simulation)</title>
7
-
8
- <!-- Importation de la police Roboto (Police standard de Telegram) -->
9
- <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap" rel="stylesheet">
10
-
11
- <!-- Importation des icônes FontAwesome -->
12
- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
13
-
14
- <style>
15
- /* =========================================
16
- VARIABLES CSS & RESET
17
- ========================================= */
18
- :root {
19
- --tg-blue: #3390ec;
20
- --tg-bg: #1d242d; /* Fond sombre style "Dark Mode" ou classique */
21
- --tg-sidebar-bg: #17212b;
22
- --tg-chat-bg: #0e1621;
23
- --tg-item-hover: #202b36;
24
- --tg-text-primary: #ffffff;
25
- --tg-text-secondary: #7f91a4;
26
- --tg-green-msg: #2b5278;
27
- --tg-white-msg: #182533;
28
- --tg-input-bg: #242f3d;
29
- --tg-border: #0e1621;
30
- --scrollbar-thumb: #2f3844;
31
- --header-height: 60px;
32
- }
33
 
34
- /* Mode Clair par défaut si préféré, mais je vais faire un thème sombre
35
- car c'est très populaire pour les "fake" modernes */
36
-
37
- * {
38
- box-sizing: border-box;
39
- margin: 0;
40
- padding: 0;
41
- outline: none;
42
- -webkit-tap-highlight-color: transparent;
43
- }
44
 
45
- body {
46
- font-family: 'Roboto', sans-serif;
47
- background-color: var(--tg-bg);
48
- color: var(--tg-text-primary);
49
- height: 100vh;
50
- overflow: hidden;
51
- display: flex;
52
- justify-content: center;
53
- align-items: center;
54
- }
55
 
56
- /* Lien obligatoire "Built with anycoder" */
57
- .anycoder-credit {
58
- position: absolute;
59
- top: 5px;
60
- left: 10px;
61
- font-size: 0.75rem;
62
- color: var(--tg-text-secondary);
63
- z-index: 1000;
64
- text-decoration: none;
65
- opacity: 0.7;
66
- transition: opacity 0.3s;
67
- }
68
- .anycoder-credit:hover {
69
- opacity: 1;
70
- color: var(--tg-blue);
71
- }
72
 
73
- /* =========================================
74
- STRUCTURE PRINCIPALE (GRID)
 
75
  ========================================= */
76
- .app-container {
77
- display: grid;
78
- grid-template-columns: 380px 1fr;
79
- width: 100%;
80
- height: 100%;
81
- max-width: 1600px;
82
- background-color: var(--tg-bg);
83
- box-shadow: 0 0 20px rgba(0,0,0,0.5);
84
- }
85
-
86
- /* =========================================
87
- SIDEBAR (LISTE DES CONTACTS)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
  ========================================= */
89
- .sidebar {
90
- background-color: var(--tg-sidebar-bg);
91
- border-right: 1px solid #000;
92
- display: flex;
93
- flex-direction: column;
94
- height: 100%;
95
- position: relative;
96
- z-index: 2;
97
- }
98
-
99
- /* Sidebar Header */
100
- .sidebar-header {
101
- height: var(--header-height);
102
- padding: 10px 15px;
103
- display: flex;
104
- align-items: center;
105
- gap: 15px;
106
- background-color: var(--tg-sidebar-bg);
107
- }
108
-
109
- .menu-btn {
110
- background: none;
111
- border: none;
112
- color: var(--tg-text-secondary);
113
- font-size: 1.2rem;
114
- cursor: pointer;
115
- transition: color 0.2s;
116
- }
117
- .menu-btn:hover { color: var(--tg-text-primary); }
118
-
119
- .search-bar {
120
- flex: 1;
121
- background-color: #242f3d;
122
- border-radius: 20px;
123
- padding: 8px 15px;
124
- display: flex;
125
- align-items: center;
126
- color: var(--tg-text-secondary);
127
- }
128
-
129
- .search-bar input {
130
- background: transparent;
131
- border: none;
132
- color: var(--tg-text-primary);
133
- width: 100%;
134
- margin-left: 10px;
135
- font-size: 0.95rem;
136
- }
137
-
138
- /* Contact List */
139
- .contact-list {
140
- flex: 1;
141
- overflow-y: auto;
142
- scrollbar-width: thin;
143
- scrollbar-color: var(--scrollbar-thumb) transparent;
144
- }
145
-
146
- .contact-list::-webkit-scrollbar {
147
- width: 6px;
148
- }
149
- .contact-list::-webkit-scrollbar-thumb {
150
- background-color: var(--scrollbar-thumb);
151
- border-radius: 3px;
152
- }
153
-
154
- .contact-item {
155
- display: flex;
156
- padding: 10px 15px;
157
- cursor: pointer;
158
- transition: background 0.2s;
159
- align-items: center;
160
- }
161
-
162
- .contact-item:hover {
163
- background-color: var(--tg-item-hover);
164
- }
165
-
166
- .contact-item.active {
167
- background-color: var(--tg-blue);
168
- }
169
-
170
- .contact-item.active .last-message,
171
- .contact-item.active .time,
172
- .contact-item.active .name {
173
- color: #fff;
174
- }
175
-
176
- .avatar {
177
- width: 50px;
178
- height: 50px;
179
- border-radius: 50%;
180
- object-fit: cover;
181
- margin-right: 15px;
182
- background-color: #ccc;
183
- }
184
-
185
- .contact-info {
186
- flex: 1;
187
- display: flex;
188
- flex-direction: column;
189
- justify-content: center;
190
- overflow: hidden;
191
- }
192
-
193
- .top-row {
194
- display: flex;
195
- justify-content: space-between;
196
- margin-bottom: 4px;
197
- }
198
-
199
- .name {
200
- font-weight: 500;
201
- font-size: 1rem;
202
- white-space: nowrap;
203
- overflow: hidden;
204
- text-overflow: ellipsis;
205
- }
206
-
207
- .time {
208
- font-size: 0.75rem;
209
- color: var(--tg-text-secondary);
210
- }
211
-
212
- .bottom-row {
213
- display: flex;
214
- justify-content: space-between;
215
- align-items: center;
216
- }
217
-
218
- .last-message {
219
- color: var(--tg-text-secondary);
220
- font-size: 0.85rem;
221
- white-space: nowrap;
222
- overflow: hidden;
223
- text-overflow: ellipsis;
224
- max-width: 200px;
225
- }
226
-
227
- .unread-badge {
228
- background-color: var(--tg-blue);
229
- color: white;
230
- font-size: 0.75rem;
231
- padding: 2px 6px;
232
- border-radius: 10px;
233
- min-width: 20px;
234
- text-align: center;
235
- font-weight: bold;
236
- }
237
-
238
- /* =========================================
239
- CHAT AREA
240
  ========================================= */
241
- .chat-area {
242
- background-color: var(--tg-chat-bg);
243
- background-image: radial-gradient(#1f2f40 1px, transparent 1px);
244
- background-size: 20px 20px; /* Pattern subtil */
245
- display: flex;
246
- flex-direction: column;
247
- position: relative;
248
- }
249
-
250
- /* Default Placeholder State */
251
- .empty-state {
252
- display: flex;
253
- flex-direction: column;
254
- align-items: center;
255
- justify-content: center;
256
- height: 100%;
257
- color: var(--tg-text-secondary);
258
- text-align: center;
259
- }
260
- .empty-state i {
261
- font-size: 5rem;
262
- margin-bottom: 20px;
263
- color: #2f3844;
264
- }
265
-
266
- /* Chat Header */
267
- .chat-header {
268
- height: var(--header-height);
269
- background-color: var(--tg-sidebar-bg);
270
- display: flex;
271
- align-items: center;
272
- padding: 0 15px;
273
- justify-content: space-between;
274
- border-bottom: 1px solid #000;
275
- z-index: 10;
276
- }
277
-
278
- .chat-header-info {
279
- display: flex;
280
- align-items: center;
281
- cursor: pointer;
282
- }
283
-
284
- .back-btn {
285
- display: none; /* Visible only on mobile */
286
- margin-right: 15px;
287
- font-size: 1.2rem;
288
- color: var(--tg-text-secondary);
289
- background: none;
290
- border: none;
291
- cursor: pointer;
292
- }
293
-
294
- .chat-header-text {
295
- display: flex;
296
- flex-direction: column;
297
- margin-left: 10px;
298
- }
299
-
300
- .chat-name {
301
- font-weight: 600;
302
- font-size: 1rem;
303
- }
304
- .chat-status {
305
- font-size: 0.75rem;
306
- color: var(--tg-text-secondary);
307
- }
308
-
309
- .chat-actions {
310
- display: flex;
311
- gap: 20px;
312
- color: var(--tg-text-secondary);
313
- }
314
- .chat-actions i {
315
- cursor: pointer;
316
- transition: color 0.2s;
317
- }
318
- .chat-actions i:hover {
319
- color: var(--tg-text-primary);
320
- }
321
-
322
- /* Messages List */
323
- .messages-container {
324
- flex: 1;
325
- padding: 20px;
326
- overflow-y: auto;
327
- display: flex;
328
- flex-direction: column;
329
- gap: 8px;
330
- }
331
-
332
- .messages-container::-webkit-scrollbar {
333
- width: 6px;
334
- }
335
- .messages-container::-webkit-scrollbar-thumb {
336
- background-color: rgba(0,0,0,0.2);
337
- }
338
-
339
- .message {
340
- max-width: 65%;
341
- padding: 8px 12px;
342
- border-radius: 12px;
343
- position: relative;
344
- font-size: 0.95rem;
345
- line-height: 1.4;
346
- word-wrap: break-word;
347
- animation: fadeIn 0.3s ease;
348
- }
349
-
350
- @keyframes fadeIn {
351
- from { opacity: 0; transform: translateY(10px); }
352
- to { opacity: 1; transform: translateY(0); }
353
- }
354
-
355
- .message.received {
356
- align-self: flex-start;
357
- background-color: var(--tg-white-msg);
358
- border-bottom-left-radius: 4px;
359
- }
360
-
361
- .message.sent {
362
- align-self: flex-end;
363
- background-color: var(--tg-green-msg);
364
- border-bottom-right-radius: 4px;
365
- }
366
-
367
- .msg-meta {
368
- display: flex;
369
- justify-content: flex-end;
370
- align-items: center;
371
- gap: 4px;
372
- font-size: 0.7rem;
373
- color: rgba(255,255,255,0.6);
374
- margin-top: 4px;
375
- float: right;
376
- margin-left: 8px;
377
- }
378
-
379
- /* Input Area */
380
- .input-area {
381
- min-height: 60px;
382
- background-color: var(--tg-sidebar-bg);
383
- padding: 10px 15px;
384
- display: flex;
385
- align-items: flex-end;
386
- gap: 15px;
387
- }
388
-
389
- .attach-btn, .emoji-btn {
390
- color: var(--tg-text-secondary);
391
- font-size: 1.4rem;
392
- background: none;
393
- border: none;
394
- cursor: pointer;
395
- padding-bottom: 5px;
396
- }
397
-
398
- .message-input-wrapper {
399
- flex: 1;
400
- background-color: var(--tg-input-bg);
401
- border-radius: 10px; /* Plus arrondi style moderne */
402
- padding: 10px 15px;
403
- display: flex;
404
- align-items: center;
405
- }
406
-
407
- .message-input {
408
- width: 100%;
409
- background: transparent;
410
- border: none;
411
- color: var(--tg-text-primary);
412
- font-size: 1rem;
413
- resize: none;
414
- max-height: 100px;
415
- font-family: inherit;
416
- }
417
-
418
- .send-btn {
419
- color: var(--tg-blue);
420
- font-size: 1.5rem;
421
- background: none;
422
- border: none;
423
- cursor: pointer;
424
- padding-bottom: 5px;
425
- transition: transform 0.1s;
426
- }
427
- .send-btn:active {
428
- transform: scale(0.9);
429
- }
430
-
431
- /* =========================================
432
- RESPONSIVE (MOBILE)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
433
  ========================================= */
434
- @media (max-width: 800px) {
435
- .app-container {
436
- grid-template-columns: 1fr;
437
- }
438
-
439
- .chat-area {
440
- position: absolute;
441
- top: 0;
442
- left: 0;
443
- width: 100%;
444
- height: 100%;
445
- transform: translateX(100%);
446
- transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
447
- z-index: 5;
448
- }
449
-
450
- .chat-area.active {
451
- transform: translateX(0);
452
- }
453
-
454
- .back-btn {
455
- display: block;
456
- }
457
-
458
- .sidebar {
459
- width: 100%;
460
- }
461
- }
462
- </style>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
463
  </head>
 
464
  <body>
465
 
466
- <!-- Lien crédit obligatoire -->
467
- <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" class="anycoder-credit">
468
- Built with anycoder <i class="fas fa-external-link-alt"></i>
469
- </a>
470
 
471
- <div class="app-container">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
472
 
473
- <!-- SIDEBAR -->
474
- <aside class="sidebar">
475
- <header class="sidebar-header">
476
- <button class="menu-btn"><i class="fas fa-bars"></i></button>
477
- <div class="search-bar">
478
- <i class="fas fa-search"></i>
479
- <input type="text" id="searchInput" placeholder="Rechercher">
480
- </div>
481
- </header>
482
-
483
- <div class="contact-list" id="contactList">
484
- <!-- Les contacts seront injectés ici par JS -->
485
- </div>
486
- </aside>
487
-
488
- <!-- CHAT AREA -->
489
- <main class="chat-area" id="chatArea">
490
- <!-- État vide (aucune sélection) -->
491
- <div class="empty-state" id="emptyState">
492
- <i class="fab fa-telegram"></i>
493
- <h2>Sélectionnez un chat</h2>
494
- <p>pour commencer la conversation</p>
495
  </div>
496
 
497
- <!-- Contenu du chat (caché par défaut) -->
498
- <div id="chatContent" style="display: none; flex-direction: column; height: 100%;">
499
- <header class="chat-header">
500
- <div class="chat-header-info">
501
- <button class="back-btn" id="backBtn"><i class="fas fa-arrow-left"></i></button>
502
- <img src="" alt="" class="avatar" id="headerAvatar" style="width: 40px; height: 40px;">
503
- <div class="chat-header-text">
504
- <span class="chat-name" id="headerName">Nom</span>
505
- <span class="chat-status" id="headerStatus">en ligne</span>
506
- </div>
507
- </div>
508
- <div class="chat-actions">
509
- <i class="fas fa-phone"></i>
510
- <i class="fas fa-video"></i>
511
- <i class="fas fa-search"></i>
512
- <i class="fas fa-ellipsis-v"></i>
513
- </div>
514
- </header>
515
-
516
- <div class="messages-container" id="messagesContainer">
517
- <!-- Messages injectés ici -->
518
- </div>
519
-
520
- <footer class="input-area">
521
- <button class="attach-btn"><i class="fas fa-paperclip"></i></button>
522
- <div class="message-input-wrapper">
523
- <input type="text" class="message-input" id="messageInput" placeholder="Écrivez un message..." autocomplete="off">
524
- </div>
525
- <button class="emoji-btn"><i class="far fa-smile"></i></button>
526
- <button class="send-btn" id="sendBtn"><i class="fas fa-paper-plane"></i></button>
527
- </footer>
528
  </div>
529
- </main>
530
- </div>
531
-
532
- <script>
533
- // =========================================
534
- // DONNÉES SIMULÉES (MOCK DATA)
535
- // =========================================
536
- const contactsData = [
537
- {
538
- id: 1,
539
- name: "Sophie Martin",
540
- avatar: "https://picsum.photos/seed/sophie/100/100",
541
- time: "14:30",
542
- unread: 2,
543
- lastMsg: "Tu as vu le nouveau projet ?",
544
- status: "en ligne",
545
- messages: [
546
- { id: 1, text: "Salut ! Comment ça va ?", type: "received", time: "14:25" },
547
- { id: 2, text: "Ça va super, et toi ?", type: "sent", time: "14:26", read: true },
548
- { id: 3, text: "Très bien. Tu as vu le nouveau projet ?", type: "received", time: "14:30" }
549
- ]
550
- },
551
- {
552
- id: 2,
553
- name: "Groupe Développement",
554
- avatar: "https://picsum.photos/seed/dev/100/100",
555
- time: "12:15",
556
- unread: 0,
557
- lastMsg: "Lucas: Le merge est ready",
558
- status: "5 membres",
559
- messages: [
560
- { id: 1, text: "Avez-vous pushé le code ?", type: "received", time: "12:00" },
561
- { id: 2, text: "Je le fais maintenant.", type: "sent", time: "12:05", read: true },
562
- { id: 3, text: "Lucas: Le merge est ready", type: "received", time: "12:15" }
563
- ]
564
- },
565
- {
566
- id: 3,
567
- name: "Marc Dupont",
568
- avatar: "https://picsum.photos/seed/marc/100/100",
569
- time: "Hier",
570
- unread: 0,
571
- lastMsg: "Ok, à demain alors !",
572
- status: "dernière connexion hier à 23h",
573
- messages: [
574
- { id: 1, text: "On se voit demain matin ?", type: "received", time: "Hier" },
575
- { id: 2, text: "Oui, sans problème.", type: "sent", time: "Hier", read: true },
576
- { id: 3, text: "Ok, à demain alors !", type: "received", time: "Hier" }
577
- ]
578
- },
579
- {
580
- id: 4,
581
- name: "Service Client",
582
- avatar: "https://picsum.photos/seed/support/100/100",
583
- time: "Mar",
584
- unread: 1,
585
- lastMsg: "Votre ticket a été résolu.",
586
- status: "bot",
587
- messages: [
588
- { id: 1, text: "Bonjour, comment pouvons-nous vous aider ?", type: "received", time: "Mar" },
589
- { id: 2, text: "J'ai un souci de connexion.", type: "sent", time: "Mar", read: false },
590
- { id: 3, text: "Votre ticket a été résolu.", type: "received", time: "Mar" }
591
- ]
592
- },
593
- {
594
- id: 5,
595
- name: "Julie Design",
596
- avatar: "https://picsum.photos/seed/julie/100/100",
597
- time: "Lun",
598
- unread: 0,
599
- lastMsg: "J'adore les nouvelles couleurs !",
600
- status: "en ligne",
601
- messages: [
602
- { id: 1, text: "Tu as vu les maquettes ?", type: "sent", time: "Lun", read: true },
603
- { id: 2, text: "J'adore les nouvelles couleurs !", type: "received", time: "Lun" }
604
- ]
605
- }
606
- ];
607
-
608
- let activeContactId = null;
609
-
610
- // =========================================
611
- // DOM ELEMENTS
612
- // =========================================
613
- const contactListEl = document.getElementById('contactList');
614
- const emptyStateEl = document.getElementById('emptyState');
615
- const chatContentEl = document.getElementById('chatContent');
616
- const chatAreaEl = document.getElementById('chatArea');
617
-
618
- // Header Chat
619
- const headerName = document.getElementById('headerName');
620
- const headerStatus = document.getElementById('headerStatus');
621
- const headerAvatar = document.getElementById('headerAvatar');
622
-
623
- // Messages
624
- const messagesContainer = document.getElementById('messagesContainer');
625
-
626
- // Input
627
- const messageInput = document.getElementById('messageInput');
628
- const sendBtn = document.getElementById('sendBtn');
629
- const searchInput = document.getElementById('searchInput');
630
- const backBtn = document.getElementById('backBtn');
631
-
632
- // =========================================
633
- // FONCTIONS
634
- // =========================================
635
-
636
- // 1. Rendu de la liste des contacts
637
- function renderContacts(filter = "") {
638
- contactListEl.innerHTML = "";
639
-
640
- const filteredContacts = contactsData.filter(c =>
641
- c.name.toLowerCase().includes(filter.toLowerCase())
642
- );
643
-
644
- filteredContacts.forEach(contact => {
645
- const li = document.createElement('div');
646
- li.className = `contact-item ${activeContactId === contact.id ? 'active' : ''}`;
647
- li.onclick = () => openChat(contact.id);
648
-
649
- let unreadHtml = contact.unread > 0
650
- ? `<span class="unread-badge">${contact.unread}</span>`
651
- : '';
652
-
653
- li.innerHTML = `
654
- <img src="${contact.avatar}" alt="${contact.name}" class="avatar">
655
- <div class="contact-info">
656
- <div class="top-row">
657
- <span class="name">${contact.name}</span>
658
- <span class="time">${contact.time}</span>
659
- </div>
660
- <div class="bottom-row">
661
- <span class="last-message">${contact.lastMsg}</span>
662
- ${unreadHtml}
663
- </div>
664
- </div>
665
- `;
666
- contactListEl.appendChild(li);
667
- });
668
- }
669
 
670
- // 2. Ouvrir un chat
671
- function openChat(id) {
672
- activeContactId = id;
673
- const contact = contactsData.find(c => c.id === id);
674
-
675
- // UI Updates
676
- renderContacts(searchInput.value); // Refresh pour mettre la classe active
677
- emptyStateEl.style.display = 'none';
678
- chatContentEl.style.display = 'flex';
679
-
680
- // Mobile: Slide effect
681
- chatAreaEl.classList.add('active');
682
-
683
- // Header Info
684
- headerName.textContent = contact.name;
685
- headerStatus.textContent = contact.status;
686
- headerAvatar.src = contact.avatar;
687
-
688
- // Reset unread count
689
- contact.unread = 0;
690
- renderContacts(searchInput.value);
691
-
692
- // Render Messages
693
- renderMessages(contact.messages);
694
-
695
- // Focus input
696
- messageInput.focus();
697
- }
698
-
699
- // 3. Afficher les messages
700
- function renderMessages(messages) {
701
- messagesContainer.innerHTML = "";
702
-
703
- // Date divider (fake)
704
- const dateDiv = document.createElement('div');
705
- dateDiv.style.textAlign = 'center';
706
- dateDiv.style.margin = '10px 0';
707
- dateDiv.innerHTML = '<span style="background:rgba(0,0,0,0.2); padding:4px 10px; border-radius:10px; font-size:0.75rem; color:#aaa;">Aujourd\'hui</span>';
708
- messagesContainer.appendChild(dateDiv);
709
-
710
- messages.forEach(msg => {
711
- appendMessageToDOM(msg);
712
- });
713
- scrollToBottom();
714
- }
715
-
716
- // 4. Ajouter un message au DOM
717
- function appendMessageToDOM(msg) {
718
- const div = document.createElement('div');
719
- div.className = `message ${msg.type}`;
720
-
721
- const checks = msg.type === 'sent'
722
- ? (msg.read ? '<i class="fas fa-check-double"></i>' : '<i class="fas fa-check"></i>')
723
- : '';
724
-
725
- div.innerHTML = `
726
- ${msg.text}
727
- <span class="msg-meta">
728
- ${msg.time} ${checks}
729
- </span>
730
- `;
731
- messagesContainer.appendChild(div);
732
- }
733
-
734
- // 5. Scroll en bas
735
- function scrollToBottom() {
736
- messagesContainer.scrollTop = messagesContainer.scrollHeight;
737
- }
738
 
739
- // 6. Envoyer un message
740
- function sendMessage() {
741
- const text = messageInput.value.trim();
742
- if (!text || !activeContactId) return;
743
-
744
- const contact = contactsData.find(c => c.id === activeContactId);
745
- const now = new Date();
746
- const timeString = now.getHours().toString().padStart(2, '0') + ':' + now.getMinutes().toString().padStart(2, '0');
747
-
748
- // Création objet message
749
- const newMsg = {
750
- id: Date.now(),
751
- text: text,
752
- type: 'sent',
753
- time: timeString,
754
- read: false
755
- };
756
-
757
- // Update Data
758
- contact.messages.push(newMsg);
759
- contact.lastMsg = text;
760
- contact.time = timeString;
761
-
762
- // Update UI
763
- appendMessageToDOM(newMsg);
764
- messageInput.value = "";
765
- scrollToBottom();
766
- renderContacts(searchInput.value); // Update preview in sidebar
767
-
768
- // Simulation réponse automatique
769
- simulateReply(contact);
770
- }
771
 
772
- // 7. Simulation de réponse (Bot)
773
- function simulateReply(contact) {
774
- const replies = [
775
- "C'est intéressant !",
776
- "Je suis d'accord.",
777
- "Tu pourrais m'en dire plus ?",
778
- "Ok, je note ça.",
779
- "Haha, trop drôle ! 😂",
780
- "Je suis occupé là, je te réponds plus tard."
781
- ];
782
-
783
- // Random delay entre 1s et 3s
784
- const delay = Math.floor(Math.random() * 2000) + 1000;
785
-
786
- setTimeout(() => {
787
- // On vérifie si on est toujours sur le même chat
788
- if (activeContactId === contact.id) {
789
- const replyText = replies[Math.floor(Math.random() * replies.length)];
790
- const now = new Date();
791
- const timeString = now.getHours().toString().padStart(2, '0') + ':' + now.getMinutes().toString().padStart(2, '0');
792
-
793
- const replyMsg = {
794
- id: Date.now(),
795
- text: replyText,
796
- type: 'received',
797
- time: timeString
798
- };
799
-
800
- contact.messages.push(replyMsg);
801
- contact.lastMsg = replyText;
802
- contact.time = timeString;
803
-
804
- appendMessageToDOM(replyMsg);
805
- scrollToBottom();
806
-
807
- // Marquer le message envoyé précédemment comme "lu"
808
- const lastSent = contact.messages.filter(m => m.type === 'sent').pop();
809
- if(lastSent) lastSent.read = true;
810
-
811
- // Rafraichir la vue (pour les checks bleus)
812
- // Dans une vraie app on ne rafraichirait pas tout, mais ici c'est simple
813
- renderMessages(contact.messages);
814
- renderContacts(searchInput.value);
815
-
816
- } else {
817
- // Si l'utilisateur a changé de chat, on incrémente les non-lus
818
- contact.unread++;
819
- const replyText = "Nouveau message reçu";
820
- contact.lastMsg = replyText;
821
- renderContacts(searchInput.value);
822
- }
823
- }, delay);
824
- }
825
 
826
- // =========================================
827
- // EVENT LISTENERS
828
- // =========================================
829
-
830
- // Clic sur envoyer
831
- sendBtn.addEventListener('click', sendMessage);
832
-
833
- // Touche Entrée
834
- messageInput.addEventListener('keypress', (e) => {
835
- if (e.key === 'Enter') sendMessage();
836
- });
837
-
838
- // Recherche
839
- searchInput.addEventListener('input', (e) => {
840
- renderContacts(e.target.value);
841
- });
842
-
843
- // Bouton retour (Mobile)
844
- backBtn.addEventListener('click', () => {
845
- chatAreaEl.classList.remove('active');
846
- setTimeout(() => {
847
- activeContactId = null;
848
- // Optionnel: désélectionner visuellement dans la sidebar
849
- renderContacts(searchInput.value);
850
- }, 300);
851
- });
852
-
853
- // Initialisation
854
- renderContacts();
855
-
856
- </script>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
857
  </body>
 
858
  </html>
 
1
  <!DOCTYPE html>
2
  <html lang="fr">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
 
4
+ <head>
5
+ <meta charset="UTF-8">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
7
+ <title>AI Video Generator Studio</title>
 
 
 
 
 
 
8
 
9
+ <!-- Importation de la police Inter (Moderne et clean) -->
10
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
 
 
 
 
 
 
 
 
11
 
12
+ <!-- Importation des icônes FontAwesome -->
13
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
 
15
+ <style>
16
+ /* =========================================
17
+ VARIABLES CSS & RESET
18
  ========================================= */
19
+ :root {
20
+ --bg-body: #09090b;
21
+ --bg-sidebar: #121214;
22
+ --bg-card: #18181b;
23
+ --bg-input: #27272a;
24
+ --primary: #8b5cf6; /* Violet */
25
+ --primary-hover: #7c3aed;
26
+ --accent: #ec4899; /* Rose */
27
+ --text-main: #ffffff;
28
+ --text-secondary: #a1a1aa;
29
+ --border: #27272a;
30
+ --success: #10b981;
31
+ --radius-md: 12px;
32
+ --radius-lg: 16px;
33
+ --header-height: 70px;
34
+ }
35
+
36
+ * {
37
+ box-sizing: border-box;
38
+ margin: 0;
39
+ padding: 0;
40
+ outline: none;
41
+ -webkit-tap-highlight-color: transparent;
42
+ }
43
+
44
+ body {
45
+ font-family: 'Inter', sans-serif;
46
+ background-color: var(--bg-body);
47
+ color: var(--text-main);
48
+ height: 100vh;
49
+ overflow: hidden; /* Prevent body scroll, handle inside containers */
50
+ display: flex;
51
+ }
52
+
53
+ /* Lien obligatoire "Built with anycoder" */
54
+ .anycoder-credit {
55
+ position: fixed;
56
+ bottom: 10px;
57
+ left: 50%;
58
+ transform: translateX(-50%);
59
+ font-size: 0.8rem;
60
+ color: var(--text-secondary);
61
+ z-index: 1000;
62
+ text-decoration: none;
63
+ opacity: 0.6;
64
+ transition: opacity 0.3s;
65
+ background: rgba(0,0,0,0.5);
66
+ padding: 4px 12px;
67
+ border-radius: 20px;
68
+ backdrop-filter: blur(4px);
69
+ }
70
+
71
+ .anycoder-credit:hover {
72
+ opacity: 1;
73
+ color: var(--primary);
74
+ }
75
+
76
+ /* =========================================
77
+ LAYOUT PRINCIPAL
78
  ========================================= */
79
+ .app-container {
80
+ display: flex;
81
+ width: 100%;
82
+ height: 100%;
83
+ }
84
+
85
+ /* SIDEBAR */
86
+ .sidebar {
87
+ width: 260px;
88
+ background-color: var(--bg-sidebar);
89
+ border-right: 1px solid var(--border);
90
+ display: flex;
91
+ flex-direction: column;
92
+ padding: 20px;
93
+ transition: transform 0.3s ease;
94
+ }
95
+
96
+ .logo {
97
+ font-size: 1.5rem;
98
+ font-weight: 700;
99
+ color: var(--text-main);
100
+ margin-bottom: 40px;
101
+ display: flex;
102
+ align-items: center;
103
+ gap: 10px;
104
+ }
105
+
106
+ .logo i {
107
+ color: var(--primary);
108
+ }
109
+
110
+ .nav-menu {
111
+ list-style: none;
112
+ flex: 1;
113
+ }
114
+
115
+ .nav-item {
116
+ margin-bottom: 8px;
117
+ }
118
+
119
+ .nav-link {
120
+ display: flex;
121
+ align-items: center;
122
+ gap: 12px;
123
+ padding: 12px 16px;
124
+ color: var(--text-secondary);
125
+ text-decoration: none;
126
+ border-radius: var(--radius-md);
127
+ transition: all 0.2s;
128
+ font-weight: 500;
129
+ }
130
+
131
+ .nav-link:hover, .nav-link.active {
132
+ background-color: var(--bg-input);
133
+ color: var(--text-main);
134
+ }
135
+
136
+ .nav-link.active {
137
+ border-left: 3px solid var(--primary);
138
+ }
139
+
140
+ .user-profile {
141
+ display: flex;
142
+ align-items: center;
143
+ gap: 10px;
144
+ padding-top: 20px;
145
+ border-top: 1px solid var(--border);
146
+ }
147
+
148
+ .avatar-small {
149
+ width: 36px;
150
+ height: 36px;
151
+ border-radius: 50%;
152
+ background: linear-gradient(45deg, var(--primary), var(--accent));
153
+ }
154
+
155
+ /* MAIN CONTENT */
156
+ .main-content {
157
+ flex: 1;
158
+ display: flex;
159
+ flex-direction: column;
160
+ overflow: hidden;
161
+ position: relative;
162
+ }
163
+
164
+ .top-bar {
165
+ height: var(--header-height);
166
+ border-bottom: 1px solid var(--border);
167
+ display: flex;
168
+ align-items: center;
169
+ justify-content: space-between;
170
+ padding: 0 30px;
171
+ }
172
+
173
+ .page-title {
174
+ font-size: 1.2rem;
175
+ font-weight: 600;
176
+ }
177
+
178
+ .credits {
179
+ background: rgba(139, 92, 246, 0.1);
180
+ color: var(--primary);
181
+ padding: 6px 12px;
182
+ border-radius: 20px;
183
+ font-size: 0.85rem;
184
+ font-weight: 600;
185
+ }
186
+
187
+ /* SCROLLABLE AREA */
188
+ .content-scroll {
189
+ flex: 1;
190
+ overflow-y: auto;
191
+ padding: 30px;
192
+ }
193
+
194
+ /* =========================================
195
+ GENERATOR SECTION
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
196
  ========================================= */
197
+ .generator-grid {
198
+ display: grid;
199
+ grid-template-columns: 1fr 400px;
200
+ gap: 30px;
201
+ max-width: 1400px;
202
+ margin: 0 auto;
203
+ }
204
+
205
+ /* INPUT AREA */
206
+ .input-card {
207
+ background-color: var(--bg-card);
208
+ border: 1px solid var(--border);
209
+ border-radius: var(--radius-lg);
210
+ padding: 24px;
211
+ display: flex;
212
+ flex-direction: column;
213
+ gap: 20px;
214
+ }
215
+
216
+ .form-group {
217
+ display: flex;
218
+ flex-direction: column;
219
+ gap: 8px;
220
+ }
221
+
222
+ .form-label {
223
+ font-size: 0.9rem;
224
+ color: var(--text-secondary);
225
+ font-weight: 500;
226
+ }
227
+
228
+ textarea.prompt-input {
229
+ width: 100%;
230
+ height: 120px;
231
+ background-color: var(--bg-input);
232
+ border: 1px solid var(--border);
233
+ border-radius: var(--radius-md);
234
+ padding: 16px;
235
+ color: var(--text-main);
236
+ font-family: inherit;
237
+ font-size: 1rem;
238
+ resize: none;
239
+ transition: border-color 0.2s;
240
+ }
241
+
242
+ textarea.prompt-input:focus {
243
+ border-color: var(--primary);
244
+ }
245
+
246
+ /* OPTIONS CONTROLS */
247
+ .controls-row {
248
+ display: flex;
249
+ gap: 20px;
250
+ flex-wrap: wrap;
251
+ }
252
+
253
+ .control-group {
254
+ flex: 1;
255
+ min-width: 140px;
256
+ }
257
+
258
+ select, .custom-select-trigger {
259
+ width: 100%;
260
+ background-color: var(--bg-input);
261
+ border: 1px solid var(--border);
262
+ color: var(--text-main);
263
+ padding: 10px 14px;
264
+ border-radius: var(--radius-md);
265
+ cursor: pointer;
266
+ appearance: none; /* Hide default arrow for custom styling if needed */
267
+ }
268
+
269
+ /* Style Chips for Ratios */
270
+ .ratio-options {
271
+ display: flex;
272
+ background: var(--bg-input);
273
+ padding: 4px;
274
+ border-radius: var(--radius-md);
275
+ gap: 4px;
276
+ }
277
+
278
+ .ratio-btn {
279
+ flex: 1;
280
+ background: transparent;
281
+ border: none;
282
+ color: var(--text-secondary);
283
+ padding: 8px;
284
+ border-radius: 8px;
285
+ cursor: pointer;
286
+ font-size: 0.85rem;
287
+ transition: all 0.2s;
288
+ }
289
+
290
+ .ratio-btn.active {
291
+ background-color: var(--bg-card);
292
+ color: var(--text-main);
293
+ box-shadow: 0 2px 4px rgba(0,0,0,0.2);
294
+ }
295
+
296
+ .generate-btn {
297
+ background: linear-gradient(135deg, var(--primary), var(--accent));
298
+ color: white;
299
+ border: none;
300
+ padding: 16px;
301
+ border-radius: var(--radius-md);
302
+ font-size: 1rem;
303
+ font-weight: 600;
304
+ cursor: pointer;
305
+ transition: transform 0.1s, opacity 0.2s;
306
+ display: flex;
307
+ align-items: center;
308
+ justify-content: center;
309
+ gap: 10px;
310
+ }
311
+
312
+ .generate-btn:hover {
313
+ opacity: 0.9;
314
+ }
315
+
316
+ .generate-btn:active {
317
+ transform: scale(0.98);
318
+ }
319
+
320
+ .generate-btn:disabled {
321
+ background: var(--bg-input);
322
+ color: var(--text-secondary);
323
+ cursor: not-allowed;
324
+ }
325
+
326
+ /* PREVIEW AREA */
327
+ .preview-card {
328
+ background-color: var(--bg-card);
329
+ border: 1px solid var(--border);
330
+ border-radius: var(--radius-lg);
331
+ padding: 24px;
332
+ display: flex;
333
+ flex-direction: column;
334
+ align-items: center;
335
+ justify-content: center;
336
+ min-height: 400px;
337
+ position: relative;
338
+ overflow: hidden;
339
+ }
340
+
341
+ .video-placeholder {
342
+ width: 100%;
343
+ aspect-ratio: 16/9;
344
+ background-color: #000;
345
+ border-radius: var(--radius-md);
346
+ display: flex;
347
+ align-items: center;
348
+ justify-content: center;
349
+ position: relative;
350
+ overflow: hidden;
351
+ }
352
+
353
+ /* Animation when "video" is playing */
354
+ .video-active {
355
+ background-image: url('https://picsum.photos/seed/ai-video/800/450');
356
+ background-size: cover;
357
+ background-position: center;
358
+ }
359
+
360
+ .video-overlay {
361
+ position: absolute;
362
+ inset: 0;
363
+ background: linear-gradient(to top, rgba(0,0,0,0.6), transparent);
364
+ display: flex;
365
+ align-items: flex-end;
366
+ padding: 15px;
367
+ }
368
+
369
+ .play-btn {
370
+ width: 60px;
371
+ height: 60px;
372
+ background: rgba(255,255,255,0.2);
373
+ backdrop-filter: blur(5px);
374
+ border-radius: 50%;
375
+ display: flex;
376
+ align-items: center;
377
+ justify-content: center;
378
+ color: white;
379
+ font-size: 1.5rem;
380
+ border: none;
381
+ cursor: pointer;
382
+ transition: transform 0.2s;
383
+ }
384
+
385
+ .play-btn:hover {
386
+ transform: scale(1.1);
387
+ background: var(--primary);
388
+ }
389
+
390
+ /* Loading State */
391
+ .loading-overlay {
392
+ position: absolute;
393
+ inset: 0;
394
+ background: rgba(15, 23, 42, 0.9);
395
+ display: flex;
396
+ flex-direction: column;
397
+ align-items: center;
398
+ justify-content: center;
399
+ z-index: 10;
400
+ display: none; /* Hidden by default */
401
+ }
402
+
403
+ .loading-overlay.active {
404
+ display: flex;
405
+ }
406
+
407
+ .spinner {
408
+ width: 40px;
409
+ height: 40px;
410
+ border: 3px solid rgba(139, 92, 246, 0.3);
411
+ border-radius: 50%;
412
+ border-top-color: var(--primary);
413
+ animation: spin 1s ease-in-out infinite;
414
+ margin-bottom: 20px;
415
+ }
416
+
417
+ @keyframes spin {
418
+ to { transform: rotate(360deg); }
419
+ }
420
+
421
+ .progress-bar {
422
+ width: 80%;
423
+ height: 6px;
424
+ background-color: var(--bg-input);
425
+ border-radius: 3px;
426
+ overflow: hidden;
427
+ margin-bottom: 10px;
428
+ }
429
+
430
+ .progress-fill {
431
+ height: 100%;
432
+ width: 0%;
433
+ background: linear-gradient(90deg, var(--primary), var(--accent));
434
+ transition: width 0.3s linear;
435
+ }
436
+
437
+ .status-text {
438
+ font-size: 0.9rem;
439
+ color: var(--text-secondary);
440
+ animation: pulse 1.5s infinite;
441
+ }
442
+
443
+ @keyframes pulse {
444
+ 0%, 100% { opacity: 1; }
445
+ 50% { opacity: 0.5; }
446
+ }
447
+
448
+ /* HISTORY SECTION */
449
+ .history-section {
450
+ margin-top: 40px;
451
+ max-width: 1400px;
452
+ margin-left: auto;
453
+ margin-right: auto;
454
+ }
455
+
456
+ .section-header {
457
+ display: flex;
458
+ justify-content: space-between;
459
+ align-items: center;
460
+ margin-bottom: 20px;
461
+ }
462
+
463
+ .history-grid {
464
+ display: grid;
465
+ grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
466
+ gap: 20px;
467
+ }
468
+
469
+ .history-item {
470
+ background-color: var(--bg-card);
471
+ border-radius: var(--radius-md);
472
+ overflow: hidden;
473
+ border: 1px solid var(--border);
474
+ transition: transform 0.2s;
475
+ cursor: pointer;
476
+ }
477
+
478
+ .history-item:hover {
479
+ transform: translateY(-5px);
480
+ border-color: var(--primary);
481
+ }
482
+
483
+ .history-thumb {
484
+ width: 100%;
485
+ height: 140px;
486
+ background-color: #000;
487
+ background-size: cover;
488
+ background-position: center;
489
+ position: relative;
490
+ }
491
+
492
+ .history-info {
493
+ padding: 12px;
494
+ }
495
+
496
+ .history-prompt {
497
+ font-size: 0.85rem;
498
+ color: var(--text-main);
499
+ white-space: nowrap;
500
+ overflow: hidden;
501
+ text-overflow: ellipsis;
502
+ margin-bottom: 4px;
503
+ }
504
+
505
+ .history-meta {
506
+ font-size: 0.75rem;
507
+ color: var(--text-secondary);
508
+ display: flex;
509
+ justify-content: space-between;
510
+ }
511
+
512
+ /* =========================================
513
+ RESPONSIVE DESIGN
514
  ========================================= */
515
+ @media (max-width: 900px) {
516
+ .generator-grid {
517
+ grid-template-columns: 1fr;
518
+ }
519
+
520
+ .preview-card {
521
+ min-height: 300px;
522
+ order: -1; /* Put preview on top on mobile */
523
+ }
524
+ }
525
+
526
+ @media (max-width: 768px) {
527
+ .sidebar {
528
+ position: fixed;
529
+ left: -100%;
530
+ top: 0;
531
+ bottom: 0;
532
+ z-index: 100;
533
+ width: 80%;
534
+ box-shadow: 10px 0 20px rgba(0,0,0,0.5);
535
+ }
536
+
537
+ .sidebar.active {
538
+ left: 0;
539
+ }
540
+
541
+ .menu-toggle {
542
+ display: block;
543
+ font-size: 1.2rem;
544
+ background: none;
545
+ border: none;
546
+ color: var(--text-main);
547
+ cursor: pointer;
548
+ margin-right: 15px;
549
+ }
550
+
551
+ .content-scroll {
552
+ padding: 15px;
553
+ }
554
+ }
555
+
556
+ @media (min-width: 769px) {
557
+ .menu-toggle {
558
+ display: none;
559
+ }
560
+ }
561
+ </style>
562
  </head>
563
+
564
  <body>
565
 
566
+ <!-- Lien crédit obligatoire -->
567
+ <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" class="anycoder-credit">
568
+ Built with anycoder <i class="fas fa-external-link-alt"></i>
569
+ </a>
570
 
571
+ <div class="app-container">
572
+
573
+ <!-- SIDEBAR -->
574
+ <aside class="sidebar" id="sidebar">
575
+ <div class="logo">
576
+ <i class="fas fa-video"></i>
577
+ <span>VideoGen AI</span>
578
+ </div>
579
+
580
+ <ul class="nav-menu">
581
+ <li class="nav-item">
582
+ <a href="#" class="nav-link active">
583
+ <i class="fas fa-wand-magic-sparkles"></i>
584
+ <span>Créer</span>
585
+ </a>
586
+ </li>
587
+ <li class="nav-item">
588
+ <a href="#" class="nav-link">
589
+ <i class="fas fa-clock-rotate-left"></i>
590
+ <span>Historique</span>
591
+ </a>
592
+ </li>
593
+ <li class="nav-item">
594
+ <a href="#" class="nav-link">
595
+ <i class="fas fa-bookmark"></i>
596
+ <span>Favoris</span>
597
+ </a>
598
+ </li>
599
+ <li class="nav-item">
600
+ <a href="#" class="nav-link">
601
+ <i class="fas fa-gear"></i>
602
+ <span>Paramètres</span>
603
+ </a>
604
+ </li>
605
+ </ul>
606
+
607
+ <div class="user-profile">
608
+ <div class="avatar-small"></div>
609
+ <div>
610
+ <div style="font-size: 0.9rem; font-weight: 600;">Mon Compte</div>
611
+ <div style="font-size: 0.75rem; color: var(--text-secondary);">Plan Pro</div>
612
+ </div>
613
+ </div>
614
+ </aside>
615
+
616
+ <!-- MAIN CONTENT -->
617
+ <main class="main-content">
618
+ <!-- Top Bar -->
619
+ <header class="top-bar">
620
+ <div style="display: flex; align-items: center;">
621
+ <button class="menu-toggle" id="menuToggle"><i class="fas fa-bars"></i></button>
622
+ <h1 class="page-title">Nouvelle Vidéo</h1>
623
+ </div>
624
+ <div class="credits">
625
+ <i class="fas fa-coins"></i> 120 Crédits restants
626
+ </div>
627
+ </header>
628
+
629
+ <!-- Scrollable Content -->
630
+ <div class="content-scroll">
631
 
632
+ <div class="generator-grid">
633
+
634
+ <!-- INPUT COLUMN -->
635
+ <div class="input-card">
636
+ <div class="form-group">
637
+ <label class="form-label">Votre prompt</label>
638
+ <textarea class="prompt-input" id="promptInput" placeholder="Décrivez la vidéo que vous souhaitez générer... (ex: Un astronaute marchant sur Mars au coucher du soleil, style cinématographique 4K)"></textarea>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
639
  </div>
640
 
641
+ <div class="controls-row">
642
+ <div class="control-group">
643
+ <label class="form-label">Style</label>
644
+ <select id="styleSelect">
645
+ <option value="cinematic">Cinématique</option>
646
+ <option value="anime">Anime</option>
647
+ <option value="3d">Rendu 3D</option>
648
+ <option value="cyberpunk">Cyberpunk</option>
649
+ <option value="realistic">Réaliste</option>
650
+ </select>
651
+ </div>
652
+
653
+ <div class="control-group">
654
+ <label class="form-label">Durée</label>
655
+ <select id="durationSelect">
656
+ <option value="5">5 secondes</option>
657
+ <option value="10">10 secondes</option>
658
+ <option value="15">15 secondes</option>
659
+ </select>
660
+ </div>
 
 
 
 
 
 
 
 
 
 
 
661
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
662
 
663
+ <div class="form-group">
664
+ <label class="form-label">Format</label>
665
+ <div class="ratio-options">
666
+ <button class="ratio-btn active" data-ratio="16:9">16:9</button>
667
+ <button class="ratio-btn" data-ratio="9:16">9:16</button>
668
+ <button class="ratio-btn" data-ratio="1:1">1:1</button>
669
+ </div>
670
+ </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
671
 
672
+ <button class="generate-btn" id="generateBtn">
673
+ <i class="fas fa-sparkles"></i> Générer la vidéo
674
+ </button>
675
+ </div>
676
+
677
+ <!-- PREVIEW COLUMN -->
678
+ <div class="preview-card">
679
+ <div class="loading-overlay" id="loadingOverlay">
680
+ <div class="spinner"></div>
681
+ <div class="progress-bar">
682
+ <div class="progress-fill" id="progressFill"></div>
683
+ </div>
684
+ <div class="status-text" id="statusText">Initialisation...</div>
685
+ </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
686
 
687
+ <div class="video-placeholder" id="videoPlaceholder">
688
+ <div style="text-align: center; color: var(--text-secondary);">
689
+ <i class="fas fa-film" style="font-size: 3rem; margin-bottom: 15px; opacity: 0.5;"></i>
690
+ <p>Aperçu de la vidéo</p>
691
+ </div>
692
+ </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
693
 
694
+ <div id="videoControls" style="width: 100%; margin-top: 20px; display: none; justify-content: space-between; align-items: center;">
695
+ <div style="font-size: 0.9rem; color: var(--text-secondary);">
696
+ <i class="fas fa-check-circle" style="color: var(--success);"></i> Généré avec succès
697
+ </div>
698
+ <button style="background: var(--bg-input); border: none; color: white; padding: 8px 16px; border-radius: 6px; cursor: pointer;">
699
+ <i class="fas fa-download"></i> Télécharger
700
+ </button>
701
+ </div>
702
+ </div>
703
+
704
+ </div>
705
+
706
+ <!-- HISTORY GRID -->
707
+ <div class="history-section">
708
+ <div class="section-header">
709
+ <h2>Générations récentes</h2>
710
+ <a href="#" style="color: var(--primary); text-decoration: none; font-size: 0.9rem;">Voir tout</a>
711
+ </div>
712
+
713
+ <div class="history-grid" id="historyGrid">
714
+ <!-- Items injectés via JS -->
715
+ </div>
716
+ </div>
717
+
718
+ </div>
719
+ </main>
720
+ </div>
721
+
722
+ <script>
723
+ // =========================================
724
+ // DOM ELEMENTS
725
+ // =========================================
726
+ const promptInput = document.getElementById('promptInput');
727
+ const generateBtn = document.getElementById('generateBtn');
728
+ const loadingOverlay = document.getElementById('loadingOverlay');
729
+ const progressFill = document.getElementById('progressFill');
730
+ const statusText = document.getElementById('statusText');
731
+ const videoPlaceholder = document.getElementById('videoPlaceholder');
732
+ const videoControls = document.getElementById('videoControls');
733
+ const historyGrid = document.getElementById('historyGrid');
734
+ const ratioBtns = document.querySelectorAll('.ratio-btn');
735
+ const menuToggle = document.getElementById('menuToggle');
736
+ const sidebar = document.getElementById('sidebar');
737
+
738
+ // =========================================
739
+ // STATE & DATA
740
+ // =========================================
741
+ let isGenerating = false;
742
+ const historyData = [
743
+ {
744
+ id: 1,
745
+ prompt: "Ville futuriste avec voitures volantes, nuit",
746
+ style: "Cyberpunk",
747
+ duration: "5s",
748
+ thumb: "https://picsum.photos/seed/cyber1/400/250"
749
+ },
750
+ {
751
+ id: 2,
752
+ prompt: "Chat buvant du café dans un café parisien",
753
+ style: "Réaliste",
754
+ duration: "10s",
755
+ thumb: "https://picsum.photos/seed/cat2/400/250"
756
+ },
757
+ {
758
+ id: 3,
759
+ prompt: "Dragon cracheur de feu sur une montagne",
760
+ style: "Cinématique",
761
+ duration: "5s",
762
+ thumb: "https://picsum.photos/seed/dragon3/400/250"
763
+ }
764
+ ];
765
+
766
+ // =========================================
767
+ // FUNCTIONS
768
+ // =========================================
769
+
770
+ // 1. Gestion du menu mobile
771
+ menuToggle.addEventListener('click', () => {
772
+ sidebar.classList.toggle('active');
773
+ });
774
+
775
+ // Clic hors sidebar pour fermer
776
+ document.addEventListener('click', (e) => {
777
+ if (window.innerWidth <= 768) {
778
+ if (!sidebar.contains(e.target) && !menuToggle.contains(e.target)) {
779
+ sidebar.classList.remove('active');
780
+ }
781
+ }
782
+ });
783
+
784
+ // 2. Gestion des boutons de ratio
785
+ ratioBtns.forEach(btn => {
786
+ btn.addEventListener('click', () => {
787
+ ratioBtns.forEach(b => b.classList.remove('active'));
788
+ btn.classList.add('active');
789
+
790
+ // Changer l'aspect ratio du placeholder visuellement
791
+ const ratio = btn.dataset.ratio;
792
+ if(ratio === '16:9') videoPlaceholder.style.aspectRatio = '16/9';
793
+ if(ratio === '9:16') videoPlaceholder.style.aspectRatio = '9/16';
794
+ if(ratio === '1:1') videoPlaceholder.style.aspectRatio = '1/1';
795
+ });
796
+ });
797
+
798
+ // 3. Rendu de l'historique
799
+ function renderHistory() {
800
+ historyGrid.innerHTML = '';
801
+ historyData.forEach(item => {
802
+ const div = document.createElement('div');
803
+ div.className = 'history-item';
804
+ div.innerHTML = `
805
+ <div class="history-thumb" style="background-image: url('${item.thumb}');">
806
+ <div style="position:absolute; bottom:8px; right:8px; background:rgba(0,0,0,0.7); color:white; font-size:0.7rem; padding:2px 6px; border-radius:4px;">${item.duration}</div>
807
+ </div>
808
+ <div class="history-info">
809
+ <div class="history-prompt">${item.prompt}</div>
810
+ <div class="history-meta">
811
+ <span>${item.style}</span>
812
+ <span>Il y a 2h</span>
813
+ </div>
814
+ </div>
815
+ `;
816
+ div.onclick = () => loadVideoToPreview(item);
817
+ historyGrid.appendChild(div);
818
+ });
819
+ }
820
+
821
+ // 4. Charger une vidéo depuis l'historique
822
+ function loadVideoToPreview(item) {
823
+ videoPlaceholder.innerHTML = `
824
+ <div class="video-overlay">
825
+ <h3 style="color:white; margin-bottom:10px;">${item.prompt}</h3>
826
+ <button class="play-btn"><i class="fas fa-play"></i></button>
827
+ </div>
828
+ `;
829
+ videoPlaceholder.style.backgroundImage = `url('${item.thumb}')`;
830
+ videoPlaceholder.classList.add('video-active');
831
+ videoControls.style.display = 'flex';
832
+
833
+ // Reset placeholder text logic
834
+ videoPlaceholder.querySelector('div[style*="text-align"]').style.display = 'none';
835
+ }
836
+
837
+ // 5. Simulation de la génération
838
+ generateBtn.addEventListener('click', () => {
839
+ const prompt = promptInput.value.trim();
840
+
841
+ if (!prompt) {
842
+ promptInput.focus();
843
+ promptInput.style.borderColor = 'var(--accent)';
844
+ setTimeout(() => promptInput.style.borderColor = 'var(--border)', 2000);
845
+ return;
846
+ }
847
+
848
+ if (isGenerating) return;
849
+ isGenerating = true;
850
+
851
+ // UI Updates
852
+ generateBtn.disabled = true;
853
+ generateBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Génération...';
854
+ loadingOverlay.classList.add('active');
855
+ videoControls.style.display = 'none';
856
+ videoPlaceholder.classList.remove('video-active');
857
+ videoPlaceholder.style.backgroundImage = 'none';
858
+
859
+ // Reset Placeholder to default but hide text while loading
860
+ videoPlaceholder.innerHTML = `
861
+ <div style="text-align: center; color: var(--text-secondary); opacity: 0;">
862
+ <i class="fas fa-film" style="font-size: 3rem; margin-bottom: 15px;"></i>
863
+ <p>Aperçu de la vidéo</p>
864
+ </div>
865
+ `;
866
+
867
+ // Simulation des étapes
868
+ const steps = [
869
+ { pct: 0, text: "Analyse du prompt..." },
870
+ { pct: 20, text: "Génération des keyframes..." },
871
+ { pct: 45, text: "Interpolation des mouvements..." },
872
+ { pct: 70, text: "Rendu des textures..." },
873
+ { pct: 90, text: "Encodage final..." },
874
+ { pct: 100, text: "Terminé !" }
875
+ ];
876
+
877
+ let stepIndex = 0;
878
+
879
+ const interval = setInterval(() => {
880
+ if (stepIndex >= steps.length) {
881
+ clearInterval(interval);
882
+ finishGeneration(prompt);
883
+ return;
884
+ }
885
+
886
+ const step = steps[stepIndex];
887
+ progressFill.style.width = step.pct + '%';
888
+ statusText.textContent = step.text;
889
+ stepIndex++;
890
+
891
+ }, 600); // 600ms par étape (environ 3.6s total)
892
+ });
893
+
894
+ function finishGeneration(prompt) {
895
+ isGenerating = false;
896
+
897
+ // Masquer le loader
898
+ loadingOverlay.classList.remove('active');
899
+
900
+ // Réafficher le bouton
901
+ generateBtn.disabled = false;
902
+ generateBtn.innerHTML = '<i class="fas fa-sparkles"></i> Générer la vidéo';
903
+
904
+ // Afficher le résultat (Simulation)
905
+ const randomSeed = Math.floor(Math.random() * 1000);
906
+ const thumbUrl = `https://picsum.photos/seed/${randomSeed}/800/450`;
907
+
908
+ videoPlaceholder.style.backgroundImage = `url('${thumbUrl}')`;
909
+ videoPlaceholder.classList.add('video-active');
910
+ videoPlaceholder.innerHTML = `
911
+ <div class="video-overlay">
912
+ <h3 style="color:white; margin-bottom:10px; font-size:0.9rem;">Résultat généré</h3>
913
+ <button class="play-btn"><i class="fas fa-play"></i></button>
914
+ </div>
915
+ `;
916
+
917
+ videoControls.style.display = 'flex';
918
+
919
+ // Ajouter à l'historique
920
+ const newItem = {
921
+ id: Date.now(),
922
+ prompt: prompt,
923
+ style: document.getElementById('styleSelect').value,
924
+ duration: document.getElementById('durationSelect').value + 's',
925
+ thumb: thumbUrl
926
+ };
927
+
928
+ historyData.unshift(newItem); // Ajouter au début
929
+ renderHistory();
930
+ }
931
+
932
+ // Init
933
+ renderHistory();
934
+
935
+ </script>
936
  </body>
937
+
938
  </html>