Spaces:
Running
Running
<html lang="fr"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>Math Solver - 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"> | |
<style> | |
: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 { | |
text-align: center; | |
padding: 20px 0; | |
margin-bottom: 30px; | |
} | |
.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; | |
text-align: center; | |
} | |
h1 { | |
color: var(--primary-color); | |
margin-top: 0; | |
} | |
.feature-list { | |
list-style-type: none; | |
padding: 0; | |
margin: 30px 0; | |
text-align: left; | |
} | |
.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; | |
} | |
.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: 20px 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); | |
} | |
.upgrade-section { | |
margin-top: 30px; | |
padding: 20px; | |
border-top: 1px solid #ddd; | |
} | |
footer { | |
text-align: center; | |
padding: 20px 0; | |
color: #666; | |
font-size: 0.9rem; | |
} | |
#solution { | |
background: #fff; | |
padding: 0; | |
border-radius: 8px; | |
text-align: left; | |
line-height: 1.8; | |
font-size: 16px; | |
background-color: transparent; | |
box-shadow: var(--box-shadow); | |
overflow: hidden; | |
} | |
.step-section, .code-section, .output-section { | |
margin: 0 0 1px 0; | |
padding: 20px; | |
border-radius: 0; | |
overflow-x: auto; | |
background-color: #f9f9f9; | |
} | |
.step-section { | |
border-left: 4px solid var(--primary-color); | |
padding-left: calc(20px - 4px); | |
background-color: #f9f9f9; | |
font-size: 16px; | |
line-height: 1.8; | |
} | |
/* Improved LaTeX styling */ | |
.step-section .mjx-chtml { | |
display: inline-block ; | |
line-height: 0; | |
text-indent: 0; | |
text-align: left; | |
text-transform: none; | |
font-size: 100%; | |
font-style: normal; | |
font-weight: normal; | |
font-family: MJXZERO, MJXTEX; | |
direction: ltr; | |
margin: 0 0.2em; | |
vertical-align: -0.25em; | |
} | |
.step-section .MathJax { | |
display: inline ; | |
font-size: 100% ; | |
text-align: left; | |
} | |
/* Fix for inline math to prevent weird line breaks */ | |
.step-section p { | |
display: block; | |
margin-block-start: 1em; | |
margin-block-end: 1em; | |
margin-inline-start: 0px; | |
margin-inline-end: 0px; | |
} | |
/* Improve display for block equations */ | |
.latex-display { | |
overflow-x: auto; | |
padding: 10px 0; | |
margin: 15px 0; | |
text-align: center; | |
display: block; | |
width: 100%; | |
} | |
.code-section { | |
background-color: transparent; | |
padding: 0; | |
border-left: none; | |
} | |
.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; | |
border-top-left-radius: 0px; | |
border-top-right-radius: 0px; | |
} | |
.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-left-radius: 0px; | |
border-bottom-right-radius: 0px; | |
} | |
.output-section { | |
background-color: var(--output-bg); | |
padding: 15px; | |
border-top: 1px solid #ddd; | |
color: #333; | |
font-family: 'Courier New', monospace; | |
font-size: 14px; | |
white-space: pre-wrap; | |
overflow-x: auto; | |
border-left: none; | |
border-bottom-left-radius: 0px; | |
border-bottom-right-radius: 0px; | |
} | |
/* Add border-radius back to the very first and very last child inside #solution */ | |
#solution > div:first-child, | |
#solution > div:first-child .code-header { | |
border-top-left-radius: 8px; | |
border-top-right-radius: 8px; | |
} | |
#solution > div:last-child, | |
#solution > div:last-child .code-content, | |
#solution > div:last-child.output-section { | |
border-bottom-left-radius: 8px; | |
border-bottom-right-radius: 8px; | |
} | |
#solution > .code-section:last-child .code-content { | |
border-bottom-left-radius: 8px; | |
border-bottom-right-radius: 8px; | |
} | |
#solution > .code-section:not(:last-child) .code-content { | |
border-bottom-left-radius: 0; | |
border-bottom-right-radius: 0; | |
} | |
/* Fixed display of MathJax content */ | |
.mjx-container { | |
display: inline-block ; | |
margin: 0 ; | |
text-align: center ; | |
overflow-x: auto; | |
overflow-y: hidden; | |
padding: 0 ; | |
} | |
.mjx-container.MJX-display { | |
display: block ; | |
margin: 1em auto ; | |
text-align: center ; | |
max-width: 100%; | |
} | |
/* Fix markdown list rendering */ | |
.step-section ul, .step-section ol { | |
display: block; | |
margin-block-start: 1em; | |
margin-block-end: 1em; | |
padding-inline-start: 30px; | |
margin-inline-start: 0px; | |
margin-inline-end: 0px; | |
} | |
.step-section ul li, .step-section ol li { | |
display: list-item; | |
margin-bottom: 0.5em; | |
} | |
.thinking-indicator, .executing-indicator, .answering-indicator { | |
display: flex; | |
align-items: center; | |
padding: 12px 15px; | |
margin: 10px 0; | |
border-radius: 8px; | |
font-size: 0.95rem; | |
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05); | |
} | |
.thinking-indicator { background-color: #e3f2fd; color: #1565c0; } | |
.executing-indicator { background-color: #ede7f6; color: #5e35b1; } | |
.answering-indicator { background-color: #e8f5e9; color: #2e7d32; } | |
.indicator-icon { | |
margin-right: 12px; | |
animation: pulse 1.5s infinite ease-in-out; | |
font-size: 1.1rem; | |
} | |
@keyframes pulse { | |
0% { opacity: 0.6; } | |
50% { opacity: 1; } | |
100% { opacity: 0.6; } | |
} | |
/* Fix assistive MML elements */ | |
mjx-assistive-mml { | |
position: absolute ; | |
top: 0 ; | |
left: 0 ; | |
clip: rect(1px, 1px, 1px, 1px) ; | |
padding: 1px 0 0 0 ; | |
display: block ; | |
} | |
/* Unified consistent LaTeX size for all equations */ | |
.step-section .MathJax { | |
font-size: 16px ; | |
} | |
.step-section .MathJax_Display { | |
text-align: center ; | |
margin: 1em 0 ; | |
display: block ; | |
overflow-x: auto; | |
overflow-y: hidden; | |
} | |
</style> | |
</head> | |
<body> | |
<div class="container"> | |
<header> | |
<div class="logo">Math Solver</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 class="feature-list"> | |
<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</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"> | |
<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> | |
</form> | |
<p id="uploadStatus"></p> | |
<div id="imagePreview" style="display: none; margin: 20px auto; max-width: 500px;"> | |
<img id="preview" style="width: 100%; border-radius: 8px; box-shadow: var(--box-shadow);"> | |
</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" style="margin-top: 30px; text-align: left; display: none;"> | |
<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> | |
<!-- Container for the dynamically added content blocks --> | |
<div id="solution"> | |
<!-- Content will be added here by JS --> | |
</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> | |
<script> | |
window.MathJax = { | |
tex: { | |
inlineMath: [[' | |
</script> | |
<script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-svg.js"></script> | |
<script> | |
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}`; | |
} | |
reader.readAsDataURL(file); | |
} | |
}); | |
document.getElementById('solveButton').addEventListener('click', function() { | |
const formData = new FormData(document.getElementById('imageForm')); | |
const solutionOutput = document.getElementById('solutionOutput'); | |
const loadingIndicator = document.getElementById('loadingIndicator'); | |
const solutionContainer = document.getElementById('solution'); | |
solutionOutput.style.display = 'block'; | |
loadingIndicator.style.display = 'flex'; | |
solutionContainer.innerHTML = ''; | |
fetch('/solved', { | |
method: 'POST', | |
body: formData | |
}) | |
.then(response => { | |
if (!response.ok) { | |
throw new Error(`Erreur serveur: ${response.statusText}`); | |
} | |
const reader = response.body.getReader(); | |
const decoder = new TextDecoder(); | |
let buffer = ''; | |
function processStream({ done, value }) { | |
if (done) { | |
loadingIndicator.style.display = 'none'; | |
// Force MathJax to reprocess entire solution when done | |
if (typeof MathJax !== 'undefined') { | |
MathJax.typesetPromise([solutionContainer]).catch(e => console.error('MathJax final typesetting error:', e)); | |
} | |
return; | |
} | |
buffer += decoder.decode(value, { stream: true }); | |
const lines = buffer.split(/\r?\n\r?\n/); | |
buffer = lines.pop(); | |
for (const line of lines) { | |
if (line.startsWith('data: ')) { | |
try { | |
const data = JSON.parse(line.substr(6)); | |
if (data.mode) { | |
const modes = { | |
thinking: { icon: 'fa-brain', text: 'Je réfléchis...', class: 'thinking-indicator' }, | |
answering: { icon: 'fa-pencil-alt', text: 'Rédaction...', class: 'answering-indicator' }, | |
executing_code: { icon: 'fa-code', text: 'Exécution...', class: 'executing-indicator' }, | |
code_result: { icon: 'fa-terminal', text: 'Résultats...', class: 'executing-indicator' } | |
}; | |
const modeInfo = modes[data.mode]; | |
if (modeInfo) { | |
loadingIndicator.className = modeInfo.class; | |
loadingIndicator.innerHTML = `<i class="fas ${modeInfo.icon} indicator-icon"></i><span>${modeInfo.text}</span>`; | |
} | |
} | |
if (data.content) { | |
const content = data.content; | |
const tempDiv = document.createElement('div'); | |
tempDiv.innerHTML = content; | |
const codeSection = tempDiv.querySelector('.code-section'); | |
const outputSection = tempDiv.querySelector('.output-section'); | |
if (codeSection) { | |
solutionContainer.appendChild(codeSection); | |
codeSection.querySelectorAll('pre code').forEach((block) => { | |
hljs.highlightElement(block); | |
}); | |
} else if (outputSection) { | |
solutionContainer.appendChild(outputSection); | |
} else { | |
// Process regular text/LaTeX step section | |
const stepDiv = document.createElement('div'); | |
stepDiv.className = 'step-section'; | |
// Fix markdown content before adding | |
let processedContent = content; | |
// Ensure proper paragraph breaks | |
processedContent = processedContent.replace(/\n\n/g, '</p><p>'); | |
// Wrap in paragraph tags if not already | |
if (!processedContent.startsWith('<p>')) { | |
processedContent = '<p>' + processedContent; | |
} | |
if (!processedContent.endsWith('</p>')) { | |
processedContent = processedContent + '</p>'; | |
} | |
// Fix lists in markdown | |
processedContent = processedContent.replace(/<p>(\s*[-*]\s+.*?)<\/p>/g, '<ul><li>$1</li></ul>'); | |
processedContent = processedContent.replace(/<p>(\s*\d+\.\s+.*?)<\/p>/g, '<ol><li>$1</li></ol>'); | |
stepDiv.innerHTML = processedContent; | |
solutionContainer.appendChild(stepDiv); | |
// Process MathJax for this section | |
if (typeof MathJax !== 'undefined') { | |
MathJax.typesetPromise([stepDiv]).catch(e => console.error('MathJax typesetting error:', e)); | |
} | |
} | |
} | |
if (data.error) { | |
const errorDiv = document.createElement('div'); | |
errorDiv.className = 'step-section'; | |
errorDiv.style.color = 'red'; | |
errorDiv.style.backgroundColor = '#ffeeee'; | |
errorDiv.style.borderColor = 'red'; | |
errorDiv.textContent = `Erreur: ${data.error}`; | |
solutionContainer.appendChild(errorDiv); | |
loadingIndicator.style.display = 'none'; | |
} | |
} catch (e) { | |
console.error('Error parsing JSON or processing chunk:', e, line); | |
const errorDiv = document.createElement('div'); | |
errorDiv.className = 'step-section'; | |
errorDiv.style.color = 'orange'; | |
errorDiv.textContent = `Erreur traitement chunk: ${line.substring(0, 100)}...`; | |
solutionContainer.appendChild(errorDiv); | |
} | |
} | |
} | |
window.scrollTo(0, document.body.scrollHeight); | |
return reader.read().then(processStream); | |
} | |
return reader.read().then(processStream); | |
}) | |
.catch(error => { | |
const errorDiv = document.createElement('div'); | |
errorDiv.className = 'step-section'; | |
errorDiv.style.color = 'red'; | |
errorDiv.style.backgroundColor = '#ffeeee'; | |
errorDiv.style.borderColor = 'red'; | |
errorDiv.textContent = `Erreur de connexion ou du serveur: ${error}`; | |
solutionContainer.appendChild(errorDiv); | |
loadingIndicator.style.display = 'none'; | |
console.error('Fetch error:', error); | |
}); | |
}); | |
// Helper function to ensure proper markdown formatting | |
function processMarkdown(content) { | |
// Process lists | |
content = content.replace(/^(\s*[-*]\s+.*?)$/gm, '<li>$1</li>'); | |
content = content.replace(/^(\s*\d+\.\s+.*?)$/gm, '<li>$1</li>'); | |
// Wrap adjacent list items in proper containers | |
content = content.replace(/<li>.*?<\/li>(\s*<li>.*?<\/li>)+/g, match => { | |
if (match.includes('*') || match.includes('-')) { | |
return '<ul>' + match + '</ul>'; | |
} else { | |
return '<ol>' + match + '</ol>'; | |
} | |
}); | |
return content; | |
} | |
</script> | |
</body> | |
</html>, ' | |
</script> | |
<script id="MathJax-script" async src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/3.2.2/es5/tex-svg.js"></script> | |
<script> | |
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}`; | |
} | |
reader.readAsDataURL(file); | |
} | |
}); | |
document.getElementById('solveButton').addEventListener('click', function() { | |
const formData = new FormData(document.getElementById('imageForm')); | |
const solutionOutput = document.getElementById('solutionOutput'); | |
const loadingIndicator = document.getElementById('loadingIndicator'); | |
const solutionContainer = document.getElementById('solution'); | |
solutionOutput.style.display = 'block'; | |
loadingIndicator.style.display = 'flex'; | |
solutionContainer.innerHTML = ''; | |
fetch('/solved', { | |
method: 'POST', | |
body: formData | |
}) | |
.then(response => { | |
if (!response.ok) { | |
throw new Error(`Erreur serveur: ${response.statusText}`); | |
} | |
const reader = response.body.getReader(); | |
const decoder = new TextDecoder(); | |
let buffer = ''; | |
function processStream({ done, value }) { | |
if (done) { | |
loadingIndicator.style.display = 'none'; | |
// Force MathJax to reprocess entire solution when done | |
if (typeof MathJax !== 'undefined') { | |
MathJax.typesetPromise([solutionContainer]).catch(e => console.error('MathJax final typesetting error:', e)); | |
} | |
return; | |
} | |
buffer += decoder.decode(value, { stream: true }); | |
const lines = buffer.split(/\r?\n\r?\n/); | |
buffer = lines.pop(); | |
for (const line of lines) { | |
if (line.startsWith('data: ')) { | |
try { | |
const data = JSON.parse(line.substr(6)); | |
if (data.mode) { | |
const modes = { | |
thinking: { icon: 'fa-brain', text: 'Je réfléchis...', class: 'thinking-indicator' }, | |
answering: { icon: 'fa-pencil-alt', text: 'Rédaction...', class: 'answering-indicator' }, | |
executing_code: { icon: 'fa-code', text: 'Exécution...', class: 'executing-indicator' }, | |
code_result: { icon: 'fa-terminal', text: 'Résultats...', class: 'executing-indicator' } | |
}; | |
const modeInfo = modes[data.mode]; | |
if (modeInfo) { | |
loadingIndicator.className = modeInfo.class; | |
loadingIndicator.innerHTML = `<i class="fas ${modeInfo.icon} indicator-icon"></i><span>${modeInfo.text}</span>`; | |
} | |
} | |
if (data.content) { | |
const content = data.content; | |
const tempDiv = document.createElement('div'); | |
tempDiv.innerHTML = content; | |
const codeSection = tempDiv.querySelector('.code-section'); | |
const outputSection = tempDiv.querySelector('.output-section'); | |
if (codeSection) { | |
solutionContainer.appendChild(codeSection); | |
codeSection.querySelectorAll('pre code').forEach((block) => { | |
hljs.highlightElement(block); | |
}); | |
} else if (outputSection) { | |
solutionContainer.appendChild(outputSection); | |
} else { | |
// Process regular text/LaTeX step section | |
const stepDiv = document.createElement('div'); | |
stepDiv.className = 'step-section'; | |
// Fix markdown content before adding | |
let processedContent = content; | |
// Ensure proper paragraph breaks | |
processedContent = processedContent.replace(/\n\n/g, '</p><p>'); | |
// Wrap in paragraph tags if not already | |
if (!processedContent.startsWith('<p>')) { | |
processedContent = '<p>' + processedContent; | |
} | |
if (!processedContent.endsWith('</p>')) { | |
processedContent = processedContent + '</p>'; | |
} | |
// Fix lists in markdown | |
processedContent = processedContent.replace(/<p>(\s*[-*]\s+.*?)<\/p>/g, '<ul><li>$1</li></ul>'); | |
processedContent = processedContent.replace(/<p>(\s*\d+\.\s+.*?)<\/p>/g, '<ol><li>$1</li></ol>'); | |
stepDiv.innerHTML = processedContent; | |
solutionContainer.appendChild(stepDiv); | |
// Process MathJax for this section | |
if (typeof MathJax !== 'undefined') { | |
MathJax.typesetPromise([stepDiv]).catch(e => console.error('MathJax typesetting error:', e)); | |
} | |
} | |
} | |
if (data.error) { | |
const errorDiv = document.createElement('div'); | |
errorDiv.className = 'step-section'; | |
errorDiv.style.color = 'red'; | |
errorDiv.style.backgroundColor = '#ffeeee'; | |
errorDiv.style.borderColor = 'red'; | |
errorDiv.textContent = `Erreur: ${data.error}`; | |
solutionContainer.appendChild(errorDiv); | |
loadingIndicator.style.display = 'none'; | |
} | |
} catch (e) { | |
console.error('Error parsing JSON or processing chunk:', e, line); | |
const errorDiv = document.createElement('div'); | |
errorDiv.className = 'step-section'; | |
errorDiv.style.color = 'orange'; | |
errorDiv.textContent = `Erreur traitement chunk: ${line.substring(0, 100)}...`; | |
solutionContainer.appendChild(errorDiv); | |
} | |
} | |
} | |
window.scrollTo(0, document.body.scrollHeight); | |
return reader.read().then(processStream); | |
} | |
return reader.read().then(processStream); | |
}) | |
.catch(error => { | |
const errorDiv = document.createElement('div'); | |
errorDiv.className = 'step-section'; | |
errorDiv.style.color = 'red'; | |
errorDiv.style.backgroundColor = '#ffeeee'; | |
errorDiv.style.borderColor = 'red'; | |
errorDiv.textContent = `Erreur de connexion ou du serveur: ${error}`; | |
solutionContainer.appendChild(errorDiv); | |
loadingIndicator.style.display = 'none'; | |
console.error('Fetch error:', error); | |
}); | |
}); | |
// Helper function to ensure proper markdown formatting | |
function processMarkdown(content) { | |
// Process lists | |
content = content.replace(/^(\s*[-*]\s+.*?)$/gm, '<li>$1</li>'); | |
content = content.replace(/^(\s*\d+\.\s+.*?)$/gm, '<li>$1</li>'); | |
// Wrap adjacent list items in proper containers | |
content = content.replace(/<li>.*?<\/li>(\s*<li>.*?<\/li>)+/g, match => { | |
if (match.includes('*') || match.includes('-')) { | |
return '<ul>' + match + '</ul>'; | |
} else { | |
return '<ol>' + match + '</ol>'; | |
} | |
}); | |
return content; | |
} | |
</script> | |
</body> | |
</html>], ['\\(', '\\)']], | |
displayMath: [['$', '$'], ['\\[', '\\]']], | |
processEscapes: true, | |
processEnvironments: true, | |
packages: {'[+]': ['noerrors', 'physics', 'cancel', 'color']} | |
}, | |
options: { | |
enableMenu: false, | |
ignoreHtmlClass: 'code-content', | |
processHtmlClass: 'step-section|latex-display', | |
skipHtmlTags: ['script', 'noscript', 'style', 'textarea', 'pre', 'code'] | |
}, | |
loader: { | |
load: ['input/tex-full', 'output/svg'] | |
}, | |
svg: { | |
fontCache: 'global', | |
scale: 1, // Global scaling factor for all expressions | |
minScale: 1, // Minimum scaling factor | |
mtextInheritFont: true, // True to make mtext elements use surrounding font | |
merrorInheritFont: true, // True to make merror text use surrounding font | |
mathmlSpacing: false // True for MathML spacing rules, false for TeX rules | |
}, | |
chtml: { | |
scale: 1, | |
minScale: 1, | |
mtextInheritFont: true, | |
merrorInheritFont: true | |
} | |
}; | |
</script> | |
<script id="MathJax-script" async src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/3.2.2/es5/tex-svg.js"></script> | |
<script> | |
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}`; | |
} | |
reader.readAsDataURL(file); | |
} | |
}); | |
document.getElementById('solveButton').addEventListener('click', function() { | |
const formData = new FormData(document.getElementById('imageForm')); | |
const solutionOutput = document.getElementById('solutionOutput'); | |
const loadingIndicator = document.getElementById('loadingIndicator'); | |
const solutionContainer = document.getElementById('solution'); | |
solutionOutput.style.display = 'block'; | |
loadingIndicator.style.display = 'flex'; | |
solutionContainer.innerHTML = ''; | |
fetch('/solved', { | |
method: 'POST', | |
body: formData | |
}) | |
.then(response => { | |
if (!response.ok) { | |
throw new Error(`Erreur serveur: ${response.statusText}`); | |
} | |
const reader = response.body.getReader(); | |
const decoder = new TextDecoder(); | |
let buffer = ''; | |
function processStream({ done, value }) { | |
if (done) { | |
loadingIndicator.style.display = 'none'; | |
// Force MathJax to reprocess entire solution when done | |
if (typeof MathJax !== 'undefined') { | |
MathJax.typesetPromise([solutionContainer]).catch(e => console.error('MathJax final typesetting error:', e)); | |
} | |
return; | |
} | |
buffer += decoder.decode(value, { stream: true }); | |
const lines = buffer.split(/\r?\n\r?\n/); | |
buffer = lines.pop(); | |
for (const line of lines) { | |
if (line.startsWith('data: ')) { | |
try { | |
const data = JSON.parse(line.substr(6)); | |
if (data.mode) { | |
const modes = { | |
thinking: { icon: 'fa-brain', text: 'Je réfléchis...', class: 'thinking-indicator' }, | |
answering: { icon: 'fa-pencil-alt', text: 'Rédaction...', class: 'answering-indicator' }, | |
executing_code: { icon: 'fa-code', text: 'Exécution...', class: 'executing-indicator' }, | |
code_result: { icon: 'fa-terminal', text: 'Résultats...', class: 'executing-indicator' } | |
}; | |
const modeInfo = modes[data.mode]; | |
if (modeInfo) { | |
loadingIndicator.className = modeInfo.class; | |
loadingIndicator.innerHTML = `<i class="fas ${modeInfo.icon} indicator-icon"></i><span>${modeInfo.text}</span>`; | |
} | |
} | |
if (data.content) { | |
const content = data.content; | |
const tempDiv = document.createElement('div'); | |
tempDiv.innerHTML = content; | |
const codeSection = tempDiv.querySelector('.code-section'); | |
const outputSection = tempDiv.querySelector('.output-section'); | |
if (codeSection) { | |
solutionContainer.appendChild(codeSection); | |
codeSection.querySelectorAll('pre code').forEach((block) => { | |
hljs.highlightElement(block); | |
}); | |
} else if (outputSection) { | |
solutionContainer.appendChild(outputSection); | |
} else { | |
// Process regular text/LaTeX step section | |
const stepDiv = document.createElement('div'); | |
stepDiv.className = 'step-section'; | |
// Fix markdown content before adding | |
let processedContent = content; | |
// Ensure proper paragraph breaks | |
processedContent = processedContent.replace(/\n\n/g, '</p><p>'); | |
// Wrap in paragraph tags if not already | |
if (!processedContent.startsWith('<p>')) { | |
processedContent = '<p>' + processedContent; | |
} | |
if (!processedContent.endsWith('</p>')) { | |
processedContent = processedContent + '</p>'; | |
} | |
// Fix lists in markdown | |
processedContent = processedContent.replace(/<p>(\s*[-*]\s+.*?)<\/p>/g, '<ul><li>$1</li></ul>'); | |
processedContent = processedContent.replace(/<p>(\s*\d+\.\s+.*?)<\/p>/g, '<ol><li>$1</li></ol>'); | |
stepDiv.innerHTML = processedContent; | |
solutionContainer.appendChild(stepDiv); | |
// Process MathJax for this section | |
if (typeof MathJax !== 'undefined') { | |
MathJax.typesetPromise([stepDiv]).catch(e => console.error('MathJax typesetting error:', e)); | |
} | |
} | |
} | |
if (data.error) { | |
const errorDiv = document.createElement('div'); | |
errorDiv.className = 'step-section'; | |
errorDiv.style.color = 'red'; | |
errorDiv.style.backgroundColor = '#ffeeee'; | |
errorDiv.style.borderColor = 'red'; | |
errorDiv.textContent = `Erreur: ${data.error}`; | |
solutionContainer.appendChild(errorDiv); | |
loadingIndicator.style.display = 'none'; | |
} | |
} catch (e) { | |
console.error('Error parsing JSON or processing chunk:', e, line); | |
const errorDiv = document.createElement('div'); | |
errorDiv.className = 'step-section'; | |
errorDiv.style.color = 'orange'; | |
errorDiv.textContent = `Erreur traitement chunk: ${line.substring(0, 100)}...`; | |
solutionContainer.appendChild(errorDiv); | |
} | |
} | |
} | |
window.scrollTo(0, document.body.scrollHeight); | |
return reader.read().then(processStream); | |
} | |
return reader.read().then(processStream); | |
}) | |
.catch(error => { | |
const errorDiv = document.createElement('div'); | |
errorDiv.className = 'step-section'; | |
errorDiv.style.color = 'red'; | |
errorDiv.style.backgroundColor = '#ffeeee'; | |
errorDiv.style.borderColor = 'red'; | |
errorDiv.textContent = `Erreur de connexion ou du serveur: ${error}`; | |
solutionContainer.appendChild(errorDiv); | |
loadingIndicator.style.display = 'none'; | |
console.error('Fetch error:', error); | |
}); | |
}); | |
// Helper function to ensure proper markdown formatting | |
function processMarkdown(content) { | |
// Process lists | |
content = content.replace(/^(\s*[-*]\s+.*?)$/gm, '<li>$1</li>'); | |
content = content.replace(/^(\s*\d+\.\s+.*?)$/gm, '<li>$1</li>'); | |
// Wrap adjacent list items in proper containers | |
content = content.replace(/<li>.*?<\/li>(\s*<li>.*?<\/li>)+/g, match => { | |
if (match.includes('*') || match.includes('-')) { | |
return '<ul>' + match + '</ul>'; | |
} else { | |
return '<ol>' + match + '</ol>'; | |
} | |
}); | |
return content; | |
} | |
</script> | |
</body> | |
</html> |