Skip to main content
Glama
ChenYCL

web-design-harvester

by ChenYCL

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-эндпоинты — нет.


Как показать страницу браузеру

Это та часть, на которой все спотыкаются, поэтому стоит быть точным.

Что у вас есть

Работает?

Как

Опубликованный сайт https://<name>.figma.site

✅ Да

Просто передайте URL. Это обычная публичная страница.

Превью-iframe https://<uuid>-v2-figmaiframepreview.figma.site

Нет

См. ниже.

Неопубликованный сайт, открытый в вашем Chrome

✅ Да

--cdp — см. ниже.

Любой другой сайт, 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

Опция

По умолчанию

--out <dir>

./out

выходная директория

--widths <list>

1440,375

брейкпоинты, напр. 1440,768,375

--selector <css>

auto

принудительные границы блоков

--settle <ms>

800

дополнительное ожидание после стабилизации страницы

--max-nodes <n>

400

лимит узлов на блок

--skip-assets

пропустить загрузку и анализ ассетов

--clean

сначала очистить выходную директорию

--headed

показать окно браузера

--persist [dir]

переиспользовать профиль, сохраняя логины между запусками

--cdp <endpoint>

подключиться к запущенному Chrome (порт или ws:// URL)

--json

машиночитаемый stdout

Начните с outline или blocks на незнакомой странице. Они быстрые и показывают, нашла ли автоматическая сегментация разумные секции, прежде чем вы запустите полный прогон.

node bin/harvest.mjs blocks https://figma.site --widths 1440
strategy: 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 hash

block.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 Chromiumnpm 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 контент захватывается как пиксели на скриншоте; структуры для извлечения нет.

  • Анимация, управляемая прокруткой, сэмплируется в одной точке. Страница сначала прокручивается от начала до конца, чтобы вызвать появления, затем возвращается вверх; секция, появление которой зависит от позиции прокрутки, может быть не в конечном состоянии.

-
license - not tested
Not graded
quality - not tested
C
maintenance

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.

View all MCP Connectors

Latest Blog Posts

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