Docfile commited on
Commit
4c53cd5
·
verified ·
1 Parent(s): e00b7c1

Update templates/index.html

Browse files
Files changed (1) hide show
  1. templates/index.html +275 -292
templates/index.html CHANGED
@@ -14,7 +14,7 @@
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
 
@@ -72,170 +72,101 @@
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);
92
- color: white;
93
- padding: 12px 25px;
94
- border-radius: 5px;
95
- text-decoration: none;
96
- font-weight: bold;
97
- transition: all 0.3s ease;
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: 0; /* Remove padding from the container */
125
  border-radius: 8px;
126
  text-align: left;
127
  line-height: 1.8;
128
  font-size: 16px;
129
- overflow: hidden; /* Clip child border-radius */
130
- box-shadow: var(--box-shadow); /* Add shadow to the main container */
131
- }
132
-
133
- .step-section, .code-section, .output-section {
134
- margin: 0; /* Remove vertical margins between blocks */
135
- padding: 15px 20px; /* Add padding inside each block, matching original #solution padding */
136
- border-radius: 0; /* Remove rounded corners between blocks */
137
- border-left: none; /* Remove default left border */
138
- overflow-x: auto;
139
- white-space: pre-wrap; /* Preserve white space and line breaks */
140
- background-color: transparent; /* Ensure consistent background */
141
- }
142
-
143
- .step-section {
144
- border-left: 4px solid var(--primary-color); /* Keep the left border as a visual cue */
145
- padding-left: calc(20px - 4px); /* Adjust padding to maintain alignment */
146
- background-color: #f9f9f9; /* Subtle background for text steps */
147
- }
148
-
149
- .code-section {
150
- /* Code section internal elements handle their own padding and background */
151
  }
152
 
153
- .code-header {
154
- background-color: #343a40;
155
- color: white;
156
- padding: 8px 20px; /* Match horizontal padding */
157
- font-size: 14px;
158
- font-family: 'Courier New', monospace;
159
- display: flex;
160
- justify-content: space-between;
161
- align-items: center;
162
- border-top-left-radius: 8px;
163
- border-top-right-radius: 8px;
 
 
 
 
 
 
 
 
 
 
164
  }
165
-
166
- .code-content {
167
- margin: 0;
168
- padding: 15px 20px; /* Match horizontal padding */
169
- background-color: var(--code-bg);
170
- color: #e6e6e6;
171
- overflow-x: auto;
172
  font-family: 'Courier New', monospace;
173
  font-size: 14px;
174
  line-height: 1.5;
 
175
  }
 
 
176
 
177
- .output-section {
178
- background-color: var(--output-bg);
179
- padding: 15px 20px; /* Match horizontal padding */
180
- border-top: 1px solid #ddd;
181
- color: #333;
182
- font-family: 'Courier New', monospace;
183
- font-size: 14px;
184
- white-space: pre-wrap;
185
- overflow-x: auto;
186
- border-bottom-left-radius: 8px;
187
- border-bottom-right-radius: 8px;
188
- }
189
 
190
- /* Apply border-radius to the very first and very last child inside #solution */
191
- #solution > div:first-child {
192
- border-top-left-radius: 8px;
193
- border-top-right-radius: 8px;
194
- }
195
-
196
- #solution > div:last-child {
197
- border-bottom-left-radius: 8px;
198
- border-bottom-right-radius: 8px;
199
- }
200
 
201
- /* Special case for first code header within the first child */
202
- #solution > .code-section:first-child .code-header {
203
- border-top-left-radius: 8px;
204
- border-top-right-radius: 8px;
205
- }
206
-
207
- /* Special case for last code content if it's the very last child and not followed by output */
208
- #solution > .code-section:last-child .code-content:last-child:not(+.output-section) {
209
- border-bottom-left-radius: 8px;
210
- border-bottom-right-radius: 8px;
211
- }
212
-
213
- /* MathJax rendering output styles */
214
  mjx-container {
215
- overflow-x: auto;
216
  overflow-y: hidden;
217
  display: block; /* Block math */
218
  margin: 1em 0; /* Vertical spacing for block math */
219
  text-align: initial;
220
- padding: 0 !important;
 
 
221
  }
222
- /* Inline math container */
223
  mjx-container[display="inline"] {
224
- display: inline-block; /* Inline math behaves like text */
225
  margin: 0 !important; /* No vertical margin for inline math */
 
226
  }
227
-
228
- mjx-assistive-mml {
229
- display: none !important;
230
- }
231
-
232
- .MathJax nobr,.MathJax .mjx-chtml{
233
- display: inline-block !important;
234
- white-space: normal !important;
235
- }
236
- .step-section span.MathJax_Preview {
237
- display: none !important;
238
- }
239
 
240
  </style>
241
  </head>
@@ -250,49 +181,53 @@
250
  <h1>Version Gratuite</h1>
251
  <p>Vous utilisez actuellement la version gratuite de Math Solver qui vous permet de résoudre 3 problèmes par jour.</p>
252
 
253
- <div class="feature-list">
254
- <h2>Fonctionnalités disponibles :</h2>
255
- <ul class="feature-list">
256
- <li><i class="fas fa-check-circle"></i> Résolution de problèmes mathématiques basiques</li>
257
- <li><i class="fas fa-check-circle"></i> 3 résolutions gratuites par jour</li>
258
- <li><i class="fas fa-check-circle"></i> Explication des étapes de résolution</li>
259
- <li><i class="fas fa-times-circle"></i> <span style="color: #999;">Mode d'exécution de code avancé (version Pro)</span></li>
260
- <li><i class="fas fa-times-circle"></i> <span style="color: #999;">Résolutions illimitées (version Pro)</span></li>
261
- <li><i class="fas fa-times-circle"></i> <span style="color: #999;">Support prioritaire (version Pro)</span></li>
262
- </ul>
263
- </div>
264
-
265
- <div class="upload-section">
266
- <form id="imageForm" enctype="multipart/form-data">
267
- <input type="file" id="imageInput" name="image" accept="image/*" style="display: none;">
268
- <button type="button" id="uploadButton" class="cta-button" onclick="document.getElementById('imageInput').click()">
269
- <i class="fas fa-upload"></i> Télécharger une image
270
- </button>
271
- </form>
272
-
273
- <p id="uploadStatus"></p>
274
- <div id="imagePreview" style="display: none; margin: 20px auto; max-width: 500px;">
275
- <img id="preview" style="width: 100%; border-radius: 8px; box-shadow: var(--box-shadow);">
276
- </div>
277
-
278
- <button id="solveButton" class="cta-button" style="display: none; background-color: var(--secondary-color);">
279
- <i class="fas fa-calculator"></i> Résoudre ce problème
280
- </button>
281
- </div>
282
-
283
- <div id="solutionOutput" style="margin-top: 30px; text-align: left; display: none;">
 
 
284
  <h3>Solution :</h3>
285
  <div id="loadingIndicator" class="thinking-indicator" style="display: none;">
286
  <i class="fas fa-brain indicator-icon"></i>
287
  <span>Je réfléchis au problème...</span>
288
  </div>
289
- <div id="solution"></div>
 
290
  </div>
291
 
 
292
  <div class="upgrade-section">
293
- <h2>Besoin de plus de puissance ?</h2>
294
- <p>Passez à la version Pro pour des fonctionnalités avancées et des résolutions illimitées.</p>
295
- <a href="#" class="cta-button">Passer à la version Pro</a>
296
  </div>
297
  </div>
298
 
@@ -301,9 +236,12 @@
301
  </footer>
302
  </div>
303
 
 
304
  <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/highlight.min.js"></script>
305
  <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/languages/python.min.js"></script>
 
306
 
 
307
  <script>
308
  window.MathJax = {
309
  tex: {
@@ -315,13 +253,21 @@
315
  },
316
  options: {
317
  enableMenu: false,
318
- ignoreHtmlClass: 'code-content',
319
- processHtmlClass: 'step-section', /* Process step-section for Math */
320
- skipHtmlTags: ['script', 'noscript', 'style', 'textarea', 'pre', 'code']
321
  },
322
  loader: {
323
  load: ['[tex]/ams', '[tex]/noerrors', '[tex]/physics', '[tex]/cancel', '[tex]/color', '[tex]/mhchem', '[tex]/mathtools']
324
  },
 
 
 
 
 
 
 
 
325
  svg: {
326
  fontCache: 'global'
327
  }
@@ -329,140 +275,177 @@
329
  </script>
330
  <script id="MathJax-script" async src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/3.2.2/es5/tex-svg.js"></script>
331
 
 
332
  <script>
333
- document.getElementById('imageInput').addEventListener('change', function(event) {
334
- const file = event.target.files[0];
335
- if (file) {
336
- const reader = new FileReader();
337
- reader.onload = function(e) {
338
- document.getElementById('preview').src = e.target.result;
339
- document.getElementById('imagePreview').style.display = 'block';
340
- document.getElementById('solveButton').style.display = 'inline-block';
341
- document.getElementById('uploadStatus').textContent = `Image sélectionnée : ${file.name}`;
342
- }
343
- reader.readAsDataURL(file);
344
- }
345
- });
346
-
347
- document.getElementById('solveButton').addEventListener('click', function() {
348
- const formData = new FormData(document.getElementById('imageForm'));
349
  const solutionOutput = document.getElementById('solutionOutput');
350
  const loadingIndicator = document.getElementById('loadingIndicator');
351
- const solution = document.getElementById('solution');
352
-
353
- solutionOutput.style.display = 'block';
354
- loadingIndicator.style.display = 'flex';
355
- solution.innerHTML = '';
356
-
357
- fetch('/solved', {
358
- method: 'POST',
359
- body: formData
360
- })
361
- .then(response => {
362
- const reader = response.body.getReader();
363
- const decoder = new TextDecoder();
364
- let buffer = '';
365
-
366
- function processStream({ done, value }) {
367
- if (done) {
368
- loadingIndicator.style.display = 'none';
369
- return;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
370
  }
 
 
 
371
 
372
- buffer += decoder.decode(value, { stream: true });
373
- const lines = buffer.split('\n\n');
374
- buffer = lines.pop();
375
-
376
- for (const line of lines) {
377
- if (line.startsWith('data: ')) {
378
- try {
379
- const data = JSON.parse(line.substr(6));
380
-
381
- if (data.mode) {
382
- if (data.mode === 'thinking') {
383
- loadingIndicator.className = 'thinking-indicator';
384
- loadingIndicator.innerHTML = '<i class="fas fa-brain indicator-icon"></i><span>Je réfléchis au problème...</span>';
385
- } else if (data.mode === 'answering') {
386
- loadingIndicator.className = 'answering-indicator';
387
- loadingIndicator.innerHTML = '<i class="fas fa-pencil-alt indicator-icon"></i><span>Rédaction de la solution...</span>';
388
- } else if (data.mode === 'executing_code') {
389
- loadingIndicator.className = 'executing-indicator';
390
- loadingIndicator.innerHTML = '<i class="fas fa-code indicator-icon"></i><span>Exécution de code pour la résolution...</span>';
391
- } else if (data.mode === 'code_result') {
392
- loadingIndicator.className = 'executing-indicator';
393
- loadingIndicator.innerHTML = '<i class="fas fa-terminal indicator-icon"></i><span>Traitement des résultats...</span>';
394
- }
 
 
 
 
 
 
 
 
 
 
 
395
  }
 
 
 
 
 
 
396
 
397
- if (data.content) {
398
- const content = data.content;
399
- const contentContainer = document.createElement('div');
400
-
401
- if (content.includes('```python')) {
402
- const codeHtml = content.replace(/```python\n([\s\S]*?)\n```/g, function(match, p1) {
403
- return `<div class="code-section">
404
- <div class="code-header">
405
- <span>Code Python</span>
406
- </div>
407
- <pre class="code-content"><code class="language-python">${p1}</code></pre>
408
- </div>`;
409
- });
410
- contentContainer.innerHTML = codeHtml;
411
- solution.appendChild(contentContainer);
412
- contentContainer.querySelectorAll('pre code').forEach((block) => {
413
- hljs.highlightElement(block);
414
- });
415
- }
416
- else if (content.includes('Résultat d\'exécution:')) {
417
- const outputHtml = content.replace(/Résultat d'exécution:\n```\n([\s\S]*?)\n```/g, function(match, p1) {
418
- return `<div class="output-section">${p1}</div>`;
419
- });
420
- contentContainer.innerHTML = outputHtml;
421
- solution.appendChild(contentContainer);
422
  }
423
- else {
424
- contentContainer.className = 'step-section';
425
- contentContainer.innerHTML = content;
426
- solution.appendChild(contentContainer);
427
- MathJax.typesetPromise([contentContainer]).catch(e => console.error('MathJax typesetting error:', e));
428
  }
429
- }
430
 
431
- if (data.error) {
432
- const errorDiv = document.createElement('div');
433
- errorDiv.style.color = 'red';
434
- errorDiv.style.margin = '15px 0';
435
- errorDiv.style.padding = '10px';
436
- errorDiv.style.background = '#ffeeee';
437
- errorDiv.style.borderRadius = '5px';
438
- errorDiv.textContent = `Erreur: ${data.error}`;
439
- solution.appendChild(errorDiv);
440
- loadingIndicator.style.display = 'none';
 
441
  }
442
- } catch (e) {
443
- console.error('Error parsing JSON from stream:', e, line);
444
  }
445
  }
 
 
446
  }
447
-
448
- window.scrollTo(0, document.body.scrollHeight);
449
-
450
  return reader.read().then(processStream);
451
- }
452
-
453
- return reader.read().then(processStream);
454
- })
455
- .catch(error => {
456
- const errorDiv = document.createElement('div');
457
- errorDiv.style.color = 'red';
458
- errorDiv.style.margin = '15px 0';
459
- errorDiv.style.padding = '10px';
460
- errorDiv.style.background = '#ffeeee';
461
- errorDiv.style.borderRadius = '5px';
462
- errorDiv.textContent = `Erreur de connexion ou du serveur: ${error}`;
463
- solution.appendChild(errorDiv);
464
- loadingIndicator.style.display = 'none';
465
- console.error('Fetch error:', error);
466
  });
467
  });
468
  </script>
 
14
  --background-color: #f8f9fa;
15
  --text-color: #333;
16
  --box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
17
+ --code-bg: #2c323c; /* Background for code blocks generated by marked+hljs */
18
  --output-bg: #f1f8f9;
19
  }
20
 
 
72
  margin: 30px 0;
73
  text-align: left;
74
  }
75
+ .feature-list li { padding: 10px 0; margin-bottom: 10px; display: flex; align-items: center; }
76
+ .feature-list i { color: var(--accent-color); margin-right: 10px; font-size: 1.2rem; }
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: 20px 10px; border: none; cursor: pointer; }
79
+ .cta-button:hover { background-color: var(--secondary-color); transform: translateY(-2px); box-shadow: 0 6px 12px rgba(0, 0, 0, 0.15); }
 
 
 
 
 
 
 
 
 
 
80
 
81
+ .upgrade-section { margin-top: 30px; padding: 20px; border-top: 1px solid #ddd; }
82
+ footer { text-align: center; padding: 20px 0; color: #666; font-size: 0.9rem; }
 
 
 
 
 
 
 
 
 
 
 
83
 
84
+ /* --- Styles for Solution Area --- */
85
+ #solutionOutput {
 
 
 
 
 
86
  margin-top: 30px;
87
+ text-align: left;
88
+ display: none;
 
 
 
 
 
 
 
89
  }
90
 
91
+ #solutionContainer {
92
  background: #fff;
93
+ padding: 20px;
94
  border-radius: 8px;
95
  text-align: left;
96
  line-height: 1.8;
97
  font-size: 16px;
98
+ box-shadow: var(--box-shadow);
99
+ overflow-x: hidden; /* Prevent container scroll, let content scroll if needed */
100
+ max-height: 70vh; /* Limit height and enable scrolling */
101
+ overflow-y: auto; /* Enable vertical scroll */
102
+ scroll-behavior: smooth;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
103
  }
104
 
105
+ /* Styles for elements generated by Marked.js */
106
+ #solutionContainer p { margin-bottom: 1em; }
107
+ #solutionContainer ul, #solutionContainer ol { margin-left: 2em; margin-bottom: 1em; }
108
+ #solutionContainer li { margin-bottom: 0.5em; }
109
+ #solutionContainer h1, #solutionContainer h2, #solutionContainer h3 { margin-top: 1.5em; margin-bottom: 0.8em; font-weight: bold; color: var(--primary-color); }
110
+ #solutionContainer h1 { font-size: 1.8em; }
111
+ #solutionContainer h2 { font-size: 1.5em; }
112
+ #solutionContainer h3 { font-size: 1.2em; }
113
+ #solutionContainer strong, #solutionContainer b { font-weight: bold; }
114
+ #solutionContainer em, #solutionContainer i { font-style: italic; }
115
+ #solutionContainer table { border-collapse: collapse; width: 100%; margin-bottom: 1rem; }
116
+ #solutionContainer th, #solutionContainer td { border: 1px solid #d1d5db; padding: 0.5rem; text-align: left; }
117
+ #solutionContainer th { background-color: #f3f4f6; font-weight: 600; }
118
+ #solutionContainer blockquote { border-left: 4px solid #ccc; margin-left: 0; padding-left: 1em; color: #666; }
119
+
120
+ /* Styles for Code Blocks (generated by Marked + Highlight.js) */
121
+ #solutionContainer pre {
122
+ margin: 1.5em 0;
123
+ border-radius: 8px;
124
+ overflow: hidden; /* Contains the code block */
125
+ box-shadow: 0 2px 5px rgba(0,0,0,0.1);
126
  }
127
+ #solutionContainer pre code.hljs {
128
+ display: block;
129
+ padding: 15px;
130
+ background-color: var(--code-bg); /* Use defined code background */
131
+ color: #e6e6e6; /* Default text color for atom-one-dark */
132
+ overflow-x: auto; /* Enable horizontal scroll for long code lines */
 
133
  font-family: 'Courier New', monospace;
134
  font-size: 14px;
135
  line-height: 1.5;
136
+ border-radius: 0 0 8px 8px; /* Bottom radius */
137
  }
138
+ /* Optional: Add a language header like in the previous code */
139
+ /* This would require modifying how marked generates the HTML or adding JS */
140
 
 
 
 
 
 
 
 
 
 
 
 
 
141
 
142
+ /* Styles for Loading Indicators */
143
+ .thinking-indicator, .executing-indicator, .answering-indicator { display: flex; align-items: center; padding: 10px; margin: 10px 0; border-radius: 8px; font-size: 0.9rem; }
144
+ .thinking-indicator { background-color: #e3f2fd; color: #1565c0; }
145
+ .executing-indicator { background-color: #ede7f6; color: #5e35b1; }
146
+ .answering-indicator { background-color: #e8f5e9; color: #2e7d32; }
147
+ .indicator-icon { margin-right: 10px; animation: pulse 1.5s infinite ease-in-out; }
148
+ @keyframes pulse { 0% { opacity: 0.6; } 50% { opacity: 1; } 100% { opacity: 0.6; } }
 
 
 
149
 
150
+ /* --- MathJax Specific Styles (from previous attempts, seem okay) --- */
 
 
 
 
 
 
 
 
 
 
 
 
151
  mjx-container {
152
+ overflow-x: auto !important; /* Ensure horizontal scroll for wide math */
153
  overflow-y: hidden;
154
  display: block; /* Block math */
155
  margin: 1em 0; /* Vertical spacing for block math */
156
  text-align: initial;
157
+ padding: 2px 0 !important; /* Adjust padding */
158
+ min-width: 0 !important; /* Prevent excessive width */
159
+ max-width: 100% !important; /* Ensure it doesn't overflow container */
160
  }
161
+ /* Inline math container */
162
  mjx-container[display="inline"] {
163
+ display: inline-block !important; /* Inline math behaves like text */
164
  margin: 0 !important; /* No vertical margin for inline math */
165
+ overflow-x: visible !important; /* Allow inline math to flow naturally */
166
  }
167
+ mjx-assistive-mml { display: none !important; }
168
+ .MathJax nobr,.MathJax .mjx-chtml{ display: inline-block !important; white-space: normal !important; }
169
+ span.MathJax_Preview { display: none !important; }
 
 
 
 
 
 
 
 
 
170
 
171
  </style>
172
  </head>
 
181
  <h1>Version Gratuite</h1>
182
  <p>Vous utilisez actuellement la version gratuite de Math Solver qui vous permet de résoudre 3 problèmes par jour.</p>
183
 
184
+ <!-- Feature list, Upload section remain the same -->
185
+ <div class="feature-list">
186
+ <h2>Fonctionnalités disponibles :</h2>
187
+ <ul class="feature-list">
188
+ <li><i class="fas fa-check-circle"></i> Résolution de problèmes mathématiques basiques</li>
189
+ <li><i class="fas fa-check-circle"></i> 3 résolutions gratuites par jour</li>
190
+ <li><i class="fas fa-check-circle"></i> Explication des étapes de résolution</li>
191
+ <li><i class="fas fa-times-circle"></i> <span style="color: #999;">Mode d'exécution de code avancé (version Pro)</span></li>
192
+ <li><i class="fas fa-times-circle"></i> <span style="color: #999;">Résolutions illimitées (version Pro)</span></li>
193
+ <li><i class="fas fa-times-circle"></i> <span style="color: #999;">Support prioritaire (version Pro)</span></li>
194
+ </ul>
195
+ </div>
196
+
197
+ <div class="upload-section">
198
+ <form id="imageForm" enctype="multipart/form-data">
199
+ <input type="file" id="imageInput" name="image" accept="image/*" style="display: none;">
200
+ <button type="button" id="uploadButton" class="cta-button" onclick="document.getElementById('imageInput').click()">
201
+ <i class="fas fa-upload"></i> Télécharger une image
202
+ </button>
203
+ </form>
204
+
205
+ <p id="uploadStatus"></p>
206
+ <div id="imagePreview" style="display: none; margin: 20px auto; max-width: 500px;">
207
+ <img id="preview" style="width: 100%; border-radius: 8px; box-shadow: var(--box-shadow);">
208
+ </div>
209
+
210
+ <button id="solveButton" class="cta-button" style="display: none; background-color: var(--secondary-color);">
211
+ <i class="fas fa-calculator"></i> Résoudre ce problème
212
+ </button>
213
+ </div>
214
+
215
+
216
+ <div id="solutionOutput">
217
  <h3>Solution :</h3>
218
  <div id="loadingIndicator" class="thinking-indicator" style="display: none;">
219
  <i class="fas fa-brain indicator-icon"></i>
220
  <span>Je réfléchis au problème...</span>
221
  </div>
222
+ <!-- Single container for the entire solution -->
223
+ <div id="solutionContainer"></div>
224
  </div>
225
 
226
+ <!-- Upgrade section remains the same -->
227
  <div class="upgrade-section">
228
+ <h2>Besoin de plus de puissance ?</h2>
229
+ <p>Passez à la version Pro pour des fonctionnalités avancées et des résolutions illimitées.</p>
230
+ <a href="#" class="cta-button">Passer à la version Pro</a>
231
  </div>
232
  </div>
233
 
 
236
  </footer>
237
  </div>
238
 
239
+ <!-- Dependencies -->
240
  <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/highlight.min.js"></script>
241
  <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/languages/python.min.js"></script>
242
+ <script src="https://cdn.jsdelivr.net/npm/[email protected]/marked.min.js"></script> <!-- Use marked 4.x -->
243
 
244
+ <!-- MathJax Configuration and Loading -->
245
  <script>
246
  window.MathJax = {
247
  tex: {
 
253
  },
254
  options: {
255
  enableMenu: false,
256
+ ignoreHtmlClass: 'no-mathjax', // Ignore specific classes if needed
257
+ processHtmlClass: 'mathjax-process', // Process only specific classes if needed
258
+ skipHtmlTags: ['script', 'noscript', 'style', 'textarea', 'pre'] // Skip pre for manual highlight control
259
  },
260
  loader: {
261
  load: ['[tex]/ams', '[tex]/noerrors', '[tex]/physics', '[tex]/cancel', '[tex]/color', '[tex]/mhchem', '[tex]/mathtools']
262
  },
263
+ startup: {
264
+ // Function to run once MathJax is ready
265
+ ready: () => {
266
+ console.log('MathJax is ready');
267
+ window.mathJaxReady = true; // Flag for our script
268
+ MathJax.startup.defaultReady(); // Perform default startup actions
269
+ }
270
+ },
271
  svg: {
272
  fontCache: 'global'
273
  }
 
275
  </script>
276
  <script id="MathJax-script" async src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/3.2.2/es5/tex-svg.js"></script>
277
 
278
+ <!-- Main Application Logic -->
279
  <script>
280
+ document.addEventListener('DOMContentLoaded', () => {
281
+ // DOM Elements
282
+ const imageInput = document.getElementById('imageInput');
283
+ const imagePreview = document.getElementById('imagePreview');
284
+ const previewImage = document.getElementById('preview');
285
+ const solveButton = document.getElementById('solveButton');
286
+ const uploadStatus = document.getElementById('uploadStatus');
 
 
 
 
 
 
 
 
 
287
  const solutionOutput = document.getElementById('solutionOutput');
288
  const loadingIndicator = document.getElementById('loadingIndicator');
289
+ const solutionContainer = document.getElementById('solutionContainer');
290
+ const imageForm = document.getElementById('imageForm');
291
+
292
+ // State Variables
293
+ let solutionBuffer = '';
294
+ let currentMode = null; // To potentially handle 'thinking' vs 'answering' differently if needed
295
+ let updateTimeout = null;
296
+
297
+ // Configure Marked and Highlight.js
298
+ marked.setOptions({
299
+ highlight: function(code, lang) {
300
+ const language = hljs.getLanguage(lang) ? lang : 'plaintext';
301
+ return hljs.highlight(code, { language }).value;
302
+ },
303
+ langPrefix: 'hljs language-', // CSS class prefix for hljs
304
+ gfm: true, // Enable GitHub Flavored Markdown
305
+ breaks: true // Convert single line breaks to <br>
306
+ });
307
+
308
+ // Function to update the display (debounced)
309
+ const updateDisplay = async () => {
310
+ if (!solutionContainer) return;
311
+
312
+ // Parse the entire buffer with Marked
313
+ solutionContainer.innerHTML = marked.parse(solutionBuffer);
314
+
315
+ // Ensure MathJax is ready before typesetting
316
+ if (window.mathJaxReady) {
317
+ try {
318
+ // Tell MathJax to process the updated container
319
+ await MathJax.typesetPromise([solutionContainer]);
320
+ console.log("MathJax typesetting complete.");
321
+ } catch (e) {
322
+ console.error('MathJax typesetting failed:', e);
323
+ }
324
+ } else {
325
+ console.warn("MathJax not ready, typesetting skipped for this update.");
326
+ // Optionally, queue this update or retry later
327
+ }
328
+
329
+ // Highlight code blocks (already done by marked's highlight option)
330
+ // hljs.highlightAll(); // Not needed if using marked's highlight option
331
+
332
+ // Scroll to bottom
333
+ solutionContainer.scrollTop = solutionContainer.scrollHeight;
334
+ updateTimeout = null; // Clear the timeout flag
335
+ };
336
+
337
+ // Schedule display update (debouncing)
338
+ const scheduleUpdate = () => {
339
+ if (!updateTimeout) {
340
+ updateTimeout = setTimeout(updateDisplay, 150); // Update slightly faster
341
+ }
342
+ };
343
+
344
+ // Image selection handler
345
+ imageInput.addEventListener('change', function(event) {
346
+ const file = event.target.files[0];
347
+ if (file) {
348
+ const reader = new FileReader();
349
+ reader.onload = function(e) {
350
+ previewImage.src = e.target.result;
351
+ imagePreview.style.display = 'block';
352
+ solveButton.style.display = 'inline-block';
353
+ uploadStatus.textContent = `Image sélectionnée : ${file.name}`;
354
  }
355
+ reader.readAsDataURL(file);
356
+ }
357
+ });
358
 
359
+ // Solve button handler
360
+ solveButton.addEventListener('click', function() {
361
+ const formData = new FormData(imageForm);
362
+
363
+ solutionOutput.style.display = 'block';
364
+ loadingIndicator.style.display = 'flex';
365
+ solutionContainer.innerHTML = ''; // Clear previous solution
366
+ solutionBuffer = ''; // Reset buffer
367
+ currentMode = null; // Reset mode
368
+
369
+ fetch('/solved', { // Ensure this endpoint matches your backend
370
+ method: 'POST',
371
+ body: formData
372
+ })
373
+ .then(response => {
374
+ if (!response.ok) {
375
+ throw new Error(`HTTP error! status: ${response.status}`);
376
+ }
377
+ const reader = response.body.getReader();
378
+ const decoder = new TextDecoder();
379
+ let streamBuffer = ''; // Buffer for incoming stream chunks
380
+
381
+ function processStream({ done, value }) {
382
+ if (done) {
383
+ loadingIndicator.style.display = 'none';
384
+ // Process any remaining data in streamBuffer (optional, depends on backend)
385
+ if (streamBuffer && streamBuffer.startsWith('data: ')) {
386
+ try {
387
+ const data = JSON.parse(streamBuffer.substr(6));
388
+ if (data.content) {
389
+ solutionBuffer += data.content; // Add final piece
390
+ }
391
+ } catch (e) {
392
+ console.error('Error parsing final chunk:', e, streamBuffer);
393
  }
394
+ }
395
+ // Final forced update
396
+ clearTimeout(updateTimeout); // Cancel any scheduled update
397
+ updateDisplay(); // Render everything received
398
+ return;
399
+ }
400
 
401
+ streamBuffer += decoder.decode(value, { stream: true });
402
+ const lines = streamBuffer.split('\n\n');
403
+ streamBuffer = lines.pop(); // Keep incomplete chunk
404
+
405
+ for (const line of lines) {
406
+ if (line.startsWith('data: ')) {
407
+ try {
408
+ const data = JSON.parse(line.substr(6));
409
+
410
+ if (data.mode) {
411
+ currentMode = data.mode; // Store current mode if backend provides it
412
+ // Update loading indicator based on mode (same as before)
413
+ if (data.mode === 'thinking') { loadingIndicator.className = 'thinking-indicator'; loadingIndicator.innerHTML = '<i class="fas fa-brain indicator-icon"></i><span>Je réfléchis...</span>'; }
414
+ else if (data.mode === 'answering') { loadingIndicator.className = 'answering-indicator'; loadingIndicator.innerHTML = '<i class="fas fa-pencil-alt indicator-icon"></i><span>Rédaction...</span>'; }
415
+ else if (data.mode === 'executing_code') { loadingIndicator.className = 'executing-indicator'; loadingIndicator.innerHTML = '<i class="fas fa-code indicator-icon"></i><span>Exécution...</span>'; }
416
+ else if (data.mode === 'code_result') { loadingIndicator.className = 'executing-indicator'; loadingIndicator.innerHTML = '<i class="fas fa-terminal indicator-icon"></i><span>Résultats...</span>'; }
 
 
 
 
 
 
 
 
 
417
  }
418
+
419
+ if (data.content) {
420
+ solutionBuffer += data.content; // Append content to buffer
421
+ scheduleUpdate(); // Schedule an update
 
422
  }
 
423
 
424
+ if (data.error) {
425
+ // Display error prominently
426
+ solutionBuffer += `\n\n**Erreur:** ${data.error}\n\n`;
427
+ scheduleUpdate(); // Show the error
428
+ loadingIndicator.style.display = 'none';
429
+ }
430
+ } catch (e) {
431
+ console.error('Error parsing JSON from stream:', e, line);
432
+ // Optionally display a parsing error message to the user
433
+ solutionBuffer += `\n\n**Erreur interne:** Impossible d'interpréter une partie de la réponse.\n\n`;
434
+ scheduleUpdate();
435
  }
 
 
436
  }
437
  }
438
+ // Continue reading the stream
439
+ return reader.read().then(processStream);
440
  }
441
+ // Start processing the stream
 
 
442
  return reader.read().then(processStream);
443
+ })
444
+ .catch(error => {
445
+ console.error('Fetch error:', error);
446
+ solutionContainer.innerHTML = `<p style="color: red; font-weight: bold;">Erreur de connexion ou du serveur: ${error.message}</p>`;
447
+ loadingIndicator.style.display = 'none';
448
+ });
 
 
 
 
 
 
 
 
 
449
  });
450
  });
451
  </script>