Spaces:
Running
Running
Update templates/index.html
Browse files- templates/index.html +341 -606
templates/index.html
CHANGED
@@ -3,645 +3,380 @@
|
|
3 |
<head>
|
4 |
<meta charset="UTF-8">
|
5 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
6 |
-
<title>Mariam
|
7 |
-
<script src="https://cdn.tailwindcss.com"></script>
|
8 |
-
<script src="https://cdnjs.cloudflare.com/ajax/libs/alpinejs/3.12.0/cdn.min.js" defer></script>
|
9 |
-
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
10 |
<style>
|
11 |
-
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap');
|
12 |
-
|
13 |
body {
|
14 |
-
font-family:
|
15 |
-
|
|
|
|
|
|
|
16 |
}
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
|
|
|
|
|
|
21 |
}
|
22 |
-
|
23 |
-
|
24 |
-
border-color: #4338ca;
|
25 |
-
background-color: #eef2ff;
|
26 |
}
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
word-wrap: break-word;
|
31 |
-
max-height: 500px; /* Hauteur max pour les blocs de code/log */
|
32 |
-
overflow-y: auto;
|
33 |
-
background-color: #f9fafb; /* Fond légèrement différent pour pre */
|
34 |
-
border: 1px solid #e5e7eb;
|
35 |
-
border-radius: 0.375rem; /* rounded-md */
|
36 |
}
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
44 |
}
|
45 |
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
52 |
}
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
|
|
|
|
57 |
}
|
58 |
</style>
|
59 |
</head>
|
60 |
-
<body
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
Mariam Science PDF
|
69 |
-
</h1>
|
70 |
-
<p class="mt-2 text-indigo-200">Générateur de solutions mathématiques avec export PDF</p>
|
71 |
-
</div>
|
72 |
-
<div class="hidden md:block">
|
73 |
-
<a href="https://mariam-241.vercel.app" class="bg-white text-indigo-700 px-4 py-2 rounded-md font-medium hover:bg-indigo-50 transition duration-300">
|
74 |
-
Site Principal
|
75 |
-
</a>
|
76 |
-
</div>
|
77 |
-
</div>
|
78 |
</div>
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
:class="{
|
86 |
-
'bg-red-100 border-red-500 text-red-800': systemStatus.type === 'error',
|
87 |
-
'bg-green-100 border-green-500 text-green-800': systemStatus.type === 'success',
|
88 |
-
'bg-yellow-100 border-yellow-500 text-yellow-800': systemStatus.type === 'warning',
|
89 |
-
'bg-blue-100 border-blue-500 text-blue-800': systemStatus.type === 'info'
|
90 |
-
}"
|
91 |
-
class="mb-6 p-4 border-l-4 rounded-md flex items-start shadow">
|
92 |
-
<div class="flex-shrink-0 mr-3 mt-1">
|
93 |
-
<i :class="{
|
94 |
-
'fas fa-exclamation-circle text-red-500': systemStatus.type === 'error',
|
95 |
-
'fas fa-check-circle text-green-500': systemStatus.type === 'success',
|
96 |
-
'fas fa-exclamation-triangle text-yellow-500': systemStatus.type === 'warning',
|
97 |
-
'fas fa-info-circle text-blue-500': systemStatus.type === 'info'
|
98 |
-
}" class="text-xl"></i>
|
99 |
-
</div>
|
100 |
-
<div>
|
101 |
-
<p class="font-medium" x-text="systemStatus.title"></p>
|
102 |
-
<!-- Utiliser v-html pour permettre des sauts de ligne simples si besoin -->
|
103 |
-
<p class="text-sm mt-1" x-html="systemStatus.message.replace(/\n/g, '<br>')"></p>
|
104 |
-
</div>
|
105 |
-
<button @click="systemStatus.show = false" class="ml-auto pl-3 flex-shrink-0">
|
106 |
-
<i class="fas fa-times text-gray-500 hover:text-gray-700"></i>
|
107 |
-
</button>
|
108 |
</div>
|
109 |
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
<div class="bg-white rounded-lg shadow-md p-6 h-full flex flex-col">
|
115 |
-
<div class="flex items-center mb-4">
|
116 |
-
<div class="bg-indigo-100 text-indigo-700 rounded-full w-8 h-8 flex items-center justify-center mr-3 flex-shrink-0">
|
117 |
-
<i class="fas fa-upload"></i>
|
118 |
-
</div>
|
119 |
-
<h2 class="text-xl font-semibold">1. Télécharger une image</h2>
|
120 |
-
</div>
|
121 |
-
<p class="text-gray-600 mb-4">Importez une photo d'un exercice de mathématiques.</p>
|
122 |
-
|
123 |
-
<!-- Upload Area -->
|
124 |
-
<div class="mt-4 flex-grow">
|
125 |
-
<div
|
126 |
-
@dragover.prevent="dragOver = true"
|
127 |
-
@dragleave.prevent="dragOver = false"
|
128 |
-
@drop.prevent="handleDrop($event)"
|
129 |
-
:class="{'active': dragOver}"
|
130 |
-
class="drop-zone h-52 rounded-lg flex flex-col items-center justify-center text-center p-4 cursor-pointer"
|
131 |
-
@click="document.getElementById('fileInput').click()">
|
132 |
-
|
133 |
-
<template x-if="!imagePreview">
|
134 |
-
<div>
|
135 |
-
<i class="fas fa-cloud-upload-alt text-4xl text-indigo-500 mb-3"></i>
|
136 |
-
<p class="text-gray-700">Glissez votre image ici ou</p>
|
137 |
-
<p class="text-indigo-600 font-medium">Cliquez pour parcourir</p>
|
138 |
-
<p class="text-gray-500 text-sm mt-2">(PNG, JPG, JPEG)</p>
|
139 |
-
</div>
|
140 |
-
</template>
|
141 |
-
|
142 |
-
<template x-if="imagePreview">
|
143 |
-
<img :src="imagePreview" alt="Aperçu" class="max-h-full object-contain rounded"/>
|
144 |
-
</template>
|
145 |
-
</div>
|
146 |
-
|
147 |
-
<input type="file" id="fileInput" class="hidden" accept="image/png, image/jpeg, image/jpg" @change="handleFileSelect($event)">
|
148 |
-
</div>
|
149 |
-
<button x-show="imagePreview" @click="clearUpload()" class="mt-4 w-full text-sm text-red-600 hover:text-red-800 transition duration-300">
|
150 |
-
<i class="fas fa-times mr-1"></i> Changer l'image
|
151 |
-
</button>
|
152 |
-
</div>
|
153 |
-
</div>
|
154 |
|
155 |
-
|
156 |
-
<div class="w-full lg:w-1/3 px-2 mb-4">
|
157 |
-
<div class="bg-white rounded-lg shadow-md p-6 h-full flex flex-col">
|
158 |
-
<div class="flex items-center mb-4">
|
159 |
-
<div class="bg-indigo-100 text-indigo-700 rounded-full w-8 h-8 flex items-center justify-center mr-3 flex-shrink-0">
|
160 |
-
<i class="fas fa-robot"></i>
|
161 |
-
</div>
|
162 |
-
<h2 class="text-xl font-semibold">2. Analyse IA</h2>
|
163 |
-
</div>
|
164 |
-
<p class="text-gray-600 mb-4">L'IA analysera l'exercice et génèrera une solution détaillée en LaTeX.</p>
|
165 |
-
|
166 |
-
<div class="mt-4 flex-grow flex flex-col justify-center">
|
167 |
-
<button @click="processImage()"
|
168 |
-
:disabled="!imageFile || processing || latexCheckFailed"
|
169 |
-
:class="{
|
170 |
-
'bg-indigo-300 cursor-not-allowed': !imageFile || processing || latexCheckFailed,
|
171 |
-
'bg-indigo-600 hover:bg-indigo-700': imageFile && !processing && !latexCheckFailed
|
172 |
-
}"
|
173 |
-
class="w-full py-3 rounded-md text-white font-medium flex items-center justify-center transition duration-300">
|
174 |
-
<template x-if="processing">
|
175 |
-
<i class="fas fa-spinner fa-spin mr-2"></i>
|
176 |
-
</template>
|
177 |
-
<span x-text="processing ? 'Traitement en cours...' : 'Analyser et Résoudre'"></span>
|
178 |
-
</button>
|
179 |
-
<p x-show="latexCheckFailed" class="text-xs text-red-600 text-center mt-2">
|
180 |
-
Impossible de continuer. Veuillez vérifier le message d'erreur système ci-dessus concernant l'installation LaTeX.
|
181 |
-
</p>
|
182 |
-
|
183 |
-
<div x-show="processingStage" class="mt-4">
|
184 |
-
<div class="bg-indigo-50 rounded-md p-3">
|
185 |
-
<div class="flex items-center">
|
186 |
-
<i class="fas fa-sync fa-spin text-indigo-600 mr-2"></i>
|
187 |
-
<span class="text-indigo-800 font-medium text-sm" x-text="processingStage"></span>
|
188 |
-
</div>
|
189 |
-
<div class="w-full bg-indigo-200 rounded-full h-1.5 mt-2 overflow-hidden">
|
190 |
-
<div class="bg-indigo-600 h-1.5 rounded-full transition-all duration-300 ease-linear" :style="`width: ${processingProgress}%`"></div>
|
191 |
-
</div>
|
192 |
-
</div>
|
193 |
-
</div>
|
194 |
-
</div>
|
195 |
-
</div>
|
196 |
-
</div>
|
197 |
|
198 |
-
|
199 |
-
<
|
200 |
-
|
201 |
-
|
202 |
-
|
203 |
-
|
204 |
-
</div>
|
205 |
-
<h2 class="text-xl font-semibold">3. Résultat PDF</h2>
|
206 |
-
</div>
|
207 |
-
<p class="text-gray-600 mb-4">La solution sera compilée en un PDF professionnel prêt à télécharger.</p>
|
208 |
-
|
209 |
-
<div class="mt-4 flex-grow flex flex-col justify-center">
|
210 |
-
<button @click="downloadPDF()"
|
211 |
-
:disabled="!pdfData"
|
212 |
-
:class="{'bg-gray-300 cursor-not-allowed': !pdfData, 'bg-green-600 hover:bg-green-700': pdfData}"
|
213 |
-
class="w-full py-3 rounded-md text-white font-medium flex items-center justify-center transition duration-300">
|
214 |
-
<i class="fas fa-download mr-2"></i>
|
215 |
-
<span>Télécharger le PDF</span>
|
216 |
-
</button>
|
217 |
-
|
218 |
-
<p x-show="!pdfData && !processing && showResults" class="text-sm text-red-600 text-center mt-3">
|
219 |
-
Échec de la génération du PDF. Vérifiez les logs de compilation.
|
220 |
-
</p>
|
221 |
-
<p x-show="!pdfData && !processing && !showResults" class="text-sm text-gray-500 text-center mt-3">
|
222 |
-
Le PDF sera disponible après une analyse réussie.
|
223 |
-
</p>
|
224 |
-
<p x-show="!pdfData && processing" class="text-sm text-gray-500 text-center mt-3">
|
225 |
-
Génération du PDF en cours...
|
226 |
-
</p>
|
227 |
-
</div>
|
228 |
-
</div>
|
229 |
</div>
|
230 |
-
</div>
|
231 |
|
232 |
-
|
233 |
-
<div x-show="showResults" class="mt-8">
|
234 |
-
<div class="bg-white rounded-lg shadow-md overflow-hidden">
|
235 |
-
<div class="bg-gray-100 border-b border-gray-200 px-6 py-4">
|
236 |
-
<h2 class="text-xl font-semibold text-gray-800">Résultats de l'Analyse</h2>
|
237 |
-
</div>
|
238 |
|
239 |
-
|
240 |
-
|
241 |
-
<
|
242 |
-
|
243 |
-
|
244 |
-
|
245 |
-
|
246 |
-
|
247 |
-
<i class="fas fa-file-pdf mr-2"></i>Aperçu PDF
|
248 |
-
<span x-show="!pdfData" class="ml-2 text-xs bg-red-500 text-white rounded-full px-1.5 py-0.5">Erreur</span>
|
249 |
-
<span x-show="pdfData" class="ml-2 text-xs bg-green-500 text-white rounded-full px-1.5 py-0.5">OK</span>
|
250 |
-
</button>
|
251 |
-
</div>
|
252 |
-
<div class="px-1 mb-1">
|
253 |
-
<button @click="activeTab = 'latex'"
|
254 |
-
:class="{'bg-indigo-600 text-white shadow-sm': activeTab === 'latex', 'bg-white text-indigo-700 hover:bg-indigo-100': activeTab !== 'latex'}"
|
255 |
-
class="px-4 py-2 rounded-md font-medium text-sm transition duration-200 flex items-center">
|
256 |
-
<i class="fas fa-code mr-2"></i>Code LaTeX
|
257 |
-
</button>
|
258 |
-
</div>
|
259 |
-
<!-- NOUVEL ONGLET POUR LOGS DE COMPILATION -->
|
260 |
-
<div class="px-1 mb-1" x-show="compilationLog">
|
261 |
-
<button @click="activeTab = 'log'"
|
262 |
-
:class="{'bg-indigo-600 text-white shadow-sm': activeTab === 'log', 'bg-white text-indigo-700 hover:bg-indigo-100': activeTab !== 'log'}"
|
263 |
-
class="px-4 py-2 rounded-md font-medium text-sm transition duration-200 flex items-center">
|
264 |
-
<i class="fas fa-terminal mr-2"></i>Log Compilation
|
265 |
-
<span x-show="!pdfData" class="ml-2 text-xs bg-red-500 text-white rounded-full px-1.5 py-0.5"><i class="fas fa-exclamation-triangle"></i></span>
|
266 |
-
<span x-show="pdfData" class="ml-2 text-xs bg-green-500 text-white rounded-full px-1.5 py-0.5"><i class="fas fa-check"></i></span>
|
267 |
-
</button>
|
268 |
-
</div>
|
269 |
-
<div class="px-1 mb-1" x-show="thinkingProcess">
|
270 |
-
<button @click="activeTab = 'thinking'"
|
271 |
-
:class="{'bg-indigo-600 text-white shadow-sm': activeTab === 'thinking', 'bg-white text-indigo-700 hover:bg-indigo-100': activeTab !== 'thinking'}"
|
272 |
-
class="px-4 py-2 rounded-md font-medium text-sm transition duration-200 flex items-center">
|
273 |
-
<i class="fas fa-brain mr-2"></i>Processus IA
|
274 |
-
</button>
|
275 |
-
</div>
|
276 |
-
</div>
|
277 |
-
</div>
|
278 |
-
|
279 |
-
<!-- Content Panels -->
|
280 |
-
<div class="w-full p-6">
|
281 |
-
<!-- PDF Preview -->
|
282 |
-
<div x-show="activeTab === 'pdf'">
|
283 |
-
<div class="pdf-container bg-gray-100 flex items-center justify-center">
|
284 |
-
<iframe x-show="pdfData" :src="'data:application/pdf;base64,' + pdfData" width="100%" height="100%" class="border-0">
|
285 |
-
Votre navigateur ne supporte pas les iframes pour PDF. Vous pouvez le <a @click.prevent="downloadPDF()" href="#" class="text-indigo-600 hover:underline">télécharger</a>.
|
286 |
-
</iframe>
|
287 |
-
<div x-show="!pdfData" class="text-center text-gray-500 p-8">
|
288 |
-
<i class="fas fa-file-excel text-6xl mb-4 text-red-400"></i>
|
289 |
-
<h3 class="text-xl font-semibold mb-2 text-red-600">Échec de la génération du PDF</h3>
|
290 |
-
<p class="mb-4">Impossible d'afficher l'aperçu car le fichier PDF n'a pas pu être créé.</p>
|
291 |
-
<p>Veuillez consulter l'onglet <strong class="text-gray-700">"Log Compilation"</strong> pour identifier la cause de l'erreur.</p>
|
292 |
-
<button @click="activeTab = 'log'" class="mt-4 px-4 py-2 bg-indigo-600 text-white rounded-md hover:bg-indigo-700 transition duration-300 text-sm">
|
293 |
-
Voir le Log de Compilation
|
294 |
-
</button>
|
295 |
-
</div>
|
296 |
-
</div>
|
297 |
-
</div>
|
298 |
-
|
299 |
-
<!-- LaTeX Code -->
|
300 |
-
<div x-show="activeTab === 'latex'">
|
301 |
-
<div class="bg-gray-100 px-4 py-2 border-b border-gray-200 flex justify-between items-center rounded-t-md">
|
302 |
-
<span class="font-medium text-gray-700">Code LaTeX généré</span>
|
303 |
-
<button @click="copyToClipboard(latexCode)" title="Copier le code LaTeX" class="text-indigo-600 hover:text-indigo-800 transition duration-300 text-sm px-3 py-1 bg-white rounded border border-indigo-200 hover:bg-indigo-50">
|
304 |
-
<i class="fas fa-copy mr-1"></i>Copier
|
305 |
-
</button>
|
306 |
-
</div>
|
307 |
-
<pre class="p-4 text-sm border-t-0 rounded-b-md rounded-t-none"><code x-text="latexCode || 'Aucun code LaTeX disponible'"></code></pre>
|
308 |
-
</div>
|
309 |
-
|
310 |
-
<!-- NOUVEAU PANNEAU : Log de Compilation -->
|
311 |
-
<div x-show="activeTab === 'log'">
|
312 |
-
<div :class="!pdfData ? 'compilation-log-error' : 'bg-gray-50 rounded-md p-4 border border-gray-200'">
|
313 |
-
<div class="flex justify-between items-center mb-3">
|
314 |
-
<h3 class="font-medium" :class="!pdfData ? 'text-red-800' : 'text-gray-700'">
|
315 |
-
<i class="fas fa-terminal mr-2"></i>Log de Compilation pdflatex
|
316 |
-
</h3>
|
317 |
-
<button @click="copyToClipboard(compilationLog)" title="Copier le log" class="text-indigo-600 hover:text-indigo-800 transition duration-300 text-sm px-3 py-1 bg-white rounded border border-indigo-200 hover:bg-indigo-50">
|
318 |
-
<i class="fas fa-copy mr-1"></i>Copier le Log
|
319 |
-
</button>
|
320 |
-
</div>
|
321 |
-
<pre class="p-3 text-xs"><code x-text="compilationLog || 'Aucun log de compilation disponible'"></code></pre>
|
322 |
-
<p x-show="!pdfData" class="mt-3 text-sm text-red-700">
|
323 |
-
<i class="fas fa-exclamation-circle mr-1"></i> Le PDF n'a pas pu être généré. Recherchez les lignes commençant par `!` (erreur) ou `Warning:` dans le log ci-dessus pour comprendre le problème. Les erreurs courantes incluent des packages manquants, des commandes inconnues ou des erreurs de syntaxe LaTeX.
|
324 |
-
</p>
|
325 |
-
<p x-show="pdfData" class="mt-3 text-sm text-green-700">
|
326 |
-
<i class="fas fa-check-circle mr-1"></i> Le PDF a été généré. Le log peut contenir des avertissements (`Warning:`) qui n'ont pas empêché la compilation mais pourraient indiquer des problèmes mineurs.
|
327 |
-
</p>
|
328 |
-
</div>
|
329 |
-
</div>
|
330 |
-
|
331 |
-
<!-- Thinking Process -->
|
332 |
-
<div x-show="activeTab === 'thinking'">
|
333 |
-
<div class="bg-gray-100 px-4 py-2 border-b border-gray-200 rounded-t-md">
|
334 |
-
<span class="font-medium text-gray-700">Processus de réflexion de l'IA</span>
|
335 |
-
</div>
|
336 |
-
<pre class="p-4 text-sm border-t-0 rounded-b-md rounded-t-none"><code x-text="thinkingProcess || 'Aucune information sur le processus de réflexion disponible'"></code></pre>
|
337 |
-
</div>
|
338 |
-
</div>
|
339 |
</div>
|
340 |
</div>
|
341 |
</div>
|
342 |
-
|
343 |
-
<!-- Footer -->
|
344 |
-
<footer class="mt-12 text-center text-gray-600 text-sm">
|
345 |
-
<p>Mariam AI © 2025 - Solution d'automatisation pour les sciences</p>
|
346 |
-
<p class="mt-1">Version Alpha - Nécessite une installation LaTeX (pdflatex) fonctionnelle sur le serveur.</p>
|
347 |
-
<p class="mt-1" x-show="latexCheckMessage" x-text="latexCheckMessage"></p>
|
348 |
-
</footer>
|
349 |
-
</main>
|
350 |
-
|
351 |
-
<!-- Feedback Flottant -->
|
352 |
-
<div x-show="feedback.show"
|
353 |
-
class="fixed bottom-4 right-4 z-50 max-w-sm p-4 rounded-md shadow-lg text-white"
|
354 |
-
:class="{ 'bg-green-600': feedback.type === 'success', 'bg-red-600': feedback.type === 'error' }"
|
355 |
-
x-transition:enter="transition ease-out duration-300"
|
356 |
-
x-transition:enter-start="opacity-0 transform translate-y-2"
|
357 |
-
x-transition:enter-end="opacity-100 transform translate-y-0"
|
358 |
-
x-transition:leave="transition ease-in duration-200"
|
359 |
-
x-transition:leave-start="opacity-100 transform translate-y-0"
|
360 |
-
x-transition:leave-end="opacity-0 transform translate-y-2">
|
361 |
-
<div class="flex items-center">
|
362 |
-
<i :class="{ 'fas fa-check-circle': feedback.type === 'success', 'fas fa-exclamation-circle': feedback.type === 'error' }" class="mr-2 text-xl"></i>
|
363 |
-
<span x-text="feedback.message"></span>
|
364 |
-
<button @click="feedback.show = false" class="ml-auto -mr-1 -mt-1 p-1">
|
365 |
-
<i class="fas fa-times text-white opacity-70 hover:opacity-100"></i>
|
366 |
-
</button>
|
367 |
-
</div>
|
368 |
</div>
|
369 |
|
370 |
-
|
371 |
<script>
|
372 |
-
|
373 |
-
|
374 |
-
|
375 |
-
|
376 |
-
|
377 |
-
|
378 |
-
|
379 |
-
|
380 |
-
|
381 |
-
|
382 |
-
|
383 |
-
|
384 |
-
|
385 |
-
|
386 |
-
|
387 |
-
|
388 |
-
|
389 |
-
|
390 |
-
|
391 |
-
|
392 |
-
|
393 |
-
|
394 |
-
|
395 |
-
|
396 |
-
|
397 |
-
|
398 |
-
|
399 |
-
|
400 |
-
|
401 |
-
|
402 |
-
|
403 |
-
|
404 |
-
|
405 |
-
|
406 |
-
|
407 |
-
|
408 |
-
|
409 |
-
|
410 |
-
|
411 |
-
|
412 |
-
|
413 |
-
|
414 |
-
|
415 |
-
|
416 |
-
|
417 |
-
|
418 |
-
|
419 |
-
|
420 |
-
|
421 |
-
|
422 |
-
|
423 |
-
|
424 |
-
|
425 |
-
|
426 |
-
|
427 |
-
},
|
428 |
-
|
429 |
-
showStatus(type, title, message, duration = null) {
|
430 |
-
this.systemStatus = { show: true, type, title, message };
|
431 |
-
if (duration) {
|
432 |
-
setTimeout(() => {
|
433 |
-
// Ne masquer que si le message actuel est toujours celui qu'on voulait masquer
|
434 |
-
if (this.systemStatus.message === message) {
|
435 |
-
this.systemStatus.show = false;
|
436 |
-
}
|
437 |
-
}, duration);
|
438 |
-
}
|
439 |
-
},
|
440 |
-
|
441 |
-
showFeedback(type, message, duration = 4000) {
|
442 |
-
this.feedback = { show: true, type, message };
|
443 |
-
setTimeout(() => { this.feedback.show = false; }, duration);
|
444 |
-
},
|
445 |
-
|
446 |
-
handleDrop(event) {
|
447 |
-
this.dragOver = false;
|
448 |
-
const files = event.dataTransfer.files;
|
449 |
-
if (files.length > 0) {
|
450 |
-
this.processFile(files[0]);
|
451 |
-
}
|
452 |
-
},
|
453 |
|
454 |
-
|
455 |
-
|
456 |
-
|
457 |
-
|
458 |
-
|
459 |
-
|
460 |
-
|
461 |
-
|
462 |
-
|
463 |
-
|
464 |
-
|
465 |
-
|
466 |
-
|
467 |
-
|
468 |
-
|
469 |
-
|
470 |
-
|
471 |
-
|
472 |
-
|
473 |
-
|
474 |
-
|
475 |
-
|
476 |
-
|
477 |
-
|
478 |
-
|
479 |
-
|
480 |
-
|
481 |
-
|
482 |
-
|
483 |
-
|
484 |
-
|
485 |
-
|
486 |
-
|
487 |
-
|
488 |
-
|
489 |
-
|
490 |
-
|
491 |
-
|
492 |
-
|
493 |
-
|
494 |
-
|
495 |
-
|
496 |
-
|
497 |
-
|
498 |
-
|
499 |
-
|
500 |
-
|
501 |
-
|
502 |
-
|
503 |
-
|
504 |
-
|
505 |
-
|
506 |
-
|
507 |
-
this.activeTab = 'pdf'; // Toujours revenir à l'onglet PDF par défaut
|
508 |
-
},
|
509 |
-
|
510 |
-
processImage() {
|
511 |
-
if (!this.imageFile || this.processing || this.latexCheckFailed) return;
|
512 |
-
|
513 |
-
this.processing = true;
|
514 |
-
this.resetResults(); // Effacer les anciens résultats avant de commencer
|
515 |
-
this.systemStatus.show = false; // Cacher les alertes
|
516 |
-
this.updateProcessingStatus("Téléversement de l'image...", 5);
|
517 |
-
|
518 |
-
const formData = new FormData();
|
519 |
-
formData.append('image', this.imageFile);
|
520 |
-
|
521 |
-
setTimeout(() => this.updateProcessingStatus("Analyse par l'IA et génération LaTeX...", 20), 500);
|
522 |
-
|
523 |
-
fetch('/process-image', {
|
524 |
method: 'POST',
|
525 |
body: formData
|
526 |
-
})
|
527 |
-
.then(response => {
|
528 |
-
// Gérer les erreurs HTTP (e.g., 500 Internal Server Error)
|
529 |
-
if (!response.ok) {
|
530 |
-
// Essayer de lire le message d'erreur JSON s'il y en a un
|
531 |
-
return response.json().then(errData => {
|
532 |
-
throw new Error(errData.message || `Erreur serveur: ${response.status} ${response.statusText}`);
|
533 |
-
}).catch(() => {
|
534 |
-
// Si pas de JSON, lancer une erreur générique
|
535 |
-
throw new Error(`Erreur serveur: ${response.status} ${response.statusText}`);
|
536 |
-
});
|
537 |
-
}
|
538 |
-
return response.json(); // Traiter la réponse JSON si statut OK
|
539 |
-
})
|
540 |
-
.then(data => {
|
541 |
-
// Simulation de la compilation
|
542 |
-
let progress = 60;
|
543 |
-
this.updateProcessingStatus("Compilation LaTeX vers PDF...", progress);
|
544 |
-
const interval = setInterval(() => {
|
545 |
-
progress += 5;
|
546 |
-
if (progress <= 95) {
|
547 |
-
this.updateProcessingStatus("Compilation LaTeX vers PDF...", progress);
|
548 |
-
} else {
|
549 |
-
clearInterval(interval);
|
550 |
-
}
|
551 |
-
}, 300); // Augmenter toutes les 300ms
|
552 |
-
|
553 |
-
// Traitement de la réponse (après la simulation)
|
554 |
-
setTimeout(() => {
|
555 |
-
clearInterval(interval); // S'assurer que l'intervalle est arrêté
|
556 |
-
this.processing = false;
|
557 |
-
this.processingStage = null;
|
558 |
-
this.processingProgress = 0;
|
559 |
-
this.showResults = true; // Afficher la section des résultats
|
560 |
-
|
561 |
-
// Mettre à jour les données même en cas d'échec partiel
|
562 |
-
this.latexCode = data.latex || null;
|
563 |
-
this.thinkingProcess = data.thinking || null;
|
564 |
-
this.compilationLog = data.compilation_log || null; // Très important
|
565 |
-
this.pdfData = data.pdf_base64 || null; // Sera null si échec
|
566 |
-
|
567 |
-
if (data.success) {
|
568 |
-
this.showStatus('success', 'Analyse Réussie', data.message);
|
569 |
-
this.activeTab = 'pdf'; // Afficher le PDF si succès
|
570 |
-
} else {
|
571 |
-
// Message d'erreur principal
|
572 |
-
this.showStatus('error', 'Échec du Traitement', data.message);
|
573 |
-
|
574 |
-
// Si échec de compilation, aller directement au log
|
575 |
-
if (this.compilationLog && !this.pdfData) {
|
576 |
-
this.activeTab = 'log';
|
577 |
-
} else if (this.latexCode) {
|
578 |
-
this.activeTab = 'latex'; // Sinon, au LaTeX si disponible
|
579 |
-
} else {
|
580 |
-
this.activeTab = 'pdf'; // Fallback
|
581 |
-
}
|
582 |
-
}
|
583 |
-
}, 2000); // Laisser le temps à la "compilation" de s'afficher
|
584 |
-
|
585 |
-
})
|
586 |
-
.catch(error => {
|
587 |
-
console.error('Erreur lors du traitement de l\'image:', error);
|
588 |
-
this.processing = false;
|
589 |
-
this.processingStage = null;
|
590 |
-
this.processingProgress = 0;
|
591 |
-
// Afficher l'erreur venant du serveur ou une erreur générique
|
592 |
-
this.showStatus('error', 'Erreur Critique', error.message || 'Une erreur inattendue est survenue lors de la communication avec le serveur.');
|
593 |
-
this.resetResults(); // Pas de résultats partiels en cas d'erreur critique
|
594 |
});
|
595 |
-
|
596 |
-
|
597 |
-
|
598 |
-
|
599 |
-
|
600 |
-
|
601 |
-
|
602 |
-
|
603 |
-
|
604 |
-
|
605 |
-
|
606 |
-
|
607 |
-
|
608 |
-
try {
|
609 |
-
const byteCharacters = atob(this.pdfData);
|
610 |
-
const byteNumbers = new Array(byteCharacters.length);
|
611 |
-
for (let i = 0; i < byteCharacters.length; i++) {
|
612 |
-
byteNumbers[i] = byteCharacters.charCodeAt(i);
|
613 |
}
|
614 |
-
const byteArray = new Uint8Array(byteNumbers);
|
615 |
-
const blob = new Blob([byteArray], {type: 'application/pdf'});
|
616 |
-
|
617 |
-
const link = document.createElement('a');
|
618 |
-
link.href = URL.createObjectURL(blob);
|
619 |
-
link.download = 'solution_mariam_ai.pdf';
|
620 |
-
document.body.appendChild(link);
|
621 |
-
link.click();
|
622 |
-
document.body.removeChild(link);
|
623 |
-
URL.revokeObjectURL(link.href); // Libérer la mémoire
|
624 |
-
this.showFeedback('success', 'Téléchargement lancé.');
|
625 |
-
} catch (e) {
|
626 |
-
console.error("Erreur lors de la création du lien de téléchargement PDF:", e);
|
627 |
-
this.showFeedback('error', 'Erreur lors de la préparation du téléchargement.');
|
628 |
}
|
629 |
-
|
630 |
-
|
631 |
-
|
632 |
-
|
633 |
-
|
634 |
-
navigator.clipboard.writeText(text)
|
635 |
-
.then(() => {
|
636 |
-
this.showFeedback('success', 'Copié dans le presse-papiers !');
|
637 |
-
})
|
638 |
-
.catch(err => {
|
639 |
-
console.error('Erreur lors de la copie:', err);
|
640 |
-
this.showFeedback('error', 'Impossible de copier automatiquement.');
|
641 |
-
});
|
642 |
}
|
643 |
-
};
|
644 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
645 |
</script>
|
646 |
</body>
|
647 |
</html>
|
|
|
3 |
<head>
|
4 |
<meta charset="UTF-8">
|
5 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
6 |
+
<title>Mariam AI - Correcteur d'Exercices</title>
|
|
|
|
|
|
|
7 |
<style>
|
|
|
|
|
8 |
body {
|
9 |
+
font-family: sans-serif;
|
10 |
+
line-height: 1.6;
|
11 |
+
margin: 20px;
|
12 |
+
background-color: #f4f4f4;
|
13 |
+
color: #333;
|
14 |
}
|
15 |
+
.container {
|
16 |
+
max-width: 900px;
|
17 |
+
margin: auto;
|
18 |
+
background: #fff;
|
19 |
+
padding: 25px;
|
20 |
+
border-radius: 8px;
|
21 |
+
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
|
22 |
}
|
23 |
+
h1, h2, h3 {
|
24 |
+
color: #444;
|
|
|
|
|
25 |
}
|
26 |
+
h1 {
|
27 |
+
text-align: center;
|
28 |
+
margin-bottom: 30px;
|
|
|
|
|
|
|
|
|
|
|
|
|
29 |
}
|
30 |
+
.status-checks p {
|
31 |
+
padding: 8px;
|
32 |
+
border-radius: 4px;
|
33 |
+
margin: 5px 0;
|
34 |
+
}
|
35 |
+
.status-success {
|
36 |
+
background-color: #e7f4e7;
|
37 |
+
color: #0d6a0d;
|
38 |
+
border: 1px solid #b8d9b8;
|
39 |
+
}
|
40 |
+
.status-error {
|
41 |
+
background-color: #fdecea;
|
42 |
+
color: #a61a1a;
|
43 |
+
border: 1px solid #f8c8c8;
|
44 |
+
}
|
45 |
+
#upload-form label {
|
46 |
+
display: block;
|
47 |
+
margin-bottom: 8px;
|
48 |
+
font-weight: bold;
|
49 |
+
}
|
50 |
+
#image-input {
|
51 |
+
display: block;
|
52 |
+
margin-bottom: 15px;
|
53 |
+
padding: 10px;
|
54 |
+
border: 1px solid #ccc;
|
55 |
+
border-radius: 4px;
|
56 |
+
width: calc(100% - 22px); /* Adjust for padding and border */
|
57 |
+
}
|
58 |
+
#process-button, #download-button {
|
59 |
+
background-color: #5c67f2;
|
60 |
+
color: white;
|
61 |
+
padding: 12px 20px;
|
62 |
+
border: none;
|
63 |
+
border-radius: 4px;
|
64 |
+
cursor: pointer;
|
65 |
+
font-size: 16px;
|
66 |
+
transition: background-color 0.3s ease;
|
67 |
+
}
|
68 |
+
#process-button:hover, #download-button:hover {
|
69 |
+
background-color: #4a54c4;
|
70 |
+
}
|
71 |
+
#process-button:disabled {
|
72 |
+
background-color: #ccc;
|
73 |
+
cursor: not-allowed;
|
74 |
+
}
|
75 |
+
#loading {
|
76 |
+
text-align: center;
|
77 |
+
margin: 20px 0;
|
78 |
+
font-style: italic;
|
79 |
+
color: #666;
|
80 |
+
}
|
81 |
+
#messages {
|
82 |
+
margin-top: 20px;
|
83 |
+
padding: 15px;
|
84 |
+
border-radius: 4px;
|
85 |
+
text-align: center;
|
86 |
+
}
|
87 |
+
.message-success {
|
88 |
+
background-color: #dff0d8;
|
89 |
+
color: #3c763d;
|
90 |
+
border: 1px solid #d6e9c6;
|
91 |
+
}
|
92 |
+
.message-error {
|
93 |
+
background-color: #f2dede;
|
94 |
+
color: #a94442;
|
95 |
+
border: 1px solid #ebccd1;
|
96 |
+
white-space: pre-wrap; /* Pour afficher les retours à la ligne des erreurs */
|
97 |
+
text-align: left;
|
98 |
+
}
|
99 |
+
#results {
|
100 |
+
margin-top: 30px;
|
101 |
+
border-top: 1px solid #eee;
|
102 |
+
padding-top: 20px;
|
103 |
+
}
|
104 |
+
#pdf-preview iframe {
|
105 |
+
width: 100%;
|
106 |
+
height: 600px;
|
107 |
+
border: 1px solid #ccc;
|
108 |
+
border-radius: 4px;
|
109 |
+
}
|
110 |
+
#download-button {
|
111 |
+
display: block; /* Make it a block element */
|
112 |
+
margin: 15px auto 25px auto; /* Center the button */
|
113 |
+
width: fit-content; /* Adjust width to content */
|
114 |
}
|
115 |
|
116 |
+
#raw-output {
|
117 |
+
margin-top: 20px;
|
118 |
+
background-color: #f9f9f9;
|
119 |
+
border: 1px solid #ddd;
|
120 |
+
padding: 15px;
|
121 |
+
border-radius: 4px;
|
122 |
+
}
|
123 |
+
#raw-output h3 {
|
124 |
+
margin-top: 0;
|
125 |
+
margin-bottom: 10px;
|
126 |
+
border-bottom: 1px solid #eee;
|
127 |
+
padding-bottom: 5px;
|
128 |
+
}
|
129 |
+
#raw-output pre {
|
130 |
+
background-color: #efefef;
|
131 |
+
padding: 10px;
|
132 |
+
border-radius: 4px;
|
133 |
+
white-space: pre-wrap; /* Wrap long lines */
|
134 |
+
word-wrap: break-word; /* Break words if necessary */
|
135 |
+
max-height: 400px;
|
136 |
+
overflow-y: auto; /* Add scroll if content is too long */
|
137 |
}
|
138 |
+
#raw-output code {
|
139 |
+
font-family: monospace;
|
140 |
+
font-size: 0.9em;
|
141 |
+
}
|
142 |
+
.hidden {
|
143 |
+
display: none;
|
144 |
}
|
145 |
</style>
|
146 |
</head>
|
147 |
+
<body>
|
148 |
+
<div class="container">
|
149 |
+
<h1>Mariam AI - Correcteur d'Exercices Mathématiques</h1>
|
150 |
+
|
151 |
+
<div class="status-checks">
|
152 |
+
<h2>Vérifications Initiales</h2>
|
153 |
+
<p id="latex-status">Vérification de LaTeX...</p>
|
154 |
+
<p id="api-status">Vérification de la clé API Mariam AI...</p>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
155 |
</div>
|
156 |
+
|
157 |
+
<div id="upload-form">
|
158 |
+
<h2>Soumettre un Exercice</h2>
|
159 |
+
<label for="image-input">Choisissez une image de l'exercice :</label>
|
160 |
+
<input type="file" id="image-input" accept="image/*">
|
161 |
+
<button id="process-button">Générer la Solution</button>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
162 |
</div>
|
163 |
|
164 |
+
<div id="loading" class="hidden">
|
165 |
+
<p>Traitement en cours... Mariam AI réfléchit et compile le PDF...</p>
|
166 |
+
<p>Cela peut prendre jusqu'à 1 minute.</p>
|
167 |
+
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
168 |
|
169 |
+
<div id="messages" class="hidden"></div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
170 |
|
171 |
+
<div id="results" class="hidden">
|
172 |
+
<h2>Résultats</h2>
|
173 |
+
|
174 |
+
<div id="pdf-preview">
|
175 |
+
<h3>Aperçu du PDF</h3>
|
176 |
+
<iframe id="pdf-viewer" title="Aperçu du PDF de la solution"></iframe>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
177 |
</div>
|
|
|
178 |
|
179 |
+
<button id="download-button" class="hidden">Télécharger le PDF</button>
|
|
|
|
|
|
|
|
|
|
|
180 |
|
181 |
+
<div id="raw-output">
|
182 |
+
<div id="latex-section" class="hidden">
|
183 |
+
<h3>Code LaTeX Généré</h3>
|
184 |
+
<pre><code id="latex-output"></code></pre>
|
185 |
+
</div>
|
186 |
+
<div id="thinking-section" class="hidden">
|
187 |
+
<h3>Processus de Réflexion (IA)</h3>
|
188 |
+
<pre><code id="thinking-output"></code></pre>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
189 |
</div>
|
190 |
</div>
|
191 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
192 |
</div>
|
193 |
|
|
|
194 |
<script>
|
195 |
+
document.addEventListener('DOMContentLoaded', () => {
|
196 |
+
const latexStatusEl = document.getElementById('latex-status');
|
197 |
+
const apiStatusEl = document.getElementById('api-status');
|
198 |
+
const imageInput = document.getElementById('image-input');
|
199 |
+
const processButton = document.getElementById('process-button');
|
200 |
+
const loadingEl = document.getElementById('loading');
|
201 |
+
const messagesEl = document.getElementById('messages');
|
202 |
+
const resultsEl = document.getElementById('results');
|
203 |
+
const pdfViewer = document.getElementById('pdf-viewer');
|
204 |
+
const downloadButton = document.getElementById('download-button');
|
205 |
+
const rawOutputEl = document.getElementById('raw-output');
|
206 |
+
const latexSection = document.getElementById('latex-section');
|
207 |
+
const latexOutputEl = document.getElementById('latex-output');
|
208 |
+
const thinkingSection = document.getElementById('thinking-section');
|
209 |
+
const thinkingOutputEl = document.getElementById('thinking-output');
|
210 |
+
|
211 |
+
let currentPdfBase64 = null; // Stocker les données PDF pour le téléchargement
|
212 |
+
|
213 |
+
// --- Fonctions Utilitaires ---
|
214 |
+
function showLoading() {
|
215 |
+
loadingEl.classList.remove('hidden');
|
216 |
+
processButton.disabled = true;
|
217 |
+
messagesEl.classList.add('hidden');
|
218 |
+
resultsEl.classList.add('hidden');
|
219 |
+
pdfViewer.src = 'about:blank'; // Clear previous PDF
|
220 |
+
latexOutputEl.textContent = '';
|
221 |
+
thinkingOutputEl.textContent = '';
|
222 |
+
latexSection.classList.add('hidden');
|
223 |
+
thinkingSection.classList.add('hidden');
|
224 |
+
downloadButton.classList.add('hidden');
|
225 |
+
currentPdfBase64 = null;
|
226 |
+
}
|
227 |
+
|
228 |
+
function hideLoading() {
|
229 |
+
loadingEl.classList.add('hidden');
|
230 |
+
processButton.disabled = false;
|
231 |
+
}
|
232 |
+
|
233 |
+
function showMessage(message, isError = false) {
|
234 |
+
messagesEl.textContent = message;
|
235 |
+
messagesEl.className = isError ? 'message-error' : 'message-success';
|
236 |
+
messagesEl.classList.remove('hidden');
|
237 |
+
}
|
238 |
+
|
239 |
+
function displayResults(data) {
|
240 |
+
resultsEl.classList.remove('hidden');
|
241 |
+
|
242 |
+
if (data.pdf_base64) {
|
243 |
+
pdfViewer.src = `data:application/pdf;base64,${data.pdf_base64}`;
|
244 |
+
currentPdfBase64 = data.pdf_base64;
|
245 |
+
downloadButton.classList.remove('hidden');
|
246 |
+
} else {
|
247 |
+
pdfViewer.src = 'about:blank'; // Ensure it's blank if no PDF
|
248 |
+
downloadButton.classList.add('hidden');
|
249 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
250 |
|
251 |
+
if (data.latex) {
|
252 |
+
latexOutputEl.textContent = data.latex;
|
253 |
+
latexSection.classList.remove('hidden');
|
254 |
+
} else {
|
255 |
+
latexSection.classList.add('hidden');
|
256 |
+
}
|
257 |
+
|
258 |
+
if (data.thinking) {
|
259 |
+
thinkingOutputEl.textContent = data.thinking;
|
260 |
+
thinkingSection.classList.remove('hidden');
|
261 |
+
} else {
|
262 |
+
thinkingSection.classList.add('hidden');
|
263 |
+
}
|
264 |
+
}
|
265 |
+
|
266 |
+
// --- Vérifications Initiales ---
|
267 |
+
async function checkStatus() {
|
268 |
+
try {
|
269 |
+
const latexRes = await fetch('/check-latex');
|
270 |
+
const latexData = await latexRes.json();
|
271 |
+
latexStatusEl.textContent = latexData.message;
|
272 |
+
latexStatusEl.className = latexData.success ? 'status-success' : 'status-error';
|
273 |
+
} catch (error) {
|
274 |
+
latexStatusEl.textContent = '❌ Erreur lors de la vérification de LaTeX: ' + error;
|
275 |
+
latexStatusEl.className = 'status-error';
|
276 |
+
}
|
277 |
+
|
278 |
+
try {
|
279 |
+
const apiRes = await fetch('/check-api');
|
280 |
+
const apiData = await apiRes.json();
|
281 |
+
apiStatusEl.textContent = apiData.message;
|
282 |
+
apiStatusEl.className = apiData.success ? 'status-success' : 'status-error';
|
283 |
+
} catch (error) {
|
284 |
+
apiStatusEl.textContent = '❌ Erreur lors de la vérification de l\'API: ' + error;
|
285 |
+
apiStatusEl.className = 'status-error';
|
286 |
+
}
|
287 |
+
}
|
288 |
+
|
289 |
+
// --- Traitement de l'Image ---
|
290 |
+
processButton.addEventListener('click', async () => {
|
291 |
+
const file = imageInput.files[0];
|
292 |
+
if (!file) {
|
293 |
+
showMessage('Veuillez sélectionner un fichier image.', true);
|
294 |
+
return;
|
295 |
+
}
|
296 |
+
|
297 |
+
showLoading();
|
298 |
+
|
299 |
+
const formData = new FormData();
|
300 |
+
formData.append('image', file);
|
301 |
+
|
302 |
+
try {
|
303 |
+
const response = await fetch('/process', {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
304 |
method: 'POST',
|
305 |
body: formData
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
306 |
});
|
307 |
+
|
308 |
+
const data = await response.json();
|
309 |
+
hideLoading();
|
310 |
+
|
311 |
+
if (data.success) {
|
312 |
+
showMessage('Solution générée et compilée avec succès !');
|
313 |
+
displayResults(data);
|
314 |
+
} else {
|
315 |
+
showMessage(`Erreur : ${data.message}`, true);
|
316 |
+
// Afficher le LaTeX/Thinking même en cas d'erreur de compilation
|
317 |
+
if(data.latex || data.thinking) {
|
318 |
+
displayResults(data); // Affiche LaTeX/Thinking sans PDF
|
|
|
|
|
|
|
|
|
|
|
|
|
319 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
320 |
}
|
321 |
+
|
322 |
+
} catch (error) {
|
323 |
+
hideLoading();
|
324 |
+
showMessage(`Erreur de communication avec le serveur : ${error}`, true);
|
325 |
+
console.error("Fetch Error:", error);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
326 |
}
|
327 |
+
});
|
328 |
+
|
329 |
+
// --- Téléchargement du PDF ---
|
330 |
+
downloadButton.addEventListener('click', async () => {
|
331 |
+
if (!currentPdfBase64) {
|
332 |
+
showMessage('Aucune donnée PDF à télécharger.', true);
|
333 |
+
return;
|
334 |
+
}
|
335 |
+
|
336 |
+
try {
|
337 |
+
const response = await fetch('/download-pdf', {
|
338 |
+
method: 'POST',
|
339 |
+
headers: {
|
340 |
+
'Content-Type': 'application/json',
|
341 |
+
},
|
342 |
+
body: JSON.stringify({ pdf_data: currentPdfBase64 }),
|
343 |
+
});
|
344 |
+
|
345 |
+
if (response.ok) {
|
346 |
+
// Le backend utilise send_file avec as_attachment=True,
|
347 |
+
// le navigateur devrait déclencher le téléchargement automatiquement.
|
348 |
+
// On récupère le blob pour créer un lien de secours au cas où.
|
349 |
+
const blob = await response.blob();
|
350 |
+
const url = window.URL.createObjectURL(blob);
|
351 |
+
const a = document.createElement('a');
|
352 |
+
a.style.display = 'none';
|
353 |
+
a.href = url;
|
354 |
+
// Le nom de fichier est défini côté serveur, mais on le remet ici
|
355 |
+
a.download = response.headers.get('Content-Disposition')?.split('filename=')[1]?.replaceAll('"', '') || 'solution_mariam_ai.pdf';
|
356 |
+
document.body.appendChild(a);
|
357 |
+
a.click();
|
358 |
+
window.URL.revokeObjectURL(url);
|
359 |
+
a.remove();
|
360 |
+
showMessage('Téléchargement démarré.');
|
361 |
+
} else {
|
362 |
+
// Essayer de lire le message d'erreur JSON du serveur
|
363 |
+
let errorMsg = `Échec du téléchargement (code ${response.status}).`;
|
364 |
+
try {
|
365 |
+
const errorData = await response.json();
|
366 |
+
if(errorData.message) errorMsg += ` Raison: ${errorData.message}`;
|
367 |
+
} catch(e) { /* Ignorer l'erreur si la réponse n'est pas JSON */ }
|
368 |
+
showMessage(errorMsg, true);
|
369 |
+
}
|
370 |
+
} catch (error) {
|
371 |
+
showMessage(`Erreur lors de la tentative de téléchargement : ${error}`, true);
|
372 |
+
console.error("Download Error:", error);
|
373 |
+
}
|
374 |
+
});
|
375 |
+
|
376 |
+
|
377 |
+
// Exécuter les vérifications au chargement
|
378 |
+
checkStatus();
|
379 |
+
});
|
380 |
</script>
|
381 |
</body>
|
382 |
</html>
|