static/chat.js (55 lines of code) (raw):

document.querySelector("form").addEventListener("submit", function (event) { event.preventDefault(); const messageInput = document.querySelector( 'textarea[name="message"]' ); const message = messageInput.value.trim(); const chatContainer = document.querySelector(".messages"); // Append the user's message to the chat container if (message) { const roleDiv = document.createElement("div"); roleDiv.classList.add("message-role"); roleDiv.classList.add("user"); roleDiv.textContent = "User"; chatContainer.appendChild(roleDiv); const userMessageDiv = document.createElement("div"); userMessageDiv.classList.add("user-message"); userMessageDiv.textContent = message; chatContainer.appendChild(userMessageDiv); } // Clear the message input messageInput.value = ""; // Send the user's message to the server using AJAX fetch("/chat", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ message: message }), }) .then((response) => response.json()) .then((data) => { if (data.success) { const roleDiv = document.createElement("div"); roleDiv.classList.add("message-role"); roleDiv.classList.add("assistant"); roleDiv.textContent = "Model"; chatContainer.appendChild(roleDiv); // Prepare the model message container const assistantMessageDiv = document.createElement("div"); assistantMessageDiv.classList.add("assistant-message"); chatContainer.appendChild(assistantMessageDiv); // Open a connection to receive streamed responses const eventSource = new EventSource("/stream"); eventSource.onmessage = function (event) { const currentText = assistantMessageDiv.textContent; const newText = event.data; const lastChar = currentText.slice(-1); // Check if we need to add a space (streamed chunks might be missing it) if (/[.,!?]/.test(lastChar) && newText.charAt(0) !== " ") { assistantMessageDiv.textContent += " " + newText; } else { assistantMessageDiv.textContent += newText; } // Scroll to the bottom of the chat container chatContainer.scrollTop = chatContainer.scrollHeight; }; eventSource.onerror = function () { eventSource.close(); }; } }); });