docs/cr3.md gains a variant-status table: every supported and rejected CRX
coding variant, the function that gates it and the exact Unsupported text.
crates/raw/tests/cr3_variants.rs enforces the rejections (19 tests). Each
rejected variant is rebuilt as a small synthetic CR3 (container, CMP1
descriptor, CRX stream) and the test asserts the exact string, and that
probe_info (the import path) and decode return the same error. Control
fixtures decode through the same container, including both edges of the
accepted uniform quantization range (4..=44) and adaptive QP range
(131..=168), so a rejection can only come from the documented gate. The
table itself is prose and is not checked against the tests.
The doc said only QP values 131-167 were accepted; the decoder also accepts
168 (quantization value 44, step 102), so that sentence now says so.
Tests and docs only; no decoder change.
A double-click on a slider's name or thumb already reset it, but nothing said so: only the value had a
tooltip. Resting the pointer on the name or on the thumb now shows "Exposure — double-click to reset
it", the wording (and translations) the curve channels already use. The rest of the track, which is
clicked and dragged, gets no tooltip, and the value keeps "Click to type a value".
The first click of a double-click on the thumb moved the value to where the pointer landed, a few
points off its centre: one history step, then the reset another. A click on the thumb now leaves the
value alone, so the reset is one undo step, as from the name. A disabled slider's name no longer
resets it.
No per-slider reset button: Lightroom has none, and the double-click it documents is now shown where
it works. Tests: tests_slider_reset.rs (name and thumb reset Exposure and Contrast in one undo step,
the hints, a click on the thumb keeps the value, typing a value still works) and
widgets/tests_slider_reset.rs (a disabled slider's name).
The Remove tool's spot pins (Remove / Content-Aware / Heal / Clone) and the
mask pins now follow a Tool Overlay mode, saved with the UI state:
- Auto (default): shown while the pointer is over the photo (or a pin is
dragged), hidden while a new spot or brush stroke is painted
- Always: all of them
- Selected: only the selected spot's or mask's
- Never: none; the tool still works and a click paints a new spot
A hidden spot can't be grabbed. Mask pins also need Show Pins
(view.maskPins), which keeps its meaning and reply.
Command view.toolOverlay {mode?: auto|always|selected|never} (no mode steps
to the next), Shift+H (H stays Remove), View > Tool Overlay with the modes
and the cycle item, and a Tool Overlay dropdown in the Remove and Masking
panels. Headless tests cover each mode with the pointer over and away from
the photo, grabbing, the key, the command, the menu, the dropdown, mask pins
with Show Pins, and the ui.json round trip.
In Compare a filmstrip click goes to `compare.set` (#747), so the grid's
modifier clicks never reached `library.select` and multi-select from the
filmstrip was impossible there (#187 follow-up). A ⌘/Ctrl-click now toggles
the photo and a Shift-click extends from the active photo, as in the grid;
the two photos shown stay selected and the active photo is unchanged, so a
rating key still acts on a photo that is on screen. A plain click still
replaces the active side.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NH3itd85jPuM6wNjgcGfEH
The grid queues no render behind an unedited raw's quick thumbnail, since the embedded JPEG is
meant to be the thumbnail. But whether that JPEG can be read is only known when the quick job
runs, and `quick_thumb_job` had no fallback: when the preview loader returned nothing the job
failed with "no quick preview", the failure was remembered for that key, and the cell stayed
empty until an edit changed it. The quick job now carries the real thumbnail job as its last
resort, as `quick_view_job` already does for the loupe.
Four CC0 EOS RP samples from raw.pixls.us (lossless RAW and C-RAW, full
frame and 1.6x crop mode) now have exact full-sensor regressions against
the DNGLab 0.8.0 black-box oracle (Windows binary, checked first against
the documented M50 RAW hash). The decoder already matched; no decoder
change was needed. Crop mode is a single 4032x2656 tile, full frame two
3192-wide tiles.
docs/cr3.md also records that the white level is Canon's specular white,
which at the -1/3-stop ISOs (e.g. 11062 at ISO 5000) is well below the
sensor's saturation point.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Conflicts: the 9 locale *-formats.json catalogs (both sides appended
messages at the end; kept both) and docs/parity-checklist.md (header
change list, regenerated summary via `cargo xtask parity --write`,
revision history rows from both sides).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#1123 gives HDR and panorama merges of raws without colour matrices the colour they open with, but writes
ColorMatrix1 from the forward matrix, so the as-shot white the DNG declares lands wherever the fitted look and the
generic camera model take it. On real ILCE-7M3 brackets the HDRs opened at As Shot 6113–7307 K / +115 (Duv +0.038)
where the ARWs show 3932–5268 K / +7, and any white-balance edit turned them green (CIEDE2000 11–13 against the ARW
at the same Temp/Tint). The render at As Shot was right (0.4–0.8).
The colour matrices now declare the white balance the source shows, decided by files::WbScaleOf (new dng_white):
the camera's measured matrices when they give the source's scale (lightcraft_raw::spectral; the DNG then reads every
white as the source does), else a matrix under which the as-shot neutral is the white the scale shows (Sony's
presets' reading, 6500 K / 0 on the relative scale, the file's own white otherwise). Forward matrix and neutral are
unchanged, so is the render at As Shot.
Measured on the same brackets with this change on a fork build: As Shot 4813 / 5268 / 3932 K, +8 / +8 / +7 as the
ARWs (declared white Duv +0.003); same Temp/Tint within CIEDE2000 0.8–1.7 of the ARW for most changes, about 5 for
the largest (5268 → 3200 K, 3932 → 6500 K: the ARW adapts its look with Bradford, the DNG rebalances in camera space).
Tests: a merged DNG shows its source's As Shot on all four scales (measured matrices, presets, relative, own); with a
look that tints white the DNG declares the shown white, gives the measured model's multipliers at other whites and
renders at As Shot as the source does. Both fail on main (ILCE-7M3 case: (7342, 115) for (5268, 8)).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015gRtdWF1E9358FfzhzYqCS
An unedited raw's face tile is cut from its largest embedded preview, read
the planned way (header and JPEG, never the sensor data), when the preview
holds at least 3/4 of the tile's pixels across the face; otherwise, and for
edited photos and other files, it is developed as before. Tiles keep going
to the memory + on-disk thumbnail cache, now at 16 px size steps so the
thumbnail slider reuses them. The planned preview path carries an Adobe RGB
preview colour space and applies it instead of declining to the whole-file
read (Sony alpha 1 previews).
As in PhotoCraft: when portable.txt (or LightCraft.portable) sits next to the
executable, config_dir() is <exe dir>/LightCraftData, so settings, logs and
models stay with a portable copy instead of %APPDATA%. LIGHTCRAFT_CONFIG_DIR
still wins and is used without probing beside the executable. A portable
folder that can't be written logs a warning and the platform folder is used.
The check runs once per process.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Conflicts: crates/engine/src/tests_merge.rs (#1123's test landed where this branch's is; both kept),
docs/parity-checklist.md (the LR-MERGE-HDR row: both notes) and docs/whats-new.md (both entries).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015gRtdWF1E9358FfzhzYqCS
Open the configured SAM directory itself and preserve the pending mask
action and existing files when folder creation or the opener fails.
Run the Linux desktop opener in a background task and deliver failures
to the UI for retry. Directory creation remains synchronous.
Cover configured paths, creation and opener failures, retry state and
worker completion.
Partially addresses #366; model download hosting remains unresolved.
Credit: sanyamsingh001
Co-authored-by: Codex <codex@openai.com>