Mariam-cards / templates /index.html
Docfile's picture
Update templates/index.html
1759e8b verified
raw
history blame
44.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 - 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 !important;
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 !important;
font-size: 100% !important;
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 !important;
margin: 0 !important;
text-align: center !important;
overflow-x: auto;
overflow-y: hidden;
padding: 0 !important;
}
.mjx-container.MJX-display {
display: block !important;
margin: 1em auto !important;
text-align: center !important;
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 !important;
top: 0 !important;
left: 0 !important;
clip: rect(1px, 1px, 1px, 1px) !important;
padding: 1px 0 0 0 !important;
display: block !important;
}
/* Unified consistent LaTeX size for all equations */
.step-section .MathJax {
font-size: 16px !important;
}
.step-section .MathJax_Display {
text-align: center !important;
margin: 1em 0 !important;
display: block !important;
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>