fix(dashboard): allow textarea taps + resync iOS keyboard on page restore

Two fixes for residual mobile chat issues:

1) Textarea touch-action: none was preventing iOS from registering a
   clean tap-to-focus, causing the keyboard to flash up via the
   programmatic focus() in onTouchStart and then auto-dismiss because
   iOS never saw the gesture complete. Switching to manipulation
   allows tap while still blocking pan/zoom — the composer stays
   anchored thanks to overscroll-behavior: contain on its container.

2) On switch-away-and-back the visualViewport metrics could get
   stuck in a half-state (composer pushed up, or blank pane covering
   it). Adding a visibilitychange / pageshow handler on ChatView that
   force-blurs and re-focuses the active textarea makes iOS resync
   the keyboard / vv metrics cleanly.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
gsxdsm
2026-05-06 21:44:39 -07:00
parent e059a2a02d
commit 2ed722ecbd
2 changed files with 31 additions and 8 deletions

View File

@@ -848,14 +848,12 @@
line-height: 1.4;
max-height: 120px;
min-height: 40px;
/* iOS: any pan-y gesture on the textarea (including pan-y itself)
can drive an auto-scroll-into-view that lifts the composer up
off-screen even with body locked. touch-action: none blocks
gesture-driven panning while keeping tap-to-focus and typing
intact. Trade-off: loses gesture scroll within the textarea once
content exceeds max-height (~6 lines); arrow keys / caret moves
still work. */
touch-action: none;
/* touch-action: manipulation allows tap-to-focus (so iOS doesn't
auto-dismiss the keyboard between gesture frames) and blocks
double-tap zoom + pan/zoom gestures. We previously tried `none`
to lock the composer against pan, but that broke first-tap
focus on iOS. */
touch-action: manipulation;
overscroll-behavior: contain;
}

View File

@@ -900,6 +900,31 @@ export function ChatView({ projectId, addToast }: ChatViewProps) {
}
}, [contextMenu]);
// On mount and on visibility/page restore, if iOS thinks the keyboard is
// up but the textarea isn't actually focused (or vice versa), the
// visualViewport metrics get stuck in a half-state — composer pushed up
// or covered by a blank pane. Force a blur+refocus on the textarea to
// make iOS resync. Only runs on mobile and only when ChatView holds the
// active session (avoids stealing focus from other views).
useEffect(() => {
if (!isMobile || !activeSession) return;
const resync = () => {
const ta = inputRef.current;
if (!ta) return;
if (document.activeElement !== ta) return; // only if it was focused
ta.blur();
window.setTimeout(() => {
ta.focus({ preventScroll: true });
}, 0);
};
document.addEventListener("visibilitychange", resync);
window.addEventListener("pageshow", resync);
return () => {
document.removeEventListener("visibilitychange", resync);
window.removeEventListener("pageshow", resync);
};
}, [isMobile, activeSession]);
// Fetch agents on mount for name resolution (project-scoped with stale-request protection)
useEffect(() => {
let cancelled = false;