Skip to content `; } else if (type === 'error') { messageDiv.classList.add('error'); messageDiv.textContent = `Error: ${text}`; } else { // Basic sanitization for text content (more robust needed for prod) const textNode = document.createTextNode(text); messageDiv.appendChild(textNode); } chatWindow.appendChild(messageDiv); scrollToBottom(); return messageDiv; // Return the element if we need to modify it (e.g., remove typing indicator) } // Auto-scroll to the bottom of the chat window function scrollToBottom() { chatWindow.scrollTop = chatWindow.scrollHeight; } // Handle sending a message async function sendMessage() { const messageText = messageInput.value.trim(); if (messageText === '') return; addMessage(messageText, 'user'); messageInput.value = ''; // Clear input messageInput.style.height = 'auto'; // Reset textarea height sendButton.disabled = true; // Disable send button while processing // Show typing indicator const typingIndicator = addMessage('', 'bot', 'typing'); try { const response = await fetch(API_URL, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ contents: [ { parts: [ { text: messageText } ] } ] // You can add generationConfig here if needed: // "generationConfig": { // "temperature": 0.9, // "topK": 1, // "topP": 1, // "maxOutputTokens": 2048, // "stopSequences": [] // } }), }); // Remove typing indicator if (typingIndicator) typingIndicator.remove(); if (!response.ok) { const errorData = await response.json(); console.error('API Error Response:', errorData); let errorMessage = `API Error: ${response.status} ${response.statusText}.`; if (errorData.error && errorData.error.message) { errorMessage += ` Details: ${errorData.error.message}`; } // Check for specific API key error if (response.status === 400 && errorData.error && errorData.error.message.toLowerCase().includes('api key not valid')) { errorMessage = "API Key not valid. Please check your API key in the script."; } addMessage(errorMessage, 'bot', 'error'); return; } const data = await response.json(); if (data.candidates && data.candidates.length > 0 && data.candidates[0].content && data.candidates[0].content.parts && data.candidates[0].content.parts.length > 0) { const botResponse = data.candidates[0].content.parts[0].text; addMessage(botResponse, 'bot'); } else if (data.promptFeedback && data.promptFeedback.blockReason) { let blockMessage = `Your request was blocked: ${data.promptFeedback.blockReason}.`; if(data.promptFeedback.safetyRatings && data.promptFeedback.safetyRatings.length > 0){ blockMessage += ` Category: ${data.promptFeedback.safetyRatings[0].category}`; } addMessage(blockMessage, 'bot', 'error'); } else { console.error('Unexpected API response structure:', data); addMessage('Sorry, I received an unexpected response. Please try again.', 'bot', 'error'); } } catch (error) { console.error('Fetch Error:', error); if (typingIndicator) typingIndicator.remove(); addMessage(`Network or other error: ${error.message}. Check console for details.`, 'bot', 'error'); } finally { sendButton.disabled = false; // Re-enable send button messageInput.focus(); // Focus back on input } } // Auto-adjust textarea height function autoAdjustTextareaHeight() { messageInput.style.height = 'auto'; // Reset height messageInput.style.height = (messageInput.scrollHeight) + 'px'; // Set to scroll height } // --- Event Listeners --- sendButton.addEventListener('click', sendMessage); messageInput.addEventListener('keypress', (event) => { if (event.key === 'Enter' && !event.shiftKey) { // Send on Enter, allow Shift+Enter for newline event.preventDefault(); // Prevent default Enter behavior (newline) sendMessage(); } }); messageInput.addEventListener('input', autoAdjustTextareaHeight); // --- Initial Focus & Welcome Message (Optional) --- messageInput.focus(); addMessage("Hello! I'm powered by Gemini. How can I assist you today?", "bot"); autoAdjustTextareaHeight(); // Initial adjustment });