Note backgrounds: Unsplash through our server, plus uploads (credit steps aside for sheets) #281

Closed
kayg wants to merge 0 commits from issue/102-note-backgrounds-3 into dev
Owner

Closes #102. Replaces #270 (same work). Fix: the corner credit now hides while any sheet (style, info, insert, move, link) is open, sits under the bar, drawers, menus and toasts (z-index 10; they are 19+), and on a phone rides clear above the bar. The inspector keeps its own credit.

Decisions applied

  1. Credit: a quiet "Photo by X on Unsplash" in the bottom-right corner of the screen (just above the bar on a phone), faint (40%) until hover, focus or tap (a tap lights it for 4 s), linking to the photographer and to Unsplash with utm_source=calternal&utm_medium=referral. Still shown in the inspector too.
  2. Uploads: no size limit and no count cap. Only the 40-megapixel guard (read from the header, never decoded) stays.
  3. Key: deploy/cloud/calternal.container gets EnvironmentFile=-%h/calternal-secrets.env (optional file). The server reads CALTERNAL_UNSPLASH_KEY.
  4. Settings switch: "Photos from Unsplash" is now a real setting (ui.unsplash, on by default, saved on the server like the others). Off hides search and shuffle in the picker and says so; uploads and already-chosen photos stay.

What this is (unchanged from #127): crates/backdrops proxies Unsplash (key only on the server; pictures served and cached from our own address so the CSP stays img-src 'self'; pick reported once per person and photo; picture URLs only followed for images.unsplash.com), plus uploads (JPEG/PNG/WebP by content, stored by hash under the person's Home, private). Picker in the style inspector (Note tab); the choice is per note in calternal-backdrop, no choice follows Settings. ADR 0039.

Rebase notes: ported by hand onto the Width inspector, the new prefs sync and the dynamic-port Playwright config (the stub Unsplash gets its own free port; the dev-user and served servers get the stub's key/address). MIN_OPACITY (40%) already existed on dev.

Tests: 17 Rust tests in crates/backdrops (size limit test now proves a 12 MiB file is accepted; pixel bomb still refused) + 1 --ignored HTTPS test; clippy -D warnings and fmt clean. Playwright on Chromium 1440 and 390: pick a photo (same-origin, credit in the inspector and the corner with UTM links, faint then lit on hover, saved in the file, no layout shift, download reported with the key), upload/remove, refused non-picture, no-key picker, photos-off picker, opacity floor, nothing fetched until the inspector opens, served-server CSP test, and a prefs test that the Settings switch saves, survives reload and reaches another device.

Known: served.spec "the app boots, connects live and saves" fails on current dev too (it waits for a status role that no longer exists); not touched here.

Not done: real Unsplash photos not fetched from here (the key is on the prod server); WebKit not re-run this round.

🤖 Generated with Claude Code

https://claude.ai/code/session_018Yyy8fQGxPLkLa8v8NXz7T

Closes #102. Replaces #270 (same work). Fix: the corner credit now hides while any sheet (style, info, insert, move, link) is open, sits under the bar, drawers, menus and toasts (z-index 10; they are 19+), and on a phone rides clear above the bar. The inspector keeps its own credit. **Decisions applied** 1. **Credit:** a quiet "Photo by X on Unsplash" in the bottom-right corner of the screen (just above the bar on a phone), faint (40%) until hover, focus or tap (a tap lights it for 4 s), linking to the photographer and to Unsplash with `utm_source=calternal&utm_medium=referral`. Still shown in the inspector too. 2. **Uploads:** no size limit and no count cap. Only the 40-megapixel guard (read from the header, never decoded) stays. 3. **Key:** `deploy/cloud/calternal.container` gets `EnvironmentFile=-%h/calternal-secrets.env` (optional file). The server reads `CALTERNAL_UNSPLASH_KEY`. 4. **Settings switch:** "Photos from Unsplash" is now a real setting (`ui.unsplash`, on by default, saved on the server like the others). Off hides search and shuffle in the picker and says so; uploads and already-chosen photos stay. **What this is** (unchanged from #127): `crates/backdrops` proxies Unsplash (key only on the server; pictures served and cached from our own address so the CSP stays `img-src 'self'`; pick reported once per person and photo; picture URLs only followed for `images.unsplash.com`), plus uploads (JPEG/PNG/WebP by content, stored by hash under the person's Home, private). Picker in the style inspector (Note tab); the choice is per note in `calternal-backdrop`, no choice follows Settings. ADR 0039. **Rebase notes:** ported by hand onto the Width inspector, the new prefs sync and the dynamic-port Playwright config (the stub Unsplash gets its own free port; the dev-user and served servers get the stub's key/address). `MIN_OPACITY` (40%) already existed on dev. **Tests:** 17 Rust tests in `crates/backdrops` (size limit test now proves a 12 MiB file is accepted; pixel bomb still refused) + 1 `--ignored` HTTPS test; clippy `-D warnings` and fmt clean. Playwright on Chromium 1440 and 390: pick a photo (same-origin, credit in the inspector and the corner with UTM links, faint then lit on hover, saved in the file, no layout shift, download reported with the key), upload/remove, refused non-picture, no-key picker, photos-off picker, opacity floor, nothing fetched until the inspector opens, served-server CSP test, and a prefs test that the Settings switch saves, survives reload and reaches another device. **Known:** `served.spec` "the app boots, connects live and saves" fails on current dev too (it waits for a `status` role that no longer exists); not touched here. **Not done:** real Unsplash photos not fetched from here (the key is on the prod server); WebKit not re-run this round. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_018Yyy8fQGxPLkLa8v8NXz7T
Note backgrounds: Unsplash through our server, plus uploads (#102)
Some checks failed
CI / server (pull_request) Successful in 44s
CI / web (pull_request) Failing after 22s
CI / rust (pull_request) Successful in 2m12s
b1f24486ba
Key on the server only (CALTERNAL_UNSPLASH_KEY, read from the optional
calternal-secrets.env). Pictures come from our own address, so the CSP stays
at 'self'. Picker in the style inspector; the choice is saved per note. A quiet
photographer credit sits in the corner of the screen. Uploads are guarded only
by the 40-megapixel limit. The Settings switch for Unsplash photos now works.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018Yyy8fQGxPLkLa8v8NXz7T
Corner credit steps aside while a sheet is open and clears the bar (#102)
Some checks failed
CI / server (pull_request) Successful in 21s
CI / web (pull_request) Failing after 20s
CI / rust (pull_request) Successful in 1m41s
dc17fda40b
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018Yyy8fQGxPLkLa8v8NXz7T
Author
Owner

Landed in merge train #282.

Landed in merge train #282.
kayg closed this pull request 2026-10-14 13:10:21 +00:00
Some checks failed
CI / server (pull_request) Successful in 21s
CI / web (pull_request) Failing after 20s
CI / rust (pull_request) Successful in 1m41s

Pull request closed

Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
kayg/calternal!281
No description provided.