diff --git a/ai-chat.js b/ai-chat.js index a92e2d5..f13eaa9 100644 --- a/ai-chat.js +++ b/ai-chat.js @@ -76,7 +76,9 @@ const messages = chatRoot.querySelector(".ai-chat__messages"); const form = chatRoot.querySelector(".ai-chat__form"); const input = chatRoot.querySelector(".ai-chat__input"); + const sendBtn = chatRoot.querySelector(".ai-chat__send"); const backToTop = document.getElementById("back-to-top"); + const viewport = window.visualViewport; function addMessage(text, fromUser) { const bubble = document.createElement("p"); @@ -96,12 +98,14 @@ addMessage(introMessage, false); } input.focus(); + syncKeyboardOffset(); } function closeChat() { panel.classList.remove("is-open"); panel.setAttribute("aria-hidden", "true"); fab.setAttribute("aria-expanded", "false"); + syncKeyboardOffset(); } function randomItem(items) { @@ -123,6 +127,18 @@ ); } + function syncKeyboardOffset() { + if (!viewport) { + chatRoot.style.setProperty("--ai-chat-keyboard-offset", "0px"); + return; + } + const offset = Math.max( + 0, + Math.round(window.innerHeight - viewport.height - viewport.offsetTop), + ); + chatRoot.style.setProperty("--ai-chat-keyboard-offset", `${offset}px`); + } + fab.addEventListener("click", () => { if (panel.classList.contains("is-open")) { closeChat(); @@ -132,6 +148,10 @@ }); closeBtn.addEventListener("click", closeChat); + sendBtn.addEventListener("pointerdown", (event) => { + // Keep focus on the input so mobile keyboards stay open after sending. + event.preventDefault(); + }); form.addEventListener("submit", (event) => { event.preventDefault(); @@ -142,6 +162,7 @@ addMessage(text, true); addMessage(buildReply(), false); input.value = ""; + input.focus({ preventScroll: true }); }); if (backToTop) { @@ -152,5 +173,11 @@ }); window.addEventListener("resize", syncChatPosition); } + if (viewport) { + viewport.addEventListener("resize", syncKeyboardOffset); + viewport.addEventListener("scroll", syncKeyboardOffset); + } + window.addEventListener("resize", syncKeyboardOffset); syncChatPosition(); + syncKeyboardOffset(); })(); diff --git a/styles.css b/styles.css index aab1c1e..cb9d0ee 100644 --- a/styles.css +++ b/styles.css @@ -881,7 +881,7 @@ li a span { .ai-chat { position: fixed; right: 24px; - bottom: 24px; + bottom: calc(24px + var(--ai-chat-keyboard-offset, 0px)); z-index: 1100; transition: bottom 0.25s ease; font-family: @@ -896,7 +896,7 @@ li a span { } .ai-chat.ai-chat--raised { - bottom: 96px; + bottom: calc(96px + var(--ai-chat-keyboard-offset, 0px)); } .ai-chat__fab { @@ -1012,21 +1012,22 @@ li a span { @media (max-width: 600px) { .ai-chat { right: 14px; - bottom: 14px; + bottom: calc(14px + var(--ai-chat-keyboard-offset, 0px)); } .ai-chat.ai-chat--raised { - bottom: 82px; + bottom: calc(82px + var(--ai-chat-keyboard-offset, 0px)); } .ai-chat__panel { position: fixed; - inset: 0; + top: 0; + right: 0; + left: 0; + bottom: var(--ai-chat-keyboard-offset, 0px); width: 100vw; max-width: none; border-radius: 0; - bottom: 0; - right: 0; } .ai-chat__panel.is-open {