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> | |
<script src="https://cdn.tailwindcss.com"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/marked/4.0.2/marked.min.js"></script> | |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.1.1/css/all.min.css"> | |
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script> | |
<script src="https://polyfill.io/v3/polyfill.min.js?features=es6"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/animejs/3.2.1/anime.min.js"></script> | |
<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"></script> | |
<style> | |
.animated-button { | |
background-image: linear-gradient(to right, #4facfe, #00f2fe); | |
box-shadow: 0 4px 15px 0 rgba(65, 132, 234, 0.75); | |
transition: background-position 0.3s ease-in-out, transform 0.2s ease-in-out, box-shadow 0.3s ease-in-out; | |
background-size: 200% auto; | |
} | |
.animated-button:hover { | |
background-position: right center; | |
transform: translateY(-2px); | |
box-shadow: 0 6px 20px 0 rgba(65, 132, 234, 0.9); | |
} | |
.input-underline { | |
background-image: linear-gradient(to right, #4facfe, #00f2fe); | |
background-size: 0% 2px; | |
background-repeat: no-repeat; | |
background-position: left bottom; | |
transition: background-size 0.3s ease; | |
} | |
.input-underline:focus { | |
outline: none; | |
background-size: 100% 2px; | |
} | |
</style> | |
</head> | |
<body class="bg-gradient-to-r from-gray-100 to-gray-200 min-h-screen flex items-center justify-center font-sans overflow-x-hidden"> | |
<div class="container mx-auto p-8 bg-white rounded-3xl shadow-2xl max-w-3xl transform transition-all duration-500 ease-in-out hover:scale-102"> | |
<div class="flex justify-between items-center mb-6"> | |
<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 transition duration-300 flex items-center"> | |
<i class="fas fa-info-circle mr-2"></i>Info | |
</button> | |
</div> | |
<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 transition duration-200" | |
rows="4" placeholder="Entrez votre question ici..."></textarea> | |
</div> | |
<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 transition duration-200" | |
placeholder="Entrez une URL"> | |
<button onclick="addUrl()" | |
class="animated-button text-white px-4 py-2 rounded-r-lg transition duration-300">Ajouter</button> | |
</div> | |
<div id="urlList" class="mt-2"></div> | |
</div> | |
<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 transition duration-200" | |
multiple> | |
</div> | |
<button onclick="submitQuestion()" | |
class="animated-button w-full text-white px-4 py-3 rounded-lg transition duration-300 text-lg font-medium">Soumettre</button> | |
<div id="loader" class="hidden mt-4 text-center"> | |
<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> | |
<div id="response" class="mt-6 p-4 bg-gray-100 rounded-lg shadow-lg opacity-0 transform translate-y-4"></div> | |
<div id="copyResponseContainer" class="hidden mt-4"> | |
<button onclick="copyResponse()" | |
class="animated-button w-full text-white px-4 py-2 rounded-lg transition duration-300 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>La réponse sera formatée en Markdown et peut inclure des équations LaTeX.</p> | |
<p class="mb-2">Et si vous avez le courage de résoudre vos exos de math ici, veuillez signaler à Mariam de répondre en utilisant du 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) { | |
const urlItem = document.createElement('div'); | |
urlItem.className = 'url-item flex items-center mt-2 bg-gray-200 p-2 rounded-lg shadow-sm transform transition-all duration-300 ease-in-out hover:scale-105'; | |
urlItem.innerHTML = ` | |
<span class="flex-grow truncate">${url}</span> | |
<button onclick="removeUrl(this)" class="ml-2 text-red-500 hover:text-red-700 transition duration-300"> | |
<i class="fas fa-times"></i> | |
</button> | |
`; | |
urlList.appendChild(urlItem); | |
urlInput.value = ''; | |
anime({ | |
targets: urlItem, | |
opacity: [0, 1], | |
translateY: [-10, 0], | |
duration: 300, | |
easing: 'easeOutExpo' | |
}); | |
} | |
} | |
function removeUrl(button) { | |
const urlItem = button.parentElement; | |
anime({ | |
targets: urlItem, | |
opacity: [1, 0], | |
translateY: -10, | |
duration: 300, | |
easing: 'easeInExpo', | |
complete: function (anim) { | |
urlItem.remove(); | |
} | |
}); | |
} | |
function submitQuestion() { | |
const question = document.getElementById('questionInput').value; | |
if (question.trim()) { | |
const loader = document.getElementById('loader'); | |
const responseDiv = document.getElementById('response'); | |
const copyResponseContainer = document.getElementById('copyResponseContainer'); | |
loader.classList.remove('hidden'); | |
responseDiv.style.opacity = 0; | |
responseDiv.style.transform = 'translateY(40px)'; | |
copyResponseContainer.classList.add('hidden'); | |
const formData = new FormData(); | |
formData.append('question', question); | |
document.querySelectorAll('.url-item').forEach(item => { | |
formData.append('urls', item.querySelector('span').textContent); | |
}); | |
Array.from(document.getElementById('fileUpload').files).forEach(file => { | |
formData.append('files', file); | |
}); | |
fetch('/submit', { | |
method: 'POST', | |
body: formData | |
}) | |
.then(response => response.json()) | |
.then(data => { | |
loader.classList.add('hidden'); | |
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'); | |
MathJax.typesetPromise([responseDiv]).then(() => { | |
console.log('LaTeX rendu avec succès'); | |
anime({ | |
targets: responseDiv, | |
opacity: [0, 1], | |
translateY: [40, 0], | |
duration: 500, | |
easing: 'easeOutExpo' | |
}); | |
}).catch((err) => { | |
console.error('Erreur lors du rendu LaTeX:', err); | |
responseDiv.innerHTML = `<p class="text-red-600">Erreur lors du rendu LaTeX. Vérifiez votre syntaxe.</p>`; | |
anime({ | |
targets: responseDiv, | |
opacity: [0, 1], | |
translateY: [40, 0], | |
duration: 500, | |
easing: 'easeOutExpo' | |
}); | |
}); | |
} | |
}) | |
.catch(error => { | |
loader.classList.add('hidden'); | |
responseDiv.innerHTML = `<p class="text-red-600">Erreur : ${error.message}</p>`; | |
anime({ | |
targets: responseDiv, | |
opacity: [0, 1], | |
translateY: [40, 0], | |
duration: 500, | |
easing: 'easeOutExpo' | |
}); | |
}); | |
} | |
} | |
function copyResponse() { | |
const responseDiv = document.getElementById('response'); | |
const range = document.createRange(); | |
range.selectNode(responseDiv); | |
window.getSelection().removeAllRanges(); | |
window.getSelection().addRange(range); | |
document.execCommand('copy'); | |
window.getSelection().removeAllRanges(); | |
Swal.fire({ | |
icon: 'success', | |
title: 'Copié !', | |
text: 'La réponse a été copiée dans le presse-papiers.', | |
showConfirmButton: false, | |
timer: 1500 | |
}); | |
} | |
</script> | |
</body> | |
</html> |