Docfile commited on
Commit
fa7810f
·
verified ·
1 Parent(s): a04e8b6

Update templates/maj.html

Browse files
Files changed (1) hide show
  1. templates/maj.html +192 -376
templates/maj.html CHANGED
@@ -3,11 +3,10 @@
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>Math Solver - Version Gratuite</title>
7
- <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet"> <!-- Updated Font Awesome -->
8
  <link href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/styles/atom-one-dark.min.css" rel="stylesheet">
9
- <!-- KaTeX CSS -->
10
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/katex.min.css" integrity="sha384-n8MVd4RsNIU0tAv4ct0nTaAbDJwPJzDEaqSD1tBCETAMZN+vtEQFPxfujAHU4/mz" crossorigin="anonymous">
11
  <style>
12
  :root {
13
  --primary-color: #4a6fa5;
@@ -15,7 +14,7 @@
15
  --accent-color: #4fc3f7;
16
  --background-color: #f8f9fa;
17
  --text-color: #333;
18
- --box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); /* Slightly enhanced shadow */
19
  --code-bg: #2c323c;
20
  --output-bg: #f1f8f9;
21
  }
@@ -27,14 +26,14 @@
27
  padding: 0;
28
  background-color: var(--background-color);
29
  color: var(--text-color);
30
- font-size: 16px; /* Base font size */
31
  }
32
 
33
  .container {
34
  max-width: 1000px;
35
  margin: 0 auto;
36
  padding: 20px;
37
- box-sizing: border-box; /* Include padding in width */
38
  }
39
 
40
  header {
@@ -45,14 +44,14 @@
45
  }
46
 
47
  .logo {
48
- font-size: 2.2rem; /* Adjusted size */
49
  font-weight: bold;
50
  color: var(--primary-color);
51
  margin-bottom: 5px;
52
  }
53
 
54
  .subtitle {
55
- font-size: 1.1rem; /* Adjusted size */
56
  color: var(--secondary-color);
57
  margin-bottom: 20px;
58
  }
@@ -66,299 +65,133 @@
66
  text-align: center;
67
  }
68
 
69
- h1 {
70
- color: var(--primary-color);
71
- margin-top: 0;
72
- margin-bottom: 15px;
73
- font-size: 1.8rem;
74
- }
75
-
76
- h2 {
77
- color: var(--secondary-color);
78
- margin-top: 25px;
79
- margin-bottom: 15px;
80
- font-size: 1.5rem;
81
- }
82
- h3 {
83
- color: var(--primary-color);
84
- margin-top: 20px;
85
- margin-bottom: 10px;
86
- font-size: 1.3rem;
87
- text-align: left; /* Align solution heading left */
88
- }
89
 
90
- .feature-list {
91
- list-style-type: none;
92
- padding: 0;
93
- margin: 20px 0 30px 0;
94
- text-align: left;
95
- display: inline-block; /* Center the list block itself */
96
- }
97
 
98
- .feature-list li {
99
- padding: 8px 0;
100
- margin-bottom: 8px;
101
- display: flex;
102
- align-items: center;
103
- font-size: 1rem;
104
- }
105
-
106
- .feature-list i {
107
- color: var(--accent-color);
108
- margin-right: 12px;
109
- font-size: 1.2rem;
110
- width: 20px; /* Ensure alignment */
111
- text-align: center;
112
- }
113
- .feature-list li .fa-times-circle {
114
- color: #aaa; /* Dimmer color for unavailable features */
115
- }
116
- .feature-list li span {
117
- color: #777; /* Style the descriptive text */
118
- }
119
 
120
- .cta-button {
121
- display: inline-block;
122
- background-color: var(--primary-color);
123
- color: white;
124
- padding: 12px 25px;
125
- border-radius: 5px;
126
- text-decoration: none;
127
- font-weight: bold;
128
- transition: all 0.3s ease;
129
- margin: 10px 5px; /* Adjusted margin */
130
- border: none;
131
- cursor: pointer;
132
- font-size: 1rem;
133
- text-align: center;
134
- }
135
 
136
- .cta-button:hover {
137
- background-color: var(--secondary-color);
138
- transform: translateY(-2px);
139
- box-shadow: 0 6px 12px rgba(0, 0, 0, 0.15);
140
- }
141
- .cta-button i {
142
- margin-right: 8px;
143
- }
144
 
145
- .upload-section, .upgrade-section {
146
- margin-top: 30px;
147
- padding-top: 20px;
148
- border-top: 1px solid #eee;
149
- }
150
-
151
- footer {
152
- text-align: center;
153
- padding: 30px 0 20px 0;
154
- color: #666;
155
- font-size: 0.9rem;
156
- border-top: 1px solid #eee;
157
- margin-top: 40px;
158
- }
159
-
160
- #solutionOutput {
161
- margin-top: 30px;
162
- text-align: left;
163
- display: none; /* Initially hidden */
164
- }
165
-
166
- #solution {
167
- background: #fff; /* White background for readability */
168
- padding: 20px;
169
- border-radius: 8px;
170
- text-align: left;
171
- line-height: 1.8;
172
- font-size: 1rem; /* Ensure consistent font size */
173
- border: 1px solid #eee; /* Subtle border */
174
- margin-top: 15px;
175
- overflow-x: auto; /* Allow horizontal scroll if needed */
176
- }
177
 
178
  /* Code Block Styling */
179
- .code-section {
180
- margin: 20px 0;
181
- border-radius: 8px;
182
- overflow: hidden; /* Important for border radius */
183
- box-shadow: 0 2px 5px rgba(0,0,0,0.1);
184
- }
185
-
186
- .code-header {
187
- background-color: #343a40;
188
- color: white;
189
- padding: 10px 15px;
190
- font-size: 0.9em;
191
- font-family: 'Courier New', monospace;
192
- }
193
-
194
- .code-content {
195
- margin: 0;
196
- padding: 15px;
197
- background-color: var(--code-bg);
198
- color: #e6e6e6;
199
- overflow-x: auto; /* Essential for long code lines */
200
- font-family: 'Courier New', monospace;
201
- font-size: 0.9em;
202
- line-height: 1.5;
203
- }
204
- /* Ensure pre/code take full advantage */
205
- .code-content pre, .code-content code {
206
- margin: 0;
207
- padding: 0;
208
- background: none;
209
- white-space: pre; /* Let overflow-x handle wrapping */
210
- }
211
 
212
  /* Output Block Styling */
213
- .output-section {
214
- background-color: var(--output-bg);
215
- padding: 15px;
216
- margin: 15px 0; /* Spacing */
217
- border-radius: 8px;
218
- border: 1px solid #d6e0e2; /* Subtle border */
219
- color: #333;
220
- font-family: 'Courier New', monospace;
221
- font-size: 0.9em;
222
- white-space: pre-wrap; /* Wrap text */
223
- overflow-x: auto; /* Scroll if needed */
224
- box-shadow: inset 0 1px 3px rgba(0,0,0,0.05);
225
- }
226
 
227
  /* Step Styling */
228
- .step-section {
229
- margin: 20px 0;
230
- padding: 15px;
231
- background-color: #fdfdfd; /* Slightly off-white */
232
- border-left: 4px solid var(--accent-color); /* Use accent color */
233
- border-radius: 0 5px 5px 0;
234
- overflow-wrap: break-word; /* Break long words/URLs */
235
- }
236
- .step-section:first-child {
237
- margin-top: 0;
238
- }
239
- .step-section:last-child {
240
- margin-bottom: 0;
241
- }
242
 
243
  /* Indicator Styling */
244
- .indicator {
245
- display: flex;
246
- align-items: center;
247
- padding: 12px 15px;
248
- margin: 15px 0;
249
- border-radius: 8px;
250
- font-size: 0.95rem;
251
- border: 1px solid transparent;
252
- }
253
- .indicator.thinking-indicator {
254
- background-color: #e3f2fd; color: #1565c0; border-color: #bde0fe;
255
- }
256
- .indicator.executing-indicator {
257
- background-color: #ede7f6; color: #5e35b1; border-color: #d1c4e9;
258
- }
259
- .indicator.answering-indicator {
260
- background-color: #e8f5e9; color: #2e7d32; border-color: #c8e6c9;
261
- }
262
- .indicator i {
263
- margin-right: 10px;
264
- font-size: 1.1em;
265
- animation: pulse 1.5s infinite ease-in-out;
266
- }
267
- @keyframes pulse {
268
- 0%, 100% { opacity: 0.7; }
269
- 50% { opacity: 1; }
270
- }
271
-
272
- /* KaTeX Display adjustments */
273
- .katex-display {
274
- overflow-x: auto; /* Allow scrolling for wide equations */
275
  overflow-y: hidden;
276
- padding: 8px 0; /* Add some vertical padding */
277
- }
278
- .katex {
279
- font-size: 1.1em; /* Slightly larger math font */
280
- text-align: left; /* Default math alignment */
281
- }
282
- .katex-display > .katex {
283
- text-align: center; /* Center display math */
284
- display: block; /* Ensure it takes block space */
285
- }
286
 
287
 
288
  /* --- Responsive Design --- */
289
-
290
- /* Smaller Desktops / Large Tablets */
291
- @media (max-width: 992px) {
292
- .container {
293
- max-width: 90%;
294
- }
295
- }
296
-
297
- /* Tablets and smaller */
298
  @media (max-width: 768px) {
299
- .container {
300
- padding: 15px;
301
- }
302
- .logo {
303
- font-size: 2rem;
304
- }
305
- .subtitle {
306
- font-size: 1rem;
307
- }
308
- h1 { font-size: 1.6rem; }
309
- h2 { font-size: 1.3rem; }
310
- h3 { font-size: 1.2rem; }
311
-
312
- .content-box {
313
- padding: 20px;
314
- }
315
- .feature-list {
316
- display: block; /* Stack list items */
317
- text-align: left; /* Align text left on mobile */
318
- margin-left: auto; /* Keep centering effect if desired */
319
- margin-right: auto;
320
- }
321
- .upload-section, .upgrade-section {
322
- padding-top: 15px;
323
- }
324
- #imagePreview {
325
- max-width: 100%; /* Ensure preview fits */
326
- }
327
- #imagePreview img {
328
- max-width: 100%; /* Scale image within preview */
329
- height: auto; /* Maintain aspect ratio */
330
- }
331
  }
332
-
333
- /* Mobile phones */
334
  @media (max-width: 576px) {
335
- body {
336
- font-size: 15px; /* Slightly smaller base font */
337
- }
338
- .logo {
339
- font-size: 1.8rem;
340
- }
341
- .cta-button {
342
- display: block; /* Stack buttons */
343
- width: 100%;
344
- margin: 10px 0; /* Adjust margin for stacked buttons */
345
- box-sizing: border-box; /* Include padding in width */
346
- }
347
- #solveButton {
348
- margin-top: 15px;
349
  }
350
- .content-box {
351
- padding: 15px;
352
- }
353
- #solution {
354
- padding: 15px;
355
- }
356
- .code-header, .code-content, .output-section {
357
- font-size: 0.85em; /* Smaller code font on mobile */
358
- }
359
  }
360
-
361
  </style>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
362
  </head>
363
  <body>
364
  <div class="container">
@@ -376,7 +209,7 @@
376
  <li><i class="fas fa-check-circle"></i> Résolution de problèmes mathématiques</li>
377
  <li><i class="fas fa-check-circle"></i> 3 résolutions gratuites par jour</li>
378
  <li><i class="fas fa-check-circle"></i> Explication des étapes de résolution</li>
379
- <li><i class="fas fa-check-circle"></i> Support du format LaTeX</li>
380
  <li><i class="fas fa-times-circle"></i> <span>Exécution de code Python (Pro)</span></li>
381
  <li><i class="fas fa-times-circle"></i> <span>Résolutions illimitées (Pro)</span></li>
382
  <li><i class="fas fa-times-circle"></i> <span>Support prioritaire (Pro)</span></li>
@@ -385,16 +218,14 @@
385
  <div class="upload-section">
386
  <h2>Soumettre un problème</h2>
387
  <form id="imageForm" enctype="multipart/form-data">
388
- <!-- Hide the actual input -->
389
  <input type="file" id="imageInput" name="image" accept="image/*" style="display: none;">
390
- <!-- Style a button to trigger the input -->
391
  <button type="button" class="cta-button" onclick="document.getElementById('imageInput').click()">
392
  <i class="fas fa-upload"></i> Télécharger une image
393
  </button>
394
  </form>
395
 
396
  <p id="uploadStatus" style="margin-top: 15px; font-size: 0.9em; color: #555;"></p>
397
- <div id="imagePreview" style="display: none; margin: 20px auto; max-width: 400px;"> <!-- Adjusted max-width -->
398
  <img id="preview" style="display: block; width: 100%; border-radius: 8px; box-shadow: var(--box-shadow);">
399
  </div>
400
 
@@ -403,7 +234,7 @@
403
  </button>
404
  </div>
405
 
406
- <div id="solutionOutput" style="display: none;"> <!-- Container for the whole solution section -->
407
  <h3>Solution Détaillée :</h3>
408
  <div id="loadingIndicator" class="indicator" style="display: none;">
409
  <!-- Content set by JS -->
@@ -411,6 +242,9 @@
411
  <div id="solution">
412
  <!-- Solution content will be injected here -->
413
  </div>
 
 
 
414
  </div>
415
 
416
 
@@ -430,11 +264,6 @@
430
  <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/highlight.min.js"></script>
431
  <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/languages/python.min.js"></script>
432
 
433
- <!-- Dependencies: KaTeX -->
434
- <!-- The loading of KaTeX is deferred to speed up page rendering -->
435
- <script defer src="https://cdn.jsdelivr.net/npm/[email protected]/dist/katex.min.js" integrity="sha384-XjKyOOlGwcjNTAIQHIpgOno0Hl1YQqzUOEleOLALmuqehneUG+vnGctmUb0ZY0l8" crossorigin="anonymous"></script>
436
- <script defer src="https://cdn.jsdelivr.net/npm/[email protected]/dist/contrib/auto-render.min.js" integrity="sha384-+VBxd3r6XgURycqtZ117nYw44OOcIax56Z4dCRWbxyPt0Koah1uHoK0o4+/RRE05" crossorigin="anonymous"></script>
437
-
438
  <script>
439
  document.addEventListener('DOMContentLoaded', (event) => {
440
 
@@ -446,7 +275,8 @@
446
  const imageForm = document.getElementById('imageForm');
447
  const solutionOutput = document.getElementById('solutionOutput');
448
  const loadingIndicator = document.getElementById('loadingIndicator');
449
- const solutionDiv = document.getElementById('solution'); // Renamed for clarity
 
450
 
451
  imageInput.addEventListener('change', function(event) {
452
  const file = event.target.files[0];
@@ -455,14 +285,14 @@
455
  reader.onload = function(e) {
456
  preview.src = e.target.result;
457
  imagePreview.style.display = 'block';
458
- solveButton.style.display = 'inline-block'; // Or 'block' based on mobile styles
459
  uploadStatus.textContent = `Image sélectionnée : ${file.name}`;
460
- solutionOutput.style.display = 'none'; // Hide previous solution
461
- solutionDiv.innerHTML = ''; // Clear previous solution
 
462
  }
463
  reader.readAsDataURL(file);
464
  } else {
465
- // Reset if no file selected
466
  uploadStatus.textContent = '';
467
  imagePreview.style.display = 'none';
468
  solveButton.style.display = 'none';
@@ -477,87 +307,83 @@
477
  return;
478
  }
479
 
480
- solutionOutput.style.display = 'block'; // Show the solution area
481
- loadingIndicator.style.display = 'flex'; // Show loading
482
- loadingIndicator.className = 'indicator thinking-indicator'; // Reset class
483
  loadingIndicator.innerHTML = '<i class="fas fa-brain indicator-icon"></i><span>Préparation de la requête...</span>';
484
- solutionDiv.innerHTML = ''; // Clear previous solution content
 
485
 
486
- fetch('/solved', { // Make sure this endpoint matches your backend
487
  method: 'POST',
488
  body: formData
489
  })
490
  .then(response => {
491
- if (!response.ok) {
492
- throw new Error(`Erreur HTTP: ${response.status}`);
493
- }
494
- if (!response.body) {
495
- throw new Error("La réponse ne contient pas de corps.");
496
- }
497
 
498
  const reader = response.body.getReader();
499
  const decoder = new TextDecoder();
500
  let buffer = '';
501
- let accumulatedContent = ''; // Store all content for final rendering
502
 
503
  function processStream({ done, value }) {
504
  if (done) {
505
  loadingIndicator.style.display = 'none';
506
- // Process any remaining buffer
 
507
  if (buffer.trim()) {
508
- // This assumes the last part is regular text/LaTeX
509
- // Might need adjustment if code/output can be the last chunk
510
- const finalStepDiv = document.createElement('div');
511
- finalStepDiv.className = 'step-section';
512
- finalStepDiv.innerHTML = buffer; // Use innerHTML to allow potential HTML tags in the last chunk
513
- solutionDiv.appendChild(finalStepDiv);
514
- accumulatedContent += buffer;
515
  }
516
 
517
- // FINAL LaTeX Rendering
518
- try {
519
- // Check if KaTeX is loaded
520
- if (window.renderMathInElement) {
521
- renderMathInElement(solutionDiv, {
522
- delimiters: [
523
- {left: '$$', right: '$$', display: true},
524
- {left: '$', right: '$', display: false},
525
- {left: '\\(', right: '\\)', display: false},
526
- {left: '\\[', right: '\\]', display: true}
527
- ],
528
- throwOnError: false // Don't stop rendering on single error
529
- });
530
- } else {
531
- console.warn("KaTeX auto-render not loaded yet.");
532
- // Optionally add a message or retry mechanism
533
- }
534
- } catch (e) {
535
- console.error('Erreur lors du rendu final de LaTeX:', e);
536
- solutionDiv.innerHTML += `<div style="color: orange; margin-top: 10px;">Erreur lors de l'affichage de certaines formules mathématiques.</div>`;
 
 
 
 
 
 
 
 
537
  }
538
-
539
- // Final Highlight.js call (optional, could rely on per-block)
540
- // hljs.highlightAll(); // Or just rely on highlightElement below
541
-
542
- // Scroll to the solution area
543
- solutionOutput.scrollIntoView({ behavior: 'smooth', block: 'start' });
544
- return;
545
- }
546
 
547
  buffer += decoder.decode(value, { stream: true });
548
- // Split by double newline, assuming that separates messages
549
  const parts = buffer.split('\n\n');
550
- buffer = parts.pop() || ''; // Keep incomplete part for next chunk
551
 
552
  parts.forEach(part => {
553
  if (part.startsWith('data: ')) {
554
  try {
555
- const jsonData = JSON.parse(part.substring(6)); // Remove 'data: '
556
- accumulatedContent += jsonData.content || ''; // Add to total content
557
 
558
- // Update Indicator
559
  if (jsonData.mode) {
560
- loadingIndicator.style.display = 'flex'; // Ensure visible
561
  if (jsonData.mode === 'thinking') {
562
  loadingIndicator.className = 'indicator thinking-indicator';
563
  loadingIndicator.innerHTML = '<i class="fas fa-brain indicator-icon"></i><span>Je réfléchis au problème...</span>';
@@ -568,12 +394,12 @@
568
  loadingIndicator.className = 'indicator executing-indicator';
569
  loadingIndicator.innerHTML = '<i class="fas fa-code indicator-icon"></i><span>Exécution du code Python...</span>';
570
  } else if (jsonData.mode === 'code_result') {
571
- loadingIndicator.className = 'indicator executing-indicator'; // Keep same style?
572
  loadingIndicator.innerHTML = '<i class="fas fa-terminal indicator-icon"></i><span>Traitement des résultats du code...</span>';
573
  }
574
  }
575
 
576
- // Append Content
577
  if (jsonData.content) {
578
  const content = jsonData.content;
579
  let elementToAppend;
@@ -584,28 +410,25 @@
584
  elementToAppend.className = 'code-section';
585
  elementToAppend.innerHTML = `
586
  <div class="code-header">Code Python</div>
587
- <div class="code-content"><pre><code class="language-python">${escapeHtml(code)}</code></pre></div>
588
- `;
589
  solutionDiv.appendChild(elementToAppend);
590
- // Highlight this specific block
591
  const codeBlock = elementToAppend.querySelector('code');
592
- if (codeBlock && hljs) {
593
- hljs.highlightElement(codeBlock);
594
- }
595
  } else if (content.startsWith('Résultat d\'exécution:\n```')) {
596
  const output = content.replace(/^Résultat d'exécution:\n```\n?([\s\S]*?)\n?```$/, '$1');
597
  elementToAppend = document.createElement('div');
598
- elementToAppend.className = 'output-section';
599
- elementToAppend.textContent = output; // Use textContent for plain output
600
  solutionDiv.appendChild(elementToAppend);
 
601
  } else {
602
- // Regular text, potentially with inline LaTeX
603
  elementToAppend = document.createElement('div');
604
  elementToAppend.className = 'step-section';
605
- // Use innerHTML carefully, assuming backend sanitizes or it's trusted markdown/html
606
- elementToAppend.innerHTML = content;
607
- solutionDiv.appendChild(elementToAppend);
608
- // We wait until the end to render LaTeX
609
  }
610
  }
611
 
@@ -614,44 +437,37 @@
614
  errorDiv.style.cssText = 'color: red; margin: 15px 0; padding: 10px; background: #ffeeee; border: 1px solid #ffdddd; border-radius: 5px;';
615
  errorDiv.textContent = `Erreur: ${jsonData.error}`;
616
  solutionDiv.appendChild(errorDiv);
617
- loadingIndicator.style.display = 'none'; // Hide loading on error display
618
  }
619
 
620
  } catch (e) {
621
  console.error('Erreur de parsing JSON ou traitement:', e, 'Part:', part);
622
- // Append raw part if parsing fails, for debugging?
623
- // const errorDiv = document.createElement('div');
624
- // errorDiv.style.color = 'orange';
625
- // errorDiv.textContent = `Donnée brute non traitée: ${part}`;
626
- // solutionDiv.appendChild(errorDiv);
627
  }
628
  }
629
  });
630
 
631
- // Scroll down automatically as content arrives
632
- // window.scrollTo(0, document.body.scrollHeight);
633
  solutionDiv.lastChild?.scrollIntoView({ behavior: 'smooth', block: 'end' });
634
 
635
-
636
  // Continue reading the stream
637
  return reader.read().then(processStream);
638
- }
639
 
640
  // Start processing the stream
641
  return reader.read().then(processStream);
642
  })
643
  .catch(error => {
644
  console.error('Erreur lors de la requête fetch:', error);
645
- loadingIndicator.style.display = 'none'; // Hide loading
646
  const errorDiv = document.createElement('div');
647
  errorDiv.style.cssText = 'color: red; margin: 15px 0; padding: 10px; background: #ffeeee; border: 1px solid #ffdddd; border-radius: 5px;';
648
  errorDiv.textContent = `Erreur de communication avec le serveur: ${error.message}`;
649
  solutionDiv.appendChild(errorDiv);
 
650
  solutionOutput.scrollIntoView({ behavior: 'smooth', block: 'start' });
651
  });
652
  });
653
 
654
- // Helper function to escape HTML for code blocks
655
  function escapeHtml(unsafe) {
656
  return unsafe
657
  .replace(/&/g, "&")
@@ -660,7 +476,7 @@
660
  .replace(/"/g, """)
661
  .replace(/'/g, "'");
662
  }
663
- }); // End DOMContentLoaded
664
  </script>
665
 
666
  </body>
 
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Math Solver - Version Gratuite (MathJax)</title>
7
+ <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/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
+ <!-- REMOVED KaTeX CSS -->
 
10
  <style>
11
  :root {
12
  --primary-color: #4a6fa5;
 
14
  --accent-color: #4fc3f7;
15
  --background-color: #f8f9fa;
16
  --text-color: #333;
17
+ --box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
18
  --code-bg: #2c323c;
19
  --output-bg: #f1f8f9;
20
  }
 
26
  padding: 0;
27
  background-color: var(--background-color);
28
  color: var(--text-color);
29
+ font-size: 16px;
30
  }
31
 
32
  .container {
33
  max-width: 1000px;
34
  margin: 0 auto;
35
  padding: 20px;
36
+ box-sizing: border-box;
37
  }
38
 
39
  header {
 
44
  }
45
 
46
  .logo {
47
+ font-size: 2.2rem;
48
  font-weight: bold;
49
  color: var(--primary-color);
50
  margin-bottom: 5px;
51
  }
52
 
53
  .subtitle {
54
+ font-size: 1.1rem;
55
  color: var(--secondary-color);
56
  margin-bottom: 20px;
57
  }
 
65
  text-align: center;
66
  }
67
 
68
+ h1 { color: var(--primary-color); margin-top: 0; margin-bottom: 15px; font-size: 1.8rem; }
69
+ h2 { color: var(--secondary-color); margin-top: 25px; margin-bottom: 15px; font-size: 1.5rem; }
70
+ h3 { color: var(--primary-color); margin-top: 20px; margin-bottom: 10px; font-size: 1.3rem; text-align: left; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
71
 
72
+ .feature-list { list-style-type: none; padding: 0; margin: 20px 0 30px 0; text-align: left; display: inline-block; }
73
+ .feature-list li { padding: 8px 0; margin-bottom: 8px; display: flex; align-items: center; font-size: 1rem; }
74
+ .feature-list i { color: var(--accent-color); margin-right: 12px; font-size: 1.2rem; width: 20px; text-align: center; }
75
+ .feature-list li .fa-times-circle { color: #aaa; }
76
+ .feature-list li span { color: #777; }
 
 
77
 
78
+ .cta-button { display: inline-block; background-color: var(--primary-color); color: white; padding: 12px 25px; border-radius: 5px; text-decoration: none; font-weight: bold; transition: all 0.3s ease; margin: 10px 5px; border: none; cursor: pointer; font-size: 1rem; text-align: center; }
79
+ .cta-button:hover { background-color: var(--secondary-color); transform: translateY(-2px); box-shadow: 0 6px 12px rgba(0, 0, 0, 0.15); }
80
+ .cta-button i { margin-right: 8px; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
81
 
82
+ .upload-section, .upgrade-section { margin-top: 30px; padding-top: 20px; border-top: 1px solid #eee; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
83
 
84
+ footer { text-align: center; padding: 30px 0 20px 0; color: #666; font-size: 0.9rem; border-top: 1px solid #eee; margin-top: 40px; }
 
 
 
 
 
 
 
85
 
86
+ #solutionOutput { margin-top: 30px; text-align: left; display: none; }
87
+ #solution { background: #fff; padding: 20px; border-radius: 8px; text-align: left; line-height: 1.8; font-size: 1rem; border: 1px solid #eee; margin-top: 15px; overflow-x: hidden; /* Prevent container scroll, let MathJax handle internal scroll */ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
 
89
  /* Code Block Styling */
90
+ .code-section { margin: 20px 0; border-radius: 8px; overflow: hidden; box-shadow: 0 2px 5px rgba(0,0,0,0.1); }
91
+ .code-header { background-color: #343a40; color: white; padding: 10px 15px; font-size: 0.9em; font-family: 'Courier New', monospace; }
92
+ .code-content { margin: 0; padding: 15px; background-color: var(--code-bg); color: #e6e6e6; overflow-x: auto; font-family: 'Courier New', monospace; font-size: 0.9em; line-height: 1.5; }
93
+ .code-content pre, .code-content code { margin: 0; padding: 0; background: none; white-space: pre; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
94
 
95
  /* Output Block Styling */
96
+ .output-section { background-color: var(--output-bg); padding: 15px; margin: 15px 0; border-radius: 8px; border: 1px solid #d6e0e2; color: #333; font-family: 'Courier New', monospace; font-size: 0.9em; white-space: pre-wrap; overflow-x: auto; box-shadow: inset 0 1px 3px rgba(0,0,0,0.05); }
 
 
 
 
 
 
 
 
 
 
 
 
97
 
98
  /* Step Styling */
99
+ .step-section { margin: 20px 0; padding: 15px; background-color: #fdfdfd; border-left: 4px solid var(--accent-color); border-radius: 0 5px 5px 0; overflow-wrap: break-word; }
100
+ .step-section:first-child { margin-top: 0; }
101
+ .step-section:last-child { margin-bottom: 0; }
 
 
 
 
 
 
 
 
 
 
 
102
 
103
  /* Indicator Styling */
104
+ .indicator { display: flex; align-items: center; padding: 12px 15px; margin: 15px 0; border-radius: 8px; font-size: 0.95rem; border: 1px solid transparent; }
105
+ .indicator.thinking-indicator { background-color: #e3f2fd; color: #1565c0; border-color: #bde0fe; }
106
+ .indicator.executing-indicator { background-color: #ede7f6; color: #5e35b1; border-color: #d1c4e9; }
107
+ .indicator.answering-indicator { background-color: #e8f5e9; color: #2e7d32; border-color: #c8e6c9; }
108
+ .indicator i { margin-right: 10px; font-size: 1.1em; animation: pulse 1.5s infinite ease-in-out; }
109
+ @keyframes pulse { 0%, 100% { opacity: 0.7; } 50% { opacity: 1; } }
110
+
111
+ /* --- MathJax Specific Styles --- */
112
+ /* Ensure container allows for potential horizontal scrolling of large equations */
113
+ .mjx-chtml { /* Target MathJax CommonHTML output */
114
+ overflow-x: auto;
115
+ overflow-y: hidden;
116
+ }
117
+ /* Improve spacing for display math */
118
+ mjx-container[display="true"] {
119
+ display: block;
120
+ margin: 1em 0;
121
+ text-align: center;
122
+ }
123
+ /* Handle potential overflow within display math specifically */
124
+ mjx-container[display="true"] > .mjx-chtml {
125
+ overflow-x: auto;
 
 
 
 
 
 
 
 
 
126
  overflow-y: hidden;
127
+ padding: 0.2em 0; /* Add slight padding if scrollbar appears */
128
+ }
 
 
 
 
 
 
 
 
129
 
130
 
131
  /* --- Responsive Design --- */
132
+ @media (max-width: 992px) { .container { max-width: 90%; } }
 
 
 
 
 
 
 
 
133
  @media (max-width: 768px) {
134
+ .container { padding: 15px; }
135
+ .logo { font-size: 2rem; }
136
+ .subtitle { font-size: 1rem; }
137
+ h1 { font-size: 1.6rem; } h2 { font-size: 1.3rem; } h3 { font-size: 1.2rem; }
138
+ .content-box { padding: 20px; }
139
+ .feature-list { display: block; text-align: left; margin-left: auto; margin-right: auto; }
140
+ .upload-section, .upgrade-section { padding-top: 15px; }
141
+ #imagePreview { max-width: 100%; }
142
+ #imagePreview img { max-width: 100%; height: auto; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
143
  }
 
 
144
  @media (max-width: 576px) {
145
+ body { font-size: 15px; }
146
+ .logo { font-size: 1.8rem; }
147
+ .cta-button { display: block; width: 100%; margin: 10px 0; box-sizing: border-box; }
148
+ #solveButton { margin-top: 15px; }
149
+ .content-box { padding: 15px; }
150
+ #solution { padding: 15px; }
151
+ .code-header, .code-content, .output-section { font-size: 0.85em; }
152
+ /* Ensure MathJax equations can still scroll on small screens */
153
+ .mjx-chtml {
154
+ font-size: 0.95em; /* Slightly smaller math on mobile if needed */
 
 
 
 
155
  }
 
 
 
 
 
 
 
 
 
156
  }
 
157
  </style>
158
+
159
+ <!-- MathJax Configuration -->
160
+ <script>
161
+ window.MathJax = {
162
+ tex: {
163
+ inlineMath: [['$', '$'], ['\\(', '\\)']], // Define inline math delimiters
164
+ displayMath: [['$$', '$$'], ['\\[', '\\]']], // Define display math delimiters
165
+ processEscapes: true, // Process \$ etc.
166
+ tags: 'ams', // Use AMS numbering convention
167
+ macros: {
168
+ // Define custom macros here if needed
169
+ // R: "\\mathbb{R}",
170
+ // C: "\\mathbb{C}"
171
+ }
172
+ },
173
+ chtml: {
174
+ matchFontHeight: true, // Match surrounding text font size
175
+ scale: 1.0 // Adjust global scaling if necessary
176
+ },
177
+ options: {
178
+ ignoreHtmlClass: 'tex2jax_ignore', // Class to ignore processing inside
179
+ processHtmlClass: 'tex2jax_process', // Class to explicitly process inside
180
+ skipHtmlTags: ['script', 'noscript', 'style', 'textarea', 'pre', 'code'] // Tags to skip
181
+ },
182
+ startup: {
183
+ // Don't automatically typeset on page load, we'll trigger it manually
184
+ typeset: false,
185
+ // pageReady: () => {
186
+ // console.log("MathJax is ready to process content.");
187
+ // }
188
+ }
189
+ };
190
+ </script>
191
+ <!-- Load MathJax -->
192
+ <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script>
193
+ <!-- REMOVED KaTeX JS -->
194
+
195
  </head>
196
  <body>
197
  <div class="container">
 
209
  <li><i class="fas fa-check-circle"></i> Résolution de problèmes mathématiques</li>
210
  <li><i class="fas fa-check-circle"></i> 3 résolutions gratuites par jour</li>
211
  <li><i class="fas fa-check-circle"></i> Explication des étapes de résolution</li>
212
+ <li><i class="fas fa-check-circle"></i> Support du format LaTeX (MathJax)</li>
213
  <li><i class="fas fa-times-circle"></i> <span>Exécution de code Python (Pro)</span></li>
214
  <li><i class="fas fa-times-circle"></i> <span>Résolutions illimitées (Pro)</span></li>
215
  <li><i class="fas fa-times-circle"></i> <span>Support prioritaire (Pro)</span></li>
 
218
  <div class="upload-section">
219
  <h2>Soumettre un problème</h2>
220
  <form id="imageForm" enctype="multipart/form-data">
 
221
  <input type="file" id="imageInput" name="image" accept="image/*" style="display: none;">
 
222
  <button type="button" class="cta-button" onclick="document.getElementById('imageInput').click()">
223
  <i class="fas fa-upload"></i> Télécharger une image
224
  </button>
225
  </form>
226
 
227
  <p id="uploadStatus" style="margin-top: 15px; font-size: 0.9em; color: #555;"></p>
228
+ <div id="imagePreview" style="display: none; margin: 20px auto; max-width: 400px;">
229
  <img id="preview" style="display: block; width: 100%; border-radius: 8px; box-shadow: var(--box-shadow);">
230
  </div>
231
 
 
234
  </button>
235
  </div>
236
 
237
+ <div id="solutionOutput" style="display: none;">
238
  <h3>Solution Détaillée :</h3>
239
  <div id="loadingIndicator" class="indicator" style="display: none;">
240
  <!-- Content set by JS -->
 
242
  <div id="solution">
243
  <!-- Solution content will be injected here -->
244
  </div>
245
+ <div id="mathjaxProcessingIndicator" class="indicator answering-indicator" style="display: none; margin-top: 10px;">
246
+ <i class="fas fa-magic indicator-icon"></i><span>Mise en forme des équations...</span>
247
+ </div>
248
  </div>
249
 
250
 
 
264
  <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/highlight.min.js"></script>
265
  <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/languages/python.min.js"></script>
266
 
 
 
 
 
 
267
  <script>
268
  document.addEventListener('DOMContentLoaded', (event) => {
269
 
 
275
  const imageForm = document.getElementById('imageForm');
276
  const solutionOutput = document.getElementById('solutionOutput');
277
  const loadingIndicator = document.getElementById('loadingIndicator');
278
+ const solutionDiv = document.getElementById('solution');
279
+ const mathjaxIndicator = document.getElementById('mathjaxProcessingIndicator');
280
 
281
  imageInput.addEventListener('change', function(event) {
282
  const file = event.target.files[0];
 
285
  reader.onload = function(e) {
286
  preview.src = e.target.result;
287
  imagePreview.style.display = 'block';
288
+ solveButton.style.display = 'inline-block';
289
  uploadStatus.textContent = `Image sélectionnée : ${file.name}`;
290
+ solutionOutput.style.display = 'none';
291
+ solutionDiv.innerHTML = '';
292
+ mathjaxIndicator.style.display = 'none'; // Reset MathJax indicator
293
  }
294
  reader.readAsDataURL(file);
295
  } else {
 
296
  uploadStatus.textContent = '';
297
  imagePreview.style.display = 'none';
298
  solveButton.style.display = 'none';
 
307
  return;
308
  }
309
 
310
+ solutionOutput.style.display = 'block';
311
+ loadingIndicator.style.display = 'flex';
312
+ loadingIndicator.className = 'indicator thinking-indicator';
313
  loadingIndicator.innerHTML = '<i class="fas fa-brain indicator-icon"></i><span>Préparation de la requête...</span>';
314
+ solutionDiv.innerHTML = '';
315
+ mathjaxIndicator.style.display = 'none'; // Hide MathJax indicator
316
 
317
+ fetch('/solved', {
318
  method: 'POST',
319
  body: formData
320
  })
321
  .then(response => {
322
+ if (!response.ok) { throw new Error(`Erreur HTTP: ${response.status}`); }
323
+ if (!response.body) { throw new Error("La réponse ne contient pas de corps."); }
 
 
 
 
324
 
325
  const reader = response.body.getReader();
326
  const decoder = new TextDecoder();
327
  let buffer = '';
328
+ // No need for accumulatedContent if rendering at the end
329
 
330
  function processStream({ done, value }) {
331
  if (done) {
332
  loadingIndicator.style.display = 'none';
333
+
334
+ // Process any remaining buffer content
335
  if (buffer.trim()) {
336
+ // Append the final chunk - assumes it's a regular step
337
+ const finalStepDiv = document.createElement('div');
338
+ finalStepDiv.className = 'step-section';
339
+ finalStepDiv.innerHTML = buffer; // Use innerHTML
340
+ solutionDiv.appendChild(finalStepDiv);
 
 
341
  }
342
 
343
+ // --- Trigger MathJax Rendering ---
344
+ if (typeof MathJax !== 'undefined' && MathJax.typesetPromise) {
345
+ // Show MathJax processing indicator
346
+ mathjaxIndicator.style.display = 'flex';
347
+
348
+ MathJax.typesetPromise([solutionDiv]) // Target specific container
349
+ .then(() => {
350
+ console.log('MathJax typesetting complete.');
351
+ mathjaxIndicator.style.display = 'none'; // Hide indicator on success
352
+ // Optional: Re-apply highlight.js if MathJax altered code blocks (unlikely but possible)
353
+ // document.querySelectorAll('#solution pre code').forEach((block) => {
354
+ // hljs.highlightElement(block);
355
+ // });
356
+ })
357
+ .catch((err) => {
358
+ console.error('Erreur de rendu MathJax:', err);
359
+ mathjaxIndicator.style.display = 'none'; // Hide indicator on error
360
+ solutionDiv.innerHTML += `<div style="color: orange; margin-top: 10px;">Erreur lors de l'affichage des formules mathématiques (MathJax).</div>`;
361
+ })
362
+ .finally(() => {
363
+ // Scroll to the solution area after rendering attempt
364
+ solutionOutput.scrollIntoView({ behavior: 'smooth', block: 'start' });
365
+ });
366
+ } else {
367
+ console.warn("MathJax n'est pas prêt ou n'a pas pu être chargé.");
368
+ solutionDiv.innerHTML += `<div style="color: orange; margin-top: 10px;">Impossible de charger le module d'affichage mathématique (MathJax).</div>`;
369
+ // Scroll anyway
370
+ solutionOutput.scrollIntoView({ behavior: 'smooth', block: 'start' });
371
  }
372
+ return; // End of stream processing
373
+ } // end if(done)
 
 
 
 
 
 
374
 
375
  buffer += decoder.decode(value, { stream: true });
 
376
  const parts = buffer.split('\n\n');
377
+ buffer = parts.pop() || '';
378
 
379
  parts.forEach(part => {
380
  if (part.startsWith('data: ')) {
381
  try {
382
+ const jsonData = JSON.parse(part.substring(6));
 
383
 
384
+ // Update Indicator (same as before)
385
  if (jsonData.mode) {
386
+ loadingIndicator.style.display = 'flex';
387
  if (jsonData.mode === 'thinking') {
388
  loadingIndicator.className = 'indicator thinking-indicator';
389
  loadingIndicator.innerHTML = '<i class="fas fa-brain indicator-icon"></i><span>Je réfléchis au problème...</span>';
 
394
  loadingIndicator.className = 'indicator executing-indicator';
395
  loadingIndicator.innerHTML = '<i class="fas fa-code indicator-icon"></i><span>Exécution du code Python...</span>';
396
  } else if (jsonData.mode === 'code_result') {
397
+ loadingIndicator.className = 'indicator executing-indicator';
398
  loadingIndicator.innerHTML = '<i class="fas fa-terminal indicator-icon"></i><span>Traitement des résultats du code...</span>';
399
  }
400
  }
401
 
402
+ // Append Content (same as before, but NO LaTeX rendering here)
403
  if (jsonData.content) {
404
  const content = jsonData.content;
405
  let elementToAppend;
 
410
  elementToAppend.className = 'code-section';
411
  elementToAppend.innerHTML = `
412
  <div class="code-header">Code Python</div>
413
+ <div class="code-content"><pre><code class="language-python tex2jax_ignore">${escapeHtml(code)}</code></pre></div>
414
+ `; // Added tex2jax_ignore to prevent MathJax trying to process code
415
  solutionDiv.appendChild(elementToAppend);
 
416
  const codeBlock = elementToAppend.querySelector('code');
417
+ if (codeBlock && hljs) { hljs.highlightElement(codeBlock); }
418
+
 
419
  } else if (content.startsWith('Résultat d\'exécution:\n```')) {
420
  const output = content.replace(/^Résultat d'exécution:\n```\n?([\s\S]*?)\n?```$/, '$1');
421
  elementToAppend = document.createElement('div');
422
+ elementToAppend.className = 'output-section tex2jax_ignore'; // Ignore MathJax here too
423
+ elementToAppend.textContent = output;
424
  solutionDiv.appendChild(elementToAppend);
425
+
426
  } else {
427
+ // Regular step, potentially with LaTeX delimiters inside
428
  elementToAppend = document.createElement('div');
429
  elementToAppend.className = 'step-section';
430
+ elementToAppend.innerHTML = content; // Add raw content
431
+ solutionDiv.appendChild(elementToAppend);
 
 
432
  }
433
  }
434
 
 
437
  errorDiv.style.cssText = 'color: red; margin: 15px 0; padding: 10px; background: #ffeeee; border: 1px solid #ffdddd; border-radius: 5px;';
438
  errorDiv.textContent = `Erreur: ${jsonData.error}`;
439
  solutionDiv.appendChild(errorDiv);
440
+ loadingIndicator.style.display = 'none';
441
  }
442
 
443
  } catch (e) {
444
  console.error('Erreur de parsing JSON ou traitement:', e, 'Part:', part);
 
 
 
 
 
445
  }
446
  }
447
  });
448
 
449
+ // Scroll down as content arrives
 
450
  solutionDiv.lastChild?.scrollIntoView({ behavior: 'smooth', block: 'end' });
451
 
 
452
  // Continue reading the stream
453
  return reader.read().then(processStream);
454
+ } // end processStream function
455
 
456
  // Start processing the stream
457
  return reader.read().then(processStream);
458
  })
459
  .catch(error => {
460
  console.error('Erreur lors de la requête fetch:', error);
461
+ loadingIndicator.style.display = 'none';
462
  const errorDiv = document.createElement('div');
463
  errorDiv.style.cssText = 'color: red; margin: 15px 0; padding: 10px; background: #ffeeee; border: 1px solid #ffdddd; border-radius: 5px;';
464
  errorDiv.textContent = `Erreur de communication avec le serveur: ${error.message}`;
465
  solutionDiv.appendChild(errorDiv);
466
+ mathjaxIndicator.style.display = 'none';
467
  solutionOutput.scrollIntoView({ behavior: 'smooth', block: 'start' });
468
  });
469
  });
470
 
 
471
  function escapeHtml(unsafe) {
472
  return unsafe
473
  .replace(/&/g, "&")
 
476
  .replace(/"/g, """)
477
  .replace(/'/g, "'");
478
  }
479
+ });
480
  </script>
481
 
482
  </body>