Docfile commited on
Commit
9aabf46
·
verified ·
1 Parent(s): cb2907c

Delete templates/index.html

Browse files
Files changed (1) hide show
  1. templates/index.html +0 -413
templates/index.html DELETED
@@ -1,413 +0,0 @@
1
-
2
-
3
- <!DOCTYPE html>
4
- <html lang="fr">
5
- <head>
6
- <meta charset="UTF-8">
7
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
8
- <title>Mariam M-0 | Solution Mathématique</title>
9
- <!-- Tailwind CSS -->
10
- <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/tailwind.min.css" rel="stylesheet">
11
-
12
- <!-- SweetAlert2 -->
13
- <script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
14
-
15
- <!-- Configuration de MathJax -->
16
- <script>
17
- window.MathJax = {
18
- tex: {
19
- inlineMath: [['$', '$']],
20
- displayMath: [['$$', '$$']],
21
- processEscapes: true,
22
- packages: {'[+]': ['autoload', 'ams']}
23
- },
24
- options: {
25
- enableMenu: false,
26
- messageStyle: 'none'
27
- },
28
- startup: {
29
- pageReady: () => { window.mathJaxReady = true; }
30
- }
31
- };
32
- </script>
33
- <script src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js" id="MathJax-script" async></script>
34
- <script src="https://cdn.jsdelivr.net/npm/[email protected]/lib/marked.umd.min.js"></script>
35
-
36
- <style>
37
- @import url('https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;700&display=swap');
38
- body { font-family: 'Space Grotesk', sans-serif; }
39
-
40
- .uploadArea {
41
- background: #f3f4f6;
42
- border: 2px dashed #d1d5db;
43
- transition: border-color 0.2s ease;
44
- }
45
- .uploadArea:hover { border-color: #3b82f6; }
46
-
47
- .blue-button { background: #3b82f6; transition: background-color 0.2s ease; }
48
- .blue-button:hover { background: #2563eb; }
49
-
50
- .loader {
51
- width: 48px;
52
- height: 48px;
53
- border: 3px solid #3b82f6;
54
- border-bottom-color: transparent;
55
- border-radius: 50%;
56
- display: inline-block;
57
- animation: rotation 1s linear infinite;
58
- }
59
- @keyframes rotation { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
60
-
61
- .thought-box {
62
- transition: max-height 0.3s ease-out;
63
- max-height: 0;
64
- overflow: hidden;
65
- }
66
- .thought-box.open { max-height: 500px; }
67
-
68
- #thoughtsContent, #answerContent {
69
- max-height: 500px;
70
- overflow-y: auto;
71
- scroll-behavior: smooth;
72
- white-space: pre-wrap;
73
- }
74
-
75
- .preview-image { max-width: 300px; max-height: 300px; object-fit: contain; }
76
-
77
- .timestamp { color: #3b82f6; font-size: 0.9em; margin-left: 8px; }
78
-
79
- table {
80
- border-collapse: collapse;
81
- width: 100%;
82
- margin-bottom: 1rem;
83
- }
84
- th, td {
85
- border: 1px solid #d1d5db;
86
- padding: 0.5rem;
87
- text-align: left;
88
- }
89
- th { background-color: #f3f4f6; font-weight: 600; }
90
- .table-responsive { overflow-x: auto; }
91
-
92
- /* Style pour le bouton Sauvegarder afin de le mettre en évidence */
93
- #saveButton {
94
- background: #3b82f6;
95
- color: white;
96
- padding: 0.5rem 1rem;
97
- border-radius: 0.375rem;
98
- transition: background-color 0.2s ease;
99
- }
100
- #saveButton:hover { background: #2563eb; }
101
-
102
- /* Modal plein écran pour les sauvegardes */
103
- #savedModal {
104
- display: none;
105
- position: fixed;
106
- inset: 0;
107
- background: rgba(0,0,0,0.5);
108
- z-index: 50;
109
- }
110
- #savedModal.active { display: block; }
111
- #savedModalContent {
112
- background: #fff;
113
- width: 100%;
114
- height: 100%;
115
- overflow-y: auto;
116
- }
117
- </style>
118
- </head>
119
- <body class="p-4">
120
- <div class="max-w-4xl mx-auto">
121
- <header class="p-6 text-center mb-8">
122
- <h1 class="text-4xl font-bold text-blue-600">Mariam M-1</h1>
123
- <p class="text-gray-600">Solution Mathématique/Physique/Chimie Intelligente</p>
124
- <div class="mt-4 flex justify-end">
125
- <button id="openSaved" class="blue-button px-4 py-2 text-white rounded">Sauvegardes</button>
126
- </div>
127
- </header>
128
-
129
- <main id="mainContent">
130
- <form id="problemForm" class="space-y-6" novalidate>
131
- <!-- Zone de dépôt / sélection d'image -->
132
- <div class="uploadArea p-8 text-center relative" aria-label="Zone de dépôt d'image">
133
- <input type="file" id="imageInput" accept="image/*" class="absolute inset-0 w-full h-full opacity-0 cursor-pointer" aria-label="Choisir une image">
134
- <div class="space-y-3">
135
- <div class="w-16 h-16 mx-auto border-2 border-blue-400 rounded-full flex items-center justify-center">
136
- <svg class="w-8 h-8 text-blue-400" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
137
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
138
- 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" />
139
- </svg>
140
- </div>
141
- <p class="text-gray-700 font-medium">Déposez votre image ici</p>
142
- <p class="text-gray-500 text-sm">ou cliquez pour sélectionner</p>
143
- </div>
144
- </div>
145
- <!-- Aperçu de l'image -->
146
- <div id="imagePreview" class="hidden text-center">
147
- <img id="previewImage" class="preview-image mx-auto" alt="Prévisualisation de l'image">
148
- </div>
149
- <button type="submit" class="blue-button w-full py-3 text-white font-medium rounded-lg">
150
- Résoudre le problème
151
- </button>
152
- </form>
153
-
154
- <!-- Loader -->
155
- <div id="loader" class="hidden mt-8 text-center">
156
- <span class="loader"></span>
157
- <p class="mt-4 text-gray-600">Analyse en cours...</p>
158
- </div>
159
-
160
- <!-- Zone d'affichage de la solution -->
161
- <section id="solution" class="hidden mt-8 space-y-6 relative">
162
- <div class="border-t pt-4">
163
- <button id="thoughtsToggle" type="button" class="w-full flex justify-between items-center p-2">
164
- <span class="font-medium text-gray-700">Processus de Réflexion</span>
165
- <span id="timestamp" class="timestamp"></span>
166
- </button>
167
- <div id="thoughtsBox" class="thought-box">
168
- <div id="thoughtsContent" class="p-4 text-gray-600"></div>
169
- </div>
170
- </div>
171
- <div class="border-t pt-6">
172
- <div class="flex justify-between items-center">
173
- <h3 class="text-xl font-bold text-gray-800 mb-4">Solution</h3>
174
- <!-- Bouton Sauvegarder mis en évidence -->
175
- <button id="saveButton">Sauvegarder</button>
176
- </div>
177
- <div id="answerContent" class="text-gray-700 table-responsive"></div>
178
- </div>
179
- </section>
180
- </main>
181
- </div>
182
-
183
- <!-- Modal plein écran pour les sauvegardes -->
184
- <div id="savedModal">
185
- <div id="savedModalContent" class="p-6">
186
- <header class="flex justify-between items-center border-b pb-4">
187
- <h2 class="text-2xl font-bold">Sauvegardes</h2>
188
- <button id="closeSaved" class="text-3xl text-gray-600">&times;</button>
189
- </header>
190
- <div id="savedListContainer" class="mt-4">
191
- <ul id="savedList" class="space-y-4">
192
- <!-- Liste des sauvegardes insérée dynamiquement -->
193
- </ul>
194
- </div>
195
- <div class="mt-6">
196
- <button id="newExercise" class="blue-button w-full py-3 text-white font-medium rounded-lg">
197
- Résoudre un nouvel exercice
198
- </button>
199
- </div>
200
- </div>
201
- </div>
202
-
203
- <script>
204
- document.addEventListener('DOMContentLoaded', () => {
205
- // Récupération des éléments
206
- const form = document.getElementById('problemForm');
207
- const imageInput = document.getElementById('imageInput');
208
- const loader = document.getElementById('loader');
209
- const solutionSection = document.getElementById('solution');
210
- const thoughtsContent = document.getElementById('thoughtsContent');
211
- const answerContent = document.getElementById('answerContent');
212
- const thoughtsToggle = document.getElementById('thoughtsToggle');
213
- const thoughtsBox = document.getElementById('thoughtsBox');
214
- const imagePreview = document.getElementById('imagePreview');
215
- const previewImage = document.getElementById('previewImage');
216
- const timestamp = document.getElementById('timestamp');
217
- const saveButton = document.getElementById('saveButton');
218
- const openSaved = document.getElementById('openSaved');
219
- const closeSaved = document.getElementById('closeSaved');
220
- const savedModal = document.getElementById('savedModal');
221
- const savedList = document.getElementById('savedList');
222
- const newExercise = document.getElementById('newExercise');
223
- const mainContent = document.getElementById('mainContent');
224
-
225
- let startTime = null;
226
- let timerInterval = null;
227
- let thoughtsBuffer = '';
228
- let answerBuffer = '';
229
- let currentMode = null;
230
- let updateTimeout = null;
231
-
232
- // Mise à jour du temps écoulé
233
- const updateTimestamp = () => {
234
- if (startTime) {
235
- const seconds = Math.floor((Date.now() - startTime) / 1000);
236
- timestamp.textContent = `${seconds}s`;
237
- }
238
- };
239
- const startTimer = () => { startTime = Date.now(); timerInterval = setInterval(updateTimestamp, 1000); updateTimestamp(); };
240
- const stopTimer = () => { clearInterval(timerInterval); startTime = null; timestamp.textContent = ''; };
241
-
242
- // Affichage de l'image sélectionnée
243
- const handleFileSelect = file => {
244
- if (!file) return;
245
- const reader = new FileReader();
246
- reader.onload = e => {
247
- previewImage.src = e.target.result;
248
- imagePreview.classList.remove('hidden');
249
- };
250
- reader.readAsDataURL(file);
251
- };
252
-
253
- thoughtsToggle.addEventListener('click', () => { thoughtsBox.classList.toggle('open'); });
254
- imageInput.addEventListener('change', e => handleFileSelect(e.target.files[0]));
255
-
256
- // Gestion du glisser-déposer
257
- const dropZone = document.querySelector('.uploadArea');
258
- dropZone.addEventListener('dragover', e => { e.preventDefault(); dropZone.classList.add('border-blue-400'); });
259
- dropZone.addEventListener('dragleave', e => { e.preventDefault(); dropZone.classList.remove('border-blue-400'); });
260
- dropZone.addEventListener('drop', e => { e.preventDefault(); dropZone.classList.remove('border-blue-400'); handleFileSelect(e.dataTransfer.files[0]); });
261
-
262
- // Rendu MathJax et mise à jour de l'affichage
263
- const typesetAnswerIfReady = async () => {
264
- if (window.mathJaxReady) {
265
- MathJax.startup.document.elements = [document.getElementById('answerContent')];
266
- await MathJax.typesetPromise();
267
- answerContent.scrollTop = answerContent.scrollHeight;
268
- } else { setTimeout(typesetAnswerIfReady, 200); }
269
- };
270
- const updateDisplay = async () => {
271
- thoughtsContent.innerHTML = marked.parse(thoughtsBuffer);
272
- answerContent.innerHTML = marked.parse(answerBuffer);
273
- await typesetAnswerIfReady();
274
- updateTimeout = null;
275
- };
276
- const scheduleUpdate = () => { if (!updateTimeout) updateTimeout = setTimeout(updateDisplay, 200); };
277
-
278
- marked.setOptions({ gfm: true, breaks: true });
279
-
280
- // Envoi de l'image pour résolution
281
- form.addEventListener('submit', async e => {
282
- e.preventDefault();
283
- const file = imageInput.files[0];
284
- if (!file) { alert('Veuillez sélectionner une image.'); return; }
285
- startTimer();
286
- loader.classList.remove('hidden');
287
- solutionSection.classList.add('hidden');
288
- thoughtsContent.innerHTML = '';
289
- answerContent.innerHTML = '';
290
- thoughtsBuffer = '';
291
- answerBuffer = '';
292
- currentMode = null;
293
- thoughtsBox.classList.add('open');
294
-
295
- const formData = new FormData();
296
- formData.append('image', file);
297
- try {
298
- const response = await fetch('/solve', { method: 'POST', body: formData });
299
- const reader = response.body.getReader();
300
- const decoder = new TextDecoder();
301
- let buffer = '';
302
- const processChunk = async chunk => {
303
- buffer += decoder.decode(chunk, { stream: true });
304
- const lines = buffer.split('\n\n');
305
- buffer = lines.pop();
306
- for (const line of lines) {
307
- if (!line.startsWith('data:')) continue;
308
- const data = JSON.parse(line.slice(5));
309
- if (data.mode) {
310
- currentMode = data.mode;
311
- loader.classList.add('hidden');
312
- solutionSection.classList.remove('hidden');
313
- }
314
- if (data.content) {
315
- if (currentMode === 'thinking') { thoughtsBuffer += data.content; }
316
- else if (currentMode === 'answering') { answerBuffer += data.content; }
317
- }
318
- }
319
- scheduleUpdate();
320
- };
321
- while (true) {
322
- const { done, value } = await reader.read();
323
- if (done) {
324
- if (buffer) {
325
- const data = JSON.parse(buffer.slice(5));
326
- if (data.content) {
327
- if (currentMode === 'thinking') { thoughtsBuffer += data.content; }
328
- else if (currentMode === 'answering') { answerBuffer += data.content; }
329
- }
330
- }
331
- scheduleUpdate();
332
- break;
333
- }
334
- await processChunk(value);
335
- }
336
- stopTimer();
337
- } catch (error) {
338
- console.error('Erreur:', error);
339
- alert('Une erreur est survenue.');
340
- loader.classList.add('hidden');
341
- stopTimer();
342
- }
343
- });
344
-
345
- // Sauvegarde de la solution avec SweetAlert2
346
- saveButton.addEventListener('click', () => {
347
- const saveName = prompt("Entrez un nom pour la sauvegarde :");
348
- if (!saveName) return;
349
- const saveData = {
350
- answer: answerContent.innerHTML,
351
- thinking: thoughtsContent.innerHTML,
352
- date: new Date().toLocaleString()
353
- };
354
- let savedExercises = JSON.parse(localStorage.getItem('savedExercises') || '{}');
355
- savedExercises[saveName] = saveData;
356
- localStorage.setItem('savedExercises', JSON.stringify(savedExercises));
357
- Swal.fire({
358
- icon: 'success',
359
- title: 'Sauvegarde réussie',
360
- text: 'Votre solution a bien été sauvegardée !',
361
- timer: 2000,
362
- showConfirmButton: false
363
- });
364
- });
365
-
366
- // Chargement des sauvegardes dans le modal
367
- const loadSavedList = () => {
368
- savedList.innerHTML = '';
369
- const savedExercises = JSON.parse(localStorage.getItem('savedExercises') || '{}');
370
- for (const [name, data] of Object.entries(savedExercises)) {
371
- const li = document.createElement('li');
372
- li.innerHTML = `<button class="w-full text-left text-blue-600 hover:underline" data-save="${name}">${name} <span class="text-gray-500 text-xs">(${data.date})</span></button>`;
373
- savedList.appendChild(li);
374
- }
375
- };
376
-
377
- savedList.addEventListener('click', (e) => {
378
- if (e.target && e.target.dataset.save) {
379
- const saveName = e.target.dataset.save;
380
- const savedExercises = JSON.parse(localStorage.getItem('savedExercises') || '{}');
381
- const data = savedExercises[saveName];
382
- if (data) {
383
- form.classList.add('hidden');
384
- loader.classList.add('hidden');
385
- solutionSection.classList.remove('hidden');
386
- thoughtsContent.innerHTML = data.thinking;
387
- answerContent.innerHTML = data.answer;
388
- savedModal.classList.remove('active');
389
- }
390
- }
391
- });
392
-
393
- // Ouverture / fermeture du modal de sauvegardes
394
- openSaved.addEventListener('click', () => { loadSavedList(); savedModal.classList.add('active'); });
395
- closeSaved.addEventListener('click', () => { savedModal.classList.remove('active'); });
396
-
397
- // Bouton présent uniquement dans le modal pour lancer un nouvel exercice
398
- newExercise.addEventListener('click', () => {
399
- form.reset();
400
- form.classList.remove('hidden');
401
- solutionSection.classList.add('hidden');
402
- imagePreview.classList.add('hidden');
403
- thoughtsContent.innerHTML = '';
404
- answerContent.innerHTML = '';
405
- thoughtsBuffer = '';
406
- answerBuffer = '';
407
- savedModal.classList.remove('active');
408
- });
409
- });
410
- </script>
411
- </body>
412
- </html>
413
-