Testpdf / templates /index.html
Docfile's picture
Update templates/index.html
1f4ef47 verified
raw
history blame
13.1 kB
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Résolution d'exercices (Maths, Physique et Chimie)</title>
<script src="https://cdn.tailwindcss.com"></script>
<style>
/* Custom scrollbar for pre block (optional) */
pre::-webkit-scrollbar {
width: 8px;
height: 8px;
}
pre::-webkit-scrollbar-track {
background: #f1f1f1;
border-radius: 10px;
}
pre::-webkit-scrollbar-thumb {
background: #888;
border-radius: 10px;
}
pre::-webkit-scrollbar-thumb:hover {
background: #555;
}
.spinner {
border: 4px solid rgba(0, 0, 0, 0.1);
width: 36px;
height: 36px;
border-radius: 50%;
border-left-color: #0ea5e9; /* sky-500 */
animation: spin 1s ease infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
</style>
</head>
<body class="bg-slate-100 text-slate-800 min-h-screen flex flex-col items-center justify-center p-4 font-sans">
<div class="bg-white p-6 sm:p-8 rounded-xl shadow-2xl w-full max-w-2xl">
<header class="mb-6 sm:mb-8 text-center">
<h1 class="text-2xl sm:text-3xl font-bold text-sky-700">
Résolution d'Exercices
</h1>
<p class="text-sm text-slate-500">Mathématiques, Physique et Chimie</p>
</header>
<form id="solveForm" class="space-y-6">
<div>
<label for="imageUpload" class="block text-sm font-medium text-gray-700 mb-1">
Télécharger une image de l'exercice :
</label>
<input type="file" id="imageUpload" name="image" accept="image/*" required
class="block w-full text-sm text-slate-500
file:mr-4 file:py-2 file:px-4
file:rounded-full file:border-0
file:text-sm file:font-semibold
file:bg-sky-50 file:text-sky-700
hover:file:bg-sky-100
border border-gray-300 rounded-md p-2 focus:outline-none focus:ring-2 focus:ring-sky-500">
</div>
<div>
<span class="block text-sm font-medium text-gray-700 mb-2">Choisir le format de la solution :</span>
<div class="space-y-2">
<div class="flex items-center">
<input id="promptRefined" name="prompt_type" type="radio" value="refined" checked
class="h-4 w-4 text-sky-600 border-gray-300 focus:ring-sky-500">
<label for="promptRefined" class="ml-2 block text-sm text-gray-900">
Format Raffiné & Complet <span class="text-xs text-gray-500">(mise en page avancée)</span>
</label>
</div>
<div class="flex items-center">
<input id="promptLight" name="prompt_type" type="radio" value="light"
class="h-4 w-4 text-sky-600 border-gray-300 focus:ring-sky-500">
<label for="promptLight" class="ml-2 block text-sm text-gray-900">
Format simple
</label>
</div>
</div>
</div>
<button type="submit" id="submitButton"
class="w-full bg-sky-600 hover:bg-sky-700 text-white font-bold py-2.5 px-4 rounded-md
focus:outline-none focus:ring-2 focus:ring-sky-500 focus:ring-offset-2
transition duration-150 ease-in-out
disabled:bg-slate-400 disabled:cursor-not-allowed">
Résoudre l'exercice
</button>
</form>
<div id="statusArea" class="mt-6 space-y-3 hidden">
<div class="flex items-center space-x-3 p-3 rounded-md bg-sky-50 border border-sky-200">
<div id="spinner" class="spinner hidden"></div>
<p id="statusMessage" class="text-sm font-medium text-sky-700"></p>
</div>
<div id="errorMessage" class="p-3 rounded-md bg-red-50 border border-red-200 text-red-700 text-sm hidden"></div>
<div id="errorDetailMessage" class="p-3 rounded-md bg-amber-50 border border-amber-200 text-amber-700 text-sm hidden"></div>
</div>
<div id="resultArea" class="mt-6 hidden">
<h3 class="text-lg font-semibold text-slate-700 mb-2">Code LaTeX Généré :</h3>
<div class="bg-gray-900 text-gray-100 p-4 rounded-md shadow">
<pre><code id="latexOutput" class="text-sm whitespace-pre-wrap break-all block max-h-96 overflow-auto"></code></pre>
</div>
<button id="copyLatexButton" class="mt-3 bg-slate-200 hover:bg-slate-300 text-slate-700 text-sm font-medium py-1.5 px-3 rounded-md transition duration-150 ease-in-out">
Copier le LaTeX
</button>
</div>
</div>
<footer class="mt-8 text-center text-xs text-slate-500">
<p>© 2025 Mariam AI - Solution Propulsée par Mariam</p>
</footer>
<script>
const solveForm = document.getElementById('solveForm');
const submitButton = document.getElementById('submitButton');
const imageUpload = document.getElementById('imageUpload');
const statusArea = document.getElementById('statusArea');
const spinner = document.getElementById('spinner');
const statusMessage = document.getElementById('statusMessage');
const errorMessage = document.getElementById('errorMessage');
const errorDetailMessage = document.getElementById('errorDetailMessage');
const resultArea = document.getElementById('resultArea');
const latexOutput = document.getElementById('latexOutput');
const copyLatexButton = document.getElementById('copyLatexButton');
let eventSource = null;
solveForm.addEventListener('submit', async function(event) {
event.preventDefault();
if (!imageUpload.files || imageUpload.files.length === 0) {
showError("Veuillez sélectionner un fichier image.");
return;
}
submitButton.disabled = true;
showStatus("Initialisation...", true);
hideError();
hideResult();
const formData = new FormData(solveForm);
try {
const response = await fetch('/solve', {
method: 'POST',
body: formData
});
if (!response.ok) {
const errorData = await response.json().catch(() => ({ error: 'Erreur serveur inconnue' }));
throw new Error(errorData.error || `Erreur HTTP: ${response.status}`);
}
const data = await response.json();
if (data.task_id) {
showStatus("Tâche créée. En attente de la réponse...", true);
startStreaming(data.task_id);
} else {
throw new Error("ID de tâche non reçu.");
}
} catch (error) {
showError(`Erreur lors de la soumission : ${error.message}`);
submitButton.disabled = false;
hideStatus();
}
});
function startStreaming(taskId) {
if (eventSource) {
eventSource.close();
}
eventSource = new EventSource(`/stream/${taskId}`);
showStatus("Connexion au flux de progression...", true);
eventSource.onmessage = function(event) {
const data = JSON.parse(event.data);
let userFriendlyStatus = data.status;
switch(data.status) {
case 'pending': userFriendlyStatus = "En attente de traitement..."; break;
case 'processing': userFriendlyStatus = "Traitement de l'image..."; break;
case 'generating_latex': userFriendlyStatus = "Génération du code LaTeX en cours..."; break;
case 'cleaning_latex': userFriendlyStatus = "Nettoyage du code LaTeX..."; break;
case 'generating_pdf': userFriendlyStatus = "Génération du PDF en cours..."; break;
case 'completed': userFriendlyStatus = "Terminé ! Solution générée (LaTeX et PDF)."; break;
case 'completed_tex_only': userFriendlyStatus = "Terminé ! Solution LaTeX générée (PDF non disponible)."; break;
case 'pdf_error': userFriendlyStatus = "Erreur lors de la génération du PDF. Le code LaTeX est disponible."; break;
case 'error': userFriendlyStatus = "Une erreur est survenue."; break;
}
showStatus(userFriendlyStatus, !['completed', 'error', 'pdf_error', 'completed_tex_only'].includes(data.status));
if (data.error) {
showError(data.error);
} else {
hideError(); // Hide general error if a specific one is not present in this message
}
if (data.error_detail) {
showErrorDetail(data.error_detail);
} else {
hideErrorDetail();
}
if (data.response) {
latexOutput.textContent = data.response;
showResult();
}
if (['completed', 'error', 'pdf_error', 'completed_tex_only'].includes(data.status)) {
eventSource.close();
submitButton.disabled = false;
if (data.status !== 'error' && !data.error) { // If not a critical error, keep status positive
// Status already set by switch
} else if (data.error) {
showStatus("Traitement terminé avec des erreurs.", false);
}
}
};
eventSource.onerror = function(err) {
console.error("Erreur EventSource:", err);
showError("Erreur de connexion au serveur pour les mises à jour. Veuillez réessayer.");
showStatus("Déconnecté.", false);
eventSource.close();
submitButton.disabled = false;
};
}
function showStatus(message, showSpinner = false) {
statusArea.classList.remove('hidden');
statusMessage.textContent = message;
if (showSpinner) {
spinner.classList.remove('hidden');
} else {
spinner.classList.add('hidden');
}
}
function hideStatus() {
statusArea.classList.add('hidden');
spinner.classList.add('hidden');
}
function showError(message) {
errorMessage.textContent = message;
errorMessage.classList.remove('hidden');
statusArea.classList.remove('hidden'); // Ensure status area is visible to show error
}
function hideError() {
errorMessage.classList.add('hidden');
errorMessage.textContent = '';
}
function showErrorDetail(message) {
errorDetailMessage.textContent = message;
errorDetailMessage.classList.remove('hidden');
statusArea.classList.remove('hidden');
}
function hideErrorDetail() {
errorDetailMessage.classList.add('hidden');
errorDetailMessage.textContent = '';
}
function showResult() {
resultArea.classList.remove('hidden');
}
function hideResult() {
resultArea.classList.add('hidden');
latexOutput.textContent = '';
}
copyLatexButton.addEventListener('click', () => {
if (latexOutput.textContent) {
navigator.clipboard.writeText(latexOutput.textContent)
.then(() => {
const originalText = copyLatexButton.textContent;
copyLatexButton.textContent = 'Copié !';
setTimeout(() => {
copyLatexButton.textContent = originalText;
}, 2000);
})
.catch(err => {
console.error('Erreur de copie: ', err);
alert('Erreur lors de la copie du texte.');
});
}
});
</script>
</body>
</html>