atagen

malice

public
Star0
Watch0

Commit 56f87be03935

tarball

atagen <boss@atagen.co>2026-10-27 05:42 UTC

2 files changed+24-4
web: stop the hidden pointer from hovering what scrolls under it

While typing, the hidden pointer stayed where it was, so each message scrolling under it took the hover, lit up and opened its reaction toolbar, and links under it made Helium show its URL bubble over the composer. While hidden, nothing takes pointer events except the scroller under it, which keeps the wheel working.
Modified file: web/src/App.svelte+22 additions−4 deletions
@@ -315,19 +315,37 @@
315315 return target instanceof HTMLTextAreaElement || target instanceof HTMLInputElement || target.isContentEditable;
316316 }
317317
318+ function scrollerAt(x: number, y: number): HTMLElement | null {
319+ for (let el = document.elementFromPoint(x, y); el instanceof HTMLElement; el = el.parentElement) {
320+ const { overflowY } = getComputedStyle(el);
321+ if ((overflowY === 'auto' || overflowY === 'scroll') && el.scrollHeight > el.clientHeight) return el;
322+ }
323+ return null;
324+ }
325+
318326 onMount(() => {
319327 const onPopState = (ev: PopStateEvent) => {
320328 if (!backStack.pop(ev.state)) void restoreRoute();
321329 };
322330 const onAttention = () => queueReadReceipt();
331+ let pointerAt: { x: number; y: number } | null = null;
332+ let pointerScroller: HTMLElement | null = null;
323333 const hidePointer = (e: KeyboardEvent) => {
324334 if (e.ctrlKey || e.metaKey || e.altKey || !isEditable(e.target)) return;
325- if (e.key.length === 1 || e.key === 'Backspace' || e.key === 'Delete' || e.key === 'Enter') {
326- document.documentElement.classList.add('pointer-hidden');
327- }
335+ if (e.key.length !== 1 && e.key !== 'Backspace' && e.key !== 'Delete' && e.key !== 'Enter') return;
336+ const root = document.documentElement;
337+ if (root.classList.contains('pointer-hidden')) return;
338+ pointerScroller = pointerAt ? scrollerAt(pointerAt.x, pointerAt.y) : null;
339+ pointerScroller?.setAttribute('data-pointer-scroll', '');
340+ root.classList.add('pointer-hidden');
341+ };
342+ const showPointer = () => {
343+ document.documentElement.classList.remove('pointer-hidden');
344+ pointerScroller?.removeAttribute('data-pointer-scroll');
345+ pointerScroller = null;
328346 };
329- const showPointer = () => document.documentElement.classList.remove('pointer-hidden');
330347 const movePointer = (e: PointerEvent) => {
348+ pointerAt = { x: e.clientX, y: e.clientY };
331349 if (e.movementX !== 0 || e.movementY !== 0) showPointer();
332350 };
333351 backStack.init();
Modified file: web/src/app.css+2 additions
@@ -156,6 +156,8 @@ html, body, #app {
156156 }
157157
158158 html.pointer-hidden, html.pointer-hidden * { cursor: none !important; }
159+/* A still pointer keeps hovering whatever scrolls under it, lighting rows and the browser's link URL bubble. The scroller under it keeps the wheel. */
160+html.pointer-hidden body *:not([data-pointer-scroll]) { pointer-events: none !important; }
159161
160162 body {
161163 background: var(--bg);