Spaces:
Running
Running
<html lang="fr"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>Résolution d'exercices avec Gemini</title> | |
<!-- Inclusion de MathJax pour le rendu LaTeX --> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.7/MathJax.js?config=TeX-MML-AM_CHTML"></script> | |
<!-- Inclusion de highlight.js pour la coloration syntaxique --> | |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/styles/default.min.css"> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/highlight.min.js"></script> | |
<style> | |
body { | |
font-family: Arial, sans-serif; | |
line-height: 1.6; | |
max-width: 800px; | |
margin: 0 auto; | |
padding: 20px; | |
} | |
h1, h2 { | |
color: #333; | |
} | |
.container { | |
margin-bottom: 30px; | |
} | |
.form-group { | |
margin-bottom: 15px; | |
} | |
label { | |
display: block; | |
margin-bottom: 5px; | |
font-weight: bold; | |
} | |
input[type="file"] { | |
margin-bottom: 10px; | |
} | |
button { | |
background-color: #4CAF50; | |
color: white; | |
padding: 10px 15px; | |
border: none; | |
border-radius: 4px; | |
cursor: pointer; | |
font-size: 16px; | |
} | |
button:hover { | |
background-color: #45a049; | |
} | |
#result-container { | |
margin-top: 30px; | |
border: 1px solid #ddd; | |
padding: 20px; | |
border-radius: 5px; | |
background-color: #f9f9f9; | |
} | |
.thinking-indicator { | |
color: #666; | |
font-style: italic; | |
margin-bottom: 10px; | |
} | |
.loading-indicator { | |
color: #666; | |
font-style: italic; | |
margin-bottom: 10px; | |
} | |
.error-message { | |
color: red; | |
font-weight: bold; | |
margin-top: 10px; | |
} | |
pre { | |
background-color: #f8f8f8; | |
border: 1px solid #ddd; | |
border-radius: 3px; | |
padding: 10px; | |
overflow-x: auto; | |
} | |
code { | |
font-family: 'Courier New', Courier, monospace; | |
} | |
.code-result { | |
background-color: #eaffea; | |
padding: 10px; | |
border-left: 3px solid #4CAF50; | |
margin-bottom: 20px; | |
} | |
.markdown-content { | |
margin-bottom: 15px; | |
} | |
img { | |
max-width: 100%; | |
height: auto; | |
margin: 10px 0; | |
border: 1px solid #ddd; | |
border-radius: 4px; | |
padding: 5px; | |
} | |
.buttons-container { | |
display: flex; | |
gap: 10px; | |
margin-top: 20px; | |
} | |
.tab-content { | |
display: none; | |
} | |
.tab-content.active { | |
display: block; | |
} | |
</style> | |
</head> | |
<body> | |
<h1>Résolution d'exercices avec Gemini</h1> | |
<div class="container"> | |
<h2>Mode Normal (Gemini Pro)</h2> | |
<form id="solve-form" enctype="multipart/form-data"> | |
<div class="form-group"> | |
<label for="image">Téléchargez une image de votre exercice:</label> | |
<input type="file" id="image" name="image" accept="image/*" required> | |
</div> | |
<button type="submit">Résoudre</button> | |
</form> | |
</div> | |
<div class="container"> | |
<h2>Mode Rapide (Gemini Flash)</h2> | |
<form id="solved-form" enctype="multipart/form-data"> | |
<div class="form-group"> | |
<label for="image2">Téléchargez une image de votre exercice:</label> | |
<input type="file" id="image2" name="image" accept="image/*" required> | |
</div> | |
<button type="submit">Résoudre rapidement</button> | |
</form> | |
</div> | |
<div id="result-container"> | |
<p>Les résultats apparaîtront ici après avoir soumis une image.</p> | |
</div> | |
<!-- Inclusion du script JS personnalisé --> | |
<script> | |
// Configuration de MathJax pour le rendu LaTeX | |
MathJax.Hub.Config({ | |
tex2jax: { | |
inlineMath: [['$', '$'], ['\\(', '\\)']], | |
displayMath: [['$$', '$$'], ['\\[', '\\]']], | |
processEscapes: true | |
} | |
}); | |
// Initialisation de highlight.js une fois que le DOM est chargé | |
document.addEventListener('DOMContentLoaded', function() { | |
document.querySelectorAll('pre code').forEach((block) => { | |
hljs.highlightBlock(block); | |
}); | |
}); | |
</script> | |
<script> | |
// Ce script doit être inclus dans votre template HTML | |
document.addEventListener('DOMContentLoaded', function() { | |
// Supposons que nous avons un élément pour afficher les résultats | |
const resultContainer = document.getElementById('result-container'); | |
// Fonction pour créer un élément de code formaté | |
function createCodeElement(code) { | |
const pre = document.createElement('pre'); | |
const codeEl = document.createElement('code'); | |
codeEl.textContent = code; | |
pre.appendChild(codeEl); | |
return pre; | |
} | |
// Fonction pour créer un élément Markdown | |
function createMarkdownElement(text) { | |
// Vous pourriez utiliser une bibliothèque comme marked.js ici | |
// Pour simplifier, nous utilisons simplement un paragraphe | |
const div = document.createElement('div'); | |
div.className = 'markdown-content'; | |
// Détection simple des éléments LaTeX | |
// Remplacez ceci par une bibliothèque complète comme MathJax dans votre implémentation réelle | |
text = text.replace(/\$\$(.*?)\$\$/g, '<span class="latex-block">$$$1$$</span>'); | |
text = text.replace(/\$(.*?)\$/g, '<span class="latex-inline">$$1$</span>'); | |
div.innerHTML = text; | |
return div; | |
} | |
// Fonction pour créer un élément image | |
function createImageElement(base64Data, format = 'png') { | |
const img = document.createElement('img'); | |
img.src = `data:image/${format};base64,${base64Data}`; | |
img.style.maxWidth = '100%'; | |
return img; | |
} | |
// Fonction pour gérer les événements SSE | |
function setupEventSource(url, formData) { | |
// Vider le conteneur de résultats | |
resultContainer.innerHTML = ''; | |
// Afficher un indicateur de chargement | |
const loadingIndicator = document.createElement('div'); | |
loadingIndicator.className = 'loading-indicator'; | |
loadingIndicator.textContent = 'Chargement en cours...'; | |
resultContainer.appendChild(loadingIndicator); | |
// Envoyer la requête avec fetch pour téléverser l'image | |
fetch(url, { | |
method: 'POST', | |
body: formData | |
}).then(response => { | |
// Supprimer l'indicateur de chargement | |
resultContainer.removeChild(loadingIndicator); | |
// Créer un nouvel EventSource avec les données de la réponse | |
const reader = response.body.getReader(); | |
function processText(text) { | |
const lines = text.split('\n\n'); | |
lines.forEach(line => { | |
if (line.startsWith('data: ')) { | |
try { | |
const jsonData = JSON.parse(line.substring(6)); | |
if (jsonData.mode === 'thinking') { | |
// Afficher un indicateur de réflexion | |
const thinkingIndicator = document.createElement('div'); | |
thinkingIndicator.className = 'thinking-indicator'; | |
thinkingIndicator.textContent = 'Gemini réfléchit...'; | |
resultContainer.appendChild(thinkingIndicator); | |
} else if (jsonData.mode === 'answering') { | |
// Supprimer l'indicateur de réflexion s'il existe | |
const thinkingIndicator = resultContainer.querySelector('.thinking-indicator'); | |
if (thinkingIndicator) { | |
resultContainer.removeChild(thinkingIndicator); | |
} | |
} | |
if (jsonData.content) { | |
let element; | |
switch(jsonData.type) { | |
case 'text': | |
element = createMarkdownElement(jsonData.content); | |
break; | |
case 'code': | |
element = createCodeElement(jsonData.content); | |
break; | |
case 'result': | |
element = createMarkdownElement(jsonData.content); | |
element.className = 'code-result'; | |
break; | |
case 'image': | |
element = createImageElement(jsonData.content); | |
break; | |
default: | |
// Si aucun type n'est spécifié, on traite comme du texte | |
element = createMarkdownElement(jsonData.content); | |
} | |
resultContainer.appendChild(element); | |
// Faire défiler vers le bas | |
window.scrollTo(0, document.body.scrollHeight); | |
} | |
if (jsonData.error) { | |
const errorElement = document.createElement('div'); | |
errorElement.className = 'error-message'; | |
errorElement.textContent = 'Erreur: ' + jsonData.error; | |
resultContainer.appendChild(errorElement); | |
} | |
} catch (e) { | |
console.error('Erreur lors du parsing JSON:', e); | |
} | |
} | |
}); | |
} | |
// Fonction pour lire les chunks de données | |
function readChunk() { | |
return reader.read().then(({ done, value }) => { | |
if (done) { | |
return; | |
} | |
// Convertir le chunk en texte | |
const chunk = new TextDecoder().decode(value); | |
processText(chunk); | |
// Lire le prochain chunk | |
return readChunk(); | |
}); | |
} | |
return readChunk(); | |
}).catch(error => { | |
console.error('Erreur:', error); | |
resultContainer.innerHTML = `<div class="error-message">Erreur de connexion: ${error.message}</div>`; | |
}); | |
} | |
// Gestion du formulaire pour /solve | |
const solveForm = document.getElementById('solve-form'); | |
if (solveForm) { | |
solveForm.addEventListener('submit', function(e) { | |
e.preventDefault(); | |
const formData = new FormData(solveForm); | |
setupEventSource('/solve', formData); | |
}); | |
} | |
// Gestion du formulaire pour /solved | |
const solvedForm = document.getElementById('solved-form'); | |
if (solvedForm) { | |
solvedForm.addEventListener('submit', function(e) { | |
e.preventDefault(); | |
const formData = new FormData(solvedForm); | |
setupEventSource('/solved', formData); | |
}); | |
} | |
}); | |
</script> | |
</body> | |
</html> |