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 nebenindex.html, sodass relative Pfade einfach funktionieren.
Eingaben
- 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. - Projekt-Website – Frage den Benutzer nach der URL, falls nicht bekannt. Rufe sie mit WebFetch ab, um Autoreninformationen, gehostete Bilder und Links zu extrahieren.
- Referenzposter – Werden automatisch in
references/erkannt. Sieh dir alle dortigen Dateien an und übernimm ihren Stil. - 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. - Formatierungsanforderungen – Frage nach Postermaßen, Ausrichtung und Spaltenanzahl.
- 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 KartenreihenfolgeDEFAULT_LOGOS– institutionslogos für die Kopfzeile- React-Status verwaltet das Layout mit localStorage-Persistenz
window.posterAPIstellt Funktionen für programmgesteuerte Steuerung bereit
Wichtige Anpassungen:
- Aktualisiere
CARD_REGISTRYmit dem Inhalt des Papers (jeder Abschnitt ist eine Karte) - Aktualisiere
DEFAULT_LAYOUTmit den seitenverhältnisoptimierten Spaltenzuweisungen - Aktualisiere
DEFAULT_LOGOSmit den institutionslogos des Benutzers - Aktualisiere
DEFAULT_FONT_SCALE(Start bei 1.3, Benutzer kann mit A-/A+-Buttons anpassen) - Aktualisiere die Kopfzeile (Titel, Autoren, Zugehörigkeiten, Konferenzbadge, QR-Codes)
- Aktualisiere
@page { size: BREITEmm HOEHEmm; }undbody { width: BREITEmm; height: HOEHEmm; }für die Postermaße - Aktualisiere
posterAPIfit()-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>mitclassName="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:containverwenden (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 leftist entscheidend@media printmusstransform: none !importantsetzen, um die korrekte Druckauflösung zu gewährleistengetBoundingClientRect()gibt SKALIERTE Werte zurück – teile in Resize-Handlern immer durchcurrentScaleRef.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.jsonherunter - 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.jsonherunter - 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:containbei 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.3und lass den Benutzer anpassen. - Druckoptimiertes CSS.
@media printblendet die gesamte Bearbeitungsoberfläche aus und setzttransform: none !important.@pagesetzt genaue Maße. - Posterskill-QR-Code. Füge immer einen QR-Code ein, der auf
https://github.com/ethanweber/posterskillverweist, 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 inDEFAULT_LOGOSauf. 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 aufoverleaf/-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 pixelHeightund weise sie entsprechend den Spalten zu.
Benutzer-Workflow für Konfigurationsaktualisierungen
Der Benutzer kann das Poster im Browser anpassen und Änderungen zurückmelden:
- Benutzer klickt auf "Config kopieren" in der Symbolleiste
- Benutzer fügt das JSON im Chat ein
- Claude aktualisiert
DEFAULT_LAYOUT,DEFAULT_CARD_HEIGHTS,DEFAULT_FONT_SCALEundDEFAULT_LOGOSinindex.htmlentsprechend - Claude schreibt die Konfiguration auch in
poster-config.json - 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 verschiebensetColumnWidth(colId, widthMm)– Spaltenbreite setzen (null für flexibel)setCardHeight(cardId, heightMm)– Kartenhöhe setzen (null für auto)getWaste()– Leerraumdetails abrufenfit()– Poster in Viewport einpassen
