From 33236ac531b008b40905dba68c6325cbac86cb9c Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 12 Mar 2026 07:18:03 +0000 Subject: [PATCH] fix: use pointer events for backdrop close instead of timeout guard Pointer events are never synthetically re-dispatched from touch (unlike mouse events), so the tap that opened the editor cannot ghost-click the overlay. This eliminates the mobile first-load bug without relying on an arbitrary delay. Both pointerdown and pointerup must target the overlay itself to trigger close, which also handles the text-selection-outside-editor edge case. https://claude.ai/code/session_018DYwSysqysMmaRi5XkqTpo --- src/lib/components/NoteEditor.svelte | 35 +++++++++++----------------- 1 file changed, 13 insertions(+), 22 deletions(-) diff --git a/src/lib/components/NoteEditor.svelte b/src/lib/components/NoteEditor.svelte index feb819e..6ac7ade 100644 --- a/src/lib/components/NoteEditor.svelte +++ b/src/lib/components/NoteEditor.svelte @@ -170,28 +170,19 @@ bgStyle = `background-color: ${colors.bg}`; }); - // Track whether mousedown started on the overlay (not inside the editor). - // Prevents closing when the user selects text inside the editor and - // releases the mouse outside — the click event fires on the overlay, - // but we only want to close if the press also started there. - let mousedownOnOverlay = false; + // Use pointer events to detect backdrop clicks. Unlike mouse events, + // pointer events are never synthetically re-dispatched from touch, so + // the tap that opened the editor on mobile cannot ghost-click the overlay. + // Both pointerdown and pointerup must target the overlay itself to close. + let pointerdownOnOverlay = false; - // On mobile, the tap that opens the editor produces synthetic mouse events - // (mousedown → mouseup → click) at the original touch coordinates. If those - // coordinates land on the overlay backdrop, the editor would close instantly. - // On first page load, synthetic mousedown can arrive ~300ms after the tap - // (well after a single rAF). Use a timestamp guard to ignore early events. - const mountTime = Date.now(); - const OVERLAY_READY_DELAY = 400; - - function handleOverlayMousedown(e: MouseEvent) { - if (Date.now() - mountTime < OVERLAY_READY_DELAY) return; - mousedownOnOverlay = e.target === e.currentTarget; + function handleOverlayPointerdown(e: PointerEvent) { + pointerdownOnOverlay = e.target === e.currentTarget; } - function handleOverlayClick() { - if (mousedownOnOverlay) saveAndClose(); - mousedownOnOverlay = false; + function handleOverlayPointerup(e: PointerEvent) { + if (pointerdownOnOverlay && e.target === e.currentTarget) saveAndClose(); + pointerdownOnOverlay = false; } async function saveAndClose() { @@ -255,8 +246,8 @@
@@ -264,7 +255,7 @@
e.stopPropagation()} + onpointerdown={(e) => e.stopPropagation()} onkeydown={(e) => { e.stopPropagation(); handleKeydown(e); }} data-testid="note-editor" >