(function () { "use strict"; const messagesEl = document.getElementById("messages"); const formEl = document.getElementById("composer"); const inputEl = document.getElementById("input"); const sendEl = document.getElementById("send"); const GREETING = "Hi! I'm the Bookly support assistant. I can help you check on an order, start a return, or answer questions about shipping, returns, or password reset. How can I help today?"; function appendMessage(role, text) { const el = document.createElement("div"); el.className = "message message--" + role; // SECURITY: always use textContent here, never innerHTML. The reply // comes from the model and must be treated as untrusted data. el.textContent = text; messagesEl.appendChild(el); messagesEl.scrollTop = messagesEl.scrollHeight; return el; } function appendTypingIndicator() { const el = document.createElement("div"); el.className = "message message--assistant message--typing"; el.setAttribute("aria-label", "Assistant is typing"); const dotCount = 3; for (let i = 0; i < dotCount; i += 1) { el.appendChild(document.createElement("span")); } messagesEl.appendChild(el); messagesEl.scrollTop = messagesEl.scrollHeight; return el; } async function sendMessage(text) { // The session is tracked server-side via an HttpOnly cookie. We do not // send a session_id in the body and cannot read the cookie from JS. const response = await fetch("/api/chat", { method: "POST", credentials: "same-origin", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ message: text }), }); if (response.status === 429) { throw new Error("rate_limited"); } if (!response.ok) { throw new Error("Server returned " + response.status); } const data = await response.json(); return data.reply; } formEl.addEventListener("submit", async function (event) { event.preventDefault(); const text = inputEl.value.trim(); if (!text) return; appendMessage("user", text); inputEl.value = ""; inputEl.disabled = true; sendEl.disabled = true; const typing = appendTypingIndicator(); try { const reply = await sendMessage(text); typing.remove(); appendMessage("assistant", reply); } catch (err) { typing.remove(); const message = err && err.message === "rate_limited" ? "You're sending messages very fast. Please wait a moment and try again." : "Sorry, I couldn't reach the server. Please try again in a moment."; appendMessage("assistant", message); console.error(err); } finally { inputEl.disabled = false; sendEl.disabled = false; inputEl.focus(); } }); appendMessage("assistant", GREETING); inputEl.focus(); })();