Klarbild
Verwandelt Screenshots und Fundstücke in saubere, druckfertige Bilder — Rahmen und Shop-Oberflächen entfernen, exakte Fotoformate (30×40 cm etc.), The-Frame-Querformat, für den Cricut freistellen, und automatisch nach Picdrop ausliefern.
Claim: „Screenshot rein, sauberes Bild raus."
Stack
Astro 5 (SSR, Node standalone) + React-Inseln · Postgres · S3 (MinIO) · pg-boss · sharp · pdf-lib (Druckbogen) · grammY (Telegram) · OKLCH-Tokens, kein Tailwind. Deploy: Gitea → Coolify → Hetzner.
Entwicklung
npm install
cp .env.example .env # Werte eintragen
npm run dev
Migrationen und Seeds laufen beim Serverstart automatisch. Erststart-Passwörter über
SEED_TILL_PASSWORD / SEED_LEA_PASSWORD.
Funktionen (Kurzüberblick)
- Modi: Bereinigen (Screenshot säubern/freistellen/formatieren), Umwandeln (ein Bild + Text, z. B. Foto → Ölgemälde), Kombinieren (mehrere Bilder + Text), Neu erzeugen (nur Text).
- Formate: feste cm-/DIN-/Screen-Formate und frei definierbare — Schlüssel
WxH(z. B.25x35) odersticker<N>(sticker5= 5×5 cm). Parsing insrc/lib/format.ts, keine Migration nötig. - Dateiformat: PNG (verlustfrei, Transparenz) oder JPG (klein, Picdrop-freundlich). Global über
settings.output_ext, pro Rezept viarecipes.output_ext, pro Umwandlung im Studio. JPG nur ohne Alpha. - Auslieferung: Picdrop (SFTP/FTPS) + beliebige Zusatzziele; NAS/Backup-Ziele; optionaler
.md-Metadaten-Beileger pro Ziel schaltbar. Nicht-transparente Ergebnisse gehen als JPG an Picdrop. - Bibliothek: Ordner, Farbmarkierungen, Prompt-Anzeige, Vorher/Nachher, Vollbild + „In Fotos sichern".
- Rechte/Datenschutz: Sichtbarkeit (eigene/alle), anonyme Generierungen, private Sessions, NSFW-Gate.
- Telegram-Bot (grammY-Webhook) als vollwertiger Website-Ersatz. API-Token (
Bearer) für/api/*. - Druck (
/druck, ohne KI): Bilder exakt auf physische Maße bringen (interaktiver Zuschnitt mit Zoom/Verschieben) und mehrere davon in 100-%-Größe mit Schnittmarken auf einen Bogen setzen — Passbildsatz, Kita-Satz, Sticker. Papier DIN A6–A2 inkl. A3+, Fotopapier 9×13–20×30, Letter/Legal oder freies Maß. Marken: Eckmarken (0,25 pt, außerhalb des Endformats) oder durchgehende Linien. Beschnittzugabe 0–10 mm. Ausgabe: PDF in 1:1 (pdf-lib) bzw. Einzelbild mit dpi-Metadaten. Layout: gleiche Größen → exaktes Raster, gemischte Größen → MaxRects-Packer. Kern:src/lib/paper.ts(Formate),src/lib/printlayout.ts(reine Mathematik, getestet),src/lib/printrender.ts(sharp + pdf-lib). Auch per Telegram („📐 Druckbogen") und MCP (exact_size,print_sheet). - Admin: Key/Picdrop/Modelle/Nutzer/Kostendeckel/Presets/Speicherverwaltung, Picdrop-Diagnose.
Datenmodell / Migrationen
Nummerierte SQL in migrations/, beim Start automatisch (kein ORM-Push). Wichtige Spalten:
items.has_alpha, jobs.status (inkl. failed), jobs.finished_at,
settings.output_ext / recipes.output_ext, settings.*_metadata_sidecar, delivery_targets.*,
print_presets (Bogen-Vorlagen, 013).
Tests
npm test — Node-Testrunner über tsx. Deckt die Druck-Layoutmathematik ab (Maß-Parsing,
mm → px exakt, Überlappungsfreiheit, Ränder, Beschnittzugabe, Markengeometrie, Mehrseitigkeit).
Stolpersteine & Lösungen (Learnings)
- „Bild bei Picdrop ✓, aber nicht in der Galerie": Fehldiagnose vermeiden — die Dateien lagen die ganze
Zeit korrekt per SFTP in
/POSTER LEA(per Admin → Picdrop → „Ordner anzeigen" verifizierbar). Der eigentliche Grund war die Sortierung in der Picdrop-Weboberfläche, nicht das Format. Unabhängig davon liefern wir nicht-transparente Ergebnisse jetzt als JPG aus (src/lib/delivery.ts,deliverableBuffer): ~25 MB PNG → ~2 MB JPG, schneller und handlicher — plus wählbares Ausgabeformat (output_ext). - „Unbekanntes Format: sticker5": Seed legte ein Rezept mit Formatschlüssel
sticker5an, den der Resolver nicht kannte → jeder Lauf schlug fehl. Lösung: generisches Parsen vonsticker<N>/WxH. - Auftrag trotz Fehler grün:
jobs.statuskannte keinfailed; alle Positionen fehlgeschlagen wurde trotzdemdone. Lösung: Statusfailed, wenndone=0 && failed>0(src/worker.tsmaybeComplete). - Doppelte Datums-/UUID-Namen:
\b-Wortgrenzen greifen nicht an Unterstrichen. Lösung:\bentfernt, Datum/UUID/Hex global ersetzt (src/lib/pipeline.tscleanMotif). - Content-Type bei gemischten PNG/JPG-Ergebnissen:
file.tssnifft jetzt die Magic Bytes. .tmp--Reste auf Picdrop: früher Temp-Rename-Bug;cleanupTmp+ Admin-Knopf „Reste aufräumen".
Status
Reihenfolge und Nachweise siehe CLAUDE.md. Anforderungen im Handover-Paket
(Imagetool-Lea/00..07). Führend: 01-anforderungen.md.