|
<!DOCTYPE html> |
|
<html lang="fr"> |
|
<head> |
|
<meta charset="UTF-8"> |
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
<title>Assistant IA</title> |
|
|
|
<script src="https://cdn.tailwindcss.com?plugins=forms,typography"></script> |
|
|
|
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script> |
|
<style> |
|
:root { |
|
--primary-blue: #1E40AF; |
|
--light-blue: #EFF6FF; |
|
--accent-blue: #3B82F6; |
|
--text-dark: #111827; |
|
--text-light: #4B5563; |
|
--bg-user: #2563EB; |
|
--bg-assistant: #FFFFFF; |
|
--border-color: #E5E7EB; |
|
} |
|
|
|
body { |
|
background: linear-gradient(135deg, var(--light-blue) 0%, #ffffff 100%); |
|
min-height: 100vh; |
|
font-family: 'Inter', sans-serif; |
|
} |
|
|
|
|
|
.glass-effect { |
|
background: rgba(255, 255, 255, 0.8); |
|
backdrop-filter: blur(12px) saturate(180%); |
|
-webkit-backdrop-filter: blur(12px) saturate(180%); |
|
border: 1px solid rgba(230, 230, 230, 0.3); |
|
box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.15); |
|
} |
|
|
|
|
|
.message-container { |
|
display: flex; |
|
margin-bottom: 1rem; |
|
opacity: 0; |
|
transform: translateY(10px); |
|
animation: fadeIn 0.5s ease forwards; |
|
} |
|
|
|
@keyframes fadeIn { |
|
to { |
|
opacity: 1; |
|
transform: translateY(0); |
|
} |
|
} |
|
|
|
.message-bubble { |
|
max-width: 75%; |
|
padding: 0.75rem 1rem; |
|
border-radius: 1rem; |
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05); |
|
word-wrap: break-word; |
|
overflow-wrap: break-word; |
|
} |
|
|
|
.message-container.user { |
|
justify-content: flex-end; |
|
} |
|
.message-container.assistant { |
|
justify-content: flex-start; |
|
} |
|
|
|
.message-bubble.user { |
|
background-color: var(--bg-user); |
|
color: white; |
|
border-bottom-right-radius: 0.25rem; |
|
} |
|
.message-bubble.assistant { |
|
background-color: var(--bg-assistant); |
|
color: var(--text-dark); |
|
border: 1px solid var(--border-color); |
|
border-bottom-left-radius: 0.25rem; |
|
} |
|
|
|
.message-bubble pre { |
|
background-color: #f3f4f6; |
|
color: #1f2937; |
|
padding: 1em; |
|
border-radius: 0.5rem; |
|
overflow-x: auto; |
|
font-family: 'Courier New', Courier, monospace; |
|
font-size: 0.9em; |
|
margin: 0.5em 0; |
|
} |
|
.message-bubble code:not(pre code) { |
|
background-color: #e5e7eb; |
|
padding: 0.2em 0.4em; |
|
border-radius: 0.25rem; |
|
font-size: 0.9em; |
|
} |
|
.message-bubble ul, .message-bubble ol { |
|
padding-left: 1.5rem; |
|
margin-top: 0.5rem; |
|
margin-bottom: 0.5rem; |
|
} |
|
.message-bubble li > p { |
|
margin-bottom: 0.25rem; |
|
} |
|
.message-bubble blockquote { |
|
border-left: 4px solid var(--accent-blue); |
|
padding-left: 1rem; |
|
margin-left: 0; |
|
color: var(--text-light); |
|
font-style: italic; |
|
} |
|
|
|
|
|
.quick-action { |
|
background: linear-gradient(135deg, var(--primary-blue) 0%, var(--accent-blue) 100%); |
|
transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1); |
|
color: white; |
|
font-weight: 500; |
|
} |
|
.quick-action:hover { |
|
transform: translateY(-3px) scale(1.03); |
|
box-shadow: 0 6px 15px rgba(59, 130, 246, 0.35); |
|
} |
|
.quick-action:active { |
|
transform: translateY(-1px) scale(0.98); |
|
box-shadow: 0 3px 8px rgba(59, 130, 246, 0.25); |
|
} |
|
.quick-action svg { |
|
transition: transform 0.2s ease-in-out; |
|
} |
|
.quick-action:hover svg { |
|
transform: rotate(-5deg) scale(1.1); |
|
} |
|
|
|
|
|
.custom-input { |
|
border: 2px solid var(--border-color); |
|
transition: all 0.3s ease; |
|
color: var(--text-dark); |
|
padding-right: 3rem; |
|
} |
|
.custom-input::placeholder { |
|
color: #9ca3af; |
|
} |
|
.custom-input:focus { |
|
border-color: var(--accent-blue); |
|
box-shadow: 0 0 0 4px rgba(59, 130, 246, 0.1); |
|
outline: none; |
|
} |
|
|
|
#sendButton { |
|
|
|
min-width: 80px; |
|
} |
|
|
|
|
|
@keyframes spin { |
|
0% { transform: rotate(0deg); } |
|
100% { transform: rotate(360deg); } |
|
} |
|
.loading-spinner { |
|
border: 4px solid rgba(59, 130, 246, 0.2); |
|
border-top-color: var(--accent-blue); |
|
animation: spin 1s linear infinite; |
|
} |
|
#loadingOverlay { |
|
transition: opacity 0.3s ease-in-out; |
|
} |
|
|
|
|
|
.file-upload-area { |
|
background: rgba(59, 130, 246, 0.05); |
|
border: 2px dashed var(--accent-blue); |
|
transition: all 0.3s ease; |
|
} |
|
.file-upload-area:hover { |
|
background: rgba(59, 130, 246, 0.1); |
|
border-color: var(--primary-blue); |
|
} |
|
#fileName { |
|
font-size: 0.9rem; |
|
font-style: italic; |
|
} |
|
|
|
|
|
#chatMessages::-webkit-scrollbar { |
|
width: 8px; |
|
} |
|
#chatMessages::-webkit-scrollbar-track { |
|
background: var(--light-blue); |
|
border-radius: 10px; |
|
} |
|
#chatMessages::-webkit-scrollbar-thumb { |
|
background-color: var(--accent-blue); |
|
border-radius: 10px; |
|
border: 2px solid var(--light-blue); |
|
} |
|
#chatMessages { |
|
scrollbar-width: thin; |
|
scrollbar-color: var(--accent-blue) var(--light-blue); |
|
} |
|
</style> |
|
</head> |
|
<body class="text-gray-800"> |
|
<div class="container mx-auto px-4 py-8 max-w-4xl"> |
|
|
|
|
|
<header class="glass-effect rounded-2xl p-4 md:p-6 mb-6 flex flex-col sm:flex-row justify-between items-center"> |
|
<div class="flex items-center space-x-3 mb-4 sm:mb-0"> |
|
<div class="w-12 h-12 rounded-full bg-gradient-to-br from-blue-600 to-blue-800 flex items-center justify-center shadow-md"> |
|
<svg class="w-7 h-7 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> |
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 10h.01M12 10h.01M16 10h.01M9 16H5a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v8a2 2 0 01-2 2h-5l-5 5v-5z"></path> |
|
</svg> |
|
</div> |
|
<div> |
|
<h1 class="text-xl md:text-2xl font-bold text-blue-900">Assistant IA</h1> |
|
<p class="text-sm text-green-600 font-medium">En ligne</p> |
|
</div> |
|
</div> |
|
<div class="flex items-center space-x-3"> |
|
<label class="flex items-center space-x-2 text-sm text-blue-900 cursor-pointer hover:text-blue-700"> |
|
<input type="checkbox" id="webSearchToggle" class="form-checkbox h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded"> |
|
<span>Recherche web</span> |
|
</label> |
|
<button id="clearChatButton" title="Effacer la conversation" class="bg-red-500 hover:bg-red-600 text-white px-3 py-2 rounded-lg transition-colors duration-200 text-sm font-medium flex items-center space-x-1"> |
|
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"></path></svg> |
|
<span>Effacer</span> |
|
</button> |
|
</div> |
|
</header> |
|
|
|
|
|
<div class="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-5 gap-3 mb-6"> |
|
|
|
<button data-prompt="Effectuez une recherche sur " class="quick-action p-3 rounded-xl flex flex-col sm:flex-row items-center justify-center space-y-1 sm:space-y-0 sm:space-x-2 text-xs sm:text-sm"> |
|
<svg class="w-5 h-5 mb-1 sm:mb-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"/></svg> |
|
<span>Recherche</span> |
|
</button> |
|
<button data-prompt="Donnez-moi des idées sur " class="quick-action p-3 rounded-xl flex flex-col sm:flex-row items-center justify-center space-y-1 sm:space-y-0 sm:space-x-2 text-xs sm:text-sm"> |
|
<svg class="w-5 h-5 mb-1 sm:mb-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z"/></svg> |
|
<span>Brainstorm</span> |
|
</button> |
|
<button data-prompt="Analysez les données suivantes : " class="quick-action p-3 rounded-xl flex flex-col sm:flex-row items-center justify-center space-y-1 sm:space-y-0 sm:space-x-2 text-xs sm:text-sm"> |
|
<svg class="w-5 h-5 mb-1 sm:mb-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"/></svg> |
|
<span>Analyse</span> |
|
</button> |
|
<button data-prompt="Créez une image de " class="quick-action p-3 rounded-xl flex flex-col sm:flex-row items-center justify-center space-y-1 sm:space-y-0 sm:space-x-2 text-xs sm:text-sm"> |
|
<svg class="w-5 h-5 mb-1 sm:mb-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z"/></svg> |
|
<span>Images</span> |
|
</button> |
|
<button data-prompt="Écrivez du code pour " class="quick-action p-3 rounded-xl flex flex-col sm:flex-row items-center justify-center space-y-1 sm:space-y-0 sm:space-x-2 text-xs sm:text-sm"> |
|
<svg class="w-5 h-5 mb-1 sm:mb-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4"/></svg> |
|
<span>Code</span> |
|
</button> |
|
</div> |
|
|
|
|
|
<div id="chatMessages" class="glass-effect rounded-2xl p-4 md:p-6 mb-6 h-[50vh] md:h-[60vh] overflow-y-auto space-y-4"> |
|
|
|
{% for message in messages %} |
|
<div class="message-container {{ message.role }}"> {# Use role for class #} |
|
<div class="message-bubble {{ message.role }}"> |
|
{# Use marked.parse and sanitize output #} |
|
{{ message.content | safe }} {# Assume backend sends sanitized HTML or use JS #} |
|
</div> |
|
</div> |
|
{% endfor %} |
|
|
|
</div> |
|
|
|
|
|
<div class="glass-effect rounded-2xl p-4 md:p-6"> |
|
|
|
<div class="mb-4"> |
|
<input type="file" id="fileUpload" class="hidden" accept=".jpg,.jpeg,.png,.gif,.webp,.heic,.heif,.pdf,.txt,.mp3,.wav,.ogg,.mp4,.mov,.avi"> |
|
<label for="fileUpload" class="file-upload-area p-4 rounded-xl flex flex-col items-center justify-center cursor-pointer hover:bg-blue-50 text-center"> |
|
<svg class="w-8 h-8 text-blue-500 mb-2" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> |
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12"></path> |
|
</svg> |
|
<span id="fileName" class="text-blue-600 text-sm">Cliquez ou déposez un fichier ici (Max 100Mo)</span> |
|
<span id="uploadStatus" class="text-xs text-gray-500 mt-1"></span> |
|
</label> |
|
</div> |
|
|
|
|
|
<div class="flex items-center space-x-3"> |
|
<div class="relative flex-grow"> |
|
<textarea id="messageInput" |
|
class="custom-input w-full rounded-xl px-4 py-3 pr-12 text-sm md:text-base resize-none border-gray-300 focus:border-blue-500 focus:ring focus:ring-blue-200 focus:ring-opacity-50" |
|
placeholder="Écrivez votre message... (Shift+Enter pour nouvelle ligne)" |
|
rows="1" |
|
style="min-height: 48px; max-height: 150px;" |
|
oninput="this.style.height = 'auto'; this.style.height = (this.scrollHeight) + 'px';" |
|
></textarea> |
|
</div> |
|
<button id="sendButton" title="Envoyer le message" class="quick-action px-4 py-3 rounded-xl flex items-center justify-center self-end transition-transform duration-150 ease-in-out"> |
|
<svg class="w-6 h-6 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 19l9 2-9-18-9 18 9-2zm0 0v-8"></path></svg> |
|
<span class="sr-only">Envoyer</span> |
|
</button> |
|
</div> |
|
</div> |
|
|
|
<footer class="text-center mt-6 text-xs text-gray-500"> |
|
Propulsé par Gemini | Conçu par Youssouf |
|
</footer> |
|
</div> |
|
|
|
|
|
<div id="loadingOverlay" class="fixed inset-0 bg-white bg-opacity-75 backdrop-filter backdrop-blur-sm hidden items-center justify-center z-50" style="display: none;"> |
|
<div class="loading-spinner w-16 h-16 rounded-full"></div> |
|
<span id="loadingText" class="ml-4 text-blue-800 font-medium">Chargement...</span> |
|
</div> |
|
|
|
<script> |
|
const messageInput = document.getElementById('messageInput'); |
|
const chatMessages = document.getElementById('chatMessages'); |
|
const webSearchToggle = document.getElementById('webSearchToggle'); |
|
const fileUpload = document.getElementById('fileUpload'); |
|
const fileNameLabel = document.getElementById('fileName'); |
|
const uploadStatusLabel = document.getElementById('uploadStatus'); |
|
const loadingOverlay = document.getElementById('loadingOverlay'); |
|
const loadingText = document.getElementById('loadingText'); |
|
const sendButton = document.getElementById('sendButton'); |
|
const clearChatButton = document.getElementById('clearChatButton'); |
|
|
|
let currentFileName = null; |
|
|
|
|
|
function showLoading(text = "Chargement...") { |
|
loadingText.textContent = text; |
|
loadingOverlay.style.display = 'flex'; |
|
loadingOverlay.style.opacity = 1; |
|
} |
|
|
|
function hideLoading() { |
|
|
|
loadingOverlay.style.opacity = 0; |
|
setTimeout(() => { |
|
loadingOverlay.style.display = 'none'; |
|
}, 300); |
|
} |
|
|
|
function sanitizeHTML(str) { |
|
|
|
const temp = document.createElement('div'); |
|
temp.textContent = str; |
|
return temp.innerHTML; |
|
} |
|
|
|
function addMessage(content, role = 'assistant') { |
|
const messageContainer = document.createElement('div'); |
|
messageContainer.className = `message-container ${role}`; |
|
|
|
const messageBubble = document.createElement('div'); |
|
messageBubble.className = `message-bubble ${role}`; |
|
|
|
|
|
|
|
if (typeof marked === 'undefined') { |
|
console.error("Marked.js library is not loaded."); |
|
messageBubble.textContent = content; |
|
} else { |
|
|
|
|
|
const rawHtml = marked.parse(content); |
|
|
|
|
|
|
|
messageBubble.innerHTML = rawHtml; |
|
} |
|
|
|
messageContainer.appendChild(messageBubble); |
|
chatMessages.appendChild(messageContainer); |
|
|
|
|
|
chatMessages.scrollTo({ top: chatMessages.scrollHeight, behavior: 'smooth' }); |
|
} |
|
|
|
|
|
sendButton.addEventListener('click', sendMessage); |
|
messageInput.addEventListener('keypress', (e) => { |
|
if (e.key === 'Enter' && !e.shiftKey) { |
|
e.preventDefault(); |
|
sendMessage(); |
|
} |
|
}); |
|
|
|
clearChatButton.addEventListener('click', clearChat); |
|
|
|
fileUpload.addEventListener('change', handleFileUpload); |
|
|
|
document.querySelectorAll('.quick-action[data-prompt]').forEach(button => { |
|
button.addEventListener('click', () => { |
|
const promptText = button.getAttribute('data-prompt'); |
|
messageInput.value = promptText; |
|
messageInput.focus(); |
|
|
|
messageInput.style.height = 'auto'; |
|
messageInput.style.height = (messageInput.scrollHeight) + 'px'; |
|
|
|
messageInput.setSelectionRange(promptText.length, promptText.length); |
|
}); |
|
}); |
|
|
|
|
|
|
|
async function sendMessage() { |
|
const messageText = messageInput.value.trim(); |
|
if (!messageText && !currentFileName) { |
|
console.log("Empty message, not sending."); |
|
return; |
|
} |
|
|
|
|
|
|
|
addMessage(sanitizeHTML(messageText), 'user'); |
|
const userMessageToSend = messageText; |
|
messageInput.value = ''; |
|
messageInput.style.height = '48px'; |
|
|
|
showLoading("Envoi en cours..."); |
|
|
|
try { |
|
const response = await fetch('/send_message', { |
|
method: 'POST', |
|
headers: { |
|
'Content-Type': 'application/json', |
|
'Accept': 'application/json' |
|
}, |
|
body: JSON.stringify({ |
|
message: userMessageToSend, |
|
web_search: webSearchToggle.checked |
|
|
|
}) |
|
}); |
|
|
|
hideLoading(); |
|
|
|
if (!response.ok) { |
|
|
|
let errorMsg = `Erreur HTTP: ${response.status}`; |
|
try { |
|
const errorData = await response.json(); |
|
errorMsg = `Erreur: ${errorData.error || response.statusText}`; |
|
} catch (jsonError) { |
|
|
|
console.warn("Could not parse error response as JSON:", jsonError); |
|
} |
|
addMessage(errorMsg, 'assistant'); |
|
console.error("Send message error:", errorMsg); |
|
return; |
|
} |
|
|
|
const data = await response.json(); |
|
|
|
if (data.error) { |
|
addMessage(`Erreur: ${sanitizeHTML(data.error)}`, 'assistant'); |
|
} else if (data.response) { |
|
addMessage(data.response, 'assistant'); |
|
|
|
|
|
|
|
} else { |
|
addMessage("Réponse inattendue du serveur.", 'assistant'); |
|
} |
|
|
|
} catch (error) { |
|
hideLoading(); |
|
console.error('Erreur lors de l\'envoi du message:', error); |
|
addMessage(`Erreur de connexion ou de traitement: ${sanitizeHTML(error.message)}`, 'assistant'); |
|
} |
|
} |
|
|
|
async function handleFileUpload(event) { |
|
const file = event.target.files[0]; |
|
if (!file) return; |
|
|
|
|
|
const maxSize = 100 * 1024 * 1024; |
|
if (file.size > maxSize) { |
|
uploadStatusLabel.textContent = `Fichier trop volumineux (Max ${maxSize / 1024 / 1024}Mo).`; |
|
uploadStatusLabel.style.color = 'red'; |
|
fileUpload.value = ''; |
|
return; |
|
} |
|
|
|
|
|
fileNameLabel.textContent = `Fichier sélectionné : ${file.name}`; |
|
uploadStatusLabel.textContent = 'Upload en cours...'; |
|
uploadStatusLabel.style.color = 'gray'; |
|
showLoading("Téléversement du fichier..."); |
|
|
|
const formData = new FormData(); |
|
formData.append('file', file); |
|
|
|
try { |
|
const response = await fetch('/upload', { |
|
method: 'POST', |
|
body: formData |
|
|
|
}); |
|
|
|
hideLoading(); |
|
const data = await response.json(); |
|
|
|
if (!response.ok || data.error) { |
|
const errorMsg = data.error || `Erreur HTTP ${response.status}`; |
|
uploadStatusLabel.textContent = `Échec: ${sanitizeHTML(errorMsg)}`; |
|
uploadStatusLabel.style.color = 'red'; |
|
fileNameLabel.textContent = 'Cliquez ou déposez un fichier ici (Max 100Mo)'; |
|
fileUpload.value = ''; |
|
currentFileName = null; |
|
console.error("File upload error:", errorMsg); |
|
} else if (data.success) { |
|
uploadStatusLabel.textContent = 'Prêt à être envoyé avec votre message.'; |
|
uploadStatusLabel.style.color = 'green'; |
|
currentFileName = data.filename; |
|
|
|
|
|
} else { |
|
uploadStatusLabel.textContent = 'Réponse serveur inattendue.'; |
|
uploadStatusLabel.style.color = 'red'; |
|
currentFileName = null; |
|
} |
|
|
|
} catch (error) { |
|
hideLoading(); |
|
console.error('Erreur lors de l\'upload:', error); |
|
uploadStatusLabel.textContent = `Erreur: ${sanitizeHTML(error.message)}`; |
|
uploadStatusLabel.style.color = 'red'; |
|
fileNameLabel.textContent = 'Cliquez ou déposez un fichier ici (Max 100Mo)'; |
|
fileUpload.value = ''; |
|
currentFileName = null; |
|
} |
|
} |
|
|
|
async function clearChat() { |
|
if (!confirm("Êtes-vous sûr de vouloir effacer toute la conversation et les fichiers uploadés ?")) { |
|
return; |
|
} |
|
showLoading("Effacement..."); |
|
try { |
|
const response = await fetch('/clear_chat', { method: 'POST' }); |
|
hideLoading(); |
|
if (!response.ok) { |
|
throw new Error(`Erreur serveur: ${response.status}`); |
|
} |
|
const data = await response.json(); |
|
if (data.success) { |
|
chatMessages.innerHTML = ''; |
|
resetFileUploadUI(); |
|
messageInput.value = ''; |
|
messageInput.style.height = '48px'; |
|
console.log("Chat cleared successfully."); |
|
|
|
|
|
} else { |
|
throw new Error("Le serveur n'a pas confirmé l'effacement."); |
|
} |
|
} catch (error) { |
|
hideLoading(); |
|
console.error('Erreur lors de l\'effacement:', error); |
|
|
|
alert(`Erreur lors de l'effacement : ${error.message}`); |
|
} |
|
} |
|
|
|
function resetFileUploadUI() { |
|
fileNameLabel.textContent = 'Cliquez ou déposez un fichier ici (Max 100Mo)'; |
|
uploadStatusLabel.textContent = ''; |
|
fileUpload.value = ''; |
|
currentFileName = null; |
|
} |
|
|
|
|
|
|
|
window.addEventListener('load', () => { |
|
chatMessages.scrollTop = chatMessages.scrollHeight; |
|
|
|
messageInput.style.height = 'auto'; |
|
messageInput.style.height = (messageInput.scrollHeight) + 'px'; |
|
messageInput.focus(); |
|
|
|
|
|
|
|
document.querySelectorAll('.message-bubble').forEach(bubble => { |
|
|
|
|
|
|
|
|
|
|
|
|
|
if (typeof marked !== 'undefined') { |
|
const currentContent = bubble.innerHTML; |
|
bubble.innerHTML = marked.parse(currentContent); |
|
} |
|
}); |
|
}); |
|
|
|
|
|
window.addEventListener('offline', () => addMessage('⚠️ Vous êtes hors ligne. Vérifiez votre connexion.', 'system')); |
|
window.addEventListener('online', () => addMessage('✅ Vous êtes de nouveau en ligne.', 'system')); |
|
|
|
</script> |
|
|
|
</body> |
|
</html> |