Fredrik JohanssonandClaude Sonnet 5 cb0e0acc11
Docker / build-and-push (push) Successful in 4m34s
Add drag-to-reposition for aggressive crops
Non-original aspects (square, 4:5, 16:9) always centered the crop with
no way to choose what got cut off. Crop position is now a normalized
0-1 offset per axis instead of a fixed /2 center, and dragging directly
on the canvas -- only when the current aspect actually leaves slack to
pan within -- adjusts it. Pointer movement converts to rotated-image
pixels via the crop-width-to-display-width ratio, so it stays 1:1
regardless of the maxDim downscale or the canvas's own CSS scaling.
Resets to centered on aspect or rotation change, since old offsets
don't make sense against new dimensions. Verified with a real drag test:
a marker cropped out at the centered default becomes visible after
dragging, and the "original" aspect (no crop, no slack) correctly
leaves panning disabled.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-07 17:29:38 +02:00
2026-07-16 15:29:34 +02:00

latent

A phone-photo editor with a "develop it later" option. Upload a photo, apply film-style effects (grain, light leaks, vignette, halation, color cast) entirely in the browser, then either download it right away or send it off to "develop" — the server holds it behind a randomized delay (hours to days) before the link actually resolves to the image. Getting your photos back later, on purpose, for no practical reason except that it's a fun mechanic.

Named after the latent image, the photographic term for the picture that already exists on exposed film but is invisible until developed.

See PROPOSAL.md for the original design writeup.

Two finishing paths off one editor

  1. Instant — edit, then download directly, entirely client-side. Optionally generate a share link (explicit action, not automatic).
  2. Delayed — edit, then "send to develop": uploads to server storage, assigns a random develop_at within a configured range, and gives back a link immediately — it just won't resolve to the actual image until that time passes.

Running locally

npm install --prefix server
npm start --prefix server

Then open http://localhost:3000. DATA_DIR defaults to ./data (gitignored) for the SQLite db and image blobs.

Deploying

docker compose up using the provided docker-compose.yml / .env.example (copy .env.example to .env and adjust). CI builds and pushes the image on push to main via .gitea/workflows/docker.yml.

Config

All via env vars, see .env.example:

  • DEVELOP_MIN_HOURS / DEVELOP_MAX_HOURS — randomized delay range for the "send to develop" path (default 2–48h).
  • INSTANT_TTL_HOURS — retention for share links off the instant path (default 24h; deliberately shorter than the delayed path since it's a more casual "just give me a link" flow).
  • DELAYED_VIEW_WINDOW_HOURS — how long a delayed image stays available after develop_at passes, before the TTL sweep deletes it (default 168h / 7 days).
  • MAX_UPLOAD_BYTES, RATE_LIMIT_PER_HOUR — basic abuse limits; no accounts or auth on any endpoint, the link itself is the credential.

Non-goals

No accounts, no galleries, no e2e encryption (nothing here is sensitive — the whole point is a link other people can open), no collaborative editing.

S
Description
No description provided
Readme
86 KiB
Languages
JavaScript 69.5%
CSS 16.2%
HTML 12.8%
Dockerfile 1.5%