Docfile commited on
Commit
0a3c1c2
·
verified ·
1 Parent(s): 2482b18

Update templates/index.html

Browse files
Files changed (1) hide show
  1. templates/index.html +154 -150
templates/index.html CHANGED
@@ -3,184 +3,147 @@
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>Mariam AI Assistant</title>
 
7
  <script src="https://cdn.tailwindcss.com"></script>
8
- <script src="https://cdnjs.cloudflare.com/ajax/libs/marked/5.1.1/marked.min.js"></script>
9
- <script src="https://cdnjs.cloudflare.com/ajax/libs/DOMPurify/3.0.3/purify.min.js"></script>
10
  <script>
11
  tailwind.config = {
 
12
  theme: {
13
  extend: {
14
  colors: {
15
- 'dark': {
16
- 100: '#f1f1f1',
17
- 200: '#e0e0e0',
18
- 300: '#ccc',
19
- 400: '#999',
20
- 500: '#777',
21
- 600: '#555',
22
- 700: '#444',
23
- 800: '#333',
24
- 900: '#2c2c2c',
25
- 950: '#1e1e1e'
26
- }
27
  }
28
  }
29
  }
30
  }
31
  </script>
32
  <style>
33
- /* Custom scrollbar styles */
34
- .custom-scrollbar::-webkit-scrollbar {
35
- width: 6px;
36
  }
37
- .custom-scrollbar::-webkit-scrollbar-track {
38
- background: #1e1e1e;
39
  }
40
- .custom-scrollbar::-webkit-scrollbar-thumb {
41
- background: #555;
42
- border-radius: 3px;
 
 
 
43
  }
44
- .custom-scrollbar::-webkit-scrollbar-thumb:hover {
45
- background: #777;
 
46
  }
47
-
48
- /* Animation for typing dots */
49
- @keyframes typing {
50
- 0%, 60%, 100% {
51
- transform: scale(0.8);
52
- opacity: 0.5;
53
- }
54
- 30% {
55
- transform: scale(1.2);
56
- opacity: 1;
57
- }
58
  }
59
- .typing-dot:nth-child(1) { animation-delay: 0s; }
60
- .typing-dot:nth-child(2) { animation-delay: 0.2s; }
61
- .typing-dot:nth-child(3) { animation-delay: 0.4s; }
62
- .typing-dot {
63
- animation: typing 1.4s infinite ease-in-out;
 
 
64
  }
65
 
66
- /* Fade in animation */
 
 
 
 
67
  @keyframes fadeIn {
68
  from { opacity: 0; transform: translateY(10px); }
69
  to { opacity: 1; transform: translateY(0); }
70
  }
71
- .fade-in {
72
- animation: fadeIn 0.3s ease;
73
- }
 
 
 
 
 
74
 
75
- /* Markdown styles */
76
- .markdown-content h1, .markdown-content h2, .markdown-content h3 {
77
- margin: 1em 0 0.5em 0;
78
- font-weight: 600;
79
  }
80
- .markdown-content h1 { font-size: 1.5em; }
81
- .markdown-content h2 { font-size: 1.3em; }
82
- .markdown-content h3 { font-size: 1.1em; }
83
- .markdown-content p { margin: 0.5em 0; }
84
- .markdown-content ul, .markdown-content ol {
85
- margin: 0.5em 0;
86
- padding-left: 1.5em;
87
- }
88
- .markdown-content li { margin: 0.2em 0; }
89
- .markdown-content code {
90
- background: #444;
91
- padding: 2px 6px;
92
- border-radius: 4px;
93
- font-family: 'Courier New', monospace;
94
- font-size: 0.9em;
95
- }
96
- .markdown-content pre {
97
- background: #333;
98
- padding: 1em;
99
- border-radius: 8px;
100
- margin: 1em 0;
101
- overflow-x: auto;
102
- }
103
- .markdown-content pre code {
104
- background: none;
105
- padding: 0;
106
- }
107
- .markdown-content blockquote {
108
- border-left: 3px solid #666;
109
- padding-left: 1em;
110
- margin: 1em 0;
111
- color: #ccc;
112
- font-style: italic;
113
  }
114
  </style>
115
  </head>
116
- <body class="bg-gray-900 text-gray-100 min-h-screen flex justify-center items-center p-0 md:p-5">
117
- <div class="w-full max-w-4xl h-screen md:h-[90vh] bg-dark-950 flex flex-col md:rounded-xl md:border md:border-dark-800 overflow-hidden">
118
  <!-- Header -->
119
- <div class="bg-dark-950 px-6 py-5 border-b border-dark-800 flex-shrink-0">
120
- <div class="text-center">
121
- <h1 class="text-xl font-semibold mb-1">Mariam AI Assistant</h1>
122
- <p class="text-sm text-dark-300">❤️</p>
123
- <div class="flex justify-center items-center mt-4">
124
- <button
125
- class="bg-transparent border border-dark-600 text-dark-300 px-4 py-2 rounded-full text-sm hover:bg-dark-800 transition-colors duration-200"
126
- onclick="resetConversation()"
127
- >
128
- 🔄 Reset
129
- </button>
130
  </div>
 
 
 
131
  </div>
132
  </div>
133
 
134
  <!-- Messages -->
135
- <div id="messages" class="flex-1 overflow-y-auto px-4 py-5 space-y-4 custom-scrollbar">
136
- <div class="max-w-[85%] bg-dark-900 text-dark-200 p-4 rounded-2xl rounded-bl-sm fade-in">
137
- <div>👋 Bonjour, sur quoi allons-nous travailler aujourd'hui ?</div>
138
  </div>
139
  </div>
140
 
141
  <!-- Typing Indicator -->
142
- <div id="typingIndicator" class="hidden px-4 pb-3 flex items-center gap-3 text-dark-400 italic">
143
  <span>L'assistant réfléchit</span>
144
  <div class="flex gap-1">
145
- <span class="w-2 h-2 bg-dark-600 rounded-full typing-dot"></span>
146
- <span class="w-2 h-2 bg-dark-600 rounded-full typing-dot"></span>
147
- <span class="w-2 h-2 bg-dark-600 rounded-full typing-dot"></span>
148
  </div>
149
  </div>
150
 
151
  <!-- Input Container -->
152
- <div class="bg-dark-950 p-4 border-t border-dark-800 flex-shrink-0">
153
- <div id="filePreview" class="hidden mb-3 p-3 bg-dark-900 rounded-lg text-sm"></div>
154
  <div class="flex gap-3 items-end">
155
- <input
156
- type="file"
157
- id="fileInput"
158
- class="hidden"
159
- accept="image/*,video/*,.pdf,.txt,.csv,.json"
160
- onchange="handleFileSelect(event)"
161
- >
162
- <button
163
- class="bg-dark-800 hover:bg-dark-700 text-gray-100 h-12 w-12 rounded-full flex items-center justify-center text-xl transition-colors duration-200 flex-shrink-0"
164
- onclick="document.getElementById('fileInput').click()"
165
- title="Joindre un fichier"
166
- >
167
  📎
168
  </button>
169
  <div class="flex-1 relative">
170
  <textarea
171
  id="messageInput"
172
- class="w-full min-h-[48px] max-h-32 px-5 py-3 pr-14 bg-dark-900 text-gray-100 border border-dark-700 rounded-full resize-none outline-none focus:border-dark-500 transition-colors duration-200 placeholder-dark-400"
173
  placeholder="Tapez votre message ici..."
174
  rows="1"
175
  onkeydown="handleKeyDown(event)"
176
  oninput="autoResize(this)"
177
  ></textarea>
178
- <button
179
- id="sendButton"
180
- class="absolute right-1 top-1/2 -translate-y-1/2 bg-dark-800 hover:bg-dark-700 disabled:bg-dark-900 disabled:text-dark-600 text-gray-100 w-10 h-10 rounded-full flex items-center justify-center text-lg transition-colors duration-200"
181
- onclick="sendMessage()"
182
- title="Envoyer"
183
- >
184
 
185
  </button>
186
  </div>
@@ -192,7 +155,7 @@
192
  let currentFile = null;
193
  let conversationId = 'session_' + Date.now();
194
 
195
- // Configuration de marked pour le rendu markdown
196
  marked.setOptions({
197
  breaks: true,
198
  gfm: true
@@ -200,7 +163,7 @@
200
 
201
  function autoResize(textarea) {
202
  textarea.style.height = 'auto';
203
- textarea.style.height = Math.min(textarea.scrollHeight, 128) + 'px';
204
  }
205
 
206
  function handleKeyDown(event) {
@@ -228,8 +191,8 @@
228
  currentFile = data;
229
  filePreview.innerHTML = `
230
  <div class="flex justify-between items-center">
231
- <span>📎 ${data.filename}</span>
232
- <button onclick="removeFile()" class="bg-dark-600 hover:bg-dark-500 text-white px-3 py-1 rounded text-sm transition-colors duration-200">✕</button>
233
  </div>
234
  `;
235
  filePreview.classList.remove('hidden');
@@ -265,14 +228,16 @@
265
  }
266
 
267
  typingIndicator.classList.remove('hidden');
 
268
 
269
  messageInput.value = '';
270
  autoResize(messageInput);
271
 
 
272
  const endpoint = currentFile ? '/chat_with_file' : '/chat';
273
  const payload = {
274
  message: message || 'Analyse ce fichier',
275
- thinking_enabled: false, // Désactivé pour une meilleure UX
276
  conversation_id: conversationId
277
  };
278
 
@@ -294,7 +259,10 @@
294
  const decoder = new TextDecoder();
295
  let buffer = '';
296
  let messageElement = null;
297
- let contentBuffer = '';
 
 
 
298
 
299
  function processStream() {
300
  return reader.read().then(({ done, value }) => {
@@ -312,24 +280,33 @@
312
  const data = JSON.parse(line.substring(6));
313
 
314
  if (data.type === 'text') {
 
315
  if (!messageElement) {
316
  messageElement = addMessage('assistant', '');
317
  }
318
- contentBuffer += data.content;
319
  // Rendu markdown en temps réel
320
- const htmlContent = DOMPurify.sanitize(marked.parse(contentBuffer));
321
- messageElement.innerHTML = `<div class="markdown-content">${htmlContent}</div>`;
 
 
 
 
 
 
 
 
322
  } else if (data.type === 'error') {
323
  showError(data.content);
324
  } else if (data.type === 'end') {
325
  typingIndicator.classList.add('hidden');
 
326
  messageInput.disabled = false;
327
  sendButton.disabled = false;
328
  messageInput.focus();
329
  removeFile();
330
  return;
331
  }
332
- // Scroll vers le bas
333
  const messagesContainer = document.getElementById('messages');
334
  messagesContainer.scrollTop = messagesContainer.scrollHeight;
335
 
@@ -345,6 +322,7 @@
345
  })
346
  .catch(error => {
347
  typingIndicator.classList.add('hidden');
 
348
  messageInput.disabled = false;
349
  sendButton.disabled = false;
350
  showError('Erreur: ' + error.message);
@@ -357,11 +335,12 @@
357
  const messageDiv = document.createElement('div');
358
 
359
  if (role === 'user') {
360
- messageDiv.className = 'max-w-[85%] bg-dark-700 text-gray-100 p-4 rounded-2xl rounded-br-sm ml-auto fade-in';
361
- messageDiv.innerHTML = formatUserMessage(content);
 
362
  } else {
363
- messageDiv.className = 'max-w-[85%] bg-dark-900 text-dark-200 p-4 rounded-2xl rounded-bl-sm fade-in';
364
- messageDiv.innerHTML = content || '<div class="markdown-content"></div>';
365
  }
366
 
367
  messagesContainer.appendChild(messageDiv);
@@ -369,23 +348,48 @@
369
  return messageDiv;
370
  }
371
 
372
- function formatUserMessage(content) {
373
- return escapeHtml(content).replace(/\n/g, '<br>');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
374
  }
375
-
376
- function escapeHtml(unsafe) {
377
- return unsafe
378
- .replace(/&/g, "&amp;")
379
- .replace(/</g, "&lt;")
380
- .replace(/>/g, "&gt;")
381
- .replace(/"/g, "&quot;")
382
- .replace(/'/g, "&#039;");
 
 
 
 
 
383
  }
384
 
385
  function showError(message) {
386
  const messagesContainer = document.getElementById('messages');
387
  const errorDiv = document.createElement('div');
388
- errorDiv.className = 'bg-red-900/50 text-red-200 border-l-4 border-red-500 p-4 rounded-lg fade-in';
389
  errorDiv.textContent = message;
390
  messagesContainer.appendChild(errorDiv);
391
  messagesContainer.scrollTop = messagesContainer.scrollHeight;
@@ -401,8 +405,8 @@
401
  .then(response => {
402
  if (response.ok) {
403
  document.getElementById('messages').innerHTML = `
404
- <div class="max-w-[85%] bg-dark-900 text-dark-200 p-4 rounded-2xl rounded-bl-sm fade-in">
405
- <div>👋 Conversation réinitialisée ! Comment puis-je vous aider ?</div>
406
  </div>
407
  `;
408
  conversationId = 'session_' + Date.now();
 
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>AI Assistant - Flat Black & White</title>
7
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/tailwindcss/2.2.19/tailwind.min.css"></script>
8
  <script src="https://cdn.tailwindcss.com"></script>
9
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/marked/4.3.0/marked.min.js"></script>
 
10
  <script>
11
  tailwind.config = {
12
+ darkMode: 'class',
13
  theme: {
14
  extend: {
15
  colors: {
16
+ 'dark-bg': '#121212',
17
+ 'dark-card': '#1e1e1e',
18
+ 'dark-elevated': '#2c2c2c',
19
+ 'dark-border': '#333',
20
+ 'dark-text': '#f1f1f1',
21
+ 'dark-muted': '#aaa'
 
 
 
 
 
 
22
  }
23
  }
24
  }
25
  }
26
  </script>
27
  <style>
28
+ /* Configuration Marked pour le markdown */
29
+ .markdown-content {
30
+ @apply text-dark-text leading-relaxed;
31
  }
32
+ .markdown-content h1, .markdown-content h2, .markdown-content h3 {
33
+ @apply font-bold mb-2 mt-4;
34
  }
35
+ .markdown-content h1 { @apply text-xl; }
36
+ .markdown-content h2 { @apply text-lg; }
37
+ .markdown-content h3 { @apply text-base; }
38
+ .markdown-content p { @apply mb-2; }
39
+ .markdown-content ul, .markdown-content ol {
40
+ @apply ml-4 mb-2;
41
  }
42
+ .markdown-content li { @apply mb-1; }
43
+ .markdown-content code {
44
+ @apply bg-gray-700 px-2 py-1 rounded text-sm font-mono;
45
  }
46
+ .markdown-content pre {
47
+ @apply bg-gray-800 p-4 rounded-lg overflow-x-auto mb-4;
48
+ }
49
+ .markdown-content pre code {
50
+ @apply bg-transparent p-0;
 
 
 
 
 
 
51
  }
52
+ .markdown-content blockquote {
53
+ @apply border-l-4 border-gray-600 pl-4 italic text-gray-300 mb-4;
54
+ }
55
+ .markdown-content strong { @apply font-semibold; }
56
+ .markdown-content em { @apply italic; }
57
+ .markdown-content a {
58
+ @apply text-blue-400 hover:text-blue-300 underline;
59
  }
60
 
61
+ /* Animations et styles personnalisés */
62
+ @keyframes typing {
63
+ 0%, 60%, 100% { transform: scale(0.8); opacity: 0.5; }
64
+ 30% { transform: scale(1.2); opacity: 1; }
65
+ }
66
  @keyframes fadeIn {
67
  from { opacity: 0; transform: translateY(10px); }
68
  to { opacity: 1; transform: translateY(0); }
69
  }
70
+ .animate-typing { animation: typing 1.4s infinite ease-in-out; }
71
+ .animate-fade-in { animation: fadeIn 0.3s ease; }
72
+
73
+ /* Scrollbar personnalisée */
74
+ .custom-scrollbar::-webkit-scrollbar { width: 8px; }
75
+ .custom-scrollbar::-webkit-scrollbar-track { background: #1e1e1e; }
76
+ .custom-scrollbar::-webkit-scrollbar-thumb { background: #555; border-radius: 4px; }
77
+ .custom-scrollbar::-webkit-scrollbar-thumb:hover { background: #777; }
78
 
79
+ /* Style pour les pensées déroulantes */
80
+ .thoughts-toggle:checked + .thoughts-content {
81
+ max-height: 1000px;
82
+ opacity: 1;
83
  }
84
+ .thoughts-content {
85
+ max-height: 0;
86
+ opacity: 0;
87
+ overflow: hidden;
88
+ transition: max-height 0.3s ease, opacity 0.3s ease;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
89
  }
90
  </style>
91
  </head>
92
+ <body class="bg-dark-bg text-dark-text min-h-screen flex justify-center items-center p-0 md:p-5">
93
+ <div class="w-full max-w-4xl h-screen md:h-[90vh] bg-dark-card flex flex-col overflow-hidden md:rounded-xl md:border md:border-dark-border">
94
  <!-- Header -->
95
+ <div class="bg-dark-card text-white p-5 text-center border-b border-dark-border flex-shrink-0">
96
+ <h1 class="text-xl font-semibold mb-1">Mariam AI Assistant</h1>
97
+ <p class="text-sm text-dark-muted">❤️</p>
98
+ <div class="flex justify-center items-center gap-5 mt-4">
99
+ <div class="flex items-center gap-3 text-sm text-gray-300">
100
+ <span>Réflexion Mode:</span>
101
+ <label class="relative inline-block w-12 h-7">
102
+ <input type="checkbox" id="thinkingToggle" checked class="opacity-0 w-0 h-0">
103
+ <span class="absolute cursor-pointer top-0 left-0 right-0 bottom-0 bg-gray-600 transition-all duration-200 rounded-full before:absolute before:content-[''] before:h-5 before:w-5 before:left-1 before:bottom-1 before:bg-white before:transition-all before:duration-200 before:rounded-full checked:bg-gray-500 checked:before:translate-x-5"></span>
104
+ </label>
 
105
  </div>
106
+ <button class="bg-transparent border border-gray-600 text-gray-300 px-4 py-2 rounded-full text-sm hover:bg-gray-700 transition-colors" onclick="resetConversation()">
107
+ 🔄 Reset
108
+ </button>
109
  </div>
110
  </div>
111
 
112
  <!-- Messages -->
113
+ <div class="flex-1 overflow-y-auto p-4 md:p-6 flex flex-col gap-4 custom-scrollbar" id="messages">
114
+ <div class="max-w-[85%] p-4 rounded-2xl bg-dark-elevated text-gray-200 self-start rounded-bl-sm animate-fade-in">
115
+ <div class="markdown-content">👋 Bonjour, sur quoi allons-nous travailler aujourd'hui ?</div>
116
  </div>
117
  </div>
118
 
119
  <!-- Typing Indicator -->
120
+ <div class="px-4 md:px-6 pb-2 hidden items-center gap-2 text-gray-500 text-sm italic" id="typingIndicator">
121
  <span>L'assistant réfléchit</span>
122
  <div class="flex gap-1">
123
+ <span class="w-2 h-2 rounded-full bg-gray-600 animate-typing"></span>
124
+ <span class="w-2 h-2 rounded-full bg-gray-600 animate-typing" style="animation-delay: 0.2s;"></span>
125
+ <span class="w-2 h-2 rounded-full bg-gray-600 animate-typing" style="animation-delay: 0.4s;"></span>
126
  </div>
127
  </div>
128
 
129
  <!-- Input Container -->
130
+ <div class="p-4 md:p-6 bg-dark-card border-t border-dark-border flex-shrink-0">
131
+ <div class="mb-3 p-3 bg-dark-elevated rounded-lg text-sm hidden" id="filePreview"></div>
132
  <div class="flex gap-3 items-end">
133
+ <input type="file" id="fileInput" class="hidden" accept="image/*,video/*,.pdf,.txt,.csv,.json" onchange="handleFileSelect(event)">
134
+ <button class="bg-gray-700 text-white h-12 w-12 rounded-full hover:bg-gray-600 transition-colors flex-shrink-0 flex items-center justify-center text-xl" onclick="document.getElementById('fileInput').click()" title="Joindre un fichier">
 
 
 
 
 
 
 
 
 
 
135
  📎
136
  </button>
137
  <div class="flex-1 relative">
138
  <textarea
139
  id="messageInput"
140
+ class="w-full min-h-[48px] p-3 pr-14 border border-gray-600 rounded-3xl text-base resize-none outline-none transition-colors bg-dark-elevated text-dark-text placeholder-gray-500 focus:border-gray-500"
141
  placeholder="Tapez votre message ici..."
142
  rows="1"
143
  onkeydown="handleKeyDown(event)"
144
  oninput="autoResize(this)"
145
  ></textarea>
146
+ <button class="absolute right-1 top-1/2 transform -translate-y-1/2 bg-gray-700 hover:bg-gray-600 disabled:bg-gray-800 disabled:text-gray-600 border-none rounded-full w-10 h-10 text-white cursor-pointer transition-colors flex items-center justify-center text-lg" id="sendButton" onclick="sendMessage()" title="Envoyer">
 
 
 
 
 
147
 
148
  </button>
149
  </div>
 
155
  let currentFile = null;
156
  let conversationId = 'session_' + Date.now();
157
 
158
+ // Configuration de marked pour le markdown
159
  marked.setOptions({
160
  breaks: true,
161
  gfm: true
 
163
 
164
  function autoResize(textarea) {
165
  textarea.style.height = 'auto';
166
+ textarea.style.height = Math.min(textarea.scrollHeight, 150) + 'px';
167
  }
168
 
169
  function handleKeyDown(event) {
 
191
  currentFile = data;
192
  filePreview.innerHTML = `
193
  <div class="flex justify-between items-center">
194
+ <span class="text-gray-300">📎 ${data.filename}</span>
195
+ <button onclick="removeFile()" class="bg-gray-600 hover:bg-gray-500 text-white px-3 py-1 rounded transition-colors">✕</button>
196
  </div>
197
  `;
198
  filePreview.classList.remove('hidden');
 
228
  }
229
 
230
  typingIndicator.classList.remove('hidden');
231
+ typingIndicator.classList.add('flex');
232
 
233
  messageInput.value = '';
234
  autoResize(messageInput);
235
 
236
+ const thinkingEnabled = document.getElementById('thinkingToggle').checked;
237
  const endpoint = currentFile ? '/chat_with_file' : '/chat';
238
  const payload = {
239
  message: message || 'Analyse ce fichier',
240
+ thinking_enabled: thinkingEnabled,
241
  conversation_id: conversationId
242
  };
243
 
 
259
  const decoder = new TextDecoder();
260
  let buffer = '';
261
  let messageElement = null;
262
+ let thoughtsElement = null;
263
+ let thoughtsContainer = null;
264
+ let messageContent = '';
265
+ let thoughtsContent = '';
266
 
267
  function processStream() {
268
  return reader.read().then(({ done, value }) => {
 
280
  const data = JSON.parse(line.substring(6));
281
 
282
  if (data.type === 'text') {
283
+ messageContent += data.content;
284
  if (!messageElement) {
285
  messageElement = addMessage('assistant', '');
286
  }
 
287
  // Rendu markdown en temps réel
288
+ const markdownHtml = marked.parse(messageContent);
289
+ messageElement.querySelector('.markdown-content').innerHTML = markdownHtml;
290
+ } else if (data.type === 'thought' && thinkingEnabled) {
291
+ thoughtsContent += data.content;
292
+ if (!thoughtsContainer) {
293
+ thoughtsContainer = addThoughtsContainer();
294
+ }
295
+ // Rendu markdown des pensées
296
+ const thoughtsHtml = marked.parse(thoughtsContent);
297
+ thoughtsContainer.querySelector('.thoughts-content .markdown-content').innerHTML = thoughtsHtml;
298
  } else if (data.type === 'error') {
299
  showError(data.content);
300
  } else if (data.type === 'end') {
301
  typingIndicator.classList.add('hidden');
302
+ typingIndicator.classList.remove('flex');
303
  messageInput.disabled = false;
304
  sendButton.disabled = false;
305
  messageInput.focus();
306
  removeFile();
307
  return;
308
  }
309
+
310
  const messagesContainer = document.getElementById('messages');
311
  messagesContainer.scrollTop = messagesContainer.scrollHeight;
312
 
 
322
  })
323
  .catch(error => {
324
  typingIndicator.classList.add('hidden');
325
+ typingIndicator.classList.remove('flex');
326
  messageInput.disabled = false;
327
  sendButton.disabled = false;
328
  showError('Erreur: ' + error.message);
 
335
  const messageDiv = document.createElement('div');
336
 
337
  if (role === 'user') {
338
+ messageDiv.className = 'max-w-[85%] p-4 rounded-2xl bg-gray-600 text-gray-100 self-end rounded-br-sm animate-fade-in';
339
+ const markdownHtml = marked.parse(content);
340
+ messageDiv.innerHTML = `<div class="markdown-content">${markdownHtml}</div>`;
341
  } else {
342
+ messageDiv.className = 'max-w-[85%] p-4 rounded-2xl bg-dark-elevated text-gray-200 self-start rounded-bl-sm animate-fade-in';
343
+ messageDiv.innerHTML = `<div class="markdown-content">${content}</div>`;
344
  }
345
 
346
  messagesContainer.appendChild(messageDiv);
 
348
  return messageDiv;
349
  }
350
 
351
+ function addThoughtsContainer() {
352
+ const messagesContainer = document.getElementById('messages');
353
+ const thoughtsDiv = document.createElement('div');
354
+ thoughtsDiv.className = 'bg-gray-800 border-l-4 border-gray-600 rounded-lg p-4 mb-2 max-w-[90%] self-start';
355
+ thoughtsDiv.innerHTML = `
356
+ <div class="flex items-center justify-between cursor-pointer" onclick="toggleThoughts(this)">
357
+ <div class="flex items-center gap-2 font-medium text-gray-300">
358
+ <span>🧠</span>
359
+ <span>Réflexion de l'assistant</span>
360
+ </div>
361
+ <svg class="w-5 h-5 text-gray-400 transition-transform duration-200" fill="none" stroke="currentColor" viewBox="0 0 24 24">
362
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path>
363
+ </svg>
364
+ </div>
365
+ <input type="checkbox" class="thoughts-toggle hidden">
366
+ <div class="thoughts-content mt-3 text-sm text-gray-400 italic">
367
+ <div class="markdown-content"></div>
368
+ </div>
369
+ `;
370
+ messagesContainer.appendChild(thoughtsDiv);
371
+ messagesContainer.scrollTop = messagesContainer.scrollHeight;
372
+ return thoughtsDiv;
373
  }
374
+
375
+ function toggleThoughts(element) {
376
+ const container = element.parentElement;
377
+ const checkbox = container.querySelector('.thoughts-toggle');
378
+ const arrow = element.querySelector('svg');
379
+
380
+ checkbox.checked = !checkbox.checked;
381
+
382
+ if (checkbox.checked) {
383
+ arrow.style.transform = 'rotate(180deg)';
384
+ } else {
385
+ arrow.style.transform = 'rotate(0deg)';
386
+ }
387
  }
388
 
389
  function showError(message) {
390
  const messagesContainer = document.getElementById('messages');
391
  const errorDiv = document.createElement('div');
392
+ errorDiv.className = 'bg-red-900 text-red-200 border-l-4 border-red-600 p-4 rounded-lg mb-2';
393
  errorDiv.textContent = message;
394
  messagesContainer.appendChild(errorDiv);
395
  messagesContainer.scrollTop = messagesContainer.scrollHeight;
 
405
  .then(response => {
406
  if (response.ok) {
407
  document.getElementById('messages').innerHTML = `
408
+ <div class="max-w-[85%] p-4 rounded-2xl bg-dark-elevated text-gray-200 self-start rounded-bl-sm animate-fade-in">
409
+ <div class="markdown-content">👋 Conversation réinitialisée ! Comment puis-je vous aider ?</div>
410
  </div>
411
  `;
412
  conversationId = 'session_' + Date.now();