Spaces:
Running
Running
<html lang="fr"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>Résolveur d'exercices</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/animate.css/4.1.1/animate.min.css" rel="stylesheet"> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/3.2.0/es5/tex-mml-chtml.js"></script> | |
<style> | |
:root { | |
--primary-color: #4361ee; | |
--secondary-color: #3f37c9; | |
--accent-color: #f72585; | |
--text-color: #333; | |
--light-color: #f8f9fa; | |
--dark-color: #212529; | |
--success-color: #4cc9a0; | |
--border-radius: 8px; | |
--box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1); | |
--transition: all 0.3s ease; | |
} | |
* { | |
margin: 0; | |
padding: 0; | |
box-sizing: border-box; | |
} | |
body { | |
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; | |
background: linear-gradient(135deg, #f5f7fa 0%, #e4e8f0 100%); | |
color: var(--text-color); | |
line-height: 1.6; | |
min-height: 100vh; | |
padding: 20px; | |
} | |
header { | |
text-align: center; | |
margin-bottom: 2rem; | |
animation: fadeIn 1s; | |
} | |
h1 { | |
color: var(--primary-color); | |
font-size: 2.2rem; | |
margin-bottom: 0.5rem; | |
} | |
.subheading { | |
color: var(--secondary-color); | |
font-size: 1.1rem; | |
font-weight: 500; | |
} | |
.container { | |
max-width: 800px; | |
margin: 0 auto; | |
background-color: white; | |
border-radius: var(--border-radius); | |
box-shadow: var(--box-shadow); | |
overflow: hidden; | |
} | |
.tabs { | |
display: flex; | |
background-color: var(--light-color); | |
border-bottom: 1px solid #e1e5ea; | |
} | |
.tab { | |
flex: 1; | |
padding: 15px; | |
text-align: center; | |
cursor: pointer; | |
transition: var(--transition); | |
font-weight: 600; | |
color: var(--text-color); | |
position: relative; | |
overflow: hidden; | |
} | |
.tab.active { | |
background-color: white; | |
color: var(--primary-color); | |
} | |
.tab:after { | |
content: ''; | |
position: absolute; | |
bottom: 0; | |
left: 0; | |
width: 100%; | |
height: 3px; | |
background-color: var(--primary-color); | |
transform: scaleX(0); | |
transition: transform 0.3s ease; | |
} | |
.tab.active:after { | |
transform: scaleX(1); | |
} | |
.tab-content { | |
padding: 30px; | |
display: none; | |
} | |
.tab-content.active { | |
display: block; | |
animation: fadeIn 0.5s; | |
} | |
.upload-container { | |
text-align: center; | |
padding: 20px; | |
border: 2px dashed #d1d5db; | |
border-radius: var(--border-radius); | |
margin-bottom: 20px; | |
transition: var(--transition); | |
cursor: pointer; | |
} | |
.upload-container:hover { | |
border-color: var(--primary-color); | |
background-color: rgba(67, 97, 238, 0.05); | |
} | |
.upload-icon { | |
font-size: 3rem; | |
color: var(--primary-color); | |
margin-bottom: 15px; | |
} | |
.upload-text { | |
margin-bottom: 15px; | |
font-size: 1.1rem; | |
} | |
.upload-input { | |
display: none; | |
} | |
.btn { | |
display: inline-block; | |
background-color: var(--primary-color); | |
color: white; | |
padding: 12px 25px; | |
border: none; | |
border-radius: var(--border-radius); | |
cursor: pointer; | |
font-size: 1rem; | |
font-weight: 600; | |
transition: var(--transition); | |
text-align: center; | |
text-decoration: none; | |
margin: 10px 0; | |
} | |
.btn:hover { | |
background-color: var(--secondary-color); | |
transform: translateY(-2px); | |
box-shadow: 0 5px 15px rgba(67, 97, 238, 0.3); | |
} | |
.btn-full { | |
width: 100%; | |
} | |
.btn-secondary { | |
background-color: var(--light-color); | |
color: var(--text-color); | |
} | |
.btn-secondary:hover { | |
background-color: #e2e6ea; | |
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1); | |
} | |
.btn-accent { | |
background-color: var(--accent-color); | |
} | |
.btn-accent:hover { | |
background-color: #e5147a; | |
box-shadow: 0 5px 15px rgba(247, 37, 133, 0.3); | |
} | |
.image-preview { | |
max-width: 100%; | |
height: auto; | |
margin: 20px 0; | |
border-radius: var(--border-radius); | |
box-shadow: var(--box-shadow); | |
display: none; | |
} | |
.result-container { | |
margin-top: 30px; | |
padding: 20px; | |
background-color: var(--light-color); | |
border-radius: var(--border-radius); | |
display: none; | |
} | |
.loading { | |
display: flex; | |
flex-direction: column; | |
align-items: center; | |
justify-content: center; | |
padding: 30px; | |
display: none; | |
} | |
.loading-text { | |
margin-top: 15px; | |
font-weight: 600; | |
color: var(--primary-color); | |
} | |
.thinking-indicator { | |
display: inline-block; | |
margin-left: 10px; | |
font-size: 1.5rem; | |
color: var(--primary-color); | |
animation: pulse 1.5s infinite; | |
} | |
@keyframes pulse { | |
0% { transform: scale(0.95); opacity: 0.7; } | |
50% { transform: scale(1.05); opacity: 1; } | |
100% { transform: scale(0.95); opacity: 0.7; } | |
} | |
@keyframes fadeIn { | |
from { opacity: 0; transform: translateY(10px); } | |
to { opacity: 1; transform: translateY(0); } | |
} | |
.features { | |
display: grid; | |
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); | |
gap: 20px; | |
margin: 30px 0; | |
} | |
.feature { | |
text-align: center; | |
padding: 20px; | |
background-color: white; | |
border-radius: var(--border-radius); | |
box-shadow: var(--box-shadow); | |
transition: var(--transition); | |
} | |
.feature:hover { | |
transform: translateY(-5px); | |
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1); | |
} | |
.feature-icon { | |
font-size: 2.5rem; | |
color: var(--primary-color); | |
margin-bottom: 15px; | |
} | |
.feature-title { | |
font-size: 1.2rem; | |
margin-bottom: 10px; | |
font-weight: 600; | |
} | |
.feature-desc { | |
color: #666; | |
font-size: 0.95rem; | |
} | |
footer { | |
text-align: center; | |
margin-top: 3rem; | |
color: #6c757d; | |
font-size: 0.9rem; | |
} | |
/* Mobile responsive styles */ | |
@media (max-width: 768px) { | |
body { | |
padding: 10px; | |
} | |
h1 { | |
font-size: 1.8rem; | |
} | |
.subheading { | |
font-size: 1rem; | |
} | |
.container { | |
border-radius: var(--border-radius); | |
} | |
.tab { | |
padding: 12px 5px; | |
font-size: 0.9rem; | |
} | |
.tab-content { | |
padding: 20px 15px; | |
} | |
.upload-icon { | |
font-size: 2.5rem; | |
} | |
.upload-text { | |
font-size: 1rem; | |
} | |
.btn { | |
padding: 10px 20px; | |
font-size: 0.95rem; | |
} | |
.features { | |
grid-template-columns: 1fr; | |
} | |
} | |
</style> | |
</head> | |
<body> | |
<header> | |
<h1>SolveMath.AI</h1> | |
<p class="subheading">Résolution avancée d'exercices par Intelligence Artificielle</p> | |
</header> | |
<div class="container"> | |
<div class="tabs"> | |
<div class="tab active" data-tab="standard">Standard</div> | |
<div class="tab" data-tab="pro">Pro (Réflexion)</div> | |
</div> | |
<div class="tab-content active" id="standard-content"> | |
<p>Utilisez notre IA standard pour résoudre rapidement vos exercices.</p> | |
<div class="upload-container" id="standard-upload"> | |
<i class="fas fa-cloud-upload-alt upload-icon"></i> | |
<p class="upload-text">Cliquez ou déposez une image de votre exercice</p> | |
<input type="file" id="standard-file" class="upload-input" accept="image/*"> | |
</div> | |
<img id="standard-preview" class="image-preview"> | |
<button id="standard-solve" class="btn btn-full" disabled>Résoudre l'exercice</button> | |
<div id="standard-loading" class="loading"> | |
<i class="fas fa-cog fa-spin fa-2x"></i> | |
<p class="loading-text">Analyse en cours... <span id="standard-thinking-status"></span></p> | |
</div> | |
<div id="standard-result" class="result-container"> | |
<h3>Solution :</h3> | |
<div id="standard-solution"></div> | |
</div> | |
</div> | |
<div class="tab-content" id="pro-content"> | |
<p>Mode Pro : utilise une réflexion approfondie pour résoudre des exercices complexes.</p> | |
<div class="upload-container" id="pro-upload"> | |
<i class="fas fa-cloud-upload-alt upload-icon"></i> | |
<p class="upload-text">Cliquez ou déposez une image de votre exercice complexe</p> | |
<input type="file" id="pro-file" class="upload-input" accept="image/*"> | |
</div> | |
<img id="pro-preview" class="image-preview"> | |
<button id="pro-solve" class="btn btn-full btn-accent" disabled>Résoudre avec réflexion approfondie</button> | |
<div id="pro-loading" class="loading"> | |
<i class="fas fa-brain fa-2x"></i> | |
<p class="loading-text">Analyse approfondie... <span id="pro-thinking-status"></span></p> | |
</div> | |
<div id="pro-result" class="result-container"> | |
<h3>Solution détaillée :</h3> | |
<div id="pro-solution"></div> | |
</div> | |
</div> | |
</div> | |
<div class="features"> | |
<div class="feature"> | |
<i class="fas fa-bolt feature-icon"></i> | |
<h3 class="feature-title">Rapide</h3> | |
<p class="feature-desc">Solutions instantanées pour les exercices simples</p> | |
</div> | |
<div class="feature"> | |
<i class="fas fa-brain feature-icon"></i> | |
<p class="feature-title">Intelligent</p> | |
<p class="feature-desc">Résolution détaillée de problèmes complexes</p> | |
</div> | |
<div class="feature"> | |
<i class="fas fa-mobile-alt feature-icon"></i> | |
<p class="feature-title">Mobile</p> | |
<p class="feature-desc">Fonctionne parfaitement sur tous les appareils</p> | |
</div> | |
</div> | |
<footer> | |
<p>© 2025 SolveMath.AI - Propulsé par l'intelligence artificielle</p> | |
</footer> | |
<script> | |
// Tab switching functionality | |
const tabs = document.querySelectorAll('.tab'); | |
const tabContents = document.querySelectorAll('.tab-content'); | |
tabs.forEach(tab => { | |
tab.addEventListener('click', () => { | |
const tabId = tab.getAttribute('data-tab'); | |
// Update active tab | |
tabs.forEach(t => t.classList.remove('active')); | |
tab.classList.add('active'); | |
// Update active content | |
tabContents.forEach(content => content.classList.remove('active')); | |
document.getElementById(`${tabId}-content`).classList.add('active'); | |
}); | |
}); | |
// File upload functionality - Standard mode | |
const standardUpload = document.getElementById('standard-upload'); | |
const standardFile = document.getElementById('standard-file'); | |
const standardPreview = document.getElementById('standard-preview'); | |
const standardSolve = document.getElementById('standard-solve'); | |
const standardLoading = document.getElementById('standard-loading'); | |
const standardResult = document.getElementById('standard-result'); | |
const standardSolution = document.getElementById('standard-solution'); | |
const standardThinkingStatus = document.getElementById('standard-thinking-status'); | |
standardUpload.addEventListener('click', () => standardFile.click()); | |
standardFile.addEventListener('change', (e) => { | |
if (e.target.files.length > 0) { | |
const file = e.target.files[0]; | |
const reader = new FileReader(); | |
reader.onload = (e) => { | |
standardPreview.src = e.target.result; | |
standardPreview.style.display = 'block'; | |
standardSolve.disabled = false; | |
}; | |
reader.readAsDataURL(file); | |
} | |
}); | |
// File upload functionality - Pro mode | |
const proUpload = document.getElementById('pro-upload'); | |
const proFile = document.getElementById('pro-file'); | |
const proPreview = document.getElementById('pro-preview'); | |
const proSolve = document.getElementById('pro-solve'); | |
const proLoading = document.getElementById('pro-loading'); | |
const proResult = document.getElementById('pro-result'); | |
const proSolution = document.getElementById('pro-solution'); | |
const proThinkingStatus = document.getElementById('pro-thinking-status'); | |
proUpload.addEventListener('click', () => proFile.click()); | |
proFile.addEventListener('change', (e) => { | |
if (e.target.files.length > 0) { | |
const file = e.target.files[0]; | |
const reader = new FileReader(); | |
reader.onload = (e) => { | |
proPreview.src = e.target.result; | |
proPreview.style.display = 'block'; | |
proSolve.disabled = false; | |
}; | |
reader.readAsDataURL(file); | |
} | |
}); | |
// Drag and drop functionality | |
['standard', 'pro'].forEach(mode => { | |
const uploadContainer = document.getElementById(`${mode}-upload`); | |
const fileInput = document.getElementById(`${mode}-file`); | |
const preview = document.getElementById(`${mode}-preview`); | |
const solveBtn = document.getElementById(`${mode}-solve`); | |
['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => { | |
uploadContainer.addEventListener(eventName, preventDefaults, false); | |
}); | |
function preventDefaults(e) { | |
e.preventDefault(); | |
e.stopPropagation(); | |
} | |
['dragenter', 'dragover'].forEach(eventName => { | |
uploadContainer.addEventListener(eventName, () => { | |
uploadContainer.classList.add('highlight'); | |
}, false); | |
}); | |
['dragleave', 'drop'].forEach(eventName => { | |
uploadContainer.addEventListener(eventName, () => { | |
uploadContainer.classList.remove('highlight'); | |
}, false); | |
}); | |
uploadContainer.addEventListener('drop', (e) => { | |
const file = e.dataTransfer.files[0]; | |
if (file && file.type.startsWith('image/')) { | |
fileInput.files = e.dataTransfer.files; | |
const reader = new FileReader(); | |
reader.onload = (e) => { | |
preview.src = e.target.result; | |
preview.style.display = 'block'; | |
solveBtn.disabled = false; | |
}; | |
reader.readAsDataURL(file); | |
} | |
}, false); | |
}); | |
// Solve functionality - Standard | |
standardSolve.addEventListener('click', () => { | |
if (!standardFile.files.length) return; | |
const formData = new FormData(); | |
formData.append('image', standardFile.files[0]); | |
standardSolve.disabled = true; | |
standardLoading.style.display = 'flex'; | |
standardResult.style.display = 'none'; | |
standardSolution.innerHTML = ''; | |
const eventSource = new EventSource('/solved?' + new URLSearchParams({ | |
timestamp: new Date().getTime() | |
})); | |
let accumulatedText = ''; | |
eventSource.addEventListener('message', function(event) { | |
const data = JSON.parse(event.data); | |
if (data.mode === 'thinking') { | |
standardThinkingStatus.innerHTML = '<span class="thinking-indicator">🤔</span>'; | |
} | |
if (data.mode === 'answering') { | |
standardThinkingStatus.innerHTML = ''; | |
} | |
if (data.content) { | |
accumulatedText += data.content; | |
standardSolution.innerHTML = accumulatedText; | |
standardResult.style.display = 'block'; | |
// Render LaTeX if present | |
if (typeof MathJax !== 'undefined') { | |
MathJax.typesetPromise([standardSolution]); | |
} | |
} | |
if (data.error) { | |
standardSolution.innerHTML = `<p class="error">Erreur: ${data.error}</p>`; | |
standardResult.style.display = 'block'; | |
eventSource.close(); | |
standardLoading.style.display = 'none'; | |
standardSolve.disabled = false; | |
} | |
}); | |
eventSource.addEventListener('error', function() { | |
eventSource.close(); | |
standardLoading.style.display = 'none'; | |
standardSolve.disabled = false; | |
if (!accumulatedText) { | |
standardSolution.innerHTML = '<p class="error">Une erreur est survenue lors de la communication avec le serveur.</p>'; | |
standardResult.style.display = 'block'; | |
} | |
}); | |
// Actual fetch to backend | |
fetch('/solved', { | |
method: 'POST', | |
body: formData | |
}).catch(error => { | |
console.error('Error:', error); | |
eventSource.close(); | |
standardLoading.style.display = 'none'; | |
standardSolve.disabled = false; | |
standardSolution.innerHTML = '<p class="error">Une erreur est survenue lors de l\'envoi de l\'image.</p>'; | |
standardResult.style.display = 'block'; | |
}); | |
}); | |
// Solve functionality - Pro | |
proSolve.addEventListener('click', () => { | |
if (!proFile.files.length) return; | |
const formData = new FormData(); | |
formData.append('image', proFile.files[0]); | |
proSolve.disabled = true; | |
proLoading.style.display = 'flex'; | |
proResult.style.display = 'none'; | |
proSolution.innerHTML = ''; | |
const eventSource = new EventSource('/solve?' + new URLSearchParams({ | |
timestamp: new Date().getTime() | |
})); | |
let accumulatedText = ''; | |
eventSource.addEventListener('message', function(event) { | |
const data = JSON.parse(event.data); | |
if (data.mode === 'thinking') { | |
proThinkingStatus.innerHTML = '<span class="thinking-indicator">💭</span>'; | |
} | |
if (data.mode === 'answering') { | |
proThinkingStatus.innerHTML = ''; | |
} | |
if (data.content) { | |
accumulatedText += data.content; | |
proSolution.innerHTML = accumulatedText; | |
proResult.style.display = 'block'; | |
// Render LaTeX if present | |
if (typeof MathJax !== 'undefined') { | |
MathJax.typesetPromise([proSolution]); | |
} | |
} | |
if (data.error) { | |
proSolution.innerHTML = `<p class="error">Erreur: ${data.error}</p>`; | |
proResult.style.display = 'block'; | |
eventSource.close(); | |
proLoading.style.display = 'none'; | |
proSolve.disabled = false; | |
} | |
}); | |
eventSource.addEventListener('error', function() { | |
eventSource.close(); | |
proLoading.style.display = 'none'; | |
proSolve.disabled = false; | |
if (!accumulatedText) { | |
proSolution.innerHTML = '<p class="error">Une erreur est survenue lors de la communication avec le serveur.</p>'; | |
proResult.style.display = 'block'; | |
} | |
}); | |
// Actual fetch to backend | |
fetch('/solve', { | |
method: 'POST', | |
body: formData | |
}).catch(error => { | |
console.error('Error:', error); | |
eventSource.close(); | |
proLoading.style.display = 'none'; | |
proSolve.disabled = false; | |
proSolution.innerHTML = '<p class="error">Une erreur est survenue lors de l\'envoi de l\'image.</p>'; | |
proResult.style.display = 'block'; | |
}); | |
}); | |
// Support for drag and drop highlight styling | |
document.querySelectorAll('.upload-container').forEach(container => { | |
container.addEventListener('dragenter', () => { | |
container.style.borderColor = 'var(--primary-color)'; | |
container.style.backgroundColor = 'rgba(67, 97, 238, 0.05)'; | |
}); | |
container.addEventListener('dragleave', () => { | |
container.style.borderColor = '#d1d5db'; | |
container.style.backgroundColor = ''; | |
}); | |
}); | |
</script> | |
</body> | |
</html> |