Docfile commited on
Commit
3557cac
·
verified ·
1 Parent(s): 1950996

Update templates/index.html

Browse files
Files changed (1) hide show
  1. templates/index.html +242 -543
templates/index.html CHANGED
@@ -6,11 +6,12 @@
6
  <title>Math Solver - Version Gratuite</title>
7
  <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css" rel="stylesheet">
8
  <link href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/styles/atom-one-dark.min.css" rel="stylesheet">
9
- <!-- Ajout de Marked.js pour le traitement Markdown -->
10
- <script src="https://cdnjs.cloudflare.com/ajax/libs/marked/4.3.0/marked.min.js"></script>
11
  <!-- CSS pour KaTeX -->
12
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.16.8/katex.min.css">
13
  <style>
 
14
  :root {
15
  --primary-color: #4a6fa5;
16
  --secondary-color: #166088;
@@ -62,20 +63,24 @@
62
  box-shadow: var(--box-shadow);
63
  padding: 30px;
64
  margin-bottom: 30px;
 
65
  }
66
 
67
- .content-box > h1, .content-box > p:first-of-type { /* Cibler le premier paragraphe après h1 */
 
68
  text-align: center;
69
  }
70
- .content-box .feature-list h2 {
71
- text-align: center;
72
- }
73
- .content-box .upload-section {
 
 
74
  text-align: center;
75
  }
76
- .content-box .upload-section h2 {
77
- color: var(--primary-color);
78
- margin-bottom: 20px;
79
  }
80
  .content-box .upgrade-section {
81
  text-align: center;
@@ -93,9 +98,6 @@
93
  .feature-list {
94
  list-style-type: none;
95
  padding: 0;
96
- margin: 30px auto; /* Centrer la liste elle-même */
97
- max-width: 600px; /* Limiter la largeur pour meilleure lisibilité */
98
- text-align: left;
99
  }
100
 
101
  .feature-list li {
@@ -128,7 +130,6 @@
128
  margin: 10px; /* Ajustement marge */
129
  border: none;
130
  cursor: pointer;
131
- font-size: 1rem; /* Taille de police cohérente */
132
  }
133
 
134
  .cta-button:hover {
@@ -169,11 +170,12 @@
169
  }
170
 
171
  /* Style de base pour tous les blocs dans #solution */
172
- #solution > div, #solution > section { /* Accepter div ou section */
 
173
  padding: 20px;
174
  margin: 0;
175
  border-radius: 0; /* Sera ajusté pour first/last child */
176
- overflow-x: auto;
177
  background-color: #f9f9f9; /* Fond par défaut */
178
  border-bottom: 1px solid #eee; /* Séparateur par défaut */
179
  /* Ajout pour débordement horizontal */
@@ -181,7 +183,6 @@
181
  overflow-wrap: break-word; /* Standard CSS */
182
  }
183
 
184
- /* --- Sections spécifiques --- */
185
  .code-section {
186
  background-color: transparent !important; /* Le fond est géré par header/content */
187
  padding: 0 !important;
@@ -192,71 +193,51 @@
192
  background-color: var(--output-bg) !important;
193
  border-left: 4px solid var(--secondary-color); /* Distinction visuelle */
194
  padding-left: calc(20px - 4px);
195
- font-family: 'Courier New', Courier, monospace;
196
- font-size: 0.95em;
197
- white-space: pre-wrap; /* Respecter les retours à la ligne et espaces de la sortie */
198
- word-break: break-all; /* Casser les longues lignes sans espaces */
199
  }
200
 
201
  .step-section {
202
  background-color: #ffffff; /* Fond blanc pour les étapes normales */
203
  border-left: 4px solid var(--primary-color);
204
  padding-left: calc(20px - 4px); /* Compenser la bordure */
205
- font-size: 1rem; /* Taille de base */
206
  line-height: 1.8;
207
  }
208
- .error-message { /* Style pour les messages d'erreur injectés */
209
- color: #a94442; /* Rouge foncé */
210
- background-color: #f2dede; /* Rose pâle */
211
- border-color: #ebccd1 !important; /* Bordure rouge plus pâle */
212
- border-left-width: 4px !important;
213
- border-style: solid;
214
- }
215
- .error-message strong {
216
- color: #a94442;
217
- }
218
- .error-message pre { /* Si l'erreur contient du code brut */
219
- background-color: #eacccc;
220
- color: #843534;
221
- border-color: #d9a9a9;
222
- }
223
-
224
 
225
  /* --- Amélioration du rendu KaTeX --- */
226
  .step-section .katex {
227
  font-size: 1.1em; /* Légèrement plus grand que le texte normal */
228
  line-height: normal; /* Laisser KaTeX gérer */
229
  text-indent: 0;
230
- text-rendering: auto;
231
- /* Eviter le saut de ligne au milieu d'une formule inline si possible */
232
- white-space: nowrap;
 
233
  }
234
 
235
  .step-section .katex-display {
236
  display: block; /* Comportement block pour $$...$$ */
237
  text-align: center; /* Centrer les équations display */
238
- margin: 1.2em 0; /* Espacement vertical un peu plus grand */
239
  overflow-x: auto; /* Permet le scroll horizontal si équation trop large */
240
  overflow-y: hidden;
241
- padding: 0.5em 0.2em; /* Un peu d'espace interne, moins sur les côtés */
242
- /* background-color: #fdfdfd; */ /* Optionnel : léger fond différent */
243
- /* border: 1px solid #eee; */ /* Optionnel : bordure légère */
244
- /* border-radius: 4px; */ /* Optionnel : coins arrondis */
245
  }
246
  /* Conteneur interne créé par KaTeX */
247
  .step-section .katex-display > .katex {
248
  display: inline-block; /* Permet le centrage et évite la pleine largeur */
249
  text-align: initial; /* Texte de l'équation aligné à gauche par défaut */
250
  max-width: 100%;
 
251
  }
252
  /* --- Fin Amélioration KaTeX --- */
253
 
254
- /* --- Sections de Code Dédiées (Python) --- */
255
  .code-header {
256
  background-color: #343a40;
257
  color: white;
258
  padding: 10px 15px;
259
- font-size: 0.9em; /* Plus petit */
260
  font-family: 'Courier New', monospace;
261
  display: flex;
262
  justify-content: space-between;
@@ -270,132 +251,30 @@
270
  color: #e6e6e6;
271
  overflow-x: auto;
272
  font-family: 'Courier New', monospace;
273
- font-size: 0.9em; /* Plus petit */
274
  line-height: 1.5;
275
  border-bottom: 1px solid #444; /* Bordure sous le code */
276
  }
277
- .code-content pre { /* S'assurer que pre dans le bloc code est stylé correctement */
278
- margin: 0;
279
- padding: 0;
280
- background-color: transparent;
281
- border: none;
282
- border-radius: 0;
283
- }
284
- .code-content pre code {
285
- font-size: 1em; /* Hérite de .code-content */
286
- line-height: inherit;
287
- background-color: transparent;
288
- color: inherit;
289
- padding: 0;
290
- }
291
- /* --- Fin Sections de Code Dédiées --- */
292
 
293
- /* --- Règles pour les coins arrondis et bordures --- */
294
- #solution > *:first-child,
295
- #solution > .code-section:first-child .code-header {
296
  border-top-left-radius: 8px;
297
  border-top-right-radius: 8px;
298
  }
299
 
300
- #solution > *:last-child,
301
- #solution > .code-section:last-child .code-content {
302
  border-bottom-left-radius: 8px;
303
  border-bottom-right-radius: 8px;
304
  border-bottom: none; /* Pas de bordure en bas du dernier élément */
305
  }
306
 
307
- #solution > *:only-child {
308
  border-radius: 8px;
309
  }
310
- /* Supprimer la bordure du bas si le suivant est une section de code */
311
- #solution > *:not(:last-child):not(.code-section) {
312
- border-bottom: 1px solid #eee;
313
- }
314
- #solution > .code-section + * { /* Si un élément suit une section code */
315
- border-top: 1px solid #eee; /* Ajouter une ligne de séparation au dessus */
316
- }
317
- #solution > .code-section .code-content {
318
- border-bottom: none; /* Pas de double bordure */
319
- }
320
-
321
-
322
- /* --- Amélioration du rendu Markdown --- */
323
- .step-section *:first-child { margin-top: 0; } /* Éviter marge sup en début de bloc */
324
- .step-section *:last-child { margin-bottom: 0; } /* Éviter marge inf en fin de bloc */
325
-
326
- .step-section p { margin-bottom: 1em; } /* Espacement standard paragraphe */
327
-
328
- .step-section ul, .step-section ol {
329
- margin-block-start: 1em;
330
- margin-block-end: 1em;
331
- padding-inline-start: 30px; /* Indentation standard */
332
- }
333
- .step-section ul li, .step-section ol li {
334
- margin-bottom: 0.5em;
335
- }
336
- .step-section ul ul,
337
- .step-section ol ol,
338
- .step-section ul ol,
339
- .step-section ol ul {
340
- margin-block-start: 0.5em;
341
- margin-block-end: 0.5em;
342
- }
343
 
344
- /* Amélioration des tables en Markdown */
345
- .step-section .table-wrapper { /* Conteneur pour le défilement horizontal */
346
- overflow-x: auto;
347
- margin: 1em 0;
348
- }
349
- .step-section table {
350
- border-collapse: collapse;
351
- width: 100%; /* Prend la largeur du wrapper */
352
- min-width: 400px; /* Largeur minimale pour éviter écrasement */
353
- border: 1px solid #ccc; /* Bordure extérieure */
354
- }
355
- .step-section th,
356
- .step-section td {
357
- border: 1px solid #ddd;
358
- padding: 8px 12px;
359
- text-align: left;
360
- }
361
- .step-section th {
362
- background-color: #f2f2f2;
363
- font-weight: bold;
364
- }
365
- .step-section tr:nth-child(even) {
366
- background-color: #f9f9f9;
367
- }
368
-
369
- /* Rendu des blocs de code en Markdown (pas Python dédié) */
370
- .step-section pre {
371
- background-color: #f5f5f5;
372
- border-radius: 4px;
373
- padding: 12px;
374
- overflow-x: auto;
375
- margin: 1em 0;
376
- border: 1px solid #ddd;
377
- line-height: 1.45; /* Consistant avec hljs */
378
- }
379
- .step-section pre code { /* Style pour le code dans pre */
380
- background-color: transparent;
381
- padding: 0;
382
- border-radius: 0;
383
- font-size: 0.95em; /* Un peu plus petit */
384
- color: #333; /* Couleur de base du code */
385
- }
386
- /* Style pour `code` inline */
387
- .step-section code:not(pre *) {
388
- font-family: Consolas, 'Courier New', Courier, monospace; /* Police plus adaptée */
389
- background-color: #f0f0f0; /* Gris clair */
390
- padding: 3px 6px; /* Un peu plus d'espace */
391
- border-radius: 3px;
392
- font-size: 0.9em;
393
- color: #c7254e; /* Couleur style "rouge" pour code inline */
394
- border: 1px solid #e8e8e8;
395
- word-break: break-word; /* Casser si trop long */
396
- }
397
-
398
- /* --- Indicateurs de statut --- */
399
  .thinking-indicator, .executing-indicator, .answering-indicator {
400
  display: flex;
401
  align-items: center;
@@ -407,7 +286,6 @@
407
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
408
  max-width: 400px; /* Limiter la largeur */
409
  }
410
-
411
  .thinking-indicator { background-color: #e3f2fd; color: #1565c0; border: 1px solid #bbdefb; }
412
  .executing-indicator { background-color: #ede7f6; color: #5e35b1; border: 1px solid #d1c4e9; }
413
  .answering-indicator { background-color: #e8f5e9; color: #2e7d32; border: 1px solid #c8e6c9; }
@@ -417,165 +295,114 @@
417
  animation: pulse 1.5s infinite ease-in-out;
418
  font-size: 1.1rem;
419
  }
420
- /* --- Fin Indicateurs --- */
421
-
422
  @keyframes pulse {
423
  0% { opacity: 0.6; transform: scale(1); }
424
  50% { opacity: 1; transform: scale(1.05); }
425
  100% { opacity: 0.6; transform: scale(1); }
426
  }
427
 
428
- /* --- Améliorations supplémentaires pour Markdown --- */
429
- .step-section blockquote {
430
- border-left: 4px solid #ccc; /* Gris standard */
431
- margin: 1.5em 0; /* Plus de marge verticale */
432
- padding: 0.8em 15px; /* Plus de padding */
433
- color: #555;
434
- background-color: #f9f9f9;
435
  }
436
- .step-section blockquote p:last-child {
437
- margin-bottom: 0; /* Éviter double marge dans blockquote */
 
 
 
 
438
  }
439
 
440
- .step-section hr {
441
- border: none;
442
- border-top: 2px solid #eee; /* Ligne plus visible */
443
- margin: 2em 0;
444
- }
445
-
446
- .step-section h1,
447
- .step-section h2,
448
- .step-section h3,
449
- .step-section h4,
450
- .step-section h5,
451
- .step-section h6 {
452
- margin-top: 1.8em; /* Plus d'espace avant les titres */
453
- margin-bottom: 0.8em;
454
- color: var(--primary-color);
455
- font-weight: 600; /* Un peu plus gras */
456
- padding-bottom: 0.3em;
457
- border-bottom: 1px solid #eee; /* Souligner légèrement les titres */
458
- line-height: 1.3; /* Ajuster interligne titre */
459
- }
460
- .step-section h1:first-child, /* Pas de marge sup pour le tout premier titre */
461
- .step-section h2:first-child,
462
- .step-section h3:first-child,
463
- .step-section h4:first-child,
464
- .step-section h5:first-child,
465
- .step-section h6:first-child { margin-top: 0; }
466
-
467
- .step-section h1 { font-size: 1.8em; border-bottom-width: 2px; }
468
- .step-section h2 { font-size: 1.6em; }
469
- .step-section h3 { font-size: 1.4em; }
470
- .step-section h4 { font-size: 1.2em; }
471
- .step-section h5 { font-size: 1.1em; color: var(--secondary-color); } /* Couleur différente pour H5/H6 */
472
- .step-section h6 { font-size: 1.0em; color: #666; font-style: italic;}
473
 
474
  </style>
475
  </head>
476
  <body>
477
  <div class="container">
478
  <header>
479
- <div class="logo">Math Solver</div>
480
- <div class="subtitle">La solution intelligente pour vos problèmes mathématiques</div>
481
  </header>
482
 
483
  <div class="content-box">
484
- <h1>Version Gratuite</h1>
485
- <p>Vous utilisez actuellement la version gratuite de Math Solver qui vous permet de résoudre 3 problèmes par jour.</p>
486
-
487
- <div class="feature-list">
488
- <h2>Fonctionnalités disponibles :</h2>
489
- <ul class="feature-list">
490
- <li><i class="fas fa-check-circle"></i> Résolution de problèmes mathématiques</li>
491
- <li><i class="fas fa-check-circle"></i> 3 résolutions gratuites par jour</li>
492
- <li><i class="fas fa-check-circle"></i> Explication détaillée des étapes</li>
493
- <li><i class="fas fa-times-circle"></i> <span style="color: #999;">Mode d'exécution de code avancé (version Pro)</span></li>
494
- <li><i class="fas fa-times-circle"></i> <span style="color: #999;">Résolutions illimitées (version Pro)</span></li>
495
- <li><i class="fas fa-times-circle"></i> <span style="color: #999;">Support prioritaire (version Pro)</span></li>
496
- </ul>
497
- </div>
498
-
499
- <div class="upload-section">
 
500
  <h2>Soumettez votre problème</h2>
501
- <form id="imageForm" enctype="multipart/form-data">
502
- <input type="file" id="imageInput" name="image" accept="image/*" style="display: none;">
503
- <button type="button" id="uploadButton" class="cta-button" onclick="document.getElementById('imageInput').click()">
504
- <i class="fas fa-upload"></i> Télécharger une image
505
- </button>
506
  <p id="uploadStatus" style="margin-top: 10px; font-size: 0.9em; color: #555; min-height: 1.2em;"></p>
507
- </form>
508
 
509
- <div id="imagePreview" style="display: none; margin: 20px auto; max-width: 400px; max-height: 300px; overflow: hidden; border: 1px solid #ddd; border-radius: 8px;">
510
- <img id="preview" style="display: block; width: 100%; height: auto; border-radius: 8px;">
511
- </div>
512
 
513
- <button id="solveButton" class="cta-button" style="display: none; background-color: var(--secondary-color);">
514
- <i class="fas fa-calculator"></i> Résoudre ce problème
515
- </button>
516
- </div>
517
 
518
  <!-- Section pour afficher la solution -->
519
- <div id="solutionOutput"> <!-- Initialement caché via CSS -->
520
- <h3>Solution :</h3>
521
- <div id="loadingIndicator" class="thinking-indicator" style="display: none;">
522
- <!-- Contenu mis à jour par JS -->
523
- </div>
524
- <!-- Container pour les blocs de contenu dynamiques -->
525
- <div id="solution">
526
- <!-- Le contenu (steps, code, output) sera ajouté ici par JS -->
527
- </div>
528
- </div>
529
-
530
- <div class="upgrade-section">
531
- <h2>Besoin de plus de puissance ?</h2>
532
- <p>Passez à la version Pro pour des fonctionnalités avancées et des résolutions illimitées.</p>
533
- <a href="#" class="cta-button">Passer à la version Pro</a>
534
- </div>
535
- </div>
536
-
537
- <footer>
538
- <p>© 2025 Math Solver. Tous droits réservés.</p>
539
- </footer>
540
- </div>
541
-
542
- <!-- JS Libraries -->
543
  <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/highlight.min.js"></script>
544
  <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/languages/python.min.js"></script>
 
 
545
  <script src="https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.16.8/katex.min.js"></script>
546
  <script src="https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.16.8/contrib/auto-render.min.js"></script>
547
- <!-- Marked.js est déjà chargé dans le <head> -->
548
 
549
  <script>
550
- // --- Configuration Marked.js ---
551
- marked.setOptions({
552
- gfm: true,
553
- breaks: false, // Standard GFM: ne pas convertir les simples retours à la ligne en <br>
554
- pedantic: false,
555
- smartLists: true,
556
- smartypants: false, // Désactiver pour éviter interférence avec $
557
- xhtml: true,
558
- highlight: function(code, lang) {
559
- // Utilise highlight.js pour la coloration dans les blocs ```lang ... ```
560
- const language = hljs.getLanguage(lang) ? lang : 'plaintext';
561
- return hljs.highlight(code, { language, ignoreIllegals: true }).value;
562
- },
563
- // Ajouter un wrapper pour les tables pour permettre le scroll horizontal
564
- renderer: (function() {
565
- const renderer = new marked.Renderer();
566
- // Conserver le rendu par défaut pour la plupart des éléments
567
- const defaultTableRenderer = renderer.table;
568
- renderer.table = function(header, body) {
569
- // Appeler le rendu par défaut pour obtenir le HTML de la table
570
- const tableHtml = defaultTableRenderer.call(this, header, body);
571
- // Envelopper la table dans un div pour le style et le défilement
572
- return `<div class="table-wrapper">${tableHtml}</div>`;
573
- };
574
- return renderer;
575
- })()
576
- });
577
-
578
- // --- Configuration KaTeX ---
579
  const katexOptions = {
580
  delimiters: [
581
  {left: '$$', right: '$$', display: true},
@@ -583,341 +410,213 @@
583
  {left: '\\(', right: '\\)', display: false},
584
  {left: '\\[', right: '\\]', display: true}
585
  ],
586
- throwOnError: false, // Ne pas bloquer en cas d'erreur LaTeX mineure
587
- strict: (errorCode) => {
588
- // Gérer ou ignorer certains avertissements KaTeX si nécessaire
 
589
  if (errorCode === 'unicodeTextInMathMode') {
590
- return 'ignore'; // Ou 'warn'
591
  }
592
- // 'warn' affiche l'erreur dans la console sans arrêter le rendu
593
- // 'error' arrêterait le rendu de cette formule
594
- return 'warn';
595
- },
596
- macros: { // Optionnel: Définir des macros LaTeX personnalisées
597
- // "\\RR": "\\mathbb{R}"
598
  },
599
- // trust: (context) => context.command === '\\htmlClass' // Activer si besoin de commandes spécifiques
 
 
 
 
600
  };
601
 
602
- // --- Gestion Upload Image ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
603
  document.getElementById('imageInput').addEventListener('change', function(event) {
604
  const file = event.target.files[0];
605
- const uploadStatus = document.getElementById('uploadStatus');
606
- const imagePreviewDiv = document.getElementById('imagePreview');
607
- const previewImg = document.getElementById('preview');
608
- const solveButton = document.getElementById('solveButton');
609
- const solutionOutputDiv = document.getElementById('solutionOutput');
610
- const solutionContainer = document.getElementById('solution');
611
-
612
  if (file) {
613
  const reader = new FileReader();
614
  reader.onload = function(e) {
615
- previewImg.src = e.target.result;
616
- imagePreviewDiv.style.display = 'block';
617
- solveButton.style.display = 'inline-block';
618
- uploadStatus.textContent = `Image sélectionnée : ${file.name}`;
619
- // Cacher l'ancienne solution et l'indicateur
620
- solutionOutputDiv.style.display = 'none';
621
- solutionContainer.innerHTML = '';
622
- document.getElementById('loadingIndicator').style.display = 'none';
623
  }
624
  reader.readAsDataURL(file);
625
- uploadStatus.textContent = 'Chargement de l\'aperçu...';
626
- } else {
627
- imagePreviewDiv.style.display = 'none';
628
- solveButton.style.display = 'none';
629
- uploadStatus.textContent = '';
630
  }
631
  });
632
 
633
- // --- Gestion Clic Bouton Résoudre ---
634
  document.getElementById('solveButton').addEventListener('click', function() {
635
  const formData = new FormData(document.getElementById('imageForm'));
636
  const solutionOutputDiv = document.getElementById('solutionOutput');
637
  const loadingIndicator = document.getElementById('loadingIndicator');
638
  const solutionContainer = document.getElementById('solution');
639
 
640
- // Vérifier s'il y a une image sélectionnée
641
- if (!document.getElementById('imageInput').files[0]) {
642
- alert("Veuillez d'abord sélectionner une image.");
643
- return;
644
- }
645
-
646
- solutionOutputDiv.style.display = 'block'; // Afficher la section solution
647
- loadingIndicator.innerHTML = `<i class="fas fa-spinner fa-spin indicator-icon"></i><span>Envoi de l'image...</span>`;
648
- loadingIndicator.className = 'thinking-indicator'; // Classe par défaut
649
- loadingIndicator.style.display = 'flex'; // Afficher l'indicateur de chargement
650
- solutionContainer.innerHTML = ''; // Effacer la solution précédente
651
-
652
- // Scroller pour rendre l'indicateur visible
653
  loadingIndicator.scrollIntoView({ behavior: 'smooth', block: 'center' });
654
 
655
- fetch('/solved', { // Endpoint Flask (adapter si nécessaire)
656
  method: 'POST',
657
  body: formData
658
  })
659
  .then(response => {
660
- if (!response.ok || !response.body) {
661
- // Tenter de lire le message d'erreur du corps si disponible
662
- return response.text().then(text => {
663
- let errorMsg = `Erreur serveur: ${response.status} ${response.statusText}`;
664
- if (text) {
665
- // Essayer d'extraire un message d'erreur plus précis si c'est du JSON
666
- try {
667
- const errorJson = JSON.parse(text);
668
- errorMsg += `\n${errorJson.error || errorJson.message || text}`;
669
- } catch (e) {
670
- errorMsg += `\n${text}`; // Afficher le texte brut si ce n'est pas du JSON
671
- }
672
- } else {
673
- errorMsg += "\nAucun détail supplémentaire fourni par le serveur.";
674
- }
675
- throw new Error(errorMsg);
676
- });
677
- }
678
- // Préparation à la lecture du flux SSE
679
  const reader = response.body.getReader();
680
  const decoder = new TextDecoder();
681
  let buffer = '';
682
- let currentMode = 'thinking'; // Suivre l'état actuel
683
-
684
- // Mettre à jour l'indicateur pour le premier état
685
- updateLoadingIndicator('thinking');
686
 
687
  function processStream({ done, value }) {
688
  if (done) {
689
- // Le flux est terminé
690
- if (solutionContainer.innerHTML === '') {
691
- // Si rien n'a été affiché, montrer un message
692
- displayError("Aucune réponse reçue du serveur après la fin du flux.", solutionContainer);
693
- }
694
- loadingIndicator.style.display = 'none'; // Cacher l'indicateur final
695
- // Optionnel : Passe finale de rendu KaTeX pour toute la solution
696
- try {
697
- if (window.renderMathInElement) { // Vérifier si la fonction existe
698
- renderMathInElement(solutionContainer, katexOptions);
699
- }
700
- } catch (e) {
701
- console.error('Erreur de rendu KaTeX final:', e);
702
- }
703
  return;
704
  }
705
 
706
- // Ajouter les nouvelles données au buffer
707
  buffer += decoder.decode(value, { stream: true });
 
 
708
 
709
- // Traiter les messages complets dans le buffer (séparés par \n\n)
710
- let boundary = buffer.indexOf('\n\n');
711
- while (boundary >= 0) {
712
- const message = buffer.substring(0, boundary).trim(); // Prendre un message complet
713
- buffer = buffer.substring(boundary + 2); // Enlever le message traité du buffer
714
-
715
  if (message.startsWith('data: ')) {
716
  try {
717
- const data = JSON.parse(message.substring(6)); // Extraire le JSON
718
 
719
  // --- Gérer les mises à jour de mode ---
720
- if (data.mode && data.mode !== currentMode) {
721
- currentMode = data.mode;
722
- updateLoadingIndicator(currentMode);
723
- loadingIndicator.style.display = 'flex'; // Assurer qu'il est visible
 
 
 
 
 
 
 
 
724
  }
725
 
726
  // --- Gérer les blocs de contenu ---
727
- if (data.content) {
728
- loadingIndicator.style.display = 'none'; // Cacher l'indicateur dès qu'on reçoit du contenu
729
- const rawContent = data.content;
 
 
 
730
 
731
- // Détecter si c'est du code/output dédié ou un step normal
732
- // On se base sur la présence de classes spécifiques envoyées par le serveur
733
  const tempDiv = document.createElement('div');
734
- tempDiv.innerHTML = rawContent; // Parser temporairement pour inspection
 
735
  const codeSection = tempDiv.querySelector('.code-section');
736
  const outputSection = tempDiv.querySelector('.output-section');
737
 
738
- let elementToAppend; // L'élément final à ajouter au DOM
739
-
740
  if (codeSection) {
741
- // --- Section Code Python Dédiée ---
742
- elementToAppend = codeSection;
743
- // Appliquer highlight.js sur les blocs de code DANS cette section
744
- elementToAppend.querySelectorAll('pre code').forEach((block) => {
745
- // Vérifier si hljs est chargé
746
- if (window.hljs) {
747
- hljs.highlightElement(block);
748
- } else {
749
- console.warn("highlight.js (hljs) non trouvé.");
750
- }
751
  });
 
 
752
 
753
  } else if (outputSection) {
754
- // --- Section Output de Code ---
755
- elementToAppend = outputSection;
756
- // Pas de traitement spécial nécessaire ici (styles CSS gèrent l'affichage)
 
 
757
 
758
  } else {
759
- // --- Section d'Étape (Markdown + LaTeX) ---
760
- const stepDiv = document.createElement('div');
761
- stepDiv.className = 'step-section';
762
- elementToAppend = stepDiv;
763
-
764
- try {
765
- let processedMdContent = rawContent;
766
- const latexPlaceholders = {};
767
- let placeholderIndex = 0;
768
- const placeholderPrefix = "___KXTEMP_"; // Préfixe court et unique
769
-
770
- // 1. Protéger $$...$$ (Display Math)
771
- // Regex: trouve $$ suivi de n'importe quoi (non-gourmand) jusqu'à $$
772
- processedMdContent = processedMdContent.replace(/(\$\$[\s\S]*?\$\$)/g, (match) => {
773
- const placeholder = `${placeholderPrefix}D${placeholderIndex++}___`;
774
- latexPlaceholders[placeholder] = match;
775
- return placeholder;
776
- });
777
-
778
- // 2. Protéger $...$ (Inline Math)
779
- // Regex: trouve un $ non précédé par \ ou un autre $, suivi de caractères (non-$), jusqu'au prochain $ non précédé par \.
780
- // Attention: cette regex est simplifiée et peut avoir des edge cases.
781
- processedMdContent = processedMdContent.replace(/(?<![\$\\])\$([^$]+?)\$(?![\$])/g, (match) => {
782
- // (?<![\$\\]) : Ne pas être précédé par $ ou \
783
- // \$ : Le dollar de début
784
- // ([^$]+?) : Contenu (au moins un caractère, non gourmand)
785
- // \$ : Le dollar de fin
786
- // (?![\$]) : Ne pas être suivi par un $
787
- const placeholder = `${placeholderPrefix}I${placeholderIndex++}___`;
788
- latexPlaceholders[placeholder] = match;
789
- return placeholder;
790
- });
791
-
792
-
793
- // 3. Parser le Markdown *avec* les placeholders
794
- let htmlContent = marked.parse(processedMdContent);
795
-
796
- // 4. Restaurer le LaTeX original à partir des placeholders
797
- // Utiliser une fonction pour la robustesse
798
- htmlContent = htmlContent.replace(new RegExp(placeholderPrefix + "(D|I)(\\d+)___", "g"), (match) => {
799
- return latexPlaceholders[match] || match; // Remplace ou laisse le placeholder si non trouvé
800
- });
801
-
802
- // 5. Injecter le HTML final dans le div
803
- stepDiv.innerHTML = htmlContent;
804
-
805
- // 6. Rendre le LaTeX DANS ce div spécifique avec KaTeX (après ajout au DOM)
806
- // Note: KaTeX sera appelé après l'ajout au DOM ci-dessous
807
-
808
- } catch (e) {
809
- console.error('Erreur pendant le traitement Markdown/Placeholder:', e);
810
- // En cas d'erreur majeure, afficher le contenu brut
811
- stepDiv.innerHTML = `<p class="error-message"><strong>Erreur de rendu Markdown :</strong> ${e.message}</p><pre>${rawContent.replace(/</g, "<")}</pre>`;
812
- }
813
  }
 
814
 
815
- // --- Ajouter l'élément préparé au conteneur ---
816
- if (elementToAppend) {
817
- solutionContainer.appendChild(elementToAppend);
818
-
819
- // --- Appeler KaTeX si c'était une step-section ---
820
- if (elementToAppend.classList.contains('step-section')) {
821
- try {
822
- if (window.renderMathInElement) {
823
- renderMathInElement(elementToAppend, katexOptions);
824
- } else {
825
- console.warn("KaTeX auto-render (renderMathInElement) non trouvé.");
826
- }
827
- } catch (renderError) {
828
- console.error('Erreur rendu KaTeX sur le bloc:', renderError, elementToAppend);
829
- // Afficher un message d'erreur dans le bloc lui-même
830
- const errorDiv = document.createElement('div');
831
- errorDiv.className = 'error-message';
832
- errorDiv.innerHTML = `<strong>Erreur Rendu LaTeX:</strong> ${renderError.message || renderError}`;
833
- elementToAppend.appendChild(errorDiv);
834
- }
835
- }
836
-
837
- // Faire défiler pour voir le nouveau contenu
838
- elementToAppend.scrollIntoView({ behavior: 'smooth', block: 'end' });
839
- }
840
- } // Fin if(data.content)
841
 
842
- // --- Gérer les erreurs spécifiques envoyées par le serveur ---
843
  if (data.error) {
844
- displayError(`Erreur reçue du serveur: ${data.error}`, solutionContainer);
845
- loadingIndicator.style.display = 'none'; // Cacher l'indicateur en cas d'erreur fatale
 
 
 
 
 
846
  }
847
 
848
  } catch (e) {
849
  console.error('Erreur analyse JSON ou traitement message SSE:', e, message);
850
- displayError(`Erreur interne lors du traitement de la réponse: ${message.substring(0, 150)}...`, solutionContainer);
 
 
 
 
 
 
851
  loadingIndicator.style.display = 'none';
852
  }
853
- } else if (message.trim() !== '') {
854
- // Ignorer les lignes vides mais logguer les autres lignes non-SSE
855
- console.warn("Message SSE ignoré (ne commence pas par 'data:'):", message);
856
  }
 
 
 
 
857
 
858
- // Chercher la prochaine limite de message
859
- boundary = buffer.indexOf('\n\n');
860
- } // Fin while boundary
861
 
862
  // Continuer à lire le flux
863
- reader.read().then(processStream);
864
- } // Fin processStream
865
 
866
  // Démarrer le traitement du flux
867
  reader.read().then(processStream);
868
-
869
  })
870
  .catch(error => {
871
- // Gérer les erreurs de fetch initial (connexion, DNS, etc.) ou les erreurs levées plus tôt
872
  console.error('Erreur Fetch ou connexion:', error);
873
- displayError(`Erreur de communication avec le serveur: ${error.message || error}`, solutionContainer);
874
- loadingIndicator.style.display = 'none'; // Cacher l'indicateur
 
 
 
 
875
  });
876
  });
877
 
878
- // --- Fonction pour mettre à jour l'indicateur de chargement ---
879
- function updateLoadingIndicator(mode) {
880
- const loadingIndicator = document.getElementById('loadingIndicator');
881
- const modes = {
882
- thinking: { icon: 'fa-brain', text: 'Je réfléchis au problème...', class: 'thinking-indicator' },
883
- answering: { icon: 'fa-pencil-alt', text: 'Je rédige la réponse...', class: 'answering-indicator' },
884
- executing_code: { icon: 'fa-play', text: 'Exécution du code Python...', class: 'executing-indicator' },
885
- code_result: { icon: 'fa-terminal', text: 'Analyse des résultats du code...', class: 'executing-indicator' },
886
- // Ajouter d'autres modes si nécessaire
887
- default: { icon: 'fa-sync-alt fa-spin', text: 'Traitement en cours...', class: 'thinking-indicator' }
888
- };
889
- const modeInfo = modes[mode] || modes.default;
890
-
891
- loadingIndicator.className = modeInfo.class; // Appliquer la classe CSS pour le style
892
- loadingIndicator.innerHTML = `<i class="fas ${modeInfo.icon} indicator-icon"></i><span>${modeInfo.text}</span>`;
893
- }
894
-
895
- // --- Fonction pour afficher les erreurs de manière cohérente ---
896
- function displayError(errorMessage, container) {
897
- const errorDiv = document.createElement('div');
898
- // Utiliser la classe CSS définie pour les erreurs
899
- errorDiv.className = 'step-section error-message';
900
- // Utiliser marked pour permettre un formatage simple dans les erreurs si besoin
901
- try {
902
- errorDiv.innerHTML = `<strong>Erreur :</strong> ${marked.parseInline(errorMessage || 'Une erreur inconnue est survenue.')}`;
903
- } catch (e) { // Fallback si marked échoue
904
- errorDiv.innerHTML = `<strong>Erreur :</strong> ${errorMessage || 'Une erreur inconnue est survenue.'}`;
905
- }
906
- container.appendChild(errorDiv);
907
- errorDiv.scrollIntoView({ behavior: 'smooth', block: 'center' });
908
- }
909
-
910
-
911
- // --- Initialisation au chargement de la page ---
912
  document.addEventListener('DOMContentLoaded', function() {
913
- // Optionnel : Rendre le LaTeX statique si présent dans la page initiale
914
- try {
915
- if (window.renderMathInElement) {
916
- renderMathInElement(document.body, katexOptions);
917
- }
918
- } catch (e) {
919
- console.error('Erreur KaTeX initiale sur le corps de la page:', e)
920
- }
921
  });
922
  </script>
923
  </body>
 
6
  <title>Math Solver - Version Gratuite</title>
7
  <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css" rel="stylesheet">
8
  <link href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/styles/atom-one-dark.min.css" rel="stylesheet">
9
+ <!-- PAS BESOIN de marked.js pour cette approche -->
10
+ <!-- <script src="https://cdnjs.cloudflare.com/ajax/libs/marked/4.3.0/marked.min.js"></script> -->
11
  <!-- CSS pour KaTeX -->
12
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.16.8/katex.min.css">
13
  <style>
14
+ /* --- Styles CSS (inchangés par rapport à la version précédente) --- */
15
  :root {
16
  --primary-color: #4a6fa5;
17
  --secondary-color: #166088;
 
63
  box-shadow: var(--box-shadow);
64
  padding: 30px;
65
  margin-bottom: 30px;
66
+ /* text-align: center; enlevé pour que le contenu dynamique ne soit pas centré par défaut */
67
  }
68
 
69
+ /* Ajustement pour le contenu généré */
70
+ .content-box > h1, .content-box > p:not(#uploadStatus) {
71
  text-align: center;
72
  }
73
+ .content-box .feature-list {
74
+ text-align: left; /* Garder le texte des features aligné à gauche */
75
+ max-width: 600px;
76
+ margin: 30px auto;
77
+ }
78
+ .content-box .feature-list h2 {
79
  text-align: center;
80
  }
81
+ .content-box .upload-section {
82
+ text-align: center;
83
+ margin-top: 30px; /* Ajouter de l'espace avant */
84
  }
85
  .content-box .upgrade-section {
86
  text-align: center;
 
98
  .feature-list {
99
  list-style-type: none;
100
  padding: 0;
 
 
 
101
  }
102
 
103
  .feature-list li {
 
130
  margin: 10px; /* Ajustement marge */
131
  border: none;
132
  cursor: pointer;
 
133
  }
134
 
135
  .cta-button:hover {
 
170
  }
171
 
172
  /* Style de base pour tous les blocs dans #solution */
173
+ /* Utiliser 'section' pour les blocs pour plus de sémantique */
174
+ #solution > section {
175
  padding: 20px;
176
  margin: 0;
177
  border-radius: 0; /* Sera ajusté pour first/last child */
178
+ overflow-x: auto; /* Permet le scroll horizontal si contenu trop large */
179
  background-color: #f9f9f9; /* Fond par défaut */
180
  border-bottom: 1px solid #eee; /* Séparateur par défaut */
181
  /* Ajout pour débordement horizontal */
 
183
  overflow-wrap: break-word; /* Standard CSS */
184
  }
185
 
 
186
  .code-section {
187
  background-color: transparent !important; /* Le fond est géré par header/content */
188
  padding: 0 !important;
 
193
  background-color: var(--output-bg) !important;
194
  border-left: 4px solid var(--secondary-color); /* Distinction visuelle */
195
  padding-left: calc(20px - 4px);
 
 
 
 
196
  }
197
 
198
  .step-section {
199
  background-color: #ffffff; /* Fond blanc pour les étapes normales */
200
  border-left: 4px solid var(--primary-color);
201
  padding-left: calc(20px - 4px); /* Compenser la bordure */
202
+ font-size: 16px;
203
  line-height: 1.8;
204
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
205
 
206
  /* --- Amélioration du rendu KaTeX --- */
207
  .step-section .katex {
208
  font-size: 1.1em; /* Légèrement plus grand que le texte normal */
209
  line-height: normal; /* Laisser KaTeX gérer */
210
  text-indent: 0;
211
+ /* text-rendering: optimizeLegibility; */ /* Peut améliorer mais coûteux */
212
+ color: #333; /* Assurer la couleur du texte */
213
+ /* display: inline-block; */ /* Peut causer des problèmes de wrapping */
214
+ /* white-space: normal; */ /* Assurer le retour à la ligne si nécessaire */
215
  }
216
 
217
  .step-section .katex-display {
218
  display: block; /* Comportement block pour $$...$$ */
219
  text-align: center; /* Centrer les équations display */
220
+ margin: 1em 0; /* Espacement vertical */
221
  overflow-x: auto; /* Permet le scroll horizontal si équation trop large */
222
  overflow-y: hidden;
223
+ padding: 0.5em 0.2em; /* Un peu d'espace interne, même horizontal */
224
+ background-color: #fdfdfd; /* Fond très légèrement différent pour les blocs */
225
+ border-radius: 4px;
 
226
  }
227
  /* Conteneur interne créé par KaTeX */
228
  .step-section .katex-display > .katex {
229
  display: inline-block; /* Permet le centrage et évite la pleine largeur */
230
  text-align: initial; /* Texte de l'équation aligné à gauche par défaut */
231
  max-width: 100%;
232
+ /* white-space: nowrap; */ /* Empêche le retour à la ligne DANS l'équation, utiliser scroll */
233
  }
234
  /* --- Fin Amélioration KaTeX --- */
235
 
 
236
  .code-header {
237
  background-color: #343a40;
238
  color: white;
239
  padding: 10px 15px;
240
+ font-size: 14px;
241
  font-family: 'Courier New', monospace;
242
  display: flex;
243
  justify-content: space-between;
 
251
  color: #e6e6e6;
252
  overflow-x: auto;
253
  font-family: 'Courier New', monospace;
254
+ font-size: 14px;
255
  line-height: 1.5;
256
  border-bottom: 1px solid #444; /* Bordure sous le code */
257
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
258
 
259
+ /* --- Règles pour les coins arrondis (simplifié) --- */
260
+ #solution > section:first-child,
261
+ #solution > section.code-section:first-child .code-header {
262
  border-top-left-radius: 8px;
263
  border-top-right-radius: 8px;
264
  }
265
 
266
+ #solution > section:last-child,
267
+ #solution > section.code-section:last-child .code-content {
268
  border-bottom-left-radius: 8px;
269
  border-bottom-right-radius: 8px;
270
  border-bottom: none; /* Pas de bordure en bas du dernier élément */
271
  }
272
 
273
+ #solution > section:only-child {
274
  border-radius: 8px;
275
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
276
 
277
+ /* --- Indicateurs --- */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
278
  .thinking-indicator, .executing-indicator, .answering-indicator {
279
  display: flex;
280
  align-items: center;
 
286
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
287
  max-width: 400px; /* Limiter la largeur */
288
  }
 
289
  .thinking-indicator { background-color: #e3f2fd; color: #1565c0; border: 1px solid #bbdefb; }
290
  .executing-indicator { background-color: #ede7f6; color: #5e35b1; border: 1px solid #d1c4e9; }
291
  .answering-indicator { background-color: #e8f5e9; color: #2e7d32; border: 1px solid #c8e6c9; }
 
295
  animation: pulse 1.5s infinite ease-in-out;
296
  font-size: 1.1rem;
297
  }
 
 
298
  @keyframes pulse {
299
  0% { opacity: 0.6; transform: scale(1); }
300
  50% { opacity: 1; transform: scale(1.05); }
301
  100% { opacity: 0.6; transform: scale(1); }
302
  }
303
 
304
+ /* --- Classe pour les erreurs --- */
305
+ .error-message {
306
+ background-color: #fff0f0 !important;
307
+ color: red !important;
308
+ border-color: red !important;
309
+ font-weight: bold;
 
310
  }
311
+ .error-message code { /* Code dans les messages d'erreur */
312
+ background-color: #fdd;
313
+ color: #c00;
314
+ padding: 2px 4px;
315
+ border-radius: 3px;
316
+ font-family: monospace;
317
  }
318
 
319
+ /* Styles spécifiques pour le contenu sans Markdown (peut nécessiter ajustement) */
320
+ .step-section p { /* Ajouter marge basique pour les paragraphes si pas de Markdown */
321
+ margin-bottom: 1em;
322
+ }
323
+ .step-section p:last-child {
324
+ margin-bottom: 0;
325
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
326
 
327
  </style>
328
  </head>
329
  <body>
330
  <div class="container">
331
  <header>
332
+ <div class="logo">Math Solver</div>
333
+ <div class="subtitle">La solution intelligente pour vos problèmes mathématiques</div>
334
  </header>
335
 
336
  <div class="content-box">
337
+ <h1>Version Gratuite</h1>
338
+ <p>Vous utilisez actuellement la version gratuite de Math Solver qui vous permet de résoudre 3 problèmes par jour.</p>
339
+
340
+ <div class="feature-list">
341
+ <h2>Fonctionnalités disponibles :</h2>
342
+ <!-- Liste des fonctionnalités -->
343
+ <ul>
344
+ <li><i class="fas fa-check-circle"></i> Résolution de problèmes mathématiques basiques</li>
345
+ <li><i class="fas fa-check-circle"></i> 3 résolutions gratuites par jour</li>
346
+ <li><i class="fas fa-check-circle"></i> Explication des étapes de résolution</li>
347
+ <li><i class="fas fa-times-circle"></i> <span style="color: #999;">Mode d'exécution de code avancé (version Pro)</span></li>
348
+ <li><i class="fas fa-times-circle"></i> <span style="color: #999;">Résolutions illimitées (version Pro)</span></li>
349
+ <li><i class="fas fa-times-circle"></i> <span style="color: #999;">Support prioritaire (version Pro)</span></li>
350
+ </ul>
351
+ </div>
352
+
353
+ <div class="upload-section">
354
  <h2>Soumettez votre problème</h2>
355
+ <form id="imageForm" enctype="multipart/form-data">
356
+ <input type="file" id="imageInput" name="image" accept="image/*" style="display: none;">
357
+ <button type="button" id="uploadButton" class="cta-button" onclick="document.getElementById('imageInput').click()">
358
+ <i class="fas fa-upload"></i> Télécharger une image
359
+ </button>
360
  <p id="uploadStatus" style="margin-top: 10px; font-size: 0.9em; color: #555; min-height: 1.2em;"></p>
361
+ </form>
362
 
363
+ <div id="imagePreview" style="display: none; margin: 20px auto; max-width: 400px; max-height: 300px; overflow: hidden; border: 1px solid #ddd; border-radius: 8px;">
364
+ <img id="preview" style="display: block; width: 100%; height: auto; border-radius: 8px;">
365
+ </div>
366
 
367
+ <button id="solveButton" class="cta-button" style="display: none; background-color: var(--secondary-color);">
368
+ <i class="fas fa-calculator"></i> Résoudre ce problème
369
+ </button>
370
+ </div>
371
 
372
  <!-- Section pour afficher la solution -->
373
+ <div id="solutionOutput"> <!-- Initialement caché via CSS -->
374
+ <h3>Solution :</h3>
375
+ <div id="loadingIndicator" class="thinking-indicator" style="display: none;">
376
+ <i class="fas fa-brain indicator-icon"></i>
377
+ <span>Je réfléchis au problème...</span>
378
+ </div>
379
+ <!-- Container pour les blocs de contenu dynamiques -->
380
+ <div id="solution">
381
+ <!-- Le contenu (steps, code, output) sera ajouté ici par JS -->
382
+ </div>
383
+ </div>
384
+
385
+ <div class="upgrade-section">
386
+ <h2>Besoin de plus de puissance ?</h2>
387
+ <p>Passez à la version Pro pour des fonctionnalités avancées et des résolutions illimitées.</p>
388
+ <a href="#" class="cta-button">Passer à la version Pro</a>
389
+ </div>
390
+ </div>
391
+
392
+ <footer>
393
+ <p>© 2025 Math Solver. Tous droits réservés.</p>
394
+ </footer>
395
+ </div>
396
+
397
  <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/highlight.min.js"></script>
398
  <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/languages/python.min.js"></script>
399
+
400
+ <!-- KaTeX Scripts -->
401
  <script src="https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.16.8/katex.min.js"></script>
402
  <script src="https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.16.8/contrib/auto-render.min.js"></script>
 
403
 
404
  <script>
405
+ // Configuration de KaTeX (options globales)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
406
  const katexOptions = {
407
  delimiters: [
408
  {left: '$$', right: '$$', display: true},
 
410
  {left: '\\(', right: '\\)', display: false},
411
  {left: '\\[', right: '\\]', display: true}
412
  ],
413
+ throwOnError: false, // Ne pas bloquer toute la page en cas d'erreur LaTeX
414
+ strict: (errorCode, errorMsg, token) => {
415
+ // Log plus détaillé des erreurs KaTeX pour le débogage
416
+ // console.warn(`KaTeX ${errorCode}: ${errorMsg}`, token);
417
  if (errorCode === 'unicodeTextInMathMode') {
418
+ return 'ignore'; // Permet certains caractères unicode si besoin
419
  }
420
+ return 'warn'; // Affiche les autres erreurs dans la console sans planter
 
 
 
 
 
421
  },
422
+ // IMPORTANT: Pour autoriser le rendu dans différentes situations
423
+ // trust: true peut être nécessaire si le contenu vient d'une source
424
+ // non sûre et utilise des commandes spécifiques, mais essayons sans d'abord.
425
+ // trust: true,
426
+ output: 'htmlAndMathml' // Améliore l'accessibilité
427
  };
428
 
429
+ // Fonction pour déclencher le rendu KaTeX sur un élément spécifique
430
+ // En utilisant setTimeout pour s'assurer que le DOM est mis à jour
431
+ function triggerKatexRender(element) {
432
+ // Utiliser setTimeout pour décaler légèrement l'exécution
433
+ setTimeout(() => {
434
+ try {
435
+ if (window.renderMathInElement) {
436
+ window.renderMathInElement(element, katexOptions);
437
+ } else {
438
+ console.error("renderMathInElement n'est pas disponible. Vérifiez le chargement de KaTeX auto-render.");
439
+ }
440
+ } catch (e) {
441
+ console.error('Erreur lors du rendu KaTeX sur élément:', e, element);
442
+ }
443
+ }, 0); // 0 ms de délai suffit généralement
444
+ }
445
+
446
+
447
  document.getElementById('imageInput').addEventListener('change', function(event) {
448
  const file = event.target.files[0];
 
 
 
 
 
 
 
449
  if (file) {
450
  const reader = new FileReader();
451
  reader.onload = function(e) {
452
+ document.getElementById('preview').src = e.target.result;
453
+ document.getElementById('imagePreview').style.display = 'block';
454
+ document.getElementById('solveButton').style.display = 'inline-block';
455
+ document.getElementById('uploadStatus').textContent = `Image sélectionnée : ${file.name}`;
456
+ document.getElementById('solutionOutput').style.display = 'none';
457
+ document.getElementById('solution').innerHTML = '';
 
 
458
  }
459
  reader.readAsDataURL(file);
 
 
 
 
 
460
  }
461
  });
462
 
 
463
  document.getElementById('solveButton').addEventListener('click', function() {
464
  const formData = new FormData(document.getElementById('imageForm'));
465
  const solutionOutputDiv = document.getElementById('solutionOutput');
466
  const loadingIndicator = document.getElementById('loadingIndicator');
467
  const solutionContainer = document.getElementById('solution');
468
 
469
+ solutionOutputDiv.style.display = 'block';
470
+ loadingIndicator.style.display = 'flex';
471
+ solutionContainer.innerHTML = '';
 
 
 
 
 
 
 
 
 
 
472
  loadingIndicator.scrollIntoView({ behavior: 'smooth', block: 'center' });
473
 
474
+ fetch('/solved', { // Endpoint Flask
475
  method: 'POST',
476
  body: formData
477
  })
478
  .then(response => {
479
+ if (!response.ok) {
480
+ return response.text().then(text => {
481
+ throw new Error(`Erreur serveur: ${response.status} ${response.statusText}\n${text || '(Aucun détail)'}`);
482
+ });
483
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
484
  const reader = response.body.getReader();
485
  const decoder = new TextDecoder();
486
  let buffer = '';
 
 
 
 
487
 
488
  function processStream({ done, value }) {
489
  if (done) {
490
+ loadingIndicator.style.display = 'none';
491
+ // Passe finale de rendu KaTeX pour être sûr (peut-être redondant mais sûr)
492
+ triggerKatexRender(solutionContainer);
 
 
 
 
 
 
 
 
 
 
 
493
  return;
494
  }
495
 
 
496
  buffer += decoder.decode(value, { stream: true });
497
+ const messages = buffer.split(/\r?\n\r?\n/);
498
+ buffer = messages.pop();
499
 
500
+ messages.forEach(message => {
 
 
 
 
 
501
  if (message.startsWith('data: ')) {
502
  try {
503
+ const data = JSON.parse(message.substring(6));
504
 
505
  // --- Gérer les mises à jour de mode ---
506
+ if (data.mode) {
507
+ const modes = {
508
+ thinking: { icon: 'fa-brain', text: 'Je réfléchis...', class: 'thinking-indicator' },
509
+ answering: { icon: 'fa-pencil-alt', text: 'Je rédige la réponse...', class: 'answering-indicator' },
510
+ executing_code: { icon: 'fa-play', text: 'Exécution du code...', class: 'executing-indicator' },
511
+ code_result: { icon: 'fa-terminal', text: 'Traitement des résultats...', class: 'executing-indicator' }
512
+ };
513
+ const modeInfo = modes[data.mode] || { icon: 'fa-sync-alt fa-spin', text: 'Traitement...', class: 'thinking-indicator' };
514
+
515
+ loadingIndicator.className = `${modeInfo.class}`;
516
+ loadingIndicator.innerHTML = `<i class="fas ${modeInfo.icon} indicator-icon"></i><span>${modeInfo.text}</span>`;
517
+ loadingIndicator.style.display = 'flex';
518
  }
519
 
520
  // --- Gérer les blocs de contenu ---
521
+ if (data.content !== undefined && data.content !== null) { // Vérifier existence et non-nullité
522
+ loadingIndicator.style.display = 'none';
523
+ const content = data.content;
524
+
525
+ // Créer un élément conteneur générique (section)
526
+ const blockElement = document.createElement('section');
527
 
528
+ // Analyser le fragment HTML reçu pour détecter le type de bloc
 
529
  const tempDiv = document.createElement('div');
530
+ tempDiv.innerHTML = content;
531
+
532
  const codeSection = tempDiv.querySelector('.code-section');
533
  const outputSection = tempDiv.querySelector('.output-section');
534
 
 
 
535
  if (codeSection) {
536
+ // C'est une section de code Python dédiée
537
+ // Remplacer le contenu de blockElement par codeSection
538
+ blockElement.outerHTML = codeSection.outerHTML;
539
+ // Il faut récupérer la référence au nouvel élément ajouté
540
+ const addedCodeSection = solutionContainer.appendChild(blockElement.cloneNode(true));
541
+ addedCodeSection.querySelectorAll('pre code').forEach((block) => {
542
+ hljs.highlightElement(block);
 
 
 
543
  });
544
+ // Déclencher KaTeX au cas où il y aurait du LaTeX dans les commentaires du code (?)
545
+ // triggerKatexRender(addedCodeSection); // Probablement pas nécessaire pour le code pur
546
 
547
  } else if (outputSection) {
548
+ // C'est une section de sortie de code
549
+ blockElement.outerHTML = outputSection.outerHTML;
550
+ const addedOutputSection = solutionContainer.appendChild(blockElement.cloneNode(true));
551
+ // Déclencher KaTeX si la sortie peut contenir du LaTeX
552
+ triggerKatexRender(addedOutputSection);
553
 
554
  } else {
555
+ // C'est une section d'étape (texte + LaTeX)
556
+ // **Approche simplifiée : injecter le HTML brut**
557
+ blockElement.className = 'step-section'; // Appliquer la classe pour le style
558
+ blockElement.innerHTML = content; // Injecter directement
559
+ solutionContainer.appendChild(blockElement);
560
+
561
+ // **Déclencher le rendu KaTeX sur ce bloc spécifique**
562
+ triggerKatexRender(blockElement);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
563
  }
564
+ }
565
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
566
 
567
+ // --- Gérer les erreurs spécifiques ---
568
  if (data.error) {
569
+ const errorElement = document.createElement('section');
570
+ errorElement.className = 'step-section error-message'; // Utiliser classe d'erreur
571
+ // Utiliser innerHTML pour interpréter d'éventuelles balises simples (gras, etc.)
572
+ // ou juste textContent si l'erreur est toujours du texte brut.
573
+ errorElement.innerHTML = `<strong>Erreur :</strong> ${data.error || 'Une erreur inconnue est survenue.'}`;
574
+ solutionContainer.appendChild(errorElement);
575
+ loadingIndicator.style.display = 'none';
576
  }
577
 
578
  } catch (e) {
579
  console.error('Erreur analyse JSON ou traitement message SSE:', e, message);
580
+ const errorElement = document.createElement('section');
581
+ errorElement.className = 'step-section error-message';
582
+ errorElement.style.color = 'orange'; // Erreur de parsing client
583
+ errorElement.style.backgroundColor = '#fff8e1';
584
+ errorElement.style.borderColor = 'orange';
585
+ errorElement.textContent = `Erreur de traitement du message reçu: ${message.substring(0, 150)}... (Voir console pour détails)`;
586
+ solutionContainer.appendChild(errorElement);
587
  loadingIndicator.style.display = 'none';
588
  }
 
 
 
589
  }
590
+ });
591
+
592
+ // Défiler vers le bas pour voir le nouveau contenu au fur et à mesure
593
+ solutionContainer.lastChild?.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
594
 
 
 
 
595
 
596
  // Continuer à lire le flux
597
+ return reader.read().then(processStream);
598
+ }
599
 
600
  // Démarrer le traitement du flux
601
  reader.read().then(processStream);
 
602
  })
603
  .catch(error => {
 
604
  console.error('Erreur Fetch ou connexion:', error);
605
+ const errorElement = document.createElement('section');
606
+ errorElement.className = 'step-section error-message';
607
+ errorElement.innerHTML = `<strong>Erreur de connexion ou serveur :</strong> ${error.message || error}`;
608
+ solutionContainer.appendChild(errorElement);
609
+ loadingIndicator.style.display = 'none';
610
+ errorElement.scrollIntoView({ behavior: 'smooth', block: 'center' });
611
  });
612
  });
613
 
614
+ // Premier rendu KaTeX au chargement (pour contenu statique éventuel)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
615
  document.addEventListener('DOMContentLoaded', function() {
616
+ // Inutile de le faire sur tout le body si le contenu est dynamique
617
+ // Mais on peut le laisser au cas où il y aurait du LaTeX statique
618
+ // triggerKatexRender(document.body);
619
+ console.log("DOM chargé, KaTeX auto-render prêt.");
 
 
 
 
620
  });
621
  </script>
622
  </body>