Docfile commited on
Commit
4ea31b4
·
verified ·
1 Parent(s): 4896d95

Update templates/index.html

Browse files
Files changed (1) hide show
  1. templates/index.html +487 -245
templates/index.html CHANGED
@@ -3,293 +3,535 @@
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>Résolveur avec Gemini AI</title>
7
- <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
8
- <script src="https://polyfill.io/v3/polyfill.min.js?features=es6"></script>
9
- <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script>
10
  <style>
11
- .drop-zone {
12
- max-width: 100%;
13
- height: 200px;
14
- padding: 25px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
  display: flex;
16
  align-items: center;
17
- justify-content: center;
18
- text-align: center;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  cursor: pointer;
20
- color: #444;
21
- border: 2px dashed #009578;
22
- border-radius: 10px;
23
  }
24
-
25
- .drop-zone--over {
26
- border-style: solid;
 
 
27
  }
28
-
29
- .drop-zone__input {
30
- display: none;
 
 
31
  }
32
-
33
- .drop-zone__thumb {
34
- width: 100%;
35
- height: 100%;
36
- border-radius: 10px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
  overflow: hidden;
38
- background-color: #cccccc;
39
- background-size: cover;
40
- position: relative;
41
- }
42
-
43
- .drop-zone__thumb::after {
44
- content: attr(data-label);
45
- position: absolute;
46
- bottom: 0;
47
- left: 0;
48
- width: 100%;
49
- padding: 5px 0;
50
- color: #ffffff;
51
- background: rgba(0, 0, 0, 0.75);
52
  font-size: 14px;
53
- text-align: center;
 
 
 
54
  }
55
-
56
- #response, #thinking {
57
- white-space: pre-wrap;
58
  padding: 15px;
59
- border-radius: 10px;
60
- background-color: #f8f9fa;
61
- margin-top: 20px;
 
 
 
62
  }
63
-
64
- #thinking {
65
- color: #666;
66
- font-style: italic;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
67
  }
68
-
69
- .loading {
70
- display: none;
 
 
71
  text-align: center;
72
- margin-top: 20px;
73
- }
74
-
75
- .loading-spinner {
76
- width: 50px;
77
- height: 50px;
78
- border: 5px solid rgba(0, 0, 0, 0.1);
79
- border-radius: 50%;
80
- border-top-color: #009578;
81
- animation: spin 1s ease-in-out infinite;
82
- margin: 0 auto;
83
  }
84
-
85
- @keyframes spin {
86
- to { transform: rotate(360deg); }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
87
  }
88
  </style>
89
  </head>
90
  <body>
91
- <div class="container my-5">
92
- <h1 class="text-center mb-4">Résolveur de problèmes avec Gemini AI</h1>
93
-
94
- <div class="row mb-4">
95
- <div class="col-md-6 offset-md-3">
96
- <div class="card">
97
- <div class="card-body">
98
- <h5 class="card-title">Téléchargez une image de votre problème</h5>
99
- <div class="drop-zone">
100
- <span class="drop-zone__prompt">Déposez votre image ici ou cliquez pour parcourir</span>
101
- <input type="file" name="image" class="drop-zone__input" accept="image/*">
102
- </div>
103
-
104
- <div class="d-grid gap-2 mt-3">
105
- <button id="solveBtn" class="btn btn-primary" disabled>Résoudre</button>
106
- <button id="fastSolveBtn" class="btn btn-outline-primary" disabled>Résolution rapide (moins précise)</button>
107
- </div>
108
- </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
109
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
110
  </div>
111
  </div>
112
 
113
- <div class="loading">
114
- <div class="loading-spinner"></div>
115
- <p class="mt-2">Traitement en cours...</p>
116
- </div>
117
-
118
- <div id="thinking" style="display: none;">
119
- <h4>Gemini réfléchit...</h4>
120
- <div id="thinking-content"></div>
121
- </div>
122
-
123
- <div id="response" style="display: none;"></div>
124
  </div>
125
 
 
 
 
 
126
  <script>
127
- document.addEventListener("DOMContentLoaded", function() {
128
- // Gestion de la zone de dépôt d'image
129
- document.querySelectorAll(".drop-zone__input").forEach(inputElement => {
130
- const dropZoneElement = inputElement.closest(".drop-zone");
131
-
132
- dropZoneElement.addEventListener("click", e => {
133
- inputElement.click();
134
- });
135
-
136
- inputElement.addEventListener("change", e => {
137
- if (inputElement.files.length) {
138
- updateThumbnail(dropZoneElement, inputElement.files[0]);
139
- document.getElementById("solveBtn").disabled = false;
140
- document.getElementById("fastSolveBtn").disabled = false;
141
- }
142
- });
143
-
144
- dropZoneElement.addEventListener("dragover", e => {
145
- e.preventDefault();
146
- dropZoneElement.classList.add("drop-zone--over");
147
- });
148
-
149
- ["dragleave", "dragend"].forEach(type => {
150
- dropZoneElement.addEventListener(type, e => {
151
- dropZoneElement.classList.remove("drop-zone--over");
152
- });
153
- });
154
-
155
- dropZoneElement.addEventListener("drop", e => {
156
- e.preventDefault();
157
-
158
- if (e.dataTransfer.files.length) {
159
- inputElement.files = e.dataTransfer.files;
160
- updateThumbnail(dropZoneElement, e.dataTransfer.files[0]);
161
- document.getElementById("solveBtn").disabled = false;
162
- document.getElementById("fastSolveBtn").disabled = false;
163
- }
164
-
165
- dropZoneElement.classList.remove("drop-zone--over");
166
- });
167
- });
168
-
169
- function updateThumbnail(dropZoneElement, file) {
170
- let thumbnailElement = dropZoneElement.querySelector(".drop-zone__thumb");
171
-
172
- // Supprimer le texte de prompt
173
- if (dropZoneElement.querySelector(".drop-zone__prompt")) {
174
- dropZoneElement.querySelector(".drop-zone__prompt").remove();
175
  }
176
-
177
- // Première fois - créer l'élément thumbnail
178
- if (!thumbnailElement) {
179
- thumbnailElement = document.createElement("div");
180
- thumbnailElement.classList.add("drop-zone__thumb");
181
- dropZoneElement.appendChild(thumbnailElement);
182
- }
183
-
184
- thumbnailElement.dataset.label = file.name;
185
-
186
- // Afficher l'image
187
- if (file.type.startsWith("image/")) {
188
- const reader = new FileReader();
189
-
190
- reader.readAsDataURL(file);
191
- reader.onload = () => {
192
- thumbnailElement.style.backgroundImage = `url('${reader.result}')`;
193
- };
194
- } else {
195
- thumbnailElement.style.backgroundImage = null;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
196
  }
 
197
  }
 
 
 
 
 
 
 
198
 
199
- // Gestion des boutons
200
- document.getElementById("solveBtn").addEventListener("click", function() {
201
- processImage("/solve");
202
- });
203
 
204
- document.getElementById("fastSolveBtn").addEventListener("click", function() {
205
- processImage("/solved");
206
- });
207
 
208
- // Fonction pour envoyer l'image et traiter la réponse
209
- function processImage(endpoint) {
210
- const fileInput = document.querySelector(".drop-zone__input");
211
- if (!fileInput.files.length) return;
212
-
213
- const file = fileInput.files[0];
214
- const formData = new FormData();
215
- formData.append("image", file);
216
-
217
- // Réinitialiser l'affichage
218
- document.getElementById("response").style.display = "none";
219
- document.getElementById("response").innerHTML = "";
220
- document.getElementById("thinking").style.display = "none";
221
- document.getElementById("thinking-content").innerHTML = "";
222
- document.querySelector(".loading").style.display = "block";
223
 
224
- // Désactiver les boutons pendant le traitement
225
- document.getElementById("solveBtn").disabled = true;
226
- document.getElementById("fastSolveBtn").disabled = true;
227
-
228
- // Configurer SSE
229
- const eventSource = new EventSource(endpoint, {
230
- method: "POST",
231
- body: formData
232
- });
233
-
234
- let mode = "starting";
235
-
236
- eventSource.onmessage = function(event) {
237
- const data = JSON.parse(event.data);
238
-
239
- // Masquer le chargement initial
240
- document.querySelector(".loading").style.display = "none";
241
-
242
- // Gestion du mode
243
- if (data.mode) {
244
- mode = data.mode;
245
- if (mode === "thinking") {
246
- document.getElementById("thinking").style.display = "block";
247
- } else if (mode === "answering") {
248
- document.getElementById("response").style.display = "block";
249
  }
 
250
  }
251
 
252
- // Ajout du contenu
253
- if (data.content) {
254
- if (mode === "thinking") {
255
- document.getElementById("thinking-content").innerHTML += data.content;
256
- } else if (mode === "answering") {
257
- document.getElementById("response").innerHTML += data.content;
258
- // Rendre LaTeX si présent
259
- if (window.MathJax) {
260
- MathJax.typesetPromise([document.getElementById("response")]);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
261
  }
262
  }
263
  }
264
 
265
- // Gestion des erreurs
266
- if (data.error) {
267
- document.getElementById("response").style.display = "block";
268
- document.getElementById("response").innerHTML += `<div class="alert alert-danger">${data.error}</div>`;
269
- eventSource.close();
270
- }
271
- };
272
-
273
- eventSource.onerror = function(error) {
274
- document.querySelector(".loading").style.display = "none";
275
- document.getElementById("response").style.display = "block";
276
- document.getElementById("response").innerHTML += `<div class="alert alert-danger">Erreur de connexion. Veuillez réessayer.</div>`;
277
- eventSource.close();
278
 
279
- // Réactiver les boutons
280
- document.getElementById("solveBtn").disabled = false;
281
- document.getElementById("fastSolveBtn").disabled = false;
282
- };
283
 
284
- // Fermeture de la connexion quand c'est terminé
285
- eventSource.addEventListener("done", function() {
286
- eventSource.close();
287
-
288
- // Réactiver les boutons
289
- document.getElementById("solveBtn").disabled = false;
290
- document.getElementById("fastSolveBtn").disabled = false;
291
- });
292
- }
 
 
 
 
293
  });
294
  </script>
295
  </body>
 
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Math Solver - Version Gratuite</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;
12
+ --secondary-color: #166088;
13
+ --accent-color: #4fc3f7;
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;
24
+ margin: 0;
25
+ padding: 0;
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);
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
+ }
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>
255
  <body>
256
+ <div class="container">
257
+ <header>
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>