Files
klarbild/src/pages/llms.txt.ts
T

152 lines
11 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import type { APIRoute } from 'astro';
export const prerender = false;
/**
* Maschinen-/KI-lesbare Kurzdoku unter /llms.txt (öffentlich, ohne Geheimnisse).
* Beschreibt Zweck, Fähigkeiten und die HTTP-/MCP-Schnittstelle von Klarbild,
* damit ein KI-Agent das Tool ohne Weboberfläche bedienen kann.
*/
const BODY = `# Klarbild
> Selbst gehostetes KI-Bildwerkzeug: verwandelt Screenshots und Fundstücke in
> saubere, druckfertige Bilder (bereinigen, freistellen, exakte Fotoformate,
> The-Frame-Querformat, Sticker mit Kontur), liefert an Picdrop (SFTP/FTPS)
> aus und sichert auf beliebige Backup-Ziele (z. B. NAS). Zusätzlich ein
> KI-freies Druckmodul (/drucken): Bilder exakt auf physische Maße bringen und mehrere
> davon in 100-%-Größe mit Schnittmarken auf einen Druckbogen setzen (PDF).
> Bedienung über Web, Telegram-Bot und HTTP-API/MCP.
Basis-URL: https://klarbild.heidrich-digital.de
Öffentliche Vorstellung des Druckmoduls (ohne Login, reine Beschreibung): /vorstellung
Sprache der Oberfläche: Deutsch. Stack: Astro 5 (SSR) · Postgres · sharp · pg-boss.
## Authentifizierung (für Agenten)
- Header: \`Authorization: Bearer <API-Token>\` (Admin → „Automatisierung / MCP-Zugriff").
- Der Token gilt für \`/api/*\` — NICHT für \`/api/admin/*\` (nur per Login-Session).
- Ohne Token/Session antworten API-Routen mit 401.
## Kernbegriffe
- Modus (mode): \`each\` (Screenshot bereinigen/freistellen/formatieren),
\`compose\` (ein Bild umwandeln ODER mehrere kombinieren, mit Textbeschreibung),
\`generate\` (neues Bild allein aus Text).
- Aufgaben (tasks, nur bei each): \`clean\`, \`cutout\`, \`format\`, \`contour\`.
- Rezept (recipe): gespeicherte Voreinstellung. Auftrag (job): ein Stapel; Position (item): ein Bild.
- Auftrags-Status: queued, running, paused, done, failed, cancelled. \`failed\` = alle Positionen fehlgeschlagen.
## Formate (output_format)
- Feste Schlüssel: 9x13,10x15,13x18,15x20,20x30,30x40,30x45,40x50,40x60,50x70,60x90,
A4,A3,A2,20x20,30x30, theframe (3840x2160), hochformat (2160x3840), keep (Original behalten).
- Freie Formate ohne Vorgabe: \`BREITExHOEHE\` in cm (z. B. \`25x35\`) oder \`sticker<N>\`
für N×N cm (z. B. \`sticker5\` = 5×5 cm). Grenze 300 cm. Exakte Pixel: round(cm/2.54*dpi).
- Dateiformat (output_ext): \`png\` (verlustfrei, Transparenz) oder \`jpg\` (klein, Picdrop-freundlich).
JPG nur ohne Transparenz; freigestellte Motive bleiben immer PNG. Global (Admin) oder pro Rezept.
## HTTP-API (Auswahl, JSON)
- GET /api/health — Status (öffentlich).
- GET /api/me — aktueller Nutzer/Rechte.
- GET /api/models — verfügbare Bildmodelle (Qualitätsstufen).
- GET /api/recipes — Rezepte/Presets auflisten.
- POST /api/recipes — Rezept anlegen. Body u. a.: name, mode, tasks[], output_format,
orientation(portrait|landscape), crop_mode(crop|extend), dpi, contour_mm, output_ext(png|jpg|null),
delivery(library|picdrop|both), picdrop_gallery, delivery_target_id.
- DELETE /api/recipes/:id — Rezept löschen.
- GET /api/delivery-targets — Auslieferungs-/Backup-Ziele.
- POST /api/jobs — Auftrag starten. Body: { recipe, mode, delivery, private,
prompt_text?(compose/generate), sources:[{source_path, filename}] }. Antwort: { jobId }.
- GET /api/jobs — letzte Aufträge. GET /api/jobs/:id — Auftrag + Positionen (inkl. error_message, finished_at).
- POST /api/jobs/:id/{pause|resume|cancel|retry-failed|delete}.
- POST /api/items/:id/{retry|reuse|alternative}.
- GET /api/items/:id/file — Ergebnisbild. Query: ?thumb=1 (Vorschau), ?preview=1 (kleines JPG fürs Vollbild),
?download=1 (als Datei), ?src=1 (Quelle). Content-Type wird aus den Magic Bytes bestimmt (PNG/JPG/WEBP).
## Drucken — exakte Maße & Druckbogen (ohne KI, /drucken)
Rein lokale Geometrie: Zuschnitt (sharp) + PDF (pdf-lib). Kein Modell, keine Kosten.
- Maßangaben: \`12x15\` = 12×15 cm · \`35x45mm\` · \`5\` = 5×5 cm · \`4:3/15\` = Verhältnis 4:3, längere Kante 15 cm.
- Papierformate (id): A6,A5,A4,A3,A3plus(329×483 mm),A2, F9x13,F10x15,F13x18,F15x20,F20x30, Letter, Legal — oder freies Maß.
- Bildformate: 86 Vorgaben in 10 Gruppen (Passbild, Klein, Foto, Zoll (US), Sofortbild,
Poster & Rahmen, Quadrat, DIN, Karten, Seitenverhältnis) — oder freies Maß. Auswahl der ids:
P35x45 (biometrisch), P50x50 · K20x30,K30x40,K40x50,K45x60,K60x80,K60x90 ·
S9x13,S10x15,S11x15,S12x15,S13x18,S15x20,S18x24,S20x25,S20x30,S24x30 ·
R30x40,R30x45,R40x50,R40x60,R50x70,R60x80,R60x90,R70x100 (Poster & Rahmen) ·
Q10x10,Q13x13,Q20x20,Q30x30 · DA6,DA5,DA4,DA3,DA2 ·
W16x9,W9x16,W3x2,W4x3,W5x4 (Seitenverhältnisse als Druckmaß, u. a. The Frame).
Damit sind alle Formate der KI-Generierung (src/lib/format.ts) auch hier druckbar.
- Passt das Bild nicht zum Format (fit je Zelle): \`cover\` = zuschneiden bis ausgefüllt (Standard,
nichts bleibt leer, außen fehlt etwas) oder \`contain\` = ganzes Bild einpassen, außen bleibt die
Randfarbe \`bg\` (#rrggbb, Standard #ffffff). Verzerrt wird nie.
- Schnitthilfen (marks.mode): \`none\` · \`corner\` (Eckmarken außerhalb des Endformats, Standard 4 mm lang,
3 mm Versatz, 0,25 pt Haarlinie) · \`grid\` (durchgehende Linien über den Bogen, laufen nie durch ein Motiv).
- Beschnittzugabe (bleedMm): das Bild ragt je Seite darüber hinaus; die Trimmbox bleibt exakt der gewählte Ausschnitt.
Der Abstand zwischen zwei Bildern ist immer ≥ 2 × Beschnittzugabe.
- Platzierung: gleich große Bilder → exaktes Raster; gemischte Größen → MaxRects-Packer mit fester
Ausrichtung je Format (probiert alle Ausrichtungs-Kombinationen und nimmt die mit den wenigsten Bogen).
- Die PDF-Seite hat exakt das Bogenmaß. Beim Drucken „Tatsächliche Größe / 100 %" wählen — nicht „an Seite anpassen".
- **Größer als der Bogen** (z. B. 20 × 30 cm auf DIN-A4-Fotopapier) — je Zelle über \`oversize\`:
\`fit\` = melden, dass es nicht passt (Standard) · \`overflow\` = mittig aufs ganze Blatt legen,
der Überstand wird beim Drucken abgeschnitten (20 × 30 auf A4: genau 3 mm in der Höhe) ·
\`tile\` = Posterdruck über mehrere Blätter mit Klebefalz (\`overlapMm\`, Standard 10 mm, gestrichelt markiert;
jedes Blatt trägt Nummer, Reihe und Spalte). 30 × 40 cm auf A4 ergibt 2 × 2 Blätter.
- **Beschriftung** (\`footer\`): \`false\` = aus · \`true\` = automatisch (Maße, dpi, Druckhinweis) ·
\`{ text, position, sizePt }\` = eigener Text. Position: \`unten-links|unten-mitte|unten-rechts|
oben-links|oben-mitte|oben-rechts\`. Die Zeile entfällt automatisch, wenn an der gewählten
Kante kein Platz frei ist — sie wird nie über ein Bild gedruckt.
- Dasselbe Bild darf mehrfach im \`cells\`-Feld stehen (verschiedene \`id\`, gleiche \`src\`), um es
in mehreren Maßen auf einen Bogen zu setzen. In der Weboberfläche macht das der Knopf „Duplizieren".
- POST /api/print/single — ein Bild exakt auf Maß. Body: { src:{kind:'upload',path}|{kind:'item',id},
crop?:{x,y,w,h} (relativ 0..1; ohne Angabe mittiger Ausschnitt), size|wMm+hMm, landscape?, dpi?, ext?(jpg|png), bleedMm? }.
Antwort: die Bilddatei. Header: X-Klarbild-Px, X-Klarbild-Real-Dpi, X-Klarbild-Dpi-Ok.
- GET /api/print/single?size=12x15&dpi=300 — nur rechnen: mm → Pixel.
- POST /api/print/sheet — Druckbogen als PDF. Body: { paper:{id}|{size}|{wMm,hMm}, landscape?, marginMm?,
gapMm?, bleedMm?, center?, dpi?, ext?, footer?, marks:{mode,lengthMm?,offsetMm?},
cells:[{ id, src, crop?, size|wMm+hMm, landscape?, count?, allowRotate?, fit?, bg?,
oversize?(fit|overflow|tile), overlapMm? }] }.
Antwort: application/pdf. Header: X-Klarbild-Pages, X-Klarbild-Per-Sheet.
Grenzen: 40 Zellen, 200 Exemplare je Zelle, 500 Einzelbilder und 60 Bogen je Auftrag.
- GET/POST/DELETE /api/print/presets — Bogen-Vorlagen (Papier, Marken, Formate, Stückzahlen).
Mitgeseedet: „Kita-Satz (A4)", „Schulsatz klein (A4)", „Passbildbogen 35×45 (A4)",
„2 × 10×7,5 auf Fotopapier 10×15". Eine Vorlage mit mehreren Formaten und nur einem Bild
klont das Bild in alle Formate.
- Bogen ausliefern: \`deliver: { target: ''|'nas'|<uuid>, gallery? }\` in /api/print/sheet legt das PDF
zusätzlich ins Ziel. Antwort bleibt das PDF; Header X-Klarbild-Delivered (0|1) und
X-Klarbild-Delivery-Msg (URI-kodiert).
## MCP-Server (für Assistenten)
Werkzeug \`klarbild\` (mcp/klarbild-mcp.mjs), nutzt denselben API-Token:
- list_recipes — verfügbare Rezepte.
- process_images — Bilder (lokaler Ordner / URLs) mit einem Rezept verarbeiten lassen.
- job_status — Fortschritt/Ergebnis eines Auftrags abfragen.
- exact_size — OHNE KI: ein Bild exakt auf ein Maß bringen, Datei lokal speichern.
- print_sheet — OHNE KI: Druckbogen (mehrere Bilder × Stückzahl, Schnittmarken) als PDF lokal speichern.
## Dateinamen der Ergebnisse
Schema: \`JJJJ-MM-TT_HHMMSS_<motiv>_<format>_<kürzel>.<ext>\` — z. B. \`2026-08-20_143207_lea-badge_the-frame_a3f9.jpg\`.
- Der Zeitstempel steht vorn, deshalb sortiert jede Dateiliste (Picdrop, NAS, Finder) von allein chronologisch.
- Das vierstellige Kürzel stammt aus der Item-ID. Dadurch sind zwei Bilder **nie** namensgleich —
auch nicht bei gleichem Motiv, gleichem Format und gleichem Tag. (Vor dem 21.08.2026 fehlten Uhrzeit
und Kürzel; gleichartige Bilder desselben Tages überschrieben sich beim Ausliefern.)
- Zeitzone: \`KLARBILD_TZ\`, Standard \`Europe/Berlin\`.
- Motiv wird aus dem Ursprungsnamen geschält (Datum, UUIDs und der eigene Stempel fallen weg);
generische Namen wie \`IMG\`, \`foto\`, \`telegram\` werden zu \`klarbild\`.
- Nachbenennung der ganzen Bibliothek: Admin → „Dateinamen" bzw.
\`POST /api/admin/storage { action: 'rename_preview' | 'rename_apply' | 'redeliver_all' }\` (nur per Login-Session,
nicht per API-Token). Idempotent — Stempel aus \`created_at\`, Kürzel aus der Item-ID.
## Auslieferung
- Picdrop = SFTP/FTPS-Ziel; Galerie = Unterordner unter dem Basispfad. Voller Pfad = Basisordner + Galerie
(wird erst beim Ausliefern zusammengesetzt). Standard-Galerie „POSTER LEA"; „The Frame" → „TheFrame-Backgrounds".
- Ausgeliefert wird das Ergebnis im gewählten Dateiformat (output_ext, global oder pro Auftrag) — PNG oder JPG. Freigestellte Motive bleiben PNG.
- Optionaler .md-Metadaten-Beileger je Ziel schaltbar (Picdrop/NAS/Zusatzziel).
## Datenschutz
- Private Session (job.private=true): kein Bibliothekseintrag, keine Auslieferung/Sicherung,
kein gespeicherter Prompt; Ergebnis wird nach kurzer Zeit gelöscht.
- Sichtbarkeit global steuerbar (nur eigene / alle sehen alles); Admin sieht alles.
Weitere Doku (menschlich): In-App /anleitung und /changelog.
`;
export const GET: APIRoute = async () =>
new Response(BODY, { headers: { 'Content-Type': 'text/plain; charset=utf-8', 'Cache-Control': 'public, max-age=3600' } });