Docfile commited on
Commit
a1c9b60
·
verified ·
1 Parent(s): 910f83e

Update templates/maj.html

Browse files
Files changed (1) hide show
  1. templates/maj.html +220 -122
templates/maj.html CHANGED
@@ -1,17 +1,22 @@
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 | Solution Mathématique</title>
 
7
  <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/tailwind.min.css" rel="stylesheet">
 
 
8
  <script>
9
  window.MathJax = {
10
  tex: {
11
  inlineMath: [['$', '$']],
12
  displayMath: [['$$', '$$']],
13
  processEscapes: true,
14
- packages: {'[+]': ['autoload','ams', 'textmacros']}
15
  },
16
  options: {
17
  enableMenu: false,
@@ -26,42 +31,122 @@
26
  };
27
  </script>
28
  <script src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js" id="MathJax-script" async></script>
 
29
  <script src="https://cdn.jsdelivr.net/npm/marked/lib/marked.umd.min.js"></script>
30
 
31
  <style>
32
  @import url('https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;700&display=swap');
33
 
34
- body { font-family: 'Space Grotesk', sans-serif; }
35
- .uploadArea { background: #f3f4f6; border: 2px dashed #d1d5db; transition: border-color 0.2s ease; }
36
- .uploadArea:hover { border-color: #3b82f6; }
37
- .blue-button { background: #3b82f6; transition: background-color 0.2s ease; }
38
- .blue-button:hover { background: #2563eb; }
39
- .loader { width: 48px; height: 48px; border: 3px solid #3b82f6; border-bottom-color: transparent; border-radius: 50%; display: inline-block; animation: rotation 1s linear infinite; }
40
- @keyframes rotation { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
41
- .thought-box { transition: max-height 0.3s ease-out; max-height: 0; overflow: hidden; }
42
- .thought-box.open { max-height: 500px; }
43
- #thoughtsContent, #answerContent { max-height: 500px; overflow-y: auto; scroll-behavior: smooth; word-wrap: break-word; }
44
- #answerContent mjx-container { margin-top: 0.5em; margin-bottom: 0.5em; }
45
- .preview-image { max-width: 300px; max-height: 300px; object-fit: contain; }
46
- .timestamp { color: #3b82f6; font-size: 0.9em; margin-left: 8px; }
47
- table { border-collapse: collapse; width: 100%; margin-bottom: 1rem; border: 1px solid #d1d5db; }
48
- th, td { border: 1px solid #d1d5db; padding: 0.5rem; text-align: left; }
49
- th { background-color: #f3f4f6; font-weight: 600; }
50
- .table-responsive { overflow-x: auto; }
51
- .performance-warning { color: red; font-weight: bold; font-size: 1.2em; margin-top: 10px; margin-bottom: 25px; text-align: center; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52
  </style>
53
  </head>
54
- <body class="p-4 bg-gray-50">
55
- <div class="max-w-4xl mx-auto bg-white p-6 rounded-lg shadow-md">
56
- <header class="p-6 text-center mb-8 border-b">
57
  <h1 class="text-4xl font-bold text-blue-600">Mariam - M-0</h1>
58
- <p class="text-gray-600 mt-2">Solution Mathématique/Physique/Chimie Intelligente</p>
59
- <p class="performance-warning">Vous utilisez actuellement les modèles/performances moyens. Accédez à des performances supérieures avec un abonnement premium !</p>
 
 
60
  </header>
61
 
62
  <main>
63
  <form id="problemForm" class="space-y-6" novalidate>
64
- <div class="uploadArea p-8 text-center relative rounded-md" aria-label="Zone de dépôt d'image">
65
  <input type="file" id="imageInput" accept="image/*" class="absolute inset-0 w-full h-full opacity-0 cursor-pointer" aria-label="Choisir une image">
66
  <div class="space-y-3">
67
  <div class="w-16 h-16 mx-auto border-2 border-blue-400 rounded-full flex items-center justify-center">
@@ -74,11 +159,13 @@
74
  </div>
75
  </div>
76
 
77
- <div id="imagePreview" class="hidden text-center mt-4">
78
- <img id="previewImage" class="preview-image mx-auto border rounded" alt="Prévisualisation de l'image sélectionnée">
79
  </div>
80
 
81
- <button type="submit" class="blue-button w-full py-3 text-white font-medium rounded-lg">Résoudre le problème</button>
 
 
82
  </form>
83
 
84
  <div id="loader" class="hidden mt-8 text-center">
@@ -87,21 +174,21 @@
87
  </div>
88
 
89
  <section id="solution" class="hidden mt-8 space-y-6">
90
- <div class="border rounded-md shadow-sm">
91
- <button id="thoughtsToggle" type="button" class="w-full flex justify-between items-center p-3 bg-gray-100 rounded-t-md">
92
  <span class="font-medium text-gray-700">Processus de Réflexion</span>
93
  <span id="timestamp" class="timestamp"></span>
94
  </button>
95
- <div id="thoughtsBox" class="thought-box border-t">
96
- <div id="thoughtsContent" class="p-4 text-gray-600 prose prose-sm max-w-none"></div>
97
  </div>
98
  </div>
99
-
100
- <div class="border rounded-md shadow-sm">
101
- <div class="flex justify-between items-center p-3 bg-gray-100 rounded-t-md border-b">
102
  <h3 class="text-xl font-bold text-gray-800">Solution</h3>
 
103
  </div>
104
- <div id="answerContent" class="p-4 text-gray-700 table-responsive"></div>
105
  </div>
106
  </section>
107
  </main>
@@ -127,7 +214,8 @@
127
  let answerBuffer = '';
128
  let currentMode = null;
129
  let updateTimeout = null;
130
- let mathJaxProcessing = false;
 
131
 
132
  const updateTimestamp = () => {
133
  if (startTime) {
@@ -144,6 +232,9 @@
144
 
145
  const stopTimer = () => {
146
  clearInterval(timerInterval);
 
 
 
147
  };
148
 
149
  const handleFileSelect = file => {
@@ -174,62 +265,67 @@
174
  dropZone.addEventListener('drop', e => {
175
  e.preventDefault();
176
  dropZone.classList.remove('border-blue-400');
177
- if (e.dataTransfer.files.length > 0) {
178
- imageInput.files = e.dataTransfer.files;
179
- handleFileSelect(e.dataTransfer.files[0]);
180
- }
181
  });
182
 
 
183
  const typesetContentIfReady = async () => {
184
- if (window.mathJaxReady && !mathJaxProcessing && typeof MathJax !== 'undefined' && MathJax.typesetPromise) {
185
- mathJaxProcessing = true;
186
- console.log("Début du rendu MathJax...");
187
- try {
188
- MathJax.startup.document.elements = [answerContent];
189
- await MathJax.typesetPromise();
190
- console.log("Rendu MathJax terminé.");
191
- answerContent.scrollTop = answerContent.scrollHeight;
192
- } catch (error) {
193
- console.error("Erreur pendant MathJax typesetPromise:", error);
194
- } finally {
195
- mathJaxProcessing = false;
196
- console.log("Flag MathJax débloqué.");
197
- }
198
- } else if (mathJaxProcessing) {
199
- console.log('Rendu MathJax déjà en cours, report...');
200
  } else {
201
- console.log('MathJax pas prêt ou indéfini, report du rendu...');
202
- setTimeout(scheduleUpdate, 300);
203
  }
204
  };
205
 
206
- const updateDisplay = () => {
207
- if (typeof marked !== 'undefined' && marked.parse) {
208
- thoughtsContent.innerHTML = marked.parse(thoughtsBuffer || '', { async: false });
 
 
209
  } else {
210
- console.warn("Marked.js n'est pas chargé ou `parse` n'est pas une fonction.");
211
- thoughtsContent.textContent = thoughtsBuffer;
212
  }
213
 
214
- answerContent.innerHTML = answerBuffer;
 
 
 
 
 
 
 
 
 
215
  updateTimeout = null;
216
- typesetContentIfReady();
217
  };
218
 
219
  const scheduleUpdate = () => {
220
  if (updateTimeout) return;
221
- updateTimeout = setTimeout(updateDisplay, 150);
 
222
  };
223
 
 
224
  if (typeof marked !== 'undefined') {
225
- marked.setOptions({
226
- gfm: true,
227
- breaks: true,
228
- mangle: false,
229
- headerIds: false
230
- });
231
- } else {
232
- console.warn("Marked.js n'est pas chargé. 'thoughtsContent' sera affiché en texte brut.");
233
  }
234
 
235
  form.addEventListener('submit', async e => {
@@ -248,7 +344,7 @@
248
  thoughtsBuffer = '';
249
  answerBuffer = '';
250
  currentMode = null;
251
- thoughtsBox.classList.add('open');
252
 
253
  const formData = new FormData();
254
  formData.append('image', file);
@@ -260,86 +356,88 @@
260
  });
261
 
262
  if (!response.ok) {
263
- let errorBody = await response.text();
264
- throw new Error(`Erreur HTTP: ${response.status} ${response.statusText}. ${errorBody ? 'Détails: ' + errorBody : ''}`);
265
  }
266
  if (!response.body) {
267
- throw new Error("La réponse ne contient pas de corps (ReadableStream).");
268
  }
269
 
270
  const reader = response.body.getReader();
271
  const decoder = new TextDecoder();
272
  let buffer = '';
273
- let firstChunkReceived = false;
274
 
275
  const processChunk = async ({ done, value }) => {
276
  if (done) {
277
- if (buffer.startsWith('data:')) {
 
278
  try {
279
- const data = JSON.parse(buffer.slice(5));
280
- if (data.content) {
281
- if (currentMode === 'thinking') thoughtsBuffer += data.content;
282
- else if (currentMode === 'answering') answerBuffer += data.content;
283
- }
 
 
 
284
  } catch(jsonError){
285
- console.error("Erreur JSON dans le buffer final:", jsonError, "Buffer:", buffer);
286
  }
287
- } else if (buffer.trim()) {
288
- console.warn("Données restantes non traitées dans le buffer final:", buffer);
289
  }
290
-
291
- if (updateTimeout) clearTimeout(updateTimeout);
292
- updateDisplay();
293
- stopTimer();
294
  console.log("Stream terminé.");
295
- return true;
296
  }
297
-
298
  buffer += decoder.decode(value, { stream: true });
299
  const lines = buffer.split('\n\n');
300
- buffer = lines.pop() || '';
301
 
302
  for (const line of lines) {
303
  if (!line.startsWith('data:')) continue;
304
  try {
305
- const jsonData = line.slice(5).trim();
306
- if (!jsonData) continue;
307
- const data = JSON.parse(jsonData);
308
-
309
- if (data.mode) {
310
- currentMode = data.mode;
311
- if (!firstChunkReceived) {
312
- loader.classList.add('hidden');
313
- solutionSection.classList.remove('hidden');
314
- firstChunkReceived = true;
315
  }
316
- }
317
- if (data.content) {
318
- if (currentMode === 'thinking') {
319
- thoughtsBuffer += data.content;
320
- } else if (currentMode === 'answering') {
321
- answerBuffer += data.content;
 
 
322
  }
323
- scheduleUpdate();
324
- }
325
  } catch(jsonError) {
326
- console.error("Erreur JSON dans le stream:", jsonError, "Ligne:", line);
 
327
  }
328
  }
329
- return false;
330
  };
331
 
 
332
  let streamFinished = false;
333
  while (!streamFinished) {
334
- const { done, value } = await reader.read();
335
- streamFinished = await processChunk({ done, value });
336
  }
337
 
338
  } catch (error) {
339
- console.error('Erreur lors de la requête ou du traitement du stream:', error);
340
- answerContent.innerHTML = `<p class="text-red-600 font-bold">Une erreur est survenue :</p><p class="text-red-500 mt-2">${error.message}</p>`;
341
- solutionSection.classList.remove('hidden');
342
  loader.classList.add('hidden');
 
343
  stopTimer();
344
  }
345
  });
 
1
+
2
+
3
+ <!DOCTYPE html>
4
  <html lang="fr">
5
  <head>
6
  <meta charset="UTF-8">
7
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
8
  <title>Mariam | Solution Mathématique</title>
9
+ <!-- Tailwind CSS -->
10
  <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/tailwind.min.css" rel="stylesheet">
11
+
12
+ <!-- Configuration MathJax -->
13
  <script>
14
  window.MathJax = {
15
  tex: {
16
  inlineMath: [['$', '$']],
17
  displayMath: [['$$', '$$']],
18
  processEscapes: true,
19
+ packages: {'[+]': ['autoload','ams']}
20
  },
21
  options: {
22
  enableMenu: false,
 
31
  };
32
  </script>
33
  <script src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js" id="MathJax-script" async></script>
34
+ <!-- Marked.js - Toujours inclus au cas où il serait utilisé pour thoughtsContent -->
35
  <script src="https://cdn.jsdelivr.net/npm/marked/lib/marked.umd.min.js"></script>
36
 
37
  <style>
38
  @import url('https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;700&display=swap');
39
 
40
+ body {
41
+ font-family: 'Space Grotesk', sans-serif;
42
+ }
43
+
44
+ .uploadArea {
45
+ background: #f3f4f6;
46
+ border: 2px dashed #d1d5db;
47
+ transition: border-color 0.2s ease;
48
+ }
49
+ .uploadArea:hover {
50
+ border-color: #3b82f6;
51
+ }
52
+
53
+ .blue-button {
54
+ background: #3b82f6;
55
+ transition: background-color 0.2s ease;
56
+ }
57
+ .blue-button:hover {
58
+ background: #2563eb;
59
+ }
60
+
61
+ /* Suppression du style du bouton de téléchargement */
62
+
63
+ .loader {
64
+ width: 48px;
65
+ height: 48px;
66
+ border: 3px solid #3b82f6;
67
+ border-bottom-color: transparent;
68
+ border-radius: 50%;
69
+ display: inline-block;
70
+ animation: rotation 1s linear infinite;
71
+ }
72
+ @keyframes rotation {
73
+ 0% { transform: rotate(0deg); }
74
+ 100% { transform: rotate(360deg); }
75
+ }
76
+
77
+ .thought-box {
78
+ transition: max-height 0.3s ease-out;
79
+ max-height: 0;
80
+ overflow: hidden;
81
+ }
82
+ .thought-box.open {
83
+ max-height: 500px; /* Ajusté si nécessaire */
84
+ }
85
+
86
+ #thoughtsContent, #answerContent {
87
+ max-height: 500px;
88
+ overflow-y: auto;
89
+ scroll-behavior: smooth;
90
+ white-space: pre-wrap; /* Important pour préserver les espaces/retours ligne du LaTeX brut */
91
+ }
92
+
93
+ .preview-image {
94
+ max-width: 300px;
95
+ max-height: 300px;
96
+ object-fit: contain;
97
+ }
98
+
99
+ .timestamp {
100
+ color: #3b82f6;
101
+ font-size: 0.9em;
102
+ margin-left: 8px;
103
+ }
104
+
105
+ /* Styles pour les tables générées par MathJax/Markdown si nécessaire */
106
+ table {
107
+ border-collapse: collapse;
108
+ width: 100%;
109
+ margin-bottom: 1rem;
110
+ border: 1px solid #d1d5db; /* Ajout bordure table */
111
+ }
112
+ th, td {
113
+ border: 1px solid #d1d5db;
114
+ padding: 0.5rem;
115
+ text-align: left;
116
+ }
117
+ th {
118
+ background-color: #f3f4f6;
119
+ font-weight: 600;
120
+ }
121
+ .table-responsive {
122
+ overflow-x: auto;
123
+ }
124
+
125
+ /* Suppression du style pour l'impression */
126
+
127
+ .performance-warning {
128
+ color: red;
129
+ font-weight: bold;
130
+ font-size: 1.2em;
131
+ margin-top: 10px;
132
+ margin-bottom: 25px;
133
+ text-align: center;
134
+ }
135
  </style>
136
  </head>
137
+ <body class="p-4">
138
+ <div class="max-w-4xl mx-auto">
139
+ <header class="p-6 text-center mb-8">
140
  <h1 class="text-4xl font-bold text-blue-600">Mariam - M-0</h1>
141
+ <p class="text-gray-600">Solution Mathématique/Physique/Chimie Intelligente</p>
142
+ <p class="performance-warning">
143
+ Vous utilisez actuellement les modèles/performances moyens. Accédez à des performances supérieures avec un abonnement premium !
144
+ </p>
145
  </header>
146
 
147
  <main>
148
  <form id="problemForm" class="space-y-6" novalidate>
149
+ <div class="uploadArea p-8 text-center relative" aria-label="Zone de dépôt d'image">
150
  <input type="file" id="imageInput" accept="image/*" class="absolute inset-0 w-full h-full opacity-0 cursor-pointer" aria-label="Choisir une image">
151
  <div class="space-y-3">
152
  <div class="w-16 h-16 mx-auto border-2 border-blue-400 rounded-full flex items-center justify-center">
 
159
  </div>
160
  </div>
161
 
162
+ <div id="imagePreview" class="hidden text-center">
163
+ <img id="previewImage" class="preview-image mx-auto" alt="Prévisualisation de l'image sélectionnée">
164
  </div>
165
 
166
+ <button type="submit" class="blue-button w-full py-3 text-white font-medium rounded-lg">
167
+ Résoudre le problème
168
+ </button>
169
  </form>
170
 
171
  <div id="loader" class="hidden mt-8 text-center">
 
174
  </div>
175
 
176
  <section id="solution" class="hidden mt-8 space-y-6">
177
+ <div class="border-t pt-4">
178
+ <button id="thoughtsToggle" type="button" class="w-full flex justify-between items-center p-2">
179
  <span class="font-medium text-gray-700">Processus de Réflexion</span>
180
  <span id="timestamp" class="timestamp"></span>
181
  </button>
182
+ <div id="thoughtsBox" class="thought-box">
183
+ <div id="thoughtsContent" class="p-4 text-gray-600"></div>
184
  </div>
185
  </div>
186
+ <div class="border-t pt-6">
187
+ <div class="flex justify-between items-center mb-4">
 
188
  <h3 class="text-xl font-bold text-gray-800">Solution</h3>
189
+ <!-- Suppression du bouton de téléchargement -->
190
  </div>
191
+ <div id="answerContent" class="text-gray-700 table-responsive"></div>
192
  </div>
193
  </section>
194
  </main>
 
214
  let answerBuffer = '';
215
  let currentMode = null;
216
  let updateTimeout = null;
217
+
218
+ // Suppression de la fonction generatePDF et de son appel
219
 
220
  const updateTimestamp = () => {
221
  if (startTime) {
 
232
 
233
  const stopTimer = () => {
234
  clearInterval(timerInterval);
235
+ startTime = null;
236
+ // Ne pas effacer le timestamp final
237
+ // timestamp.textContent = '';
238
  };
239
 
240
  const handleFileSelect = file => {
 
265
  dropZone.addEventListener('drop', e => {
266
  e.preventDefault();
267
  dropZone.classList.remove('border-blue-400');
268
+ handleFileSelect(e.dataTransfer.files[0]);
 
 
 
269
  });
270
 
271
+ // --- Déclaration de typesetContentIfReady ---
272
  const typesetContentIfReady = async () => {
273
+ if (window.mathJaxReady && typeof MathJax !== 'undefined' && MathJax.typesetPromise) {
274
+ // Cible les deux conteneurs si nécessaire, ou juste answerContent si seul lui contient du LaTeX
275
+ // Pour être sûr, on peut cibler la section solution entière ou des éléments spécifiques.
276
+ // Ici, on cible explicitement answerContent comme dans le code original.
277
+ // Si thoughtsContent peut aussi avoir du MathJax, ajoutez-le au tableau.
278
+ // MathJax.startup.document.elements = [answerContent, thoughtsContent];
279
+ MathJax.startup.document.elements = [answerContent]; // Cible uniquement answerContent
280
+ try {
281
+ await MathJax.typesetPromise();
282
+ // Fait défiler vers le bas après le rendu MathJax
283
+ answerContent.scrollTop = answerContent.scrollHeight;
284
+ // Si thoughtsContent est aussi traité:
285
+ // thoughtsContent.scrollTop = thoughtsContent.scrollHeight;
286
+ } catch (error) {
287
+ console.error("Erreur pendant MathJax typesetPromise:", error);
288
+ }
289
  } else {
290
+ console.log('MathJax pas prêt, report du rendu...');
291
+ setTimeout(typesetContentIfReady, 200); // Réessayer
292
  }
293
  };
294
 
295
+ // --- Déclaration de updateDisplay ---
296
+ const updateDisplay = async () => {
297
+ // Utilise Marked pour thoughtsContent (supposant qu'il contient du Markdown)
298
+ if (typeof marked !== 'undefined') {
299
+ thoughtsContent.innerHTML = marked.parse(thoughtsBuffer);
300
  } else {
301
+ thoughtsContent.textContent = thoughtsBuffer; // Fallback si Marked n'est pas chargé
 
302
  }
303
 
304
+ // **CORRECTION:** Insère le contenu brut dans answerContent, sans passer par Marked
305
+ answerContent.innerHTML = answerBuffer;
306
+
307
+ // Déclenche le rendu MathJax sur le contenu mis à jour
308
+ await typesetContentIfReady();
309
+
310
+ // Scrolling (peut être redondant si fait dans typesetContentIfReady)
311
+ thoughtsContent.scrollTop = thoughtsContent.scrollHeight;
312
+ // answerContent.scrollTop = answerContent.scrollHeight; // Déplacé dans typesetContentIfReady
313
+
314
  updateTimeout = null;
 
315
  };
316
 
317
  const scheduleUpdate = () => {
318
  if (updateTimeout) return;
319
+ // Délai légèrement augmenté pour laisser le temps au DOM de se mettre à jour avant MathJax
320
+ updateTimeout = setTimeout(updateDisplay, 250);
321
  };
322
 
323
+ // Configure Marked (si utilisé pour thoughtsContent)
324
  if (typeof marked !== 'undefined') {
325
+ marked.setOptions({
326
+ gfm: true,
327
+ breaks: true
328
+ });
 
 
 
 
329
  }
330
 
331
  form.addEventListener('submit', async e => {
 
344
  thoughtsBuffer = '';
345
  answerBuffer = '';
346
  currentMode = null;
347
+ thoughtsBox.classList.add('open'); // Ouvre par défaut
348
 
349
  const formData = new FormData();
350
  formData.append('image', file);
 
356
  });
357
 
358
  if (!response.ok) {
359
+ throw new Error(`Erreur HTTP: ${response.status} ${response.statusText}`);
 
360
  }
361
  if (!response.body) {
362
+ throw new Error("La réponse ne contient pas de corps.");
363
  }
364
 
365
  const reader = response.body.getReader();
366
  const decoder = new TextDecoder();
367
  let buffer = '';
368
+ let firstChunkReceived = false; // Pour afficher la section dès réception
369
 
370
  const processChunk = async ({ done, value }) => {
371
  if (done) {
372
+ // Traiter le reste du buffer s'il y en a
373
+ if (buffer && buffer.startsWith('data:')) {
374
  try {
375
+ const data = JSON.parse(buffer.slice(5));
376
+ if (data.content) {
377
+ if (currentMode === 'thinking') {
378
+ thoughtsBuffer += data.content;
379
+ } else if (currentMode === 'answering') {
380
+ answerBuffer += data.content;
381
+ }
382
+ }
383
  } catch(jsonError){
384
+ console.error("Erreur JSON dans le buffer final:", jsonError, "Buffer:", buffer);
385
  }
 
 
386
  }
387
+ // Assurer une dernière mise à jour après la fin du stream
388
+ await updateDisplay();
389
+ stopTimer(); // Arrêter le timer à la fin
 
390
  console.log("Stream terminé.");
391
+ return true; // Indique que le stream est terminé
392
  }
393
+
394
  buffer += decoder.decode(value, { stream: true });
395
  const lines = buffer.split('\n\n');
396
+ buffer = lines.pop() || ''; // Garde la partie incomplète pour le prochain chunk
397
 
398
  for (const line of lines) {
399
  if (!line.startsWith('data:')) continue;
400
  try {
401
+ const data = JSON.parse(line.slice(5));
402
+
403
+ if (data.mode) {
404
+ currentMode = data.mode;
405
+ // Afficher la section dès la première donnée reçue après le début du mode
406
+ if (!firstChunkReceived) {
407
+ loader.classList.add('hidden');
408
+ solutionSection.classList.remove('hidden');
409
+ firstChunkReceived = true;
410
+ }
411
  }
412
+ if (data.content) {
413
+ if (currentMode === 'thinking') {
414
+ thoughtsBuffer += data.content;
415
+ } else if (currentMode === 'answering') {
416
+ answerBuffer += data.content;
417
+ }
418
+ // Mise à jour programmée pour éviter trop d'appels DOM/MathJax
419
+ scheduleUpdate();
420
  }
 
 
421
  } catch(jsonError) {
422
+ console.error("Erreur JSON dans le stream:", jsonError, "Ligne:", line);
423
+ // Continuer avec les lignes suivantes si possible
424
  }
425
  }
426
+ return false; // Indique que le stream n'est pas terminé
427
  };
428
 
429
+ // Boucle de lecture du stream
430
  let streamFinished = false;
431
  while (!streamFinished) {
432
+ const { done, value } = await reader.read();
433
+ streamFinished = await processChunk({ done, value });
434
  }
435
 
436
  } catch (error) {
437
+ console.error('Erreur lors de la récupération ou du traitement:', error);
438
+ alert(`Une erreur est survenue: ${error.message}`);
 
439
  loader.classList.add('hidden');
440
+ solutionSection.classList.add('hidden'); // Cacher si erreur
441
  stopTimer();
442
  }
443
  });