Commit Graph

62 Commits

Author SHA1 Message Date
till 7289cde03a docs: changelog for the code-review fixes 2026-08-18 07:50:30 +00:00
till e87c61435c fix: code-review findings in the print module
Security and robustness:
- EXIF orientation is now applied before any geometry. Phone photos carry the
  rotation only as metadata; sharp was cropping the unrotated raster, so a
  portrait shot came out of the printer sideways and wrongly framed.
- renderCell no longer materialises the padded image at source resolution.
  It is one extract-resize-extend chain now, which is also sharp's internal
  order. A panorama into a narrow contain target used to build a ~960 MB
  intermediate and then fail; it is 90 ms and a few MB now.
- Target size is capped (300 Mpx) and bleedMm is clamped in /api/print/single,
  which had no bound at all.
- The delivery gallery is validated before use - posixpath.join let a crafted
  name escape the target's base folder and create directories there.
- Sheet requests are capped at 500 pieces and the packer has a step budget, so
  a degenerate request cannot block the single-threaded server.
- Print presets: delete only your own (admins all), config size limit, count
  limit, and by_name honours anonymous_generations.
- Telegram callbacks require an active pairing, like every other path.
- Error responses no longer leak storage paths or delivery hostnames.

Correctness:
- allowRotate:undefined now means allowed, consistently with the packer.
- The many-formats shortcut no longer drops a format that only fits rotated.
- unplaced names the format that is actually missing, not the first one.
- Crop marks never sit inside the printed bleed - the offset is raised.
- capacity() computes the grid instead of probing with 200 copies.
- Image keys in the sheet cannot collide with a cell literally named x::rot.
- labelMm keeps real decimals; parseSizeMm reads a:b as width:height, so
  3:4/15 is portrait and 4:3/15 is landscape.
- The footer is skipped when there is no free space at the bottom.
- The UI warns when corner marks do not fit the margin, and when continuous
  guides are used with mixed sizes.

Tests: 21 -> 31, each finding has a regression test.
2026-08-18 07:50:11 +00:00
till 7d42782c41 feat: all AI formats printable, crop-vs-border rule, mobile layout, Passbilder rename
Print now offers every format the AI pipeline knows (9x13 to 60x90, DIN A6-A2,
squares, poster/frame sizes to 70x100, and the screen ratios as physical sizes).
When a picture does not match the target ratio the user picks per image between
cropping to fill and keeping the whole picture on a border colour - never a
stretch. The module is called Passbilder now; /druck redirects.

Fixes two real defects: sharp runs extend after resize, so padded cells came out
oversized (a 35x45 mm cell became 35x171 mm with a border), and the grid packer
rotated a single portrait photo just because more would fit sideways.

Mobile: cards become rows, touch targets ~40px, crop editor as a bottom sheet.
Verified at 390/820/1440px - all three produce the same PDF.
2026-08-18 07:04:08 +00:00
till f6b81a0f5e fix: drop tsx dev dependency, run tests with node's native type stripping
The tsx postinstall pulls its own esbuild binary, which fails inside the
Docker build ("Expected 0.28.2 but got 0.27.7") and broke the deploy.
Tests now run on node --experimental-strip-types, so the image installs
nothing extra.
2026-08-18 06:45:28 +00:00
till 606a78bef0 feat: print module — exact sizes, print sheets and crop marks without AI 2026-08-18 06:40:55 +00:00
till f0442a617c docs: seeded templates and sheet delivery in llms.txt 2026-08-18 06:40:51 +00:00
till 4fb34cdab8 feat: sheet templates, margin profiles and delivering the sheet
Seeds the four sheet setups Till actually prints (Kita set, small school set,
full passport sheet, two 10x7,5 on 10x15 paper). A multi-format template
applied to a single image clones it into every format, so one click yields the
whole set. Margin gets one-click borderless / 5 mm / 10 mm profiles, and the
finished PDF can be pushed to Picdrop, the NAS or any extra target.
2026-08-18 06:40:31 +00:00
till 21a0a68972 docs: print module in help page, llms.txt, README and changelog 2026-08-18 06:20:46 +00:00
till 32f3b91779 feat: print sheets via Telegram and MCP
Telegram gains a fully button-driven print flow (photo -> size -> count ->
A4 PDF with corner marks), running inline without the queue or a model. MCP
gains exact_size and print_sheet so an assistant can produce print-ready files
from a local folder. Keeps the rule that every feature works on all three ways in.
2026-08-18 06:20:46 +00:00
till cd9081823d feat: print page with crop editor and live sheet preview
New Druck tab: upload or pick from the library, choose a target size and copy
count per image, pan/zoom crop on a locked aspect ratio, and a live preview
that renders the same layout the PDF will contain. Sheet controls for paper,
margin, gap, bleed, crop marks, dpi and templates.
2026-08-18 06:20:46 +00:00
till e8b3fde652 feat: print API - exact single image, sheet PDF, sheet presets
POST /api/print/single (image at an exact size, dpi headers incl. a real-dpi
warning), GET /api/print/single as a mm-to-px calculator, POST /api/print/sheet
(multi-image sheet as PDF, limits on cells/copies/pages) and print_presets for
reusable sheet setups.
2026-08-18 06:20:46 +00:00
till a593dea6c5 feat: exact-size rendering and 1:1 sheet PDF (sharp + pdf-lib)
renderCell brings a relative crop onto an exact physical size; missing bleed
pixels are copied from the edge instead of stretching the motif. Without a
crop it falls back to a centred cover crop, never a distorted fill.
buildSheetPdf writes a PDF page that is exactly the sheet size in mm with
hairline crop marks and an optional footer. Source resolution guards uploads
to the sources/ prefix and reuses the library visibility rules.
2026-08-18 06:20:46 +00:00
till a651025422 feat: mm-based print layout math and paper/photo size tables
Pure geometry, no I/O: DIN A6-A2 incl. A3+ (329x483), photo-paper cuts,
US Letter/Legal, biometric and small photo formats, plus a forgiving free
size parser (12x15 cm, 35x45mm, 5, 4:3/15). Packing: exact grid for uniform
sizes, MaxRects with one fixed orientation per format for mixed sets, so
small prints fill the space next to a large one. Crop marks as line segments
(corner marks outside the trim box; continuous guides that never cross a
motif). Covered by tests/printlayout.test.ts.
2026-08-18 06:20:45 +00:00
till ed22a76fda feat: deliver in chosen format (not forced JPEG); wire output_ext through jobs/Telegram/MCP
- delivery.ts: upload the stored result in its chosen format (output_ext,
  global or per-recipe) instead of always converting non-alpha to JPEG.
- jobs POST froze the snapshot without output_ext -> per-conversion/per-recipe
  format choice was silently dropped. Now carried through.
- telegram.ts: recipe snapshots carry output_ext + delivery_target_id
  (custom formats already flow via output_format).
- mcp/klarbild-mcp.mjs: process_images gains tasks, output_ext, orientation,
  crop_mode, contour_mm, picdrop_gallery; job_status shows finished_at + errors.
- Docs: llms.txt, mcp/README, changelog updated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XNQ8ghPfzAfsyVYd6HgFb6
2026-07-24 09:12:07 +00:00
till ae91f4bfd5 feat: llms.txt (AI-readable docs), job delete, friendly missing-source error
- /llms.txt: public machine-readable capability/API/MCP doc for agents.
- Job delete action + queue button for terminal jobs; cleans objects.
- process.ts: clear 'Quelle nicht mehr vorhanden' message instead of raw
  EISDIR/ENOENT when a source was purged.
- Docs corrected: Picdrop 'missing images' was web-UI sorting, not PNG;
  JPG delivery kept as size/perf improvement.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XNQ8ghPfzAfsyVYd6HgFb6
2026-07-24 08:57:31 +00:00
till d54d322d6a docs: changelog + README handover for Picdrop/JPG/formats/failed-jobs
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XNQ8ghPfzAfsyVYd6HgFb6
2026-07-24 08:41:06 +00:00
till 3a061aa612 feat: custom formats (WxH/stickerN), JPG output option, failed-job status
- format.ts parses free formats from the key: sticker<N> -> N×N cm,
  <W>x<H> -> W×H cm. Fixes 'Unbekanntes Format: sticker5' (the seeded
  'Sticker 5 cm' recipe was unusable) and lets admins define ANY format
  without a migration. Guarded to <=300 cm.
- Output file format PNG/JPG: global default (settings.output_ext) plus
  per-recipe/per-conversion override. JPG only for non-alpha; cutouts
  stay PNG. file.ts sniffs content-type from magic bytes.
- Jobs can now be 'failed' (all items failed) -> red in the queue; add
  finished time to queue list + detail. migration 012.
- Studio: custom-measure input + Dateiformat selector; Admin: global
  Standard-Dateiformat.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XNQ8ghPfzAfsyVYd6HgFb6
2026-07-24 08:32:03 +00:00
till efcb889c96 fix: strip dates/UUIDs in filenames even when adjacent to underscores
Word-boundary (\b) anchors don't fire next to underscores, so
already-processed names like 2026-07-24_2026-07-24-<uuid>_30x40 kept
their doubled date prefix. Drop \b; match dates/hex globally.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XNQ8ghPfzAfsyVYd6HgFb6
2026-07-24 08:13:36 +00:00
till 70f3904d0e fix: deliver JPEG (not huge PNG) to Picdrop, clean filenames, tmp cleanup
- Picdrop is a photo-proofing tool that expects handy JPGs; non-alpha
  results are now delivered as JPEG q92 (4:4:4) instead of 12-32 MB PNGs.
  Cutouts with alpha stay PNG. Fixes images not showing in POSTER LEA.
- buildResultFilename strips doubled dates/UUIDs/hex to avoid messy names.
- picdrop-ls gains action:cleanup_tmp; Admin 'Reste aufräumen' button
  removes leftover .tmp- files from the default gallery.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XNQ8ghPfzAfsyVYd6HgFb6
2026-07-24 08:07:16 +00:00
till 32f06297a6 feat: per-source .md sidecar toggles, Picdrop folder-list diagnostic, fast JPEG fullscreen preview + thumb-first, thumbnail backfill
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XNQ8ghPfzAfsyVYd6HgFb6
2026-07-24 07:56:57 +00:00
till 5f0c33e44b docs: changelog for color tags + tap-to-save
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XNQ8ghPfzAfsyVYd6HgFb6
2026-07-24 07:43:57 +00:00
till af34cb9cc8 feat: Picdrop-style color tags (Rot/Orange/Gruen/Final) in library + in .md metadata; tap-to-open fullscreen for iPhone Save-to-Photos
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XNQ8ghPfzAfsyVYd6HgFb6
2026-07-24 07:38:20 +00:00
till f95a3b58b7 docs: changelog for access control, private sessions, NSFW gate
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XNQ8ghPfzAfsyVYd6HgFb6
2026-07-23 21:47:25 +00:00
till e6b325eb53 feat: per-user visibility (own/shared), anonymous generations, private sessions (generate+forget), NSFW model gating
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XNQ8ghPfzAfsyVYd6HgFb6
2026-07-23 21:46:47 +00:00
till 2588a54eb7 docs: changelog + roadmap for transform/backup/metadata/prompt/presets/MCP
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XNQ8ghPfzAfsyVYd6HgFb6
2026-07-23 21:30:45 +00:00
till 295f0ce91e feat: single-image transform, NAS/FTP in delivery, backup-all any target, metadata sidecar, prompt view, preset manager, API token + MCP server
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XNQ8ghPfzAfsyVYd6HgFb6
2026-07-23 21:29:43 +00:00
till 5cfafb456c docs: changelog + roadmap update strategy & multi-target
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XNQ8ghPfzAfsyVYd6HgFb6
2026-07-23 21:06:04 +00:00
till 36f3c8da22 feat: multiple delivery targets (FTP/SFTP), per-preset wiring; The Frame -> TheFrame-Backgrounds gallery
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XNQ8ghPfzAfsyVYd6HgFb6
2026-07-23 21:04:27 +00:00
till 3a098d708c feat: mirror-all-to-NAS, content reset (danger zone), in-app changelog + CHANGELOG.md, How-To refresh
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XNQ8ghPfzAfsyVYd6HgFb6
2026-07-23 20:52:48 +00:00
till 79454c0b92 feat: show NAS backup status badge in library
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XNQ8ghPfzAfsyVYd6HgFb6
2026-07-23 20:43:10 +00:00
till 5fba397efa refactor: NAS mirror uses base folder directly (no redundant klarbild segment)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XNQ8ghPfzAfsyVYd6HgFb6
2026-07-23 20:32:50 +00:00
till 3bfeadbaa0 feat: bulk ZIP download, bulk move-to-folder, folder rename
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XNQ8ghPfzAfsyVYd6HgFb6
2026-07-23 20:23:03 +00:00
till 7608bc62da feat: button-driven Telegram menu (reply keyboard + commands), reachability diagnostic, How-To update
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XNQ8ghPfzAfsyVYd6HgFb6
2026-07-23 20:12:27 +00:00
till 5105c7d529 fix: before/after works for compose (source fallback), hidden for text-only; add reuse result as new Studio input
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XNQ8ghPfzAfsyVYd6HgFb6
2026-07-23 20:01:17 +00:00
till 063903c60a feat: How-To/Anleitung page + nav link
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XNQ8ghPfzAfsyVYd6HgFb6
2026-07-23 19:39:05 +00:00
till 5b654b1436 feat: offer FLUX.2 (open-source) image model
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XNQ8ghPfzAfsyVYd6HgFb6
2026-07-23 19:39:05 +00:00
till 78737936fb feat: functional folders (filter/delete/gallery) + always show model + variant switcher
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XNQ8ghPfzAfsyVYd6HgFb6
2026-07-23 19:39:05 +00:00
till e59d26fadd feat: regenerate/alternatives grouped by source (variant_of)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XNQ8ghPfzAfsyVYd6HgFb6
2026-07-23 19:39:04 +00:00
till 34aa45b292 fix: idempotent job completion + monthly budget cap enforcement
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XNQ8ghPfzAfsyVYd6HgFb6
2026-07-23 19:19:47 +00:00
till a69e2452e1 feat: admin storage + NAS settings card
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XNQ8ghPfzAfsyVYd6HgFb6
2026-07-23 19:19:47 +00:00
till 3e2e09a38d feat: Studio mode selector + Telegram compose/generate (full parity)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XNQ8ghPfzAfsyVYd6HgFb6
2026-07-23 19:19:47 +00:00
till f5cbcdf4d1 feat: storage management (retention, source purge) + Synology NAS backup
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XNQ8ghPfzAfsyVYd6HgFb6
2026-07-23 19:19:40 +00:00
till b705d77240 feat: low-res webp thumbnails for library previews
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XNQ8ghPfzAfsyVYd6HgFb6
2026-07-23 19:19:39 +00:00
till 044eb7f1ae feat: compose + text-to-image generation modes (multi-image + free-text)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XNQ8ghPfzAfsyVYd6HgFb6
2026-07-23 19:19:39 +00:00
till 6d58c6c8fd feat: model selection+visibility, better filenames, picdrop direct upload + configurable gallery
- studio: quality/model selector (friendly names from active models); library shows model used
- seed 2 models (Schnell/Beste Qualitaet); GET /api/models for studio+library
- filename: YYYY-MM-DD_motif_format.png (was redundant slug, no date)
- picdrop: direct upload to final name (fix .tmp- leftover from failed rename on picdrop); cleanupTmp helper
- configurable default gallery (migration 003, settings + admin field); base_path=SFTP root, gallery=subfolder
2026-07-23 18:54:37 +00:00
till b047f55687 feat: mobile-first redesign (design-taste-frontend)
- responsive nav: compact sticky topbar + bottom tab bar on mobile (SVG icons, safe-area insets), inline top nav on desktop; no more horizontal overflow
- previews show full aspect ratio (object-fit contain) on subtle checker bg for transparency (library, studio, queue)
- 16px inputs on mobile (no iOS zoom), tactile :active feedback, larger touch targets
- keeps Klarbild identity (Space Grotesk/Mono, blue, paper, regmark), no glows/gradients
2026-07-23 18:43:47 +00:00
till dba5620ff6 fix: telegram callback_data <64 bytes (BUTTON_DATA_INVALID) — recipe uuid only, resolve draft by chat; send before status update 2026-07-23 18:31:28 +00:00
till d680b3f67a feat: telegram bot (grammy webhook) — full parity channel
- pairing (admin code -> chat link), bundling of forwarded images via drafts+quiet-window sweeper
- recipe pick via inline keyboard, job (origin=telegram) creation, one summary reply with result files + links
- compressed-photo detection/flag; /start /help /rezepte /status
- webhook route (secret token), admin pairing-code + links endpoints, AdminApp telegram section
- worker notifies chat on completion; migration 002 adds jobs.telegram_chat_id; setupTelegram on init
2026-07-23 17:39:49 +00:00
till 26c3e0ef07 feat: picdrop delivery — worker delivers on job completion, /api/deliver for manual, gallery default 'POSTER LEA', library bulk send + status badges 2026-07-23 15:41:04 +00:00
till 25d0a5cd97 feat: Bibliothek + Admin
- Bibliothek: grid, before/after slider, rename, download, folder assign, delete, multi-select
- Admin: stats+costs, OpenRouter key (masked/encrypted), budget, defaults, Picdrop config+test,
  models (list/add from OpenRouter/set default/delete), users (add/set password)
- api: items/:id PATCH+DELETE, folders CRUD, admin/{settings,stats,models,test-picdrop,users}
- lib/picdrop.ts (SFTP/FTPS test+upload, temp->rename)
2026-07-23 15:22:12 +00:00