Skip to content

fix(ui): restore triple-click paragraph selection#546

Merged
backnotprop merged 1 commit intomainfrom
fix/triple-click
Apr 12, 2026
Merged

fix(ui): restore triple-click paragraph selection#546
backnotprop merged 1 commit intomainfrom
fix/triple-click

Conversation

@backnotprop
Copy link
Copy Markdown
Owner

Summary

  • Skip outside-click dismiss on multi-click (event.detail >= 2) in useDismissOnOutsideAndEscape
  • One-line guard + comment in packages/ui/hooks/useDismissOnOutsideAndEscape.ts

Root Cause

Regression from 39b63c9 ("dismiss annotation toolbars on outside click and Escape", #182). That commit added a pointerdown capture listener to dismiss the annotation toolbar on outside click. During a triple-click:

  1. 2nd mouseup — web-highlighter wraps the word in <mark>, toolbar mounts, dismiss hook starts listening
  2. 3rd pointerdown (fires before mousedown) — dismiss hook fires → handleToolbarClose() removes the <mark> and normalises text nodes
  3. 3rd mousedown — the DOM mutation in step 2 changed the element under the cursor (from <mark> to plain text), resetting the browser's click-count. The 3rd click is treated as detail=1 instead of detail=3
  4. 3rd mouseupwindow.getSelection().isCollapsed is true → no highlight → no toolbar

Fix

Guard handlePointerDown with event.detail >= 2. Multi-click pointerdowns are part of an active selection gesture — the web-highlighter CREATE handler already cleans up stale pending highlights when a new selection commits, so the eager dismiss is unnecessary.

Test plan

  • Double-click a word → toolbar appears for the word
  • Triple-click a paragraph → toolbar appears for the full paragraph/line
  • Single-click outside toolbar → toolbar dismisses
  • Double-click at a new location while toolbar is showing → old toolbar replaced by new word toolbar
  • Press Escape → toolbar dismisses
  • Code review: click outside annotation toolbar → still dismisses
  • Mobile/touch: tap outside toolbar → still dismisses (touch detail is 0)

Fixes #544

Switch outside-click dismiss from pointerdown to mousedown and skip
on multi-click (event.detail >= 2).

The capture-phase pointerdown listener from useDismissOnOutsideAndEscape
was calling handleToolbarClose on the 3rd click of a triple-click,
which removed the word <mark> and normalised text nodes.  That DOM
mutation between pointerdown and mousedown reset the browser's
click-count tracking, so mousedown fired with detail=1 instead of
detail=3 — placing a cursor instead of selecting the paragraph.

Using mousedown instead of pointerdown because MouseEvent.detail is
spec-guaranteed to carry the click count (UI Events spec), whereas
the Pointer Events spec says PointerEvent.detail SHOULD be 0.

Multi-click mousedowns are part of an active selection gesture; the
web-highlighter CREATE handler already cleans up stale pending
highlights when a new selection commits, so the eager dismiss is
unnecessary.

Side-benefit: touch scroll gestures no longer dismiss the toolbar
(browsers suppress compatibility mousedown for scrolls); only taps
dismiss.  closeOnScrollOut already handles the anchor-scrolled-away
case.

Regression introduced in 39b63c9.
Fixes #544

For provenance purposes, this commit was AI assisted.
@backnotprop backnotprop merged commit 8ee7c6b into main Apr 12, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug: triple-click closes the selected text tooltip

1 participant