Skip to main content
Glama
ChenYCL

web-design-harvester

by ChenYCL

web-design-harvester

Convierte una página web renderizada en una especificación de diseño que un LLM pueda usar para construir: capturas de pantalla por sección, CSS computado destilado, tokens de diseño, deltas responsivos y activos analizados hasta su canal alfa.

Creado para reproducir páginas de Figma Sites, pero nada en él es específico de Figma: funciona con cualquier URL que se renderice.

npm install
node bin/harvest.mjs https://example.figma.site --out ./spec --clean
# then point a model at ./spec/README.md

Por qué existe esto

Reproducir un diseño solía ser: seleccionar un bloque en Figma → Copiar todo el CSS → pegar más de 2000 líneas en un chat → el modelo extrae el puñado de valores que importan → captura de pantalla del desajuste → repetir, siete u ocho veces.

Cada paso de eso es mecánico, y el material de origen es peor de lo que parece: la exportación CSS de Figma trae marcos rotados con coordenadas negativas ilegibles, capas de marcador de posición con display: none, y variantes de escritorio y móvil intercaladas.

El DOM renderizado no tiene ninguno de esos problemas. getComputedStyle() en una página viva es la verdad resuelta, en cualquier punto de interrupción que quieras, con la lista de activos de la red adjunta. Esta herramienta lee eso y lo escribe.

La API REST de Figma no es una opción

GET /v1/files/{key} devuelve 400 "File type not supported by this endpoint" para archivos con editorType: "sites" o "make". Solo los archivos clásicos de design son legibles. Comprueba /v1/files/{key}/meta antes de planificar cualquier extracción: /meta y /styles funcionan para todos los tipos, los endpoints de nodos no.


Poner la página delante del navegador

Esta es la parte que hace tropezar a la gente, así que vale la pena ser precisos.

Lo que tienes

¿Funciona?

Cómo

Sitio publicado https://<name>.figma.site

✅ Sí

Simplemente pasa la URL. Es una página pública normal.

Iframe de vista previa https://<uuid>-v2-figmaiframepreview.figma.site

No

Ver abajo.

Sitio no publicado, abierto en tu Chrome

✅ Sí

--cdp — ver abajo.

Cualquier otro sitio, localhost, staging

✅ Sí

Simplemente pasa la URL.

La URL del iframe de vista previa no funciona de forma independiente

Parece una página y devuelve HTTP 200, pero al obtenerla obtienes un shell de ~3.6KB que contiene solo un listener de postMessage. No tiene contenido propio:

// 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

El código del sitio llega a través de un MessagePort desde una pestaña de figma.com con sesión iniciada. Carga la URL directamente y obtienes un documento en blanco, no importa cuánto esperes. No hay token que proporcionar ni cabecera que establecer: el contenido simplemente no está ahí.

Para un sitio no publicado: conéctate a tu propio navegador

Inicia Chrome con depuración remota, inicia sesión en Figma, abre la vista previa del sitio y luego apunta el harvester a esa pestaña:

# 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

Con --cdp la herramienta se conecta a tu navegador y nunca lanza ni cierra nada. La alternativa más simple, si puedes: publica el sitio y cosecha la URL pública.

Para sitios detrás de un inicio de sesión que prefieras no volver a introducir en cada ejecución, --persist mantiene un perfil en disco entre ejecuciones.


Uso

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

Opción

Por defecto

--out <dir>

./out

directorio de salida

--widths <lista>

1440,375

puntos de interrupción, p. ej. 1440,768,375

--selector <css>

auto

forzar límites de bloque

--settle <ms>

800

espera extra después de que la página se estabilice

--max-nodes <n>

400

límite de nodos por bloque

--skip-assets

omitir descarga y análisis de activos

--clean

limpiar el directorio de salida primero

--headed

mostrar la ventana del navegador

--persist [dir]

reutilizar un perfil, manteniendo inicios de sesión entre ejecuciones

--cdp <endpoint>

conectarse a un Chrome en ejecución (puerto o URL ws://)

--json

salida estándar legible por máquina

Empieza con outline o blocks en una página desconocida. Son rápidos y te dicen si la segmentación automática encontró secciones sensatas antes de comprometerte a una ejecución completa.

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…
  …

Si los límites son incorrectos, pasa --selector "main > section".


Salida

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

Un block.md se ve así:

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

más geometría por punto de interrupción, el texto, los activos utilizados y una tabla de deltas responsivos. El JSON junto a él tiene los valores completos si algo parece raro.


Lo que hace que una captura de pantalla no hace

Las capturas de pantalla son 1 píxel CSS = 1 píxel de imagen. deviceScaleFactor: 1 más scale: 'css' significa que una distancia medida en el PNG es un píxel CSS. Sin factor de conversión, así que no hay errores de conversión. (Las exportaciones @2x de Figma ponen 1798 píxeles de imagen contra un diseño de 1440px — cada medición necesitaba dividirse por 1.2486 primero, y equivocarse producía números plausibles pero incorrectos.)

Los estilos computados se destilan, no se vuelcan. Tres filtros se ejecutan sobre cada elemento: se eliminan los valores por defecto de UA para esa etiqueta, se eliminan los valores heredados que el padre ya declara, y las declaraciones que aparecen en casi todos los nodos (box-sizing: border-box y similares) se extraen y se declaran una vez. Lo que sobrevive es lo que difiere, que es lo que realmente tienes que escribir. En la práctica, esto es aproximadamente un orden de magnitud más pequeño que un volcado crudo.

Los activos se miden, no se asumen. Para cada imagen y video, la herramienta decodifica un fotograma y encuentra el cuadro delimitador de contenido a partir del canal alfa. Los activos de diseño suelen venir como un cuadrado de 1200×1200 con la obra ocupando una región descentrada de 1049×677 — desde las dimensiones del archivo solo eso es invisible, y tanto object-contain (espacio muerto) como object-cover + centro (recorta fuera de eje) lo hacen mal. La salida indica el object-position a usar.

VP9 WebM con alfa se maneja especialmente: ffprobe informa pix_fmt=yuv420p y no muestra canal alfa, pero los navegadores lo componen correctamente. El alfa solo aparece si fuerzas el decodificador libvpx-vp9.

Los diseños imposibles se señalan en la primera ejecución. Cuando la relación de contenido de un activo y la caja en la que se encuentra discrepan mucho, ningún valor de object-fit lo arregla: el activo necesita re-exportarse. El README lo señala de inmediato, con el porcentaje de obra que cover descartaría, en lugar de dejarte pasar seis rondas ajustando CSS para arreglar un problema de exportación.

Ambos puntos de interrupción, porque la mitad de la especificación está en la diferencia. Radio de tarjeta 12px → 6px, título 20/30 → 16/24, padding de cabecera 32px → 32px (sin cambios). Nada de eso es derivable escalando; responsive.md lista cada valor que se mueve.

Cualquier número de puntos de interrupción. --widths 1440,768,375 captura tres, y todo escala con ello: una captura de pantalla y un árbol de estilos por punto de interrupción, recuentos de uso desglosados por punto de interrupción en tokens.md, y tablas de deltas para cada par adyacentedesktop → tablet, luego tablet → mobile. Adyacente en lugar de todo contra escritorio, porque refleja cómo se escriben las media queries: cada paso solo reafirma lo que cambió desde el anterior. responsive.md abre con una matriz de qué bloques cambian en qué paso.

Nada se descarta en silencio. Después de la segmentación, la herramienta comprueba que los bloques cubren la página, busca un elemento que cubra cualquier hueco e informa la proporción de cobertura. Las regiones que genuinamente no puede reclamar se listan en lugar de ignorarse. Las dimensiones de la captura de pantalla se verifican contra lo solicitado, porque una captura truncada es peor que una fallida: parece bien y cada medición sobre ella está silenciosamente mal.


Servirlo a un modelo

Una ejecución en frío pasa la mayor parte del tiempo en el arranque del navegador y la primera pintura. Si un modelo está iterando — revisa ese bloque, ahora a 768px, ¿de qué color es ese icono? — pagar eso por pregunta hace que la herramienta sea inutilizable. Ambos modos de servidor mantienen un navegador y sus páginas cargadas calientes.

Medido en https://figma.site: 26.8s en frío → 0.03s en caliente.

MCP (stdio)

{
  "mcpServers": {
    "web-design-harvester": {
      "command": "node",
      "args": ["/absolute/path/to/web-design-harvester/bin/harvest.mjs", "mcp"]
    }
  }
}

Herramientas: harvest_outline, harvest_blocks, harvest_block, harvest_tokens, harvest_assets, harvest_screenshot, harvest_analyse_asset, harvest_site, harvest_status.

El bucle típico es harvest_blocks para encontrar la sección, luego harvest_block para su estructura: la segunda llamada llega en decenas de milisegundos porque la página ya está abierta.

Daemon 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"

Se vincula solo a loopback: obtiene URLs arbitrarias y escribe archivos donde se le indique, así que no debería ser accesible desde fuera. Pasa --host si realmente lo quieres. Las páginas inactivas se cierran después de 10 minutos.


Requisitos

  • Node 18+

  • Playwright Chromiumnpm install lo descarga mediante el hook de postinstall

  • ffmpeg / ffprobe (opcional) — necesario para cajas de contenido, paletas y análisis de video. Sin él, todo lo demás sigue funcionando; la inteligencia de activos se omite con una advertencia. brew install ffmpeg

npm test    # 46 tests, ~10s, hermetic (local fixture, no network)

Limitaciones conocidas

  • Los iframes de origen cruzado son agujeros tanto en el DOM como en la captura de pantalla. La herramienta los detecta y lista su tamaño, origen y src en block.md, pero no puede ver dentro. Lo que el frame renderice tiene que manejarse por separado.

  • Los estilos de hover y focus no se capturan: necesitan interacción en vivo. interactions.md te da la propiedad de transición y duración de cada elemento, lo que te dice qué anima y qué tan rápido, pero no el estado final.

  • Video en streaming (DASH/fMP4) llega como segmentos que no son archivos independientes válidos. Se etiquetan en lugar de informarse como corruptos.

  • El contenido de Canvas y WebGL se captura como píxeles en la captura de pantalla; no hay estructura que extraer.

  • La animación impulsada por scroll se muestrea en un punto. La página se desplaza de principio a fin primero para activar revelaciones, luego se devuelve a la parte superior; una sección cuya apariencia depende de la posición de scroll puede no estar en su estado final.

-
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