Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 346b32236a |
@@ -363,34 +363,32 @@ export const TerminalComponent = React.forwardRef<TerminalRef, TerminalProps>(
|
|||||||
let startY = 0;
|
let startY = 0;
|
||||||
let startX = 0;
|
let startX = 0;
|
||||||
let isScrolling = false;
|
let isScrolling = false;
|
||||||
let scrollPending = 0;
|
let debugOverlay: HTMLDivElement | null = null;
|
||||||
const WHEEL_DISTANCE = 12;
|
const showDebug = (msg: string) => {
|
||||||
|
if (!debugOverlay) {
|
||||||
const flushScroll = (force = false) => {
|
debugOverlay = document.createElement("div");
|
||||||
if (scrollPending === 0) return;
|
debugOverlay.style.position = "fixed";
|
||||||
if (!force && Math.abs(scrollPending) < WHEEL_DISTANCE) return;
|
debugOverlay.style.bottom = "120px";
|
||||||
const direction = Math.sign(scrollPending);
|
debugOverlay.style.left = "8px";
|
||||||
const steps = Math.max(
|
debugOverlay.style.right = "8px";
|
||||||
1,
|
debugOverlay.style.zIndex = "9999";
|
||||||
Math.floor(Math.abs(scrollPending) / WHEEL_DISTANCE),
|
debugOverlay.style.background = "rgba(0,0,0,0.8)";
|
||||||
);
|
debugOverlay.style.color = "#0f0";
|
||||||
const ws = wsRef.current;
|
debugOverlay.style.fontFamily = "monospace";
|
||||||
if (ws?.readyState === WebSocket.OPEN && termRef.current) {
|
debugOverlay.style.fontSize = "11px";
|
||||||
const buf = termRef.current.buffer.active;
|
debugOverlay.style.padding = "8px";
|
||||||
const col = buf.cursorX + 1;
|
debugOverlay.style.borderRadius = "6px";
|
||||||
const row = buf.cursorY + 1;
|
debugOverlay.style.pointerEvents = "none";
|
||||||
const btn = direction > 0 ? 64 : 65;
|
debugOverlay.style.whiteSpace = "pre-wrap";
|
||||||
for (let i = 0; i < steps; i++) {
|
debugOverlay.style.maxHeight = "120px";
|
||||||
ws.send(`\x1b[<${btn};${col};${row}M`);
|
debugOverlay.style.overflow = "auto";
|
||||||
|
document.body.appendChild(debugOverlay);
|
||||||
}
|
}
|
||||||
}
|
const line = `${new Date().toLocaleTimeString()} ${msg}`;
|
||||||
scrollPending = 0;
|
debugOverlay.textContent =
|
||||||
};
|
line +
|
||||||
|
"\n" +
|
||||||
const isAlternateScreen = () => {
|
debugOverlay.textContent?.split("\n").slice(0, 8).join("\n");
|
||||||
const term = termRef.current;
|
|
||||||
if (!term) return false;
|
|
||||||
return term.buffer.active === term.buffer.alternate;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const onTouchStart = (e: TouchEvent) => {
|
const onTouchStart = (e: TouchEvent) => {
|
||||||
@@ -398,7 +396,6 @@ export const TerminalComponent = React.forwardRef<TerminalRef, TerminalProps>(
|
|||||||
startY = e.touches[0].clientY;
|
startY = e.touches[0].clientY;
|
||||||
startX = e.touches[0].clientX;
|
startX = e.touches[0].clientX;
|
||||||
isScrolling = false;
|
isScrolling = false;
|
||||||
scrollPending = 0;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const onTouchMove = (e: TouchEvent) => {
|
const onTouchMove = (e: TouchEvent) => {
|
||||||
@@ -406,6 +403,7 @@ export const TerminalComponent = React.forwardRef<TerminalRef, TerminalProps>(
|
|||||||
const touch = e.touches[0];
|
const touch = e.touches[0];
|
||||||
const deltaY = startY - touch.clientY;
|
const deltaY = startY - touch.clientY;
|
||||||
const deltaX = Math.abs(startX - touch.clientX);
|
const deltaX = Math.abs(startX - touch.clientX);
|
||||||
|
showDebug(`move dy=${deltaY.toFixed(0)} dx=${deltaX.toFixed(0)} scrolling=${isScrolling}`);
|
||||||
|
|
||||||
// Decide early whether this is a vertical scroll gesture.
|
// Decide early whether this is a vertical scroll gesture.
|
||||||
if (!isScrolling) {
|
if (!isScrolling) {
|
||||||
@@ -415,32 +413,34 @@ export const TerminalComponent = React.forwardRef<TerminalRef, TerminalProps>(
|
|||||||
}
|
}
|
||||||
if (!isScrolling) return;
|
if (!isScrolling) return;
|
||||||
|
|
||||||
// Take over the gesture so the page/toolbar never scrolls.
|
// Take over the gesture so the browser never scrolls the
|
||||||
|
// page or the terminal canvas. We synthesize wheel events
|
||||||
|
// and dispatch them into xterm.js, which already handles
|
||||||
|
// both normal buffer scrolling and alternate-screen apps
|
||||||
|
// (tmux/vim) correctly.
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
if (isAlternateScreen()) {
|
const target = container.querySelector(".xterm-viewport") as
|
||||||
// Alternate screen (tmux/vim): accumulate the swipe and
|
| HTMLElement
|
||||||
// send SGR 1006 mouse-wheel events in steps.
|
| null;
|
||||||
scrollPending += deltaY;
|
if (target) {
|
||||||
flushScroll();
|
showDebug(`dispatch wheel deltaY=${deltaY.toFixed(0)}`);
|
||||||
} else if (termRef.current) {
|
const wheel = new WheelEvent("wheel", {
|
||||||
// Normal buffer: let xterm.js scroll its own viewport by
|
deltaY,
|
||||||
// the number of lines corresponding to the swipe distance.
|
deltaX: 0,
|
||||||
const lineHeight =
|
deltaZ: 0,
|
||||||
termRef.current.options.fontSize != null
|
clientX: touch.clientX,
|
||||||
? (termRef.current.options.fontSize as number) * 1.2
|
clientY: touch.clientY,
|
||||||
: 10;
|
screenX: touch.screenX,
|
||||||
const lines = Math.round(deltaY / lineHeight);
|
screenY: touch.screenY,
|
||||||
if (lines !== 0) {
|
bubbles: true,
|
||||||
termRef.current.scrollLines(lines);
|
cancelable: true,
|
||||||
}
|
});
|
||||||
|
target.dispatchEvent(wheel);
|
||||||
}
|
}
|
||||||
startY = touch.clientY;
|
startY = touch.clientY;
|
||||||
};
|
};
|
||||||
const onTouchEnd = () => {
|
const onTouchEnd = () => {
|
||||||
if (isScrolling) {
|
|
||||||
flushScroll(true);
|
|
||||||
}
|
|
||||||
isScrolling = false;
|
isScrolling = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user