Review fixes for the saved Upright import.
- The saved matrix is placed when the sidecar is merged, in the frame the file's orientation and
then the photo's own Rotate Left / Right give (Orientation::then). Read Metadata after a
rotation no longer keeps it in the old frame (up to 21 px off on DSC_1457). Rotate Left / Right
turns a stored Upright correction with the photo (upright::turned), LightCraft's own as well.
- Sidecar crops go through the Lightroom catalog import's conversion (catalog_crop, moved to
crs::crop_in_frame unchanged) whenever the file's orientation is known. A portrait raw's crop
used to cut the wrong side (DSC_1486 rendered 2548 x 4812 for Lightroom's 3200 x 3808; now
0.12 px mean from Lightroom's export after the NEF frame inset), and straightened crops from a
real catalog's settings came out 8459 x 3827 for Lightroom's 7725 x 5150. A straightened crop
on top of a saved Upright isn't measured: the mode is solved again, with the warning.
- Lightroom catalogs don't read the saved Upright until a real catalog verifies its encoding and
frame: the mode is solved again and the import reports it.
- Guided Upright guides are reported whether or not automatic matrices are present.
The new regressions fail on the previous commit and pass here.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Lightroom saves one Upright matrix per mode (crs:UprightTransform_0 to _5). LightCraft read
only the mode and solved the photo again, so on public references its framing was 23 to 78 px
away from Lightroom's export on average. The selected mode's matrix is now kept where the
photo's frame is known (photo import, Read Metadata from File, Lightroom catalog import) and
turned from Lightroom's stored-image frame into LightCraft's oriented, centred one.
The convention was measured on Lightroom Classic 12 exports of public NEFs (one shown in
portrait, one cropped with Constrain Crop): a forward homography on column vectors, normalised
to the stored image (y down, before the EXIF orientation), applied before the crop, with the
fill scale in it. LightCraft's render mapping with the kept matrix matches those exports to
0.11 to 0.54 px on average and under 1 px at worst; end-to-end renders match to 0.08 to 0.50 px.
PerspectiveUpright 2, 3 and 4 now read as Full, Level and Vertical (they were read as Level,
Vertical and Full), and a stated mode no longer keeps a transform solved for another one.
Guided, Upright versions other than 151388160 and unusable matrices are solved as before, with
the import warning. Upright Off no longer warns, and Lightroom's Upright bookkeeping fields are
no longer reported as unimported adjustments.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On merge: the HDR histogram is applied in develop_reporting (#712 split develop), is skipped for
draft renders (a second render per drag tick), and HdrBins::bin clamps a hand-built split so it
can't underflow (test hdr_bins_with_any_split_stay_in_range).
photo.editExternal takes the selection (or ids, at most 50): each photo gets its own -Edit copy
stacked on its original, the copies are selected and returned in copies (path / id / original
still name the first). Photo ▸ Edit In opens each copy in the editor.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BKWB9TCnvzJHV6xMLAj2J4
On merge: Lightroom's default profiles (Adobe Standard, Adobe Color), named by nearly every sidecar,
give no camera-profile warning; other profiles still do (test default_lightroom_profiles_are_not_reported).
The import help string keeps main's onDeleted wording and gains warnings.
On merge: the quick preview path orients like the whole-file path (preview_orientation with the
raw's header prefix, so a portrait raw whose embedded JPEG carries no orientation is not sideways),
and its doc links point at preview_window.
Turning on AI Denoise without a model opens Settings > AI Denoise and
keeps a pending request, so the switch showed checked and Settings
offered "Cancel pending action". Closing the setup with no model
download or installation running now drops that request: the switch
goes back off and nothing claims to be pending. A download started
before closing still turns AI Denoise on when it finishes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BKWB9TCnvzJHV6xMLAj2J4
File > Import Profiles & Presets... now builds one toast for both halves
(a second toast replaced the first, so a .cube-only import said "No new
presets"). The preset half is skipped when no preset files were picked,
and profile.import's failures are shown with their reason. profile.import
also reports .cube files and zips it couldn't read instead of dropping
them silently. The command's result carries the profile half under
"profiles".
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BKWB9TCnvzJHV6xMLAj2J4
On merge: metadata.downloadPlaceNames accepts plain http only for the loopback address itself
(http://127.0.0.1.example.com/ and http://127.0.0.1@host/ are refused; test
download_base_is_https_or_loopback_only).