script = """ // Generate a new session ID on every page load let sessionId = Math.random().toString(36).substring(2); // Handle page load window.onload = function() { autoResizeTextarea(); // Handle the initial state of the textarea renderHomeText(); // Render the home text (markdown content) }; // Function to handle sending a message async function sendMessage() { const messageElem = document.getElementById('message'); const sendButton = document.querySelector('.send-button'); const message = messageElem.value.trim(); // Return early if the message is empty if (!message) return; const output = document.getElementById('output'); // Disable textarea and button while processing messageElem.disabled = true; sendButton.disabled = true; sendButton.classList.add('disabled'); // Visual indication // Display user's message in the chat const userMessage = document.createElement('p'); userMessage.classList.add('message', 'user'); userMessage.innerHTML = message.replace(/\\n/g, '
'); // Preserve line breaks output.appendChild(userMessage); // Clear the textarea and reset its height messageElem.value = ''; autoResizeTextarea(); // Scroll to the bottom of the output output.scrollTop = output.scrollHeight; // Create a new div for the AI's response let aiMessage = document.createElement('p'); aiMessage.classList.add('message', 'ai'); output.appendChild(aiMessage); // Open a connection to stream the AI's response const eventSource = new EventSource(`/stream?message=${encodeURIComponent(message)}&session_id=${encodeURIComponent(sessionId)}`); let partialResponse = ''; // Accumulate streaming response eventSource.onmessage = function(event) { partialResponse += event.data; // Convert markdown to HTML and sanitize it const sanitizedHtml = DOMPurify.sanitize(marked.parse(partialResponse)); aiMessage.innerHTML = sanitizedHtml; output.scrollTop = output.scrollHeight; // Scroll to the bottom }; // Handle errors during the SSE connection eventSource.onerror = function() { console.error("Error occurred with SSE"); resetInputState(messageElem, sendButton); // Re-enable input on error eventSource.close(); // Close the connection }; eventSource.onopen = function() { console.log("Connection to server opened."); }; // Re-enable textarea and button after the AI finishes responding eventSource.onclose = function() { console.log("Connection to server closed."); resetInputState(messageElem, sendButton); // Re-enable input after response }; } // Function to reset the input state (re-enable textarea and send button) function resetInputState(messageElem, sendButton) { messageElem.disabled = false; sendButton.disabled = false; sendButton.classList.remove('disabled'); messageElem.focus(); } // Auto-resize the textarea as the user types and manage send button state function autoResizeTextarea() { const textarea = document.getElementById('message'); const sendButton = document.querySelector('.send-button'); textarea.style.height = 'auto'; // Reset height to auto const maxHeight = 220; let newHeight = textarea.scrollHeight; if (newHeight > maxHeight) { textarea.style.height = maxHeight + 'px'; textarea.style.overflowY = 'auto'; } else { textarea.style.height = newHeight + 'px'; textarea.style.overflowY = 'hidden'; } // Enable/disable the send button based on textarea content sendButton.disabled = textarea.value.trim() === ''; sendButton.classList.toggle('disabled', !textarea.value.trim()); } // Enable sending message on Enter key press (without shift) function checkEnter(e) { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); sendMessage(); } } // Function to render home page text from Markdown (on page load) function renderHomeText() { const homeTextContainer = document.getElementById('home-text-container'); const markdownContent = homeTextContainer.getAttribute('data-home-text'); // Parse markdown and sanitize HTML const sanitizedHtml = DOMPurify.sanitize(marked.parse(markdownContent)); homeTextContainer.innerHTML = sanitizedHtml; } """