Communitygithub.com

ethanweber/posterskill

Generiere ein HTML-Konferenzposter aus einem Paper und einer Projekt-Website, druckbar als PDF.

Was ist posterskill?

Diese Fähigkeit (Skill) generiert ein professionelles, eigenständiges HTML-Konferenzposter, das auf einem wissenschaftlichen Paper und einer zugehörigen Projekt-Website basiert. Der gesamte Workflow ist darauf ausgelegt, eine nahtlose Iteration zu ermöglichen: Der Skill extrahiert Inhalte aus LaTeX-Quellen, Bildern und Referenzpostern, erstellt eine interaktive React-App im `poster/`-Verzeichnis, optimiert das Layout automatisch mittels Playwright und bietet dem Benutzer schließlich eine visuelle Bearbeitungsoberfläche im Browser an. Das Hauptziel ist die Minimierung von Leerraum durch intelligente Spalten- und Bildplatzierung. Der Skill unterstützt die Anpassung von Schriftgröße, Spaltenbreiten, Kartenanordnung und Logos und speichert die finale Konfiguration, sodass Änderungen übernommen werden können. Er ist für Claude Code unter macOS optimiert und verwendet ausschließlich eigenständige HTML-Dateien, die ohne Build-Schritt oder Server sofort funktionieren.

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

In Ihrer bevorzugten KI fragen

Öffnet einen neuen Chat, in dem dieser Agent-Skill bereits geladen ist.

Vorschau

Aus dem Skill-README

posterskill preview

Dokumentation

Conference Poster Generator (HTML)

Generiere ein professionelles HTML-Poster. Benutzer-Notizen: $ARGUMENTS

Das Poster ist ein React-basierter interaktiver Editor – eine einzige eigenständige HTML-Datei im Verzeichnis poster/. Kein Build-Schritt erforderlich (React/Babel werden via CDN geladen). Der Benutzer kann das Layout visuell im Browser anpassen und dann die Konfiguration an Claude zurückgeben, um weitere Änderungen vorzunehmen.

Projektordnerstruktur

<projekt>/
├── overleaf/              # Paper-Quelle von Overleaf
│   ├── paper.tex
│   ├── figures/
│   └── ...
├── references/            # Referenzposter für Stilangleichung
│   └── (beliebiges Format: pdf, png, jpg, html, pptx, ...)
├── poster/                # GENERIERT: Eigenständige Poster-Website
│   ├── index.html         # Das Poster (React-App)
│   ├── poster-config.json # Layout-Konfiguration (Spalten, Kartenreihenfolge, Höhen, Schrift-Skalierung)
│   ├── logos/             # Institutionslogos
│   ├── teaser.png         # Kopierte/konvertierte Abbildungen
│   ├── qr.png             # QR-Code der Projektsseite
│   ├── qr-posterskill.png # QR-Code des Posterskills
│   └── ...
└── .claude/skills/make-poster/
  • overleaf/ enthält die Paper-Quelle. Lies die Haupt-.tex-Datei und alle Dateien, die sie mit \input{} einbindet.
  • references/ enthält Beispiel-Poster, die den bevorzugten visuellen Stil des Benutzers zeigen. Lies/sieh dir ALLE Dateien in diesem Ordner an, um die Designsprache zu übernehmen.
  • poster/ ist die generierte Ausgabe – eine eigenständige Website. Alle Abbildungen und Assets befinden sich neben index.html, sodass relative Pfade einfach funktionieren.

Eingaben

  1. Paper-Quelle – Befindet sich in overleaf/. Frage den Benutzer, welche .tex-Datei die Hauptdatei ist (z. B. paper.tex, main.tex). Lese sie und alle Dateien, die sie mit \input{} einbindet.
  2. Projekt-Website – Frage den Benutzer nach der URL, falls nicht bekannt. Rufe sie mit WebFetch ab, um Autoreninformationen, gehostete Bilder und Links zu extrahieren.
  3. Referenzposter – Werden automatisch in references/ erkannt. Sieh dir alle dortigen Dateien an und übernimm ihren Stil.
  4. Autoren-Website (optional) – Rufe sie mit WebFetch ab und extrahiere Designsignale (Farbpalette, Typografie, Logos). Lade institutionslogos von der Autorenseite mit Playwright herunter (curl könnte aufgrund von Weiterleitungen fehlschlagen): page.request.get(url) um die Rohdaten zu laden.
  5. Formatierungsanforderungen – Frage nach Postermaßen, Ausrichtung und Spaltenanzahl.
  6. Git-Repo (optional) – Frage den Benutzer, ob er ein GitHub-Repo hat, um das Poster hochzuladen.

Wenn der Benutzer keine Formatierung angibt, frage vor dem Fortfahren nach. Gehe nicht von Standardwerten für Maße, Ausrichtung oder Spaltenanzahl aus.

Ablauf

Schritt 0: Stilreferenzen analysieren

Suche in references/ nach PDF-, PNG- oder Bilddateien. Konvertiere PDFs in PNGs (sips -s format png auf macOS). Betrachte jede Datei und notiere den visuellen Stil – Layout, Farben, Typografie, Kartenstile, Bildplatzierung. Passe den Referenzstil an – verwende kein dunkles Theme, wenn die Referenz hell ist usw.

Schritt 1: Inhalt aus Paper-Quelle extrahieren

Frage den Benutzer, welche .tex-Datei gelesen werden soll. Extrahiere: Titel, Autoren, Zugehörigkeiten, Zusammenfassung (2-3 Sätze), Kernmethode, Ergebnisse (Tabellen + Abbildungen), Kernformeln (max. 1-2), Fazit.

Schritt 2: Projekt-Website abrufen

Verwende WebFetch, um Autorennamen, Zugehörigkeiten, Bild-URLs, Projekt-URL für QR-Code, Links zu Code/ArXiv/Video zu erhalten.

Schritt 3: Assets in poster/ sammeln

Abbildungen: Kopiere aus overleaf/figures/ und konvertiere PDFs in PNGs mit hoher Auflösung:

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

Website-Bilder: Lade qualitativ hochwertigere Bilder von der Projekt-Website mit Playwright herunter (nicht curl – viele Seiten leiten weiter):

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

Logos: Lade institutionslogos von der persönlichen Website des Autors mit Playwright herunter. Speichere sie in poster/logos/. Die Vorlage invertiert sie automatisch in Weiß für die Kopfzeile.

QR-Codes: Generiere und speichere:

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

Schritt 4: Bild-Seitenverhältnisse messen

Dies ist entscheidend, um Leerraum zu vermeiden. Miss jedes Bild:

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

Ordne Bilder dann basierend auf dem Seitenverhältnis den Spalten zu:

  • Breite Bilder (>2:1 Verhältnis, z. B. Teaser, Architektur): in die breiteste Spalte
  • Quadratische Bilder (~1:1 Verhältnis): in schmale Spalten
  • Hochformat-Bilder (<1:1 Verhältnis): in die schmalste Spalte

Dies verhindert das häufigste Leerraumproblem: breite Bilder in schmalen Zellen (oder umgekehrt) hinterlassen große Lücken.

Schritt 5: Poster-HTML generieren

Verwende die Vorlage unter ${{CLAUDE_SKILL_DIR}}/template.html als Ausgangspunkt. Die Vorlage ist eine React-App mit:

Architektur:

  • CARD_REGISTRY – definiert den Inhalt jeder Karte (Titel, Farbe, JSX-Body)
  • DEFAULT_LAYOUT – definiert die Spaltenstruktur und Kartenreihenfolge
  • DEFAULT_LOGOS – institutionslogos für die Kopfzeile
  • React-Status verwaltet das Layout mit localStorage-Persistenz
  • window.posterAPI stellt Funktionen für programmgesteuerte Steuerung bereit

Wichtige Anpassungen:

  1. Aktualisiere CARD_REGISTRY mit dem Inhalt des Papers (jeder Abschnitt ist eine Karte)
  2. Aktualisiere DEFAULT_LAYOUT mit den seitenverhältnisoptimierten Spaltenzuweisungen
  3. Aktualisiere DEFAULT_LOGOS mit den institutionslogos des Benutzers
  4. Aktualisiere DEFAULT_FONT_SCALE (Start bei 1.3, Benutzer kann mit A-/A+-Buttons anpassen)
  5. Aktualisiere die Kopfzeile (Titel, Autoren, Zugehörigkeiten, Konferenzbadge, QR-Codes)
  6. Aktualisiere @page { size: BREITEmm HOEHEmm; } und body { width: BREITEmm; height: HOEHEmm; } für die Postermaße
  7. Aktualisiere posterAPI fit()-Funktion mit denselben Maßen

Karteninhalts-Muster:

  • Abbildungskarte: <div className="fig"><div className="fig-wrap"><img src="datei.png" alt="..." /></div><div className="cap"><b>Bildunterschrift.</b> Beschreibung.</div></div>
  • Textkarte: <div className="hl"><p>Hervorzuhebender Text</p></div><ul><li>Punkt 1</li></ul>
  • Tabellenkarte: <table><thead>...</thead><tbody>...</tbody></table> mit className="best" auf gewinnenden Zellen
  • Formelkarte: <div className="eq">{'$LaTeX-Formel$'}</div> (Escape von Backslashes in JSX)

Kritische CSS-Regeln für null Leerraum:

  • Bilder MÜSSEN width:100%; height:100%; object-fit:contain verwenden (NICHT max-width/max-height – diese verhindern Hochskalieren)
  • Jede Spalte muss immer eine Karte mit grow: true (flex:1) haben, die den verbleibenden Platz füllt
  • Die Funktion isCardGrow() stellt dies automatisch sicher – wenn keine Karte grow hat, erhält die letzte Karte sie

Viewport-Skalierung:

  • Verwende translate() + scale() auf dem Body, um das Poster in jedem Browser-Viewport zu zentrieren und einzupassen
  • transform-origin: top left ist entscheidend
  • @media print muss transform: none !important setzen, um die korrekte Druckauflösung zu gewährleisten
  • getBoundingClientRect() gibt SKALIERTE Werte zurück – teile in Resize-Handlern immer durch currentScaleRef.current

Schritt 6: Layout mit Playwright automatisch optimieren

Nach der Generierung verwende Playwright, um Leerraum zu messen und optimale Spaltenbreiten zu finden:

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)

    # Leerraum messen
    waste = page.evaluate('window.posterAPI.getWaste()')
    print(f"Gesamter Leerraum: {waste['total']}px")
    for d in waste['details']:
        print(f"  {d['card']}: H={d['wasteH']} B={d['wasteW']} ({d['pct']}%)")

    # Verschiedene Spaltenbreiten ausprobieren
    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

    # Beste Einstellung anwenden und Screenshot machen
    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')

    # Auch Karten zwischen Spalten tauschen
    page.evaluate('window.posterAPI.swapCards("karteA", "karteB")')
    # ... Leerraum erneut messen ...

    browser.close()

Lies dann /tmp/poster_screenshot.png, um visuell zu überprüfen. Wiederhole mehrere Durchläufe – mache Screenshots, behebe Probleme, erstelle erneut Screenshots, bis das Poster minimalen Leerraum aufweist.

Nachdem optimale Werte gefunden wurden, setze sie in DEFAULT_LAYOUT, DEFAULT_CARD_HEIGHTS usw. im HTML fest.

Schritt 7: PDF generieren und überprüfen

page.pdf(
    path='poster/poster.pdf',
    width='841mm', height='594mm',  # Postermaße anpassen
    margin={'top':'0','right':'0','bottom':'0','left':'0'},
    print_background=True
)

Konvertiere das PDF in PNG und lies es, um zu überprüfen, ob es in voller Auflösung rendert:

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

Schritt 8: Öffnen und mit Benutzer iterieren

Öffne das Poster im Browser:

open poster/index.html

Erkläre dem Benutzer die Bearbeitungssteuerung:

  • Vorschau – Blende die Bearbeitungsoberfläche aus, um zu sehen, wie es gedruckt wird
  • A-/A+ – Passe die Schriftgröße global an
  • Spaltenteiler ziehen (vertikale blaue Balken) – Spalten links/rechts in der Breite anpassen
  • Zeilenteiler ziehen (horizontale blaue Balken) – Kartenhöhen innerhalb von Spalten anpassen
  • Klicken zum Tauschen – Klicke den Rautengriff einer Karte (wird orange), dann klicke eine andere Karte, um sie zu tauschen
  • Verschieben/Einfügen – Klicke den Griff einer Karte, dann klicke eine gestrichelte orangefarbene Ablagezone, um sie dorthin zu verschieben
  • Speichern – Lade poster-config.json herunter
  • Config kopieren – Kopiere das Layout-JSON in die Zwischenablage
  • Zurücksetzen – Setze auf Standardwerte zurück

Schlage proaktiv Verbesserungen vor: Nach der ersten Vorschau schlage konkrete Änderungen vor:

  • "Die Karte zur Modellarchitektur hat Leerraum – versuche, den Zeilenteiler darüber nach unten zu ziehen, um ihr weniger Platz zu geben"
  • "Die Abschlussabbildung könnte in Spalte 2 besser aussehen, da sie breiter ist – versuche, auf ihren Diamanten zu klicken, dann auf eine Ablagezone in Spalte 2"
  • "Du möchtest vielleicht die Schriftgröße ein paar Mal mit A+ erhöhen"

Fördere die Rückkopplungsschleife: Sage dem Benutzer:

Versuche, das Poster in deinem Browser neu anzuordnen! Wenn dir das Layout gefällt, klicke in der Symbolleiste oben rechts auf Config kopieren und füge es hier ein – ich werde diese Änderungen in die Standardwerte übernehmen, damit sie dauerhaft bleiben.

  • Speichern – Lade poster-config.json herunter
  • Config kopieren – Kopiere das Layout-JSON in die Zwischenablage zum Einfügen in Claude
  • Zurücksetzen – Setze auf Standardwerte zurück

Wenn der Benutzer ein Konfig-JSON einfügt, aktualisiere DEFAULT_LAYOUT, DEFAULT_CARD_HEIGHTS, DEFAULT_FONT_SCALE und DEFAULT_LOGOS im HTML entsprechend. Schreibe es auch in poster-config.json.

Schritt 9: In GitHub pushen (optional)

Wenn der Benutzer eine GitHub-Repo-URL angibt:

cd poster
git init
git remote add origin <REPO_URL>
git add .
git commit -m "Poster: <papertitel>"
git push -u origin main

Wichtige Richtlinien

  • Kein Leerraum. Dies ist die oberste Priorität. Verwende seitenverhältnisbewusste Spaltenzuweisung, width:100%; height:100%; object-fit:contain bei Bildern, automatisch wachsende Karten und den Playwright-Optimierer. Iteriere, bis der Leerraum minimal ist.
  • Text minimal halten. Poster sind visuell – Aufzählungspunkte, keine Absätze. Verständnis in 2 Minuten.
  • Referenzstil anpassen. Wenn das Referenzposter hell/sauber ist, verwende kein dunkles Theme. Passe die Gesamtästhetik an.
  • Schriftskalierung. Alle Textgrößen verwenden calc(Xpt * var(--font-scale)), damit die A-/A+-Buttons funktionieren. Starte mit --font-scale: 1.3 und lass den Benutzer anpassen.
  • Druckoptimiertes CSS. @media print blendet die gesamte Bearbeitungsoberfläche aus und setzt transform: none !important. @page setzt genaue Maße.
  • Posterskill-QR-Code. Füge immer einen QR-Code ein, der auf https://github.com/ethanweber/posterskill verweist, mit dem Label "Poster made with my Claude skill".
  • Keine Danksagungs-Fußzeile. Halte das Poster sauber – standardmäßig keine Fußzeile.
  • Logos in der Kopfzeile. Lade institutionslogos von der Autorenwebsite herunter, speichere sie in poster/logos/ und liste sie in DEFAULT_LOGOS auf. Sie werden automatisch über CSS-Filter in Weiß invertiert.
  • Eigenständig. Kein Build-Schritt, kein npm, kein Server. Einzelne HTML-Datei mit CDN-Abhängigkeiten. Funktioniert, wenn sie direkt als file:// geöffnet wird.
  • Formeln. Verwende KaTeX (via CDN geladen). Escape Backslashes in JSX-Strings: {'$\mathcal{E}$'}.

Abbildungsbehandlung

  • Kopiere immer benötigte Abbildungen in poster/ – verweise nicht im HTML auf overleaf/-Pfade.
  • Konvertiere PDFs in PNGs mit hoher Auflösung: sips -s format png input.pdf --out poster/output.png -Z 3000
  • Lade Website-Bilder mit Playwrights page.request.get() herunter (nicht curl – Websites leiten oft weiter).
  • Miss Seitenverhältnisse mit sips -g pixelWidth -g pixelHeight und weise sie entsprechend den Spalten zu.

Benutzer-Workflow für Konfigurationsaktualisierungen

Der Benutzer kann das Poster im Browser anpassen und Änderungen zurückmelden:

  1. Benutzer klickt auf "Config kopieren" in der Symbolleiste
  2. Benutzer fügt das JSON im Chat ein
  3. Claude aktualisiert DEFAULT_LAYOUT, DEFAULT_CARD_HEIGHTS, DEFAULT_FONT_SCALE und DEFAULT_LOGOS in index.html entsprechend
  4. Claude schreibt die Konfiguration auch in poster-config.json
  5. Benutzer aktualisiert die Seite und klickt auf "Zurücksetzen", um die neuen Standardwerte zu laden

Programmatische API (window.posterAPI)

Verfügbar in der Browser-Konsole oder über Playwright:

  • swapCards(id1, id2) – zwei Karten tauschen (funktioniert spaltenübergreifend)
  • moveCard(cardId, targetColId, position) – eine Karte an eine bestimmte Position verschieben
  • setColumnWidth(colId, widthMm) – Spaltenbreite setzen (null für flexibel)
  • setCardHeight(cardId, heightMm) – Kartenhöhe setzen (null für auto)
  • getWaste() – Leerraumdetails abrufen
  • fit() – Poster in Viewport einpassen

Verwandte Skills