// Small UX touches on top of HTMX: Enter to send, auto-growing textarea, // clearing the input after sending, and keeping the newest message in view. (() => { const form = document.getElementById("composer"); const input = form.querySelector("textarea"); const messages = document.getElementById("messages"); const grow = () => { input.style.height = "auto"; input.style.height = input.scrollHeight + "px"; }; const nearBottom = () => messages.scrollHeight - messages.scrollTop - messages.clientHeight < 120; const scrollDown = () => (messages.scrollTop = messages.scrollHeight); input.addEventListener("input", grow); input.addEventListener("keydown", (e) => { if (e.key === "Enter" && !e.shiftKey && !e.isComposing) { e.preventDefault(); if (input.value.trim()) form.requestSubmit(); } }); form.addEventListener("htmx:afterRequest", (e) => { if (e.detail.successful) { form.reset(); grow(); scrollDown(); } }); // Follow streaming tokens, unless the user scrolled up to read. let follow = true; messages.addEventListener("scroll", () => (follow = nearBottom())); document.body.addEventListener("htmx:sseMessage", () => follow && scrollDown()); document.body.addEventListener("htmx:afterSwap", () => follow && scrollDown()); scrollDown(); })();