draft the crop instead of saving every pointer move

Panning went berserk. Wired straight to MediaField, `onCropChange` fired on every
move, each move saved, each save reloaded the row, and the reload handed the crop
back as the last SAVED value — yanking the picture out from under the drag.

BlockMedia exists for exactly this and I went around it. It keeps the crop in a
local draft, saves once when the drag stops, and resets on the url and the shape
rather than on the crop — its own note says why, and the failure it describes is
the one I reproduced.

Also: an empty picture slot looked the same whether the carousel was still being
read or genuinely had nothing in it. `undefined` now means loading and `null`
means empty, and the line underneath says which.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-05 15:46:22 +01:00
co-authored by Claude Opus 5
parent 5968cb5d83
commit 05ad4f4d51
2 changed files with 21 additions and 8 deletions
+3 -1
View File
@@ -122,7 +122,9 @@ export const SubjectPanel = <T extends SubjectRecord>({ section }: { section: Su
record={selected}
// What the cover falls back to when the row has not chosen one — the same first item the
// grid below is already showing, so the two cannot disagree about which picture that is.
firstPhoto={photos?.[0]?.path ?? null}
// `undefined` while the carousel is still being read, `null` when it genuinely has none.
// The two look identical in an empty picture slot, and only one of them is a problem.
firstPhoto={photos === null ? undefined : (photos[0]?.path ?? null)}
onSaved={() => void load()}
/>
) : null,