Loading...
Loading...
Compare original and translation side by side
Keyword:·react-grab·grab·element contextcopy component to aiPoint at any UI element in your browser, press Cmd/Ctrl+C, and instantly copy its React component name, source file path, and HTML markup to clipboard — ready for your AI coding agent.
关键词:·react-grab·grab·element contextcopy component to ai指向浏览器中的任意UI元素,按下Cmd/Ctrl+C,即可立即将其React组件名称、源文件路径和HTML标记复制到剪贴板——随时可供你的AI编码Agent使用。
getElementContextfreezegetElementContextfreezebash scripts/install.shnpx -y grab@latest initbash scripts/add-agent.sh mcpFor detailed API and framework-specific setup, see references/api.md.
bash scripts/install.shnpx -y grab@latest initbash scripts/add-agent.sh mcp如需详细API和框架特定设置,请查看references/api.md。
undefinedundefinedundefinedundefinedundefinedundefinedundefinedundefinedimport { registerPlugin } from "react-grab";
registerPlugin({
name: "open-in-figma",
actions: [{
id: "figma",
label: "Find in Figma",
shortcut: "F",
onAction: (ctx) => {
window.open(`figma://search?q=${ctx.componentName}`);
},
}],
});import { registerPlugin } from "react-grab";
registerPlugin({
name: "open-in-figma",
actions: [{
id: "figma",
label: "在Figma中查找",
shortcut: "F",
onAction: (ctx) => {
window.open(`figma://search?q=${ctx.componentName}`);
},
}],
});undefinedundefined
After installation, hover over any element in your browser during development and press:
- **Mac**: `Cmd+C`
- **Windows/Linux**: `Ctrl+C`
Output copied to clipboard:
安装完成后,在开发过程中悬停在浏览器中的任意元素上,按下:
- **Mac**:`Cmd+C`
- **Windows/Linux**:`Ctrl+C`
复制到剪贴板的内容:npx -y grab@latest initnpx -y grab@latest initapp/layout.tsximport Script from 'next/script'
export default function RootLayout({ children }) {
return (
<html>
<body>
{children}
{process.env.NODE_ENV === "development" && (
<Script
src="//unpkg.com/react-grab/dist/index.global.js"
crossOrigin="anonymous"
strategy="beforeInteractive"
/>
)}
</body>
</html>
)
}pages/_document.tsximport Script from 'next/script'
// Inside _document component:
{process.env.NODE_ENV === "development" && (
<Script
src="//unpkg.com/react-grab/dist/index.global.js"
crossOrigin="anonymous"
strategy="beforeInteractive"
/>
)}index.html<script type="module">
if (import.meta.env.DEV) {
await import('//unpkg.com/react-grab/dist/index.global.js');
}
</script>if (process.env.NODE_ENV === 'development') {
import('react-grab');
}npm install react-grabapp/layout.tsximport Script from 'next/script'
export default function RootLayout({ children }) {
return (
<html>
<body>
{children}
{process.env.NODE_ENV === "development" && (
<Script
src="//unpkg.com/react-grab/dist/index.global.js"
crossOrigin="anonymous"
strategy="beforeInteractive"
/>
)}
</body>
</html>
)
}pages/_document.tsximport Script from 'next/script'
// 在_document组件中:
{process.env.NODE_ENV === "development" && (
<Script
src="//unpkg.com/react-grab/dist/index.global.js"
crossOrigin="anonymous"
strategy="beforeInteractive"
/>
)}index.html<script type="module">
if (import.meta.env.DEV) {
await import('//unpkg.com/react-grab/dist/index.global.js');
}
</script>if (process.env.NODE_ENV === 'development') {
import('react-grab');
}npm install react-grabundefinedundefinedundefinedundefined
---
---in <ComponentName> at <file-path>:<line>:<col>
<element-html>in <ComponentName> at <file-path>:<line>:<col>
<element-html>import { getGlobalApi } from "react-grab";
const api = getGlobalApi();
// Activate the overlay
api.activate(); // show overlay
api.deactivate(); // hide overlay
api.toggle(); // toggle
// Copy an element's context to clipboard
await api.copyElement(document.querySelector('.my-button'));
// Get source info without copying
const source = await api.getSource(element);
console.log(source.filePath); // "src/components/Button.tsx"
console.log(source.lineNumber); // 42
console.log(source.componentName); // "Button"
// Get the full stack context string
const stack = await api.getStackContext(element);
// "in Button (at Button.tsx:42:5)\n in Card (at Card.tsx:10:3)"
// Check current state
const state = api.getState();
// { isActive, isDragging, isCopying, targetElement, ... }import { getGlobalApi } from "react-grab";
const api = getGlobalApi();
// 激活覆盖层
api.activate(); // 显示覆盖层
api.deactivate(); // 隐藏覆盖层
api.toggle(); // 切换状态
// 将元素上下文复制到剪贴板
await api.copyElement(document.querySelector('.my-button'));
// 获取源信息但不复制
const source = await api.getSource(element);
console.log(source.filePath); // "src/components/Button.tsx"
console.log(source.lineNumber); // 42
console.log(source.componentName); // "Button"
// 获取完整的组件栈上下文字符串
const stack = await api.getStackContext(element);
// "in Button (at Button.tsx:42:5)\n in Card (at Card.tsx:10:3)"
// 检查当前状态
const state = api.getState();
// { isActive, isDragging, isCopying, targetElement, ... }import { getElementContext, freeze, unfreeze, openFile } from "react-grab/primitives";
// or: import { getElementContext } from "react-grab/core";
const button = document.querySelector('.submit-btn');
// Freeze page state (pause React updates, CSS animations, preserve :hover/:focus)
freeze();
// Get all context for an element
const context = await getElementContext(button);
console.log(context.componentName); // "SubmitButton"
console.log(context.selector); // "button.submit-btn"
console.log(context.stackString); // "in SubmitButton (at Button.tsx:12:5)"
console.log(context.htmlPreview); // "<button class=\"submit-btn\">Submit</button>"
console.log(context.styles); // Relevant computed CSS
unfreeze(); // Restore normal page behavior
// Open the source file in editor
await openFile("/src/components/Button.tsx", 12);import { getElementContext, freeze, unfreeze, openFile } from "react-grab/primitives";
// 或:import { getElementContext } from "react-grab/core";
const button = document.querySelector('.submit-btn');
// 冻结页面状态(暂停React更新、CSS动画,保留:hover/:focus状态)
freeze();
// 获取元素的完整上下文
const context = await getElementContext(button);
console.log(context.componentName); // "SubmitButton"
console.log(context.selector); // "button.submit-btn"
console.log(context.stackString); // "in SubmitButton (at Button.tsx:12:5)"
console.log(context.htmlPreview); // "<button class=\"submit-btn\">Submit</button>"
console.log(context.styles); // 相关计算后的CSS样式
unfreeze(); // 恢复页面正常行为
// 在编辑器中打开源文件
await openFile("/src/components/Button.tsx", 12);@react-grab/mcpundefined@react-grab/mcpundefined
The MCP server exposes a `get_element_context` tool that returns the most recent element
selection from the browser overlay. Your AI agent calls this tool to receive the context
that you selected in the browser.
**Flow:**
1. You hover over element in browser → react-grab captures context
2. AI agent calls `get_element_context` MCP tool
3. Agent receives: component name, file path, line number, HTML markup
4. Agent makes precise code changes without file searching
---
MCP服务器暴露`get_element_context`工具,返回浏览器覆盖层中最近选中的元素上下文。你的AI Agent可以调用该工具,获取你在浏览器中选中的上下文信息。
**流程:**
1. 你在浏览器中悬停在元素上→react-grab捕获上下文
2. AI Agent调用`get_element_context` MCP工具
3. Agent接收:组件名称、文件路径、行号、HTML标记
4. Agent无需搜索文件即可精准修改代码
---npx -y grab@latest add claude-code # Claude Code
npx -y grab@latest add cursor # Cursor
npx -y grab@latest add copilot # GitHub Copilot
npx -y grab@latest add codex # OpenAI Codex
npx -y grab@latest add gemini # Google Gemini CLI
npx -y grab@latest add opencode # OpenCode
npx -y grab@latest add amp # Amp
npx -y grab@latest add droid # Droidnpx -y grab@latest remove cursornpx -y grab@latest add claude-code # Claude Code
npx -y grab@latest add cursor # Cursor
npx -y grab@latest add copilot # GitHub Copilot
npx -y grab@latest add codex # OpenAI Codex
npx -y grab@latest add gemini # Google Gemini CLI
npx -y grab@latest add opencode # OpenCode
npx -y grab@latest add amp # Amp
npx -y grab@latest add droid # Droidnpx -y grab@latest remove cursorimport { registerPlugin } from "react-grab";
registerPlugin({
name: "send-to-jira",
hooks: {
// Add annotation to clipboard content
transformCopyContent: async (content, elements) => {
return content + "\n// Sent from react-grab";
},
// Custom file-open behavior
onOpenFile: (filePath, lineNumber) => {
// Return true if handled; false to use default behavior
return false;
},
},
actions: [
{
id: "jira-action",
label: "Create Jira Ticket",
shortcut: "J",
onAction: async (context) => {
await createJiraTicket({
filePath: context.filePath,
componentName: context.componentName,
html: context.element.outerHTML,
});
context.hideContextMenu();
},
},
{
id: "toolbar-inspect",
label: "Inspect",
target: "toolbar", // Places in toolbar instead of context menu
onAction: () => {
console.log("Inspect triggered");
},
},
],
theme: {
hue: 200, // Change overlay color (0-360 HSL)
crosshair: { enabled: false }, // Disable crosshair overlay
},
});import { registerPlugin } from "react-grab";
registerPlugin({
name: "send-to-jira",
hooks: {
// 为剪贴板内容添加注释
transformCopyContent: async (content, elements) => {
return content + "\n// 来自react-grab";
},
// 自定义文件打开行为
onOpenFile: (filePath, lineNumber) => {
// 若已处理返回true;返回false则使用默认行为
return false;
},
},
actions: [
{
id: "jira-action",
label: "创建Jira工单",
shortcut: "J",
onAction: async (context) => {
await createJiraTicket({
filePath: context.filePath,
componentName: context.componentName,
html: context.element.outerHTML,
});
context.hideContextMenu();
},
},
{
id: "toolbar-inspect",
label: "检查",
target: "toolbar", // 放置在工具栏而非右键菜单
onAction: () => {
console.log("触发检查操作");
},
},
],
theme: {
hue: 200, // 修改覆盖层颜色(0-360 HSL)
crosshair: { enabled: false }, // 禁用十字准星覆盖层
},
});hooks: {
onActivate?: () => void
onDeactivate?: () => void
onElementHover?: (element: Element) => void
onElementSelect?: (element: Element) => boolean | void // return true to cancel default
onBeforeCopy?: (elements: Element[]) => void
transformCopyContent?: (content: string, elements: Element[]) => string | Promise<string>
onAfterCopy?: (elements: Element[], success: boolean) => void
onOpenFile?: (filePath: string, lineNumber?: number) => boolean | void
transformHtmlContent?: (html: string, elements: Element[]) => string | Promise<string>
transformAgentContext?: (ctx: AgentContext, elements: Element[]) => AgentContext
transformSnippet?: (snippet: string, element: Element) => string | Promise<string>
}hooks: {
onActivate?: () => void
onDeactivate?: () => void
onElementHover?: (element: Element) => void
onElementSelect?: (element: Element) => boolean | void // 返回true可取消默认行为
onBeforeCopy?: (elements: Element[]) => void
transformCopyContent?: (content: string, elements: Element[]) => string | Promise<string>
onAfterCopy?: (elements: Element[], success: boolean) => void
onOpenFile?: (filePath: string, lineNumber?: number) => boolean | void
transformHtmlContent?: (html: string, elements: Element[]) => string | Promise<string>
transformAgentContext?: (ctx: AgentContext, elements: Element[]) => AgentContext
transformSnippet?: (snippet: string, element: Element) => string | Promise<string>
}grab [command] [options]
Commands:
init Auto-detect project and install react-grab
add Connect react-grab to an AI coding agent (alias: install)
remove Disconnect from an AI coding agent
configure Configure options (activation key, context lines, etc.)
Options:
-y, --yes Skip confirmation prompts
-c, --cwd Working directory (default: process.cwd())
-v, --version Display version
-h, --help Display helpgrab [command] [options]
命令:
init 自动检测项目并安装react-grab
add 将react-grab连接到AI编码Agent(别名:install)
remove 断开与AI编码Agent的连接
configure 配置选项(激活键、上下文行数等)
选项:
-y, --yes 跳过确认提示
-c, --cwd 工作目录(默认:process.cwd())
-v, --version 显示版本
-h, --help 显示帮助init()import { init } from "react-grab";
init({
enabled: true, // Enable/disable entirely
activationMode: "toggle", // "toggle" | "hold"
activationKey: "c", // Key to press (with Cmd/Ctrl)
maxContextLines: 10, // Limit React component stack depth
freezeReactUpdates: true, // Pause React state while active
getContent: async (elements) => { // Custom clipboard content formatter
const contexts = await generateSnippet(elements);
return contexts.join("\n---\n");
},
});window.__REACT_GRAB_DISABLED__ = true;init()import { init } from "react-grab";
init({
enabled: true, // 完全启用/禁用
activationMode: "toggle", // "toggle" | "hold"
activationKey: "c", // 触发键(配合Cmd/Ctrl)
maxContextLines: 10, // 限制React组件栈深度
freezeReactUpdates: true, // 激活时暂停React状态更新
getContent: async (elements) => { // 自定义剪贴板内容格式化器
const contexts = await generateSnippet(elements);
return contexts.join("\n---\n");
},
});window.__REACT_GRAB_DISABLED__ = true;NODE_ENV === "development"import.meta.env.DEVinitnpx -y grab@latest initgrab add mcpfreeze()NODE_ENV === "development"import.meta.env.DEVnpx -y grab@latest initgrab add mcpfreeze()| Issue | Solution |
|---|---|
| Overlay not appearing | Check |
Component name shows as | Ensure |
| File path missing | Requires React source maps enabled (dev mode only; not minified builds) |
| MCP tool returns empty | You need to select an element in the browser first before calling |
| Check Node.js >=18 and package manager (npm/pnpm/yarn/bun) is installed |
| 问题 | 解决方案 |
|---|---|
| 覆盖层不显示 | 检查是否用 |
组件名称显示为 | 确保 |
| 文件路径缺失 | 需要启用React源映射(仅开发模式;非压缩构建) |
| MCP工具返回空内容 | 调用 |
| 检查Node.js版本>=18,且已安装包管理器(npm/pnpm/yarn/bun) |
install.shadd-agent.shinstall.shadd-agent.sh