web-design-harvester
web-design-harvester
将渲染后的网页转化为 LLM 可据以构建的设计规格:逐区块截图、提炼后的计算样式(computed CSS)、设计令牌、响应式差异,以及分析到 alpha 通道级别的资源。
专为复刻 Figma Sites 页面而构建,但其中没有任何 Figma 专属内容——它适用于任何能渲染的 URL。
npm install
node bin/harvest.mjs https://example.figma.site --out ./spec --clean
# then point a model at ./spec/README.md为什么存在
复刻一个设计过去通常是这样的流程:在 Figma 中选择一个区块 → 复制全部 CSS → 将 2000 多行粘贴到聊天中 → 模型从中挖出少数几个关键值 → 对不一致处截图 → 重复,七八次。
每一步都是机械性的,而且源材料比看起来更糟:Figma 的 CSS 导出带有旋转后坐标不可读的负值 frame、display: none 占位图层,以及交错排列的桌面端和移动端变体。
渲染后的 DOM 没有这些问题。在活动页面上调用 getComputedStyle() 得到的是解析后的真实值,可在任意断点获取,并附带网络的资源列表。本工具读取这些信息并将其记录下来。
Figma 的 REST API 不可行
对于 editorType: "sites" 或 "make" 的文件,GET /v1/files/{key} 会返回 400 "File type not supported by this endpoint"。只有经典的 design 文件可读。在规划任何提取之前,先检查 /v1/files/{key}/meta——/meta 和 /styles 适用于所有类型,节点端点则不适用。
让页面呈现在浏览器中
这是最容易让人栽跟头的部分,因此值得精确说明。
你拥有的内容 | 是否可行? | 方法 |
已发布站点 | ✅ 是 | 直接传入 URL 即可。这是一个普通的公开页面。 |
预览 iframe | ❌ 否 | 见下文。 |
未发布站点,已在你的 Chrome 中打开 | ✅ 是 |
|
任何其他站点、localhost、staging | ✅ 是 | 直接传入 URL 即可。 |
预览 iframe URL 无法独立工作
它看起来像一个页面并返回 HTTP 200,但获取它只会得到一个约 3.6KB 的空壳,其中仅包含一个 postMessage 监听器。它本身没有任何内容:
// what that URL actually serves, in full:
window.addEventListener('message', (e) => {
if (isAllowedOrigin(e.origin)) { // only figma.com and friends
if (e.data.type === 'iframe-init') {
script.src = e.data.initScriptURL // ← the real app comes from the parent站点的代码通过 MessagePort 从已登录的 figma.com 标签页传来。直接加载该 URL 只会得到空白文档,无论等待多久。没有可提供的令牌,也没有可设置的请求头——内容根本不存在。
对于未发布站点:附加到你自己的浏览器
以远程调试模式启动 Chrome,登录 Figma,打开站点的 Preview,然后将 harvester 指向该标签页:
# 1. Chrome with a debugging port (use a separate profile to avoid clobbering yours)
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome \
--remote-debugging-port=9222 --user-data-dir=/tmp/figma-profile
# 2. Log into figma.com in that window, open your Sites file, hit Preview.
# 3. Harvest the rendered iframe
node bin/harvest.mjs "https://<uuid>-v2-figmaiframepreview.figma.site" \
--cdp 9222 --out ./spec使用 --cdp 时,工具会附加到你的浏览器,不会启动或关闭任何东西。如果条件允许,更简单的替代方案是:发布站点,然后采集公开 URL。
对于位于登录墙之后、你不想每次运行都重新输入的站点,--persist 会在磁盘上保留配置文件,供多次运行之间复用。
使用方法
harvest <url> [options] full harvest -> spec directory
harvest outline <url> print the DOM outline (recon)
harvest blocks <url> list blocks that would be captured
harvest asset <file...> analyse local media files
harvest serve [--port 8787] HTTP daemon, browser stays warm
harvest mcp MCP server on stdio选项 | 默认值 | |||||
|
| 输出目录 | ||||
|
| 断点,例如 |
| auto | 强制指定区块边界 | |
|
| 页面稳定后的额外等待时间 | ||||
|
| 每个区块的节点上限 | ||||
| 跳过下载和分析资源 | |||||
| 先清空输出目录 | |||||
| 显示浏览器窗口 | |||||
| 复用配置文件,在多次运行之间保留登录状态 | |||||
| 附加到正在运行的 Chrome(端口或 ws:// URL) | |||||
| 机器可读的 stdout |
在不熟悉的页面上,先从 outline 或 blocks 开始。它们运行很快,能让你在投入完整运行之前了解自动分割是否找到了合理的区块。
node bin/harvest.mjs blocks https://figma.site --widths 1440strategy: semantic-landmarks
coverage: 100% (11248 of 11248px)
01 header.fig-suku18 1440×78 @0 [sticky] What you can do in figma
02 section.fig-lqoz33 1440×1109 @78 Figma Sites
03 section.fig-15ba1hq 1440×1117 @1187 Perfect websites every time…
…如果边界不正确,传入 --selector "main > section"。
输出
spec/
README.md ← start here; index, warnings, token summary
index.json machine-readable manifest
tokens.md design tokens ranked by usage
tokens.css the same tokens as CSS custom properties
responsive.md every value that changes between breakpoints
interactions.md clickable/focusable elements and their transitions
warnings.json asset fit problems, in full
page-desktop.png full-page screenshot per breakpoint
page-mobile.png
blocks/
02-figma-sites/
block.md ← spec sheet for one section
desktop.png screenshot, exactly the block's size
mobile.png
tree.desktop.json exact computed values, full precision
tree.mobile.json
assets/
README.md every asset with content box and fit guidance
manifest.json
<files> deduplicated by content hashblock.md 看起来像这样:
section.fig-lqoz33 1440×1108.6 pad:0/0/32/0 relative bg:#ffffff
└─ div.fig-umtrpl 1440×1076.6 flex-col gap:64 pad:64/0/0/0
├─ h1.fig-6late5 660×72 mar:0/0/32/0 72/72 ls:-1.44 "Figma Sites"
└─ a.fig-1jz30fp 135×46.4 flex-row jc:center pad:12/22
#ffffff bg:#000000 r:8 href:/site/new此外还有每个断点的几何信息、文案、所用资源,以及响应式差异表。旁边的 JSON 包含未删减的完整值,供出现异常时参考。
它能做到而截图做不到的事
截图中 1 CSS 像素 = 1 图像像素。 deviceScaleFactor: 1 加上 scale: 'css' 意味着从 PNG 上量出的距离就是 CSS 像素。无需换算系数,因此不会出现换算错误。(Figma 的 @2x 导出将 1798 个图像像素对应到 1440px 的设计——每项测量都需要先除以 1.2486,而算错就会产生看似合理实则错误的数字。)
计算样式是提炼而非倾倒。 对每个元素运行三个过滤器:丢弃该标签的 UA 默认值,丢弃父元素已声明的继承值,并将几乎每个节点上都出现的声明(box-sizing: border-box 等)提升出来只声明一次。保留下来的就是有差异的部分,也就是你真正需要编写的部分。实际上,这比原始转储大约小一个数量级。
资源是测量而非猜测。 对于每张图片和视频,工具会解码一帧并从 alpha 通道找出内容边界框。设计资源通常以 1200×1200 的正方形交付,而实际图形只占据一个偏离中心的 1049×677 区域——仅从文件尺寸完全看不出来,object-contain(留白)和 object-cover + 居中(偏离轴心裁剪)都会出错。输出会指明应使用的 object-position。
带 alpha 通道的 VP9 WebM 需要特殊处理:ffprobe 报告 pix_fmt=yuv420p 且不显示 alpha 通道,但浏览器能正确合成。只有强制使用 libvpx-vp9 解码器时 alpha 通道才会显现。
不可能实现的布局在第一次运行时就指出。 当资源的内容比例与其所在盒子的比例严重不一致时,任何 object-fit 值都无法修复——资源需要重新导出。README 会立即标记这一点,并给出 cover 会丢弃的图形百分比,而不是让你花六轮时间调整 CSS 去修复一个导出问题。
两个断点,因为规格的一半在于差异。 卡片圆角 12px → 6px,标题 20/30 → 16/24,页头内边距 32px → 32px(未变)。这些都无法通过缩放推导出来;responsive.md 列出了每一个发生变化的数值。
任意数量的断点。 --widths 1440,768,375 捕获三个断点,一切随之扩展:每个断点一张截图和一棵样式树,tokens.md 中按断点分别统计使用次数,以及每个相邻对的差异表——desktop → tablet,然后是 tablet → mobile。采用相邻对比而非全部与桌面端对比,因为这反映了媒体查询的编写方式:每一步只重述自上一步以来发生的变化。responsive.md 以矩阵开头,显示哪些区块在哪一步发生变化。
没有任何内容被静默丢弃。 分割之后,工具会检查区块是否铺满整个页面,寻找覆盖任何间隙的元素,并报告覆盖率。确实无法覆盖的区域会被列出而非忽略。截图尺寸会与请求的尺寸进行核对,因为截断的捕获比失败的捕获更糟——它看起来正常,但基于它的每项测量都会悄悄出错。
为模型提供服务
冷启动运行的大部分时间花在浏览器启动和首次绘制上。如果模型在迭代——重新检查那个区块,现在在 768px 下,那个图标是什么颜色——每次提问都付出这个代价会让工具无法使用。两种服务器模式都能保持浏览器及其已加载页面处于热状态。
在 https://figma.site 上实测:冷启动 26.8 秒 → 热状态 0.03 秒。
MCP (stdio)
{
"mcpServers": {
"web-design-harvester": {
"command": "node",
"args": ["/absolute/path/to/web-design-harvester/bin/harvest.mjs", "mcp"]
}
}
}工具:harvest_outline、harvest_blocks、harvest_block、harvest_tokens、harvest_assets、harvest_screenshot、harvest_analyse_asset、harvest_site、harvest_status。
典型循环是先调用 harvest_blocks 找到区块,再调用 harvest_block 获取其结构——第二次调用只需几十毫秒,因为页面已经打开。
HTTP 守护进程
node bin/harvest.mjs serve --port 8787
curl "http://127.0.0.1:8787/blocks?url=https://figma.site&width=1440"
curl "http://127.0.0.1:8787/block?url=https://figma.site&index=4"仅绑定到 loopback——它会获取任意 URL 并按指示写入文件,因此不应可从外部访问。如果你确实需要,可以传入 --host。空闲页面在 10 分钟后关闭。
环境要求
Node 18+
Playwright Chromium —
npm install通过 postinstall 钩子获取ffmpeg / ffprobe (可选) — 用于内容框、调色板和视频分析。没有它其余功能仍可运行;资源智能分析会被跳过并给出警告。
brew install ffmpeg
npm test # 46 tests, ~10s, hermetic (local fixture, no network)已知限制
跨域 iframe 是 DOM 和截图中的空洞。工具能检测到它们,并在
block.md中列出其大小、来源和src,但无法看到内部。iframe 渲染的任何内容都需要单独处理。悬停和聚焦样式不会被捕获——它们需要实时交互。
interactions.md会给出每个元素的过渡属性和时长,告诉你什么会动画以及动画速度,但不包含最终状态。流媒体视频(DASH/fMP4)以分片形式到达,这些分片不是有效的独立文件。它们会被标记,而不会被报告为损坏。
Canvas 和 WebGL 内容在截图中以像素形式捕获;没有可提取的结构。
滚动驱动动画只在一个时间点采样。页面会先从头到尾滚动以触发显现效果,然后回到顶部;外观依赖于滚动位置的区块可能不处于其最终状态。
This server cannot be installed
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Connectors
Turn any live website into brand colors, fonts, design tokens, SVGs, Lottie and paste-ready code.
UI design from prompts, screenshots, and URLs for AI coding agents and theme tokens.
Score any URL against a real design contract — 40 checks, A-F grade, token + motion validation.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/ChenYCL/web-design-harvester'
If you have feedback or need assistance with the MCP directory API, please join our Discord server