# Changelog All notable changes to Klarbild are documented here. Newest first. ## 2026-08-18 (4) — Renamed to "Drucken", 86 sizes, crop editor fixed ### Fixed - **The crop editor distorted the picture.** `.crop-box` had `width:100%` *and* `max-height`, so the height was clamped while the width stayed put — the frame no longer had the target aspect ratio, and `object-fit:fill` stretched the image into it. Measured on a 390 px viewport: frame 0.843 instead of 0.778, picture **8.3 % too wide**. The frame width is now derived from the allowed height and the ratio, and the image carries its own `aspect-ratio` instead of a second percentage — it can no longer stretch, even if the frame is ever off. - **Dragging felt broken.** At "fill" zoom the crop sits exactly at the stop in one axis, so pulling that way does nothing — correct, but invisible. The editor now says which way the picture can move, double-click/double-tap zooms a step, arrow keys nudge (shift for bigger steps), and the zoom slider shows its factor. ### Changed - **Renamed to "Drucken"** — the tab, page, title and Telegram button. `/druck` and `/passbilder` both redirect permanently to `/drucken`. - **86 image sizes in ten groups** (was 41): passport formats per country, cm small sizes, classic photo sizes, **US inch sizes** (wallet to 24 × 36), **instant-film picture areas** (Instax mini/Square/Wide, Polaroid), poster and frame sizes up to 100 × 140, squares, **DIN A0–A7 incl. A3+**, cards (business card, postcard, DIN lang, cheque card) and aspect ratios as physical sizes (16:9, 9:16, 21:9, 2:1, 3:2, 2:3, 4:3, 3:4, 5:4, 4:5, 1:1). - Paper list grew to 19: DIN A1, square photo paper, and the US inch cuts. ## 2026-08-18 (3) — Code review: fixes ### Fixed - **EXIF orientation was ignored.** Phone photos carry their rotation as metadata only. sharp cropped the unrotated raster, so a portrait shot came out of the printer sideways and with the wrong framing. Orientation is now applied before any geometry, and `/api/uploads` reports oriented dimensions so the crop editor agrees with the render. - **Memory blow-up on "Rand lassen".** The padded image was materialised at source resolution before the resize. A panorama into a narrow contain target built a ~960 MB intermediate and then failed outright; it is one extract→resize→extend chain now (sharp's own order) — 90 ms and a few MB. - **`bleedMm` was unbounded** in `/api/print/single` (the sheet endpoint clamped it). - **Delivery gallery could escape the target's base folder** — `posixpath.join` happily resolves `../..`, and the folder is created before upload. Names are validated now. - **Denial of service:** sheet requests are capped at 500 pieces and the packer has a step budget, so a degenerate request can no longer block the single-threaded server. - **Print presets:** delete only your own (admins all), config size and count limits, and `by_name` honours `anonymous_generations`. - **Telegram callbacks** now require an active pairing, like every other path. - **Error responses** no longer leak storage paths or delivery hostnames. - `allowRotate: undefined` meant "no rotation" in one place and "rotation allowed" in two others — a picture that only fits rotated was reported as unplaceable. - The many-formats shortcut dropped a format that only fits rotated. - `unplaced` blamed the first format instead of the one actually missing. - Corner marks could land inside the printed bleed; the offset is raised to clear it. - `capacity()` silently capped at 200. - Image keys could collide with a cell literally named `x::rot`. - `labelMm` rounded away real decimals (11,25 cm became 11,3); `parseSizeMm` ignored the order in `a:b`, so `3:4/15` and `4:3/15` produced the same portrait size. - The footer was drawn over the artwork when the margin was small. - The UI now warns when corner marks do not fit the margin, and when continuous guides are combined with mixed sizes (they cannot run through). ### Changed - Tests grew from 21 to 31 — every finding above has a regression test. ## 2026-08-18 (2) — Passbildfunktion: all sizes, fit rules, mobile ### Added - **Renamed to "Passbilder"** — the tab, page and Telegram button now say what it is. `/druck` permanently redirects to `/passbilder`. - **Every size the AI pipeline knows** is now printable too: 9×13 … 60×90, DIN A6–A2, squares, plus poster/frame sizes up to 70×100 and the screen ratios (16:9 "The Frame", 9:16, 3:2, 4:3, 5:4) as physical measurements. 41 presets in seven groups. - **Fit rule per image** — when the picture does not match the target ratio, choose **Zuschneiden** (fill the format, crop the overflow — default) or **Rand lassen** (keep the whole picture, pad with a border colour: white, black, paper or custom). Nothing is ever distorted. Works in the crop editor, the sheet preview, the PDF, `/api/print/*` (`fit`, `bg`) and the saved templates. - **Mobile layout** — image cards become a row on phones, touch targets grow to ~40 px, the crop editor turns into a full-width bottom sheet, no horizontal overflow. Verified at 390 / 820 / 1440 px; all three produce the identical PDF. ### Fixed - **Sheet cells could come out oversized.** sharp applies `extend` *after* `resize`, so padding was added on top of the finished size — a 35×45 mm cell became 35×171 mm in "Rand lassen" mode, and bleed near an image edge was off too. Crop and padding now run in their own pass. Covered by `tests/printrender.test.ts`. - **Orientation is no longer overridden.** A single portrait passport photo was laid down sideways just because more would fit that way. Rotation now only happens when it actually saves a sheet. ## 2026-08-18 — Print module: exact sizes, sheets & crop marks (no AI) ### Added - **Print module (`/druck`)** — a lab-style, completely AI-free path: crop, scale and place images at exact physical sizes. No model call, no cost, no queue. - **Interactive crop editor** — pan and zoom on a fixed target aspect ratio (rule-of-thirds overlay); the crop is stored relative (0..1) so it survives any source resolution. - **Size presets & free input** — biometric passport 35 × 45 mm, 2 × 3 / 3 × 4 / 4 × 5 / 4,5 × 6 cm, 9 × 13 … 50 × 70 cm, squares, DIN. Free input understands `12x15` (cm), `35x45mm`, `5` (= 5 × 5 cm) and `4:3/15` (ratio + long edge). - **Sheet layout** — paper presets DIN A6–A2 incl. **A3+ (329 × 483 mm)**, photo-paper cuts (9 × 13 … 20 × 30), US Letter/Legal, plus any custom paper size. Portrait/landscape, printer margin, centering, per-image copy count. - **Automatic nesting** — identical sizes produce an exact grid; mixed sizes go through a MaxRects packer that fixes one orientation per format and tries every combination, so eight passport photos land in the free space next to the 13 × 18. - **Crop marks** — `corner` (fine 0.25 pt marks outside the trim box, Photoshop/InDesign convention, 4 mm long / 3 mm offset by default) or `grid` (continuous guides across the sheet that never cross another image). Gap between images adapts to the chosen marks. - **Bleed** (0–10 mm) — the image extends past the trim box, edge pixels are copied rather than stretched; the gap is always ≥ 2 × bleed so neighbouring bleeds cannot overlap. - **PDF output at 1:1** — the PDF page is exactly the sheet size (pdf-lib), with a footer stating the sizes, dpi and the "print at 100 %, not fit-to-page" reminder. - **Single export** — one image at an exact size as PNG/JPG with dpi metadata, plus a resolution warning when the source cannot hold the requested dpi. - **Sheet templates** — save the whole sheet setup (paper, marks, formats, counts) as a reusable preset (`print_presets`, migration `013`). - **All three entrances** — `/druck` in the web app, `📐 Druckbogen` in Telegram (fully button-driven, returns an A4 PDF), and the MCP tools `exact_size` / `print_sheet`. - **API** — `POST /api/print/single`, `GET /api/print/single?size=…` (mm → px calculator), `POST /api/print/sheet`, `GET|POST|DELETE /api/print/presets`. Documented in `/llms.txt`. - **Seeded sheet templates** — "Kita-Satz (A4)", "Schulsatz klein (A4)", "Passbildbogen 35×45 (A4)" and "2 × 10×7,5 auf Fotopapier 10×15". Loading a multi-format template with a single image clones that image into every format, so one click produces the whole set. - **Margin profiles** — borderless (0 mm), standard (5 mm) and safe (10 mm) as one-click buttons. - **Deliver the sheet** — the finished PDF can additionally be pushed to Picdrop, the NAS or any extra delivery target (`deliver` in the sheet request). - **Tests** — `tests/printlayout.test.ts` (`npm test`) covers size parsing, exact mm → px, non-overlap, margins, bleed spacing, mark geometry and multi-sheet paging. ## 2026-07-23 — Compose, generate & second backup ### Added - **Compose mode** — combine multiple images + a text description into one new image. - **Generate mode** — create images from text only, no source needed. - **The Frame "extend"** — outpaint more scene around the motif instead of only cropping. - **Reuse** — take a finished result back into the studio as a new source to edit further. - **Alternatives** — generate additional versions of the same image, grouped with a switcher. - **Folders** — filter, rename and delete in the library; per-folder delivery gallery mapping. - **Bulk actions** — download selected results as a ZIP, move several into a folder at once. - **Thumbnails** — low-res previews for the library grid. - **Models** — quality tiers incl. open-source (FLUX.2); the used model is shown per image. - **Color tags** — Picdrop-style flags (red / orange / green / final) per image, filterable, and written into the companion `.md` metadata. - **Tap-to-save** — "Laden" opens the image full-bleed so on iPhone/iPad a long-press offers "Save to Photos" (plus a classic file download). - **Access control & privacy** — library visibility (own-only vs shared), optional anonymous generations (creator hidden from non-admins), admins always see all. - **Private sessions** — generate-and-forget: no library entry, no delivery/backup, no stored prompt, sources purged, result auto-deleted shortly after. Enabled globally by admin or forced per user. - **NSFW gate** — models flagged NSFW are only selectable/visible when the admin enables it. Operator responsibility; content involving minors is never permitted. - **Single-image transform** — compose now accepts one image, so a single photo can be transformed by prompt (e.g. "make it an oil painting") and put onto a format. - **Multiple delivery targets** — besides the default Picdrop, add any number of FTP/SFTP destinations; selectable in the studio ("Wohin?", incl. NAS); a preset or folder can be hard-wired to a specific target + gallery. - **Backup to any target** — mark any target as a backup destination (like the NAS); one-click "back up all images" mirrors to all backup destinations. - **Metadata sidecar** — optionally ship a companion `.md` (prompt, model, format …) next to each image on Picdrop/NAS/FTP. - **Prompt view** — every generated image shows its prompt in the library (with copy). - **Preset manager** — save current settings as a preset, load and delete presets. - **API token + MCP server** — programmatic access so an assistant can push images from a local folder or iMessage into Klarbild (`mcp/klarbild-mcp.mjs`). - **The Frame** preset now delivers to its own gallery ("TheFrame-Backgrounds"). - **Second backup** — mirror every result over SFTP/FTPS to a NAS (works over Tailscale too), with a "NAS ✓" badge and a "mirror all existing images" action. - **Storage management** — thumbnails, delete sources after processing, auto-retention. - **Telegram** — fully button-driven (persistent keyboard + command menu): edit, compose, "new image", reuse — no slash commands required. - **Reset** — wipe library, jobs and folders cleanly (keeps users, settings, models, recipes), guarded by a typed confirmation. - **Help page**, in-app **changelog**, and a NAS **reachability diagnostic**. ### Fixed / hardened - Real per-image cost cap (monthly budget) enforced by the worker. - Idempotent job completion (no duplicate delivery/notification). - Before/after comparison works for composed images; hidden for text-only results. ## 2026-07-22 — Foundation ### Added - Astro 5 + React islands UI (mobile-first), PostgreSQL, fs/S3 storage abstraction. - pg-boss job queue with in-process worker; sharp pipeline (exact cm/dpi print sizes, sticker contour). - OpenRouter image gateway; admin-managed models. - Picdrop delivery over SFTP/FTPS; recipes; library with before/after. - Telegram bot (grammY webhook); argon2 auth; AES-256-GCM secret encryption.