Docfile commited on
Commit
3ccc735
·
verified ·
1 Parent(s): f012284

Create tst.html

Browse files
Files changed (1) hide show
  1. templates/tst.html +363 -0
templates/tst.html ADDED
@@ -0,0 +1,363 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="fr">
3
+
4
+ <head>
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) => {
42
+ const script = document.createElement('script');
43
+ script.src = 'https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js';
44
+ script.async = true;
45
+ script.id = 'MathJax-script';
46
+ script.onload = resolve;
47
+ script.onerror = 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"
65
+ class="dropzone rounded-lg p-8 text-center cursor-pointer bg-white shadow-sm hover:shadow-md transition-all">
66
+ <input type="file" id="fileInput" class="hidden" accept="image/*">
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>
84
+ <span>Analyser l'image</span>
85
+ </button>
86
+ </div>
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>
115
+ <span class="block sm:inline" id="errorText"></span>
116
+ </div>
117
+ </div>
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');
124
+ const loading = document.getElementById('loading');
125
+ const response = document.getElementById('response');
126
+ const latexContent = document.getElementById('latexContent');
127
+ const errorMessage = document.getElementById('errorMessage');
128
+ const errorText = document.getElementById('errorText');
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>
166
+ `;
167
+ latexContent.classList.remove('math-hidden');
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;
178
+ handleFileSelect(files[0]);
179
+ } else {
180
+ showError('Veuillez déposer une image valide');
181
+ }
182
+ }
183
+
184
+ function handleFileSelect(file) {
185
+ if (file && file.type.startsWith('image/')) {
186
+ const reader = new FileReader();
187
+ reader.onload = function (e) {
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
+ };
196
+ reader.readAsDataURL(file);
197
+ } else {
198
+ showError('Veuillez sélectionner une image valide');
199
+ }
200
+ }
201
+
202
+ dropzone.addEventListener('dragover', handleDragOver);
203
+ dropzone.addEventListener('dragleave', handleDragLeave);
204
+ dropzone.addEventListener('drop', handleDrop);
205
+ dropzone.addEventListener('click', () => fileInput.click());
206
+
207
+ fileInput.addEventListener('change', (e) => {
208
+ if (e.target.files.length > 0) {
209
+ handleFileSelect(e.target.files[0]);
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);
221
+ showError('Erreur lors de la sauvegarde de la réponse en local');
222
+ }
223
+ }
224
+
225
+ async function clearSavedResponses() {
226
+ Swal.fire({
227
+ title: 'Êtes-vous sûr?',
228
+ text: "Vous ne pourrez pas revenir en arrière!",
229
+ icon: 'warning',
230
+ showCancelButton: true,
231
+ confirmButtonColor: '#d33',
232
+ cancelButtonColor: '#3085d6',
233
+ confirmButtonText: 'Oui, effacer!',
234
+ cancelButtonText: 'Annuler'
235
+ }).then(async (result) => {
236
+ if (result.isConfirmed) {
237
+ try {
238
+ await localforage.clear();
239
+ console.log('Réponses sauvegardées effacées');
240
+ loadSavedResponses();
241
+ Swal.fire(
242
+ 'Effacé!',
243
+ 'Les réponses ont été supprimées.',
244
+ 'success'
245
+ );
246
+ } catch (error) {
247
+ console.error('Erreur lors de l\'effacement des réponses sauvegardées:', error);
248
+ showError('Erreur lors de l\'effacement des réponses sauvegardées');
249
+ Swal.fire(
250
+ 'Erreur!',
251
+ 'Une erreur est survenue lors de la suppression.',
252
+ 'error'
253
+ );
254
+ }
255
+ }
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>
278
+ </div>
279
+ `;
280
+ responseItem.appendChild(header);
281
+
282
+ const content = document.createElement('div');
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);
302
+ showError('Erreur lors de la suppression de la réponse');
303
+ }
304
+ });
305
+
306
+ MathJax.typesetPromise([content]);
307
+ content.classList.remove('math-hidden');
308
+ }
309
+ } catch (error) {
310
+ console.error('Erreur lors du chargement des réponses sauvegardées:', error);
311
+ showError('Erreur lors du chargement des réponses sauvegardées');
312
+ }
313
+ }
314
+
315
+ uploadForm.addEventListener('submit', async (e) => {
316
+ e.preventDefault();
317
+
318
+ if (!fileInput.files.length) {
319
+ showError('Veuillez sélectionner une image');
320
+ return;
321
+ }
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;
329
+ loading.classList.add('active');
330
+ response.classList.add('hidden');
331
+ errorMessage.classList.add('hidden');
332
+
333
+ const res = await fetch('/generate', {
334
+ method: 'POST',
335
+ body: formData
336
+ });
337
+
338
+ const data = await res.json();
339
+
340
+ if (data.error) {
341
+ throw new Error(data.error);
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);
350
+ showError(error.message || 'Une erreur est survenue lors du traitement');
351
+ } finally {
352
+ loading.classList.remove('active');
353
+ submitButton.disabled = false;
354
+ }
355
+ });
356
+
357
+ loadSavedResponses();
358
+ clearSavedResponsesButton.addEventListener('click', clearSavedResponses);
359
+ });
360
+ </script>
361
+ </body>
362
+
363
+ </html>