diff --git a/src/components/TerminalViewport.tsx b/src/components/TerminalViewport.tsx index 0157f3a..65eb121 100644 --- a/src/components/TerminalViewport.tsx +++ b/src/components/TerminalViewport.tsx @@ -112,6 +112,7 @@ const TerminalViewport = React.forwardRef(null); const followOutputRef = React.useRef(true); const touchScrollCleanupRef = React.useRef<(() => void) | null>(null); + const mouseTrackingWheelCleanupRef = React.useRef<(() => void) | null>(null); const viewportDiscoveryTimeoutRef = React.useRef(null); const viewportDiscoveryAttemptsRef = React.useRef(0); const hiddenInputRef = React.useRef(null); @@ -996,6 +997,26 @@ const TerminalViewport = React.forwardRef { + canvas.removeEventListener('wheel', handleMouseTrackingWheel, { capture: true }); + }; + const handleMouseTrackingWheel = (event: WheelEvent) => { + const wasmTerm = terminal.wasmTerm; + if (wasmTerm && wasmTerm.hasMouseTracking()) { + event.stopImmediatePropagation(); + const rect = canvas.getBoundingClientRect(); + const x = Math.round((event.clientX - rect.left) / (rect.width / terminal.cols)); + const y = Math.round((event.clientY - rect.top) / (rect.height / terminal.rows)); + const button = event.deltaY < 0 ? 64 : 65; + terminal.input(`\x1b[<${button};${x};${y}M`); + terminal.input(`\x1b[<${button - 32};${x};${y}m`); + } + }; + canvas.addEventListener('wheel', handleMouseTrackingWheel, { capture: true }); + } + localTerminalTextarea = (terminal as unknown as { textarea?: HTMLTextAreaElement | null }).textarea ?? container.querySelector('textarea'); @@ -1082,6 +1103,8 @@ const TerminalViewport = React.forwardRef disposable.dispose()); restorePatchedScrollToBottom?.(); restorePatchedScrollToBottom = null; + mouseTrackingWheelCleanupRef.current?.(); + mouseTrackingWheelCleanupRef.current = null; if (localTerminalTextarea) { localTerminalTextarea.removeEventListener('focus', handleTerminalTextareaFocus); localTerminalTextarea.removeEventListener('blur', handleTerminalTextareaBlur);