experiments in a post-browser web
10
fork

Configure Feed

Select the types of activity you want to include in your feed.

fix(page): require mouse movement for webview hold-to-drag, debounce bridge injection

- Hold timer now sets a ready flag instead of immediately activating the drag
overlay. Overlay only activates when mouse actually moves, so clicks that
hold >80ms (e.g. YouTube pause button) still reach the webview.
- Debounce did-navigate mouse bridge and opener shim injection timers to
prevent MaxListenersExceededWarning from rapid SPA navigations.
- Silence expected executeJavaScript errors during navigation.

+34 -14
+34 -14
app/page/page.js
··· 394 394 let webviewHoldTimer = null; 395 395 let webviewHoldScreenX = 0; 396 396 let webviewHoldScreenY = 0; 397 + let webviewHoldReady = false; // true after hold threshold, waiting for movement to start drag 397 398 398 399 function cancelWebviewHold() { 399 400 if (webviewHoldTimer) { 400 401 clearTimeout(webviewHoldTimer); 401 402 webviewHoldTimer = null; 402 403 } 404 + webviewHoldReady = false; 403 405 } 404 406 405 407 webview.addEventListener('console-message', (e) => { ··· 420 422 webviewHoldTimer = setTimeout(() => { 421 423 webviewHoldTimer = null; 422 424 if (!webviewMouseDown) return; // Released before threshold 423 - // Activate the overlay so it captures all subsequent mouse events for the drag 424 - dragOverlay.classList.add('active'); 425 - pageMouseButtonDown = true; 426 - startDrag(webviewHoldScreenX, webviewHoldScreenY); 427 - DEBUG && console.log('[page] Webview hold-drag started at', webviewHoldScreenX, webviewHoldScreenY); 425 + // Don't activate overlay yet — wait for mouse movement to confirm drag intent. 426 + // Activating immediately on hold would block the webview from receiving mouseup, 427 + // preventing click events (e.g., YouTube pause button) for holds > 80ms. 428 + webviewHoldReady = true; 429 + DEBUG && console.log('[page] Webview hold ready, waiting for movement'); 428 430 }, DRAG_HOLD_THRESHOLD); 429 431 return; 430 432 } ··· 463 465 return; 464 466 } 465 467 468 + // Hold threshold passed and mouse is moving — start drag now 469 + if (webviewHoldReady) { 470 + webviewHoldReady = false; 471 + dragOverlay.classList.add('active'); 472 + pageMouseButtonDown = true; 473 + startDrag(webviewHoldScreenX, webviewHoldScreenY); 474 + DEBUG && console.log('[page] Webview hold-drag started at', webviewHoldScreenX, webviewHoldScreenY); 475 + return; 476 + } 477 + 466 478 if (!webviewHoldTimer) return; // No pending hold 467 479 const dx = Math.abs(screenX - webviewHoldScreenX); 468 480 const dy = Math.abs(screenY - webviewHoldScreenY); ··· 503 515 } 504 516 }); 505 517 518 + let mouseBridgeTimer = null; 519 + 506 520 function injectMouseBridge() { 507 - webview.executeJavaScript(WEBVIEW_MOUSE_BRIDGE_JS).catch(err => { 508 - DEBUG && console.log('[page] Failed to inject mouse bridge:', err.message); 509 - }); 521 + webview.executeJavaScript(WEBVIEW_MOUSE_BRIDGE_JS).catch(() => {}); 510 522 } 511 523 512 524 // Inject on dom-ready (fires on each full navigation) 513 525 webview.addEventListener('dom-ready', injectMouseBridge); 514 526 515 - // Also inject on did-navigate for robustness (some navigations may not fire dom-ready) 527 + // Also inject on did-navigate for robustness (some navigations may not fire dom-ready). 528 + // Cancel any pending injection from a previous navigation to avoid listener buildup. 516 529 webview.addEventListener('did-navigate', () => { 517 - setTimeout(injectMouseBridge, 50); 530 + if (mouseBridgeTimer) clearTimeout(mouseBridgeTimer); 531 + mouseBridgeTimer = setTimeout(() => { 532 + mouseBridgeTimer = null; 533 + injectMouseBridge(); 534 + }, 50); 518 535 }); 519 536 520 537 dragOverlay.addEventListener('mouseup', () => { ··· 1325 1342 ` : null; 1326 1343 1327 1344 if (OPENER_SHIM_JS) { 1345 + let openerShimTimer = null; 1328 1346 function injectOpenerShim() { 1329 - webview.executeJavaScript(OPENER_SHIM_JS).catch(err => { 1330 - DEBUG && console.log('[page] Failed to inject opener shim:', err.message); 1331 - }); 1347 + webview.executeJavaScript(OPENER_SHIM_JS).catch(() => {}); 1332 1348 } 1333 1349 webview.addEventListener('dom-ready', injectOpenerShim); 1334 1350 webview.addEventListener('did-navigate', () => { 1335 - setTimeout(injectOpenerShim, 50); 1351 + if (openerShimTimer) clearTimeout(openerShimTimer); 1352 + openerShimTimer = setTimeout(() => { 1353 + openerShimTimer = null; 1354 + injectOpenerShim(); 1355 + }, 50); 1336 1356 }); 1337 1357 DEBUG && console.log('[page] Opener shim will be injected (openerUrl:', openerUrl, ')'); 1338 1358 }