Docfile commited on
Commit
1bfe49c
·
verified ·
1 Parent(s): 57e1e1f

Update templates/maj.html

Browse files
Files changed (1) hide show
  1. templates/maj.html +426 -350
templates/maj.html CHANGED
@@ -5,7 +5,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.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
  <style>
10
  :root {
11
  --primary-color: #4a6fa5;
@@ -14,10 +15,11 @@
14
  --background-color: #f8f9fa;
15
  --text-color: #333;
16
  --box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
17
- --code-bg: #2c323c;
 
18
  --output-bg: #f1f8f9;
19
  }
20
-
21
  body {
22
  font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
23
  line-height: 1.6;
@@ -26,66 +28,76 @@
26
  background-color: var(--background-color);
27
  color: var(--text-color);
28
  }
29
-
30
  .container {
31
  max-width: 1000px;
32
  margin: 0 auto;
33
  padding: 20px;
34
  }
35
-
36
  header {
37
  text-align: center;
38
  padding: 20px 0;
39
  margin-bottom: 30px;
40
  }
41
-
42
  .logo {
43
  font-size: 2.5rem;
44
  font-weight: bold;
45
  color: var(--primary-color);
46
  margin-bottom: 10px;
47
  }
48
-
49
  .subtitle {
50
  font-size: 1.2rem;
51
  color: var(--secondary-color);
52
  margin-bottom: 20px;
53
  }
54
-
55
  .content-box {
56
  background-color: white;
57
  border-radius: 10px;
58
  box-shadow: var(--box-shadow);
59
  padding: 30px;
60
  margin-bottom: 30px;
61
- text-align: center;
62
  }
63
-
64
- h1 {
65
  color: var(--primary-color);
66
  margin-top: 0;
 
67
  }
68
-
 
 
 
69
  .feature-list {
70
  list-style-type: none;
71
  padding: 0;
72
- margin: 30px 0;
73
  text-align: left;
 
74
  }
75
-
76
  .feature-list li {
77
  padding: 10px 0;
78
  margin-bottom: 10px;
79
  display: flex;
80
  align-items: center;
81
  }
82
-
83
  .feature-list i {
84
  color: var(--accent-color);
85
  margin-right: 10px;
86
  font-size: 1.2rem;
 
 
 
 
 
87
  }
88
-
89
  .cta-button {
90
  display: inline-block;
91
  background-color: var(--primary-color);
@@ -98,157 +110,194 @@
98
  margin: 20px 10px;
99
  border: none;
100
  cursor: pointer;
 
101
  }
102
-
103
  .cta-button:hover {
104
  background-color: var(--secondary-color);
105
  transform: translateY(-2px);
106
  box-shadow: 0 6px 12px rgba(0, 0, 0, 0.15);
107
  }
108
-
 
 
 
 
 
 
109
  .upgrade-section {
110
- margin-top: 30px;
111
- padding: 20px;
112
  border-top: 1px solid #ddd;
 
113
  }
114
-
115
  footer {
116
  text-align: center;
117
  padding: 20px 0;
118
  color: #666;
119
  font-size: 0.9rem;
120
  }
121
-
 
 
 
 
 
122
  #solution {
123
  background: #fff;
124
  padding: 20px;
125
  border-radius: 8px;
126
- text-align: left;
 
127
  line-height: 1.8;
128
  font-size: 16px;
 
 
129
  }
130
-
131
- .code-section {
132
- margin: 20px 0;
133
- border-radius: 8px;
134
- overflow: hidden;
135
- box-shadow: 0 2px 5px rgba(0,0,0,0.1);
136
- }
137
-
138
- .code-header {
139
- background-color: #343a40;
140
- color: white;
141
- padding: 8px 15px;
142
- font-size: 14px;
143
- font-family: 'Courier New', monospace;
144
- display: flex;
145
- justify-content: space-between;
146
- align-items: center;
147
- }
148
-
149
- .code-content {
150
- margin: 0;
151
- padding: 15px;
152
  background-color: var(--code-bg);
153
- color: #e6e6e6;
154
- overflow-x: auto;
155
- font-family: 'Courier New', monospace;
156
- font-size: 14px;
157
- line-height: 1.5;
158
- }
159
-
160
- .output-section {
161
- background-color: var(--output-bg);
162
- padding: 15px;
163
- border-radius: 0 0 8px 8px;
164
- border-top: 1px solid #ddd;
165
- color: #333;
166
- font-family: 'Courier New', monospace;
167
- font-size: 14px;
168
- white-space: pre-wrap;
169
- overflow-x: auto;
170
- }
171
-
172
- .step-section {
173
- margin: 25px 0;
174
  padding: 15px;
175
- background-color: #f9f9f9;
176
- border-left: 4px solid var(--primary-color);
177
- border-radius: 0 8px 8px 0;
178
- overflow-x: auto; /* Pour les formules LaTeX larges */
179
- }
180
-
181
- .latex-display {
182
  overflow-x: auto;
183
- padding: 10px 0;
184
- margin: 15px 0;
185
- text-align: center;
186
  }
187
-
188
- .thinking-indicator, .executing-indicator, .answering-indicator {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
189
  display: flex;
190
  align-items: center;
191
- padding: 10px;
192
- margin: 10px 0;
 
193
  border-radius: 8px;
194
- font-size: 0.9rem;
195
- }
196
-
197
- .thinking-indicator {
198
  background-color: #e3f2fd;
199
  color: #1565c0;
 
200
  }
201
-
202
- .executing-indicator {
203
- background-color: #ede7f6;
204
- color: #5e35b1;
205
- }
206
-
207
- .answering-indicator {
208
- background-color: #e8f5e9;
209
- color: #2e7d32;
210
  }
211
-
212
- .indicator-icon {
213
- margin-right: 10px;
214
- animation: pulse 1.5s infinite ease-in-out;
215
  }
216
-
217
- @keyframes pulse {
218
- 0% { opacity: 0.6; }
219
- 50% { opacity: 1; }
220
- 100% { opacity: 0.6; }
221
- }
222
-
223
- /* Styles spécifiques à MathJax */
224
- .MathJax {
225
  overflow-x: auto !important;
226
  overflow-y: hidden !important;
227
  max-width: 100% !important;
 
 
 
 
228
  }
229
-
230
- .mjx-chtml {
231
- overflow-x: auto !important;
232
- overflow-y: hidden !important;
233
- max-width: 100% !important;
234
  }
235
-
236
- mjx-container {
237
- overflow-x: auto !important;
238
- overflow-y: hidden !important;
239
- max-width: 100% !important;
240
- min-width: 0 !important;
241
- padding: 2px 0;
242
- }
243
-
244
  @media (max-width: 768px) {
245
  .container {
246
  padding: 15px;
247
  }
248
-
249
  .content-box {
250
  padding: 20px;
251
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
252
  }
253
  </style>
254
  </head>
@@ -258,281 +307,308 @@
258
  <div class="logo">Math Solver</div>
259
  <div class="subtitle">La solution intelligente pour vos problèmes mathématiques</div>
260
  </header>
261
-
262
  <div class="content-box">
263
  <h1>Version Gratuite</h1>
264
- <p>Vous utilisez actuellement la version gratuite de Math Solver qui vous permet de résoudre 3 problèmes par jour.</p>
265
-
266
- <div class="feature-list">
267
- <h2>Fonctionnalités disponibles :</h2>
268
- <ul class="feature-list">
269
- <li><i class="fas fa-check-circle"></i> Résolution de problèmes mathématiques basiques</li>
270
- <li><i class="fas fa-check-circle"></i> 3 résolutions gratuites par jour</li>
271
- <li><i class="fas fa-check-circle"></i> Explication des étapes de résolution</li>
272
- <li><i class="fas fa-times-circle"></i> <span style="color: #999;">Mode d'exécution de code avancé (version Pro)</span></li>
273
- <li><i class="fas fa-times-circle"></i> <span style="color: #999;">Résolutions illimitées (version Pro)</span></li>
274
- <li><i class="fas fa-times-circle"></i> <span style="color: #999;">Support prioritaire (version Pro)</span></li>
275
- </ul>
276
- </div>
277
-
278
  <div class="upload-section">
 
279
  <form id="imageForm" enctype="multipart/form-data">
280
  <input type="file" id="imageInput" name="image" accept="image/*" style="display: none;">
281
- <button type="button" id="uploadButton" class="cta-button" onclick="document.getElementById('imageInput').click()">
282
  <i class="fas fa-upload"></i> Télécharger une image
283
  </button>
284
  </form>
285
-
286
  <p id="uploadStatus"></p>
287
- <div id="imagePreview" style="display: none; margin: 20px auto; max-width: 500px;">
288
- <img id="preview" style="width: 100%; border-radius: 8px; box-shadow: var(--box-shadow);">
289
  </div>
290
-
291
- <button id="solveButton" class="cta-button" style="display: none; background-color: var(--secondary-color);">
292
  <i class="fas fa-calculator"></i> Résoudre ce problème
293
  </button>
294
  </div>
295
-
296
- <div id="solutionOutput" style="margin-top: 30px; text-align: left; display: none;">
 
 
297
  <h3>Solution :</h3>
298
- <div id="loadingIndicator" class="thinking-indicator" style="display: none;">
299
- <i class="fas fa-brain indicator-icon"></i>
300
- <span>Je réfléchis au problème...</span>
301
  </div>
302
- <div id="solution" style="background: #fff; padding: 20px; border-radius: 8px;"></div>
 
303
  </div>
304
-
 
 
 
 
 
 
 
 
 
 
 
 
 
305
  <div class="upgrade-section">
306
- <h2>Besoin de plus de puissance ?</h2>
307
  <p>Passez à la version Pro pour des fonctionnalités avancées et des résolutions illimitées.</p>
308
- <a href="#" class="cta-button">Passer à la version Pro</a>
309
  </div>
310
  </div>
311
-
312
  <footer>
313
- <p>&copy; 2025 Math Solver. Tous droits réservés.</p>
314
  </footer>
315
  </div>
316
-
317
- <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/highlight.min.js"></script>
318
- <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/languages/python.min.js"></script>
319
-
 
 
 
 
 
 
320
  <!-- MathJax Configuration -->
321
  <script>
322
- window.MathJax = {
323
- tex: {
324
- inlineMath: [['$', '$'], ['\\(', '\\)']],
325
- displayMath: [['$$', '$$'], ['\\[', '\\]']],
326
- processEscapes: true,
327
- processEnvironments: true,
328
- packages: {'[+]': ['ams', 'noerrors', 'physics', 'cancel', 'color', 'mhchem', 'mathtools']}
329
- },
330
- options: {
331
- enableMenu: false,
332
- renderActions: {
333
- addMenu: [], // Disable menu
334
- checkLoading: []
335
- },
336
- ignoreHtmlClass: 'code-content',
337
- processHtmlClass: 'step-section|latex-display',
338
- skipHtmlTags: ['script', 'noscript', 'style', 'textarea', 'pre', 'code']
339
- },
340
- startup: {
341
- pageReady: function() {
342
- return MathJax.startup.defaultPageReady();
343
- }
344
- },
345
- loader: {
346
- load: ['[tex]/ams', '[tex]/noerrors', '[tex]/physics', '[tex]/cancel', '[tex]/color', '[tex]/mhchem', '[tex]/mathtools']
347
- },
348
- svg: {
349
- fontCache: 'global'
350
- }
351
- };
352
  </script>
353
- <script id="MathJax-script" async src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/3.2.2/es5/tex-svg.js"></script>
354
-
 
355
  <script>
356
- // Variables to manage MathJax rendering state
357
- let mathJaxQueue = [];
358
- let isProcessingMathQueue = false;
359
-
360
- // Process MathJax queue with delay to avoid blocking UI
361
- async function processMathJaxQueue() {
362
- if (isProcessingMathQueue || mathJaxQueue.length === 0) return;
363
-
364
- isProcessingMathQueue = true;
365
- const element = mathJaxQueue.shift();
366
-
367
- try {
368
- await MathJax.typesetPromise([element]);
369
- } catch (e) {
370
- console.error('MathJax typesetting failed:', e);
371
- }
372
-
373
- isProcessingMathQueue = false;
374
- setTimeout(processMathJaxQueue, 100); // Small delay to allow UI updates
375
- }
376
-
377
- function addToMathJaxQueue(element) {
378
- mathJaxQueue.push(element);
379
- processMathJaxQueue();
380
- }
381
-
382
- document.getElementById('imageInput').addEventListener('change', function(event) {
383
  const file = event.target.files[0];
384
  if (file) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
385
  const reader = new FileReader();
386
  reader.onload = function(e) {
387
- document.getElementById('preview').src = e.target.result;
388
- document.getElementById('imagePreview').style.display = 'block';
389
- document.getElementById('solveButton').style.display = 'inline-block';
390
- document.getElementById('uploadStatus').textContent = `Image sélectionnée : ${file.name}`;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
391
  }
392
  reader.readAsDataURL(file);
 
 
 
 
 
 
393
  }
394
  });
395
-
396
- document.getElementById('solveButton').addEventListener('click', function() {
397
- const formData = new FormData(document.getElementById('imageForm'));
398
- const solutionOutput = document.getElementById('solutionOutput');
399
- const loadingIndicator = document.getElementById('loadingIndicator');
400
- const solution = document.getElementById('solution');
401
-
402
- solutionOutput.style.display = 'block';
403
- loadingIndicator.style.display = 'flex';
404
- solution.innerHTML = '';
405
-
406
- // Reset MathJax queue
407
- mathJaxQueue = [];
408
- isProcessingMathQueue = false;
409
-
410
- fetch('/solved', {
 
 
 
 
 
411
  method: 'POST',
412
  body: formData
413
  })
414
  .then(response => {
415
- const reader = response.body.getReader();
416
- const decoder = new TextDecoder();
417
- let buffer = '';
418
-
419
- function processStream({ done, value }) {
420
- if (done) {
421
- loadingIndicator.style.display = 'none';
422
-
423
- // Final MathJax render at the end
424
- try {
425
- addToMathJaxQueue(solution);
426
- } catch (e) {
427
- console.error('Error in final MathJax rendering:', e);
428
- }
429
- return;
430
- }
431
-
432
- buffer += decoder.decode(value, { stream: true });
433
- const lines = buffer.split('\n\n');
434
- buffer = lines.pop(); // Keep the incomplete chunk for next time
435
-
436
- for (const line of lines) {
437
- if (line.startsWith('data: ')) {
438
- try {
439
- const data = JSON.parse(line.substr(6));
440
-
441
- if (data.mode === 'thinking') {
442
- loadingIndicator.className = 'thinking-indicator';
443
- loadingIndicator.innerHTML = '<i class="fas fa-brain indicator-icon"></i><span>Je réfléchis au problème...</span>';
444
- } else if (data.mode === 'answering') {
445
- loadingIndicator.className = 'answering-indicator';
446
- loadingIndicator.innerHTML = '<i class="fas fa-pencil-alt indicator-icon"></i><span>Rédaction de la solution...</span>';
447
- } else if (data.mode === 'executing_code') {
448
- loadingIndicator.className = 'executing-indicator';
449
- loadingIndicator.innerHTML = '<i class="fas fa-code indicator-icon"></i><span>Exécution de code pour la résolution...</span>';
450
- } else if (data.mode === 'code_result') {
451
- loadingIndicator.className = 'executing-indicator';
452
- loadingIndicator.innerHTML = '<i class="fas fa-terminal indicator-icon"></i><span>Traitement des résultats...</span>';
453
- }
454
-
455
- if (data.content) {
456
- const content = data.content;
457
-
458
- // Create a container for this new content
459
- const contentContainer = document.createElement('div');
460
-
461
- // Detect if this is code or output and format appropriately
462
- if (content.includes('```python')) {
463
- // This is code
464
- const codeHtml = content.replace(/```python\n([\s\S]*?)\n```/g, function(match, p1) {
465
- return `<div class="code-section">
466
- <div class="code-header">
467
- <span>Code Python</span>
468
- </div>
469
- <pre class="code-content"><code class="language-python">${p1}</code></pre>
470
- </div>`;
471
- });
472
- contentContainer.innerHTML = codeHtml;
473
- solution.appendChild(contentContainer);
474
-
475
- // Apply syntax highlighting
476
- contentContainer.querySelectorAll('pre code').forEach((block) => {
477
- hljs.highlightElement(block);
478
- });
479
- }
480
- else if (content.includes('Résultat d\'exécution:')) {
481
- // This is code output
482
- const outputHtml = content.replace(/Résultat d'exécution:\n```\n([\s\S]*?)\n```/g, function(match, p1) {
483
- return `<div class="output-section">${p1}</div>`;
484
- });
485
- contentContainer.innerHTML = outputHtml;
486
- solution.appendChild(contentContainer);
487
- }
488
- else {
489
- // Regular text, might contain LaTeX
490
- contentContainer.className = 'step-section';
491
- contentContainer.innerHTML = content;
492
- solution.appendChild(contentContainer);
493
-
494
- // Add to MathJax rendering queue
495
- addToMathJaxQueue(contentContainer);
496
- }
497
- }
498
-
499
- if (data.error) {
500
- const errorDiv = document.createElement('div');
501
- errorDiv.style.color = 'red';
502
- errorDiv.style.margin = '15px 0';
503
- errorDiv.style.padding = '10px';
504
- errorDiv.style.background = '#ffeeee';
505
- errorDiv.style.borderRadius = '5px';
506
- errorDiv.textContent = `Erreur: ${data.error}`;
507
- solution.appendChild(errorDiv);
508
- loadingIndicator.style.display = 'none';
509
- }
510
- } catch (e) {
511
- console.error('Error parsing JSON:', e, line);
512
- }
513
- }
514
  }
515
-
516
- // Scroll to bottom automatically
517
- window.scrollTo(0, document.body.scrollHeight);
518
-
519
- return reader.read().then(processStream);
 
 
 
 
 
 
 
 
 
 
 
520
  }
521
-
522
- return reader.read().then(processStream);
523
  })
524
  .catch(error => {
525
- const errorDiv = document.createElement('div');
526
- errorDiv.style.color = 'red';
527
- errorDiv.style.margin = '15px 0';
528
- errorDiv.style.padding = '10px';
529
- errorDiv.style.background = '#ffeeee';
530
- errorDiv.style.borderRadius = '5px';
531
- errorDiv.textContent = `Erreur de connexion: ${error}`;
532
- solution.appendChild(errorDiv);
533
  loadingIndicator.style.display = 'none';
 
 
 
 
 
 
 
 
 
 
534
  });
535
  });
 
 
 
 
 
 
 
 
 
 
 
536
  </script>
537
  </body>
538
  </html>
 
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.0.0-beta3/css/all.min.css" rel="stylesheet">
8
+ <!-- Use a CDN that hosts the specific atom-one-dark theme -->
9
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/atom-one-dark.min.css">
10
  <style>
11
  :root {
12
  --primary-color: #4a6fa5;
 
15
  --background-color: #f8f9fa;
16
  --text-color: #333;
17
  --box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
18
+ --code-bg: #282c34; /* atom-one-dark background */
19
+ --code-text: #abb2bf; /* atom-one-dark default text */
20
  --output-bg: #f1f8f9;
21
  }
22
+
23
  body {
24
  font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
25
  line-height: 1.6;
 
28
  background-color: var(--background-color);
29
  color: var(--text-color);
30
  }
31
+
32
  .container {
33
  max-width: 1000px;
34
  margin: 0 auto;
35
  padding: 20px;
36
  }
37
+
38
  header {
39
  text-align: center;
40
  padding: 20px 0;
41
  margin-bottom: 30px;
42
  }
43
+
44
  .logo {
45
  font-size: 2.5rem;
46
  font-weight: bold;
47
  color: var(--primary-color);
48
  margin-bottom: 10px;
49
  }
50
+
51
  .subtitle {
52
  font-size: 1.2rem;
53
  color: var(--secondary-color);
54
  margin-bottom: 20px;
55
  }
56
+
57
  .content-box {
58
  background-color: white;
59
  border-radius: 10px;
60
  box-shadow: var(--box-shadow);
61
  padding: 30px;
62
  margin-bottom: 30px;
63
+ /* Removed text-align: center; allowing content to align left */
64
  }
65
+
66
+ h1, h2, h3 {
67
  color: var(--primary-color);
68
  margin-top: 0;
69
+ text-align: center; /* Center headings specifically */
70
  }
71
+ h2 {
72
+ margin-top: 1.5em; /* Add space above h2 */
73
+ }
74
+
75
  .feature-list {
76
  list-style-type: none;
77
  padding: 0;
78
+ margin: 30px auto; /* Center list container */
79
  text-align: left;
80
+ max-width: 500px; /* Constrain width for better readability */
81
  }
82
+
83
  .feature-list li {
84
  padding: 10px 0;
85
  margin-bottom: 10px;
86
  display: flex;
87
  align-items: center;
88
  }
89
+
90
  .feature-list i {
91
  color: var(--accent-color);
92
  margin-right: 10px;
93
  font-size: 1.2rem;
94
+ width: 20px; /* Ensure consistent icon alignment */
95
+ text-align: center;
96
+ }
97
+ .feature-list i.fa-times-circle {
98
+ color: #aaa; /* Muted color for disabled features */
99
  }
100
+
101
  .cta-button {
102
  display: inline-block;
103
  background-color: var(--primary-color);
 
110
  margin: 20px 10px;
111
  border: none;
112
  cursor: pointer;
113
+ font-size: 1rem;
114
  }
115
+
116
  .cta-button:hover {
117
  background-color: var(--secondary-color);
118
  transform: translateY(-2px);
119
  box-shadow: 0 6px 12px rgba(0, 0, 0, 0.15);
120
  }
121
+ .cta-button:disabled {
122
+ background-color: #ccc;
123
+ cursor: not-allowed;
124
+ transform: none;
125
+ box-shadow: none;
126
+ }
127
+
128
  .upgrade-section {
129
+ margin-top: 40px;
130
+ padding-top: 20px;
131
  border-top: 1px solid #ddd;
132
+ text-align: center;
133
  }
134
+
135
  footer {
136
  text-align: center;
137
  padding: 20px 0;
138
  color: #666;
139
  font-size: 0.9rem;
140
  }
141
+
142
+ #solutionOutput {
143
+ margin-top: 30px;
144
+ text-align: left; /* Ensure solution text aligns left */
145
+ }
146
+
147
  #solution {
148
  background: #fff;
149
  padding: 20px;
150
  border-radius: 8px;
151
+ border: 1px solid #eee; /* Add a subtle border */
152
+ margin-top: 15px;
153
  line-height: 1.8;
154
  font-size: 16px;
155
+ overflow-wrap: break-word; /* Wrap long lines */
156
+ white-space: pre-wrap; /* Preserve whitespace and newlines from response */
157
  }
158
+
159
+ /* Styling for Markdown code blocks generated by Python */
160
+ #solution pre {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
161
  background-color: var(--code-bg);
162
+ color: var(--code-text);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
163
  padding: 15px;
164
+ border-radius: 5px;
 
 
 
 
 
 
165
  overflow-x: auto;
166
+ margin: 15px 0; /* Add spacing around code blocks */
167
+ box-shadow: inset 0 1px 3px rgba(0,0,0,0.2);
 
168
  }
169
+
170
+ #solution pre code {
171
+ font-family: 'Courier New', Courier, monospace;
172
+ font-size: 14px;
173
+ line-height: 1.5;
174
+ white-space: pre; /* Ensure pre formatting is respected within code tag */
175
+ background: none; /* Remove double background */
176
+ padding: 0; /* Remove double padding */
177
+ }
178
+
179
+ /* Styling for the execution result block (if Python wraps it) */
180
+ #solution .output-section { /* Use if Python wraps result in <div class="output-section"> */
181
+ background-color: var(--output-bg);
182
+ padding: 15px;
183
+ border-radius: 5px;
184
+ border: 1px solid #ddd;
185
+ color: #333;
186
+ font-family: 'Courier New', monospace;
187
+ font-size: 14px;
188
+ white-space: pre-wrap;
189
+ overflow-x: auto;
190
+ margin: 15px 0;
191
+ }
192
+ /* Styling for result block if using Markdown ``` only */
193
+ #solution pre code:not(.language-python) {
194
+ /* Style non-Python code blocks (likely execution results) differently */
195
+ background-color: var(--output-bg);
196
+ color: #333;
197
+ display: block; /* Make it block level for padding */
198
+ padding: 10px;
199
+ border-radius: 4px;
200
+ border: 1px solid #eee;
201
+ }
202
+ /* Identify result block by preceding text */
203
+ #solution strong:contains("Résultat d'exécution:") + pre {
204
+ /* Style the <pre> immediately following the specific strong tag */
205
+ border: 1px dashed #ccc;
206
+ background-color: #fdfdfd;
207
+ }
208
+ #solution strong:contains("Résultat d'exécution:") + pre code {
209
+ /* Style the <code> inside that specific <pre> */
210
+ background-color: transparent; /* No extra background */
211
+ color: #222;
212
+ }
213
+
214
+
215
+ /* Removed step-section styling as structure is now simpler */
216
+
217
+ /* Removed indicator styles (thinking, executing etc.) */
218
+ .loading-indicator {
219
  display: flex;
220
  align-items: center;
221
+ justify-content: center; /* Center content */
222
+ padding: 15px;
223
+ margin: 20px 0;
224
  border-radius: 8px;
225
+ font-size: 1rem;
 
 
 
226
  background-color: #e3f2fd;
227
  color: #1565c0;
228
+ border: 1px solid #bbdefb;
229
  }
230
+ .loading-indicator i {
231
+ margin-right: 10px;
232
+ animation: spin 1.5s linear infinite;
 
 
 
 
 
 
233
  }
234
+ @keyframes spin {
235
+ 0% { transform: rotate(0deg); }
236
+ 100% { transform: rotate(360deg); }
 
237
  }
238
+
239
+
240
+ /* MathJax specific styles for better overflow handling */
241
+ .MathJax, mjx-container {
 
 
 
 
 
242
  overflow-x: auto !important;
243
  overflow-y: hidden !important;
244
  max-width: 100% !important;
245
+ min-width: 0 !important; /* Prevent minimum width issues */
246
+ padding: 5px 0; /* Add a bit of vertical padding */
247
+ display: block !important; /* Ensure it behaves like a block */
248
+ text-align: left !important; /* Align LaTeX left by default */
249
  }
250
+ mjx-container[display="true"] {
251
+ display: block !important;
252
+ margin: 1em 0 !important; /* Spacing for display math */
253
+ text-align: center !important; /* Center display math */
 
254
  }
255
+
 
 
 
 
 
 
 
 
256
  @media (max-width: 768px) {
257
  .container {
258
  padding: 15px;
259
  }
 
260
  .content-box {
261
  padding: 20px;
262
  }
263
+ #solution pre {
264
+ padding: 10px;
265
+ }
266
+ #solution pre code {
267
+ font-size: 13px;
268
+ }
269
+ }
270
+ .upload-section {
271
+ text-align: center; /* Center upload elements */
272
+ }
273
+ #imagePreview {
274
+ display: none;
275
+ margin: 20px auto;
276
+ max-width: 90%; /* Responsive max width */
277
+ max-height: 400px; /* Limit preview height */
278
+ }
279
+ #preview {
280
+ max-width: 100%;
281
+ max-height: 400px; /* Match container height */
282
+ height: auto; /* Maintain aspect ratio */
283
+ width: auto; /* Maintain aspect ratio */
284
+ border-radius: 8px;
285
+ box-shadow: var(--box-shadow);
286
+ border: 1px solid #ddd;
287
+ }
288
+ #uploadStatus {
289
+ margin-top: 15px;
290
+ font-size: 0.9em;
291
+ color: #555;
292
+ }
293
+ .error-message {
294
+ color: red;
295
+ background-color: #ffeeee;
296
+ border: 1px solid red;
297
+ padding: 10px;
298
+ margin: 15px 0;
299
+ border-radius: 5px;
300
+ text-align: left;
301
  }
302
  </style>
303
  </head>
 
307
  <div class="logo">Math Solver</div>
308
  <div class="subtitle">La solution intelligente pour vos problèmes mathématiques</div>
309
  </header>
310
+
311
  <div class="content-box">
312
  <h1>Version Gratuite</h1>
313
+ <p style="text-align: center;">Vous utilisez actuellement la version gratuite de Math Solver.</p> <!-- Centered paragraph -->
314
+
 
 
 
 
 
 
 
 
 
 
 
 
315
  <div class="upload-section">
316
+ <h2>Soumettez votre problème</h2>
317
  <form id="imageForm" enctype="multipart/form-data">
318
  <input type="file" id="imageInput" name="image" accept="image/*" style="display: none;">
319
+ <button type="button" id="uploadButton" class="cta-button">
320
  <i class="fas fa-upload"></i> Télécharger une image
321
  </button>
322
  </form>
 
323
  <p id="uploadStatus"></p>
324
+ <div id="imagePreview">
325
+ <img id="preview" alt="Aperçu de l'image">
326
  </div>
327
+ <button id="solveButton" class="cta-button" style="display: none; background-color: var(--secondary-color);" disabled>
 
328
  <i class="fas fa-calculator"></i> Résoudre ce problème
329
  </button>
330
  </div>
331
+
332
+ <!-- Solution Output Area -->
333
+ <div id="solutionOutput" style="display: none;"> <!-- Keep hidden initially -->
334
+ <hr style="margin: 30px 0;"> <!-- Separator -->
335
  <h3>Solution :</h3>
336
+ <div id="loadingIndicator" class="loading-indicator" style="display: none;">
337
+ <i class="fas fa-spinner"></i> <!-- Changed to spinner -->
338
+ <span>Résolution en cours...</span>
339
  </div>
340
+ <div id="solution"></div> <!-- Solution content will appear here -->
341
+ <div id="errorContainer" class="error-message" style="display: none;"></div> <!-- Error messages -->
342
  </div>
343
+
344
+
345
+ <div class="feature-list">
346
+ <h2>Fonctionnalités :</h2>
347
+ <ul> <!-- Removed ul class -->
348
+ <li><i class="fas fa-check-circle"></i> Résolution de problèmes mathématiques</li>
349
+ <li><i class="fas fa-check-circle"></i> Explication des étapes (si fournies par l'IA)</li>
350
+ <li><i class="fas fa-check-circle"></i> Utilisation de LaTeX pour les formules</li>
351
+ <li><i class="fas fa-check-circle"></i> Exécution de code Python pour calculs</li>
352
+ <li><i class="fas fa-times-circle"></i> <span style="color: #999;">Résolutions illimitées (version Pro)</span></li>
353
+ <li><i class="fas fa-times-circle"></i> <span style="color: #999;">Support prioritaire (version Pro)</span></li>
354
+ </ul>
355
+ </div>
356
+
357
  <div class="upgrade-section">
358
+ <h2>Besoin de plus ?</h2>
359
  <p>Passez à la version Pro pour des fonctionnalités avancées et des résolutions illimitées.</p>
360
+ <a href="/" class="cta-button">Découvrir la version Pro</a> <!-- Link to Pro version page -->
361
  </div>
362
  </div>
363
+
364
  <footer>
365
+ <p 2025 Math Solver. Tous droits réservés.</p>
366
  </footer>
367
  </div>
368
+
369
+ <!-- Use latest highlight.js -->
370
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
371
+ <!-- Add languages you expect, python is essential -->
372
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/python.min.js"></script>
373
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/bash.min.js"></script> <!-- For shell output maybe -->
374
+
375
+ <!-- Use Marked.js for Markdown rendering -->
376
+ <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
377
+
378
  <!-- MathJax Configuration -->
379
  <script>
380
+ window.MathJax = {
381
+ tex: {
382
+ inlineMath: [['$', '$'], ['\\(', '\\)']],
383
+ displayMath: [['$$', '$$'], ['\\[', '\\]']],
384
+ processEscapes: true,
385
+ processEnvironments: true,
386
+ packages: {'[+]': ['ams', 'noerrors', 'physics', 'cancel', 'color', 'mhchem', 'mathtools']} // Load common packages
387
+ },
388
+ options: {
389
+ enableMenu: false, // Disable right-click menu
390
+ ignoreHtmlClass: 'tex2jax_ignore', // Class to ignore
391
+ processHtmlClass: 'tex2jax_process', // Class to process (can add to #solution if needed)
392
+ skipHtmlTags: ['script', 'noscript', 'style', 'textarea', 'pre', 'code'] // Skip these tags
393
+ },
394
+ startup: {
395
+ // Function to run once MathJax is ready
396
+ ready: () => {
397
+ console.log('MathJax is ready');
398
+ MathJax.startup.defaultReady();
399
+ // You could potentially trigger an initial typeset if needed
400
+ // MathJax.startup.promise.then(() => { MathJax.typesetPromise(); });
401
+ }
402
+ },
403
+ loader: {
404
+ load: ['[tex]/ams', '[tex]/noerrors', '[tex]/physics', '[tex]/cancel', '[tex]/color', '[tex]/mhchem', '[tex]/mathtools'] // Ensure packages are loaded
405
+ },
406
+ svg: {
407
+ fontCache: 'global' // Cache fonts for better performance
408
+ }
409
+ };
410
  </script>
411
+ <!-- Load MathJax AFTER configuration -->
412
+ <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-svg.js"></script>
413
+
414
  <script>
415
+ // DOM Elements
416
+ const imageInput = document.getElementById('imageInput');
417
+ const uploadButton = document.getElementById('uploadButton');
418
+ const imageForm = document.getElementById('imageForm');
419
+ const uploadStatus = document.getElementById('uploadStatus');
420
+ const imagePreview = document.getElementById('imagePreview');
421
+ const preview = document.getElementById('preview');
422
+ const solveButton = document.getElementById('solveButton');
423
+ const solutionOutput = document.getElementById('solutionOutput');
424
+ const loadingIndicator = document.getElementById('loadingIndicator');
425
+ const solutionDiv = document.getElementById('solution');
426
+ const errorContainer = document.getElementById('errorContainer');
427
+
428
+ // --- Event Listeners ---
429
+
430
+ // Trigger file input when upload button is clicked
431
+ uploadButton.addEventListener('click', () => imageInput.click());
432
+
433
+ // Handle image selection
434
+ imageInput.addEventListener('change', function(event) {
 
 
 
 
 
 
 
435
  const file = event.target.files[0];
436
  if (file) {
437
+ // Basic client-side validation (type and size)
438
+ if (!file.type.startsWith('image/')) {
439
+ uploadStatus.textContent = 'Erreur : Veuillez sélectionner un fichier image.';
440
+ uploadStatus.style.color = 'red';
441
+ imagePreview.style.display = 'none';
442
+ solveButton.style.display = 'none';
443
+ solveButton.disabled = true;
444
+ return;
445
+ }
446
+ // Optional: Size limit (e.g., 10MB)
447
+ const maxSize = 10 * 1024 * 1024;
448
+ if (file.size > maxSize) {
449
+ uploadStatus.textContent = 'Erreur : L\'image est trop grande (max 10MB).';
450
+ uploadStatus.style.color = 'red';
451
+ imagePreview.style.display = 'none';
452
+ solveButton.style.display = 'none';
453
+ solveButton.disabled = true;
454
+ return;
455
+ }
456
+
457
+
458
  const reader = new FileReader();
459
  reader.onload = function(e) {
460
+ preview.src = e.target.result;
461
+ imagePreview.style.display = 'block';
462
+ solveButton.style.display = 'inline-block';
463
+ solveButton.disabled = false; // Enable solve button
464
+ uploadStatus.textContent = `Image sélectionnée : ${file.name}`;
465
+ uploadStatus.style.color = '#555'; // Reset color
466
+ // Clear previous solution/error
467
+ solutionOutput.style.display = 'none';
468
+ solutionDiv.innerHTML = '';
469
+ errorContainer.style.display = 'none';
470
+ errorContainer.textContent = '';
471
+
472
+ }
473
+ reader.onerror = function() {
474
+ uploadStatus.textContent = 'Erreur : Impossible de lire le fichier image.';
475
+ uploadStatus.style.color = 'red';
476
+ imagePreview.style.display = 'none';
477
+ solveButton.style.display = 'none';
478
+ solveButton.disabled = true;
479
  }
480
  reader.readAsDataURL(file);
481
+ } else {
482
+ // No file selected or selection cancelled
483
+ uploadStatus.textContent = '';
484
+ imagePreview.style.display = 'none';
485
+ solveButton.style.display = 'none';
486
+ solveButton.disabled = true;
487
  }
488
  });
489
+
490
+ // Handle form submission (clicking the solve button)
491
+ solveButton.addEventListener('click', function() {
492
+ const file = imageInput.files[0];
493
+ if (!file) {
494
+ showError('Veuillez d\'abord sélectionner une image.');
495
+ return;
496
+ }
497
+
498
+ const formData = new FormData(imageForm);
499
+
500
+ // Reset UI for new request
501
+ solutionOutput.style.display = 'block'; // Show the output area
502
+ loadingIndicator.style.display = 'flex'; // Show loading spinner
503
+ solutionDiv.innerHTML = ''; // Clear previous solution
504
+ errorContainer.style.display = 'none'; // Hide previous error
505
+ errorContainer.textContent = '';
506
+ solveButton.disabled = true; // Disable button during processing
507
+ uploadButton.disabled = true; // Disable upload during processing
508
+
509
+ fetch('/solved', { // Ensure this matches your Flask route
510
  method: 'POST',
511
  body: formData
512
  })
513
  .then(response => {
514
+ if (!response.ok) {
515
+ // Try to parse JSON error body from server
516
+ return response.json().then(errData => {
517
+ // Throw an error with the message from server JSON
518
+ throw new Error(errData.error || `Erreur HTTP ${response.status}`);
519
+ }).catch(() => {
520
+ // If JSON parsing fails, throw generic HTTP error
521
+ throw new Error(`Erreur HTTP ${response.status}: ${response.statusText}`);
522
+ });
523
+ }
524
+ return response.json(); // Parse successful response as JSON
525
+ })
526
+ .then(data => {
527
+ loadingIndicator.style.display = 'none'; // Hide loading indicator
528
+
529
+ if (data.error) {
530
+ // Handle errors returned in the JSON payload
531
+ showError(data.error);
532
+ } else if (data.solution) {
533
+ // Use Marked.js to render the Markdown solution from the server
534
+ // This handles ```python blocks, standard markdown, etc.
535
+ // Ensure Marked converts ```python to <pre><code class="language-python">
536
+ marked.setOptions({
537
+ highlight: function(code, lang) {
538
+ const language = hljs.getLanguage(lang) ? lang : 'plaintext';
539
+ return hljs.highlight(code, { language, ignoreIllegals: true }).value;
540
+ },
541
+ langPrefix: 'language-', // standard prefix for hljs
542
+ pedantic: false,
543
+ gfm: true, // Enable GitHub Flavored Markdown
544
+ breaks: true, // Convert single newlines in paragraphs into <br>
545
+ smartLists: true,
546
+ smartypants: false, // Avoid changing quotes/dashes
547
+ xhtml: false
548
+ });
549
+
550
+ solutionDiv.innerHTML = marked.parse(data.solution);
551
+
552
+ // --- MathJax Typesetting ---
553
+ // Check if MathJax is loaded and ready before typesetting
554
+ if (window.MathJax && MathJax.startup) {
555
+ MathJax.startup.promise = MathJax.startup.promise.then(() => {
556
+ console.log("Typesetting MathJax content...");
557
+ // Typeset the entire solutionDiv AFTER rendering Markdown
558
+ return MathJax.typesetPromise([solutionDiv]);
559
+ }).catch((err) => {
560
+ console.error('MathJax typesetting failed:', err);
561
+ showError('Erreur lors du rendu des formules mathématiques.');
562
+ });
563
+ } else {
564
+ console.warn('MathJax not fully loaded or ready yet.');
565
+ // Optionally, try typesetting later or inform user
566
+ showError('MathJax n\'est pas chargé, le rendu LaTeX peut échouer.');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
567
  }
568
+
569
+ // --- Optional: Post-processing (e.g., styling result blocks) ---
570
+ // Example: Add specific style to result blocks identified by text
571
+ const resultHeaders = solutionDiv.querySelectorAll('strong');
572
+ resultHeaders.forEach(header => {
573
+ if (header.textContent.includes("Résultat d'exécution:")) {
574
+ const nextElement = header.nextElementSibling;
575
+ if (nextElement && nextElement.tagName === 'PRE') {
576
+ nextElement.classList.add('execution-result-block'); // Add a class for styling
577
+ }
578
+ }
579
+ });
580
+
581
+
582
+ } else {
583
+ showError('La réponse reçue est vide ou invalide.');
584
  }
 
 
585
  })
586
  .catch(error => {
587
+ console.error('Fetch Error:', error);
 
 
 
 
 
 
 
588
  loadingIndicator.style.display = 'none';
589
+ // Display the caught error message
590
+ showError(`Erreur de communication : ${error.message}`);
591
+ })
592
+ .finally(() => {
593
+ // Re-enable buttons regardless of success or failure
594
+ solveButton.disabled = false;
595
+ uploadButton.disabled = false;
596
+ // Scroll to the solution area
597
+ // Use smooth scrolling if supported
598
+ solutionOutput.scrollIntoView({ behavior: 'smooth', block: 'start' });
599
  });
600
  });
601
+
602
+ // Helper function to display errors
603
+ function showError(message) {
604
+ errorContainer.textContent = message;
605
+ errorContainer.style.display = 'block';
606
+ solutionDiv.innerHTML = ''; // Clear any partial solution
607
+ loadingIndicator.style.display = 'none'; // Ensure loading is hidden
608
+ }
609
+
610
+ // Initialize Highlight.js (optional, if not done by Marked config)
611
+ // hljs.highlightAll(); // Usually called after content is added
612
  </script>
613
  </body>
614
  </html>