Skip to main content
Glama

earmark

点击正在运行的应用中的某个元素,说明应该修改什么,你的编程代理就会获得 CSS 选择器、源文件及行号、组件路径、计算样式和盒子几何信息 — 而不是“右边的按钮看起来不对”这样的描述。

适用于任何框架。悬浮层无需构建步骤。

┌─ browser ──────────────┐        ┌─ broker ────────┐        ┌─ agent ─────────┐
│ click → annotate       │ POST   │ store + SSE     │  MCP   │ list / watch    │
│ pins, panel, markdown  │───────▶│ long-poll       │◀──────▶│ ask / resolve   │
│                        │◀───────│ .earmark/*.json │        │ dismiss         │
└────────────────────────┘  SSE   └─────────────────┘        └─────────────────┘

30 秒快速体验

npm install && npm run example

打开 http://127.0.0.1:5173/examples/vanilla/,点击工具栏(右下角)中的箭头或按 alt+a,然后点击页面上的任意内容。

着陆页和完整指南也随其一并托管在 http://127.0.0.1:5173/site/ — 源代码位于 site/index.html,这是一个无依赖的单文件网页。

如需实时代理同步,请在第二个终端中运行 broker:

npm run server

Related MCP server: vibe-annotations

安装

npm install -D earmark
import { createEarmark } from 'earmark';

if (import.meta.env.DEV) {
  createEarmark();
}

无需打包器:

<script type="module" src="/node_modules/earmark/src/index.js" data-earmark-auto></script>

选项

createEarmark({
  endpoint: 'http://127.0.0.1:7331', // or false for copy-paste only
  hotkey: 'alt+a',
  theme: 'auto',                     // 'auto' | 'light' | 'dark'
  persist: true,                     // keep annotations across reloads
  onAnnotate: (annotation) => {},
});

端点默认指向本地 broker,当没有服务在监听时会静默降级 — 悬浮层仍可工作,只是同步圆点变为灰色。


使用方法

工具

作用

➤

点击一个元素。按住 Shift 点击可添加更多元素,然后点击完成。

T

选择文本 — 精确的字符串是交给代理后最便于 grep 的东西。

⛶

拖拽一个区域。报告区域内的每个元素,若区域为空则标记出来。

❊

冻结所有正在移动的内容 — CSS 动画、element.animate()、<video>、<audio>。

☰

面板:查看、删除、回复代理、复制 markdown。

⌘↵ 保存注解,esc 取消,alt+a 切换拾取模式。每个注解都可以标记为高、普通或低优先级;high 会优先排列给代理。


复制粘贴模式

点击面板中的复制 Markdown,然后粘贴到你的代理中:

## UI feedback — 1 annotation

- **Page:** http://localhost:5173/dashboard
- **Viewport:** 1440×900 @2x, dark mode
- **Framework:** react

### 1. Export button padding is too tight — needs 10px 16px

- **Element:** `<button>` <ExportButton>
- **Selector:** `[data-testid="export-btn"]`
- **Source:** `src/components/Card.tsx:42:7`
- **Component path:** App › Dashboard › Card › ExportButton
- **Text:** "Export"
- **Box:** 66×37 at (194, 376)
- **Computed:** padding: 7px 13px; border-radius: 8px; font-size: 13px
- **Ancestors:** div.row ← section.card ← main

代理同步模式(MCP)

claude mcp add earmark -- npx -y earmark-mcp

或者,将其写入项目的 .mcp.json:

npx earmark-mcp init

这一个进程同时运行 MCP 服务器和浏览器所连接的 broker。当某些功能不正常时,向它询问原因:

npx earmark-mcp doctor
✓ Node version: v24.12.0
✓ sqlite backend: available
✓ MCP registration: earmark is registered in .mcp.json
✓ Broker: responding on http://127.0.0.1:7331 — 1 annotations, 2 sessions
✓ Browser overlay: http://localhost:5173/ (1 annotations)

每项失败的检查都会打印修复命令,并且 doctor 以非零状态退出,以便 CI 使用。

工具

工具

作用

earmark_list_annotations

未完成的工作,以 markdown 格式输出(或 format: "json");通过 session 限定范围

earmark_watch_annotations

阻塞直到人类完成注解

earmark_get_annotation

单条注解及其完整回复线程

earmark_list_sessions

哪些浏览器标签页处于打开状态,以及哪些路由被注解过

earmark_get_session

单个标签页及其产生的每条注解

earmark_acknowledge

“我已读,我正在处理” — 图钉变为蓝色

earmark_ask

提出澄清问题 — 图钉变为琥珀色

earmark_resolve

标记为已完成并附上摘要 — 图钉变为绿色

earmark_dismiss

拒绝并附上人类可见的原因

earmark_clear

删除所有内容

earmark_status

悬浮层是否已连接?应使用哪个端点?

由此实现的修复循环:

watch → acknowledge → read the source path → edit the file → resolve → watch

acknowledge 在慢任务中很重要:没有它,一个重构进行到一半的代理与一个无视你的代理看起来毫无区别。蓝色图钉表示已接取,绿色表示实际完成。

当反馈不明确时,使用 ask 而不是猜测。问题会显示在图钉上;人类的回答会唤醒下一次 watch。

状态

open → acknowledged → resolved,当代理等待人类输入时为 needs-input,当代理拒绝时为 dismissed。图钉带有颜色标识:橙色、蓝色、绿色、琥珀色、灰色。

会话

会话是一个浏览器标签页,而不是一次页面加载 — id 存于 sessionStorage,因此刷新后仍会保留。注解带有自己的 page.url,因此一个跨三个路由的会话会给代理一个包含三个不同路由项的分组。

SPA 导航也会被追踪:pushState、replaceState、popstate 和 hashchange 都会更新会话的路由列表。一个标签页的 SSE 流保持打开多长时间,它就计为连接多长时间。

curl http://127.0.0.1:7331/sessions

源文件路径

选择器告诉代理要搜索什么。源路径精确地告诉它在哪里查找,这是一次编辑与三次 grep 之间的区别。

React 19 移除了运行时的 _debugSource fiber 字段,因此这一步在构建时完成:

// vite.config.js
import earmark from 'vite-plugin-earmark';

export default {
  plugins: [react(), earmark()],
};

每个内置 JSX 元素都会在 vite dev 期间获得 data-earmark-src="src/Card.tsx:42:7"。该插件还会注入悬浮层,因此应用代码中的 createEarmark() 变为可选。

earmark({
  inject: false,        // do not auto-mount the overlay
  endpoint: '…',        // passed through to createEarmark
  applyInBuild: true,   // also stamp production builds (off by default)
})

没有该插件,一切仍能工作 — 你仍能获得选择器、组件名称和文本,只是没有 file:line。你也可以手动添加 data-earmark-src。

纯 HTML 和 CSS — 无需构建步骤

静态站点没有可供标记的构建产物,因此 earmark 改为在注解时解析源代码:

  • HTML — 重新获取文档并使用位置跟踪进行解析,然后在源代码中沿着元素的子索引路径查找。每一步都会与实际标签名进行核对,因此由框架渲染的页面(提供的 HTML 只是一个壳)会报告无结果,而不会凭空捏造行号。

  • CSS — 所有匹配该元素的规则,都会映射回声明这些规则的文件和行号。这一点在任何地方都有效,无论是否使用框架。

- **Source:** `index.html:101:11` _(resolved from the served HTML)_
- **CSS rules that style it:**
  - `button` → `index.html (inline <style>):49`
    - padding: 7px 13px; border-radius: 8px; border: 1px solid var(--line);
  - `button.primary` → `index.html (inline <style>):59`
    - background: var(--accent); color: rgb(255, 255, 255);

代理现在知道需要修改的 padding 位于通用 button 规则的第 49 行,而不是在 .primary 中。内联 <style> 块会偏移到其宿主文档中;外部样式表会报告自身路径;跨域样式表会被跳过,因为其内容不可读取。


独立 broker

npx earmark-server --port 7331
curl http://127.0.0.1:7331/markdown

路由

GET /health

存活状态 + 计数

GET /annotations?status=open&session=ID

列表

POST /annotations

创建(批量)

GET /annotations/wait?since=N&timeout=30000

长轮询

PATCH /annotations/:id

更新状态

POST /annotations/:id/replies

追加到线程

DELETE /annotations/:id · DELETE /annotations

删除 · 清空

POST /session

注册标签页 / 记录路由变化

GET /sessions · GET /sessions/:id

标签页,含计数和注解

GET /events?session=ID

SSE 流;同时也是标签页的存活信号

GET /markdown

面向代理的文档

标志:--host --store --file --no-persist --webhook --token --quiet。

存储

--store json(默认)会在 250 ms 防抖后写入可读的 .earmark/annotations.json。--store sqlite 会通过 node:sqlite 将每次更改立即写入 .earmark/annotations.db,因此崩溃时最多丢失正在执行的语句 — 无依赖,Node 22.5+,如果不可用则回退到 JSON。--store memory 不保留任何内容。

Webhooks

npx earmark-server --webhook https://hooks.example/earmark

此外还有 EARMARK_WEBHOOK_URL 和 EARMARK_WEBHOOKS(逗号分隔)。每个注解事件都会通过带有 x-earmark-event 头的 POST 请求发送。投递为即发即弃模式,超时 5 秒并重试一次,因此失效的端点不会阻塞注解循环。


安全性

这是一个开发工具。

  • broker 仅绑定 127.0.0.1。不要将其绑定到 0.0.0.0。

  • CORS 按设计保持开放 — 你的开发服务器运行在任意源上。

  • 浏览器中打开的任何页面都可以访问回环端口。如果这对你的机器很重要,请传入 --token SECRET。

  • Webhooks 会将注解内容发送到你的机器之外 — 页面 URL、元素文本,以及你输入的任何内容。只配置你控制的端点。

  • 源解析会从同一源重新获取你自己的页面和样式表。不会向任何地方发送任何内容。

  • 不要在共享或公共主机上运行它。


测试

npm test

七个套件,88 个测试:存储和 HTTP 行为、由真实 stdio 客户端驱动的 MCP 接口、悬浮层的同步客户端、两种持久化后端、webhook 投递、init/doctor CLI,以及源解析器。


不支持的功能

仅支持桌面浏览器。不支持 iframe、canvas/WebGL 内部、截图。完整的开放列表和每项设计决策背后的理由,请参阅 plan.md。


许可证

MIT。净室实现 — 不衍生自任何其他工具的源代码。

Related MCP Connectors

Related MCP Servers