Docfile commited on
Commit
17887be
·
verified ·
1 Parent(s): 4948acf

Create index.html

Browse files
Files changed (1) hide show
  1. templates/index.html +404 -0
templates/index.html ADDED
@@ -0,0 +1,404 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ <title>Mariam M-0 | Solution Mathématique</title>
7
+ <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/tailwind.min.css" rel="stylesheet">
8
+ <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
9
+ <script>
10
+ window.MathJax = {
11
+ tex: {
12
+ inlineMath: [['$', '$'], ['\\(', '\\)']],
13
+ displayMath: [['$$', '$$'], ['\\[', '\\]']],
14
+ processEscapes: true,
15
+ packages: ['base', 'ams', 'noerrors', 'noundefined']
16
+ },
17
+ options: {
18
+ ignoreHtmlClass: 'tex2jax_ignore',
19
+ processHtmlClass: 'tex2jax_process'
20
+ },
21
+ svg: {
22
+ fontCache: 'global'
23
+ }
24
+ };
25
+ </script>
26
+ <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script>
27
+ <style>
28
+ @import url('https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300;400;500;600;700&display=swap');
29
+
30
+ body {
31
+ font-family: 'Space Grotesk', sans-serif;
32
+ background-color: white;
33
+ }
34
+
35
+ .uploadArea {
36
+ background: #f3f4f6;
37
+ border: 2px dashed #d1d5db;
38
+ }
39
+
40
+ .uploadArea:hover {
41
+ border-color: #3b82f6;
42
+ transform: translateY(-2px);
43
+ }
44
+
45
+ .blue-button {
46
+ background: #3b82f6;
47
+ transition: all 0.3s ease;
48
+ }
49
+
50
+ .blue-button:hover {
51
+ background: #2563eb;
52
+ transform: translateY(-2px);
53
+ }
54
+
55
+ .loader {
56
+ width: 48px;
57
+ height: 48px;
58
+ border: 3px solid #3b82f6;
59
+ border-bottom-color: transparent;
60
+ border-radius: 50%;
61
+ display: inline-block;
62
+ box-sizing: border-box;
63
+ animation: rotation 1s linear infinite;
64
+ }
65
+
66
+ @keyframes rotation {
67
+ 0% { transform: rotate(0deg); }
68
+ 100% { transform: rotate(360deg); }
69
+ }
70
+
71
+ .thought-box {
72
+ transition: all 0.5s cubic-bezier(0.4, 0, 0.2, 1);
73
+ max-height: 0;
74
+ overflow: hidden;
75
+ }
76
+
77
+ .thought-box.open {
78
+ max-height: 1000px;
79
+ }
80
+
81
+ .preview-container {
82
+ display: flex;
83
+ justify-content: center;
84
+ align-items: center;
85
+ margin-top: 20px;
86
+ }
87
+
88
+ .preview-image {
89
+ max-width: 300px;
90
+ max-height: 300px;
91
+ border-radius: 8px;
92
+ }
93
+
94
+ #thoughtsContent, #answerContent {
95
+ max-height: 500px;
96
+ overflow-y: auto;
97
+ }
98
+
99
+ .timestamp {
100
+ color: #3b82f6;
101
+ margin-left: 10px;
102
+ font-size: 0.9em;
103
+ }
104
+
105
+ /* Styles pour le contenu Markdown */
106
+ .markdown-content h1 { @apply text-3xl font-bold my-4; }
107
+ .markdown-content h2 { @apply text-2xl font-bold my-3; }
108
+ .markdown-content h3 { @apply text-xl font-bold my-2; }
109
+ .markdown-content p { @apply my-2; }
110
+ .markdown-content ul { @apply list-disc list-inside my-2; }
111
+ .markdown-content ol { @apply list-decimal list-inside my-2; }
112
+ .markdown-content code { @apply bg-gray-100 px-1 rounded; }
113
+ .markdown-content pre { @apply bg-gray-100 p-4 rounded my-2; }
114
+ .markdown-content blockquote { @apply border-l-4 border-gray-300 pl-4 my-2; }
115
+ .markdown-content a { @apply text-blue-600 hover:underline; }
116
+ .markdown-content table { @apply w-full border-collapse my-4; }
117
+ .markdown-content th, .markdown-content td { @apply border p-2; }
118
+ .markdown-content img { @apply max-w-full h-auto my-4; }
119
+ </style>
120
+ </head>
121
+ <body class="p-4">
122
+ <div class="w-full">
123
+ <div class="p-8">
124
+ <div class="text-center mb-12">
125
+ <h1 class="text-4xl font-bold text-blue-600 mb-4">Mariam M-0</h1>
126
+ <p class="text-gray-600 text-lg">Solution Mathématique Intelligente</p>
127
+ </div>
128
+
129
+ <form id="problemForm" class="space-y-8" enctype="multipart/form-data">
130
+ <div class="relative">
131
+ <div class="uploadArea p-12 text-center transition-all duration-300 cursor-pointer">
132
+ <input type="file" id="imageInput" name="image" accept="image/*" class="absolute inset-0 w-full h-full opacity-0 cursor-pointer">
133
+ <div class="space-y-4">
134
+ <div class="w-20 h-20 mx-auto border-2 border-blue-400 rounded-full flex items-center justify-center">
135
+ <svg class="w-10 h-10 text-blue-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
136
+ <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" />
137
+ </svg>
138
+ </div>
139
+ <div class="text-gray-700 text-lg font-medium">Déposez votre image ici</div>
140
+ <div class="text-gray-500 text-sm">ou cliquez pour sélectionner</div>
141
+ </div>
142
+ </div>
143
+ <div id="imagePreview" class="preview-container hidden">
144
+ <img id="previewImage" src="#" alt="Prévisualisation de l'image" class="preview-image">
145
+ </div>
146
+ </div>
147
+
148
+ <button type="submit" class="blue-button w-full py-4 text-white font-medium text-lg transition-all duration-300">
149
+ Résoudre le problème
150
+ </button>
151
+ </form>
152
+
153
+ <div id="loader" class="hidden mt-12">
154
+ <div class="flex flex-col items-center justify-center space-y-4">
155
+ <span class="loader"></span>
156
+ <div class="text-gray-600 text-lg">Analyse en cours...</div>
157
+ </div>
158
+ </div>
159
+
160
+ <div id="solution" class="hidden mt-12 space-y-8">
161
+ <div class="border-t pt-6">
162
+ <button id="thoughtsToggle" class="w-full flex justify-between items-center text-left text-lg font-medium text-gray-700 hover:text-blue-600 transition-colors">
163
+ <div class="flex items-center">
164
+ <span>Processus de Réflexion</span>
165
+ <span id="timestamp" class="timestamp"></span>
166
+ </div>
167
+ <svg class="w-6 h-6 transform transition-transform duration-300" fill="none" stroke="currentColor" viewBox="0 0 24 24">
168
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/>
169
+ </svg>
170
+ </button>
171
+ <div id="thoughtsBox" class="thought-box">
172
+ <div id="thoughtsContent" class="prose max-w-none text-gray-600 mt-4 markdown-content"></div>
173
+ </div>
174
+ </div>
175
+
176
+ <div class="border-t pt-8">
177
+ <h3 class="text-2xl font-bold text-gray-800 mb-6">Solution</h3>
178
+ <div id="answerContent" class="prose max-w-none text-gray-700 markdown-content"></div>
179
+ </div>
180
+ </div>
181
+ </div>
182
+ </div>
183
+
184
+ <script>
185
+ document.addEventListener('DOMContentLoaded', () => {
186
+ const form = document.getElementById('problemForm');
187
+ const imageInput = document.getElementById('imageInput');
188
+ const loader = document.getElementById('loader');
189
+ const solutionDiv = document.getElementById('solution');
190
+ const thoughtsContent = document.getElementById('thoughtsContent');
191
+ const answerContent = document.getElementById('answerContent');
192
+ const thoughtsToggle = document.getElementById('thoughtsToggle');
193
+ const thoughtsBox = document.getElementById('thoughtsBox');
194
+ const imagePreview = document.getElementById('imagePreview');
195
+ const previewImage = document.getElementById('previewImage');
196
+ const timestamp = document.getElementById('timestamp');
197
+
198
+ let startTime = null;
199
+ let timerInterval = null;
200
+
201
+ // Configuration de marked
202
+ marked.setOptions({
203
+ breaks: true,
204
+ gfm: true,
205
+ renderer: new marked.Renderer(),
206
+ highlight: function(code, lang) {
207
+ if (lang === 'math') {
208
+ return `$$${code}$$`;
209
+ }
210
+ return code;
211
+ }
212
+ });
213
+
214
+ // Fonction pour traiter le contenu avec Markdown et MathJax
215
+ function processContent(content, container) {
216
+ // Protection des expressions MathJax avant le traitement Markdown
217
+ const mathExpressions = [];
218
+ let protectedContent = content.replace(/\$\$([\s\S]*?)\$\$|\$(.*?)\$/g, (match, displayMath, inlineMath) => {
219
+ mathExpressions.push(match);
220
+ return `MATHPLACEHOLDER${mathExpressions.length - 1}`;
221
+ });
222
+
223
+ // Conversion Markdown
224
+ let htmlContent = marked.parse(protectedContent);
225
+
226
+ // Restauration des expressions MathJax
227
+ htmlContent = htmlContent.replace(/MATHPLACEHOLDER(\d+)/g, (_, index) => mathExpressions[index]);
228
+
229
+ // Ajout du contenu au container
230
+ container.innerHTML += htmlContent;
231
+
232
+ // Traitement MathJax
233
+ if (window.MathJax) {
234
+ MathJax.typesetPromise([container]).catch((err) => console.log('MathJax error:', err));
235
+ }
236
+ }
237
+
238
+ function updateTimestamp() {
239
+ if (startTime) {
240
+ const elapsed = Math.floor((Date.now() - startTime) / 1000);
241
+ timestamp.textContent = elapsed + "s";
242
+ }
243
+ }
244
+
245
+ function startTimer() {
246
+ startTime = Date.now();
247
+ timerInterval = setInterval(updateTimestamp, 1000);
248
+ updateTimestamp();
249
+ }
250
+
251
+ function stopTimer() {
252
+ if (timerInterval) {
253
+ clearInterval(timerInterval);
254
+ timerInterval = null;
255
+ }
256
+ startTime = null;
257
+ timestamp.textContent = "";
258
+ }
259
+
260
+ thoughtsToggle.addEventListener('click', () => {
261
+ thoughtsBox.classList.toggle('open');
262
+ thoughtsToggle.querySelector('svg').classList.toggle('rotate-180');
263
+ });
264
+
265
+ // Gestion de la prévisualisation de l'image
266
+ imageInput.addEventListener('change', function(e) {
267
+ const file = this.files[0];
268
+ if (file) {
269
+ const reader = new FileReader();
270
+ reader.onload = function(e) {
271
+ previewImage.src = e.target.result;
272
+ imagePreview.classList.remove('hidden');
273
+ }
274
+ reader.readAsDataURL(file);
275
+ } else {
276
+ previewImage.src = "";
277
+ imagePreview.classList.add('hidden');
278
+ }
279
+ });
280
+
281
+ // Gestion du drag & drop
282
+ const dropZone = document.querySelector('.uploadArea');
283
+
284
+ ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
285
+ dropZone.addEventListener(eventName, preventDefaults, false);
286
+ });
287
+
288
+ function preventDefaults(e) {
289
+ e.preventDefault();
290
+ e.stopPropagation();
291
+ }
292
+
293
+ ['dragenter', 'dragover'].forEach(eventName => {
294
+ dropZone.addEventListener(eventName, highlight, false);
295
+ });
296
+
297
+ ['dragleave', 'drop'].forEach(eventName => {
298
+ dropZone.addEventListener(eventName, unhighlight, false);
299
+ });
300
+
301
+ function highlight(e) {
302
+ dropZone.classList.add('border-blue-400');
303
+ }
304
+
305
+ function unhighlight(e) {
306
+ dropZone.classList.remove('border-blue-400');
307
+ }
308
+
309
+ dropZone.addEventListener('drop', handleDrop, false);
310
+
311
+ function handleDrop(e) {
312
+ const dt = e.dataTransfer;
313
+ const files = dt.files;
314
+
315
+ if (files.length) {
316
+ imageInput.files = files;
317
+ const file = files[0];
318
+ const reader = new FileReader();
319
+ reader.onload = function(e) {
320
+ previewImage.src = e.target.result;
321
+ imagePreview.classList.remove('hidden');
322
+ }
323
+ reader.readAsDataURL(file);
324
+ }
325
+ }
326
+
327
+ // Gestion de la soumission du formulaire
328
+ form.addEventListener('submit', async (event) => {
329
+ event.preventDefault();
330
+ const file = imageInput.files[0];
331
+ if (!file) {
332
+ alert('Veuillez sélectionner une image.');
333
+ return;
334
+ }
335
+
336
+ startTimer();
337
+
338
+ loader.classList.remove('hidden');
339
+ solutionDiv.classList.add('hidden');
340
+ thoughtsContent.innerHTML = '';
341
+ answerContent.innerHTML = '';
342
+ thoughtsBox.classList.add('open');
343
+
344
+ const formData = new FormData();
345
+ formData.append('image', file);
346
+
347
+ try {
348
+ let currentMode = null;
349
+ const response = await fetch('/solve', {
350
+ method: 'POST',
351
+ body: formData
352
+ });
353
+
354
+ const reader = response.body.getReader();
355
+ const decoder = new TextDecoder();
356
+ let buffer = '';
357
+
358
+ while (true) {
359
+ const { done, value } = await reader.read();
360
+ if (done) {
361
+ stopTimer();
362
+ break;
363
+ }
364
+
365
+ buffer += decoder.decode(value, { stream: true });
366
+ let eolIndex;
367
+
368
+ while ((eolIndex = buffer.indexOf('\n\n')) >= 0) {
369
+ const line = buffer.slice(0, eolIndex).trim();
370
+ buffer = buffer.slice(eolIndex + 2);
371
+
372
+ if (line.startsWith('data:')) {
373
+ const data = JSON.parse(line.slice(5));
374
+
375
+ if (data.mode) {
376
+ currentMode = data.mode;
377
+ loader.classList.add('hidden');
378
+ solutionDiv.classList.remove('hidden');
379
+ }
380
+
381
+ if (data.content) {
382
+ const content = data.content;
383
+ if (currentMode === 'thinking') {
384
+ processContent(content, thoughtsContent);
385
+ thoughtsContent.scrollTop = thoughtsContent.scrollHeight;
386
+ } else if (currentMode === 'answering') {
387
+ processContent(content, answerContent);
388
+ answerContent.scrollTop = answerContent.scrollHeight;
389
+ }
390
+ }
391
+ }
392
+ }
393
+ }
394
+ } catch (error) {
395
+ console.error('Erreur:', error);
396
+ alert('Une erreur est survenue lors du traitement de la requête.');
397
+ loader.classList.add('hidden');
398
+ stopTimer();
399
+ }
400
+ });
401
+ });
402
+ </script>
403
+ </body>
404
+ </html>