Communitygithub.com

ethanweber/posterskill

從論文和專案網站生成可列印為 PDF 的 HTML 會議海報。

posterskill 是什麼?

這是一個強大的 AI 技能,能夠自動從學術論文和專案網站生成專業的 HTML 會議海報。它主要支援 Claude Code,但也能與其他 AI 代理工具一起使用。此技能的核心優勢在於其視覺化編輯器和迭代最佳化流程:它會先分析參考海報的風格,從 Overleaf 原始碼和專案網站提取內容,然後生成一個基於 React 的自動調整版面的 HTML 檔案。使用者可以直接在瀏覽器中拖曳欄位和卡片、調整字體大小、交換內容,並將最終版面匯出回 Claude 進行永久儲存。此外,它還整合了 Playwright 自動化工具來測量和消除空白區域,確保版面緊湊美觀,並支援生成高解析度 PDF 與 GitHub 部署。

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

在你喜歡的 AI 中提問

開啟一個已預先載入此 Agent Skill 的新對話。

預覽

來自技能 README

posterskill preview

說明文件

會議海報產生器 (HTML)

產生專業的 HTML 海報。使用者備註:$ARGUMENTS

此海報是一個 基於 React 的互動式編輯器 — 單一自包含的 HTML 檔案,位於 poster/ 目錄中。無需建置步驟(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 碼
│   ├── qr-posterskill.png # Posterskill QR 碼
│   └── ...
└── .claude/skills/make-poster/
  • overleaf/ 包含論文原始碼。請閱讀主要的 .tex 檔案以及任何它用 \input{} 引入的檔案。
  • references/ 包含展示使用者偏好的視覺風格的範例海報。請閱讀/檢視此資料夾中的所有檔案,以匹配其設計語言。
  • poster/ 是產生的輸出 — 一個自包含的網站。所有圖表和資產都與 index.html 放在同一層,因此相對路徑可以直接使用。

輸入

  1. 論文原始碼 - 位於 overleaf/。詢問使用者要讀取哪個 .tex 檔案(例如 paper.tex、main.tex)。然後讀取該檔案以及它用 \input{} 引入的所有檔案。
  2. 專案網站 - 如果尚未知道,請詢問使用者 URL。使用 WebFetch 取得作者資訊、託管圖片和連結。
  3. 參考海報 - 從 references/ 自動發現。檢視該處的所有檔案並匹配其風格。
  4. 作者網站(可選)- 使用 WebFetch 取得設計線索(調色盤、排版、標誌)。使用 Playwright 從作者網站下載機構標誌(curl 因重新導向可能失敗):page.request.get(url) 下載原始位元組。
  5. 格式化要求 - 詢問海報尺寸、方向、欄位數量。
  6. 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/。範本會自動將它們反轉為白色以用於頁首。

QR 碼: 產生並儲存:

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,例如 teaser、架構圖):放在最寬的欄位
  • 方形圖(比例 ~1:1):放在窄欄位
  • 直立圖(比例 <1:1):放在最窄的欄位

這可以防止第一大空白問題:寬圖放在窄儲存格中(或反之),留下巨大空隙。

步驟 5:產生海報 HTML

使用位於 ${{CLAUDE_SKILL_DIR}}/template.html 的範本作為起點。該範本是一個 React 應用程式,具有:

架構:

  • CARD_REGISTRY — 定義每張卡片的內容(標題、顏色、JSX 主體)
  • DEFAULT_LAYOUT — 定義欄位結構和卡片順序
  • DEFAULT_LOGOS — 用於頁首的機構標誌
  • React 狀態管理版面,並使用 localStorage 持久化
  • window.posterAPI 公開用於程式控制的函式

需要自訂的關鍵項目:

  1. 使用論文內容更新 CARD_REGISTRY(每個章節是一張卡片)
  2. 使用長寬比最佳化的欄位分配更新 DEFAULT_LAYOUT
  3. 使用使用者的機構標誌更新 DEFAULT_LOGOS
  4. 更新 DEFAULT_FONT_SCALE(從 1.3 開始,使用者可以透過 A-/A+ 按鈕調整)
  5. 更新頁首(標題、作者、所屬機構、會議徽章、QR 碼)
  6. 更新 @page { size: WIDTHmm HEIGHTmm; } 和 body { width: WIDTHmm; height: HEIGHTmm; } 以符合海報尺寸
  7. 使用相同的尺寸更新 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() 函式會自動確保這一點 — 如果沒有卡片設定 grow,則最後一張卡片會獲得 grow

檢視區縮放:

  • 在 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 "海報:<論文標題>"
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 QR 碼。 始終在頁首包含一個連結到 https://github.com/ethanweber/posterskill 的 QR 碼,並附帶標籤 "使用我的 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 測量長寬比,並相應地分配到欄位。

使用者設定更新工作流程

使用者可以在瀏覽器中調整海報並將更改分享回來:

  1. 使用者點擊工具列中的 "複製設定"
  2. 使用者在聊天中貼上 JSON
  3. Claude 更新 index.html 中的 DEFAULT_LAYOUT、DEFAULT_CARD_HEIGHTS、DEFAULT_FONT_SCALE、DEFAULT_LOGOS 以匹配
  4. Claude 也將設定寫入 poster-config.json
  5. 使用者重新整理並點擊 "重設" 以載入新的預設值

程式化 API (window.posterAPI)

可在瀏覽器控制台或透過 Playwright 使用:

  • swapCards(id1, id2) — 交換兩張卡片(可跨欄位)
  • moveCard(cardId, targetColId, position) — 將一張卡片移動到特定位置
  • setColumnWidth(colId, widthMm) — 設定欄位寬度(null 表示 flex)

…

[已截斷以供翻譯]

相關技能