Docfile commited on
Commit
c34f4b9
·
verified ·
1 Parent(s): e17df7c

Delete templates/math.html

Browse files
Files changed (1) hide show
  1. templates/math.html +0 -643
templates/math.html DELETED
@@ -1,643 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="fr">
3
- <head>
4
- <meta charset="UTF-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <meta name="description" content="Assistant intelligent pour la résolution détaillée de problèmes mathématiques">
7
- <title>Mariam - Résolution de Problèmes Mathématiques</title>
8
-
9
- <!-- Preload des ressources critiques -->
10
- <link rel="preload" href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap" as="style">
11
- <link rel="preload" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" as="style">
12
-
13
- <!-- CSS -->
14
- <script src="https://cdn.tailwindcss.com"></script>
15
- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
16
- <link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap" rel="stylesheet">
17
-
18
- <style>
19
- /* Base styles */
20
- :root {
21
- --primary-color: #3b82f6;
22
- --primary-dark: #2563eb;
23
- --error-color: #ef4444;
24
- --success-color: #22c55e;
25
- --border-color: #d1d5db;
26
- }
27
-
28
- body {
29
- font-family: 'Poppins', system-ui, sans-serif;
30
- background-color: #f9fafb;
31
- }
32
-
33
- /* Optimized dropzone */
34
- .dropzone {
35
- border: 3px dashed var(--primary-color);
36
- transition: all 0.2s ease;
37
- border-radius: 1rem;
38
- padding: 2rem;
39
- background-color: rgba(59, 130, 246, 0.05);
40
- min-height: 200px;
41
- display: flex;
42
- align-items: center;
43
- justify-content: center;
44
- }
45
-
46
- .dropzone:hover, .dropzone.drag-active {
47
- border-color: var(--primary-dark);
48
- background-color: rgba(59, 130, 246, 0.1);
49
- transform: scale(1.01);
50
- }
51
-
52
- /* Enhanced button styles */
53
- .btn {
54
- padding: 0.75rem 1.5rem;
55
- border-radius: 0.5rem;
56
- font-weight: 600;
57
- transition: all 0.2s ease;
58
- display: inline-flex;
59
- align-items: center;
60
- gap: 0.5rem;
61
- cursor: pointer;
62
- }
63
-
64
- .btn:disabled {
65
- opacity: 0.6;
66
- cursor: not-allowed;
67
- }
68
-
69
- .btn-primary {
70
- background-color: var(--primary-color);
71
- color: white;
72
- }
73
-
74
- .btn-primary:hover:not(:disabled) {
75
- background-color: var(--primary-dark);
76
- transform: translateY(-1px);
77
- }
78
-
79
- .btn-danger {
80
- background-color: var(--error-color);
81
- color: white;
82
- }
83
-
84
- .btn-danger:hover {
85
- background-color: #dc2626;
86
- transform: translateY(-1px);
87
- }
88
-
89
- /* Optimized math content display */
90
- .math-content {
91
- font-size: 1.1em;
92
- line-height: 1.6;
93
- overflow-x: auto;
94
- opacity: 0;
95
- transition: opacity 0.3s ease;
96
- }
97
-
98
- .math-content.visible {
99
- opacity: 1;
100
- }
101
-
102
- .math-content p {
103
- margin-bottom: 1rem;
104
- white-space: pre-wrap;
105
- }
106
-
107
- /* Enhanced form controls */
108
- .form-input {
109
- width: 100%;
110
- padding: 0.75rem 1rem;
111
- border: 2px solid var(--border-color);
112
- border-radius: 0.5rem;
113
- transition: all 0.2s ease;
114
- }
115
-
116
- .form-input:focus {
117
- border-color: var(--primary-color);
118
- box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.3);
119
- outline: none;
120
- }
121
-
122
- /* Improved loading animation */
123
- @keyframes pulse {
124
- 0%, 100% { transform: scale(1); }
125
- 50% { transform: scale(1.1); }
126
- }
127
-
128
- .loading-spinner {
129
- width: 3rem;
130
- height: 3rem;
131
- border: 4px solid var(--primary-color);
132
- border-radius: 50%;
133
- border-top-color: transparent;
134
- animation: spin 1s linear infinite;
135
- }
136
-
137
- @keyframes spin {
138
- to { transform: rotate(360deg); }
139
- }
140
-
141
- /* Responsive adjustments */
142
- @media (max-width: 640px) {
143
- .container {
144
- padding: 1rem;
145
- }
146
-
147
- .btn {
148
- width: 100%;
149
- justify-content: center;
150
- }
151
-
152
- .dropzone {
153
- padding: 1.5rem;
154
- }
155
- }
156
-
157
- /* Enhanced accessibility */
158
- .sr-only {
159
- position: absolute;
160
- width: 1px;
161
- height: 1px;
162
- padding: 0;
163
- margin: -1px;
164
- overflow: hidden;
165
- clip: rect(0, 0, 0, 0);
166
- white-space: nowrap;
167
- border: 0;
168
- }
169
-
170
- /* Toast notifications */
171
- .toast {
172
- position: fixed;
173
- bottom: 1rem;
174
- right: 1rem;
175
- padding: 1rem;
176
- border-radius: 0.5rem;
177
- background: white;
178
- box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
179
- z-index: 50;
180
- max-width: 24rem;
181
- transform: translateY(100%);
182
- opacity: 0;
183
- transition: all 0.3s ease;
184
- }
185
-
186
- .toast.show {
187
- transform: translateY(0);
188
- opacity: 1;
189
- }
190
- </style>
191
- </head>
192
-
193
- <body>
194
- <div class="container mx-auto px-4 py-8 max-w-4xl">
195
- <!-- Header -->
196
- <header class="text-center mb-12">
197
- <h1 class="text-4xl font-bold mb-4">
198
- <span class="bg-gradient-to-r from-blue-500 to-blue-700 text-transparent bg-clip-text">Mariam</span>
199
- - Résolution de Problèmes Mathématiques
200
- </h1>
201
- <p class="text-gray-600 text-lg">Assistant intelligent pour des solutions mathématiques détaillées</p>
202
- </header>
203
-
204
- <!-- Main Form -->
205
- <form id="uploadForm" class="space-y-6">
206
- <div id="dropzone" class="dropzone">
207
- <input type="file" id="fileInput" class="sr-only" accept="image/*">
208
- <div class="text-center">
209
- <i class="fas fa-cloud-upload-alt text-5xl text-blue-500 mb-4"></i>
210
- <p class="text-lg text-gray-700">
211
- Glissez votre image ici ou <button type="button" class="text-blue-500 font-semibold hover:text-blue-700">parcourez vos fichiers</button>
212
- </p>
213
- <p class="text-sm text-gray-500 mt-2">Formats acceptés: PNG, JPG, JPEG</p>
214
- </div>
215
- </div>
216
-
217
- <div class="space-y-4">
218
- <div>
219
- <label for="customInstruction" class="block text-gray-700 font-medium mb-2">
220
- Instruction personnalisée (optionnel)
221
- </label>
222
- <input type="text" id="customInstruction" class="form-input"
223
- placeholder="Exemple : Résoudre en utilisant le théorème de Pythagore">
224
- </div>
225
-
226
- <div class="flex flex-col sm:flex-row gap-4">
227
- <select id="modelChoice" class="form-input">
228
- <option value="mariam's">Mariam's (Ultra performant)</option>
229
- <option value="qwen2">Qwen2 (lent et performant)</option>
230
- </select>
231
-
232
- <button type="submit" class="btn btn-primary" disabled>
233
- <i class="fas fa-paper-plane"></i>
234
- <span>Analyser l'image</span>
235
- </button>
236
- </div>
237
- </div>
238
- </form>
239
-
240
- <!-- Loading State -->
241
- <div id="loading" class="hidden mt-8 text-center">
242
- <div class="loading-spinner mx-auto mb-4"></div>
243
- <p class="text-gray-600 font-medium">Analyse en cours...</p>
244
- </div>
245
-
246
- <!-- Response Display -->
247
- <div id="response" class="hidden mt-8">
248
- <div class="bg-white rounded-xl shadow-lg p-6">
249
- <h2 class="text-2xl font-semibold text-blue-600 mb-4">
250
- Solution (Modèle: <span id="modelName"></span>)
251
- </h2>
252
- <div id="latexContent" class="math-content"></div>
253
- </div>
254
- </div>
255
-
256
- <!-- Saved Responses -->
257
- <section id="savedResponsesSection" class="mt-8">
258
- <div class="flex justify-between items-center mb-4">
259
- <h2 class="text-2xl font-semibold text-blue-600">Réponses Sauvegardées</h2>
260
- <button id="clearSavedResponses" class="btn btn-danger">
261
- <i class="fas fa-trash-alt"></i>
262
- <span>Effacer Tout</span>
263
- </button>
264
- </div>
265
- <div id="savedResponses" class="space-y-4"></div>
266
- </section>
267
-
268
- <!-- Toast Container -->
269
- <div id="toastContainer"></div>
270
- </div>
271
-
272
- <!-- Scripts -->
273
- <script src="https://cdnjs.cloudflare.com/ajax/libs/marked/9.1.6/marked.min.js" defer></script>
274
- <script src="https://cdn.jsdelivr.net/npm/sweetalert2@11" defer></script>
275
- <script src="https://cdn.jsdelivr.net/npm/localforage@1.10.0/dist/localforage.min.js" defer></script>
276
- <script>
277
- document.addEventListener('DOMContentLoaded', () => {
278
- // Initialize MathJax
279
- window.MathJax = {
280
- tex: {
281
- inlineMath: [['$', '$'], ['\\(', '\\)']],
282
- displayMath: [['$$', '$$'], ['\\[', '\\]']],
283
- processEscapes: true,
284
- macros: {
285
- R: "{\\mathbb{R}}",
286
- N: "{\\mathbb{N}}",
287
- Z: "{\\mathbb{Z}}",
288
- vecv: ["\\begin{pmatrix}#1\\\\#2\\\\#3\\end{pmatrix}", 3]
289
- }
290
- },
291
- svg: {
292
- fontCache: 'global'
293
- }
294
- };
295
-
296
- // Load MathJax dynamically
297
- const loadMathJax = () => {
298
- const script = document.createElement('script');
299
- script.src = 'https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js';
300
- script.async = true;
301
- document.head.appendChild(script);
302
- return new Promise(resolve => script.onload = resolve);
303
- };
304
-
305
- // Toast notification system
306
- const showToast = (message, type = 'info') => {
307
- const toast = document.createElement('div');
308
- toast.className = `toast ${type}`;
309
- toast.innerHTML = `
310
- <div class="flex items-center">
311
- <i class="fas fa-${type === 'error' ? 'exclamation-circle' : 'info-circle'} mr-2"></i>
312
- <p>${message}</p>
313
- </div>
314
- `;
315
- document.getElementById('toastContainer').appendChild(toast);
316
- requestAnimationFrame(() => toast.classList.add('show'));
317
- setTimeout(() => {
318
- toast.classList.remove('show');
319
- setTimeout(() => toast.remove(), 300);
320
- }, 3000);
321
- };
322
-
323
- // Enhanced file handling
324
- const handleFile = file => {
325
- if (!file.type.startsWith('image/')) {
326
- showToast('Veuillez sélectionner une image valide', 'error');
327
- return false;
328
- }
329
-
330
- const maxSize = 10 * 1024 * 1024; // 10MB
331
- if (file.size > maxSize) {
332
- showToast('L\'image est trop volumineuse. Maximum 10MB.', 'error');
333
- return false;
334
- }
335
-
336
- return true;
337
- };
338
-
339
- // Initialize the application
340
- const init = async () => {
341
- const dropzone = document.getElementById('dropzone');
342
- const fileInput = document.getElementById('fileInput');
343
- const form = document.getElementById('uploadForm');
344
- const submitBtn = form.querySelector('button[type="submit"]');
345
-
346
- // Enhanced drag and drop
347
- dropzone.addEventListener('dragenter', e => {
348
- e.preventDefault();
349
- dropzone.classList.add('drag-active');
350
- });
351
-
352
- dropzone.addEventListener('dragleave', e => {
353
- e.preventDefault();
354
- dropzone.classList.remove('drag-active');
355
- });
356
-
357
- dropzone.addEventListener('dragover', e => {
358
- e.preventDefault();
359
- });
360
-
361
- dropzone.addEventListener('drop', e => {
362
- e.preventDefault();
363
- dropzone.classList.remove('drag-active');
364
- const file = e.dataTransfer.files[0];
365
- if (handleFile(file)) {
366
- fileInput.files = e.dataTransfer.files;
367
- handleFileSelect(file);
368
- }
369
- });
370
-
371
- // File selection
372
- const handleFileSelect = file => {
373
- const reader = new FileReader();
374
- reader.onload = e => {
375
- const preview = dropzone.querySelector('img') || document.createElement('img');
376
- preview.src = e.target.result;
377
- preview.className = 'max-h-48 mx-auto mt-4 rounded-lg';
378
- if (!dropzone.querySelector('img')) {
379
- dropzone.appendChild(preview);
380
- }
381
- submitBtn.disabled = false;
382
- };
383
- reader.readAsDataURL(file);
384
- };
385
-
386
- fileInput.addEventListener('change', e => {
387
- const file = e.target.files[0];
388
- if (handleFile(file)) {
389
- handleFileSelect(file);
390
- }
391
- });
392
-
393
- // Form submission
394
- form.addEventListener('submit', async e => {
395
- e.preventDefault();
396
- if (!fileInput.files.length) {
397
- showToast('Veuillez sélectionner une image', 'error');
398
- return;
399
- }
400
-
401
- const formData = new FormData(form);
402
- submitBtn.disabled = true;
403
-
404
- try {
405
- document.getElementById('loading').classList.remove('hidden');
406
- document.getElementById('response').classList.add('hidden');
407
-
408
- const response = await fetch('/upload', {
409
- method: 'POST',
410
- body: formData
411
- });
412
-
413
- const data = await response.json();
414
- if (!response.ok) throw new Error(data.error || 'Erreur serveur');
415
-
416
- await renderMathContent(data.result);
417
- document.getElementById('modelName').textContent = data.model;
418
- await saveResponse(data.result, data.model);
419
-
420
- // Gestion des graphiques générés
421
- if (data.image_paths?.length) {
422
- const imageContainer = document.createElement('div');
423
- imageContainer.className = 'mt-4 grid gap-4 grid-cols-1 sm:grid-cols-2';
424
-
425
- for (const path of data.image_paths) {
426
- const figure = document.createElement('figure');
427
- figure.className = 'relative group';
428
-
429
- const img = document.createElement('img');
430
- img.src = `/temp/${path.split('/').pop()}`;
431
- img.className = 'w-full h-auto rounded-lg shadow-md transition-transform hover:scale-105';
432
- img.loading = 'lazy';
433
-
434
- figure.appendChild(img);
435
- imageContainer.appendChild(figure);
436
- }
437
-
438
- document.getElementById('latexContent').appendChild(imageContainer);
439
- }
440
-
441
- showToast('Analyse terminée avec succès', 'success');
442
- } catch (error) {
443
- console.error('Erreur:', error);
444
- showToast(error.message || 'Une erreur est survenue', 'error');
445
- } finally {
446
- document.getElementById('loading').classList.add('hidden');
447
- submitBtn.disabled = false;
448
- }
449
- });
450
-
451
- // Optimized MathJax rendering
452
- const renderMathContent = async (text) => {
453
- const latexContent = document.getElementById('latexContent');
454
- try {
455
- latexContent.innerHTML = '';
456
- latexContent.classList.remove('visible');
457
-
458
- // Utilisation de marked avec des options de sécurité
459
- const htmlContent = marked.parse(text, {
460
- breaks: true,
461
- gfm: true,
462
- sanitize: true
463
- });
464
-
465
- latexContent.innerHTML = htmlContent;
466
- await MathJax.typesetPromise([latexContent]);
467
-
468
- document.getElementById('response').classList.remove('hidden');
469
- requestAnimationFrame(() => latexContent.classList.add('visible'));
470
- } catch (error) {
471
- console.error('Erreur de rendu:', error);
472
- showToast('Erreur lors du rendu mathématique', 'error');
473
- latexContent.innerHTML = `
474
- <div class="text-red-600 mb-4">Une erreur s'est produite lors du rendu. Voici le texte brut :</div>
475
- <pre class="bg-gray-100 p-4 rounded-lg overflow-x-auto">${text}</pre>
476
- `;
477
- }
478
- };
479
-
480
- // Enhanced local storage management
481
- const saveResponse = async (response, model) => {
482
- try {
483
- const timestamp = Date.now();
484
- const key = `response-${timestamp}-${model}`;
485
- await localforage.setItem(key, {
486
- content: response,
487
- model,
488
- timestamp,
489
- id: key
490
- });
491
- await loadSavedResponses();
492
- } catch (error) {
493
- console.error('Erreur de sauvegarde:', error);
494
- showToast('Erreur lors de la sauvegarde', 'error');
495
- }
496
- };
497
-
498
- const loadSavedResponses = async () => {
499
- const container = document.getElementById('savedResponses');
500
- container.innerHTML = '';
501
-
502
- try {
503
- const keys = await localforage.keys();
504
- const responses = await Promise.all(
505
- keys.map(async key => {
506
- const data = await localforage.getItem(key);
507
- return { ...data, key };
508
- })
509
- );
510
-
511
- // Tri par date décroissante
512
- responses.sort((a, b) => b.timestamp - a.timestamp);
513
-
514
- for (const response of responses) {
515
- const element = createResponseElement(response);
516
- container.appendChild(element);
517
- }
518
-
519
- if (responses.length === 0) {
520
- container.innerHTML = `
521
- <div class="text-center text-gray-500 py-8">
522
- <i class="fas fa-inbox text-4xl mb-4"></i>
523
- <p>Aucune réponse sauvegardée</p>
524
- </div>
525
- `;
526
- }
527
- } catch (error) {
528
- console.error('Erreur de chargement:', error);
529
- showToast('Erreur lors du chargement des réponses', 'error');
530
- }
531
- };
532
-
533
- const createResponseElement = (response) => {
534
- const element = document.createElement('div');
535
- element.className = 'bg-white rounded-lg shadow-md overflow-hidden';
536
-
537
- const header = document.createElement('div');
538
- header.className = 'flex items-center justify-between p-4 bg-gray-50';
539
-
540
- const date = new Date(response.timestamp).toLocaleString('fr-FR', {
541
- dateStyle: 'medium',
542
- timeStyle: 'short'
543
- });
544
-
545
- header.innerHTML = `
546
- <div class="flex items-center space-x-2">
547
- <span class="font-medium text-gray-700">${date}</span>
548
- <span class="text-sm text-gray-500">(${response.model})</span>
549
- </div>
550
- <div class="flex space-x-2">
551
- <button class="btn-toggle p-2 rounded-full hover:bg-gray-200 transition-colors">
552
- <i class="fas fa-chevron-down"></i>
553
- </button>
554
- <button class="btn-delete p-2 rounded-full hover:bg-red-100 text-red-500 transition-colors">
555
- <i class="fas fa-trash-alt"></i>
556
- </button>
557
- </div>
558
- `;
559
-
560
- const content = document.createElement('div');
561
- content.className = 'p-4 math-content hidden';
562
- content.innerHTML = marked.parse(response.content);
563
-
564
- element.appendChild(header);
565
- element.appendChild(content);
566
-
567
- // Event listeners
568
- header.querySelector('.btn-toggle').addEventListener('click', async (e) => {
569
- const icon = e.currentTarget.querySelector('i');
570
- icon.classList.toggle('fa-chevron-down');
571
- icon.classList.toggle('fa-chevron-up');
572
- content.classList.toggle('hidden');
573
-
574
- if (!content.classList.contains('hidden')) {
575
- await MathJax.typesetPromise([content]);
576
- }
577
- });
578
-
579
- header.querySelector('.btn-delete').addEventListener('click', async () => {
580
- if (await confirmDelete()) {
581
- try {
582
- await localforage.removeItem(response.key);
583
- element.remove();
584
- showToast('Réponse supprimée', 'success');
585
- } catch (error) {
586
- console.error('Erreur de suppression:', error);
587
- showToast('Erreur lors de la suppression', 'error');
588
- }
589
- }
590
- });
591
-
592
- return element;
593
- };
594
-
595
- const confirmDelete = () => {
596
- return Swal.fire({
597
- title: 'Confirmer la suppression',
598
- text: 'Cette action est irréversible',
599
- icon: 'warning',
600
- showCancelButton: true,
601
- confirmButtonColor: '#ef4444',
602
- cancelButtonColor: '#6b7280',
603
- confirmButtonText: 'Supprimer',
604
- cancelButtonText: 'Annuler'
605
- }).then(result => result.isConfirmed);
606
- };
607
-
608
- // Initialize
609
- await loadMathJax();
610
- await loadSavedResponses();
611
-
612
- // Clear all responses
613
- document.getElementById('clearSavedResponses').addEventListener('click', async () => {
614
- const result = await Swal.fire({
615
- title: 'Tout effacer ?',
616
- text: 'Cette action supprimera toutes vos réponses sauvegardées',
617
- icon: 'warning',
618
- showCancelButton: true,
619
- confirmButtonColor: '#ef4444',
620
- cancelButtonColor: '#6b7280',
621
- confirmButtonText: 'Tout effacer',
622
- cancelButtonText: 'Annuler'
623
- });
624
-
625
- if (result.isConfirmed) {
626
- try {
627
- await localforage.clear();
628
- await loadSavedResponses();
629
- showToast('Toutes les réponses ont été supprimées', 'success');
630
- } catch (error) {
631
- console.error('Erreur de suppression:', error);
632
- showToast('Erreur lors de la suppression', 'error');
633
- }
634
- }
635
- });
636
- };
637
-
638
- // Start the application
639
- init().catch(console.error);
640
- });
641
- </script>
642
- </body>
643
- </html>