React Grab

aidenybai/react-grab · 開発中アプリ上で UI を選び、エージェント向けコンテキストをコピー

1 — 概要

React Grab は、ブラウザ上の任意の UI 要素を指定し、その背後にあるソースファイル・行番号・React コンポーネントスタックをクリップボードにまとめてコピーするツール。Cursor・Claude Code・Copilot などのコーディングエージェントに貼り付けることで、「どのコンポーネントのどの行か」を明示したまま修正依頼できる。公式ベンチマークでは編集が約 2 倍速になり精度も上がるとしている。

⌘Cデフォルト操作
Solidオーバーレイ UI
bippyFiber / スタック
MCPエージェント連携

ランタイムは 開発環境のみで読み込む想定。CLI(grab)が Next.js / Vite / TanStack Start / Webpack などを検出し、フレームワーク別の dev-only 設定を自動適用する。

開発専用: 本番バンドルに含めない。Next.js では NODE_ENV === "development" のときだけ react-grab のスクリプトまたは dynamic import を有効にする。
2 — 操作フロー
STEP 1
ホバー
開発サーバー起動中のアプリ上で、修正したい要素の上にポインタを置く。オーバーレイが要素境界とコンポーネント名を表示する。
STEP 2
コピー
⌘C(Mac)または Ctrl+C(Windows/Linux)。ホールド/トグルなどの有効化モードは grab configure で変更できる。
STEP 3
貼り付け
エージェントのプロンプトに貼り付ける。HTML 断片・周辺ソース行・in ComponentName (at path:line:col) 形式のスタックが含まれる。
<a class="ml-auto inline-block text-sm" href="#">
  Forgot your password?
</a>

// components/login-form.tsx:46
  45| <div className="flex items-center">
> 46|   <a className="ml-auto inline-block text-sm" href="#">
  47|     Forgot your password?
  48|   </a>

  in LoginForm (at components/login-form.tsx:46:19)
3 — アーキテクチャ
flowchart TD
  subgraph apps["apps/"]
    Web["website / website-v2"]
    Ext["web-extension"]
    E2E["e2e-app"]
    OS["openstory"]
  end
  subgraph pkgs["packages/"]
    RG["react-grab\nコア・オーバーレイ"]
    CLI["@react-grab/cli\ngrab init / configure"]
    GrabPkg["grab\nnpm CLI エントリ"]
    MCP["@react-grab/mcp\nMCP サーバー"]
  end
  Dev["開発者の React アプリ"]
  Agent["コーディングエージェント"]

  Dev -->|"dev import"| RG
  CLI -->|"設定注入"| Dev
  GrabPkg --> CLI
  RG -->|"クリップボード"| Agent
  MCP --> Agent
  Ext --> RG
  Web --> RG

  classDef core fill:#ccfbf1,stroke:#0d9488,color:#134e4a,rx:6
  classDef tool fill:#f0fdf4,stroke:#16a34a,color:#14532d,rx:6
  classDef app fill:#f8fafc,stroke:#64748b,color:#334155,rx:6
  class RG core
  class CLI,GrabPkg,MCP tool
  class Web,Ext,E2E,OS,Dev,Agent app
          

init() 後、キーのホールドまたはトグルで active に入る。ホバー・クリック固定(frozen)・矩形ドラッグ選択・コピー中(copying)を core/store.ts の判別共用体で管理する。

stateDiagram-v2
  [*] --> idle
  idle --> holding: キー押下
  holding --> active: ホールド時間到達
  holding --> idle: 早期リリース
  idle --> active: トグル ON
  state active {
    [*] --> hovering
    hovering --> frozen: クリック固定
    frozen --> hovering: 解除
    hovering --> dragging: 矩形選択
    dragging --> hovering: 完了
  }
  active --> copying: コピー実行
  copying --> active: ホールドモード継続
  copying --> idle: トグルモード終了
  active --> idle: 非活性化
          
  • ブラウザで import されると init() が走り、API が window.__REACT_GRAB__ に露出する(SSR 時は同形の Noop API)。
  • オーバーレイ UI は Shadow DOM にマウントし、ホストは pointer-events: none(子 UI だけがイベントを受ける)。
  • レンダラー(Solid コンポーネント群)は dynamic import で遅延読み込みし、検出ロジックを先に立ち上げる。
  • コピー・コメント・エディタで開くはすべてプラグインとして登録され、コアはパイプラインだけを提供する。
4 — パッケージ構成
npm 名役割
react-grabコア。要素ハイライト・コンテキスト解決(bippy + source map)・コピー・プラグインレジストリ。Solid.js で UI/状態。
grab公開 CLI パッケージ。npx grab@latest init のエントリ。
@react-grab/cliCLI 実装。フレームワーク検出・設定ファイル変換・MCP インストール補助。
@react-grab/mcpMCP サーバー / クライアント。npx grab add mcp でエージェントに接続。

apps/(リポジトリ内)

ディレクトリ役割
website / website-v2マーケティングサイト・デモ
web-extensionブラウザ拡張(任意サイトで Grab を使う)
e2e-appPlaywright E2E 用のサンプルアプリ
openstoryコンポーネントストーリー / デモ
5 — 導入

プロジェクトルート(next.config.ts や vite.config.ts がある階層)で:

npx grab@latest init

MCP 連携を追加する場合:

npx grab@latest add mcp

有効化キーやホールド時間などは対話式またはフラグ付きで更新できる:

npx grab@latest configure
npx grab@latest configure --mode hold --hold-duration 500
npx grab@latest init -k "Meta+K" -y
コマンド主なオプション
grab init-y 確認スキップ、-k 有効化キー、-f 上書き、--skip-install
grab configure--mode toggle|hold、--hold-duration、--context-lines、--cdn
grab add mcp対応エージェント向け MCP 設定を追加

CLI が使えない場合、エントリの先頭で開発時のみ import する:

if (import.meta.env.DEV) {
  import("react-grab");
}

Next.js App Router では app/layout.tsx に unpkg 経由の <Script src="//unpkg.com/react-grab/dist/index.global.js" /> を development 条件付きで入れる方法もある(詳細は公式 README)。

6 — プラグイン

コンテキストメニュー・ツールバー・ライフサイクルフック・テーマ上書きを registerPlugin / unregisterPlugin で追加する。組み込みプラグインは copy(デフォルトコピー)、comment(プロンプトモード)、open(エディタで開く)の 3 つ。

import { registerPlugin, unregisterPlugin } from "react-grab";

registerPlugin({
  name: "my-plugin",
  actions: [
    {
      id: "inspect",
      label: "Inspect",
      shortcut: "I",
      onAction: (ctx) => console.dir(ctx.element),
    },
    {
      id: "toggle-freeze",
      label: "Freeze",
      target: "toolbar",
      onAction: () => toggleFreeze(),
    },
  ],
  hooks: {
    onElementSelect: (element) => {
      console.log("Selected:", element.tagName);
    },
  },
});

コピー内容は onBeforeCopy → transformSnippet → transformCopyContent の reduce パイプラインを通過する。型定義は Plugin / PluginHooks(packages/react-grab/src/types.ts)。

  • init(options?) → ReactGrabAPI — activate / deactivate / toggle
  • copyElement(elements)、getSource(element)、getStackContext(element)
  • registerPlugin / unregisterPlugin、setOptions、getState、dispose
  • react-grab/primitives — getElementContext、freeze / unfreeze、openFile(カスタム選択 UI 向け)
7 — 内部設計

オーバーレイ表示中は見た目が動かないよう、複数系統を同時に止める(いずれも React 内部へのパッチを含み、バージョン依存で脆い)。

  • React 更新 — ディスパッチャをラップし、useState / useReducer 等の dispatch をバッファ。解除時に順序付きでリプレイ。
  • アニメーション — CSS / WAAPI / SVG SMIL / GSAP を一時停止。解除時は finish() でジャンプを避ける。
  • 疑似状態 — :hover / :focus の見た目をインラインスタイルで固定し、ポインタがオーバーレイに移っても色が変わらないようにする。
  1. DOM → bippy で最寄りの React fiber
  2. fiber → getOwnerStack()(React 19 は _debugStack、17–18 はフォールバック生成)
  3. バンドル URL → source map で元ファイル・行へ
  4. RSC の rsc:// URL → Next.js の /__nextjs_original-stack-frames で実パスへ
  5. 表示名フィルタ(InnerLayoutRouter 等の内部名を除外)→ エディタ起動(Vite / Next の open-in-editor、失敗時は vscode:// リダイレクト)
詳細: フリーズ実装・キーボードの key ゲッターパッチ・Canvas オーバーレイなどは、リポジトリ内の packages/react-grab/docs/architecture.md に長文でまとまっている。