Communitygithub.com

ethanweber/posterskill

Gera um pôster de conferência em HTML a partir de um artigo e site do projeto, imprimível em PDF.

O que é posterskill?

Esta skill do Claude cria um pôster de conferência profissional em HTML, totalmente autossuficiente e interativo, pronto para ser impresso em PDF. Ela extrai conteúdo do artigo fonte em LaTeX, baixa imagens de alta qualidade do site do projeto e analisa pôsteres de referência fornecidos pelo usuário para casar o estilo visual. O resultado é uma aplicação React em um único arquivo HTML que pode ser editada visualmente no navegador: o usuário pode redimensionar colunas, reorganizar cards, ajustar fontes e otimizar layout, exportando a configuração para refinamento posterior com Claude. A skill prioriza a eliminação de espaços em branco através de ajuste automático de aspect ratio de imagens e otimização via Playwright, suportando dashboards de conferências como Overleaf (para o artigo) e sites de projetos. Ideal para pesquisadores que desejam criar pôsteres atraentes e adaptáveis com mínimo esforço manual, mantendo total controle sobre o design final.

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

Perguntar na sua IA favorita

Abre um novo chat com esta habilidade de agente já pré-carregada.

Prévia

Do README da skill

posterskill preview

Documentação

Gerador de Pôster de Conferência (HTML)

Gere um pôster HTML profissional. Notas do usuário: $ARGUMENTS

O pôster é um editor interativo baseado em React — um único arquivo HTML autocontido no diretório poster/. Nenhuma etapa de build necessária (React/Babel carregados via CDN). O usuário pode ajustar visualmente o layout no navegador e exportar a configuração de volta para Claude para alterações adicionais.

Estrutura da pasta do projeto

<projeto>/
├── overleaf/              # Código-fonte do artigo do Overleaf
│   ├── paper.tex
│   ├── figures/
│   └── ...
├── references/            # Pôsteres de referência para correspondência de estilo
│   └── (qualquer formato: pdf, png, jpg, html, pptx, ...)
├── poster/                # GERADO: site do pôster autocontido
│   ├── index.html         # O pôster (aplicação React)
│   ├── poster-config.json # Configuração do layout (colunas, ordem dos cards, alturas, escala de fonte)
│   ├── logos/             # Logos institucionais
│   ├── teaser.png         # Figuras copiadas/convertidas
│   ├── qr.png             # QR code da página do projeto
│   ├── qr-posterskill.png # QR code do Posterskill
│   └── ...
└── .claude/skills/make-poster/
  • overleaf/ contém o código-fonte do artigo. Leia o arquivo .tex principal e quaisquer arquivos que ele inclua com \input{}.
  • references/ contém pôsteres de exemplo mostrando o estilo visual preferido do usuário. Leia/visualize TODOS os arquivos nesta pasta para casar a linguagem de design.
  • poster/ é a saída gerada — um site autocontido. Todas as figuras e assets ficam junto com index.html, então caminhos relativos funcionam.

Entradas

  1. Código-fonte do artigo - Localizado em overleaf/. Pergunte ao usuário qual arquivo .tex é o principal (ex.: paper.tex, main.tex). Em seguida, leia-o e quaisquer arquivos que ele inclua com \input{}.
  2. Site do projeto - Pergunte ao usuário a URL, se não for conhecida. Busque com WebFetch para extrair informações dos autores, imagens hospedadas e links.
  3. Pôsteres de referência - Descobertos automaticamente em references/. Visualize todos os arquivos lá e case o estilo.
  4. Site do autor (opcional) - Busque com WebFetch e extraia sinais de design (paleta de cores, tipografia, logos). Baixe logos institucionais do site do autor usando Playwright (curl pode falhar devido a redirecionamentos): page.request.get(url) para baixar os bytes brutos.
  5. Requisitos de formatação - Pergunte pelas dimensões do pôster, orientação, número de colunas.
  6. Repositório Git (opcional) - Pergunte ao usuário se ele tem um repositório GitHub para enviar o pôster.

Se o usuário não especificar formatação, pergunte antes de prosseguir. Não assuma padrões para dimensões, orientação ou número de colunas.

Processo

Passo 0: Analisar referências de estilo

Procure em references/ por arquivos PDF, PNG ou imagem. Converta PDFs para PNGs (sips -s format png no macOS). Visualize cada um e anote o estilo visual — layout, cores, tipografia, estilos de cards, posicionamento de figuras. Case o estilo da referência — não use um tema escuro se a referência for clara, etc.

Passo 1: Extrair conteúdo do artigo fonte

Pergunte ao usuário qual arquivo .tex ler. Extraia: título, autores, afiliações, resumo (2-3 frases), método principal, resultados (tabelas + figuras), equações principais (1-2 no máximo), conclusão.

Passo 2: Buscar o site do projeto

Use WebFetch para obter nomes dos autores, afiliações, URLs de figuras, URL do projeto para QR, links para código/arxiv/vídeo.

Passo 3: Reunir assets em poster/

Figuras: Copie de overleaf/figures/, convertendo PDFs para PNGs em alta resolução:

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

Imagens do site: Baixe imagens de maior qualidade do site do projeto usando Playwright (não curl — muitos sites redirecionam):

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

Logos: Baixe logos institucionais do site pessoal do autor usando Playwright. Salve em poster/logos/. O template inverte automaticamente para branco no cabeçalho.

QR codes: Gere e salve:

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"

Passo 4: Medir proporções das imagens

Isso é crítico para eliminar espaços em branco. Meça cada imagem:

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

Em seguida, atribua imagens às colunas com base na proporção:

  • Imagens largas (proporção >2:1, ex.: teaser, arquitetura): coloque na coluna mais larga
  • Imagens quadradas (~1:1): coloque em colunas estreitas
  • Imagens retrato (<1:1): coloque na coluna mais estreita

Isso evita o problema #1 de espaços em branco: imagens largas em células estreitas (ou vice-versa) deixando grandes lacunas.

Passo 5: Gerar o HTML do pôster

Use o template em ${{CLAUDE_SKILL_DIR}}/template.html como ponto de partida. O template é uma aplicação React com:

Arquitetura:

  • CARD_REGISTRY — define o conteúdo de cada card (título, cor, corpo JSX)
  • DEFAULT_LAYOUT — define a estrutura de colunas e a ordem dos cards
  • DEFAULT_LOGOS — logos institucionais para o cabeçalho
  • Estado React gerencia o layout, com persistência em localStorage
  • window.posterAPI expõe funções para controle programático

Coisas principais para personalizar:

  1. Atualize CARD_REGISTRY com o conteúdo do artigo (cada seção é um card)
  2. Atualize DEFAULT_LAYOUT com as atribuições de colunas otimizadas por proporção
  3. Atualize DEFAULT_LOGOS com os logos institucionais do usuário
  4. Atualize DEFAULT_FONT_SCALE (comece em 1.3, o usuário pode ajustar com botões A-/A+)
  5. Atualize o cabeçalho (título, autores, afiliações, selo da conferência, QR codes)
  6. Atualize @page { size: LARGURAMm ALTURAMm; } e body { width: LARGURAMm; height: ALTURAMm; } para as dimensões do pôster
  7. Atualize a função fit() do posterAPI com as mesmas dimensões

Padrões de conteúdo dos cards:

  • Card de figura: <div className="fig"><div className="fig-wrap"><img src="file.png" alt="..." /></div><div className="cap"><b>Título da legenda.</b> Descrição.</div></div>
  • Card de texto: <div className="hl"><p>Texto de destaque</p></div><ul><li>Ponto 1</li></ul>
  • Card de tabela: <table><thead>...</thead><tbody>...</tbody></table> com className="best" nas células vencedoras
  • Card de equação: <div className="eq">{'$Equação LaTeX$'}</div> (escape barras invertidas no JSX)

Regras CSS críticas para zero espaço em branco:

  • Imagens DEVEM usar width:100%; height:100%; object-fit:contain (NÃO max-width/max-height — eles impedem o redimensionamento para cima)
  • Cada coluna deve sempre ter um card com grow: true (flex:1) que preenche o espaço restante
  • A função isCardGrow() garante isso automaticamente — se nenhum card tiver grow, o último card recebe

Escala da viewport:

  • Use translate() + scale() no body para centralizar e ajustar o pôster a qualquer viewport do navegador
  • transform-origin: top left é crítico
  • @media print deve definir transform: none !important para resolução de impressão correta
  • getBoundingClientRect() retorna valores ESCALONADOS — sempre divida por currentScaleRef.current nos manipuladores de redimensionamento

Passo 6: Auto-otimizar layout com Playwright

Após gerar, use Playwright para medir espaços em branco e encontrar larguras de coluna ideais:

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 espaço em branco
    waste = page.evaluate('window.posterAPI.getWaste()')
    print(f"Total de desperdício: {waste['total']}px")
    for d in waste['details']:
        print(f"  {d['card']}: H={d['wasteH']} W={d['wasteW']} ({d['pct']}%)")

    # Tentar diferentes larguras de coluna para minimizar desperdício
    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 melhor e capturar tela
    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')

    # Também tentar trocar cards entre colunas
    page.evaluate('window.posterAPI.swapCards("cardA", "cardB")')
    # ... medir desperdício novamente ...

    browser.close()

Em seguida, leia /tmp/poster_screenshot.png para inspeção visual. Itere várias vezes — capture telas, corrija problemas, recapture até que o pôster tenha espaço em branco mínimo.

Após encontrar valores ideais, incorpore-os em DEFAULT_LAYOUT, DEFAULT_CARD_HEIGHTS, etc. no HTML.

Passo 7: Gerar PDF e verificar

page.pdf(
    path='poster/poster.pdf',
    width='841mm', height='594mm',  # corresponder às dimensões do pôster
    margin={'top':'0','right':'0','bottom':'0','left':'0'},
    print_background=True
)

Converta o PDF para PNG e leia para verificar que ele renderiza em resolução total:

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

Passo 8: Abrir e iterar com o usuário

Abra o pôster no navegador:

open poster/index.html

Explique os controles de edição ao usuário:

  • Visualizar — desative a UI de edição para ver exatamente como será impresso
  • A-/A+ — ajuste o tamanho da fonte globalmente
  • Arrastar divisores de coluna (barras azuis verticais) — redimensione colunas para esquerda/direita
  • Arrastar divisores de linha (barras azuis horizontais) — redimensione cards para cima/baixo dentro das colunas
  • Clique-para-trocar — clique no identificador de diamante de um card (fica laranja), depois clique em outro para trocá-los
  • Mover/inserir — clique no identificador de um card, depois clique em uma zona de soltar laranja tracejada para movê-lo
  • Salvar — baixa poster-config.json
  • Copiar Config — copia o JSON do layout para a área de transferência
  • Resetar — restaurar padrões

Sugira melhorias proativamente: Após mostrar o primeiro rascunho, sugira mudanças específicas:

  • "O card de arquitetura do modelo tem algum espaço em branco — tente arrastar o divisor de linha acima dele para baixo para dar menos espaço"
  • "A figura de conclusão pode ficar melhor na coluna 2, já que é mais larga — tente clicar no diamante, depois clicar em uma zona de soltar na coluna 2"
  • "Você pode querer aumentar o tamanho da fonte com A+ algumas vezes"

Incentive o ciclo de feedback: Diga ao usuário:

Tente reorganizar o pôster no seu navegador! Quando estiver satisfeito com o layout, clique em Copiar Config na barra de ferramentas superior direita e cole aqui — eu incorporarei essas alterações nos padrões para que persistam.

  • Salvar — baixa poster-config.json
  • Copiar Config — copia o JSON do layout para a área de transferência para colar no Claude
  • Resetar — restaurar padrões

Quando o usuário colar um JSON de configuração, atualize DEFAULT_LAYOUT, DEFAULT_CARD_HEIGHTS, DEFAULT_FONT_SCALE e DEFAULT_LOGOS no HTML para corresponder. Também escreva em poster-config.json.

Passo 9: Enviar para GitHub (opcional)

Se o usuário fornecer uma URL de repositório GitHub:

cd poster
git init
git remote add origin <REPO_URL>
git add .
git commit -m "Poster: <título do artigo>"
git push -u origin main

Diretrizes importantes

  • Sem espaço em branco. Esta é a prioridade #1. Use atribuição de colunas ciente da proporção, width:100%; height:100%; object-fit:contain nas imagens, cards de crescimento automático e o otimizador Playwright. Itere até que o desperdício seja mínimo.
  • Mantenha o texto mínimo. Pôsteres são visuais — bullets, não parágrafos. Compreensão em 2 minutos.
  • Case o estilo da referência. Se o pôster de referência for claro/limpo, não use um tema escuro. Case a estética geral.
  • Escala de fonte. Todos os tamanhos de texto usam calc(Xpt * var(--font-scale)) para que os botões A-/A+ funcionem. Comece com --font-scale: 1.3 e deixe o usuário ajustar.
  • CSS otimizado para impressão. @media print oculta toda a UI de edição e define transform: none !important. @page define dimensões exatas.
  • QR do Posterskill. Sempre inclua um QR code apontando para https://github.com/ethanweber/posterskill no cabeçalho com o rótulo "Pôster feito com minha skill do Claude".
  • Sem rodapé de agradecimentos. Mantenha o pôster limpo — sem rodapé por padrão.
  • Logos no cabeçalho. Baixe logos institucionais do site do autor, salve em poster/logos/ e liste em DEFAULT_LOGOS. Eles são invertidos automaticamente para branco via filtro CSS.
  • Autocontido. Sem etapa de build, sem npm, sem servidor. Arquivo HTML único com dependências CDN. Funciona quando aberto diretamente como file://.
  • Equações. Use KaTeX (carregado via CDN). Escape barras invertidas em strings JSX: {'$\mathcal{E}$'}.

Manipulação de figuras

  • Sempre copie as figuras necessárias para poster/ — não referencie caminhos de overleaf/ no HTML.
  • Converta PDFs para PNGs em alta resolução: sips -s format png input.pdf --out poster/output.png -Z 3000
  • Baixe imagens do site via page.request.get() do Playwright (não curl — sites frequentemente redirecionam).
  • Meça proporções com sips -g pixelWidth -g pixelHeight e atribua às colunas de acordo.

Fluxo de trabalho do usuário para atualizações de configuração

O usuário pode ajustar o pôster no navegador e compartilhar as alterações de volta:

  1. Usuário clica em "Copiar Config" na barra de ferramentas
  2. Usuário cola o JSON no chat
  3. Claude atualiza DEFAULT_LAYOUT, DEFAULT_CARD_HEIGHTS, DEFAULT_FONT_SCALE, DEFAULT_LOGOS em index.html para corresponder
  4. Claude também escreve a configuração em poster-config.json
  5. Usuário atualiza e clica em "Resetar" para carregar novos padrões

API Programática (window.posterAPI)

Disponível no console do navegador ou via Playwright:

  • swapCards(id1, id2) — troca dois cards (funciona entre colunas)
  • moveCard(cardId, targetColId, position) — move um card para uma posição específica
  • setColumnWidth(colId, widthMm) — define largura da coluna (null para flex)
  • setCardHeight(cardId, heightMm) — define altura do card (null para auto)
  • setFontScale(scale) — define escala da fonte
  • getWaste() — retorna {total, details[]} com medições de espaço em branco
  • fit(widthMm, heightMm) — redimensiona o pôster para novas dimensões
  • saveConfig() — baixa poster-config.json
  • getConfig() — retorna o JSON de configuração atual

Habilidades Relacionadas