Docfile commited on
Commit
380ae1f
·
verified ·
1 Parent(s): b7acc70

Update templates/index.html

Browse files
Files changed (1) hide show
  1. templates/index.html +252 -247
templates/index.html CHANGED
@@ -62,8 +62,26 @@
62
  box-shadow: var(--box-shadow);
63
  padding: 30px;
64
  margin-bottom: 30px;
 
 
 
 
 
 
 
 
65
  text-align: center;
66
  }
 
 
 
 
 
 
 
 
 
 
67
 
68
  h1 {
69
  color: var(--primary-color);
@@ -73,7 +91,8 @@
73
  .feature-list {
74
  list-style-type: none;
75
  padding: 0;
76
- margin: 30px 0;
 
77
  text-align: left;
78
  }
79
 
@@ -88,6 +107,11 @@
88
  color: var(--accent-color);
89
  margin-right: 10px;
90
  font-size: 1.2rem;
 
 
 
 
 
91
  }
92
 
93
  .cta-button {
@@ -99,7 +123,7 @@
99
  text-decoration: none;
100
  font-weight: bold;
101
  transition: all 0.3s ease;
102
- margin: 20px 10px;
103
  border: none;
104
  cursor: pointer;
105
  }
@@ -110,11 +134,6 @@
110
  box-shadow: 0 6px 12px rgba(0, 0, 0, 0.15);
111
  }
112
 
113
- .upgrade-section {
114
- margin-top: 30px;
115
- padding: 20px;
116
- border-top: 1px solid #ddd;
117
- }
118
 
119
  footer {
120
  text-align: center;
@@ -123,67 +142,86 @@
123
  font-size: 0.9rem;
124
  }
125
 
 
 
 
 
 
 
 
 
 
 
 
126
  #solution {
127
- background: #fff;
128
  padding: 0;
129
  border-radius: 8px;
130
  text-align: left;
131
  line-height: 1.8;
132
  font-size: 16px;
133
- background-color: transparent;
134
  box-shadow: var(--box-shadow);
135
- overflow: hidden;
 
136
  }
137
 
138
- #solution > div {
 
139
  padding: 20px;
140
  margin: 0;
141
- border-radius: 0;
142
  overflow-x: auto;
143
- background-color: #f9f9f9;
144
- border-left: 4px solid var(--primary-color);
145
- border-bottom: 1px solid #eee;
 
 
146
  }
147
 
148
  .code-section {
149
- background-color: transparent !important;
150
  padding: 0 !important;
151
- border-left: none !important;
152
- border-bottom: none !important;
153
  }
154
 
155
  .output-section {
156
  background-color: var(--output-bg) !important;
157
- border-left: none !important;
 
158
  }
159
 
160
  .step-section {
 
161
  border-left: 4px solid var(--primary-color);
162
- padding-left: calc(20px - 4px);
163
- background-color: #f9f9f9;
164
  font-size: 16px;
165
  line-height: 1.8;
166
  }
167
 
168
- /* Amélioration du rendu LaTeX avec KaTeX */
169
  .step-section .katex {
170
- font-size: 1.1em;
171
- line-height: 1.2;
172
  text-indent: 0;
173
  text-rendering: auto;
 
174
  }
175
 
176
  .step-section .katex-display {
177
- margin: 1em 0;
178
- display: block;
179
- text-align: center;
180
- }
181
-
 
 
 
182
  .step-section .katex-display > .katex {
183
- display: inline-block;
184
- text-align: center;
185
- max-width: 100%;
186
  }
 
187
 
188
  .code-header {
189
  background-color: #343a40;
@@ -194,8 +232,6 @@
194
  display: flex;
195
  justify-content: space-between;
196
  align-items: center;
197
- border-top-left-radius: 0;
198
- border-top-right-radius: 0;
199
  }
200
 
201
  .code-content {
@@ -207,94 +243,41 @@
207
  font-family: 'Courier New', monospace;
208
  font-size: 14px;
209
  line-height: 1.5;
210
- border-bottom-left-radius: 0;
211
- border-bottom-right-radius: 0;
212
- }
213
-
214
- /* Règles pour les coins arrondis */
215
- #solution > div:first-child {
216
- border-top-left-radius: 8px;
217
- border-top-right-radius: 8px;
218
  }
219
 
 
 
220
  #solution > .code-section:first-child .code-header {
221
  border-top-left-radius: 8px;
222
  border-top-right-radius: 8px;
223
  }
224
 
225
- #solution > .output-section:first-child {
226
- border-top-left-radius: 8px;
227
- border-top-right-radius: 8px;
228
- }
229
-
230
- #solution > div:last-child {
231
- border-bottom-left-radius: 8px;
232
- border-bottom-right-radius: 8px;
233
- border-bottom: none;
234
- }
235
-
236
  #solution > .code-section:last-child .code-content {
237
  border-bottom-left-radius: 8px;
238
  border-bottom-right-radius: 8px;
 
239
  }
240
 
241
- #solution > .output-section:last-child {
242
- border-bottom-left-radius: 8px;
243
- border-bottom-right-radius: 8px;
244
- border-bottom: none;
245
- }
246
-
247
- #solution > div:only-child {
248
- border-radius: 8px;
249
- border-bottom: none;
250
- }
251
-
252
- #solution > .code-section:only-child .code-header {
253
- border-top-left-radius: 8px;
254
- border-top-right-radius: 8px;
255
- }
256
-
257
- #solution > .code-section:only-child .code-content {
258
- border-bottom-left-radius: 8px;
259
- border-bottom-right-radius: 8px;
260
- }
261
-
262
- #solution > .output-section:only-child {
263
  border-radius: 8px;
264
- border-bottom: none;
265
- }
266
-
267
- #solution > div:not(:last-child) {
268
- border-bottom: 1px solid #eee;
269
- }
270
-
271
- #solution > .code-section {
272
- border-bottom: none;
273
- }
274
-
275
- #solution > .output-section:not(:last-child) {
276
- border-bottom: 1px solid #ddd;
277
  }
278
 
279
- /* Amélioration du rendu Markdown */
280
  .step-section ul, .step-section ol {
281
- display: block;
282
  margin-block-start: 1em;
283
  margin-block-end: 1em;
284
- padding-inline-start: 30px;
285
- margin-inline-start: 0px;
286
- margin-inline-end: 0px;
287
  }
288
 
289
  .step-section ul li, .step-section ol li {
290
- display: list-item;
291
  margin-bottom: 0.5em;
292
  }
293
-
294
  /* Meilleur espacement pour les listes imbriquées */
295
- .step-section ul ul,
296
- .step-section ol ol,
297
- .step-section ul ol,
298
  .step-section ol ul {
299
  margin-block-start: 0.5em;
300
  margin-block-end: 0.5em;
@@ -303,56 +286,65 @@
303
  /* Amélioration des tables en Markdown */
304
  .step-section table {
305
  border-collapse: collapse;
306
- width: 100%;
307
- margin: 1em 0;
 
 
308
  }
309
-
310
- .step-section th,
311
  .step-section td {
312
  border: 1px solid #ddd;
313
  padding: 8px 12px;
314
  text-align: left;
315
  }
316
-
317
  .step-section th {
318
  background-color: #f2f2f2;
319
  font-weight: bold;
320
  }
321
-
322
  .step-section tr:nth-child(even) {
323
  background-color: #f9f9f9;
324
  }
325
 
326
- /* Meilleur rendu des blocs de code en Markdown */
327
  .step-section pre {
328
- background-color: #f5f5f5;
329
  border-radius: 4px;
330
  padding: 12px;
331
  overflow-x: auto;
332
  margin: 1em 0;
 
333
  }
334
-
335
- .step-section code {
 
 
 
 
 
 
336
  font-family: 'Courier New', Courier, monospace;
337
- background-color: #f5f5f5;
338
- padding: 2px 4px;
339
  border-radius: 3px;
340
  font-size: 0.9em;
 
341
  }
342
 
343
  .thinking-indicator, .executing-indicator, .answering-indicator {
344
  display: flex;
345
  align-items: center;
 
346
  padding: 12px 15px;
347
- margin: 10px 0;
348
  border-radius: 8px;
349
  font-size: 0.95rem;
350
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
 
351
  }
352
 
353
- .thinking-indicator { background-color: #e3f2fd; color: #1565c0; }
354
- .executing-indicator { background-color: #ede7f6; color: #5e35b1; }
355
- .answering-indicator { background-color: #e8f5e9; color: #2e7d32; }
356
 
357
  .indicator-icon {
358
  margin-right: 12px;
@@ -361,36 +353,49 @@
361
  }
362
 
363
  @keyframes pulse {
364
- 0% { opacity: 0.6; }
365
- 50% { opacity: 1; }
366
- 100% { opacity: 0.6; }
367
  }
368
 
369
  /* Améliorations supplémentaires pour Markdown */
370
  .step-section blockquote {
371
- border-left: 4px solid #ddd;
372
  margin: 1em 0;
373
- padding: 0.5em 10px;
374
- color: #666;
375
- font-style: italic;
376
  background-color: #f9f9f9;
377
  }
 
 
 
378
 
379
- .step-section h1,
380
- .step-section h2,
381
- .step-section h3,
382
- .step-section h4,
383
- .step-section h5,
 
 
 
 
 
 
384
  .step-section h6 {
385
  margin-top: 1.5em;
386
- margin-bottom: 0.5em;
387
  color: var(--primary-color);
 
 
 
388
  }
389
-
390
- .step-section h1 { font-size: 1.8em; }
391
  .step-section h2 { font-size: 1.6em; }
392
  .step-section h3 { font-size: 1.4em; }
393
  .step-section h4 { font-size: 1.2em; }
 
 
 
394
  </style>
395
  </head>
396
  <body>
@@ -417,16 +422,17 @@
417
  </div>
418
 
419
  <div class="upload-section">
 
420
  <form id="imageForm" enctype="multipart/form-data">
421
  <input type="file" id="imageInput" name="image" accept="image/*" style="display: none;">
422
  <button type="button" id="uploadButton" class="cta-button" onclick="document.getElementById('imageInput').click()">
423
  <i class="fas fa-upload"></i> Télécharger une image
424
  </button>
 
425
  </form>
426
 
427
- <p id="uploadStatus"></p>
428
- <div id="imagePreview" style="display: none; margin: 20px auto; max-width: 500px;">
429
- <img id="preview" style="width: 100%; border-radius: 8px; box-shadow: var(--box-shadow);">
430
  </div>
431
 
432
  <button id="solveButton" class="cta-button" style="display: none; background-color: var(--secondary-color);">
@@ -434,15 +440,16 @@
434
  </button>
435
  </div>
436
 
437
- <div id="solutionOutput" style="margin-top: 30px; text-align: left; display: none;">
 
438
  <h3>Solution :</h3>
439
  <div id="loadingIndicator" class="thinking-indicator" style="display: none;">
440
  <i class="fas fa-brain indicator-icon"></i>
441
  <span>Je réfléchis au problème...</span>
442
  </div>
443
- <!-- Container for the dynamically added content blocks -->
444
  <div id="solution">
445
- <!-- Content will be added here by JS -->
446
  </div>
447
  </div>
448
 
@@ -461,34 +468,44 @@
461
  <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/highlight.min.js"></script>
462
  <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/languages/python.min.js"></script>
463
 
464
- <!-- KaTeX Scripts (remplacement de MathJax pour de meilleures performances) -->
465
  <script src="https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.16.8/katex.min.js"></script>
466
  <script src="https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.16.8/contrib/auto-render.min.js"></script>
467
 
468
  <script>
469
  // Configuration de marked pour le rendu Markdown
470
  marked.setOptions({
471
- gfm: true, // GitHub Flavored Markdown
472
- breaks: true, // Convertit les retours à la ligne en <br>
473
- smartLists: true, // Listes intelligentes
474
- smartypants: true, // Typographie intelligente (guillemets, tirets, etc.)
475
- xhtml: true // XHTML conforme
 
 
 
 
 
 
476
  });
477
 
478
- // Fonction pour rendre le LaTeX avec KaTeX dans un élément
479
- function renderMathInElement(element) {
480
- renderMathInElement(element, {
481
- delimiters: [
482
- {left: '$$', right: '$$', display: true},
483
- {left: '$', right: '$', display: false},
484
- {left: '\\(', right: '\\)', display: false},
485
- {left: '\\[', right: '\\]', display: true}
486
- ],
487
- throwOnError: false,
488
- trust: true,
489
- strict: false
490
- });
491
- }
 
 
 
 
492
 
493
  document.getElementById('imageInput').addEventListener('change', function(event) {
494
  const file = event.target.files[0];
@@ -499,6 +516,9 @@
499
  document.getElementById('imagePreview').style.display = 'block';
500
  document.getElementById('solveButton').style.display = 'inline-block';
501
  document.getElementById('uploadStatus').textContent = `Image sélectionnée : ${file.name}`;
 
 
 
502
  }
503
  reader.readAsDataURL(file);
504
  }
@@ -506,23 +526,24 @@
506
 
507
  document.getElementById('solveButton').addEventListener('click', function() {
508
  const formData = new FormData(document.getElementById('imageForm'));
509
- const solutionOutput = document.getElementById('solutionOutput');
510
  const loadingIndicator = document.getElementById('loadingIndicator');
511
  const solutionContainer = document.getElementById('solution');
512
 
513
- solutionOutput.style.display = 'block';
514
- loadingIndicator.style.display = 'flex';
515
  solutionContainer.innerHTML = ''; // Effacer la solution précédente
 
 
516
 
517
- fetch('/solved', { // En supposant que l'endpoint Flask est '/solved'
518
  method: 'POST',
519
  body: formData
520
  })
521
  .then(response => {
522
  if (!response.ok) {
523
- // Tenter de lire le message d'erreur du corps si disponible
524
  return response.text().then(text => {
525
- throw new Error(`Erreur serveur: ${response.status} ${response.statusText}\n${text}`);
526
  });
527
  }
528
  const reader = response.body.getReader();
@@ -532,161 +553,145 @@
532
  function processStream({ done, value }) {
533
  if (done) {
534
  loadingIndicator.style.display = 'none';
535
- // Forcer le retraitement du LaTeX lorsque terminé pour la solution entière
536
  try {
537
- renderMathInElement(solutionContainer);
538
  } catch (e) {
539
- console.error('Erreur de rendu LaTeX final:', e);
540
  }
541
  return;
542
  }
543
 
544
  buffer += decoder.decode(value, { stream: true });
545
- // Séparer par double saut de ligne pour les messages SSE, mais garder le dernier morceau incomplet
546
- const messages = buffer.split(/\r?\n\r?\n/);
547
- buffer = messages.pop(); // Stocker le dernier morceau potentiellement incomplet
548
 
549
- for (const message of messages) {
550
- // Traiter uniquement les lignes commençant par 'data: ' pour SSE
551
  if (message.startsWith('data: ')) {
552
  try {
553
- const data = JSON.parse(message.substr(6)); // Extraire JSON après 'data: '
554
 
555
- // Gérer les mises à jour de mode
556
  if (data.mode) {
557
  const modes = {
558
  thinking: { icon: 'fa-brain', text: 'Je réfléchis...', class: 'thinking-indicator' },
559
- answering: { icon: 'fa-pencil-alt', text: 'Rédaction...', class: 'answering-indicator' },
560
- executing_code: { icon: 'fa-code', text: 'Exécution...', class: 'executing-indicator' },
561
- code_result: { icon: 'fa-terminal', text: 'Résultats...', class: 'executing-indicator' }
562
  };
563
- const modeInfo = modes[data.mode];
564
- if (modeInfo) {
565
- loadingIndicator.className = modeInfo.class;
566
- loadingIndicator.innerHTML = `<i class="fas ${modeInfo.icon} indicator-icon"></i><span>${modeInfo.text}</span>`;
567
- loadingIndicator.style.display = 'flex';
568
- } else {
569
- loadingIndicator.className = 'thinking-indicator';
570
- loadingIndicator.innerHTML = `<i class="fas fa-sync-alt indicator-icon fa-spin"></i><span>Traitement...</span>`;
571
- loadingIndicator.style.display = 'flex';
572
- }
573
  }
574
 
575
- // Gérer les blocs de contenu
576
  if (data.content) {
 
577
  const content = data.content;
578
  const tempDiv = document.createElement('div');
579
- tempDiv.innerHTML = content; // Analyser le fragment HTML reçu du serveur
580
 
581
  const codeSection = tempDiv.querySelector('.code-section');
582
  const outputSection = tempDiv.querySelector('.output-section');
 
583
 
584
  if (codeSection) {
 
585
  solutionContainer.appendChild(codeSection);
586
- // Surligner le code dans la section ajoutée
587
  codeSection.querySelectorAll('pre code').forEach((block) => {
588
- hljs.highlightElement(block);
589
  });
590
  } else if (outputSection) {
 
591
  solutionContainer.appendChild(outputSection);
592
  } else {
593
- // Traiter la section d'étape de texte/LaTeX normale avec Markdown amélioré
 
594
  const stepDiv = document.createElement('div');
595
- stepDiv.className = 'step-section';
596
-
597
- // Traitement Markdown avancé avec marked.js
598
- // Conversion du contenu en Markdown valide pour les listes, les tableaux, etc.
599
- const markdownContent = content
600
- // Préservation des délimiteurs LaTeX
601
- .replace(/\$\$([\s\S]*?)\$\$/g, (match) => {
602
- return match.replace(/\n/g, '\\n');
603
- })
604
- .replace(/\$([\s\S]*?)\$/g, (match) => {
605
- return match.replace(/\n/g, '\\n');
606
- });
607
-
608
- try {
609
- // Conversion Markdown vers HTML
610
- const htmlContent = marked.parse(markdownContent);
611
-
612
- // Restauration des sauts de ligne dans les formules LaTeX
613
- const fixedHtmlContent = htmlContent
614
- .replace(/\$\$([\s\S]*?)\$\$/g, (match) => {
615
- return match.replace(/\\n/g, '\n');
616
- })
617
- .replace(/\$([\s\S]*?)\$/g, (match) => {
618
- return match.replace(/\\n/g, '\n');
619
- });
620
-
621
- stepDiv.innerHTML = fixedHtmlContent;
622
- } catch (e) {
623
- console.error('Erreur de traitement Markdown:', e);
624
- stepDiv.innerHTML = content; // Fallback en cas d'erreur
625
- }
626
-
627
- solutionContainer.appendChild(stepDiv);
628
-
629
- // Rendu LaTeX avec KaTeX dans cette section spécifique
630
  try {
631
- renderMathInElement(stepDiv);
 
 
 
 
 
 
 
 
 
632
  } catch (e) {
633
- console.error('Erreur de rendu LaTeX:', e);
 
 
 
634
  }
635
  }
636
  }
637
 
638
- // Gérer les messages d'erreur
639
  if (data.error) {
640
  const errorDiv = document.createElement('div');
641
- errorDiv.className = 'step-section';
 
642
  errorDiv.style.color = 'red';
643
- errorDiv.style.backgroundColor = '#ffeeee';
644
  errorDiv.style.borderColor = 'red';
645
- errorDiv.textContent = `Erreur: ${data.error}`;
646
  solutionContainer.appendChild(errorDiv);
647
  loadingIndicator.style.display = 'none';
648
  }
 
649
  } catch (e) {
650
- console.error('Erreur d\'analyse JSON ou de traitement du message:', e, message);
651
  const errorDiv = document.createElement('div');
652
- errorDiv.className = 'step-section';
653
  errorDiv.style.color = 'orange';
654
- errorDiv.textContent = `Erreur traitement message: ${message.substring(0, 100)}...`;
 
 
655
  solutionContainer.appendChild(errorDiv);
656
  loadingIndicator.style.display = 'none';
657
  }
658
  }
659
- }
 
 
 
660
 
661
- // Défiler vers le bas du conteneur de solution à mesure que du nouveau contenu arrive
662
- solutionOutput.scrollTop = solutionOutput.scrollHeight;
663
 
664
  // Continuer à lire le flux
665
  return reader.read().then(processStream);
666
  }
667
 
668
- // Commencer à traiter le flux
669
- return reader.read().then(processStream);
670
  })
671
  .catch(error => {
672
- // Gérer les erreurs pendant le fetch ou de la vérification de réponse initiale
673
  const errorDiv = document.createElement('div');
674
- errorDiv.className = 'step-section';
675
  errorDiv.style.color = 'red';
676
- errorDiv.style.backgroundColor = '#ffeeee';
677
  errorDiv.style.borderColor = 'red';
678
- errorDiv.textContent = `Erreur de connexion ou du serveur: ${error}`;
679
  solutionContainer.appendChild(errorDiv);
680
  loadingIndicator.style.display = 'none';
681
- console.error('Erreur fetch:', error);
682
  });
683
  });
684
 
685
- // Auto-render KaTeX function
686
  document.addEventListener('DOMContentLoaded', function() {
687
- document.querySelectorAll('.step-section').forEach(function(element) {
688
- renderMathInElement(element);
689
- });
 
 
 
690
  });
691
  </script>
692
  </body>
 
62
  box-shadow: var(--box-shadow);
63
  padding: 30px;
64
  margin-bottom: 30px;
65
+ /* text-align: center; enlevé pour que le contenu dynamique ne soit pas centré par défaut */
66
+ }
67
+
68
+ /* Ajustement pour le contenu généré */
69
+ .content-box > h1, .content-box > p {
70
+ text-align: center;
71
+ }
72
+ .content-box .feature-list h2 {
73
  text-align: center;
74
  }
75
+ .content-box .upload-section {
76
+ text-align: center;
77
+ }
78
+ .content-box .upgrade-section {
79
+ text-align: center;
80
+ margin-top: 30px;
81
+ padding: 20px;
82
+ border-top: 1px solid #ddd;
83
+ }
84
+
85
 
86
  h1 {
87
  color: var(--primary-color);
 
91
  .feature-list {
92
  list-style-type: none;
93
  padding: 0;
94
+ margin: 30px auto; /* Centrer la liste elle-même */
95
+ max-width: 600px; /* Limiter la largeur pour meilleure lisibilité */
96
  text-align: left;
97
  }
98
 
 
107
  color: var(--accent-color);
108
  margin-right: 10px;
109
  font-size: 1.2rem;
110
+ width: 20px; /* Donner une largeur fixe pour l'alignement */
111
+ text-align: center;
112
+ }
113
+ .feature-list .fa-times-circle {
114
+ color: #aaa; /* Griser l'icône aussi */
115
  }
116
 
117
  .cta-button {
 
123
  text-decoration: none;
124
  font-weight: bold;
125
  transition: all 0.3s ease;
126
+ margin: 10px; /* Ajustement marge */
127
  border: none;
128
  cursor: pointer;
129
  }
 
134
  box-shadow: 0 6px 12px rgba(0, 0, 0, 0.15);
135
  }
136
 
 
 
 
 
 
137
 
138
  footer {
139
  text-align: center;
 
142
  font-size: 0.9rem;
143
  }
144
 
145
+ #solutionOutput {
146
+ margin-top: 30px;
147
+ text-align: left;
148
+ display: none; /* Initialement caché */
149
+ }
150
+ #solutionOutput h3 {
151
+ text-align: center; /* Centrer le titre "Solution :" */
152
+ color: var(--primary-color);
153
+ margin-bottom: 20px;
154
+ }
155
+
156
  #solution {
157
+ background: transparent; /* Le fond sera sur les divs enfants */
158
  padding: 0;
159
  border-radius: 8px;
160
  text-align: left;
161
  line-height: 1.8;
162
  font-size: 16px;
 
163
  box-shadow: var(--box-shadow);
164
+ overflow: hidden; /* Pour que les border-radius s'appliquent aux enfants */
165
+ margin-top: 20px; /* Espace entre indicateur et contenu */
166
  }
167
 
168
+ /* Style de base pour tous les blocs dans #solution */
169
+ #solution > div, #solution > section { /* Accepter div ou section */
170
  padding: 20px;
171
  margin: 0;
172
+ border-radius: 0; /* Sera ajusté pour first/last child */
173
  overflow-x: auto;
174
+ background-color: #f9f9f9; /* Fond par défaut */
175
+ border-bottom: 1px solid #eee; /* Séparateur par défaut */
176
+ /* Ajout pour débordement horizontal */
177
+ word-wrap: break-word; /* Pour casser les mots longs si nécessaire */
178
+ overflow-wrap: break-word; /* Standard CSS */
179
  }
180
 
181
  .code-section {
182
+ background-color: transparent !important; /* Le fond est géré par header/content */
183
  padding: 0 !important;
184
+ border-bottom: none !important; /* Le content aura la bordure */
 
185
  }
186
 
187
  .output-section {
188
  background-color: var(--output-bg) !important;
189
+ border-left: 4px solid var(--secondary-color); /* Distinction visuelle */
190
+ padding-left: calc(20px - 4px);
191
  }
192
 
193
  .step-section {
194
+ background-color: #ffffff; /* Fond blanc pour les étapes normales */
195
  border-left: 4px solid var(--primary-color);
196
+ padding-left: calc(20px - 4px); /* Compenser la bordure */
 
197
  font-size: 16px;
198
  line-height: 1.8;
199
  }
200
 
201
+ /* --- Amélioration du rendu KaTeX --- */
202
  .step-section .katex {
203
+ font-size: 1.1em; /* Légèrement plus grand que le texte normal */
204
+ line-height: normal; /* Laisser KaTeX gérer */
205
  text-indent: 0;
206
  text-rendering: auto;
207
+ /* display: inline-block; */ /* Peut causer des problèmes de wrapping, à tester si besoin */
208
  }
209
 
210
  .step-section .katex-display {
211
+ display: block; /* Comportement block pour $$...$$ */
212
+ text-align: center; /* Centrer les équations display */
213
+ margin: 1em 0; /* Espacement vertical */
214
+ overflow-x: auto; /* Permet le scroll horizontal si équation trop large */
215
+ overflow-y: hidden;
216
+ padding: 0.5em 0; /* Un peu d'espace interne */
217
+ }
218
+ /* Ceci est le conteneur interne créé par KaTeX */
219
  .step-section .katex-display > .katex {
220
+ display: inline-block; /* Permet le centrage et évite la pleine largeur */
221
+ text-align: initial; /* Texte de l'équation aligné à gauche par défaut */
222
+ max-width: 100%;
223
  }
224
+ /* --- Fin Amélioration KaTeX --- */
225
 
226
  .code-header {
227
  background-color: #343a40;
 
232
  display: flex;
233
  justify-content: space-between;
234
  align-items: center;
 
 
235
  }
236
 
237
  .code-content {
 
243
  font-family: 'Courier New', monospace;
244
  font-size: 14px;
245
  line-height: 1.5;
246
+ border-bottom: 1px solid #444; /* Bordure sous le code */
 
 
 
 
 
 
 
247
  }
248
 
249
+ /* --- Règles pour les coins arrondis --- */
250
+ #solution > *:first-child,
251
  #solution > .code-section:first-child .code-header {
252
  border-top-left-radius: 8px;
253
  border-top-right-radius: 8px;
254
  }
255
 
256
+ #solution > *:last-child,
 
 
 
 
 
 
 
 
 
 
257
  #solution > .code-section:last-child .code-content {
258
  border-bottom-left-radius: 8px;
259
  border-bottom-right-radius: 8px;
260
+ border-bottom: none; /* Pas de bordure en bas du dernier élément */
261
  }
262
 
263
+ #solution > *:only-child {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
264
  border-radius: 8px;
 
 
 
 
 
 
 
 
 
 
 
 
 
265
  }
266
 
267
+ /* --- Amélioration du rendu Markdown --- */
268
  .step-section ul, .step-section ol {
 
269
  margin-block-start: 1em;
270
  margin-block-end: 1em;
271
+ padding-inline-start: 30px; /* Indentation standard */
 
 
272
  }
273
 
274
  .step-section ul li, .step-section ol li {
 
275
  margin-bottom: 0.5em;
276
  }
 
277
  /* Meilleur espacement pour les listes imbriquées */
278
+ .step-section ul ul,
279
+ .step-section ol ol,
280
+ .step-section ul ol,
281
  .step-section ol ul {
282
  margin-block-start: 0.5em;
283
  margin-block-end: 0.5em;
 
286
  /* Amélioration des tables en Markdown */
287
  .step-section table {
288
  border-collapse: collapse;
289
+ width: auto; /* Ne prend que la largeur nécessaire */
290
+ max-width: 100%; /* Empêche le débordement */
291
+ margin: 1em auto; /* Centrer les tableaux */
292
+ border: 1px solid #ccc; /* Bordure extérieure */
293
  }
294
+ .step-section th,
 
295
  .step-section td {
296
  border: 1px solid #ddd;
297
  padding: 8px 12px;
298
  text-align: left;
299
  }
 
300
  .step-section th {
301
  background-color: #f2f2f2;
302
  font-weight: bold;
303
  }
 
304
  .step-section tr:nth-child(even) {
305
  background-color: #f9f9f9;
306
  }
307
 
308
+ /* Meilleur rendu des blocs de code en Markdown (pas Python) */
309
  .step-section pre {
310
+ background-color: #f0f0f0; /* Légèrement différent du fond Python */
311
  border-radius: 4px;
312
  padding: 12px;
313
  overflow-x: auto;
314
  margin: 1em 0;
315
+ border: 1px solid #ddd;
316
  }
317
+ .step-section pre code { /* Style pour le code dans pre */
318
+ background-color: transparent; /* Le fond est sur pre */
319
+ padding: 0;
320
+ border-radius: 0;
321
+ font-size: 0.95em; /* Un peu plus petit */
322
+ }
323
+ /* Style pour `code` inline */
324
+ .step-section code:not(pre *) {
325
  font-family: 'Courier New', Courier, monospace;
326
+ background-color: #e8e8e8;
327
+ padding: 2px 5px;
328
  border-radius: 3px;
329
  font-size: 0.9em;
330
+ color: #c7254e; /* Couleur pour code inline */
331
  }
332
 
333
  .thinking-indicator, .executing-indicator, .answering-indicator {
334
  display: flex;
335
  align-items: center;
336
+ justify-content: center; /* Centrer le contenu */
337
  padding: 12px 15px;
338
+ margin: 0 auto 20px auto; /* Centrer l'indicateur lui-même */
339
  border-radius: 8px;
340
  font-size: 0.95rem;
341
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
342
+ max-width: 400px; /* Limiter la largeur */
343
  }
344
 
345
+ .thinking-indicator { background-color: #e3f2fd; color: #1565c0; border: 1px solid #bbdefb; }
346
+ .executing-indicator { background-color: #ede7f6; color: #5e35b1; border: 1px solid #d1c4e9; }
347
+ .answering-indicator { background-color: #e8f5e9; color: #2e7d32; border: 1px solid #c8e6c9; }
348
 
349
  .indicator-icon {
350
  margin-right: 12px;
 
353
  }
354
 
355
  @keyframes pulse {
356
+ 0% { opacity: 0.6; transform: scale(1); }
357
+ 50% { opacity: 1; transform: scale(1.05); }
358
+ 100% { opacity: 0.6; transform: scale(1); }
359
  }
360
 
361
  /* Améliorations supplémentaires pour Markdown */
362
  .step-section blockquote {
363
+ border-left: 4px solid #ccc; /* Gris standard */
364
  margin: 1em 0;
365
+ padding: 0.5em 15px;
366
+ color: #555;
 
367
  background-color: #f9f9f9;
368
  }
369
+ .step-section blockquote p:last-child {
370
+ margin-bottom: 0; /* Éviter double marge dans blockquote */
371
+ }
372
 
373
+ .step-section hr {
374
+ border: none;
375
+ border-top: 1px solid #eee;
376
+ margin: 2em 0;
377
+ }
378
+
379
+ .step-section h1,
380
+ .step-section h2,
381
+ .step-section h3,
382
+ .step-section h4,
383
+ .step-section h5,
384
  .step-section h6 {
385
  margin-top: 1.5em;
386
+ margin-bottom: 0.8em;
387
  color: var(--primary-color);
388
+ font-weight: 600; /* Un peu plus gras */
389
+ padding-bottom: 0.2em;
390
+ border-bottom: 1px solid #eee; /* Souligner légèrement les titres */
391
  }
392
+ .step-section h1 { font-size: 1.8em; border-bottom-width: 2px; }
 
393
  .step-section h2 { font-size: 1.6em; }
394
  .step-section h3 { font-size: 1.4em; }
395
  .step-section h4 { font-size: 1.2em; }
396
+ .step-section h5 { font-size: 1.1em; }
397
+ .step-section h6 { font-size: 1.0em; color: #666; }
398
+
399
  </style>
400
  </head>
401
  <body>
 
422
  </div>
423
 
424
  <div class="upload-section">
425
+ <h2>Soumettez votre problème</h2>
426
  <form id="imageForm" enctype="multipart/form-data">
427
  <input type="file" id="imageInput" name="image" accept="image/*" style="display: none;">
428
  <button type="button" id="uploadButton" class="cta-button" onclick="document.getElementById('imageInput').click()">
429
  <i class="fas fa-upload"></i> Télécharger une image
430
  </button>
431
+ <p id="uploadStatus" style="margin-top: 10px; font-size: 0.9em; color: #555;"></p>
432
  </form>
433
 
434
+ <div id="imagePreview" style="display: none; margin: 20px auto; max-width: 400px; max-height: 300px; overflow: hidden; border: 1px solid #ddd; border-radius: 8px;">
435
+ <img id="preview" style="display: block; width: 100%; height: auto; border-radius: 8px;">
 
436
  </div>
437
 
438
  <button id="solveButton" class="cta-button" style="display: none; background-color: var(--secondary-color);">
 
440
  </button>
441
  </div>
442
 
443
+ <!-- Section pour afficher la solution -->
444
+ <div id="solutionOutput"> <!-- Initialement caché via CSS -->
445
  <h3>Solution :</h3>
446
  <div id="loadingIndicator" class="thinking-indicator" style="display: none;">
447
  <i class="fas fa-brain indicator-icon"></i>
448
  <span>Je réfléchis au problème...</span>
449
  </div>
450
+ <!-- Container pour les blocs de contenu dynamiques -->
451
  <div id="solution">
452
+ <!-- Le contenu (steps, code, output) sera ajouté ici par JS -->
453
  </div>
454
  </div>
455
 
 
468
  <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/highlight.min.js"></script>
469
  <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/languages/python.min.js"></script>
470
 
471
+ <!-- KaTeX Scripts -->
472
  <script src="https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.16.8/katex.min.js"></script>
473
  <script src="https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.16.8/contrib/auto-render.min.js"></script>
474
 
475
  <script>
476
  // Configuration de marked pour le rendu Markdown
477
  marked.setOptions({
478
+ gfm: true,
479
+ breaks: true, // Convertit les simples retours à la ligne en <br> (peut être désactivé si non souhaité)
480
+ smartLists: true,
481
+ smartypants: true,
482
+ xhtml: true,
483
+ highlight: function(code, lang) {
484
+ // Utilise highlight.js pour la coloration syntaxique DANS les blocs de code Markdown
485
+ // (différent des blocs de code Python dédiés)
486
+ const language = hljs.getLanguage(lang) ? lang : 'plaintext';
487
+ return hljs.highlight(code, { language }).value;
488
+ }
489
  });
490
 
491
+ // Configuration de KaTeX (options globales)
492
+ const katexOptions = {
493
+ delimiters: [
494
+ {left: '$$', right: '$$', display: true},
495
+ {left: '$', right: '$', display: false},
496
+ {left: '\\(', right: '\\)', display: false},
497
+ {left: '\\[', right: '\\]', display: true}
498
+ ],
499
+ throwOnError: false, // Ne pas bloquer toute la page en cas d'erreur LaTeX
500
+ // trust: true, // Activer seulement si besoin de commandes comme \htmlClass
501
+ strict: (errorCode) => {
502
+ // Permet d'ignorer certaines erreurs "mineures" si nécessaire
503
+ if (errorCode === 'unicodeTextInMathMode') {
504
+ return 'ignore'; // Ou 'warn'
505
+ }
506
+ return 'warn'; // Affiche les erreurs dans la console sans planter
507
+ }
508
+ };
509
 
510
  document.getElementById('imageInput').addEventListener('change', function(event) {
511
  const file = event.target.files[0];
 
516
  document.getElementById('imagePreview').style.display = 'block';
517
  document.getElementById('solveButton').style.display = 'inline-block';
518
  document.getElementById('uploadStatus').textContent = `Image sélectionnée : ${file.name}`;
519
+ // Cacher l'ancienne solution si une nouvelle image est chargée
520
+ document.getElementById('solutionOutput').style.display = 'none';
521
+ document.getElementById('solution').innerHTML = '';
522
  }
523
  reader.readAsDataURL(file);
524
  }
 
526
 
527
  document.getElementById('solveButton').addEventListener('click', function() {
528
  const formData = new FormData(document.getElementById('imageForm'));
529
+ const solutionOutputDiv = document.getElementById('solutionOutput');
530
  const loadingIndicator = document.getElementById('loadingIndicator');
531
  const solutionContainer = document.getElementById('solution');
532
 
533
+ solutionOutputDiv.style.display = 'block'; // Afficher la section solution
534
+ loadingIndicator.style.display = 'flex'; // Afficher l'indicateur de chargement
535
  solutionContainer.innerHTML = ''; // Effacer la solution précédente
536
+ // Scroller pour rendre l'indicateur visible
537
+ loadingIndicator.scrollIntoView({ behavior: 'smooth', block: 'center' });
538
 
539
+ fetch('/solved', { // Endpoint Flask
540
  method: 'POST',
541
  body: formData
542
  })
543
  .then(response => {
544
  if (!response.ok) {
 
545
  return response.text().then(text => {
546
+ throw new Error(`Erreur serveur: ${response.status} ${response.statusText}\n${text || '(Aucun détail)'}`);
547
  });
548
  }
549
  const reader = response.body.getReader();
 
553
  function processStream({ done, value }) {
554
  if (done) {
555
  loadingIndicator.style.display = 'none';
556
+ // Passe finale de rendu KaTeX pour être sûr
557
  try {
558
+ renderMathInElement(solutionContainer, katexOptions);
559
  } catch (e) {
560
+ console.error('Erreur de rendu KaTeX final:', e);
561
  }
562
  return;
563
  }
564
 
565
  buffer += decoder.decode(value, { stream: true });
566
+ const messages = buffer.split(/\r?\n\r?\n/); // Sépare les messages SSE
567
+ buffer = messages.pop(); // Garde le dernier morceau potentiellement incomplet
 
568
 
569
+ messages.forEach(message => {
 
570
  if (message.startsWith('data: ')) {
571
  try {
572
+ const data = JSON.parse(message.substring(6)); // Extrait le JSON
573
 
574
+ // --- Gérer les mises à jour de mode ---
575
  if (data.mode) {
576
  const modes = {
577
  thinking: { icon: 'fa-brain', text: 'Je réfléchis...', class: 'thinking-indicator' },
578
+ answering: { icon: 'fa-pencil-alt', text: 'Je rédige la réponse...', class: 'answering-indicator' },
579
+ executing_code: { icon: 'fa-play', text: 'Exécution du code...', class: 'executing-indicator' }, // Icône différente
580
+ code_result: { icon: 'fa-terminal', text: 'Traitement des résultats...', class: 'executing-indicator' }
581
  };
582
+ const modeInfo = modes[data.mode] || { icon: 'fa-sync-alt fa-spin', text: 'Traitement...', class: 'thinking-indicator' }; // Fallback
583
+
584
+ loadingIndicator.className = `${modeInfo.class}`; // Assigner la classe CSS
585
+ loadingIndicator.innerHTML = `<i class="fas ${modeInfo.icon} indicator-icon"></i><span>${modeInfo.text}</span>`;
586
+ loadingIndicator.style.display = 'flex';
 
 
 
 
 
587
  }
588
 
589
+ // --- Gérer les blocs de contenu ---
590
  if (data.content) {
591
+ loadingIndicator.style.display = 'none'; // Cacher l'indicateur quand le contenu arrive
592
  const content = data.content;
593
  const tempDiv = document.createElement('div');
594
+ tempDiv.innerHTML = content; // Analyser le fragment HTML reçu
595
 
596
  const codeSection = tempDiv.querySelector('.code-section');
597
  const outputSection = tempDiv.querySelector('.output-section');
598
+ const stepSection = tempDiv.querySelector('.step-section'); // Peut être directement le contenu
599
 
600
  if (codeSection) {
601
+ // C'est une section de code Python dédiée
602
  solutionContainer.appendChild(codeSection);
 
603
  codeSection.querySelectorAll('pre code').forEach((block) => {
604
+ hljs.highlightElement(block); // Appliquer highlight.js
605
  });
606
  } else if (outputSection) {
607
+ // C'est une section de sortie de code
608
  solutionContainer.appendChild(outputSection);
609
  } else {
610
+ // C'est une section d'étape (texte, Markdown, LaTeX)
611
+ // Créer un conteneur propre pour cette étape
612
  const stepDiv = document.createElement('div');
613
+ stepDiv.className = 'step-section'; // Appliquer la classe pour le style
614
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
615
  try {
616
+ // 1. Convertir le Markdown en HTML en utilisant marked.js
617
+ const htmlContent = marked.parse(content);
618
+ stepDiv.innerHTML = htmlContent;
619
+
620
+ // 2. Ajouter le div au conteneur principal
621
+ solutionContainer.appendChild(stepDiv);
622
+
623
+ // 3. Rendre le LaTeX DANS ce div spécifique avec KaTeX
624
+ renderMathInElement(stepDiv, katexOptions);
625
+
626
  } catch (e) {
627
+ console.error('Erreur pendant le traitement Markdown ou KaTeX:', e);
628
+ // En cas d'erreur, afficher le contenu brut pour le débogage
629
+ stepDiv.innerHTML = `<p>Erreur de rendu :</p><pre>${content.replace(/</g, "<")}</pre>`;
630
+ solutionContainer.appendChild(stepDiv);
631
  }
632
  }
633
  }
634
 
635
+ // --- Gérer les erreurs spécifiques ---
636
  if (data.error) {
637
  const errorDiv = document.createElement('div');
638
+ // Utiliser une classe existante ou créer une classe 'error-section'
639
+ errorDiv.className = 'step-section error-message'; // Réutiliser step-section avec une classe d'erreur
640
  errorDiv.style.color = 'red';
641
+ errorDiv.style.backgroundColor = '#fff0f0';
642
  errorDiv.style.borderColor = 'red';
643
+ errorDiv.innerHTML = `<strong>Erreur :</strong> ${marked.parse(data.error || 'Une erreur inconnue est survenue.')}`; // Permet le markdown dans l'erreur
644
  solutionContainer.appendChild(errorDiv);
645
  loadingIndicator.style.display = 'none';
646
  }
647
+
648
  } catch (e) {
649
+ console.error('Erreur analyse JSON ou traitement message SSE:', e, message);
650
  const errorDiv = document.createElement('div');
651
+ errorDiv.className = 'step-section error-message';
652
  errorDiv.style.color = 'orange';
653
+ errorDiv.style.backgroundColor = '#fff8e1';
654
+ errorDiv.style.borderColor = 'orange';
655
+ errorDiv.textContent = `Erreur de traitement du message reçu: ${message.substring(0, 150)}...`;
656
  solutionContainer.appendChild(errorDiv);
657
  loadingIndicator.style.display = 'none';
658
  }
659
  }
660
+ });
661
+
662
+ // Défiler vers le bas pour voir le nouveau contenu au fur et à mesure
663
+ solutionContainer.lastChild?.scrollIntoView({ behavior: 'smooth', block: 'end' });
664
 
 
 
665
 
666
  // Continuer à lire le flux
667
  return reader.read().then(processStream);
668
  }
669
 
670
+ // Démarrer le traitement du flux
671
+ reader.read().then(processStream);
672
  })
673
  .catch(error => {
674
+ console.error('Erreur Fetch ou connexion:', error);
675
  const errorDiv = document.createElement('div');
676
+ errorDiv.className = 'step-section error-message';
677
  errorDiv.style.color = 'red';
678
+ errorDiv.style.backgroundColor = '#fff0f0';
679
  errorDiv.style.borderColor = 'red';
680
+ errorDiv.innerHTML = `<strong>Erreur de connexion ou serveur :</strong> ${error.message || error}`;
681
  solutionContainer.appendChild(errorDiv);
682
  loadingIndicator.style.display = 'none';
683
+ errorDiv.scrollIntoView({ behavior: 'smooth', block: 'center' });
684
  });
685
  });
686
 
687
+ // Premier rendu KaTeX au chargement de la page (pour tout contenu statique éventuel)
688
  document.addEventListener('DOMContentLoaded', function() {
689
+ try {
690
+ // Utilise document.body pour scanner toute la page initiale
691
+ renderMathInElement(document.body, katexOptions);
692
+ } catch (e) {
693
+ console.error('Erreur KaTeX initiale:', e)
694
+ }
695
  });
696
  </script>
697
  </body>