Communitygithub.com

ethanweber/posterskill

Genera un póster de conferencia en HTML a partir de un artículo y un sitio web del proyecto, imprimible como PDF.

¿Qué es posterskill?

Esta habilidad para agentes de IA (como Claude Code) permite generar automáticamente un póster profesional de conferencia en formato HTML, partiendo del código fuente de un artículo académico y su sitio web asociado. El póster es una aplicación React interactiva contenida en un solo archivo HTML, sin necesidad de compilación. El agente extrae el contenido del artículo (título, autores, resumen, figuras, ecuaciones, etc.), obtiene imágenes y logos desde el sitio web, analiza pósters de referencia para igualar el estilo visual, y genera un diseño optimizado en columnas. Incluye un optimizador automático con Playwright que minimiza los espacios en blanco, y permite al usuario ajustar el diseño interactivamente en el navegador (redimensionar columnas, intercambiar tarjetas, cambiar escala de fuente) y exportar la configuración de vuelta al agente para iterar. El resultado final es un póster autónomo y listo para imprimir en PDF, con soporte para logos institucionales, códigos QR y ecuaciones KaTeX.

Compatible con✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/ethanweber/posterskill/tree/HEAD/.claude/skills/make-poster

Preguntar en tu IA favorita

Abre un nuevo chat con esta habilidad de agente ya precargada.

Vista previa

Del README del skill

posterskill preview

Documentación

Generador de Póster de Conferencia (HTML)

Genera un póster HTML profesional. Notas del usuario: $ARGUMENTS

El póster es un editor interactivo basado en React — un único archivo HTML autocontenido en el directorio poster/. No necesita paso de compilación (React/Babel se cargan vía CDN). El usuario puede ajustar visualmente la disposición en su navegador y luego exportar la configuración de vuelta a Claude para realizar más cambios.

Estructura de la carpeta del proyecto

<proyecto>/
├── overleaf/              # Código fuente del artículo desde Overleaf
│   ├── paper.tex
│   ├── figures/
│   └── ...
├── references/            # Pósters de referencia para igualar el estilo
│   └── (cualquier formato: pdf, png, jpg, html, pptx, ...)
├── poster/                # GENERADO: sitio web del póster autocontenido
│   ├── index.html         # El póster (aplicación React)
│   ├── poster-config.json # Configuración de diseño (columnas, orden de tarjetas, alturas, escala de fuente)
│   ├── logos/             # Logos institucionales
│   ├── teaser.png         # Figuras copiadas/convertidas
│   ├── qr.png             # Código QR de la página del proyecto
│   ├── qr-posterskill.png # Código QR de Posterskill
│   └── ...
└── .claude/skills/make-poster/
  • overleaf/ contiene el código fuente del artículo. Lea el archivo .tex principal y cualquier archivo que incluya con \input{}.
  • references/ contiene pósters de ejemplo que muestran el estilo visual preferido del usuario. Lea/vea TODOS los archivos de esta carpeta para igualar su lenguaje de diseño.
  • poster/ es la salida generada — un sitio web autocontenido. Todas las figuras y activos se encuentran junto a index.html para que las rutas relativas funcionen sin problemas.

Entradas

  1. Código fuente del artículo - Ubicado en overleaf/. Pregunte al usuario qué archivo .tex es el principal (ej., paper.tex, main.tex). Luego léalo junto con los archivos que incluya con \input{}.
  2. Sitio web del proyecto - Pregunte al usuario la URL si no se conoce ya. Obténgalo con WebFetch para extraer información de autores, imágenes alojadas y enlaces.
  3. Pósters de referencia - Descubiertos automáticamente desde references/. Vea todos los archivos allí y empareje su estilo.
  4. Sitio web del autor (opcional) - Obténgalo con WebFetch y extraiga señales de diseño (paleta de colores, tipografía, logos). Descargue los logos institucionales del sitio del autor usando Playwright (curl puede fallar debido a redirecciones): page.request.get(url) para descargar los bytes sin procesar.
  5. Requisitos de formato - Pregunte por las dimensiones del póster, orientación y número de columnas.
  6. Repositorio Git (opcional) - Pregunte al usuario si tiene un repositorio de GitHub para subir el póster.

Si el usuario no especifica el formato, pregúntele antes de continuar. No asuma valores predeterminados para dimensiones, orientación o número de columnas.

Proceso

Paso 0: Analizar referencias de estilo

Busque en references/ archivos PDF, PNG o de imagen. Convierta los PDF a PNG (sips -s format png en macOS). Vea cada uno y observe el estilo visual — diseño, colores, tipografía, estilos de tarjetas, colocación de figuras. Empareje el estilo de referencia — no use un tema oscuro por defecto si la referencia es clara, etc.

Paso 1: Extraer contenido del artículo

Pregunte al usuario qué archivo .tex leer. Extraiga: título, autores, afiliaciones, resumen (2-3 oraciones), método clave, resultados (tablas + figuras), ecuaciones clave (1-2 máximo), conclusión.

Paso 2: Obtener el sitio web del proyecto

Use WebFetch para obtener nombres de autores, afiliaciones, URL de figuras, URL del proyecto para el código QR, enlaces al código/arxiv/video.

Paso 3: Reunir activos en poster/

Figuras: Copie desde overleaf/figures/, convirtiendo PDF a PNG en alta resolución:

sips -s format png input.pdf --out poster/output.png -Z 3000

Imágenes del sitio web: Descargue imágenes de mayor calidad del sitio web del proyecto usando Playwright (no curl — muchos sitios redirigen):

resp = page.request.get(url)
with open('poster/filename.png', 'wb') as f:
    f.write(resp.body())

Logos: Descargue los logos institucionales del sitio web personal del autor usando Playwright. Guárdelos en poster/logos/. La plantilla los invierte automáticamente a blanco para el encabezado.

Códigos QR: Genere y guarde:

curl -sL -o poster/qr.png "https://api.qrserver.com/v1/create-qr-code/?size=400x400&data=PROJECT_URL"
curl -sL -o poster/qr-posterskill.png "https://api.qrserver.com/v1/create-qr-code/?size=400x400&data=https://github.com/ethanweber/posterskill"

Paso 4: Medir las relaciones de aspecto de las imágenes

Esto es crítico para eliminar espacios en blanco. Mida cada imagen:

sips -g pixelWidth -g pixelHeight poster/*.png poster/*.jpg

Luego asigne imágenes a columnas según su relación de aspecto:

  • Imágenes anchas (>2:1 ratio, ej., teaser, arquitectura): colóquelas en la columna más ancha
  • Imágenes cuadradas (~1:1 ratio): colóquelas en columnas estrechas
  • Imágenes verticales (<1:1 ratio): colóquelas en la columna más estrecha

Esto evita el problema #1 de espacios en blanco: imágenes anchas en celdas estrechas (o viceversa) que dejan grandes huecos.

Paso 5: Generar el HTML del póster

Use la plantilla en ${{CLAUDE_SKILL_DIR}}/template.html como punto de partida. La plantilla es una aplicación React con:

Arquitectura:

  • CARD_REGISTRY — define el contenido de cada tarjeta (título, color, cuerpo JSX)
  • DEFAULT_LAYOUT — define la estructura de columnas y el orden de tarjetas
  • DEFAULT_LOGOS — logos institucionales para el encabezado
  • El estado de React gestiona el diseño, con persistencia en localStorage
  • window.posterAPI expone funciones para control programático

Cosas clave para personalizar:

  1. Actualice CARD_REGISTRY con el contenido del artículo (cada sección es una tarjeta)
  2. Actualice DEFAULT_LAYOUT con las asignaciones de columna optimizadas por relación de aspecto
  3. Actualice DEFAULT_LOGOS con los logos institucionales del usuario
  4. Actualice DEFAULT_FONT_SCALE (empiece en 1.3, el usuario puede ajustar con botones A-/A+)
  5. Actualice el encabezado (título, autores, afiliaciones, insignia de conferencia, códigos QR)
  6. Actualice @page { size: WIDTHmm HEIGHTmm; } y body { width: WIDTHmm; height: HEIGHTmm; } para las dimensiones del póster
  7. Actualice la función fit() de posterAPI con las mismas dimensiones

Patrones de contenido de tarjetas:

  • Tarjeta de figura: <div className="fig"><div className="fig-wrap"><img src="file.png" alt="..." /></div><div className="cap"><b>Título del pie.</b> Descripción.</div></div>
  • Tarjeta de texto: <div className="hl"><p>Texto destacado</p></div><ul><li>Punto 1</li></ul>
  • Tarjeta de tabla: <table><thead>...</thead><tbody>...</tbody></table> con className="best" en las celdas ganadoras
  • Tarjeta de ecuación: <div className="eq">{'$Ecuación LaTeX$'}</div> (escape de barras invertidas en JSX)

Reglas CSS críticas para cero espacios en blanco:

  • Las imágenes DEBEN usar width:100%; height:100%; object-fit:contain (NO max-width/max-height — evitan el escalado ascendente)
  • Cada columna debe tener siempre una tarjeta con grow: true (flex:1) que llene el espacio restante
  • La función isCardGrow() garantiza esto automáticamente — si ninguna tarjeta tiene grow, la última tarjeta lo recibe

Escalado de ventana gráfica:

  • Use translate() + scale() en el body para centrar y ajustar el póster a cualquier ventana del navegador
  • transform-origin: top left es crítico
  • @media print debe establecer transform: none !important para una resolución de impresión correcta
  • getBoundingClientRect() devuelve valores ESCALADOS — divida siempre por currentScaleRef.current en los manejadores de redimensionamiento

Paso 6: Optimizar automáticamente el diseño con Playwright

Después de generar, use Playwright para medir espacios en blanco y encontrar anchos de columna óptimos:

from playwright.sync_api import sync_playwright
import os

with sync_playwright() as p:
    browser = p.chromium.launch()
    page = browser.new_page(viewport={'width': 3200, 'height': 2260})
    page.goto('file://' + os.path.abspath('poster/index.html'))
    page.wait_for_load_state('networkidle')
    page.wait_for_timeout(3000)

    # Medir espacios en blanco
    waste = page.evaluate('window.posterAPI.getWaste()')
    print(f"Desperdicio total: {waste['total']}px")
    for d in waste['details']:
        print(f"  {d['card']}: H={d['wasteH']} W={d['wasteW']} ({d['pct']}%)")

    # Probar diferentes anchos de columna para minimizar desperdicio
    best_waste = waste['total']
    best_c1, best_c3 = 300, 230
    for c1 in range(200, 350, 10):
        for c3 in range(160, 280, 10):
            page.evaluate(f'window.posterAPI.setColumnWidth("col1", {c1})')
            page.evaluate(f'window.posterAPI.setColumnWidth("col3", {c3})')
            page.wait_for_timeout(30)
            w = page.evaluate('window.posterAPI.getWaste().total')
            if w < best_waste:
                best_waste = w
                best_c1, best_c3 = c1, c3

    # Aplicar el mejor y capturar pantalla
    page.evaluate(f'window.posterAPI.setColumnWidth("col1", {best_c1})')
    page.evaluate(f'window.posterAPI.setColumnWidth("col3", {best_c3})')
    page.wait_for_timeout(500)
    page.screenshot(path='/tmp/poster_screenshot.png')

    # También probar intercambiar tarjetas entre columnas
    page.evaluate('window.posterAPI.swapCards("cardA", "cardB")')
    # ... medir desperdicio otra vez ...

    browser.close()

Luego lea /tmp/poster_screenshot.png para inspeccionar visualmente. Itere varias veces — tome capturas de pantalla, corrija problemas, vuelva a tomar capturas hasta que el póster tenga un espacio en blanco mínimo.

Después de encontrar valores óptimos, intégrelos en DEFAULT_LAYOUT, DEFAULT_CARD_HEIGHTS, etc. en el HTML.

Paso 7: Generar PDF y verificar

page.pdf(
    path='poster/poster.pdf',
    width='841mm', height='594mm',  # coincidir con las dimensiones del póster
    margin={'top':'0','right':'0','bottom':'0','left':'0'},
    print_background=True
)

Convierta el PDF a PNG y léalo para verificar que se renderiza a máxima resolución:

sips -s format png poster/poster.pdf --out /tmp/poster_pdf_check.png -Z 3000

Paso 8: Abrir e iterar con el usuario

Abra el póster en el navegador:

open poster/index.html

Explique al usuario los controles de edición:

  • Vista previa — desactive la interfaz de edición para ver exactamente cómo se imprimirá
  • A-/A+ — ajuste el tamaño de fuente globalmente
  • Arrastrar divisores de columna (barras azules verticales) — redimensione columnas a izquierda/derecha
  • Arrastrar divisores de fila (barras azules horizontales) — redimensione tarjetas arriba/abajo dentro de las columnas
  • Haga clic para intercambiar — haga clic en el asa de diamante de una tarjeta (se vuelve naranja), luego haga clic en otra para intercambiarlas
  • Mover/insertar — haga clic en el asa de una tarjeta, luego haga clic en una zona de destino naranja discontinua para moverla allí
  • Guardar — descarga poster-config.json
  • Copiar Config — copia el JSON de diseño al portapapeles
  • Restablecer — restaura los valores predeterminados

Sugiera mejoras proactivamente: Después de mostrar el primer borrador, sugiera cambios específicos:

  • "La tarjeta de arquitectura del modelo tiene algo de espacio en blanco — intente arrastrar el divisor de fila hacia abajo para darle menos espacio"
  • "La figura de finalización podría verse mejor en la columna 2 ya que es más ancha — intente hacer clic en su diamante, luego haga clic en una zona de destino en la columna 2"
  • "Podría aumentar el tamaño de fuente con A+ varias veces"

Fomente el ciclo de retroalimentación: Dígale al usuario:

¡Intente reorganizar el póster en su navegador! Cuando esté satisfecho con el diseño, haga clic en Copiar Config en la barra de herramientas superior derecha y péguelo aquí — incorporaré esos cambios en los valores predeterminados para que persistan.

  • Guardar — descargar poster-config.json
  • Copiar Config — copiar el JSON de diseño al portapapeles para pegarlo a Claude
  • Restablecer — restaurar valores predeterminados

Cuando el usuario pegue un JSON de configuración, actualice DEFAULT_LAYOUT, DEFAULT_CARD_HEIGHTS, DEFAULT_FONT_SCALE y DEFAULT_LOGOS en el HTML para coincidir. También escríbalo a poster-config.json.

Paso 9: Subir a GitHub (opcional)

Si el usuario proporciona una URL de repositorio de GitHub:

cd poster
git init
git remote add origin <REPO_URL>
git add .
git commit -m "Póster: <título del artículo>"
git push -u origin main

Pautas importantes

  • Sin espacios en blanco. Esta es la prioridad #1. Use asignación de columnas consciente de la relación de aspecto, width:100%; height:100%; object-fit:contain en imágenes, tarjetas de crecimiento automático y el optimizador de Playwright. Itere hasta que el desperdicio sea mínimo.
  • Mantenga el texto al mínimo. Los pósters son visuales — viñetas, no párrafos. Comprensión en 2 minutos.
  • Empareje el estilo de referencia. Si el póster de referencia es claro/limpio, no use un tema oscuro. Empareje la estética general.
  • Escalado de fuente. Todos los tamaños de texto usan calc(Xpt * var(--font-scale)) para que los botones A-/A+ funcionen. Empiece con --font-scale: 1.3 y deje que el usuario ajuste.
  • CSS optimizado para impresión. @media print oculta toda la interfaz de edición y establece transform: none !important. @page establece dimensiones exactas.
  • QR de Posterskill. Incluya siempre un código QR que enlace a https://github.com/ethanweber/posterskill en el encabezado con la etiqueta "Póster hecho con mi habilidad de Claude".
  • Sin pie de página de agradecimientos. Mantenga el póster limpio — sin pie de página por defecto.
  • Logos en el encabezado. Descargue logos institucionales del sitio web del autor, guárdelos en poster/logos/ y enumérelos en DEFAULT_LOGOS. Se invierten automáticamente a blanco mediante filtro CSS.
  • Autocontenido. Sin paso de compilación, sin npm, sin servidor. Un solo archivo HTML con dependencias CDN. Funciona cuando se abre directamente como file://.
  • Ecuaciones. Use KaTeX (cargado vía CDN). Escape las barras invertidas en cadenas JSX: {'$\mathcal{E}$'}.

Manejo de figuras

  • Siempre copie las figuras necesarias en poster/ — no haga referencia a rutas de overleaf/ en el HTML.
  • Convierta PDF a PNG en alta resolución: sips -s format png input.pdf --out poster/output.png -Z 3000
  • Descargue imágenes del sitio web mediante page.request.get() de Playwright (no curl — los sitios web a menudo redirigen).
  • Mida las relaciones de aspecto con sips -g pixelWidth -g pixelHeight y asigne a columnas según corresponda.

Flujo de trabajo del usuario para actualizaciones de configuración

El usuario puede ajustar el póster en su navegador y compartir los cambios:

  1. El usuario hace clic en "Copiar Config" en la barra de herramientas
  2. El usuario pega el JSON en el chat
  3. Claude actualiza DEFAULT_LAYOUT, DEFAULT_CARD_HEIGHTS, DEFAULT_FONT_SCALE, DEFAULT_LOGOS en index.html para coincidir
  4. Claude también escribe la configuración en poster-config.json
  5. El usuario actualiza y hace clic en "Restablecer" para cargar los nuevos valores predeterminados

API programática (window.posterAPI)

Disponible en la consola del navegador o mediante Playwright:

  • swapCards(id1, id2) — intercambia dos tarjetas (funciona entre columnas)
  • moveCard(cardId, targetColId, position) — mueve una tarjeta a una posición específica
  • setColumnWidth(colId, widthMm) — establece el ancho de la columna (null para flex)
  • ...

[truncado para traducción]

Skills relacionados