molluskandClaude Opus 4.8 f029a30ea7 feat(avatars): custom image upload — W4 Phase 3
Completes W4: users can upload a custom avatar image.

- `Avatar::Custom(String)` carries a base64 PNG. `process_upload` decodes an
  arbitrary png/jpeg, downscales so the longest side is 128px (aspect kept),
  re-encodes PNG, base64s, and rejects anything over a hard cap.
- Settings "Avatar" gains an "Upload image…" button (native picker via rfd's
  xdg-portal backend, off-thread through Task::perform) and shows the current
  custom avatar as a selected tile.
- Untrusted peer avatars are validated at gossip ingest (`sanitize_incoming`):
  a custom image must be within the byte cap and decode as a PNG within bounds
  (image-crate decode limits guard against decompression bombs) or it's
  downgraded to a monogram.
- Raised the gossip max message size to 64 KB so a capped custom avatar fits
  inline on the presence plane (all peers already need a matching build).
- Deps: image (png/jpeg only), rfd (xdg-portal, no GTK); only `rfd`+`pollster`
  are actually new in the lockfile (rest were already transitive). cargo audit
  clean (0 vulns; the 2 unmaintained warnings are pre-existing S7).
- `Controller::send` now returns bool (was a Result carrying the now-larger
  CoreCommand by value, which tripped result_large_err).
- +5 avatar unit tests (upload resize/round-trip, reject non-image, ingest
  accept/reject). 227 lib tests green, clippy clean.

Manual check: Settings → Avatar → Upload; confirm the picker opens and the
image shows for you and (after redeploy) for a peer.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 17:00:18 -04:00
S
Description
No description provided
MIT
15 MiB
2026-07-19 19:57:06 +00:00
Languages
Rust 98.8%
Shell 0.4%
Nix 0.4%
Python 0.2%
Inno Setup 0.2%