Docfile commited on
Commit
f6160ea
·
verified ·
1 Parent(s): 886c92a

Update templates/index.html

Browse files
Files changed (1) hide show
  1. templates/index.html +448 -653
templates/index.html CHANGED
@@ -3,7 +3,7 @@
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>Chatbot Gemini Avancé</title>
7
  <style>
8
  * {
9
  margin: 0;
@@ -14,219 +14,169 @@
14
  body {
15
  font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
16
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
17
- height: 100vh;
18
- overflow: hidden;
 
 
 
19
  }
20
 
21
- .container {
22
- display: flex;
23
- height: 100vh;
24
- max-width: 1400px;
25
- margin: 0 auto;
26
  background: rgba(255, 255, 255, 0.95);
27
- box-shadow: 0 8px 32px rgba(31, 38, 135, 0.37);
28
- backdrop-filter: blur(8px);
 
 
 
 
29
  }
30
 
31
- .sidebar {
32
- width: 300px;
33
- background: rgba(255, 255, 255, 0.1);
34
- border-right: 1px solid rgba(255, 255, 255, 0.2);
35
  padding: 20px;
36
- overflow-y: auto;
37
- }
38
-
39
- .sidebar h2 {
40
- color: #333;
41
- margin-bottom: 20px;
42
- font-size: 1.3em;
43
  text-align: center;
 
44
  }
45
 
46
- .control-group {
47
- margin-bottom: 20px;
48
- padding: 15px;
49
- background: rgba(255, 255, 255, 0.8);
50
- border-radius: 10px;
51
- box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
52
- }
53
-
54
- .control-group h3 {
55
- color: #444;
56
  margin-bottom: 10px;
57
- font-size: 1em;
58
- border-bottom: 2px solid #667eea;
59
- padding-bottom: 5px;
60
  }
61
 
62
- .form-group {
63
- margin-bottom: 15px;
 
 
 
 
64
  }
65
 
66
- label {
67
- display: block;
68
- margin-bottom: 5px;
69
- font-weight: 500;
70
- color: #555;
71
  }
72
 
73
- select, input[type="file"] {
74
- width: 100%;
75
- padding: 8px;
76
- border: 2px solid #ddd;
77
- border-radius: 5px;
78
- font-size: 14px;
79
- transition: border-color 0.3s;
80
  }
81
 
82
- select:focus, input[type="file"]:focus {
83
- border-color: #667eea;
84
- outline: none;
 
85
  }
86
 
87
- .checkbox-group {
88
- display: flex;
89
- align-items: center;
90
- margin-bottom: 10px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
91
  }
92
 
93
- .checkbox-group input[type="checkbox"] {
94
- margin-right: 8px;
95
- transform: scale(1.2);
96
  }
97
 
98
- .checkbox-group label {
99
- margin-bottom: 0;
100
- cursor: pointer;
101
  }
102
 
103
- .btn {
104
- background: linear-gradient(45deg, #667eea, #764ba2);
 
105
  color: white;
106
- border: none;
107
- padding: 10px 15px;
108
- border-radius: 5px;
109
  cursor: pointer;
110
  font-size: 14px;
111
- transition: transform 0.2s, box-shadow 0.2s;
112
- width: 100%;
113
  }
114
 
115
- .btn:hover {
 
116
  transform: translateY(-2px);
117
- box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4);
118
- }
119
-
120
- .btn-danger {
121
- background: linear-gradient(45deg, #ff6b6b, #ee5a52);
122
- }
123
-
124
- .btn-danger:hover {
125
- box-shadow: 0 4px 15px rgba(255, 107, 107, 0.4);
126
- }
127
-
128
- .main-content {
129
- flex: 1;
130
- display: flex;
131
- flex-direction: column;
132
- height: 100vh;
133
- }
134
-
135
- .chat-header {
136
- background: rgba(255, 255, 255, 0.9);
137
- padding: 20px;
138
- border-bottom: 1px solid rgba(255, 255, 255, 0.2);
139
- text-align: center;
140
  }
141
 
142
- .chat-header h1 {
143
- color: #333;
144
- font-size: 1.5em;
145
- margin-bottom: 5px;
146
- }
147
-
148
- .chat-status {
149
- color: #666;
150
- font-size: 0.9em;
151
- }
152
-
153
- .chat-container {
154
  flex: 1;
155
  overflow-y: auto;
156
  padding: 20px;
157
- background: rgba(255, 255, 255, 0.05);
158
- }
159
-
160
- .message {
161
- margin-bottom: 20px;
162
  display: flex;
163
- animation: slideIn 0.3s ease-out;
164
- }
165
-
166
- @keyframes slideIn {
167
- from {
168
- opacity: 0;
169
- transform: translateY(20px);
170
- }
171
- to {
172
- opacity: 1;
173
- transform: translateY(0);
174
- }
175
- }
176
-
177
- .message.user {
178
- justify-content: flex-end;
179
  }
180
 
181
- .message-content {
182
- max-width: 70%;
183
  padding: 15px 20px;
184
  border-radius: 18px;
185
- position: relative;
186
- box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
 
187
  }
188
 
189
- .message.user .message-content {
190
- background: linear-gradient(135deg, #667eea, #764ba2);
191
  color: white;
192
- border-bottom-right-radius: 5px;
 
193
  }
194
 
195
- .message.assistant .message-content {
196
- background: rgba(255, 255, 255, 0.9);
197
  color: #333;
198
- border-bottom-left-radius: 5px;
199
- border: 1px solid rgba(255, 255, 255, 0.2);
200
- }
201
-
202
- .message-time {
203
- font-size: 0.8em;
204
- opacity: 0.7;
205
- margin-top: 5px;
206
  }
207
 
208
- .thinking-section {
209
- background: rgba(255, 235, 59, 0.1);
210
- border: 1px solid rgba(255, 235, 59, 0.3);
 
 
 
211
  border-radius: 10px;
212
- padding: 10px;
213
- margin-bottom: 10px;
214
  font-style: italic;
215
- color: #666;
216
  }
217
 
218
- .thinking-section::before {
219
- content: "🤔 Réflexion du modèle:";
220
- font-weight: bold;
221
- color: #f57f17;
222
- display: block;
223
- margin-bottom: 5px;
224
  }
225
 
226
  .input-container {
227
  padding: 20px;
228
- background: rgba(255, 255, 255, 0.9);
229
- border-top: 1px solid rgba(255, 255, 255, 0.2);
230
  }
231
 
232
  .input-row {
@@ -235,282 +185,235 @@
235
  align-items: flex-end;
236
  }
237
 
238
- .message-input {
239
  flex: 1;
240
- padding: 12px 15px;
241
- border: 2px solid #ddd;
 
 
 
 
 
 
242
  border-radius: 25px;
243
  font-size: 16px;
244
  resize: none;
245
- min-height: 50px;
246
- max-height: 120px;
247
- transition: border-color 0.3s;
248
  }
249
 
250
  .message-input:focus {
251
  border-color: #667eea;
252
- outline: none;
253
  }
254
 
255
- .send-btn {
256
- background: linear-gradient(45deg, #667eea, #764ba2);
257
- color: white;
 
 
 
258
  border: none;
259
  border-radius: 50%;
260
- width: 50px;
261
- height: 50px;
 
262
  cursor: pointer;
 
263
  display: flex;
264
  align-items: center;
265
  justify-content: center;
266
- transition: transform 0.2s, box-shadow 0.2s;
267
  }
268
 
269
- .send-btn:hover:not(:disabled) {
270
- transform: scale(1.1);
271
- box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4);
272
  }
273
 
274
- .send-btn:disabled {
275
  background: #ccc;
276
  cursor: not-allowed;
277
  }
278
 
279
- .file-upload-area {
280
- margin-bottom: 10px;
281
- padding: 10px;
282
- border: 2px dashed #ddd;
283
- border-radius: 10px;
284
- text-align: center;
285
- transition: border-color 0.3s;
286
  }
287
 
288
- .file-upload-area.dragover {
289
- border-color: #667eea;
290
- background: rgba(102, 126, 234, 0.1);
 
 
 
 
 
 
291
  }
292
 
293
- .uploaded-file {
294
- background: rgba(102, 126, 234, 0.1);
295
- border: 1px solid #667eea;
296
- border-radius: 5px;
297
- padding: 5px 10px;
298
- margin: 5px 0;
299
- display: flex;
300
- justify-content: space-between;
301
- align-items: center;
302
  }
303
 
304
- .remove-file {
305
- background: #ff6b6b;
306
- color: white;
307
- border: none;
308
- border-radius: 50%;
309
- width: 20px;
310
- height: 20px;
311
- cursor: pointer;
312
- font-size: 12px;
313
  }
314
 
315
  .typing-indicator {
316
  display: none;
317
- padding: 10px 20px;
318
- background: rgba(255, 255, 255, 0.9);
319
- border-radius: 18px;
320
- margin-bottom: 20px;
321
- width: fit-content;
322
- }
323
-
324
- .typing-indicator.show {
325
- display: block;
326
- animation: slideIn 0.3s ease-out;
327
  }
328
 
329
  .typing-dots {
330
  display: flex;
331
- gap: 4px;
332
  }
333
 
334
  .typing-dots span {
335
  width: 8px;
336
  height: 8px;
337
- background: #667eea;
338
  border-radius: 50%;
339
- animation: typing 1.4s infinite;
 
340
  }
341
 
342
- .typing-dots span:nth-child(2) {
343
- animation-delay: 0.2s;
344
- }
345
 
346
- .typing-dots span:nth-child(3) {
347
- animation-delay: 0.4s;
 
348
  }
349
 
350
- @keyframes typing {
351
- 0%, 60%, 100% {
352
- transform: translateY(0);
353
- opacity: 0.4;
354
- }
355
- 30% {
356
- transform: translateY(-10px);
357
- opacity: 1;
358
- }
359
  }
360
 
361
- .error-message {
362
- background: #ffebee;
363
- color: #c62828;
364
- border: 1px solid #ffcdd2;
365
- border-radius: 5px;
366
- padding: 10px;
367
  margin: 10px 0;
368
  }
369
 
370
- /* Responsive Design */
371
- @media (max-width: 768px) {
372
- .container {
373
- flex-direction: column;
374
- }
 
 
 
375
 
376
- .sidebar {
377
- width: 100%;
378
- height: auto;
379
- max-height: 40vh;
380
- order: 2;
381
- }
382
 
383
- .main-content {
384
- order: 1;
385
- height: 60vh;
386
- }
387
 
388
- .message-content {
389
- max-width: 85%;
390
- }
391
  }
392
 
393
- /* Code styling */
394
- pre {
395
- background: #f5f5f5;
396
- border-radius: 5px;
397
- padding: 10px;
398
- overflow-x: auto;
399
- margin: 10px 0;
400
  }
401
 
402
- code {
403
- background: #f5f5f5;
404
- padding: 2px 4px;
405
- border-radius: 3px;
406
- font-family: 'Courier New', monospace;
 
 
 
 
 
 
 
 
 
 
407
  }
408
  </style>
409
  </head>
410
  <body>
411
- <div class="container">
412
- <!-- Sidebar -->
413
- <div class="sidebar">
414
- <h2>🤖 Configuration</h2>
415
-
416
- <!-- Modèle -->
417
- <div class="control-group">
418
- <h3>Modèle</h3>
419
- <div class="form-group">
420
- <label for="model-select">Modèle Gemini:</label>
421
- <select id="model-select">
422
- {% for name, value in models.items() %}
423
- <option value="{{ value }}" {% if name == "Gemini 2.5 Flash" %}selected{% endif %}>{{ name }}</option>
424
- {% endfor %}
425
- </select>
426
- </div>
427
- </div>
428
-
429
- <!-- Thinking Options -->
430
- <div class="control-group">
431
- <h3>🧠 Options de Réflexion</h3>
432
- <div class="checkbox-group">
433
- <input type="checkbox" id="thinking-enabled" checked>
434
- <label for="thinking-enabled">Activer le raisonnement</label>
435
- </div>
436
- <div class="checkbox-group">
437
- <input type="checkbox" id="include-thoughts">
438
- <label for="include-thoughts">Afficher les pensées</label>
439
  </div>
440
- <p style="font-size: 0.8em; color: #666; margin-top: 5px;">
441
- 🔧 Tous les outils avancés sont actifs par défaut:<br>
442
- • Exécution de code<br>
443
- • Recherche Google<br>
444
- • Contexte URL
445
- </p>
446
- </div>
447
-
448
- <!-- File Upload -->
449
- <div class="control-group">
450
- <h3>📁 Upload de fichier</h3>
451
- <div class="file-upload-area" id="file-upload-area">
452
- <p>Glissez un fichier ici ou cliquez pour sélectionner</p>
453
- <input type="file" id="file-input" style="display: none;" accept=".png,.jpg,.jpeg,.pdf,.mp4,.mov,.txt,.csv,.json">
454
- </div>
455
- <div id="uploaded-files"></div>
456
- </div>
457
-
458
- <!-- Actions -->
459
- <div class="control-group">
460
- <h3>Actions</h3>
461
- <button class="btn btn-danger" onclick="resetChat()">🗑️ Réinitialiser</button>
462
  </div>
463
  </div>
464
 
465
- <!-- Main Content -->
466
- <div class="main-content">
467
- <!-- Header -->
468
- <div class="chat-header">
469
- <h1>💬 Chatbot Gemini Avancé</h1>
470
- <div class="chat-status" id="chat-status">Prêt à discuter</div>
471
- </div>
472
-
473
- <!-- Chat Container -->
474
- <div class="chat-container" id="chat-container">
475
- <div class="message assistant">
476
- <div class="message-content">
477
- <div>👋 Bonjour ! Je suis votre assistant Gemini avec toutes les fonctionnalités avancées activées :</div>
478
- <div style="margin-top: 10px;">
479
- ✅ <strong>Exécution de code Python</strong><br>
480
- ✅ <strong>Recherche Google en temps réel</strong><br>
481
- ✅ <strong>Analyse d'URLs</strong><br>
482
- ✅ <strong>Traitement de fichiers (images, PDF, vidéos, textes)</strong><br>
483
- ✅ <strong>Raisonnement avancé avec Thinking</strong>
484
- </div>
485
- <div style="margin-top: 10px; font-style: italic;">
486
- Comment puis-je vous aider aujourd'hui ?
487
- </div>
488
- <div class="message-time">Just now</div>
489
- </div>
490
- </div>
491
  </div>
 
492
 
493
- <!-- Typing Indicator -->
494
- <div class="typing-indicator" id="typing-indicator">
495
- <div class="typing-dots">
496
- <span></span>
497
- <span></span>
498
- <span></span>
499
- </div>
500
  </div>
 
501
 
502
- <!-- Input Area -->
503
- <div class="input-container">
504
- <div class="input-row">
 
 
 
 
 
505
  <textarea
506
- id="message-input"
507
  class="message-input"
508
- placeholder="Tapez votre message... (Shift+Entrée pour nouvelle ligne)"
509
- rows="1"></textarea>
510
- <button id="send-btn" class="send-btn">
511
- <svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor">
512
- <path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z"/>
513
- </svg>
 
514
  </button>
515
  </div>
516
  </div>
@@ -518,356 +421,248 @@
518
  </div>
519
 
520
  <script>
521
- // Variables globales
522
  let currentFile = null;
523
- let isWaitingResponse = false;
524
-
525
- // Éléments DOM
526
- const chatContainer = document.getElementById('chat-container');
527
- const messageInput = document.getElementById('message-input');
528
- const sendBtn = document.getElementById('send-btn');
529
- const typingIndicator = document.getElementById('typing-indicator');
530
- const fileInput = document.getElementById('file-input');
531
- const fileUploadArea = document.getElementById('file-upload-area');
532
- const uploadedFilesDiv = document.getElementById('uploaded-files');
533
- const chatStatus = document.getElementById('chat-status');
534
-
535
- // Configuration du drag & drop
536
- fileUploadArea.addEventListener('click', () => fileInput.click());
537
- fileUploadArea.addEventListener('dragover', (e) => {
538
- e.preventDefault();
539
- fileUploadArea.classList.add('dragover');
540
- });
541
- fileUploadArea.addEventListener('dragleave', () => {
542
- fileUploadArea.classList.remove('dragover');
543
- });
544
- fileUploadArea.addEventListener('drop', (e) => {
545
- e.preventDefault();
546
- fileUploadArea.classList.remove('dragover');
547
- const files = e.dataTransfer.files;
548
- if (files.length > 0) {
549
- handleFileUpload(files[0]);
550
- }
551
- });
552
 
553
- // Configuration de l'input file
554
- fileInput.addEventListener('change', (e) => {
555
- if (e.target.files.length > 0) {
556
- handleFileUpload(e.target.files[0]);
557
- }
558
- });
559
-
560
- // Auto-resize textarea
561
- messageInput.addEventListener('input', function() {
562
- this.style.height = 'auto';
563
- this.style.height = Math.min(this.scrollHeight, 120) + 'px';
564
- });
565
 
566
- // Gestion des touches
567
- messageInput.addEventListener('keydown', (e) => {
568
- if (e.key === 'Enter' && !e.shiftKey) {
569
- e.preventDefault();
570
  sendMessage();
571
  }
572
- });
573
-
574
- // Bouton d'envoi
575
- sendBtn.addEventListener('click', sendMessage);
576
-
577
- // Fonctions utilitaires
578
- function formatTime() {
579
- return new Date().toLocaleTimeString('fr-FR', {
580
- hour: '2-digit',
581
- minute: '2-digit'
582
- });
583
- }
584
-
585
- function scrollToBottom() {
586
- chatContainer.scrollTop = chatContainer.scrollHeight;
587
- }
588
-
589
- function showTyping() {
590
- typingIndicator.classList.add('show');
591
- scrollToBottom();
592
  }
593
 
594
- function hideTyping() {
595
- typingIndicator.classList.remove('show');
596
- }
597
 
598
- function updateChatStatus(status) {
599
- chatStatus.textContent = status;
600
- }
601
-
602
- // Gestion de l'upload de fichiers
603
- async function handleFileUpload(file) {
604
  const formData = new FormData();
605
  formData.append('file', file);
606
 
607
- try {
608
- updateChatStatus('Upload du fichier...');
609
- const response = await fetch('/upload_file', {
610
- method: 'POST',
611
- body: formData
612
- });
613
-
614
- const result = await response.json();
615
-
616
- if (result.success) {
617
- currentFile = {
618
- id: result.file_id,
619
- name: result.filename,
620
- type: result.mime_type
621
- };
622
-
623
- displayUploadedFile(result.filename, result.mime_type);
624
- updateChatStatus('Fichier uploadé avec succès');
625
  } else {
626
- showError('Erreur lors de l\'upload: ' + result.error);
627
- updateChatStatus('Erreur d\'upload');
628
  }
629
- } catch (error) {
630
- console.error('Erreur upload:', error);
631
- showError('Erreur lors de l\'upload du fichier');
632
- updateChatStatus('Erreur d\'upload');
633
- }
634
- }
635
-
636
- function displayUploadedFile(filename, mimeType) {
637
- const fileIcon = getFileIcon(mimeType);
638
- uploadedFilesDiv.innerHTML = `
639
- <div class="uploaded-file">
640
- <span>${fileIcon} ${filename}</span>
641
- <button class="remove-file" onclick="removeFile()">×</button>
642
- </div>
643
- `;
644
- }
645
-
646
- function getFileIcon(mimeType) {
647
- if (mimeType.startsWith('image/')) return '🖼️';
648
- if (mimeType === 'application/pdf') return '📄';
649
- if (mimeType.startsWith('video/')) return '🎥';
650
- if (mimeType.includes('text')) return '📝';
651
- return '📎';
652
  }
653
 
654
  function removeFile() {
655
  currentFile = null;
656
- uploadedFilesDiv.innerHTML = '';
657
- fileInput.value = '';
658
- updateChatStatus('Fichier retiré');
659
  }
660
 
661
- // Gestion des messages
662
- function addMessage(content, isUser, hasThoughts = false, thoughtsContent = '') {
663
- const messageDiv = document.createElement('div');
664
- messageDiv.className = `message ${isUser ? 'user' : 'assistant'}`;
665
-
666
- let messageHTML = `
667
- <div class="message-content">
668
- ${hasThoughts ? `<div class="thinking-section">${thoughtsContent}</div>` : ''}
669
- <div>${formatMessage(content)}</div>
670
- <div class="message-time">${formatTime()}</div>
671
- </div>
672
- `;
673
 
674
- messageDiv.innerHTML = messageHTML;
675
- chatContainer.appendChild(messageDiv);
676
- scrollToBottom();
677
- return messageDiv;
678
- }
679
 
680
- function formatMessage(content) {
681
- // Conversion basique markdown vers HTML
682
- return content
683
- .replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>')
684
- .replace(/\*(.*?)\*/g, '<em>$1</em>')
685
- .replace(/`([^`]+)`/g, '<code>$1</code>')
686
- .replace(/```([^```]+)```/g, '<pre><code>$1</code></pre>')
687
- .replace(/\n/g, '<br>');
688
- }
689
-
690
- function showError(message) {
691
- const errorDiv = document.createElement('div');
692
- errorDiv.className = 'error-message';
693
- errorDiv.textContent = message;
694
- chatContainer.appendChild(errorDiv);
695
- scrollToBottom();
696
 
697
- setTimeout(() => {
698
- errorDiv.remove();
699
- }, 5000);
700
- }
701
-
702
- // Envoi de messages
703
- async function sendMessage() {
704
- if (isWaitingResponse) return;
705
 
706
- const message = messageInput.value.trim();
707
- if (!message) return;
708
-
709
- const model = document.getElementById('model-select').value;
710
- const thinkingEnabled = document.getElementById('thinking-enabled').checked;
711
- const includeThoughts = document.getElementById('include-thoughts').checked;
712
-
713
- // Afficher le message de l'utilisateur
714
- addMessage(message, true);
715
 
716
- // Nettoyer l'input
717
  messageInput.value = '';
718
  messageInput.style.height = 'auto';
719
-
720
- // Désactiver l'envoi
721
- isWaitingResponse = true;
722
- sendBtn.disabled = true;
723
- showTyping();
724
- updateChatStatus('Génération de la réponse...');
725
-
726
- try {
727
- const endpoint = currentFile ? '/send_message_with_file' : '/send_message';
728
- const requestBody = {
729
- message: message,
730
- model: model,
731
- thinking_enabled: thinkingEnabled,
732
- include_thoughts: includeThoughts
733
- };
734
-
735
- if (currentFile) {
736
- requestBody.file_id = currentFile.id;
737
- }
738
 
739
- const response = await fetch(endpoint, {
740
- method: 'POST',
741
- headers: {
742
- 'Content-Type': 'application/json',
743
- },
744
- body: JSON.stringify(requestBody)
745
- });
 
 
 
 
 
746
 
 
 
 
 
 
 
 
 
 
747
  if (!response.ok) {
748
- throw new Error('Erreur de réseau');
749
  }
750
 
751
  const reader = response.body.getReader();
752
  const decoder = new TextDecoder();
753
-
754
- let assistantMessage = null;
755
- let fullResponse = '';
756
- let thoughtsContent = '';
757
-
758
- hideTyping();
759
-
760
- while (true) {
761
- const { done, value } = await reader.read();
762
- if (done) break;
763
-
764
- const chunk = decoder.decode(value);
765
- const lines = chunk.split('\n');
766
-
767
- for (const line of lines) {
768
- if (line.startsWith('data: ')) {
769
- try {
770
- const data = JSON.parse(line.slice(6));
771
-
772
- if (data.error) {
773
- showError(data.error);
774
- break;
775
- }
776
-
777
- if (data.type === 'thought') {
778
- thoughtsContent += data.content;
779
- } else if (data.type === 'text') {
780
- if (!assistantMessage) {
781
- assistantMessage = addMessage('', false, includeThoughts, thoughtsContent);
782
- }
783
-
784
- fullResponse += data.content;
785
 
786
- // Mettre à jour le contenu du message
787
- const messageContent = assistantMessage.querySelector('.message-content > div:last-child');
788
- if (messageContent) {
789
- messageContent.innerHTML = formatMessage(fullResponse) + '<div class="message-time">' + formatTime() + '</div>';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
790
  }
791
-
792
- scrollToBottom();
793
- } else if (data.type === 'end') {
794
- // Message terminé
795
- updateChatStatus('Prêt à discuter');
796
  }
797
- } catch (e) {
798
- console.error('Erreur parsing JSON:', e);
799
  }
800
  }
801
- }
802
- }
803
 
804
- // Nettoyer le fichier après envoi
805
- if (currentFile) {
806
- removeFile();
807
  }
808
 
809
- } catch (error) {
810
- console.error('Erreur:', error);
811
- hideTyping();
812
- showError('Erreur lors de l\'envoi du message: ' + error.message);
813
- updateChatStatus('Erreur de communication');
814
- } finally {
815
- isWaitingResponse = false;
816
- sendBtn.disabled = false;
817
- }
818
  }
819
 
820
- // Reset du chat
821
- async function resetChat() {
822
- if (!confirm('Êtes-vous sûr de vouloir réinitialiser la conversation ?')) {
823
- return;
824
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
825
 
826
- try {
827
- const response = await fetch('/reset_chat', {
 
 
 
 
 
 
 
 
 
 
828
  method: 'POST',
829
  headers: {
830
- 'Content-Type': 'application/json',
831
- }
832
- });
833
-
834
- const result = await response.json();
835
-
836
- if (result.success) {
837
- // Nettoyer l'interface
838
- chatContainer.innerHTML = `
839
  <div class="message assistant">
840
- <div class="message-content">
841
- <div>👋 Nouvelle conversation démarrée ! Toutes les fonctionnalités avancées sont toujours actives.</div>
842
- <div class="message-time">${formatTime()}</div>
843
- </div>
844
  </div>
845
  `;
846
-
847
  removeFile();
848
- updateChatStatus('Conversation réinitialisée');
849
- } else {
850
- showError('Erreur lors de la réinitialisation');
851
- }
852
- } catch (error) {
853
- console.error('Erreur reset:', error);
854
- showError('Erreur lors de la réinitialisation');
855
  }
856
  }
857
 
858
- // Initialisation
859
  document.addEventListener('DOMContentLoaded', function() {
860
- updateChatStatus('Prêt à discuter');
861
- messageInput.focus();
862
- });
863
-
864
- // Gestion de la reconnexion
865
- window.addEventListener('online', () => {
866
- updateChatStatus('Reconnecté');
867
- });
868
-
869
- window.addEventListener('offline', () => {
870
- updateChatStatus('Hors ligne');
871
  });
872
  </script>
873
  </body>
 
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Gemini AI Assistant</title>
7
  <style>
8
  * {
9
  margin: 0;
 
14
  body {
15
  font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
16
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
17
+ min-height: 100vh;
18
+ display: flex;
19
+ justify-content: center;
20
+ align-items: center;
21
+ padding: 20px;
22
  }
23
 
24
+ .chat-container {
25
+ width: 100%;
26
+ max-width: 800px;
27
+ height: 90vh;
 
28
  background: rgba(255, 255, 255, 0.95);
29
+ border-radius: 20px;
30
+ box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
31
+ display: flex;
32
+ flex-direction: column;
33
+ overflow: hidden;
34
+ backdrop-filter: blur(10px);
35
  }
36
 
37
+ .header {
38
+ background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
39
+ color: white;
 
40
  padding: 20px;
 
 
 
 
 
 
 
41
  text-align: center;
42
+ position: relative;
43
  }
44
 
45
+ .header h1 {
46
+ font-size: 24px;
47
+ font-weight: 600;
 
 
 
 
 
 
 
48
  margin-bottom: 10px;
 
 
 
49
  }
50
 
51
+ .controls {
52
+ display: flex;
53
+ justify-content: center;
54
+ align-items: center;
55
+ gap: 20px;
56
+ margin-top: 10px;
57
  }
58
 
59
+ .thinking-toggle {
60
+ display: flex;
61
+ align-items: center;
62
+ gap: 10px;
 
63
  }
64
 
65
+ .switch {
66
+ position: relative;
67
+ display: inline-block;
68
+ width: 60px;
69
+ height: 34px;
 
 
70
  }
71
 
72
+ .switch input {
73
+ opacity: 0;
74
+ width: 0;
75
+ height: 0;
76
  }
77
 
78
+ .slider {
79
+ position: absolute;
80
+ cursor: pointer;
81
+ top: 0;
82
+ left: 0;
83
+ right: 0;
84
+ bottom: 0;
85
+ background-color: rgba(255, 255, 255, 0.3);
86
+ transition: .4s;
87
+ border-radius: 34px;
88
+ }
89
+
90
+ .slider:before {
91
+ position: absolute;
92
+ content: "";
93
+ height: 26px;
94
+ width: 26px;
95
+ left: 4px;
96
+ bottom: 4px;
97
+ background-color: white;
98
+ transition: .4s;
99
+ border-radius: 50%;
100
  }
101
 
102
+ input:checked + .slider {
103
+ background-color: rgba(255, 255, 255, 0.5);
 
104
  }
105
 
106
+ input:checked + .slider:before {
107
+ transform: translateX(26px);
 
108
  }
109
 
110
+ .btn-reset {
111
+ background: rgba(255, 255, 255, 0.2);
112
+ border: 2px solid white;
113
  color: white;
114
+ padding: 8px 16px;
115
+ border-radius: 20px;
 
116
  cursor: pointer;
117
  font-size: 14px;
118
+ transition: all 0.3s ease;
 
119
  }
120
 
121
+ .btn-reset:hover {
122
+ background: rgba(255, 255, 255, 0.3);
123
  transform: translateY(-2px);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
124
  }
125
 
126
+ .messages {
 
 
 
 
 
 
 
 
 
 
 
127
  flex: 1;
128
  overflow-y: auto;
129
  padding: 20px;
 
 
 
 
 
130
  display: flex;
131
+ flex-direction: column;
132
+ gap: 15px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
133
  }
134
 
135
+ .message {
136
+ max-width: 80%;
137
  padding: 15px 20px;
138
  border-radius: 18px;
139
+ font-size: 16px;
140
+ line-height: 1.5;
141
+ animation: fadeIn 0.3s ease;
142
  }
143
 
144
+ .message.user {
145
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
146
  color: white;
147
+ align-self: flex-end;
148
+ margin-left: auto;
149
  }
150
 
151
+ .message.assistant {
152
+ background: #f8f9fa;
153
  color: #333;
154
+ align-self: flex-start;
155
+ border: 1px solid #e9ecef;
 
 
 
 
 
 
156
  }
157
 
158
+ .thoughts {
159
+ background: linear-gradient(135deg, #ffecd2 0%, #fcb69f 100%);
160
+ color: #8b4513;
161
+ border-left: 4px solid #ff9a56;
162
+ margin: 10px 0;
163
+ padding: 15px;
164
  border-radius: 10px;
 
 
165
  font-style: italic;
 
166
  }
167
 
168
+ .thoughts-header {
169
+ font-weight: 600;
170
+ margin-bottom: 8px;
171
+ display: flex;
172
+ align-items: center;
173
+ gap: 8px;
174
  }
175
 
176
  .input-container {
177
  padding: 20px;
178
+ background: #f8f9fa;
179
+ border-top: 1px solid #e9ecef;
180
  }
181
 
182
  .input-row {
 
185
  align-items: flex-end;
186
  }
187
 
188
+ .input-wrapper {
189
  flex: 1;
190
+ position: relative;
191
+ }
192
+
193
+ .message-input {
194
+ width: 100%;
195
+ min-height: 50px;
196
+ padding: 15px 60px 15px 20px;
197
+ border: 2px solid #e9ecef;
198
  border-radius: 25px;
199
  font-size: 16px;
200
  resize: none;
201
+ outline: none;
202
+ transition: all 0.3s ease;
203
+ font-family: inherit;
204
  }
205
 
206
  .message-input:focus {
207
  border-color: #667eea;
208
+ box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
209
  }
210
 
211
+ .send-button {
212
+ position: absolute;
213
+ right: 5px;
214
+ top: 50%;
215
+ transform: translateY(-50%);
216
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
217
  border: none;
218
  border-radius: 50%;
219
+ width: 40px;
220
+ height: 40px;
221
+ color: white;
222
  cursor: pointer;
223
+ transition: all 0.3s ease;
224
  display: flex;
225
  align-items: center;
226
  justify-content: center;
 
227
  }
228
 
229
+ .send-button:hover:not(:disabled) {
230
+ transform: translateY(-50%) scale(1.1);
231
+ box-shadow: 0 5px 15px rgba(102, 126, 234, 0.3);
232
  }
233
 
234
+ .send-button:disabled {
235
  background: #ccc;
236
  cursor: not-allowed;
237
  }
238
 
239
+ .file-input {
240
+ display: none;
 
 
 
 
 
241
  }
242
 
243
+ .file-button {
244
+ background: #28a745;
245
+ border: none;
246
+ color: white;
247
+ padding: 12px 20px;
248
+ border-radius: 25px;
249
+ cursor: pointer;
250
+ font-size: 14px;
251
+ transition: all 0.3s ease;
252
  }
253
 
254
+ .file-button:hover {
255
+ background: #218838;
256
+ transform: translateY(-2px);
 
 
 
 
 
 
257
  }
258
 
259
+ .file-preview {
260
+ margin-top: 10px;
261
+ padding: 10px;
262
+ background: #e3f2fd;
263
+ border-radius: 10px;
264
+ display: none;
 
 
 
265
  }
266
 
267
  .typing-indicator {
268
  display: none;
269
+ align-items: center;
270
+ gap: 5px;
271
+ color: #666;
272
+ font-style: italic;
 
 
 
 
 
 
273
  }
274
 
275
  .typing-dots {
276
  display: flex;
277
+ gap: 3px;
278
  }
279
 
280
  .typing-dots span {
281
  width: 8px;
282
  height: 8px;
 
283
  border-radius: 50%;
284
+ background: #666;
285
+ animation: typing 1.4s infinite ease-in-out;
286
  }
287
 
288
+ .typing-dots span:nth-child(1) { animation-delay: 0s; }
289
+ .typing-dots span:nth-child(2) { animation-delay: 0.2s; }
290
+ .typing-dots span:nth-child(3) { animation-delay: 0.4s; }
291
 
292
+ @keyframes typing {
293
+ 0%, 60%, 100% { transform: scale(0.8); opacity: 0.5; }
294
+ 30% { transform: scale(1.2); opacity: 1; }
295
  }
296
 
297
+ @keyframes fadeIn {
298
+ from { opacity: 0; transform: translateY(10px); }
299
+ to { opacity: 1; transform: translateY(0); }
 
 
 
 
 
 
300
  }
301
 
302
+ .error {
303
+ background: #f8d7da;
304
+ color: #721c24;
305
+ border: 1px solid #f5c6cb;
306
+ padding: 15px;
307
+ border-radius: 10px;
308
  margin: 10px 0;
309
  }
310
 
311
+ .success {
312
+ background: #d4edda;
313
+ color: #155724;
314
+ border: 1px solid #c3e6cb;
315
+ padding: 15px;
316
+ border-radius: 10px;
317
+ margin: 10px 0;
318
+ }
319
 
320
+ /* Scrollbar styling */
321
+ .messages::-webkit-scrollbar {
322
+ width: 8px;
323
+ }
 
 
324
 
325
+ .messages::-webkit-scrollbar-track {
326
+ background: #f1f1f1;
327
+ border-radius: 4px;
328
+ }
329
 
330
+ .messages::-webkit-scrollbar-thumb {
331
+ background: #c1c1c1;
332
+ border-radius: 4px;
333
  }
334
 
335
+ .messages::-webkit-scrollbar-thumb:hover {
336
+ background: #a8a8a8;
 
 
 
 
 
337
  }
338
 
339
+ /* Responsive */
340
+ @media (max-width: 768px) {
341
+ .chat-container {
342
+ height: 100vh;
343
+ border-radius: 0;
344
+ }
345
+
346
+ .message {
347
+ max-width: 90%;
348
+ }
349
+
350
+ .controls {
351
+ flex-direction: column;
352
+ gap: 10px;
353
+ }
354
  }
355
  </style>
356
  </head>
357
  <body>
358
+ <div class="chat-container">
359
+ <div class="header">
360
+ <h1>🤖 Gemini AI Assistant</h1>
361
+ <p>Assistant IA avancé avec outils intégrés</p>
362
+ <div class="controls">
363
+ <div class="thinking-toggle">
364
+ <span>Thinking Mode:</span>
365
+ <label class="switch">
366
+ <input type="checkbox" id="thinkingToggle" checked>
367
+ <span class="slider"></span>
368
+ </label>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
369
  </div>
370
+ <button class="btn-reset" onclick="resetConversation()">
371
+ 🔄 Reset
372
+ </button>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
373
  </div>
374
  </div>
375
 
376
+ <div class="messages" id="messages">
377
+ <div class="message assistant">
378
+ <div>👋 Bonjour ! Je suis votre assistant IA Gemini. Je peux :</div>
379
+ <ul style="margin-top: 10px; padding-left: 20px;">
380
+ <li>💻 Exécuter du code Python</li>
381
+ <li>🔍 Rechercher des informations sur Google</li>
382
+ <li>📄 Analyser vos fichiers (images, PDF, vidéos, texte)</li>
383
+ <li>🧠 Utiliser le mode "thinking" pour des raisonnements complexes</li>
384
+ <li>🌐 Accéder au contenu d'URLs</li>
385
+ </ul>
386
+ <div style="margin-top: 10px;">Comment puis-je vous aider aujourd'hui ?</div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
387
  </div>
388
+ </div>
389
 
390
+ <div class="typing-indicator" id="typingIndicator">
391
+ <span>L'assistant réfléchit</span>
392
+ <div class="typing-dots">
393
+ <span></span>
394
+ <span></span>
395
+ <span></span>
 
396
  </div>
397
+ </div>
398
 
399
+ <div class="input-container">
400
+ <div class="file-preview" id="filePreview"></div>
401
+ <div class="input-row">
402
+ <input type="file" id="fileInput" class="file-input" accept="image/*,video/*,.pdf,.txt,.csv,.json" onchange="handleFileSelect(event)">
403
+ <button class="file-button" onclick="document.getElementById('fileInput').click()">
404
+ 📎 Fichier
405
+ </button>
406
+ <div class="input-wrapper">
407
  <textarea
408
+ id="messageInput"
409
  class="message-input"
410
+ placeholder="Tapez votre message ici..."
411
+ rows="1"
412
+ onkeydown="handleKeyDown(event)"
413
+ oninput="autoResize(this)"
414
+ ></textarea>
415
+ <button class="send-button" id="sendButton" onclick="sendMessage()">
416
+
417
  </button>
418
  </div>
419
  </div>
 
421
  </div>
422
 
423
  <script>
 
424
  let currentFile = null;
425
+ let conversationId = 'session_' + Date.now();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
426
 
427
+ function autoResize(textarea) {
428
+ textarea.style.height = 'auto';
429
+ textarea.style.height = Math.min(textarea.scrollHeight, 120) + 'px';
430
+ }
 
 
 
 
 
 
 
 
431
 
432
+ function handleKeyDown(event) {
433
+ if (event.key === 'Enter' && !event.shiftKey) {
434
+ event.preventDefault();
 
435
  sendMessage();
436
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
437
  }
438
 
439
+ function handleFileSelect(event) {
440
+ const file = event.target.files[0];
441
+ if (!file) return;
442
 
443
+ const filePreview = document.getElementById('filePreview');
 
 
 
 
 
444
  const formData = new FormData();
445
  formData.append('file', file);
446
 
447
+ fetch('/upload', {
448
+ method: 'POST',
449
+ body: formData
450
+ })
451
+ .then(response => response.json())
452
+ .then(data => {
453
+ if (data.success) {
454
+ currentFile = data;
455
+ filePreview.innerHTML = `
456
+ <div style="display: flex; justify-content: space-between; align-items: center;">
457
+ <span>📎 ${data.filename}</span>
458
+ <button onclick="removeFile()" style="background: #dc3545; color: white; border: none; padding: 5px 10px; border-radius: 5px; cursor: pointer;">✕</button>
459
+ </div>
460
+ `;
461
+ filePreview.style.display = 'block';
 
 
 
462
  } else {
463
+ showError('Erreur lors du téléchargement: ' + data.error);
 
464
  }
465
+ })
466
+ .catch(error => {
467
+ showError('Erreur lors du téléchargement: ' + error.message);
468
+ });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
469
  }
470
 
471
  function removeFile() {
472
  currentFile = null;
473
+ document.getElementById('filePreview').style.display = 'none';
474
+ document.getElementById('fileInput').value = '';
 
475
  }
476
 
477
+ function sendMessage() {
478
+ const messageInput = document.getElementById('messageInput');
479
+ const message = messageInput.value.trim();
 
 
 
 
 
 
 
 
 
480
 
481
+ if (!message && !currentFile) return;
 
 
 
 
482
 
483
+ const sendButton = document.getElementById('sendButton');
484
+ const typingIndicator = document.getElementById('typingIndicator');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
485
 
486
+ // Désactiver l'interface
487
+ messageInput.disabled = true;
488
+ sendButton.disabled = true;
 
 
 
 
 
489
 
490
+ // Afficher le message utilisateur
491
+ if (message) {
492
+ addMessage('user', message);
493
+ }
494
+
495
+ // Afficher l'indicateur de frappe
496
+ typingIndicator.style.display = 'flex';
 
 
497
 
498
+ // Vider l'input
499
  messageInput.value = '';
500
  messageInput.style.height = 'auto';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
501
 
502
+ // Préparer la requête
503
+ const thinkingEnabled = document.getElementById('thinkingToggle').checked;
504
+ const endpoint = currentFile ? '/chat_with_file' : '/chat';
505
+ const payload = {
506
+ message: message || 'Analysez ce fichier',
507
+ thinking_enabled: thinkingEnabled,
508
+ conversation_id: conversationId
509
+ };
510
+
511
+ if (currentFile) {
512
+ payload.file_data = currentFile;
513
+ }
514
 
515
+ // Envoyer la requête avec streaming
516
+ fetch(endpoint, {
517
+ method: 'POST',
518
+ headers: {
519
+ 'Content-Type': 'application/json'
520
+ },
521
+ body: JSON.stringify(payload)
522
+ })
523
+ .then(response => {
524
  if (!response.ok) {
525
+ throw new Error('Erreur réseau');
526
  }
527
 
528
  const reader = response.body.getReader();
529
  const decoder = new TextDecoder();
530
+ let buffer = '';
531
+ let currentMessage = '';
532
+ let currentThoughts = '';
533
+ let messageElement = null;
534
+ let thoughtsElement = null;
535
+
536
+ function processStream() {
537
+ return reader.read().then(({ done, value }) => {
538
+ if (done) {
539
+ typingIndicator.style.display = 'none';
540
+ messageInput.disabled = false;
541
+ sendButton.disabled = false;
542
+ messageInput.focus();
543
+ removeFile();
544
+ return;
545
+ }
546
+
547
+ buffer += decoder.decode(value, { stream: true });
548
+ const lines = buffer.split('\n');
549
+ buffer = lines.pop() || '';
550
+
551
+ for (const line of lines) {
552
+ if (line.startsWith('data: ')) {
553
+ try {
554
+ const data = JSON.parse(line.substring(6));
 
 
 
 
 
 
 
555
 
556
+ if (data.type === 'text') {
557
+ if (!messageElement) {
558
+ messageElement = addMessage('assistant', '');
559
+ }
560
+ currentMessage += data.content;
561
+ messageElement.innerHTML = formatMessage(currentMessage);
562
+ } else if (data.type === 'thought' && thinkingEnabled) {
563
+ if (!thoughtsElement) {
564
+ thoughtsElement = addThoughts('');
565
+ }
566
+ currentThoughts += data.content;
567
+ thoughtsElement.innerHTML = `
568
+ <div class="thoughts-header">🧠 Réflexion de l'assistant:</div>
569
+ <div>${formatMessage(currentThoughts)}</div>
570
+ `;
571
+ } else if (data.type === 'error') {
572
+ showError(data.content);
573
+ } else if (data.type === 'end') {
574
+ typingIndicator.style.display = 'none';
575
+ messageInput.disabled = false;
576
+ sendButton.disabled = false;
577
+ messageInput.focus();
578
+ removeFile();
579
+ return;
580
  }
581
+ } catch (e) {
582
+ console.error('Erreur parsing JSON:', e);
 
 
 
583
  }
 
 
584
  }
585
  }
 
 
586
 
587
+ return processStream();
588
+ });
 
589
  }
590
 
591
+ return processStream();
592
+ })
593
+ .catch(error => {
594
+ typingIndicator.style.display = 'none';
595
+ messageInput.disabled = false;
596
+ sendButton.disabled = false;
597
+ showError('Erreur: ' + error.message);
598
+ messageInput.focus();
599
+ });
600
  }
601
 
602
+ function addMessage(role, content) {
603
+ const messagesContainer = document.getElementById('messages');
604
+ const messageDiv = document.createElement('div');
605
+ messageDiv.className = `message ${role}`;
606
+ messageDiv.innerHTML = formatMessage(content);
607
+ messagesContainer.appendChild(messageDiv);
608
+ messagesContainer.scrollTop = messagesContainer.scrollHeight;
609
+ return messageDiv;
610
+ }
611
+
612
+ function addThoughts(content) {
613
+ const messagesContainer = document.getElementById('messages');
614
+ const thoughtsDiv = document.createElement('div');
615
+ thoughtsDiv.className = 'thoughts';
616
+ messagesContainer.appendChild(thoughtsDiv);
617
+ messagesContainer.scrollTop = messagesContainer.scrollHeight;
618
+ return thoughtsDiv;
619
+ }
620
+
621
+ function formatMessage(content) {
622
+ // Simple formatting pour le markdown basique
623
+ return content
624
+ .replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>')
625
+ .replace(/\*(.*?)\*/g, '<em>$1</em>')
626
+ .replace(/`(.*?)`/g, '<code style="background: #f4f4f4; padding: 2px 4px; border-radius: 3px;">$1</code>')
627
+ .replace(/\n/g, '<br>');
628
+ }
629
 
630
+ function showError(message) {
631
+ const messagesContainer = document.getElementById('messages');
632
+ const errorDiv = document.createElement('div');
633
+ errorDiv.className = 'error';
634
+ errorDiv.textContent = message;
635
+ messagesContainer.appendChild(errorDiv);
636
+ messagesContainer.scrollTop = messagesContainer.scrollHeight;
637
+ }
638
+
639
+ function resetConversation() {
640
+ if (confirm('Êtes-vous sûr de vouloir réinitialiser la conversation ?')) {
641
+ fetch('/reset_conversation', {
642
  method: 'POST',
643
  headers: {
644
+ 'Content-Type': 'application/json'
645
+ },
646
+ body: JSON.stringify({ conversation_id: conversationId })
647
+ })
648
+ .then(() => {
649
+ document.getElementById('messages').innerHTML = `
 
 
 
650
  <div class="message assistant">
651
+ <div>👋 Conversation réinitialisée ! Comment puis-je vous aider ?</div>
 
 
 
652
  </div>
653
  `;
654
+ conversationId = 'session_' + Date.now();
655
  removeFile();
656
+ })
657
+ .catch(error => {
658
+ showError('Erreur lors de la réinitialisation: ' + error.message);
659
+ });
 
 
 
660
  }
661
  }
662
 
663
+ // Focus automatique sur l'input au chargement
664
  document.addEventListener('DOMContentLoaded', function() {
665
+ document.getElementById('messageInput').focus();
 
 
 
 
 
 
 
 
 
 
666
  });
667
  </script>
668
  </body>