fix: prevent mobile keyboard from occluding terminal viewport (#1303)

* fix: prevent mobile keyboard from occluding terminal viewport

On mobile, 100dvh does not shrink when the soft keyboard opens, causing
the terminal and other absolute-inset-0 views to extend behind the keyboard.

Add useVisualViewport hook that tracks window.visualViewport.resize/scroll
via RAF. On mobile, use the visual viewport height (actual visible area)
for the root container instead of 100dvh, so content resizes above the
keyboard when it opens.

* fix: skip unchanged visual viewport updates

---------

Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
This commit is contained in:
Junlang Huang
2026-05-18 18:05:13 +03:00
committed by GitHub
co-authored by Bohdan Triapitsyn
parent 76eef473aa
commit 48af84222a
2 changed files with 57 additions and 2 deletions
@@ -20,6 +20,7 @@ import { useUIStore } from '@/stores/useUIStore';
import { useUpdateStore } from '@/stores/useUpdateStore';
import { useDeviceInfo } from '@/lib/device';
import { useEffectiveDirectory } from '@/hooks/useEffectiveDirectory';
import { useVisualViewport } from '@/hooks/useVisualViewport';
import { useI18n } from '@/lib/i18n';
import { cn } from '@/lib/utils';
import { isDesktopShell } from '@/lib/desktop';
@@ -84,6 +85,7 @@ export const MainLayout: React.FC = () => {
const multiRunLauncherPrefillPrompt = useUIStore((state) => state.multiRunLauncherPrefillPrompt);
const { isMobile, isTablet } = useDeviceInfo();
const visualViewport = useVisualViewport();
const isDesktopShellRuntime = React.useMemo(() => isDesktopShell(), []);
const sidebarWidth = useUIStore((state) => state.sidebarWidth);
const rightSidebarWidth = useUIStore((state) => state.rightSidebarWidth);
@@ -390,10 +392,11 @@ export const MainLayout: React.FC = () => {
<div
data-page-scroll-lock="true"
className={cn(
'main-content-safe-area h-[100dvh]',
isMobile ? 'flex flex-col' : 'flex',
'main-content-safe-area',
isMobile ? 'flex flex-col' : 'flex h-[100dvh]',
'bg-background'
)}
style={isMobile && visualViewport.height > 0 ? { height: visualViewport.height } : undefined}
>
<CommandPalette />
<HelpDialog />