fix(dashboard): use non-passive touchmove listener on textarea

React's onTouchMove handler is registered as a passive listener by
default, so the previous JSX-handler preventDefault() was silently a
no-op — drags on the composer still scrolled the input box up.

Attaches the listener imperatively via addEventListener with
{ passive: false } so preventDefault actually cancels the drag. Tap
(touchstart + touchend without touchmove between) is still unaffected,
so first-tap focus continues to work.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
gsxdsm
2026-05-06 22:39:35 -07:00
parent bb11c7102d
commit f28e75cc90

View File

@@ -905,6 +905,26 @@ export function ChatView({ projectId, addToast }: ChatViewProps) {
}
}, [contextMenu]);
// Lock the composer textarea against drag-pan on mobile. React's onTouchMove
// is registered as a passive listener so preventDefault() inside a JSX
// handler is a no-op — we attach a non-passive listener directly to the
// textarea element. touchmove cancellation blocks drags but tap (touchstart
// + touchend without touchmove in between) is unaffected, so first-tap
// focus still works.
useEffect(() => {
if (!isMobile) return;
const ta = inputRef.current;
if (!ta) return;
const onTouchMove = (event: TouchEvent) => {
if (typeof window === "undefined" || window.innerWidth > 768) return;
event.preventDefault();
};
ta.addEventListener("touchmove", onTouchMove, { passive: false });
return () => {
ta.removeEventListener("touchmove", onTouchMove);
};
}, [isMobile]);
// 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
@@ -1957,16 +1977,6 @@ export function ChatView({ projectId, addToast }: ChatViewProps) {
event.preventDefault();
event.currentTarget.focus({ preventScroll: true });
}}
onTouchMove={(event) => {
// Lock the composer in place. touch-action: manipulation
// is required for first-tap focus to register on iOS,
// but it also permits pan-y, which lets the user drag
// the input box up off-screen. Cancelling touchmove
// blocks the drag without affecting tap (a tap fires
// touchstart + touchend with no touchmove in between).
if (typeof window === "undefined" || window.innerWidth > 768) return;
event.preventDefault();
}}
rows={1}
data-testid="chat-input"
/>