Skip to content

fix(Draggable): defer captureGesture to first motion (A22)#84

Merged
re-marked merged 2 commits into
mainfrom
fix/draggable-defer-capture-to-motion
May 9, 2026
Merged

fix(Draggable): defer captureGesture to first motion (A22)#84
re-marked merged 2 commits into
mainfrom
fix/draggable-defer-capture-to-motion

Conversation

@re-marked

Copy link
Copy Markdown
Owner

Closes #72 (A22). Pairs with #81 (A21) to fully fix #55 (A3).

What

`MouseDownEvent` gains `captureGestureTentatively(handlers)`. Same first-call-wins rule as `captureGesture` (cross-protected — confirmed and tentative compete via the same single-slot field).

`Draggable` switches its press handler to `captureGestureTentatively`. App-level coordinator (`App.tsx`) gets `activeGestureTentative` flag and:

  • Press: tentative capture installs the gesture but does NOT skip selection-start / multi-click bookkeeping.
  • Motion: first motion promotes (cancels in-progress selection, clears tentative flag, fires onMove). After this point behaves identically to a confirmed capture.
  • Release without motion: drops the gesture silently (no `onUp`) and falls through to normal release path → click dispatches to descendants normally.
  • Lost-release recovery (focus-out, no-button-motion, fresh-press-while-active): tentative gestures drop silently; confirmed gestures still get `onUp` for cleanup.

Net effect

`I want clicks!` works on first try. No `stopImmediatePropagation` boilerplate. Same for `` / `` / any clickable child.

Test plan

  • 708 tests passing (was 703; +5 for the new tentative API at the MouseDownEvent + dispatchMouseDown layers, plus updated Draggable assertion)
  • typecheck / lint / build clean

API surface

  • New: `MouseDownEvent.captureGestureTentatively(handlers)`, `MouseDownEvent.gestureTentative`
  • Changed: `dispatchMouseDown` returns `CapturedGesture | null` (was `GestureHandlers | null`); same for `Ink.dispatchMouseDown` and `App.props.onMouseDownAt`. Tests updated.
  • Existing `captureGesture` semantic unchanged — still confirmed-immediate, still first-call-wins.

Cross-refs

Adds captureGestureTentatively to MouseDownEvent. Tentative captures
drop silently on release-without-motion and fall through to normal
click dispatch — descendants with onClick now receive their click on
a press-without-motion inside a Draggable, no boilerplate needed.
First motion promotes the gesture (cancels in-progress selection,
fires onMove). dispatchMouseDown returns CapturedGesture instead of
GestureHandlers; App.activeGestureTentative tracks the new state.

Co-Authored-By: Mark <psyhik17@gmail.com>
Co-Authored-By: Claude <noreply@anthropic.com>
@re-marked re-marked added the source:shakedown Imported from the local Yokai shakedown issue list label May 9, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 996827e9b4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +814 to +815
if (app.activeGestureTentative) {
app.activeGestureTentative = false

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reset click cadence when tentative capture becomes a drag

When a tentative Draggable press receives its first motion, it is now a real drag, but the press-time clickCount/lastClick* state that was updated below remains intact. After a small drag or trackpad jitter, a quick next click within the multi-click window and distance is treated as click #2 and goes through onMultiClick, suppressing the intended onClick; confirmed captures avoid this by resetting clickCount when they short-circuit. Clear the multi-click chain on promotion so promoted tentative drags behave like the existing captured-gesture path.

Useful? React with 👍 / 👎.

@re-marked
re-marked merged commit ad7f75c into main May 9, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

source:shakedown Imported from the local Yokai shakedown issue list

Projects

None yet

Development

Successfully merging this pull request may close these issues.

A22: Draggable's eager captureGesture on press claims gestures it doesn't end up using A3: <Draggable> gesture capture eats onClick on inner children

1 participant