LR-MERGE-PANO: the merge dialog makes a new preview each time it opens, also after a failed one (#2708)

The dialog remembered the options of a failed preview and never retried them, and opening it again cleared the error
but not that memory. With the same options, a reopened dialog showed neither a preview nor an error until an option
changed, so Auto Select looked stuck while picking another projection worked. Opening the dialog now forgets the
failure, and a failure only holds for the photos it was made with.
This commit is contained in:
Bruno-Alumn
2026-10-23 22:15:18 +02:00
parent 069e32f909
commit e5ea8043f6
4 changed files with 46 additions and 8 deletions
+11 -6
View File
@@ -86,6 +86,8 @@ pub struct MergeTask {
pub job: MergeJob,
/// The dialog options it was started with.
pub options: MergeDialog,
/// The photos it merges.
pub ids: Vec<PhotoId>,
pub progress: Arc<Mutex<(f32, String)>>,
pub cancel: Arc<AtomicBool>,
rx: Receiver<Result<MergeOutput, String>>,
@@ -102,8 +104,9 @@ pub struct MergeState {
pub preview: Option<(egui::TextureHandle, [usize; 2], MergeDialog, Value)>,
pub preview_pixels: Option<Arc<egui::ColorImage>>,
pub error: Option<String>,
/// Options whose preview failed (not retried until they change).
pub failed_options: Option<MergeDialog>,
/// The photos and options whose preview failed (not retried until either changes or the dialog
/// opens again).
pub failed_options: Option<(Vec<PhotoId>, MergeDialog)>,
/// The last finished merge (`{id, path, …}`).
pub last_result: Option<Value>,
/// Photos the dialog merges (the selection when it opened).
@@ -143,7 +146,7 @@ fn spawn(app: &LightcraftApp, options: &MergeDialog, ids: &[PhotoId], preview: b
std::thread::Builder::new().name("photo-merge".into()).spawn(work).map_err(|e| e.to_string())?;
#[cfg(target_arch = "wasm32")]
work();
Ok(MergeTask { preview, job, options: options.clone(), progress, cancel, rx, guard: None })
Ok(MergeTask { preview, job, options: options.clone(), ids: ids.to_vec(), progress, cancel, rx, guard: None })
}
/// The options of the last merge of kind `command` started (kept in the library preferences), or
@@ -162,6 +165,8 @@ pub fn open(app: &mut LightcraftApp, command: &str) -> Result<Value, String> {
}
app.merge.ids = ids;
app.merge.error = None;
// a preview that failed before is tried again, or the dialog would show neither a preview nor its error (#2708)
app.merge.failed_options = None;
app.merge.preview = None;
app.merge.preview_pixels = None;
if let Some(t) = app.merge.preview_task.take() {
@@ -253,7 +258,7 @@ pub fn poll(app: &mut LightcraftApp, ctx: &egui::Context) {
}
Err(e) => {
app.merge.error = Some(e);
app.merge.failed_options = Some(opts.clone());
app.merge.failed_options = Some((ids, opts.clone()));
}
}
}
@@ -285,7 +290,7 @@ pub fn poll(app: &mut LightcraftApp, ctx: &egui::Context) {
let Some(t) = app.merge.preview_task.take() else { return };
if e != "cancelled" {
app.merge.error = Some(e);
app.merge.failed_options = Some(t.options);
app.merge.failed_options = Some((t.ids, t.options));
}
}
Err(_) => ctx.request_repaint_after(std::time::Duration::from_millis(50)),
@@ -362,7 +367,7 @@ fn added_message(what: &str, info: &Value) -> String {
}
fn last_failed_matches(app: &LightcraftApp, opts: &MergeDialog) -> bool {
app.merge.failed_options.as_ref() == Some(opts)
app.merge.failed_options.as_ref().is_some_and(|(ids, o)| ids == &app.merge.ids && o == opts)
}
/// The dialog body (options on the left, preview on the right).
+25
View File
@@ -394,6 +394,31 @@ fn merge_dialog_remembers_the_last_settings_per_kind() {
let _ = std::fs::remove_dir_all(dir);
}
/// Issue #2708: a preview that failed (a photo's file missing) is tried again when the dialog opens again; it used to
/// show neither a preview nor an error until an option changed.
#[test]
fn merge_dialog_retries_a_failed_preview_when_it_opens_again() {
let (mut h, dir) = bracket("merge-retry");
let (file, away) = (dir.join("IMG_1.dng"), dir.join("IMG_1.away"));
std::fs::rename(&file, &away).unwrap();
crate::merge::open(&mut h.app, "merge.panorama").unwrap();
h.step();
assert!(h.step_until(T, |h| h.app.merge.preview_task.is_none()));
assert!(h.app.merge.error.is_some(), "the preview failed");
h.app.ui.dialog = None;
h.step();
// the file is back (a drive reconnected); the same photos and options again
std::fs::rename(&away, &file).unwrap();
crate::merge::open(&mut h.app, "merge.panorama").unwrap();
h.step();
assert!(h.app.merge.preview_task.is_some() || h.app.merge.error.is_some(), "the dialog shows a preview being made, or why not");
assert!(h.step_until(T, |h| h.app.merge.preview_task.is_none()));
assert!(h.app.merge.preview.is_some(), "{:?}", h.app.merge.error);
h.app.ui.dialog = None;
h.step();
let _ = std::fs::remove_dir_all(dir);
}
/// HDR for Each Bracket Set runs the merges one after the other, each with its activity row; cancelling one stops the
/// rest.
#[test]
+3 -2
View File
@@ -1,6 +1,6 @@
# Lightroom parity checklist
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-21 · **Change:** minor (LR-MERGE-HDR / LR-MERGE-PANO: Lanczos-3 warps; LR-IMP-LOCAL: browsing from the sidebar records the folder; LR-VIEW-ZOOM: only a full-size drag within 20 ms clears a slow-drag verdict; LR-IMP-ADD-DIALOG: hierarchical import album destination reconciled with main, #2362; KEY-SLIDER-NUDGE: vertical wheel nudges need a click or drag in the row; leaving it disarms them; LR-EDIT-OPTICS-PROFILE: Fujifilm nine-knot lens tables read at the plain radius; LR-VIEW-ZOOM: only a full-size drag within 20 ms clears a slow-drag verdict; LRC-LIB-LIBFOLDERS, LR-BEHAV-BG-TASKS: Synchronize Folder's scan shows its folder listing and stops it on ✕; LRC-LIB-LIBFOLDERS: Synchronize Folder says a folder is up to date; LR-LIB-STACK: filmstrip stack badge click; LRC-LIB-REFVIEW / CATALOG-SWITCH: Escape preserves edits; catalog-local pins; deleted and malformed references refused; LRC-LIB-COMPARE: the Info panel labels the active photo while culling, #2398; KEYC-RATING: `\` shows / hides the Filter Bar in the grids; LR-VIEW-PHOTOGRID: the grid reads only the photos an import batch or an edit touched, not the whole list; earlier: LRC-PRINT-PAGE: Print Page Options; LR-META-XMP: fewer false unimported-XMP warnings) · **Target:** Adobe Lightroom Classic 15.5 / Lightroom 9.6
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-23 · **Change:** minor (LR-MERGE-HDR / LR-MERGE-PANO: the dialog makes a new preview each time it opens, also after a failed one, #2708; LR-MERGE-HDR / LR-MERGE-PANO: Lanczos-3 warps; LR-IMP-LOCAL: browsing from the sidebar records the folder; LR-VIEW-ZOOM: only a full-size drag within 20 ms clears a slow-drag verdict; LR-IMP-ADD-DIALOG: hierarchical import album destination reconciled with main, #2362; KEY-SLIDER-NUDGE: vertical wheel nudges need a click or drag in the row; leaving it disarms them; LR-EDIT-OPTICS-PROFILE: Fujifilm nine-knot lens tables read at the plain radius; LR-VIEW-ZOOM: only a full-size drag within 20 ms clears a slow-drag verdict; LRC-LIB-LIBFOLDERS, LR-BEHAV-BG-TASKS: Synchronize Folder's scan shows its folder listing and stops it on ✕; LRC-LIB-LIBFOLDERS: Synchronize Folder says a folder is up to date; LR-LIB-STACK: filmstrip stack badge click; LRC-LIB-REFVIEW / CATALOG-SWITCH: Escape preserves edits; catalog-local pins; deleted and malformed references refused; LRC-LIB-COMPARE: the Info panel labels the active photo while culling, #2398; KEYC-RATING: `\` shows / hides the Filter Bar in the grids; LR-VIEW-PHOTOGRID: the grid reads only the photos an import batch or an edit touched, not the whole list; earlier: LRC-PRINT-PAGE: Print Page Options; LR-META-XMP: fewer false unimported-XMP warnings) · **Target:** Adobe Lightroom Classic 15.5 / Lightroom 9.6
The measured, row-by-row checklist: one row per Lightroom feature, menu item and shortcut, what we
have and what is missing. It answers *does the feature exist?* (feature breadth). Whether it is good
@@ -353,7 +353,7 @@ differences and missing bindings are in [ui-parity.md](ui-parity.md#shortcuts).
| Id | Feature | Tier | Status | Evidence | Missing / notes |
|---|---|---|---|---|---|
| LR-MERGE-HDR | HDR merge | P2 | 🟡 | `cmd:merge.hdr`, `cmd:dialog.mergeHdr`, `crates/merge/src/frame.rs` (`Frame::lens`), `crates/merge/src/hdr.rs` | auto align (Lanczos-3 warp), deghost None–High + overlay, auto settings, float DNG, Create Stack; the DNG keeps the reference frame's embedded lens corrections (tested by `hdr_merge_keeps_the_reference_frames_lens_corrections`); raws without colour matrices of their own (CR2, NEF, ARW…) keep the colour they open with, written as DNG tags (`starting_colour_tags`; the look's chroma curve has no tag; panoramas too), issue #965; the DNG's `AsShotNeutral` is the median of the inputs' vendor white-balance multipliers, issue #1144. Tested on real Sony A7R VI brackets (67 MP, 2026-10-14): frames agree with the merged radiance within ~1 % in all channels, exposure steps match EXIF within 0.03 EV. A clipped highlight of the reference that moved in other frames (a star at 200 mm) stays instead of becoming a black hole, and deghost (≤ 2048 px maps) catches a brighter frame's ghost in a dark sky (`a_clipped_star_of_the_reference_is_not_erased_by_frames_where_it_moved`, `deghost_removes_a_bright_frames_ghost_in_a_dark_sky`). 🟡 because: AI Denoise doesn't run on merged DNGs (Bayer only; Lightroom's does), JPEG brackets are treated as linear (no camera response curve), and every frame is held as 32-bit float (~0.86 GB per 67 MP frame, so 7 frames need ~7 GB); gap 29 |
| LR-MERGE-PANO | Panorama | P2 | ✅ | `cmd:merge.panorama`, `cmd:dialog.mergePanorama` | spherical/cylindrical/perspective + auto (projected with Lanczos-3), boundary warp, auto crop, fill edges (diffusion), DNG whose `AsShotNeutral` is the median of its frames' as-shot white balance (vendor multipliers for CR2, NEF, ARW…), issue #1144; no lens model / 360° wrap |
| LR-MERGE-PANO | Panorama | P2 | ✅ | `cmd:merge.panorama`, `cmd:dialog.mergePanorama` | spherical/cylindrical/perspective + auto (projected with Lanczos-3), boundary warp, auto crop, fill edges (diffusion), DNG whose `AsShotNeutral` is the median of its frames' as-shot white balance (vendor multipliers for CR2, NEF, ARW…), issue #1144; the dialog makes a new preview each time it opens, also after a failed one (issue #2708, `merge_dialog_retries_a_failed_preview_when_it_opens_again`); no lens model / 360° wrap |
| LR-MERGE-HDRPANO | HDR panorama | P2 | ✅ | `cmd:merge.hdrPanorama`, `cmd:dialog.mergeHdrPanorama` | brackets grouped by EXIF |
| LR-MERGE-HEADLESS | Merge with last settings | P2 | ✅ | `cmd:merge.hdrLast`, `cmd:merge.panoramaLast`, `cmd:merge.hdrPanoramaLast`, `cmd:merge.hdrBatch`, `crates/catalog/src/stacks.rs` (`bracket_sets`) | no dialog; the options of the last merge of that kind (defaults the first time), kept in the library's prefs.json so they survive a restart; the HDR / Panorama / HDR Panorama dialogs open with them too (`merge_dialog_remembers_the_last_settings_per_kind`). HDR for Each Bracket Set merges a batch like Lightroom's merge of stacks: one HDR per stack among the photos, then per bracket (photos taken less than a second apart, after the shutter time, at exposures ≥ ½ EV apart; bursts at one exposure are skipped), one after the other in the activity stack (cancelling one stops the rest; a failed set doesn't); CLI `merge hdr-batch FILES…` |
@@ -798,6 +798,7 @@ Latin stays upright. Advanced Japanese composition remains open. Regression cove
| Date | Change | Summary |
|---|---|---|
| 2026-10-23 | patch | LR-MERGE-HDR / LR-MERGE-PANO: the merge dialog makes a new preview each time it opens, also after a failed one (#2708); status unchanged |
| 2026-10-21 | patch | LR-IMP-ADD-DIALOG: reconcile #2362 with current main; deleted-album guard and hierarchical picker retained; counts unchanged |
| 2026-10-20 | minor | LR-IMP-ADD-DIALOG: #2362 gives the import review the shared hierarchy/search album picker while retaining None/New album and existing destination IDs; status and feature counts unchanged |
| 2026-10-21 | patch | KEY-SLIDER-NUDGE: vertical wheel nudges need a click or drag in the slider's row; leaving it (even between two frames) or Tab focus alone does not arm them (issue #1355); status unchanged |
+7
View File
@@ -1,5 +1,12 @@
# What's new in LightCraft
## 2026-10-23
### Merge previews after a failed one
- The HDR, Panorama and HDR Panorama dialogs make a new preview each time they open (issue #2708). After a preview
had failed once, for example because a photo's file was missing, opening the dialog again with the same options
showed neither a preview nor an error until an option was changed.
## 2026-10-21
### Sharper resampling in HDR and panorama merges