Spaces:
Sleeping
Sleeping
| <html lang="fr"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Mariam M-0</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/purify.min.js"></script> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github.min.css"> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script> | |
| <style> | |
| @keyframes gradient { | |
| 0% { background-position: 0% 50%; } | |
| 50% { background-position: 100% 50%; } | |
| 100% { background-position: 0% 50%; } | |
| } | |
| .gradient-bg { | |
| background: linear-gradient(-45deg, #ee7752, #e73c7e, #23a6d5, #23d5ab); | |
| background-size: 400% 400%; | |
| animation: gradient 15s ease infinite; | |
| } | |
| .markdown-content h1 { @apply text-2xl font-bold mb-4 mt-6; } | |
| .markdown-content h2 { @apply text-xl font-bold mb-3 mt-5; } | |
| .markdown-content h3 { @apply text-lg font-bold mb-2 mt-4; } | |
| .markdown-content p { @apply mb-4 leading-relaxed; } | |
| .markdown-content ul { @apply list-disc ml-6 mb-4; } | |
| .markdown-content ol { @apply list-decimal ml-6 mb-4; } | |
| .markdown-content li { @apply mb-1; } | |
| .markdown-content a { @apply text-blue-600 hover:text-blue-800 underline; } | |
| .markdown-content blockquote { @apply pl-4 border-l-4 border-gray-300 italic my-4; } | |
| .markdown-content code:not(pre code) { @apply bg-gray-100 px-1 rounded text-sm font-mono; } | |
| .markdown-content pre { @apply bg-gray-100 p-4 rounded-lg mb-4 overflow-x-auto; } | |
| .markdown-content table { @apply min-w-full border border-gray-300 mb-4; } | |
| .markdown-content th { @apply bg-gray-100 border-b border-gray-300 px-4 py-2 text-left; } | |
| .markdown-content td { @apply border-b border-gray-300 px-4 py-2; } | |
| .markdown-content img { @apply max-w-full h-auto my-4 rounded-lg; } | |
| </style> | |
| </head> | |
| <body class="min-h-screen bg-gray-50"> | |
| <div class="gradient-bg h-2 w-full fixed top-0"></div> | |
| <div class="max-w-4xl mx-auto px-4 py-8"> | |
| <header class="text-center mb-12"> | |
| <h1 class="text-4xl font-bold text-gray-800 mb-2">Mariam M-0</h1> | |
| <p class="text-gray-600">Votre assistant IA personnel</p> | |
| </header> | |
| <div class="bg-white rounded-xl shadow-lg p-6 mb-8"> | |
| <textarea | |
| id="question" | |
| class="w-full h-32 p-4 border border-gray-200 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent resize-none" | |
| placeholder="Posez votre question ici..." | |
| ></textarea> | |
| <button | |
| id="submitBtn" | |
| class="mt-4 px-6 py-3 bg-gradient-to-r from-blue-500 to-blue-600 text-white rounded-lg hover:from-blue-600 hover:to-blue-700 transition-all duration-200 flex items-center justify-center w-full sm:w-auto" | |
| > | |
| <span>Obtenir une réponse</span> | |
| <div id="spinner" class="hidden ml-3 animate-spin rounded-full h-5 w-5 border-b-2 border-white"></div> | |
| </button> | |
| </div> | |
| <div id="responseContainer" class="space-y-6"> | |
| <div id="answerBox" class="hidden bg-white rounded-xl shadow-lg p-6"> | |
| <h2 class="text-xl font-semibold text-gray-800 mb-4">Réponse</h2> | |
| <div id="answer" class="markdown-content text-gray-700"></div> | |
| </div> | |
| <div id="thinkingBox" class="hidden bg-white rounded-xl shadow-lg p-6"> | |
| <div class="flex items-center justify-between mb-4"> | |
| <h2 class="text-xl font-semibold text-gray-800">Raisonnement</h2> | |
| <button id="toggleThinking" class="text-blue-500 hover:text-blue-600"> | |
| Afficher | |
| </button> | |
| </div> | |
| <div id="thinking" class="hidden markdown-content text-gray-600"></div> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| // Configuration de marked | |
| marked.setOptions({ | |
| highlight: function(code, lang) { | |
| if (lang && hljs.getLanguage(lang)) { | |
| return hljs.highlight(code, { language: lang }).value; | |
| } | |
| return hljs.highlightAuto(code).value; | |
| }, | |
| breaks: true, | |
| gfm: true | |
| }); | |
| // Fonction pour rendre le Markdown de manière sécurisée | |
| function renderMarkdown(content) { | |
| const rawHtml = marked.parse(content); | |
| return DOMPurify.sanitize(rawHtml); | |
| } | |
| const submitBtn = document.getElementById('submitBtn'); | |
| const spinner = document.getElementById('spinner'); | |
| const answerBox = document.getElementById('answerBox'); | |
| const thinkingBox = document.getElementById('thinkingBox'); | |
| const answer = document.getElementById('answer'); | |
| const thinking = document.getElementById('thinking'); | |
| const toggleThinking = document.getElementById('toggleThinking'); | |
| toggleThinking.addEventListener('click', () => { | |
| const isHidden = thinking.classList.contains('hidden'); | |
| thinking.classList.toggle('hidden'); | |
| toggleThinking.textContent = isHidden ? 'Masquer' : 'Afficher'; | |
| }); | |
| submitBtn.addEventListener('click', async () => { | |
| const question = document.getElementById('question').value; | |
| if (!question.trim()) return; | |
| // Reset UI | |
| answer.innerHTML = ''; | |
| thinking.innerHTML = ''; | |
| submitBtn.disabled = true; | |
| spinner.classList.remove('hidden'); | |
| answerBox.classList.add('hidden'); | |
| thinkingBox.classList.add('hidden'); | |
| try { | |
| const response = await fetch('/ask', { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| }, | |
| body: JSON.stringify({ question }), | |
| }); | |
| const reader = response.body.getReader(); | |
| const decoder = new TextDecoder(); | |
| while (true) { | |
| const { done, value } = await reader.read(); | |
| if (done) break; | |
| const chunks = decoder.decode(value).split('\n'); | |
| chunks.forEach(chunk => { | |
| if (!chunk) return; | |
| const data = JSON.parse(chunk); | |
| if (data.type === 'thinking') { | |
| thinkingBox.classList.remove('hidden'); | |
| thinking.innerHTML = renderMarkdown(data.content); | |
| } else if (data.type === 'answer') { | |
| answerBox.classList.remove('hidden'); | |
| answer.innerHTML = renderMarkdown(data.content); | |
| // Mettre à jour la coloration syntaxique pour les nouveaux blocs de code | |
| answer.querySelectorAll('pre code').forEach((block) => { | |
| hljs.highlightBlock(block); | |
| }); | |
| } | |
| }); | |
| } | |
| } catch (error) { | |
| console.error('Error:', error); | |
| answer.innerHTML = renderMarkdown("❌ Une erreur est survenue. Veuillez réessayer."); | |
| } finally { | |
| submitBtn.disabled = false; | |
| spinner.classList.add('hidden'); | |
| } | |
| }); | |
| </script> | |
| </body> | |
| </html> |