Docfile commited on
Commit
601e910
·
verified ·
1 Parent(s): b88caed

Update templates/index.html

Browse files
Files changed (1) hide show
  1. templates/index.html +306 -415
templates/index.html CHANGED
@@ -3,461 +3,352 @@
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>Math Solver (MathJax) - 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
- <!-- PAS de CSS KaTeX -->
 
 
 
 
 
10
  <style>
11
- /* --- Styles CSS (Largement inchangés, suppression des refs spécifiques à KaTeX) --- */
12
- :root {
13
- --primary-color: #4a6fa5;
14
- --secondary-color: #166088;
15
- --accent-color: #4fc3f7;
16
- --background-color: #f8f9fa;
17
- --text-color: #333;
18
- --box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
19
- --code-bg: #2c323c;
20
- --output-bg: #f1f8f9;
21
- }
22
-
23
  body {
24
- font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
25
  line-height: 1.6;
26
- margin: 0;
27
- padding: 0;
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, .content-box > h1, .content-box > p:not(#uploadStatus),
39
- .upload-section h2, .upgrade-section, footer, #solutionOutput h3 {
40
- text-align: center;
41
  }
42
-
43
- .logo {
44
- font-size: 2.5rem; font-weight: bold; color: var(--primary-color); margin-bottom: 10px;
45
  }
46
- .subtitle {
47
- font-size: 1.2rem; color: var(--secondary-color); margin-bottom: 20px;
 
48
  }
49
- .content-box {
50
- background-color: white; border-radius: 10px; box-shadow: var(--box-shadow);
51
- padding: 30px; margin-bottom: 30px;
 
 
52
  }
53
- h1 { color: var(--primary-color); margin-top: 0; }
54
-
55
- .feature-list {
56
- list-style-type: none; padding: 0; margin: 30px auto; max-width: 600px; text-align: left;
57
  }
58
- .feature-list h2 { text-align: center; }
59
- .feature-list li { padding: 10px 0; margin-bottom: 10px; display: flex; align-items: center; }
60
- .feature-list i {
61
- color: var(--accent-color); margin-right: 10px; font-size: 1.2rem; width: 20px; text-align: center;
 
 
 
 
 
62
  }
63
- .feature-list .fa-times-circle { color: #aaa; }
64
-
65
- .cta-button {
66
- display: inline-block; background-color: var(--primary-color); color: white;
67
- padding: 12px 25px; border-radius: 5px; text-decoration: none; font-weight: bold;
68
- transition: all 0.3s ease; margin: 10px; border: none; cursor: pointer;
69
  }
70
- .cta-button:hover {
71
- background-color: var(--secondary-color); transform: translateY(-2px);
72
- box-shadow: 0 6px 12px rgba(0, 0, 0, 0.15);
 
 
 
 
73
  }
74
-
75
- .upload-section { margin-top: 30px; }
76
- #uploadStatus { margin-top: 10px; font-size: 0.9em; color: #555; min-height: 1.2em; }
77
- #imagePreview {
78
- display: none; margin: 20px auto; max-width: 400px; max-height: 300px;
79
- overflow: hidden; border: 1px solid #ddd; border-radius: 8px;
80
  }
81
- #preview { display: block; width: 100%; height: auto; border-radius: 8px; }
82
-
83
- footer { padding: 20px 0; color: #666; font-size: 0.9rem; }
84
-
85
- #solutionOutput { margin-top: 30px; text-align: left; display: none; }
86
- #solutionOutput h3 { margin-bottom: 20px; }
87
-
88
- #solution {
89
- background: transparent; padding: 0; border-radius: 8px; text-align: left;
90
- line-height: 1.8; font-size: 16px; box-shadow: var(--box-shadow);
91
- overflow: hidden; margin-top: 20px;
92
  }
93
-
94
- #solution > section { /* Utiliser section */
95
- padding: 20px; margin: 0; border-radius: 0; overflow-x: auto;
96
- background-color: #f9f9f9; border-bottom: 1px solid #eee;
97
- word-wrap: break-word; overflow-wrap: break-word;
98
  }
99
-
100
- .code-section { background-color: transparent !important; padding: 0 !important; border-bottom: none !important; }
101
- .output-section { background-color: var(--output-bg) !important; border-left: 4px solid var(--secondary-color); padding-left: calc(20px - 4px); }
102
- .step-section { background-color: #ffffff; border-left: 4px solid var(--primary-color); padding-left: calc(20px - 4px); }
103
-
104
- /* Styles spécifiques pour MathJax (si nécessaire, souvent pas besoin au début) */
105
- mjx-container[display="true"] { /* Cible les maths en mode display */
106
- display: block;
107
- overflow-x: auto; /* Scroll horizontal pour les équations longues */
108
- overflow-y: hidden;
109
- margin: 1em 0;
110
- padding: 0.5em 0.2em;
111
- background-color: #fdfdfd;
112
- border-radius: 4px;
113
- text-align: center; /* Centrer le conteneur */
114
  }
115
- /* Pour s'assurer que le texte normal est bien aligné avec les maths inline */
116
- mjx-container { line-height: 0; } /* MathJax ajuste souvent cela */
117
-
118
-
119
- .code-header {
120
- background-color: #343a40; color: white; padding: 10px 15px; font-size: 14px;
121
- font-family: 'Courier New', monospace; display: flex; justify-content: space-between; align-items: center;
122
  }
123
- .code-content {
124
- margin: 0; padding: 15px; background-color: var(--code-bg); color: #e6e6e6;
125
- overflow-x: auto; font-family: 'Courier New', monospace; font-size: 14px;
126
- line-height: 1.5; border-bottom: 1px solid #444;
 
 
127
  }
128
-
129
- #solution > section:first-child,
130
- #solution > section.code-section:first-child .code-header {
131
- border-top-left-radius: 8px; border-top-right-radius: 8px;
132
  }
133
- #solution > section:last-child,
134
- #solution > section.code-section:last-child .code-content {
135
- border-bottom-left-radius: 8px; border-bottom-right-radius: 8px; border-bottom: none;
 
 
 
 
 
136
  }
137
- #solution > section:only-child { border-radius: 8px; }
138
-
139
- .thinking-indicator, .executing-indicator, .answering-indicator {
140
- display: flex; align-items: center; justify-content: center; padding: 12px 15px;
141
- margin: 0 auto 20px auto; border-radius: 8px; font-size: 0.95rem;
142
- box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05); max-width: 400px;
143
  }
144
- .thinking-indicator { background-color: #e3f2fd; color: #1565c0; border: 1px solid #bbdefb; }
145
- .executing-indicator { background-color: #ede7f6; color: #5e35b1; border: 1px solid #d1c4e9; }
146
- .answering-indicator { background-color: #e8f5e9; color: #2e7d32; border: 1px solid #c8e6c9; }
147
-
148
- .indicator-icon { margin-right: 12px; animation: pulse 1.5s infinite ease-in-out; font-size: 1.1rem; }
149
- @keyframes pulse { 0% { opacity: 0.6; transform: scale(1); } 50% { opacity: 1; transform: scale(1.05); } 100% { opacity: 0.6; transform: scale(1); } }
150
-
151
- .error-message {
152
- background-color: #fff0f0 !important; color: red !important; border-color: red !important; font-weight: bold;
153
  }
154
- .error-message code { background-color: #fdd; color: #c00; padding: 2px 4px; border-radius: 3px; font-family: monospace; }
155
-
156
- .step-section p { margin-bottom: 1em; }
157
- .step-section p:last-child { margin-bottom: 0; }
158
- /* Styles Markdown basiques si le serveur envoie du HTML simple pour listes/etc */
159
- .step-section ul, .step-section ol { padding-left: 30px; margin-bottom: 1em; }
160
- .step-section li { margin-bottom: 0.5em; }
161
- .step-section code { font-family: monospace; background-color: #eee; padding: 2px 4px; border-radius: 3px; font-size: 0.9em; }
162
- .step-section pre { background-color: #f0f0f0; border-radius: 4px; padding: 12px; overflow-x: auto; margin: 1em 0; border: 1px solid #ddd; }
163
- .step-section pre code { background: transparent; padding: 0; border-radius: 0; font-size: 0.95em; }
164
-
165
-
166
  </style>
167
  </head>
168
  <body>
 
 
169
  <div class="container">
170
- <header>
171
- <div class="logo">Math Solver (MathJax)</div>
172
- <div class="subtitle">La solution intelligente pour vos problèmes mathématiques</div>
173
- </header>
174
-
175
- <div class="content-box">
176
- <h1>Version Gratuite</h1>
177
- <p>Vous utilisez actuellement la version gratuite de Math Solver qui vous permet de résoudre 3 problèmes par jour.</p>
178
-
179
- <div class="feature-list">
180
- <h2>Fonctionnalités disponibles :</h2>
181
- <ul>
182
- <li><i class="fas fa-check-circle"></i> Résolution de problèmes mathématiques basiques</li>
183
- <li><i class="fas fa-check-circle"></i> 3 résolutions gratuites par jour</li>
184
- <li><i class="fas fa-check-circle"></i> Explication des étapes de résolution (avec MathJax)</li>
185
- <li><i class="fas fa-times-circle"></i> <span style="color: #999;">Mode d'exécution de code avancé (version Pro)</span></li>
186
- <li><i class="fas fa-times-circle"></i> <span style="color: #999;">Résolutions illimitées (version Pro)</span></li>
187
- <li><i class="fas fa-times-circle"></i> <span style="color: #999;">Support prioritaire (version Pro)</span></li>
188
- </ul>
189
  </div>
190
-
191
- <div class="upload-section">
192
- <h2>Soumettez votre problème</h2>
193
- <form id="imageForm" enctype="multipart/form-data">
194
- <input type="file" id="imageInput" name="image" accept="image/*" style="display: none;">
195
- <button type="button" id="uploadButton" class="cta-button" onclick="document.getElementById('imageInput').click()">
196
- <i class="fas fa-upload"></i> Télécharger une image
197
- </button>
198
- <p id="uploadStatus"></p>
199
- </form>
200
-
201
- <div id="imagePreview"><img id="preview"></div>
202
-
203
- <button id="solveButton" class="cta-button" style="display: none; background-color: var(--secondary-color);">
204
- <i class="fas fa-calculator"></i> Résoudre ce problème
205
- </button>
206
- </div>
207
-
208
- <div id="solutionOutput">
209
- <h3>Solution :</h3>
210
- <div id="loadingIndicator" class="thinking-indicator" style="display: none;">
211
- <i class="fas fa-brain indicator-icon"></i>
212
- <span>Je réfléchis au problème...</span>
213
- </div>
214
- <div id="solution">
215
- <!-- Contenu dynamique ajouté ici -->
216
- </div>
217
- </div>
218
-
219
- <div class="upgrade-section">
220
- <h2>Besoin de plus de puissance ?</h2>
221
- <p>Passez à la version Pro pour des fonctionnalités avancées et des résolutions illimitées.</p>
222
- <a href="#" class="cta-button">Passer à la version Pro</a>
223
  </div>
224
- </div>
225
-
226
- <footer>
227
- <p>© 2025 Math Solver. Tous droits réservés.</p>
228
- </footer>
229
  </div>
230
-
231
- <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/highlight.min.js"></script>
232
- <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/languages/python.min.js"></script>
233
-
234
- <!-- Configuration MathJax (DOIT être avant le script principal) -->
235
- <script>
236
- window.MathJax = {
237
- tex: {
238
- inlineMath: [['$', '$'], ['\\(', '\\)']], // Délimiteurs inline
239
- displayMath: [['$$', '$$'], ['\\[', '\\]']], // Délimiteurs display
240
- processEscapes: true, // Permet d'échapper les $ avec \$
241
- tags: 'ams', // Numérotation des équations style AMS
242
- packages: {'[+]': ['ams']} // Charger le package AMS pour plus de commandes
243
- },
244
- options: {
245
- skipInitialTypeset: true, // Très important : on ne type pas la page au début
246
- ignoreHtmlClass: 'tex2jax_ignore', // Classes à ignorer
247
- processHtmlClass: 'tex2jax_process' // Classes à traiter explicitement (si besoin)
248
- },
249
- chtml: {
250
- matchFontHeight: true, // Tente d'ajuster la taille à la police environnante
251
- scale: 1.0 // Ajustement global de la taille (1.0 = normal)
252
- },
253
- loader: {
254
- load: ['[tex]/ams'] // Assure le chargement du package AMS
255
- }
256
- };
257
- </script>
258
- <!-- Script principal MathJax v3 -->
259
- <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"></script>
260
-
261
  <script>
262
- // Pas besoin de config marked.js ici
263
-
264
- document.getElementById('imageInput').addEventListener('change', function(event) {
265
- const file = event.target.files[0];
266
- if (file) {
267
- const reader = new FileReader();
268
- reader.onload = function(e) {
269
- document.getElementById('preview').src = e.target.result;
270
- document.getElementById('imagePreview').style.display = 'block';
271
- document.getElementById('solveButton').style.display = 'inline-block';
272
- document.getElementById('uploadStatus').textContent = `Image sélectionnée : ${file.name}`;
273
- document.getElementById('solutionOutput').style.display = 'none';
274
- document.getElementById('solution').innerHTML = '';
275
- }
276
- reader.readAsDataURL(file);
277
  }
278
  });
 
 
 
 
 
 
 
 
279
 
280
- document.getElementById('solveButton').addEventListener('click', function() {
281
- const formData = new FormData(document.getElementById('imageForm'));
282
- const solutionOutputDiv = document.getElementById('solutionOutput');
283
- const loadingIndicator = document.getElementById('loadingIndicator');
284
- const solutionContainer = document.getElementById('solution');
285
-
286
- solutionOutputDiv.style.display = 'block';
287
- loadingIndicator.style.display = 'flex';
288
- solutionContainer.innerHTML = '';
289
- loadingIndicator.scrollIntoView({ behavior: 'smooth', block: 'center' });
290
-
291
- fetch('/solved', { // Endpoint Flask
292
- method: 'POST',
293
- body: formData
294
- })
295
- .then(response => {
296
- if (!response.ok) {
297
- return response.text().then(text => {
298
- throw new Error(`Erreur serveur: ${response.status} ${response.statusText}\n${text || '(Aucun détail)'}`);
299
- });
300
- }
301
- const reader = response.body.getReader();
302
- const decoder = new TextDecoder();
303
- let buffer = '';
304
- let elementsToTypeset = []; // Pour regrouper les éléments à traiter par MathJax
305
-
306
- // Fonction pour traiter les éléments en attente avec MathJax
307
- function processPendingMathJax() {
308
- if (elementsToTypeset.length > 0) {
309
- // Copier et vider le tableau avant l'appel asynchrone
310
- const elements = [...elementsToTypeset];
311
- elementsToTypeset = [];
312
- // Appeler MathJax.typesetPromise sur les éléments ajoutés
313
- if (window.MathJax && window.MathJax.typesetPromise) {
314
- window.MathJax.typesetPromise(elements).catch((err) => {
315
- console.error('Erreur MathJax typesetting:', err);
316
- // Afficher une erreur utilisateur si pertinent
317
- elements.forEach(el => {
318
- const errorDiv = document.createElement('div');
319
- errorDiv.style.color = 'red';
320
- errorDiv.textContent = '[Erreur MathJax]';
321
- el.appendChild(errorDiv);
322
- });
323
- });
324
- } else {
325
- console.warn("MathJax n'est pas encore prêt ou typesetPromise n'existe pas.");
326
- // Remettre les éléments dans la file d'attente pour un essai ultérieur ?
327
- // elementsToTypeset.push(...elements); // Attention aux boucles infinies
328
- }
329
- }
330
- }
331
-
332
-
333
- function processStream({ done, value }) {
334
- if (done) {
335
- loadingIndicator.style.display = 'none';
336
- // Traiter les derniers éléments restants
337
- processPendingMathJax();
338
- return;
339
- }
340
-
341
- buffer += decoder.decode(value, { stream: true });
342
- const messages = buffer.split(/\r?\n\r?\n/);
343
- buffer = messages.pop();
344
-
345
- messages.forEach(message => {
346
- if (message.startsWith('data: ')) {
347
- try {
348
- const data = JSON.parse(message.substring(6));
349
- let blockAdded = null; // Référence au bloc ajouté
350
-
351
- // --- Gérer les mises à jour de mode ---
352
- if (data.mode) {
353
- const modes = { /* ... (comme avant) ... */ };
354
- const modeInfo = modes[data.mode] || { icon: 'fa-sync-alt fa-spin', text: 'Traitement...', class: 'thinking-indicator' };
355
- loadingIndicator.className = `${modeInfo.class}`;
356
- loadingIndicator.innerHTML = `<i class="fas ${modeInfo.icon} indicator-icon"></i><span>${modeInfo.text}</span>`;
357
- loadingIndicator.style.display = 'flex';
358
- }
359
-
360
- // --- Gérer les blocs de contenu ---
361
- if (data.content !== undefined && data.content !== null) {
362
- loadingIndicator.style.display = 'none';
363
- const content = data.content;
364
- const tempDiv = document.createElement('div');
365
- tempDiv.innerHTML = content;
366
-
367
- const codeSection = tempDiv.querySelector('.code-section');
368
- const outputSection = tempDiv.querySelector('.output-section');
369
- let newElement = document.createElement('section'); // Conteneur par défaut
370
-
371
- if (codeSection) {
372
- // Code Python dédié
373
- newElement.outerHTML = codeSection.outerHTML; // Remplacer la section par le contenu du code
374
- // On doit récupérer la référence APRES ajout au DOM
375
- blockAdded = solutionContainer.appendChild(newElement.cloneNode(true));
376
- blockAdded.querySelectorAll('pre code').forEach((block) => {
377
- hljs.highlightElement(block);
378
- });
379
- // Pas de MathJax sur le code python a priori
380
-
381
- } else if (outputSection) {
382
- // Sortie de code
383
- newElement.outerHTML = outputSection.outerHTML;
384
- blockAdded = solutionContainer.appendChild(newElement.cloneNode(true));
385
- // Ajouter aux éléments à traiter par MathJax
386
- if (blockAdded) elementsToTypeset.push(blockAdded);
387
-
388
- } else {
389
- // Section d'étape (Texte + LaTeX potentiel)
390
- newElement.className = 'step-section';
391
- newElement.innerHTML = content; // Injecter le contenu HTML brut
392
- blockAdded = solutionContainer.appendChild(newElement);
393
- // Ajouter aux éléments à traiter par MathJax
394
- if (blockAdded) elementsToTypeset.push(blockAdded);
395
- }
396
  }
397
-
398
- // --- Gérer les erreurs spécifiques ---
399
- if (data.error) {
400
- const errorElement = document.createElement('section');
401
- errorElement.className = 'step-section error-message';
402
- errorElement.innerHTML = `<strong>Erreur :</strong> ${data.error || 'Une erreur inconnue est survenue.'}`;
403
- blockAdded = solutionContainer.appendChild(errorElement);
404
- loadingIndicator.style.display = 'none';
405
- // Pas besoin de MathJax sur les erreurs
 
 
 
 
 
 
 
 
 
 
 
 
 
406
  }
407
-
408
- // Déclencher le rendu MathJax périodiquement ou après chaque message
409
- // (Regrouper améliore un peu les perfs vs appeler à chaque fois)
410
- // On pourrait utiliser un debounce ou juste après chaque message traité ici
411
- processPendingMathJax();
412
-
413
-
414
- } catch (e) {
415
- console.error('Erreur analyse JSON ou traitement message SSE:', e, message);
416
- const errorElement = document.createElement('section');
417
- errorElement.className = 'step-section error-message';
418
- errorElement.style.color = 'orange'; errorElement.style.backgroundColor = '#fff8e1'; errorElement.style.borderColor = 'orange';
419
- errorElement.textContent = `Erreur traitement message: ${message.substring(0, 150)}...`;
420
- solutionContainer.appendChild(errorElement);
421
- loadingIndicator.style.display = 'none';
422
  }
 
 
423
  }
424
- });
425
-
426
- // Défiler vers le bas
427
- solutionContainer.lastChild?.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
428
-
429
- // Continuer à lire le flux
430
- return reader.read().then(processStream);
431
- }
432
-
433
- // Démarrer le traitement du flux
434
- // S'assurer que MathJax est prêt avant de commencer (optionnel mais plus sûr)
435
- if (window.MathJax && window.MathJax.startup) {
436
- window.MathJax.startup.promise.then(() => {
437
- console.log('MathJax prêt, démarrage du stream processing.');
438
- reader.read().then(processStream);
439
- });
440
- } else {
441
- // Fallback si MathJax n'est pas encore configuré (ne devrait pas arriver avec script async + config avant)
442
- console.warn('MathJax pas encore prêt, démarrage direct du stream.');
443
- reader.read().then(processStream);
444
- }
445
-
446
- })
447
- .catch(error => {
448
- console.error('Erreur Fetch ou connexion:', error);
449
- const errorElement = document.createElement('section');
450
- errorElement.className = 'step-section error-message';
451
- errorElement.innerHTML = `<strong>Erreur de connexion ou serveur :</strong> ${error.message || error}`;
452
- solutionContainer.appendChild(errorElement);
453
- loadingIndicator.style.display = 'none';
454
- errorElement.scrollIntoView({ behavior: 'smooth', block: 'center' });
455
- });
456
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
457
 
458
- // Pas besoin d'event listener DOMContentLoaded pour MathJax avec skipInitialTypeset: true
459
- console.log("Page chargée. MathJax configuré pour rendu manuel.");
460
-
461
- </script>
462
  </body>
463
  </html>
 
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Résolution d'exercices avec Gemini</title>
7
+
8
+ <!-- Inclusion de MathJax pour le rendu LaTeX -->
9
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.7/MathJax.js?config=TeX-MML-AM_CHTML"></script>
10
+
11
+ <!-- Inclusion de highlight.js pour la coloration syntaxique -->
12
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/styles/default.min.css">
13
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/highlight.min.js"></script>
14
+
15
  <style>
 
 
 
 
 
 
 
 
 
 
 
 
16
  body {
17
+ font-family: Arial, sans-serif;
18
  line-height: 1.6;
19
+ max-width: 800px;
 
 
 
 
 
 
 
20
  margin: 0 auto;
21
  padding: 20px;
22
  }
23
+
24
+ h1, h2 {
25
+ color: #333;
 
26
  }
27
+
28
+ .container {
29
+ margin-bottom: 30px;
30
  }
31
+
32
+ .form-group {
33
+ margin-bottom: 15px;
34
  }
35
+
36
+ label {
37
+ display: block;
38
+ margin-bottom: 5px;
39
+ font-weight: bold;
40
  }
41
+
42
+ input[type="file"] {
43
+ margin-bottom: 10px;
 
44
  }
45
+
46
+ button {
47
+ background-color: #4CAF50;
48
+ color: white;
49
+ padding: 10px 15px;
50
+ border: none;
51
+ border-radius: 4px;
52
+ cursor: pointer;
53
+ font-size: 16px;
54
  }
55
+
56
+ button:hover {
57
+ background-color: #45a049;
 
 
 
58
  }
59
+
60
+ #result-container {
61
+ margin-top: 30px;
62
+ border: 1px solid #ddd;
63
+ padding: 20px;
64
+ border-radius: 5px;
65
+ background-color: #f9f9f9;
66
  }
67
+
68
+ .thinking-indicator {
69
+ color: #666;
70
+ font-style: italic;
71
+ margin-bottom: 10px;
 
72
  }
73
+
74
+ .loading-indicator {
75
+ color: #666;
76
+ font-style: italic;
77
+ margin-bottom: 10px;
 
 
 
 
 
 
78
  }
79
+
80
+ .error-message {
81
+ color: red;
82
+ font-weight: bold;
83
+ margin-top: 10px;
84
  }
85
+
86
+ pre {
87
+ background-color: #f8f8f8;
88
+ border: 1px solid #ddd;
89
+ border-radius: 3px;
90
+ padding: 10px;
91
+ overflow-x: auto;
 
 
 
 
 
 
 
 
92
  }
93
+
94
+ code {
95
+ font-family: 'Courier New', Courier, monospace;
 
 
 
 
96
  }
97
+
98
+ .code-result {
99
+ background-color: #eaffea;
100
+ padding: 10px;
101
+ border-left: 3px solid #4CAF50;
102
+ margin-bottom: 20px;
103
  }
104
+
105
+ .markdown-content {
106
+ margin-bottom: 15px;
 
107
  }
108
+
109
+ img {
110
+ max-width: 100%;
111
+ height: auto;
112
+ margin: 10px 0;
113
+ border: 1px solid #ddd;
114
+ border-radius: 4px;
115
+ padding: 5px;
116
  }
117
+
118
+ .buttons-container {
119
+ display: flex;
120
+ gap: 10px;
121
+ margin-top: 20px;
 
122
  }
123
+
124
+ .tab-content {
125
+ display: none;
126
+ }
127
+
128
+ .tab-content.active {
129
+ display: block;
 
 
130
  }
 
 
 
 
 
 
 
 
 
 
 
 
131
  </style>
132
  </head>
133
  <body>
134
+ <h1>Résolution d'exercices avec Gemini</h1>
135
+
136
  <div class="container">
137
+ <h2>Mode Normal (Gemini Pro)</h2>
138
+ <form id="solve-form" enctype="multipart/form-data">
139
+ <div class="form-group">
140
+ <label for="image">Téléchargez une image de votre exercice:</label>
141
+ <input type="file" id="image" name="image" accept="image/*" required>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
142
  </div>
143
+ <button type="submit">Résoudre</button>
144
+ </form>
145
+ </div>
146
+
147
+ <div class="container">
148
+ <h2>Mode Rapide (Gemini Flash)</h2>
149
+ <form id="solved-form" enctype="multipart/form-data">
150
+ <div class="form-group">
151
+ <label for="image2">Téléchargez une image de votre exercice:</label>
152
+ <input type="file" id="image2" name="image" accept="image/*" required>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
153
  </div>
154
+ <button type="submit">Résoudre rapidement</button>
155
+ </form>
 
 
 
156
  </div>
157
+
158
+ <div id="result-container">
159
+ <p>Les résultats apparaîtront ici après avoir soumis une image.</p>
160
+ </div>
161
+
162
+ <!-- Inclusion du script JS personnalisé -->
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
163
  <script>
164
+ // Configuration de MathJax pour le rendu LaTeX
165
+ MathJax.Hub.Config({
166
+ tex2jax: {
167
+ inlineMath: [['$', '$'], ['\\(', '\\)']],
168
+ displayMath: [['$$', '$$'], ['\\[', '\\]']],
169
+ processEscapes: true
 
 
 
 
 
 
 
 
 
170
  }
171
  });
172
+
173
+ // Initialisation de highlight.js une fois que le DOM est chargé
174
+ document.addEventListener('DOMContentLoaded', function() {
175
+ document.querySelectorAll('pre code').forEach((block) => {
176
+ hljs.highlightBlock(block);
177
+ });
178
+ });
179
+ </script>
180
 
181
+ <script>
182
+
183
+ // Ce script doit être inclus dans votre template HTML
184
+
185
+ document.addEventListener('DOMContentLoaded', function() {
186
+ // Supposons que nous avons un élément pour afficher les résultats
187
+ const resultContainer = document.getElementById('result-container');
188
+
189
+ // Fonction pour créer un élément de code formaté
190
+ function createCodeElement(code) {
191
+ const pre = document.createElement('pre');
192
+ const codeEl = document.createElement('code');
193
+ codeEl.textContent = code;
194
+ pre.appendChild(codeEl);
195
+ return pre;
196
+ }
197
+
198
+ // Fonction pour créer un élément Markdown
199
+ function createMarkdownElement(text) {
200
+ // Vous pourriez utiliser une bibliothèque comme marked.js ici
201
+ // Pour simplifier, nous utilisons simplement un paragraphe
202
+ const div = document.createElement('div');
203
+ div.className = 'markdown-content';
204
+
205
+ // Détection simple des éléments LaTeX
206
+ // Remplacez ceci par une bibliothèque complète comme MathJax dans votre implémentation réelle
207
+ text = text.replace(/\$\$(.*?)\$\$/g, '<span class="latex-block">$$$1$$</span>');
208
+ text = text.replace(/\$(.*?)\$/g, '<span class="latex-inline">$$1$</span>');
209
+
210
+ div.innerHTML = text;
211
+ return div;
212
+ }
213
+
214
+ // Fonction pour créer un élément image
215
+ function createImageElement(base64Data, format = 'png') {
216
+ const img = document.createElement('img');
217
+ img.src = `data:image/${format};base64,${base64Data}`;
218
+ img.style.maxWidth = '100%';
219
+ return img;
220
+ }
221
+
222
+ // Fonction pour gérer les événements SSE
223
+ function setupEventSource(url, formData) {
224
+ // Vider le conteneur de résultats
225
+ resultContainer.innerHTML = '';
226
+
227
+ // Afficher un indicateur de chargement
228
+ const loadingIndicator = document.createElement('div');
229
+ loadingIndicator.className = 'loading-indicator';
230
+ loadingIndicator.textContent = 'Chargement en cours...';
231
+ resultContainer.appendChild(loadingIndicator);
232
+
233
+ // Envoyer la requête avec fetch pour téléverser l'image
234
+ fetch(url, {
235
+ method: 'POST',
236
+ body: formData
237
+ }).then(response => {
238
+ // Supprimer l'indicateur de chargement
239
+ resultContainer.removeChild(loadingIndicator);
240
+
241
+ // Créer un nouvel EventSource avec les données de la réponse
242
+ const reader = response.body.getReader();
243
+
244
+ function processText(text) {
245
+ const lines = text.split('\n\n');
246
+ lines.forEach(line => {
247
+ if (line.startsWith('data: ')) {
248
+ try {
249
+ const jsonData = JSON.parse(line.substring(6));
250
+
251
+ if (jsonData.mode === 'thinking') {
252
+ // Afficher un indicateur de réflexion
253
+ const thinkingIndicator = document.createElement('div');
254
+ thinkingIndicator.className = 'thinking-indicator';
255
+ thinkingIndicator.textContent = 'Gemini réfléchit...';
256
+ resultContainer.appendChild(thinkingIndicator);
257
+ } else if (jsonData.mode === 'answering') {
258
+ // Supprimer l'indicateur de réflexion s'il existe
259
+ const thinkingIndicator = resultContainer.querySelector('.thinking-indicator');
260
+ if (thinkingIndicator) {
261
+ resultContainer.removeChild(thinkingIndicator);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
262
  }
263
+ }
264
+
265
+ if (jsonData.content) {
266
+ let element;
267
+
268
+ switch(jsonData.type) {
269
+ case 'text':
270
+ element = createMarkdownElement(jsonData.content);
271
+ break;
272
+ case 'code':
273
+ element = createCodeElement(jsonData.content);
274
+ break;
275
+ case 'result':
276
+ element = createMarkdownElement(jsonData.content);
277
+ element.className = 'code-result';
278
+ break;
279
+ case 'image':
280
+ element = createImageElement(jsonData.content);
281
+ break;
282
+ default:
283
+ // Si aucun type n'est spécifié, on traite comme du texte
284
+ element = createMarkdownElement(jsonData.content);
285
  }
286
+
287
+ resultContainer.appendChild(element);
288
+
289
+ // Faire défiler vers le bas
290
+ window.scrollTo(0, document.body.scrollHeight);
291
+ }
292
+
293
+ if (jsonData.error) {
294
+ const errorElement = document.createElement('div');
295
+ errorElement.className = 'error-message';
296
+ errorElement.textContent = 'Erreur: ' + jsonData.error;
297
+ resultContainer.appendChild(errorElement);
 
 
 
298
  }
299
+ } catch (e) {
300
+ console.error('Erreur lors du parsing JSON:', e);
301
  }
302
+ }
303
+ });
304
+ }
305
+
306
+ // Fonction pour lire les chunks de données
307
+ function readChunk() {
308
+ return reader.read().then(({ done, value }) => {
309
+ if (done) {
310
+ return;
311
+ }
312
+
313
+ // Convertir le chunk en texte
314
+ const chunk = new TextDecoder().decode(value);
315
+ processText(chunk);
316
+
317
+ // Lire le prochain chunk
318
+ return readChunk();
319
+ });
320
+ }
321
+
322
+ return readChunk();
323
+ }).catch(error => {
324
+ console.error('Erreur:', error);
325
+ resultContainer.innerHTML = `<div class="error-message">Erreur de connexion: ${error.message}</div>`;
 
 
 
 
 
 
 
 
326
  });
327
+ }
328
+
329
+ // Gestion du formulaire pour /solve
330
+ const solveForm = document.getElementById('solve-form');
331
+ if (solveForm) {
332
+ solveForm.addEventListener('submit', function(e) {
333
+ e.preventDefault();
334
+ const formData = new FormData(solveForm);
335
+ setupEventSource('/solve', formData);
336
+ });
337
+ }
338
+
339
+ // Gestion du formulaire pour /solved
340
+ const solvedForm = document.getElementById('solved-form');
341
+ if (solvedForm) {
342
+ solvedForm.addEventListener('submit', function(e) {
343
+ e.preventDefault();
344
+ const formData = new FormData(solvedForm);
345
+ setupEventSource('/solved', formData);
346
+ });
347
+ }
348
+ });
349
+
350
+ </script>
351
+
352
 
 
 
 
 
353
  </body>
354
  </html>