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:
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user