Docfile commited on
Commit
e8228d2
·
verified ·
1 Parent(s): f7af6f6

Update templates/index.html

Browse files
Files changed (1) hide show
  1. templates/index.html +43 -48
templates/index.html CHANGED
@@ -1,24 +1,22 @@
1
-
2
  <!DOCTYPE html>
3
  <html lang="fr">
4
  <head>
5
  <meta charset="UTF-8">
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
  <title>Mariam M-0 | Solution Mathématique</title>
8
- <!-- Tailwind CSS (version 2.2.19 utilisée ici, à mettre à jour si besoin) -->
9
  <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/tailwind.min.css" rel="stylesheet">
10
-
11
- <!-- Configuration optimisée de MathJax avec support étendu LaTeX -->
 
12
  <script>
13
  window.MathJax = {
14
  tex: {
15
  inlineMath: [['$', '$']],
16
  displayMath: [['$$', '$$']],
17
  processEscapes: true,
18
- // Chargement automatique de commandes supplémentaires (ex. environnements de tableaux)
19
- packages: {'[+]': ['autoload','ams']}
20
  },
21
-
22
  options: {
23
  enableMenu: false,
24
  messageStyle: 'none'
@@ -32,32 +30,34 @@
32
  };
33
  </script>
34
  <script src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js" id="MathJax-script" async></script>
 
35
  <script src="https://cdn.jsdelivr.net/npm/marked/lib/marked.umd.min.js"></script>
36
-
37
  <style>
38
- @import url('https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;700&display=swap');
39
-
40
  body {
41
  font-family: 'Space Grotesk', sans-serif;
 
 
 
 
 
42
  }
43
-
44
  .uploadArea {
45
  background: #f3f4f6;
46
  border: 2px dashed #d1d5db;
47
- transition: border-color 0.2s ease;
48
  }
49
  .uploadArea:hover {
50
  border-color: #3b82f6;
 
51
  }
52
-
53
  .blue-button {
54
  background: #3b82f6;
55
- transition: background-color 0.2s ease;
56
  }
57
  .blue-button:hover {
58
  background: #2563eb;
 
59
  }
60
-
61
  .loader {
62
  width: 48px;
63
  height: 48px;
@@ -71,36 +71,36 @@
71
  0% { transform: rotate(0deg); }
72
  100% { transform: rotate(360deg); }
73
  }
74
-
75
  .thought-box {
76
- transition: max-height 0.3s ease-out;
77
  max-height: 0;
 
78
  overflow: hidden;
79
  }
80
  .thought-box.open {
81
  max-height: 500px;
 
82
  }
83
-
84
  #thoughtsContent, #answerContent {
85
  max-height: 500px;
86
  overflow-y: auto;
87
  scroll-behavior: smooth;
88
  white-space: pre-wrap;
89
  }
90
-
91
  .preview-image {
92
  max-width: 300px;
93
  max-height: 300px;
94
  object-fit: contain;
 
 
 
 
95
  }
96
-
97
  .timestamp {
98
  color: #3b82f6;
99
  font-size: 0.9em;
100
  margin-left: 8px;
101
  }
102
-
103
- /* Styles supplémentaires pour une meilleure présentation des tableaux */
104
  table {
105
  border-collapse: collapse;
106
  width: 100%;
@@ -115,20 +115,32 @@
115
  background-color: #f3f4f6;
116
  font-weight: 600;
117
  }
118
- /* Pour un rendu responsive, ajouter éventuellement : */
119
  .table-responsive {
120
  overflow-x: auto;
121
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
122
  </style>
123
  </head>
124
  <body class="p-4">
125
  <div class="max-w-4xl mx-auto">
126
- <header class="p-6 text-center mb-8">
127
  <h1 class="text-4xl font-bold text-blue-600">Mariam M-0</h1>
128
  <p class="text-gray-600">Solution Mathématique/Physique/Chimie Intelligente</p>
129
  </header>
130
 
131
- <main>
132
  <form id="problemForm" class="space-y-6" novalidate>
133
  <!-- Zone de dépôt et sélection d'image -->
134
  <div class="uploadArea p-8 text-center relative" aria-label="Zone de dépôt d'image">
@@ -164,7 +176,7 @@
164
  <!-- Affichage de la solution -->
165
  <section id="solution" class="hidden mt-8 space-y-6">
166
  <div class="border-t pt-4">
167
- <button id="thoughtsToggle" type="button" class="w-full flex justify-between items-center p-2">
168
  <span class="font-medium text-gray-700">Processus de Réflexion</span>
169
  <span id="timestamp" class="timestamp"></span>
170
  </button>
@@ -174,7 +186,6 @@
174
  </div>
175
  <div class="border-t pt-6">
176
  <h3 class="text-xl font-bold text-gray-800 mb-4">Solution</h3>
177
- <!-- Enveloppement d'éventuels tableaux dans une div responsive -->
178
  <div id="answerContent" class="text-gray-700 table-responsive"></div>
179
  </div>
180
  </section>
@@ -183,7 +194,6 @@
183
 
184
  <script>
185
  document.addEventListener('DOMContentLoaded', () => {
186
- // Récupération des éléments DOM
187
  const form = document.getElementById('problemForm');
188
  const imageInput = document.getElementById('imageInput');
189
  const loader = document.getElementById('loader');
@@ -201,9 +211,8 @@
201
  let thoughtsBuffer = '';
202
  let answerBuffer = '';
203
  let currentMode = null;
204
- let updateTimeout = null; // Variable pour le debounce des mises à jour
205
 
206
- // Mise à jour de l'affichage du temps écoulé
207
  const updateTimestamp = () => {
208
  if (startTime) {
209
  const seconds = Math.floor((Date.now() - startTime) / 1000);
@@ -223,25 +232,23 @@
223
  timestamp.textContent = '';
224
  };
225
 
226
- // Gestion de la sélection ou du dépôt de l'image
227
  const handleFileSelect = file => {
228
  if (!file) return;
229
  const reader = new FileReader();
230
  reader.onload = e => {
231
  previewImage.src = e.target.result;
232
  imagePreview.classList.remove('hidden');
 
233
  };
234
  reader.readAsDataURL(file);
235
  };
236
 
237
- // Toggle pour afficher/cacher le processus de réflexion
238
  thoughtsToggle.addEventListener('click', () => {
239
  thoughtsBox.classList.toggle('open');
240
  });
241
 
242
  imageInput.addEventListener('change', e => handleFileSelect(e.target.files[0]));
243
 
244
- // Gestion des événements de glisser-déposer
245
  const dropZone = document.querySelector('.uploadArea');
246
  dropZone.addEventListener('dragover', e => {
247
  e.preventDefault();
@@ -257,7 +264,6 @@
257
  handleFileSelect(e.dataTransfer.files[0]);
258
  });
259
 
260
- // Fonction pour relancer le rendu MathJax dès que le contenu de la réponse est mis à jour
261
  const typesetAnswerIfReady = async () => {
262
  if (window.mathJaxReady) {
263
  MathJax.startup.document.elements = [document.getElementById('answerContent')];
@@ -268,30 +274,23 @@
268
  }
269
  };
270
 
271
- // Fonction pour déclencher la mise à jour de l'affichage
272
  const updateDisplay = async () => {
273
- // Mise à jour du contenu via marked
274
  thoughtsContent.innerHTML = marked.parse(thoughtsBuffer);
275
  answerContent.innerHTML = marked.parse(answerBuffer);
276
-
277
- // Lancer le rendu MathJax
278
  await typesetAnswerIfReady();
279
  updateTimeout = null;
280
  };
281
 
282
- // Fonction pour planifier la mise à jour (dé-bounce)
283
  const scheduleUpdate = () => {
284
  if (updateTimeout) return;
285
  updateTimeout = setTimeout(updateDisplay, 200);
286
  };
287
 
288
- // Configuration de marked avec support du mode GFM (pour les tableaux) et interprétation des sauts de ligne
289
  marked.setOptions({
290
  gfm: true,
291
  breaks: true
292
  });
293
 
294
- // Soumission du formulaire
295
  form.addEventListener('submit', async e => {
296
  e.preventDefault();
297
  const file = imageInput.files[0];
@@ -300,9 +299,9 @@
300
  return;
301
  }
302
 
303
- // Initialisation de l'affichage et des variables
304
  startTimer();
305
  loader.classList.remove('hidden');
 
306
  solutionSection.classList.add('hidden');
307
  thoughtsContent.innerHTML = '';
308
  answerContent.innerHTML = '';
@@ -315,7 +314,6 @@
315
  formData.append('image', file);
316
 
317
  try {
318
- // Envoi de la requête au serveur
319
  const response = await fetch('/solve', {
320
  method: 'POST',
321
  body: formData
@@ -325,11 +323,10 @@
325
  const decoder = new TextDecoder();
326
  let buffer = '';
327
 
328
- // Traitement d'un chunk de données
329
  const processChunk = async chunk => {
330
  buffer += decoder.decode(chunk, { stream: true });
331
  const lines = buffer.split('\n\n');
332
- buffer = lines.pop(); // Conserver la dernière ligne incomplète
333
 
334
  for (const line of lines) {
335
  if (!line.startsWith('data:')) continue;
@@ -339,6 +336,7 @@
339
  currentMode = data.mode;
340
  loader.classList.add('hidden');
341
  solutionSection.classList.remove('hidden');
 
342
  }
343
  if (data.content) {
344
  if (currentMode === 'thinking') {
@@ -348,15 +346,12 @@
348
  }
349
  }
350
  }
351
- // Planifier la mise à jour dé-bouncée
352
  scheduleUpdate();
353
  };
354
 
355
- // Lecture du flux de réponse
356
  while (true) {
357
  const { done, value } = await reader.read();
358
  if (done) {
359
- // Traitement du contenu restant dans le buffer
360
  if (buffer) {
361
  const data = JSON.parse(buffer.slice(5));
362
  if (data.content) {
 
 
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
+ <!-- Tailwind CSS -->
8
  <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/tailwind.min.css" rel="stylesheet">
9
+ <!-- Google Fonts -->
10
+ <link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;700&display=swap" rel="stylesheet">
11
+ <!-- MathJax -->
12
  <script>
13
  window.MathJax = {
14
  tex: {
15
  inlineMath: [['$', '$']],
16
  displayMath: [['$$', '$$']],
17
  processEscapes: true,
18
+ packages: {'[+]': ['autoload', 'ams']}
 
19
  },
 
20
  options: {
21
  enableMenu: false,
22
  messageStyle: 'none'
 
30
  };
31
  </script>
32
  <script src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js" id="MathJax-script" async></script>
33
+ <!-- Marked -->
34
  <script src="https://cdn.jsdelivr.net/npm/marked/lib/marked.umd.min.js"></script>
 
35
  <style>
 
 
36
  body {
37
  font-family: 'Space Grotesk', sans-serif;
38
+ animation: fadeIn 0.8s ease-in;
39
+ }
40
+ @keyframes fadeIn {
41
+ from { opacity: 0; }
42
+ to { opacity: 1; }
43
  }
 
44
  .uploadArea {
45
  background: #f3f4f6;
46
  border: 2px dashed #d1d5db;
47
+ transition: border-color 0.3s ease, transform 0.2s ease;
48
  }
49
  .uploadArea:hover {
50
  border-color: #3b82f6;
51
+ transform: scale(1.02);
52
  }
 
53
  .blue-button {
54
  background: #3b82f6;
55
+ transition: background-color 0.3s ease, transform 0.2s ease;
56
  }
57
  .blue-button:hover {
58
  background: #2563eb;
59
+ transform: scale(1.05);
60
  }
 
61
  .loader {
62
  width: 48px;
63
  height: 48px;
 
71
  0% { transform: rotate(0deg); }
72
  100% { transform: rotate(360deg); }
73
  }
 
74
  .thought-box {
75
+ transition: max-height 0.4s ease-out, opacity 0.3s ease;
76
  max-height: 0;
77
+ opacity: 0;
78
  overflow: hidden;
79
  }
80
  .thought-box.open {
81
  max-height: 500px;
82
+ opacity: 1;
83
  }
 
84
  #thoughtsContent, #answerContent {
85
  max-height: 500px;
86
  overflow-y: auto;
87
  scroll-behavior: smooth;
88
  white-space: pre-wrap;
89
  }
 
90
  .preview-image {
91
  max-width: 300px;
92
  max-height: 300px;
93
  object-fit: contain;
94
+ transition: transform 0.3s ease;
95
+ }
96
+ .preview-image:hover {
97
+ transform: scale(1.05);
98
  }
 
99
  .timestamp {
100
  color: #3b82f6;
101
  font-size: 0.9em;
102
  margin-left: 8px;
103
  }
 
 
104
  table {
105
  border-collapse: collapse;
106
  width: 100%;
 
115
  background-color: #f3f4f6;
116
  font-weight: 600;
117
  }
 
118
  .table-responsive {
119
  overflow-x: auto;
120
  }
121
+ /* Ajout d'effets visuels subtils */
122
+ header, main {
123
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
124
+ border-radius: 8px;
125
+ }
126
+ h1 {
127
+ font-size: 2.5rem;
128
+ font-weight: 700;
129
+ }
130
+ /* Icônes */
131
+ .icon {
132
+ margin-right: 0.5rem;
133
+ }
134
  </style>
135
  </head>
136
  <body class="p-4">
137
  <div class="max-w-4xl mx-auto">
138
+ <header class="p-6 text-center mb-8 bg-white">
139
  <h1 class="text-4xl font-bold text-blue-600">Mariam M-0</h1>
140
  <p class="text-gray-600">Solution Mathématique/Physique/Chimie Intelligente</p>
141
  </header>
142
 
143
+ <main class="bg-white p-6">
144
  <form id="problemForm" class="space-y-6" novalidate>
145
  <!-- Zone de dépôt et sélection d'image -->
146
  <div class="uploadArea p-8 text-center relative" aria-label="Zone de dépôt d'image">
 
176
  <!-- Affichage de la solution -->
177
  <section id="solution" class="hidden mt-8 space-y-6">
178
  <div class="border-t pt-4">
179
+ <button id="thoughtsToggle" type="button" class="w-full flex justify-between items-center p-2 hover:bg-gray-100 rounded">
180
  <span class="font-medium text-gray-700">Processus de Réflexion</span>
181
  <span id="timestamp" class="timestamp"></span>
182
  </button>
 
186
  </div>
187
  <div class="border-t pt-6">
188
  <h3 class="text-xl font-bold text-gray-800 mb-4">Solution</h3>
 
189
  <div id="answerContent" class="text-gray-700 table-responsive"></div>
190
  </div>
191
  </section>
 
194
 
195
  <script>
196
  document.addEventListener('DOMContentLoaded', () => {
 
197
  const form = document.getElementById('problemForm');
198
  const imageInput = document.getElementById('imageInput');
199
  const loader = document.getElementById('loader');
 
211
  let thoughtsBuffer = '';
212
  let answerBuffer = '';
213
  let currentMode = null;
214
+ let updateTimeout = null;
215
 
 
216
  const updateTimestamp = () => {
217
  if (startTime) {
218
  const seconds = Math.floor((Date.now() - startTime) / 1000);
 
232
  timestamp.textContent = '';
233
  };
234
 
 
235
  const handleFileSelect = file => {
236
  if (!file) return;
237
  const reader = new FileReader();
238
  reader.onload = e => {
239
  previewImage.src = e.target.result;
240
  imagePreview.classList.remove('hidden');
241
+ imagePreview.classList.add('animate-fadeIn');
242
  };
243
  reader.readAsDataURL(file);
244
  };
245
 
 
246
  thoughtsToggle.addEventListener('click', () => {
247
  thoughtsBox.classList.toggle('open');
248
  });
249
 
250
  imageInput.addEventListener('change', e => handleFileSelect(e.target.files[0]));
251
 
 
252
  const dropZone = document.querySelector('.uploadArea');
253
  dropZone.addEventListener('dragover', e => {
254
  e.preventDefault();
 
264
  handleFileSelect(e.dataTransfer.files[0]);
265
  });
266
 
 
267
  const typesetAnswerIfReady = async () => {
268
  if (window.mathJaxReady) {
269
  MathJax.startup.document.elements = [document.getElementById('answerContent')];
 
274
  }
275
  };
276
 
 
277
  const updateDisplay = async () => {
 
278
  thoughtsContent.innerHTML = marked.parse(thoughtsBuffer);
279
  answerContent.innerHTML = marked.parse(answerBuffer);
 
 
280
  await typesetAnswerIfReady();
281
  updateTimeout = null;
282
  };
283
 
 
284
  const scheduleUpdate = () => {
285
  if (updateTimeout) return;
286
  updateTimeout = setTimeout(updateDisplay, 200);
287
  };
288
 
 
289
  marked.setOptions({
290
  gfm: true,
291
  breaks: true
292
  });
293
 
 
294
  form.addEventListener('submit', async e => {
295
  e.preventDefault();
296
  const file = imageInput.files[0];
 
299
  return;
300
  }
301
 
 
302
  startTimer();
303
  loader.classList.remove('hidden');
304
+ loader.classList.add('animate-fadeIn');
305
  solutionSection.classList.add('hidden');
306
  thoughtsContent.innerHTML = '';
307
  answerContent.innerHTML = '';
 
314
  formData.append('image', file);
315
 
316
  try {
 
317
  const response = await fetch('/solve', {
318
  method: 'POST',
319
  body: formData
 
323
  const decoder = new TextDecoder();
324
  let buffer = '';
325
 
 
326
  const processChunk = async chunk => {
327
  buffer += decoder.decode(chunk, { stream: true });
328
  const lines = buffer.split('\n\n');
329
+ buffer = lines.pop();
330
 
331
  for (const line of lines) {
332
  if (!line.startsWith('data:')) continue;
 
336
  currentMode = data.mode;
337
  loader.classList.add('hidden');
338
  solutionSection.classList.remove('hidden');
339
+ solutionSection.classList.add('animate-fadeIn');
340
  }
341
  if (data.content) {
342
  if (currentMode === 'thinking') {
 
346
  }
347
  }
348
  }
 
349
  scheduleUpdate();
350
  };
351
 
 
352
  while (true) {
353
  const { done, value } = await reader.read();
354
  if (done) {
 
355
  if (buffer) {
356
  const data = JSON.parse(buffer.slice(5));
357
  if (data.content) {