Spaces:
Sleeping
Sleeping
<html lang="fr"> | |
<head> | |
<meta charset="UTF-8" /> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> | |
<title>Question Générale</title> | |
<!-- Tailwind CSS --> | |
<script src="https://cdn.tailwindcss.com"></script> | |
<!-- Marked (Markdown) --> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/marked/4.0.2/marked.min.js" defer></script> | |
<!-- Font Awesome --> | |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.1.1/css/all.min.css" /> | |
<!-- SweetAlert2 --> | |
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11" defer></script> | |
<!-- Polyfill --> | |
<script src="https://polyfill.io/v3/polyfill.min.js?features=es6" defer></script> | |
<!-- Anime.js --> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/animejs/3.2.1/anime.min.js" defer></script> | |
<!-- MathJax --> | |
<script> | |
MathJax = { | |
tex: { | |
inlineMath: [['$', '$'], ['\\(', '\\)']], | |
displayMath: [['$$', '$$'], ['\\[', '\\]']], | |
processEscapes: true | |
}, | |
options: { | |
skipHtmlTags: ['script', 'noscript', 'style', 'textarea', 'pre'] | |
}, | |
startup: { | |
pageReady: () => { | |
return MathJax.startup.defaultPageReady().then(() => { | |
console.log('MathJax initial typesetting complete'); | |
}); | |
} | |
} | |
}; | |
</script> | |
<script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js" defer></script> | |
<style> | |
/* Bouton animé avec un dégradé pastel */ | |
.animated-button { | |
background-image: linear-gradient(to right, #a1c4fd, #c2e9fb); | |
background-size: 200% auto; | |
box-shadow: 0 4px 15px rgba(161, 196, 253, 0.5); | |
transition: background-position 0.3s ease, transform 0.2s ease, box-shadow 0.3s ease; | |
} | |
.animated-button:hover { | |
background-position: right center; | |
transform: translateY(-2px); | |
box-shadow: 0 6px 20px rgba(161, 196, 253, 0.7); | |
} | |
/* Effet de soulignement sur les champs de saisie */ | |
.input-underline { | |
background-image: linear-gradient(to right, #a1c4fd, #c2e9fb); | |
background-size: 0% 2px; | |
background-repeat: no-repeat; | |
background-position: left bottom; | |
transition: background-size 0.3s ease; | |
} | |
.input-underline:focus { | |
background-size: 100% 2px; | |
outline: none; | |
} | |
/* Conteneur de réponse */ | |
#response { | |
overflow-x: auto; | |
white-space: pre-wrap; | |
} | |
</style> | |
</head> | |
<body class="bg-gradient-to-r from-gray-100 to-gray-200 min-h-screen flex items-center justify-center font-sans"> | |
<div class="container mx-auto p-8 bg-white rounded-3xl shadow-2xl max-w-3xl"> | |
<!-- En-tête --> | |
<div class="flex flex-col md:flex-row md:justify-between items-start md:items-center mb-6 space-y-4 md:space-y-0"> | |
<h1 class="text-3xl font-bold text-gray-800">Poser une question générale</h1> | |
<button onclick="showInfo()" class="animated-button text-white px-4 py-2 rounded-lg flex items-center"> | |
<i class="fas fa-info-circle mr-2"></i> Info | |
</button> | |
</div> | |
<!-- Zone de question --> | |
<div class="mb-6"> | |
<label for="questionInput" class="block mb-2 text-lg font-medium text-gray-700">Votre question :</label> | |
<textarea id="questionInput" class="w-full p-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-400 input-underline" rows="4" placeholder="Entrez votre question ici..."></textarea> | |
</div> | |
<!-- Zone d'ajout d'URLs --> | |
<div class="mb-6"> | |
<label for="urlInput" class="block mb-2 text-lg font-medium text-gray-700">URLs (optionnel) :</label> | |
<div class="flex"> | |
<input type="text" id="urlInput" class="flex-grow p-3 border border-gray-300 rounded-l-lg focus:ring-2 focus:ring-blue-400 input-underline" placeholder="Entrez une URL" /> | |
<button onclick="addUrl()" class="animated-button text-white px-4 py-2 rounded-r-lg"> | |
Ajouter | |
</button> | |
</div> | |
<div id="urlList" class="mt-2 space-y-2"></div> | |
</div> | |
<!-- Zone d'ajout de fichiers --> | |
<div class="mb-6"> | |
<label for="fileUpload" class="block mb-2 text-lg font-medium text-gray-700">Fichiers (optionnel) :</label> | |
<input type="file" id="fileUpload" class="w-full p-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-400 input-underline" multiple /> | |
</div> | |
<!-- Bouton de soumission --> | |
<button onclick="submitQuestion()" class="animated-button w-full text-white px-4 py-3 rounded-lg text-lg font-medium"> | |
Soumettre | |
</button> | |
<!-- Loader --> | |
<div id="loader" class="hidden mt-4"> | |
<div class="flex justify-center items-center"> | |
<div class="animate-spin rounded-full h-12 w-12 border-t-4 border-b-4 border-blue-500"></div> | |
<p class="ml-2 text-gray-600">Chargement en cours...</p> | |
</div> | |
</div> | |
<!-- Affichage de la réponse --> | |
<div id="response" class="mt-6 p-4 bg-gray-100 rounded-lg shadow-lg"></div> | |
<!-- Bouton pour copier la réponse --> | |
<div id="copyResponseContainer" class="hidden mt-4"> | |
<button onclick="copyResponse()" class="animated-button w-full text-white px-4 py-2 rounded-lg flex items-center justify-center"> | |
<i class="fas fa-copy mr-2"></i> Copier la réponse | |
</button> | |
</div> | |
</div> | |
<script> | |
function showInfo() { | |
Swal.fire({ | |
title: 'Information', | |
html: ` | |
<p class="mb-2">Ce formulaire vous permet de poser des questions générales.</p> | |
<p class="mb-2">Vous pouvez également ajouter des URLs et des fichiers pour fournir plus de contexte à votre question.</p> | |
<p class="mb-2">La réponse sera formatée en Markdown et peut inclure des équations LaTeX.</p> | |
<p>Si vous souhaitez résoudre vos exercices de mathématiques ici, signalez-le à Mariam pour une réponse en LaTeX.</p> | |
`, | |
icon: 'info', | |
confirmButtonText: 'Compris' | |
}); | |
} | |
function addUrl() { | |
const urlInput = document.getElementById('urlInput'); | |
const urlList = document.getElementById('urlList'); | |
const url = urlInput.value.trim(); | |
if (url) { | |
// Création d'un élément URL avec la classe "url-item" | |
const urlItem = document.createElement('div'); | |
urlItem.className = 'url-item flex items-center bg-gray-200 p-2 rounded-lg shadow-sm'; | |
urlItem.innerHTML = ` | |
<span class="flex-grow truncate">${url}</span> | |
<button onclick="removeUrl(this)" class="ml-2 text-red-500 hover:text-red-700"> | |
<i class="fas fa-times"></i> | |
</button> | |
`; | |
urlList.appendChild(urlItem); | |
urlInput.value = ''; | |
} | |
} | |
function removeUrl(button) { | |
const urlItem = button.closest('.url-item'); | |
if(urlItem) urlItem.remove(); | |
} | |
async function submitQuestion() { | |
const question = document.getElementById('questionInput').value.trim(); | |
if (!question) return; | |
const loader = document.getElementById('loader'); | |
const responseDiv = document.getElementById('response'); | |
const copyResponseContainer = document.getElementById('copyResponseContainer'); | |
loader.classList.remove('hidden'); | |
responseDiv.innerHTML = ''; | |
responseDiv.classList.add('opacity-0'); | |
copyResponseContainer.classList.add('hidden'); | |
const formData = new FormData(); | |
formData.append('question', question); | |
// Récupération des URLs | |
document.querySelectorAll('#urlList .url-item').forEach(item => { | |
formData.append('urls', item.querySelector('span').textContent); | |
}); | |
// Récupération des fichiers | |
Array.from(document.getElementById('fileUpload').files).forEach(file => { | |
formData.append('files', file); | |
}); | |
try { | |
const response = await fetch('/submit', { | |
method: 'POST', | |
body: formData | |
}); | |
const data = await response.json(); | |
loader.classList.add('hidden'); | |
responseDiv.classList.remove('opacity-0'); | |
if (data.error) { | |
responseDiv.innerHTML = `<p class="text-red-600">Erreur : ${data.error}</p>`; | |
} else { | |
const htmlContent = marked.parse(data.response); | |
responseDiv.innerHTML = htmlContent; | |
copyResponseContainer.classList.remove('hidden'); | |
// Rendu de LaTeX avec MathJax | |
await MathJax.typesetPromise([responseDiv]); | |
console.log('LaTeX rendu avec succès'); | |
} | |
} catch (error) { | |
loader.classList.add('hidden'); | |
responseDiv.innerHTML = `<p class="text-red-600">Erreur : ${error.message}</p>`; | |
responseDiv.classList.remove('opacity-0'); | |
} | |
} | |
function copyResponse() { | |
const responseDiv = document.getElementById('response'); | |
const text = responseDiv.innerText; | |
navigator.clipboard.writeText(text).then(() => { | |
Swal.fire({ | |
icon: 'success', | |
title: 'Copié !', | |
text: 'La réponse a été copiée dans le presse-papiers.', | |
showConfirmButton: false, | |
timer: 1500 | |
}); | |
}).catch(err => { | |
console.error('Erreur lors de la copie:', err); | |
}); | |
} | |
</script> | |
</body> | |
</html> | |