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.texprincipal 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 comindex.html, então caminhos relativos funcionam.
Entradas
- 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{}. - 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.
- Pôsteres de referência - Descobertos automaticamente em
references/. Visualize todos os arquivos lá e case o estilo. - 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. - Requisitos de formatação - Pergunte pelas dimensões do pôster, orientação, número de colunas.
- 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 cardsDEFAULT_LOGOS— logos institucionais para o cabeçalho- Estado React gerencia o layout, com persistência em localStorage
window.posterAPIexpõe funções para controle programático
Coisas principais para personalizar:
- Atualize
CARD_REGISTRYcom o conteúdo do artigo (cada seção é um card) - Atualize
DEFAULT_LAYOUTcom as atribuições de colunas otimizadas por proporção - Atualize
DEFAULT_LOGOScom os logos institucionais do usuário - Atualize
DEFAULT_FONT_SCALE(comece em 1.3, o usuário pode ajustar com botões A-/A+) - Atualize o cabeçalho (título, autores, afiliações, selo da conferência, QR codes)
- Atualize
@page { size: LARGURAMm ALTURAMm; }ebody { width: LARGURAMm; height: ALTURAMm; }para as dimensões do pôster - Atualize a função fit() do
posterAPIcom 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>comclassName="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 printdeve definirtransform: none !importantpara resolução de impressão corretagetBoundingClientRect()retorna valores ESCALONADOS — sempre divida porcurrentScaleRef.currentnos 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:containnas 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.3e deixe o usuário ajustar. - CSS otimizado para impressão.
@media printoculta toda a UI de edição e definetransform: none !important.@pagedefine dimensões exatas. - QR do Posterskill. Sempre inclua um QR code apontando para
https://github.com/ethanweber/posterskillno 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 emDEFAULT_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 deoverleaf/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 pixelHeighte 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:
- Usuário clica em "Copiar Config" na barra de ferramentas
- Usuário cola o JSON no chat
- Claude atualiza
DEFAULT_LAYOUT,DEFAULT_CARD_HEIGHTS,DEFAULT_FONT_SCALE,DEFAULT_LOGOSemindex.htmlpara corresponder - Claude também escreve a configuração em
poster-config.json - 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íficasetColumnWidth(colId, widthMm)— define largura da coluna (null para flex)setCardHeight(cardId, heightMm)— define altura do card (null para auto)setFontScale(scale)— define escala da fontegetWaste()— retorna {total, details[]} com medições de espaço em brancofit(widthMm, heightMm)— redimensiona o pôster para novas dimensõessaveConfig()— baixaposter-config.jsongetConfig()— retorna o JSON de configuração atual
