MCPcopy Create free account
hub / github.com/coder/ghostty-web / processMouseMove

Method processMouseMove

lib/terminal.ts:1266–1394  ·  view source on GitHub ↗

* Process mouse move for link detection (internal, called by throttled handler)

(e: MouseEvent)

Source from the content-addressed store, hash-verified

1264 * Process mouse move for link detection (internal, called by throttled handler)
1265 */
1266 private processMouseMove(e: MouseEvent): void {
1267 if (!this.canvas || !this.renderer || !this.linkDetector || !this.wasmTerm) return;
1268
1269 // Convert mouse coordinates to terminal cell position
1270 const rect = this.canvas.getBoundingClientRect();
1271 const x = Math.floor((e.clientX - rect.left) / this.renderer.charWidth);
1272 const y = Math.floor((e.clientY - rect.top) / this.renderer.charHeight);
1273
1274 // Get hyperlink_id directly from the cell at this position
1275 // Must account for viewportY (scrollback position)
1276 const viewportRow = y; // Row in the viewport (0 to rows-1)
1277 let hyperlinkId = 0;
1278
1279 // When scrolled, fetch from scrollback or screen based on position
1280 // NOTE: viewportY may be fractional during smooth scrolling. The renderer
1281 // uses Math.floor(viewportY) when mapping viewport rows to scrollback vs
1282 // screen; we mirror that logic here so link hit-testing matches what the
1283 // user sees on screen.
1284 let line: GhosttyCell[] | null = null;
1285 const rawViewportY = this.getViewportY();
1286 const viewportY = Math.max(0, Math.floor(rawViewportY));
1287 if (viewportY > 0) {
1288 const scrollbackLength = this.wasmTerm.getScrollbackLength();
1289 if (viewportRow < viewportY) {
1290 // Mouse is over scrollback content
1291 const scrollbackOffset = scrollbackLength - viewportY + viewportRow;
1292 line = this.wasmTerm.getScrollbackLine(scrollbackOffset);
1293 } else {
1294 // Mouse is over screen content (bottom part of viewport)
1295 const screenRow = viewportRow - viewportY;
1296 line = this.wasmTerm.getLine(screenRow);
1297 }
1298 } else {
1299 // At bottom - just use screen buffer
1300 line = this.wasmTerm.getLine(viewportRow);
1301 }
1302
1303 if (line && x >= 0 && x < line.length) {
1304 hyperlinkId = line[x].hyperlink_id;
1305 }
1306
1307 // Update renderer for underline rendering
1308 const previousHyperlinkId = (this.renderer as any).hoveredHyperlinkId || 0;
1309 if (hyperlinkId !== previousHyperlinkId) {
1310 this.renderer.setHoveredHyperlinkId(hyperlinkId);
1311
1312 // The 60fps render loop will pick up the change automatically
1313 // No need to force a render - this keeps performance smooth
1314 }
1315
1316 // Check if there's a link at this position (for click handling and cursor)
1317 // Buffer API expects absolute buffer coordinates (including scrollback)
1318 // When scrolled, we need to adjust the buffer row based on viewportY
1319 const scrollbackLength = this.wasmTerm.getScrollbackLength();
1320 let bufferRow: number;
1321
1322 // Use floored viewportY for buffer mapping (must match renderer & selection)
1323 const rawViewportYForBuffer = this.getViewportY();

Callers 1

TerminalClass · 0.95

Calls 8

getViewportYMethod · 0.95
setHoveredHyperlinkIdMethod · 0.80
getLinkAtMethod · 0.80
hoverMethod · 0.80
setHoveredLinkRangeMethod · 0.80
getScrollbackLengthMethod · 0.65
getScrollbackLineMethod · 0.65
getLineMethod · 0.65

Tested by

no test coverage detected