会议海报生成器 (HTML)
生成专业的 HTML 海报。用户备注:$ARGUMENTS
此海报是一个基于 React 的交互式编辑器——一个位于 poster/ 目录下的单一自包含 HTML 文件。无需构建步骤(React/Babel 通过 CDN 加载)。用户可以在浏览器中直观地调整布局,然后将配置导出回 Claude 以进行进一步修改。
项目文件夹结构
<project>/
├── overleaf/ # Overleaf 的论文源文件
│ ├── paper.tex
│ ├── figures/
│ └── ...
├── references/ # 用于风格匹配的参考海报
│ └── (任何格式:pdf, png, jpg, html, pptx, ...)
├── poster/ # 生成的自包含海报网站
│ ├── index.html # 海报 (React 应用)
│ ├── poster-config.json # 布局配置 (列数、卡片顺序、高度、字体缩放)
│ ├── logos/ # 机构徽标
│ ├── teaser.png # 复制/转换的图表
│ ├── qr.png # 项目页面二维码
│ ├── qr-posterskill.png # Posterskill 二维码
│ └── ...
└── .claude/skills/make-poster/
overleaf/包含论文源文件。读取主.tex文件及其通过\input{}引入的任何文件。references/包含展示用户偏好的视觉风格的示例海报。读取/查看此文件夹中的所有文件以匹配其设计语言。poster/是生成的输出——一个自包含的网站。所有图表和资产都与index.html放置在一起,相对路径即可正常工作。
输入
- 论文源文件 - 位于
overleaf/。询问用户哪个.tex文件是主文件(例如paper.tex、main.tex)。然后读取它及其通过\input{}引入的任何文件。 - 项目网站 - 如果尚未知晓,询问用户 URL。使用 WebFetch 提取作者信息、托管图像和链接。
- 参考海报 - 从
references/自动发现。查看其中的所有文件并匹配其风格。 - 作者网站 (可选) - 使用 WebFetch 提取设计信号(调色板、字体、徽标)。使用 Playwright 从作者网站下载机构徽标(curl 可能因重定向失败):
page.request.get(url)以下载原始字节。 - 格式要求 - 询问海报尺寸、方向和列数。
- Git 仓库 (可选) - 询问用户是否有 GitHub 仓库来推送海报。
如果用户未指定格式,请在继续前询问。不要假设尺寸、方向或列数的默认值。
流程
第 0 步:分析风格参考
在 references/ 中查找任何 PDF、PNG 或图像文件。将 PDF 转换为 PNG(在 macOS 上使用 sips -s format png)。查看每个文件并记录视觉风格——布局、颜色、字体、卡片样式、图表放置。匹配参考风格——如果参考是浅色主题,则不要默认使用深色主题。
第 1 步:从论文源文件提取内容
询问用户要读取哪个 .tex 文件。提取:标题、作者、附属机构、摘要(2-3 句)、关键方法、结果(表格 + 图表)、关键公式(最多 1-2 个)、结论。
第 2 步:获取项目网站
使用 WebFetch 获取作者姓名、附属机构、图表 URL、用于 QR 的项目 URL、代码/arxiv/视频的链接。
第 3 步:收集资产到 poster/
图表: 从 overleaf/figures/ 复制,将 PDF 转换为高分辨率 PNG:
sips -s format png input.pdf --out poster/output.png -Z 3000
网站图像: 使用 Playwright 从项目网站下载更高质量的图像(不要用 curl——许多网站会重定向):
resp = page.request.get(url)
with open('poster/filename.png', 'wb') as f:
f.write(resp.body())
徽标: 使用 Playwright 从作者个人网站下载机构徽标。保存到 poster/logos/。模板会自动将其反色为白色以用于标题。
二维码: 生成并保存:
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"
第 4 步:测量图像宽高比
这对于消除空白至关重要。 测量每张图像:
sips -g pixelWidth -g pixelHeight poster/*.png poster/*.jpg
然后根据宽高比将图像分配到列:
- 宽图像 (比例 > 2:1,例如展示图、架构图):放在最宽的列中
- 方形图像 (比例 ~ 1:1):放在较窄的列中
- 竖版图像 (比例 < 1:1):放在最窄的列中
这可以防止头号空白问题:宽图像放在窄单元格中(反之亦然)会留下巨大空隙。
第 5 步:生成海报 HTML
使用 ${{CLAUDE_SKILL_DIR}}/template.html 作为起点。模板是一个 React 应用,包含:
架构:
CARD_REGISTRY— 定义每个卡片的内容(标题、颜色、JSX 主体)DEFAULT_LAYOUT— 定义列结构和卡片顺序DEFAULT_LOGOS— 用于标题的机构徽标- React 状态管理布局,具有 localStorage 持久化
window.posterAPI公开用于程序控制的函数
关键的自定义项:
- 使用论文内容更新
CARD_REGISTRY(每个部分是一张卡片) - 使用经过宽高比优化的列分配更新
DEFAULT_LAYOUT - 使用用户的机构徽标更新
DEFAULT_LOGOS - 更新
DEFAULT_FONT_SCALE(从 1.3 开始,用户可以通过 A-/A+ 按钮调整) - 更新标题(标题、作者、附属机构、会议徽章、二维码)
- 更新
@page { size: WIDTHmm HEIGHTmm; }和body { width: WIDTHmm; height: HEIGHTmm; }以适应海报尺寸 - 使用相同尺寸更新
posterAPI的 fit() 函数
卡片内容模式:
- 图表卡片:
<div className="fig"><div className="fig-wrap"><img src="file.png" alt="..." /></div><div className="cap"><b>标题。</b>描述。</div></div> - 文本卡片:
<div className="hl"><p>高亮文本</p></div><ul><li>要点 1</li></ul> - 表格卡片:
<table><thead>...</thead><tbody>...</tbody></table>并在获胜单元格上使用className="best" - 公式卡片:
<div className="eq">{'$LaTeX equation$'}</div>(在 JSX 中转义反斜杠)
关键 CSS 规则(零空白):
- 图像必须使用
width:100%; height:100%; object-fit:contain(不能使用 max-width/max-height——它们会阻止放大) - 每列必须始终有一张
grow: true(flex:1)的卡片来填充剩余空间 isCardGrow()函数会自动确保这一点——如果没有卡片增长,最后一张卡片将获得增长属性
视口缩放:
- 使用 body 上的
translate() + scale()将海报居中对齐并适配任何浏览器视口 transform-origin: top left至关重要@media print必须设置transform: none !important以获得正确的打印分辨率getBoundingClientRect()返回缩放后的值——始终在调整大小处理程序中除以currentScaleRef.current
第 6 步:使用 Playwright 自动优化布局
生成后,使用 Playwright 测量空白并找到最佳列宽:
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)
# 测量空白
waste = page.evaluate('window.posterAPI.getWaste()')
print(f"总空白: {waste['total']}px")
for d in waste['details']:
print(f" {d['card']}: H={d['wasteH']} W={d['wasteW']} ({d['pct']}%)")
# 尝试不同的列宽以最小化空白
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
# 应用最佳值并截屏
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')
# 也尝试在列之间交换卡片
page.evaluate('window.posterAPI.swapCards("cardA", "cardB")')
# ... 再次测量空白 ...
browser.close()
然后读取 /tmp/poster_screenshot.png 进行视觉检查。迭代多次——截屏、修复问题、重新截屏,直到海报的空白区域最小。
找到最佳值后,将它们烘焙到 HTML 的 DEFAULT_LAYOUT、DEFAULT_CARD_HEIGHTS 等中。
第 7 步:生成 PDF 并验证
page.pdf(
path='poster/poster.pdf',
width='841mm', height='594mm', # 匹配海报尺寸
margin={'top':'0','right':'0','bottom':'0','left':'0'},
print_background=True
)
将 PDF 转换为 PNG 并读取它以验证它能以全分辨率渲染:
sips -s format png poster/poster.pdf --out /tmp/poster_pdf_check.png -Z 3000
第 8 步:打开并与用户迭代
在浏览器中打开海报:
open poster/index.html
向用户解释编辑控件:
- 预览 — 关闭编辑 UI 以查看打印时的确切效果
- A-/A+ — 全局调整字体大小
- 拖动列分隔线 (垂直蓝色条) — 左右调整列宽
- 拖动行分隔线 (水平蓝色条) — 在列内上下调整卡片高度
- 单击交换 — 单击一张卡片的菱形手柄(变为橙色),然后单击另一张卡片进行交换
- 移动/插入 — 单击卡片手柄,然后单击虚线橙色放置区将其移动到那里
- 保存 — 下载
poster-config.json - 复制配置 — 将布局 JSON 复制到剪贴板
- 重置 — 恢复默认值
主动建议改进: 展示初稿后,建议具体更改:
- "模型架构卡片有一些空白——尝试将其上方的行分隔线向下拖动,给它更少的空间"
- "完成图可能放在第 2 列更好看,因为它更宽——尝试单击其菱形,然后单击第 2 列中的一个放置区"
- "您可能想使用 A+ 按钮增加几次字体大小"
鼓励反馈循环: 告诉用户:
尝试在浏览器中重新排列海报!当您对布局满意时,单击右上角工具栏中的复制配置并粘贴到这里——我会将这些更改烘焙到默认值中,以便它们持久化。
- 保存 — 下载
poster-config.json - 复制配置 — 将布局 JSON 复制到剪贴板以粘贴到 Claude
- 重置 — 恢复默认值
当用户粘贴配置 JSON 时,更新 HTML 中的 DEFAULT_LAYOUT、DEFAULT_CARD_HEIGHTS、DEFAULT_FONT_SCALE 和 DEFAULT_LOGOS 以匹配。同时将其写入 poster-config.json。
第 9 步:推送到 GitHub(可选)
如果用户提供 GitHub 仓库 URL:
cd poster
git init
git remote add origin <REPO_URL>
git add .
git commit -m "Poster: <paper title>"
git push -u origin main
重要指南
- 无空白区域。 这是第一优先级。使用宽高比感知的列分配、
width:100%; height:100%; object-fit:contain的图像、自动增长卡片和 Playwright 优化器。迭代直到空白最小化。 - 文本尽量精简。 海报是视觉化的——使用要点,不要用段落。目标是在 2 分钟内理解。
- 匹配参考风格。 如果参考海报是浅色/干净的,不要使用深色主题。匹配整体美学。
- 字体缩放。 所有文本大小使用
calc(Xpt * var(--font-scale)),以便 A-/A+ 按钮工作。从--font-scale: 1.3开始,让用户调整。 - 打印优化的 CSS。
@media print隐藏所有编辑 UI 并设置transform: none !important。@page设置精确尺寸。 - Posterskill 二维码。 始终在标题中包含一个指向
https://github.com/ethanweber/posterskill的二维码,标签为"使用我的 Claude 技能制作的海报"。 - 无致谢页脚。 保持海报干净——默认没有页脚。
- 标题中的徽标。 从作者网站下载机构徽标,保存到
poster/logos/,并在DEFAULT_LOGOS中列出。它们通过 CSS 滤镜自动反色为白色。 - 自包含。 无需构建步骤、npm 或服务器。单个 HTML 文件,依赖 CDN。直接作为
file://打开即可工作。 - 公式。 使用 KaTeX(通过 CDN 加载)。在 JSX 字符串中转义反斜杠:
{'$\\mathcal{E}$'}。
图表处理
- 始终将所需的图表复制到
poster/中——不要在 HTML 中引用overleaf/的路径。 - 将 PDF 转换为高分辨率 PNG:
sips -s format png input.pdf --out poster/output.png -Z 3000 - 通过 Playwright 的
page.request.get()下载网站图像(不要用 curl——网站通常重定向)。 - 使用
sips -g pixelWidth -g pixelHeight测量宽高比,并相应地分配到列。
用户配置更新工作流
用户可以在浏览器中调整海报并将更改分享回来:
- 用户单击工具栏中的"复制配置"
- 用户将 JSON 粘贴到聊天中
- Claude 更新
index.html中的DEFAULT_LAYOUT、DEFAULT_CARD_HEIGHTS、DEFAULT_FONT_SCALE、DEFAULT_LOGOS以匹配 - Claude 还将配置写入
poster-config.json - 用户刷新页面并单击"重置"以加载新的默认值
程序化 API (window.posterAPI)
可通过浏览器控制台或 Playwright 使用:
swapCards(id1, id2)— 交换两张卡片(跨列工作)moveCard(cardId, targetColId, position)— 将卡片移动到指定位置setColumnWidth(colId, widthMm)— 设置列宽(null 表示 flex)
…
[已截断]
