document.addEventListener('DOMContentLoaded', () => {
// DOM Elements
const chatForm = document.getElementById('chat-form');
const userInput = document.getElementById('user-input');
const chatMessages = document.getElementById('chat-messages');
const sendButton = document.getElementById('send-button');
const typingIndicator = document.getElementById('typing-indicator');
const clearChatButton = document.getElementById('clear-chat');
const themeToggle = document.getElementById('theme-toggle');
const charCounter = document.getElementById('char-counter');
const toastContainer = document.getElementById('toast-container');
// State
let darkMode = document.documentElement.classList.contains('dark');
let retryCount = 0;
// Event Listeners
chatForm.addEventListener('submit', handleSubmit);
clearChatButton.addEventListener('click', clearChat);
themeToggle.addEventListener('click', toggleTheme);
userInput.addEventListener('input', handleInput);
userInput.addEventListener('keydown', handleKeydown);
document.addEventListener('keydown', handleHotkeys);
// Initialize
loadChat();
updateThemeButton();
userInput.focus();
function handleInput(e) {
const length = e.target.value.length;
charCounter.textContent = `${length}/${config.MAX_CHARS}`;
autoResize();
}
function autoResize() {
userInput.style.height = 'auto';
userInput.style.height = userInput.scrollHeight + 'px';
}
function handleKeydown(e) {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
chatForm.requestSubmit();
}
}
function handleHotkeys(e) {
if (e.ctrlKey || e.metaKey) {
switch(e.key) {
case '/':
e.preventDefault();
userInput.focus();
break;
case 'l':
e.preventDefault();
clearChat();
break;
}
}
}
async function handleSubmit(e) {
e.preventDefault();
const message = userInput.value.trim();
if (message) {
addMessage('user', message);
userInput.value = '';
userInput.style.height = 'auto';
charCounter.textContent = `0/${config.MAX_CHARS}`;
disableInput(true);
showTypingIndicator();
try {
const response = await sendMessageWithRetry(message);
addMessage('bot', response);
showToast('Message sent successfully!', 'success');
} catch (error) {
console.error('Error:', error);
showToast('Failed to send message. Please try again.', 'error');
}
disableInput(false);
hideTypingIndicator();
userInput.focus();
}
}
async function sendMessageWithRetry(message, attempt = 1) {
try {
return await sendMessageToBot(message);
} catch (error) {
if (attempt < config.MAX_RETRIES) {
await new Promise(resolve => setTimeout(resolve, config.RETRY_DELAY * attempt));
return sendMessageWithRetry(message, attempt + 1);
}
throw error;
}
}
async function sendMessageToBot(message) {
const response = await fetch(config.COHERE_API_URL, {
method: 'POST',
headers: {
'Authorization': `BEARER ${config.COHERE_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
model: 'command-nightly',
prompt: `Human: ${message}\n${config.BOT_NAME}:`,
max_tokens: 150,
temperature: 0.7,
k: 20,
stop_sequences: ["Human:", "\n"],
return_likelihoods: 'NONE',
}),
});
if (!response.ok) {
throw new Error('Failed to fetch from Cohere API');
}
const data = await response.json();
return data.generations[0].text.trim();
}
function addMessage(sender, content) {
const messageElement = document.createElement('div');
messageElement.classList.add('message', `${sender}-message`);
messageElement.textContent = content;
// Add copy button
const copyButton = document.createElement('button');
copyButton.classList.add('copy-button');
copyButton.innerHTML = '';
copyButton.addEventListener('click', () => copyToClipboard(content));
messageElement.appendChild(copyButton);
// Add for screen readers
messageElement.setAttribute('role', 'log');
messageElement.setAttribute('aria-label', `${sender} message: ${content}`);
chatMessages.appendChild(messageElement);
messageElement.scrollIntoView({ behavior: 'smooth' });
saveChat();
}
function copyToClipboard(text) {
navigator.clipboard.writeText(text).then(() => {
showToast('Copied to clipboard!', 'success');
}).catch(err => {
console.error('Failed to copy: ', err);
showToast('Failed to copy. Please try again.', 'error');
});
}
function showTypingIndicator() {
typingIndicator.classList.remove('hidden');
typingIndicator.scrollIntoView({ behavior: 'smooth' });
}
function hideTypingIndicator() {
typingIndicator.classList.add('hidden');
}
function disableInput(disabled) {
userInput.disabled = disabled;
sendButton.disabled = disabled;
}
function showToast(message, type = 'info') {
const toast = document.createElement('div');
toast.classList.add('toast', type);
toast.textContent = message;
toastContainer.appendChild(toast);
setTimeout(() => {
toast.style.opacity = '0';
setTimeout(() => toast.remove(), 300);
}, 3000);
}
function saveChat() {
const messages = Array.from(chatMessages.children)
.filter(el => el.classList.contains('message'))
.map(msg => ({
sender: msg.classList.contains('user-message') ? 'user' : 'bot',
content: msg.textContent
}));
localStorage.setItem('chatHistory', JSON.stringify(messages));
}
function loadChat() {
const savedChat = localStorage.getItem('chatHistory');
if (savedChat) {
const messages = JSON.parse(savedChat);
chatMessages.innerHTML = '';
messages.forEach(msg => addMessage(msg.sender, msg.content));
}
}
function clearChat() {
chatMessages.innerHTML = '';
localStorage.removeItem('chatHistory');
addMessage('bot', config.WELCOME_MESSAGE);
showToast('Chat cleared', 'success');
}
function toggleTheme() {
darkMode = !darkMode;
document.documentElement.classList.toggle('dark', darkMode);
localStorage.setItem('theme', darkMode ? 'dark' : 'light');
updateThemeButton();
showToast(`${darkMode ? 'Dark' : 'Light'} mode activated`, 'success');
}
function updateThemeButton() {
themeToggle.innerHTML = ``;
}
});