diff --git a/ui/src/components/MessageInput.tsx b/ui/src/components/MessageInput.tsx index ae7c7cea7566e70ecac9a02ecf1588315e555e9b..c00049bd66b8bc75beb9e35ce1e28367b91812d5 100644 --- a/ui/src/components/MessageInput.tsx +++ b/ui/src/components/MessageInput.tsx @@ -381,6 +381,29 @@ function MessageInput({ } }, [autoFocus]); + // Handle virtual keyboard appearance on mobile (especially Android Firefox) + // The visualViewport API lets us detect when the keyboard shrinks the viewport + useEffect(() => { + if (typeof window === "undefined" || !window.visualViewport) { + return; + } + + const handleViewportResize = () => { + // Only scroll if our textarea is focused (keyboard is for us) + if (document.activeElement === textareaRef.current) { + // Small delay to let the viewport settle after resize + requestAnimationFrame(() => { + textareaRef.current?.scrollIntoView({ behavior: "smooth", block: "center" }); + }); + } + }; + + window.visualViewport.addEventListener("resize", handleViewportResize); + return () => { + window.visualViewport?.removeEventListener("resize", handleViewportResize); + }; + }, []); + const isDisabled = disabled || uploadsInProgress > 0; const canSubmit = message.trim() && !isDisabled && !submitting;