Skip to content

feat(react-grab): Alt+click selects all instances of the nearest component#454

Open
aidenybai wants to merge 2 commits into
mainfrom
cursor/alt-select-component-instances-f111
Open

feat(react-grab): Alt+click selects all instances of the nearest component#454
aidenybai wants to merge 2 commits into
mainfrom
cursor/alt-select-component-instances-f111

Conversation

@aidenybai

@aidenybai aidenybai commented Jun 3, 2026

Copy link
Copy Markdown
Owner

Summary

Holding Opt/Alt while selecting targets every rendered instance of the clicked element's nearest component at once. This works for grabbing/copying and for the Style (edit) panel, so a style tweak previews across all instances simultaneously.

Behaviors

  • Alt + hover → live highlight of every matching instance (so you can see what will be selected before committing).
  • Alt + click → selects/copies all instances (per-element labels).
  • Alt + click in Style/edit mode (or Alt + right-click → Style) → opens the Style panel with the live preview applied to all instances. Drag a slider / press arrows and every instance updates inline; dismiss restores them all.

For example, Alt-interacting with one TodoItem row targets all TodoItem rows. If only a single instance exists, it falls back to a normal single-element selection.

How it works (bippy)

utils/find-component-instances.ts resolves the nearest meaningfully-named component fiber from the clicked DOM node, then traverses each React root with bippy's traverseFiber to find fibers sharing that component type, mapping each back to its nearest host (DOM) node via getNearestHostFiber. Uses getFiberFromHostInstance, getDisplayName, isCompositeFiber, getType, traverseFiber, getNearestHostFiber, and _fiberRoots (with a fallback to the target's own root). The clicked instance is always returned first; capped by MAX_COMPONENT_INSTANCE_SELECTION.

Key changes

  • utils/find-component-instances.ts (new) — the bippy-based resolver.
  • utils/preview-styles.tscreatePreviewStyles now accepts one element or many, applying/restoring inline styles across all targets.
  • core/edit-mode.tstrigger accepts optional previewElements so the Style preview can span multiple elements.
  • core/index.tsx:
    • Threads event.altKey through handlePointerUphandleSingleClick; selectComponentInstances for the copy flow.
    • Alt branch in the pending-action (edit/comment select) path and in the contextmenu handler expands to all instances and aligns the primary element with the frozen set.
    • openEditMode passes all frozen instances as preview targets.
    • New isAltKeyHeld signal (tracked via pointer/keydown/keyup/blur) drives an altPreviewElements/altPreviewBounds memo that feeds selectionBoundsMultiple for the live hover highlight.
  • constants.tsMAX_COMPONENT_INSTANCE_SELECTION.

Testing

  • pnpm build, pnpm typecheck, pnpm lint, pnpm format pass.
  • New e2e/alt-select.spec.ts:
    • Alt+click selects all 7 TodoItem instances (and for a deeply-nested child).
    • Alt+right-click → Style applies a tweak to all 7 instances' inline styles.
    • Singleton falls back to single selection.
  • Regression: 216 existing tests pass across selection, shift-multi-select, drag-selection, edit-panel, context-menu, toolbar, keyboard-shortcuts (no regressions).
Open in Web Open in Cursor 

Summary by cubic

Opt/Alt+click now selects all rendered instances of the nearest named React component in react-grab, with a live Alt-hover preview of the full set; edits in Style/Edit mode apply across all instances. Falls back to a normal click when only one instance exists. Implements Linear f111.

  • New Features
    • Alt+click climbs to the nearest named component fiber, then uses bippy to collect same-type instances across roots; clicked instance first, capped by MAX_COMPONENT_INSTANCE_SELECTION (200).
    • Holding Alt shows multi-instance hover bounds and snap; Alt+right-click or Alt in default actions opens Style/Edit with all instances frozen, and createPreviewStyles previews and restores styles across them.
    • E2E: deep child Alt-selection, style tweak across all via Alt+right-click, and singleton fallback.

Written for commit 14ec394. Summary will update on new commits.

Review in cubic

Co-authored-by: Aiden Bai <aidenybai@users.noreply.github.com>
@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 3, 2026 11:25pm
react-grab-website Ready Ready Preview, Comment Jun 3, 2026 11: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@454
npm i https://pkg.pr.new/grab@454
npm i https://pkg.pr.new/react-grab@454

commit: 14ec394

@aidenybai aidenybai marked this pull request as ready for review June 3, 2026 23:00
// Holding Alt selects every rendered instance of the clicked element's
// nearest component at once, not just the element under the pointer.
if (isAltHeld && !store.pendingCommentMode && !isPendingContextMenuSelect()) {
const instanceElements = findComponentInstanceElements(selectedElement);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Alt select ignores pointer target

Medium Severity

Alt component-instance selection calls findComponentInstanceElements with selectedElement, which can come from detectedElement or a prior frozen selection when nothing grabbable is under the pointer. Shift multi-select only uses elementAtPointer, so an Alt+click on empty chrome can bulk-select every instance of a hovered or stale target instead of doing nothing.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 49fc5e6. Configure here.

@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

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/core/index.tsx">

<violation number="1" location="packages/react-grab/src/core/index.tsx:1931">
P2: The Alt-select path passes `selectedElement` to `findComponentInstanceElements`, but `selectedElement` can fall back to the previously frozen element or `detectedElement` when nothing grabbable is directly under the pointer. This means Alt+clicking on empty chrome (or outside any grabbable target) can unintentionally bulk-select all instances of a stale/hovered component. Consider guarding with `elementAtPointer` (similar to the Shift multi-select path) so that Alt+click on empty space is a no-op.</violation>
</file>

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

Re-trigger cubic

// Holding Alt selects every rendered instance of the clicked element's
// nearest component at once, not just the element under the pointer.
if (isAltHeld && !store.pendingCommentMode && !isPendingContextMenuSelect()) {
const instanceElements = findComponentInstanceElements(selectedElement);

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.

P2: The Alt-select path passes selectedElement to findComponentInstanceElements, but selectedElement can fall back to the previously frozen element or detectedElement when nothing grabbable is directly under the pointer. This means Alt+clicking on empty chrome (or outside any grabbable target) can unintentionally bulk-select all instances of a stale/hovered component. Consider guarding with elementAtPointer (similar to the Shift multi-select path) so that Alt+click on empty space is a no-op.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/react-grab/src/core/index.tsx, line 1931:

<comment>The Alt-select path passes `selectedElement` to `findComponentInstanceElements`, but `selectedElement` can fall back to the previously frozen element or `detectedElement` when nothing grabbable is directly under the pointer. This means Alt+clicking on empty chrome (or outside any grabbable target) can unintentionally bulk-select all instances of a stale/hovered component. Consider guarding with `elementAtPointer` (similar to the Shift multi-select path) so that Alt+click on empty space is a no-op.</comment>

<file context>
@@ -1891,6 +1925,17 @@ export const init = (rawOptions?: Options): ReactGrabAPI => {
+      // Holding Alt selects every rendered instance of the clicked element's
+      // nearest component at once, not just the element under the pointer.
+      if (isAltHeld && !store.pendingCommentMode && !isPendingContextMenuSelect()) {
+        const instanceElements = findComponentInstanceElements(selectedElement);
+        if (instanceElements.length > 1) {
+          keyboardSelectedElement = null;
</file context>

…yle/edit mode

Co-authored-by: Aiden Bai <aidenybai@users.noreply.github.com>

@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 2 potential issues.

There are 3 total unresolved issues (including 1 from previous review).

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 14ec394. Configure here.

selectionBounds: createElementBounds(element),
properties,
preview: createPreviewStyles(element),
preview: createPreviewStyles(previewTargets),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Style submit ignores multi-instance selection

Medium Severity

After Alt multi-select, openEditMode passes every frozen instance into createPreviewStyles, but submit still calls performCopyWithLabel with only currentState.element. Style tweaks preview on all instances, yet the post-submit copy flow describes a single element.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 14ec394. Configure here.

selectionBounds: createElementBounds(element),
properties,
preview: createPreviewStyles(element),
preview: createPreviewStyles(previewTargets),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Edit open collapses multi frozen selection

Medium Severity

When several instances are frozen via Alt, openEditMode supplies all of them as preview targets, but editMode.trigger still calls setFrozenElement, replacing frozenElements with a single entry. Overlays and labels show one selection while style preview updates every instance.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 14ec394. Configure here.

@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/core/index.tsx">

<violation number="1" location="packages/react-grab/src/core/index.tsx:1485">
P2: Style preview applies to all Alt-selected instances via `previewElements`, but `element` (single) is passed as the primary target to `editMode.trigger`. When the user submits from the edit panel, the copy/submit flow will reference only this single element rather than all previewed instances, creating a visual-vs-action mismatch.</violation>
</file>

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

Re-trigger cubic

store.frozenElements.length > 1 && store.frozenElements.includes(element)
? [...store.frozenElements]
: [element];
return editMode.trigger(element, position, overrides, previewElements);

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.

P2: Style preview applies to all Alt-selected instances via previewElements, but element (single) is passed as the primary target to editMode.trigger. When the user submits from the edit panel, the copy/submit flow will reference only this single element rather than all previewed instances, creating a visual-vs-action mismatch.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/react-grab/src/core/index.tsx, line 1485:

<comment>Style preview applies to all Alt-selected instances via `previewElements`, but `element` (single) is passed as the primary target to `editMode.trigger`. When the user submits from the edit panel, the copy/submit flow will reference only this single element rather than all previewed instances, creating a visual-vs-action mismatch.</comment>

<file context>
@@ -1453,7 +1475,15 @@ export const init = (rawOptions?: Options): ReactGrabAPI => {
+        store.frozenElements.length > 1 && store.frozenElements.includes(element)
+          ? [...store.frozenElements]
+          : [element];
+      return editMode.trigger(element, position, overrides, previewElements);
+    };
 
</file context>

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