Docfile commited on
Commit
bee8a16
·
verified ·
1 Parent(s): fe4735c

Delete templates/index.html

Browse files
Files changed (1) hide show
  1. templates/index.html +0 -588
templates/index.html DELETED
@@ -1,588 +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="Solution mathématique intelligente pour résoudre vos problèmes">
7
- <title>Mariam M-0 | Solution Mathématique</title>
8
-
9
- <!-- Preload des ressources critiques -->
10
- <link rel="preload" href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;700&display=swap" as="style">
11
- <link rel="preload" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/tailwind.min.css" as="style">
12
- <link rel="preload" href="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.0/gsap.min.js" as="script">
13
-
14
- <!-- Chargement asynchrone des styles -->
15
- <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/tailwind.min.css" rel="stylesheet">
16
- <link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;700&display=swap" rel="stylesheet">
17
-
18
- <!-- Scripts différés -->
19
- <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.0/gsap.min.js" defer></script>
20
- <script src="https://cdn.jsdelivr.net/npm/marked/lib/marked.umd.min.js" defer></script>
21
-
22
- <!-- MathJax config et chargement optimisé -->
23
- <script>
24
- window.MathJax = {
25
- tex: {
26
- inlineMath: [['$', '$']],
27
- displayMath: [['$$', '$$']],
28
- processEscapes: true,
29
- packages: {'[+]': ['autoload','ams']}
30
- },
31
- options: {
32
- enableMenu: false,
33
- messageStyle: 'none'
34
- },
35
- startup: {
36
- pageReady: () => {
37
- window.mathJaxReady = true;
38
- window.dispatchEvent(new CustomEvent('mathJaxReady'));
39
- }
40
- }
41
- };
42
- </script>
43
- <script src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js" id="MathJax-script" async></script>
44
-
45
- <style>
46
- /* Styles critiques chargés en premier */
47
- :root {
48
- --primary: #3b82f6;
49
- --primary-dark: #2563eb;
50
- --animation-timing: 0.3s;
51
- }
52
-
53
- /* Optimisation des performances d'animation */
54
- .optimize-animation {
55
- will-change: transform, opacity;
56
- transform: translateZ(0);
57
- backface-visibility: hidden;
58
- }
59
-
60
- /* Styles de base */
61
- body {
62
- font-family: 'Space Grotesk', -apple-system, BlinkMacSystemFont, sans-serif;
63
- background: linear-gradient(135deg, #f0f4ff 0%, #ffffff 100%);
64
- min-height: 100vh;
65
- overflow-x: hidden;
66
- }
67
-
68
- /* Media queries pour le responsive */
69
- @media (max-width: 640px) {
70
- .container {
71
- padding: 1rem;
72
- }
73
- .title {
74
- font-size: 1.75rem;
75
- }
76
- .upload-container {
77
- padding: 1rem;
78
- }
79
- }
80
-
81
- @media (min-width: 641px) and (max-width: 1024px) {
82
- .container {
83
- padding: 1.5rem;
84
- }
85
- .upload-container {
86
- padding: 1.5rem;
87
- }
88
- }
89
-
90
- /* Animations optimisées */
91
- @keyframes fadeInUp {
92
- from {
93
- opacity: 0;
94
- transform: translate3d(0, 20px, 0);
95
- }
96
- to {
97
- opacity: 1;
98
- transform: translate3d(0, 0, 0);
99
- }
100
- }
101
-
102
- .fade-in-up {
103
- animation: fadeInUp var(--animation-timing) cubic-bezier(0.4, 0, 0.2, 1) forwards;
104
- }
105
-
106
- /* Styles pour le loader et les composants UI */
107
- .loader {
108
- width: 48px;
109
- height: 48px;
110
- border: 3px solid var(--primary);
111
- border-bottom-color: transparent;
112
- border-radius: 50%;
113
- display: inline-block;
114
- animation: rotation 1s linear infinite;
115
- }
116
-
117
- @keyframes rotation {
118
- to {
119
- transform: rotate(360deg);
120
- }
121
- }
122
-
123
- .upload-area {
124
- border: 2px dashed #d1d5db;
125
- transition: all var(--animation-timing) cubic-bezier(0.4, 0, 0.2, 1);
126
- background: rgba(255, 255, 255, 0.8);
127
- backdrop-filter: blur(8px);
128
- }
129
-
130
- .upload-area:hover {
131
- border-color: var(--primary);
132
- transform: scale(1.01);
133
- }
134
-
135
- /* Style pour le contenu mathématique */
136
- .math-content {
137
- overflow-x: auto;
138
- -webkit-overflow-scrolling: touch;
139
- max-width: 100%;
140
- }
141
-
142
- /* Optimisations pour le scroll */
143
- .smooth-scroll {
144
- scroll-behavior: smooth;
145
- -webkit-overflow-scrolling: touch;
146
- }
147
- </style>
148
- </head>
149
-
150
- <body>
151
- <div class="container max-w-4xl mx-auto px-4 sm:px-6 lg:px-8">
152
- <header class="py-6 sm:py-8 md:py-10 text-center optimize-animation fade-in-up">
153
- <h1 class="title text-3xl sm:text-4xl md:text-5xl font-bold text-blue-600">Mariam M-0</h1>
154
- <p class="mt-2 text-gray-600">Solution Mathématique/Physique/Chimie Intelligente</p>
155
- </header>
156
-
157
- <main class="space-y-6 sm:space-y-8">
158
- <form id="problemForm" class="space-y-4 sm:space-y-6" novalidate>
159
- <div class="upload-area p-4 sm:p-6 md:p-8 text-center relative rounded-lg optimize-animation">
160
- <input
161
- type="file"
162
- id="imageInput"
163
- accept="image/*"
164
- class="absolute inset-0 w-full h-full opacity-0 cursor-pointer"
165
- aria-label="Choisir une image"
166
- >
167
- <div class="space-y-3">
168
- <div class="w-12 sm:w-16 h-12 sm:h-16 mx-auto border-2 border-blue-400 rounded-full flex items-center justify-center">
169
- <svg class="w-6 sm:w-8 h-6 sm:h-8 text-blue-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
170
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z" />
171
- </svg>
172
- </div>
173
- <p class="text-gray-700 font-medium">Déposez votre image ici</p>
174
- <p class="text-gray-500 text-sm">ou cliquez pour sélectionner</p>
175
- </div>
176
- </div>
177
-
178
- <div id="imagePreview" class="hidden text-center">
179
- <img id="previewImage" class="max-w-xs sm:max-w-sm md:max-w-md mx-auto rounded-lg shadow-lg optimize-animation" alt="Prévisualisation">
180
- </div>
181
-
182
- <button type="submit" class="w-full py-3 px-4 bg-blue-600 text-white font-medium rounded-lg transition-all duration-300 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2">
183
- Résoudre le problème
184
- </button>
185
- </form>
186
-
187
- <div id="loader" class="hidden mt-8 text-center optimize-animation">
188
- <span class="loader"></span>
189
- <p class="mt-4 text-gray-600">Analyse en cours...</p>
190
- </div>
191
-
192
- <section id="solution" class="hidden mt-8 space-y-6">
193
- <div class="border-t pt-4">
194
- <button id="thoughtsToggle" class="w-full flex justify-between items-center p-2 hover:bg-gray-50 rounded-lg transition-colors">
195
- <span class="font-medium text-gray-700">Processus de Réflexion</span>
196
- <span id="timestamp" class="text-blue-600 text-sm"></span>
197
- </button>
198
- <div id="thoughtsBox" class="thought-box">
199
- <div id="thoughtsContent" class="p-4 text-gray-600 math-content"></div>
200
- </div>
201
- </div>
202
- <div class="border-t pt-6">
203
- <h3 class="text-xl font-bold text-gray-800 mb-4">Solution</h3>
204
- <div id="answerContent" class="text-gray-700 math-content smooth-scroll"></div>
205
- </div>
206
- </section>
207
- </main>
208
- </div>
209
-
210
- <script>
211
- // Gestionnaire d'événements principal avec gestion améliorée des erreurs et de la performance
212
- class MathSolver {
213
- constructor() {
214
- this.form = document.getElementById('problemForm');
215
- this.imageInput = document.getElementById('imageInput');
216
- this.imagePreview = document.getElementById('imagePreview');
217
- this.previewImage = document.getElementById('previewImage');
218
- this.loader = document.getElementById('loader');
219
- this.solution = document.getElementById('solution');
220
- this.thoughtsContent = document.getElementById('thoughtsContent');
221
- this.answerContent = document.getElementById('answerContent');
222
- this.thoughtsToggle = document.getElementById('thoughtsToggle');
223
- this.thoughtsBox = document.getElementById('thoughtsBox');
224
- this.timestamp = document.getElementById('timestamp');
225
-
226
- this.startTime = null;
227
- this.timerInterval = null;
228
- this.thoughtsBuffer = '';
229
- this.answerBuffer = '';
230
- this.currentMode = null;
231
- this.updateTimeout = null;
232
- this.uploadArea = document.querySelector('.upload-area');
233
-
234
- this.init();
235
- }
236
-
237
- init() {
238
- this.setupEventListeners();
239
- this.setupMarkedOptions();
240
- this.setupIntersectionObserver();
241
- }
242
-
243
- setupEventListeners() {
244
- // Utilisation des event listeners passifs pour améliorer les performances
245
- this.form.addEventListener('submit', this.handleSubmit.bind(this));
246
- this.imageInput.addEventListener('change', (e) => this.handleFileSelect(e.target.files[0]), { passive: true });
247
- this.thoughtsToggle.addEventListener('click', this.toggleThoughts.bind(this), { passive: true });
248
-
249
- // Gestion optimisée du drag & drop
250
- this.setupDragAndDrop();
251
-
252
- // Gestionnaire de redimensionnement optimisé
253
- let resizeTimeout;
254
- window.addEventListener('resize', () => {
255
- clearTimeout(resizeTimeout);
256
- resizeTimeout = setTimeout(() => this.handleResize(), 150);
257
- }, { passive: true });
258
- }
259
-
260
- setupMarkedOptions() {
261
- marked.setOptions({
262
- gfm: true,
263
- breaks: true,
264
- sanitize: true,
265
- smartLists: true,
266
- smartypants: true
267
- });
268
- }
269
-
270
- setupIntersectionObserver() {
271
- const observer = new IntersectionObserver(
272
- (entries) => {
273
- entries.forEach(entry => {
274
- if (entry.isIntersecting) {
275
- entry.target.classList.add('fade-in-up');
276
- observer.unobserve(entry.target);
277
- }
278
- });
279
- },
280
- { threshold: 0.1 }
281
- );
282
-
283
- document.querySelectorAll('.optimize-animation').forEach(el => observer.observe(el));
284
- }
285
-
286
- setupDragAndDrop() {
287
- ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
288
- this.uploadArea.addEventListener(eventName, (e) => {
289
- e.preventDefault();
290
- e.stopPropagation();
291
- }, { passive: false });
292
- });
293
-
294
- ['dragenter', 'dragover'].forEach(eventName => {
295
- this.uploadArea.addEventListener(eventName, () => {
296
- this.uploadArea.classList.add('border-blue-400');
297
- }, { passive: true });
298
- });
299
-
300
- ['dragleave', 'drop'].forEach(eventName => {
301
- this.uploadArea.addEventListener(eventName, () => {
302
- this.uploadArea.classList.remove('border-blue-400');
303
- }, { passive: true });
304
- });
305
-
306
- this.uploadArea.addEventListener('drop', (e) => {
307
- const file = e.dataTransfer.files[0];
308
- if (file) this.handleFileSelect(file);
309
- }, { passive: false });
310
- }
311
-
312
- handleFileSelect(file) {
313
- if (!file) return;
314
-
315
- // Validation du type de fichier
316
- if (!file.type.startsWith('image/')) {
317
- this.showError('Veuillez sélectionner une image valide.');
318
- return;
319
- }
320
-
321
- // Utilisation de createObjectURL pour une meilleure performance
322
- const objectUrl = URL.createObjectURL(file);
323
- this.previewImage.onload = () => {
324
- URL.revokeObjectURL(objectUrl);
325
- this.imagePreview.classList.remove('hidden');
326
- requestAnimationFrame(() => {
327
- this.previewImage.classList.add('fade-in-up');
328
- });
329
- };
330
- this.previewImage.src = objectUrl;
331
- }
332
-
333
- async handleSubmit(e) {
334
- e.preventDefault();
335
- const file = this.imageInput.files[0];
336
- if (!file) {
337
- this.showError('Veuillez sélectionner une image.');
338
- return;
339
- }
340
-
341
- this.startProcessing();
342
-
343
- try {
344
- const formData = new FormData();
345
- formData.append('image', file);
346
-
347
- const response = await this.sendRequest(formData);
348
- await this.processResponse(response);
349
- } catch (error) {
350
- console.error('Erreur:', error);
351
- this.showError('Une erreur est survenue lors du traitement.');
352
- } finally {
353
- this.stopProcessing();
354
- }
355
- }
356
-
357
- async sendRequest(formData) {
358
- const response = await fetch('/solve', {
359
- method: 'POST',
360
- body: formData
361
- });
362
-
363
- if (!response.ok) {
364
- throw new Error(`HTTP error! status: ${response.status}`);
365
- }
366
-
367
- return response;
368
- }
369
-
370
- async processResponse(response) {
371
- const reader = response.body.getReader();
372
- const decoder = new TextDecoder();
373
- let buffer = '';
374
-
375
- const processChunk = async (chunk) => {
376
- buffer += decoder.decode(chunk, { stream: true });
377
- const lines = buffer.split('\n\n');
378
- buffer = lines.pop() || '';
379
-
380
- for (const line of lines) {
381
- if (!line.startsWith('data:')) continue;
382
-
383
- try {
384
- const data = JSON.parse(line.slice(5));
385
- await this.handleStreamData(data);
386
- } catch (error) {
387
- console.error('Erreur parsing JSON:', error);
388
- continue;
389
- }
390
- }
391
- };
392
-
393
- try {
394
- while (true) {
395
- const { done, value } = await reader.read();
396
-
397
- if (done) {
398
- if (buffer) {
399
- const data = JSON.parse(buffer.slice(5));
400
- await this.handleStreamData(data);
401
- }
402
- break;
403
- }
404
-
405
- await processChunk(value);
406
- }
407
- } catch (error) {
408
- throw new Error('Erreur lors du traitement du flux: ' + error.message);
409
- }
410
- }
411
-
412
- async handleStreamData(data) {
413
- if (data.mode) {
414
- this.currentMode = data.mode;
415
- this.loader.classList.add('hidden');
416
- this.solution.classList.remove('hidden');
417
- await this.animateSolutionAppearance();
418
- }
419
-
420
- if (data.content) {
421
- if (this.currentMode === 'thinking') {
422
- this.thoughtsBuffer += data.content;
423
- } else if (this.currentMode === 'answering') {
424
- this.answerBuffer += data.content;
425
- }
426
- await this.scheduleUpdate();
427
- }
428
- }
429
-
430
- async scheduleUpdate() {
431
- if (this.updateTimeout) return;
432
-
433
- this.updateTimeout = setTimeout(async () => {
434
- await this.updateDisplay();
435
- this.updateTimeout = null;
436
- }, 100);
437
- }
438
-
439
- async updateDisplay() {
440
- // Utilisation de requestAnimationFrame pour les mises à jour visuelles
441
- return new Promise(resolve => {
442
- requestAnimationFrame(async () => {
443
- try {
444
- // Mise à jour du contenu avec sanitization
445
- this.thoughtsContent.innerHTML = DOMPurify.sanitize(marked.parse(this.thoughtsBuffer));
446
- this.answerContent.innerHTML = DOMPurify.sanitize(marked.parse(this.answerBuffer));
447
-
448
- // Rendu MathJax optimisé
449
- if (window.mathJaxReady) {
450
- await this.renderMathJax();
451
- }
452
-
453
- // Scroll automatique optimisé
454
- this.smoothScroll();
455
- resolve();
456
- } catch (error) {
457
- console.error('Erreur mise à jour affichage:', error);
458
- resolve();
459
- }
460
- });
461
- });
462
- }
463
-
464
- async renderMathJax() {
465
- try {
466
- MathJax.startup.document.state(0);
467
- await MathJax.typesetPromise([this.answerContent]);
468
- } catch (error) {
469
- console.error('Erreur rendu MathJax:', error);
470
- }
471
- }
472
-
473
- smoothScroll() {
474
- const scrollOptions = {
475
- behavior: 'smooth',
476
- block: 'end'
477
- };
478
-
479
- if ('scrollBehavior' in document.documentElement.style) {
480
- this.answerContent.scrollIntoView(scrollOptions);
481
- } else {
482
- // Fallback pour les navigateurs ne supportant pas scroll-behavior
483
- this.answerContent.scrollIntoView(false);
484
- }
485
- }
486
-
487
- startProcessing() {
488
- this.resetState();
489
- this.startTimer();
490
- this.loader.classList.remove('hidden');
491
- this.solution.classList.add('hidden');
492
- this.thoughtsBox.classList.add('open');
493
- }
494
-
495
- stopProcessing() {
496
- this.stopTimer();
497
- this.loader.classList.add('hidden');
498
- }
499
-
500
- resetState() {
501
- this.thoughtsBuffer = '';
502
- this.answerBuffer = '';
503
- this.currentMode = null;
504
- this.thoughtsContent.innerHTML = '';
505
- this.answerContent.innerHTML = '';
506
- }
507
-
508
- startTimer() {
509
- this.startTime = Date.now();
510
- this.updateTimestamp();
511
- this.timerInterval = setInterval(() => this.updateTimestamp(), 1000);
512
- }
513
-
514
- stopTimer() {
515
- clearInterval(this.timerInterval);
516
- this.startTime = null;
517
- this.timestamp.textContent = '';
518
- }
519
-
520
- updateTimestamp() {
521
- if (!this.startTime) return;
522
-
523
- const seconds = Math.floor((Date.now() - this.startTime) / 1000);
524
- this.timestamp.textContent = `${seconds}s`;
525
- this.timestamp.classList.add('visible');
526
- }
527
-
528
- toggleThoughts() {
529
- this.thoughtsBox.classList.toggle('open');
530
- }
531
-
532
- async animateSolutionAppearance() {
533
- return new Promise(resolve => {
534
- requestAnimationFrame(() => {
535
- this.solution.style.opacity = '0';
536
- this.solution.classList.remove('hidden');
537
-
538
- requestAnimationFrame(() => {
539
- this.solution.style.transition = 'opacity 0.3s ease';
540
- this.solution.style.opacity = '1';
541
- setTimeout(resolve, 300);
542
- });
543
- });
544
- });
545
- }
546
-
547
- handleResize() {
548
- // Ajustements responsifs lors du redimensionnement
549
- const isMobile = window.innerWidth < 640;
550
- const containerPadding = isMobile ? '1rem' : '2rem';
551
- document.querySelector('.container').style.padding = containerPadding;
552
- }
553
-
554
- showError(message) {
555
- // Système de notification d'erreur optimisé
556
- const notification = document.createElement('div');
557
- notification.className = 'fixed top-4 right-4 bg-red-500 text-white px-6 py-3 rounded-lg shadow-lg transform translate-y-[-100%] opacity-0 transition-all duration-300';
558
- notification.textContent = message;
559
-
560
- document.body.appendChild(notification);
561
-
562
- requestAnimationFrame(() => {
563
- notification.style.transform = 'translate(0)';
564
- notification.style.opacity = '1';
565
-
566
- setTimeout(() => {
567
- notification.style.transform = 'translate-y-[-100%]';
568
- notification.style.opacity = '0';
569
- setTimeout(() => notification.remove(), 300);
570
- }, 3000);
571
- });
572
- }
573
- }
574
-
575
- // Initialisation de l'application avec gestion des erreurs
576
- window.addEventListener('DOMContentLoaded', () => {
577
- try {
578
- window.mathSolver = new MathSolver();
579
- } catch (error) {
580
- console.error('Erreur initialisation:', error);
581
- }
582
- });
583
- </script>
584
-
585
- <!-- Script de sanitization -->
586
- <script src="https://cdnjs.cloudflare.com/ajax/libs/dompurify/2.4.1/purify.min.js" defer></script>
587
- </body>
588
- </html>