Skip to content

Add mouse-move discard prompt for keyboard tweaks#447

Merged
aidenybai merged 4 commits into
mainfrom
cursor/mouse-move-discard-prompt-ea1a
Jun 14, 2026
Merged

Add mouse-move discard prompt for keyboard tweaks#447
aidenybai merged 4 commits into
mainfrom
cursor/mouse-move-discard-prompt-ea1a

Conversation

@aidenybai

@aidenybai aidenybai commented Jun 3, 2026

Copy link
Copy Markdown
Owner

Summary

  • show the edit panel discard prompt when mouse movement follows keyboard-driven pending tweaks
  • add a Copy action to the discard prompt that submits the same changes as Enter/header Copy
  • update e2e coverage for the new keyboard-to-mouse prompt flow

Verification

  • pnpm dlx --package @antfu/ni nr build
  • pnpm test
  • pnpm lint
  • pnpm typecheck
  • pnpm format
Open in Web Open in Cursor 

Summary by cubic

Adds a one-time discard prompt when the mouse moves after keyboard tweaks or arrow-key selection, with Copy to keep the edit or copy the keyboard-selected element. While shown, detection pauses, Expand is disabled, Enter confirms Copy/Yes, arrow keys are ignored, and clicks prefer the keyboard-selected element.

  • New Features

    • Edit panel: Keyboard commits arm a mouse-only handoff; pointer move or hover opens a discard prompt only if submittable tweaks are pending. Touch pointer moves are ignored. Movement while open is ignored. The handoff is consumed on open/cancel and cleared on Copy and outside-click. Adds EditPanelCopyButton, data-react-grab-discard-button="copy", and data-react-grab-copy-button.
    • Selection label: Arrow-key navigation arms a mouse-move handoff; moving the mouse shows “Discard selection?” with Yes/Copy (Cancel hidden). Enter activates Copy/Yes. Arrow keys are suppressed while the prompt is open. Copy preserves the arrow-selected element even if the mouse is over a different element. Expand is disabled while the prompt is shown. Adds data-react-grab-discard-copy, data-react-grab-discard-yes (and -no for the standard prompt).
  • Refactors

    • Introduced createPointerMovePromptHandoff and createKeyboardSelectionController. Replaced isPendingDismiss/onCancelDismiss with a single discardPrompt?: SelectionDiscardPrompt that uses an isKeyboardSelection flag across renderer/label and updated stories.
    • Gated Enter-to-expand on discardPrompt so Enter triggers Copy/Yes, and threaded commit sources ("keyboard" | "pointer") through steppers/color picker to arm the handoff only for keyboard commits. Removed unreachable guards and dead props. Expanded e2e coverage for discard flows, including touch pointer moves and Enter on Copy/Yes.

Written for commit ec80831. Summary will update on new commits.

Review in cubic

@vercel

vercel Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
react-grab-storybook Ready Ready Preview, Comment Jun 13, 2026 10:25pm
react-grab-website Ready Ready Preview, Comment Jun 13, 2026 10:25pm

@pkg-pr-new

pkg-pr-new Bot commented Jun 3, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/@react-grab/cli@447
npm i https://pkg.pr.new/grab@447
npm i https://pkg.pr.new/react-grab@447

commit: ec80831

Comment thread packages/react-grab/src/components/edit-panel/index.tsx
Comment thread packages/react-grab/src/components/edit-panel/index.tsx Outdated
Comment thread packages/react-grab/src/components/edit-panel/index.tsx Outdated

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

3 issues found across 3 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="packages/react-grab/src/components/edit-panel/index.tsx">

<violation number="1" location="packages/react-grab/src/components/edit-panel/index.tsx:307">
P2: `navigateActive` arms `shouldPromptOnPointerMove` whenever `hasPendingTweaks()` is true, but this includes pending edits created by pointer-only slider changes (not keyboard). If a user drags a slider, then navigates with Arrow keys, this will incorrectly trigger the discard prompt on subsequent mouse movement. Consider gating this on a flag that tracks whether the pending tweaks originated from keyboard stepping (e.g., check `isCompact()` or introduce a dedicated `hasKeyboardTweaks` signal).</violation>

<violation number="2" location="packages/react-grab/src/components/edit-panel/index.tsx:435">
P2: Pointer-triggered discard prompt is never disarmed after first open, so cancelling once causes the prompt to reopen on every subsequent mouse move.</violation>

<violation number="3" location="packages/react-grab/src/components/edit-panel/index.tsx:435">
P2: Race condition: browsers fire `pointermove` before `mousedown` during a click gesture. When the user clicks outside the panel after a keyboard tweak, this handler opens the discard prompt via `attemptDismiss("pointer")`, but the same click's `mousedown` event then triggers the outside-dismiss logic, potentially discarding the edits immediately without user confirmation. Consider disarming `shouldPromptOnPointerMove` once `attemptDismiss` is called here, or preventing the outside-click dismiss handler from running when a discard prompt is already visible.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread packages/react-grab/src/components/edit-panel/index.tsx
Comment thread packages/react-grab/src/components/edit-panel/index.tsx
Comment thread packages/react-grab/src/components/edit-panel/index.tsx Outdated
Comment thread packages/react-grab/src/components/edit-panel/index.tsx Outdated

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

1 issue found across 4 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="packages/react-grab/src/components/edit-panel/index.tsx">

<violation number="1" location="packages/react-grab/src/components/edit-panel/index.tsx:307">
P2: `navigateActive` arms `shouldPromptOnPointerMove` whenever `hasPendingTweaks()` is true, but this includes pending edits created by pointer-only slider changes (not keyboard). If a user drags a slider, then navigates with Arrow keys, this will incorrectly trigger the discard prompt on subsequent mouse movement. Consider gating this on a flag that tracks whether the pending tweaks originated from keyboard stepping (e.g., check `isCompact()` or introduce a dedicated `hasKeyboardTweaks` signal).</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread packages/react-grab/src/components/edit-panel/index.tsx
Comment thread packages/react-grab/src/components/edit-panel/index.tsx Outdated
Comment thread packages/react-grab/src/components/edit-panel/index.tsx Outdated

@vercel vercel Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Additional Suggestion:

Clicking the "No" button to cancel the discard prompt doesn't reset the isPointerMovePromptArmed flag, causing the prompt to trigger again immediately on the next mouse movement

Fix on Vercel

Comment thread packages/react-grab/src/components/edit-panel/index.tsx
Comment thread packages/react-grab/src/components/edit-panel/index.tsx
Comment thread packages/react-grab/src/components/edit-panel/index.tsx
Comment thread packages/react-grab/src/core/index.tsx Outdated

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

1 issue found across 8 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="packages/react-grab/src/components/selection-label/discard-prompt.tsx">

<violation number="1" location="packages/react-grab/src/components/selection-label/discard-prompt.tsx:73">
P2: The new Copy button is not keyboard-activatable with Enter because the prompt’s capture-phase key handler forces Enter to `onConfirm`, causing discard instead of copy.</violation>
</file>

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

@vercel vercel Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Additional Suggestions:

  1. Enter key handling in discard prompt always calls onConfirm() regardless of which button has focus, breaking keyboard accessibility
  1. commitActive function doesn't pass source: "keyboard" to commit() calls, breaking the mouse-move discard prompt for keyboard-sourced edits in inline controls
  1. Enter key in discard prompt unconditionally calls props.onConfirm() without checking which button has focus, breaking keyboard accessibility
  1. Click on arrow-selected element bypasses discard prompt when no mouse movement occurs - the arrow navigation pointer handoff is not checked in handleSingleClick

Fix on Vercel

Comment thread packages/react-grab/src/components/edit-panel/index.tsx
Comment thread packages/react-grab/src/components/edit-panel/index.tsx
Comment thread packages/react-grab/src/components/edit-panel/index.tsx

@cursor cursor 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.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

There are 3 total unresolved issues (including 2 from previous reviews).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 640a121. Configure here.

Comment thread packages/react-grab/src/core/index.tsx

@vercel vercel Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Additional Suggestions:

  1. Enter key handling unconditionally calls onConfirm() regardless of which button has focus, breaking keyboard accessibility when Copy or No buttons are focused
  1. pointerup event handler doesn't check isPendingDismiss(), allowing click actions to bypass the discard prompt when keyboard selection has a pending dismissal
  1. commitActive() function calls commit() without passing source option, preventing pointerMovePromptHandoff.arm() from being triggered when edits come from keyboard sources like ValueStepper text input or ColorPicker text input
  1. Enter key in discard prompt unconditionally calls onConfirm() regardless of which button has focus, breaking keyboard accessibility
  1. Arrow navigation menu selections don't arm the mouse-move discard prompt, unlike keyboard arrow navigation

Fix on Vercel

Comment thread packages/react-grab/src/components/edit-panel/index.tsx
Comment thread packages/react-grab/src/components/edit-panel/index.tsx

@vercel vercel Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Additional Suggestions:

  1. handleSubmit() function submits edits but does not close the edit panel, leaving it open when it should close after copying
  1. The pointerup event handler does not check keyboardSelection.isPendingDismiss(), allowing click actions to bypass the discard prompt when keyboard selection has a pending dismissal

Fix on Vercel

Comment thread packages/react-grab/src/components/edit-panel/copy-button.tsx
Comment thread packages/react-grab/src/components/edit-panel/index.tsx
cursoragent and others added 2 commits June 13, 2026 03:31
Show the edit panel discard prompt when mouse movement follows
keyboard-driven pending tweaks, add a Copy action to the discard prompt,
and prompt before discarding an arrow-key selection on mouse handoff via
a keyboard-selection controller.
- Gate the selection label's Enter-to-expand on discardPrompt so Enter on
  the keyboard-selection prompt's Copy/Yes copies/discards instead of
  opening the Style panel (matches the existing canToggleExpand gate).
- Remove the unreachable keyboard-selection cancel/re-arm path, the dead
  isPendingDismiss/onCancelDismiss renderer/label props plus the renderer
  fallback (migrate openstory stories to the discardPrompt union), an
  unreachable pointermove guard, commitActive's unreachable default
  source, and an unneeded signal in the pointer-move handoff.

@vercel vercel Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Additional Suggestion:

pointerup event handler doesn't check keyboardSelection.isPendingDismiss(), allowing click actions to bypass the discard prompt when keyboard selection has pending dismissal

Fix on Vercel

Comment thread packages/react-grab/src/core/index.tsx
Comment thread packages/react-grab/src/components/edit-panel/copy-button.tsx
Comment thread packages/react-grab/src/components/edit-panel/index.tsx
Comment thread packages/react-grab/src/components/edit-panel/index.tsx
@aidenybai

Copy link
Copy Markdown
Owner Author

Triaged the latest Vercel VADE suggestions as false-positives (resolved, not fixed):

  • "handleSubmit / discard-prompt Copy doesn't close the panel" — it does. handleSubmit copies via props.onSubmit(...), which flows to the edit-mode controller's submit()clearAll() (panel state set to null). It intentionally avoids onDismiss (that's the discard-without-copy path). Covered by edit-panel.spec.ts:288 ("mouse movement after keyboard tweak opens discard prompt with Copy"), which clicks the discard Copy and asserts isEditPanelVisible === false. The suggested extra closePanel("preserve") would double-dismiss an already-cleared panel.
  • "copy-button uses wrong data attribute, breaking Enter" — the edit-panel discard prompt uses the data-react-grab-discard-button scheme; handleSearchKeyDown checks [data-react-grab-discard-button] (index.tsx:457) and EditPanelCopyButton sets data-react-grab-discard-button="copy", so Enter→click→Copy works. data-react-grab-discard-copy is the selection-label prompt's separate scheme.
  • "clicking arrow-navigated element bypasses discard prompt" — intended: a click copies the arrow-selected element; the discard prompt is only for mouse movement (leave-intent). Covered by keyboard-navigation.spec.ts:135.

The two discard prompts differ little in practice, so collapse the
StandardSelectionDiscardPrompt | KeyboardSelectionDiscardPrompt union into
a single SelectionDiscardPrompt with an isKeyboardSelection flag. Simpler
to construct and read; the label-rendering ternaries collapse accordingly.
Edit panel: mouse move with no pending tweaks stays silent, the prompt's
Yes reverts the tweak, a fresh keyboard commit re-arms the consumed
one-shot handoff, an autoApplied class arms it, and a touch pointermove
neither opens the prompt nor consumes the arm.

Keyboard selection: Enter on the focused Copy copies without opening the
Style panel (guards the Enter-to-expand regression), Enter on Yes discards
without copying, and arrow keys are ignored while the prompt is open.
@aidenybai aidenybai merged commit c0e9887 into main Jun 14, 2026
17 checks passed
@aidenybai aidenybai deleted the cursor/mouse-move-discard-prompt-ea1a branch June 14, 2026 00:17
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.

2 participants