Docfile commited on
Commit
010d7ee
1 Parent(s): 0696ad1

Update templates/math.html

Browse files
Files changed (1) hide show
  1. templates/math.html +45 -207
templates/math.html CHANGED
@@ -5,129 +5,37 @@
5
  <meta charset="UTF-8">
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
  <title>Résolution de Problèmes Mathématiques</title>
8
-
9
- <!-- Tailwind CSS -->
10
  <script src="https://cdn.tailwindcss.com"></script>
11
-
12
- <!-- Marked.js pour le Markdown -->
13
  <script defer src="https://cdnjs.cloudflare.com/ajax/libs/marked/9.1.6/marked.min.js"></script>
14
-
15
- <!-- Font Awesome pour les icônes -->
16
  <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
17
-
18
- <!-- SweetAlert2 -->
19
  <script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
20
-
21
  <style>
22
- /* Styles personnalisés */
23
- .dropzone {
24
- border: 2px dashed #4F46E5;
25
- transition: all 0.3s ease;
26
- }
27
-
28
- .dropzone:hover {
29
- border-color: #312E81;
30
- background-color: rgba(79, 70, 229, 0.1);
31
- }
32
-
33
- .loading {
34
- display: none;
35
- }
36
-
37
- .loading.active {
38
- display: flex;
39
- }
40
-
41
- .math-content {
42
- font-size: 1.1em;
43
- line-height: 1.6;
44
- overflow-x: auto;
45
- }
46
-
47
- .math-content p {
48
- margin-bottom: 1rem;
49
- white-space: pre-wrap;
50
- }
51
-
52
- .math-content .MathJax {
53
- overflow-x: auto;
54
- overflow-y: hidden;
55
- padding: 0.5rem 0;
56
- }
57
-
58
- @media (max-width: 640px) {
59
- .math-content .MathJax {
60
- font-size: 0.9em;
61
- }
62
- }
63
-
64
- /* Style pour masquer le contenu pendant le chargement de MathJax */
65
- .math-hidden {
66
- visibility: hidden;
67
- }
68
-
69
- .saved-response-header {
70
- cursor: pointer;
71
- display: flex;
72
- justify-content: space-between;
73
- align-items: center;
74
- padding: 0.75rem 1rem;
75
- background-color: #f3f4f6;
76
- border-bottom: 1px solid #e5e7eb;
77
- }
78
-
79
- .saved-response-content {
80
- padding: 1rem;
81
- display: none;
82
- }
83
-
84
- .saved-response-item.open .saved-response-content {
85
- display: block;
86
- }
87
  </style>
88
-
89
- <!-- Configuration de MathJax -->
90
  <script>
91
  window.MathJax = {
92
  tex: {
93
- inlineMath: [
94
- ['$', '$'],
95
- ['\\(', '\\)']
96
- ],
97
- displayMath: [
98
- ['$$', '$$'],
99
- ['\\[', '\\]']
100
- ],
101
  processEscapes: true,
102
- macros: {
103
- R: "{\\mathbb{R}}",
104
- N: "{\\mathbb{N}}",
105
- Z: "{\\mathbb{Z}}",
106
- vecv: ["\\begin{pmatrix}#1\\\\#2\\\\#3\\end{pmatrix}", 3]
107
- }
108
- },
109
- svg: {
110
- fontCache: 'global'
111
- },
112
- startup: {
113
- pageReady: () => {
114
- return Promise.resolve();
115
- }
116
  },
117
- options: {
118
- renderActions: {
119
- addMenu: [],
120
- checkLoading: [150, () => {
121
- document.querySelectorAll('.math-content').forEach(el => {
122
- el.classList.remove('math-hidden');
123
- });
124
- }]
125
- }
126
- }
127
  };
128
  </script>
129
-
130
- <!-- Chargement de MathJax -->
131
  <script>
132
  function loadMathJax() {
133
  return new Promise((resolve, reject) => {
@@ -140,27 +48,17 @@
140
  document.head.appendChild(script);
141
  });
142
  }
143
-
144
- // Charger MathJax immédiatement
145
  loadMathJax().catch(console.error);
146
  </script>
147
-
148
- <!-- localForage pour le stockage local -->
149
  <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/localforage.min.js"></script>
150
-
151
  </head>
152
 
153
  <body class="min-h-screen bg-gradient-to-br from-blue-50 to-white">
154
- <!-- Container principal -->
155
  <div class="container mx-auto px-4 py-8 max-w-4xl">
156
- <!-- En-tête -->
157
  <header class="text-center mb-12">
158
  <h1 class="text-4xl font-bold text-blue-800 mb-4">Résolution de Problèmes Mathématiques</h1>
159
- <p class="text-gray-600 text-lg">Soumettez une image de votre problème mathématique pour obtenir une solution
160
- détaillée</p>
161
  </header>
162
-
163
- <!-- Zone de dépôt d'image -->
164
  <div class="mb-8">
165
  <form id="uploadForm" class="space-y-4">
166
  <div id="dropzone"
@@ -169,14 +67,17 @@
169
  <div class="flex flex-col items-center space-y-4">
170
  <i class="fas fa-cloud-upload-alt text-4xl text-blue-600"></i>
171
  <div class="text-lg text-gray-700">
172
- Glissez votre image ici ou <span class="text-blue-600 font-semibold">cliquez pour
173
- sélectionner</span>
174
  </div>
175
  <p class="text-sm text-gray-500">Formats acceptés: PNG, JPG, JPEG</p>
176
  </div>
177
  </div>
178
 
179
- <div class="flex justify-center">
 
 
 
 
180
  <button type="submit"
181
  class="px-6 py-3 bg-blue-600 hover:bg-blue-700 text-white rounded-lg transition-colors duration-200 flex items-center space-x-2 disabled:opacity-50 disabled:cursor-not-allowed">
182
  <i class="fas fa-paper-plane"></i>
@@ -186,37 +87,28 @@
186
  </form>
187
  </div>
188
 
189
- <!-- Indicateur de chargement -->
190
  <div id="loading" class="loading flex-col items-center justify-center space-y-4 my-8">
191
  <div class="animate-spin rounded-full h-12 w-12 border-4 border-blue-200 border-t-blue-600"></div>
192
  <p class="text-gray-700 font-medium">Analyse en cours...</p>
193
  </div>
194
 
195
- <!-- Zone de réponse -->
196
  <div id="response" class="hidden">
197
  <div class="bg-white rounded-lg shadow-lg p-6 mb-8">
198
- <h2 class="text-2xl font-semibold text-blue-800 mb-4">Solution</h2>
199
- <div id="latexContent" class="prose max-w-none math-content math-hidden">
200
- <!-- Le contenu sera inséré ici -->
201
- </div>
202
  </div>
203
  </div>
204
 
205
- <!-- Réponses sauvegardées -->
206
  <div id="savedResponsesSection" class="mt-8">
207
  <div class="flex justify-between items-center mb-4">
208
  <h2 class="text-2xl font-semibold text-blue-800">Réponses Sauvegardées</h2>
209
- <button id="clearSavedResponses"
210
- class="px-4 py-2 bg-red-600 hover:bg-red-700 text-white rounded-lg transition-colors duration-200">
211
  <i class="fas fa-trash-alt"></i> Effacer Tout
212
  </button>
213
  </div>
214
- <div id="savedResponses" class="space-y-2">
215
- <!-- Les réponses sauvegardées seront affichées ici -->
216
- </div>
217
  </div>
218
 
219
- <!-- Message d'erreur -->
220
  <div id="errorMessage"
221
  class="hidden bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative my-4" role="alert">
222
  <strong class="font-bold">Erreur!</strong>
@@ -226,7 +118,6 @@
226
 
227
  <script>
228
  document.addEventListener('DOMContentLoaded', function () {
229
- // Éléments DOM
230
  const dropzone = document.getElementById('dropzone');
231
  const fileInput = document.getElementById('fileInput');
232
  const uploadForm = document.getElementById('uploadForm');
@@ -238,57 +129,37 @@
238
  const submitButton = uploadForm.querySelector('button[type="submit"]');
239
  const savedResponsesContainer = document.getElementById('savedResponses');
240
  const clearSavedResponsesButton = document.getElementById('clearSavedResponses');
 
 
241
 
242
- // Attendre que MathJax soit complètement chargé
243
  let mathJaxReady = false;
244
  window.MathJax.startup.promise.then(() => {
245
  mathJaxReady = true;
246
  });
247
 
248
- // Configuration de marked
249
- marked.setOptions({
250
- breaks: true,
251
- gfm: true,
252
- pedantic: false,
253
- smartLists: true
254
- });
255
 
256
- // Fonction pour afficher les erreurs
257
  function showError(message) {
258
  errorText.textContent = message;
259
  errorMessage.classList.remove('hidden');
260
- setTimeout(() => {
261
- errorMessage.classList.add('hidden');
262
- }, 5000);
263
  }
264
 
265
- // Fonction pour rendre le contenu mathématique
266
  async function renderMathContent(text) {
267
  try {
268
- // Attendre que MathJax soit prêt
269
  if (!mathJaxReady) {
270
  await window.MathJax.startup.promise;
271
  }
272
-
273
- // Nettoyer le contenu précédent
274
  latexContent.innerHTML = '';
275
  latexContent.classList.add('math-hidden');
276
-
277
- // Convertir le Markdown en HTML
278
  const htmlContent = marked.parse(text);
279
  latexContent.innerHTML = htmlContent;
280
-
281
- // Rendre les formules mathématiques avec MathJax
282
  await MathJax.typesetPromise([latexContent]);
283
-
284
- // Afficher le contenu
285
  response.classList.remove('hidden');
286
  latexContent.classList.remove('math-hidden');
287
-
288
  } catch (error) {
289
  console.error('Erreur lors du rendu:', error);
290
  showError('Erreur lors du rendu de la formule mathématique');
291
-
292
  latexContent.innerHTML = `
293
  <div class="text-red-600 mb-4">Une erreur s'est produite lors du rendu. Voici le texte brut :</div>
294
  <pre class="bg-gray-100 p-4 rounded-lg overflow-x-auto">${text}</pre>
@@ -297,24 +168,10 @@
297
  }
298
  }
299
 
300
- // Gestionnaire de drag & drop
301
- function handleDragOver(e) {
302
- e.preventDefault();
303
- e.stopPropagation();
304
- dropzone.classList.add('bg-blue-50');
305
- }
306
-
307
- function handleDragLeave(e) {
308
- e.preventDefault();
309
- e.stopPropagation();
310
- dropzone.classList.remove('bg-blue-50');
311
- }
312
-
313
  function handleDrop(e) {
314
- e.preventDefault();
315
- e.stopPropagation();
316
- dropzone.classList.remove('bg-blue-50');
317
-
318
  const files = e.dataTransfer.files;
319
  if (files.length > 0 && files[0].type.startsWith('image/')) {
320
  fileInput.files = files;
@@ -324,7 +181,6 @@
324
  }
325
  }
326
 
327
- // Gestion de la sélection de fichier
328
  function handleFileSelect(file) {
329
  if (file && file.type.startsWith('image/')) {
330
  const reader = new FileReader();
@@ -332,10 +188,8 @@
332
  const preview = document.createElement('img');
333
  preview.src = e.target.result;
334
  preview.classList.add('max-h-48', 'mx-auto', 'mt-4', 'rounded-lg');
335
-
336
  const oldPreview = dropzone.querySelector('img');
337
  if (oldPreview) oldPreview.remove();
338
-
339
  dropzone.appendChild(preview);
340
  submitButton.disabled = false;
341
  };
@@ -345,7 +199,6 @@
345
  }
346
  }
347
 
348
- // Gestionnaires d'événements
349
  dropzone.addEventListener('dragover', handleDragOver);
350
  dropzone.addEventListener('dragleave', handleDragLeave);
351
  dropzone.addEventListener('drop', handleDrop);
@@ -357,14 +210,11 @@
357
  }
358
  });
359
 
360
- // Fonction pour sauvegarder la réponse
361
- async function saveResponse(response) {
362
  const timestamp = new Date().getTime();
363
- const key = `response-${timestamp}`;
364
  try {
365
  await localforage.setItem(key, response);
366
- console.log('Réponse sauvegardée avec la clé:', key);
367
- // Recharger les réponses affichées après la sauvegarde
368
  loadSavedResponses();
369
  } catch (error) {
370
  console.error('Erreur lors de la sauvegarde:', error);
@@ -372,7 +222,6 @@
372
  }
373
  }
374
 
375
- // Fonction pour effacer toutes les réponses sauvegardées
376
  async function clearSavedResponses() {
377
  Swal.fire({
378
  title: 'Êtes-vous sûr?',
@@ -407,22 +256,22 @@
407
  });
408
  }
409
 
410
- // Fonction pour charger et afficher les réponses sauvegardées
411
  async function loadSavedResponses() {
412
  try {
413
  savedResponsesContainer.innerHTML = '';
414
  const keys = await localforage.keys();
415
- keys.sort((a, b) => parseInt(b.replace('response-', '')) - parseInt(a.replace('response-', '')));
416
 
417
  for (const key of keys) {
418
  const response = await localforage.getItem(key);
 
419
  const responseItem = document.createElement('div');
420
  responseItem.className = 'saved-response-item bg-white rounded-lg shadow-md overflow-hidden';
421
 
422
  const header = document.createElement('div');
423
  header.className = 'saved-response-header';
424
  header.innerHTML = `
425
- <span class="text-blue-800 font-medium">Réponse du ${new Date(parseInt(key.replace('response-', ''))).toLocaleString()}</span>
426
  <div>
427
  <button class="toggle-content px-1 py-0.5 rounded-md text-xs bg-blue-500 hover:bg-blue-700 text-white mr-1"><i class="fas fa-chevron-down"></i></button>
428
  <button class="delete-response px-1 py-0.5 rounded-md text-xs bg-red-500 hover:bg-red-700 text-white"><i class="fas fa-trash-alt"></i></button>
@@ -434,24 +283,19 @@
434
  content.className = 'saved-response-content math-content math-hidden';
435
  content.innerHTML = marked.parse(response);
436
  responseItem.appendChild(content);
437
-
438
  savedResponsesContainer.appendChild(responseItem);
439
 
440
- // Gestionnaire d'événement pour afficher/masquer le contenu
441
  header.querySelector('.toggle-content').addEventListener('click', () => {
442
  responseItem.classList.toggle('open');
443
  header.querySelector('i').classList.toggle('fa-chevron-down');
444
  header.querySelector('i').classList.toggle('fa-chevron-up');
445
  MathJax.typesetPromise([content]);
446
  content.classList.remove('math-hidden');
447
-
448
  });
449
 
450
- // Gestionnaire d'événement pour supprimer la réponse
451
  header.querySelector('.delete-response').addEventListener('click', async () => {
452
  try {
453
  await localforage.removeItem(key);
454
- console.log('Réponse supprimée:', key);
455
  responseItem.remove();
456
  } catch (error) {
457
  console.error('Erreur lors de la suppression de la réponse:', error);
@@ -468,7 +312,6 @@
468
  }
469
  }
470
 
471
- // Gestion du formulaire
472
  uploadForm.addEventListener('submit', async (e) => {
473
  e.preventDefault();
474
 
@@ -479,6 +322,7 @@
479
 
480
  const formData = new FormData();
481
  formData.append('image', fileInput.files[0]);
 
482
 
483
  try {
484
  submitButton.disabled = true;
@@ -498,9 +342,8 @@
498
  }
499
 
500
  await renderMathContent(data.result);
501
-
502
- // Sauvegarder la réponse après le rendu
503
- saveResponse(data.result);
504
 
505
  } catch (error) {
506
  console.error('Erreur:', error);
@@ -511,15 +354,10 @@
511
  }
512
  });
513
 
514
- // Charger les réponses sauvegardées au chargement de la page
515
  loadSavedResponses();
516
-
517
- // Gestionnaire d'événement pour effacer les réponses sauvegardées
518
  clearSavedResponsesButton.addEventListener('click', clearSavedResponses);
519
-
520
  });
521
-
522
  </script>
523
  </body>
524
 
525
- </html>
 
5
  <meta charset="UTF-8">
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
  <title>Résolution de Problèmes Mathématiques</title>
 
 
8
  <script src="https://cdn.tailwindcss.com"></script>
 
 
9
  <script defer src="https://cdnjs.cloudflare.com/ajax/libs/marked/9.1.6/marked.min.js"></script>
 
 
10
  <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
 
 
11
  <script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
 
12
  <style>
13
+ .dropzone { border: 2px dashed #4F46E5; transition: all 0.3s ease; }
14
+ .dropzone:hover { border-color: #312E81; background-color: rgba(79, 70, 229, 0.1); }
15
+ .loading { display: none; }
16
+ .loading.active { display: flex; }
17
+ .math-content { font-size: 1.1em; line-height: 1.6; overflow-x: auto; }
18
+ .math-content p { margin-bottom: 1rem; white-space: pre-wrap; }
19
+ .math-content .MathJax { overflow-x: auto; overflow-y: hidden; padding: 0.5rem 0; }
20
+ @media (max-width: 640px) { .math-content .MathJax { font-size: 0.9em; } }
21
+ .math-hidden { visibility: hidden; }
22
+ .saved-response-header { cursor: pointer; display: flex; justify-content: space-between; align-items: center; padding: 0.75rem 1rem; background-color: #f3f4f6; border-bottom: 1px solid #e5e7eb; }
23
+ .saved-response-content { padding: 1rem; display: none; }
24
+ .saved-response-item.open .saved-response-content { display: block; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
  </style>
 
 
26
  <script>
27
  window.MathJax = {
28
  tex: {
29
+ inlineMath: [['$', '$'], ['\\(', '\\)']],
30
+ displayMath: [['$$', '$$'], ['\\[', '\\]']],
 
 
 
 
 
 
31
  processEscapes: true,
32
+ macros: { R: "{\\mathbb{R}}", N: "{\\mathbb{N}}", Z: "{\\mathbb{Z}}", vecv: ["\\begin{pmatrix}#1\\\\#2\\\\#3\\end{pmatrix}", 3] }
 
 
 
 
 
 
 
 
 
 
 
 
 
33
  },
34
+ svg: { fontCache: 'global' },
35
+ startup: { pageReady: () => { return Promise.resolve(); } },
36
+ options: { renderActions: { addMenu: [], checkLoading: [150, () => { document.querySelectorAll('.math-content').forEach(el => { el.classList.remove('math-hidden'); }); }] } }
 
 
 
 
 
 
 
37
  };
38
  </script>
 
 
39
  <script>
40
  function loadMathJax() {
41
  return new Promise((resolve, reject) => {
 
48
  document.head.appendChild(script);
49
  });
50
  }
 
 
51
  loadMathJax().catch(console.error);
52
  </script>
 
 
53
  <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/localforage.min.js"></script>
 
54
  </head>
55
 
56
  <body class="min-h-screen bg-gradient-to-br from-blue-50 to-white">
 
57
  <div class="container mx-auto px-4 py-8 max-w-4xl">
 
58
  <header class="text-center mb-12">
59
  <h1 class="text-4xl font-bold text-blue-800 mb-4">Résolution de Problèmes Mathématiques</h1>
60
+ <p class="text-gray-600 text-lg">Soumettez une image de votre problème mathématique pour obtenir une solution détaillée</p>
 
61
  </header>
 
 
62
  <div class="mb-8">
63
  <form id="uploadForm" class="space-y-4">
64
  <div id="dropzone"
 
67
  <div class="flex flex-col items-center space-y-4">
68
  <i class="fas fa-cloud-upload-alt text-4xl text-blue-600"></i>
69
  <div class="text-lg text-gray-700">
70
+ Glissez votre image ici ou <span class="text-blue-600 font-semibold">cliquez pour sélectionner</span>
 
71
  </div>
72
  <p class="text-sm text-gray-500">Formats acceptés: PNG, JPG, JPEG</p>
73
  </div>
74
  </div>
75
 
76
+ <div class="flex justify-center items-center space-x-4">
77
+ <select id="modelChoice" name="model_choice" class="rounded-md border-gray-300 shadow-sm focus:border-blue-300 focus:ring focus:ring-blue-200 focus:ring-opacity-50">
78
+ <option value="mariam's">Mariam's(Rapide et performant)</option>
79
+ <option value="qwen2">Qwen2(lent mais 2 +performant)</option>
80
+ </select>
81
  <button type="submit"
82
  class="px-6 py-3 bg-blue-600 hover:bg-blue-700 text-white rounded-lg transition-colors duration-200 flex items-center space-x-2 disabled:opacity-50 disabled:cursor-not-allowed">
83
  <i class="fas fa-paper-plane"></i>
 
87
  </form>
88
  </div>
89
 
 
90
  <div id="loading" class="loading flex-col items-center justify-center space-y-4 my-8">
91
  <div class="animate-spin rounded-full h-12 w-12 border-4 border-blue-200 border-t-blue-600"></div>
92
  <p class="text-gray-700 font-medium">Analyse en cours...</p>
93
  </div>
94
 
 
95
  <div id="response" class="hidden">
96
  <div class="bg-white rounded-lg shadow-lg p-6 mb-8">
97
+ <h2 id="modelUsed" class="text-2xl font-semibold text-blue-800 mb-4">Solution (Modèle: <span id="modelName"></span>)</h2>
98
+ <div id="latexContent" class="prose max-w-none math-content math-hidden"></div>
 
 
99
  </div>
100
  </div>
101
 
 
102
  <div id="savedResponsesSection" class="mt-8">
103
  <div class="flex justify-between items-center mb-4">
104
  <h2 class="text-2xl font-semibold text-blue-800">Réponses Sauvegardées</h2>
105
+ <button id="clearSavedResponses" class="px-4 py-2 bg-red-600 hover:bg-red-700 text-white rounded-lg transition-colors duration-200">
 
106
  <i class="fas fa-trash-alt"></i> Effacer Tout
107
  </button>
108
  </div>
109
+ <div id="savedResponses" class="space-y-2"></div>
 
 
110
  </div>
111
 
 
112
  <div id="errorMessage"
113
  class="hidden bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative my-4" role="alert">
114
  <strong class="font-bold">Erreur!</strong>
 
118
 
119
  <script>
120
  document.addEventListener('DOMContentLoaded', function () {
 
121
  const dropzone = document.getElementById('dropzone');
122
  const fileInput = document.getElementById('fileInput');
123
  const uploadForm = document.getElementById('uploadForm');
 
129
  const submitButton = uploadForm.querySelector('button[type="submit"]');
130
  const savedResponsesContainer = document.getElementById('savedResponses');
131
  const clearSavedResponsesButton = document.getElementById('clearSavedResponses');
132
+ const modelChoiceSelect = document.getElementById('modelChoice');
133
+ const modelNameSpan = document.getElementById('modelName');
134
 
 
135
  let mathJaxReady = false;
136
  window.MathJax.startup.promise.then(() => {
137
  mathJaxReady = true;
138
  });
139
 
140
+ marked.setOptions({ breaks: true, gfm: true, pedantic: false, smartLists: true });
 
 
 
 
 
 
141
 
 
142
  function showError(message) {
143
  errorText.textContent = message;
144
  errorMessage.classList.remove('hidden');
145
+ setTimeout(() => { errorMessage.classList.add('hidden'); }, 5000);
 
 
146
  }
147
 
 
148
  async function renderMathContent(text) {
149
  try {
 
150
  if (!mathJaxReady) {
151
  await window.MathJax.startup.promise;
152
  }
 
 
153
  latexContent.innerHTML = '';
154
  latexContent.classList.add('math-hidden');
 
 
155
  const htmlContent = marked.parse(text);
156
  latexContent.innerHTML = htmlContent;
 
 
157
  await MathJax.typesetPromise([latexContent]);
 
 
158
  response.classList.remove('hidden');
159
  latexContent.classList.remove('math-hidden');
 
160
  } catch (error) {
161
  console.error('Erreur lors du rendu:', error);
162
  showError('Erreur lors du rendu de la formule mathématique');
 
163
  latexContent.innerHTML = `
164
  <div class="text-red-600 mb-4">Une erreur s'est produite lors du rendu. Voici le texte brut :</div>
165
  <pre class="bg-gray-100 p-4 rounded-lg overflow-x-auto">${text}</pre>
 
168
  }
169
  }
170
 
171
+ function handleDragOver(e) { e.preventDefault(); e.stopPropagation(); dropzone.classList.add('bg-blue-50'); }
172
+ function handleDragLeave(e) { e.preventDefault(); e.stopPropagation(); dropzone.classList.remove('bg-blue-50'); }
 
 
 
 
 
 
 
 
 
 
 
173
  function handleDrop(e) {
174
+ e.preventDefault(); e.stopPropagation(); dropzone.classList.remove('bg-blue-50');
 
 
 
175
  const files = e.dataTransfer.files;
176
  if (files.length > 0 && files[0].type.startsWith('image/')) {
177
  fileInput.files = files;
 
181
  }
182
  }
183
 
 
184
  function handleFileSelect(file) {
185
  if (file && file.type.startsWith('image/')) {
186
  const reader = new FileReader();
 
188
  const preview = document.createElement('img');
189
  preview.src = e.target.result;
190
  preview.classList.add('max-h-48', 'mx-auto', 'mt-4', 'rounded-lg');
 
191
  const oldPreview = dropzone.querySelector('img');
192
  if (oldPreview) oldPreview.remove();
 
193
  dropzone.appendChild(preview);
194
  submitButton.disabled = false;
195
  };
 
199
  }
200
  }
201
 
 
202
  dropzone.addEventListener('dragover', handleDragOver);
203
  dropzone.addEventListener('dragleave', handleDragLeave);
204
  dropzone.addEventListener('drop', handleDrop);
 
210
  }
211
  });
212
 
213
+ async function saveResponse(response, model) {
 
214
  const timestamp = new Date().getTime();
215
+ const key = `response-${timestamp}-${model}`;
216
  try {
217
  await localforage.setItem(key, response);
 
 
218
  loadSavedResponses();
219
  } catch (error) {
220
  console.error('Erreur lors de la sauvegarde:', error);
 
222
  }
223
  }
224
 
 
225
  async function clearSavedResponses() {
226
  Swal.fire({
227
  title: 'Êtes-vous sûr?',
 
256
  });
257
  }
258
 
 
259
  async function loadSavedResponses() {
260
  try {
261
  savedResponsesContainer.innerHTML = '';
262
  const keys = await localforage.keys();
263
+ keys.sort((a, b) => parseInt(b.replace('response-', '').split('-')[0]) - parseInt(a.replace('response-', '').split('-')[0]));
264
 
265
  for (const key of keys) {
266
  const response = await localforage.getItem(key);
267
+ const [ , timestamp, model ] = key.split('-');
268
  const responseItem = document.createElement('div');
269
  responseItem.className = 'saved-response-item bg-white rounded-lg shadow-md overflow-hidden';
270
 
271
  const header = document.createElement('div');
272
  header.className = 'saved-response-header';
273
  header.innerHTML = `
274
+ <span class="text-blue-800 font-medium">Réponse du ${new Date(parseInt(timestamp)).toLocaleString()} (Modèle: ${model})</span>
275
  <div>
276
  <button class="toggle-content px-1 py-0.5 rounded-md text-xs bg-blue-500 hover:bg-blue-700 text-white mr-1"><i class="fas fa-chevron-down"></i></button>
277
  <button class="delete-response px-1 py-0.5 rounded-md text-xs bg-red-500 hover:bg-red-700 text-white"><i class="fas fa-trash-alt"></i></button>
 
283
  content.className = 'saved-response-content math-content math-hidden';
284
  content.innerHTML = marked.parse(response);
285
  responseItem.appendChild(content);
 
286
  savedResponsesContainer.appendChild(responseItem);
287
 
 
288
  header.querySelector('.toggle-content').addEventListener('click', () => {
289
  responseItem.classList.toggle('open');
290
  header.querySelector('i').classList.toggle('fa-chevron-down');
291
  header.querySelector('i').classList.toggle('fa-chevron-up');
292
  MathJax.typesetPromise([content]);
293
  content.classList.remove('math-hidden');
 
294
  });
295
 
 
296
  header.querySelector('.delete-response').addEventListener('click', async () => {
297
  try {
298
  await localforage.removeItem(key);
 
299
  responseItem.remove();
300
  } catch (error) {
301
  console.error('Erreur lors de la suppression de la réponse:', error);
 
312
  }
313
  }
314
 
 
315
  uploadForm.addEventListener('submit', async (e) => {
316
  e.preventDefault();
317
 
 
322
 
323
  const formData = new FormData();
324
  formData.append('image', fileInput.files[0]);
325
+ formData.append('model_choice', modelChoiceSelect.value);
326
 
327
  try {
328
  submitButton.disabled = true;
 
342
  }
343
 
344
  await renderMathContent(data.result);
345
+ modelNameSpan.textContent = data.model;
346
+ saveResponse(data.result, data.model);
 
347
 
348
  } catch (error) {
349
  console.error('Erreur:', error);
 
354
  }
355
  });
356
 
 
357
  loadSavedResponses();
 
 
358
  clearSavedResponsesButton.addEventListener('click', clearSavedResponses);
 
359
  });
 
360
  </script>
361
  </body>
362
 
363
+ </html>