fix(dashboard): keep Android keyboard open in main chat; disable kanban pinch-zoom

Two Android-specific fixes:

1. Keyboard dismissing in main chat. App.tsx derives `mobileKeyboardOpen`
   from useMobileKeyboard and uses it to gate the
   `project-content--with-mobile-nav` / `--with-footer` className
   assignment plus MobileNavBar rendering. When the soft keyboard opened
   on Android, those classes were removed and the nav unmounted, shrinking
   padding-bottom by ~80px in a single render. Android Chrome treats the
   resulting jump of the focused chat input as the focus target moving and
   instantly dismisses the keyboard. With interactive-widget=resizes-content
   set on Android, the layout viewport itself shrinks with the keyboard, so
   the hide-nav-on-keyboard pattern was redundant on Android (and harmful).
   The whole pattern is now gated to iOS via isIOS(). iOS path is unchanged.

2. Pinch-zoom on kanban. Android Chrome ignores user-scalable=no for a11y,
   and kanban's overflow-x:auto columns combined with the inflated ICB
   produce a broken visual when the user zooms out. Adds
   touch-action: pan-x pan-y to html,body inside the mobile media query
   (keeps scroll panning, blocks pinch-zoom). Chat and MissionManager were
   unaffected before because they don't expose a wide horizontal
   scrollable region.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
gsxdsm
2026-05-22 14:07:15 -07:00
parent 60a0012697
commit a10fc56ee2
4 changed files with 27 additions and 3 deletions

View File

@@ -0,0 +1,11 @@
---
"@fusion/dashboard": patch
---
fix(dashboard): keep Android keyboard open in main chat; disable kanban pinch-zoom
Two Android-specific fixes:
1. **Keyboard dismissing in main chat.** `mobileKeyboardOpen` in `App.tsx` (derived from `useMobileKeyboard`) gates `project-content--with-mobile-nav` / `--with-footer` className assignment and MobileNavBar rendering. When the soft keyboard opened, those classes were removed and the nav unmounted, shrinking padding-bottom by ~80px in a single render. Android Chrome treats the resulting jump of the focused chat input as the focus target moving and instantly dismisses the keyboard. With `interactive-widget=resizes-content` set on Android, the layout viewport itself shrinks with the keyboard, so the hide-nav-on-keyboard behavior was redundant on Android (and harmful). The whole pattern is now gated to iOS via `isIOS()`. iOS path is unchanged.
2. **Pinch-zoom on kanban.** Android Chrome ignores `user-scalable=no` for accessibility, and the kanban board's `overflow-x: auto` columns combined with the inflated ICB produce a broken visual when the user zooms out. Adds `touch-action: pan-x pan-y` to `html, body` inside the mobile media query, which keeps scroll panning but disables pinch-zoom (Chat and MissionManager were unaffected because they don't expose a wide horizontal scrollable region).

View File

@@ -57,7 +57,7 @@ import { useDeepLink } from "./hooks/useDeepLink";
import { useFavorites } from "./hooks/useFavorites";
import { useAuthOnboarding } from "./hooks/useAuthOnboarding";
import { useMobileKeyboard } from "./hooks/useMobileKeyboard";
import { useMobileScrollLock } from "./hooks/useMobileScrollLock";
import { isIOS, useMobileScrollLock } from "./hooks/useMobileScrollLock";
import { useSetupReadiness } from "./hooks/useSetupReadiness";
import { useUpdateCheck } from "./hooks/useUpdateCheck";
import { useViewState, type TaskView } from "./hooks/useViewState";
@@ -463,7 +463,15 @@ function AppInner() {
// affecting the underlying dashboard layout — the modal handles its own
// viewport. Without this guard, modal keyboard state leaks into the app-level
// layout, causing stale bottom-padding offsets after the keyboard closes.
const mobileKeyboardOpen = isMobile && keyboardOpen && !modalManager.anyModalOpen;
//
// Android-gated: with `interactive-widget=resizes-content` the layout
// viewport itself shrinks with the soft keyboard, so we DON'T want to
// also hide the nav bar / strip its padding — that produces a layout
// jump while the focused input is settling, which Android Chrome treats
// as the focus target moving and dismisses the keyboard immediately.
// iOS doesn't shrink the layout viewport, so the iOS path keeps the
// hide-nav-on-keyboard behavior intact.
const mobileKeyboardOpen = isMobile && keyboardOpen && !modalManager.anyModalOpen && isIOS();
// App-level scroll lock for inline editing (TaskCard inline edit, etc.):
// when the keyboard is up outside of any modal, pin the body so iOS can't
// shift the document or visualViewport, and so the dashboard snaps back

View File

@@ -21,7 +21,7 @@ function isMobileDevice(): boolean {
* Chrome shrinks the layout viewport with the keyboard, so no drift
* compensation is needed there.
*/
function isIOS(): boolean {
export function isIOS(): boolean {
if (typeof window === "undefined") return false;
const ua = navigator.userAgent || "";
// iPad on iPadOS 13+ reports as MacIntel + touch — handle that too.

View File

@@ -3219,6 +3219,11 @@ input[type="range"]:focus-visible {
html,
body {
overflow: hidden;
/* Disable pinch-zoom on mobile — Android Chrome ignores user-scalable=no
for a11y, and the kanban board's horizontally-scrollable layout
interacts badly with zoom-out (exposes the offscreen-right area).
`pan-x pan-y` keeps scroll panning but blocks pinch-zoom. */
touch-action: pan-x pan-y;
}
#root {