web-design-harvester
web-design-harvester
Превращает отрендеренную веб-страницу в дизайн-спецификацию, по которой LLM может собрать сайт: посекционные скриншоты, дистиллированные вычисленные CSS-стили, дизайн-токены, адаптивные дельты и ассеты, проанализированные вплоть до альфа-канала.
Создан для воспроизведения страниц 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 → Copy all CSS → вставить 2000+ строк в чат → модель выуживает несколько важных значений → скриншот несоответствия → повторять семь-восемь раз.
Каждый шаг этого процесса механический, а исходный материал хуже, чем кажется: CSS-экспорт Figma содержит повёрнутые фреймы с нечитаемыми отрицательными координатами, слои-заглушки с display: none и перемешанные десктопные и мобильные варианты.
У отрендеренного DOM нет ни одной из этих проблем. getComputedStyle() на живой странице — это разрешённая истина, на любом брейкпоинте, с приложенным списком ассетов из сети. Этот инструмент читает это и записывает.
REST API Figma — не вариант
GET /v1/files/{key} возвращает 400 "File type not supported by this endpoint" для файлов с editorType: "sites" или "make". Читаются только классические файлы design. Проверьте /v1/files/{key}/meta перед планированием любой выгрузки — /meta и /styles работают для всех типов, а node-эндпоинты — нет.
Related MCP server: figma-context-mcp-server
Как показать страницу браузеру
Это та часть, на которой все спотыкаются, поэтому стоит быть точным.
Что у вас есть | Работает? | Как |
Опубликованный сайт | ✅ Да | Просто передайте URL. Это обычная публичная страница. |
Превью-iframe | ❌ Нет | См. ниже. |
Неопубликованный сайт, открытый в вашем Chrome | ✅ Да |
|
Любой другой сайт, localhost, staging | ✅ Да | Просто передайте URL. |
URL превью-iframe не работает сам по себе
Он выглядит как страница и возвращает HTTP 200, но при загрузке вы получаете оболочку размером ~3,6 КБ, содержащую только слушатель 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, откройте превью сайта, затем укажите харвестеру на эту вкладку:
# 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 и подобные), выносятся и указываются один раз. Выживает то, что отличается, — а именно это и нужно писать. На практике это примерно на порядок меньше, чем сырой дамп.
Ассеты измеряются, а не предполагаются. Для каждого изображения и видео инструмент декодирует кадр и находит границы содержимого по альфа-каналу. Дизайн-ассеты часто поставляются как квадрат 1200×1200, где арт занимает смещённую область 1049×677 — по одним размерам файла это не видно, и оба варианта object-contain (мёртвая зона) и object-cover + центр (смещённые кадрирования) ошибаются. В выводе указывается object-position, который нужно использовать.
VP9 WebM с альфа-каналом обрабатывается особым образом: ffprobe сообщает pix_fmt=yuv420p и не показывает альфа-канал, но браузеры композитируют его корректно. Альфа появляется только при принудительном использовании декодера libvpx-vp9.
Невозможные макеты выявляются на первом же прогоне. Когда соотношение сторон содержимого ассета и контейнера, в котором он лежит, сильно расходятся, ни одно значение 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.8s холодный → 0.03s тёплый.
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, и в скриншоте. Инструмент обнаруживает их и перечисляет их размер, origin и
srcвblock.md, но не может заглянуть внутрь. Всё, что рендерит фрейм, нужно обрабатывать отдельно.Стили наведения и фокуса не захватываются — им нужно живое взаимодействие.
interactions.mdдаёт вам свойство перехода и длительность для каждого элемента, что показывает, что анимируется и как быстро, но не конечное состояние.Стриминговое видео (DASH/fMP4) приходит сегментами, которые не являются валидными отдельными файлами. Они помечаются, а не сообщаются как повреждённые.
Canvas и WebGL контент захватывается как пиксели на скриншоте; структуры для извлечения нет.
Анимация, управляемая прокруткой, сэмплируется в одной точке. Страница сначала прокручивается от начала до конца, чтобы вызвать появления, затем возвращается вверх; секция, появление которой зависит от позиции прокрутки, может быть не в конечном состоянии.
This server cannot be deployed
Maintenance
Related MCP Connectors
Turn any live website into brand colors, fonts, design tokens, SVGs, Lottie and paste-ready code.
See the rendered web: design tokens, fonts, animation timings, WebGL shaders and 3D from any URL.
Give agents eyes on any web page: structured context, and changes explained in plain language.
Captured web interfaces, screenshots, flows and structured evidence for AI coding agents.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceEnables AI agents to see, analyze, and visually verify web page changes through pixel-perfect diffing, theme extraction, layout analysis, and interactive element detection.2 npmMIT
- FlicenseAqualityDmaintenanceConverts Figma design data into structured semantic analysis JSON and screenshots for AI agents to use in design-to-code workflows.5-
- AlicenseAqualityAmaintenanceAnalyzes and extracts design tokens, assets, and layout from live websites to enable AI clients to faithfully replicate them, with tools for screenshotting, component inspection, and pixel-diff verification.146 npm4MIT
- AlicenseNot gradedqualityAmaintenanceEnables AI agents to inspect web pages deterministically as token-cheap visual geometry instead of pixel dumps, surfacing layout tooling for overlaps, overflow, WCAG contrast ratios, and quality scoring. Agents can pass HTML source or a live URL per call and receive byte-identical, actionable findings they can act on and re-verify.1MIT