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

Update templates/index.html

Browse files
Files changed (1) hide show
  1. templates/index.html +318 -275
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; /* Background for code blocks generated by marked+hljs */
18
  --output-bg: #f1f8f9;
19
  }
20
 
@@ -72,101 +72,183 @@
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,53 +263,52 @@
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,12 +317,9 @@
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: {
@@ -249,203 +327,168 @@
249
  displayMath: [['$$', '$$'], ['\\[', '\\]']],
250
  processEscapes: true,
251
  processEnvironments: true,
252
- packages: {'[+]': ['ams', 'noerrors', 'physics', 'cancel', 'color', 'mhchem', 'mathtools']}
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
  }
274
  };
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>
 
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
  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: 20px;
125
  border-radius: 8px;
126
  text-align: left;
127
  line-height: 1.8;
128
  font-size: 16px;
129
+ /* Allow child divs to handle their own background */
130
+ background-color: transparent;
131
+ padding: 0; /* Remove padding if children manage it */
 
 
132
  }
133
 
134
+ .step-section, .code-section, .output-section {
135
+ margin: 0;
136
+ padding: 15px;
137
+ border-radius: 0;
138
+ border-left: none;
139
+ overflow-x: auto;
140
+ white-space: pre-wrap; /* Important for text formatting */
141
+ /* Common background or let specifics override */
142
+ background-color: #f9f9f9;
 
 
 
 
 
 
 
 
 
 
 
 
143
  }
144
+
145
+ .step-section {
146
+ border-left: 4px solid var(--primary-color);
147
+ padding-left: calc(15px - 4px);
148
+ background-color: #f9f9f9; /* Specific background for steps */
149
+ }
150
+
151
+
152
+ .code-section {
153
+ /* Code section manages its background via children */
154
+ background-color: transparent;
155
+ padding: 0; /* No padding on the container itself */
156
+ border-left: none; /* Override general rule */
157
+ }
158
+
159
+ .code-header {
160
+ background-color: #343a40;
161
+ color: white;
162
+ padding: 8px 15px;
163
+ font-size: 14px;
164
+ font-family: 'Courier New', monospace;
165
+ display: flex;
166
+ justify-content: space-between;
167
+ align-items: center;
168
+ border-top-left-radius: 0px; /* Managed by first-child rule */
169
+ border-top-right-radius: 0px;
170
+ }
171
+
172
+ .code-content {
173
+ margin: 0;
174
  padding: 15px;
175
+ background-color: var(--code-bg);
176
+ color: #e6e6e6;
177
+ overflow-x: auto;
178
  font-family: 'Courier New', monospace;
179
  font-size: 14px;
180
  line-height: 1.5;
181
+ border-bottom-left-radius: 0px; /* Managed by last-child rule */
182
+ border-bottom-right-radius: 0px;
183
+ }
184
+
185
+ .output-section {
186
+ background-color: var(--output-bg);
187
+ padding: 15px;
188
+ border-top: 1px solid #ddd;
189
+ color: #333;
190
+ font-family: 'Courier New', monospace;
191
+ font-size: 14px;
192
+ white-space: pre-wrap;
193
+ overflow-x: auto;
194
+ border-left: none; /* Override general rule */
195
+ border-bottom-left-radius: 0px; /* Managed by last-child rule */
196
+ border-bottom-right-radius: 0px;
197
+ }
198
+
199
+ /* Add border-radius back to the very first and very last child inside #solution */
200
+ #solution > div:first-child,
201
+ #solution > div:first-child .code-header {
202
+ border-top-left-radius: 8px;
203
+ border-top-right-radius: 8px;
204
+ }
205
+
206
+ #solution > div:last-child,
207
+ #solution > div:last-child .code-content, /* If last element is code */
208
+ #solution > div:last-child.output-section /* If last element is output */
209
+ {
210
+ border-bottom-left-radius: 8px;
211
+ border-bottom-right-radius: 8px;
212
+ }
213
+ /* Ensure bottom radius on code content only if it IS the last element */
214
+ #solution > .code-section:last-child .code-content {
215
+ border-bottom-left-radius: 8px;
216
+ border-bottom-right-radius: 8px;
217
+ }
218
+ /* If output follows code, remove bottom radius from code content */
219
+ #solution > .code-section:not(:last-child) .code-content {
220
+ border-bottom-left-radius: 0;
221
+ border-bottom-right-radius: 0;
222
+ }
223
+
224
+
225
+ .latex-display {
226
+ overflow-x: auto;
227
+ padding: 10px 0;
228
+ margin: 15px 0;
229
+ text-align: center;
230
  }
 
 
231
 
232
+ .thinking-indicator, .executing-indicator, .answering-indicator {
233
+ display: flex;
234
+ align-items: center;
235
+ padding: 10px;
236
+ margin: 10px 20px; /* Add margin to separate from content box */
237
+ border-radius: 8px;
238
+ font-size: 0.9rem;
239
+ }
240
 
 
 
241
  .thinking-indicator { background-color: #e3f2fd; color: #1565c0; }
242
  .executing-indicator { background-color: #ede7f6; color: #5e35b1; }
243
  .answering-indicator { background-color: #e8f5e9; color: #2e7d32; }
244
+
245
  .indicator-icon { margin-right: 10px; animation: pulse 1.5s infinite ease-in-out; }
246
  @keyframes pulse { 0% { opacity: 0.6; } 50% { opacity: 1; } 100% { opacity: 0.6; } }
247
 
248
+ mjx-container { overflow-x: auto; overflow-y: hidden; display: block; margin: 1em 0; text-align: initial; padding: 0 !important; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
249
  mjx-assistive-mml { display: none !important; }
250
  .MathJax nobr,.MathJax .mjx-chtml{ display: inline-block !important; white-space: normal !important; }
251
+ .step-section span.MathJax_Preview { display: none !important; }
252
 
253
  </style>
254
  </head>
 
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
+ <!-- Container for the dynamically added content blocks -->
303
+ <div id="solution" style="background: #fff; border-radius: 8px; overflow: hidden;">
304
+ <!-- Content will be added here by JS -->
305
+ </div>
306
  </div>
307
 
 
308
  <div class="upgrade-section">
309
+ <h2>Besoin de plus de puissance ?</h2>
310
+ <p>Passez à la version Pro pour des fonctionnalités avancées et des résolutions illimitées.</p>
311
+ <a href="#" class="cta-button">Passer à la version Pro</a>
312
  </div>
313
  </div>
314
 
 
317
  </footer>
318
  </div>
319
 
 
320
  <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/highlight.min.js"></script>
321
  <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/languages/python.min.js"></script>
 
322
 
 
323
  <script>
324
  window.MathJax = {
325
  tex: {
 
327
  displayMath: [['$$', '$$'], ['\\[', '\\]']],
328
  processEscapes: true,
329
  processEnvironments: true,
330
+ packages: {'[+]': ['ams', 'noerrors', 'physics', 'cancel', 'color', 'mhchem', 'mathtools']} // "Full options" packages
331
  },
332
  options: {
333
  enableMenu: false,
334
+ ignoreHtmlClass: 'code-content', // Don't process code
335
+ processHtmlClass: 'step-section|latex-display', // Process text/latex sections
336
+ skipHtmlTags: ['script', 'noscript', 'style', 'textarea', 'pre', 'code']
337
  },
338
  loader: {
339
  load: ['[tex]/ams', '[tex]/noerrors', '[tex]/physics', '[tex]/cancel', '[tex]/color', '[tex]/mhchem', '[tex]/mathtools']
340
  },
 
 
 
 
 
 
 
 
341
  svg: {
342
+ fontCache: 'global' // Good for performance
343
  }
344
  };
345
  </script>
346
  <script id="MathJax-script" async src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/3.2.2/es5/tex-svg.js"></script>
347
 
 
348
  <script>
349
+ document.getElementById('imageInput').addEventListener('change', function(event) {
350
+ const file = event.target.files[0];
351
+ if (file) {
352
+ const reader = new FileReader();
353
+ reader.onload = function(e) {
354
+ document.getElementById('preview').src = e.target.result;
355
+ document.getElementById('imagePreview').style.display = 'block';
356
+ document.getElementById('solveButton').style.display = 'inline-block';
357
+ document.getElementById('uploadStatus').textContent = `Image sélectionnée : ${file.name}`;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
358
  }
359
+ reader.readAsDataURL(file);
360
+ }
361
+ });
362
 
363
+ document.getElementById('solveButton').addEventListener('click', function() {
364
+ const formData = new FormData(document.getElementById('imageForm'));
365
+ const solutionOutput = document.getElementById('solutionOutput');
366
+ const loadingIndicator = document.getElementById('loadingIndicator');
367
+ const solutionContainer = document.getElementById('solution'); // Target the inner container
368
+
369
+ solutionOutput.style.display = 'block';
370
+ loadingIndicator.style.display = 'flex';
371
+ solutionContainer.innerHTML = ''; // Clear previous solution
372
+
373
+ fetch('/solved', { // Ensure this endpoint exists and processes the image
374
+ method: 'POST',
375
+ body: formData
376
+ })
377
+ .then(response => {
378
+ if (!response.ok) {
379
+ throw new Error(`Erreur serveur: ${response.statusText}`);
380
  }
381
+ const reader = response.body.getReader();
382
+ const decoder = new TextDecoder();
383
+ let buffer = '';
384
+
385
+ function processStream({ done, value }) {
386
+ if (done) {
387
+ loadingIndicator.style.display = 'none';
388
+ return;
 
 
 
 
389
  }
 
 
 
390
 
391
+ buffer += decoder.decode(value, { stream: true });
392
+ // Split reliably by double newline, handling different line ending types
393
+ const lines = buffer.split(/\r?\n\r?\n/);
394
+ buffer = lines.pop(); // Keep potentially incomplete chunk
395
+
396
+ for (const line of lines) {
397
+ if (line.startsWith('data: ')) {
398
+ try {
399
+ const data = JSON.parse(line.substr(6));
400
+
401
+ if (data.mode) {
402
+ // Update loading indicator (same as before)
403
+ if (data.mode === 'thinking') { /* ... */ }
404
+ else if (data.mode === 'answering') { /* ... */ }
405
+ /* ... other modes ... */
406
+
407
+ // Simpler indicator update
408
+ const modes = {
409
+ thinking: { icon: 'fa-brain', text: 'Je réfléchis...', class: 'thinking-indicator' },
410
+ answering: { icon: 'fa-pencil-alt', text: 'Rédaction...', class: 'answering-indicator' },
411
+ executing_code: { icon: 'fa-code', text: 'Exécution...', class: 'executing-indicator' },
412
+ code_result: { icon: 'fa-terminal', text: 'Résultats...', class: 'executing-indicator' }
413
+ };
414
+ const modeInfo = modes[data.mode];
415
+ if (modeInfo) {
416
+ loadingIndicator.className = modeInfo.class;
417
+ loadingIndicator.innerHTML = `<i class="fas ${modeInfo.icon} indicator-icon"></i><span>${modeInfo.text}</span>`;
 
 
 
 
418
  }
 
 
419
  }
 
 
 
 
 
 
420
 
421
+ if (data.content) {
422
+ const content = data.content;
423
+ // Create a temporary div to parse the potential HTML structure from content
424
+ const tempDiv = document.createElement('div');
425
+ tempDiv.innerHTML = content; // Let browser parse potential HTML within content
426
+
427
+ // Check if content contains a code block structure
428
+ const codeSection = tempDiv.querySelector('.code-section');
429
+ const outputSection = tempDiv.querySelector('.output-section');
430
+
431
+ if (codeSection) {
432
+ // It's a code block, append it directly
433
+ solutionContainer.appendChild(codeSection);
434
+ // Highlight the code within the appended section
435
+ codeSection.querySelectorAll('pre code').forEach((block) => {
436
+ hljs.highlightElement(block);
437
+ });
438
+ } else if (outputSection) {
439
+ // It's an output block
440
+ solutionContainer.appendChild(outputSection);
441
  }
442
+ else {
443
+ // Assume it's a regular text/LaTeX step section
444
+ const stepDiv = document.createElement('div');
445
+ stepDiv.className = 'step-section';
446
+ // Use innerHTML to correctly interpret potential entities from JSON string
447
+ stepDiv.innerHTML = content;
448
+ solutionContainer.appendChild(stepDiv);
449
+ // Typeset MathJax for this specific new section
450
+ MathJax.typesetPromise([stepDiv]).catch(e => console.error('MathJax typesetting error:', e));
451
  }
452
+ }
453
 
454
+
455
+ if (data.error) {
456
+ const errorDiv = document.createElement('div');
457
+ errorDiv.className = 'step-section'; // Style like other steps
458
+ errorDiv.style.color = 'red';
459
+ errorDiv.style.backgroundColor = '#ffeeee';
460
+ errorDiv.style.borderColor = 'red';
461
+ errorDiv.textContent = `Erreur: ${data.error}`;
462
+ solutionContainer.appendChild(errorDiv);
463
+ loadingIndicator.style.display = 'none';
 
464
  }
465
+ } catch (e) {
466
+ console.error('Error parsing JSON or processing chunk:', e, line);
467
+ // Optionally display a generic error message chunk
468
+ const errorDiv = document.createElement('div');
469
+ errorDiv.className = 'step-section'; errorDiv.style.color = 'orange';
470
+ errorDiv.textContent = `Erreur traitement chunk: ${line.substring(0, 100)}...`;
471
+ solutionContainer.appendChild(errorDiv);
472
  }
473
  }
 
 
474
  }
475
+
476
+ window.scrollTo(0, document.body.scrollHeight);
477
  return reader.read().then(processStream);
478
+ }
479
+
480
+ return reader.read().then(processStream);
481
+ })
482
+ .catch(error => {
483
+ const errorDiv = document.createElement('div');
484
+ errorDiv.className = 'step-section'; // Style like other steps
485
+ errorDiv.style.color = 'red';
486
+ errorDiv.style.backgroundColor = '#ffeeee';
487
+ errorDiv.style.borderColor = 'red';
488
+ errorDiv.textContent = `Erreur de connexion ou du serveur: ${error}`;
489
+ solutionContainer.appendChild(errorDiv); // Append error inside the solution area
490
+ loadingIndicator.style.display = 'none';
491
+ console.error('Fetch error:', error);
492
  });
493
  });
494
  </script>