fix: remove all touch interception, let browser scroll xterm viewport natively
- xterm.js has zero touch event handlers (verified: only 1 'touch' ref in entire library), so it wasn't intercepting anything - Our touch-action: none + preventDefault() combo was blocking the browser from scrolling the .xterm-viewport natively - Removed all custom touch event handlers from terminal.tsx - Removed touch-action: none from .terminal-container - Added touch-action: pan-y to .xterm-viewport so browser allows vertical pan - Body scroll lock (terminal-page-open) prevents page from scrolling
This commit is contained in:
@@ -429,74 +429,7 @@ export const TerminalComponent = React.forwardRef<TerminalRef, TerminalProps>(
|
||||
};
|
||||
document.addEventListener("visibilitychange", handleVisibilityChange);
|
||||
|
||||
// Mobile touch scroll forwarding.
|
||||
// xterm.js intercepts touch events for selection, blocking native scroll.
|
||||
// We attach to *document* in capture phase so we run before xterm.js
|
||||
// even on child elements. We only act when the touch target is inside
|
||||
// this terminal container.
|
||||
let touchStartY = 0;
|
||||
let touchStartX = 0;
|
||||
let isVerticalScroll = false;
|
||||
let accumulatedDeltaY = 0;
|
||||
|
||||
const isInThisTerminal = (target: EventTarget | null): boolean => {
|
||||
if (!(target instanceof Node)) return false;
|
||||
return container.contains(target);
|
||||
};
|
||||
|
||||
const handleTouchStart = (e: TouchEvent) => {
|
||||
if (e.touches.length !== 1) return;
|
||||
if (!isInThisTerminal(e.target)) return;
|
||||
touchStartY = e.touches[0].clientY;
|
||||
touchStartX = e.touches[0].clientX;
|
||||
isVerticalScroll = false;
|
||||
accumulatedDeltaY = 0;
|
||||
};
|
||||
|
||||
const handleTouchMove = (e: TouchEvent) => {
|
||||
if (e.touches.length !== 1 || !termRef.current) return;
|
||||
if (!isInThisTerminal(e.target)) return;
|
||||
const touch = e.touches[0];
|
||||
const deltaY = touchStartY - touch.clientY;
|
||||
const deltaX = Math.abs(touchStartX - touch.clientX);
|
||||
|
||||
if (!isVerticalScroll) {
|
||||
if (Math.abs(deltaY) > deltaX && Math.abs(deltaY) > 3) {
|
||||
isVerticalScroll = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (isVerticalScroll) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
accumulatedDeltaY += deltaY;
|
||||
touchStartY = touch.clientY;
|
||||
const lines = Math.round(accumulatedDeltaY / 20);
|
||||
if (lines !== 0) {
|
||||
termRef.current.scrollLines(-lines);
|
||||
accumulatedDeltaY = 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleTouchEnd = () => {
|
||||
isVerticalScroll = false;
|
||||
accumulatedDeltaY = 0;
|
||||
};
|
||||
|
||||
if (isMobile) {
|
||||
document.addEventListener("touchstart", handleTouchStart, {
|
||||
passive: true,
|
||||
capture: true,
|
||||
});
|
||||
document.addEventListener("touchmove", handleTouchMove, {
|
||||
passive: false,
|
||||
capture: true,
|
||||
});
|
||||
document.addEventListener("touchend", handleTouchEnd, {
|
||||
capture: true,
|
||||
});
|
||||
}
|
||||
|
||||
return () => {
|
||||
isUnmountingRef.current = true;
|
||||
@@ -509,17 +442,6 @@ export const TerminalComponent = React.forwardRef<TerminalRef, TerminalProps>(
|
||||
"visibilitychange",
|
||||
handleVisibilityChange,
|
||||
);
|
||||
if (isMobile) {
|
||||
document.removeEventListener("touchstart", handleTouchStart, {
|
||||
capture: true,
|
||||
});
|
||||
document.removeEventListener("touchmove", handleTouchMove, {
|
||||
capture: true,
|
||||
});
|
||||
document.removeEventListener("touchend", handleTouchEnd, {
|
||||
capture: true,
|
||||
});
|
||||
}
|
||||
if (ws) {
|
||||
ws.close(1000, "Component unmounting");
|
||||
}
|
||||
|
||||
@@ -3707,13 +3707,17 @@ a.nav-item,
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
touch-action: none;
|
||||
}
|
||||
|
||||
/* xterm.js manages its own sizing */
|
||||
|
||||
/* xterm.js manages its own scrolling and viewport dimensions */
|
||||
|
||||
/* Mobile: allow native vertical touch panning on xterm viewport */
|
||||
.terminal-wrapper.mobile .xterm .xterm-viewport {
|
||||
touch-action: pan-y;
|
||||
}
|
||||
|
||||
/* Special Keys Strip */
|
||||
.special-keys-strip {
|
||||
flex-shrink: 0;
|
||||
|
||||
Reference in New Issue
Block a user