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

Update templates/index.html

Browse files
Files changed (1) hide show
  1. templates/index.html +185 -325
templates/index.html CHANGED
@@ -3,49 +3,62 @@
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;
10
  padding: 0;
11
  box-sizing: border-box;
12
  }
13
 
 
 
 
 
 
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 {
@@ -53,27 +66,25 @@
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;
@@ -82,59 +93,52 @@
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;
@@ -142,27 +146,28 @@
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 {
@@ -171,194 +176,152 @@
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 {
183
- display: flex;
184
- gap: 10px;
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>
@@ -375,15 +338,8 @@
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
 
@@ -400,8 +356,8 @@
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
@@ -412,7 +368,7 @@
412
  onkeydown="handleKeyDown(event)"
413
  oninput="autoResize(this)"
414
  ></textarea>
415
- <button class="send-button" id="sendButton" onclick="sendMessage()">
416
 
417
  </button>
418
  </div>
@@ -420,13 +376,14 @@
420
  </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) {
@@ -444,28 +401,20 @@
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() {
@@ -483,120 +432,41 @@
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) {
@@ -613,17 +483,20 @@
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
 
@@ -638,29 +511,16 @@
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
  });
 
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Gemini AI Assistant - Flat Black & White</title>
7
  <style>
8
+ /* --- RESET ET CONFIGURATION GLOBALE --- */
9
  * {
10
  margin: 0;
11
  padding: 0;
12
  box-sizing: border-box;
13
  }
14
 
15
+ html {
16
+ /* Permet un défilement plus doux */
17
+ scroll-behavior: smooth;
18
+ }
19
+
20
  body {
21
+ /* Police système : performante et native */
22
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
23
+ background-color: #121212; /* Fond sombre principal */
24
+ color: #f1f1f1; /* Couleur de texte par défaut */
25
  min-height: 100vh;
26
  display: flex;
27
  justify-content: center;
28
  align-items: center;
29
+ padding: 0; /* Pas de padding sur le body pour le mobile */
30
  }
31
 
32
+ /* --- CONTENEUR PRINCIPAL DU CHAT --- */
33
  .chat-container {
34
  width: 100%;
35
  max-width: 800px;
36
+ height: 100vh; /* Prend toute la hauteur par défaut (mobile-first) */
37
+ background-color: #1e1e1e; /* Fond du conteneur légèrement plus clair */
 
 
38
  display: flex;
39
  flex-direction: column;
40
  overflow: hidden;
 
41
  }
42
 
43
+ /* --- EN-TÊTE --- */
44
  .header {
45
+ background-color: #1e1e1e;
46
+ color: #ffffff;
47
  padding: 20px;
48
  text-align: center;
49
+ border-bottom: 1px solid #333; /* Séparateur plat */
50
+ flex-shrink: 0;
51
  }
52
 
53
  .header h1 {
54
+ font-size: 22px;
55
  font-weight: 600;
56
+ margin-bottom: 5px;
57
+ }
58
+
59
+ .header p {
60
+ font-size: 14px;
61
+ color: #aaa;
62
  }
63
 
64
  .controls {
 
66
  justify-content: center;
67
  align-items: center;
68
  gap: 20px;
69
+ margin-top: 15px;
70
  }
71
 
72
  .thinking-toggle {
73
  display: flex;
74
  align-items: center;
75
  gap: 10px;
76
+ font-size: 14px;
77
+ color: #ccc;
78
  }
79
 
80
  .switch {
81
  position: relative;
82
  display: inline-block;
83
+ width: 50px;
84
+ height: 28px;
85
  }
86
 
87
+ .switch input { opacity: 0; width: 0; height: 0; }
 
 
 
 
88
 
89
  .slider {
90
  position: absolute;
 
93
  left: 0;
94
  right: 0;
95
  bottom: 0;
96
+ background-color: #444;
97
+ transition: .2s;
98
+ border-radius: 28px;
99
  }
100
 
101
  .slider:before {
102
  position: absolute;
103
  content: "";
104
+ height: 20px;
105
+ width: 20px;
106
  left: 4px;
107
  bottom: 4px;
108
+ background-color: #f1f1f1;
109
+ transition: .2s;
110
  border-radius: 50%;
111
  }
112
 
113
+ input:checked + .slider { background-color: #777; }
114
+ input:checked + .slider:before { transform: translateX(22px); }
 
 
 
 
 
115
 
116
  .btn-reset {
117
+ background: transparent;
118
+ border: 1px solid #555;
119
+ color: #ccc;
120
  padding: 8px 16px;
121
  border-radius: 20px;
122
  cursor: pointer;
123
  font-size: 14px;
124
+ transition: background-color 0.2s ease;
125
  }
126
 
127
+ .btn-reset:hover { background-color: #333; }
 
 
 
128
 
129
+ /* --- ZONE DES MESSAGES --- */
130
  .messages {
131
  flex: 1;
132
  overflow-y: auto;
133
+ padding: 20px 15px;
134
  display: flex;
135
  flex-direction: column;
136
  gap: 15px;
137
  }
138
 
139
  .message {
140
+ max-width: 85%;
141
+ padding: 12px 18px;
142
  border-radius: 18px;
143
  font-size: 16px;
144
  line-height: 1.5;
 
146
  }
147
 
148
  .message.user {
149
+ background-color: #3a3a3a; /* Couleur message utilisateur */
150
+ color: #f1f1f1;
151
  align-self: flex-end;
152
+ border-bottom-right-radius: 4px;
153
  }
154
 
155
  .message.assistant {
156
+ background-color: #2c2c2c; /* Couleur message assistant */
157
+ color: #e0e0e0;
158
  align-self: flex-start;
159
+ border-bottom-left-radius: 4px;
160
  }
161
 
162
  .thoughts {
163
+ background: #252525;
164
+ color: #aaa;
165
+ border-left: 3px solid #666; /* Indicateur visuel plat */
166
  margin: 10px 0;
167
  padding: 15px;
168
+ border-radius: 8px;
169
  font-style: italic;
170
+ font-size: 14px;
171
  }
172
 
173
  .thoughts-header {
 
176
  display: flex;
177
  align-items: center;
178
  gap: 8px;
179
+ color: #ccc;
180
  }
181
 
182
+ /* --- ZONE DE SAISIE --- */
183
  .input-container {
184
+ padding: 15px;
185
+ background-color: #1e1e1e;
186
+ border-top: 1px solid #333;
187
+ flex-shrink: 0;
 
 
 
 
 
188
  }
189
 
190
+ .input-row { display: flex; gap: 10px; align-items: flex-end; }
191
+ .input-wrapper { flex: 1; position: relative; }
 
 
192
 
193
  .message-input {
194
  width: 100%;
195
+ min-height: 48px;
196
+ padding: 12px 55px 12px 20px;
197
+ border: 1px solid #444;
198
+ border-radius: 24px;
199
  font-size: 16px;
200
  resize: none;
201
  outline: none;
202
+ transition: border-color 0.2s ease;
203
  font-family: inherit;
204
+ background-color: #2c2c2c;
205
+ color: #f1f1f1;
206
  }
207
+
208
+ .message-input::placeholder { color: #777; }
209
+ .message-input:focus { border-color: #888; }
 
 
210
 
211
  .send-button {
212
  position: absolute;
213
+ right: 4px;
214
  top: 50%;
215
  transform: translateY(-50%);
216
+ background-color: #333;
217
  border: none;
218
  border-radius: 50%;
219
  width: 40px;
220
  height: 40px;
221
+ color: #f1f1f1;
222
  cursor: pointer;
223
+ transition: background-color 0.2s ease;
224
  display: flex;
225
  align-items: center;
226
  justify-content: center;
227
+ font-size: 20px; /* Taille de l'icône */
228
  }
229
 
230
+ .send-button:hover:not(:disabled) { background-color: #444; }
231
+ .send-button:disabled { background: #252525; color: #555; cursor: not-allowed; }
 
 
 
 
 
 
 
232
 
233
+ .file-input { display: none; }
 
 
234
 
235
  .file-button {
236
+ background: #333;
237
  border: none;
238
+ color: #f1f1f1;
239
+ height: 48px;
240
+ width: 48px;
241
+ border-radius: 50%;
242
  cursor: pointer;
243
+ font-size: 20px; /* Taille de l'icône */
244
+ transition: background-color 0.2s ease;
245
+ flex-shrink: 0;
246
  }
247
 
248
+ .file-button:hover { background-color: #444; }
 
 
 
249
 
250
  .file-preview {
251
+ margin-bottom: 10px;
252
  padding: 10px;
253
+ background: #2c2c2c;
254
  border-radius: 10px;
255
+ font-size: 14px;
256
  display: none;
257
  }
258
 
259
+ /* --- INDICATEURS ET NOTIFICATIONS --- */
260
  .typing-indicator {
261
  display: none;
262
  align-items: center;
263
+ gap: 8px;
264
+ color: #999;
265
  font-style: italic;
266
+ padding: 0 15px 10px;
 
 
 
 
267
  }
268
 
269
  .typing-dots span {
270
+ width: 8px; height: 8px; border-radius: 50%;
 
 
271
  background: #666;
272
  animation: typing 1.4s infinite ease-in-out;
273
  }
 
274
  .typing-dots span:nth-child(1) { animation-delay: 0s; }
275
  .typing-dots span:nth-child(2) { animation-delay: 0.2s; }
276
  .typing-dots span:nth-child(3) { animation-delay: 0.4s; }
277
 
 
 
 
 
 
 
 
 
 
 
278
  .error {
279
+ background: #4d1c20; color: #ffacb6;
280
+ border-left: 3px solid #e53935;
281
+ padding: 15px; border-radius: 8px; margin: 10px 0;
 
 
 
282
  }
 
283
  .success {
284
+ background: #1a4331; color: #a6f1c4;
285
+ border-left: 3px solid #2e7d32;
286
+ padding: 15px; border-radius: 8px; margin: 10px 0;
 
 
 
287
  }
288
 
289
+ /* --- STYLE DE LA SCROLLBAR (THÈME SOMBRE) --- */
290
+ .messages::-webkit-scrollbar { width: 8px; }
291
+ .messages::-webkit-scrollbar-track { background: #1e1e1e; }
292
+ .messages::-webkit-scrollbar-thumb { background: #555; border-radius: 4px; }
293
+ .messages::-webkit-scrollbar-thumb:hover { background: #777; }
 
 
 
 
294
 
295
+ /* --- ANIMATIONS --- */
296
+ @keyframes typing {
297
+ 0%, 60%, 100% { transform: scale(0.8); opacity: 0.5; }
298
+ 30% { transform: scale(1.2); opacity: 1; }
299
  }
300
 
301
+ @keyframes fadeIn {
302
+ from { opacity: 0; transform: translateY(10px); }
303
+ to { opacity: 1; transform: translateY(0); }
304
  }
305
 
306
+ /* --- RESPONSIVE DESIGN (pour les écrans plus larges) --- */
307
+ @media (min-width: 768px) {
308
+ body {
309
+ padding: 20px; /* Ajoute de l'espace autour sur grand écran */
 
310
  }
311
+ .chat-container {
312
+ height: 90vh; /* Hauteur limitée sur grand écran */
313
+ border-radius: 12px;
314
+ border: 1px solid #333;
 
 
 
 
315
  }
316
  }
317
+
318
  </style>
319
  </head>
320
  <body>
321
  <div class="chat-container">
322
  <div class="header">
323
  <h1>🤖 Gemini AI Assistant</h1>
324
+ <p>Interface Plate & Minimaliste</p>
325
  <div class="controls">
326
  <div class="thinking-toggle">
327
  <span>Thinking Mode:</span>
 
338
 
339
  <div class="messages" id="messages">
340
  <div class="message assistant">
341
+ <div>👋 Bonjour ! Ceci est une interface de chat au design plat et minimaliste.</div>
342
+ <div style="margin-top: 10px;">Posez-moi une question pour commencer.</div>
 
 
 
 
 
 
 
343
  </div>
344
  </div>
345
 
 
356
  <div class="file-preview" id="filePreview"></div>
357
  <div class="input-row">
358
  <input type="file" id="fileInput" class="file-input" accept="image/*,video/*,.pdf,.txt,.csv,.json" onchange="handleFileSelect(event)">
359
+ <button class="file-button" onclick="document.getElementById('fileInput').click()" title="Joindre un fichier">
360
+ 📎
361
  </button>
362
  <div class="input-wrapper">
363
  <textarea
 
368
  onkeydown="handleKeyDown(event)"
369
  oninput="autoResize(this)"
370
  ></textarea>
371
+ <button class="send-button" id="sendButton" onclick="sendMessage()" title="Envoyer">
372
 
373
  </button>
374
  </div>
 
376
  </div>
377
  </div>
378
 
379
+ <!-- Le JavaScript est identique à votre version originale -->
380
  <script>
381
  let currentFile = null;
382
  let conversationId = 'session_' + Date.now();
383
 
384
  function autoResize(textarea) {
385
  textarea.style.height = 'auto';
386
+ textarea.style.height = Math.min(textarea.scrollHeight, 150) + 'px'; // Augmentation de la hauteur max
387
  }
388
 
389
  function handleKeyDown(event) {
 
401
  const formData = new FormData();
402
  formData.append('file', file);
403
 
404
+ // Simule l'upload pour l'exemple
405
+ // Remplacez cette partie par votre logique d'upload réelle si nécessaire
406
+ try {
407
+ currentFile = { success: true, filename: file.name, path: 'simulated/path/' + file.name };
408
+ filePreview.innerHTML = `
409
+ <div style="display: flex; justify-content: space-between; align-items: center;">
410
+ <span>📎 ${currentFile.filename}</span>
411
+ <button onclick="removeFile()" style="background: #555; color: white; border: none; padding: 5px 10px; border-radius: 5px; cursor: pointer;">✕</button>
412
+ </div>
413
+ `;
414
+ filePreview.style.display = 'block';
415
+ } catch (error) {
416
+ showError('Erreur lors de la sélection du fichier: ' + error.message);
417
+ }
 
 
 
 
 
 
 
 
418
  }
419
 
420
  function removeFile() {
 
432
  const sendButton = document.getElementById('sendButton');
433
  const typingIndicator = document.getElementById('typingIndicator');
434
 
 
435
  messageInput.disabled = true;
436
  sendButton.disabled = true;
437
 
 
438
  if (message) {
439
  addMessage('user', message);
440
  }
441
 
 
442
  typingIndicator.style.display = 'flex';
443
 
 
444
  messageInput.value = '';
445
+ autoResize(messageInput);
446
 
 
447
  const thinkingEnabled = document.getElementById('thinkingToggle').checked;
448
+
449
+ // --- SIMULATION DE RÉPONSE DE L'IA ---
450
+ // Remplacez cette simulation par votre appel fetch réel
451
+ setTimeout(() => {
452
+ typingIndicator.style.display = 'none';
453
+
454
+ if (thinkingEnabled) {
455
+ const thoughtsElement = addThoughts('');
456
+ thoughtsElement.innerHTML = `
457
+ <div class="thoughts-header">🧠 Réflexion de l'assistant:</div>
458
+ <div>L'utilisateur a demandé une réponse. Je vais formater ma réponse en utilisant du markdown simple. Le fichier ${currentFile ? `"${currentFile.filename}"` : ''} a été mentionné. Je vais construire une réponse cohérente.</div>
459
+ `;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
460
  }
461
 
462
+ addMessage('assistant', `Ceci est une réponse simulée à votre message : "${message}". ${currentFile ? `Vous avez également joint le fichier : **${currentFile.filename}**.` : ''}`);
463
+
 
 
464
  messageInput.disabled = false;
465
  sendButton.disabled = false;
 
466
  messageInput.focus();
467
+ removeFile();
468
+ }, 2000);
469
+ // --- FIN DE LA SIMULATION ---
470
  }
471
 
472
  function addMessage(role, content) {
 
483
  const messagesContainer = document.getElementById('messages');
484
  const thoughtsDiv = document.createElement('div');
485
  thoughtsDiv.className = 'thoughts';
486
+ thoughtsDiv.innerHTML = content; // Le contenu est déjà du HTML
487
  messagesContainer.appendChild(thoughtsDiv);
488
  messagesContainer.scrollTop = messagesContainer.scrollHeight;
489
  return thoughtsDiv;
490
  }
491
 
492
  function formatMessage(content) {
493
+ // Sécurise le contenu pour éviter l'injection de HTML
494
+ const escapedContent = content.replace(/</g, "&lt;").replace(/>/g, "&gt;");
495
+
496
+ return escapedContent
497
  .replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>')
498
  .replace(/\*(.*?)\*/g, '<em>$1</em>')
499
+ .replace(/`(.*?)`/g, '<code style="background: #444; padding: 2px 5px; border-radius: 4px; font-family: monospace;">$1</code>')
500
  .replace(/\n/g, '<br>');
501
  }
502
 
 
511
 
512
  function resetConversation() {
513
  if (confirm('Êtes-vous sûr de vouloir réinitialiser la conversation ?')) {
514
+ document.getElementById('messages').innerHTML = `
515
+ <div class="message assistant">
516
+ <div>👋 Conversation réinitialisée ! Comment puis-je vous aider ?</div>
517
+ </div>
518
+ `;
519
+ conversationId = 'session_' + Date.now();
520
+ removeFile();
 
 
 
 
 
 
 
 
 
 
 
 
521
  }
522
  }
523
 
 
524
  document.addEventListener('DOMContentLoaded', function() {
525
  document.getElementById('messageInput').focus();
526
  });