Mariam-cards / templates /index.html
Docfile's picture
Update templates/index.html
aaebe0a verified
raw
history blame
24.8 kB
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Math Solver (MathJax) - Version Gratuite</title>
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css" rel="stylesheet">
<link href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/styles/atom-one-dark.min.css" rel="stylesheet">
<!-- PAS de CSS KaTeX -->
<style>
/* --- Styles CSS (Largement inchangés, suppression des refs spécifiques à KaTeX) --- */
:root {
--primary-color: #4a6fa5;
--secondary-color: #166088;
--accent-color: #4fc3f7;
--background-color: #f8f9fa;
--text-color: #333;
--box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
--code-bg: #2c323c;
--output-bg: #f1f8f9;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
line-height: 1.6;
margin: 0;
padding: 0;
background-color: var(--background-color);
color: var(--text-color);
}
.container {
max-width: 1000px;
margin: 0 auto;
padding: 20px;
}
header, .content-box > h1, .content-box > p:not(#uploadStatus),
.upload-section h2, .upgrade-section, footer, #solutionOutput h3 {
text-align: center;
}
.logo {
font-size: 2.5rem; font-weight: bold; color: var(--primary-color); margin-bottom: 10px;
}
.subtitle {
font-size: 1.2rem; color: var(--secondary-color); margin-bottom: 20px;
}
.content-box {
background-color: white; border-radius: 10px; box-shadow: var(--box-shadow);
padding: 30px; margin-bottom: 30px;
}
h1 { color: var(--primary-color); margin-top: 0; }
.feature-list {
list-style-type: none; padding: 0; margin: 30px auto; max-width: 600px; text-align: left;
}
.feature-list h2 { text-align: center; }
.feature-list li { padding: 10px 0; margin-bottom: 10px; display: flex; align-items: center; }
.feature-list i {
color: var(--accent-color); margin-right: 10px; font-size: 1.2rem; width: 20px; text-align: center;
}
.feature-list .fa-times-circle { color: #aaa; }
.cta-button {
display: inline-block; background-color: var(--primary-color); color: white;
padding: 12px 25px; border-radius: 5px; text-decoration: none; font-weight: bold;
transition: all 0.3s ease; margin: 10px; border: none; cursor: pointer;
}
.cta-button:hover {
background-color: var(--secondary-color); transform: translateY(-2px);
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.15);
}
.upload-section { margin-top: 30px; }
#uploadStatus { margin-top: 10px; font-size: 0.9em; color: #555; min-height: 1.2em; }
#imagePreview {
display: none; margin: 20px auto; max-width: 400px; max-height: 300px;
overflow: hidden; border: 1px solid #ddd; border-radius: 8px;
}
#preview { display: block; width: 100%; height: auto; border-radius: 8px; }
footer { padding: 20px 0; color: #666; font-size: 0.9rem; }
#solutionOutput { margin-top: 30px; text-align: left; display: none; }
#solutionOutput h3 { margin-bottom: 20px; }
#solution {
background: transparent; padding: 0; border-radius: 8px; text-align: left;
line-height: 1.8; font-size: 16px; box-shadow: var(--box-shadow);
overflow: hidden; margin-top: 20px;
}
#solution > section { /* Utiliser section */
padding: 20px; margin: 0; border-radius: 0; overflow-x: auto;
background-color: #f9f9f9; border-bottom: 1px solid #eee;
word-wrap: break-word; overflow-wrap: break-word;
}
.code-section { background-color: transparent !important; padding: 0 !important; border-bottom: none !important; }
.output-section { background-color: var(--output-bg) !important; border-left: 4px solid var(--secondary-color); padding-left: calc(20px - 4px); }
.step-section { background-color: #ffffff; border-left: 4px solid var(--primary-color); padding-left: calc(20px - 4px); }
/* Styles spécifiques pour MathJax (si nécessaire, souvent pas besoin au début) */
mjx-container[display="true"] { /* Cible les maths en mode display */
display: block;
overflow-x: auto; /* Scroll horizontal pour les équations longues */
overflow-y: hidden;
margin: 1em 0;
padding: 0.5em 0.2em;
background-color: #fdfdfd;
border-radius: 4px;
text-align: center; /* Centrer le conteneur */
}
/* Pour s'assurer que le texte normal est bien aligné avec les maths inline */
mjx-container { line-height: 0; } /* MathJax ajuste souvent cela */
.code-header {
background-color: #343a40; color: white; padding: 10px 15px; font-size: 14px;
font-family: 'Courier New', monospace; display: flex; justify-content: space-between; align-items: center;
}
.code-content {
margin: 0; padding: 15px; background-color: var(--code-bg); color: #e6e6e6;
overflow-x: auto; font-family: 'Courier New', monospace; font-size: 14px;
line-height: 1.5; border-bottom: 1px solid #444;
}
#solution > section:first-child,
#solution > section.code-section:first-child .code-header {
border-top-left-radius: 8px; border-top-right-radius: 8px;
}
#solution > section:last-child,
#solution > section.code-section:last-child .code-content {
border-bottom-left-radius: 8px; border-bottom-right-radius: 8px; border-bottom: none;
}
#solution > section:only-child { border-radius: 8px; }
.thinking-indicator, .executing-indicator, .answering-indicator {
display: flex; align-items: center; justify-content: center; padding: 12px 15px;
margin: 0 auto 20px auto; border-radius: 8px; font-size: 0.95rem;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05); max-width: 400px;
}
.thinking-indicator { background-color: #e3f2fd; color: #1565c0; border: 1px solid #bbdefb; }
.executing-indicator { background-color: #ede7f6; color: #5e35b1; border: 1px solid #d1c4e9; }
.answering-indicator { background-color: #e8f5e9; color: #2e7d32; border: 1px solid #c8e6c9; }
.indicator-icon { margin-right: 12px; animation: pulse 1.5s infinite ease-in-out; font-size: 1.1rem; }
@keyframes pulse { 0% { opacity: 0.6; transform: scale(1); } 50% { opacity: 1; transform: scale(1.05); } 100% { opacity: 0.6; transform: scale(1); } }
.error-message {
background-color: #fff0f0 !important; color: red !important; border-color: red !important; font-weight: bold;
}
.error-message code { background-color: #fdd; color: #c00; padding: 2px 4px; border-radius: 3px; font-family: monospace; }
.step-section p { margin-bottom: 1em; }
.step-section p:last-child { margin-bottom: 0; }
/* Styles Markdown basiques si le serveur envoie du HTML simple pour listes/etc */
.step-section ul, .step-section ol { padding-left: 30px; margin-bottom: 1em; }
.step-section li { margin-bottom: 0.5em; }
.step-section code { font-family: monospace; background-color: #eee; padding: 2px 4px; border-radius: 3px; font-size: 0.9em; }
.step-section pre { background-color: #f0f0f0; border-radius: 4px; padding: 12px; overflow-x: auto; margin: 1em 0; border: 1px solid #ddd; }
.step-section pre code { background: transparent; padding: 0; border-radius: 0; font-size: 0.95em; }
</style>
</head>
<body>
<div class="container">
<header>
<div class="logo">Math Solver (MathJax)</div>
<div class="subtitle">La solution intelligente pour vos problèmes mathématiques</div>
</header>
<div class="content-box">
<h1>Version Gratuite</h1>
<p>Vous utilisez actuellement la version gratuite de Math Solver qui vous permet de résoudre 3 problèmes par jour.</p>
<div class="feature-list">
<h2>Fonctionnalités disponibles :</h2>
<ul>
<li><i class="fas fa-check-circle"></i> Résolution de problèmes mathématiques basiques</li>
<li><i class="fas fa-check-circle"></i> 3 résolutions gratuites par jour</li>
<li><i class="fas fa-check-circle"></i> Explication des étapes de résolution (avec MathJax)</li>
<li><i class="fas fa-times-circle"></i> <span style="color: #999;">Mode d'exécution de code avancé (version Pro)</span></li>
<li><i class="fas fa-times-circle"></i> <span style="color: #999;">Résolutions illimitées (version Pro)</span></li>
<li><i class="fas fa-times-circle"></i> <span style="color: #999;">Support prioritaire (version Pro)</span></li>
</ul>
</div>
<div class="upload-section">
<h2>Soumettez votre problème</h2>
<form id="imageForm" enctype="multipart/form-data">
<input type="file" id="imageInput" name="image" accept="image/*" style="display: none;">
<button type="button" id="uploadButton" class="cta-button" onclick="document.getElementById('imageInput').click()">
<i class="fas fa-upload"></i> Télécharger une image
</button>
<p id="uploadStatus"></p>
</form>
<div id="imagePreview"><img id="preview"></div>
<button id="solveButton" class="cta-button" style="display: none; background-color: var(--secondary-color);">
<i class="fas fa-calculator"></i> Résoudre ce problème
</button>
</div>
<div id="solutionOutput">
<h3>Solution :</h3>
<div id="loadingIndicator" class="thinking-indicator" style="display: none;">
<i class="fas fa-brain indicator-icon"></i>
<span>Je réfléchis au problème...</span>
</div>
<div id="solution">
<!-- Contenu dynamique ajouté ici -->
</div>
</div>
<div class="upgrade-section">
<h2>Besoin de plus de puissance ?</h2>
<p>Passez à la version Pro pour des fonctionnalités avancées et des résolutions illimitées.</p>
<a href="#" class="cta-button">Passer à la version Pro</a>
</div>
</div>
<footer>
<p>© 2025 Math Solver. Tous droits réservés.</p>
</footer>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/highlight.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/languages/python.min.js"></script>
<!-- Configuration MathJax (DOIT être avant le script principal) -->
<script>
window.MathJax = {
tex: {
inlineMath: [['$', '$'], ['\\(', '\\)']], // Délimiteurs inline
displayMath: [['$$', '$$'], ['\\[', '\\]']], // Délimiteurs display
processEscapes: true, // Permet d'échapper les $ avec \$
tags: 'ams', // Numérotation des équations style AMS
packages: {'[+]': ['ams']} // Charger le package AMS pour plus de commandes
},
options: {
skipInitialTypeset: true, // Très important : on ne type pas la page au début
ignoreHtmlClass: 'tex2jax_ignore', // Classes à ignorer
processHtmlClass: 'tex2jax_process' // Classes à traiter explicitement (si besoin)
},
chtml: {
matchFontHeight: true, // Tente d'ajuster la taille à la police environnante
scale: 1.0 // Ajustement global de la taille (1.0 = normal)
},
loader: {
load: ['[tex]/ams'] // Assure le chargement du package AMS
}
};
</script>
<!-- Script principal MathJax v3 -->
<script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"></script>
<script>
// Pas besoin de config marked.js ici
document.getElementById('imageInput').addEventListener('change', function(event) {
const file = event.target.files[0];
if (file) {
const reader = new FileReader();
reader.onload = function(e) {
document.getElementById('preview').src = e.target.result;
document.getElementById('imagePreview').style.display = 'block';
document.getElementById('solveButton').style.display = 'inline-block';
document.getElementById('uploadStatus').textContent = `Image sélectionnée : ${file.name}`;
document.getElementById('solutionOutput').style.display = 'none';
document.getElementById('solution').innerHTML = '';
}
reader.readAsDataURL(file);
}
});
document.getElementById('solveButton').addEventListener('click', function() {
const formData = new FormData(document.getElementById('imageForm'));
const solutionOutputDiv = document.getElementById('solutionOutput');
const loadingIndicator = document.getElementById('loadingIndicator');
const solutionContainer = document.getElementById('solution');
solutionOutputDiv.style.display = 'block';
loadingIndicator.style.display = 'flex';
solutionContainer.innerHTML = '';
loadingIndicator.scrollIntoView({ behavior: 'smooth', block: 'center' });
fetch('/solved', { // Endpoint Flask
method: 'POST',
body: formData
})
.then(response => {
if (!response.ok) {
return response.text().then(text => {
throw new Error(`Erreur serveur: ${response.status} ${response.statusText}\n${text || '(Aucun détail)'}`);
});
}
const reader = response.body.getReader();
const decoder = new TextDecoder();
let buffer = '';
let elementsToTypeset = []; // Pour regrouper les éléments à traiter par MathJax
// Fonction pour traiter les éléments en attente avec MathJax
function processPendingMathJax() {
if (elementsToTypeset.length > 0) {
// Copier et vider le tableau avant l'appel asynchrone
const elements = [...elementsToTypeset];
elementsToTypeset = [];
// Appeler MathJax.typesetPromise sur les éléments ajoutés
if (window.MathJax && window.MathJax.typesetPromise) {
window.MathJax.typesetPromise(elements).catch((err) => {
console.error('Erreur MathJax typesetting:', err);
// Afficher une erreur utilisateur si pertinent
elements.forEach(el => {
const errorDiv = document.createElement('div');
errorDiv.style.color = 'red';
errorDiv.textContent = '[Erreur MathJax]';
el.appendChild(errorDiv);
});
});
} else {
console.warn("MathJax n'est pas encore prêt ou typesetPromise n'existe pas.");
// Remettre les éléments dans la file d'attente pour un essai ultérieur ?
// elementsToTypeset.push(...elements); // Attention aux boucles infinies
}
}
}
function processStream({ done, value }) {
if (done) {
loadingIndicator.style.display = 'none';
// Traiter les derniers éléments restants
processPendingMathJax();
return;
}
buffer += decoder.decode(value, { stream: true });
const messages = buffer.split(/\r?\n\r?\n/);
buffer = messages.pop();
messages.forEach(message => {
if (message.startsWith('data: ')) {
try {
const data = JSON.parse(message.substring(6));
let blockAdded = null; // Référence au bloc ajouté
// --- Gérer les mises à jour de mode ---
if (data.mode) {
const modes = { /* ... (comme avant) ... */ };
const modeInfo = modes[data.mode] || { icon: 'fa-sync-alt fa-spin', text: 'Traitement...', class: 'thinking-indicator' };
loadingIndicator.className = `${modeInfo.class}`;
loadingIndicator.innerHTML = `<i class="fas ${modeInfo.icon} indicator-icon"></i><span>${modeInfo.text}</span>`;
loadingIndicator.style.display = 'flex';
}
// --- Gérer les blocs de contenu ---
if (data.content !== undefined && data.content !== null) {
loadingIndicator.style.display = 'none';
const content = data.content;
const tempDiv = document.createElement('div');
tempDiv.innerHTML = content;
const codeSection = tempDiv.querySelector('.code-section');
const outputSection = tempDiv.querySelector('.output-section');
let newElement = document.createElement('section'); // Conteneur par défaut
if (codeSection) {
// Code Python dédié
newElement.outerHTML = codeSection.outerHTML; // Remplacer la section par le contenu du code
// On doit récupérer la référence APRES ajout au DOM
blockAdded = solutionContainer.appendChild(newElement.cloneNode(true));
blockAdded.querySelectorAll('pre code').forEach((block) => {
hljs.highlightElement(block);
});
// Pas de MathJax sur le code python a priori
} else if (outputSection) {
// Sortie de code
newElement.outerHTML = outputSection.outerHTML;
blockAdded = solutionContainer.appendChild(newElement.cloneNode(true));
// Ajouter aux éléments à traiter par MathJax
if (blockAdded) elementsToTypeset.push(blockAdded);
} else {
// Section d'étape (Texte + LaTeX potentiel)
newElement.className = 'step-section';
newElement.innerHTML = content; // Injecter le contenu HTML brut
blockAdded = solutionContainer.appendChild(newElement);
// Ajouter aux éléments à traiter par MathJax
if (blockAdded) elementsToTypeset.push(blockAdded);
}
}
// --- Gérer les erreurs spécifiques ---
if (data.error) {
const errorElement = document.createElement('section');
errorElement.className = 'step-section error-message';
errorElement.innerHTML = `<strong>Erreur :</strong> ${data.error || 'Une erreur inconnue est survenue.'}`;
blockAdded = solutionContainer.appendChild(errorElement);
loadingIndicator.style.display = 'none';
// Pas besoin de MathJax sur les erreurs
}
// Déclencher le rendu MathJax périodiquement ou après chaque message
// (Regrouper améliore un peu les perfs vs appeler à chaque fois)
// On pourrait utiliser un debounce ou juste après chaque message traité ici
processPendingMathJax();
} catch (e) {
console.error('Erreur analyse JSON ou traitement message SSE:', e, message);
const errorElement = document.createElement('section');
errorElement.className = 'step-section error-message';
errorElement.style.color = 'orange'; errorElement.style.backgroundColor = '#fff8e1'; errorElement.style.borderColor = 'orange';
errorElement.textContent = `Erreur traitement message: ${message.substring(0, 150)}...`;
solutionContainer.appendChild(errorElement);
loadingIndicator.style.display = 'none';
}
}
});
// Défiler vers le bas
solutionContainer.lastChild?.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
// Continuer à lire le flux
return reader.read().then(processStream);
}
// Démarrer le traitement du flux
// S'assurer que MathJax est prêt avant de commencer (optionnel mais plus sûr)
if (window.MathJax && window.MathJax.startup) {
window.MathJax.startup.promise.then(() => {
console.log('MathJax prêt, démarrage du stream processing.');
reader.read().then(processStream);
});
} else {
// Fallback si MathJax n'est pas encore configuré (ne devrait pas arriver avec script async + config avant)
console.warn('MathJax pas encore prêt, démarrage direct du stream.');
reader.read().then(processStream);
}
})
.catch(error => {
console.error('Erreur Fetch ou connexion:', error);
const errorElement = document.createElement('section');
errorElement.className = 'step-section error-message';
errorElement.innerHTML = `<strong>Erreur de connexion ou serveur :</strong> ${error.message || error}`;
solutionContainer.appendChild(errorElement);
loadingIndicator.style.display = 'none';
errorElement.scrollIntoView({ behavior: 'smooth', block: 'center' });
});
});
// Pas besoin d'event listener DOMContentLoaded pour MathJax avec skipInitialTypeset: true
console.log("Page chargée. MathJax configuré pour rendu manuel.");
</script>
</body>
</html>