Docfile commited on
Commit
1759e8b
·
verified ·
1 Parent(s): 5e4af80

Update templates/index.html

Browse files
Files changed (1) hide show
  1. templates/index.html +604 -114
templates/index.html CHANGED
@@ -121,53 +121,91 @@
121
 
122
  #solution {
123
  background: #fff;
124
- padding: 20px;
125
  border-radius: 8px;
126
  text-align: left;
127
  line-height: 1.8;
128
  font-size: 16px;
129
- /* Allow child divs to handle their own background */
130
- background-color: transparent;
131
- padding: 0; /* Remove padding if children manage it */
132
  }
133
 
134
  .step-section, .code-section, .output-section {
135
- margin: 0;
136
- padding: 15px;
137
  border-radius: 0;
138
- border-left: none;
139
  overflow-x: auto;
140
- white-space: pre-wrap; /* Important for text formatting */
141
- /* Common background or let specifics override */
142
  background-color: #f9f9f9;
143
  }
144
 
145
  .step-section {
146
- border-left: 4px solid var(--primary-color);
147
- padding-left: calc(15px - 4px);
148
- background-color: #f9f9f9; /* Specific background for steps */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
149
  }
150
 
 
 
 
 
 
 
 
 
 
151
 
152
  .code-section {
153
- /* Code section manages its background via children */
154
- background-color: transparent;
155
- padding: 0; /* No padding on the container itself */
156
- border-left: none; /* Override general rule */
157
  }
158
 
159
  .code-header {
160
- background-color: #343a40;
161
- color: white;
162
- padding: 8px 15px;
163
- font-size: 14px;
164
- font-family: 'Courier New', monospace;
165
- display: flex;
166
- justify-content: space-between;
167
- align-items: center;
168
- border-top-left-radius: 0px; /* Managed by first-child rule */
169
- border-top-right-radius: 0px;
170
- }
171
 
172
  .code-content {
173
  margin: 0;
@@ -178,7 +216,7 @@
178
  font-family: 'Courier New', monospace;
179
  font-size: 14px;
180
  line-height: 1.5;
181
- border-bottom-left-radius: 0px; /* Managed by last-child rule */
182
  border-bottom-right-radius: 0px;
183
  }
184
 
@@ -191,65 +229,115 @@
191
  font-size: 14px;
192
  white-space: pre-wrap;
193
  overflow-x: auto;
194
- border-left: none; /* Override general rule */
195
- border-bottom-left-radius: 0px; /* Managed by last-child rule */
196
  border-bottom-right-radius: 0px;
197
  }
198
 
199
  /* Add border-radius back to the very first and very last child inside #solution */
200
  #solution > div:first-child,
201
  #solution > div:first-child .code-header {
202
- border-top-left-radius: 8px;
203
- border-top-right-radius: 8px;
204
  }
205
 
206
  #solution > div:last-child,
207
- #solution > div:last-child .code-content, /* If last element is code */
208
- #solution > div:last-child.output-section /* If last element is output */
209
- {
210
- border-bottom-left-radius: 8px;
211
- border-bottom-right-radius: 8px;
212
- }
213
- /* Ensure bottom radius on code content only if it IS the last element */
214
- #solution > .code-section:last-child .code-content {
215
  border-bottom-left-radius: 8px;
216
  border-bottom-right-radius: 8px;
217
- }
218
- /* If output follows code, remove bottom radius from code content */
219
- #solution > .code-section:not(:last-child) .code-content {
220
- border-bottom-left-radius: 0;
221
- border-bottom-right-radius: 0;
222
- }
223
 
 
 
 
 
224
 
225
- .latex-display {
 
 
 
 
226
  overflow-x: auto;
227
- padding: 10px 0;
228
- margin: 15px 0;
229
- text-align: center;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
230
  }
231
 
232
  .thinking-indicator, .executing-indicator, .answering-indicator {
233
  display: flex;
234
  align-items: center;
235
- padding: 10px;
236
- margin: 10px 20px; /* Add margin to separate from content box */
237
  border-radius: 8px;
238
- font-size: 0.9rem;
 
239
  }
240
 
241
  .thinking-indicator { background-color: #e3f2fd; color: #1565c0; }
242
  .executing-indicator { background-color: #ede7f6; color: #5e35b1; }
243
  .answering-indicator { background-color: #e8f5e9; color: #2e7d32; }
244
 
245
- .indicator-icon { margin-right: 10px; animation: pulse 1.5s infinite ease-in-out; }
246
- @keyframes pulse { 0% { opacity: 0.6; } 50% { opacity: 1; } 100% { opacity: 0.6; } }
 
 
 
 
 
 
 
 
 
247
 
248
- mjx-container { overflow-x: auto; overflow-y: hidden; display: block; margin: 1em 0; text-align: initial; padding: 0 !important; }
249
- mjx-assistive-mml { display: none !important; }
250
- .MathJax nobr,.MathJax .mjx-chtml{ display: inline-block !important; white-space: normal !important; }
251
- .step-section span.MathJax_Preview { display: none !important; }
 
 
 
 
 
252
 
 
 
 
 
 
 
 
 
 
 
 
 
253
  </style>
254
  </head>
255
  <body>
@@ -299,8 +387,8 @@
299
  <i class="fas fa-brain indicator-icon"></i>
300
  <span>Je réfléchis au problème...</span>
301
  </div>
302
- <!-- Container for the dynamically added content blocks -->
303
- <div id="solution" style="background: #fff; border-radius: 8px; overflow: hidden;">
304
  <!-- Content will be added here by JS -->
305
  </div>
306
  </div>
@@ -323,23 +411,396 @@
323
  <script>
324
  window.MathJax = {
325
  tex: {
326
- inlineMath: [['$', '$'], ['\\(', '\\)']],
327
- displayMath: [['$$', '$$'], ['\\[', '\\]']],
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
328
  processEscapes: true,
329
  processEnvironments: true,
330
- packages: {'[+]': ['ams', 'noerrors', 'physics', 'cancel', 'color', 'mhchem', 'mathtools']} // "Full options" packages
331
  },
332
  options: {
333
  enableMenu: false,
334
- ignoreHtmlClass: 'code-content', // Don't process code
335
- processHtmlClass: 'step-section|latex-display', // Process text/latex sections
336
  skipHtmlTags: ['script', 'noscript', 'style', 'textarea', 'pre', 'code']
337
  },
338
  loader: {
339
- load: ['[tex]/ams', '[tex]/noerrors', '[tex]/physics', '[tex]/cancel', '[tex]/color', '[tex]/mhchem', '[tex]/mathtools']
340
  },
341
  svg: {
342
- fontCache: 'global' // Good for performance
 
 
 
 
 
 
 
 
 
 
 
343
  }
344
  };
345
  </script>
@@ -364,13 +825,13 @@
364
  const formData = new FormData(document.getElementById('imageForm'));
365
  const solutionOutput = document.getElementById('solutionOutput');
366
  const loadingIndicator = document.getElementById('loadingIndicator');
367
- const solutionContainer = document.getElementById('solution'); // Target the inner container
368
 
369
  solutionOutput.style.display = 'block';
370
  loadingIndicator.style.display = 'flex';
371
- solutionContainer.innerHTML = ''; // Clear previous solution
372
 
373
- fetch('/solved', { // Ensure this endpoint exists and processes the image
374
  method: 'POST',
375
  body: formData
376
  })
@@ -385,13 +846,16 @@
385
  function processStream({ done, value }) {
386
  if (done) {
387
  loadingIndicator.style.display = 'none';
 
 
 
 
388
  return;
389
  }
390
 
391
  buffer += decoder.decode(value, { stream: true });
392
- // Split reliably by double newline, handling different line ending types
393
  const lines = buffer.split(/\r?\n\r?\n/);
394
- buffer = lines.pop(); // Keep potentially incomplete chunk
395
 
396
  for (const line of lines) {
397
  if (line.startsWith('data: ')) {
@@ -399,74 +863,82 @@
399
  const data = JSON.parse(line.substr(6));
400
 
401
  if (data.mode) {
402
- // Update loading indicator (same as before)
403
- if (data.mode === 'thinking') { /* ... */ }
404
- else if (data.mode === 'answering') { /* ... */ }
405
- /* ... other modes ... */
406
-
407
- // Simpler indicator update
408
- const modes = {
409
- thinking: { icon: 'fa-brain', text: 'Je réfléchis...', class: 'thinking-indicator' },
410
- answering: { icon: 'fa-pencil-alt', text: 'Rédaction...', class: 'answering-indicator' },
411
- executing_code: { icon: 'fa-code', text: 'Exécution...', class: 'executing-indicator' },
412
- code_result: { icon: 'fa-terminal', text: 'Résultats...', class: 'executing-indicator' }
413
- };
414
- const modeInfo = modes[data.mode];
415
- if (modeInfo) {
416
- loadingIndicator.className = modeInfo.class;
417
- loadingIndicator.innerHTML = `<i class="fas ${modeInfo.icon} indicator-icon"></i><span>${modeInfo.text}</span>`;
418
- }
419
  }
420
 
421
  if (data.content) {
422
  const content = data.content;
423
- // Create a temporary div to parse the potential HTML structure from content
424
  const tempDiv = document.createElement('div');
425
- tempDiv.innerHTML = content; // Let browser parse potential HTML within content
426
 
427
- // Check if content contains a code block structure
428
  const codeSection = tempDiv.querySelector('.code-section');
429
  const outputSection = tempDiv.querySelector('.output-section');
430
 
431
  if (codeSection) {
432
- // It's a code block, append it directly
433
  solutionContainer.appendChild(codeSection);
434
- // Highlight the code within the appended section
435
  codeSection.querySelectorAll('pre code').forEach((block) => {
436
  hljs.highlightElement(block);
437
  });
438
  } else if (outputSection) {
439
- // It's an output block
440
- solutionContainer.appendChild(outputSection);
441
- }
442
- else {
443
- // Assume it's a regular text/LaTeX step section
444
  const stepDiv = document.createElement('div');
445
  stepDiv.className = 'step-section';
446
- // Use innerHTML to correctly interpret potential entities from JSON string
447
- stepDiv.innerHTML = content;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
448
  solutionContainer.appendChild(stepDiv);
449
- // Typeset MathJax for this specific new section
450
- MathJax.typesetPromise([stepDiv]).catch(e => console.error('MathJax typesetting error:', e));
 
 
 
451
  }
452
  }
453
 
454
-
455
  if (data.error) {
456
  const errorDiv = document.createElement('div');
457
- errorDiv.className = 'step-section'; // Style like other steps
458
- errorDiv.style.color = 'red';
459
- errorDiv.style.backgroundColor = '#ffeeee';
460
- errorDiv.style.borderColor = 'red';
461
- errorDiv.textContent = `Erreur: ${data.error}`;
462
  solutionContainer.appendChild(errorDiv);
463
  loadingIndicator.style.display = 'none';
464
  }
465
  } catch (e) {
466
  console.error('Error parsing JSON or processing chunk:', e, line);
467
- // Optionally display a generic error message chunk
468
  const errorDiv = document.createElement('div');
469
- errorDiv.className = 'step-section'; errorDiv.style.color = 'orange';
 
470
  errorDiv.textContent = `Erreur traitement chunk: ${line.substring(0, 100)}...`;
471
  solutionContainer.appendChild(errorDiv);
472
  }
@@ -481,16 +953,34 @@
481
  })
482
  .catch(error => {
483
  const errorDiv = document.createElement('div');
484
- errorDiv.className = 'step-section'; // Style like other steps
485
  errorDiv.style.color = 'red';
486
  errorDiv.style.backgroundColor = '#ffeeee';
487
  errorDiv.style.borderColor = 'red';
488
  errorDiv.textContent = `Erreur de connexion ou du serveur: ${error}`;
489
- solutionContainer.appendChild(errorDiv); // Append error inside the solution area
490
  loadingIndicator.style.display = 'none';
491
  console.error('Fetch error:', error);
492
  });
493
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
494
  </script>
495
  </body>
496
  </html>
 
121
 
122
  #solution {
123
  background: #fff;
124
+ padding: 0;
125
  border-radius: 8px;
126
  text-align: left;
127
  line-height: 1.8;
128
  font-size: 16px;
129
+ background-color: transparent;
130
+ box-shadow: var(--box-shadow);
131
+ overflow: hidden;
132
  }
133
 
134
  .step-section, .code-section, .output-section {
135
+ margin: 0 0 1px 0;
136
+ padding: 20px;
137
  border-radius: 0;
 
138
  overflow-x: auto;
 
 
139
  background-color: #f9f9f9;
140
  }
141
 
142
  .step-section {
143
+ border-left: 4px solid var(--primary-color);
144
+ padding-left: calc(20px - 4px);
145
+ background-color: #f9f9f9;
146
+ font-size: 16px;
147
+ line-height: 1.8;
148
+ }
149
+
150
+ /* Improved LaTeX styling */
151
+ .step-section .mjx-chtml {
152
+ display: inline-block !important;
153
+ line-height: 0;
154
+ text-indent: 0;
155
+ text-align: left;
156
+ text-transform: none;
157
+ font-size: 100%;
158
+ font-style: normal;
159
+ font-weight: normal;
160
+ font-family: MJXZERO, MJXTEX;
161
+ direction: ltr;
162
+ margin: 0 0.2em;
163
+ vertical-align: -0.25em;
164
+ }
165
+
166
+ .step-section .MathJax {
167
+ display: inline !important;
168
+ font-size: 100% !important;
169
+ text-align: left;
170
+ }
171
+
172
+ /* Fix for inline math to prevent weird line breaks */
173
+ .step-section p {
174
+ display: block;
175
+ margin-block-start: 1em;
176
+ margin-block-end: 1em;
177
+ margin-inline-start: 0px;
178
+ margin-inline-end: 0px;
179
  }
180
 
181
+ /* Improve display for block equations */
182
+ .latex-display {
183
+ overflow-x: auto;
184
+ padding: 10px 0;
185
+ margin: 15px 0;
186
+ text-align: center;
187
+ display: block;
188
+ width: 100%;
189
+ }
190
 
191
  .code-section {
192
+ background-color: transparent;
193
+ padding: 0;
194
+ border-left: none;
 
195
  }
196
 
197
  .code-header {
198
+ background-color: #343a40;
199
+ color: white;
200
+ padding: 10px 15px;
201
+ font-size: 14px;
202
+ font-family: 'Courier New', monospace;
203
+ display: flex;
204
+ justify-content: space-between;
205
+ align-items: center;
206
+ border-top-left-radius: 0px;
207
+ border-top-right-radius: 0px;
208
+ }
209
 
210
  .code-content {
211
  margin: 0;
 
216
  font-family: 'Courier New', monospace;
217
  font-size: 14px;
218
  line-height: 1.5;
219
+ border-bottom-left-radius: 0px;
220
  border-bottom-right-radius: 0px;
221
  }
222
 
 
229
  font-size: 14px;
230
  white-space: pre-wrap;
231
  overflow-x: auto;
232
+ border-left: none;
233
+ border-bottom-left-radius: 0px;
234
  border-bottom-right-radius: 0px;
235
  }
236
 
237
  /* Add border-radius back to the very first and very last child inside #solution */
238
  #solution > div:first-child,
239
  #solution > div:first-child .code-header {
240
+ border-top-left-radius: 8px;
241
+ border-top-right-radius: 8px;
242
  }
243
 
244
  #solution > div:last-child,
245
+ #solution > div:last-child .code-content,
246
+ #solution > div:last-child.output-section {
247
+ border-bottom-left-radius: 8px;
248
+ border-bottom-right-radius: 8px;
249
+ }
250
+
251
+ #solution > .code-section:last-child .code-content {
 
252
  border-bottom-left-radius: 8px;
253
  border-bottom-right-radius: 8px;
254
+ }
 
 
 
 
 
255
 
256
+ #solution > .code-section:not(:last-child) .code-content {
257
+ border-bottom-left-radius: 0;
258
+ border-bottom-right-radius: 0;
259
+ }
260
 
261
+ /* Fixed display of MathJax content */
262
+ .mjx-container {
263
+ display: inline-block !important;
264
+ margin: 0 !important;
265
+ text-align: center !important;
266
  overflow-x: auto;
267
+ overflow-y: hidden;
268
+ padding: 0 !important;
269
+ }
270
+
271
+ .mjx-container.MJX-display {
272
+ display: block !important;
273
+ margin: 1em auto !important;
274
+ text-align: center !important;
275
+ max-width: 100%;
276
+ }
277
+
278
+ /* Fix markdown list rendering */
279
+ .step-section ul, .step-section ol {
280
+ display: block;
281
+ margin-block-start: 1em;
282
+ margin-block-end: 1em;
283
+ padding-inline-start: 30px;
284
+ margin-inline-start: 0px;
285
+ margin-inline-end: 0px;
286
+ }
287
+
288
+ .step-section ul li, .step-section ol li {
289
+ display: list-item;
290
+ margin-bottom: 0.5em;
291
  }
292
 
293
  .thinking-indicator, .executing-indicator, .answering-indicator {
294
  display: flex;
295
  align-items: center;
296
+ padding: 12px 15px;
297
+ margin: 10px 0;
298
  border-radius: 8px;
299
+ font-size: 0.95rem;
300
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
301
  }
302
 
303
  .thinking-indicator { background-color: #e3f2fd; color: #1565c0; }
304
  .executing-indicator { background-color: #ede7f6; color: #5e35b1; }
305
  .answering-indicator { background-color: #e8f5e9; color: #2e7d32; }
306
 
307
+ .indicator-icon {
308
+ margin-right: 12px;
309
+ animation: pulse 1.5s infinite ease-in-out;
310
+ font-size: 1.1rem;
311
+ }
312
+
313
+ @keyframes pulse {
314
+ 0% { opacity: 0.6; }
315
+ 50% { opacity: 1; }
316
+ 100% { opacity: 0.6; }
317
+ }
318
 
319
+ /* Fix assistive MML elements */
320
+ mjx-assistive-mml {
321
+ position: absolute !important;
322
+ top: 0 !important;
323
+ left: 0 !important;
324
+ clip: rect(1px, 1px, 1px, 1px) !important;
325
+ padding: 1px 0 0 0 !important;
326
+ display: block !important;
327
+ }
328
 
329
+ /* Unified consistent LaTeX size for all equations */
330
+ .step-section .MathJax {
331
+ font-size: 16px !important;
332
+ }
333
+
334
+ .step-section .MathJax_Display {
335
+ text-align: center !important;
336
+ margin: 1em 0 !important;
337
+ display: block !important;
338
+ overflow-x: auto;
339
+ overflow-y: hidden;
340
+ }
341
  </style>
342
  </head>
343
  <body>
 
387
  <i class="fas fa-brain indicator-icon"></i>
388
  <span>Je réfléchis au problème...</span>
389
  </div>
390
+ <!-- Container for the dynamically added content blocks -->
391
+ <div id="solution">
392
  <!-- Content will be added here by JS -->
393
  </div>
394
  </div>
 
411
  <script>
412
  window.MathJax = {
413
  tex: {
414
+ inlineMath: [['
415
+ </script>
416
+ <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-svg.js"></script>
417
+
418
+ <script>
419
+ document.getElementById('imageInput').addEventListener('change', function(event) {
420
+ const file = event.target.files[0];
421
+ if (file) {
422
+ const reader = new FileReader();
423
+ reader.onload = function(e) {
424
+ document.getElementById('preview').src = e.target.result;
425
+ document.getElementById('imagePreview').style.display = 'block';
426
+ document.getElementById('solveButton').style.display = 'inline-block';
427
+ document.getElementById('uploadStatus').textContent = `Image sélectionnée : ${file.name}`;
428
+ }
429
+ reader.readAsDataURL(file);
430
+ }
431
+ });
432
+
433
+ document.getElementById('solveButton').addEventListener('click', function() {
434
+ const formData = new FormData(document.getElementById('imageForm'));
435
+ const solutionOutput = document.getElementById('solutionOutput');
436
+ const loadingIndicator = document.getElementById('loadingIndicator');
437
+ const solutionContainer = document.getElementById('solution');
438
+
439
+ solutionOutput.style.display = 'block';
440
+ loadingIndicator.style.display = 'flex';
441
+ solutionContainer.innerHTML = '';
442
+
443
+ fetch('/solved', {
444
+ method: 'POST',
445
+ body: formData
446
+ })
447
+ .then(response => {
448
+ if (!response.ok) {
449
+ throw new Error(`Erreur serveur: ${response.statusText}`);
450
+ }
451
+ const reader = response.body.getReader();
452
+ const decoder = new TextDecoder();
453
+ let buffer = '';
454
+
455
+ function processStream({ done, value }) {
456
+ if (done) {
457
+ loadingIndicator.style.display = 'none';
458
+ // Force MathJax to reprocess entire solution when done
459
+ if (typeof MathJax !== 'undefined') {
460
+ MathJax.typesetPromise([solutionContainer]).catch(e => console.error('MathJax final typesetting error:', e));
461
+ }
462
+ return;
463
+ }
464
+
465
+ buffer += decoder.decode(value, { stream: true });
466
+ const lines = buffer.split(/\r?\n\r?\n/);
467
+ buffer = lines.pop();
468
+
469
+ for (const line of lines) {
470
+ if (line.startsWith('data: ')) {
471
+ try {
472
+ const data = JSON.parse(line.substr(6));
473
+
474
+ if (data.mode) {
475
+ const modes = {
476
+ thinking: { icon: 'fa-brain', text: 'Je réfléchis...', class: 'thinking-indicator' },
477
+ answering: { icon: 'fa-pencil-alt', text: 'Rédaction...', class: 'answering-indicator' },
478
+ executing_code: { icon: 'fa-code', text: 'Exécution...', class: 'executing-indicator' },
479
+ code_result: { icon: 'fa-terminal', text: 'Résultats...', class: 'executing-indicator' }
480
+ };
481
+ const modeInfo = modes[data.mode];
482
+ if (modeInfo) {
483
+ loadingIndicator.className = modeInfo.class;
484
+ loadingIndicator.innerHTML = `<i class="fas ${modeInfo.icon} indicator-icon"></i><span>${modeInfo.text}</span>`;
485
+ }
486
+ }
487
+
488
+ if (data.content) {
489
+ const content = data.content;
490
+ const tempDiv = document.createElement('div');
491
+ tempDiv.innerHTML = content;
492
+
493
+ const codeSection = tempDiv.querySelector('.code-section');
494
+ const outputSection = tempDiv.querySelector('.output-section');
495
+
496
+ if (codeSection) {
497
+ solutionContainer.appendChild(codeSection);
498
+ codeSection.querySelectorAll('pre code').forEach((block) => {
499
+ hljs.highlightElement(block);
500
+ });
501
+ } else if (outputSection) {
502
+ solutionContainer.appendChild(outputSection);
503
+ } else {
504
+ // Process regular text/LaTeX step section
505
+ const stepDiv = document.createElement('div');
506
+ stepDiv.className = 'step-section';
507
+
508
+ // Fix markdown content before adding
509
+ let processedContent = content;
510
+
511
+ // Ensure proper paragraph breaks
512
+ processedContent = processedContent.replace(/\n\n/g, '</p><p>');
513
+
514
+ // Wrap in paragraph tags if not already
515
+ if (!processedContent.startsWith('<p>')) {
516
+ processedContent = '<p>' + processedContent;
517
+ }
518
+ if (!processedContent.endsWith('</p>')) {
519
+ processedContent = processedContent + '</p>';
520
+ }
521
+
522
+ // Fix lists in markdown
523
+ processedContent = processedContent.replace(/<p>(\s*[-*]\s+.*?)<\/p>/g, '<ul><li>$1</li></ul>');
524
+ processedContent = processedContent.replace(/<p>(\s*\d+\.\s+.*?)<\/p>/g, '<ol><li>$1</li></ol>');
525
+
526
+ stepDiv.innerHTML = processedContent;
527
+ solutionContainer.appendChild(stepDiv);
528
+
529
+ // Process MathJax for this section
530
+ if (typeof MathJax !== 'undefined') {
531
+ MathJax.typesetPromise([stepDiv]).catch(e => console.error('MathJax typesetting error:', e));
532
+ }
533
+ }
534
+ }
535
+
536
+ if (data.error) {
537
+ const errorDiv = document.createElement('div');
538
+ errorDiv.className = 'step-section';
539
+ errorDiv.style.color = 'red';
540
+ errorDiv.style.backgroundColor = '#ffeeee';
541
+ errorDiv.style.borderColor = 'red';
542
+ errorDiv.textContent = `Erreur: ${data.error}`;
543
+ solutionContainer.appendChild(errorDiv);
544
+ loadingIndicator.style.display = 'none';
545
+ }
546
+ } catch (e) {
547
+ console.error('Error parsing JSON or processing chunk:', e, line);
548
+ const errorDiv = document.createElement('div');
549
+ errorDiv.className = 'step-section';
550
+ errorDiv.style.color = 'orange';
551
+ errorDiv.textContent = `Erreur traitement chunk: ${line.substring(0, 100)}...`;
552
+ solutionContainer.appendChild(errorDiv);
553
+ }
554
+ }
555
+ }
556
+
557
+ window.scrollTo(0, document.body.scrollHeight);
558
+ return reader.read().then(processStream);
559
+ }
560
+
561
+ return reader.read().then(processStream);
562
+ })
563
+ .catch(error => {
564
+ const errorDiv = document.createElement('div');
565
+ errorDiv.className = 'step-section';
566
+ errorDiv.style.color = 'red';
567
+ errorDiv.style.backgroundColor = '#ffeeee';
568
+ errorDiv.style.borderColor = 'red';
569
+ errorDiv.textContent = `Erreur de connexion ou du serveur: ${error}`;
570
+ solutionContainer.appendChild(errorDiv);
571
+ loadingIndicator.style.display = 'none';
572
+ console.error('Fetch error:', error);
573
+ });
574
+ });
575
+
576
+ // Helper function to ensure proper markdown formatting
577
+ function processMarkdown(content) {
578
+ // Process lists
579
+ content = content.replace(/^(\s*[-*]\s+.*?)$/gm, '<li>$1</li>');
580
+ content = content.replace(/^(\s*\d+\.\s+.*?)$/gm, '<li>$1</li>');
581
+
582
+ // Wrap adjacent list items in proper containers
583
+ content = content.replace(/<li>.*?<\/li>(\s*<li>.*?<\/li>)+/g, match => {
584
+ if (match.includes('*') || match.includes('-')) {
585
+ return '<ul>' + match + '</ul>';
586
+ } else {
587
+ return '<ol>' + match + '</ol>';
588
+ }
589
+ });
590
+
591
+ return content;
592
+ }
593
+ </script>
594
+ </body>
595
+ </html>, '
596
+ </script>
597
+ <script id="MathJax-script" async src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/3.2.2/es5/tex-svg.js"></script>
598
+
599
+ <script>
600
+ document.getElementById('imageInput').addEventListener('change', function(event) {
601
+ const file = event.target.files[0];
602
+ if (file) {
603
+ const reader = new FileReader();
604
+ reader.onload = function(e) {
605
+ document.getElementById('preview').src = e.target.result;
606
+ document.getElementById('imagePreview').style.display = 'block';
607
+ document.getElementById('solveButton').style.display = 'inline-block';
608
+ document.getElementById('uploadStatus').textContent = `Image sélectionnée : ${file.name}`;
609
+ }
610
+ reader.readAsDataURL(file);
611
+ }
612
+ });
613
+
614
+ document.getElementById('solveButton').addEventListener('click', function() {
615
+ const formData = new FormData(document.getElementById('imageForm'));
616
+ const solutionOutput = document.getElementById('solutionOutput');
617
+ const loadingIndicator = document.getElementById('loadingIndicator');
618
+ const solutionContainer = document.getElementById('solution');
619
+
620
+ solutionOutput.style.display = 'block';
621
+ loadingIndicator.style.display = 'flex';
622
+ solutionContainer.innerHTML = '';
623
+
624
+ fetch('/solved', {
625
+ method: 'POST',
626
+ body: formData
627
+ })
628
+ .then(response => {
629
+ if (!response.ok) {
630
+ throw new Error(`Erreur serveur: ${response.statusText}`);
631
+ }
632
+ const reader = response.body.getReader();
633
+ const decoder = new TextDecoder();
634
+ let buffer = '';
635
+
636
+ function processStream({ done, value }) {
637
+ if (done) {
638
+ loadingIndicator.style.display = 'none';
639
+ // Force MathJax to reprocess entire solution when done
640
+ if (typeof MathJax !== 'undefined') {
641
+ MathJax.typesetPromise([solutionContainer]).catch(e => console.error('MathJax final typesetting error:', e));
642
+ }
643
+ return;
644
+ }
645
+
646
+ buffer += decoder.decode(value, { stream: true });
647
+ const lines = buffer.split(/\r?\n\r?\n/);
648
+ buffer = lines.pop();
649
+
650
+ for (const line of lines) {
651
+ if (line.startsWith('data: ')) {
652
+ try {
653
+ const data = JSON.parse(line.substr(6));
654
+
655
+ if (data.mode) {
656
+ const modes = {
657
+ thinking: { icon: 'fa-brain', text: 'Je réfléchis...', class: 'thinking-indicator' },
658
+ answering: { icon: 'fa-pencil-alt', text: 'Rédaction...', class: 'answering-indicator' },
659
+ executing_code: { icon: 'fa-code', text: 'Exécution...', class: 'executing-indicator' },
660
+ code_result: { icon: 'fa-terminal', text: 'Résultats...', class: 'executing-indicator' }
661
+ };
662
+ const modeInfo = modes[data.mode];
663
+ if (modeInfo) {
664
+ loadingIndicator.className = modeInfo.class;
665
+ loadingIndicator.innerHTML = `<i class="fas ${modeInfo.icon} indicator-icon"></i><span>${modeInfo.text}</span>`;
666
+ }
667
+ }
668
+
669
+ if (data.content) {
670
+ const content = data.content;
671
+ const tempDiv = document.createElement('div');
672
+ tempDiv.innerHTML = content;
673
+
674
+ const codeSection = tempDiv.querySelector('.code-section');
675
+ const outputSection = tempDiv.querySelector('.output-section');
676
+
677
+ if (codeSection) {
678
+ solutionContainer.appendChild(codeSection);
679
+ codeSection.querySelectorAll('pre code').forEach((block) => {
680
+ hljs.highlightElement(block);
681
+ });
682
+ } else if (outputSection) {
683
+ solutionContainer.appendChild(outputSection);
684
+ } else {
685
+ // Process regular text/LaTeX step section
686
+ const stepDiv = document.createElement('div');
687
+ stepDiv.className = 'step-section';
688
+
689
+ // Fix markdown content before adding
690
+ let processedContent = content;
691
+
692
+ // Ensure proper paragraph breaks
693
+ processedContent = processedContent.replace(/\n\n/g, '</p><p>');
694
+
695
+ // Wrap in paragraph tags if not already
696
+ if (!processedContent.startsWith('<p>')) {
697
+ processedContent = '<p>' + processedContent;
698
+ }
699
+ if (!processedContent.endsWith('</p>')) {
700
+ processedContent = processedContent + '</p>';
701
+ }
702
+
703
+ // Fix lists in markdown
704
+ processedContent = processedContent.replace(/<p>(\s*[-*]\s+.*?)<\/p>/g, '<ul><li>$1</li></ul>');
705
+ processedContent = processedContent.replace(/<p>(\s*\d+\.\s+.*?)<\/p>/g, '<ol><li>$1</li></ol>');
706
+
707
+ stepDiv.innerHTML = processedContent;
708
+ solutionContainer.appendChild(stepDiv);
709
+
710
+ // Process MathJax for this section
711
+ if (typeof MathJax !== 'undefined') {
712
+ MathJax.typesetPromise([stepDiv]).catch(e => console.error('MathJax typesetting error:', e));
713
+ }
714
+ }
715
+ }
716
+
717
+ if (data.error) {
718
+ const errorDiv = document.createElement('div');
719
+ errorDiv.className = 'step-section';
720
+ errorDiv.style.color = 'red';
721
+ errorDiv.style.backgroundColor = '#ffeeee';
722
+ errorDiv.style.borderColor = 'red';
723
+ errorDiv.textContent = `Erreur: ${data.error}`;
724
+ solutionContainer.appendChild(errorDiv);
725
+ loadingIndicator.style.display = 'none';
726
+ }
727
+ } catch (e) {
728
+ console.error('Error parsing JSON or processing chunk:', e, line);
729
+ const errorDiv = document.createElement('div');
730
+ errorDiv.className = 'step-section';
731
+ errorDiv.style.color = 'orange';
732
+ errorDiv.textContent = `Erreur traitement chunk: ${line.substring(0, 100)}...`;
733
+ solutionContainer.appendChild(errorDiv);
734
+ }
735
+ }
736
+ }
737
+
738
+ window.scrollTo(0, document.body.scrollHeight);
739
+ return reader.read().then(processStream);
740
+ }
741
+
742
+ return reader.read().then(processStream);
743
+ })
744
+ .catch(error => {
745
+ const errorDiv = document.createElement('div');
746
+ errorDiv.className = 'step-section';
747
+ errorDiv.style.color = 'red';
748
+ errorDiv.style.backgroundColor = '#ffeeee';
749
+ errorDiv.style.borderColor = 'red';
750
+ errorDiv.textContent = `Erreur de connexion ou du serveur: ${error}`;
751
+ solutionContainer.appendChild(errorDiv);
752
+ loadingIndicator.style.display = 'none';
753
+ console.error('Fetch error:', error);
754
+ });
755
+ });
756
+
757
+ // Helper function to ensure proper markdown formatting
758
+ function processMarkdown(content) {
759
+ // Process lists
760
+ content = content.replace(/^(\s*[-*]\s+.*?)$/gm, '<li>$1</li>');
761
+ content = content.replace(/^(\s*\d+\.\s+.*?)$/gm, '<li>$1</li>');
762
+
763
+ // Wrap adjacent list items in proper containers
764
+ content = content.replace(/<li>.*?<\/li>(\s*<li>.*?<\/li>)+/g, match => {
765
+ if (match.includes('*') || match.includes('-')) {
766
+ return '<ul>' + match + '</ul>';
767
+ } else {
768
+ return '<ol>' + match + '</ol>';
769
+ }
770
+ });
771
+
772
+ return content;
773
+ }
774
+ </script>
775
+ </body>
776
+ </html>], ['\\(', '\\)']],
777
+ displayMath: [['$', '$'], ['\\[', '\\]']],
778
  processEscapes: true,
779
  processEnvironments: true,
780
+ packages: {'[+]': ['noerrors', 'physics', 'cancel', 'color']}
781
  },
782
  options: {
783
  enableMenu: false,
784
+ ignoreHtmlClass: 'code-content',
785
+ processHtmlClass: 'step-section|latex-display',
786
  skipHtmlTags: ['script', 'noscript', 'style', 'textarea', 'pre', 'code']
787
  },
788
  loader: {
789
+ load: ['input/tex-full', 'output/svg']
790
  },
791
  svg: {
792
+ fontCache: 'global',
793
+ scale: 1, // Global scaling factor for all expressions
794
+ minScale: 1, // Minimum scaling factor
795
+ mtextInheritFont: true, // True to make mtext elements use surrounding font
796
+ merrorInheritFont: true, // True to make merror text use surrounding font
797
+ mathmlSpacing: false // True for MathML spacing rules, false for TeX rules
798
+ },
799
+ chtml: {
800
+ scale: 1,
801
+ minScale: 1,
802
+ mtextInheritFont: true,
803
+ merrorInheritFont: true
804
  }
805
  };
806
  </script>
 
825
  const formData = new FormData(document.getElementById('imageForm'));
826
  const solutionOutput = document.getElementById('solutionOutput');
827
  const loadingIndicator = document.getElementById('loadingIndicator');
828
+ const solutionContainer = document.getElementById('solution');
829
 
830
  solutionOutput.style.display = 'block';
831
  loadingIndicator.style.display = 'flex';
832
+ solutionContainer.innerHTML = '';
833
 
834
+ fetch('/solved', {
835
  method: 'POST',
836
  body: formData
837
  })
 
846
  function processStream({ done, value }) {
847
  if (done) {
848
  loadingIndicator.style.display = 'none';
849
+ // Force MathJax to reprocess entire solution when done
850
+ if (typeof MathJax !== 'undefined') {
851
+ MathJax.typesetPromise([solutionContainer]).catch(e => console.error('MathJax final typesetting error:', e));
852
+ }
853
  return;
854
  }
855
 
856
  buffer += decoder.decode(value, { stream: true });
 
857
  const lines = buffer.split(/\r?\n\r?\n/);
858
+ buffer = lines.pop();
859
 
860
  for (const line of lines) {
861
  if (line.startsWith('data: ')) {
 
863
  const data = JSON.parse(line.substr(6));
864
 
865
  if (data.mode) {
866
+ const modes = {
867
+ thinking: { icon: 'fa-brain', text: 'Je réfléchis...', class: 'thinking-indicator' },
868
+ answering: { icon: 'fa-pencil-alt', text: 'Rédaction...', class: 'answering-indicator' },
869
+ executing_code: { icon: 'fa-code', text: 'Exécution...', class: 'executing-indicator' },
870
+ code_result: { icon: 'fa-terminal', text: 'Résultats...', class: 'executing-indicator' }
871
+ };
872
+ const modeInfo = modes[data.mode];
873
+ if (modeInfo) {
874
+ loadingIndicator.className = modeInfo.class;
875
+ loadingIndicator.innerHTML = `<i class="fas ${modeInfo.icon} indicator-icon"></i><span>${modeInfo.text}</span>`;
876
+ }
 
 
 
 
 
 
877
  }
878
 
879
  if (data.content) {
880
  const content = data.content;
 
881
  const tempDiv = document.createElement('div');
882
+ tempDiv.innerHTML = content;
883
 
 
884
  const codeSection = tempDiv.querySelector('.code-section');
885
  const outputSection = tempDiv.querySelector('.output-section');
886
 
887
  if (codeSection) {
 
888
  solutionContainer.appendChild(codeSection);
 
889
  codeSection.querySelectorAll('pre code').forEach((block) => {
890
  hljs.highlightElement(block);
891
  });
892
  } else if (outputSection) {
893
+ solutionContainer.appendChild(outputSection);
894
+ } else {
895
+ // Process regular text/LaTeX step section
 
 
896
  const stepDiv = document.createElement('div');
897
  stepDiv.className = 'step-section';
898
+
899
+ // Fix markdown content before adding
900
+ let processedContent = content;
901
+
902
+ // Ensure proper paragraph breaks
903
+ processedContent = processedContent.replace(/\n\n/g, '</p><p>');
904
+
905
+ // Wrap in paragraph tags if not already
906
+ if (!processedContent.startsWith('<p>')) {
907
+ processedContent = '<p>' + processedContent;
908
+ }
909
+ if (!processedContent.endsWith('</p>')) {
910
+ processedContent = processedContent + '</p>';
911
+ }
912
+
913
+ // Fix lists in markdown
914
+ processedContent = processedContent.replace(/<p>(\s*[-*]\s+.*?)<\/p>/g, '<ul><li>$1</li></ul>');
915
+ processedContent = processedContent.replace(/<p>(\s*\d+\.\s+.*?)<\/p>/g, '<ol><li>$1</li></ol>');
916
+
917
+ stepDiv.innerHTML = processedContent;
918
  solutionContainer.appendChild(stepDiv);
919
+
920
+ // Process MathJax for this section
921
+ if (typeof MathJax !== 'undefined') {
922
+ MathJax.typesetPromise([stepDiv]).catch(e => console.error('MathJax typesetting error:', e));
923
+ }
924
  }
925
  }
926
 
 
927
  if (data.error) {
928
  const errorDiv = document.createElement('div');
929
+ errorDiv.className = 'step-section';
930
+ errorDiv.style.color = 'red';
931
+ errorDiv.style.backgroundColor = '#ffeeee';
932
+ errorDiv.style.borderColor = 'red';
933
+ errorDiv.textContent = `Erreur: ${data.error}`;
934
  solutionContainer.appendChild(errorDiv);
935
  loadingIndicator.style.display = 'none';
936
  }
937
  } catch (e) {
938
  console.error('Error parsing JSON or processing chunk:', e, line);
 
939
  const errorDiv = document.createElement('div');
940
+ errorDiv.className = 'step-section';
941
+ errorDiv.style.color = 'orange';
942
  errorDiv.textContent = `Erreur traitement chunk: ${line.substring(0, 100)}...`;
943
  solutionContainer.appendChild(errorDiv);
944
  }
 
953
  })
954
  .catch(error => {
955
  const errorDiv = document.createElement('div');
956
+ errorDiv.className = 'step-section';
957
  errorDiv.style.color = 'red';
958
  errorDiv.style.backgroundColor = '#ffeeee';
959
  errorDiv.style.borderColor = 'red';
960
  errorDiv.textContent = `Erreur de connexion ou du serveur: ${error}`;
961
+ solutionContainer.appendChild(errorDiv);
962
  loadingIndicator.style.display = 'none';
963
  console.error('Fetch error:', error);
964
  });
965
  });
966
+
967
+ // Helper function to ensure proper markdown formatting
968
+ function processMarkdown(content) {
969
+ // Process lists
970
+ content = content.replace(/^(\s*[-*]\s+.*?)$/gm, '<li>$1</li>');
971
+ content = content.replace(/^(\s*\d+\.\s+.*?)$/gm, '<li>$1</li>');
972
+
973
+ // Wrap adjacent list items in proper containers
974
+ content = content.replace(/<li>.*?<\/li>(\s*<li>.*?<\/li>)+/g, match => {
975
+ if (match.includes('*') || match.includes('-')) {
976
+ return '<ul>' + match + '</ul>';
977
+ } else {
978
+ return '<ol>' + match + '</ol>';
979
+ }
980
+ });
981
+
982
+ return content;
983
+ }
984
  </script>
985
  </body>
986
  </html>