SIGN IN SIGN UP

fix(settings): close the wallpaper market's review findings

The market shipped three things that misbehave in ordinary use, plus a
tail of smaller gaps. All are fixed here; the contract change between the
Rust summary and its TypeScript mirror is why this is one commit.

Concurrent downloads shared one staging file. The grid disabled only the
card being downloaded, so a second wallpaper could be picked mid-download,
and both writers then fought over `background.img.tmp`: the second
`File::create` truncates what the first is still writing, and whichever
`rename` lands second fails with ENOENT. On APFS that reproduced as
"download failed" on a download that had actually succeeded, 58 times out
of 60 interleavings. Writers now stage into `background.img.<pid>.<seq>.tmp`
— rename stays atomic, so overlapping writers are a clean last-writer-wins
and neither can truncate the other — and the dialog serializes downloads
behind a synchronous ref, because two clicks in one frame both read the
state guard before it re-renders. Crash debris is swept on a 10-minute age
gate, which a live writer's file can never trip.

Roughly one wallpaper in fifteen could never be applied. Downloads are
capped at 16 MiB / 40 Mpx; measured against the default browse view, 5.0%
of listings exceed the byte cap and 1.7% the pixel cap — one or two dead
cards per page, each answering a click with "please retry" for a condition
retrying cannot change. The listing already reports `file_size`, so the
summary now carries it alongside the dimensions, and those cards are
disabled up front, badged, and told the offending number against the
ceiling. Real failures now show the backend's own message instead.

The host allowlist stopped at the first URL. reqwest's default policy
follows ten hops across hosts, and `fetch_asset` hands the body back to
the caller — so a redirect off wallhaven made this a reader of whatever
the host can reach, which in server mode may be a private network. Every
hop is now re-checked, the way `forge` and `remote_proxy` re-check theirs.

Also: the validate-and-write tail moved off the async runtime, matching
every other command in `commands::background`; the 30 s total deadline
became a read-stall timeout plus per-request deadlines, since 16 MiB in
30 s demanded 4.4 Mbit/s from exactly the networks this proxy exists for;
the source-URL key joined the cross-window storage listener, so a second
window stops claiming a replaced wallpaper is "in use"; the thumbnail
cache is LRU-bounded; the search debounce no longer resets the page on its
mount tick, which used to snap you back to page 1 if you paged within
300 ms of opening; paging follows the page wallhaven actually served; the
card's accessible name says what it does and the focus ring shows it;
and three i18n keys nothing referenced were dropped.

`validate_background`'s doc claimed its callers only ever pass files the
user picked off their own disk. The market broke that premise, so the doc
now says what still holds on that path and what does not.
X
xintaofei committed
e79a512496742e36880a7db0d7190b3c82405fe6
Parent: 75cdec5