Docfile commited on
Commit
78c3147
·
verified ·
1 Parent(s): 4d3627a

Create index.html

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