document.addEventListener('DOMContentLoaded', function() {
// DOM Elements
const chatWindow = document.getElementById('chatWindow');
const userInput = document.getElementById('userInput');
const sendButton = document.getElementById('sendButton');
const clearButton = document.getElementById('clearButton');
const clearButtonNav = document.getElementById('clearButtonNav');
const imageInput = document.getElementById('imageInput');
const uploadImageButton = document.getElementById('uploadImageButton');
const imagePreviewArea = document.getElementById('imagePreviewArea');
const imagePreviewContainer = document.getElementById('imagePreviewContainer');
const chatHistoryList = document.getElementById('chatHistoryList');
const saveCurrentChatButton = document.getElementById('saveCurrentChatButton');
const settingsButton = document.getElementById('settingsButtonNav');
const toggleNavButton = document.getElementById('toggleNavButton');
const closeSideNavButton = document.getElementById('closeSideNavButton');
const sideNav = document.getElementById('sideNav');
const mainContent = document.querySelector('.main-content');
const welcomeContainer = document.querySelector('.welcome-container');
const suggestionBubbles = document.querySelectorAll('.suggestion-bubble');
// Templates
const loadingTemplate = document.getElementById('loadingTemplate');
const userMessageTemplate = document.getElementById('userMessageTemplate');
const userImageMessageTemplate = document.getElementById('userImageMessageTemplate');
const botMessageTemplate = document.getElementById('botMessageTemplate');
const errorMessageTemplate = document.getElementById('errorMessageTemplate');
// State variables
let chatHistory = [];
let selectedImagesData = []; // Pour stocker plusieurs images
let conversationStarted = false;
// --- IMAGE HANDLING ---
// Upload image button click
uploadImageButton.addEventListener('click', function() {
imageInput.click();
});
// Image input change - support for multiple images
imageInput.addEventListener('change', function(e) {
const files = Array.from(e.target.files);
if (!files.length) return;
// Clear previous image container
imagePreviewContainer.innerHTML = '';
selectedImagesData = [];
// Process each file
files.forEach(file => {
// Check file type
if (!file.type.startsWith('image/')) {
alert('Veuillez sélectionner uniquement des fichiers image valides.');
return;
}
// Check file size (max 5MB)
if (file.size > 5 * 1024 * 1024) {
alert('La taille de chaque image ne doit pas dépasser 5 Mo.');
return;
}
const reader = new FileReader();
reader.onload = function(event) {
// Create preview container
const previewContainer = document.createElement('div');
previewContainer.className = 'image-preview-container';
// Create image preview
const imgPreview = document.createElement('img');
imgPreview.src = event.target.result;
imgPreview.alt = 'Image preview';
previewContainer.appendChild(imgPreview);
// Create remove button
const removeBtn = document.createElement('button');
removeBtn.className = 'remove-image-button';
removeBtn.innerHTML = '';
previewContainer.appendChild(removeBtn);
// Add preview to container
imagePreviewContainer.appendChild(previewContainer);
// Store image data
const imageData = event.target.result;
selectedImagesData.push(imageData);
// Add event listener to remove button
removeBtn.addEventListener('click', () => {
// Remove this specific image
const index = selectedImagesData.indexOf(imageData);
if (index > -1) {
selectedImagesData.splice(index, 1);
}
previewContainer.remove();
// Hide preview area if no images left
if (selectedImagesData.length === 0) {
imagePreviewArea.classList.add('hidden');
imageInput.value = '';
}
});
};
reader.readAsDataURL(file);
});
// Show preview area
imagePreviewArea.classList.remove('hidden');
// Focus on input for caption
userInput.focus();
});
// --- SIDE NAVIGATION HANDLING ---
// Toggle side navigation
toggleNavButton.addEventListener('click', function() {
sideNav.classList.add('active');
document.body.insertAdjacentHTML('beforeend', '
');
const overlay = document.getElementById('navOverlay');
overlay.classList.add('active');
loadChatHistoryList();
// Close side nav when clicking overlay
overlay.addEventListener('click', closeSideNav);
});
// Close side navigation
closeSideNavButton.addEventListener('click', closeSideNav);
function closeSideNav() {
sideNav.classList.remove('active');
const overlay = document.getElementById('navOverlay');
if (overlay) {
overlay.remove();
}
}
// Clear chat from nav button
if (clearButtonNav) {
clearButtonNav.addEventListener('click', function() {
clearChat(true); // With confirmation
closeSideNav();
});
}
// Settings button in side nav
if (settingsButtonNav) {
settingsButtonNav.addEventListener('click', function() {
alert('Fonctionnalité en cours de développement');
closeSideNav();
});
}
// Load chat history list
function loadChatHistoryList() {
fetch('/api/load-chats')
.then(response => response.json())
.then(data => {
if (data.error) {
chatHistoryList.innerHTML = `${data.error}
`;
return;
}
if (!data.chats || data.chats.length === 0) {
chatHistoryList.innerHTML = 'Aucune conversation sauvegardée
';
return;
}
// Create history items
let historyHTML = '';
data.chats.forEach(chat => {
// Format timestamp (YYYYMMDD_HHMMSS to readable format)
const timestamp = chat.timestamp;
const year = timestamp.substring(0, 4);
const month = timestamp.substring(4, 6);
const day = timestamp.substring(6, 8);
const hour = timestamp.substring(9, 11);
const minute = timestamp.substring(11, 13);
const formattedDate = `${day}/${month}/${year} ${hour}:${minute}`;
historyHTML += `
Conversation
${formattedDate}
`;
});
chatHistoryList.innerHTML = historyHTML;
// Add click event to history items
const historyItems = chatHistoryList.querySelectorAll('.chat-history-item');
historyItems.forEach(item => {
item.addEventListener('click', function() {
const filename = this.getAttribute('data-filename');
loadChatHistory(filename);
toggleHistoryDropdown();
});
});
})
.catch(error => {
console.error('Error loading chat history:', error);
chatHistoryList.innerHTML = 'Erreur lors du chargement de l\'historique
';
});
}
// Load specific chat history
function loadChatHistory(filename) {
fetch(`/api/load-chat/${filename}`)
.then(response => response.json())
.then(data => {
if (data.error) {
alert(data.error);
return;
}
if (data.history) {
// Clear current chat
clearChat(false); // No confirmation needed
// Load history
chatHistory = data.history;
// Display messages
data.history.forEach(msg => {
if (msg.sender === 'user') {
const messageElement = userMessageTemplate.content.cloneNode(true);
messageElement.querySelector('p').textContent = msg.text;
chatWindow.appendChild(messageElement);
} else {
const messageElement = botMessageTemplate.content.cloneNode(true);
const messageParagraph = messageElement.querySelector('p');
if (window.marked) {
messageParagraph.innerHTML = marked.parse(msg.text);
} else {
messageParagraph.textContent = msg.text;
}
chatWindow.appendChild(messageElement);
if (window.Prism) {
const codeBlocks = messageParagraph.querySelectorAll('pre code');
codeBlocks.forEach(block => {
Prism.highlightElement(block);
});
}
}
});
// Scroll to bottom
scrollToBottom();
}
})
.catch(error => {
console.error('Error loading chat:', error);
alert('Erreur lors du chargement de la conversation.');
});
}
// Save current chat
saveCurrentChatButton.addEventListener('click', function() {
if (chatHistory.length <= 1) {
alert('Aucune conversation à sauvegarder. Veuillez d\'abord discuter avec le chatbot.');
return;
}
fetch('/api/save-chat', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
history: chatHistory
})
})
.then(response => response.json())
.then(data => {
if (data.error) {
alert(data.error);
} else {
alert('Conversation sauvegardée avec succès !');
loadChatHistoryList();
}
})
.catch(error => {
console.error('Error saving chat:', error);
alert('Erreur lors de la sauvegarde de la conversation.');
});
});
// --- TEXT INPUT HANDLING ---
// Auto-resize textarea based on content
userInput.addEventListener('input', function() {
this.style.height = 'auto';
this.style.height = (this.scrollHeight) + 'px';
// Reset height if empty
if (this.value === '') {
this.style.height = '';
}
});
// Send message when Enter key is pressed (without shift)
userInput.addEventListener('keydown', function(e) {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
sendMessage();
}
});
// Send message when send button is clicked
sendButton.addEventListener('click', sendMessage);
// Clear chat history when clear button is clicked
if (clearButton) {
clearButton.addEventListener('click', function() {
clearChat(true); // With confirmation
});
}
// --- CHAT FUNCTIONS ---
// Function to clear the chat history
function clearChat(withConfirmation = true) {
if (!withConfirmation || (chatHistory.length > 0 && confirm('Êtes-vous sûr de vouloir effacer toute la conversation ?'))) {
// Clear the chat window except for the welcome message
while (chatWindow.childElementCount > 1) {
chatWindow.removeChild(chatWindow.lastChild);
}
// Reset chat history but keep the welcome message
const welcomeMsg = chatHistory[0];
chatHistory = welcomeMsg ? [welcomeMsg] : [];
// Focus on input field
userInput.focus();
}
}
// Function to send message
function sendMessage() {
const message = userInput.value.trim();
// Require either text or images
if (message === '' && selectedImagesData.length === 0) return;
if (selectedImagesData.length > 0) {
// Add user message with images to UI
addUserImageMessage(message, selectedImagesData[0]); // Pour l'instant afficher seulement la première image dans l'UI
} else {
// Add user text message to UI
addUserMessage(message);
}
// Clear input field and reset height
userInput.value = '';
userInput.style.height = '';
// Show loading indicator
const loadingElement = addLoadingIndicator();
// Send message to API
sendToAPI(message, loadingElement, selectedImagesData.length > 0 ? selectedImagesData[0] : null);
// Clear image previews if any
if (selectedImagesData.length > 0) {
imagePreviewContainer.innerHTML = '';
imagePreviewArea.classList.add('hidden');
selectedImagesData = [];
imageInput.value = '';
}
}
// Add user message to chat window
function addUserMessage(message) {
// Hide welcome container if visible (first message)
if (!conversationStarted && welcomeContainer) {
welcomeContainer.style.display = 'none';
conversationStarted = true;
}
const messageElement = userMessageTemplate.content.cloneNode(true);
messageElement.querySelector('p').textContent = message;
chatWindow.appendChild(messageElement);
// Add to chat history
chatHistory.push({
sender: 'user',
text: message
});
// Scroll to bottom
scrollToBottom();
}
// Add user message with image to chat window
function addUserImageMessage(message, imageData) {
// Hide welcome container if visible (first message)
if (!conversationStarted && welcomeContainer) {
welcomeContainer.style.display = 'none';
conversationStarted = true;
}
const messageElement = userImageMessageTemplate.content.cloneNode(true);
// Set image source
const imageElement = messageElement.querySelector('.chat-image');
imageElement.src = imageData;
// Set message text if any
const textElement = messageElement.querySelector('p');
if (message) {
textElement.textContent = message;
} else {
textElement.style.display = 'none'; // Hide text element if no message
}
chatWindow.appendChild(messageElement);
// Add to chat history (we don't add the image to history, just the text)
chatHistory.push({
sender: 'user',
text: message || 'Image envoyée' // Default text if no message provided
});
// Scroll to bottom
scrollToBottom();
}
// Add bot message to chat window with markdown support
function addBotMessage(message) {
// Hide welcome container if visible (first message)
if (!conversationStarted && welcomeContainer) {
welcomeContainer.style.display = 'none';
conversationStarted = true;
}
const messageElement = botMessageTemplate.content.cloneNode(true);
const messageParagraph = messageElement.querySelector('p');
// Use the marked library to parse Markdown if available
if (window.marked) {
messageParagraph.innerHTML = marked.parse(message);
} else {
messageParagraph.textContent = message;
}
chatWindow.appendChild(messageElement);
// Add syntax highlighting to code blocks if Prism is available
if (window.Prism) {
const codeBlocks = messageParagraph.querySelectorAll('pre code');
codeBlocks.forEach(block => {
Prism.highlightElement(block);
});
}
// Add to chat history
chatHistory.push({
sender: 'bot',
text: message
});
// Scroll to bottom
scrollToBottom();
}
// Add loading indicator to chat window
function addLoadingIndicator() {
const loadingElement = loadingTemplate.content.cloneNode(true);
chatWindow.appendChild(loadingElement);
// Scroll to bottom
scrollToBottom();
// Return the loading element so it can be removed later
return chatWindow.lastElementChild;
}
// Add error message to chat window
function addErrorMessage(error, retryMessage, retryImage) {
const errorElement = errorMessageTemplate.content.cloneNode(true);
errorElement.querySelector('p').textContent = error;
// Add retry functionality if a message to retry is provided
if (retryMessage || retryImage) {
const retryButton = errorElement.querySelector('.retry-button');
retryButton.addEventListener('click', function() {
// Remove the error message
this.closest('.message-container').remove();
// Show loading indicator
const loadingElement = addLoadingIndicator();
// Retry sending the message
sendToAPI(retryMessage, loadingElement, retryImage);
});
} else {
// Hide retry button if no retry message
errorElement.querySelector('.retry-button').style.display = 'none';
}
chatWindow.appendChild(errorElement);
// Scroll to bottom
scrollToBottom();
}
// Send message to API
function sendToAPI(message, loadingElement, imageData = null) {
// Disable input while processing
userInput.disabled = true;
sendButton.disabled = true;
uploadImageButton.disabled = true;
if (clearButton) clearButton.disabled = true;
// Prepare request data
const requestData = {
message: message,
history: chatHistory
};
// Add image data if provided
if (imageData) {
requestData.image = imageData;
}
fetch('/api/chat', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(requestData)
})
.then(response => {
if (!response.ok) {
throw new Error(`Server responded with status: ${response.status}`);
}
return response.json();
})
.then(data => {
// Remove loading indicator
if (loadingElement) {
loadingElement.remove();
}
// Check for error
if (data.error) {
addErrorMessage(data.error, message, imageData);
} else {
// Add bot response
addBotMessage(data.response);
}
})
.catch(error => {
console.error('Error:', error);
// Remove loading indicator
if (loadingElement) {
loadingElement.remove();
}
// Add error message
addErrorMessage('Désolé, il y a eu un problème de connexion avec le serveur. Veuillez réessayer.', message, imageData);
})
.finally(() => {
// Re-enable input
userInput.disabled = false;
sendButton.disabled = false;
uploadImageButton.disabled = false;
if (clearButton) clearButton.disabled = false;
userInput.focus();
});
}
// Scroll chat window to bottom
function scrollToBottom() {
chatWindow.scrollTop = chatWindow.scrollHeight;
}
// Setup suggestion bubbles click handlers
if (suggestionBubbles) {
suggestionBubbles.forEach(bubble => {
bubble.addEventListener('click', function() {
const prompt = this.getAttribute('data-prompt');
if (prompt) {
userInput.value = prompt;
sendMessage();
}
});
});
}
// Initial focus on input field
userInput.focus();
});