Phone: keyboard no longer leaves the page shifted

Mobile browsers scroll the whole page up to show the focused input and do not
always scroll it back when the keyboard closes; the app never scrolls the page
itself, so the top bar (menu) stayed out of reach.

- Viewport interactive-widget=resizes-content: on Android the keyboard shrinks
  the page instead of shifting it.
- Elsewhere (iOS Safari) the page offset is undone when the visual viewport
  grows again or the input loses focus.
- The chat keeps following the latest message when its pane shrinks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Richard ZinkandClaude Opus 5.5 committed 2026-11-10 13:49:53 +01:00
1 parent 9fd92d5b39
commit 61219dbca2
4 files changed
+60 -1

No files matched your search

+11
View File
@@ -202,6 +202,17 @@ export function ChatView({ sessionId, onMenu, onClosePane }: { sessionId: string
const el = scrollRef.current;
if (el && stick.current) el.scrollTop = el.scrollHeight;
});
// … also when the pane gets smaller (on-screen keyboard opens) without a render.
const hasSession = !!session;
useEffect(() => {
const el = scrollRef.current;
if (!el || typeof ResizeObserver === "undefined") return;
const ro = new ResizeObserver(() => {
if (stick.current) el.scrollTop = el.scrollHeight;
});
ro.observe(el);
return () => ro.disconnect();
}, [hasSession, sessionId]);
if (loadError) return <div className="empty">{loadError}</div>;
if (!session) return <div className="empty">{t("common.loading")}</div>;
+37
View File
@@ -10,6 +10,43 @@ import { captureShare } from "./share";
applyTheme();
captureShare();
registerSW({ immediate: true });
undoKeyboardShift();
/**
* Browsers that do not resize the page for the on-screen keyboard (iOS Safari, older
* Android) scroll the whole page up to show the input – and do not always scroll it back
* when the keyboard closes, leaving the top bar (menu) out of reach. The app never
* scrolls the page itself (only its panes), so any page offset is undone once the
* visible area grows again.
*/
function undoKeyboardShift() {
const vv = window.visualViewport;
if (!vv) return;
let last = vv.height;
const reset = () => {
const el = document.scrollingElement ?? document.documentElement;
if (window.scrollY !== 0 || el.scrollTop !== 0 || document.body.scrollTop !== 0) {
window.scrollTo(0, 0);
el.scrollTop = 0;
document.body.scrollTop = 0;
}
};
vv.addEventListener("resize", () => {
// Grown back (keyboard closed or closing): after the browser's own adjustment.
if (vv.height > last + 1) {
requestAnimationFrame(reset);
setTimeout(reset, 350);
}
last = vv.height;
});
// Keyboard closed by leaving the input (no resize on some browsers).
document.addEventListener("focusout", () =>
setTimeout(() => {
const a = document.activeElement;
if (!a || !(a instanceof HTMLInputElement || a instanceof HTMLTextAreaElement || (a as HTMLElement).isContentEditable)) reset();
}, 300),
);
}
createRoot(document.getElementById("root")!).render(
<StrictMode>