Skip to content

Resolve grabs to app source instead of UI library wrappers#446

Merged
aidenybai merged 67 commits into
mainfrom
cursor/skip-package-source-frames-e88f
Jun 13, 2026
Merged

Resolve grabs to app source instead of UI library wrappers#446
aidenybai merged 67 commits into
mainfrom
cursor/skip-package-source-frames-e88f

Conversation

@aidenybai

@aidenybai aidenybai commented Jun 2, 2026

Copy link
Copy Markdown
Owner

Why

When you grab an element, React Grab resolves its source location for the label, "open file", and the copied snippet. Today it trusts the first real on-disk source it finds — React's fiber _debugSource or the top owner-stack frame. That file is often a design-system / UI-library wrapper (shadcn components/ui, Radix, Pebble, …), not the app component that actually rendered it.

So grabbing a button can point you at components/ui/button.tsx or @radix-ui/react-* instead of the page that used it — sending you and the agent into wrapper code rather than your own.

What changed

Grabs now resolve to your app's source, falling back to wrappers/packages only when there's nothing better.

  • Prefer app-owned source over both third-party packages and local UI wrappers.
  • De-prioritize components/ui by default for shadcn-style wrappers.
  • Detect third-party frames more reliably, including dependency sourcemap paths that have no node_modules segment (bundled/minified deps, relative scoped paths like ../@radix-ui/...).
  • Preserve package names in stack output when a dependency frame is the best available context.
  • Keep wrapper/package frames as fallback locations so grabs still return useful source when no app file is present.
  • Include per-element source, formatted stack context, and sanitized stack frames in the structured application/x-react-grab clipboard payload.

New option: source.ignorePaths

Mark your own design-system paths as wrappers. This accepts strings or RegExp; strings also work via script-tag data-options.

init({
  source: {
    ignorePaths: ["src/design-system", /\/packages\/ui\//],
  },
});

This is purely additive — Options.source is optional and existing behavior is unchanged when it's omitted.

How it works

A single source-frame policy decides whether a frame is:

  1. app code
  2. an ignored local wrapper
  3. a third-party package
  4. unknown / not useful as source

Resolution uses that same order:

app code → ignored local wrapper → package source

React's fiber source is considered first within each tier, then owner-stack frames. The same policy is used everywhere source is resolved: resolveSource, copy flow, getStackContext, getElementContext, primitives, and snippet generation.

Notes

  • Package-name parsing stays focused on unambiguous marker-based paths like node_modules, Vite optimized deps, and CDN URLs.
  • Ambiguous relative scoped sourcemap paths are handled inside the source-frame policy, where the app-vs-package decision belongs.
  • Raw React fiber source is cached before policy filtering, so custom ignore rules can be applied without changing what React reported.
  • Ignored wrapper frames are skipped in formatted stack output, but can still be used as fallback source locations when no app frame exists.
  • Plain text and the custom React Grab clipboard payload now expose the same trace context, so CLI/native readers and other structured consumers can use it without reparsing text.

Testing

  • nr build
  • pnpm test (Playwright e2e + Vitest unit, including parser/policy/source-selection tests)
  • pnpm lint
  • pnpm typecheck
  • pnpm format
Open in Web Open in Cursor 

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

vercel Bot commented Jun 2, 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 12, 2026 11:16am
react-grab-website Ready Ready Preview, Comment Jun 12, 2026 11:16am

@pkg-pr-new

pkg-pr-new Bot commented Jun 2, 2026

Copy link
Copy Markdown

Open in StackBlitz

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

commit: a149248

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

This comment was marked as resolved.

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

This comment was marked as resolved.

vercel[bot]

This comment was marked as resolved.

cubic-dev-ai[bot]

This comment was marked as resolved.

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

This comment was marked as resolved.

Co-authored-by: Aiden Bai <aidenybai@users.noreply.github.com>
Co-authored-by: Aiden Bai <aidenybai@users.noreply.github.com>
Co-authored-by: Aiden Bai <aidenybai@users.noreply.github.com>
cubic-dev-ai[bot]

This comment was marked as resolved.

cursor[bot]

This comment was marked as resolved.

getCachedFiberSource cached the first promise per element forever, so a
grab that resolved before the fiber's source metadata was attached could
never recover. Evict null resolutions so a later grab retries, while
still deduping concurrent in-flight lookups.
vercel[bot]

This comment was marked as resolved.

aidenybai added 2 commits June 7, 2026 04:40
ignorePaths was the only field of SourceOptions, so drop the whole
source/sourceOptions plumbing (public Options.source, the API option on
getElementContext, and the threading through resolution/copy/snippet).
The built-in components/ui ignore stays; classification now depends
solely on fileName, so it is always cacheable.
A reviewer suggested Math.min, which would collapse the hard cap onto
the soft budget and disable digging past low-signal frames. Document
why max is required.
Comment thread packages/react-grab/src/constants.ts Outdated
Comment thread packages/react-grab/src/core/context.ts Outdated
cursoragent and others added 4 commits June 9, 2026 06:08
Co-authored-by: Aiden Bai <aidenybai@users.noreply.github.com>
Co-authored-by: Aiden Bai <aidenybai@users.noreply.github.com>
Co-authored-by: Aiden Bai <aidenybai@users.noreply.github.com>
Co-authored-by: Aiden Bai <aidenybai@users.noreply.github.com>
Comment thread packages/react-grab/src/core/context.ts Outdated
Co-authored-by: Aiden Bai <aidenybai@users.noreply.github.com>
Comment thread packages/react-grab/src/core/copy.ts Outdated
Comment thread packages/react-grab/src/core/copy.ts
Co-authored-by: Aiden Bai <aidenybai@users.noreply.github.com>
Comment thread packages/react-grab/src/core/copy.ts Outdated
cursoragent and others added 2 commits June 9, 2026 06:54
Co-authored-by: Aiden Bai <aidenybai@users.noreply.github.com>
Retain all PR functionality (source classification, low-signal trace
digging, selector hints, descendant preview text) while cutting
indirection: classify source paths once at construction, collapse the
trusted/low-signal flag pair into one signal field, share one context
composer, and revert noise churn.

Split context.ts (820 -> ~475 lines) along clean seams into
next-server-frames.ts and html-preview.ts, extract
is-next-project-runtime.ts to fix a utils->core import inversion, and
delete dead options, constants, and the CSS.escape polyfill.

Fix two regressions found by adversarial review and pin them with
mutation-verified e2e tests: getHTMLPreview kept child placeholders
only for a/code/pre subsumption, and copy references preserve newlines
inside attribute values. Revert the README example to match actual
copy output.
Comment thread packages/react-grab/src/core/copy.ts Outdated
Move the thrice-declared wire shapes (SourceLocation, ReactGrabStackFrame,
ReactGrabEntry) to global types, with SourceLocation extending SourceInfo.
Inline the dead-null resolveStackFrameSource branch, flatten the
formatStackContext emit closure, collapse getTraceContext's duplicated
leading-source fallback, and drop always-constant parameters. Revert the
CLI GrabEntry to its four-field shape since it only reads commentText.

Rename source-frame-policy to classify-source-path after its export, and
replace vague locals (resolvedElement, resolved, cached, promise, name,
value) with descriptive names throughout the touched files.
Comment thread packages/react-grab/src/core/copy.ts
Comment thread packages/react-grab/src/core/context.ts
Comment thread packages/react-grab/src/core/context.ts

@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 using default effort and found 1 potential issue.

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 a149248. Configure here.

Comment thread packages/react-grab/src/core/context.ts
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