File size: 7,488 Bytes
25ac863 d510348 25ac863 d510348 25ac863 d510348 25ac863 d510348 25ac863 d510348 25ac863 d510348 25ac863 a5874c3 25ac863 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 |
document.addEventListener('DOMContentLoaded', () => { const chatForm = document.getElementById('chat-form'); const promptInput = document.getElementById('prompt'); const chatMessages = document.getElementById('chat-messages'); const loadingIndicator = document.getElementById('loading-indicator'); const errorMessageDiv = document.getElementById('error-message'); const webSearchToggle = document.getElementById('web_search_toggle'); const fileUpload = document.getElementById('file_upload'); const fileNameSpan = document.getElementById('file-name'); const sendButton = document.getElementById('send-button'); // --- Fonctions Utilitaires --- function scrollToBottom() { // Ajoute un léger délai pour laisser le temps au DOM de se mettre à jour setTimeout(() => { chatMessages.scrollTop = chatMessages.scrollHeight; }, 50); } function showLoading(show) { loadingIndicator.style.display = show ? 'block' : 'none'; sendButton.disabled = show; promptInput.disabled = show; // Optionnel: changer l'apparence du bouton pendant le chargement sendButton.classList.toggle('opacity-50', show); sendButton.classList.toggle('cursor-not-allowed', show); } function displayError(message) { errorMessageDiv.textContent = message; errorMessageDiv.style.display = 'block'; // Cache le message après quelques secondes setTimeout(() => { errorMessageDiv.style.display = 'none'; }, 5000); } function addMessageToChat(role, text) { // Assainir le texte avant de l'insérer (très basique, pour éviter XSS simple) // Pour du Markdown, une bibliothèque comme 'marked' ou 'showdown' serait nécessaire côté client // ou s'assurer que le backend renvoie du HTML sûr. // Ici, on suppose que le backend renvoie du texte simple ou du HTML déjà aseptisé avec | safe const messageDiv = document.createElement('div'); messageDiv.classList.add('flex', role === 'user' ? 'justify-end' : 'justify-start'); const bubbleDiv = document.createElement('div'); bubbleDiv.classList.add('p-3', 'rounded-lg', 'max-w-xs', 'md:max-w-md', 'shadow'); if (role === 'user') { bubbleDiv.classList.add('bg-blue-500', 'text-white', 'rounded-br-none'); } else { // Pour l'assistant, créer une div interne pour le formatage prose si nécessaire bubbleDiv.classList.add('bg-gray-200', 'text-gray-800', 'rounded-bl-none'); const proseDiv = document.createElement('div'); proseDiv.classList.add('prose', 'prose-sm', 'max-w-none'); // IMPORTANT: Si le backend renvoie du HTML, il DOIT être sûr. // Sinon, utiliser textContent pour éviter l'injection de script. proseDiv.innerHTML = text; // Suppose que le HTML reçu est sûr (ex: via Markdown rendu côté serveur) // Si juste du texte : proseDiv.textContent = text; bubbleDiv.appendChild(proseDiv); } // Si c'est un message utilisateur simple (pas de HTML) if(role === 'user'){ const paragraph = document.createElement('p'); paragraph.classList.add('text-sm'); paragraph.textContent = text; // Utiliser textContent pour la sécurité bubbleDiv.appendChild(paragraph); } messageDiv.appendChild(bubbleDiv); chatMessages.appendChild(messageDiv); // Insérer l'indicateur *après* le dernier message pour qu'il soit en bas chatMessages.appendChild(loadingIndicator); scrollToBottom(); } // --- Gestionnaires d'événements --- // Afficher le nom du fichier sélectionné fileUpload.addEventListener('change', () => { if (fileUpload.files.length > 0) { fileNameSpan.textContent = fileUpload.files[0].name; fileNameSpan.title = fileUpload.files[0].name; // Pour le nom complet au survol } else { fileNameSpan.textContent = ''; fileNameSpan.title = ''; } }); // Soumission du formulaire via Fetch API chatForm.addEventListener('submit', async (e) => { e.preventDefault(); // Empêche le rechargement de la page const prompt = promptInput.value.trim(); const file = fileUpload.files[0]; const useWebSearch = webSearchToggle.checked; if (!prompt && !file) { displayError("Veuillez entrer un message ou sélectionner un fichier."); return; } // Cacher les erreurs précédentes errorMessageDiv.style.display = 'none'; // Afficher le message utilisateur immédiatement let userMessageText = prompt; if (file) { userMessageText = `[Fichier: ${file.name}]\n\n${prompt}`; } addMessageToChat('user', userMessageText); // Préparer les données du formulaire pour l'envoi (y compris le fichier) const formData = new FormData(); formData.append('prompt', prompt); formData.append('web_search', useWebSearch); if (file) { formData.append('file', file); } // Afficher le chargement et désactiver les entrées showLoading(true); promptInput.value = ''; // Vider l'input après l'envoi fileUpload.value = ''; // Réinitialiser l'input fichier fileNameSpan.textContent = ''; // Vider le nom du fichier affiché try { const response = await fetch("{{ url_for('chat_api') }}", { // Appel vers la nouvelle route API method: 'POST', body: formData, // Pas besoin de 'Content-Type', le navigateur le définit pour FormData }); if (!response.ok) { // Essayer de lire l'erreur JSON si possible let errorMsg = `Erreur HTTP: ${response.status}`; try { const errorData = await response.json(); errorMsg = errorData.error || errorMsg; } catch (jsonError) { // Ignorer si la réponse n'est pas du JSON } throw new Error(errorMsg); } const data = await response.json(); if (data.success && data.message) { // IMPORTANT: S'assurer que data.message est du HTML sûr ou l'assainir ici addMessageToChat('assistant', data.message); } else if (data.error) { displayError(data.error); // Optionnel: supprimer le message utilisateur si l'IA n'a pas pu répondre // (peut être déroutant pour l'utilisateur) } } catch (error) { console.error("Erreur lors de l'envoi du message:", error); displayError(error.message || "Une erreur inconnue est survenue."); // Optionnel: supprimer le message utilisateur en cas d'erreur réseau grave } finally { // Cacher le chargement et réactiver les entrées showLoading(false); promptInput.focus(); // Remettre le focus sur l'input } }); // Scroll initial vers le bas au chargement de la page scrollToBottom(); promptInput.focus(); // Focus sur l'input au chargement }); |