Docfile commited on
Commit
f87680f
·
verified ·
1 Parent(s): 86f1083

Update templates/index.html

Browse files
Files changed (1) hide show
  1. templates/index.html +295 -159
templates/index.html CHANGED
@@ -1,174 +1,310 @@
1
- document.addEventListener('DOMContentLoaded', () => {
2
- const chatForm = document.getElementById('chat-form');
3
- const promptInput = document.getElementById('prompt');
4
- const chatMessages = document.getElementById('chat-messages');
5
- const loadingIndicator = document.getElementById('loading-indicator');
6
- const errorMessageDiv = document.getElementById('error-message');
7
- const webSearchToggle = document.getElementById('web_search_toggle');
8
- const fileUpload = document.getElementById('file_upload');
9
- const fileNameSpan = document.getElementById('file-name');
10
- const sendButton = document.getElementById('send-button');
11
-
12
- // --- Fonctions Utilitaires ---
13
-
14
- function scrollToBottom() {
15
- // Ajoute un léger délai pour laisser le temps au DOM de se mettre à jour
16
- setTimeout(() => {
17
- chatMessages.scrollTop = chatMessages.scrollHeight;
18
- }, 50);
19
- }
20
-
21
- function showLoading(show) {
22
- loadingIndicator.style.display = show ? 'block' : 'none';
23
- sendButton.disabled = show;
24
- promptInput.disabled = show;
25
- // Optionnel: changer l'apparence du bouton pendant le chargement
26
- sendButton.classList.toggle('opacity-50', show);
27
- sendButton.classList.toggle('cursor-not-allowed', show);
28
- }
29
-
30
- function displayError(message) {
31
- errorMessageDiv.textContent = message;
32
- errorMessageDiv.style.display = 'block';
33
- // Cache le message après quelques secondes
34
- setTimeout(() => {
35
- errorMessageDiv.style.display = 'none';
36
- }, 5000);
37
- }
38
-
39
- function addMessageToChat(role, text) {
40
- // Assainir le texte avant de l'insérer (très basique, pour éviter XSS simple)
41
- // Pour du Markdown, une bibliothèque comme 'marked' ou 'showdown' serait nécessaire côté client
42
- // ou s'assurer que le backend renvoie du HTML sûr.
43
- // Ici, on suppose que le backend renvoie du texte simple ou du HTML déjà aseptisé avec | safe
44
- const messageDiv = document.createElement('div');
45
- messageDiv.classList.add('flex', role === 'user' ? 'justify-end' : 'justify-start');
46
-
47
- const bubbleDiv = document.createElement('div');
48
- bubbleDiv.classList.add('p-3', 'rounded-lg', 'max-w-xs', 'md:max-w-md', 'shadow');
49
- if (role === 'user') {
50
- bubbleDiv.classList.add('bg-blue-500', 'text-white', 'rounded-br-none');
51
- } else {
52
- // Pour l'assistant, créer une div interne pour le formatage prose si nécessaire
53
- bubbleDiv.classList.add('bg-gray-200', 'text-gray-800', 'rounded-bl-none');
54
- const proseDiv = document.createElement('div');
55
- proseDiv.classList.add('prose', 'prose-sm', 'max-w-none');
56
- // IMPORTANT: Si le backend renvoie du HTML, il DOIT être sûr.
57
- // Sinon, utiliser textContent pour éviter l'injection de script.
58
- proseDiv.innerHTML = text; // Suppose que le HTML reçu est sûr (ex: via Markdown rendu côté serveur)
59
- // Si juste du texte : proseDiv.textContent = text;
60
- bubbleDiv.appendChild(proseDiv);
61
- }
62
-
63
- // Si c'est un message utilisateur simple (pas de HTML)
64
- if(role === 'user'){
65
- const paragraph = document.createElement('p');
66
- paragraph.classList.add('text-sm');
67
- paragraph.textContent = text; // Utiliser textContent pour la sécurité
68
- bubbleDiv.appendChild(paragraph);
69
- }
70
-
71
-
72
- messageDiv.appendChild(bubbleDiv);
73
- chatMessages.appendChild(messageDiv);
74
-
75
- // Insérer l'indicateur *après* le dernier message pour qu'il soit en bas
76
- chatMessages.appendChild(loadingIndicator);
77
 
78
- scrollToBottom();
79
- }
80
-
81
- // --- Gestionnaires d'événements ---
82
-
83
- // Afficher le nom du fichier sélectionné
84
- fileUpload.addEventListener('change', () => {
85
- if (fileUpload.files.length > 0) {
86
- fileNameSpan.textContent = fileUpload.files[0].name;
87
- fileNameSpan.title = fileUpload.files[0].name; // Pour le nom complet au survol
88
- } else {
89
- fileNameSpan.textContent = '';
90
- fileNameSpan.title = '';
91
- }
92
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
93
 
94
- // Soumission du formulaire via Fetch API
95
- chatForm.addEventListener('submit', async (e) => {
96
- e.preventDefault(); // Empêche le rechargement de la page
97
 
98
- const prompt = promptInput.value.trim();
99
- const file = fileUpload.files[0];
100
- const useWebSearch = webSearchToggle.checked;
 
 
 
 
 
 
 
 
 
 
 
101
 
102
- if (!prompt && !file) {
103
- displayError("Veuillez entrer un message ou sélectionner un fichier.");
104
- return;
 
 
 
 
 
 
 
105
  }
106
 
107
- // Cacher les erreurs précédentes
108
- errorMessageDiv.style.display = 'none';
109
-
110
- // Afficher le message utilisateur immédiatement
111
- let userMessageText = prompt;
112
- if (file) {
113
- userMessageText = `[Fichier: ${file.name}]\n\n${prompt}`;
114
- }
115
- addMessageToChat('user', userMessageText);
116
-
117
- // Préparer les données du formulaire pour l'envoi (y compris le fichier)
118
- const formData = new FormData();
119
- formData.append('prompt', prompt);
120
- formData.append('web_search', useWebSearch);
121
- if (file) {
122
- formData.append('file', file);
123
  }
124
 
125
- // Afficher le chargement et désactiver les entrées
126
- showLoading(true);
127
- promptInput.value = ''; // Vider l'input après l'envoi
128
- fileUpload.value = ''; // Réinitialiser l'input fichier
129
- fileNameSpan.textContent = ''; // Vider le nom du fichier affiché
130
-
131
- try {
132
- const response = await fetch("{{ url_for('chat_api') }}", { // Appel vers la nouvelle route API
133
- method: 'POST',
134
- body: formData, // Pas besoin de 'Content-Type', le navigateur le définit pour FormData
135
- });
136
-
137
- if (!response.ok) {
138
- // Essayer de lire l'erreur JSON si possible
139
- let errorMsg = `Erreur HTTP: ${response.status}`;
140
- try {
141
- const errorData = await response.json();
142
- errorMsg = errorData.error || errorMsg;
143
- } catch (jsonError) {
144
- // Ignorer si la réponse n'est pas du JSON
145
- }
146
- throw new Error(errorMsg);
147
- }
148
 
149
- const data = await response.json();
 
 
 
 
 
 
 
 
150
 
151
- if (data.success && data.message) {
152
- // IMPORTANT: S'assurer que data.message est du HTML sûr ou l'assainir ici
153
- addMessageToChat('assistant', data.message);
154
- } else if (data.error) {
155
- displayError(data.error);
156
- // Optionnel: supprimer le message utilisateur si l'IA n'a pas pu répondre
157
- // (peut être déroutant pour l'utilisateur)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
158
  }
159
 
160
- } catch (error) {
161
- console.error("Erreur lors de l'envoi du message:", error);
162
- displayError(error.message || "Une erreur inconnue est survenue.");
163
- // Optionnel: supprimer le message utilisateur en cas d'erreur réseau grave
164
- } finally {
165
- // Cacher le chargement et réactiver les entrées
166
- showLoading(false);
167
- promptInput.focus(); // Remettre le focus sur l'input
168
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
169
  });
 
170
 
171
- // Scroll initial vers le bas au chargement de la page
172
- scrollToBottom();
173
- promptInput.focus(); // Focus sur l'input au chargement
174
- });
 
1
+ <!DOCTYPE html>
2
+ <html lang="fr">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Mariam AI! (Standalone)</title>
7
+ <!-- Chargement de Tailwind CSS via CDN avec le plugin Forms -->
8
+ <script src="https://cdn.tailwindcss.com?plugins=forms"></script>
9
+ <!-- Favicon (Optionnel) -->
10
+ <link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>🤖</text></svg>">
11
+ <style>
12
+ /* Styles de base pour assurer la hauteur complète et le défilement */
13
+ html, body { height: 100%; margin: 0; padding: 0; }
14
+ body { display: flex; flex-direction: column; font-family: Inter, sans-serif; /* Police sympa via CDN implicite de Tailwind */ }
15
+ #chat-container { display: flex; flex-direction: column; flex-grow: 1; min-height: 0; } /* Crucial pour le scroll */
16
+ #chat-messages { flex-grow: 1; overflow-y: auto; min-height: 0; } /* Crucial pour le scroll */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
 
18
+ /* Style pour la scrollbar (optionnel) */
19
+ ::-webkit-scrollbar { width: 8px; }
20
+ ::-webkit-scrollbar-track { background: #f1f1f1; border-radius: 10px; }
21
+ ::-webkit-scrollbar-thumb { background: #a8a8a8; border-radius: 10px; }
22
+ ::-webkit-scrollbar-thumb:hover { background: #7a7a7a; }
23
+
24
+ /* Amélioration légère du rendu Markdown par défaut */
25
+ .prose code { background-color: #e5e7eb; padding: 0.2em 0.4em; font-size: 85%; border-radius: 3px; }
26
+ .prose pre > code { background-color: transparent; padding: 0; font-size: inherit; border-radius: 0; }
27
+ .prose pre { background-color: #f3f4f6; padding: 1em; border-radius: 6px; overflow-x: auto; }
28
+ .prose blockquote { border-left-color: #9ca3af; }
29
+ </style>
30
+ </head>
31
+ <body class="bg-gray-100 flex flex-col h-screen">
32
+
33
+ <!-- En-tête -->
34
+ <header class="bg-gradient-to-r from-cyan-500 to-blue-500 text-white p-4 shadow-md flex justify-between items-center sticky top-0 z-10 flex-shrink-0">
35
+ <h1 class="text-2xl font-bold">Mariam AI!</h1>
36
+ <!-- Note: Le bouton clear fonctionne si le backend Flask a la route /clear -->
37
+ <form action="/clear" method="POST" id="clear-form">
38
+ <button type="submit" class="bg-red-500 hover:bg-red-600 text-white text-xs font-semibold py-1 px-3 rounded-full transition duration-200">
39
+ Effacer Chat
40
+ </button>
41
+ </form>
42
+ </header>
43
+
44
+ <!-- Conteneur Principal du Chat -->
45
+ <div id="chat-container" class="max-w-4xl w-full mx-auto bg-white shadow-xl rounded-b-lg flex flex-col flex-grow">
46
+
47
+ <!-- Zone d'affichage des messages -->
48
+ <div id="chat-messages" class="flex-grow overflow-y-auto p-6 space-y-4 scroll-smooth">
49
+ <!-- Les messages seront ajoutés ici par JavaScript -->
50
+ <!-- Message initial pour accueillir -->
51
+ <div class="flex justify-start">
52
+ <div class="bg-gray-200 text-gray-800 p-3 rounded-lg rounded-bl-none max-w-xs md:max-w-md shadow">
53
+ <div class="prose prose-sm max-w-none">Bonjour ! Comment puis-je vous aider aujourd'hui ?</div>
54
+ </div>
55
+ </div>
56
+
57
+ <!-- Indicateur de chargement (caché par défaut) -->
58
+ <div id="loading-indicator" class="text-center text-gray-500 italic py-4" style="display: none;">
59
+ <div class="flex justify-center items-center space-x-2">
60
+ <div class="animate-spin rounded-full h-5 w-5 border-b-2 border-blue-500"></div>
61
+ <span>Mariam réfléchit...</span>
62
+ </div>
63
+ </div>
64
+ </div>
65
+
66
+ <!-- Zone d'erreur (cachée par défaut) -->
67
+ <div id="error-message" class="bg-red-100 border-l-4 border-red-500 text-red-700 px-4 py-2 rounded m-4" role="alert" style="display: none;">
68
+ <p class="font-bold">Erreur</p>
69
+ <p id="error-text">Le message d'erreur ira ici.</p>
70
+ </div>
71
+
72
+ <!-- Barre d'options et d'upload -->
73
+ <div class="bg-gray-50 border-t border-gray-200 px-4 py-2 flex-shrink-0">
74
+ <div class="flex items-center justify-between text-sm">
75
+ <label for="web_search_toggle" class="flex items-center space-x-2 cursor-pointer text-gray-600 hover:text-gray-800 select-none">
76
+ <input type="checkbox" id="web_search_toggle" name="web_search" value="true" class="form-checkbox h-4 w-4 rounded text-blue-500 focus:ring-blue-400 focus:ring-offset-0">
77
+ <span>Recherche Web</span>
78
+ </label>
79
+ <div class="flex items-center space-x-2">
80
+ <label for="file_upload" class="cursor-pointer text-blue-500 hover:text-blue-700 font-medium flex items-center">
81
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-1" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
82
+ <path stroke-linecap="round" stroke-linejoin="round" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-8l-4-4m0 0L8 8m4-4v12" />
83
+ </svg>
84
+ <span>Fichier</span>
85
+ <input type="file" id="file_upload" name="file" class="hidden" accept=".jpg,.jpeg,.png,.pdf,.txt">
86
+ </label>
87
+ <span id="file-name" class="text-gray-500 text-xs truncate max-w-[100px]" title=""></span> {# Pour afficher le nom du fichier #}
88
+ </div>
89
+ </div>
90
+ </div>
91
+
92
+ <!-- Formulaire d'entrée -->
93
+ <form id="chat-form" class="bg-gray-100 p-4 border-t border-gray-200 rounded-b-lg flex-shrink-0">
94
+ <div class="flex items-center space-x-3">
95
+ <input type="text" id="prompt" name="prompt" class="flex-grow form-input px-4 py-2 border border-gray-300 rounded-full focus:outline-none focus:ring-2 focus:ring-blue-400 shadow-sm" placeholder="Posez votre question à Mariam..." autocomplete="off">
96
+ <button type="submit" id="send-button" class="bg-blue-500 hover:bg-blue-600 text-white font-bold p-2 rounded-full transition duration-200 flex items-center justify-center shadow-md w-10 h-10 flex-shrink-0">
97
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
98
+ <path d="M10.894 2.553a1 1 0 00-1.788 0l-7 14a1 1 0 001.169 1.409l5-1.429A1 1 0 009 15.571V11a1 1 0 112 0v4.571a1 1 0 00.725.962l5 1.428a1 1 0 001.17-1.408l-7-14z" />
99
+ </svg>
100
+ </button>
101
+ </div>
102
+ </form>
103
 
104
+ </div>
 
 
105
 
106
+ <!-- Script pour l'interaction -->
107
+ <script>
108
+ document.addEventListener('DOMContentLoaded', () => {
109
+ const chatForm = document.getElementById('chat-form');
110
+ const promptInput = document.getElementById('prompt');
111
+ const chatMessages = document.getElementById('chat-messages');
112
+ const loadingIndicator = document.getElementById('loading-indicator');
113
+ const errorMessageDiv = document.getElementById('error-message');
114
+ const errorTextP = document.getElementById('error-text');
115
+ const webSearchToggle = document.getElementById('web_search_toggle');
116
+ const fileUpload = document.getElementById('file_upload');
117
+ const fileNameSpan = document.getElementById('file-name');
118
+ const sendButton = document.getElementById('send-button');
119
+ const clearForm = document.getElementById('clear-form'); // Référence au formulaire clear
120
 
121
+ // URL de l'API Backend (ASSUREZ-VOUS QUE CELA CORRESPOND À VOTRE BACKEND FLASK)
122
+ const API_ENDPOINT = '/api/chat'; // Ou l'URL complète si nécessaire
123
+ const CLEAR_ENDPOINT = '/clear'; // Endpoint pour effacer
124
+
125
+ // --- Fonctions Utilitaires ---
126
+
127
+ function scrollToBottom() {
128
+ setTimeout(() => {
129
+ chatMessages.scrollTop = chatMessages.scrollHeight;
130
+ }, 50);
131
  }
132
 
133
+ function showLoading(show) {
134
+ loadingIndicator.style.display = show ? 'block' : 'none';
135
+ sendButton.disabled = show;
136
+ promptInput.disabled = show;
137
+ sendButton.classList.toggle('opacity-50', show);
138
+ sendButton.classList.toggle('cursor-not-allowed', show);
139
+ if (show) {
140
+ // Déplacer l'indicateur de chargement à la fin de la liste des messages
141
+ chatMessages.appendChild(loadingIndicator);
142
+ scrollToBottom(); // Assure que l'indicateur est visible
143
+ }
 
 
 
 
 
144
  }
145
 
146
+ function displayError(message) {
147
+ errorTextP.textContent = message;
148
+ errorMessageDiv.style.display = 'block';
149
+ // Remonter pour que l'erreur soit visible
150
+ errorMessageDiv.scrollIntoView({ behavior: 'smooth', block: 'center' });
151
+ // Optionnel: Cacher après un délai
152
+ // setTimeout(() => { errorMessageDiv.style.display = 'none'; }, 8000);
153
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
154
 
155
+ function addMessageToChat(role, text, isHtml = false) {
156
+ // Cacher l'erreur si elle était affichée
157
+ errorMessageDiv.style.display = 'none';
158
+
159
+ const messageWrapper = document.createElement('div');
160
+ messageWrapper.classList.add('flex', role === 'user' ? 'justify-end' : 'justify-start', 'mb-4'); // Ajout mb-4
161
+
162
+ const bubbleDiv = document.createElement('div');
163
+ bubbleDiv.classList.add('p-3', 'rounded-lg', 'max-w-xs', 'sm:max-w-md', 'md:max-w-lg', 'shadow-md'); // Augmenté max-w
164
 
165
+ if (role === 'user') {
166
+ bubbleDiv.classList.add('bg-blue-500', 'text-white', 'rounded-br-none');
167
+ const paragraph = document.createElement('p');
168
+ paragraph.classList.add('text-sm', 'break-words'); // Permet la césure
169
+ paragraph.textContent = text; // Toujours utiliser textContent pour l'input utilisateur
170
+ bubbleDiv.appendChild(paragraph);
171
+ } else { // Assistant
172
+ bubbleDiv.classList.add('bg-gray-200', 'text-gray-800', 'rounded-bl-none');
173
+ const proseDiv = document.createElement('div');
174
+ // Ajout de classes prose pour le formatage markdown potentiel
175
+ proseDiv.classList.add('prose', 'prose-sm', 'max-w-none', 'text-gray-800',
176
+ 'prose-headings:text-gray-800', 'prose-a:text-blue-600',
177
+ 'prose-strong:text-gray-800', 'prose-code:text-red-600',
178
+ 'prose-blockquote:text-gray-600', 'break-words'); // Césure aussi
179
+ if (isHtml) {
180
+ // ATTENTION: Suppose que `text` est du HTML SÛR venant du backend
181
+ proseDiv.innerHTML = text;
182
+ } else {
183
+ proseDiv.textContent = text;
184
+ }
185
+ bubbleDiv.appendChild(proseDiv);
186
  }
187
 
188
+ messageWrapper.appendChild(bubbleDiv);
189
+ // Insérer avant l'indicateur de chargement pour qu'il reste en bas
190
+ chatMessages.insertBefore(messageWrapper, loadingIndicator);
191
+
192
+ scrollToBottom();
 
 
 
193
  }
194
+
195
+ // --- Gestionnaires d'événements ---
196
+
197
+ fileUpload.addEventListener('change', () => {
198
+ if (fileUpload.files.length > 0) {
199
+ const name = fileUpload.files[0].name;
200
+ fileNameSpan.textContent = name.length > 15 ? name.substring(0, 12) + '...' : name; // Tronquer si trop long
201
+ fileNameSpan.title = name; // Titre complet au survol
202
+ } else {
203
+ fileNameSpan.textContent = '';
204
+ fileNameSpan.title = '';
205
+ }
206
+ });
207
+
208
+ chatForm.addEventListener('submit', async (e) => {
209
+ e.preventDefault();
210
+
211
+ const prompt = promptInput.value.trim();
212
+ const file = fileUpload.files[0];
213
+ const useWebSearch = webSearchToggle.checked;
214
+
215
+ if (!prompt && !file) {
216
+ displayError("Veuillez entrer un message ou sélectionner un fichier.");
217
+ return;
218
+ }
219
+
220
+ errorMessageDiv.style.display = 'none'; // Cacher ancienne erreur
221
+
222
+ // Construire et afficher le message utilisateur
223
+ let userMessageText = prompt;
224
+ if (file) {
225
+ // Précéder le prompt du nom de fichier pour clarté dans l'UI
226
+ userMessageText = `[${file.name}] ${prompt}`;
227
+ }
228
+ // N'afficher que s'il y a du texte ou un fichier
229
+ if (userMessageText || file) {
230
+ addMessageToChat('user', userMessageText || `[${file.name}]`); // Afficher nom fichier si prompt vide
231
+ }
232
+
233
+
234
+ const formData = new FormData();
235
+ // Ajouter le prompt même s'il est vide si un fichier est présent
236
+ formData.append('prompt', prompt);
237
+ formData.append('web_search', useWebSearch);
238
+ if (file) {
239
+ formData.append('file', file);
240
+ }
241
+
242
+ showLoading(true);
243
+ promptInput.value = ''; // Vider input texte
244
+ fileUpload.value = ''; // Important pour pouvoir re-sélectionner le même fichier
245
+ fileNameSpan.textContent = ''; // Vider nom fichier affiché
246
+ fileNameSpan.title = '';
247
+
248
+ try {
249
+ const response = await fetch(API_ENDPOINT, {
250
+ method: 'POST',
251
+ body: formData,
252
+ });
253
+
254
+ const data = await response.json(); // Toujours essayer de lire le JSON
255
+
256
+ if (!response.ok) {
257
+ // Utiliser le message d'erreur du JSON si disponible
258
+ throw new Error(data.error || `Erreur serveur: ${response.status}`);
259
+ }
260
+
261
+ if (data.success && data.message) {
262
+ // Assumer que data.message est du HTML sûr venant du backend
263
+ addMessageToChat('assistant', data.message, true); // true indique que c'est du HTML
264
+ } else {
265
+ // Si success est true mais pas de message (peu probable) ou success est false
266
+ throw new Error(data.error || "Réponse invalide du serveur.");
267
+ }
268
+
269
+ } catch (error) {
270
+ console.error("Erreur lors de l'envoi:", error);
271
+ displayError(error.message || "Une erreur de connexion est survenue.");
272
+ } finally {
273
+ showLoading(false);
274
+ promptInput.focus();
275
+ }
276
+ });
277
+
278
+ // Optionnel : gestion du "clear" via JS pour éviter rechargement complet
279
+ clearForm.addEventListener('submit', async (e) => {
280
+ e.preventDefault(); // Empêche la soumission classique
281
+ if (confirm("Voulez-vous vraiment effacer la conversation ?")) {
282
+ try {
283
+ const response = await fetch(CLEAR_ENDPOINT, { method: 'POST' });
284
+ if (response.ok) {
285
+ // Effacer les messages côté client
286
+ chatMessages.innerHTML = ''; // Vide la zone de chat
287
+ // Remettre le message d'accueil
288
+ addMessageToChat('assistant', "Conversation effacée. Comment puis-je vous aider ?");
289
+ // Vider aussi l'erreur potentielle
290
+ errorMessageDiv.style.display = 'none';
291
+ console.log("Chat effacé côté serveur.");
292
+ } else {
293
+ throw new Error("Impossible d'effacer côté serveur.");
294
+ }
295
+ } catch (error) {
296
+ console.error("Erreur lors de l'effacement:", error);
297
+ displayError("Erreur lors de l'effacement du chat.");
298
+ }
299
+ }
300
+ });
301
+
302
+
303
+ // Scroll initial et focus
304
+ scrollToBottom();
305
+ promptInput.focus();
306
  });
307
+ </script>
308
 
309
+ </body>
310
+ </html>