From 4b57d5dc5a111f761e5a7abf0e1ae6cf6baebe3f Mon Sep 17 00:00:00 2001 From: Daniel Voshart <205798369+voshart@users.noreply.github.com> Date: Wed, 7 Oct 2026 13:54:52 -0400 Subject: [PATCH 01/41] Fetch: a bearer token is sent only when the caller names its variable Options::token_env (default none) replaces the hard-coded LIGHTCRAFT_SAM3_TOKEN, so a download of one model can never send another model's token to its host. SAM 3 names its variable through lightcraft_segment::fetch::options(). The decision (non-empty, https, the host the download began at) is a small function with its own test. Co-Authored-By: Claude Sonnet 5.5 --- crates/engine/src/segment/mod.rs | 2 +- crates/fetch/src/lib.rs | 19 ++++++++++++------- crates/fetch/src/tests.rs | 17 ++++++++++++++++- crates/segment/src/fetch/mod.rs | 8 ++++++++ 4 files changed, 37 insertions(+), 9 deletions(-) diff --git a/crates/engine/src/segment/mod.rs b/crates/engine/src/segment/mod.rs index 3cf14712..75e3d11b 100644 --- a/crates/engine/src/segment/mod.rs +++ b/crates/engine/src/segment/mod.rs @@ -262,7 +262,7 @@ impl Segmenter { if mirrors.is_empty() { return Err(lightcraft_segment::fetch::no_mirrors_message()); } - self.download.start(lightcraft_segment::fetch::SAM3_FILES, mirrors, dir, lightcraft_segment::fetch::Options::default()) + self.download.start(lightcraft_segment::fetch::SAM3_FILES, mirrors, dir, lightcraft_segment::fetch::options()) } #[cfg(not(feature = "sam"))] { diff --git a/crates/fetch/src/lib.rs b/crates/fetch/src/lib.rs index 499a6eda..12ad639c 100644 --- a/crates/fetch/src/lib.rs +++ b/crates/fetch/src/lib.rs @@ -79,11 +79,14 @@ pub struct Options { pub stall_timeout: Duration, /// Tries per mirror and file (each resumes where the last one stopped). pub attempts: u32, + /// The environment variable holding a bearer token for the first mirror's host (sent only over https, never to a + /// host a redirect leads to). `None`: nothing is ever sent, so one model's token cannot leak to another's server. + pub token_env: Option<&'static str>, } impl Default for Options { fn default() -> Self { - Options { connect_timeout: Duration::from_secs(15), stall_timeout: Duration::from_secs(30), attempts: 3 } + Options { connect_timeout: Duration::from_secs(15), stall_timeout: Duration::from_secs(30), attempts: 3, token_env: None } } } @@ -203,6 +206,12 @@ pub fn download( Ok(()) } +/// The bearer token to send to `url`: only a non-empty one, only over https, and only to the host the download began +/// at (`origin`), never to the host a redirect leads to. +fn bearer<'a>(token: Option<&'a str>, url: &Url, origin: &str) -> Option<&'a str> { + token.map(str::trim).filter(|t| !t.is_empty() && url.tls && url.host == origin) +} + /// A mirror for messages (no query string: it may hold a token). fn short(m: &str) -> String { Url::parse(m).map(|u| u.display()).unwrap_or_else(|_| "mirror".into()) @@ -268,12 +277,8 @@ fn fetch_file(f: &FileSpec, url: &str, dir: &Path, opts: &Options, cancel: &Atom headers.push(("Range", format!("bytes={have}-"))); } // a token for the configured host only (never sent on to a redirect's host) - if let Ok(t) = std::env::var("LIGHTCRAFT_SAM3_TOKEN") - && !t.trim().is_empty() - && url.tls - && url.host == origin - { - headers.push(("Authorization", format!("Bearer {}", t.trim()))); + if let Some(t) = bearer(opts.token_env.and_then(|name| std::env::var(name).ok()).as_deref(), &url, &origin) { + headers.push(("Authorization", format!("Bearer {t}"))); } let r = http::get(&url, &headers, &limits).map_err(http_fail)?; match r.status { diff --git a/crates/fetch/src/tests.rs b/crates/fetch/src/tests.rs index 57c168a2..ad3f1063 100644 --- a/crates/fetch/src/tests.rs +++ b/crates/fetch/src/tests.rs @@ -137,7 +137,7 @@ fn tmp(name: &str) -> PathBuf { } fn quick() -> Options { - Options { connect_timeout: Duration::from_secs(2), stall_timeout: Duration::from_millis(600), attempts: 2 } + Options { connect_timeout: Duration::from_secs(2), stall_timeout: Duration::from_millis(600), attempts: 2, token_env: None } } fn run(files: &[FileSpec], mirrors: &[String], dir: &Path, cancel: &AtomicBool) -> (Result<(), DownloadError>, Vec) { @@ -314,3 +314,18 @@ fn content_ranges() { assert_eq!(parse_content_range("bytes 9-1/300"), None); assert_eq!(parse_content_range("items 1-2/3"), None); } + +/// A bearer token goes only to https, only to the host the download began at, and only when there is one. +#[test] +fn a_token_is_sent_only_over_https_to_the_first_host() { + let u = |s: &str| Url::parse(s).unwrap(); + let first = u("https://huggingface.co/x/model.bin"); + assert_eq!(bearer(Some(" hf_abc \n"), &first, "huggingface.co"), Some("hf_abc")); + // the storage host a redirect leads to, plain http, no token, an empty one + assert_eq!(bearer(Some("hf_abc"), &u("https://cdn-lfs.example/x"), "huggingface.co"), None); + assert_eq!(bearer(Some("hf_abc"), &u("http://huggingface.co/x"), "huggingface.co"), None); + assert_eq!(bearer(None, &first, "huggingface.co"), None); + assert_eq!(bearer(Some(" "), &first, "huggingface.co"), None); + // a caller that names no variable sends nothing, whatever the environment holds + assert!(Options::default().token_env.is_none()); +} diff --git a/crates/segment/src/fetch/mod.rs b/crates/segment/src/fetch/mod.rs index 55c083cc..35f2d2eb 100644 --- a/crates/segment/src/fetch/mod.rs +++ b/crates/segment/src/fetch/mod.rs @@ -51,6 +51,14 @@ pub fn mirrors(env: Option<&str>, file: Option<&Path>) -> Vec { lightcraft_fetch::mirrors(env, file, DEFAULT_MIRRORS) } +/// Download settings for SAM 3: the defaults, plus [`TOKEN_ENV`] for a gated mirror. +pub fn options() -> Options { + Options { token_env: Some(TOKEN_ENV), ..Options::default() } +} + +/// Environment variable with a bearer token for the SAM 3 mirror's own host (a gated Hugging Face repository). +pub const TOKEN_ENV: &str = "LIGHTCRAFT_SAM3_TOKEN"; + /// What to tell the user when there is no mirror for the SAM 3 model. pub fn no_mirrors_message() -> String { format!( From 8dd3afce51d27d2625f8e094e6dd11b713cede01 Mon Sep 17 00:00:00 2001 From: Daniel Voshart <205798369+voshart@users.noreply.github.com> Date: Wed, 7 Oct 2026 22:59:22 -0400 Subject: [PATCH 02/41] Fetch: validate caller-supplied names, sizes and hashes; FileSpec borrows its strings; download_url for an exact address The fetch crate is about to serve downloads whose file names, sizes and addresses come from a user-editable list, so it checks them before touching the disk: an ordinary, unique file name (no separators, drive or stream syntax, control characters, trailing dots, Windows device names or .part suffix), a non-zero size within the caller's bound, a well-formed SHA-256, bounded file, mirror and attempt counts, and URLs capped at 16 KiB. FileSpec now borrows its strings (callers with static names are unchanged) so a name read from a file needs no leaking. download_url fetches one file from an exact address, query string included, for signed links. Co-Authored-By: Claude Opus 5.5 --- crates/fetch/README.md | 7 +++ crates/fetch/src/http.rs | 11 ++++- crates/fetch/src/lib.rs | 94 +++++++++++++++++++++++++++++++++------ crates/fetch/src/tests.rs | 61 ++++++++++++++++++++++++- 4 files changed, 157 insertions(+), 16 deletions(-) create mode 100644 crates/fetch/README.md diff --git a/crates/fetch/README.md b/crates/fetch/README.md new file mode 100644 index 00000000..60ac1d32 --- /dev/null +++ b/crates/fetch/README.md @@ -0,0 +1,7 @@ +# lightcraft-fetch + +Native model downloads in pure Rust (L0, Apache-2.0): HTTP/1.1 over std sockets and rustls with the RustCrypto provider, without ring, aws-lc, C or assembly build dependencies. The wasm32 crate is empty. + +Call `download` with bounded file specifications and ordered mirrors, or `download_url` for an exact catalog URL (including its query). Each file has a local filename independent of the URL, a size cap and optional pinned size/SHA-256. Transfers resume pinned `.part` files, verify before renaming, report progress and check cancellation during reads. Run on a worker thread. No proxy support; DNS resolution uses the platform resolver. Tokens are off by default and never forwarded to a different redirect host. + +Tests use local synthetic HTTP servers for redirects, resume, truncation, cancellation, stalls, hashes, size limits and hostile specifications; they make no internet requests. diff --git a/crates/fetch/src/http.rs b/crates/fetch/src/http.rs index 332f0147..e900850a 100644 --- a/crates/fetch/src/http.rs +++ b/crates/fetch/src/http.rs @@ -60,7 +60,10 @@ pub struct Url { impl Url { pub fn parse(s: &str) -> Result { - let bad = |why: &str| HttpError::BadUrl(format!("{s}: {why}")); + let bad = |why: &str| HttpError::BadUrl(why.to_string()); + if s.len() > 16 * 1024 { + return Err(bad("URL exceeds 16 KiB")); + } let s = s.trim(); if s.chars().any(|c| c.is_control() || c == ' ') { return Err(bad("contains spaces or control characters")); @@ -509,6 +512,12 @@ pub fn get(url: &Url, headers: &[(&str, String)], limits: &Limits) -> Result { + pub name: &'a str, /// Exact size in bytes, when pinned (then the download resumes and is checked against it). pub size: Option, /// SHA-256 (lower-case hex), when pinned. - pub sha256: Option<&'static str>, + pub sha256: Option<&'a str>, /// Largest size accepted when `size` is not pinned. pub max: u64, } @@ -106,6 +106,8 @@ pub struct Progress { pub enum DownloadError { /// No mirror is configured. NoMirrors, + /// Invalid filename, size, hash or aggregate bound supplied by the caller. + Invalid(String), Cancelled, /// A local file problem (folder not writable, disk full…). Disk(String), @@ -119,6 +121,7 @@ pub enum DownloadError { impl std::fmt::Display for DownloadError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { + DownloadError::Invalid(e) => write!(f, "invalid download: {e}"), DownloadError::NoMirrors => write!(f, "no download location is configured for this model in this build"), DownloadError::Cancelled => write!(f, "the download was cancelled"), DownloadError::Disk(e) => write!(f, "{e}"), @@ -135,16 +138,45 @@ enum Fail { Disk(String), } -/// Download `files` into `dir` from `mirrors` (in order; see [`mirrors`]). Files already there -/// and verified are kept. `progress` is called as bytes arrive (often: throttle in it). +/// Fetch a single file from its exact URL, preserving any query string. The local filename +/// remains `file.name`; it never comes from redirects or response headers. +pub fn download_url( + file: &FileSpec<'_>, + url: &str, + dir: &Path, + opts: &Options, + cancel: &AtomicBool, + progress: &mut dyn FnMut(&Progress), +) -> Result<(), DownloadError> { + download_source(std::slice::from_ref(file), &[url.to_string()], dir, opts, cancel, progress, true) +} + +/// Download `files` into `dir` from `mirrors` (in order; see [`mirrors`]). Verified files +/// are kept. `progress` is called as bytes arrive (often: throttle in it). pub fn download( - files: &[FileSpec], + files: &[FileSpec<'_>], mirrors: &[String], dir: &Path, opts: &Options, cancel: &AtomicBool, progress: &mut dyn FnMut(&Progress), ) -> Result<(), DownloadError> { + download_source(files, mirrors, dir, opts, cancel, progress, false) +} + +fn download_source( + files: &[FileSpec<'_>], + mirrors: &[String], + dir: &Path, + opts: &Options, + cancel: &AtomicBool, + progress: &mut dyn FnMut(&Progress), + exact_urls: bool, +) -> Result<(), DownloadError> { + let total = validate_files(files)?; + if mirrors.len() > MAX_MIRRORS { + return Err(DownloadError::Invalid("too many mirrors".into())); + } std::fs::create_dir_all(dir).map_err(|e| DownloadError::Disk(format!("can't create {}: {e}", dir.display())))?; // what's left to fetch let mut todo = Vec::new(); @@ -159,14 +191,13 @@ pub fn download( if mirrors.is_empty() { return Err(DownloadError::NoMirrors); } - let total: u64 = files.iter().map(|f| f.size.unwrap_or(0)).sum(); let mut before: u64 = files.iter().filter(|f| !todo.contains(f)).filter_map(|f| f.size).sum(); for f in todo { let mut errors = Vec::new(); let mut ok = false; 'mirrors: for m in mirrors { - let url = format!("{m}/{}", f.name); - for _ in 0..opts.attempts.max(1) { + let url = if exact_urls { m.clone() } else { format!("{m}/{}", f.name) }; + for _ in 0..opts.attempts.clamp(1, 16) { if cancel.load(Ordering::Relaxed) { return Err(DownloadError::Cancelled); } @@ -187,11 +218,11 @@ pub fn download( Err(Fail::Cancelled) => return Err(DownloadError::Cancelled), Err(Fail::Disk(e)) => return Err(DownloadError::Disk(e)), Err(Fail::Retry(e)) => { - log::warn!("download of {url}: {e} (retrying)"); + log::warn!("download of {}: {e} (retrying)", short(&url)); errors.push(format!("{}: {e}", short(m))); } Err(Fail::NextMirror(e)) => { - log::warn!("download of {url}: {e}"); + log::warn!("download of {}: {e}", short(&url)); errors.push(format!("{}: {e}", short(m))); continue 'mirrors; } @@ -206,6 +237,43 @@ pub fn download( Ok(()) } +/// Validate before touching disk, including names supplied by a user model catalog. +fn validate_files(files: &[FileSpec<'_>]) -> Result { + if files.len() > 256 { + return Err(DownloadError::Invalid("too many files".into())); + } + let mut seen = std::collections::HashSet::new(); + let mut total = 0u64; + for f in files { + let stem = f.name.split('.').next().unwrap_or_default().to_ascii_uppercase(); + let reserved = matches!(stem.as_str(), "CON" | "PRN" | "AUX" | "NUL") + || stem + .strip_prefix("COM") + .or_else(|| stem.strip_prefix("LPT")) + .is_some_and(|n| matches!(n, "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9")); + if reserved + || f.name.ends_with(['.', ' ']) + || f.name.ends_with(".part") + || f.name.is_empty() + || f.name.len() > 255 + || f.name.starts_with('.') + || f.name.contains(['/', '\\', ':']) + || f.name.chars().any(char::is_control) + || !seen.insert(f.name) + { + return Err(DownloadError::Invalid("a filename must be a unique, ordinary filename".into())); + } + if f.max == 0 + || f.size.is_some_and(|n| n == 0 || n > f.max) + || f.sha256.is_some_and(|h| h.len() != 64 || !h.bytes().all(|b| b.is_ascii_digit() || (b'a'..=b'f').contains(&b))) + { + return Err(DownloadError::Invalid("invalid file size or SHA-256".into())); + } + total = total.checked_add(f.size.unwrap_or(0)).ok_or_else(|| DownloadError::Invalid("total size overflow".into()))?; + } + Ok(total) +} + /// The bearer token to send to `url`: only a non-empty one, only over https, and only to the host the download began /// at (`origin`), never to the host a redirect leads to. fn bearer<'a>(token: Option<&'a str>, url: &Url, origin: &str) -> Option<&'a str> { @@ -218,7 +286,7 @@ fn short(m: &str) -> String { } /// Whether `path` is already the right file (exact size and hash when pinned). -fn verified(f: &FileSpec, path: &Path) -> Result { +fn verified(f: &FileSpec<'_>, path: &Path) -> Result { let Ok(meta) = std::fs::metadata(path) else { return Ok(false) }; if !meta.is_file() || meta.len() == 0 { return Ok(false); @@ -252,7 +320,7 @@ fn part_path(dir: &Path, name: &str) -> PathBuf { /// Fetch one file from one mirror into `.part` (resuming it when its size is pinned), /// check it and move it into place. -fn fetch_file(f: &FileSpec, url: &str, dir: &Path, opts: &Options, cancel: &AtomicBool, report: &mut dyn FnMut(u64, u64)) -> Result<(), Fail> { +fn fetch_file(f: &FileSpec<'_>, url: &str, dir: &Path, opts: &Options, cancel: &AtomicBool, report: &mut dyn FnMut(u64, u64)) -> Result<(), Fail> { let part = part_path(dir, f.name); let limit = f.size.unwrap_or(f.max); let disk = |e: std::io::Error| Fail::Disk(format!("{}: {e}", part.display())); diff --git a/crates/fetch/src/tests.rs b/crates/fetch/src/tests.rs index ad3f1063..4a828c6d 100644 --- a/crates/fetch/src/tests.rs +++ b/crates/fetch/src/tests.rs @@ -22,7 +22,7 @@ fn leak(s: String) -> &'static str { Box::leak(s.into_boxed_str()) } -fn pinned() -> FileSpec { +fn pinned() -> FileSpec<'static> { let p = payload(); FileSpec { name: "model.bin", size: Some(p.len() as u64), sha256: Some(leak(sha_hex(&p))), max: p.len() as u64 } } @@ -140,7 +140,7 @@ fn quick() -> Options { Options { connect_timeout: Duration::from_secs(2), stall_timeout: Duration::from_millis(600), attempts: 2, token_env: None } } -fn run(files: &[FileSpec], mirrors: &[String], dir: &Path, cancel: &AtomicBool) -> (Result<(), DownloadError>, Vec) { +fn run(files: &[FileSpec<'_>], mirrors: &[String], dir: &Path, cancel: &AtomicBool) -> (Result<(), DownloadError>, Vec) { let mut seen = Vec::new(); let r = download(files, mirrors, dir, &quick(), cancel, &mut |p| seen.push(p.clone())); (r, seen) @@ -329,3 +329,60 @@ fn a_token_is_sent_only_over_https_to_the_first_host() { // a caller that names no variable sends nothing, whatever the environment holds assert!(Options::default().token_env.is_none()); } + +#[test] +fn exact_url_keeps_query_and_owned_catalog_values() { + let listener = TcpListener::bind("127.0.0.1:0").unwrap(); + let url = format!("http://{}/download?file=weights&sig=secret", listener.local_addr().unwrap()); + let worker = std::thread::spawn(move || { + let (mut socket, _) = listener.accept().unwrap(); + let mut reader = BufReader::new(socket.try_clone().unwrap()); + let mut request = String::new(); + reader.read_line(&mut request).unwrap(); + assert_eq!(request.trim(), "GET /download?file=weights&sig=secret HTTP/1.1"); + loop { + let mut line = String::new(); + if reader.read_line(&mut line).unwrap() == 0 || line.trim().is_empty() { + break; + } + } + socket.write_all(b"HTTP/1.1 200 OK\r\nContent-Length: 3\r\nConnection: close\r\n\r\nabc").unwrap(); + }); + let name = String::from("catalog.onnx"); + let hash = sha_hex(b"abc"); + let spec = FileSpec { name: &name, size: Some(3), sha256: Some(&hash), max: 3 }; + let dir = tmp("exact"); + let mut progress = Vec::new(); + download_url(&spec, &url, &dir, &quick(), &AtomicBool::new(false), &mut |p| progress.push(p.clone())).unwrap(); + worker.join().unwrap(); + assert_eq!(std::fs::read(dir.join(name)).unwrap(), b"abc"); + assert!(progress.iter().all(|p| !p.mirror.contains("secret"))); + let _ = std::fs::remove_dir_all(dir); +} + +#[test] +fn hostile_file_specs_are_rejected_before_touching_disk() { + let dir = tmp("invalid"); + let base = FileSpec { name: "valid.onnx", size: Some(1), sha256: None, max: 1 }; + let cancel = AtomicBool::new(false); + for name in ["", ".hidden", "../escape", "a/b", "a\\b", "C:escape", "bad\nname", "trailing.", "CON.onnx", "a.part"] { + let spec = FileSpec { name, ..base.clone() }; + assert!(matches!(run(&[spec], &[], &dir, &cancel).0, Err(DownloadError::Invalid(_))), "{name}"); + assert!(!dir.exists()); + } + for spec in [ + FileSpec { max: 0, ..base.clone() }, + FileSpec { size: Some(0), ..base.clone() }, + FileSpec { size: Some(2), ..base.clone() }, + FileSpec { sha256: Some("not a SHA"), ..base.clone() }, + ] { + assert!(matches!(run(&[spec], &[], &dir, &cancel).0, Err(DownloadError::Invalid(_)))); + assert!(!dir.exists()); + } + let huge = FileSpec { size: Some(u64::MAX), max: u64::MAX, ..base.clone() }; + let other = FileSpec { name: "other.onnx", ..huge.clone() }; + for specs in [vec![huge, other], vec![base.clone(); 257], vec![base.clone(), base]] { + assert!(matches!(run(&specs, &[], &dir, &cancel).0, Err(DownloadError::Invalid(_)))); + assert!(!dir.exists()); + } +} From 03bc1469f9993a3e2540e21fd4d1ffeb14dcc636 Mon Sep 17 00:00:00 2001 From: radialmonster Date: Wed, 7 Oct 2026 23:57:26 -0400 Subject: [PATCH 03/41] LR-EDIT-OPTICS-PROFILE: apply the distortion correction Panasonic/Leica record in RW2/RWL (#256) The RW2 reader reads PanasonicRaw tag 0x0119 (16 LE words) and expresses the camera's own correction as the equivalent DNG WarpRectilinear in OpcodeList3, so embedded_lens() turns it into the existing EmbeddedLens/EmbeddedWarp and "Enable Profile Corrections" applies it like a DNG's warp (no new warp path; Convert to DNG keeps it). Rule, from our own measurements against the embedded camera JPEGs (clean-room; ExifTool tag names only): corrected = rho*(1 + (w8 rho^2 + w4 rho^4 + w11 rho^6)/32768), rho = r/w12 (w12 = active-area half diagonal), output = corrected / (1 + w5/32768), on when (w7 & 0xf) == 1. Chosen on 38 GX85 photos, held out on 35 photos from 12 other bodies / 9 other lenses; corrected renders line up with the camera JPEG within 0.75 px at 1440 px at the corners (up to 13 % of the half diagonal before). Implausible values leave the photo uncorrected. The loader now removes lens opcodes before the RW2 camera-look fit (its binned proxy needs an empty OpcodeList3); Opcode::is_lens_correction() replaces the duplicated matches. RENDER_CACHE_VERSION 11 -> 12. --- README.md | 2 +- crates/develop/src/settings.rs | 5 +- crates/engine/examples/render_bench.rs | 4 +- crates/engine/src/camera_profiles.rs | 4 +- crates/engine/src/files.rs | 141 ++++++++++++++-- crates/engine/src/media.rs | 2 +- crates/merge/src/frame.rs | 4 +- crates/pipeline/src/optics.rs | 3 +- crates/raw/src/opcodes.rs | 7 + crates/raw/src/vendor/rw2.rs | 216 ++++++++++++++++++++++++- docs/camera-preview-colour.md | 2 +- docs/parity.md | 5 +- docs/whats-new.md | 5 + 13 files changed, 376 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index f1f2e4bc..8ed09b89 100644 --- a/README.md +++ b/README.md @@ -311,7 +311,7 @@ LightCraft is young and moving fast. **Where we honestly stand** (details in the | Import: Add in place / Copy / Move, rename and folder templates, devices, duplicate detection, watched folders; Local folder browsing | ✅ | | MCP server (headless or live app, persistent libraries), CLI, control channel | ✅ | | XMP sidecars (read/write, auto-write), reading `crs:` develop settings, preset files (`.lcpreset`, XMP presets) | ✅ | -| Optics (distortion, vignetting, auto + manual CA, defringe, DNG-embedded lens corrections), Geometry (transforms, Constrain Crop), Upright (Auto/Level/Vertical/Full/Guided) | ✅ · camera lens profiles (our own) ⬜ | +| Optics (distortion, vignetting, auto + manual CA, defringe, lens corrections embedded in DNG files and Panasonic / Leica RW2 / RWL distortion data), Geometry (transforms, Constrain Crop), Upright (Auto/Level/Vertical/Full/Guided) | ✅ · camera lens profiles (our own) ⬜ | | Photo Merge: HDR (auto-align, deghost), Panorama (spherical/cylindrical/perspective, boundary warp, auto crop), HDR Panorama → DNG | ✅ | | GPU pipeline (wgpu compute, CPU-exact within 1/255), CPU fallback on device limits / errors | ✅ · WebGPU in the browser 🚧 | | AI: segmentation masks, AI denoise, super resolution, faces; HDR editing; video | ⬜ (see [roadmap](ROADMAP.md#where-were-going)) | diff --git a/crates/develop/src/settings.rs b/crates/develop/src/settings.rs index 800f229b..29e23d01 100644 --- a/crates/develop/src/settings.rs +++ b/crates/develop/src/settings.rs @@ -539,8 +539,9 @@ impl Default for Optics { } } -/// Lens corrections embedded in a DNG file (`OpcodeList3`: `WarpRectilinear`, `FixVignetteRadial`), converted -/// to the oriented, default-cropped image. This is camera/file data (stored on the photo record, not in the develop +/// Lens corrections embedded in the file, converted to the oriented, default-cropped image: a DNG's `OpcodeList3` +/// (`WarpRectilinear`, `FixVignetteRadial`), or a raw reader's equivalent of the camera's own correction (Panasonic / +/// Leica RW2 distortion, `lightcraft_raw`'s `vendor/rw2.rs`). This is camera/file data (stored on the photo record, not in the develop /// settings); "Enable Profile Corrections" applies it, scaled by the profile distortion/vignetting amounts. /// LightCraft never uses Adobe LCP lens profiles. #[derive(Clone, Copy, Debug, Default, PartialEq, Serialize, Deserialize)] diff --git a/crates/engine/examples/render_bench.rs b/crates/engine/examples/render_bench.rs index ac6a9a60..55dc2b11 100644 --- a/crates/engine/examples/render_bench.rs +++ b/crates/engine/examples/render_bench.rs @@ -93,7 +93,9 @@ fn main() { for path in &args { let bytes = std::fs::read(path).expect("read"); println!("{path}"); - let raw = lightcraft_raw::decode(&bytes).expect("decode"); + let mut raw = lightcraft_raw::decode(&bytes).expect("decode"); + // as the loader does: lens corrections are applied by the pipeline, not in the raw develop + raw.opcodes.list3.retain(|op| !op.is_lens_correction()); println!(" {}×{} cpp {} cfa {:?}", raw.width, raw.height, raw.cpp, raw.cfa.as_ref().map(|c| c.name())); println!(" {:<36} {}", "raw decode:", best(n, || drop(lightcraft_raw::decode(&bytes).expect("decode")))); println!(" {:<36} {}", "normalize:", best(n, || drop(raw.normalized().expect("norm")))); diff --git a/crates/engine/src/camera_profiles.rs b/crates/engine/src/camera_profiles.rs index 29efa4b8..3d5117e5 100644 --- a/crates/engine/src/camera_profiles.rs +++ b/crates/engine/src/camera_profiles.rs @@ -199,7 +199,9 @@ impl Pool { /// Add one raw file's colour pairs. `Ok(None)` when the file can't contribute (not an ARW or /// NEF without colour matrices, no usable camera JPEG, too little colour). pub fn add(&mut self, bytes: &[u8]) -> Result, String> { - let raw = lightcraft_raw::decode(bytes).map_err(|e| e.to_string())?; + let mut raw = lightcraft_raw::decode(bytes).map_err(|e| e.to_string())?; + // colour only: geometric lens corrections would stop the sensor proxy from binning + raw.opcodes.list3.retain(|op| !op.is_lens_correction()); let Some(model) = raw.metadata.model.as_deref().map(str::trim).filter(|m| !m.is_empty()) else { return Ok(None) }; let Some(pairs) = crate::camera_preview::profile_pairs(&raw, bytes) else { return Ok(None) }; let step = pairs.len().div_ceil(PAIRS_PER_FILE).max(1); diff --git a/crates/engine/src/files.rs b/crates/engine/src/files.rs index 62a1c467..26f970e2 100644 --- a/crates/engine/src/files.rs +++ b/crates/engine/src/files.rs @@ -43,8 +43,9 @@ fn meta_of(m: &lightcraft_meta::Metadata) -> (Meta, Option) { (meta, m.capture_time.as_ref().map(|d| d.to_iso())) } -/// Lens corrections embedded in a DNG's `OpcodeList3` (`WarpRectilinear`, `FixVignetteRadial`), re-expressed for -/// the default-cropped, EXIF-oriented image. These are the only "profile" corrections LightCraft applies. +/// Lens corrections embedded in a raw's `OpcodeList3` (`WarpRectilinear`, `FixVignetteRadial`: a DNG's own, or the +/// raw reader's equivalent of the camera's correction, e.g. Panasonic / Leica RW2 distortion), re-expressed for the +/// default-cropped, EXIF-oriented image. These are the only "profile" corrections LightCraft applies. pub fn embedded_lens(raw: &lightcraft_raw::RawInfo) -> Option { use lightcraft_develop::{EmbeddedLens, EmbeddedVignette, EmbeddedWarp}; use lightcraft_geom::Point; @@ -87,10 +88,6 @@ pub fn embedded_lens(raw: &lightcraft_raw::RawInfo) -> Option bool { - matches!(op, lightcraft_raw::Opcode::WarpRectilinear { .. } | lightcraft_raw::Opcode::FixVignetteRadial { .. }) -} - fn ext_upper(name: &str) -> String { std::path::Path::new(name).extension().map(|e| e.to_string_lossy().to_uppercase()).unwrap_or_default() } @@ -224,13 +221,14 @@ fn load_bytes_now(bytes: std::borrow::Cow<'_, [u8]>, max_edge: usize) -> Result< } Err(e) => return Err(e.to_string()), }; + // Embedded lens corrections are applied by the pipeline ("Enable Profile Corrections"), not baked in + // (removed before the camera look's binned sensor proxy too, which needs an empty `OpcodeList3`). + let lens = embedded_lens(&raw.info()); + raw.opcodes.list3.retain(|op| !op.is_lens_correction()); let xy = lightcraft_raw::color::as_shot_white_xy(&raw); let t = lightcraft_raw::color::camera_transform(&raw, xy); let camera_look = crate::camera_preview::fit_preview(&raw, &bytes, &t); drop(bytes); - // Embedded lens corrections are applied by the pipeline ("Enable Profile Corrections"), not baked in. - let lens = embedded_lens(&raw.info()); - raw.opcodes.list3.retain(|op| !is_lens_opcode(op)); // Previews and thumbnails bin the mosaic straight to (about) the size they need; only // larger levels (exports, 1:1) demosaic the whole sensor. let t0 = web_time::Instant::now(); @@ -402,6 +400,131 @@ mod tests { use super::*; use lightcraft_codecs::{ChromaSubsampling, EncodeImage, EncodeMeta, Samples, encode_jpeg}; + /// Luminance (from linear RGB) minus its 9 × 9 local mean: tone differences between a render and a camera JPEG + /// mostly cancel, edges remain. + fn detail(w: usize, h: usize, lum: impl Fn(usize, usize) -> f32) -> Vec { + let l: Vec = (0..w * h).map(|i| lum(i % w, i / w)).collect(); + let mut out = vec![0.0; w * h]; + for y in 0..h { + for x in 0..w { + let (x0, x1, y0, y1) = (x.saturating_sub(4), (x + 5).min(w), y.saturating_sub(4), (y + 5).min(h)); + let m = (y0..y1).flat_map(|yy| (x0..x1).map(move |xx| (xx, yy))).map(|(xx, yy)| l[yy * w + xx]).sum::(); + out[y * w + x] = l[y * w + x] - m / ((x1 - x0) * (y1 - y0)) as f32; + } + } + out + } + + /// The integer shift (within ±`r` px) of the `p × p` patch of `a` at `(x, y)` that best matches `b`, with its + /// normalised cross-correlation, or `None` when the patch has too little detail to tell. + fn patch_shift(a: &[f32], b: &[f32], w: usize, (x, y): (usize, usize), p: usize, r: i64) -> Option<((i64, i64), f32)> { + let pa: Vec = (0..p * p).map(|i| a[(y + i / p) * w + x + i % p]).collect(); + let na = pa.iter().map(|v| v * v).sum::().sqrt(); + if na < 1e-3 * p as f32 { + return None; + } + let mut best = (f32::MIN, (0, 0)); + for dy in -r..=r { + for dx in -r..=r { + let (bx, by) = (x as i64 + dx, y as i64 + dy); + let (mut dot, mut nb) = (0.0f32, 0.0f32); + for i in 0..p { + for j in 0..p { + let v = b[(by as usize + i) * w + bx as usize + j]; + dot += pa[i * p + j] * v; + nb += v * v; + } + } + let c = dot / (na * nb.sqrt()).max(1e-12); + if c > best.0 { + best = (c, (dx, dy)); + } + } + } + Some((best.1, best.0)) + } + + /// Issue #256: public Panasonic / Leica raws (skipped without the corpus) are corrected for distortion the way the + /// camera corrected its own JPEG (tag 0x0119): at the corners of the frame, the render with "Enable Profile + /// Corrections" lines up with the embedded JPEG within 2 px at 640 px, while the uncorrected render of the wide + /// lenses is far off. A file shot with the camera's correction off (DMC-GH1) carries no correction. + #[test] + fn corpus_rw2_distortion_matches_the_camera_jpeg() { + let dir = std::env::var_os("LIGHTCRAFT_CORPUS") + .map(std::path::PathBuf::from) + .unwrap_or_else(|| std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("../../corpus")) + .join("raw"); + let Ok(gh1) = std::fs::read(dir.join("rw2-panasonic-gh1.rw2")) else { + eprintln!("skip: {} absent", dir.display()); + return; + }; + assert_eq!(probe_bytes("gh1.rw2", &gh1).unwrap().embedded_lens, None, "correction off: nothing to apply"); + assert_eq!(load_bytes(&gh1, 320).unwrap().1.lens, None); + + // (file, how far off (px) the uncorrected corners are at the least, if they match within ±R at all) + let files = [ + ("rw2-panasonic-fz1000m2-4x3.rw2", 8), // 9.1 mm, ~13 % at the corners + ("rwl-leica-dlux7.rwl", 8), // 10.9 mm, ~13 % + ("rw2-panasonic-gx80.rw2", 8), // 14 mm, ~8 % + ("rw2-panasonic-gh6.rw2", 3), // 20 mm, ~1.6 % + ("rw2-panasonic-s9.rw2", 3), // 45 mm, 3 % zoom after the correction + ("rw2-panasonic-g9.rw2", 0), // 40 mm, ~0.5 % + ]; + const EDGE: usize = 640; + const P: usize = 64; + const R: i64 = 12; + for (name, off_at_least) in files { + let bytes = std::fs::read(dir.join(name)).unwrap(); + let probed = probe_bytes(name, &bytes).unwrap(); + assert!(probed.embedded_lens.is_some_and(|l| l.warp.is_some()), "{name}: no distortion correction read"); + let (src, info) = load_bytes(&bytes, 2 * EDGE).unwrap(); + assert_eq!(info.lens, probed.embedded_lens, "{name}: probe and decode disagree"); + // Convert to DNG keeps it: the same correction, as the DNG's own `WarpRectilinear` + let dng = lightcraft_raw::write_dng(&lightcraft_raw::decode(&bytes).unwrap(), &Default::default()).unwrap(); + assert_eq!(probe_bytes("x.dng", &dng).unwrap().embedded_lens, probed.embedded_lens, "{name}: lost in the DNG"); + let mut s = lightcraft_develop::DevelopSettings::default(); + let render = |s: &lightcraft_develop::DevelopSettings| { + lightcraft_pipeline::render(&src, &info, s, &lightcraft_pipeline::RenderRequest::fit(EDGE, EDGE)).image + }; + let before = render(&s); + s.optics.lens_profile = true; + let after = render(&s); + let (w, h) = (after.width, after.height); + assert_eq!((before.width, before.height), (w, h)); + // the camera JPEG shows the whole sensor; a render in an in-camera aspect ratio is its centre + let (cam, _) = load_embedded_preview(&bytes, 4 * EDGE).unwrap(); + let (cw, ch) = (cam.width as f64, cam.height as f64); + let (sw, sh) = if cw / ch > w as f64 / h as f64 { (ch * w as f64 / h as f64, ch) } else { (cw, cw * h as f64 / w as f64) }; + let cam = cam.crop(((cw - sw) / 2.0).round() as usize, ((ch - sh) / 2.0).round() as usize, sw.round() as usize, sh.round() as usize); + let cam = lightcraft_raster::resample::resize(&cam, w, h, Filter::Mitchell); + let lin = |v: u8| (v as f32 / 255.0).powf(2.2); + let lum8 = |im: &lightcraft_raster::Rgba8, x: usize, y: usize| { + let p = im.data[y * im.width + x]; + 0.3 * lin(p[0]) + 0.6 * lin(p[1]) + 0.1 * lin(p[2]) + }; + let reference = detail(w, h, |x, y| { + let p = cam.data[y * w + x]; + 0.3 * p[0] + 0.6 * p[1] + 0.1 * p[2] + }); + let (db, da) = (detail(w, h, |x, y| lum8(&before, x, y)), detail(w, h, |x, y| lum8(&after, x, y))); + let m = R as usize + 4; + let corners = [(m, m), (w - m - P, m), (m, h - m - P), (w - m - P, h - m - P)]; + let mut measured = 0; + for c in corners { + let Some(((dx, dy), ca)) = patch_shift(&da, &reference, w, c, P, R) else { continue }; + measured += 1; + assert!(dx.abs() <= 2 && dy.abs() <= 2 && ca >= 0.5, "{name}: corrected corner {c:?} off by ({dx}, {dy}) px, ncc {ca}"); + if off_at_least > 0 + && let Some(((bx, by), cb)) = patch_shift(&db, &reference, w, c, P, R) + { + let off = bx.abs().max(by.abs()) >= off_at_least || cb < 0.3; + assert!(off, "{name}: uncorrected corner {c:?} already within ({bx}, {by}) px, ncc {cb}"); + } + } + assert!(measured >= 2, "{name}: only {measured} corners with detail"); + } + } + /// A CR3-shaped file (not decodable yet) whose only content is a `PRVW` preview box. fn cr3_with_preview(w: u32, h: u32) -> Vec { let px: Vec = (0..w * h).flat_map(|i| [(i % 251) as u8, 128, 200]).collect(); diff --git a/crates/engine/src/media.rs b/crates/engine/src/media.rs index 76a9bf1a..7aafbf34 100644 --- a/crates/engine/src/media.rs +++ b/crates/engine/src/media.rs @@ -62,7 +62,7 @@ impl SettingsHashes { } /// Bump when the pipeline's output changes, to invalidate cached thumbnails. -pub const RENDER_CACHE_VERSION: u64 = 11; +pub const RENDER_CACHE_VERSION: u64 = 12; /// Thumbnails render at one of these long edges (so window/cell size changes reuse the cache). pub const THUMB_SIZES: [usize; 4] = [128, 256, 384, 512]; diff --git a/crates/merge/src/frame.rs b/crates/merge/src/frame.rs index df016b5d..360c7424 100644 --- a/crates/merge/src/frame.rs +++ b/crates/merge/src/frame.rs @@ -111,9 +111,7 @@ pub fn load_frame(bytes: &[u8], max_edge: Option, orient: bool) -> Result if lightcraft_raw::probe(bytes).is_some() { let mut raw = lightcraft_raw::decode(bytes).map_err(|e| MergeError::Decode(e.to_string()))?; // geometric lens corrections stay with the develop settings ("profile corrections") - raw.opcodes - .list3 - .retain(|op| !matches!(op, lightcraft_raw::Opcode::WarpRectilinear { .. } | lightcraft_raw::Opcode::FixVignetteRadial { .. })); + raw.opcodes.list3.retain(|op| !op.is_lens_correction()); let small = max_edge.is_some_and(|m| m <= 1200); let method = if small { lightcraft_raw::Method::Bilinear } else { lightcraft_raw::Method::Ahd }; let mut image = raw.develop(method).map_err(|e| MergeError::Decode(e.to_string()))?; diff --git a/crates/pipeline/src/optics.rs b/crates/pipeline/src/optics.rs index ad789fc8..f99a3cd8 100644 --- a/crates/pipeline/src/optics.rs +++ b/crates/pipeline/src/optics.rs @@ -11,7 +11,8 @@ //! dimensionless scales), so a 400 px preview and a full-size export are warped identically. //! //! Also here: automatic lateral chromatic aberration estimation ([`estimate_lateral_ca`]) and [`defringe`]. -//! Profile corrections use only lens data embedded in DNG files (`WarpRectilinear` / `FixVignetteRadial`). +//! Profile corrections use only lens data embedded in the files: DNG `WarpRectilinear` / `FixVignetteRadial`, and the +//! raw readers' equivalent of a camera's own correction (Panasonic / Leica RW2 distortion). use std::sync::Mutex; diff --git a/crates/raw/src/opcodes.rs b/crates/raw/src/opcodes.rs index f63eaf17..bca39774 100644 --- a/crates/raw/src/opcodes.rs +++ b/crates/raw/src/opcodes.rs @@ -98,6 +98,13 @@ impl Opcode { pub fn is_applied(&self) -> bool { !matches!(self, Opcode::Unknown { .. } | Opcode::TrimBounds { .. } | Opcode::WarpFisheye { .. }) } + + /// Whether this is a lens correction (`WarpRectilinear`, `FixVignetteRadial`): from a DNG, or the equivalent of + /// a camera's own correction recorded in its raw (Panasonic RW2). Developers that apply these through the + /// "profile corrections" of the develop settings remove them before demosaicing. + pub fn is_lens_correction(&self) -> bool { + matches!(self, Opcode::WarpRectilinear { .. } | Opcode::FixVignetteRadial { .. }) + } } /// The three opcode lists of a DNG raw IFD. diff --git a/crates/raw/src/vendor/rw2.rs b/crates/raw/src/vendor/rw2.rs index 10ade627..599439ce 100644 --- a/crates/raw/src/vendor/rw2.rs +++ b/crates/raw/src/vendor/rw2.rs @@ -67,12 +67,38 @@ //! - Active area: the sensor borders. Default crop: `0x002f`–`0x0032`, the in-camera aspect ratio (e.g. 4:3 or 1:1 //! on a 3:2 sensor; the embedded JPEG always shows the whole sensor). Some bodies write zeros there: ignored. //! +//! Lens distortion (tag `0x0119`, ExifTool name DistortionInfo; ExifTool's names for its words were the only outside +//! input, their meaning below is our own measurement). The tag holds 16 little-endian 16-bit words. The camera's +//! own correction, which its embedded JPEG shows, is applied as an `OpcodeList3` `WarpRectilinear` (the DNG form +//! LightCraft already applies under "Enable Profile Corrections"): +//! +//! - Word 7, low 4 bits: 1 = correction on [0 on the DMC-GH1, whose JPEG matches the uncorrected render]. +//! - Word 12: the normalisation radius in pixels, the half diagonal of the active area [2870 for 4592 × 3448 +//! (2871.2), 3240 for 5184 × 3888, 3611 for 5776 × 4336, 3288 for the FZ1000 II's whole 5472 × 3648 sensor +//! even in its 4:3 mode]. +//! - Words 8, 4 and 11 (ExifTool: DistortionParam08, 04, 11), divided by 32768, are `k2`, `k4`, `k6` of the +//! correction: a sensor point at radius ρ (in units of word 12, about the centre of the active area) appears in +//! the corrected image at ρ·(1 + k2 ρ² + k4 ρ⁴ + k6 ρ⁶). +//! - Word 5 (ExifTool: DistortionScale), divided by 32768, plus 1, is the zoom `z` the camera crops by afterwards: +//! output radius = corrected radius / z [z < 1 when the corrected corners would otherwise be empty]. +//! +//! [These rules were chosen among alternative word orders, powers, directions and readings of the zoom by fitting +//! radial curves between our uncorrected renders and the embedded JPEGs of 38 DMC-GX85 photos (12–32 mm and 25 mm +//! lenses). With no free parameter the words then predict the curves of 35 held-out photos from 12 other bodies and +//! 9 other lenses (9.1–105 mm: two compacts, Panasonic, Leica, Olympus, Sigma and Samyang lenses) within 0.12 % of +//! the half diagonal, against up to 13 % uncorrected; corrected renders of all 79 photos line up with the camera +//! JPEG within 0.75 px at 1440 px at every corner measured.] Words 2, 3, 6, 9, 10 +//! and 13 are not used (word 13 is always word 9 + 500; word 10 is fixed per body); words 0, 1, 14 and 15 change +//! from shot to shot. The correction is radial, so it is fitted by the DNG form's odd polynomial `u·(kr0 + kr1 u² + +//! kr2 u⁴ + kr3 u⁶)` over the output (within 0.4 px on a 20 MP sensor); implausible values (a radius far from the +//! active area's, a zoom beyond ±25 %, a correction that folds over) leave the photo uncorrected. +//! //! Not supported ([`RawError::Unsupported`], the embedded preview is shown instead): compression 34826, raw formats //! 1 and 3, and bit depths other than the ones above (no samples of any of them). use super::pef::{Bits, Huffman, diff}; use super::white_from_data; -use crate::{BlackLevel, Cfa, ColorData, MAX_SAMPLES, Mode, OpcodeLists, RawData, RawError, RawFormat, RawImage, Rect, Result}; +use crate::{BlackLevel, Cfa, ColorData, MAX_SAMPLES, Mode, Opcode, OpcodeLists, RawData, RawError, RawFormat, RawImage, Rect, Result}; use lightcraft_geom::Orientation; use lightcraft_tiff::{ByteOrder, Ifd, Tiff, Value, tags as t}; use rayon::prelude::*; @@ -113,6 +139,14 @@ const FORMAT8_FIXED: [(u16, &[u16]); 9] = [ (0x0041, &[17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), (0x0043, &[1]), ]; +/// The camera's lens distortion correction (ExifTool name: DistortionInfo); see the module docs. +const DISTORTION_INFO: u16 = 0x0119; +/// Fixed-point one of the distortion coefficients and zoom. +const DISTORTION_ONE: f64 = 32768.0; +/// Output radii sampled to fit the inverse mapping. +const DISTORTION_SAMPLES: usize = 64; +/// Largest error of the fitted inverse (relative to the half diagonal) accepted: real lenses fit within 1.2e-4. +const DISTORTION_MAX_FIT_ERROR: f64 = 1e-3; /// Most strips seen in a file is 3 (the 12008 × 8008 high-resolution mode). const MAX_STRIPS: usize = 64; @@ -586,13 +620,121 @@ pub(crate) fn decode(bytes: &[u8], mode: Mode) -> Result { color: ColorData::default(), wb_multipliers: wb, linearized: false, - opcodes: OpcodeLists::default(), + opcodes: OpcodeLists { list3: distortion(ifd0, active).into_iter().collect(), ..Default::default() }, metadata, }; img.validate_for(mode)?; Ok(img) } +/// The camera's distortion correction (tag `0x0119`, see the module docs) as the equivalent DNG `WarpRectilinear` +/// opcode for `OpcodeList3` (relative to the active area), so it reaches the same "profile corrections" as a DNG's +/// embedded warp. `None` when the camera recorded the correction as off, when it is the identity, or when the +/// values are implausible (the photo is then shown uncorrected rather than wrongly warped). +fn distortion(ifd: &Ifd, active: Rect) -> Option { + let b = ifd.bytes(DISTORTION_INFO)?; + let word = |i: usize| -> Option<[u8; 2]> { b.get(2 * i..2 * i + 2)?.try_into().ok() }; + let signed = |i: usize| word(i).map(|w| i16::from_le_bytes(w) as f64); + if u16::from_le_bytes(word(7)?) & 0x000f != 1 { + return None; + } + let (k2, k4, k6) = (signed(8)? / DISTORTION_ONE, signed(4)? / DISTORTION_ONE, signed(11)? / DISTORTION_ONE); + let zoom = 1.0 + signed(5)? / DISTORTION_ONE; + let radius = u16::from_le_bytes(word(12)?) as f64; + if k2 == 0.0 && k4 == 0.0 && k6 == 0.0 && zoom == 1.0 { + return None; + } + // DNG's normalisation: the distance from the centre to the farthest corner, in pixel-index units + let half_diag = (active.width as f64 - 1.0).hypot(active.height as f64 - 1.0) / 2.0; + if !(half_diag >= 1.0 && (0.5 * half_diag..=2.0 * half_diag).contains(&radius) && (0.75..=1.25).contains(&zoom)) { + return None; + } + // the camera's correction: a source point at radius ρ (in units of `radius`) moves to ρ·g(ρ) + let corrected = |r: f64| { + let r2 = r * r; + r * (1.0 + r2 * (k2 + r2 * (k4 + r2 * k6))) + }; + // the output corners show corrected radius `t_max`; the map must rise monotonically to it (a fold is no lens) + let t_max = half_diag * zoom / radius; + let mut top = None; + let mut prev = 0.0; + for i in 1..=4096 { + let r = 3.0 * t_max * i as f64 / 4096.0; + let v = corrected(r); + if v.is_nan() || v <= prev { + return None; + } + prev = v; + if v >= t_max { + top = Some(r); + break; + } + } + let top = top?; + let source = |t: f64| { + let (mut lo, mut hi) = (0.0, top); + for _ in 0..60 { + let m = 0.5 * (lo + hi); + if corrected(m) < t { lo = m } else { hi = m } + } + 0.5 * (lo + hi) + }; + // least-squares fit of source radius = u·(kr0 + kr1·u² + kr2·u⁴ + kr3·u⁶) over the output radii u ∈ [0, 1] + let samples: Vec<(f64, f64)> = (0..=DISTORTION_SAMPLES) + .map(|i| { + let u = i as f64 / DISTORTION_SAMPLES as f64; + (u, source(u * t_max) * radius / half_diag) + }) + .collect(); + let basis = |u: f64| { + let u2 = u * u; + [u, u * u2, u * u2 * u2, u * u2 * u2 * u2] + }; + let mut a = [[0.0; 4]; 4]; + let mut rhs = [0.0; 4]; + for &(u, v) in &samples { + let f = basis(u); + for (i, fi) in f.iter().enumerate() { + for (j, fj) in f.iter().enumerate() { + a[i][j] += fi * fj; + } + rhs[i] += fi * v; + } + } + let k = solve4(a, rhs)?; + let fit = |u: f64| basis(u).iter().zip(&k).map(|(f, c)| f * c).sum::(); + let worst = samples.iter().map(|&(u, v)| (fit(u) - v).abs()).fold(0.0, f64::max); + if worst.is_nan() || worst > DISTORTION_MAX_FIT_ERROR { + return None; + } + Some(Opcode::WarpRectilinear { planes: vec![[k[0], k[1], k[2], k[3], 0.0, 0.0]], center: [0.5, 0.5] }) +} + +/// Solve the 4 × 4 system `a·x = b` (Gaussian elimination with partial pivoting); `None` when singular or not finite. +fn solve4(mut a: [[f64; 4]; 4], mut b: [f64; 4]) -> Option<[f64; 4]> { + for c in 0..4 { + let p = (c..4).max_by(|&i, &j| a[i][c].abs().total_cmp(&a[j][c].abs()))?; + if a[p][c].is_nan() || a[p][c].abs() <= 1e-300 { + return None; + } + a.swap(c, p); + b.swap(c, p); + for r in c + 1..4 { + let f = a[r][c] / a[c][c]; + for k in c..4 { + a[r][k] -= f * a[c][k]; + } + b[r] -= f * b[c]; + } + } + let mut x = [0.0; 4]; + for c in (0..4).rev() { + let s: f64 = (c + 1..4).map(|k| a[c][k] * x[k]).sum(); + x[c] = (b[c] - s) / a[c][c]; + } + x.iter().all(|v| v.is_finite()).then_some(x) +} + #[cfg(test)] mod tests { use super::*; @@ -853,6 +995,76 @@ mod tests { } } + /// A tag-0x0119 block: 16 little-endian words, the given ones set. + fn distortion_block(words: &[(usize, i16)]) -> Value { + let mut w = [0i16; 16]; + for &(i, v) in words { + w[i] = v; + } + Value::Undefined(w.iter().flat_map(|v| v.to_le_bytes()).collect()) + } + + fn distorted(info: Option) -> Vec { + let (w, h) = (90usize, 60usize); + let px: Vec = (0..w * h).map(|i| (i % 4000) as u16 + 1).collect(); + let mut tags = vec![short(CFA_PATTERN, 1), short(BITS, 12), short(RAW_FORMAT, 5)]; + tags.extend(info.map(|v| (DISTORTION_INFO, v))); + let bytes = file(w as u16, h as u16, &tags, &rotate(pack(&px, 12)), &|off| vec![(RAW_OFFSET, Value::Long(vec![off]))]); + let r = crate::decode(&bytes).unwrap(); + assert_eq!(crate::probe_info(&bytes).unwrap().opcodes, r.opcodes, "headers-only probe reads the same correction"); + r.opcodes.list3 + } + + /// Known answer: correction k2 = 4096/32768 = 1/8 (word 8), zoom 1 − 1024/32768 (word 5), radius 51 px (word 12) + /// on an 86 × 58 active area. A sensor point at ρ appears at ρ + ρ³/8, so the output corner (corrected radius + /// t = half diagonal · zoom / 51) shows the root of ρ³ + 8ρ − 8t = 0, here by Cardano's formula. + #[test] + fn distortion_correction_becomes_a_rectilinear_warp() { + let on = |more: &[(usize, i16)]| { + let mut w = vec![(7, 0x0001), (8, 4096), (5, -1024), (12, 51)]; + w.extend_from_slice(more); + distortion_block(&w) + }; + let ops = distorted(Some(on(&[]))); + let [Opcode::WarpRectilinear { planes, center }] = ops.as_slice() else { panic!("{ops:?}") }; + assert_eq!((planes.len(), *center), (1, [0.5, 0.5])); + let k = planes[0]; + assert_eq!((k[4], k[5]), (0.0, 0.0), "radial only"); + let half_diag = 85f64.hypot(57.0) / 2.0; + for u in [0.1, 0.25, 0.5, 0.75, 1.0] { + let t = u * half_diag * (1.0 - 1024.0 / 32768.0) / 51.0; + let (p, q) = (8.0, -8.0 * t); + let d = (q * q / 4.0 + p * p * p / 27.0).sqrt(); + let rho = (-q / 2.0 + d).cbrt() + (-q / 2.0 - d).cbrt(); + let want = rho * 51.0 / half_diag; + let got = u * (k[0] + k[1] * u * u + k[2] * u.powi(4) + k[3] * u.powi(6)); + // the 4-term odd polynomial approximates the exact inverse (5e-5 of the half diagonal ≈ 0.2 px at 20 MP) + assert!((got - want).abs() < 5e-5, "u {u}: source radius {got}, want {want}"); + } + + // no correction: no block, recorded as off, or the identity + assert!(distorted(None).is_empty()); + assert!(distorted(Some(distortion_block(&[(7, 0x0000), (8, 4096), (12, 51)]))).is_empty(), "off"); + assert!(distorted(Some(distortion_block(&[(7, 0x7f00u16 as i16), (8, 4096), (12, 51)]))).is_empty(), "off, other bits set"); + assert!(distorted(Some(distortion_block(&[(7, 0x0001), (12, 51)]))).is_empty(), "identity"); + // hostile values are ignored, never a panic or a wild warp + for (what, block) in [ + ("truncated", Value::Undefined(vec![1, 0, 2, 0, 3])), + ("radius 0", on(&[(12, 0)])), + ("radius far beyond the sensor", on(&[(12, -1)])), + ("zoom 0", on(&[(5, -32768)])), + ("folds over", on(&[(8, -32768)])), + ("folds over at high order", on(&[(11, -32768), (4, -32768)])), + ("runs away", on(&[(8, 32767), (4, 32767), (11, 32767)])), + ] { + let ops = distorted(Some(block)); + assert!(ops.iter().all(|op| !matches!(op, Opcode::WarpRectilinear { .. })) || what == "runs away", "{what}: {ops:?}"); + if let [Opcode::WarpRectilinear { planes, .. }] = ops.as_slice() { + assert!(planes[0].iter().all(|c| c.is_finite()), "{what}"); + } + } + } + #[test] fn packed_and_word_formats() { let (w, h) = (90usize, 200usize); // > one chunk of data diff --git a/docs/camera-preview-colour.md b/docs/camera-preview-colour.md index 308287c6..60164642 100644 --- a/docs/camera-preview-colour.md +++ b/docs/camera-preview-colour.md @@ -44,7 +44,7 @@ On one sample, Exposure +1 EV, Saturation +100, Contrast +50, warm/cool WB, tint The same fit, gates and relative WB apply to Panasonic RW2, Leica RWL and the older Panasonic RAW files (all decode as `RawFormat::Rw2`; white balance from tags `0x0024`–`0x0026`). Their embedded JPEG (`JpgFromRaw`, 1920 px wide) always shows the whole active area, while the default crop is the aspect ratio set in the camera (4:3 on the 3:2 sensors, 1:1, 16:9, 2.71 on the S1R II…); when the preview matches the active area rather than the crop, the reference is cropped to the default crop before fitting. -Public CC0 samples from raw.pixls.us (178 files from 118 Panasonic and Leica bodies; the four from 2005–2007 carry no preview), rendered at 1200 px and compared with each file's embedded JPEG (mean CIE76 ΔE at 200 px): 140 of 174 fits accepted, from 93 of the 114 bodies with a preview, median ΔE 5.0 (1.2–21.3) with matching mean lightness (median |ΔL*| 0.4); 124 of them use a hue/saturation table. (Fitted with the matrix and tone curve alone, 137 were accepted at median ΔE 6.1; every one of those is as close or closer with the table and chroma curve, by 0.75 on average.) On the 14 corpus files with a preview the neutral fallback scored ΔE 9.6–27 and rendered up to 16 L* too dark; 13 fits were accepted, ΔE 2.8–8.9, lightness within 2 L*. Of the 34 files left on the neutral fallback, 21 fail the gates, almost all compacts and kit zooms (TZ/ZS, FZ2500, D-Lux, V-Lux, GF2, GX80 with its 12–32 mm): their camera JPEG is distortion-corrected, the pixel pairs no longer line up and the held-out RMS stays above 0.10 (GX80: 0.138 → 0.107); two S1 II shots just miss it (0.103, 0.107). The other 13 never reach the gates, mostly nearly colourless scenes (grey walls, concrete: under 5% coloured pixels) or dark ones (a night sky, a near-black frame). Panasonic's embedded distortion data (tag `0x0119`) is not used yet, and `lightcraft-cli calibrate` doesn't collect RW2 files: the misaligned pairs of distortion-corrected JPEGs would distort a pooled profile. +Public CC0 samples from raw.pixls.us (178 files from 118 Panasonic and Leica bodies; the four from 2005–2007 carry no preview), rendered at 1200 px and compared with each file's embedded JPEG (mean CIE76 ΔE at 200 px): 140 of 174 fits accepted, from 93 of the 114 bodies with a preview, median ΔE 5.0 (1.2–21.3) with matching mean lightness (median |ΔL*| 0.4); 124 of them use a hue/saturation table. (Fitted with the matrix and tone curve alone, 137 were accepted at median ΔE 6.1; every one of those is as close or closer with the table and chroma curve, by 0.75 on average.) On the 14 corpus files with a preview the neutral fallback scored ΔE 9.6–27 and rendered up to 16 L* too dark; 13 fits were accepted, ΔE 2.8–8.9, lightness within 2 L*. Of the 34 files left on the neutral fallback, 21 fail the gates, almost all compacts and kit zooms (TZ/ZS, FZ2500, D-Lux, V-Lux, GF2, GX80 with its 12–32 mm): their camera JPEG is distortion-corrected, the pixel pairs no longer line up and the held-out RMS stays above 0.10 (GX80: 0.138 → 0.107); two S1 II shots just miss it (0.103, 0.107). The other 13 never reach the gates, mostly nearly colourless scenes (grey walls, concrete: under 5% coloured pixels) or dark ones (a night sky, a near-black frame). Panasonic's embedded distortion data (tag `0x0119`) now corrects the render under Enable Profile Corrections (issue #256) but does not yet align the pairs of this fit, and `lightcraft-cli calibrate` doesn't collect RW2 files: the misaligned pairs of distortion-corrected JPEGs would distort a pooled profile. This is a per-file camera-look estimate, **not measured spectral calibration or Lightroom parity**. Camera local tone, picture styles, noise reduction and lens warps cannot all be reproduced by a global matrix/curve; same-aspect crops may evade the aspect check. Skin, mixed-light scenes and saturated highlights can still differ from a supplied JPEG. Broader Sony and Nikon model/lighting coverage, chart-based calibration, absolute WB and other manufacturers remain unverified. Rejected fits and other RAW formats retain their existing behaviour. diff --git a/docs/parity.md b/docs/parity.md index d5a31ea9..71177a6e 100644 --- a/docs/parity.md +++ b/docs/parity.md @@ -67,7 +67,8 @@ Take the first one nobody is working on. colour-filter layout, fixed in #85 by reading the file's own tag). 4. **LR-BEHAV-RENDER-FIDELITY** (P1): a side-by-side fidelity suite against Lightroom renders (kept local in `plan/`), then tune tone, highlights, texture/clarity/dehaze, NR and sharpening against it. -5. **LR-EDIT-OPTICS-PROFILE** (P1): a lens-profile database of our own (embedded DNG/maker corrections work today). +5. **LR-EDIT-OPTICS-PROFILE** (P1): a lens-profile database of our own (corrections embedded in DNG files and the + distortion correction Panasonic / Leica cameras record in RW2 / RWL files work today; issue #256). 6. **AI masks and Enhance** (LR-MASK-SUBJECT / SKY / PEOPLE / OBJECTS, LR-EDIT-DETAIL-DENOISE, SUPERRES, LENSBLUR): Object and Describe masks run SAM 3 in pure Rust (`crates/segment`); the weights (SAM License) are never bundled: the app offers a consented, verified download, but **LightCraft's CDN mirrors are not configured yet** (the @@ -254,7 +255,7 @@ under the UI command that wraps it (a no-op error); the UI command now wins. | LR-EDIT-DETAIL-SUPERRES | Super resolution | P2 | ⬜ | | | | LR-EDIT-DETAIL-AISHARPEN | AI sharpen | OOS | 🚫 | | | | LR-EDIT-OPTICS-CA | Remove chromatic aberration | P1 | ✅ | `crates/ui-egui/src/panels/edit.rs` (checkbox), `ctl:optics.caRed`, `ctl:optics.caBlue` | | -| LR-EDIT-OPTICS-PROFILE | Lens profile corrections | P1 | 🟡 | `ctl:optics.profileDistortion`, `ctl:optics.profileVignetting`, `crates/pipeline/src/optics.rs` | uses corrections embedded in DNG/raw files; no lens-profile database | +| LR-EDIT-OPTICS-PROFILE | Lens profile corrections | P1 | 🟡 | `ctl:optics.profileDistortion`, `ctl:optics.profileVignetting`, `crates/pipeline/src/optics.rs`, `crates/raw/src/vendor/rw2.rs` | uses corrections embedded in the file: DNG `WarpRectilinear` / `FixVignetteRadial`, and the camera's own distortion correction in Panasonic / Leica RW2 / RWL files (tag `0x0119`, read as the equivalent `WarpRectilinear`; on 79 photos from 13 bodies and 11 lenses the corrected render lines up with the camera JPEG within 0.75 px at 1440 px at the corners, from up to 13 % of the half diagonal uncorrected; issue #256); vignetting and lateral CA data of other raw formats (Olympus, Fujifilm, Sony) not read yet; no lens-profile database | | LR-EDIT-OPTICS-DEFRINGE | Defringe | P1 | ✅ | `ctl:optics.defringe*` | no fringe eyedropper | | LR-EDIT-OPTICS-MANUAL | Manual distortion / vignetting | P1 | ✅ | `ctl:optics.distortion`, `ctl:optics.vignetting`, `ctl:optics.vignettingMidpoint` | | | LR-EDIT-GEOM-UPRIGHT | Upright | P1 | ✅ | `cmd:geometry.upright`, `cmd:geometry.guides` | | diff --git a/docs/whats-new.md b/docs/whats-new.md index ea0243f4..2f310b85 100644 --- a/docs/whats-new.md +++ b/docs/whats-new.md @@ -3,6 +3,11 @@ ## October 2026 ### RAW decoding +- Panasonic and Leica raws (RW2, RWL) are now corrected for lens distortion the way the camera corrects its own JPEG + (issue #256): the correction the camera records in the file is applied under Lens Corrections ▸ Enable Profile + Corrections, on by default for newly imported photos, with the same framing as the camera's JPEG. At 12 mm the + 12–32 mm kit zoom was off by about 5 % of the image width at the corners before. Files shot with the correction off + are unchanged; photos imported before this change get it when imported again. - Sony ILCE-7M4 downsized lossless ARWs now decode subsampled YCbCr tiles into linear RGB, preserving RAW editing & full-resolution export instead of using embedded JPEG previews. From 15feb6e1fc0f951fa6b5a46feb6db5418f20ab7e Mon Sep 17 00:00:00 2001 From: radialmonster Date: Thu, 8 Oct 2026 00:00:43 -0400 Subject: [PATCH 04/41] LR-PROF-CAMERACOLOR: fit the camera look away from edges when a lens-corrected JPEG misaligns (#232) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sony A7R II camera JPEGs are distortion-corrected by default while the raw is not. On the public CC0 ILCE-7RM2 samples the JPEG is up to 15 px of 1440 off, and the misaligned window frames alone pushed the held-out RMS to 0.108-0.115 (gate 0.10): the photo opened in the grey neutral fallback, 13 L* too dark. The same shot with distortion correction off fits at 0.059. When the fit on all pixels fails the gates, try it once more, with the same gates, on pixels whose 3x3 neighbourhood in either proxy spans a luminance ratio of at most 3. Fits that already pass are unchanged (29 other corpus ARW/NEF/RW2 files render pixel-identical). ILCE-7RM2 ΔE76 vs the camera JPEG (registered) 16.6-16.9 -> 3.7-3.8; corpus GX80 19.1 -> 7.9. RENDER_CACHE_VERSION 11 -> 12. --- crates/engine/src/camera_preview.rs | 119 +++++++++++++++++++++++++--- crates/engine/src/media.rs | 2 +- docs/camera-preview-colour.md | 2 +- docs/parity.md | 2 +- docs/whats-new.md | 2 + 5 files changed, 115 insertions(+), 12 deletions(-) diff --git a/crates/engine/src/camera_preview.rs b/crates/engine/src/camera_preview.rs index cd0d475d..efadbd98 100644 --- a/crates/engine/src/camera_preview.rs +++ b/crates/engine/src/camera_preview.rs @@ -102,7 +102,7 @@ pub(crate) fn profile_pairs(raw: &RawImage, bytes: &[u8]) -> Option Option { fit_pairs_with(sensor, reference, None) } +/// Largest luminance ratio within the 3×3 neighbourhood of pixel `i` (1 on flat areas, large on edges). +fn local_contrast(image: &Rgb32f, i: usize) -> f32 { + let (w, h) = (image.width, image.height); + if w == 0 { + return f32::INFINITY; + } + let (x, y) = (i % w, i / w); + let (mut lo, mut hi) = (f32::INFINITY, 0f32); + for row in y.saturating_sub(1)..(y + 2).min(h) { + for col in x.saturating_sub(1)..(x + 2).min(w) { + let l = image.data.get(row * w + col).map_or(0.0, |p| luminance_2020(*p)); + lo = lo.min(l); + hi = hi.max(l); + } + } + if hi.is_finite() { hi / lo.max(1e-4) } else { f32::INFINITY } +} + /// Training pairs (sensor → JPEG, unclipped midtones) and the wider set including highlights -/// (for the chroma curve); `None` when too few, or the photo has too little colour. +/// (for the chroma curve); `None` when too few, or the photo has too little colour. With +/// `edge_limit`, pixels whose 3×3 neighbourhood in either proxy spans a larger luminance ratio +/// are left out: there a small geometric mismatch between the raw and its JPEG pairs unrelated +/// colours (see [`EDGE_CONTRAST`]). type Pairs = Vec<([f64; 3], [f64; 3])>; -fn collect_pairs(sensor: &Rgb32f, reference: &Rgb32f) -> Option<(Pairs, Pairs)> { +fn collect_pairs(sensor: &Rgb32f, reference: &Rgb32f, edge_limit: Option) -> Option<(Pairs, Pairs)> { if (sensor.width, sensor.height) != (reference.width, reference.height) || sensor.data.len() != reference.data.len() { return None; } @@ -161,7 +182,10 @@ fn collect_pairs(sensor: &Rgb32f, reference: &Rgb32f) -> Option<(Pairs, Pairs)> // Highlights too (camera JPEGs bleach colours toward white there), for the chroma curve only. let mut bright = Vec::new(); let mut colour = 0; - for (input, output) in sensor.data.iter().zip(&reference.data) { + for (i, (input, output)) in sensor.data.iter().zip(&reference.data).enumerate() { + if edge_limit.is_some_and(|limit| local_contrast(sensor, i) > limit || local_contrast(reference, i) > limit) { + continue; + } let y = luminance_2020(*output); if !input.iter().all(|v| v.is_finite() && *v > 0.001 && *v < 1.5) || !output.iter().all(|v| v.is_finite() && *v >= 0.0) { continue; @@ -215,11 +239,25 @@ fn fit_matrix(pairs: &[([f64; 3], [f64; 3])]) -> Option { matrix.0.iter().flatten().all(|v| v.is_finite() && v.abs() < 8.0).then_some(matrix) } +/// Neighbourhood luminance ratio above which a pixel counts as an edge for the second attempt +/// of [`fit_pairs_with`]. Camera JPEGs are often lens-corrected (Sony "Distortion Comp.: Auto", +/// compacts and kit zooms): on a 51 mm ILCE-7RM2 shot of a glass façade the JPEG is up to 15 px +/// of 1440 (about one proxy pixel) off the raw, and the fit failed the gate on the mismatched +/// window frames alone (held-out RMS 0.111; 0.059 for the same shot with distortion correction +/// off). Away from edges: 0.049. Issue #232. +const EDGE_CONTRAST: f32 = 3.0; + /// The photo's look: its own matrix (with and without a hue/saturation table), or the given /// colour model (a camera profile's, in the sensor proxy's space), each completed with a tone -/// and chroma curve fitted to this photo. +/// and chroma curve fitted to this photo. When the fit on all pixels fails the acceptance gates, +/// it is tried once more on the pixels away from edges (same gates): there colour pairs stay +/// valid when the camera JPEG's geometry differs slightly from the raw's. fn fit_pairs_with(sensor: &Rgb32f, reference: &Rgb32f, colour: Option<(Mat3, Option)>) -> Option { - let (pairs, bright) = collect_pairs(sensor, reference)?; + fit_pairs_on(sensor, reference, colour.clone(), None).or_else(|| fit_pairs_on(sensor, reference, colour, Some(EDGE_CONTRAST))) +} + +fn fit_pairs_on(sensor: &Rgb32f, reference: &Rgb32f, colour: Option<(Mat3, Option)>, edge_limit: Option) -> Option { + let (pairs, bright) = collect_pairs(sensor, reference, edge_limit)?; let candidates = match colour { Some(given) => vec![given], None => { @@ -270,9 +308,10 @@ fn fit_pairs_with(sensor: &Rgb32f, reference: &Rgb32f, colour: Option<(Mat3, Opt .sum(); if lightcraft_pipeline::profiling() { eprintln!( - "[profile] camera look holdout RMS {:.5} -> {:.5} ({samples} channels)", + "[profile] camera look holdout RMS {:.5} -> {:.5} ({samples} channels{})", (before / samples as f64).sqrt(), - (after / samples as f64).sqrt() + (after / samples as f64).sqrt(), + if edge_limit.is_some() { ", away from edges" } else { "" } ); } if samples == 0 || after >= before * MIN_IMPROVEMENT || after / samples as f64 > MAX_HOLDOUT_RMS.powi(2) { @@ -740,7 +779,7 @@ mod tests { let mut pool = Vec::new(); for seed in 0..4 { let (sensor, reference) = photo(seed, 2.0 + seed as f32 * 0.5); - pool.extend(collect_pairs(&sensor, &reference).unwrap().0); + pool.extend(collect_pairs(&sensor, &reference, None).unwrap().0); } let (matrix, _) = fit_profile(&pool).unwrap(); // the camera's colours (luminance-normalised: the tone curve sets brightness) @@ -818,6 +857,68 @@ mod tests { assert!(info.relative_wb && info.as_shot_temp == 6500.0 && info.as_shot_tint == 0.0); } + /// Issue #232 (skipped without the corpus): the public ILCE-7RM2 sample's camera JPEG is + /// distortion-corrected, and on its glass façade the fit on all pixels misses the gate + /// (held-out RMS 0.113): it used to open grey with the neutral fallback. + #[test] + fn corpus_arw_with_a_lens_corrected_preview_gets_a_camera_look() { + let path = std::env::var_os("LIGHTCRAFT_CORPUS") + .map(std::path::PathBuf::from) + .unwrap_or_else(|| std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("../../corpus")) + .join("raw/arw-sony-a7rm2-12bit-uncompressed.arw"); + let Ok(bytes) = std::fs::read(&path) else { + eprintln!("skip: {} absent", path.display()); + return; + }; + let (_, info) = crate::files::load_bytes(&bytes, 400).unwrap(); + assert!(info.camera_tone.is_some(), "no camera look fitted"); + } + + /// Issue #232: an ILCE-7RM2 camera JPEG is lens-corrected ("Distortion Comp.: Auto"), so on a + /// façade of window frames the raw and its JPEG are a pixel or two apart. The colours agree + /// everywhere away from the edges, and the look must still be found there. + #[test] + fn fits_a_slightly_misaligned_camera_jpeg_away_from_edges() { + let known = Mat3([[1.8, -0.4, -0.1], [-0.2, 1.5, -0.1], [-0.05, -0.3, 1.7]]); + let (w, h, block, shift) = (96, 64, 8, 2); + // Patches of 8×8 pixels, alternately dark and bright (frames and panes), varied in colour. + let scene = |x: usize, y: usize| -> [f32; 3] { + let (bx, by) = (x / block, y / block); + let ev = if (bx + by) % 2 == 0 { 0.04 + (bx % 3) as f32 * 0.01 } else { 0.3 + (by % 4) as f32 * 0.08 }; + [ev * (0.7 + ((bx * 7 + by * 3) % 11) as f32 * 0.05), ev, ev * (0.7 + ((bx * 5 + by) % 13) as f32 * 0.04)] + }; + let camera = |p: [f32; 3]| { + let q = known.apply_f32(p); + let y = luminance_2020(q); + q.map(|v| v * (1.0 - (-2.5 * y).exp()) / y) + }; + let mut sensor = Rgb32f::new(w, h); + let mut reference = sensor.clone(); + for y in 0..h { + for x in 0..w { + sensor.data[y * w + x] = scene(x, y); + // The JPEG shows the scene `shift` pixels to the right of where the raw has it. + reference.data[y * w + x] = camera(scene(x.saturating_sub(shift), y)); + } + } + let fit = fit_pairs(&sensor, &reference).expect("look fitted away from the misaligned edges"); + // The look itself is right: on the aligned scene it reproduces the camera. + let tone = ToneMap::camera(&fit.tone, 0.0, 0.0, 0.0); + let (mut error, mut n) = (0.0, 0); + for y in 0..h { + for x in 0..w { + let p = displayed(fit.matrix.apply(scene(x, y).map(f64::from)), &tone); + let t = camera(scene(x, y)); + error += (0..3).map(|c| (p[c] - f64::from(t[c])).powi(2)).sum::(); + n += 3; + } + } + assert!((error / n as f64).sqrt() < 0.03, "{}", (error / n as f64).sqrt()); + // Unrelated colours stay rejected away from edges too (covered below); a flat JPEG as well. + reference.data.fill([0.2; 3]); + assert!(fit_pairs(&sensor, &reference).is_none()); + } + #[test] fn rejects_monochrome_invalid_and_unrelated_previews() { let mut sensor = Rgb32f::new(32, 32); diff --git a/crates/engine/src/media.rs b/crates/engine/src/media.rs index 76a9bf1a..7aafbf34 100644 --- a/crates/engine/src/media.rs +++ b/crates/engine/src/media.rs @@ -62,7 +62,7 @@ impl SettingsHashes { } /// Bump when the pipeline's output changes, to invalidate cached thumbnails. -pub const RENDER_CACHE_VERSION: u64 = 11; +pub const RENDER_CACHE_VERSION: u64 = 12; /// Thumbnails render at one of these long edges (so window/cell size changes reuse the cache). pub const THUMB_SIZES: [usize; 4] = [128, 256, 384, 512]; diff --git a/docs/camera-preview-colour.md b/docs/camera-preview-colour.md index 308287c6..48371480 100644 --- a/docs/camera-preview-colour.md +++ b/docs/camera-preview-colour.md @@ -14,7 +14,7 @@ Matrix-only and matrix + table looks are each completed (tone curve, then chroma The matrix is applied to scene-linear sensor pixels. The curve is carried in `SourceInfo` and applied in the pipeline finish stage, after exposure/WB/local edits. It is never baked into decoded luminance or clipped to JPEG precision. CPU and GPU use the same tone LUT. Contrast, whites and blacks adjust the starting look through the display-domain controls. JPEG neutral rendering and the existing DNG colour model are unchanged. -One third of the pixels are held out from both fits. The combined model must reduce linear-display squared error by at least 30% and have per-channel RMS error ≤0.10 (0.055 rejected half of the public raw.pixls.us samples tried — ILCE-6400, -6700, -7M4 — whose fits were still 1.5–3× closer to the camera JPEG than the fallback and visibly better). These are provisional acceptance gates, not a colour-accuracy certification. Monochrome, singular, unrelated, aspect-mismatched and poor-fitting references retain the documented fallback. Both fits use the same fixed proxy at every decode resolution. +One third of the pixels are held out from both fits. The combined model must reduce linear-display squared error by at least 30% and have per-channel RMS error ≤0.10 (0.055 rejected half of the public raw.pixls.us samples tried — ILCE-6400, -6700, -7M4 — whose fits were still 1.5–3× closer to the camera JPEG than the fallback and visibly better). When the fit on all pixels fails these gates, it is tried once more, with the same gates, on the pixels away from edges (where the 3×3 neighbourhood in either proxy spans a luminance ratio above 3). Many camera JPEGs are lens-corrected while the raw is not (Sony "Distortion Comp.: Auto", compacts and kit zooms), and on a detailed scene the misaligned edges alone fail the gate although the colours agree everywhere else (issue #232). The public ILCE-7RM2 samples (raw.pixls.us, one façade shot in seven compression/correction variants, 24–70 mm at 51 mm) are up to 15 px of 1440 off their JPEG; the six with distortion correction on missed the gate at held-out RMS 0.108–0.115 and opened in the grey neutral fallback, 13 L* too dark (ΔE76 16.6–16.9 against the JPEG after registering its geometry). Away from edges they pass at 0.049–0.052 and land at ΔE 3.7–3.8 with lightness within 0.6 L*, like the variant shot with distortion correction off (0.059 on all pixels, ΔE 3.9). The corpus GX80 kit-zoom sample (up to 63 px of 1440 off) passes too: 0.107 → 0.081, ΔE 19.1 → 7.9. Fits accepted on all pixels are unchanged. These are provisional acceptance gates, not a colour-accuracy certification. Monochrome, singular, unrelated, aspect-mismatched and poor-fitting references retain the documented fallback. Both fits use the same fixed proxy at every decode resolution. ## Source metadata and white balance diff --git a/docs/parity.md b/docs/parity.md index d5a31ea9..edefc83f 100644 --- a/docs/parity.md +++ b/docs/parity.md @@ -276,7 +276,7 @@ under the UI command that wraps it (a no-op error); the UI command now wins. | LR-PROF-ADOBE | Standard raw looks (own equivalents) | P0 | ✅ | `crates/engine/src/presets.rs` (`PROFILES`), `crates/pipeline/src/profiles.rs` | six own looks: Color, Neutral, Vivid, Landscape, Portrait, Monochrome | | LR-PROF-ADAPTIVE | Adaptive profiles | P2 | ⬜ | | | | LR-PROF-CAMERA | Camera-matching looks | P2 | ⬜ | | | -| LR-PROF-CAMERACOLOR | Camera colour calibration (own) | P0 | 🟡 | `crates/raw/src/color.rs`, `crates/raw/src/profile.rs`, `crates/engine/src/camera_preview.rs` | DNG files use the colour matrices and the camera-profile look they carry (`ProfileHueSatMap`, `ProfileLookTable`, `ProfileToneCurve`, read from the file at run time per DNG spec ch. 6 — Lightroom-converted DNGs rendered flat and muted without them, issue #138); Sony ARW, Nikon NEF and Panasonic RW2 get a guarded file-local fit to their own embedded JPEG — chromaticity matrix + hue/saturation/value table + tone and chroma curves, relative WB — or, with a local per-model profile pooled from many photos (`lightcraft-cli calibrate`, ARW and NEF so far, `crates/engine/src/camera_profiles.rs`), the profile's colour and only their own tone/chroma curves (on 29 held-out ILCE-7M4 photos mean ΔE vs the camera JPEG 3.54 → 3.10), kept with smart previews (see docs/camera-preview-colour.md); ARW: on 10 public raw.pixls.us samples from 8 bodies 7 fits were accepted, mean ΔE vs the camera JPEG 17–26 → 3–10; NEF: 13 of 13 decodable samples (D750, D780, D850, D7500, Z 50 and local D7500 shots) accepted, ΔE 13–46 → 3–8 (issue #150); RW2 / RWL: 140 of 174 public samples accepted, from 93 of the 114 bodies with a preview, median ΔE 5.0 with matching lightness (13 of the 14 corpus files with a preview: ΔE 9.6–27 → 2.8–8.9; most rejections are compacts and kit zooms whose camera JPEG is distortion-corrected); this is not measured camera calibration; other raws and rejected fits use a neutral fallback (camera RGB ≈ linear sRGB, `matrix_is_fallback`) with as-shot white balance, so colours are muted and not accurate. Needs our own per-camera calibration: matrices the files carry themselves (e.g. Olympus `ColorMatrix`), fitting each camera to its own embedded JPEG, then chart shots. Adobe matrices / DCPs are never used. Biggest image-quality gap today | +| LR-PROF-CAMERACOLOR | Camera colour calibration (own) | P0 | 🟡 | `crates/raw/src/color.rs`, `crates/raw/src/profile.rs`, `crates/engine/src/camera_preview.rs` | DNG files use the colour matrices and the camera-profile look they carry (`ProfileHueSatMap`, `ProfileLookTable`, `ProfileToneCurve`, read from the file at run time per DNG spec ch. 6 — Lightroom-converted DNGs rendered flat and muted without them, issue #138); Sony ARW, Nikon NEF and Panasonic RW2 get a guarded file-local fit to their own embedded JPEG — chromaticity matrix + hue/saturation/value table + tone and chroma curves, relative WB — or, with a local per-model profile pooled from many photos (`lightcraft-cli calibrate`, ARW and NEF so far, `crates/engine/src/camera_profiles.rs`), the profile's colour and only their own tone/chroma curves (on 29 held-out ILCE-7M4 photos mean ΔE vs the camera JPEG 3.54 → 3.10), kept with smart previews (see docs/camera-preview-colour.md); ARW: on 10 public raw.pixls.us samples from 8 bodies 7 fits were accepted, mean ΔE vs the camera JPEG 17–26 → 3–10; NEF: 13 of 13 decodable samples (D750, D780, D850, D7500, Z 50 and local D7500 shots) accepted, ΔE 13–46 → 3–8 (issue #150); RW2 / RWL: 140 of 174 public samples accepted, from 93 of the 114 bodies with a preview, median ΔE 5.0 with matching lightness (13 of the 14 corpus files with a preview: ΔE 9.6–27 → 2.8–8.9; most rejections are compacts and kit zooms whose camera JPEG is distortion-corrected); a fit that fails the gates on all pixels is retried away from edges, where a lens-corrected camera JPEG still lines up (issue #232: the six distortion-corrected public ILCE-7RM2 samples ΔE 16.6–16.9 → 3.7–3.8, the corpus GX80 19.1 → 7.9); this is not measured camera calibration; other raws and rejected fits use a neutral fallback (camera RGB ≈ linear sRGB, `matrix_is_fallback`) with as-shot white balance, so colours are muted and not accurate. Needs our own per-camera calibration: matrices the files carry themselves (e.g. Olympus `ColorMatrix`), fitting each camera to its own embedded JPEG, then chart shots. Adobe matrices / DCPs are never used. Biggest image-quality gap today | | LR-PROF-CREATIVE | Creative profiles (own) | P2 | ✅ | `cmd:develop.profile`, `crates/pipeline/src/profiles.rs` | 16 own looks in Film / Cinematic / Muted / B&W (tone + point-curve fades, colour grading, mixer / B&W mix); scale with `ctl:profile.amount`; sliders untouched | | LR-PROF-LEGACY | Legacy profiles | P2 | ⬜ | | | | LR-PROF-NONRAW | Profiles for non-raw files | P0 | ✅ | `cmd:develop.profile` | same looks apply to JPEG/TIFF | diff --git a/docs/whats-new.md b/docs/whats-new.md index ea0243f4..04123882 100644 --- a/docs/whats-new.md +++ b/docs/whats-new.md @@ -3,6 +3,8 @@ ## October 2026 ### RAW decoding +- Sony A7R II (and other) raws whose camera JPEG is lens-corrected no longer open grey and too dark (issue #232): the + starting look is fitted to the camera JPEG away from edges when the misaligned edges spoil the fit on all pixels. - Sony ILCE-7M4 downsized lossless ARWs now decode subsampled YCbCr tiles into linear RGB, preserving RAW editing & full-resolution export instead of using embedded JPEG previews. From 47f40eb2d5d0fd3ea0d5a050cb5293c17da6f9b1 Mon Sep 17 00:00:00 2001 From: Gert Pellin Date: Thu, 8 Oct 2026 13:00:29 +0200 Subject: [PATCH 05/41] Desktop: write log records to a rotating log file as well as standard error The desktop app's logger (StderrLog in main.rs) wrote to standard error only. A launch from a desktop menu or the Dock has no terminal, so its warnings were lost, and the panic log in the temp folder is gone after a reboot: there was nothing to attach to a bug report. apps/lightcraft/src/logging.rs replaces it (no new dependency): - standard error plus logs/lightcraft.log in the settings folder, next to ui.json (Linux $XDG_CONFIG_HOME/lightcraft/logs, never the library); each launch rotates the previous log to lightcraft.1.log and .2.log, so the log of a run that crashed survives the next start; the file is capped at 16 MiB with one note - records logged before the settings folder is known are buffered (bounded) and written once the file is attached - default levels: info for lightcraft*, warn for everything else; LIGHTCRAFT_LOG=info|debug keeps its old meaning (a test compares it with the old logger) and wins over RUST_LOG, which otherwise replaces the default with env_logger-style directives - never panics: an unusable folder or a failed write leaves standard error as the only sink; a poisoned lock is taken back - runs with LIGHTCRAFT_NO_PREFS (tests, scripts) log to standard error only, so they don't rotate away the user's own logs - a panic hook chained after the engine guard's adds escaped panics to the file (try_lock, so a panic while logging can't hang the app); lightcraft-panics.log in the temp folder is kept The logger is installed first in main and the file is opened after argument parsing (--version, --help and usage errors leave no file). The start-up and control-server messages in main.rs and control_server.rs go through it instead of eprintln!, and a --control or LIGHTCRAFT_CONTROL_PORT value that isn't a port is now reported. Documented in README (Quick start, Logs), --help and docs/whats-new.md. --- README.md | 13 + apps/lightcraft/src/control_server.rs | 4 +- apps/lightcraft/src/logging.rs | 689 ++++++++++++++++++++++++++ apps/lightcraft/src/main.rs | 86 ++-- docs/whats-new.md | 4 + 5 files changed, 762 insertions(+), 34 deletions(-) create mode 100644 apps/lightcraft/src/logging.rs diff --git a/README.md b/README.md index f1f2e4bc..5034aebb 100644 --- a/README.md +++ b/README.md @@ -355,6 +355,19 @@ repo; see [craftrules `standards/fonts.md`](https://github.com/storytold/craftru **Interface language:** **Edit → Language** (English, 简体中文, 繁體中文(台灣), 日本語) or **Settings → General → Language**; the choice applies immediately and persists. See [docs/localization.md](docs/localization.md). +**Logs:** the desktop app writes its log to standard error and to `logs/lightcraft.log` in its settings folder +(Linux `$XDG_CONFIG_HOME/lightcraft/logs/`, by default `~/.config/lightcraft/logs/`; macOS +`~/Library/Application Support/LightCraft/logs/`; Windows `%APPDATA%\LightCraft\logs\`), never in the library. A +launch from a desktop menu or the Dock has no terminal, so attach this file to a bug report. Each start moves the +previous log to `lightcraft.1.log` and that one to `lightcraft.2.log`, so the log of a run that crashed survives the +next start; the file stops growing at 16 MiB, `--version` and `--help` write none, and runs with +`LIGHTCRAFT_NO_PREFS` log to standard error only. By default LightCraft's own crates log at `info` and everything else +at `warn`. `LIGHTCRAFT_LOG=info` or `debug` works as before (that level for LightCraft's own crates, warnings and +errors from the rest; any other value: warnings and errors only) and wins over `RUST_LOG`, which otherwise replaces +the default with env_logger-style directives such as `RUST_LOG=debug` or `RUST_LOG=warn,lightcraft_pipeline=trace` (a +directive ending in `*` covers every target starting with it, as in `lightcraft*=debug`). Panics are recorded there +too, and still in `lightcraft-panics.log` in the temp folder. The logger is `apps/lightcraft/src/logging.rs`. + The web build needs the `wasm32-unknown-unknown` target and the matching `wasm-bindgen` CLI (`cargo xtask web` prints the exact install command); see [docs/web.md](docs/web.md). diff --git a/apps/lightcraft/src/control_server.rs b/apps/lightcraft/src/control_server.rs index ff16be2f..16bd5df0 100644 --- a/apps/lightcraft/src/control_server.rs +++ b/apps/lightcraft/src/control_server.rs @@ -28,11 +28,11 @@ pub fn start(port: u16, ctx: egui::Context) -> Receiver { let listener = match TcpListener::bind(("127.0.0.1", port)) { Ok(l) => l, Err(e) => { - eprintln!("lightcraft: control server failed to bind 127.0.0.1:{port}: {e}"); + log::error!("control server failed to bind 127.0.0.1:{port}: {e}"); return rx; } }; - eprintln!("lightcraft: control server listening on 127.0.0.1:{port}"); + log::info!("control server listening on 127.0.0.1:{port}"); std::thread::spawn(move || accept_loop(listener, tx, ctx)); rx } diff --git a/apps/lightcraft/src/logging.rs b/apps/lightcraft/src/logging.rs new file mode 100644 index 00000000..718392f3 --- /dev/null +++ b/apps/lightcraft/src/logging.rs @@ -0,0 +1,689 @@ +//! The desktop app's logger: `log::` records go to standard error and to a log file. +//! +//! It replaces the stderr-only `StderrLog` that `main.rs` used to install. A launch from a +//! desktop menu or the Dock has no terminal, so the file is what a bug report can attach: +//! `/logs/lightcraft.log`, next to `ui.json` (`config_dir` in `main.rs`; Linux +//! `$XDG_CONFIG_HOME/lightcraft/logs`, by default `~/.config/lightcraft/logs`). Never in the +//! library folder. Each start moves the previous log to `lightcraft.1.log` (and that one to `.2`), +//! so the log of a run that crashed survives the next launch. Runs with `LIGHTCRAFT_NO_PREFS` +//! (tests, scripts) log to standard error only, so they don't rotate away the user's own logs. +//! +//! Levels ([`filter_spec`]): `info` for LightCraft's own crates, `warn` for everything else (wgpu +//! and naga are chatty). `LIGHTCRAFT_LOG` keeps the meaning it had with the old logger: `info` or +//! `debug` lowers LightCraft's own crates to that level, any other value means warnings and errors +//! only. Without it, `RUST_LOG` replaces the default with env_logger-style directives: `debug`, +//! `warn,lightcraft_pipeline=trace`, `wgpu_core=info`. A directive ending in `*` matches every +//! target that starts with it (`lightcraft*=debug`). +//! +//! Records logged before the settings folder is known are kept (up to [`MAX_PENDING`]) and +//! written once the file is attached. Writing never panics: a file that can't be created or +//! written leaves standard error as the only sink. Panics that escape everything are added to the +//! file by [`AppLogger::record_panic`] (the default hook already printed them on standard error). +//! +//! This is the desktop app's logger only; `lightcraft-cli` doesn't share a process with it. + +use std::fs::File; +use std::io::Write; +use std::path::{Path, PathBuf}; +use std::sync::{Mutex, OnceLock, PoisonError, TryLockError}; +use std::time::SystemTime; + +use log::LevelFilter; + +/// The current log file's name inside the log folder. +pub const LOG_FILE: &str = "lightcraft.log"; +/// How many previous logs are kept (`lightcraft.1.log` … `lightcraft..log`). +pub const KEEP: usize = 2; +/// The log file stops growing past this size (a runaway warning can't fill the disk). +pub const MAX_FILE_BYTES: u64 = 16 * 1024 * 1024; +/// Records kept in memory until the log file is attached. +pub const MAX_PENDING: usize = 512; +/// The built-in filter when neither `LIGHTCRAFT_LOG` nor `RUST_LOG` says otherwise. +pub const DEFAULT_FILTER: &str = "warn,lightcraft*=info"; + +/// The filter directives for this run from `LIGHTCRAFT_LOG` and `RUST_LOG` (blank = unset). +/// +/// `LIGHTCRAFT_LOG` wins when set and means what it did for the old stderr logger: `info` or +/// `debug` for LightCraft's own crates (warnings and errors from everything else), any other value +/// warnings and errors only. Else `RUST_LOG` as env_logger directives, else [`DEFAULT_FILTER`]. +pub fn filter_spec(lightcraft_log: Option<&str>, rust_log: Option<&str>) -> String { + let set = |v: Option<&str>| v.map(str::trim).filter(|v| !v.is_empty()).map(str::to_owned); + if let Some(level) = set(lightcraft_log) { + return match level.as_str() { + "debug" => "warn,lightcraft*=debug".to_owned(), + "info" => "warn,lightcraft*=info".to_owned(), + _ => "warn".to_owned(), + }; + } + set(rust_log).unwrap_or_else(|| DEFAULT_FILTER.to_owned()) +} + +/// Per-target level filter parsed from env_logger-style directives. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct Filter { + default: LevelFilter, + directives: Vec<(String, LevelFilter)>, +} + +impl Filter { + /// Parse `spec`; unknown levels and empty parts are skipped. Without a bare level, targets no + /// directive names log errors only. + pub fn parse(spec: &str) -> Filter { + let mut f = Filter { default: LevelFilter::Error, directives: Vec::new() }; + for part in spec.split(',').map(str::trim).filter(|p| !p.is_empty()) { + match part.split_once('=') { + Some((name, level)) => { + let name = name.trim(); + if let (false, Some(level)) = (name.is_empty(), parse_level(level)) { + f.directives.push((name.to_owned(), level)); + } + } + None => match parse_level(part) { + Some(level) => f.default = level, + // A bare target name: everything from it (env_logger does the same). + None => f.directives.push((part.to_owned(), LevelFilter::Trace)), + }, + } + } + f + } + + /// The level that applies to `target` (the most specific matching directive wins). + pub fn level_for(&self, target: &str) -> LevelFilter { + self.directives.iter().filter(|(name, _)| matches(name, target)).max_by_key(|(name, _)| name.len()).map_or(self.default, |(_, level)| *level) + } + + /// The most verbose level any target can reach (for `log::set_max_level`). + pub fn max(&self) -> LevelFilter { + self.directives.iter().map(|(_, level)| *level).fold(self.default, Ord::max) + } +} + +fn parse_level(s: &str) -> Option { + s.trim().parse().ok() +} + +/// `name` matches `target` itself and its submodules (`a` matches `a` and `a::b`, not `ab`); +/// `name*` matches every target starting with `name`. +fn matches(name: &str, target: &str) -> bool { + match name.strip_suffix('*') { + Some(prefix) => target.starts_with(prefix), + None => target.strip_prefix(name).is_some_and(|rest| rest.is_empty() || rest.starts_with("::")), + } +} + +/// `2026-10-08T07:59:17.728Z` (UTC, milliseconds). A clock before 1970 reads as the epoch. +pub fn timestamp(t: SystemTime) -> String { + let since = t.duration_since(std::time::UNIX_EPOCH).unwrap_or_default(); + let secs = since.as_secs(); + let (days, rem) = (secs / 86_400, secs % 86_400); + let (y, m, d) = civil_from_days(days); + format!("{y:04}-{m:02}-{d:02}T{:02}:{:02}:{:02}.{:03}Z", rem / 3600, rem % 3600 / 60, rem % 60, since.subsec_millis()) +} + +/// Days since 1970-01-01 to a proleptic Gregorian (year, month, day) (Howard Hinnant's +/// `civil_from_days`, unsigned because `timestamp` clamps at the epoch). Saturating, so no clock +/// value can overflow it. +fn civil_from_days(days: u64) -> (u64, u64, u64) { + let z = days.saturating_add(719_468); + let era = z / 146_097; + let doe = z % 146_097; + let yoe = (doe - doe / 1460 + doe / 36_524 - doe / 146_096) / 365; + let doy = doe - (365 * yoe + yoe / 4 - yoe / 100); + let mp = (5 * doy + 2) / 153; + let d = doy - (153 * mp + 2) / 5 + 1; + let m = if mp < 10 { mp + 3 } else { mp - 9 }; + let y = era.saturating_mul(400).saturating_add(yoe).saturating_add(u64::from(m <= 2)); + (y, m, d) +} + +fn numbered(dir: &Path, n: usize) -> PathBuf { + if n == 0 { dir.join(LOG_FILE) } else { dir.join(format!("lightcraft.{n}.log")) } +} + +/// Shift the previous logs up one (`.log` → `.1.log` → … → `..log`, the oldest dropped) +/// and create a fresh, empty log file in `dir` (created if missing). +pub fn rotate(dir: &Path) -> std::io::Result<(PathBuf, File)> { + std::fs::create_dir_all(dir)?; + for n in (1..=KEEP).rev() { + match std::fs::rename(numbered(dir, n - 1), numbered(dir, n)) { + Err(e) if e.kind() != std::io::ErrorKind::NotFound => return Err(e), + _ => {} + } + } + let path = numbered(dir, 0); + let file = File::create(&path)?; + Ok((path, file)) +} + +/// One formatted log line: ` [] : \n`. +pub fn format_line(ts: &str, level: log::Level, thread: &str, target: &str, message: &str) -> String { + let message = message.strip_suffix('\n').unwrap_or(message); + format!("{ts} {level:<5} [{thread}] {target}: {message}\n") +} + +/// Where formatted lines go besides standard error: kept in memory until a file is attached, +/// then appended to it until it reaches its size cap. +pub struct Sink { + file: Option, + pending: Vec, + dropped: usize, + written: u64, + max_bytes: u64, + capped: bool, +} + +impl Sink { + pub fn new(max_bytes: u64) -> Sink { + Sink { file: None, pending: Vec::new(), dropped: 0, written: 0, max_bytes, capped: false } + } + + /// Append `line` to the file, or keep it until [`Sink::attach`]. + pub fn write(&mut self, line: &str) { + if self.file.is_none() { + if self.capped { + // No file is coming ([`Sink::no_file`]): standard error had the line. + } else if self.pending.len() < MAX_PENDING { + self.pending.push(line.to_owned()); + } else { + self.dropped = self.dropped.saturating_add(1); + } + return; + } + self.append(line); + } + + /// Start writing to `file` (a fresh one: the size cap starts over), first the lines kept so far. + pub fn attach(&mut self, file: File) { + self.file = Some(file); + self.written = 0; + self.capped = false; + for line in std::mem::take(&mut self.pending) { + self.append(&line); + } + if self.dropped > 0 { + let note = format!("{} earlier log lines were dropped before the log file was opened\n", self.dropped); + self.dropped = 0; + self.append(¬e); + } + } + + /// There will be no log file: forget the lines kept for it and keep no more. + pub fn no_file(&mut self) { + self.file = None; + self.pending = Vec::new(); + self.dropped = 0; + self.capped = true; + } + + fn append(&mut self, line: &str) { + if self.capped { + return; + } + let Some(file) = self.file.as_mut() else { return }; + let len = u64::try_from(line.len()).unwrap_or(u64::MAX); + let text = if self.written.saturating_add(len) > self.max_bytes { + self.capped = true; + format!("log file reached {} bytes; later records go to standard error only\n", self.max_bytes) + } else { + self.written = self.written.saturating_add(len); + line.to_owned() + }; + // A full disk or a vanished file must not take the app down: drop the file sink. + if file.write_all(text.as_bytes()).is_err() { + self.file = None; + self.capped = true; + } + } +} + +/// The installed logger: a [`Filter`], standard error and a [`Sink`]. +pub struct AppLogger { + filter: Filter, + stderr: bool, + sink: Mutex, +} + +impl AppLogger { + pub fn new(filter: Filter, stderr: bool) -> AppLogger { + AppLogger { filter, stderr, sink: Mutex::new(Sink::new(MAX_FILE_BYTES)) } + } + + /// Rotate the logs in `dir` and send records to the fresh file; returns its path. On an error + /// standard error stays the only sink. + pub fn attach_dir(&self, dir: &Path) -> Result { + // A thread that panicked while logging leaves the sink usable: take it back. + let mut sink = self.sink.lock().unwrap_or_else(PoisonError::into_inner); + match rotate(dir) { + Ok((path, file)) => { + sink.attach(file); + Ok(path) + } + Err(e) => { + sink.no_file(); + Err(format!("{}: {e}", dir.display())) + } + } + } + + /// No log file for this run (`LIGHTCRAFT_NO_PREFS`): log to standard error only. + pub fn no_file(&self) { + self.sink.lock().unwrap_or_else(PoisonError::into_inner).no_file(); + } + + /// Add a panic report to the log file only (the default panic hook has printed it on standard + /// error), whatever the filter. Skipped when the sink is busy: the panic may have happened on + /// this very thread while it held the lock, and waiting would hang the app. + pub fn record_panic(&self, thread: &str, report: &str) { + let line = format_line(×tamp(SystemTime::now()), log::Level::Error, thread, "lightcraft", report); + let mut sink = match self.sink.try_lock() { + Ok(sink) => sink, + Err(TryLockError::Poisoned(p)) => p.into_inner(), + Err(TryLockError::WouldBlock) => return, + }; + sink.write(&line); + } +} + +impl log::Log for AppLogger { + fn enabled(&self, metadata: &log::Metadata<'_>) -> bool { + metadata.level() <= self.filter.level_for(metadata.target()) + } + + fn log(&self, record: &log::Record<'_>) { + if !self.enabled(record.metadata()) { + return; + } + // Formatted before the sink is locked, so a message that logs while it is formatted can't deadlock. + let thread = std::thread::current(); + let line = + format_line(×tamp(SystemTime::now()), record.level(), thread.name().unwrap_or("?"), record.target(), &record.args().to_string()); + if self.stderr { + // No terminal (a Windows GUI build, a closed pipe) is not an error worth reporting. + let _ = std::io::stderr().write_all(line.as_bytes()); + } + self.sink.lock().unwrap_or_else(PoisonError::into_inner).write(&line); + } + + fn flush(&self) {} +} + +/// Install the logger (filter from [`filter_spec`]); `None` when another logger was installed +/// first. Call [`AppLogger::attach_dir`] (or [`AppLogger::no_file`]) once the arguments are parsed. +pub fn install() -> Option<&'static AppLogger> { + static LOGGER: OnceLock = OnceLock::new(); + let var = |name| std::env::var(name).ok(); + let filter = Filter::parse(&filter_spec(var("LIGHTCRAFT_LOG").as_deref(), var("RUST_LOG").as_deref())); + let max = filter.max(); + let logger = LOGGER.get_or_init(|| AppLogger::new(filter, true)); + log::set_logger(logger).ok()?; + log::set_max_level(max); + Some(logger) +} + +/// Chain a panic hook after the current one (the engine guard's: standard error plus +/// `lightcraft-panics.log` in the temp folder) that also puts the panic in the log file. +pub fn record_panics(logger: &'static AppLogger) { + let previous = std::panic::take_hook(); + std::panic::set_hook(Box::new(move |info| { + previous(info); + let thread = std::thread::current(); + let at = info.location().map(|l| format!(" at {}:{}", l.file(), l.line())).unwrap_or_default(); + let report = format!("panic{at}: {}", lightcraft_engine::guard::panic_message(info.payload())); + logger.record_panic(thread.name().unwrap_or("?"), &report); + })); +} + +#[cfg(test)] +mod tests { + use super::*; + use std::sync::atomic::{AtomicUsize, Ordering}; + use std::time::{Duration, UNIX_EPOCH}; + + fn temp_dir(tag: &str) -> PathBuf { + static N: AtomicUsize = AtomicUsize::new(0); + let d = std::env::temp_dir().join(format!("lightcraft-logging-{tag}-{}-{}", std::process::id(), N.fetch_add(1, Ordering::Relaxed))); + let _ = std::fs::remove_dir_all(&d); + d + } + + fn read(path: &Path) -> String { + std::fs::read_to_string(path).expect("read log") + } + + fn record(logger: &AppLogger, level: log::Level, target: &str, msg: &str) { + use log::Log; + logger.log(&log::Record::builder().level(level).target(target).args(format_args!("{msg}")).build()); + } + + /// The logger this replaced (`StderrLog`): warnings and errors from every target, `info` and + /// `debug` only from LightCraft's own crates, and only when `LIGHTCRAFT_LOG` asked for them. + fn old_stderr_log_enabled(lightcraft_log: &str, level: log::Level, target: &str) -> bool { + let max = match lightcraft_log { + "debug" => LevelFilter::Debug, + "info" => LevelFilter::Info, + _ => LevelFilter::Warn, + }; + level <= max && (level <= log::Level::Warn || target.starts_with("lightcraft")) + } + + #[test] + fn the_default_filter_shows_lightcraft_info_and_other_crates_warnings() { + let f = Filter::parse(DEFAULT_FILTER); + assert_eq!(f.level_for("lightcraft"), LevelFilter::Info); + assert_eq!(f.level_for("lightcraft::control_server"), LevelFilter::Info); + assert_eq!(f.level_for("lightcraft_engine::guard"), LevelFilter::Info); + assert_eq!(f.level_for("lightcraft_ui_egui::render"), LevelFilter::Info); + assert_eq!(f.level_for("wgpu_core::device"), LevelFilter::Warn); + assert_eq!(f.level_for("naga"), LevelFilter::Warn); + assert_eq!(f.max(), LevelFilter::Info); + assert_eq!(filter_spec(None, None), DEFAULT_FILTER); + // An empty variable is the same as an unset one. + assert_eq!(filter_spec(Some(""), Some(" ")), DEFAULT_FILTER); + } + + /// `LIGHTCRAFT_LOG` keeps working exactly as it did with the old stderr-only logger. + #[test] + fn lightcraft_log_keeps_its_meaning() { + let targets = + ["lightcraft", "lightcraft::control_server", "lightcraft_engine::segment", "lightcraft_gpu", "wgpu_core::device", "naga", "eframe"]; + let levels = [log::Level::Error, log::Level::Warn, log::Level::Info, log::Level::Debug, log::Level::Trace]; + for value in ["info", "debug", "warn", "error", "verbose", "INFO"] { + let f = Filter::parse(&filter_spec(Some(value), None)); + for target in targets { + for level in levels { + assert_eq!(level <= f.level_for(target), old_stderr_log_enabled(value, level, target), "LIGHTCRAFT_LOG={value} {level} {target}"); + } + } + } + assert_eq!(Filter::parse(&filter_spec(Some("debug"), None)).max(), LevelFilter::Debug); + assert_eq!(Filter::parse(&filter_spec(Some("warn"), None)).max(), LevelFilter::Warn); + } + + #[test] + fn rust_log_replaces_the_default_and_lightcraft_log_wins_over_it() { + assert_eq!(filter_spec(None, Some("warn,wgpu_core=info")), "warn,wgpu_core=info"); + let f = Filter::parse(&filter_spec(None, Some("debug"))); + assert_eq!(f.level_for("eframe"), LevelFilter::Debug); + // The app's own variable is the more specific choice. + let f = Filter::parse(&filter_spec(Some("info"), Some("trace"))); + assert_eq!(f.level_for("eframe"), LevelFilter::Warn); + assert_eq!(f.level_for("lightcraft_engine"), LevelFilter::Info); + } + + #[test] + fn directives_follow_env_logger_and_the_most_specific_one_wins() { + let f = Filter::parse("info,wgpu_core=error,lightcraft_pipeline=trace,lightcraft_pipeline::tiles=off"); + assert_eq!(f.level_for("eframe"), LevelFilter::Info); + assert_eq!(f.level_for("wgpu_core::instance"), LevelFilter::Error); + assert_eq!(f.level_for("lightcraft_pipeline::stage"), LevelFilter::Trace); + assert_eq!(f.level_for("lightcraft_pipeline::tiles"), LevelFilter::Off); + assert_eq!(f.max(), LevelFilter::Trace); + // A module name is matched at `::` boundaries, not as a bare prefix. + assert_eq!(f.level_for("wgpu_core_extra"), LevelFilter::Info); + // A trailing `*` is a prefix. + assert_eq!(Filter::parse("lightcraft*=debug").level_for("lightcraft_raw::cr2"), LevelFilter::Debug); + assert_eq!(Filter::parse("lightcraft*=debug").level_for("eframe"), LevelFilter::Error); + // A bare target name sets that target to the most verbose level, as env_logger does. + assert_eq!(Filter::parse("naga").level_for("naga::front"), LevelFilter::Trace); + assert_eq!(Filter::parse("naga").level_for("eframe"), LevelFilter::Error); + // Levels are case-insensitive and surrounding blanks are ignored. + assert_eq!(Filter::parse(" WARN , lightcraft = Debug ").level_for("lightcraft"), LevelFilter::Debug); + assert_eq!(Filter::parse(" WARN , lightcraft = Debug ").level_for("eframe"), LevelFilter::Warn); + } + + #[test] + fn hostile_specs_never_panic_and_fall_back_sensibly() { + let long = "x".repeat(100_000); + let many = "a=info,".repeat(10_000); + for spec in [ + "", + ",,,", + "=", + "==", + "=debug", + "lightcraft=", + "nonsense=loud", + "🦀=info", + "*", + "*=", + "=*", + "a=b=c", + "warn,,lightcraft=DEBUG", + "\0", + "é::ü=trace", + &long, + &many, + ] { + let f = Filter::parse(spec); + for target in ["", "lightcraft", "🦀", "a::b", "*"] { + let _ = f.level_for(target); + } + let _ = f.max(); + let _ = filter_spec(Some(spec), Some(spec)); + } + assert_eq!(Filter::parse("warn,,lightcraft=DEBUG").level_for("lightcraft"), LevelFilter::Debug); + // An unknown level is skipped: that target keeps the default. + assert_eq!(Filter::parse("nonsense=loud").level_for("nonsense"), LevelFilter::Error); + assert_eq!(Filter::parse("lightcraft=").level_for("lightcraft"), LevelFilter::Error); + assert_eq!(Filter::parse("=debug").max(), LevelFilter::Error); + assert_eq!(Filter::parse("🦀=info").level_for("🦀::claw"), LevelFilter::Info); + } + + #[test] + fn timestamps_are_utc_with_milliseconds() { + assert_eq!(timestamp(UNIX_EPOCH), "1970-01-01T00:00:00.000Z"); + assert_eq!(timestamp(UNIX_EPOCH + Duration::from_millis(1_791_446_357_728)), "2026-10-08T07:59:17.728Z"); + // Leap day, and the day after it. + assert_eq!(timestamp(UNIX_EPOCH + Duration::from_secs(1_709_164_800)), "2024-02-29T00:00:00.000Z"); + assert_eq!(timestamp(UNIX_EPOCH + Duration::from_secs(1_709_251_199)), "2024-02-29T23:59:59.000Z"); + assert_eq!(timestamp(UNIX_EPOCH + Duration::from_secs(1_709_251_200)), "2024-03-01T00:00:00.000Z"); + // 2000 is a leap year (divisible by 400), 2100 is not. + assert_eq!(timestamp(UNIX_EPOCH + Duration::from_secs(951_782_400)), "2000-02-29T00:00:00.000Z"); + assert_eq!(timestamp(UNIX_EPOCH + Duration::from_secs(4_107_542_400)), "2100-03-01T00:00:00.000Z"); + // Before the epoch (a clock set wrong) clamps instead of panicking. + assert_eq!(timestamp(UNIX_EPOCH - Duration::from_secs(5)), "1970-01-01T00:00:00.000Z"); + } + + #[test] + fn a_line_carries_time_level_thread_target_and_message() { + assert_eq!( + format_line("2026-10-08T07:59:17.728Z", log::Level::Info, "main", "lightcraft", "library /photos: 12 photos"), + "2026-10-08T07:59:17.728Z INFO [main] lightcraft: library /photos: 12 photos\n" + ); + // A multi-line message keeps its lines; the record still ends in one newline. + assert!(format_line("t", log::Level::Error, "w", "x", "a\nb\n").ends_with("x: a\nb\n")); + } + + #[test] + fn rotation_keeps_the_previous_logs_and_starts_an_empty_file() { + let dir = temp_dir("rotate"); + for run in 1..=4 { + let (path, _file) = rotate(&dir).expect("rotate"); + assert_eq!(path, dir.join(LOG_FILE)); + assert_eq!(read(&path), ""); + std::fs::write(&path, format!("run {run}")).expect("write"); + } + assert_eq!(read(&dir.join(LOG_FILE)), "run 4"); + assert_eq!(read(&dir.join("lightcraft.1.log")), "run 3"); + assert_eq!(read(&dir.join("lightcraft.2.log")), "run 2"); + assert!(!dir.join("lightcraft.3.log").exists(), "only {KEEP} old logs are kept"); + let _ = std::fs::remove_dir_all(&dir); + } + + #[test] + fn rotation_into_an_unusable_directory_is_an_error_not_a_panic() { + let dir = temp_dir("blocked"); + std::fs::create_dir_all(dir.parent().expect("parent")).expect("tmp"); + std::fs::write(&dir, "a file where the directory should be").expect("block"); + assert!(rotate(&dir).is_err()); + let _ = std::fs::remove_file(&dir); + } + + #[test] + fn lines_logged_before_the_file_exists_are_written_when_it_is_attached() { + let dir = temp_dir("pending"); + let (path, file) = rotate(&dir).expect("rotate"); + let mut sink = Sink::new(MAX_FILE_BYTES); + sink.write("early 1\n"); + sink.write("early 2\n"); + sink.attach(file); + sink.write("late\n"); + assert_eq!(read(&path), "early 1\nearly 2\nlate\n"); + let _ = std::fs::remove_dir_all(&dir); + } + + #[test] + fn the_pending_buffer_is_bounded_and_says_how_much_it_dropped() { + let dir = temp_dir("bounded"); + let (path, file) = rotate(&dir).expect("rotate"); + let mut sink = Sink::new(MAX_FILE_BYTES); + for i in 0..MAX_PENDING + 5 { + sink.write(&format!("line {i}\n")); + } + sink.attach(file); + let text = read(&path); + assert_eq!(text.lines().filter(|l| l.starts_with("line ")).count(), MAX_PENDING); + assert!(text.contains("5 earlier log lines were dropped"), "{text}"); + assert!(text.contains("line 0\n") && !text.contains(&format!("line {MAX_PENDING}\n"))); + let _ = std::fs::remove_dir_all(&dir); + } + + #[test] + fn exactly_a_full_pending_buffer_drops_nothing() { + let dir = temp_dir("full"); + let (path, file) = rotate(&dir).expect("rotate"); + let mut sink = Sink::new(MAX_FILE_BYTES); + for i in 0..MAX_PENDING { + sink.write(&format!("line {i}\n")); + } + sink.attach(file); + let text = read(&path); + assert_eq!(text.lines().count(), MAX_PENDING); + assert!(!text.contains("dropped"), "{text}"); + let _ = std::fs::remove_dir_all(&dir); + } + + #[test] + fn without_a_file_nothing_is_kept_in_memory() { + let mut sink = Sink::new(MAX_FILE_BYTES); + sink.write("early\n"); + sink.no_file(); + sink.write("late\n"); + assert!(sink.pending.is_empty() && sink.dropped == 0); + } + + #[test] + fn the_file_stops_at_its_size_cap_with_one_note() { + let dir = temp_dir("cap"); + let (path, file) = rotate(&dir).expect("rotate"); + let mut sink = Sink::new(100); + sink.attach(file); + for i in 0..50 { + sink.write(&format!("line {i:02} with some padding\n")); + } + let text = read(&path); + assert_eq!(text.lines().filter(|l| l.starts_with("line ")).count(), 3, "{text}"); + assert_eq!(text.matches("log file reached").count(), 1, "{text}"); + let _ = std::fs::remove_dir_all(&dir); + } + + #[test] + fn the_logger_filters_records_and_writes_them_to_the_attached_file() { + use log::Log; + let dir = temp_dir("logger"); + let logger = AppLogger::new(Filter::parse(DEFAULT_FILTER), false); + record(&logger, log::Level::Info, "lightcraft", "before the settings directory"); + record(&logger, log::Level::Info, "wgpu_core::device", "too chatty"); + let path = logger.attach_dir(&dir).expect("attach"); + assert_eq!(path, dir.join(LOG_FILE)); + record(&logger, log::Level::Warn, "wgpu_hal::vulkan", "a real warning"); + record(&logger, log::Level::Error, "lightcraft_engine::guard", "`develop.reset` failed unexpectedly: boom"); + record(&logger, log::Level::Debug, "lightcraft", "below info"); + let text = read(&path); + let lines: Vec<&str> = text.lines().collect(); + assert_eq!(lines.len(), 3, "{text}"); + assert!(lines[0].contains(" INFO [") && lines[0].ends_with("lightcraft: before the settings directory"), "{text}"); + assert!(lines[1].ends_with("wgpu_hal::vulkan: a real warning"), "{text}"); + assert!(lines[2].contains(" ERROR [") && lines[2].ends_with("lightcraft_engine::guard: `develop.reset` failed unexpectedly: boom"), "{text}"); + assert!(logger.enabled(&log::Metadata::builder().level(log::Level::Info).target("lightcraft_ui_egui").build())); + assert!(!logger.enabled(&log::Metadata::builder().level(log::Level::Info).target("naga").build())); + // Attaching again rotates: the first log becomes `.1`. + logger.attach_dir(&dir).expect("attach again"); + assert_eq!(read(&dir.join("lightcraft.1.log")), text); + let _ = std::fs::remove_dir_all(&dir); + } + + #[test] + fn records_from_many_threads_are_whole_lines() { + let dir = temp_dir("threads"); + let logger = std::sync::Arc::new(AppLogger::new(Filter::parse("info"), false)); + let path = logger.attach_dir(&dir).expect("attach"); + let workers: Vec<_> = (0..8) + .map(|t| { + let l = std::sync::Arc::clone(&logger); + std::thread::spawn(move || { + for i in 0..100 { + record(&l, log::Level::Info, "lightcraft", &format!("thread {t} record {i}")); + } + }) + }) + .collect(); + for w in workers { + w.join().expect("worker"); + } + let text = read(&path); + assert_eq!(text.lines().count(), 800); + assert!(text.lines().all(|l| l.contains(" lightcraft: thread ")), "{text}"); + let _ = std::fs::remove_dir_all(&dir); + } + + #[test] + fn attaching_to_an_unusable_directory_reports_an_error_and_keeps_logging() { + let dir = temp_dir("unusable"); + std::fs::create_dir_all(dir.parent().expect("parent")).expect("tmp"); + std::fs::write(&dir, "not a directory").expect("block"); + let logger = AppLogger::new(Filter::parse("info"), false); + record(&logger, log::Level::Info, "lightcraft", "early"); + assert!(logger.attach_dir(&dir).is_err()); + record(&logger, log::Level::Error, "x", "still fine"); + // Nothing waits in memory for a file that won't come. + assert!(logger.sink.lock().expect("sink").pending.is_empty()); + let _ = std::fs::remove_file(&dir); + } + + #[test] + fn a_poisoned_sink_still_logs() { + let dir = temp_dir("poison"); + let logger = std::sync::Arc::new(AppLogger::new(Filter::parse("info"), false)); + let l = std::sync::Arc::clone(&logger); + let _ = std::thread::spawn(move || { + let _guard = l.sink.lock().expect("lock"); + panic!("poison the sink"); + }) + .join(); + assert!(logger.sink.is_poisoned()); + let path = logger.attach_dir(&dir).expect("attach"); + record(&logger, log::Level::Warn, "lightcraft", "after the poison"); + assert!(read(&path).contains("lightcraft: after the poison")); + let _ = std::fs::remove_dir_all(&dir); + } + + /// The panic hook's report lands in the file, and never deadlocks when the panic happened + /// while this thread held the sink. + #[test] + fn a_panic_report_goes_to_the_file_and_never_deadlocks() { + let dir = temp_dir("panic"); + let logger = AppLogger::new(Filter::parse("off"), false); + let path = logger.attach_dir(&dir).expect("attach"); + logger.record_panic("render", "panicked at crates/x/src/lib.rs:3:5:\nboom"); + { + let _held = logger.sink.lock().expect("lock"); + logger.record_panic("main", "while the sink is held"); + } + let text = read(&path); + assert!(text.contains(" ERROR [render] lightcraft: panicked at crates/x/src/lib.rs:3:5:\nboom\n"), "{text}"); + assert!(!text.contains("while the sink is held"), "{text}"); + let _ = std::fs::remove_dir_all(&dir); + } +} diff --git a/apps/lightcraft/src/main.rs b/apps/lightcraft/src/main.rs index 4bb1c6ff..107594ee 100644 --- a/apps/lightcraft/src/main.rs +++ b/apps/lightcraft/src/main.rs @@ -24,6 +24,7 @@ mod alloc_release; mod control_server; +mod logging; #[cfg(target_os = "macos")] mod native_menu; @@ -63,10 +64,10 @@ impl eframe::App for App { } fn on_exit(&mut self) { if let Err(e) = self.1.save(&self.0) { - eprintln!("lightcraft: {e}"); + log::error!("{e}"); } if let Err(e) = self.0.session.close_library() { - eprintln!("lightcraft: saving the library failed: {e}"); + log::error!("saving the library failed: {e}"); } } } @@ -216,8 +217,8 @@ impl PrefsWriter { match self.save(app) { Ok(()) => self.failing = false, Err(e) => { - eprintln!("lightcraft: {e}"); if !self.failing { + log::warn!("{e}"); app.notices.push(format!("{e}. LightCraft keeps trying.")); } self.failing = true; @@ -366,7 +367,7 @@ fn open_session(in_memory: bool, dir: Option, seed_demo: boo } let unopened = || Session::new().with_fs().with_system_clock(); let Some(dir) = dir else { - eprintln!("lightcraft: no library location (set --library or LIGHTCRAFT_LIBRARY)"); + log::warn!("no library location (set --library or LIGHTCRAFT_LIBRARY)"); return (unopened(), Some(LibraryProblem::new("", "There is no home folder to keep the library in. Choose a folder for it."))); }; let t0 = std::time::Instant::now(); @@ -374,8 +375,8 @@ fn open_session(in_memory: bool, dir: Option, seed_demo: boo match s.open_library(&dir, seed_demo) { Ok(r) => { let (replayed, torn) = (r.replayed, r.torn_bytes); - eprintln!( - "lightcraft: library {}: {} photos, {replayed} log records replayed{} ({:.1} ms)", + log::info!( + "library {}: {} photos, {replayed} log records replayed{} ({:.1} ms)", dir.display(), s.catalog.len(), if torn > 0 { ", torn tail repaired" } else { "" }, @@ -384,7 +385,7 @@ fn open_session(in_memory: bool, dir: Option, seed_demo: boo (s, None) } Err(e) => { - eprintln!("lightcraft: can't open library {}: {e}", dir.display()); + log::error!("can't open library {}: {e}", dir.display()); (unopened(), Some(LibraryProblem::new(dir.to_string_lossy(), e.to_string()))) } } @@ -409,41 +410,40 @@ ENVIRONMENT: LIGHTCRAFT_SAM3_DIR=DIR the SAM 3 model for Object / Describe masks (default: /models/sam3; optional: LightCraft offers to download it when first needed) LIGHTCRAFT_SAM3_MIRRORS=URL,… where to download the SAM 3 model from (base URLs, tried in order) + LIGHTCRAFT_LOG=info|debug more log detail from LightCraft itself (default: info from LightCraft, warnings + from other crates); else RUST_LOG=. Records go to stderr and to + /logs/lightcraft.log (previous runs: lightcraft.1.log, lightcraft.2.log) "; -/// Warnings and errors (failed commands, AI mask analysis) on stderr; `LIGHTCRAFT_LOG=info` -/// (or `debug`) shows more. -struct StderrLog(log::LevelFilter); - -impl log::Log for StderrLog { - fn enabled(&self, m: &log::Metadata) -> bool { - m.level() <= self.0 && (m.level() <= log::Level::Warn || m.target().starts_with("lightcraft")) +/// Where the log files live: `logs` in the settings folder, next to `ui.json` (see `logging`). +/// None with `LIGHTCRAFT_NO_PREFS` (tests, scripts), so those runs don't rotate away the user's logs. +fn log_dir() -> Option { + if std::env::var_os("LIGHTCRAFT_NO_PREFS").is_some() { + return None; } - fn log(&self, r: &log::Record) { - if self.enabled(r.metadata()) { - eprintln!("lightcraft: {} {}: {}", r.level(), r.target(), r.args()); - } - } - fn flush(&self) {} + config_dir().map(|d| d.join("logs")) } -fn install_log() { - let level = match std::env::var("LIGHTCRAFT_LOG").unwrap_or_default().as_str() { - "debug" => log::LevelFilter::Debug, - "info" => log::LevelFilter::Info, - _ => log::LevelFilter::Warn, - }; - static LOGGER: std::sync::OnceLock = std::sync::OnceLock::new(); - if log::set_logger(LOGGER.get_or_init(|| StderrLog(level))).is_ok() { - log::set_max_level(level); +/// The control port `value` names; a value that isn't a port is logged and ignored (`what` is +/// the option or variable it came from). Logged before the log file opens, so it is kept for it. +fn control_port_from(what: &str, value: Option) -> Option { + let value = value?; + let port = value.trim().parse().ok(); + if port.is_none() { + log::warn!("{what} {value:?} is not a port number; no control server"); } + port } fn main() -> eframe::Result { + // First, so every start-up record is kept for the log file (`logging`). + let logger = logging::install(); lightcraft_engine::guard::install_hook(std::env::temp_dir().join("lightcraft-panics.log")); - install_log(); + if let Some(logger) = logger { + logging::record_panics(logger); + } alloc_release::install(); - let mut control_port: Option = std::env::var("LIGHTCRAFT_CONTROL_PORT").ok().and_then(|p| p.parse().ok()); + let mut control_port = control_port_from("LIGHTCRAFT_CONTROL_PORT", std::env::var("LIGHTCRAFT_CONTROL_PORT").ok().filter(|v| !v.is_empty())); let mut files = Vec::new(); let mut seed_demo = true; let mut in_memory = false; @@ -451,7 +451,7 @@ fn main() -> eframe::Result { let mut args = std::env::args().skip(1); while let Some(a) = args.next() { match a.as_str() { - "--control" => control_port = args.next().and_then(|p| p.parse().ok()), + "--control" => control_port = control_port_from("--control", Some(args.next().unwrap_or_default())), "--library" => library_dir = args.next().map(std::path::PathBuf::from), "--no-demo" => seed_demo = false, "--memory" | "--demo" => in_memory = true, @@ -470,6 +470,18 @@ fn main() -> eframe::Result { _ => files.push(a), } } + // The log file: opened after the arguments, so `--version`, `--help` and a usage error leave + // no file behind. Records logged until now are written to it first. + if let Some(logger) = logger { + match log_dir() { + Some(dir) => match logger.attach_dir(&dir) { + Ok(path) => log::info!("LightCraft {}, log file {}", env!("CARGO_PKG_VERSION"), path.display()), + // The file sink has given up by now, so this goes to standard error only. + Err(e) => log::warn!("no log file: {e}"), + }, + None => logger.no_file(), + } + } let (prefs, prefs_warning, keep_prefs_file) = load_prefs(); // --library, else the library last opened from Settings, else the default location let library_dir = library_dir.or_else(|| { @@ -616,6 +628,16 @@ mod tests { assert!(!b.is_empty()); } + #[test] + fn a_control_port_that_is_not_a_number_is_ignored() { + assert_eq!(control_port_from("--control", Some("7980".into())), Some(7980)); + assert_eq!(control_port_from("--control", Some(" 18001 ".into())), Some(18001)); + assert_eq!(control_port_from("--control", Some("nope".into())), None); + assert_eq!(control_port_from("--control", Some(String::new())), None); + assert_eq!(control_port_from("--control", Some("70000".into())), None); + assert_eq!(control_port_from("LIGHTCRAFT_CONTROL_PORT", None), None); + } + #[test] fn ui_json_is_written_atomically() { let d = dir("atomic"); diff --git a/docs/whats-new.md b/docs/whats-new.md index ea0243f4..e614d71d 100644 --- a/docs/whats-new.md +++ b/docs/whats-new.md @@ -14,6 +14,10 @@ - 23 new built-in presets: Portrait, Landscape, Urban, Food, Seasons, Vintage and B&W toners. ### Reliability +- The desktop app keeps a log file: `logs/lightcraft.log` in its settings folder (Linux `~/.config/lightcraft/logs/`), + with the logs of the two previous runs beside it, so warnings and crashes of a run started from a desktop menu or the + Dock can be attached to a bug report. `LIGHTCRAFT_LOG` works as before; `RUST_LOG` takes env_logger-style + directives. See README → Quick start → Logs. - LightCraft no longer crashes at launch on Windows PCs whose Vulkan driver is broken (issue #136, e.g. some Intel UHD 630 drivers): on Windows the window and GPU rendering use DirectX 12 only and never load the Vulkan driver unless asked to. `LIGHTCRAFT_GPU_BACKEND=dx12 | vulkan | metal | off` (or wgpu's `WGPU_BACKEND`, which GPU rendering From 30f9eb3674cab760974d968ca68eece558c05c90 Mon Sep 17 00:00:00 2001 From: radialmonster Date: Thu, 8 Oct 2026 08:04:42 -0400 Subject: [PATCH 06/41] LR-PROF-CAMERACOLOR: list the #232 entry after the other RAW decoding entries --- docs/whats-new.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/whats-new.md b/docs/whats-new.md index 04123882..eb6051e3 100644 --- a/docs/whats-new.md +++ b/docs/whats-new.md @@ -3,10 +3,10 @@ ## October 2026 ### RAW decoding -- Sony A7R II (and other) raws whose camera JPEG is lens-corrected no longer open grey and too dark (issue #232): the - starting look is fitted to the camera JPEG away from edges when the misaligned edges spoil the fit on all pixels. - Sony ILCE-7M4 downsized lossless ARWs now decode subsampled YCbCr tiles into linear RGB, preserving RAW editing & full-resolution export instead of using embedded JPEG previews. +- Sony A7R II (and other) raws whose camera JPEG is lens-corrected no longer open grey and too dark (issue #232): the + starting look is fitted to the camera JPEG away from edges when the misaligned edges spoil the fit on all pixels. ### Presets and profiles - Import presets from other editors: XMP presets, classic `.lrtemplate` files, "DNG presets" from mobile apps and `.zip` From 6277af55bf1cd2aa044607ad5f69266c1a5c36a6 Mon Sep 17 00:00:00 2001 From: Hanmo Date: Thu, 8 Oct 2026 20:25:37 +0800 Subject: [PATCH 07/41] M1: restore library culling shortcuts and Classic shift actions Keep macOS plain-key ratings, labels and flags with egui, adapting the native menu ownership correction from PR #261 (4e012a5, Gardy). Add Shift+6-9 and contextual grid Shift+P, recognize shifted digit punctuation, and document the verified Lightroom Classic mapping. Cover batch changes and undo, exactly-once advance, Compare targets, text and crop context, physical number keys and native menu ownership. Verified native demo/control screenshots and all seven cargo xtask ci gates. Co-authored-by: Gardy --- apps/lightcraft/src/native_menu.rs | 65 +++++++-- crates/ui-egui/src/menubar.rs | 4 +- crates/ui-egui/src/panels/dialogs.rs | 15 +- crates/ui-egui/src/shortcuts.rs | 199 ++++++++++++++++++++++++++- docs/library-shortcuts.md | 41 ++++++ docs/parity.md | 22 +-- docs/whats-new.md | 5 + 7 files changed, 328 insertions(+), 23 deletions(-) create mode 100644 docs/library-shortcuts.md diff --git a/apps/lightcraft/src/native_menu.rs b/apps/lightcraft/src/native_menu.rs index 557c9abf..02d33db3 100644 --- a/apps/lightcraft/src/native_menu.rs +++ b/apps/lightcraft/src/native_menu.rs @@ -2,9 +2,12 @@ //! the in-window menus ([`lightcraft_ui_egui::menubar`]): the registry's menu paths, shortcuts, //! enabled and checked state, and live labels ("Undo Exposure"). //! -//! Key handling: a native key equivalent consumes the key press before egui sees it, so -//! - shortcuts installed natively are listed in `LightcraftApp::native_shortcuts` and skipped by -//! the egui shortcut handler (nothing fires twice); +//! Key handling: AppKit offers a key press to the menu bar only when it carries ⌘ or ⌃, or is a +//! function key (F1…). Every other key (`E`, `1`, `⇧P`, `⌥Y`…) goes straight to the window, whose +//! winit view always takes it, so a menu item never fires from it. Hence +//! - shortcuts the menu bar really receives ([`menu_delivers`]) are listed in +//! `LightcraftApp::native_shortcuts` and skipped by the egui shortcut handler (nothing fires +//! twice); the others stay on their menu items for display and egui runs them; //! - while a text field has keyboard focus, accelerators without ⌘ (`G`, `1`, `Delete`…) and the //! text-editing ones (⌘A/⌘C/⌘V/⌘X/⌘Z) are removed, so typing and text editing work; they come //! back when the field loses focus. @@ -146,6 +149,18 @@ fn yields_to_text(sc: &str) -> bool { !sc.contains("Cmd") && !sc.contains("Ctrl") || TEXT_EDIT.contains(&sc) } +/// Whether AppKit hands this shortcut to the menu bar: only key presses with ⌘ or ⌃, and function +/// keys. A plain `C` or `⇧P` never reaches it (the window's view takes the key), so egui has to run +/// those even though their menu item shows the key. +fn menu_delivers(sc: &str) -> bool { + sc.split('+').any(|part| matches!(part, "Cmd" | "Ctrl") || part.strip_prefix('F').is_some_and(|n| n.parse::().is_ok())) +} + +/// The shortcuts the menu bar runs itself, for `LightcraftApp::native_shortcuts`. +fn owned_by_menu<'a>(installed: impl Iterator, text_focus: bool) -> HashSet { + installed.filter(|sc| menu_delivers(sc) && !(text_focus && yields_to_text(sc))).map(str::to_string).collect() +} + /// `&` marks a mnemonic in muda labels. fn label_text(s: &str) -> String { lightcraft_ui_egui::i18n::tr(s).replace('&', "&&") @@ -329,13 +344,8 @@ impl NativeMenu { /// Tell the egui shortcut handler which shortcuts the menu bar currently owns. fn publish_shortcuts(&self, app: &mut LightcraftApp) { - app.native_shortcuts = self - .items - .values() - .filter(|i| i.accel.is_some()) - .filter_map(|i| i.shortcut.clone()) - .filter(|sc| !(self.text_focus && yields_to_text(sc))) - .collect::>(); + let installed = self.items.values().filter(|i| i.accel.is_some()).filter_map(|i| i.shortcut.as_deref()); + app.native_shortcuts = owned_by_menu(installed, self.text_focus); app.native_shortcuts.insert(SETTINGS_KEY.to_string()); } @@ -467,4 +477,39 @@ mod tests { assert!(accelerator(sc).is_some(), "{sc}"); } } + + /// Single keys shown in the menu bar (E, C, ⇧P, ratings…) never reach it on macOS, so egui must + /// keep handling them; ⌘ / ⌃ combinations and function keys are the menu bar's own. + #[test] + fn single_key_shortcuts_stay_with_egui() { + assert!(menu_delivers("Cmd+Shift+H") && menu_delivers("Ctrl+H") && menu_delivers("F2") && menu_delivers("Cmd+F11")); + assert!(!menu_delivers("E") && !menu_delivers("Shift+P") && !menu_delivers("Alt+Y") && !menu_delivers("1") && !menu_delivers("F")); + let app = LightcraftApp::new(lightcraft_engine::Session::with_demo(), Default::default()); + fn all(n: &[MenuNode], out: &mut Vec) { + for x in n { + match x { + MenuNode::Item { shortcut: Some(s), .. } if accelerator(s).is_some() => out.push(s.clone()), + MenuNode::Submenu { children, .. } => all(children, out), + _ => {} + } + } + } + let mut installed = Vec::new(); + for (_, v) in menu_bar(&app) { + all(&v, &mut installed); + } + for sc in ["E", "C", "Shift+P", "Cmd+Shift+H", "F2"] { + assert!(installed.iter().any(|s| s == sc), "{sc} is in the menu bar"); + } + let owned = owned_by_menu(installed.iter().map(String::as_str), false); + for sc in ["E", "C", "Shift+P", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "P", "U"] { + assert!(installed.iter().any(|s| s == sc), "{sc} is displayed in the menu"); + assert!(!owned.contains(sc), "{sc} must be left to egui"); + } + for sc in ["Cmd+Shift+H", "F2"] { + assert!(owned.contains(sc), "{sc} is run by the menu bar"); + } + // while typing, the menu bar gives up F2 too (the text field has it) + assert!(!owned_by_menu(installed.iter().map(String::as_str), true).contains("F2")); + } } diff --git a/crates/ui-egui/src/menubar.rs b/crates/ui-egui/src/menubar.rs index 7e8054f2..5411f646 100644 --- a/crates/ui-egui/src/menubar.rs +++ b/crates/ui-egui/src/menubar.rs @@ -555,7 +555,9 @@ fn expanded(app: &LightcraftApp, name: &str) -> Option> { } fn node(app: &LightcraftApp, e: &MenuEntry) -> MenuNode { - item(&e.id, Value::Null, live_label(app, &e.id, &e.label), e.shortcut.as_deref(), e.enabled, checked(app, &e.id)) + // Shift+P picks and advances in Library; don't advertise it on Presets in those views. + let shortcut = if e.id == "panel.presets" && crate::shortcuts::library_grid(app) { None } else { e.shortcut.as_deref() }; + item(&e.id, Value::Null, live_label(app, &e.id, &e.label), shortcut, e.enabled, checked(app, &e.id)) } /// Drop leading, trailing and doubled separators (also inside submenus) and empty submenus. diff --git a/crates/ui-egui/src/panels/dialogs.rs b/crates/ui-egui/src/panels/dialogs.rs index bc23383b..4436fe84 100644 --- a/crates/ui-egui/src/panels/dialogs.rs +++ b/crates/ui-egui/src/panels/dialogs.rs @@ -737,12 +737,23 @@ pub fn show(app: &mut LightcraftApp, ctx: &egui::Context) { egui::Grid::new("shortcuts").striped(true).show(ui, |ui| { for (id, label, sc, _) in crate::menus::ui_commands() { if let Some(sc) = sc { - ui.label(crate::i18n::tr(label)); + let grid_pick = *id == "panel.presets" && crate::shortcuts::library_grid(app); + ui.label(crate::i18n::tr(if grid_pick { "Flag as Pick" } else { label })); ui.label(*sc); - ui.label(egui::RichText::new(*id).color(t.text_dim)); + ui.label(egui::RichText::new(if grid_pick { "photo.flag" } else { id }).color(t.text_dim)); ui.end_row(); } } + for sc in ["0–5", "Shift+0–5"] { + ui.label(crate::i18n::tr("Set Rating")); + ui.label(sc); + ui.label(egui::RichText::new("photo.rate").color(t.text_dim)); + ui.end_row(); + } + ui.label(crate::i18n::tr("Set Color Label")); + ui.label("6–9"); + ui.label(egui::RichText::new("photo.label").color(t.text_dim)); + ui.end_row(); for c in lightcraft_engine::command_specs() { if let Some(sc) = c.shortcut { ui.label(crate::i18n::tr(c.label)); diff --git a/crates/ui-egui/src/shortcuts.rs b/crates/ui-egui/src/shortcuts.rs index c5d2ce16..2a501664 100644 --- a/crates/ui-egui/src/shortcuts.rs +++ b/crates/ui-egui/src/shortcuts.rs @@ -17,6 +17,10 @@ pub const ALIASES: &[(&str, &str, &str)] = &[ ("Shift+X", "photo.flag", r#"{"flag": "reject", "advance": true}"#), ("Shift+Z", "photo.flag", r#"{"flag": "pick", "advance": true}"#), ("Shift+U", "photo.flag", r#"{"flag": "none", "advance": true}"#), + ("Shift+6", "photo.label", r#"{"label": "red", "advance": true}"#), + ("Shift+7", "photo.label", r#"{"label": "yellow", "advance": true}"#), + ("Shift+8", "photo.label", r#"{"label": "green", "advance": true}"#), + ("Shift+9", "photo.label", r#"{"label": "blue", "advance": true}"#), // Shift+[ / Shift+] arrive as { / } on most layouts ("Shift+{", "brush.featherLess", "{}"), ("Shift+}", "brush.featherMore", "{}"), @@ -67,12 +71,21 @@ pub fn parse(s: &str) -> Option<(Modifiers, Key)> { fn matches(i: &egui::InputState, m: Modifiers, k: Key) -> bool { i.events.iter().any(|e| match e { - egui::Event::Key { key, pressed: true, modifiers, .. } => { + egui::Event::Key { key, physical_key, pressed: true, modifiers, .. } => { // `Ctrl` is the physical Control key (on macOS distinct from Cmd; elsewhere Cmd = Ctrl); // "Delete" (⌫ = Backspace) also matches forward-delete let ctrl_ok = if m.ctrl { modifiers.ctrl } else { !modifiers.ctrl || modifiers.command }; let cmd_ok = m.ctrl || modifiers.command == m.command; - (*key == k || (k == Key::Backspace && *key == Key::Delete)) && ctrl_ok && cmd_ok && modifiers.shift == m.shift && modifiers.alt == m.alt + // winit can report shifted digits as punctuation (Shift+1 = `!`). Recognize the + // physical number key for culling, without changing layout-aware letter shortcuts. + let shifted_digit = m.shift + && *physical_key == Some(k) + && matches!(k, Key::Num0 | Key::Num1 | Key::Num2 | Key::Num3 | Key::Num4 | Key::Num5 | Key::Num6 | Key::Num7 | Key::Num8 | Key::Num9); + (*key == k || shifted_digit || (k == Key::Backspace && *key == Key::Delete)) + && ctrl_ok + && cmd_ok + && modifiers.shift == m.shift + && modifiers.alt == m.alt } _ => false, }) @@ -171,6 +184,9 @@ pub fn handle(app: &mut LightcraftApp, ctx: &egui::Context) { app.toast(ctx, label); } else if let Some(l) = f.strip_prefix("label:") { cull(app, "photo.label", json!({"label": l}), false); + } else if f == "panel.presets" && library_grid(app) { + // Classic's Shift+P picks and advances in Library; keep the Presets binding elsewhere. + cull(app, "photo.flag", json!({"flag": "pick"}), true); } else if f == "view.softProof" && matches!(app.ui.view, crate::state::ViewMode::PhotoGrid | crate::state::ViewMode::SquareGrid) { // S in a grid: expand / collapse the stack (Lightroom's Library binding) let _ = app.run("stack.toggle", json!({})); @@ -268,9 +284,188 @@ pub fn handle(app: &mut LightcraftApp, ctx: &egui::Context) { } } +pub(crate) fn library_grid(app: &LightcraftApp) -> bool { + matches!(app.ui.view, crate::state::ViewMode::PhotoGrid | crate::state::ViewMode::SquareGrid) +} + #[cfg(test)] mod tests { + fn library() -> crate::headless::Headless { + use lightcraft_catalog::{Op, Photo, PhotoId, Source}; + let mut session = lightcraft_engine::Session::new(); + for id in 1..=4 { + let photo = Photo::new( + PhotoId(id), + Source::File { path: format!("/lightcraft-shortcuts/{id}.jpg") }, + &format!("{id}.jpg"), + "JPEG", + 100, + 100, + "2026-10-08", + ); + session.catalog.apply(Op::AddPhoto { photo: Box::new(photo) }).unwrap(); + } + session.execute("library.sort", &json!({"key": "fileName", "ascending": true})).unwrap(); + session.execute("library.select", &json!({"ids": [1]})).unwrap(); + let app = LightcraftApp::new(session, crate::Services { png: None, ..Default::default() }); + let mut h = crate::headless::Headless::new(app, [1200.0, 800.0], 1.0); + h.app.ui.view = crate::state::ViewMode::PhotoGrid; + for _ in 0..3 { + h.step(); + } + h + } + + fn key(h: &mut crate::headless::Headless, key: &str, shift: bool) { + let r = h.request("ui.key", json!({"key": key, "shift": shift}), std::time::Duration::from_secs(20)); + assert_eq!(r["ok"], true, "{r}"); + } + + #[test] + fn library_rating_labels_flags_and_undo() { + use lightcraft_catalog::{ColorLabel, Flag, PhotoId}; + for view in [crate::state::ViewMode::PhotoGrid, crate::state::ViewMode::SquareGrid] { + let mut h = library(); + h.app.ui.view = view; + h.app.session.execute("library.select", &json!({"ids": [1, 2], "active": 1})).unwrap(); + for rating in [1, 2, 3, 4, 5, 0] { + key(&mut h, &rating.to_string(), false); + for id in [1, 2] { + assert_eq!(h.app.session.catalog.photo(PhotoId(id)).unwrap().rating, rating); + } + } + for (k, label) in [("6", ColorLabel::Red), ("7", ColorLabel::Yellow), ("8", ColorLabel::Green), ("9", ColorLabel::Blue)] { + key(&mut h, k, false); + assert_eq!(h.app.session.catalog.photo(PhotoId(1)).unwrap().label, Some(label)); + assert_eq!(h.app.session.catalog.photo(PhotoId(2)).unwrap().label, Some(label)); + } + for (k, flag) in [("P", Flag::Pick), ("X", Flag::Reject), ("U", Flag::None)] { + key(&mut h, k, false); + assert_eq!(h.app.session.catalog.photo(PhotoId(1)).unwrap().flag, flag); + assert_eq!(h.app.session.catalog.photo(PhotoId(2)).unwrap().flag, flag); + } + h.app.session.execute("edit.undo", &json!({})).unwrap(); + assert_eq!(h.app.session.catalog.photo(PhotoId(1)).unwrap().flag, Flag::Reject); + assert_eq!(h.app.session.catalog.photo(PhotoId(2)).unwrap().flag, Flag::Reject); + h.app.session.execute("edit.redo", &json!({})).unwrap(); + assert_eq!(h.app.session.catalog.photo(PhotoId(1)).unwrap().flag, Flag::None); + assert_eq!(h.app.session.active(), Some(PhotoId(1))); + } + } + + #[test] + fn shift_culling_keys_advance_exactly_once() { + use lightcraft_catalog::{ColorLabel, Flag, PhotoId}; + for auto in [false, true] { + for (k, rating, label, flag) in [ + ("0", 0, None, Flag::Pick), + ("5", 5, None, Flag::Pick), + ("6", 3, Some(ColorLabel::Red), Flag::Pick), + ("7", 3, Some(ColorLabel::Yellow), Flag::Pick), + ("8", 3, Some(ColorLabel::Green), Flag::Pick), + ("9", 3, Some(ColorLabel::Blue), Flag::Pick), + ("P", 3, None, Flag::Pick), + ("X", 3, None, Flag::Reject), + ("U", 3, None, Flag::None), + ] { + let mut h = library(); + h.app.ui.auto_advance = auto; + h.app.session.execute("photo.rate", &json!({"rating": 3})).unwrap(); + h.app.session.execute("photo.pick", &json!({})).unwrap(); + key(&mut h, k, true); + let p = h.app.session.catalog.photo(PhotoId(1)).unwrap(); + assert_eq!(p.rating, rating, "Shift+{k}"); + assert_eq!(p.label, label, "Shift+{k}"); + assert_eq!(p.flag, flag, "Shift+{k}"); + assert_eq!(h.app.session.active(), Some(PhotoId(2)), "Shift+{k}, auto={auto}"); + } + } + let mut h = library(); + h.app.session.execute("library.select", &json!({"ids": [4]})).unwrap(); + key(&mut h, "6", true); + assert_eq!(h.app.session.active(), Some(PhotoId(4)), "last photo stays selected"); + } + + #[test] + fn shift_pick_preserves_presets_outside_the_library_grids() { + let mut h = library(); + h.app.ui.view = crate::state::ViewMode::SquareGrid; + key(&mut h, "P", true); + assert_eq!(h.app.session.active(), Some(lightcraft_catalog::PhotoId(2))); + assert!(!h.app.ui.presets, "grid Shift+P must not open Presets"); + assert_eq!(h.app.ui.view, crate::state::ViewMode::SquareGrid); + h.app.ui.view = crate::state::ViewMode::Detail; + key(&mut h, "P", true); + assert!(h.app.ui.presets); + assert_eq!(h.app.session.active(), Some(lightcraft_catalog::PhotoId(2))); + } + + #[test] + fn shift_label_targets_only_candidate_in_compare() { + use lightcraft_catalog::{ColorLabel, PhotoId}; + let mut h = library(); + h.app.session.execute("library.select", &json!({"ids": [1, 2], "active": 1})).unwrap(); + crate::panels::compare::enter_compare(&mut h.app).unwrap(); + key(&mut h, "6", true); + assert_eq!(h.app.session.catalog.photo(PhotoId(1)).unwrap().label, None); + assert_eq!(h.app.session.catalog.photo(PhotoId(2)).unwrap().label, Some(ColorLabel::Red)); + assert_eq!(h.app.session.active(), Some(PhotoId(3))); + } + + #[test] + fn plain_culling_keys_auto_advance_once() { + use lightcraft_catalog::PhotoId; + for k in ["0", "5", "6", "9", "P", "X", "U"] { + let mut h = library(); + h.app.ui.auto_advance = true; + key(&mut h, k, false); + assert_eq!(h.app.session.active(), Some(PhotoId(2)), "{k}"); + } + } + + #[test] + fn shifted_number_punctuation_rates_and_advances() { + use lightcraft_catalog::PhotoId; + let mut h = library(); + // A real winit event on a layout with Shift+1 = !, rather than ui.key's logical Num1. + for pressed in [true, false] { + h.app.synthetic.push(egui::Event::Key { + key: Key::Exclamationmark, + physical_key: Some(Key::Num1), + pressed, + repeat: false, + modifiers: Modifiers::SHIFT, + }); + } + h.step(); + assert_eq!(h.app.session.catalog.photo(PhotoId(1)).unwrap().rating, 1); + assert_eq!(h.app.session.active(), Some(PhotoId(2))); + } + + #[test] + fn library_keys_yield_to_search_and_crop_context() { + use lightcraft_catalog::{Flag, PhotoId}; + let mut h = library(); + let r = h.request("ui.clickWidget", json!({"id": "field:search"}), std::time::Duration::from_secs(20)); + assert_eq!(r["ok"], true, "{r}"); + for k in ["5", "6", "P", "X", "U"] { + key(&mut h, k, false); + key(&mut h, k, true); + } + let p = h.app.session.catalog.photo(PhotoId(1)).unwrap(); + assert_eq!(p.rating, 0); + assert_eq!(p.label, None); + assert_eq!(p.flag, Flag::None); + assert_eq!(h.app.session.active(), Some(PhotoId(1))); + + let mut h = library(); + h.app.ui.view = crate::state::ViewMode::Detail; + h.app.ui.right = crate::state::RightPanel::Crop; + key(&mut h, "X", false); + assert_eq!(h.app.session.catalog.photo(PhotoId(1)).unwrap().flag, Flag::None); + } + #[test] fn delete_means_the_backspace_key() { assert_eq!(parse("Delete"), Some((Modifiers::NONE, Key::Backspace))); diff --git a/docs/library-shortcuts.md b/docs/library-shortcuts.md new file mode 100644 index 00000000..8ef88291 --- /dev/null +++ b/docs/library-shortcuts.md @@ -0,0 +1,41 @@ +# Library ratings, labels and flags + +Select photos in Photo Grid or Square Grid, then use: + +| Key | Action | +|---|---| +| `0` | Clear the star rating | +| `1`–`5` | Set the star rating | +| `6`, `7`, `8`, `9` | Set a red, yellow, green or blue label | +| `P` | Flag as a pick | +| `X` | Flag as rejected | +| `U` | Clear the flag | +| `Shift` + any key above | Apply the action and select the next photo | + +Purple and clearing a colour label are available in Photo → Set Color Label and the photo context +menu. Purple has no default number key, matching Lightroom Classic. Rejection marks a photo for +culling; it does not delete the original or remove it from the library. + +Grid actions apply to all selected photos and support Undo / Redo. In Compare and Survey, culling +actions apply to the active photo. Auto Advance moves forward after an action; holding Shift with +Auto Advance on still advances only once. The last photo stays selected. Text fields keep their +keyboard input, and `X` in the Crop panel swaps the crop aspect. +Shifted number keys also use the physical number key when the keyboard reports punctuation +(for example, `Shift+1` as `!`). Letter shortcuts continue to follow the keyboard layout. + +`Shift+P` picks and advances in the two library grids. In other views it retains LightCraft's +Presets-panel binding. `Shift+Z` remains an alternative pick-and-advance key in all views. + +The macOS native menu displays plain-key bindings but leaves execution to egui; only shortcuts +AppKit delivers to the menu (Command, Control or function keys) are skipped by egui. The native +menu correction is adapted from [PR #261](https://github.com/storytold/lightcraft/pull/261), +commit `4e012a59a6d2442ba749f109ea27aadb5c0b11a5` by Gardy, and addresses the cause reported in +[issue #283](https://github.com/storytold/lightcraft/issues/283). That PR was open when checked +on 2026-10-08; this work extends its fix with library culling bindings and tests. + +Public behaviour references, checked 2026-10-08: + +- [Lightroom Classic keyboard shortcuts](https://helpx.adobe.com/lightroom-classic/help/keyboard-shortcuts.html) +- [Lightroom Classic flagging, labelling and rating](https://helpx.adobe.com/lightroom-classic/help/flag-label-rate-photos.html) + +Only public descriptions were consulted. No Adobe assets or application data are included. diff --git a/docs/parity.md b/docs/parity.md index 82729971..3950286e 100644 --- a/docs/parity.md +++ b/docs/parity.md @@ -59,6 +59,10 @@ Ordered by user impact, then tier, then effort. The checklist above counts featu that decide whether a photographer can switch (see the honest assessment in [ROADMAP.md](../ROADMAP.md#where-we-stand)). Take the first one nobody is working on. +Library culling keys now cover ratings, labels and flags with Shift-to-advance (see +[library shortcuts](library-shortcuts.md), issue #283 and PR #261). **KEYC-RATING** remains partial: +rating-step keys, flag cycling and filter-bar bindings are still open; Shift+P is contextual. + 1. **LR-PROF-CAMERACOLOR** (P0): our own camera colour calibration. Sony ARW and Nikon NEF now get a guarded file-local fit to their own embedded JPEG (colour matrix + hue/saturation/value table + tone and chroma curves, relative WB; per-model profiles pooled from many photos via `lightcraft-cli calibrate`; ARW: 7 of 10 public samples accepted, mean ΔE vs the camera JPEG 17–26 → 3–10; NEF: 13 of 13 decodable samples from 6 bodies accepted, ΔE 13–46 → 3–8, one mixed-light scene 13 → 12); measured calibration and fidelity remain missing. Other non-DNG raws and rejected fits still use a neutral matrix. Expand validated preview fitting and use matrices the files carry themselves; never Adobe data. 2. **LR-IMP-FORMATS** (P0): **CR3** first (every Canon body since ~2018), then compressed RAF / ORF, RW2 v4, Nikon "lossy after split" NEF, Canon sRAW; HEIC/AVIF decode. Clean-room, from prose descriptions only (see @@ -101,8 +105,10 @@ under the UI command that wraps it (a no-op error); the UI command now wins. | Create Version | ⌘⇧S (+ ⇧M) | ⇧M (Windows: Ctrl+⇧S) | — | | Select None | ⌘⇧A (+ ⌘D) | ⌘D | — | +**Library culling (KEYC-RATING):** `0–5`, `6–9` and `P/X/U` reach egui on macOS even when displayed in the native menu (adapted from PR #261; issue #283). `Shift+0–9` applies and advances once; `Shift+P` picks and advances in Photo Grid / Square Grid and retains Presets elsewhere. See [library shortcuts](library-shortcuts.md) for the verified Classic mapping and regression coverage. Remaining Classic gaps: rating `[` / `]`, flag cycling and filter-bar keys. + **Still missing / broken:** -- No command yet: F1 help, ⇧6–9 label + advance (verify the rest of the old list: full screen, settings, stacks, +- No command yet: F1 help (verify the rest of the old list: full screen, settings, stacks, visualize spots and merges have commands now). - `H` opens Remove; Lightroom also uses it (Classic) to hide pins — pins toggle from View → Show Mask Pins. - ⌘M / ⌘H / ⌘Q / ⌘W rely on the platform window defaults (unverified). @@ -143,8 +149,8 @@ under the UI command that wraps it (a no-op error); the UI command now wins. | LR-LIB-SHARED-ALBUM | Shared albums | P2 | ⬜ | | needs a sharing service | | LR-LIB-OFFLINE | Keep album offline | P2 | 🚫 | | not applicable: local-first library | | LR-LIB-TARGET | Target album | P2 | ✅ | `cmd:album.setTarget`, `cmd:album.toggleTarget` | see LRC-LIB-COLLECTIONS | -| LR-LIB-RATING | Star ratings | P0 | ✅ | `cmd:photo.rate` (`advance`), `crates/ui-egui/src/shortcuts.rs` | | -| LR-LIB-FLAG | Pick / reject flags | P0 | ✅ | `cmd:photo.pick`, `cmd:photo.reject`, `cmd:photo.unflag`, `cmd:photo.flag` | pick key is P (see Shortcuts); no flag cycle | +| LR-LIB-RATING | Star ratings | P0 | ✅ | `cmd:photo.rate` (`advance`), `crates/ui-egui/src/shortcuts.rs` | 0 clears; 1–5 set; Shift+0–5 applies and advances once, including with Auto Advance. Batch grid and undo/redo regressions; macOS native ownership corrected (PR #261) | +| LR-LIB-FLAG | Pick / reject flags | P0 | ✅ | `cmd:photo.pick`, `cmd:photo.reject`, `cmd:photo.unflag`, `cmd:photo.flag` | P pick, X reject, U clear; Shift+P picks and advances in both grids (Presets elsewhere); Shift+X/U and legacy Shift+Z advance; no flag cycle; rejection does not delete the file | | LR-LIB-LABEL | Colour labels | P1 | ✅ | `cmd:photo.label`, `cmd:label.setNames`, `cmd:label.names`, `cmd:dialog.labelNames`, keys 6–9 in `crates/ui-egui/src/shortcuts.rs` | Photo menu and grid context menu (coloured, named), Info-panel swatches, label dot in grid cells; editable label names (undoable, journaled); no purple key (as in Lightroom), no custom extra labels | | LR-LIB-KEYWORD | Keywords | P0 | ✅ | `cmd:panel.keywords`, `cmd:photo.setMeta` (`addKeywords`/`removeKeywords`), `cmd:keyword.list`, `cmd:keyword.rename`, `cmd:keyword.delete`, `cmd:keyword.merge`, `cmd:keyword.suggest`, `crates/catalog/src/keywords.rs`, `crates/ui-egui/src/panels/left.rs` | left-panel keyword tree with counts (click filters, children included; context menu rename / merge / delete / add / remove); library-wide ops are one undo step and replay from the op log; suggestions (co-occurring / most used / completions) in the Keywords panel; no keyword drag-and-drop | | LR-LIB-PEOPLE | People / faces | P2 | 🟡 | `cmd:view.people`, `cmd:view.faceBoxes`, `cmd:photo.removeRegion`, `cmd:photo.setRegion`, `cmd:library.filter`, `crates/ui-egui/src/panels/people.rs`, `crates/catalog/src/query.rs` (`people`), `crates/engine/src/media.rs` (`face_job`), `crates/meta/src/xmp.rs` | MVP: named face regions read from XMP (MWG-RS, read-only, see `docs/xmp-interop.md`; clipped to the photo; Lightroom's `Rotation` of ±π/2 and π handled, mirrored orientations taken as written; boxes follow Rotate Left/Right and flips; a sidecar stating `mwg-rs:Regions`, even empty, replaces the photo's on re-read, one without leaves them) become cards in the People view (View ▸ People, or the People button in the toolbar): a close-up of the person's largest face, name and photo count; a click filters the grid (`person` in `library.filter`, `person:` search token, filter chip). Face boxes in the loupe can be switched off (View ▸ Face Boxes, toolbar) resized with eight drag handles and removed with the × shown on hover (catalog-only and undoable, one undo step per drag; the sidecar is never rewritten for either); no moving a box by dragging it yet. No face detection, no unnamed people / suggestions, no manual tagging, no writing regions back | @@ -662,7 +668,7 @@ From `06-shortcuts.md` part 1. Evidence is our binding; conflicts are explained | KEY-ADDPHOTOS | Import photos — ⇧⌘I | P0 | ✅ | `cmd:file.addPhotos` | | | KEY-VERSIONS | Versions panel — ⇧V | P1 | ✅ | `cmd:panel.versions` | | | KEY-SECTIONS | Expand/collapse edit sections — ⌘1…⌘6 | P1 | ✅ | `cmd:section.light`, `cmd:section.color`, `cmd:section.effects`, `cmd:section.detail`, `cmd:section.optics` | ⌘1–⌘5 (Light, Color, Effects, Detail, Optics) as observed; our Edit panel has no Lens Blur / Geometry section for ⌘6 / ⌘7; Zoom 100% moved to ⌥⌘0 | -| KEY-PRESETS | Presets panel — ⇧P | P0 | ✅ | `cmd:panel.presets` | | +| KEY-PRESETS | Presets panel — ⇧P | P0 | ✅ | `cmd:panel.presets` | Shift+P opens Presets outside the two library grids; in grids it picks and advances (Classic convention). Menu hides the Presets key there; shortcut help follows the view | | KEY-HISTOGRAM | Histogram — ⌘0 | P0 | 🟡 | `cmd:view.histogram` | bound to ⌘⇧H; ⌘0 = zoom to fit | | KEY-BRUSHSIZE | Brush size — `[` / `]` | P0 | ✅ | `cmd:brush.smaller`, `cmd:brush.larger` | Masking brush and Remove tool (and its selected spot) | | KEY-BRUSHFEATHER | Brush feather — ⇧`[` / ⇧`]` | P0 | ✅ | `cmd:brush.featherLess`, `cmd:brush.featherMore` | | @@ -672,12 +678,12 @@ From `06-shortcuts.md` part 1. Evidence is our binding; conflicts are explained | KEY-PICK | Pick — Z | P0 | 🟡 | `cmd:photo.pick` | bound to P; Z toggles zoom | | KEY-UNFLAG | Unflag — U | P0 | ✅ | `cmd:photo.unflag` | | | KEY-REJECT | Reject — X | P0 | ✅ | `cmd:photo.reject` | swaps crop aspect while cropping | -| KEY-RATING | Ratings — 0…5 | P0 | ✅ | `crates/ui-egui/src/shortcuts.rs` → `cmd:photo.rate` | | -| KEY-LABELS | Labels — 6…9 | P1 | ✅ | `crates/ui-egui/src/shortcuts.rs` → `cmd:photo.label` | | +| KEY-RATING | Ratings — 0…5 | P0 | ✅ | `crates/ui-egui/src/shortcuts.rs` → `cmd:photo.rate` | 0 clears, 1–5 set; Shift applies and advances; native macOS plain keys stay with egui; grid multi-selection and undo/redo tested | +| KEY-LABELS | Labels — 6…9 | P1 | ✅ | `crates/ui-egui/src/shortcuts.rs` → `cmd:photo.label` | 6 red, 7 yellow, 8 green, 9 blue; Shift+6–9 labels and advances; purple/none available from menus | | KEY-MASKING | Masking — M | P0 | ✅ | `cmd:panel.masking` | | | KEY-ERASE | Erase while held — ⌥ | P0 | ✅ | `crates/ui-egui/src/panels/detail.rs`, `crates/ui-egui/src/panels/masking.rs` | ⌥ while painting flips Add ↔ Erase for that stroke; Add/Erase buttons too | | KEY-RATEADVANCE | Rate and advance — ⇧0…5 | P1 | ✅ | `crates/ui-egui/src/shortcuts.rs` → `cmd:photo.rate` (`advance`) | | -| KEY-FLAGADVANCE | Flag and advance — ⇧Z / ⇧X / ⇧U | P1 | ✅ | `cmd:photo.flag` (`advance`), `crates/ui-egui/src/shortcuts.rs` | ⇧Z pick, ⇧X reject, ⇧U unflag, each moving to the next photo | +| KEY-FLAGADVANCE | Flag and advance — ⇧Z / ⇧X / ⇧U | P1 | ✅ | `cmd:photo.flag` (`advance`), `crates/ui-egui/src/shortcuts.rs` | Shift+Z pick, Shift+X reject, Shift+U clear; Shift+P also picks and advances in the library grids; exactly one step with Auto Advance enabled | | KEY-NEXTPREV | Next / previous — → / ← | P0 | ✅ | `cmd:library.next`, `cmd:library.previous` | | | KEY-BA-CYCLE | Before/after — Y | P0 | ✅ | `cmd:view.beforeAfter` | toggles side by side (no cycling) | | KEY-BA-TOPBOTTOM | Before/after top/bottom — ⌥Y | P1 | ✅ | `cmd:view.beforeAfterTopBottom`, `cmd:view.beforeAfterSplitTopBottom` | ⌥Y, ⇧⌥Y | @@ -790,7 +796,7 @@ From `08-lightroom-classic-extras.md` (Classic-only features) and part 2 of `06- | KEYC-SECONDWINDOW | Classic secondary-window keys | P2 | ⬜ | | | | KEYC-CATALOG | Classic photo/catalog keys (⇧⌘I, ⌘', ⌘R, F2, ⌫, ⇧⌘E…) | P2 | ✅ | `cmd:library.import`, `cmd:photo.delete`, `cmd:photo.virtualCopy`, `cmd:app.showInFinder`, `cmd:dialog.rename`, `cmd:photo.editInExternal` | ⇧⌘I add, ⌘' virtual copy, ⌘R show in Finder, F2 rename, ⌫ delete, ⇧⌘E external editor | | KEYC-COMPARE | Classic grid/compare keys (Z, Home/End, =/−, ⌘⇧D, S…) | P2 | 🟡 | `cmd:view.zoomToggle` | Z toggles zoom; no compare, stacks, thumbnail-size keys | -| KEYC-RATING | Classic rating/flag keys (1–5, ⇧1–5, 6–9, P, X, U, ⇧X, ⇧U, `[` `]`, \`) | P2 | 🟡 | `cmd:photo.rate`, `cmd:photo.pick`, `cmd:photo.flag` | most work; no ⇧P / ⇧6–9 advance, rating `[` `]`, flag cycle, filter-bar keys | +| KEYC-RATING | Classic rating/flag keys (1–5, ⇧1–5, 6–9, P, X, U, ⇧X, ⇧U, `[` `]`, \`) | P2 | 🟡 | `cmd:photo.rate`, `cmd:photo.pick`, `cmd:photo.flag` | 0–5 / Shift+0–5 and 6–9 / Shift+6–9; P/X/U, Shift+X/U and grid Shift+P work; tests cover batch/undo, active Compare candidate and text focus. Missing: Shift+P outside grids (Presets), rating `[` / `]`, flag cycle and filter-bar keys | | KEYC-COLLECTIONS | Classic collection keys (⌘N, B…) | P2 | 🟡 | `cmd:dialog.newAlbum` | ⌘N new album; no quick collection | | KEYC-METADATA | Classic keyword/metadata keys (⌘K, ⌘S, ⌘⌥⇧C/V…) | P2 | 🟡 | `cmd:photo.saveMetadataToFile` | ⌘S saves metadata; no keyword sets, metadata copy/paste | | KEYC-DEVELOP | Classic develop keys (V, ⌘U, ⇧⌘U, R, Q, K, M, ⇧M, ⇧W, ⇧J, ⇧Q…) | P2 | 🟡 | `cmd:develop.treatment`, `cmd:develop.reset`, `cmd:crop.reset` | V, ⇧⌘R, ⌥⌘R, W, J, Y, ⇧Y, \ match; R/K/M/⇧M differ; no Classic keymap layer | diff --git a/docs/whats-new.md b/docs/whats-new.md index 00480132..7293ffd9 100644 --- a/docs/whats-new.md +++ b/docs/whats-new.md @@ -2,6 +2,11 @@ ## October 2026 +### Library keyboard culling +- On macOS, ratings `0–5`, labels `6–9` and pick/unflag `P/U` now reach the app even when shown in the native menu (issue #283; adapted from PR #261). +- `Shift+6–9` labels and advances. `Shift+P` picks and advances in Photo Grid and Square Grid; it opens Presets in other views. With Auto Advance on, Shift still moves only once. +- Help → Keyboard Shortcuts includes the number-key bindings. See [library shortcuts](library-shortcuts.md). + ### Presets and profiles - Import presets from other editors: XMP presets, classic `.lrtemplate` files, "DNG presets" from mobile apps and `.zip` bundles of any of these — whole folders at once, grouped by pack. Masks inside presets come along. From 82e827b9d966d63cd851dce5831448ee0c14ec6f Mon Sep 17 00:00:00 2001 From: Nathaniel Ledford Date: Thu, 8 Oct 2026 07:06:17 -0400 Subject: [PATCH 08/41] Sidebar: Folders section for the folders the library's photos were imported from MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Another way to explore the library next to By Date and Keywords: a tree of the folders that hold imported photos, with counts (subfolders included), under the disk or network share each is on. Local stays the way to browse any folder on disk. - catalog: Catalog::folder_tree and Filter::library_folder. Each disk is a volume row (drive letter, Windows share, /Volumes/, /media, /mnt, gvfs, /net…), found from the stored paths alone, so an offline share costs nothing. Levels that hold nothing and lead to one folder fold into one row; depth is bounded. The tree and the filter read paths through one splitter (separators, `.`, `..`, verbatim UNC prefix, repeated separators), and a row that would cover other disks' photos is not choosable - engine: library.folders, library.removeFolder (to Recently Deleted, one undo step, no file touched; a whole disk or a folder spanning several needs `disk: true`, the startup disk never), libraryFolder in library.filter, chip; a chosen folder follows folder.rename / folder.move and their undo - ui: collapsible Folders section; the chosen folder's row is always opened into view; right-click (row or triangle) Rename, Move, Show in Finder, Remove from Library…; disk rows Remove Disk from Library…; both ask first Co-Authored-By: Claude Sonnet 5.5 --- crates/catalog/src/folders.rs | 231 ++++++++++++++ crates/catalog/src/lib.rs | 4 + crates/catalog/src/query.rs | 88 ++++-- crates/catalog/src/tests_folders.rs | 331 ++++++++++++++++++++ crates/engine/src/cmd/browse.rs | 11 + crates/engine/src/cmd/library.rs | 52 ++- crates/engine/src/lib.rs | 2 + crates/engine/src/tests_folders.rs | 232 ++++++++++++++ crates/engine/src/view.rs | 5 + crates/ui-egui/locales/ja-formats.json | 3 +- crates/ui-egui/locales/ja.json | 10 +- crates/ui-egui/locales/pt-br-formats.json | 1 + crates/ui-egui/locales/pt-br.json | 8 + crates/ui-egui/locales/zh-hans-formats.json | 3 +- crates/ui-egui/locales/zh-hans.json | 10 +- crates/ui-egui/locales/zh-hant-formats.json | 3 +- crates/ui-egui/locales/zh-hant.json | 10 +- crates/ui-egui/src/lib.rs | 12 + crates/ui-egui/src/panels/dialogs.rs | 16 + crates/ui-egui/src/panels/left.rs | 246 ++++++++++++++- crates/ui-egui/src/state.rs | 9 + crates/ui-egui/src/tests_panels.rs | 187 +++++++++++ docs/parity.md | 7 +- 23 files changed, 1447 insertions(+), 34 deletions(-) create mode 100644 crates/catalog/src/folders.rs create mode 100644 crates/catalog/src/tests_folders.rs create mode 100644 crates/engine/src/tests_folders.rs diff --git a/crates/catalog/src/folders.rs b/crates/catalog/src/folders.rs new file mode 100644 index 00000000..3861fd5f --- /dev/null +++ b/crates/catalog/src/folders.rs @@ -0,0 +1,231 @@ +//! The library's folder tree: where on disk the imported photos live. +//! +//! A *folder* here is a folder holding at least one photo of the library (imported, not deleted, +//! not merely browsed in Local; demo scenes have no folder). The tree lists those folders and the +//! ones above them, never a folder that holds nothing of the library, with how many photos each +//! holds — its subfolders included, like a year in By Date counts its months. +//! +//! Folders above the imported ones that hold nothing themselves and lead to a single folder fold +//! into one row (`/Users/me/Pictures`) instead of a staircase of single-child levels; the row +//! stands for the deepest folder it names. A folder spelled two ways (`/` or `\`, repeated or +//! trailing separators, `.`) is one folder (see [`crate::query::folder_key`]). +//! +//! Each disk is a *volume*, a top-level row of its own: a drive letter (`D:`), a Windows share +//! (`//server/share`), a mounted disk or share (macOS `/Volumes/tokyo`, Linux `/media/me/usb`, +//! `/mnt/disk`, `/run/user/1000/gvfs/smb-share:…`, `/net/host`) or, for everything else, the +//! startup disk (`/`). Only the spelling of the stored paths tells them apart: no disk is +//! touched, so a share that is asleep or gone lists and counts like one that is there, and the +//! sidebar never waits on the network. +//! +//! Limits of reading only the stored spelling of a path: file names are compared as written +//! (Windows folds case, other systems do not), so on a case-insensitive disk `/Volumes/tokyo` and +//! `/Volumes/TOKYO` are two volumes here; and one place reached by two spellings +//! (`/Volumes/Macintosh HD/Users/me` and `/Users/me`, `/media/me/usb` and `/run/media/me/usb`) is +//! two rows. Each row is still consistent: choosing it shows exactly the photos it counts. +//! +//! This is the library's own view of its photos; browsing any folder on disk without importing +//! is Local's job (see [`crate::local`]). + +use std::collections::{BTreeMap, HashMap}; + +use serde::Serialize; + +use crate::query::{folder_key, folder_within, split_path}; +use crate::{Catalog, Source}; + +/// Folder levels kept per photo. No real path comes near it; a deeper one is counted at its +/// ancestor this many levels down, which bounds the tree's depth (and so every walk of it) +/// whatever a catalog holds. +pub const MAX_DEPTH: usize = 64; + +/// One row of the library's folder tree. +#[derive(Clone, Debug, PartialEq, Serialize)] +pub struct FolderNode { + /// What the row says: the folder's name, or the whole run (`Users/me/Pictures`) of folded + /// levels; for a volume, its name (`nas`, `D:`, `//server/share`, `/` for the startup disk). + pub name: String, + /// The folder the row stands for, written out from the names of the photos' paths (forward + /// slashes, no verbatim prefix); hand it back to [`crate::Filter::library_folder`] to see its + /// photos. + pub path: String, + /// Photos in this folder or any folder inside it. + pub count: usize, + /// Photos directly in this folder. + pub own: usize, + /// A disk rather than a folder in one; its `path` is where it is mounted. + pub volume: bool, + /// Whether choosing the row (`Filter::library_folder`) shows exactly its `count` photos. Not + /// so for the startup disk and for folders that hold other disks (`/Volumes`, `/mnt`…): their + /// path covers those disks' photos too, so choosing them is not offered. + pub selectable: bool, + pub children: Vec, +} + +/// A folder while the tree is being built (children by identity key). +struct Level { + name: String, + path: String, + own: usize, + volume: bool, + children: Vec, +} + +/// Where a stored folder path lies: its volume and the folder names below it. +struct Placed { + mount: String, + volume: String, + names: Vec, +} + +/// A gvfs mount's name made readable: `smb-share:server=tokyo,share=photos` → `photos on tokyo`; +/// any other (`sftp:host=nas`) as it is. +fn mount_name(n: &str) -> String { + let field = |key: &str| { + let rest = n.strip_prefix("smb-share:")?; + rest.split(',').find_map(|kv| kv.strip_prefix(key)?.strip_prefix('=')).filter(|v| !v.is_empty()) + }; + match (field("share"), field("server")) { + (Some(share), Some(server)) => format!("{share} on {server}"), + _ => n.to_string(), + } +} + +/// The volume `dir` lies on and the folder names below it, read the way [`folder_key`] reads a +/// path (so a row and the filter that chooses it never disagree). `None` for a path that is not +/// absolute or names no share. +fn place(dir: &str) -> Option { + let sp = split_path(dir, false); + let names: Vec<&str> = sp.parts.iter().map(String::as_str).collect(); + let named = |mount: String, volume: &str, used: usize| (mount, volume.to_string(), used); + let (mount, volume, used) = if sp.prefix.starts_with("//") { + // `//server/share`: a server alone (no share) is no place to keep photos + if sp.prefix.ends_with('/') { + return None; + } + named(sp.prefix.clone(), &sp.prefix, 0) + } else if !sp.prefix.is_empty() { + named(sp.prefix.clone(), &sp.prefix, 0) + } else if sp.absolute { + match names.as_slice() { + ["Volumes", n, ..] => named(format!("/Volumes/{n}"), n, 2), + ["run", "media", u, n, ..] => named(format!("/run/media/{u}/{n}"), n, 4), + ["media", u, n, ..] => named(format!("/media/{u}/{n}"), n, 3), + ["mnt", n, ..] => named(format!("/mnt/{n}"), n, 2), + // Linux desktops mount network places per user, naming server and share + ["run", "user", uid, "gvfs", n, ..] => named(format!("/run/user/{uid}/gvfs/{n}"), &mount_name(n), 5), + // NFS automounts: Linux `/net/`, macOS `/Network/Servers/` + ["net", host, ..] => named(format!("/net/{host}"), host, 2), + ["Network", "Servers", host, ..] => named(format!("/Network/Servers/{host}"), host, 3), + _ => named("/".to_string(), "/", 0), + } + } else { + return None; + }; + Some(Placed { mount, volume, names: names.iter().skip(used).take(MAX_DEPTH).map(|n| n.to_string()).collect() }) +} + +/// The disk or share (`/Volumes/nas`, `D:`, `/` for the startup disk) a file-backed photo is on, +/// from its stored path alone. +pub fn volume_of(p: &crate::Photo) -> Option { + let Source::File { path } = &p.source else { return None }; + let dir = path.rfind(['/', '\\']).and_then(|i| path.get(..i.max(1)))?; + place(dir).map(|pl| folder_key(&pl.mount)) +} + +/// Whether `path` is a whole disk or share (`/`, `C:\`, `\\srv\share`, `/Volumes/nas`), not a +/// folder in one. +pub fn is_disk_root(path: &str) -> bool { + place(path).is_some_and(|p| p.names.is_empty()) +} + +/// Whether `path` is the startup disk's root (`/`), whose path also covers every other disk. +pub fn is_startup_disk(path: &str) -> bool { + place(path).is_some_and(|p| p.names.is_empty() && p.mount == "/") +} + +/// A folder named by its last two names (`photos/travel`), so two folders called `travel` are +/// told apart; the path itself when it has no names (`/`). +pub fn folder_label(path: &str) -> String { + let names: Vec<&str> = path.split(['/', '\\']).filter(|n| !n.is_empty()).collect(); + if names.is_empty() { path.to_string() } else { names.iter().skip(names.len().saturating_sub(2)).copied().collect::>().join("/") } +} + +impl Catalog { + /// The library's volumes with their folders and photo counts: volumes by name + /// (case-insensitive), each folder with its subfolders the same way. + pub fn folder_tree(&self) -> Vec { + // photos per containing folder, as spelled (sorted, so the spelling that names a folder + // never depends on hash order) + let mut dirs: BTreeMap<&str, usize> = BTreeMap::new(); + for p in self.photos().filter(|p| p.in_library()) { + if let Source::File { path } = &p.source + && let Some(i) = path.rfind(['/', '\\']) + // a photo straight in the root has an empty folder part: the root itself + && let Some(dir) = path.get(..i.max(1)) + { + *dirs.entry(dir).or_default() += 1; + } + } + let mut levels: HashMap = HashMap::new(); + let mut roots: Vec = Vec::new(); + for (dir, n) in dirs { + let Some(placed) = place(dir) else { continue }; + let vkey = folder_key(&placed.mount); + if !levels.contains_key(&vkey) { + levels.insert( + vkey.clone(), + Level { name: placed.volume.clone(), path: placed.mount.clone(), own: 0, volume: true, children: Vec::new() }, + ); + roots.push(vkey.clone()); + } + let mut prefix = if placed.mount == "/" { String::new() } else { placed.mount.clone() }; + let mut parent = vkey; + for name in &placed.names { + prefix.push('/'); + prefix.push_str(name); + let key = folder_key(&prefix); + if !levels.contains_key(&key) { + levels.insert(key.clone(), Level { name: name.clone(), path: prefix.clone(), own: 0, volume: false, children: Vec::new() }); + if let Some(l) = levels.get_mut(&parent) { + l.children.push(key.clone()); + } + } + parent = key; + } + if let Some(l) = levels.get_mut(&parent) { + l.own += n; + } + } + let mut tree: Vec = roots.iter().filter_map(|k| build(k, &levels)).collect(); + sort(&mut tree); + // a folder that holds another disk's mount covers that disk's photos too + let mounts: Vec = tree.iter().filter(|v| v.path != "/").map(|v| v.path.clone()).collect(); + mark_selectable(&mut tree, &mounts); + tree + } +} + +/// The node for `key` with everything below it (at most [`MAX_DEPTH`] deep), folding a level +/// that holds nothing and leads to one folder into that folder (never a volume). +fn build(key: &str, levels: &HashMap) -> Option { + let l = levels.get(key)?; + let mut children: Vec = l.children.iter().filter_map(|k| build(k, levels)).collect(); + sort(&mut children); + let count = l.own + children.iter().map(|c| c.count).sum::(); + if !l.volume && l.own == 0 && children.len() == 1 { + let c = children.remove(0); + return Some(FolderNode { name: format!("{}/{}", l.name, c.name), ..c }); + } + Some(FolderNode { name: l.name.clone(), path: l.path.clone(), count, own: l.own, volume: l.volume, selectable: true, children }) +} + +fn mark_selectable(nodes: &mut [FolderNode], mounts: &[String]) { + for n in nodes { + n.selectable = !(n.volume && n.path == "/") && (n.volume || !mounts.iter().any(|m| folder_within(m, &n.path))); + mark_selectable(&mut n.children, mounts); + } +} + +fn sort(v: &mut [FolderNode]) { + v.sort_by(|a, b| a.name.to_lowercase().cmp(&b.name.to_lowercase()).then_with(|| a.name.cmp(&b.name))); +} diff --git a/crates/catalog/src/lib.rs b/crates/catalog/src/lib.rs index 208a86d0..59d8c4a8 100644 --- a/crates/catalog/src/lib.rs +++ b/crates/catalog/src/lib.rs @@ -14,6 +14,7 @@ #![deny(clippy::unwrap_used, clippy::expect_used, clippy::panic, clippy::unimplemented, clippy::todo, clippy::unreachable)] pub mod dates; +pub mod folders; pub mod journal; pub mod keywords; pub mod local; @@ -29,6 +30,7 @@ use std::collections::BTreeMap; use std::sync::Arc; pub use dates::{DateRun, GroupBy}; +pub use folders::FolderNode; pub use journal::{Journal, LoadReport, PersistStats, SnapshotPolicy, SnapshotTiming}; pub use keywords::KeywordNode; use lightcraft_develop::DevelopSettings; @@ -655,6 +657,8 @@ mod tests; #[cfg(test)] mod tests_background; #[cfg(test)] +mod tests_folders; +#[cfg(test)] mod tests_format_version; #[cfg(test)] mod tests_journal; diff --git a/crates/catalog/src/query.rs b/crates/catalog/src/query.rs index d06e1370..6f02b228 100644 --- a/crates/catalog/src/query.rs +++ b/crates/catalog/src/query.rs @@ -37,6 +37,10 @@ pub struct Filter { pub deleted: bool, /// Capture date prefix (`2026`, `2026-04`, `2026-04-12`). pub date: Option, + /// A folder the library's photos were imported from: those photos and the ones imported from + /// the folders inside it (see [`crate::folders`]). Photos only browsed in Local are never part + /// of it, unlike [`Filter::folder`]. + pub library_folder: Option, /// A keyword; hierarchical keywords match their children too (`travel` finds `travel|italy`). pub keyword: Option, /// A person: photos with a named face region of this name (case-insensitive), as read from XMP. @@ -186,6 +190,7 @@ impl Filter { ("camera", &self.camera), ("lens", &self.lens), ("date", &self.date), + ("folder", &self.library_folder), ("imported", &self.imported), ] { if let Some(x) = val { @@ -213,6 +218,17 @@ impl Filter { } pub fn matches(&self, p: &Photo, cat: &Catalog) -> bool { + self.matches_in(p, cat, self.library_root().as_deref()) + } + + /// [`Filter::library_folder`] as a folder identity ([`folder_key`]); `None` without a choice. + fn library_root(&self) -> Option { + self.library_folder.as_deref().map(str::trim).filter(|d| !d.is_empty()).map(folder_key) + } + + /// [`Filter::matches`] with the library folder already turned into its identity, so a query + /// over a big library does that once, not once per photo. + fn matches_in(&self, p: &Photo, cat: &Catalog, root: Option<&str>) -> bool { if p.deleted != self.deleted { return false; } @@ -285,6 +301,11 @@ impl Filter { { return false; } + if let Some(root) = root + && !(!p.local && crate::local::folder_of(p).is_some_and(|f| key_within(&f, root))) + { + return false; + } if let Some(d) = &self.imported && !p.imported.starts_with(d.as_str()) { @@ -317,7 +338,8 @@ impl Filter { impl Catalog { /// Photos matching `filter`, in `sort` order (ties broken by id for stability). pub fn query(&self, filter: &Filter, sort: &Sort) -> Vec { - let mut v: Vec<&Photo> = self.photos().map(|p| p.as_ref()).filter(|p| filter.matches(p, self)).collect(); + let root = filter.library_root(); + let mut v: Vec<&Photo> = self.photos().map(|p| p.as_ref()).filter(|p| filter.matches_in(p, self, root.as_deref())).collect(); v.sort_by(|a, b| { let o = match sort.key { SortKey::CaptureDate => a.date().cmp(b.date()), @@ -384,7 +406,8 @@ impl Catalog { pub fn people_in(&self, filter: &Filter) -> Vec { let filter = Filter { person: None, ..filter.clone() }; let mut m: std::collections::HashMap = Default::default(); - for p in self.photos().filter(|p| filter.matches(p, self)) { + let root = filter.library_root(); + for p in self.photos().filter(|p| filter.matches_in(p, self, root.as_deref())) { let mut seen: Vec = Vec::new(); for r in p.meta.regions.iter().filter(|r| r.kind == lightcraft_meta::RegionKind::Face) { let Some(name) = r.name.as_deref().map(str::trim).filter(|n| !n.is_empty()) else { continue }; @@ -438,12 +461,18 @@ pub fn in_folder(path: &str, dir: &str, deep: bool) -> bool { !rest.is_empty() && (deep || !rest.contains(['/', '\\'])) } -/// A folder path's identity, for telling whether two spellings name the same folder: `/` and -/// `\\` are both separators, repeated separators, `.` and a trailing separator are dropped, -/// `..` is resolved lexically, a Windows verbatim prefix (`\\?\`) is removed and the drive -/// letter lower-cased; on Windows (case-insensitive file names) the whole path is lower-cased. -/// No file-system access: the paths compared should already be absolute. -pub fn folder_key(path: &str) -> String { +/// A path read the way [`folder_key`] reads it: the part `..` can't climb above (`//server/share`, +/// `c:`; empty for `/` and relative paths), the names below it, and whether it is absolute. +pub(crate) struct SplitPath { + pub prefix: String, + pub parts: Vec, + pub absolute: bool, +} + +/// Both separators, repeated separators (also between a server and its share), `.` and a +/// trailing separator dropped, `..` resolved lexically, a Windows verbatim prefix removed; +/// `lower_drive` lower-cases a drive letter (the identity does, a display name wants it as is). +pub(crate) fn split_path(path: &str, lower_drive: bool) -> SplitPath { let mut s = path.replace('\\', "/"); if let Some(rest) = s.strip_prefix("//?/").or_else(|| s.strip_prefix("//./")) { s = match rest.strip_prefix("UNC/") { @@ -451,28 +480,39 @@ pub fn folder_key(path: &str) -> String { None => rest.to_string(), }; } - // the part `..` can't climb above: `//server/share`, `c:` or `/` let (prefix, rest) = if let Some(unc) = s.strip_prefix("//").filter(|u| !u.is_empty() && !u.starts_with('/')) { - let mut it = unc.splitn(3, '/'); - let (server, share, rest) = (it.next().unwrap_or(""), it.next().unwrap_or(""), it.next().unwrap_or("")); - (format!("//{server}/{share}"), rest.to_string()) - } else if s.len() >= 2 && s.as_bytes()[1] == b':' && s.as_bytes()[0].is_ascii_alphabetic() { - (s[..2].to_ascii_lowercase(), s[2..].to_string()) + let mut segs = unc.split('/').filter(|x| !x.is_empty()); + let (server, share) = (segs.next().unwrap_or(""), segs.next().unwrap_or("")); + (format!("//{server}/{share}"), segs.collect::>().join("/")) + } else if let (true, Some(drive), Some(rest)) = (matches!(s.as_bytes(), [a, b':', ..] if a.is_ascii_alphabetic()), s.get(..2), s.get(2..)) { + (if lower_drive { drive.to_ascii_lowercase() } else { drive.to_string() }, rest.to_string()) } else { (String::new(), s.clone()) }; let absolute = rest.starts_with('/') || !prefix.is_empty(); - let mut parts: Vec<&str> = Vec::new(); + let mut parts: Vec = Vec::new(); for c in rest.split('/') { match c { "" | "." => {} - ".." if parts.last().is_some_and(|p| *p != "..") => { + ".." if parts.last().is_some_and(|p| p != "..") => { parts.pop(); } ".." if absolute => {} - c => parts.push(c), + c => parts.push(c.to_string()), } } + SplitPath { prefix, parts, absolute } +} + +/// A folder path's identity, for telling whether two spellings name the same folder: `/` and +/// `\\` are both separators, repeated separators, `.` and a trailing separator are dropped, +/// `..` is resolved lexically, a Windows verbatim prefix (`\\?\`) is removed and the drive +/// letter lower-cased; on Windows (case-insensitive file names) the whole path is lower-cased. +/// File names are otherwise compared as written, so on a case-insensitive disk elsewhere +/// (macOS, a Samba share) two spellings that differ in case are two folders here. +/// No file-system access: the paths compared should already be absolute. +pub fn folder_key(path: &str) -> String { + let SplitPath { prefix, parts, absolute } = split_path(path, true); let key = if absolute { format!("{prefix}/{}", parts.join("/")) } else { parts.join("/") }; if cfg!(windows) { key.to_lowercase() } else { key } } @@ -480,8 +520,18 @@ pub fn folder_key(path: &str) -> String { /// Whether `path` is folder `root` or lies somewhere inside it, however either is spelled /// (see [`folder_key`]). pub fn folder_within(path: &str, root: &str) -> bool { - let (p, r) = (folder_key(path), folder_key(root)); - p == r || (p.starts_with(&r) && (r.ends_with('/') || p[r.len()..].starts_with('/'))) + key_within(&folder_key(path), &folder_key(root)) +} + +/// [`folder_within`] for two paths already turned into their [`folder_key`]. +fn key_within(p: &str, r: &str) -> bool { + // `.` or `a/..` name no folder: not "everything" + if r.is_empty() { + return false; + } + // a disk's root key ends in `/` (`c:/`, `//srv/share/`), a folder's does not: same folder + let (p, r) = (p.trim_end_matches('/'), r.trim_end_matches('/')); + p == r || p.strip_prefix(r).is_some_and(|rest| rest.starts_with('/')) } /// The kind of Photo Merge result a file is, from the name merges give it (`IMG_1-HDR.dng`, diff --git a/crates/catalog/src/tests_folders.rs b/crates/catalog/src/tests_folders.rs new file mode 100644 index 00000000..6c7c9ab7 --- /dev/null +++ b/crates/catalog/src/tests_folders.rs @@ -0,0 +1,331 @@ +//! The library's folder tree: where the imported photos live on disk (see [`crate::folders`]). +//! +//! Scenarios, in the words of someone browsing their library: +//! +//! * Given photos imported from several folders, the tree lists those folders and no others, +//! each with how many photos it holds, under the disk (volume) they are on. +//! * A folder's count includes the folders inside it. +//! * Folders above the imported ones that hold nothing themselves fold into one row, until the +//! library branches. +//! * Photos only browsed in Local, deleted photos and demo scenes are not "imported from" anywhere. +//! * However a folder is spelled, it is one folder. +//! * Choosing a folder shows what was imported from it and from the folders inside it — exactly +//! as many photos as the row says. + +use crate::*; + +fn add(c: &mut Catalog, path: &str) -> PhotoId { + add_with(c, path, |_| {}) +} + +fn add_with(c: &mut Catalog, path: &str, tweak: impl FnOnce(&mut Photo)) -> PhotoId { + let id = c.alloc_photo_id(); + let name = path.rsplit(['/', '\\']).next().unwrap_or(path); + let mut p = Photo::new(id, Source::File { path: path.into() }, name, "JPEG", 60, 40, "2026-01-01T10:00:00"); + tweak(&mut p); + c.apply(Op::AddPhoto { photo: Box::new(p) }).unwrap(); + id +} + +fn library(paths: &[&str]) -> Catalog { + let mut c = Catalog::new(); + paths.iter().for_each(|p| { + add(&mut c, p); + }); + c +} + +/// `(name, count, own)` of each node, depth first. +fn outline(nodes: &[FolderNode]) -> Vec<(String, usize, usize)> { + fn walk(n: &FolderNode, out: &mut Vec<(String, usize, usize)>) { + out.push((n.name.clone(), n.count, n.own)); + n.children.iter().for_each(|c| walk(c, out)); + } + let mut out = Vec::new(); + nodes.iter().for_each(|n| walk(n, &mut out)); + out +} + +fn row(name: &str, count: usize, own: usize) -> (String, usize, usize) { + (name.to_string(), count, own) +} + +fn rows(paths: &[&str]) -> Vec<(String, usize, usize)> { + outline(&library(paths).folder_tree()) +} + +#[test] +fn an_empty_library_has_no_folders() { + assert!(Catalog::new().folder_tree().is_empty()); +} + +#[test] +fn only_folders_with_imported_photos_are_listed_with_their_counts() { + let c = library(&["/pics/trip/a.jpg", "/pics/trip/b.jpg", "/pics/home/c.jpg"]); + let tree = c.folder_tree(); + assert_eq!(outline(&tree), vec![row("/", 3, 0), row("pics", 3, 0), row("home", 1, 1), row("trip", 2, 2)]); + assert!(tree[0].volume && !tree[0].children[0].volume); + assert_eq!(tree[0].children[0].children[1].path, "/pics/trip", "a row knows its folder, for choosing it"); +} + +#[test] +fn a_folders_count_includes_the_folders_inside_it() { + let r = rows(&["/pics/trip/a.jpg", "/pics/trip/day1/b.jpg", "/pics/trip/day1/c.jpg", "/pics/trip/day2/d.jpg"]); + assert_eq!(r, vec![row("/", 4, 0), row("pics/trip", 4, 1), row("day1", 2, 2), row("day2", 1, 1)]); +} + +#[test] +fn folders_that_hold_nothing_fold_into_one_row_until_the_library_branches() { + let c = library(&["/Users/me/Pictures/2024/a.jpg", "/Users/me/Pictures/2025/b.jpg", "/Volumes/nas/photos/c.jpg"]); + let tree = c.folder_tree(); + assert_eq!( + outline(&tree), + vec![row("/", 2, 0), row("Users/me/Pictures", 2, 0), row("2024", 1, 1), row("2025", 1, 1), row("nas", 1, 0), row("photos", 1, 1)] + ); + assert_eq!(tree[0].children[0].path, "/Users/me/Pictures", "the folded row stands for the deepest folder it names"); +} + +#[test] +fn a_folder_with_photos_of_its_own_is_not_folded_into_its_child() { + assert_eq!(rows(&["/pics/a.jpg", "/pics/trip/b.jpg"]), vec![row("/", 2, 0), row("pics", 2, 1), row("trip", 1, 1)]); +} + +#[test] +fn browsed_deleted_and_demo_photos_are_not_imported_from_anywhere() { + let mut c = Catalog::new(); + add(&mut c, "/pics/kept/a.jpg"); + add_with(&mut c, "/pics/browsed/b.jpg", |p| p.local = true); + add_with(&mut c, "/pics/gone/c.jpg", |p| p.deleted = true); + let id = c.alloc_photo_id(); + let demo = Photo::new(id, Source::Demo { scene: 1 }, "demo", "JPEG", 60, 40, "2026-01-01T10:00:00"); + c.apply(Op::AddPhoto { photo: Box::new(demo) }).unwrap(); + assert_eq!(outline(&c.folder_tree()), vec![row("/", 1, 0), row("pics/kept", 1, 1)]); +} + +#[test] +fn however_a_folder_is_spelled_it_is_one_folder() { + let r = rows(&["/pics/trip/a.jpg", "/pics//trip/./b.jpg", "/pics\\trip\\c.jpg", "/pics/x/../trip/d.jpg"]); + assert_eq!(r, vec![row("/", 4, 0), row("pics/trip", 4, 4)]); +} + +#[test] +fn each_disk_is_a_volume_of_its_own() { + // macOS, Linux and Windows habits for where disks appear + let r = rows(&["/Volumes/nas/p/1.jpg", "/media/me/usb/2.jpg", "/run/media/me/card/3.jpg", "/mnt/disk/4.jpg"]); + assert_eq!(r, vec![row("card", 1, 1), row("disk", 1, 1), row("nas", 1, 0), row("p", 1, 1), row("usb", 1, 1)]); + let r = rows(&["D:\\Photos\\2024\\a.jpg", "D:\\Photos\\2025\\b.jpg", "C:\\x\\c.jpg"]); + assert_eq!(r, vec![row("C:", 1, 0), row("x", 1, 1), row("D:", 2, 0), row("Photos", 2, 0), row("2024", 1, 1), row("2025", 1, 1)]); +} + +#[test] +fn a_network_share_is_a_volume_and_two_spellings_are_one() { + let r = rows(&["\\\\srv\\s1\\a\\1.jpg", "\\\\srv\\s2\\b\\2.jpg"]); + assert_eq!(r, vec![row("//srv/s1", 1, 0), row("a", 1, 1), row("//srv/s2", 1, 0), row("b", 1, 1)]); + // the verbatim form Windows APIs give is the same share + let r = rows(&["\\\\?\\UNC\\srv\\s1\\a\\1.jpg", "\\\\srv\\s1\\a\\2.jpg"]); + assert_eq!(r, vec![row("//srv/s1", 2, 0), row("a", 2, 2)]); +} + +#[test] +fn a_samba_share_mounted_on_a_mac_is_a_volume_named_after_the_share() { + // Finder mounts every share under /Volumes, and a second mount of the same share gets a suffix + let r = rows(&["/Volumes/tokyo/Photos/2024/a.jpg", "/Volumes/tokyo/Photos/2025/b.jpg", "/Volumes/tokyo-1/old/c.jpg", "/Volumes/東京 NAS/d.jpg"]); + assert_eq!( + r, + vec![ + row("tokyo", 2, 0), + row("Photos", 2, 0), + row("2024", 1, 1), + row("2025", 1, 1), + row("tokyo-1", 1, 0), + row("old", 1, 1), + row("東京 NAS", 1, 1) + ] + ); + let tree = library(&["/Volumes/tokyo/Photos/a.jpg"]).folder_tree(); + assert_eq!((tree[0].path.as_str(), tree[0].volume), ("/Volumes/tokyo", true), "choosing the share shows everything on it"); +} + +#[test] +fn shares_the_other_systems_mount_are_volumes_too() { + // Linux desktops: gvfs mounts per user, with the server and share in the mount's name + let gvfs = "/run/user/1000/gvfs/smb-share:server=tokyo,share=photos"; + let r = rows(&[&format!("{gvfs}/2024/a.jpg"), "/run/user/1000/gvfs/sftp:host=nas/b.jpg"]); + assert_eq!(r, vec![row("photos on tokyo", 1, 0), row("2024", 1, 1), row("sftp:host=nas", 1, 1)]); + // NFS automounts: Linux /net/, macOS /Network/Servers/ + let r = rows(&["/net/tokyo/export/a.jpg", "/Network/Servers/osaka/vol/b.jpg"]); + assert_eq!(r, vec![row("osaka", 1, 0), row("vol", 1, 1), row("tokyo", 1, 0), row("export", 1, 1)]); + // Windows: a share by name or by address, the WSL file system, a mapped drive + let r = rows(&[r"\\tokyo.local\photos\a.jpg", r"\\192.168.1.5\scans\b.jpg", r"\\wsl$\Ubuntu\home\me\c.jpg", r"Z:\d.jpg"]); + assert_eq!( + r, + vec![row("//192.168.1.5/scans", 1, 1), row("//tokyo.local/photos", 1, 1), row("//wsl$/Ubuntu", 1, 0), row("home/me", 1, 1), row("Z:", 1, 1)] + ); +} + +#[test] +fn a_share_that_is_not_mounted_costs_nothing() { + // the tree is made from the catalog's own records: it never looks at a disk, so a share that + // is asleep or gone lists (and counts) exactly like one that is there + let c = library(&["/Volumes/definitely-not-mounted/a/b.jpg", r"\\unreachable.invalid\share\c.jpg"]); + let tree = c.folder_tree(); + assert_eq!(tree.iter().map(|n| n.count).sum::(), 2); +} + +#[test] +fn a_photo_in_the_root_of_a_disk_counts_for_the_disk() { + assert_eq!(rows(&["/x.jpg", "/d/y.jpg"]), vec![row("/", 2, 1), row("d", 1, 1)]); + assert_eq!(rows(&["C:\\x.jpg"]), vec![row("C:", 1, 1)]); +} + +#[test] +fn photos_with_no_folder_are_left_out() { + assert!(rows(&["x.jpg", ""]).is_empty()); +} + +#[test] +fn folders_are_listed_by_name_ignoring_case() { + let names: Vec = rows(&["/p/b/1.jpg", "/p/A/2.jpg", "/p/c/3.jpg"]).into_iter().skip(2).map(|r| r.0).collect(); + assert_eq!(names, ["A", "b", "c"]); +} + +#[test] +fn absurdly_deep_paths_neither_crash_nor_lose_photos() { + let deep = format!("/{}/a.jpg", vec!["d"; 5000].join("/")); + let tree = library(&[&deep]).folder_tree(); + assert_eq!(tree.iter().map(|n| n.count).sum::(), 1); + assert!(outline(&tree).len() <= crate::folders::MAX_DEPTH + 2); +} + +#[test] +fn choosing_a_folder_shows_what_was_imported_from_it_and_below() { + let mut c = Catalog::new(); + let trip = add(&mut c, "/pics/trip/a.jpg"); + let day = add(&mut c, "/pics/trip/day1/b.jpg"); + let sibling = add(&mut c, "/pics/trip2/c.jpg"); // shares the name's start, not the folder + let browsed = add_with(&mut c, "/pics/trip/d.jpg", |p| p.local = true); + let f = Filter { library_folder: Some("/pics//trip/".into()), ..Default::default() }; + let shown = |id: PhotoId| f.matches(c.photo(id).unwrap(), &c); + assert!(shown(trip) && shown(day)); + assert!(!shown(sibling), "/pics/trip2 is another folder"); + assert!(!shown(browsed), "browsed photos are not part of the library's folders"); + assert_eq!(f.describe(), "folder /pics//trip/"); +} + +#[test] +fn recently_deleted_shows_the_deleted_photos_of_the_chosen_folder() { + let mut c = Catalog::new(); + let gone = add_with(&mut c, "/pics/trip/a.jpg", |p| p.deleted = true); + let kept = add(&mut c, "/pics/trip/b.jpg"); + let f = Filter { library_folder: Some("/pics/trip".into()), deleted: true, ..Default::default() }; + assert!(f.matches(c.photo(gone).unwrap(), &c)); + assert!(!f.matches(c.photo(kept).unwrap(), &c)); +} + +#[test] +fn an_empty_folder_choice_is_no_choice() { + let c = library(&["/pics/a.jpg"]); + let f = Filter { library_folder: Some(" ".into()), ..Default::default() }; + assert_eq!(c.query(&f, &Sort::default()).len(), 1); +} + +#[test] +fn every_row_shows_exactly_as_many_photos_as_it_says() { + let c = library(&[ + "/Users/me/Pictures/2024/a.jpg", + "/Users/me/Pictures/2024/b.jpg", + "/Users/me/Pictures/2025/c.jpg", + "/Users/me/Pictures/d.jpg", + "/Volumes/nas/photos/e.jpg", + "/Volumes/nas/f.jpg", + "/x.jpg", + "/a/../b/g.jpg", + "D:\\Photos\\2024\\h.jpg", + "d:/Photos/i.jpg", + "\\\\srv\\s1\\a\\j.jpg", + "\\\\?\\UNC\\srv\\s2\\k.jpg", + "/pics/trip/l.jpg", + "/pics/trip2/m.jpg", + "/Volumes/tokyo/Photos/2024/n.jpg", + "/Volumes/tokyo/o.jpg", + "/run/user/1000/gvfs/smb-share:server=tokyo,share=photos/p.jpg", + "/net/osaka/export/q.jpg", + r"\\wsl$\Ubuntu\home\me\r.jpg", + // odd spellings the tree and the filter must read the same way + "//server/share/../other/1.jpg", + "//server/share/ok/2.jpg", + "//a b//A/f.jpg", + r"\\wsl$\Ubuntu\..\a\1.jpg", + // a folder that holds other disks as well as photos of its own + "/Volumes/1.jpg", + "/mnt/2.jpg", + "/mnt/disk/3.jpg", + ]); + fn check(c: &Catalog, nodes: &[FolderNode], seen: &mut usize) { + for n in nodes { + // the startup volume's row only opens: its path "/" would cover every other disk + if n.selectable { + let f = Filter { library_folder: Some(n.path.clone()), ..Default::default() }; + assert_eq!(c.query(&f, &Sort::default()).len(), n.count, "{} ({})", n.name, n.path); + *seen += 1; + } + check(c, &n.children, seen); + } + } + let mut seen = 0; + check(&c, &c.folder_tree(), &mut seen); + assert!(seen > 10, "{seen} rows checked"); +} + +fn find<'a>(nodes: &'a [FolderNode], path: &str) -> Option<&'a FolderNode> { + nodes.iter().find_map(|n| if n.path == path { Some(n) } else { find(&n.children, path) }) +} + +#[test] +fn a_row_that_would_cover_other_disks_is_not_offered_for_choosing() { + let tree = library(&["/Users/me/a.jpg", "/Volumes/1.jpg", "/Volumes/tokyo/x.jpg", "/mnt/2.jpg"]).folder_tree(); + let sel = |p: &str| find(&tree, p).map(|n| n.selectable); + assert_eq!(sel("/"), Some(false), "the startup disk's path covers every disk"); + assert_eq!(sel("/Volumes"), Some(false), "it holds the tokyo disk"); + assert_eq!(sel("/Volumes/tokyo"), Some(true)); + assert_eq!(sel("/Users/me"), Some(true)); + assert_eq!(sel("/mnt"), Some(true), "no disk is mounted below it in this library"); +} + +#[test] +fn the_tree_and_the_filter_read_odd_spellings_the_same_way() { + // `..` cannot climb above a share, and repeated separators between server and share are one + let tree = library(&["//server/share/../other/1.jpg", "//a b//A/f.jpg"]).folder_tree(); + assert_eq!(outline(&tree), vec![row("//a b/A", 1, 1), row("//server/share", 1, 0), row("other", 1, 1)]); +} + +#[test] +fn a_path_that_names_no_folder_chooses_nothing() { + let c = library(&["/pics/a.jpg", "C:\\b\\c.jpg"]); + for dot in [".", "./", "a/..", "../.."] { + let f = Filter { library_folder: Some(dot.into()), ..Default::default() }; + assert_eq!(c.query(&f, &Sort::default()).len(), 0, "{dot:?} is no folder, not everything"); + } +} + +#[test] +fn a_folder_is_labelled_by_its_last_two_names() { + use crate::folders::folder_label; + assert_eq!(folder_label("/Volumes/tokyo/photos/travel"), "photos/travel"); + assert_eq!(folder_label("/Volumes/tokyo/"), "Volumes/tokyo"); + assert_eq!(folder_label(r"C:\Photos\2024"), "Photos/2024"); + assert_eq!(folder_label("/"), "/"); + assert_eq!(folder_label(""), ""); +} + +#[test] +fn disk_roots_are_told_from_folders() { + use crate::folders::{is_disk_root, is_startup_disk}; + for p in ["/", "C:", "C:\\", r"\\?\C:\", r"\\srv\share", "/Volumes/nas", "/mnt/disk", "/net/host"] { + assert!(is_disk_root(p), "{p}"); + } + for p in ["/Volumes", "/Users", "C:\\Photos", r"\\srv\share\x", "/Volumes/nas/p", "."] { + assert!(!is_disk_root(p), "{p}"); + } + assert!(is_startup_disk("/") && is_startup_disk("//") && !is_startup_disk("/Volumes/nas") && !is_startup_disk("C:")); +} diff --git a/crates/engine/src/cmd/browse.rs b/crates/engine/src/cmd/browse.rs index a8a9b448..338dff69 100644 --- a/crates/engine/src/cmd/browse.rs +++ b/crates/engine/src/cmd/browse.rs @@ -251,6 +251,17 @@ pub(crate) fn rename_folder_on_disk(from: &str, to: &str) -> std::result::Result /// After a folder moved from `from` to `to`: a browsed folder at or below it follows. pub(crate) fn follow_folder(s: &mut Session, from: &str, to: &str) { + // a chosen library folder at or below it follows too (compared the way the filter does, by + // folder identity, so a differently spelled choice is not widened to the folder itself) + if let Some(chosen) = s.filter.library_folder.clone() { + let (ck, fk) = (lightcraft_catalog::query::folder_key(&chosen), lightcraft_catalog::query::folder_key(from)); + if !fk.is_empty() + && let Some(rest) = ck.strip_prefix(fk.as_str()).filter(|r| r.is_empty() || r.starts_with('/') || fk.ends_with('/')) + { + let rest = rest.trim_start_matches('/'); + s.filter.library_folder = Some(if rest.is_empty() { to.to_string() } else { format!("{}/{rest}", to.trim_end_matches(['/', '\\'])) }); + } + } if let Some(b) = &mut s.browse && let Ok(rel) = Path::new(&b.path).strip_prefix(from) { diff --git a/crates/engine/src/cmd/library.rs b/crates/engine/src/cmd/library.rs index 57632ea8..688d84b3 100644 --- a/crates/engine/src/cmd/library.rs +++ b/crates/engine/src/cmd/library.rs @@ -1,7 +1,7 @@ //! Library commands: view source, filter/sort, selection, ratings/flags/labels, rotate, delete, //! metadata, albums, import. -use lightcraft_catalog::{Album, AlbumId, ColorLabel, CopyrightStatus, Flag, GroupBy, Op, PhotoId, Sort, SortKey}; +use lightcraft_catalog::{Album, AlbumId, ColorLabel, CopyrightStatus, Filter, Flag, GroupBy, Op, PhotoId, Sort, SortKey}; use serde_json::{Value, json}; use super::{CommandSpec, always, bad, bool_or, cmd, has_active, has_selection, ok, str_param}; @@ -282,7 +282,7 @@ pub fn specs() -> Vec { "Filter", [], None, - "partial Filter: {text?, rating?, ratingOp?: atLeast|exactly|atMost, flag?: pick|reject|none|null, label?, kind?, merged?: hdr|panorama|hdrPanorama|any, edited?, date?, keyword?, person?, camera?}", + "partial Filter: {text?, rating?, ratingOp?: atLeast|exactly|atMost, flag?: pick|reject|none|null, label?, kind?, merged?: hdr|panorama|hdrPanorama|any, edited?, date?, libraryFolder?: a path from library.folders, keyword?, person?, camera?}", always, |s, p| { let mut v = serde_json::to_value(&s.filter).unwrap_or_default(); @@ -291,6 +291,54 @@ pub fn specs() -> Vec { Ok(json!({"count": s.visible().len()})) } ), + cmd!( + query "library.folders", + "Library Folders", + [], + None, + "{} → [{name, path, count, own, children}] the folders the library's photos were imported from, with photo counts (subfolders included in `count`); pass a `path` to library.filter as `libraryFolder`", + always, + |s, _| Ok(serde_json::to_value(s.catalog.folder_tree()).unwrap_or_default()) + ), + cmd!( + "library.removeFolder", + "Remove Folder from Library", + [], + None, + "{path, disk?: bool} — move the library photos imported from this folder (and the folders inside it) to Recently Deleted; one undo step, no file is touched. A whole disk or share (`/Volumes/nas`, `C:\\`, `\\\\srv\\share`) goes only with `disk: true`, the startup disk never → {removed}", + always, + |s, p| { + const C: &str = "library.removeFolder"; + let path = str_param(p, "path").map(str::trim).filter(|d| !d.is_empty()).ok_or_else(|| bad(C, "missing `path`"))?; + if lightcraft_catalog::query::folder_key(path).is_empty() { + return Err(bad(C, format!("{path}: not a folder"))); + } + if lightcraft_catalog::folders::is_startup_disk(path) { + return Err(bad(C, "choose a folder, not the whole startup disk")); + } + if lightcraft_catalog::folders::is_disk_root(path) && !bool_or(p, "disk", false) { + return Err(bad(C, format!("{path} is a whole disk or share: pass `disk: true` to remove everything on it"))); + } + let f = Filter { library_folder: Some(path.to_string()), ..Default::default() }; + let ids = s.catalog.query(&f, &Sort::default()); + if ids.is_empty() { + return Err(bad(C, format!("{path}: no photo in the library was imported from it"))); + } + let disks: std::collections::BTreeSet = + ids.iter().filter_map(|id| s.catalog.photo(*id)).filter_map(|p| lightcraft_catalog::folders::volume_of(p)).collect(); + if disks.len() > 1 && !bool_or(p, "disk", false) { + return Err(bad(C, format!("{path} holds photos of {} disks: pass `disk: true` to remove them all", disks.len()))); + } + let ops = ids.iter().map(|id| Op::SetDeleted { id: *id, deleted: true }).collect(); + s.commit("Remove Folder from Library", Op::Batch { ops })?; + if s.filter.library_folder.as_deref().is_some_and(|c| lightcraft_catalog::query::folder_within(c, path)) { + s.filter.library_folder = None; + } + let vis = s.visible_cloned(); + s.selection = vis.first().map(|f| Selection::single(*f)).unwrap_or_default(); + Ok(json!({"removed": ids.len()})) + } + ), cmd!("library.clearFilter", "Clear Filters", ["View"], None, "{}", always, |s, _| { s.filter = Default::default(); Ok(json!({"count": s.visible().len()})) diff --git a/crates/engine/src/lib.rs b/crates/engine/src/lib.rs index ec39222a..cbbbbd63 100644 --- a/crates/engine/src/lib.rs +++ b/crates/engine/src/lib.rs @@ -709,6 +709,8 @@ mod tests_color; #[cfg(test)] mod tests_export; #[cfg(test)] +mod tests_folders; +#[cfg(test)] mod tests_forget_local; #[cfg(test)] mod tests_import; diff --git a/crates/engine/src/tests_folders.rs b/crates/engine/src/tests_folders.rs new file mode 100644 index 00000000..887870b5 --- /dev/null +++ b/crates/engine/src/tests_folders.rs @@ -0,0 +1,232 @@ +//! Exploring the library by folder: `library.folders` lists where the imported photos live and +//! `library.filter {libraryFolder}` shows one folder's photos (see `lightcraft_catalog::folders`); +//! `library.removeFolder` takes a folder's photos out of the library. +//! +//! * Given photos imported from two folders, an agent can list them with counts, then choose one +//! and see exactly its photos; the filter shows as a chip that clears just that choice. +//! * A path that is not a library folder shows nothing rather than everything. +//! * Removing a folder from the library moves its photos to Recently Deleted (one undo step) and +//! leaves every file where it is. +//! * Renaming or moving a folder on disk keeps the chosen folder chosen. + +use lightcraft_catalog::{Op, Photo, Source}; +use serde_json::json; + +use crate::{LibrarySource, Session, filter_chips}; + +fn add(s: &mut Session, path: &str) { + let id = s.catalog.alloc_photo_id(); + let p = Photo::new(id, Source::File { path: path.into() }, "x.jpg", "JPEG", 60, 40, "2026-01-01T10:00:00"); + s.catalog.apply(Op::AddPhoto { photo: Box::new(p) }).unwrap(); +} + +/// A scratch folder that goes away with the test, however it ends. +struct Scratch(std::path::PathBuf); + +impl Scratch { + fn new(tag: &str) -> Scratch { + let dir = std::env::temp_dir().join(format!("lc-folders-{tag}-{}", std::process::id())); + let _ = std::fs::remove_dir_all(&dir); + std::fs::create_dir_all(&dir).unwrap(); + Scratch(dir) + } + fn path(&self, rel: &str) -> String { + self.0.join(rel).to_string_lossy().to_string() + } +} + +impl Drop for Scratch { + fn drop(&mut self) { + let _ = std::fs::remove_dir_all(&self.0); + } +} + +fn session() -> Session { + let mut s = Session::new(); + for path in ["/pics/trip/a.jpg", "/pics/trip/b.jpg", "/pics/home/c.jpg"] { + add(&mut s, path); + } + s +} + +#[test] +fn the_folders_command_lists_where_the_photos_were_imported_from() { + let mut s = session(); + let r = s.execute("library.folders", &json!({})).unwrap(); + assert_eq!((r[0]["name"].as_str(), r[0]["volume"].as_bool(), r[0]["count"].as_u64()), (Some("/"), Some(true), Some(3))); + let pics = &r[0]["children"][0]; + assert_eq!(pics["name"], "pics"); + let kids: Vec<(&str, u64)> = + pics["children"].as_array().unwrap().iter().map(|c| (c["name"].as_str().unwrap(), c["count"].as_u64().unwrap())).collect(); + assert_eq!(kids, [("home", 1), ("trip", 2)]); +} + +#[test] +fn choosing_a_folder_shows_its_photos_and_a_chip_that_clears_it() { + let mut s = session(); + assert_eq!(s.visible().len(), 3); + let r = s.execute("library.filter", &json!({"libraryFolder": "/pics/trip"})).unwrap(); + assert_eq!(r["count"], 2); + let chips = filter_chips(&s.filter, &s.catalog); + assert_eq!(chips.len(), 1); + assert_eq!(chips[0].label, "Folder: pics/trip", "two names, so two folders called trip are told apart"); + s.execute("library.filter", &chips[0].clear).unwrap(); + assert_eq!(s.visible().len(), 3, "clearing the chip shows everything again"); +} + +#[test] +fn a_path_that_is_not_a_library_folder_shows_nothing() { + let mut s = session(); + let r = s.execute("library.filter", &json!({"libraryFolder": "/elsewhere"})).unwrap(); + assert_eq!(r["count"], 0); +} + +#[test] +fn removing_a_folder_moves_its_photos_to_recently_deleted_and_undo_brings_them_back() { + let mut s = session(); + let r = s.execute("library.removeFolder", &json!({"path": "/pics//trip/"})).unwrap(); + assert_eq!(r["removed"], 2); + assert_eq!(s.visible().len(), 1, "only /pics/home is left"); + let tree = s.execute("library.folders", &json!({})).unwrap(); + assert_eq!(tree[0]["count"], 1); + s.source = LibrarySource::RecentlyDeleted; + assert_eq!(s.visible().len(), 2, "they wait in Recently Deleted"); + s.source = LibrarySource::All; + s.execute("edit.undo", &json!({})).unwrap(); + assert_eq!(s.visible().len(), 3, "one undo step"); +} + +#[test] +fn removing_a_folder_that_holds_nothing_or_a_whole_disk_is_refused() { + let mut s = session(); + assert!(s.execute("library.removeFolder", &json!({"path": "/elsewhere"})).is_err()); + assert!(s.execute("library.removeFolder", &json!({"path": "/"})).is_err(), "choose a folder, not everything"); + assert!(s.execute("library.removeFolder", &json!({"path": " "})).is_err()); + assert!(s.execute("library.removeFolder", &json!({})).is_err()); + assert_eq!(s.visible().len(), 3, "nothing changed"); +} + +#[test] +fn removing_a_folder_leaves_browsed_and_already_deleted_photos_alone() { + let mut s = session(); + for (path, local, deleted) in [("/pics/trip/browsed.jpg", true, false), ("/pics/trip/gone.jpg", false, true)] { + let id = s.catalog.alloc_photo_id(); + let mut p = Photo::new(id, Source::File { path: path.into() }, "x.jpg", "JPEG", 60, 40, "2026-01-01T10:00:00"); + (p.local, p.deleted) = (local, deleted); + s.catalog.apply(Op::AddPhoto { photo: Box::new(p) }).unwrap(); + } + let r = s.execute("library.removeFolder", &json!({"path": "/pics/trip"})).unwrap(); + assert_eq!(r["removed"], 2, "only the two library photos of the folder"); + let state: Vec<(bool, bool)> = s.catalog.photos().filter(|p| p.local || p.deleted).map(|p| (p.local, p.deleted)).collect(); + assert!(state.contains(&(true, false)) && state.contains(&(false, true)), "browsed stays browsed, the old deletion stays: {state:?}"); +} + +#[test] +fn removing_the_folder_that_is_chosen_clears_the_choice_but_a_parent_stays() { + let mut s = session(); + s.execute("library.filter", &json!({"libraryFolder": "/pics/trip"})).unwrap(); + s.execute("library.removeFolder", &json!({"path": "/pics/trip"})).unwrap(); + assert_eq!(s.filter.library_folder, None, "no empty grid under a chip for a folder that is gone"); + let mut s = session(); + s.execute("library.filter", &json!({"libraryFolder": "/pics"})).unwrap(); + s.execute("library.removeFolder", &json!({"path": "/pics/trip"})).unwrap(); + assert_eq!(s.filter.library_folder.as_deref(), Some("/pics")); +} + +#[test] +fn a_path_that_names_no_folder_removes_nothing() { + let mut s = session(); + for path in [".", "./", "a/..", "../..", "x/../.."] { + assert!(s.execute("library.removeFolder", &json!({"path": path})).is_err(), "{path:?}"); + } + assert_eq!(s.visible().len(), 3); +} + +#[test] +fn a_whole_disk_goes_only_when_asked_for_by_name_and_never_the_startup_disk() { + let mut s = Session::new(); + for p in ["/Volumes/nas/a/1.jpg", "/Volumes/nas/2.jpg", "/Users/me/3.jpg", r"C:\x\4.jpg", r"\\srv\share\5.jpg"] { + add(&mut s, p); + } + for path in ["/Volumes/nas", "C:", "C:\\", r"\\?\C:\", "C:\\..", r"\\srv\share"] { + assert!(s.execute("library.removeFolder", &json!({"path": path})).is_err(), "{path:?} is a whole disk"); + } + for path in ["/", "//"] { + assert!(s.execute("library.removeFolder", &json!({"path": path, "disk": true})).is_err(), "{path:?}: never the startup disk"); + } + assert_eq!(s.visible().len(), 5, "nothing changed"); + let r = s.execute("library.removeFolder", &json!({"path": "/Volumes/nas", "disk": true})).unwrap(); + assert_eq!(r["removed"], 2, "everything on the nas, nothing elsewhere"); + assert_eq!(s.visible().len(), 3); + s.execute("edit.undo", &json!({})).unwrap(); + assert_eq!(s.visible().len(), 5); +} + +#[test] +fn a_folder_that_spans_several_disks_is_removed_only_when_asked_for_by_name() { + let mut s = Session::new(); + for p in ["/Volumes/nas/a/1.jpg", "/Volumes/tokyo/2.jpg", "/Volumes/3.jpg"] { + add(&mut s, p); + } + assert!(s.execute("library.removeFolder", &json!({"path": "/Volumes"})).is_err(), "it would take two disks' photos at once"); + assert_eq!(s.visible().len(), 3); + let r = s.execute("library.removeFolder", &json!({"path": "/Volumes", "disk": true})).unwrap(); + assert_eq!(r["removed"], 3); +} + +#[test] +fn the_folder_filter_takes_only_a_text_path() { + let mut s = session(); + assert!(s.execute("library.filter", &json!({"libraryFolder": 5})).is_err()); + assert_eq!(s.filter.library_folder, None, "refused, nothing changed"); + s.execute("library.filter", &json!({"libraryFolder": " "})).unwrap(); + assert_eq!(s.visible().len(), 3, "a blank path is no choice"); + s.execute("library.filter", &json!({"libraryFolder": "."})).unwrap(); + assert_eq!(s.visible().len(), 0, "a path that names no folder shows nothing, not everything"); +} + +#[test] +fn a_renamed_folder_stays_the_chosen_one_and_undo_follows_it_back() { + let dir = Scratch::new("rename"); + std::fs::create_dir_all(dir.0.join("trip/day1")).unwrap(); + let (trip, renamed) = (dir.path("trip"), dir.path("holiday")); + let mut s = Session::new(); + add(&mut s, &format!("{trip}/a.jpg")); + add(&mut s, &format!("{trip}/day1/b.jpg")); + s.execute("library.filter", &json!({"libraryFolder": trip})).unwrap(); + assert_eq!(s.visible().len(), 2); + s.execute("folder.rename", &json!({"path": trip, "name": "holiday"})).unwrap(); + assert_eq!(s.filter.library_folder.as_deref(), Some(renamed.as_str()), "the choice follows the folder"); + assert_eq!(s.visible().len(), 2, "and still shows its photos"); + s.execute("edit.undo", &json!({})).unwrap(); + assert_eq!(s.filter.library_folder.as_deref(), Some(trip.as_str())); + assert_eq!(s.visible().len(), 2); +} + +#[test] +fn a_moved_folder_keeps_a_chosen_subfolder_chosen() { + let dir = Scratch::new("move"); + std::fs::create_dir_all(dir.0.join("trip/day1")).unwrap(); + std::fs::create_dir_all(dir.0.join("archive")).unwrap(); + let (day1, moved) = (dir.path("trip/day1"), dir.path("archive/trip/day1")); + let mut s = Session::new(); + add(&mut s, &format!("{day1}/b.jpg")); + s.execute("library.filter", &json!({"libraryFolder": day1})).unwrap(); + s.execute("folder.move", &json!({"path": dir.path("trip"), "into": dir.path("archive")})).unwrap(); + assert_eq!(s.filter.library_folder.as_deref(), Some(moved.as_str()), "the subfolder follows its parent"); + assert_eq!(s.visible().len(), 1); + s.execute("edit.undo", &json!({})).unwrap(); + assert_eq!(s.filter.library_folder.as_deref(), Some(day1.as_str())); +} + +#[test] +fn following_a_folder_reads_both_spellings_the_same_way() { + // the choice was typed with a `..` in it; the folder that moved was not + let mut s = Session::new(); + s.filter.library_folder = Some("/a/x/../b/sub".into()); + crate::cmd::browse::follow_folder(&mut s, "/a/b", "/a/c"); + assert_eq!(s.filter.library_folder.as_deref(), Some("/a/c/sub"), "a subfolder stays a subfolder, never widens to its parent"); + s.filter.library_folder = Some("/elsewhere/b".into()); + crate::cmd::browse::follow_folder(&mut s, "/a/b", "/a/c"); + assert_eq!(s.filter.library_folder.as_deref(), Some("/elsewhere/b"), "other folders are left alone"); +} diff --git a/crates/engine/src/view.rs b/crates/engine/src/view.rs index 757adfe9..1b0f0ca4 100644 --- a/crates/engine/src/view.rs +++ b/crates/engine/src/view.rs @@ -173,6 +173,11 @@ pub fn filter_chips(f: &Filter, cat: &Catalog) -> Vec { if let Some(d) = &f.date { add(format!("Date: {}", date_label(d)), json!({"date": Null})); } + if let Some(d) = f.library_folder.as_deref().filter(|d| !d.trim().is_empty()) { + // the last two names, so two folders called "Pictures" are told apart + let name = lightcraft_catalog::folders::folder_label(d); + add(format!("Folder: {name}"), json!({"libraryFolder": Null})); + } if let Some(d) = &f.imported { add(format!("Imported: {}", date_label(d)), json!({"imported": Null})); } diff --git a/crates/ui-egui/locales/ja-formats.json b/crates/ui-egui/locales/ja-formats.json index 1944d7b6..5a7d7a11 100644 --- a/crates/ui-egui/locales/ja-formats.json +++ b/crates/ui-egui/locales/ja-formats.json @@ -118,5 +118,6 @@ "{verb} {n} Photo{}": "{n}枚を{verb}{:.0}", "{weekday}, {day}": "{day}日({weekday})", "{weekday}, {day} {month} {year}": "{year}年{month}月{day}日({weekday})", - "{year}": "{year}年" + "{year}": "{year}年", + "Remove “{name}” and its {count} photo{} from the library?": "「{name}」と写真{count}枚をライブラリから除去しますか?{:.0}" } diff --git a/crates/ui-egui/locales/ja.json b/crates/ui-egui/locales/ja.json index ca2089af..d2376d64 100644 --- a/crates/ui-egui/locales/ja.json +++ b/crates/ui-egui/locales/ja.json @@ -1029,5 +1029,13 @@ "Friday": "金曜日", "Saturday": "土曜日", "Sunday": "日曜日", - "Unknown Date": "日付不明" + "Unknown Date": "日付不明", + "Remove Folder from Library": "フォルダをライブラリから除去", + "Remove from Library…": "ライブラリから除去…", + "This Computer": "このコンピュータ", + "includes the folders inside it": "内側のフォルダを含む", + "They move to Recently Deleted and can be restored; no file on disk is touched.": "「最近削除した写真」に移動し、復元できます。ディスク上のファイルには手を加えません", + "Moves the photos imported from this folder to Recently Deleted; no file is touched": "このフォルダから読み込んだ写真を「最近削除した写真」に移動します。ファイルには手を加えません", + "Remove Disk from Library…": "ディスクをライブラリから除去…", + "Moves every photo imported from this disk to Recently Deleted; no file is touched": "このディスクから読み込んだすべての写真を「最近削除した写真」に移動します。ファイルには手を加えません" } diff --git a/crates/ui-egui/locales/pt-br-formats.json b/crates/ui-egui/locales/pt-br-formats.json index 50b0f2ab..59605687 100644 --- a/crates/ui-egui/locales/pt-br-formats.json +++ b/crates/ui-egui/locales/pt-br-formats.json @@ -61,6 +61,7 @@ "Reading photos… {done} of {total}": "Lendo fotos… {done} de {total}", "Reading photos… {} of {}": "Lendo fotos… {} de {}", "Redo {}": "Refazer {}", + "Remove “{name}” and its {count} photo{} from the library?": "Remover “{name}” e {count} foto{} da biblioteca?", "Remove “{}” from Favorites": "Remover “{}” dos favoritos", "Rename {n} Photos…": "Renomear {n} fotos…", "Rename “{name}”": "Renomear “{name}”", diff --git a/crates/ui-egui/locales/pt-br.json b/crates/ui-egui/locales/pt-br.json index 503b2eda..3b2b3b8d 100644 --- a/crates/ui-egui/locales/pt-br.json +++ b/crates/ui-egui/locales/pt-br.json @@ -681,6 +681,8 @@ "Move to": "Mover para", "Move to Group": "Mover para grupo", "Move: the files are moved to the folder below; each original (and its XMP sidecar) is removed from the source only after its copy is verified. Duplicates and files that fail stay where they are.": "Mover: os arquivos são movidos para a pasta abaixo; cada original (e seu sidecar XMP) é removido da origem somente após a cópia ser verificada. Duplicatas e arquivos que falham permanecem onde estão.", + "Moves every photo imported from this disk to Recently Deleted; no file is touched": "Move todas as fotos importadas deste disco para Excluídos recentemente; nenhum arquivo é alterado", + "Moves the photos imported from this folder to Recently Deleted; no file is touched": "Move as fotos importadas desta pasta para Excluídos recentemente; nenhum arquivo é alterado", "Muted": "Suave", "Muted Film": "Filme suave", "My Photos": "Minhas fotos", @@ -900,8 +902,11 @@ "Remove (H)": "Remover (H)", "Remove CA": "Remover AC", "Remove Chromatic Aberration": "Remover aberração cromática", + "Remove Disk from Library…": "Remover disco da biblioteca…", + "Remove Folder from Library": "Remover pasta da biblioteca", "Remove from Album": "Remover do álbum", "Remove from Favorites": "Remover dos favoritos", + "Remove from Library…": "Remover da biblioteca…", "Remove from Local": "Remover de Local", "Remove from Photo": "Remover da foto", "Remove from Selected Photos": "Remover das fotos selecionadas", @@ -1147,7 +1152,9 @@ "The photo has these settings": "A foto tem estas configurações", "Theme": "Tema", "They can be restored from Recently Deleted until it is emptied.": "Elas podem ser restauradas em Excluídos recentemente até que ele seja esvaziado.", + "They move to Recently Deleted and can be restored; no file on disk is touched.": "Elas vão para Excluídos recentemente e podem ser restauradas; nenhum arquivo no disco é alterado.", "Thirds": "Terços", + "This Computer": "Este computador", "This folder is not available (drive disconnected?). Smart previews are not built or used until it is back or you choose another folder.": "Esta pasta não está disponível (unidade desconectada?). As pré-visualizações inteligentes não são geradas nem usadas até ela voltar ou você escolher outra pasta.", "This photo can't be opened": "Não foi possível abrir esta foto", "Threshold": "Limite", @@ -1251,6 +1258,7 @@ "Zoom to page level": "Zoom no nível da página", "and": "e", "comma, separated": "separado por vírgulas", + "includes the folders inside it": "inclui as pastas dentro dela", "memory only": "somente memória", "none": "nenhum", "not available here": "não disponível aqui", diff --git a/crates/ui-egui/locales/zh-hans-formats.json b/crates/ui-egui/locales/zh-hans-formats.json index c3713eb1..e4ae48f4 100644 --- a/crates/ui-egui/locales/zh-hans-formats.json +++ b/crates/ui-egui/locales/zh-hans-formats.json @@ -118,5 +118,6 @@ "{month}": "{month}月", "{month} {year}": "{year}年{month}月", "{weekday}, {day}": "{day}日 {weekday}", - "{weekday}, {day} {month} {year}": "{year}年{month}月{day}日 {weekday}" + "{weekday}, {day} {month} {year}": "{year}年{month}月{day}日 {weekday}", + "Remove “{name}” and its {count} photo{} from the library?": "要将“{name}”及其 {count} 张照片从图库中移除吗?{:.0}" } diff --git a/crates/ui-egui/locales/zh-hans.json b/crates/ui-egui/locales/zh-hans.json index 2090461a..9f411f3c 100644 --- a/crates/ui-egui/locales/zh-hans.json +++ b/crates/ui-egui/locales/zh-hans.json @@ -1246,5 +1246,13 @@ "this photo": "这张照片", "日本語": "日本語", "繁體中文(台灣)": "繁體中文(台灣)", - "In Recently Deleted": "在“最近删除”中" + "In Recently Deleted": "在“最近删除”中", + "Remove Folder from Library": "从图库中移除文件夹", + "Remove from Library…": "从图库中移除…", + "This Computer": "此电脑", + "includes the folders inside it": "包含其中的文件夹", + "They move to Recently Deleted and can be restored; no file on disk is touched.": "它们会移到“最近删除”,可以恢复;磁盘上的文件不会被改动。", + "Moves the photos imported from this folder to Recently Deleted; no file is touched": "将从此文件夹导入的照片移到“最近删除”;不会改动任何文件", + "Remove Disk from Library…": "从图库中移除磁盘…", + "Moves every photo imported from this disk to Recently Deleted; no file is touched": "将从此磁盘导入的所有照片移到“最近删除”;不会改动任何文件" } diff --git a/crates/ui-egui/locales/zh-hant-formats.json b/crates/ui-egui/locales/zh-hant-formats.json index 816936c0..1c1266c0 100644 --- a/crates/ui-egui/locales/zh-hant-formats.json +++ b/crates/ui-egui/locales/zh-hant-formats.json @@ -118,5 +118,6 @@ "… and {more} more": "…以及另外 {more} 個", " ({} in Recently Deleted)": "(其中 {} 張在「最近刪除」中)", "Already in the library: {} photo{} selected in All Photos": "已在照片庫中:已在「所有照片」中選取 {} 張照片{:.0}", - "Already in the library, in Recently Deleted: {} photo{} selected · right-click ▸ Restore, or Delete Permanently to import afresh": "已在照片庫的「最近刪除」中:已選取 {} 張照片{:.0} · 按右鍵 ▸ 還原,或永久刪除後重新匯入" + "Already in the library, in Recently Deleted: {} photo{} selected · right-click ▸ Restore, or Delete Permanently to import afresh": "已在照片庫的「最近刪除」中:已選取 {} 張照片{:.0} · 按右鍵 ▸ 還原,或永久刪除後重新匯入", + "Remove “{name}” and its {count} photo{} from the library?": "要將「{name}」及其 {count} 張照片從照片庫移除嗎?{:.0}" } diff --git a/crates/ui-egui/locales/zh-hant.json b/crates/ui-egui/locales/zh-hant.json index db538f48..863de2db 100644 --- a/crates/ui-egui/locales/zh-hant.json +++ b/crates/ui-egui/locales/zh-hant.json @@ -1246,5 +1246,13 @@ "this photo": "這張照片", "日本語": "日本語", "繁體中文(台灣)": "繁體中文(台灣)", - "In Recently Deleted": "在「最近刪除」中" + "In Recently Deleted": "在「最近刪除」中", + "Remove Folder from Library": "從照片庫移除資料夾", + "Remove from Library…": "從照片庫移除…", + "This Computer": "這部電腦", + "includes the folders inside it": "包含其中的資料夾", + "They move to Recently Deleted and can be restored; no file on disk is touched.": "它們會移至「最近刪除」,可以還原;磁碟上的檔案不會被更動。", + "Moves the photos imported from this folder to Recently Deleted; no file is touched": "將從此資料夾匯入的照片移至「最近刪除」;不會更動任何檔案", + "Remove Disk from Library…": "從照片庫移除磁碟…", + "Moves every photo imported from this disk to Recently Deleted; no file is touched": "將從此磁碟匯入的所有照片移至「最近刪除」;不會更動任何檔案" } diff --git a/crates/ui-egui/src/lib.rs b/crates/ui-egui/src/lib.rs index b7f4d6d6..a0a3cec1 100644 --- a/crates/ui-egui/src/lib.rs +++ b/crates/ui-egui/src/lib.rs @@ -890,6 +890,7 @@ mod drop_tests { #[derive(Default)] pub struct Caches { keyword_tree: Option<(u64, std::sync::Arc>)>, + folder_tree: Option<(u64, std::sync::Arc>)>, people: Option<(u64, lightcraft_catalog::Filter, std::sync::Arc>)>, suggestions: Option<(u64, std::sync::Arc>)>, counts: Option<(u64, LibraryCounts)>, @@ -926,6 +927,17 @@ pub(crate) fn key_of(parts: impl std::hash::Hash) -> u64 { } impl Caches { + /// The folders the library's photos were imported from. + pub fn folder_tree(&mut self, cat: &lightcraft_catalog::Catalog) -> std::sync::Arc> { + match &self.folder_tree { + Some((r, t)) if *r == cat.revision => t.clone(), + _ => { + let t = std::sync::Arc::new(cat.folder_tree()); + self.folder_tree = Some((cat.revision, t.clone())); + t + } + } + } /// The library's keyword tree. pub fn keyword_tree(&mut self, cat: &lightcraft_catalog::Catalog) -> std::sync::Arc> { match &self.keyword_tree { diff --git a/crates/ui-egui/src/panels/dialogs.rs b/crates/ui-egui/src/panels/dialogs.rs index 91ffb1c9..cfcc476f 100644 --- a/crates/ui-egui/src/panels/dialogs.rs +++ b/crates/ui-egui/src/panels/dialogs.rs @@ -97,6 +97,7 @@ pub fn show(app: &mut LightcraftApp, ctx: &egui::Context) { Dialog::Merge { opts } => opts.title(), Dialog::Settings { .. } => "Settings", Dialog::ConfirmDelete { .. } => "Delete Photos", + Dialog::RemoveFolder { .. } => "Remove Folder from Library", Dialog::SamModel { .. } => "Download the SAM 3 Model?", Dialog::About => "About LightCraft", Dialog::Shortcuts => "Keyboard Shortcuts", @@ -710,6 +711,19 @@ pub fn show(app: &mut LightcraftApp, ctx: &egui::Context) { ui.label(crate::i18n::tr_format!("Move {what} to Recently Deleted?", what = what)); ui.label(egui::RichText::new(crate::i18n::tr("They can be restored from Recently Deleted until it is emptied.")).color(t.text_dim)); } + Dialog::RemoveFolder { name, count, path, .. } => { + ui.label(crate::i18n::tr_format!( + "Remove “{name}” and its {count} photo{} from the library?", + if *count == 1 { "" } else { "s" }, + name = name, + count = count + )); + ui.label(egui::RichText::new(path.as_str()).color(t.text_dim)); + ui.label( + egui::RichText::new(crate::i18n::tr("They move to Recently Deleted and can be restored; no file on disk is touched.")) + .color(t.text_dim), + ); + } Dialog::About => { ui.set_min_width(680.0); let tab_id = egui::Id::new("about_tab"); @@ -817,6 +831,7 @@ pub fn show(app: &mut LightcraftApp, ctx: &egui::Context) { } Dialog::Merge { .. } => "Merge", Dialog::ConfirmDelete { .. } => "Delete", + Dialog::RemoveFolder { .. } => "Remove", Dialog::SamModel { then: Some(_), .. } if sam_installed => "Continue", Dialog::SamModel { .. } if sam_installed || sam_running || sam_nowhere => "", Dialog::SamModel { error, .. } if error.is_some() || sam_failed => "Try Again", @@ -1030,6 +1045,7 @@ pub fn confirm_dialog(app: &mut LightcraftApp, dlg: &Dialog) -> Result crate::merge::start_final(app, opts), Dialog::Import { opts } => crate::import::start(app, opts), Dialog::ConfirmDelete { .. } => app.run("photo.delete", json!({})), + Dialog::RemoveFolder { path, disk, .. } => app.run("library.removeFolder", json!({"path": path, "disk": disk})), Dialog::About | Dialog::Shortcuts | Dialog::Settings { .. } => Ok(serde_json::Value::Null), } } diff --git a/crates/ui-egui/src/panels/left.rs b/crates/ui-egui/src/panels/left.rs index f69b487f..884d346a 100644 --- a/crates/ui-egui/src/panels/left.rs +++ b/crates/ui-egui/src/panels/left.rs @@ -1,7 +1,7 @@ //! The left "My Photos" panel: library sources, albums tree, and date groups. use egui::{Align2, Rect, Sense, pos2, vec2}; -use lightcraft_catalog::{Album, AlbumId, KeywordNode}; +use lightcraft_catalog::{Album, AlbumId, FolderNode, KeywordNode}; use lightcraft_engine::LibrarySource; use serde_json::json; @@ -19,14 +19,30 @@ fn row( count: Option, selected: bool, indent: f32, +) -> egui::Response { + row_named(app, ui, id, icon, label, None, count, selected, indent) +} + +/// [`row`] whose spoken name is `spoken` when the painted `label` is a shortened form of it. +fn row_named( + app: &mut LightcraftApp, + ui: &mut egui::Ui, + id: &str, + icon: Icon, + label: &str, + spoken: Option<&str>, + count: Option, + selected: bool, + indent: f32, ) -> egui::Response { let label = if matches!(id, "all" | "recentlyAdded" | "picks" | "missing" | "recentlyDeleted") { crate::i18n::tr(label) } else { label }; let t = Tokens::get(ui.ctx()); let (r, resp) = ui.allocate_exact_size(vec2(ui.available_width(), 29.0), Sense::click()); register(ui.ctx(), format!("source:{id}"), r); + let said = spoken.unwrap_or(label); let name = match count { - Some(n) => crate::i18n::tr_format!("{label}, {n} photos", label = label, n = n), - None => label.to_string(), + Some(n) => crate::i18n::tr_format!("{label}, {n} photos", label = said, n = n), + None => said.to_string(), }; resp.widget_info(|| egui::WidgetInfo::selected(egui::WidgetType::SelectableLabel, true, selected, &name)); let inner = r.shrink2(vec2(8.0, 0.0)); @@ -158,6 +174,7 @@ pub fn show(app: &mut LightcraftApp, ui: &mut egui::Ui) { } } } + folders_section(app, ui); keywords_section(app, ui); ui.add_space(10.0); if row(app, ui, "recentlyDeleted", Icon::Trash, "Recently Deleted", Some(deleted), src == LibrarySource::RecentlyDeleted, 0.0).clicked() { @@ -715,6 +732,199 @@ fn is_within(app: &LightcraftApp, id: lightcraft_catalog::AlbumId, ancestor: lig false } +/// "Folders": where on disk the library's photos were imported from, with photo counts (see +/// `lightcraft_catalog::folders`). A click shows the photos imported from that folder and the +/// folders inside it, the triangle opens a level. Only folders holding imported photos are +/// listed; every folder on disk is under Local. +fn folders_section(app: &mut LightcraftApp, ui: &mut egui::Ui) { + let tree = app.caches.folder_tree(&app.session.catalog); + if tree.is_empty() { + return; + } + ui.add_space(10.0); + if sidebar_section_header(app, ui, "folders", "Folders").1 { + reveal_chosen(app, ui, &tree); + folder_rows(app, ui, &tree, 0.0); + } +} + +/// `text` shortened to fit `max` (as `width` measures it): leading folders drop first +/// (`Users/me/Pictures/Lightroom` → `…/Pictures/Lightroom`) so the end of a path, which says the +/// most, stays; a single name still too long loses its end (`2024-06-12 Tri…`: folders tend to +/// differ at the start). Cuts fall on characters. The result is never empty. +fn elide_head(text: &str, max: f32, width: impl Fn(&str) -> f32) -> String { + if width(text) <= max { + return text.to_string(); + } + let mut rest = text; + while let Some(i) = rest.find('/') { + rest = rest.get(i + 1..).unwrap_or(""); + let cand = format!("…/{rest}"); + if width(&cand) <= max { + return cand; + } + } + let n = rest.chars().count(); + for keep in (1..n).rev() { + let cand: String = rest.chars().take(keep).chain(std::iter::once('…')).collect(); + if width(&cand) <= max { + return cand; + } + } + "…".to_string() +} + +/// Whenever the chosen folder changes (a click, an agent, a rename or its undo), open the rows +/// above it so it is on screen; folding one by hand afterwards sticks until the choice changes. +fn reveal_chosen(app: &LightcraftApp, ui: &egui::Ui, tree: &[FolderNode]) { + let chosen = app.session.filter.library_folder.clone().filter(|c| !lightcraft_catalog::query::folder_key(c).is_empty()); + let seen = egui::Id::new("libfolder-revealed"); + let now = chosen.as_deref().map(lightcraft_catalog::query::folder_key); + if ui.data(|d| d.get_temp::>(seen)) == Some(now.clone()) { + return; + } + ui.data_mut(|d| d.insert_temp(seen, now)); + let Some(chosen) = chosen else { return }; + fn open_above(ui: &egui::Ui, nodes: &[FolderNode], chosen: &str) { + for n in nodes { + if lightcraft_catalog::query::folder_within(chosen, &n.path) && !same_folder(chosen, &n.path) { + let key = lightcraft_catalog::query::folder_key(&n.path); + ui.data_mut(|d| d.insert_temp(egui::Id::new(("libfolder-open", key)), true)); + open_above(ui, &n.children, chosen); + } + } + } + open_above(ui, tree, &chosen); +} + +fn folder_rows(app: &mut LightcraftApp, ui: &mut egui::Ui, nodes: &[FolderNode], indent: f32) { + let t = Tokens::get(ui.ctx()); + for n in nodes { + let key = lightcraft_catalog::query::folder_key(&n.path); + let open_id = egui::Id::new(("libfolder-open", key.clone())); + // a disk starts open: its folders are what the section is for + let mut open: bool = ui.data(|d| d.get_temp(open_id)).unwrap_or(n.volume); + // a row whose path would cover other disks' photos too only opens and closes + let selectable = n.selectable; + let sel = selectable && app.session.filter.library_folder.as_deref().is_some_and(|f| same_folder(f, &n.path)); + let name = if selectable { n.name.clone() } else { crate::i18n::tr("This Computer").to_string() }; + // room for the name: between the icon and the count + let font = t.font(13.5); + let count_w = + if app.ui.show_counts { ui.painter().layout_no_wrap(n.count.to_string(), t.font(12.5), egui::Color32::WHITE).size().x } else { 0.0 }; + let room = ui.available_width() - 42.0 - indent - count_w - 28.0; + let label = elide_head(&name, room, |s| ui.painter().layout_no_wrap(s.to_string(), font.clone(), egui::Color32::WHITE).size().x); + let resp = row_named(app, ui, &format!("libfolder:{}", n.path), Icon::Folder, &label, Some(&name), Some(n.count), sel, indent); + let mut toggled = false; + if !n.children.is_empty() { + // disclosure triangle left of the icon + let c = pos2(resp.rect.left() + 10.0 + indent, resp.rect.center().y); + let tri = Rect::from_center_size(c, vec2(14.0, 14.0)); + let tr = ui.interact(tri, egui::Id::new(("libfolder-tri", key)), Sense::click()); + register(ui.ctx(), format!("libraryFolderToggle:{}", n.path), tri); + let col = if tr.hovered() { t.text } else { t.text_dim }; + let pts = if open { + vec![c + vec2(-4.0, -2.0), c + vec2(4.0, -2.0), c + vec2(0.0, 3.0)] + } else { + vec![c + vec2(-2.0, -4.0), c + vec2(3.0, 0.0), c + vec2(-2.0, 4.0)] + }; + ui.painter().add(egui::Shape::convex_polygon(pts, col, egui::Stroke::NONE)); + toggled = tr.clicked(); + // the triangle sits on the row and takes its clicks: the menu opens from it too + row_menu(app, &tr, n); + } + let tip = if n.children.is_empty() { n.path.clone() } else { format!("{} ({})", n.path, crate::i18n::tr("includes the folders inside it")) }; + let resp = resp.on_hover_text(tip); + if resp.clicked() && !toggled { + if selectable { + // the library's folder, not a mix with the Local folder being browsed + if app.session.source == LibrarySource::Folder { + let _ = app.run("library.source", json!({"kind": "all"})); + } + let v = if sel { serde_json::Value::Null } else { json!(n.path) }; + let _ = app.run("library.filter", json!({"libraryFolder": v})); + } else { + toggled = true; + } + } + if toggled { + open = !open; + ui.data_mut(|d| d.insert_temp(open_id, open)); + } + row_menu(app, &resp, n); + if open && !n.children.is_empty() { + folder_rows(app, ui, &n.children, indent + 16.0); + } + } +} + +/// The context menu of a Folders row: a folder's own, a disk's (remove it), none for the +/// startup disk. +fn row_menu(app: &mut LightcraftApp, resp: &egui::Response, n: &FolderNode) { + if !n.volume { + folder_menu_for_library(app, resp, n); + } else if n.path != "/" { + resp.context_menu(|ui| { + if ui + .button(crate::i18n::tr("Remove Disk from Library…")) + .on_hover_text(crate::i18n::tr("Moves every photo imported from this disk to Recently Deleted; no file is touched")) + .clicked() + { + app.ui.dialog = Some(crate::state::Dialog::RemoveFolder { path: n.path.clone(), name: n.name.clone(), count: n.count, disk: true }); + ui.close(); + } + }); + } +} + +/// The context menu of a folder row: the folder's disk actions (the same as Local's, photos +/// follow) and taking its photos out of the library. +fn folder_menu_for_library(app: &mut LightcraftApp, resp: &egui::Response, n: &FolderNode) { + let path = n.path.as_str(); + let name = std::path::Path::new(path).file_name().map(|f| f.to_string_lossy().to_string()).unwrap_or_else(|| path.to_string()); + // what dialogs call it: the last two names, so same-named folders are told apart + let label = lightcraft_catalog::folders::folder_label(path); + resp.context_menu(|ui| { + if ui.button(crate::i18n::tr("Rename Folder…")).clicked() { + app.ui.dialog = Some(crate::state::Dialog::TextPrompt { + title: crate::i18n::tr_format!("Rename “{name}”", name = label), + hint: "Folder name (renamed on disk; its photos follow)".into(), + value: name.clone(), + command: "folder.rename".into(), + params: json!({"path": path}), + key: "name".into(), + }); + ui.close(); + } + if app.services.pick_folder.is_some() && ui.button(crate::i18n::tr("Move Folder To…")).clicked() { + let into = app.services.pick_folder.as_mut().and_then(|f| f()); + if let Some(into) = into { + match app.run("folder.move", json!({"path": path, "into": into})) { + Ok(r) => app.toast(ui.ctx(), format!("Moved; {} photo(s) relinked", r["relinked"])), + Err(e) => app.toast(ui.ctx(), e), + } + } + ui.close(); + } + if app.services.reveal.is_some() + && ui.button(crate::i18n::tr("Show in Finder")).clicked() + && let Some(f) = app.services.reveal.as_mut() + { + let _ = f(path); + ui.close(); + } + ui.separator(); + if ui + .button(crate::i18n::tr("Remove from Library…")) + .on_hover_text(crate::i18n::tr("Moves the photos imported from this folder to Recently Deleted; no file is touched")) + .clicked() + { + app.ui.dialog = Some(crate::state::Dialog::RemoveFolder { path: path.to_string(), name: label.clone(), count: n.count, disk: false }); + ui.close(); + } + }); +} + /// "Keywords": the library's keyword tree with photo counts (`a|b|c` keywords nest). A click /// filters the grid by the keyword (children included), the triangle opens a level, and the /// context menu renames, merges or deletes the keyword across the library. @@ -786,7 +996,35 @@ fn keyword_rows(app: &mut LightcraftApp, ui: &mut egui::Ui, nodes: &[KeywordNode #[cfg(test)] mod tests { - use super::local_places; + use super::{elide_head, local_places}; + + /// Width = characters, so a limit of 12 is "12 characters". + fn fit(text: &str, max: usize) -> String { + elide_head(text, max as f32, |s| s.chars().count() as f32) + } + + #[test] + fn a_label_that_fits_is_left_alone() { + assert_eq!(fit("/Users/me/Pictures", 18), "/Users/me/Pictures"); + } + + #[test] + fn a_long_path_loses_its_leading_folders_not_its_end() { + assert_eq!(fit("/Users/me/Pictures/Lightroom", 20), "…/Pictures/Lightroom"); + assert_eq!(fit("/Users/me/Pictures/Lightroom", 12), "…/Lightroom"); + } + + #[test] + fn a_single_name_too_long_is_cut_at_the_end() { + assert_eq!(fit("2024-summer-holiday", 8), "2024-su…"); + assert_eq!(fit("2024-summer-holiday", 1), "…"); + assert_eq!(fit("2024-summer-holiday", 0), "…"); + } + + #[test] + fn multibyte_names_are_cut_on_character_boundaries() { + assert_eq!(fit("/写真/夏休み旅行の記録", 6), "夏休み旅行…"); + } fn names(v: &[(String, String)]) -> Vec<&str> { v.iter().map(|(n, _)| n.as_str()).collect() diff --git a/crates/ui-egui/src/state.rs b/crates/ui-egui/src/state.rs index 9d4df859..ecf412d2 100644 --- a/crates/ui-egui/src/state.rs +++ b/crates/ui-egui/src/state.rs @@ -533,6 +533,15 @@ pub enum Dialog { ConfirmDelete { count: usize, }, + /// Confirm taking a folder's photos out of the library (`library.removeFolder`). + RemoveFolder { + path: String, + /// What the question calls it (a folder's last two names, a disk's name). + name: String, + count: usize, + /// A whole disk or share (`library.removeFolder` takes it only on request). + disk: bool, + }, About, Shortcuts, } diff --git a/crates/ui-egui/src/tests_panels.rs b/crates/ui-egui/src/tests_panels.rs index a289e2ee..e5134290 100644 --- a/crates/ui-egui/src/tests_panels.rs +++ b/crates/ui-egui/src/tests_panels.rs @@ -199,3 +199,190 @@ fn sidebar_sections_collapse_and_remember_it() { click(&mut h, "icon:albumNew"); assert!(!h.app.ui.sidebar_section_collapsed("albums"), "the plus does not fold Albums"); } + +/// A headless app over a library of file-backed photos that exist only in the catalog. +fn folders_app(paths: &[&str]) -> Headless { + use lightcraft_catalog::{Op, Photo, Source}; + let mut session = lightcraft_engine::Session::new(); + for path in paths { + let id = session.catalog.alloc_photo_id(); + let p = Photo::new(id, Source::File { path: (*path).into() }, "x.jpg", "JPEG", 60, 40, "2026-01-01T10:00:00"); + session.catalog.apply(Op::AddPhoto { photo: Box::new(p) }).unwrap(); + } + let app = LightcraftApp::new(session, Services { png: None, ..Default::default() }); + let mut h = Headless::new(app, [1400.0, 900.0], 1.0); + let r = h.request("ui.set", json!({"view": "photoGrid", "leftPanel": true}), T); + assert_eq!(r["ok"], true, "{r}"); + h.settle(SETTLE); + h +} + +fn has(h: &Headless, id: &str) -> bool { + h.app.widgets.iter().any(|(w, _)| w == id) +} + +fn click(h: &mut Headless, id: &str) { + let r = h.request("ui.clickWidget", json!({"id": id}), T); + assert_eq!(r["ok"], true, "{r}"); + h.step(); + h.step(); +} + +/// Photos imported from two folders: the sidebar's Folders section lists them, choosing one +/// narrows the grid to its photos, and choosing it again shows everything (like By Date). +#[test] +fn folders_section_lists_where_photos_were_imported_from_and_narrows_the_grid() { + let mut h = folders_app(&["/pics/trip/a.jpg", "/pics/trip/b.jpg", "/pics/home/c.jpg"]); + // the disk's row starts open; the folders inside a folder stay folded until it is opened + assert!(has(&h, "source:libfolder:/pics"), "the Folders section lists the library's folders"); + assert!(!has(&h, "source:libfolder:/pics/trip")); + click(&mut h, "libraryFolderToggle:/pics"); + assert!(has(&h, "source:libfolder:/pics/trip") && has(&h, "source:libfolder:/pics/home")); + assert_eq!(h.app.session.visible().len(), 3); + click(&mut h, "source:libfolder:/pics/trip"); + assert_eq!(h.app.session.visible().len(), 2, "only the photos imported from that folder"); + assert_eq!(h.app.session.filter.library_folder.as_deref(), Some("/pics/trip")); + click(&mut h, "source:libfolder:/pics/trip"); + assert_eq!(h.app.session.visible().len(), 3, "choosing it again shows everything"); + // the section folds like the others + click(&mut h, "sidebarSection:folders"); + assert!(!has(&h, "source:libfolder:/pics"), "folded"); + click(&mut h, "sidebarSection:folders"); + assert!(has(&h, "source:libfolder:/pics")); +} + +/// Each disk is a row of its own; the startup disk's row only opens and closes, another disk's +/// row also chooses everything on that disk. +#[test] +fn disks_are_rows_of_their_own() { + let mut h = folders_app(&["/Users/me/a.jpg", "/Volumes/nas/p/b.jpg", "/Volumes/nas/c.jpg"]); + assert!(has(&h, "source:libfolder:/") && has(&h, "source:libfolder:/Volumes/nas"), "both disks are listed"); + assert!(has(&h, "source:libfolder:/Volumes/nas/p"), "an open disk shows its folders"); + click(&mut h, "source:libfolder:/"); + assert_eq!(h.app.session.filter.library_folder, None, "the startup disk's path would cover every disk"); + assert!(!has(&h, "source:libfolder:/Users/me"), "a click on it folds it instead"); + click(&mut h, "source:libfolder:/"); + assert!(has(&h, "source:libfolder:/Users/me")); + click(&mut h, "source:libfolder:/Volumes/nas"); + assert_eq!(h.app.session.visible().len(), 2, "everything on the nas"); +} + +/// Choosing a folder while Local is the source shows the library's folder, not a mix. +#[test] +fn choosing_a_library_folder_leaves_a_local_folder_view() { + let mut h = folders_app(&["/pics/trip/a.jpg", "/pics/home/b.jpg"]); + h.app.session.source = lightcraft_engine::LibrarySource::Folder; + click(&mut h, "libraryFolderToggle:/pics"); + click(&mut h, "source:libfolder:/pics/trip"); + assert_eq!(h.app.session.source, lightcraft_engine::LibrarySource::All); + assert_eq!(h.app.session.visible().len(), 1); +} + +/// Removing a folder from the library asks first, then moves its photos to Recently Deleted. +#[test] +fn removing_a_folder_from_the_library_asks_first() { + use crate::state::Dialog; + let mut h = folders_app(&["/pics/trip/a.jpg", "/pics/trip/b.jpg", "/pics/home/c.jpg"]); + h.app.ui.dialog = Some(Dialog::RemoveFolder { path: "/pics/trip".into(), name: "trip".into(), count: 2, disk: false }); + h.step(); + assert_eq!(h.app.session.visible().len(), 3, "nothing happens until it is confirmed"); + let r = h.request("ui.dialog.confirm", json!({}), T); + assert_eq!(r["ok"], true, "{r}"); + assert_eq!(h.app.session.visible().len(), 1, "the photos left the library"); + assert_eq!(h.app.ui.dialog, None); +} + +fn popup_open(h: &Headless) -> bool { + egui::Popup::is_any_open(&h.view.ctx) +} + +fn right_click(h: &mut Headless, id: &str) { + let c = widget(h, id).center(); + let r = h.request("ui.click", json!({"x": c.x, "y": c.y, "button": "right"}), T); + assert_eq!(r["ok"], true, "{r}"); + h.step(); + h.step(); +} + +/// A folder row that holds other disks as well would show their photos too: it only opens. +#[test] +fn a_folder_that_holds_other_disks_only_opens() { + let mut h = folders_app(&["/Volumes/1.jpg", "/Volumes/tokyo/x.jpg"]); + assert!(has(&h, "source:libfolder:/Volumes")); + click(&mut h, "source:libfolder:/Volumes"); + assert_eq!(h.app.session.filter.library_folder, None, "choosing it would show the tokyo disk too"); + click(&mut h, "source:libfolder:/Volumes/tokyo"); + assert_eq!(h.app.session.visible().len(), 1, "a disk's own row does choose"); +} + +/// Whatever sets the chosen folder (a click, an agent, a rename, undo), its row is on screen. +#[test] +fn the_chosen_folder_is_always_in_view() { + let mut h = folders_app(&["/pics/trip/day1/a.jpg", "/pics/trip/b.jpg", "/pics/home/c.jpg"]); + assert!(!has(&h, "source:libfolder:/pics/trip/day1"), "folded to begin with"); + h.app.session.filter.library_folder = Some("/pics/trip/day1".into()); + h.step(); + h.step(); + assert!(has(&h, "source:libfolder:/pics/trip/day1"), "the rows above it opened"); + // folding a parent by hand sticks until the choice changes + click(&mut h, "libraryFolderToggle:/pics/trip"); + assert!(!has(&h, "source:libfolder:/pics/trip/day1")); +} + +#[test] +fn a_renamed_folder_keeps_its_place_in_the_tree() { + let base = std::env::temp_dir().join(format!("lc-ui-rename-{}", std::process::id())); + let _ = std::fs::remove_dir_all(&base); + std::fs::create_dir_all(base.join("pics/trip")).unwrap(); + std::fs::create_dir_all(base.join("pics/home")).unwrap(); + let b = base.to_string_lossy().to_string(); + let mut h = folders_app(&[&format!("{b}/pics/trip/a.jpg"), &format!("{b}/pics/home/b.jpg")]); + h.app.session.filter.library_folder = Some(format!("{b}/pics/trip")); + h.step(); + h.step(); + assert!(has(&h, &format!("source:libfolder:{b}/pics/trip"))); + let r = h.request("engine.execute", json!({"command": "folder.rename", "params": {"path": format!("{b}/pics"), "name": "pics2"}}), T); + assert_eq!(r["ok"], true, "{r}"); + h.step(); + h.step(); + assert!(has(&h, &format!("source:libfolder:{b}/pics2/trip")), "the chosen folder is still on screen after the rename"); + let _ = std::fs::remove_dir_all(&base); +} + +/// The menu opens from anywhere on a folder row, the disclosure triangle included; a disk row +/// has its own, the startup disk none. +#[test] +fn right_click_opens_a_folders_menu_from_the_triangle_too() { + let mut h = folders_app(&["/pics/trip/a.jpg", "/pics/home/b.jpg", "/Volumes/nas/c.jpg"]); + click(&mut h, "libraryFolderToggle:/pics"); + right_click(&mut h, "source:libfolder:/pics/trip"); + assert!(popup_open(&h), "on the row"); + h.request("ui.key", json!({"key": "escape"}), T); + h.step(); + h.step(); + assert!(!popup_open(&h)); + right_click(&mut h, "libraryFolderToggle:/pics"); + assert!(popup_open(&h), "on the triangle"); + h.request("ui.key", json!({"key": "escape"}), T); + h.step(); + h.step(); + right_click(&mut h, "source:libfolder:/Volumes/nas"); + assert!(popup_open(&h), "a disk row offers removing the disk"); + h.request("ui.key", json!({"key": "escape"}), T); + h.step(); + h.step(); + right_click(&mut h, "source:libfolder:/"); + assert!(!popup_open(&h), "the startup disk offers nothing"); +} + +#[test] +fn removing_a_disk_from_the_library_asks_first() { + use crate::state::Dialog; + let mut h = folders_app(&["/Volumes/nas/a/1.jpg", "/Volumes/nas/2.jpg", "/Users/me/3.jpg"]); + h.app.ui.dialog = Some(Dialog::RemoveFolder { path: "/Volumes/nas".into(), name: "nas".into(), count: 2, disk: true }); + h.step(); + assert_eq!(h.app.session.visible().len(), 3); + let r = h.request("ui.dialog.confirm", json!({}), T); + assert_eq!(r["ok"], true, "{r}"); + assert_eq!(h.app.session.visible().len(), 1, "everything on that disk left the library"); +} diff --git a/docs/parity.md b/docs/parity.md index d5a31ea9..23582ca3 100644 --- a/docs/parity.md +++ b/docs/parity.md @@ -47,10 +47,10 @@ and prints the summary; `cargo xtask parity --write` refreshes the summary table | X. Cross-cutting behaviours (BEHAV) | 17 | 4 | 1 | 1 | 8/8 (100%) | 6/8 (75%) | | Y. Menus | 81 | 0 | 4 | 8 | 47/47 (100%) | 23/23 (100%) | | Z. Keyboard shortcuts (desktop) | 74 | 3 | 3 | 1 | 49/52 (94%) | 22/23 (96%) | -| Lightroom Classic extras | 31 | 10 | 39 | 9 | — | 21/22 (95%) | -| **Total** | 389 | 34 | 86 | 37 | 194/200 (97%) | 139/149 (93%) | +| Lightroom Classic extras | 32 | 10 | 39 | 9 | — | 22/23 (96%) | +| **Total** | 390 | 34 | 86 | 37 | 194/200 (97%) | 140/150 (93%) | -Weighted completion (✅ = 1, 🟡 = ½, 🚫 left out): **79.8%** of 509 in-scope rows — P0 98.5% of 200 · P1 95.6% of 149 · P2 41.6% of 160. +Weighted completion (✅ = 1, 🟡 = ½, 🚫 left out): **79.8%** of 510 in-scope rows — P0 98.5% of 200 · P1 95.7% of 150 · P2 41.6% of 160. ## Top gaps @@ -718,6 +718,7 @@ From `08-lightroom-classic-extras.md` (Classic-only features) and part 2 of `06- | LRC-LIB-CATALOG-PANEL | Catalog sets | P1 | ✅ | `cmd:library.source` (`missing`) | all, recently added, picks, recently deleted, missing photos (when any) | | LRC-LIB-CATALOG-LOCK | Catalog open in one program at a time | P1 | ✅ | `cmd:app.openLibrary`, `crates/catalog/src/lock.rs`, `crates/engine/src/library.rs` (`open_library`) | exclusive OS lock on `catalog.lock` for the session (app and `lightcraft-cli --library`); a second opener is refused with who has it (`catalog.lock.owner`: program, process, computer); released by the OS on a crash, so never stale; file systems without locks open unprotected with a warning; no read-only mode; the browser build (OPFS) has no guard across tabs yet. A library that can't be opened at launch (locked, unreadable, newer format, drive missing) shows a window — Try Again, Choose Another Library…, Continue Without Saving (then a banner; nothing written), Quit — instead of a silent demo session (`crates/ui-egui/src/panels/library_problem.rs`) | | LRC-LIB-FOLDERS | Disk folder tree | P1 | ✅ | `cmd:library.browse`, `cmd:folder.rename`, `cmd:folder.move`, `crates/ui-egui/src/panels/left.rs` (`local_section`, `folder_tree`) | standard places + the browsed folder + Browse Folder…, each an expandable tree of subfolders, breadcrumb navigation; context menu: Rename Folder…, Move Folder To…, Show in Finder (on disk, photos relinked; one undo step each: undo / redo move the folder back and forth, refused when the destination is taken) | +| LRC-LIB-LIBFOLDERS | Library folders | P1 | ✅ | `cmd:library.folders`, `cmd:library.filter` (`libraryFolder`), `cmd:library.removeFolder`, `cmd:folder.rename`, `cmd:folder.move`, `crates/catalog/src/folders.rs`, `crates/ui-egui/src/panels/left.rs` (`folders_section`, `folder_menu_for_library`, `row_menu`), `crates/ui-egui/src/tests_panels.rs` | sidebar Folders: the folders the library's photos were imported from, with photo counts (subfolders included), as another way to explore the library next to By Date and Keywords; Local stays the way to browse any folder on disk. Each disk is a row of its own and starts open: a drive letter, a Windows share (`\\server\share`, `\\?\UNC\…`, `\\wsl$\…`), a macOS share or disk (`/Volumes/tokyo`), a Linux mount (`/media`, `/run/media`, `/mnt`, gvfs `smb-share:…`, `/net/`), NFS (`/Network/Servers/`), else “This Computer” — told apart from the stored paths alone, so an offline share lists like a mounted one and the sidebar never waits on a network. Only folders that hold imported photos are listed (browsed, deleted and demo photos don't count); levels that hold nothing and lead to one folder fold into one row (`Users/me/Pictures`); long names are cut at the end with the full path in the tooltip; click chooses a folder and the folders inside it (click again clears; chip “Folder: pics/trip”), and its row is always opened into view, also after a rename or move and their undo. A row that would cover other disks' photos too (the startup disk, `/Volumes`, `/mnt`…) only opens. Right-click (row or triangle): Rename Folder…, Move Folder To…, Show in Finder (on disk, photos relinked, as in Local) and Remove from Library… (asks; moves the folder's photos to Recently Deleted, one undo step, no file touched); a disk row offers Remove Disk from Library… (the command needs `disk: true` for a whole disk or a folder spanning several). Limits: names are compared as written (macOS `/Volumes/tokyo` vs `/Volumes/TOKYO` are two disks; `/Volumes/Macintosh HD/…` and `/…` two rows); no disk capacity / online status; no “include subfolders” switch (a folder always includes the ones inside it); creating a folder and moving photos between folders are not here | | LRC-LIB-COLLECTIONS | Collections & sets | P1 | ✅ | `cmd:album.create`, `cmd:album.createSmart`, `cmd:album.toggleTarget`, `cmd:album.setTarget`, `cmd:album.clearQuick` | albums + folders (sets), smart albums (rule editor; rules incl. File Path — the whole string, either separator, case-insensitive), Quick Collection and target album (B in the grids adds / removes the selection; album menu ▸ Set as Target Album; the target is marked +) | | LRC-LIB-SMARTCOLL | Smart-collection rules | P1 | ✅ | `cmd:album.createSmart`, `cmd:album.setRules`, `cmd:album.ruleFields`, `cmd:dialog.smartAlbum`, `crates/catalog/src/rules.rs`, `crates/ui-egui/src/panels/rules_editor.rs` | rule editor: match all / any / none, nested groups (⌥+ or + Group), 30 fields (rating, flag, label, type, edits, keywords, any text, filename, file path, format, title, caption, camera, lens, location, creator, copyright, copyright status, capture / import / edit date, ISO, aperture, focal length, megapixels, GPS, virtual copy, album) with text / number / date / in-the-last operators; live match count; also usable as a library filter (`ruleSet`) | | LRC-LIB-PUBLISH | Publish services | P2 | ⬜ | | | From 0732edc64f96cf497f45d8622749ff40808da06e Mon Sep 17 00:00:00 2001 From: Nathaniel Ledford Date: Thu, 8 Oct 2026 07:57:41 -0400 Subject: [PATCH 09/41] Folders: every folder its own node; choosing one loads it like an album MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - catalog: a row is exactly one folder (no folding of single-child levels); the UI starts a folder that holds nothing and leads to one folder open - engine: a library folder is a source (LibrarySource::LibraryFolder with Session::library_folder, like Local's browse): library.source {kind: libraryFolder, path}, kept with the library's view, shown by library.state, followed through folder.rename / folder.move and undo, dropped back to All Photos when its folder is removed - ui: clicking a folder replaces whatever the grid showed (Picks, Recently Deleted, a Local folder…) instead of narrowing it with a filter chip; the grid is titled after the folder Co-Authored-By: Claude Sonnet 5.5 --- crates/catalog/src/folders.rs | 19 +++---- crates/catalog/src/tests_folders.rs | 33 ++++++++---- crates/engine/src/cmd/browse.rs | 29 +++++++---- crates/engine/src/cmd/library.rs | 15 +++++- crates/engine/src/cmd/query.rs | 8 ++- crates/engine/src/lib.rs | 16 +++++- crates/engine/src/library.rs | 16 +++++- crates/engine/src/tests_folders.rs | 79 +++++++++++++++++++++++++++++ crates/engine/src/tests_library.rs | 17 +++++++ crates/engine/src/view.rs | 6 ++- crates/ui-egui/src/i18n.rs | 9 ++++ crates/ui-egui/src/panels/grid.rs | 8 +-- crates/ui-egui/src/panels/left.rs | 28 +++++----- crates/ui-egui/src/tests_panels.rs | 55 +++++++++++++++----- docs/parity.md | 2 +- 15 files changed, 269 insertions(+), 71 deletions(-) diff --git a/crates/catalog/src/folders.rs b/crates/catalog/src/folders.rs index 3861fd5f..61a8f8c4 100644 --- a/crates/catalog/src/folders.rs +++ b/crates/catalog/src/folders.rs @@ -5,10 +5,10 @@ //! ones above them, never a folder that holds nothing of the library, with how many photos each //! holds — its subfolders included, like a year in By Date counts its months. //! -//! Folders above the imported ones that hold nothing themselves and lead to a single folder fold -//! into one row (`/Users/me/Pictures`) instead of a staircase of single-child levels; the row -//! stands for the deepest folder it names. A folder spelled two ways (`/` or `\`, repeated or -//! trailing separators, `.`) is one folder (see [`crate::query::folder_key`]). +//! Every folder is a row of its own, the ones above the imported photos included +//! (`/Users` → `me` → `Pictures`), so a row is exactly the folder it names. A folder spelled two +//! ways (`/` or `\`, repeated or trailing separators, `.`) is one folder (see +//! [`crate::query::folder_key`]). //! //! Each disk is a *volume*, a top-level row of its own: a drive letter (`D:`), a Windows share //! (`//server/share`), a mounted disk or share (macOS `/Volumes/tokyo`, Linux `/media/me/usb`, @@ -41,8 +41,8 @@ pub const MAX_DEPTH: usize = 64; /// One row of the library's folder tree. #[derive(Clone, Debug, PartialEq, Serialize)] pub struct FolderNode { - /// What the row says: the folder's name, or the whole run (`Users/me/Pictures`) of folded - /// levels; for a volume, its name (`nas`, `D:`, `//server/share`, `/` for the startup disk). + /// What the row says: the folder's name; for a volume, its name (`nas`, `D:`, + /// `//server/share`, `/` for the startup disk). pub name: String, /// The folder the row stands for, written out from the names of the photos' paths (forward /// slashes, no verbatim prefix); hand it back to [`crate::Filter::library_folder`] to see its @@ -205,17 +205,12 @@ impl Catalog { } } -/// The node for `key` with everything below it (at most [`MAX_DEPTH`] deep), folding a level -/// that holds nothing and leads to one folder into that folder (never a volume). +/// The node for `key` with everything below it (at most [`MAX_DEPTH`] deep). fn build(key: &str, levels: &HashMap) -> Option { let l = levels.get(key)?; let mut children: Vec = l.children.iter().filter_map(|k| build(k, levels)).collect(); sort(&mut children); let count = l.own + children.iter().map(|c| c.count).sum::(); - if !l.volume && l.own == 0 && children.len() == 1 { - let c = children.remove(0); - return Some(FolderNode { name: format!("{}/{}", l.name, c.name), ..c }); - } Some(FolderNode { name: l.name.clone(), path: l.path.clone(), count, own: l.own, volume: l.volume, selectable: true, children }) } diff --git a/crates/catalog/src/tests_folders.rs b/crates/catalog/src/tests_folders.rs index 6c7c9ab7..1a5a6128 100644 --- a/crates/catalog/src/tests_folders.rs +++ b/crates/catalog/src/tests_folders.rs @@ -5,8 +5,7 @@ //! * Given photos imported from several folders, the tree lists those folders and no others, //! each with how many photos it holds, under the disk (volume) they are on. //! * A folder's count includes the folders inside it. -//! * Folders above the imported ones that hold nothing themselves fold into one row, until the -//! library branches. +//! * Every folder is a row of its own, the ones above the imported photos included. //! * Photos only browsed in Local, deleted photos and demo scenes are not "imported from" anywhere. //! * However a folder is spelled, it is one folder. //! * Choosing a folder shows what was imported from it and from the folders inside it — exactly @@ -71,18 +70,27 @@ fn only_folders_with_imported_photos_are_listed_with_their_counts() { #[test] fn a_folders_count_includes_the_folders_inside_it() { let r = rows(&["/pics/trip/a.jpg", "/pics/trip/day1/b.jpg", "/pics/trip/day1/c.jpg", "/pics/trip/day2/d.jpg"]); - assert_eq!(r, vec![row("/", 4, 0), row("pics/trip", 4, 1), row("day1", 2, 2), row("day2", 1, 1)]); + assert_eq!(r, vec![row("/", 4, 0), row("pics", 4, 0), row("trip", 4, 1), row("day1", 2, 2), row("day2", 1, 1)]); } #[test] -fn folders_that_hold_nothing_fold_into_one_row_until_the_library_branches() { +fn every_folder_is_a_row_of_its_own_even_when_it_holds_nothing_itself() { let c = library(&["/Users/me/Pictures/2024/a.jpg", "/Users/me/Pictures/2025/b.jpg", "/Volumes/nas/photos/c.jpg"]); let tree = c.folder_tree(); assert_eq!( outline(&tree), - vec![row("/", 2, 0), row("Users/me/Pictures", 2, 0), row("2024", 1, 1), row("2025", 1, 1), row("nas", 1, 0), row("photos", 1, 1)] + vec![ + row("/", 2, 0), + row("Users", 2, 0), + row("me", 2, 0), + row("Pictures", 2, 0), + row("2024", 1, 1), + row("2025", 1, 1), + row("nas", 1, 0), + row("photos", 1, 1) + ] ); - assert_eq!(tree[0].children[0].path, "/Users/me/Pictures", "the folded row stands for the deepest folder it names"); + assert_eq!(tree[0].children[0].children[0].children[0].path, "/Users/me/Pictures", "a row is exactly the folder it names"); } #[test] @@ -99,13 +107,13 @@ fn browsed_deleted_and_demo_photos_are_not_imported_from_anywhere() { let id = c.alloc_photo_id(); let demo = Photo::new(id, Source::Demo { scene: 1 }, "demo", "JPEG", 60, 40, "2026-01-01T10:00:00"); c.apply(Op::AddPhoto { photo: Box::new(demo) }).unwrap(); - assert_eq!(outline(&c.folder_tree()), vec![row("/", 1, 0), row("pics/kept", 1, 1)]); + assert_eq!(outline(&c.folder_tree()), vec![row("/", 1, 0), row("pics", 1, 0), row("kept", 1, 1)]); } #[test] fn however_a_folder_is_spelled_it_is_one_folder() { let r = rows(&["/pics/trip/a.jpg", "/pics//trip/./b.jpg", "/pics\\trip\\c.jpg", "/pics/x/../trip/d.jpg"]); - assert_eq!(r, vec![row("/", 4, 0), row("pics/trip", 4, 4)]); + assert_eq!(r, vec![row("/", 4, 0), row("pics", 4, 0), row("trip", 4, 4)]); } #[test] @@ -159,7 +167,14 @@ fn shares_the_other_systems_mount_are_volumes_too() { let r = rows(&[r"\\tokyo.local\photos\a.jpg", r"\\192.168.1.5\scans\b.jpg", r"\\wsl$\Ubuntu\home\me\c.jpg", r"Z:\d.jpg"]); assert_eq!( r, - vec![row("//192.168.1.5/scans", 1, 1), row("//tokyo.local/photos", 1, 1), row("//wsl$/Ubuntu", 1, 0), row("home/me", 1, 1), row("Z:", 1, 1)] + vec![ + row("//192.168.1.5/scans", 1, 1), + row("//tokyo.local/photos", 1, 1), + row("//wsl$/Ubuntu", 1, 0), + row("home", 1, 0), + row("me", 1, 1), + row("Z:", 1, 1) + ] ); } diff --git a/crates/engine/src/cmd/browse.rs b/crates/engine/src/cmd/browse.rs index 338dff69..26d6130b 100644 --- a/crates/engine/src/cmd/browse.rs +++ b/crates/engine/src/cmd/browse.rs @@ -251,17 +251,9 @@ pub(crate) fn rename_folder_on_disk(from: &str, to: &str) -> std::result::Result /// After a folder moved from `from` to `to`: a browsed folder at or below it follows. pub(crate) fn follow_folder(s: &mut Session, from: &str, to: &str) { - // a chosen library folder at or below it follows too (compared the way the filter does, by - // folder identity, so a differently spelled choice is not widened to the folder itself) - if let Some(chosen) = s.filter.library_folder.clone() { - let (ck, fk) = (lightcraft_catalog::query::folder_key(&chosen), lightcraft_catalog::query::folder_key(from)); - if !fk.is_empty() - && let Some(rest) = ck.strip_prefix(fk.as_str()).filter(|r| r.is_empty() || r.starts_with('/') || fk.ends_with('/')) - { - let rest = rest.trim_start_matches('/'); - s.filter.library_folder = Some(if rest.is_empty() { to.to_string() } else { format!("{}/{rest}", to.trim_end_matches(['/', '\\'])) }); - } - } + // a library folder chosen at or below it follows too, as a source or as a filter + follow_library_folder(&mut s.library_folder, from, to); + follow_library_folder(&mut s.filter.library_folder, from, to); if let Some(b) = &mut s.browse && let Ok(rel) = Path::new(&b.path).strip_prefix(from) { @@ -271,6 +263,21 @@ pub(crate) fn follow_folder(s: &mut Session, from: &str, to: &str) { } } +/// A chosen library folder at or below `from` becomes the same place below `to`. Compared the +/// way the filter does, by folder identity, so a differently spelled choice is not widened to +/// the folder itself. +fn follow_library_folder(chosen: &mut Option, from: &str, to: &str) { + let Some(c) = chosen.as_deref() else { return }; + let (ck, fk) = (lightcraft_catalog::query::folder_key(c), lightcraft_catalog::query::folder_key(from)); + if fk.is_empty() { + return; + } + if let Some(rest) = ck.strip_prefix(fk.as_str()).filter(|r| r.is_empty() || r.starts_with('/') || fk.ends_with('/')) { + let rest = rest.trim_start_matches('/'); + *chosen = Some(if rest.is_empty() { to.to_string() } else { format!("{}/{rest}", to.trim_end_matches(['/', '\\'])) }); + } +} + /// Rename or move a folder on disk (everything in it goes along, sidecars included) and relink /// the photos inside, as one undo step: undo renames the folder back and restores the photos' /// paths, redo repeats the move — each refused (and reported) when the destination is taken. diff --git a/crates/engine/src/cmd/library.rs b/crates/engine/src/cmd/library.rs index 688d84b3..844bae89 100644 --- a/crates/engine/src/cmd/library.rs +++ b/crates/engine/src/cmd/library.rs @@ -251,7 +251,7 @@ pub fn specs() -> Vec { "Show Source", [], None, - "{kind: all|recentlyAdded|album|recentlyDeleted|picks|missing, id?: albumId}", + "{kind: all|recentlyAdded|album|recentlyDeleted|picks|missing|libraryFolder, id?: albumId, path?: a path from library.folders (for libraryFolder)}", always, |s, p| { let kind = str_param(p, "kind").unwrap_or("all"); @@ -268,6 +268,12 @@ pub fn specs() -> Vec { } LibrarySource::Album(a) } + "libraryFolder" => { + let path = str_param(p, "path").map(str::trim).filter(|d| !lightcraft_catalog::query::folder_key(d).is_empty()); + let path = path.ok_or_else(|| bad("library.source", "libraryFolder needs the `path` of a folder from library.folders"))?; + s.library_folder = Some(path.to_string()); + LibrarySource::LibraryFolder + } other => return Err(bad("library.source", format!("unknown source `{other}`"))), }; let vis = s.visible_cloned(); @@ -334,6 +340,13 @@ pub fn specs() -> Vec { if s.filter.library_folder.as_deref().is_some_and(|c| lightcraft_catalog::query::folder_within(c, path)) { s.filter.library_folder = None; } + // the folder being shown is gone: back to everything, as for a deleted album + if s.library_folder.as_deref().is_some_and(|c| lightcraft_catalog::query::folder_within(c, path)) { + s.library_folder = None; + if s.source == LibrarySource::LibraryFolder { + s.source = LibrarySource::All; + } + } let vis = s.visible_cloned(); s.selection = vis.first().map(|f| Selection::single(*f)).unwrap_or_default(); Ok(json!({"removed": ids.len()})) diff --git a/crates/engine/src/cmd/query.rs b/crates/engine/src/cmd/query.rs index e7ffde14..9b0777b3 100644 --- a/crates/engine/src/cmd/query.rs +++ b/crates/engine/src/cmd/query.rs @@ -5,7 +5,7 @@ use lightcraft_develop::{CONTROLS, controls}; use serde_json::{Value, json}; use super::{CommandSpec, always, bad, cmd, has_active}; -use crate::Session; +use crate::{LibrarySource, Session}; pub fn photo_summary(p: &Photo) -> Value { json!({ @@ -81,10 +81,14 @@ pub fn specs() -> Vec { })) }), cmd!(query "library.state", "Library State", [], None, "{}", always, |s, _| { - let label = s.source.label(&s.catalog); + let label = match (s.source, s.library_folder.as_deref()) { + (LibrarySource::LibraryFolder, Some(path)) => lightcraft_catalog::folders::folder_label(path), + _ => s.source.label(&s.catalog), + }; let n = s.visible().len(); Ok(json!({ "source": s.source, + "libraryFolder": s.library_folder, "sourceLabel": label, "filter": s.filter, "sort": s.sort, diff --git a/crates/engine/src/lib.rs b/crates/engine/src/lib.rs index cbbbbd63..bfc198dc 100644 --- a/crates/engine/src/lib.rs +++ b/crates/engine/src/lib.rs @@ -134,6 +134,9 @@ pub struct Session { pub clipboard: Option, /// The folder on disk the [`LibrarySource::Folder`] view browses. pub browse: Option, + /// The folder the [`LibrarySource::LibraryFolder`] view shows: the library's photos imported + /// from it and from the folders inside it. + pub library_folder: Option, /// Copied metadata (`photo.copyMetadata`): photo.setMeta params. pub meta_clipboard: Option, /// The photo that was active before the current one (Paste Settings from Previous). @@ -234,6 +237,7 @@ impl Session { clipboard: None, meta_clipboard: None, browse: None, + library_folder: None, previous_active: None, copy_groups: lightcraft_develop::SettingsGroup::default_copy(), presets: presets::builtin(), @@ -590,7 +594,8 @@ impl Session { /// Photos shown in the grid/filmstrip for the current source, filter and sort. pub fn visible(&mut self) -> &[PhotoId] { - let mut key = (self.catalog.revision, format!("{:?}|{:?}|{:?}|{:?}", self.source, self.filter, self.sort, self.browse)); + let mut key = + (self.catalog.revision, format!("{:?}|{:?}|{:?}|{:?}|{:?}", self.source, self.filter, self.sort, self.browse, self.library_folder)); if self.source == LibrarySource::Missing && self.media.availability.is_background() { // the view fills in as the background checks find files gone key.1.push_str(&format!("|{}", self.media.availability.generation())); @@ -605,6 +610,10 @@ impl Session { f.folder = Some(b.path); f.subfolders = b.subfolders; } + if self.source == LibrarySource::LibraryFolder { + // no folder chosen: nothing (an empty path names no folder) + f.library_folder = Some(self.library_folder.clone().unwrap_or_default()); + } let mut visible = self.catalog.query(&f, &self.sort); if matches!(self.source, LibrarySource::Album(_)) && self.sort.key == lightcraft_catalog::SortKey::CaptureDate @@ -648,7 +657,7 @@ impl Session { if self.source == LibrarySource::Missing { return None; } - let key = (self.catalog.revision, format!("{:?}|{:?}", self.source, self.browse)); + let key = (self.catalog.revision, format!("{:?}|{:?}|{:?}", self.source, self.browse, self.library_folder)); if self.total.as_ref().map(|t| &t.0) != Some(&key) { let mut f = self.source.to_filter(&Filter::default(), &self.catalog); if self.source == LibrarySource::Folder { @@ -656,6 +665,9 @@ impl Session { f.folder = Some(b.path); f.subfolders = b.subfolders; } + if self.source == LibrarySource::LibraryFolder { + f.library_folder = Some(self.library_folder.clone().unwrap_or_default()); + } let n = self.catalog.query(&f, &self.sort).len(); self.total = Some((key, n)); } diff --git a/crates/engine/src/library.rs b/crates/engine/src/library.rs index 6a63e378..b6448380 100644 --- a/crates/engine/src/library.rs +++ b/crates/engine/src/library.rs @@ -102,6 +102,8 @@ struct PresetsFile { struct ViewFile { source: LibrarySource, browse: Option, + /// The folder a `libraryFolder` source shows. + library_folder: Option, // No filter: a library opens unfiltered. A date, keyword or person left over from the last session // would silently hide photos, with only a small badge to say so. sort: lightcraft_catalog::Sort, @@ -309,6 +311,7 @@ impl Session { self.pending_log.clear(); self.selection = Selection::default(); self.source = LibrarySource::All; + self.library_folder = None; if report.created && seed_demo { crate::demo::load(self); journal.snapshot(&self.catalog)?; @@ -353,6 +356,11 @@ impl Session { if let Some(v) = settings.read::(files.as_mut(), "view.json") { self.source = v.source; self.browse = v.browse; + self.library_folder = v.library_folder.filter(|f| !f.trim().is_empty()); + if self.source == LibrarySource::LibraryFolder && self.library_folder.is_none() { + // no folder to show: everything, not an empty grid + self.source = LibrarySource::All; + } self.sort = v.sort; self.selection = v.selection; self.selection.ids.retain(|id| self.catalog.photo(*id).is_some()); @@ -500,7 +508,13 @@ impl Session { } fn view_json(&self) -> Vec { - let view = ViewFile { source: self.source, browse: self.browse.clone(), sort: self.sort, selection: self.selection.clone() }; + let view = ViewFile { + source: self.source, + browse: self.browse.clone(), + library_folder: self.library_folder.clone(), + sort: self.sort, + selection: self.selection.clone(), + }; serde_json::to_vec_pretty(&view).unwrap_or_default() } diff --git a/crates/engine/src/tests_folders.rs b/crates/engine/src/tests_folders.rs index 887870b5..e9577443 100644 --- a/crates/engine/src/tests_folders.rs +++ b/crates/engine/src/tests_folders.rs @@ -14,6 +14,10 @@ use serde_json::json; use crate::{LibrarySource, Session, filter_chips}; +fn source_folder(s: &mut Session, path: &str) -> serde_json::Value { + s.execute("library.source", &json!({"kind": "libraryFolder", "path": path})).unwrap() +} + fn add(s: &mut Session, path: &str) { let id = s.catalog.alloc_photo_id(); let p = Photo::new(id, Source::File { path: path.into() }, "x.jpg", "JPEG", 60, 40, "2026-01-01T10:00:00"); @@ -230,3 +234,78 @@ fn following_a_folder_reads_both_spellings_the_same_way() { crate::cmd::browse::follow_folder(&mut s, "/a/b", "/a/c"); assert_eq!(s.filter.library_folder.as_deref(), Some("/elsewhere/b"), "other folders are left alone"); } + +#[test] +fn choosing_a_folder_replaces_the_source_the_way_an_album_does() { + let mut s = session(); + s.execute("library.source", &json!({"kind": "picks"})).unwrap(); + assert_eq!(s.visible().len(), 0, "no picks"); + let r = source_folder(&mut s, "/pics/trip"); + assert_eq!(r["count"], 2, "the folder's photos, whatever was shown before"); + assert_eq!((s.source, s.library_folder.as_deref()), (LibrarySource::LibraryFolder, Some("/pics/trip"))); + assert!(filter_chips(&s.filter, &s.catalog).is_empty(), "a source is not a filter chip"); + let state = s.execute("library.state", &json!({})).unwrap(); + assert_eq!((state["sourceLabel"].as_str(), state["libraryFolder"].as_str()), (Some("pics/trip"), Some("/pics/trip")), "agents see which folder"); + // the filter bar narrows it further, as it does an album + s.execute("library.filter", &json!({"rating": 1})).unwrap(); + assert_eq!(s.visible().len(), 0); + s.execute("library.clearFilter", &json!({})).unwrap(); + assert_eq!(s.visible().len(), 2); + s.execute("library.source", &json!({"kind": "all"})).unwrap(); + assert_eq!(s.visible().len(), 3, "back to everything"); +} + +#[test] +fn the_folder_source_takes_a_library_folder_path_only() { + let mut s = session(); + for params in [ + json!({"kind": "libraryFolder"}), + json!({"kind": "libraryFolder", "path": 5}), + json!({"kind": "libraryFolder", "path": " "}), + json!({"kind": "libraryFolder", "path": "."}), + ] { + assert!(s.execute("library.source", ¶ms).is_err(), "{params}"); + } + assert_eq!(s.source, LibrarySource::All, "refused, nothing changed"); + assert_eq!(source_folder(&mut s, "/elsewhere")["count"], 0, "a folder with no photos shows nothing, not everything"); +} + +#[test] +fn removing_the_folder_being_shown_returns_to_all_photos() { + let mut s = session(); + source_folder(&mut s, "/pics/trip"); + s.execute("library.removeFolder", &json!({"path": "/pics/trip"})).unwrap(); + assert_eq!((s.source, s.library_folder.as_deref()), (LibrarySource::All, None)); + assert_eq!(s.visible().len(), 1); + // removing another folder leaves the source alone + let mut s = session(); + source_folder(&mut s, "/pics/home"); + s.execute("library.removeFolder", &json!({"path": "/pics/trip"})).unwrap(); + assert_eq!(s.source, LibrarySource::LibraryFolder); + assert_eq!(s.visible().len(), 1); +} + +#[test] +fn a_renamed_folder_stays_the_one_shown_and_undo_follows_it_back() { + let dir = Scratch::new("source-rename"); + std::fs::create_dir_all(dir.0.join("trip/day1")).unwrap(); + let (trip, day1, renamed) = (dir.path("trip"), dir.path("trip/day1"), dir.path("holiday")); + let mut s = Session::new(); + add(&mut s, &format!("{trip}/a.jpg")); + add(&mut s, &format!("{day1}/b.jpg")); + source_folder(&mut s, &day1); + s.execute("folder.rename", &json!({"path": trip, "name": "holiday"})).unwrap(); + assert_eq!(s.library_folder.as_deref(), Some(format!("{renamed}/day1").as_str()), "the subfolder follows its parent"); + assert_eq!(s.visible().len(), 1); + s.execute("edit.undo", &json!({})).unwrap(); + assert_eq!(s.library_folder.as_deref(), Some(day1.as_str())); + assert_eq!(s.visible().len(), 1); +} + +#[test] +fn the_shown_folder_follows_reading_both_spellings_the_same_way() { + let mut s = Session::new(); + s.library_folder = Some("/a/x/../b/sub".into()); + crate::cmd::browse::follow_folder(&mut s, "/a/b", "/a/c"); + assert_eq!(s.library_folder.as_deref(), Some("/a/c/sub")); +} diff --git a/crates/engine/src/tests_library.rs b/crates/engine/src/tests_library.rs index 45a56b2e..91ae195a 100644 --- a/crates/engine/src/tests_library.rs +++ b/crates/engine/src/tests_library.rs @@ -212,6 +212,23 @@ fn profile_favorites_and_recent_survive_reopen() { let _ = std::fs::remove_dir_all(&dir); } +#[test] +fn the_shown_library_folder_survives_reopen() { + let dir = temp_dir("libfolder"); + let mut s = open(&dir, true); + s.execute("library.source", &json!({"kind": "libraryFolder", "path": "/pics/trip"})).unwrap(); + s.save_view(); + drop(s); + let s = open(&dir, false); + assert_eq!((s.source, s.library_folder.as_deref()), (crate::LibrarySource::LibraryFolder, Some("/pics/trip"))); + drop(s); + // a view file that names the source but no folder opens on everything, never an empty grid + std::fs::write(dir.join("view.json"), br#"{"source": {"kind": "libraryFolder"}}"#).unwrap(); + let s = open(&dir, false); + assert_eq!((s.source, s.library_folder), (crate::LibrarySource::All, None)); + let _ = std::fs::remove_dir_all(&dir); +} + /// Smart albums with a rule set: all / any / none, nested groups, validation, live updates. #[test] fn smart_album_rule_sets() { diff --git a/crates/engine/src/view.rs b/crates/engine/src/view.rs index 1b0f0ca4..18149929 100644 --- a/crates/engine/src/view.rs +++ b/crates/engine/src/view.rs @@ -19,6 +19,9 @@ pub enum LibrarySource { Folder, /// Photos whose original file can't be found (`library.missing`). Missing, + /// A folder the library's photos were imported from ([`crate::Session::library_folder`]), + /// and the folders inside it (`library.folders`). + LibraryFolder, } /// The folder a [`LibrarySource::Folder`] view shows. @@ -49,7 +52,7 @@ impl LibrarySource { LibrarySource::RecentlyDeleted => f.deleted = true, LibrarySource::Picks => f.flag = Some(lightcraft_catalog::Flag::Pick), // the folder itself is filled in by the session (it holds the path) - LibrarySource::Folder | LibrarySource::Missing => {} + LibrarySource::Folder | LibrarySource::Missing | LibrarySource::LibraryFolder => {} } f } @@ -62,6 +65,7 @@ impl LibrarySource { LibrarySource::RecentlyDeleted => "Recently Deleted".into(), LibrarySource::Picks => "Picks".into(), LibrarySource::Folder => "Folder".into(), + LibrarySource::LibraryFolder => "Folder".into(), LibrarySource::Missing => "Missing Photos".into(), } } diff --git a/crates/ui-egui/src/i18n.rs b/crates/ui-egui/src/i18n.rs index 98763184..7ed26d56 100644 --- a/crates/ui-egui/src/i18n.rs +++ b/crates/ui-egui/src/i18n.rs @@ -216,6 +216,15 @@ pub fn source_label(source: lightcraft_engine::LibrarySource, catalog: &lightcra if matches!(source, lightcraft_engine::LibrarySource::Album(id) if catalog.album(id).is_some()) { label } else { tr(&label).to_string() } } +/// What the grid is titled for the session's source: [`source_label`], or a library folder's last +/// two names (`photos/travel`; the folder's name is the user's and stays verbatim). +pub fn source_title(session: &lightcraft_engine::Session) -> String { + match (session.source, session.library_folder.as_deref()) { + (lightcraft_engine::LibrarySource::LibraryFolder, Some(path)) => lightcraft_catalog::folders::folder_label(path), + (source, _) => source_label(source, &session.catalog), + } +} + /// A date group heading (`2026-09-20`, `2026-09`, `2026`) in the UI language: the grid's full /// form, or the date sidebar's `short` one ("Sunday, 20" / "September" / "2026"). English keeps the /// catalog's own wording; every other language formats it with its `*-formats.json` date diff --git a/crates/ui-egui/src/panels/grid.rs b/crates/ui-egui/src/panels/grid.rs index 7dc07602..bb7b27f0 100644 --- a/crates/ui-egui/src/panels/grid.rs +++ b/crates/ui-egui/src/panels/grid.rs @@ -150,7 +150,7 @@ fn show_inner(app: &mut LightcraftApp, ui: &mut egui::Ui) { folder_header(app, ui, hr, &b, &ids, local, &cnt) } None => { - let title = crate::i18n::source_label(app.session.source, &app.session.catalog); + let title = crate::i18n::source_title(&app.session); ui.painter().text(pos2(hr.left() + 20.0, hr.center().y), Align2::LEFT_CENTER, &title, t.semibold(17.0), t.text); ui.painter().text(pos2(hr.right() - 20.0, hr.center().y), Align2::RIGHT_CENTER, cnt, t.font(12.5), t.text_dim); } @@ -164,11 +164,7 @@ fn show_inner(app: &mut LightcraftApp, ui: &mut egui::Ui) { if !chips.is_empty() { let body = match total { Some(n) if n > 0 => { - crate::i18n::tr_format!( - "{n} photos in {} are hidden by the filters above", - crate::i18n::source_label(app.session.source, &app.session.catalog), - n = n - ) + crate::i18n::tr_format!("{n} photos in {} are hidden by the filters above", crate::i18n::source_title(&app.session), n = n) } _ => "Remove a filter above, or choose Clear all".to_string(), }; diff --git a/crates/ui-egui/src/panels/left.rs b/crates/ui-egui/src/panels/left.rs index 884d346a..be47fa21 100644 --- a/crates/ui-egui/src/panels/left.rs +++ b/crates/ui-egui/src/panels/left.rs @@ -733,8 +733,9 @@ fn is_within(app: &LightcraftApp, id: lightcraft_catalog::AlbumId, ancestor: lig } /// "Folders": where on disk the library's photos were imported from, with photo counts (see -/// `lightcraft_catalog::folders`). A click shows the photos imported from that folder and the -/// folders inside it, the triangle opens a level. Only folders holding imported photos are +/// `lightcraft_catalog::folders`). A click makes that folder the source, like an album or a +/// Local folder: its photos and those of the folders inside it fill the grid. The triangle opens +/// a level. Only folders holding imported photos are /// listed; every folder on disk is under Local. fn folders_section(app: &mut LightcraftApp, ui: &mut egui::Ui) { let tree = app.caches.folder_tree(&app.session.catalog); @@ -774,10 +775,11 @@ fn elide_head(text: &str, max: f32, width: impl Fn(&str) -> f32) -> String { "…".to_string() } -/// Whenever the chosen folder changes (a click, an agent, a rename or its undo), open the rows +/// Whenever the shown folder changes (a click, an agent, a rename or its undo), open the rows /// above it so it is on screen; folding one by hand afterwards sticks until the choice changes. fn reveal_chosen(app: &LightcraftApp, ui: &egui::Ui, tree: &[FolderNode]) { - let chosen = app.session.filter.library_folder.clone().filter(|c| !lightcraft_catalog::query::folder_key(c).is_empty()); + let shown = app.session.library_folder.clone().filter(|_| app.session.source == LibrarySource::LibraryFolder); + let chosen = shown.filter(|c| !lightcraft_catalog::query::folder_key(c).is_empty()); let seen = egui::Id::new("libfolder-revealed"); let now = chosen.as_deref().map(lightcraft_catalog::query::folder_key); if ui.data(|d| d.get_temp::>(seen)) == Some(now.clone()) { @@ -802,11 +804,15 @@ fn folder_rows(app: &mut LightcraftApp, ui: &mut egui::Ui, nodes: &[FolderNode], for n in nodes { let key = lightcraft_catalog::query::folder_key(&n.path); let open_id = egui::Id::new(("libfolder-open", key.clone())); - // a disk starts open: its folders are what the section is for - let mut open: bool = ui.data(|d| d.get_temp(open_id)).unwrap_or(n.volume); + // a disk starts open, and so does a folder that holds nothing itself and leads to one + // folder (`Users` → `me`): the first row where the library branches is what you look for + let leads_on = n.own == 0 && n.children.len() == 1; + let mut open: bool = ui.data(|d| d.get_temp(open_id)).unwrap_or(n.volume || leads_on); // a row whose path would cover other disks' photos too only opens and closes let selectable = n.selectable; - let sel = selectable && app.session.filter.library_folder.as_deref().is_some_and(|f| same_folder(f, &n.path)); + let sel = selectable + && app.session.source == LibrarySource::LibraryFolder + && app.session.library_folder.as_deref().is_some_and(|f| same_folder(f, &n.path)); let name = if selectable { n.name.clone() } else { crate::i18n::tr("This Computer").to_string() }; // room for the name: between the icon and the count let font = t.font(13.5); @@ -837,12 +843,8 @@ fn folder_rows(app: &mut LightcraftApp, ui: &mut egui::Ui, nodes: &[FolderNode], let resp = resp.on_hover_text(tip); if resp.clicked() && !toggled { if selectable { - // the library's folder, not a mix with the Local folder being browsed - if app.session.source == LibrarySource::Folder { - let _ = app.run("library.source", json!({"kind": "all"})); - } - let v = if sel { serde_json::Value::Null } else { json!(n.path) }; - let _ = app.run("library.filter", json!({"libraryFolder": v})); + // a source like an album or a Local folder: it replaces what the grid showed + let _ = app.run("library.source", json!({"kind": "libraryFolder", "path": n.path})); } else { toggled = true; } diff --git a/crates/ui-egui/src/tests_panels.rs b/crates/ui-egui/src/tests_panels.rs index e5134290..585a3e95 100644 --- a/crates/ui-egui/src/tests_panels.rs +++ b/crates/ui-egui/src/tests_panels.rs @@ -241,9 +241,11 @@ fn folders_section_lists_where_photos_were_imported_from_and_narrows_the_grid() assert_eq!(h.app.session.visible().len(), 3); click(&mut h, "source:libfolder:/pics/trip"); assert_eq!(h.app.session.visible().len(), 2, "only the photos imported from that folder"); - assert_eq!(h.app.session.filter.library_folder.as_deref(), Some("/pics/trip")); - click(&mut h, "source:libfolder:/pics/trip"); - assert_eq!(h.app.session.visible().len(), 3, "choosing it again shows everything"); + assert_eq!(h.app.session.source, lightcraft_engine::LibrarySource::LibraryFolder); + assert_eq!(h.app.session.library_folder.as_deref(), Some("/pics/trip")); + assert_eq!(h.app.session.filter, lightcraft_catalog::Filter::default(), "a source, not a filter"); + click(&mut h, "source:all"); + assert_eq!(h.app.session.visible().len(), 3, "All Photos shows everything again"); // the section folds like the others click(&mut h, "sidebarSection:folders"); assert!(!has(&h, "source:libfolder:/pics"), "folded"); @@ -259,7 +261,7 @@ fn disks_are_rows_of_their_own() { assert!(has(&h, "source:libfolder:/") && has(&h, "source:libfolder:/Volumes/nas"), "both disks are listed"); assert!(has(&h, "source:libfolder:/Volumes/nas/p"), "an open disk shows its folders"); click(&mut h, "source:libfolder:/"); - assert_eq!(h.app.session.filter.library_folder, None, "the startup disk's path would cover every disk"); + assert_eq!(h.app.session.library_folder, None, "the startup disk's path would cover every disk"); assert!(!has(&h, "source:libfolder:/Users/me"), "a click on it folds it instead"); click(&mut h, "source:libfolder:/"); assert!(has(&h, "source:libfolder:/Users/me")); @@ -267,15 +269,42 @@ fn disks_are_rows_of_their_own() { assert_eq!(h.app.session.visible().len(), 2, "everything on the nas"); } -/// Choosing a folder while Local is the source shows the library's folder, not a mix. +/// A folder loads into the grid the way an album, a Local folder or Picks does: whatever was +/// shown before is replaced, and the folder's own row is highlighted. #[test] -fn choosing_a_library_folder_leaves_a_local_folder_view() { +fn choosing_a_library_folder_replaces_whatever_was_shown() { + use lightcraft_engine::LibrarySource; let mut h = folders_app(&["/pics/trip/a.jpg", "/pics/home/b.jpg"]); - h.app.session.source = lightcraft_engine::LibrarySource::Folder; click(&mut h, "libraryFolderToggle:/pics"); + for before in [LibrarySource::Picks, LibrarySource::Folder, LibrarySource::RecentlyDeleted, LibrarySource::Missing] { + h.app.session.source = before; + click(&mut h, "source:libfolder:/pics/trip"); + assert_eq!(h.app.session.source, LibrarySource::LibraryFolder, "from {before:?}"); + assert_eq!(h.app.session.visible().len(), 1); + } click(&mut h, "source:libfolder:/pics/trip"); - assert_eq!(h.app.session.source, lightcraft_engine::LibrarySource::All); - assert_eq!(h.app.session.visible().len(), 1); + assert_eq!(h.app.session.source, LibrarySource::LibraryFolder, "choosing it again keeps it, as an album does"); + click(&mut h, "source:libfolder:/pics/home"); + assert_eq!(h.app.session.library_folder.as_deref(), Some("/pics/home")); +} + +#[test] +fn the_grid_is_titled_after_the_folder() { + let mut h = folders_app(&["/Volumes/tokyo/photos/travel/a.jpg"]); + h.app.session.source = lightcraft_engine::LibrarySource::LibraryFolder; + h.app.session.library_folder = Some("/Volumes/tokyo/photos/travel".into()); + assert_eq!(crate::i18n::source_title(&h.app.session), "photos/travel"); + h.app.session.source = lightcraft_engine::LibrarySource::All; + assert_eq!(crate::i18n::source_title(&h.app.session), "All Photos"); +} + +/// A folder that holds nothing itself and leads to one folder starts open: the tree opens down to +/// where the library branches. +#[test] +fn a_chain_of_single_folders_starts_open() { + let h = folders_app(&["/Users/me/Pictures/2024/a.jpg", "/Users/me/Pictures/2025/b.jpg"]); + assert!(has(&h, "source:libfolder:/Users/me/Pictures"), "Users and me opened by themselves"); + assert!(!has(&h, "source:libfolder:/Users/me/Pictures/2024"), "the branching folder waits to be opened"); } /// Removing a folder from the library asks first, then moves its photos to Recently Deleted. @@ -310,7 +339,7 @@ fn a_folder_that_holds_other_disks_only_opens() { let mut h = folders_app(&["/Volumes/1.jpg", "/Volumes/tokyo/x.jpg"]); assert!(has(&h, "source:libfolder:/Volumes")); click(&mut h, "source:libfolder:/Volumes"); - assert_eq!(h.app.session.filter.library_folder, None, "choosing it would show the tokyo disk too"); + assert_eq!(h.app.session.library_folder, None, "choosing it would show the tokyo disk too"); click(&mut h, "source:libfolder:/Volumes/tokyo"); assert_eq!(h.app.session.visible().len(), 1, "a disk's own row does choose"); } @@ -320,7 +349,8 @@ fn a_folder_that_holds_other_disks_only_opens() { fn the_chosen_folder_is_always_in_view() { let mut h = folders_app(&["/pics/trip/day1/a.jpg", "/pics/trip/b.jpg", "/pics/home/c.jpg"]); assert!(!has(&h, "source:libfolder:/pics/trip/day1"), "folded to begin with"); - h.app.session.filter.library_folder = Some("/pics/trip/day1".into()); + h.app.session.source = lightcraft_engine::LibrarySource::LibraryFolder; + h.app.session.library_folder = Some("/pics/trip/day1".into()); h.step(); h.step(); assert!(has(&h, "source:libfolder:/pics/trip/day1"), "the rows above it opened"); @@ -337,7 +367,8 @@ fn a_renamed_folder_keeps_its_place_in_the_tree() { std::fs::create_dir_all(base.join("pics/home")).unwrap(); let b = base.to_string_lossy().to_string(); let mut h = folders_app(&[&format!("{b}/pics/trip/a.jpg"), &format!("{b}/pics/home/b.jpg")]); - h.app.session.filter.library_folder = Some(format!("{b}/pics/trip")); + h.app.session.source = lightcraft_engine::LibrarySource::LibraryFolder; + h.app.session.library_folder = Some(format!("{b}/pics/trip")); h.step(); h.step(); assert!(has(&h, &format!("source:libfolder:{b}/pics/trip"))); diff --git a/docs/parity.md b/docs/parity.md index 23582ca3..206e2c3e 100644 --- a/docs/parity.md +++ b/docs/parity.md @@ -718,7 +718,7 @@ From `08-lightroom-classic-extras.md` (Classic-only features) and part 2 of `06- | LRC-LIB-CATALOG-PANEL | Catalog sets | P1 | ✅ | `cmd:library.source` (`missing`) | all, recently added, picks, recently deleted, missing photos (when any) | | LRC-LIB-CATALOG-LOCK | Catalog open in one program at a time | P1 | ✅ | `cmd:app.openLibrary`, `crates/catalog/src/lock.rs`, `crates/engine/src/library.rs` (`open_library`) | exclusive OS lock on `catalog.lock` for the session (app and `lightcraft-cli --library`); a second opener is refused with who has it (`catalog.lock.owner`: program, process, computer); released by the OS on a crash, so never stale; file systems without locks open unprotected with a warning; no read-only mode; the browser build (OPFS) has no guard across tabs yet. A library that can't be opened at launch (locked, unreadable, newer format, drive missing) shows a window — Try Again, Choose Another Library…, Continue Without Saving (then a banner; nothing written), Quit — instead of a silent demo session (`crates/ui-egui/src/panels/library_problem.rs`) | | LRC-LIB-FOLDERS | Disk folder tree | P1 | ✅ | `cmd:library.browse`, `cmd:folder.rename`, `cmd:folder.move`, `crates/ui-egui/src/panels/left.rs` (`local_section`, `folder_tree`) | standard places + the browsed folder + Browse Folder…, each an expandable tree of subfolders, breadcrumb navigation; context menu: Rename Folder…, Move Folder To…, Show in Finder (on disk, photos relinked; one undo step each: undo / redo move the folder back and forth, refused when the destination is taken) | -| LRC-LIB-LIBFOLDERS | Library folders | P1 | ✅ | `cmd:library.folders`, `cmd:library.filter` (`libraryFolder`), `cmd:library.removeFolder`, `cmd:folder.rename`, `cmd:folder.move`, `crates/catalog/src/folders.rs`, `crates/ui-egui/src/panels/left.rs` (`folders_section`, `folder_menu_for_library`, `row_menu`), `crates/ui-egui/src/tests_panels.rs` | sidebar Folders: the folders the library's photos were imported from, with photo counts (subfolders included), as another way to explore the library next to By Date and Keywords; Local stays the way to browse any folder on disk. Each disk is a row of its own and starts open: a drive letter, a Windows share (`\\server\share`, `\\?\UNC\…`, `\\wsl$\…`), a macOS share or disk (`/Volumes/tokyo`), a Linux mount (`/media`, `/run/media`, `/mnt`, gvfs `smb-share:…`, `/net/`), NFS (`/Network/Servers/`), else “This Computer” — told apart from the stored paths alone, so an offline share lists like a mounted one and the sidebar never waits on a network. Only folders that hold imported photos are listed (browsed, deleted and demo photos don't count); levels that hold nothing and lead to one folder fold into one row (`Users/me/Pictures`); long names are cut at the end with the full path in the tooltip; click chooses a folder and the folders inside it (click again clears; chip “Folder: pics/trip”), and its row is always opened into view, also after a rename or move and their undo. A row that would cover other disks' photos too (the startup disk, `/Volumes`, `/mnt`…) only opens. Right-click (row or triangle): Rename Folder…, Move Folder To…, Show in Finder (on disk, photos relinked, as in Local) and Remove from Library… (asks; moves the folder's photos to Recently Deleted, one undo step, no file touched); a disk row offers Remove Disk from Library… (the command needs `disk: true` for a whole disk or a folder spanning several). Limits: names are compared as written (macOS `/Volumes/tokyo` vs `/Volumes/TOKYO` are two disks; `/Volumes/Macintosh HD/…` and `/…` two rows); no disk capacity / online status; no “include subfolders” switch (a folder always includes the ones inside it); creating a folder and moving photos between folders are not here | +| LRC-LIB-LIBFOLDERS | Library folders | P1 | ✅ | `cmd:library.folders`, `cmd:library.source` (`libraryFolder`), `cmd:library.filter` (`libraryFolder`), `cmd:library.removeFolder`, `cmd:folder.rename`, `cmd:folder.move`, `crates/catalog/src/folders.rs`, `crates/ui-egui/src/panels/left.rs` (`folders_section`, `folder_menu_for_library`, `row_menu`), `crates/ui-egui/src/tests_panels.rs` | sidebar Folders: the folders the library's photos were imported from, with photo counts (subfolders included), as another way to explore the library next to By Date and Keywords; Local stays the way to browse any folder on disk. Each disk is a row of its own and starts open: a drive letter, a Windows share (`\\server\share`, `\\?\UNC\…`, `\\wsl$\…`), a macOS share or disk (`/Volumes/tokyo`), a Linux mount (`/media`, `/run/media`, `/mnt`, gvfs `smb-share:…`, `/net/`), NFS (`/Network/Servers/`), else “This Computer” — told apart from the stored paths alone, so an offline share lists like a mounted one and the sidebar never waits on a network. Only folders that hold imported photos are listed (browsed, deleted and demo photos don't count); every folder is a row of its own (`Users` → `me` → `Pictures`), and a folder that holds nothing itself and leads to one folder starts open; long names are cut at the end with the full path in the tooltip; click makes the folder the source, like an album or a Local folder (its photos and those of the folders inside it fill the grid, titled “photos/travel”; the filter bar narrows it further; `library.source` `libraryFolder` + `path`; kept with the library's view), and its row is always opened into view, also after a rename or move and their undo. A row that would cover other disks' photos too (the startup disk, `/Volumes`, `/mnt`…) only opens. Right-click (row or triangle): Rename Folder…, Move Folder To…, Show in Finder (on disk, photos relinked, as in Local) and Remove from Library… (asks; moves the folder's photos to Recently Deleted, one undo step, no file touched); a disk row offers Remove Disk from Library… (the command needs `disk: true` for a whole disk or a folder spanning several). Limits: names are compared as written (macOS `/Volumes/tokyo` vs `/Volumes/TOKYO` are two disks; `/Volumes/Macintosh HD/…` and `/…` two rows); no disk capacity / online status; no “include subfolders” switch (a folder always includes the ones inside it); creating a folder and moving photos between folders are not here | | LRC-LIB-COLLECTIONS | Collections & sets | P1 | ✅ | `cmd:album.create`, `cmd:album.createSmart`, `cmd:album.toggleTarget`, `cmd:album.setTarget`, `cmd:album.clearQuick` | albums + folders (sets), smart albums (rule editor; rules incl. File Path — the whole string, either separator, case-insensitive), Quick Collection and target album (B in the grids adds / removes the selection; album menu ▸ Set as Target Album; the target is marked +) | | LRC-LIB-SMARTCOLL | Smart-collection rules | P1 | ✅ | `cmd:album.createSmart`, `cmd:album.setRules`, `cmd:album.ruleFields`, `cmd:dialog.smartAlbum`, `crates/catalog/src/rules.rs`, `crates/ui-egui/src/panels/rules_editor.rs` | rule editor: match all / any / none, nested groups (⌥+ or + Group), 30 fields (rating, flag, label, type, edits, keywords, any text, filename, file path, format, title, caption, camera, lens, location, creator, copyright, copyright status, capture / import / edit date, ISO, aperture, focal length, megapixels, GPS, virtual copy, album) with text / number / date / in-the-last operators; live match count; also usable as a library filter (`ruleSet`) | | LRC-LIB-PUBLISH | Publish services | P2 | ⬜ | | | From 5d13d595207d1666ec761e189c7111c4809ac97e Mon Sep 17 00:00:00 2001 From: Nathaniel Ledford Date: Thu, 8 Oct 2026 08:14:30 -0400 Subject: [PATCH 10/41] Folders: review fixes (smart albums, disk guards, deep chains, long titles) - engine: a smart album made from a folder view keeps the folder; only a folder that shows exactly its own photos can be the source (not the startup disk, not a folder above disks, not one with no photos); a folder source and a folder filter never coexist; a folder source with no folder shows nothing; a saved view whose folder is gone opens on All Photos; removing a folder above a disk needs `disk: true` even with one disk mounted; library.folders documents volume/selectable - ui: deep chains stop indenting after five levels, the grid title gives way to the photo count (full name on hover), disclosure triangles have spoken names, the remove dialog says what is included and has a disk title; test temp folders are cleaned up on failure Co-Authored-By: Claude Sonnet 5.5 --- crates/engine/src/cmd/library.rs | 40 +++++++++++++--- crates/engine/src/lib.rs | 6 +-- crates/engine/src/library.rs | 11 +++-- crates/engine/src/tests_folders.rs | 70 +++++++++++++++++++++++++++- crates/engine/src/tests_library.rs | 16 +++++++ crates/ui-egui/locales/ja.json | 7 ++- crates/ui-egui/locales/pt-br.json | 5 ++ crates/ui-egui/locales/zh-hans.json | 7 ++- crates/ui-egui/locales/zh-hant.json | 7 ++- crates/ui-egui/src/panels/dialogs.rs | 5 +- crates/ui-egui/src/panels/grid.rs | 16 +++++-- crates/ui-egui/src/panels/left.rs | 41 +++++----------- crates/ui-egui/src/tests_panels.rs | 49 +++++++++++++++++-- crates/ui-egui/src/widgets.rs | 26 +++++++++++ docs/parity.md | 2 +- 15 files changed, 255 insertions(+), 53 deletions(-) diff --git a/crates/engine/src/cmd/library.rs b/crates/engine/src/cmd/library.rs index 844bae89..9c374942 100644 --- a/crates/engine/src/cmd/library.rs +++ b/crates/engine/src/cmd/library.rs @@ -271,7 +271,20 @@ pub fn specs() -> Vec { "libraryFolder" => { let path = str_param(p, "path").map(str::trim).filter(|d| !lightcraft_catalog::query::folder_key(d).is_empty()); let path = path.ok_or_else(|| bad("library.source", "libraryFolder needs the `path` of a folder from library.folders"))?; + if lightcraft_catalog::folders::is_startup_disk(path) { + return Err(bad("library.source", "the startup disk's path covers every disk: choose a folder in it")); + } + let f = Filter { library_folder: Some(path.to_string()), ..Default::default() }; + let ids = s.catalog.query(&f, &Sort::default()); + if ids.is_empty() { + return Err(bad("library.source", format!("{path}: no photo in the library was imported from it"))); + } + if covers_other_disks(s, path, &ids) { + return Err(bad("library.source", format!("{path} holds other disks as well: choose a disk or a folder on one"))); + } s.library_folder = Some(path.to_string()); + // one folder at a time: a folder filter left over would be ignored + s.filter.library_folder = None; LibrarySource::LibraryFolder } other => return Err(bad("library.source", format!("unknown source `{other}`"))), @@ -293,7 +306,11 @@ pub fn specs() -> Vec { |s, p| { let mut v = serde_json::to_value(&s.filter).unwrap_or_default(); lightcraft_develop::presets::deep_merge(&mut v, p); - s.filter = serde_json::from_value(v).map_err(|e| bad("library.filter", e.to_string()))?; + let f: Filter = serde_json::from_value(v).map_err(|e| bad("library.filter", e.to_string()))?; + if f.library_folder.is_some() && s.source == LibrarySource::LibraryFolder { + return Err(bad("library.filter", "a folder is already shown (library.source): show another source first")); + } + s.filter = f; Ok(json!({"count": s.visible().len()})) } ), @@ -302,7 +319,7 @@ pub fn specs() -> Vec { "Library Folders", [], None, - "{} → [{name, path, count, own, children}] the folders the library's photos were imported from, with photo counts (subfolders included in `count`); pass a `path` to library.filter as `libraryFolder`", + "{} → [{name, path, count, own, volume, selectable, children}] the disks and the folders the library's photos were imported from, with photo counts (subfolders included in `count`); show one with library.source {kind: libraryFolder, path} (rows with selectable false only open)", always, |s, _| Ok(serde_json::to_value(s.catalog.folder_tree()).unwrap_or_default()) ), @@ -330,10 +347,8 @@ pub fn specs() -> Vec { if ids.is_empty() { return Err(bad(C, format!("{path}: no photo in the library was imported from it"))); } - let disks: std::collections::BTreeSet = - ids.iter().filter_map(|id| s.catalog.photo(*id)).filter_map(|p| lightcraft_catalog::folders::volume_of(p)).collect(); - if disks.len() > 1 && !bool_or(p, "disk", false) { - return Err(bad(C, format!("{path} holds photos of {} disks: pass `disk: true` to remove them all", disks.len()))); + if covers_other_disks(s, path, &ids) && !bool_or(p, "disk", false) { + return Err(bad(C, format!("{path} holds whole disks: pass `disk: true` to remove everything on them"))); } let ops = ids.iter().map(|id| Op::SetDeleted { id: *id, deleted: true }).collect(); s.commit("Remove Folder from Library", Op::Batch { ops })?; @@ -1112,6 +1127,16 @@ fn merge_rules(base: &lightcraft_catalog::Filter, patch: &Value, c: &str) -> Res Ok(f) } +/// Whether `path` lies above a disk: some of its `ids` photos are on a disk (not the startup +/// disk) that the folder does not lie in. Such a folder (`/Volumes`, `/mnt`…) is not a folder of +/// one disk but a way to reach several. +fn covers_other_disks(s: &Session, path: &str, ids: &[PhotoId]) -> bool { + ids.iter() + .filter_map(|id| s.catalog.photo(*id)) + .filter_map(|p| lightcraft_catalog::folders::volume_of(p)) + .any(|v| v != "/" && !lightcraft_catalog::query::folder_within(path, &v)) +} + /// The current view (source + filter) as smart-album rules. Viewing a smart album starts from /// its rules with the filter bar's settings on top. fn view_rules(s: &Session) -> lightcraft_catalog::Filter { @@ -1138,6 +1163,9 @@ fn view_rules(s: &Session) -> lightcraft_catalog::Filter { None => { let mut f = s.source.to_filter(&s.filter, &s.catalog); f.deleted = false; + if s.source == LibrarySource::LibraryFolder { + f.library_folder = s.library_folder.clone(); + } f } } diff --git a/crates/engine/src/lib.rs b/crates/engine/src/lib.rs index bfc198dc..97cda108 100644 --- a/crates/engine/src/lib.rs +++ b/crates/engine/src/lib.rs @@ -611,8 +611,8 @@ impl Session { f.subfolders = b.subfolders; } if self.source == LibrarySource::LibraryFolder { - // no folder chosen: nothing (an empty path names no folder) - f.library_folder = Some(self.library_folder.clone().unwrap_or_default()); + // no folder chosen: nothing (`.` names no folder) + f.library_folder = Some(self.library_folder.clone().unwrap_or_else(|| ".".into())); } let mut visible = self.catalog.query(&f, &self.sort); if matches!(self.source, LibrarySource::Album(_)) @@ -666,7 +666,7 @@ impl Session { f.subfolders = b.subfolders; } if self.source == LibrarySource::LibraryFolder { - f.library_folder = Some(self.library_folder.clone().unwrap_or_default()); + f.library_folder = Some(self.library_folder.clone().unwrap_or_else(|| ".".into())); } let n = self.catalog.query(&f, &self.sort).len(); self.total = Some((key, n)); diff --git a/crates/engine/src/library.rs b/crates/engine/src/library.rs index b6448380..ef1c1e36 100644 --- a/crates/engine/src/library.rs +++ b/crates/engine/src/library.rs @@ -357,9 +357,14 @@ impl Session { self.source = v.source; self.browse = v.browse; self.library_folder = v.library_folder.filter(|f| !f.trim().is_empty()); - if self.source == LibrarySource::LibraryFolder && self.library_folder.is_none() { - // no folder to show: everything, not an empty grid - self.source = LibrarySource::All; + if self.source == LibrarySource::LibraryFolder { + // a folder that is gone (or none): everything, not an empty grid + let f = lightcraft_catalog::Filter { library_folder: self.library_folder.clone(), ..Default::default() }; + let any = self.library_folder.is_some() && !self.catalog.query(&f, &lightcraft_catalog::Sort::default()).is_empty(); + if !any { + self.source = LibrarySource::All; + self.library_folder = None; + } } self.sort = v.sort; self.selection = v.selection; diff --git a/crates/engine/src/tests_folders.rs b/crates/engine/src/tests_folders.rs index e9577443..5bd6a9a2 100644 --- a/crates/engine/src/tests_folders.rs +++ b/crates/engine/src/tests_folders.rs @@ -267,7 +267,8 @@ fn the_folder_source_takes_a_library_folder_path_only() { assert!(s.execute("library.source", ¶ms).is_err(), "{params}"); } assert_eq!(s.source, LibrarySource::All, "refused, nothing changed"); - assert_eq!(source_folder(&mut s, "/elsewhere")["count"], 0, "a folder with no photos shows nothing, not everything"); + assert!(s.execute("library.source", &json!({"kind": "libraryFolder", "path": "/elsewhere"})).is_err(), "no photo was imported from it"); + assert_eq!(s.source, LibrarySource::All); } #[test] @@ -309,3 +310,70 @@ fn the_shown_folder_follows_reading_both_spellings_the_same_way() { crate::cmd::browse::follow_folder(&mut s, "/a/b", "/a/c"); assert_eq!(s.library_folder.as_deref(), Some("/a/c/sub")); } + +#[test] +fn switching_from_one_folder_to_another_changes_what_is_shown() { + let mut s = session(); + assert_eq!(source_folder(&mut s, "/pics/trip")["count"], 2); + assert_eq!(s.source_total(), Some(2)); + assert_eq!(source_folder(&mut s, "/pics/home")["count"], 1, "same source, another folder"); + assert_eq!(s.visible().len(), 1); + assert_eq!(s.source_total(), Some(1)); +} + +#[test] +fn a_smart_album_made_from_a_folder_view_keeps_the_folder() { + let mut s = session(); + source_folder(&mut s, "/pics/trip"); + assert_eq!(s.visible().len(), 2); + let r = s.execute("album.createSmart", &json!({"name": "Trip"})).unwrap(); + assert_eq!(r["count"], 2, "the album matches the folder's photos, not the whole library: {r}"); +} + +#[test] +fn a_folder_source_and_a_folder_filter_never_disagree() { + let mut s = session(); + s.execute("library.filter", &json!({"libraryFolder": "/pics/home"})).unwrap(); + source_folder(&mut s, "/pics/trip"); + assert_eq!(s.filter.library_folder, None, "choosing a folder drops a folder filter left over"); + assert!(s.execute("library.filter", &json!({"libraryFolder": "/pics/home"})).is_err(), "one folder at a time"); + assert_eq!(s.visible().len(), 2); + s.execute("library.filter", &json!({"libraryFolder": null, "rating": 0})).unwrap(); + s.execute("library.source", &json!({"kind": "all"})).unwrap(); + assert!(s.execute("library.filter", &json!({"libraryFolder": "/pics/home"})).is_ok(), "fine while no folder is the source"); +} + +#[test] +fn a_folder_source_with_no_folder_shows_nothing() { + let mut s = session(); + s.source = LibrarySource::LibraryFolder; + s.library_folder = None; + assert_eq!(s.visible().len(), 0, "not everything"); + assert_eq!(s.source_total(), Some(0)); +} + +#[test] +fn only_a_folder_that_shows_exactly_its_own_photos_can_be_the_source() { + let mut s = Session::new(); + for p in ["/Volumes/nas/a/1.jpg", "/Users/me/2.jpg", "/Volumes/3.jpg"] { + add(&mut s, p); + } + for path in ["/", "//", "/elsewhere", "/Volumes"] { + assert!(s.execute("library.source", &json!({"kind": "libraryFolder", "path": path})).is_err(), "{path:?}"); + } + assert_eq!(s.source, LibrarySource::All, "refused, nothing changed"); + assert_eq!(source_folder(&mut s, "/Volumes/nas")["count"], 1, "a disk is a fine source"); + assert_eq!(source_folder(&mut s, "/Users/me")["count"], 1); +} + +#[test] +fn removing_a_folder_above_a_disk_needs_asking_by_name_even_with_one_disk() { + let mut s = Session::new(); + for p in ["/Volumes/nas/a/1.jpg", "/Users/me/2.jpg"] { + add(&mut s, p); + } + assert!(s.execute("library.removeFolder", &json!({"path": "/Volumes"})).is_err(), "it would take the whole nas"); + assert_eq!(s.visible().len(), 2); + assert_eq!(s.execute("library.removeFolder", &json!({"path": "/Volumes/nas/a"})).unwrap()["removed"], 1, "a folder on the disk is just a folder"); + assert_eq!(s.visible().len(), 1); +} diff --git a/crates/engine/src/tests_library.rs b/crates/engine/src/tests_library.rs index 91ae195a..f7b89225 100644 --- a/crates/engine/src/tests_library.rs +++ b/crates/engine/src/tests_library.rs @@ -216,12 +216,28 @@ fn profile_favorites_and_recent_survive_reopen() { fn the_shown_library_folder_survives_reopen() { let dir = temp_dir("libfolder"); let mut s = open(&dir, true); + let id = s.catalog.alloc_photo_id(); + let p = lightcraft_catalog::Photo::new( + id, + lightcraft_catalog::Source::File { path: "/pics/trip/a.jpg".into() }, + "a.jpg", + "JPEG", + 60, + 40, + "2026-01-01T10:00:00", + ); + s.commit("Add", lightcraft_catalog::Op::AddPhoto { photo: Box::new(p) }).unwrap(); s.execute("library.source", &json!({"kind": "libraryFolder", "path": "/pics/trip"})).unwrap(); s.save_view(); drop(s); let s = open(&dir, false); assert_eq!((s.source, s.library_folder.as_deref()), (crate::LibrarySource::LibraryFolder, Some("/pics/trip"))); drop(s); + // a folder that holds none of the library's photos any more opens on everything too + std::fs::write(dir.join("view.json"), br#"{"source": {"kind": "libraryFolder"}, "library_folder": "/no/such/folder"}"#).unwrap(); + let s = open(&dir, false); + assert_eq!((s.source, s.library_folder.clone()), (crate::LibrarySource::All, None)); + drop(s); // a view file that names the source but no folder opens on everything, never an empty grid std::fs::write(dir.join("view.json"), br#"{"source": {"kind": "libraryFolder"}}"#).unwrap(); let s = open(&dir, false); diff --git a/crates/ui-egui/locales/ja.json b/crates/ui-egui/locales/ja.json index d2376d64..cd7aad44 100644 --- a/crates/ui-egui/locales/ja.json +++ b/crates/ui-egui/locales/ja.json @@ -1037,5 +1037,10 @@ "They move to Recently Deleted and can be restored; no file on disk is touched.": "「最近削除した写真」に移動し、復元できます。ディスク上のファイルには手を加えません", "Moves the photos imported from this folder to Recently Deleted; no file is touched": "このフォルダから読み込んだ写真を「最近削除した写真」に移動します。ファイルには手を加えません", "Remove Disk from Library…": "ディスクをライブラリから除去…", - "Moves every photo imported from this disk to Recently Deleted; no file is touched": "このディスクから読み込んだすべての写真を「最近削除した写真」に移動します。ファイルには手を加えません" + "Moves every photo imported from this disk to Recently Deleted; no file is touched": "このディスクから読み込んだすべての写真を「最近削除した写真」に移動します。ファイルには手を加えません", + "Collapse": "折りたたむ", + "Expand": "展開", + "Remove Disk from Library": "ディスクをライブラリから除去", + "All the photos imported from this disk are included.": "このディスクから読み込んだすべての写真が対象です", + "That includes the photos in the folders inside it.": "内側のフォルダにある写真も含まれます" } diff --git a/crates/ui-egui/locales/pt-br.json b/crates/ui-egui/locales/pt-br.json index 3b2b3b8d..7348670f 100644 --- a/crates/ui-egui/locales/pt-br.json +++ b/crates/ui-egui/locales/pt-br.json @@ -42,6 +42,7 @@ "All Metadata…": "Todos os metadados…", "All Photos": "Todas as fotos", "All rights reserved": "Todos os direitos reservados", + "All the photos imported from this disk are included.": "Todas as fotos importadas deste disco estão incluídas.", "All types": "Todos os tipos", "Alt Text": "Texto alternativo", "Always": "Sempre", @@ -188,6 +189,7 @@ "Close all": "Fechar tudo", "Close file": "Fechar arquivo", "Cloudy": "Nublado", + "Collapse": "Recolher", "Collapse All Stacks": "Recolher todas as pilhas", "Color": "Cor", "Color Grading": "Graduação de cores", @@ -361,6 +363,7 @@ "Every EXIF, GPS and XMP field in the file": "Todos os campos EXIF, GPS e XMP do arquivo", "Existing previews": "Prévias existentes", "Exit": "Sair", + "Expand": "Expandir", "Expand All Stacks": "Expandir todas as pilhas", "Expand/Collapse Stack": "Expandir/recolher pilha", "Export": "Exportar", @@ -902,6 +905,7 @@ "Remove (H)": "Remover (H)", "Remove CA": "Remover AC", "Remove Chromatic Aberration": "Remover aberração cromática", + "Remove Disk from Library": "Remover disco da biblioteca", "Remove Disk from Library…": "Remover disco da biblioteca…", "Remove Folder from Library": "Remover pasta da biblioteca", "Remove from Album": "Remover do álbum", @@ -1146,6 +1150,7 @@ "Text": "Texto", "Text direction": "Direção do texto", "Texture": "Textura", + "That includes the photos in the folders inside it.": "Isso inclui as fotos das pastas dentro dela.", "The SAM 3 model is installed: Object and Describe masks are ready.": "O modelo SAM 3 está instalado: as máscaras Objeto e Descrever estão prontas.", "The download didn't work:": "O download não funcionou:", "The original files, unchanged, each with an XMP sidecar holding its edits.": "Os arquivos originais, inalterados, cada um com um sidecar XMP contendo suas edições.", diff --git a/crates/ui-egui/locales/zh-hans.json b/crates/ui-egui/locales/zh-hans.json index 9f411f3c..87c4d4ea 100644 --- a/crates/ui-egui/locales/zh-hans.json +++ b/crates/ui-egui/locales/zh-hans.json @@ -1254,5 +1254,10 @@ "They move to Recently Deleted and can be restored; no file on disk is touched.": "它们会移到“最近删除”,可以恢复;磁盘上的文件不会被改动。", "Moves the photos imported from this folder to Recently Deleted; no file is touched": "将从此文件夹导入的照片移到“最近删除”;不会改动任何文件", "Remove Disk from Library…": "从图库中移除磁盘…", - "Moves every photo imported from this disk to Recently Deleted; no file is touched": "将从此磁盘导入的所有照片移到“最近删除”;不会改动任何文件" + "Moves every photo imported from this disk to Recently Deleted; no file is touched": "将从此磁盘导入的所有照片移到“最近删除”;不会改动任何文件", + "Collapse": "折叠", + "Expand": "展开", + "Remove Disk from Library": "从图库中移除磁盘", + "All the photos imported from this disk are included.": "包括从此磁盘导入的所有照片。", + "That includes the photos in the folders inside it.": "其中文件夹里的照片也包括在内。" } diff --git a/crates/ui-egui/locales/zh-hant.json b/crates/ui-egui/locales/zh-hant.json index 863de2db..23463f8f 100644 --- a/crates/ui-egui/locales/zh-hant.json +++ b/crates/ui-egui/locales/zh-hant.json @@ -1254,5 +1254,10 @@ "They move to Recently Deleted and can be restored; no file on disk is touched.": "它們會移至「最近刪除」,可以還原;磁碟上的檔案不會被更動。", "Moves the photos imported from this folder to Recently Deleted; no file is touched": "將從此資料夾匯入的照片移至「最近刪除」;不會更動任何檔案", "Remove Disk from Library…": "從照片庫移除磁碟…", - "Moves every photo imported from this disk to Recently Deleted; no file is touched": "將從此磁碟匯入的所有照片移至「最近刪除」;不會更動任何檔案" + "Moves every photo imported from this disk to Recently Deleted; no file is touched": "將從此磁碟匯入的所有照片移至「最近刪除」;不會更動任何檔案", + "Collapse": "收合", + "Expand": "展開", + "Remove Disk from Library": "從照片庫移除磁碟", + "All the photos imported from this disk are included.": "包含從此磁碟匯入的所有照片。", + "That includes the photos in the folders inside it.": "其中資料夾裡的照片也包含在內。" } diff --git a/crates/ui-egui/src/panels/dialogs.rs b/crates/ui-egui/src/panels/dialogs.rs index cfcc476f..4e71684e 100644 --- a/crates/ui-egui/src/panels/dialogs.rs +++ b/crates/ui-egui/src/panels/dialogs.rs @@ -97,6 +97,7 @@ pub fn show(app: &mut LightcraftApp, ctx: &egui::Context) { Dialog::Merge { opts } => opts.title(), Dialog::Settings { .. } => "Settings", Dialog::ConfirmDelete { .. } => "Delete Photos", + Dialog::RemoveFolder { disk: true, .. } => "Remove Disk from Library", Dialog::RemoveFolder { .. } => "Remove Folder from Library", Dialog::SamModel { .. } => "Download the SAM 3 Model?", Dialog::About => "About LightCraft", @@ -711,7 +712,7 @@ pub fn show(app: &mut LightcraftApp, ctx: &egui::Context) { ui.label(crate::i18n::tr_format!("Move {what} to Recently Deleted?", what = what)); ui.label(egui::RichText::new(crate::i18n::tr("They can be restored from Recently Deleted until it is emptied.")).color(t.text_dim)); } - Dialog::RemoveFolder { name, count, path, .. } => { + Dialog::RemoveFolder { name, count, path, disk } => { ui.label(crate::i18n::tr_format!( "Remove “{name}” and its {count} photo{} from the library?", if *count == 1 { "" } else { "s" }, @@ -719,6 +720,8 @@ pub fn show(app: &mut LightcraftApp, ctx: &egui::Context) { count = count )); ui.label(egui::RichText::new(path.as_str()).color(t.text_dim)); + let scope = if *disk { "All the photos imported from this disk are included." } else { "That includes the photos in the folders inside it." }; + ui.label(egui::RichText::new(crate::i18n::tr(scope)).color(t.text_dim)); ui.label( egui::RichText::new(crate::i18n::tr("They move to Recently Deleted and can be restored; no file on disk is touched.")) .color(t.text_dim), diff --git a/crates/ui-egui/src/panels/grid.rs b/crates/ui-egui/src/panels/grid.rs index bb7b27f0..fbc35b6c 100644 --- a/crates/ui-egui/src/panels/grid.rs +++ b/crates/ui-egui/src/panels/grid.rs @@ -150,9 +150,19 @@ fn show_inner(app: &mut LightcraftApp, ui: &mut egui::Ui) { folder_header(app, ui, hr, &b, &ids, local, &cnt) } None => { - let title = crate::i18n::source_title(&app.session); - ui.painter().text(pos2(hr.left() + 20.0, hr.center().y), Align2::LEFT_CENTER, &title, t.semibold(17.0), t.text); - ui.painter().text(pos2(hr.right() - 20.0, hr.center().y), Align2::RIGHT_CENTER, cnt, t.font(12.5), t.text_dim); + let full = crate::i18n::source_title(&app.session); + let count_w = ui.painter().layout_no_wrap(cnt.clone(), t.font(12.5), t.text_dim).size().x; + // the title gives way to the count: a long album or folder name is trimmed, in full on hover + let room = (hr.width() - 20.0 - count_w - 36.0).max(0.0); + let font = t.semibold(17.0); + let title = crate::widgets::elide_head(&full, room, |s| ui.painter().layout_no_wrap(s.to_string(), font.clone(), t.text).size().x); + let title_rect = ui.painter().text(pos2(hr.left() + 20.0, hr.center().y), Align2::LEFT_CENTER, &title, font, t.text); + let count_rect = ui.painter().text(pos2(hr.right() - 20.0, hr.center().y), Align2::RIGHT_CENTER, cnt, t.font(12.5), t.text_dim); + crate::widgets::register(ui.ctx(), "grid:title", title_rect); + crate::widgets::register(ui.ctx(), "grid:count", count_rect); + if title != full { + ui.interact(title_rect, ui.id().with("grid-title"), Sense::hover()).on_hover_text(full); + } } } super::chips::show(app, ui, &chips); diff --git a/crates/ui-egui/src/panels/left.rs b/crates/ui-egui/src/panels/left.rs index be47fa21..c3a438d5 100644 --- a/crates/ui-egui/src/panels/left.rs +++ b/crates/ui-egui/src/panels/left.rs @@ -749,32 +749,6 @@ fn folders_section(app: &mut LightcraftApp, ui: &mut egui::Ui) { } } -/// `text` shortened to fit `max` (as `width` measures it): leading folders drop first -/// (`Users/me/Pictures/Lightroom` → `…/Pictures/Lightroom`) so the end of a path, which says the -/// most, stays; a single name still too long loses its end (`2024-06-12 Tri…`: folders tend to -/// differ at the start). Cuts fall on characters. The result is never empty. -fn elide_head(text: &str, max: f32, width: impl Fn(&str) -> f32) -> String { - if width(text) <= max { - return text.to_string(); - } - let mut rest = text; - while let Some(i) = rest.find('/') { - rest = rest.get(i + 1..).unwrap_or(""); - let cand = format!("…/{rest}"); - if width(&cand) <= max { - return cand; - } - } - let n = rest.chars().count(); - for keep in (1..n).rev() { - let cand: String = rest.chars().take(keep).chain(std::iter::once('…')).collect(); - if width(&cand) <= max { - return cand; - } - } - "…".to_string() -} - /// Whenever the shown folder changes (a click, an agent, a rename or its undo), open the rows /// above it so it is on screen; folding one by hand afterwards sticks until the choice changes. fn reveal_chosen(app: &LightcraftApp, ui: &egui::Ui, tree: &[FolderNode]) { @@ -799,6 +773,10 @@ fn reveal_chosen(app: &LightcraftApp, ui: &egui::Ui, tree: &[FolderNode]) { open_above(ui, tree, &chosen); } +/// Deeper folders keep this indent: a chain of a dozen single folders would otherwise push every +/// label out of the sidebar. +const MAX_FOLDER_INDENT: f32 = 16.0 * 5.0; + fn folder_rows(app: &mut LightcraftApp, ui: &mut egui::Ui, nodes: &[FolderNode], indent: f32) { let t = Tokens::get(ui.ctx()); for n in nodes { @@ -819,7 +797,8 @@ fn folder_rows(app: &mut LightcraftApp, ui: &mut egui::Ui, nodes: &[FolderNode], let count_w = if app.ui.show_counts { ui.painter().layout_no_wrap(n.count.to_string(), t.font(12.5), egui::Color32::WHITE).size().x } else { 0.0 }; let room = ui.available_width() - 42.0 - indent - count_w - 28.0; - let label = elide_head(&name, room, |s| ui.painter().layout_no_wrap(s.to_string(), font.clone(), egui::Color32::WHITE).size().x); + let label = + crate::widgets::elide_head(&name, room, |s| ui.painter().layout_no_wrap(s.to_string(), font.clone(), egui::Color32::WHITE).size().x); let resp = row_named(app, ui, &format!("libfolder:{}", n.path), Icon::Folder, &label, Some(&name), Some(n.count), sel, indent); let mut toggled = false; if !n.children.is_empty() { @@ -836,6 +815,9 @@ fn folder_rows(app: &mut LightcraftApp, ui: &mut egui::Ui, nodes: &[FolderNode], }; ui.painter().add(egui::Shape::convex_polygon(pts, col, egui::Stroke::NONE)); toggled = tr.clicked(); + tr.widget_info(|| { + egui::WidgetInfo::labeled(egui::WidgetType::Button, true, if open { crate::i18n::tr("Collapse") } else { crate::i18n::tr("Expand") }) + }); // the triangle sits on the row and takes its clicks: the menu opens from it too row_menu(app, &tr, n); } @@ -855,7 +837,7 @@ fn folder_rows(app: &mut LightcraftApp, ui: &mut egui::Ui, nodes: &[FolderNode], } row_menu(app, &resp, n); if open && !n.children.is_empty() { - folder_rows(app, ui, &n.children, indent + 16.0); + folder_rows(app, ui, &n.children, (indent + 16.0).min(MAX_FOLDER_INDENT)); } } } @@ -998,7 +980,8 @@ fn keyword_rows(app: &mut LightcraftApp, ui: &mut egui::Ui, nodes: &[KeywordNode #[cfg(test)] mod tests { - use super::{elide_head, local_places}; + use super::local_places; + use crate::widgets::elide_head; /// Width = characters, so a limit of 12 is "12 characters". fn fit(text: &str, max: usize) -> String { diff --git a/crates/ui-egui/src/tests_panels.rs b/crates/ui-egui/src/tests_panels.rs index 585a3e95..b97276f7 100644 --- a/crates/ui-egui/src/tests_panels.rs +++ b/crates/ui-egui/src/tests_panels.rs @@ -202,6 +202,10 @@ fn sidebar_sections_collapse_and_remember_it() { /// A headless app over a library of file-backed photos that exist only in the catalog. fn folders_app(paths: &[&str]) -> Headless { + folders_app_sized(paths, [1400.0, 900.0]) +} + +fn folders_app_sized(paths: &[&str], size: [f32; 2]) -> Headless { use lightcraft_catalog::{Op, Photo, Source}; let mut session = lightcraft_engine::Session::new(); for path in paths { @@ -210,7 +214,7 @@ fn folders_app(paths: &[&str]) -> Headless { session.catalog.apply(Op::AddPhoto { photo: Box::new(p) }).unwrap(); } let app = LightcraftApp::new(session, Services { png: None, ..Default::default() }); - let mut h = Headless::new(app, [1400.0, 900.0], 1.0); + let mut h = Headless::new(app, size, 1.0); let r = h.request("ui.set", json!({"view": "photoGrid", "leftPanel": true}), T); assert_eq!(r["ok"], true, "{r}"); h.settle(SETTLE); @@ -229,7 +233,7 @@ fn click(h: &mut Headless, id: &str) { } /// Photos imported from two folders: the sidebar's Folders section lists them, choosing one -/// narrows the grid to its photos, and choosing it again shows everything (like By Date). +/// fills the grid with its photos, and All Photos shows everything again. #[test] fn folders_section_lists_where_photos_were_imported_from_and_narrows_the_grid() { let mut h = folders_app(&["/pics/trip/a.jpg", "/pics/trip/b.jpg", "/pics/home/c.jpg"]); @@ -361,8 +365,16 @@ fn the_chosen_folder_is_always_in_view() { #[test] fn a_renamed_folder_keeps_its_place_in_the_tree() { + /// Removes the folder however the test ends. + struct Cleanup(std::path::PathBuf); + impl Drop for Cleanup { + fn drop(&mut self) { + let _ = std::fs::remove_dir_all(&self.0); + } + } let base = std::env::temp_dir().join(format!("lc-ui-rename-{}", std::process::id())); let _ = std::fs::remove_dir_all(&base); + let _cleanup = Cleanup(base.clone()); std::fs::create_dir_all(base.join("pics/trip")).unwrap(); std::fs::create_dir_all(base.join("pics/home")).unwrap(); let b = base.to_string_lossy().to_string(); @@ -377,7 +389,6 @@ fn a_renamed_folder_keeps_its_place_in_the_tree() { h.step(); h.step(); assert!(has(&h, &format!("source:libfolder:{b}/pics2/trip")), "the chosen folder is still on screen after the rename"); - let _ = std::fs::remove_dir_all(&base); } /// The menu opens from anywhere on a folder row, the disclosure triangle included; a disk row @@ -417,3 +428,35 @@ fn removing_a_disk_from_the_library_asks_first() { assert_eq!(r["ok"], true, "{r}"); assert_eq!(h.app.session.visible().len(), 1, "everything on that disk left the library"); } + +/// However deep the folders go, a row keeps its name, count and click targets inside the panel: +/// the indent stops growing after five levels instead of pushing the label out of the row. +#[test] +fn a_deep_chain_of_folders_keeps_its_rows_usable() { + let deep = "/a/b/c/d/e/f/g/h/i/j/k/l/m"; + let mut h = folders_app(&[&format!("{deep}/x/1.jpg"), &format!("{deep}/y/2.jpg")]); + let toggles: Vec<(String, egui::Rect)> = h.app.widgets.iter().filter(|(w, _)| w.starts_with("libraryFolderToggle:")).cloned().collect(); + assert!(toggles.len() >= 12, "the chain opens by itself: {}", toggles.len()); + let row = widget(&h, "source:libfolder:/a"); + for (id, r) in &toggles { + assert!(r.center().x <= row.left() + 10.0 + 16.0 * 5.0 + 1.0, "{id}: the triangle stays near the left edge ({r:?})"); + } + // a click in the middle of a deep row chooses it; only the little triangle folds it + let leaf = format!("source:libfolder:{deep}/x"); + assert!(has(&h, "libraryFolderToggle:/a/b/c/d/e/f/g/h/i/j/k/l/m") || has(&h, &leaf)); + click(&mut h, "source:libfolder:/a/b/c/d/e/f/g/h/i/j/k/l"); + assert_eq!(h.app.session.library_folder.as_deref(), Some("/a/b/c/d/e/f/g/h/i/j/k/l"), "the row's middle is the row, not its triangle"); +} + +/// A long folder name is trimmed before it can run under the photo count. +#[test] +fn a_long_folder_name_never_runs_under_the_photo_count() { + let long = "/very/long/2024-06-12 Tripping Through The Extremely Long Named Mountains Of Somewhere"; + let mut h = folders_app_sized(&[&format!("{long}/a.jpg")], [900.0, 700.0]); + h.app.session.source = lightcraft_engine::LibrarySource::LibraryFolder; + h.app.session.library_folder = Some(long.into()); + h.step(); + h.step(); + let (title, count) = (widget(&h, "grid:title"), widget(&h, "grid:count")); + assert!(title.right() + 8.0 <= count.left(), "title {title:?} vs count {count:?}"); +} diff --git a/crates/ui-egui/src/widgets.rs b/crates/ui-egui/src/widgets.rs index d89a9ce3..b9f463f0 100644 --- a/crates/ui-egui/src/widgets.rs +++ b/crates/ui-egui/src/widgets.rs @@ -506,3 +506,29 @@ mod access_tests { assert!(has("Button", "Delete mask"), "{found:?}"); } } + +/// `text` shortened to fit `max` (as `width` measures it): leading folders drop first +/// (`Users/me/Pictures/Lightroom` → `…/Pictures/Lightroom`) so the end of a path, which says the +/// most, stays; a single name still too long loses its end (`2024-06-12 Tri…`: folders tend to +/// differ at the start). Cuts fall on characters. The result is never empty. +pub(crate) fn elide_head(text: &str, max: f32, width: impl Fn(&str) -> f32) -> String { + if width(text) <= max { + return text.to_string(); + } + let mut rest = text; + while let Some(i) = rest.find('/') { + rest = rest.get(i + 1..).unwrap_or(""); + let cand = format!("…/{rest}"); + if width(&cand) <= max { + return cand; + } + } + let n = rest.chars().count(); + for keep in (1..n).rev() { + let cand: String = rest.chars().take(keep).chain(std::iter::once('…')).collect(); + if width(&cand) <= max { + return cand; + } + } + "…".to_string() +} diff --git a/docs/parity.md b/docs/parity.md index 206e2c3e..49dcdf0d 100644 --- a/docs/parity.md +++ b/docs/parity.md @@ -718,7 +718,7 @@ From `08-lightroom-classic-extras.md` (Classic-only features) and part 2 of `06- | LRC-LIB-CATALOG-PANEL | Catalog sets | P1 | ✅ | `cmd:library.source` (`missing`) | all, recently added, picks, recently deleted, missing photos (when any) | | LRC-LIB-CATALOG-LOCK | Catalog open in one program at a time | P1 | ✅ | `cmd:app.openLibrary`, `crates/catalog/src/lock.rs`, `crates/engine/src/library.rs` (`open_library`) | exclusive OS lock on `catalog.lock` for the session (app and `lightcraft-cli --library`); a second opener is refused with who has it (`catalog.lock.owner`: program, process, computer); released by the OS on a crash, so never stale; file systems without locks open unprotected with a warning; no read-only mode; the browser build (OPFS) has no guard across tabs yet. A library that can't be opened at launch (locked, unreadable, newer format, drive missing) shows a window — Try Again, Choose Another Library…, Continue Without Saving (then a banner; nothing written), Quit — instead of a silent demo session (`crates/ui-egui/src/panels/library_problem.rs`) | | LRC-LIB-FOLDERS | Disk folder tree | P1 | ✅ | `cmd:library.browse`, `cmd:folder.rename`, `cmd:folder.move`, `crates/ui-egui/src/panels/left.rs` (`local_section`, `folder_tree`) | standard places + the browsed folder + Browse Folder…, each an expandable tree of subfolders, breadcrumb navigation; context menu: Rename Folder…, Move Folder To…, Show in Finder (on disk, photos relinked; one undo step each: undo / redo move the folder back and forth, refused when the destination is taken) | -| LRC-LIB-LIBFOLDERS | Library folders | P1 | ✅ | `cmd:library.folders`, `cmd:library.source` (`libraryFolder`), `cmd:library.filter` (`libraryFolder`), `cmd:library.removeFolder`, `cmd:folder.rename`, `cmd:folder.move`, `crates/catalog/src/folders.rs`, `crates/ui-egui/src/panels/left.rs` (`folders_section`, `folder_menu_for_library`, `row_menu`), `crates/ui-egui/src/tests_panels.rs` | sidebar Folders: the folders the library's photos were imported from, with photo counts (subfolders included), as another way to explore the library next to By Date and Keywords; Local stays the way to browse any folder on disk. Each disk is a row of its own and starts open: a drive letter, a Windows share (`\\server\share`, `\\?\UNC\…`, `\\wsl$\…`), a macOS share or disk (`/Volumes/tokyo`), a Linux mount (`/media`, `/run/media`, `/mnt`, gvfs `smb-share:…`, `/net/`), NFS (`/Network/Servers/`), else “This Computer” — told apart from the stored paths alone, so an offline share lists like a mounted one and the sidebar never waits on a network. Only folders that hold imported photos are listed (browsed, deleted and demo photos don't count); every folder is a row of its own (`Users` → `me` → `Pictures`), and a folder that holds nothing itself and leads to one folder starts open; long names are cut at the end with the full path in the tooltip; click makes the folder the source, like an album or a Local folder (its photos and those of the folders inside it fill the grid, titled “photos/travel”; the filter bar narrows it further; `library.source` `libraryFolder` + `path`; kept with the library's view), and its row is always opened into view, also after a rename or move and their undo. A row that would cover other disks' photos too (the startup disk, `/Volumes`, `/mnt`…) only opens. Right-click (row or triangle): Rename Folder…, Move Folder To…, Show in Finder (on disk, photos relinked, as in Local) and Remove from Library… (asks; moves the folder's photos to Recently Deleted, one undo step, no file touched); a disk row offers Remove Disk from Library… (the command needs `disk: true` for a whole disk or a folder spanning several). Limits: names are compared as written (macOS `/Volumes/tokyo` vs `/Volumes/TOKYO` are two disks; `/Volumes/Macintosh HD/…` and `/…` two rows); no disk capacity / online status; no “include subfolders” switch (a folder always includes the ones inside it); creating a folder and moving photos between folders are not here | +| LRC-LIB-LIBFOLDERS | Library folders | P1 | ✅ | `cmd:library.folders`, `cmd:library.source` (`libraryFolder`), `cmd:library.filter` (`libraryFolder`), `cmd:library.removeFolder`, `cmd:folder.rename`, `cmd:folder.move`, `crates/catalog/src/folders.rs`, `crates/ui-egui/src/panels/left.rs` (`folders_section`, `folder_menu_for_library`, `row_menu`), `crates/ui-egui/src/tests_panels.rs` | sidebar Folders: the folders the library's photos were imported from, with photo counts (subfolders included), as another way to explore the library next to By Date and Keywords; Local stays the way to browse any folder on disk. Each disk is a row of its own and starts open: a drive letter, a Windows share (`\\server\share`, `\\?\UNC\…`, `\\wsl$\…`), a macOS share or disk (`/Volumes/tokyo`), a Linux mount (`/media`, `/run/media`, `/mnt`, gvfs `smb-share:…`, `/net/`), NFS (`/Network/Servers/`), else “This Computer” — told apart from the stored paths alone, so an offline share lists like a mounted one and the sidebar never waits on a network. Only folders that hold imported photos are listed (browsed, deleted and demo photos don't count); every folder is a row of its own (`Users` → `me` → `Pictures`), and a folder that holds nothing itself and leads to one folder starts open; deep chains stop indenting after five levels, long names are cut with the full path in the tooltip (the grid title too); click makes the folder the source, like an album or a Local folder (its photos and those of the folders inside it fill the grid, titled “photos/travel”; the filter bar narrows it further; `library.source` `libraryFolder` + `path`; kept with the library's view), and its row is always opened into view; the shown folder follows a rename or move and their undo (engine-tested; the sidebar test covers rename), is dropped back to All Photos when it is removed or no longer holds a photo, and a smart album made from the view keeps it. Only a folder that shows exactly its own photos can be shown (not the startup disk, not `/Volumes`-like folders above disks, not an empty one). A row that would cover other disks' photos too (the startup disk, `/Volumes`, `/mnt`…) only opens. Right-click (row or triangle): Rename Folder…, Move Folder To…, Show in Finder (on disk, photos relinked, as in Local) and Remove from Library… (asks; moves the folder's photos to Recently Deleted, one undo step, no file touched); a disk row offers Remove Disk from Library… (the command needs `disk: true` for a whole disk or a folder spanning several). Limits: names are compared as written (macOS `/Volumes/tokyo` vs `/Volumes/TOKYO` are two disks; `/Volumes/Macintosh HD/…` and `/…` two rows); no disk capacity / online status; no “include subfolders” switch (a folder always includes the ones inside it); creating a folder and moving photos between folders are not here | | LRC-LIB-COLLECTIONS | Collections & sets | P1 | ✅ | `cmd:album.create`, `cmd:album.createSmart`, `cmd:album.toggleTarget`, `cmd:album.setTarget`, `cmd:album.clearQuick` | albums + folders (sets), smart albums (rule editor; rules incl. File Path — the whole string, either separator, case-insensitive), Quick Collection and target album (B in the grids adds / removes the selection; album menu ▸ Set as Target Album; the target is marked +) | | LRC-LIB-SMARTCOLL | Smart-collection rules | P1 | ✅ | `cmd:album.createSmart`, `cmd:album.setRules`, `cmd:album.ruleFields`, `cmd:dialog.smartAlbum`, `crates/catalog/src/rules.rs`, `crates/ui-egui/src/panels/rules_editor.rs` | rule editor: match all / any / none, nested groups (⌥+ or + Group), 30 fields (rating, flag, label, type, edits, keywords, any text, filename, file path, format, title, caption, camera, lens, location, creator, copyright, copyright status, capture / import / edit date, ISO, aperture, focal length, megapixels, GPS, virtual copy, album) with text / number / date / in-the-last operators; live match count; also usable as a library filter (`ruleSet`) | | LRC-LIB-PUBLISH | Publish services | P2 | ⬜ | | | From 6f33aacd229378d90cf060dd7cdaef0c3bf2c656 Mon Sep 17 00:00:00 2001 From: Nathaniel Ledford Date: Thu, 8 Oct 2026 08:24:18 -0400 Subject: [PATCH 11/41] Sidebar: scrolls sideways so deep folders keep their nesting Every level indents one step more than its parent however deep it goes (the five-level indent cap is gone). The whole sidebar scrolls in both directions: rows report how wide they need to be, the widest one of the last frame sets the content width (at least the panel's), and a bar appears only when it overflows. Folder names are no longer cut in the tree; Create Album stays at the visible edge. Co-Authored-By: Claude Sonnet 5.5 --- crates/ui-egui/src/panels/left.rs | 54 ++++++++++++++++++++---------- crates/ui-egui/src/tests_panels.rs | 32 ++++++++++++------ docs/parity.md | 2 +- 3 files changed, 60 insertions(+), 28 deletions(-) diff --git a/crates/ui-egui/src/panels/left.rs b/crates/ui-egui/src/panels/left.rs index c3a438d5..dfab9728 100644 --- a/crates/ui-egui/src/panels/left.rs +++ b/crates/ui-egui/src/panels/left.rs @@ -10,6 +10,21 @@ use crate::icons::{Icon, paint}; use crate::theme::Tokens; use crate::widgets::{icon_button, register}; +/// How wide the sidebar's content needs to be, from the widest row of the last frame: rows are +/// drawn at this width (or the panel's, if wider), and the sidebar scrolls sideways when it +/// exceeds the panel. One frame behind, which needs no second layout pass. +pub(crate) fn content_width(ctx: &egui::Context) -> f32 { + ctx.data(|d| d.get_temp::(egui::Id::new("left-content-width"))).unwrap_or(0.0) +} + +/// A row says how wide it needs to be. +fn note_width(ui: &egui::Ui, w: f32) { + ui.data_mut(|d| { + let m = d.get_temp_mut_or_insert_with::(egui::Id::new("left-content-width-next"), || 0.0); + *m = m.max(w); + }); +} + fn row( app: &mut LightcraftApp, ui: &mut egui::Ui, @@ -57,16 +72,19 @@ fn row_named( icon, if selected { t.text } else { t.icon }, ); - ui.painter().text( + let label_rect = ui.painter().text( pos2(r.left() + 42.0 + indent, r.center().y), Align2::LEFT_CENTER, label, t.font(13.5), if selected { t.text } else { t.text_label }, ); + let mut needed = label_rect.right() - r.left() + 18.0; if let Some(n) = count.filter(|_| app.ui.show_counts) { - ui.painter().text(pos2(r.right() - 18.0, r.center().y), Align2::RIGHT_CENTER, n.to_string(), t.font(12.5), t.text_dim); + let c = ui.painter().text(pos2(r.right() - 18.0, r.center().y), Align2::RIGHT_CENTER, n.to_string(), t.font(12.5), t.text_dim); + needed += c.width() + 16.0; } + note_width(ui, needed); let _ = app; resp } @@ -106,7 +124,12 @@ pub fn show(app: &mut LightcraftApp, ui: &mut egui::Ui) { ui.painter().text(pos2(hr.left() + 18.0, hr.center().y), Align2::LEFT_CENTER, crate::i18n::tr("My Photos"), t.semibold(15.0), t.text); let counts = app.caches.counts(&app.session.catalog); let (total, picks, deleted) = (counts.total, counts.picks, counts.deleted); - egui::ScrollArea::vertical().id_salt("left-scroll").auto_shrink([false, false]).show(ui, |ui| { + egui::ScrollArea::both().id_salt("left-scroll").auto_shrink([false, false]).show_viewport(ui, |ui, viewport| { + // rows are as wide as the widest one needs (last frame), at least the panel + let wide = viewport.width().max(content_width(ui.ctx())); + ui.set_min_width(wide); + ui.set_max_width(wide); + ui.data_mut(|d| d.insert_temp(egui::Id::new("left-content-width-next"), 0.0f32)); let src = app.session.source; for (id, icon, label, count, s) in [ ("all", Icon::Photos, "All Photos", Some(total), LibrarySource::All), @@ -127,9 +150,11 @@ pub fn show(app: &mut LightcraftApp, ui: &mut egui::Ui) { ui.add_space(10.0); // Albums header let (ar, albums_open) = sidebar_section_header(app, ui, "albums", "Albums"); + // the + stays at the visible edge when the sidebar is scrolled sideways + let plus_right = (ar.left() + viewport.max.x).min(ar.right()); let mut hdr = ui.new_child( egui::UiBuilder::new() - .max_rect(Rect::from_min_max(pos2(ar.right() - 50.0, ar.top()), ar.right_bottom())) + .max_rect(Rect::from_min_max(pos2(plus_right - 50.0, ar.top()), pos2(plus_right, ar.bottom()))) .layout(egui::Layout::right_to_left(egui::Align::Center)), ); let plus = icon_button(&mut hdr, "albumNew", Icon::Plus, vec2(26.0, 26.0), false, true, "Create Album"); @@ -180,6 +205,12 @@ pub fn show(app: &mut LightcraftApp, ui: &mut egui::Ui) { if row(app, ui, "recentlyDeleted", Icon::Trash, "Recently Deleted", Some(deleted), src == LibrarySource::RecentlyDeleted, 0.0).clicked() { let _ = app.run("library.source", json!({"kind": "recentlyDeleted"})); } + // what the rows asked for becomes next frame's width + let next = ui.data(|d| d.get_temp::(egui::Id::new("left-content-width-next"))).unwrap_or(0.0); + if (next - content_width(ui.ctx())).abs() > 0.5 { + ui.data_mut(|d| d.insert_temp(egui::Id::new("left-content-width"), next)); + ui.ctx().request_repaint(); + } }); }); if let Some(w) = resized { @@ -773,10 +804,6 @@ fn reveal_chosen(app: &LightcraftApp, ui: &egui::Ui, tree: &[FolderNode]) { open_above(ui, tree, &chosen); } -/// Deeper folders keep this indent: a chain of a dozen single folders would otherwise push every -/// label out of the sidebar. -const MAX_FOLDER_INDENT: f32 = 16.0 * 5.0; - fn folder_rows(app: &mut LightcraftApp, ui: &mut egui::Ui, nodes: &[FolderNode], indent: f32) { let t = Tokens::get(ui.ctx()); for n in nodes { @@ -792,14 +819,7 @@ fn folder_rows(app: &mut LightcraftApp, ui: &mut egui::Ui, nodes: &[FolderNode], && app.session.source == LibrarySource::LibraryFolder && app.session.library_folder.as_deref().is_some_and(|f| same_folder(f, &n.path)); let name = if selectable { n.name.clone() } else { crate::i18n::tr("This Computer").to_string() }; - // room for the name: between the icon and the count - let font = t.font(13.5); - let count_w = - if app.ui.show_counts { ui.painter().layout_no_wrap(n.count.to_string(), t.font(12.5), egui::Color32::WHITE).size().x } else { 0.0 }; - let room = ui.available_width() - 42.0 - indent - count_w - 28.0; - let label = - crate::widgets::elide_head(&name, room, |s| ui.painter().layout_no_wrap(s.to_string(), font.clone(), egui::Color32::WHITE).size().x); - let resp = row_named(app, ui, &format!("libfolder:{}", n.path), Icon::Folder, &label, Some(&name), Some(n.count), sel, indent); + let resp = row_named(app, ui, &format!("libfolder:{}", n.path), Icon::Folder, &name, Some(&name), Some(n.count), sel, indent); let mut toggled = false; if !n.children.is_empty() { // disclosure triangle left of the icon @@ -837,7 +857,7 @@ fn folder_rows(app: &mut LightcraftApp, ui: &mut egui::Ui, nodes: &[FolderNode], } row_menu(app, &resp, n); if open && !n.children.is_empty() { - folder_rows(app, ui, &n.children, (indent + 16.0).min(MAX_FOLDER_INDENT)); + folder_rows(app, ui, &n.children, indent + 16.0); } } } diff --git a/crates/ui-egui/src/tests_panels.rs b/crates/ui-egui/src/tests_panels.rs index b97276f7..0396a0f3 100644 --- a/crates/ui-egui/src/tests_panels.rs +++ b/crates/ui-egui/src/tests_panels.rs @@ -429,25 +429,37 @@ fn removing_a_disk_from_the_library_asks_first() { assert_eq!(h.app.session.visible().len(), 1, "everything on that disk left the library"); } -/// However deep the folders go, a row keeps its name, count and click targets inside the panel: -/// the indent stops growing after five levels instead of pushing the label out of the row. +/// However deep the folders go, each level is indented one step more than its parent, and the +/// sidebar scrolls sideways to reach them instead of squeezing the nesting. #[test] -fn a_deep_chain_of_folders_keeps_its_rows_usable() { +fn a_deep_chain_keeps_its_nesting_and_the_sidebar_scrolls_sideways() { let deep = "/a/b/c/d/e/f/g/h/i/j/k/l/m"; let mut h = folders_app(&[&format!("{deep}/x/1.jpg"), &format!("{deep}/y/2.jpg")]); - let toggles: Vec<(String, egui::Rect)> = h.app.widgets.iter().filter(|(w, _)| w.starts_with("libraryFolderToggle:")).cloned().collect(); - assert!(toggles.len() >= 12, "the chain opens by itself: {}", toggles.len()); - let row = widget(&h, "source:libfolder:/a"); - for (id, r) in &toggles { - assert!(r.center().x <= row.left() + 10.0 + 16.0 * 5.0 + 1.0, "{id}: the triangle stays near the left edge ({r:?})"); + let mut xs: Vec = Vec::new(); + let mut path = String::new(); + for name in deep.split('/').filter(|n| !n.is_empty()) { + path.push('/'); + path.push_str(name); + xs.push(widget(&h, &format!("libraryFolderToggle:{path}")).center().x); } + assert!(xs.len() == 13 && xs.windows(2).all(|w| (w[1] - w[0] - 16.0).abs() < 0.5), "every level steps right by one indent: {xs:?}"); + let panel = widget(&h, "panel:left_panel"); + assert!(crate::panels::left::content_width(&h.view.ctx) > panel.width(), "the content is wider than the panel: the bar appears"); + // what a row needs is known before it is drawn off screen, so the plus stays reachable + assert!(widget(&h, "icon:albumNew").right() <= panel.right(), "Create Album stays inside the visible panel"); // a click in the middle of a deep row chooses it; only the little triangle folds it - let leaf = format!("source:libfolder:{deep}/x"); - assert!(has(&h, "libraryFolderToggle:/a/b/c/d/e/f/g/h/i/j/k/l/m") || has(&h, &leaf)); click(&mut h, "source:libfolder:/a/b/c/d/e/f/g/h/i/j/k/l"); assert_eq!(h.app.session.library_folder.as_deref(), Some("/a/b/c/d/e/f/g/h/i/j/k/l"), "the row's middle is the row, not its triangle"); } +/// A sidebar that fits needs no sideways scrolling. +#[test] +fn a_sidebar_that_fits_does_not_scroll_sideways() { + let h = folders_app(&["/pics/trip/a.jpg", "/pics/home/b.jpg"]); + let panel = widget(&h, "panel:left_panel"); + assert!(crate::panels::left::content_width(&h.view.ctx) <= panel.width(), "no horizontal bar for a shallow tree"); +} + /// A long folder name is trimmed before it can run under the photo count. #[test] fn a_long_folder_name_never_runs_under_the_photo_count() { diff --git a/docs/parity.md b/docs/parity.md index 49dcdf0d..ce312087 100644 --- a/docs/parity.md +++ b/docs/parity.md @@ -718,7 +718,7 @@ From `08-lightroom-classic-extras.md` (Classic-only features) and part 2 of `06- | LRC-LIB-CATALOG-PANEL | Catalog sets | P1 | ✅ | `cmd:library.source` (`missing`) | all, recently added, picks, recently deleted, missing photos (when any) | | LRC-LIB-CATALOG-LOCK | Catalog open in one program at a time | P1 | ✅ | `cmd:app.openLibrary`, `crates/catalog/src/lock.rs`, `crates/engine/src/library.rs` (`open_library`) | exclusive OS lock on `catalog.lock` for the session (app and `lightcraft-cli --library`); a second opener is refused with who has it (`catalog.lock.owner`: program, process, computer); released by the OS on a crash, so never stale; file systems without locks open unprotected with a warning; no read-only mode; the browser build (OPFS) has no guard across tabs yet. A library that can't be opened at launch (locked, unreadable, newer format, drive missing) shows a window — Try Again, Choose Another Library…, Continue Without Saving (then a banner; nothing written), Quit — instead of a silent demo session (`crates/ui-egui/src/panels/library_problem.rs`) | | LRC-LIB-FOLDERS | Disk folder tree | P1 | ✅ | `cmd:library.browse`, `cmd:folder.rename`, `cmd:folder.move`, `crates/ui-egui/src/panels/left.rs` (`local_section`, `folder_tree`) | standard places + the browsed folder + Browse Folder…, each an expandable tree of subfolders, breadcrumb navigation; context menu: Rename Folder…, Move Folder To…, Show in Finder (on disk, photos relinked; one undo step each: undo / redo move the folder back and forth, refused when the destination is taken) | -| LRC-LIB-LIBFOLDERS | Library folders | P1 | ✅ | `cmd:library.folders`, `cmd:library.source` (`libraryFolder`), `cmd:library.filter` (`libraryFolder`), `cmd:library.removeFolder`, `cmd:folder.rename`, `cmd:folder.move`, `crates/catalog/src/folders.rs`, `crates/ui-egui/src/panels/left.rs` (`folders_section`, `folder_menu_for_library`, `row_menu`), `crates/ui-egui/src/tests_panels.rs` | sidebar Folders: the folders the library's photos were imported from, with photo counts (subfolders included), as another way to explore the library next to By Date and Keywords; Local stays the way to browse any folder on disk. Each disk is a row of its own and starts open: a drive letter, a Windows share (`\\server\share`, `\\?\UNC\…`, `\\wsl$\…`), a macOS share or disk (`/Volumes/tokyo`), a Linux mount (`/media`, `/run/media`, `/mnt`, gvfs `smb-share:…`, `/net/`), NFS (`/Network/Servers/`), else “This Computer” — told apart from the stored paths alone, so an offline share lists like a mounted one and the sidebar never waits on a network. Only folders that hold imported photos are listed (browsed, deleted and demo photos don't count); every folder is a row of its own (`Users` → `me` → `Pictures`), and a folder that holds nothing itself and leads to one folder starts open; deep chains stop indenting after five levels, long names are cut with the full path in the tooltip (the grid title too); click makes the folder the source, like an album or a Local folder (its photos and those of the folders inside it fill the grid, titled “photos/travel”; the filter bar narrows it further; `library.source` `libraryFolder` + `path`; kept with the library's view), and its row is always opened into view; the shown folder follows a rename or move and their undo (engine-tested; the sidebar test covers rename), is dropped back to All Photos when it is removed or no longer holds a photo, and a smart album made from the view keeps it. Only a folder that shows exactly its own photos can be shown (not the startup disk, not `/Volumes`-like folders above disks, not an empty one). A row that would cover other disks' photos too (the startup disk, `/Volumes`, `/mnt`…) only opens. Right-click (row or triangle): Rename Folder…, Move Folder To…, Show in Finder (on disk, photos relinked, as in Local) and Remove from Library… (asks; moves the folder's photos to Recently Deleted, one undo step, no file touched); a disk row offers Remove Disk from Library… (the command needs `disk: true` for a whole disk or a folder spanning several). Limits: names are compared as written (macOS `/Volumes/tokyo` vs `/Volumes/TOKYO` are two disks; `/Volumes/Macintosh HD/…` and `/…` two rows); no disk capacity / online status; no “include subfolders” switch (a folder always includes the ones inside it); creating a folder and moving photos between folders are not here | +| LRC-LIB-LIBFOLDERS | Library folders | P1 | ✅ | `cmd:library.folders`, `cmd:library.source` (`libraryFolder`), `cmd:library.filter` (`libraryFolder`), `cmd:library.removeFolder`, `cmd:folder.rename`, `cmd:folder.move`, `crates/catalog/src/folders.rs`, `crates/ui-egui/src/panels/left.rs` (`folders_section`, `folder_menu_for_library`, `row_menu`), `crates/ui-egui/src/tests_panels.rs` | sidebar Folders: the folders the library's photos were imported from, with photo counts (subfolders included), as another way to explore the library next to By Date and Keywords; Local stays the way to browse any folder on disk. Each disk is a row of its own and starts open: a drive letter, a Windows share (`\\server\share`, `\\?\UNC\…`, `\\wsl$\…`), a macOS share or disk (`/Volumes/tokyo`), a Linux mount (`/media`, `/run/media`, `/mnt`, gvfs `smb-share:…`, `/net/`), NFS (`/Network/Servers/`), else “This Computer” — told apart from the stored paths alone, so an offline share lists like a mounted one and the sidebar never waits on a network. Only folders that hold imported photos are listed (browsed, deleted and demo photos don't count); every folder is a row of its own (`Users` → `me` → `Pictures`), and a folder that holds nothing itself and leads to one folder starts open; every level indents one step more than its parent however deep it goes: the whole sidebar (Albums, Local, By Date, Folders, Keywords) scrolls sideways when its widest row is wider than the panel (rows are measured one frame behind; Create Album stays at the visible edge); the grid title is cut with the full name on hover; click makes the folder the source, like an album or a Local folder (its photos and those of the folders inside it fill the grid, titled “photos/travel”; the filter bar narrows it further; `library.source` `libraryFolder` + `path`; kept with the library's view), and its row is always opened into view; the shown folder follows a rename or move and their undo (engine-tested; the sidebar test covers rename), is dropped back to All Photos when it is removed or no longer holds a photo, and a smart album made from the view keeps it. Only a folder that shows exactly its own photos can be shown (not the startup disk, not `/Volumes`-like folders above disks, not an empty one). A row that would cover other disks' photos too (the startup disk, `/Volumes`, `/mnt`…) only opens. Right-click (row or triangle): Rename Folder…, Move Folder To…, Show in Finder (on disk, photos relinked, as in Local) and Remove from Library… (asks; moves the folder's photos to Recently Deleted, one undo step, no file touched); a disk row offers Remove Disk from Library… (the command needs `disk: true` for a whole disk or a folder spanning several). Limits: names are compared as written (macOS `/Volumes/tokyo` vs `/Volumes/TOKYO` are two disks; `/Volumes/Macintosh HD/…` and `/…` two rows); no disk capacity / online status; no “include subfolders” switch (a folder always includes the ones inside it); creating a folder and moving photos between folders are not here | | LRC-LIB-COLLECTIONS | Collections & sets | P1 | ✅ | `cmd:album.create`, `cmd:album.createSmart`, `cmd:album.toggleTarget`, `cmd:album.setTarget`, `cmd:album.clearQuick` | albums + folders (sets), smart albums (rule editor; rules incl. File Path — the whole string, either separator, case-insensitive), Quick Collection and target album (B in the grids adds / removes the selection; album menu ▸ Set as Target Album; the target is marked +) | | LRC-LIB-SMARTCOLL | Smart-collection rules | P1 | ✅ | `cmd:album.createSmart`, `cmd:album.setRules`, `cmd:album.ruleFields`, `cmd:dialog.smartAlbum`, `crates/catalog/src/rules.rs`, `crates/ui-egui/src/panels/rules_editor.rs` | rule editor: match all / any / none, nested groups (⌥+ or + Group), 30 fields (rating, flag, label, type, edits, keywords, any text, filename, file path, format, title, caption, camera, lens, location, creator, copyright, copyright status, capture / import / edit date, ISO, aperture, focal length, megapixels, GPS, virtual copy, album) with text / number / date / in-the-last operators; live match count; also usable as a library filter (`ruleSet`) | | LRC-LIB-PUBLISH | Publish services | P2 | ⬜ | | | From 198286e58976dcb5d83489fc31bbdc1081c89e9c Mon Sep 17 00:00:00 2001 From: Nathaniel Ledford Date: Thu, 8 Oct 2026 08:51:14 -0400 Subject: [PATCH 12/41] Folders: round-4 review fixes (counts at the edge, edge-space names, smart albums) - ui: counts stay at the visible edge when the sidebar scrolls sideways (with the row's colour behind them where a long name runs under); the smart-album dialog's live count includes the album's folder - catalog: a folder path is used as given (a name with a space at its edge is not its neighbour: Remove acted on the wrong folder); a plain POSIX path is checked against the folder without building a key (about half the cost on a big library, with a test that it agrees with the careful check) - engine: editing a smart album's rules keeps the folder the editor cannot show; a shown folder that loses its last photo gives way to All Photos; library.state and view.json name the folder only while it is shown; a blank folder filter is no filter Co-Authored-By: Claude Sonnet 5.5 --- crates/catalog/src/query.rs | 30 +++++++++-- crates/catalog/src/tests_folders.rs | 58 +++++++++++++++++++++ crates/engine/src/cmd/library.rs | 19 +++++-- crates/engine/src/cmd/query.rs | 2 +- crates/engine/src/lib.rs | 14 +++++ crates/engine/src/library.rs | 3 +- crates/engine/src/tests_folders.rs | 77 ++++++++++++++++++++++++++-- crates/ui-egui/src/panels/dialogs.rs | 6 ++- crates/ui-egui/src/panels/left.rs | 20 ++++++-- crates/ui-egui/src/tests_panels.rs | 11 +++- docs/parity.md | 2 +- 11 files changed, 222 insertions(+), 20 deletions(-) diff --git a/crates/catalog/src/query.rs b/crates/catalog/src/query.rs index 6f02b228..ad14cd0a 100644 --- a/crates/catalog/src/query.rs +++ b/crates/catalog/src/query.rs @@ -223,7 +223,9 @@ impl Filter { /// [`Filter::library_folder`] as a folder identity ([`folder_key`]); `None` without a choice. fn library_root(&self) -> Option { - self.library_folder.as_deref().map(str::trim).filter(|d| !d.is_empty()).map(folder_key) + // as given: a folder may be named with spaces at its edge, and trimming would make it + // another folder + self.library_folder.as_deref().filter(|d| !d.trim().is_empty()).map(folder_key) } /// [`Filter::matches`] with the library folder already turned into its identity, so a query @@ -302,7 +304,7 @@ impl Filter { return false; } if let Some(root) = root - && !(!p.local && crate::local::folder_of(p).is_some_and(|f| key_within(&f, root))) + && !photo_in_root(p, root) { return false; } @@ -523,8 +525,30 @@ pub fn folder_within(path: &str, root: &str) -> bool { key_within(&folder_key(path), &folder_key(root)) } +/// Whether a library photo (not one only browsed in Local) lies in the folder whose +/// [`folder_key`] is `root`. A plain POSIX path (no `\`, `.`, `..`, repeated or trailing +/// separators) is its own identity, so it is read without building a key for it: the check runs +/// for every photo of the library on each refresh, and this keeps it cheap. +pub(crate) fn photo_in_root(p: &Photo, root: &str) -> bool { + if p.local { + return false; + } + let crate::Source::File { path } = &p.source else { return false }; + let plain = path.starts_with('/') + && !path.starts_with("//") + && !path.ends_with('/') + && !path.contains(['\\']) + && !path.contains("//") + && !path.contains("/."); + if !cfg!(windows) && plain && root.starts_with('/') && !root.starts_with("//") { + let r = root.trim_end_matches('/'); + return path.strip_prefix(r).is_some_and(|rest| rest.starts_with('/')); + } + crate::local::folder_of(p).is_some_and(|f| key_within(&f, root)) +} + /// [`folder_within`] for two paths already turned into their [`folder_key`]. -fn key_within(p: &str, r: &str) -> bool { +pub(crate) fn key_within(p: &str, r: &str) -> bool { // `.` or `a/..` name no folder: not "everything" if r.is_empty() { return false; diff --git a/crates/catalog/src/tests_folders.rs b/crates/catalog/src/tests_folders.rs index 1a5a6128..556f77de 100644 --- a/crates/catalog/src/tests_folders.rs +++ b/crates/catalog/src/tests_folders.rs @@ -344,3 +344,61 @@ fn disk_roots_are_told_from_folders() { } assert!(is_startup_disk("/") && is_startup_disk("//") && !is_startup_disk("/Volumes/nas") && !is_startup_disk("C:")); } + +#[test] +fn a_folder_name_with_spaces_at_its_edge_is_its_own_folder() { + // legal on macOS and Linux: `/p/shoot ` is not `/p/shoot` + let c = library(&["/p/shoot /a.jpg", "/p/shoot/b.jpg", "/p/other/c.jpg"]); + let tree = c.folder_tree(); + let shoot_space = find(&tree, "/p/shoot ").expect("its own row"); + let shoot = find(&tree, "/p/shoot").expect("and the other"); + for (node, want) in [(shoot_space, "a.jpg"), (shoot, "b.jpg")] { + let f = Filter { library_folder: Some(node.path.clone()), ..Default::default() }; + let ids = c.query(&f, &Sort::default()); + let names: Vec<&str> = ids.iter().filter_map(|id| c.photo(*id)).map(|p| p.file_name.as_str()).collect(); + assert_eq!(names, [want], "{:?} shows its own photo", node.path); + } +} + +#[test] +fn a_local_photo_never_counts_as_imported_even_when_a_disk_folder_is_also_shown() { + let mut c = Catalog::new(); + let browsed = add_with(&mut c, "/pics/trip/b.jpg", |p| p.local = true); + let f = Filter { folder: Some("/pics/trip".into()), library_folder: Some("/pics/trip".into()), ..Default::default() }; + assert!(!f.matches(c.photo(browsed).unwrap(), &c)); +} + +#[test] +fn the_quick_check_agrees_with_the_careful_one() { + use crate::query::{folder_key, key_within, photo_in_root}; + let paths = [ + "/a/b/c.jpg", + "/a/b/d/e.jpg", + "/a/bc/x.jpg", + "/a/./b/x.jpg", + "/a/../a/b/x.jpg", + "/a//b/x.jpg", + "/a\\b\\x.jpg", + "/a/b ", + "/a/b /x.jpg", + "/.hidden/x.jpg", + "/a/.b/x.jpg", + "/x.jpg", + "//srv/share/x.jpg", + "C:\\a\\b\\x.jpg", + "relative/a/b/x.jpg", + "/Volumes/tokyo/a/x.jpg", + "/日本/写真/x.jpg", + ]; + let roots = ["/", "/a", "/a/b", "/a/b/", "/a/bc", "/a/b ", "/Volumes/tokyo", "//srv/share", "C:\\a", "/日本", "relative", "/a/../a/b"]; + for p in paths { + let mut c = Catalog::new(); + let id = add(&mut c, p); + let photo = c.photo(id).unwrap(); + for r in roots { + let root = folder_key(r); + let slow = crate::local::folder_of(photo).is_some_and(|f| key_within(&f, &root)); + assert_eq!(photo_in_root(photo, &root), slow, "{p:?} in {r:?}"); + } + } +} diff --git a/crates/engine/src/cmd/library.rs b/crates/engine/src/cmd/library.rs index 9c374942..b16aa9fa 100644 --- a/crates/engine/src/cmd/library.rs +++ b/crates/engine/src/cmd/library.rs @@ -269,7 +269,7 @@ pub fn specs() -> Vec { LibrarySource::Album(a) } "libraryFolder" => { - let path = str_param(p, "path").map(str::trim).filter(|d| !lightcraft_catalog::query::folder_key(d).is_empty()); + let path = str_param(p, "path").filter(|d| !d.trim().is_empty() && !lightcraft_catalog::query::folder_key(d).is_empty()); let path = path.ok_or_else(|| bad("library.source", "libraryFolder needs the `path` of a folder from library.folders"))?; if lightcraft_catalog::folders::is_startup_disk(path) { return Err(bad("library.source", "the startup disk's path covers every disk: choose a folder in it")); @@ -307,6 +307,9 @@ pub fn specs() -> Vec { let mut v = serde_json::to_value(&s.filter).unwrap_or_default(); lightcraft_develop::presets::deep_merge(&mut v, p); let f: Filter = serde_json::from_value(v).map_err(|e| bad("library.filter", e.to_string()))?; + let mut f = f; + // a blank folder is no folder: no hidden "filters active" state + f.library_folder = f.library_folder.filter(|d| !d.trim().is_empty()); if f.library_folder.is_some() && s.source == LibrarySource::LibraryFolder { return Err(bad("library.filter", "a folder is already shown (library.source): show another source first")); } @@ -332,7 +335,7 @@ pub fn specs() -> Vec { always, |s, p| { const C: &str = "library.removeFolder"; - let path = str_param(p, "path").map(str::trim).filter(|d| !d.is_empty()).ok_or_else(|| bad(C, "missing `path`"))?; + let path = str_param(p, "path").filter(|d| !d.trim().is_empty()).ok_or_else(|| bad(C, "missing `path`"))?; if lightcraft_catalog::query::folder_key(path).is_empty() { return Err(bad(C, format!("{path}: not a folder"))); } @@ -801,8 +804,16 @@ pub fn specs() -> Vec { let rules = if bool_or(p, "fromView", false) { view_rules(s) } else { - let base = if bool_or(p, "replace", false) { Default::default() } else { cur }; - merge_rules(&base, p.get("rules").unwrap_or(&Value::Null), "album.setRules")? + let replace = bool_or(p, "replace", false); + let folder = cur.library_folder.clone(); + let base = if replace { Default::default() } else { cur }; + let mut r = merge_rules(&base, p.get("rules").unwrap_or(&Value::Null), "album.setRules")?; + // the rules dialog has no folder field: replacing its rules keeps the folder + // unless the call says (`libraryFolder: null`) to drop it + if replace && p.get("rules").and_then(|r| r.get("libraryFolder")).is_none() { + r.library_folder = folder; + } + r }; s.commit("Edit Smart Album", Op::SetAlbumRules { id, rules: Box::new(rules) })?; Ok(json!({"count": s.catalog.album_count(id)})) diff --git a/crates/engine/src/cmd/query.rs b/crates/engine/src/cmd/query.rs index 9b0777b3..3fcb68a5 100644 --- a/crates/engine/src/cmd/query.rs +++ b/crates/engine/src/cmd/query.rs @@ -88,7 +88,7 @@ pub fn specs() -> Vec { let n = s.visible().len(); Ok(json!({ "source": s.source, - "libraryFolder": s.library_folder, + "libraryFolder": s.library_folder.as_ref().filter(|_| s.source == LibrarySource::LibraryFolder), "sourceLabel": label, "filter": s.filter, "sort": s.sort, diff --git a/crates/engine/src/lib.rs b/crates/engine/src/lib.rs index 97cda108..10fd8645 100644 --- a/crates/engine/src/lib.rs +++ b/crates/engine/src/lib.rs @@ -615,6 +615,13 @@ impl Session { f.library_folder = Some(self.library_folder.clone().unwrap_or_else(|| ".".into())); } let mut visible = self.catalog.query(&f, &self.sort); + if visible.is_empty() && self.source == LibrarySource::LibraryFolder && !self.folder_holds_photos() { + // the shown folder lost its last photo (deleted, moved, removed): everything, not + // an empty grid under the name of a folder that is gone from the sidebar + self.source = LibrarySource::All; + self.library_folder = None; + return self.visible(); + } if matches!(self.source, LibrarySource::Album(_)) && self.sort.key == lightcraft_catalog::SortKey::CaptureDate && let LibrarySource::Album(a) = self.source @@ -651,6 +658,13 @@ impl Session { &self.visible } + /// Whether the library still holds a photo imported from the folder a `LibraryFolder` source + /// shows. + fn folder_holds_photos(&self) -> bool { + let f = Filter { library_folder: self.library_folder.clone(), ..Default::default() }; + self.library_folder.is_some() && !self.catalog.query(&f, &Sort::default()).is_empty() + } + /// Photos in the current source (folder, album, …) before the filter bar and search narrow /// them; `None` where that is not a separate number (Missing Photos). pub fn source_total(&mut self) -> Option { diff --git a/crates/engine/src/library.rs b/crates/engine/src/library.rs index ef1c1e36..3d24ae24 100644 --- a/crates/engine/src/library.rs +++ b/crates/engine/src/library.rs @@ -516,7 +516,8 @@ impl Session { let view = ViewFile { source: self.source, browse: self.browse.clone(), - library_folder: self.library_folder.clone(), + // only while it is shown: a leftover would be a stale choice nobody made + library_folder: self.library_folder.clone().filter(|_| self.source == LibrarySource::LibraryFolder), sort: self.sort, selection: self.selection.clone(), }; diff --git a/crates/engine/src/tests_folders.rs b/crates/engine/src/tests_folders.rs index 5bd6a9a2..6dd5fe22 100644 --- a/crates/engine/src/tests_folders.rs +++ b/crates/engine/src/tests_folders.rs @@ -344,12 +344,13 @@ fn a_folder_source_and_a_folder_filter_never_disagree() { } #[test] -fn a_folder_source_with_no_folder_shows_nothing() { +fn a_folder_source_with_no_folder_falls_back_to_all_photos() { let mut s = session(); s.source = LibrarySource::LibraryFolder; s.library_folder = None; - assert_eq!(s.visible().len(), 0, "not everything"); - assert_eq!(s.source_total(), Some(0)); + assert_eq!(s.visible().len(), 3, "everything, not an empty grid under no name"); + assert_eq!((s.source, s.library_folder.as_deref()), (LibrarySource::All, None)); + assert_eq!(s.source_total(), Some(3)); } #[test] @@ -377,3 +378,73 @@ fn removing_a_folder_above_a_disk_needs_asking_by_name_even_with_one_disk() { assert_eq!(s.execute("library.removeFolder", &json!({"path": "/Volumes/nas/a"})).unwrap()["removed"], 1, "a folder on the disk is just a folder"); assert_eq!(s.visible().len(), 1); } + +#[test] +fn a_folder_name_with_spaces_at_its_edge_is_never_mistaken_for_its_neighbour() { + let mut s = Session::new(); + for p in ["/p/shoot /a.jpg", "/p/shoot/b.jpg", "/p/other/c.jpg"] { + add(&mut s, p); + } + assert_eq!(source_folder(&mut s, "/p/shoot ")["count"], 1); + let shown: Vec = s.visible().to_vec().iter().filter_map(|id| s.catalog.photo(*id)).map(|p| p.file_name.clone()).collect(); + assert_eq!(s.library_folder.as_deref(), Some("/p/shoot "), "kept as the tree gave it"); + assert_eq!(shown.len(), 1); + let before: Vec<_> = s.catalog.photos().filter(|p| p.deleted).map(|p| p.id).collect(); + assert!(before.is_empty()); + let r = s.execute("library.removeFolder", &json!({"path": "/p/shoot "})).unwrap(); + assert_eq!(r["removed"], 1); + let gone: Vec = s + .catalog + .photos() + .filter(|p| p.deleted) + .map(|p| match &p.source { + Source::File { path } => path.clone(), + Source::Demo { .. } => String::new(), + }) + .collect(); + assert_eq!(gone, ["/p/shoot /a.jpg"], "the folder named, not its neighbour"); +} + +#[test] +fn editing_a_smart_album_made_from_a_folder_view_keeps_the_folder() { + let mut s = session(); + source_folder(&mut s, "/pics/trip"); + let r = s.execute("album.createSmart", &json!({"name": "Trip"})).unwrap(); + let id = r["id"].as_u64().unwrap(); + // the rules dialog saves what it shows (no folder field) with `replace` + let r = s.execute("album.setRules", &json!({"id": id, "replace": true, "rules": {"ruleSet": {"rules": []}}})).unwrap(); + assert_eq!(r["count"], 2, "the folder is not something the dialog can show, so it is kept: {r}"); + let r = s.execute("album.setRules", &json!({"id": id, "replace": true, "rules": {"libraryFolder": null}})).unwrap(); + assert_eq!(r["count"], 3, "and can be dropped on purpose"); +} + +#[test] +fn a_shown_folder_that_loses_its_last_photo_gives_way_to_all_photos() { + let mut s = session(); + source_folder(&mut s, "/pics/home"); + s.execute("photo.delete", &json!({})).unwrap(); + assert_eq!(s.visible().len(), 2, "back to everything that is left"); + assert_eq!((s.source, s.library_folder.as_deref()), (LibrarySource::All, None)); + // a folder whose photos are only hidden by the filter bar stays + let mut s = session(); + source_folder(&mut s, "/pics/trip"); + s.execute("library.filter", &json!({"rating": 5})).unwrap(); + assert_eq!(s.visible().len(), 0); + assert_eq!(s.source, LibrarySource::LibraryFolder, "the filters hide them, the folder is still there"); +} + +#[test] +fn the_state_names_a_folder_only_while_it_is_shown() { + let mut s = session(); + source_folder(&mut s, "/pics/trip"); + assert_eq!(s.execute("library.state", &json!({})).unwrap()["libraryFolder"], "/pics/trip"); + s.execute("library.source", &json!({"kind": "all"})).unwrap(); + assert!(s.execute("library.state", &json!({})).unwrap()["libraryFolder"].is_null(), "not a stale leftover"); +} + +#[test] +fn a_blank_folder_filter_is_no_filter() { + let mut s = session(); + s.execute("library.filter", &json!({"libraryFolder": " "})).unwrap(); + assert_eq!(s.filter, lightcraft_catalog::Filter::default(), "no hidden 'filters active' state"); +} diff --git a/crates/ui-egui/src/panels/dialogs.rs b/crates/ui-egui/src/panels/dialogs.rs index 4e71684e..8f1660d0 100644 --- a/crates/ui-egui/src/panels/dialogs.rs +++ b/crates/ui-egui/src/panels/dialogs.rs @@ -222,7 +222,7 @@ pub fn show(app: &mut LightcraftApp, ctx: &egui::Context) { } }); } - Dialog::SmartRules { name, rules, .. } => { + Dialog::SmartRules { id, name, rules } => { let r = ui.add(egui::TextEdit::singleline(name).hint_text(crate::i18n::tr("Name")).desired_width(f32::INFINITY)); crate::widgets::register(ui.ctx(), "field:smartName", r.rect); ui.add_space(6.0); @@ -230,7 +230,9 @@ pub fn show(app: &mut LightcraftApp, ctx: &egui::Context) { crate::panels::rules_editor::edit(ui, rules, "rules", 0); }); let problems = rules.problems(); - let f = lightcraft_catalog::Filter { rule_set: Some(rules.clone()), ..Default::default() }; + // the folder an album made from a folder view carries is not in the editor, but it counts + let folder = id.and_then(|id| app.session.catalog.album(lightcraft_catalog::AlbumId(id))).and_then(|a| a.smart.as_deref().and_then(|f| f.library_folder.clone())); + let f = lightcraft_catalog::Filter { rule_set: Some(rules.clone()), library_folder: folder, ..Default::default() }; let n = if problems.is_empty() { app.session.catalog.query(&f, &Default::default()).len() } else { 0 }; ui.add_space(4.0); ui.label( diff --git a/crates/ui-egui/src/panels/left.rs b/crates/ui-egui/src/panels/left.rs index dfab9728..3e98e2d7 100644 --- a/crates/ui-egui/src/panels/left.rs +++ b/crates/ui-egui/src/panels/left.rs @@ -81,8 +81,18 @@ fn row_named( ); let mut needed = label_rect.right() - r.left() + 18.0; if let Some(n) = count.filter(|_| app.ui.show_counts) { - let c = ui.painter().text(pos2(r.right() - 18.0, r.center().y), Align2::RIGHT_CENTER, n.to_string(), t.font(12.5), t.text_dim); - needed += c.width() + 16.0; + // the count stays at the visible edge when the sidebar is scrolled sideways + let visible_right: f32 = ui.data(|d| d.get_temp(egui::Id::new("left-visible-right"))).unwrap_or(f32::MAX); + let galley = ui.painter().layout_no_wrap(n.to_string(), t.font(12.5), t.text_dim); + let right = r.right().min(visible_right) - 18.0; + let rect = Rect::from_min_size(pos2(right - galley.size().x, r.center().y - galley.size().y / 2.0), galley.size()); + if label_rect.right() + 6.0 > rect.left() { + // a long name runs under it: the count gets the row's colour behind it + ui.painter().rect_filled(rect.expand2(vec2(6.0, 2.0)), 3.0, if selected { t.canvas } else { t.chrome }); + } + ui.painter().galley(rect.min, galley, t.text_dim); + register(ui.ctx(), format!("count:{id}"), rect); + needed += rect.width() + 16.0; } note_width(ui, needed); let _ = app; @@ -129,7 +139,11 @@ pub fn show(app: &mut LightcraftApp, ui: &mut egui::Ui) { let wide = viewport.width().max(content_width(ui.ctx())); ui.set_min_width(wide); ui.set_max_width(wide); - ui.data_mut(|d| d.insert_temp(egui::Id::new("left-content-width-next"), 0.0f32)); + ui.data_mut(|d| { + d.insert_temp(egui::Id::new("left-content-width-next"), 0.0f32); + // where the visible part of the content ends (screen x), for what stays at the edge + d.insert_temp(egui::Id::new("left-visible-right"), ui.cursor().left() + viewport.max.x); + }); let src = app.session.source; for (id, icon, label, count, s) in [ ("all", Icon::Photos, "All Photos", Some(total), LibrarySource::All), diff --git a/crates/ui-egui/src/tests_panels.rs b/crates/ui-egui/src/tests_panels.rs index 0396a0f3..a8a65e62 100644 --- a/crates/ui-egui/src/tests_panels.rs +++ b/crates/ui-egui/src/tests_panels.rs @@ -235,7 +235,7 @@ fn click(h: &mut Headless, id: &str) { /// Photos imported from two folders: the sidebar's Folders section lists them, choosing one /// fills the grid with its photos, and All Photos shows everything again. #[test] -fn folders_section_lists_where_photos_were_imported_from_and_narrows_the_grid() { +fn folders_section_lists_where_photos_were_imported_from_and_fills_the_grid() { let mut h = folders_app(&["/pics/trip/a.jpg", "/pics/trip/b.jpg", "/pics/home/c.jpg"]); // the disk's row starts open; the folders inside a folder stay folded until it is opened assert!(has(&h, "source:libfolder:/pics"), "the Folders section lists the library's folders"); @@ -445,6 +445,11 @@ fn a_deep_chain_keeps_its_nesting_and_the_sidebar_scrolls_sideways() { assert!(xs.len() == 13 && xs.windows(2).all(|w| (w[1] - w[0] - 16.0).abs() < 0.5), "every level steps right by one indent: {xs:?}"); let panel = widget(&h, "panel:left_panel"); assert!(crate::panels::left::content_width(&h.view.ctx) > panel.width(), "the content is wider than the panel: the bar appears"); + assert!(widget(&h, "source:all").width() > panel.width(), "the rows are as wide as the widest one, so there is something to scroll to"); + // counts stay where you can read them, at the visible edge, however wide the rows are + for id in ["count:all", "count:picks", "count:recentlyDeleted", "count:libfolder:/a"] { + assert!(widget(&h, id).right() <= panel.right(), "{id} stays inside the panel"); + } // what a row needs is known before it is drawn off screen, so the plus stays reachable assert!(widget(&h, "icon:albumNew").right() <= panel.right(), "Create Album stays inside the visible panel"); // a click in the middle of a deep row chooses it; only the little triangle folds it @@ -457,7 +462,9 @@ fn a_deep_chain_keeps_its_nesting_and_the_sidebar_scrolls_sideways() { fn a_sidebar_that_fits_does_not_scroll_sideways() { let h = folders_app(&["/pics/trip/a.jpg", "/pics/home/b.jpg"]); let panel = widget(&h, "panel:left_panel"); - assert!(crate::panels::left::content_width(&h.view.ctx) <= panel.width(), "no horizontal bar for a shallow tree"); + let w = crate::panels::left::content_width(&h.view.ctx); + assert!(w > 0.0 && w <= panel.width(), "rows were measured and fit: no horizontal bar ({w} vs {})", panel.width()); + assert!((widget(&h, "source:all").width() - (panel.width() - 0.0)).abs() < 24.0, "rows are as wide as the panel"); } /// A long folder name is trimmed before it can run under the photo count. diff --git a/docs/parity.md b/docs/parity.md index ce312087..d8b90a3c 100644 --- a/docs/parity.md +++ b/docs/parity.md @@ -718,7 +718,7 @@ From `08-lightroom-classic-extras.md` (Classic-only features) and part 2 of `06- | LRC-LIB-CATALOG-PANEL | Catalog sets | P1 | ✅ | `cmd:library.source` (`missing`) | all, recently added, picks, recently deleted, missing photos (when any) | | LRC-LIB-CATALOG-LOCK | Catalog open in one program at a time | P1 | ✅ | `cmd:app.openLibrary`, `crates/catalog/src/lock.rs`, `crates/engine/src/library.rs` (`open_library`) | exclusive OS lock on `catalog.lock` for the session (app and `lightcraft-cli --library`); a second opener is refused with who has it (`catalog.lock.owner`: program, process, computer); released by the OS on a crash, so never stale; file systems without locks open unprotected with a warning; no read-only mode; the browser build (OPFS) has no guard across tabs yet. A library that can't be opened at launch (locked, unreadable, newer format, drive missing) shows a window — Try Again, Choose Another Library…, Continue Without Saving (then a banner; nothing written), Quit — instead of a silent demo session (`crates/ui-egui/src/panels/library_problem.rs`) | | LRC-LIB-FOLDERS | Disk folder tree | P1 | ✅ | `cmd:library.browse`, `cmd:folder.rename`, `cmd:folder.move`, `crates/ui-egui/src/panels/left.rs` (`local_section`, `folder_tree`) | standard places + the browsed folder + Browse Folder…, each an expandable tree of subfolders, breadcrumb navigation; context menu: Rename Folder…, Move Folder To…, Show in Finder (on disk, photos relinked; one undo step each: undo / redo move the folder back and forth, refused when the destination is taken) | -| LRC-LIB-LIBFOLDERS | Library folders | P1 | ✅ | `cmd:library.folders`, `cmd:library.source` (`libraryFolder`), `cmd:library.filter` (`libraryFolder`), `cmd:library.removeFolder`, `cmd:folder.rename`, `cmd:folder.move`, `crates/catalog/src/folders.rs`, `crates/ui-egui/src/panels/left.rs` (`folders_section`, `folder_menu_for_library`, `row_menu`), `crates/ui-egui/src/tests_panels.rs` | sidebar Folders: the folders the library's photos were imported from, with photo counts (subfolders included), as another way to explore the library next to By Date and Keywords; Local stays the way to browse any folder on disk. Each disk is a row of its own and starts open: a drive letter, a Windows share (`\\server\share`, `\\?\UNC\…`, `\\wsl$\…`), a macOS share or disk (`/Volumes/tokyo`), a Linux mount (`/media`, `/run/media`, `/mnt`, gvfs `smb-share:…`, `/net/`), NFS (`/Network/Servers/`), else “This Computer” — told apart from the stored paths alone, so an offline share lists like a mounted one and the sidebar never waits on a network. Only folders that hold imported photos are listed (browsed, deleted and demo photos don't count); every folder is a row of its own (`Users` → `me` → `Pictures`), and a folder that holds nothing itself and leads to one folder starts open; every level indents one step more than its parent however deep it goes: the whole sidebar (Albums, Local, By Date, Folders, Keywords) scrolls sideways when its widest row is wider than the panel (rows are measured one frame behind; Create Album stays at the visible edge); the grid title is cut with the full name on hover; click makes the folder the source, like an album or a Local folder (its photos and those of the folders inside it fill the grid, titled “photos/travel”; the filter bar narrows it further; `library.source` `libraryFolder` + `path`; kept with the library's view), and its row is always opened into view; the shown folder follows a rename or move and their undo (engine-tested; the sidebar test covers rename), is dropped back to All Photos when it is removed or no longer holds a photo, and a smart album made from the view keeps it. Only a folder that shows exactly its own photos can be shown (not the startup disk, not `/Volumes`-like folders above disks, not an empty one). A row that would cover other disks' photos too (the startup disk, `/Volumes`, `/mnt`…) only opens. Right-click (row or triangle): Rename Folder…, Move Folder To…, Show in Finder (on disk, photos relinked, as in Local) and Remove from Library… (asks; moves the folder's photos to Recently Deleted, one undo step, no file touched); a disk row offers Remove Disk from Library… (the command needs `disk: true` for a whole disk or a folder spanning several). Limits: names are compared as written (macOS `/Volumes/tokyo` vs `/Volumes/TOKYO` are two disks; `/Volumes/Macintosh HD/…` and `/…` two rows); no disk capacity / online status; no “include subfolders” switch (a folder always includes the ones inside it); creating a folder and moving photos between folders are not here | +| LRC-LIB-LIBFOLDERS | Library folders | P1 | ✅ | `cmd:library.folders`, `cmd:library.source` (`libraryFolder`), `cmd:library.filter` (`libraryFolder`), `cmd:library.removeFolder`, `cmd:folder.rename`, `cmd:folder.move`, `crates/catalog/src/folders.rs`, `crates/ui-egui/src/panels/left.rs` (`folders_section`, `folder_menu_for_library`, `row_menu`), `crates/ui-egui/src/tests_panels.rs` | sidebar Folders: the folders the library's photos were imported from, with photo counts (subfolders included), as another way to explore the library next to By Date and Keywords; Local stays the way to browse any folder on disk. Each disk is a row of its own and starts open: a drive letter, a Windows share (`\\server\share`, `\\?\UNC\…`, `\\wsl$\…`), a macOS share or disk (`/Volumes/tokyo`), a Linux mount (`/media`, `/run/media`, `/mnt`, gvfs `smb-share:…`, `/net/`), NFS (`/Network/Servers/`), else “This Computer” — told apart from the stored paths alone, so an offline share lists like a mounted one and the sidebar never waits on a network. Only folders that hold imported photos are listed (browsed, deleted and demo photos don't count); every folder is a row of its own (`Users` → `me` → `Pictures`), and a folder that holds nothing itself and leads to one folder starts open; every level indents one step more than its parent however deep it goes: the whole sidebar (Albums, Local, By Date, Folders, Keywords) scrolls sideways when its widest row is wider than the panel (rows are measured one frame behind; Create Album and the counts stay at the visible edge); the grid title is cut with the full name on hover; click makes the folder the source, like an album or a Local folder (its photos and those of the folders inside it fill the grid, titled “photos/travel”; the filter bar narrows it further; `library.source` `libraryFolder` + `path`; kept with the library's view), and its row is always opened into view; the shown folder follows a rename or move and their undo (engine-tested; the sidebar test covers rename), is dropped back to All Photos when it is removed or no longer holds a photo, and a smart album made from the view keeps it, also when its rules are edited (the editor has no folder field). Only a folder that shows exactly its own photos can be shown (not the startup disk, not `/Volumes`-like folders above disks, not an empty one). A row that would cover other disks' photos too (the startup disk, `/Volumes`, `/mnt`…) only opens. Right-click (row or triangle): Rename Folder…, Move Folder To…, Show in Finder (on disk, photos relinked, as in Local) and Remove from Library… (asks; moves the folder's photos to Recently Deleted, one undo step, no file touched); a disk row offers Remove Disk from Library… (the command needs `disk: true` for a whole disk or a folder spanning several). Limits: names are compared as written (macOS `/Volumes/tokyo` vs `/Volumes/TOKYO` are two disks; `/Volumes/Macintosh HD/…` and `/…` two rows); no disk capacity / online status; no “include subfolders” switch (a folder always includes the ones inside it); creating a folder and moving photos between folders are not here | | LRC-LIB-COLLECTIONS | Collections & sets | P1 | ✅ | `cmd:album.create`, `cmd:album.createSmart`, `cmd:album.toggleTarget`, `cmd:album.setTarget`, `cmd:album.clearQuick` | albums + folders (sets), smart albums (rule editor; rules incl. File Path — the whole string, either separator, case-insensitive), Quick Collection and target album (B in the grids adds / removes the selection; album menu ▸ Set as Target Album; the target is marked +) | | LRC-LIB-SMARTCOLL | Smart-collection rules | P1 | ✅ | `cmd:album.createSmart`, `cmd:album.setRules`, `cmd:album.ruleFields`, `cmd:dialog.smartAlbum`, `crates/catalog/src/rules.rs`, `crates/ui-egui/src/panels/rules_editor.rs` | rule editor: match all / any / none, nested groups (⌥+ or + Group), 30 fields (rating, flag, label, type, edits, keywords, any text, filename, file path, format, title, caption, camera, lens, location, creator, copyright, copyright status, capture / import / edit date, ISO, aperture, focal length, megapixels, GPS, virtual copy, album) with text / number / date / in-the-last operators; live match count; also usable as a library filter (`ruleSet`) | | LRC-LIB-PUBLISH | Publish services | P2 | ⬜ | | | From 46c4a2709d92a996d4f5ba1f7846897712f08775 Mon Sep 17 00:00:00 2001 From: Nathaniel Ledford Date: Thu, 8 Oct 2026 09:06:13 -0400 Subject: [PATCH 13/41] Sidebar: names end in an ellipsis at the count; bars end at the panel edge A name that does not fit is cut with an ellipsis before the photo count (the full name on hover) instead of running past it; scrolling the sidebar sideways or widening it shows more. A row asks for room for at most 140 pt of its name, so depth, not one long name, makes the sidebar scroll. Selection and hover bars end at the panel's edge when the content is wider. Co-Authored-By: Claude Sonnet 5.5 --- crates/ui-egui/src/panels/left.rs | 50 +++++++++++++++----------- crates/ui-egui/src/tests_panels.rs | 56 ++++++++++++++++++++++++++++++ docs/parity.md | 2 +- 3 files changed, 86 insertions(+), 22 deletions(-) diff --git a/crates/ui-egui/src/panels/left.rs b/crates/ui-egui/src/panels/left.rs index 3e98e2d7..253ca4d6 100644 --- a/crates/ui-egui/src/panels/left.rs +++ b/crates/ui-egui/src/panels/left.rs @@ -17,6 +17,9 @@ pub(crate) fn content_width(ctx: &egui::Context) -> f32 { ctx.data(|d| d.get_temp::(egui::Id::new("left-content-width"))).unwrap_or(0.0) } +/// The most a row asks for its name: longer ones are cut when there is no more room. +const MAX_NAME_NEED: f32 = 140.0; + /// A row says how wide it needs to be. fn note_width(ui: &egui::Ui, w: f32) { ui.data_mut(|d| { @@ -60,9 +63,14 @@ fn row_named( None => said.to_string(), }; resp.widget_info(|| egui::WidgetInfo::selected(egui::WidgetType::SelectableLabel, true, selected, &name)); - let inner = r.shrink2(vec2(8.0, 0.0)); + // what is visible of the row: when the sidebar is scrolled sideways its bars and counts end at + // the panel's edge, not at the end of the (wider) content + let visible_right: f32 = ui.data(|d| d.get_temp(egui::Id::new("left-visible-right"))).unwrap_or(f32::MAX); + let edge = r.right().min(visible_right); + let inner = Rect::from_min_max(r.min + vec2(8.0, 0.0), pos2((r.right() - 8.0).min(edge - 8.0).max(r.left() + 8.0), r.bottom())); if selected { ui.painter().rect_filled(inner, 4.0, t.canvas); + register(ui.ctx(), format!("highlight:{id}"), inner); } else if resp.hovered() { ui.painter().rect_filled(inner, 4.0, t.hover.gamma_multiply(0.6)); } @@ -72,27 +80,27 @@ fn row_named( icon, if selected { t.text } else { t.icon }, ); - let label_rect = ui.painter().text( - pos2(r.left() + 42.0 + indent, r.center().y), - Align2::LEFT_CENTER, - label, - t.font(13.5), - if selected { t.text } else { t.text_label }, - ); - let mut needed = label_rect.right() - r.left() + 18.0; - if let Some(n) = count.filter(|_| app.ui.show_counts) { - // the count stays at the visible edge when the sidebar is scrolled sideways - let visible_right: f32 = ui.data(|d| d.get_temp(egui::Id::new("left-visible-right"))).unwrap_or(f32::MAX); - let galley = ui.painter().layout_no_wrap(n.to_string(), t.font(12.5), t.text_dim); - let right = r.right().min(visible_right) - 18.0; - let rect = Rect::from_min_size(pos2(right - galley.size().x, r.center().y - galley.size().y / 2.0), galley.size()); - if label_rect.right() + 6.0 > rect.left() { - // a long name runs under it: the count gets the row's colour behind it - ui.painter().rect_filled(rect.expand2(vec2(6.0, 2.0)), 3.0, if selected { t.canvas } else { t.chrome }); - } - ui.painter().galley(rect.min, galley, t.text_dim); - register(ui.ctx(), format!("count:{id}"), rect); + let font = t.font(13.5); + let color = if selected { t.text } else { t.text_label }; + let count_galley = count.filter(|_| app.ui.show_counts).map(|n| ui.painter().layout_no_wrap(n.to_string(), t.font(12.5), t.text_dim)); + let label_left = r.left() + 42.0 + indent; + let count_left = count_galley.as_ref().map_or(edge - 18.0, |g| edge - 18.0 - g.size().x); + // the name gives way to the count: cut with an ellipsis, in full on hover + let room = count_left - 8.0 - label_left; + let measure = |s: &str| ui.painter().layout_no_wrap(s.to_string(), font.clone(), color).size().x; + let full_w = measure(label); + let shown = if full_w <= room { label.to_string() } else { crate::widgets::elide_head(label, room.max(0.0), measure) }; + let label_rect = ui.painter().text(pos2(label_left, r.center().y), Align2::LEFT_CENTER, &shown, font.clone(), color); + register(ui.ctx(), format!("label:{id}"), label_rect); + let resp = if shown != label { resp.on_hover_text(label) } else { resp }; + // a row asks for room for its name up to a share of a panel, so one very long name does not + // make everything scroll: depth does that + let mut needed = 42.0 + indent + full_w.min(MAX_NAME_NEED) + 18.0; + if let Some(galley) = count_galley { + let rect = Rect::from_min_size(pos2(edge - 18.0 - galley.size().x, r.center().y - galley.size().y / 2.0), galley.size()); needed += rect.width() + 16.0; + register(ui.ctx(), format!("count:{id}"), rect); + ui.painter().galley(rect.min, galley, t.text_dim); } note_width(ui, needed); let _ = app; diff --git a/crates/ui-egui/src/tests_panels.rs b/crates/ui-egui/src/tests_panels.rs index a8a65e62..ecb5cb5c 100644 --- a/crates/ui-egui/src/tests_panels.rs +++ b/crates/ui-egui/src/tests_panels.rs @@ -479,3 +479,59 @@ fn a_long_folder_name_never_runs_under_the_photo_count() { let (title, count) = (widget(&h, "grid:title"), widget(&h, "grid:count")); assert!(title.right() + 8.0 <= count.left(), "title {title:?} vs count {count:?}"); } + +fn tooltip_shown(h: &Headless) -> bool { + h.view.ctx.memory(|m| m.areas().visible_layer_ids().into_iter().any(|l| l.order == egui::Order::Tooltip)) +} + +const LONG_NAME: &str = "Aliah Ira Polanco-Grylls and a name much longer than any sidebar row can show"; + +/// A name never runs past the photo count: it is cut with an ellipsis, in full on hover. +#[test] +fn a_long_name_stops_at_the_count_and_shows_in_full_on_hover() { + let mut h = folders_app(&[&format!("/pics/{LONG_NAME}/a.jpg"), "/pics/short/b.jpg"]); + click(&mut h, "libraryFolderToggle:/pics"); + let (long, short) = (format!("/pics/{LONG_NAME}"), "/pics/short".to_string()); + let (label, count) = (widget(&h, &format!("label:libfolder:{long}")), widget(&h, &format!("count:libfolder:{long}"))); + assert!(label.right() + 4.0 <= count.left(), "label {label:?} stays left of the count {count:?}"); + let short_label = widget(&h, &format!("label:libfolder:{short}")); + assert!(short_label.width() < label.width(), "a name that fits is not cut"); + // hovering the cut one shows the whole name; a name that fits needs no tooltip of its own + let c = widget(&h, &format!("source:libfolder:{long}")).center(); + let r = h.request("ui.move", json!({"x": c.x, "y": c.y}), T); + assert_eq!(r["ok"], true, "{r}"); + assert!(h.step_until(Duration::from_secs(5), tooltip_shown), "the full name appears on hover"); +} + +/// More room shows more of the name. +#[test] +fn widening_the_sidebar_shows_more_of_a_long_name() { + let mut h = folders_app(&[&format!("/pics/{LONG_NAME}/a.jpg"), "/pics/short/b.jpg"]); + click(&mut h, "libraryFolderToggle:/pics"); + let id = format!("label:libfolder:/pics/{LONG_NAME}"); + let narrow = widget(&h, &id).width(); + let r = h.request("ui.set", json!({"leftWidth": 480.0}), T); + assert_eq!(r["ok"], true, "{r}"); + h.settle(SETTLE); + h.step(); + assert!(widget(&h, &id).width() > narrow + 100.0, "a wider panel shows more of it"); +} + +/// A long name alone does not make the sidebar scroll sideways: depth does. +#[test] +fn a_long_name_alone_does_not_make_the_sidebar_scroll() { + let h = folders_app(&[&format!("/{LONG_NAME}/a.jpg")]); + let panel = widget(&h, "panel:left_panel"); + assert!(crate::panels::left::content_width(&h.view.ctx) <= panel.width(), "a name takes at most what a row can show"); +} + +/// The selection bar ends at the panel's edge, not past it, when the content is wider. +#[test] +fn the_selection_bar_stays_inside_the_panel() { + let deep = "/a/b/c/d/e/f/g/h/i/j/k/l/m"; + let mut h = folders_app(&[&format!("{deep}/x/1.jpg"), &format!("{deep}/y/2.jpg")]); + click(&mut h, "source:libfolder:/a/b/c/d/e/f/g/h/i/j/k/l"); + let panel = widget(&h, "panel:left_panel"); + let bar = widget(&h, "highlight:libfolder:/a/b/c/d/e/f/g/h/i/j/k/l"); + assert!(bar.right() <= panel.right(), "the bar {bar:?} ends inside the panel {panel:?}"); +} diff --git a/docs/parity.md b/docs/parity.md index d8b90a3c..2a524c43 100644 --- a/docs/parity.md +++ b/docs/parity.md @@ -718,7 +718,7 @@ From `08-lightroom-classic-extras.md` (Classic-only features) and part 2 of `06- | LRC-LIB-CATALOG-PANEL | Catalog sets | P1 | ✅ | `cmd:library.source` (`missing`) | all, recently added, picks, recently deleted, missing photos (when any) | | LRC-LIB-CATALOG-LOCK | Catalog open in one program at a time | P1 | ✅ | `cmd:app.openLibrary`, `crates/catalog/src/lock.rs`, `crates/engine/src/library.rs` (`open_library`) | exclusive OS lock on `catalog.lock` for the session (app and `lightcraft-cli --library`); a second opener is refused with who has it (`catalog.lock.owner`: program, process, computer); released by the OS on a crash, so never stale; file systems without locks open unprotected with a warning; no read-only mode; the browser build (OPFS) has no guard across tabs yet. A library that can't be opened at launch (locked, unreadable, newer format, drive missing) shows a window — Try Again, Choose Another Library…, Continue Without Saving (then a banner; nothing written), Quit — instead of a silent demo session (`crates/ui-egui/src/panels/library_problem.rs`) | | LRC-LIB-FOLDERS | Disk folder tree | P1 | ✅ | `cmd:library.browse`, `cmd:folder.rename`, `cmd:folder.move`, `crates/ui-egui/src/panels/left.rs` (`local_section`, `folder_tree`) | standard places + the browsed folder + Browse Folder…, each an expandable tree of subfolders, breadcrumb navigation; context menu: Rename Folder…, Move Folder To…, Show in Finder (on disk, photos relinked; one undo step each: undo / redo move the folder back and forth, refused when the destination is taken) | -| LRC-LIB-LIBFOLDERS | Library folders | P1 | ✅ | `cmd:library.folders`, `cmd:library.source` (`libraryFolder`), `cmd:library.filter` (`libraryFolder`), `cmd:library.removeFolder`, `cmd:folder.rename`, `cmd:folder.move`, `crates/catalog/src/folders.rs`, `crates/ui-egui/src/panels/left.rs` (`folders_section`, `folder_menu_for_library`, `row_menu`), `crates/ui-egui/src/tests_panels.rs` | sidebar Folders: the folders the library's photos were imported from, with photo counts (subfolders included), as another way to explore the library next to By Date and Keywords; Local stays the way to browse any folder on disk. Each disk is a row of its own and starts open: a drive letter, a Windows share (`\\server\share`, `\\?\UNC\…`, `\\wsl$\…`), a macOS share or disk (`/Volumes/tokyo`), a Linux mount (`/media`, `/run/media`, `/mnt`, gvfs `smb-share:…`, `/net/`), NFS (`/Network/Servers/`), else “This Computer” — told apart from the stored paths alone, so an offline share lists like a mounted one and the sidebar never waits on a network. Only folders that hold imported photos are listed (browsed, deleted and demo photos don't count); every folder is a row of its own (`Users` → `me` → `Pictures`), and a folder that holds nothing itself and leads to one folder starts open; every level indents one step more than its parent however deep it goes: the whole sidebar (Albums, Local, By Date, Folders, Keywords) scrolls sideways when its widest row is wider than the panel (rows are measured one frame behind; Create Album and the counts stay at the visible edge); the grid title is cut with the full name on hover; click makes the folder the source, like an album or a Local folder (its photos and those of the folders inside it fill the grid, titled “photos/travel”; the filter bar narrows it further; `library.source` `libraryFolder` + `path`; kept with the library's view), and its row is always opened into view; the shown folder follows a rename or move and their undo (engine-tested; the sidebar test covers rename), is dropped back to All Photos when it is removed or no longer holds a photo, and a smart album made from the view keeps it, also when its rules are edited (the editor has no folder field). Only a folder that shows exactly its own photos can be shown (not the startup disk, not `/Volumes`-like folders above disks, not an empty one). A row that would cover other disks' photos too (the startup disk, `/Volumes`, `/mnt`…) only opens. Right-click (row or triangle): Rename Folder…, Move Folder To…, Show in Finder (on disk, photos relinked, as in Local) and Remove from Library… (asks; moves the folder's photos to Recently Deleted, one undo step, no file touched); a disk row offers Remove Disk from Library… (the command needs `disk: true` for a whole disk or a folder spanning several). Limits: names are compared as written (macOS `/Volumes/tokyo` vs `/Volumes/TOKYO` are two disks; `/Volumes/Macintosh HD/…` and `/…` two rows); no disk capacity / online status; no “include subfolders” switch (a folder always includes the ones inside it); creating a folder and moving photos between folders are not here | +| LRC-LIB-LIBFOLDERS | Library folders | P1 | ✅ | `cmd:library.folders`, `cmd:library.source` (`libraryFolder`), `cmd:library.filter` (`libraryFolder`), `cmd:library.removeFolder`, `cmd:folder.rename`, `cmd:folder.move`, `crates/catalog/src/folders.rs`, `crates/ui-egui/src/panels/left.rs` (`folders_section`, `folder_menu_for_library`, `row_menu`), `crates/ui-egui/src/tests_panels.rs` | sidebar Folders: the folders the library's photos were imported from, with photo counts (subfolders included), as another way to explore the library next to By Date and Keywords; Local stays the way to browse any folder on disk. Each disk is a row of its own and starts open: a drive letter, a Windows share (`\\server\share`, `\\?\UNC\…`, `\\wsl$\…`), a macOS share or disk (`/Volumes/tokyo`), a Linux mount (`/media`, `/run/media`, `/mnt`, gvfs `smb-share:…`, `/net/`), NFS (`/Network/Servers/`), else “This Computer” — told apart from the stored paths alone, so an offline share lists like a mounted one and the sidebar never waits on a network. Only folders that hold imported photos are listed (browsed, deleted and demo photos don't count); every folder is a row of its own (`Users` → `me` → `Pictures`), and a folder that holds nothing itself and leads to one folder starts open; every level indents one step more than its parent however deep it goes: the whole sidebar (Albums, Local, By Date, Folders, Keywords) scrolls sideways when its widest row is wider than the panel (rows are measured one frame behind; Create Album and the counts stay at the visible edge); a name that does not fit ends in an ellipsis at the count (the full name on hover; a row asks for room for at most 140 pt of its name, so depth, not one long name, makes the sidebar scroll), as does the grid title; selection and hover bars end at the panel's edge; click makes the folder the source, like an album or a Local folder (its photos and those of the folders inside it fill the grid, titled “photos/travel”; the filter bar narrows it further; `library.source` `libraryFolder` + `path`; kept with the library's view), and its row is always opened into view; the shown folder follows a rename or move and their undo (engine-tested; the sidebar test covers rename), is dropped back to All Photos when it is removed or no longer holds a photo, and a smart album made from the view keeps it, also when its rules are edited (the editor has no folder field). Only a folder that shows exactly its own photos can be shown (not the startup disk, not `/Volumes`-like folders above disks, not an empty one). A row that would cover other disks' photos too (the startup disk, `/Volumes`, `/mnt`…) only opens. Right-click (row or triangle): Rename Folder…, Move Folder To…, Show in Finder (on disk, photos relinked, as in Local) and Remove from Library… (asks; moves the folder's photos to Recently Deleted, one undo step, no file touched); a disk row offers Remove Disk from Library… (the command needs `disk: true` for a whole disk or a folder spanning several). Limits: names are compared as written (macOS `/Volumes/tokyo` vs `/Volumes/TOKYO` are two disks; `/Volumes/Macintosh HD/…` and `/…` two rows); no disk capacity / online status; no “include subfolders” switch (a folder always includes the ones inside it); creating a folder and moving photos between folders are not here | | LRC-LIB-COLLECTIONS | Collections & sets | P1 | ✅ | `cmd:album.create`, `cmd:album.createSmart`, `cmd:album.toggleTarget`, `cmd:album.setTarget`, `cmd:album.clearQuick` | albums + folders (sets), smart albums (rule editor; rules incl. File Path — the whole string, either separator, case-insensitive), Quick Collection and target album (B in the grids adds / removes the selection; album menu ▸ Set as Target Album; the target is marked +) | | LRC-LIB-SMARTCOLL | Smart-collection rules | P1 | ✅ | `cmd:album.createSmart`, `cmd:album.setRules`, `cmd:album.ruleFields`, `cmd:dialog.smartAlbum`, `crates/catalog/src/rules.rs`, `crates/ui-egui/src/panels/rules_editor.rs` | rule editor: match all / any / none, nested groups (⌥+ or + Group), 30 fields (rating, flag, label, type, edits, keywords, any text, filename, file path, format, title, caption, camera, lens, location, creator, copyright, copyright status, capture / import / edit date, ISO, aperture, focal length, megapixels, GPS, virtual copy, album) with text / number / date / in-the-last operators; live match count; also usable as a library filter (`ruleSet`) | | LRC-LIB-PUBLISH | Publish services | P2 | ⬜ | | | From 57c9e252c1d8bd3f8bf7f408c0b03af79b4aa058 Mon Sep 17 00:00:00 2001 From: Hanmo Date: Thu, 8 Oct 2026 21:25:02 +0800 Subject: [PATCH 14/41] M1: show bottom confirmation for colour label actions --- crates/ui-egui/locales/ja.json | 1 + crates/ui-egui/locales/zh-hans.json | 1 + crates/ui-egui/locales/zh-hant.json | 1 + crates/ui-egui/src/lib.rs | 17 +++++++++++++++- crates/ui-egui/src/shortcuts.rs | 31 +++++++++++++++++++++++++++++ docs/library-shortcuts.md | 2 ++ docs/parity.md | 2 +- docs/whats-new.md | 1 + 8 files changed, 54 insertions(+), 2 deletions(-) diff --git a/crates/ui-egui/locales/ja.json b/crates/ui-egui/locales/ja.json index f131d0b4..eaab8398 100644 --- a/crates/ui-egui/locales/ja.json +++ b/crates/ui-egui/locales/ja.json @@ -147,6 +147,7 @@ "Cloudy": "曇り", "Collapse All Stacks": "すべてのスタックを閉じる", "Color": "カラー", + "Color label cleared": "カラーラベルを解除しました", "Color Grading": "カラーグレーディング", "Color Mixer": "カラーミキサー", "Color Noise Reduction": "カラーノイズ軽減", diff --git a/crates/ui-egui/locales/zh-hans.json b/crates/ui-egui/locales/zh-hans.json index 7dc4d7c3..9f1c6262 100644 --- a/crates/ui-egui/locales/zh-hans.json +++ b/crates/ui-egui/locales/zh-hans.json @@ -147,6 +147,7 @@ "Cloudy": "阴天", "Collapse All Stacks": "折叠所有堆叠", "Color": "颜色", + "Color label cleared": "已清除颜色标签", "Color Grading": "颜色分级", "Color Mixer": "颜色混合器", "Color Noise Reduction": "颜色降噪", diff --git a/crates/ui-egui/locales/zh-hant.json b/crates/ui-egui/locales/zh-hant.json index 79c95291..2af33327 100644 --- a/crates/ui-egui/locales/zh-hant.json +++ b/crates/ui-egui/locales/zh-hant.json @@ -187,6 +187,7 @@ "Cloudy": "陰天", "Collapse All Stacks": "收合所有堆疊", "Color": "色彩", + "Color label cleared": "已清除色彩標籤", "Color Grading": "色彩分級", "Color Label Names": "色彩標籤名稱", "Color Label Sets": "色彩標籤集", diff --git a/crates/ui-egui/src/lib.rs b/crates/ui-egui/src/lib.rs index a4addbf3..e46e8663 100644 --- a/crates/ui-egui/src/lib.rs +++ b/crates/ui-egui/src/lib.rs @@ -52,6 +52,8 @@ use serde_json::Value; pub use control::{ControlRequest, ControlResponse}; pub use state::UiState; +const TOAST_SECONDS: f64 = 1.4; + pub type PickFiles = Box Vec>; /// A save dialog: suggested file name → chosen path (`None` = cancelled). pub type SaveFile = Box Option>; @@ -265,6 +267,19 @@ impl LightcraftApp { return r; } let r = self.session.execute(id, ¶ms).map_err(|e| e.to_string()); + if r.is_ok() && id == "photo.label" { + let text = match params.get("label").and_then(Value::as_str).and_then(lightcraft_catalog::ColorLabel::parse) { + Some(label) => { + let name = + self.session.catalog.custom_label_name(label).map(str::to_owned).unwrap_or_else(|| i18n::tr(&format!("{label:?}")).into()); + i18n::tr_format!("{name} Label", name = name) + } + None => i18n::tr("Color label cleared").into(), + }; + // Native menu clicks can arrive before logic() updates last_time after an idle gap. + let now = self.tasks.repaint.as_ref().map(|ctx| ctx.input(|i| i.time)).unwrap_or(self.last_time); + self.ui.toast = Some((text, now + TOAST_SECONDS)); + } if let Err(e) = &r { log::warn!("{id}: {e}"); self.ui.status = e.clone(); @@ -351,7 +366,7 @@ impl LightcraftApp { } pub fn toast(&mut self, ctx: &egui::Context, text: impl Into) { - self.toast_for(ctx, text, 1.4); + self.toast_for(ctx, text, TOAST_SECONDS); } /// A toast that stays `secs` seconds (messages that say where to look or what to do next). diff --git a/crates/ui-egui/src/shortcuts.rs b/crates/ui-egui/src/shortcuts.rs index 2a501664..8c47e191 100644 --- a/crates/ui-egui/src/shortcuts.rs +++ b/crates/ui-egui/src/shortcuts.rs @@ -424,6 +424,37 @@ mod tests { } } + #[test] + fn color_label_actions_show_feedback_only_after_success() { + use lightcraft_catalog::PhotoId; + let mut h = library(); + for shift in [false, true] { + for (k, name) in [("6", "Red"), ("7", "Yellow"), ("8", "Green"), ("9", "Blue")] { + h.app.session.execute("library.select", &json!({"ids": [1]})).unwrap(); + h.app.ui.toast = None; + key(&mut h, k, shift); + assert_eq!(h.app.ui.toast.as_ref().map(|t| t.0.as_str()), Some(format!("{name} Label").as_str())); + assert_eq!(h.app.session.active(), Some(PhotoId(if shift { 2 } else { 1 }))); + } + } + // Menus and the Info-panel swatches dispatch the same command, including purple/clear. + h.app.run("photo.label", json!({"label": "purple"})).unwrap(); + assert_eq!(h.app.ui.toast.as_ref().map(|t| t.0.as_str()), Some("Purple Label")); + h.app.run("photo.label", json!({"label": "none"})).unwrap(); + assert_eq!(h.app.ui.toast.as_ref().map(|t| t.0.as_str()), Some("Color label cleared")); + h.app.session.execute("label.setNames", &json!({"names": {"red": "Needs review"}})).unwrap(); + key(&mut h, "6", false); + assert_eq!(h.app.ui.toast.as_ref().map(|t| t.0.as_str()), Some("Needs review Label")); + // Native menu callbacks run before logic() catches up to the current frame's clock. + let now = h.view.ctx.input(|i| i.time); + h.app.last_time = now - 10.0; + h.app.run("photo.label", json!({"label": "blue"})).unwrap(); + assert!(h.app.ui.toast.as_ref().is_some_and(|t| t.1 > now), "menu feedback must survive an idle gap"); + h.app.ui.toast = None; + assert!(h.app.run("photo.label", json!({"label": "invalid"})).is_err()); + assert!(h.app.ui.toast.is_none(), "failed actions must not announce success"); + } + #[test] fn shifted_number_punctuation_rates_and_advances() { use lightcraft_catalog::PhotoId; diff --git a/docs/library-shortcuts.md b/docs/library-shortcuts.md index 8ef88291..4adddd51 100644 --- a/docs/library-shortcuts.md +++ b/docs/library-shortcuts.md @@ -15,6 +15,8 @@ Select photos in Photo Grid or Square Grid, then use: Purple and clearing a colour label are available in Photo → Set Color Label and the photo context menu. Purple has no default number key, matching Lightroom Classic. Rejection marks a photo for culling; it does not delete the original or remove it from the library. +Setting a colour label shows the same brief bottom toast as a rating, using the colour or your +custom label name. Clearing it shows a confirmation too, whether applied by a key, menu or swatch. Grid actions apply to all selected photos and support Undo / Redo. In Compare and Survey, culling actions apply to the active photo. Auto Advance moves forward after an action; holding Shift with diff --git a/docs/parity.md b/docs/parity.md index 3950286e..4fbd4184 100644 --- a/docs/parity.md +++ b/docs/parity.md @@ -151,7 +151,7 @@ under the UI command that wraps it (a no-op error); the UI command now wins. | LR-LIB-TARGET | Target album | P2 | ✅ | `cmd:album.setTarget`, `cmd:album.toggleTarget` | see LRC-LIB-COLLECTIONS | | LR-LIB-RATING | Star ratings | P0 | ✅ | `cmd:photo.rate` (`advance`), `crates/ui-egui/src/shortcuts.rs` | 0 clears; 1–5 set; Shift+0–5 applies and advances once, including with Auto Advance. Batch grid and undo/redo regressions; macOS native ownership corrected (PR #261) | | LR-LIB-FLAG | Pick / reject flags | P0 | ✅ | `cmd:photo.pick`, `cmd:photo.reject`, `cmd:photo.unflag`, `cmd:photo.flag` | P pick, X reject, U clear; Shift+P picks and advances in both grids (Presets elsewhere); Shift+X/U and legacy Shift+Z advance; no flag cycle; rejection does not delete the file | -| LR-LIB-LABEL | Colour labels | P1 | ✅ | `cmd:photo.label`, `cmd:label.setNames`, `cmd:label.names`, `cmd:dialog.labelNames`, keys 6–9 in `crates/ui-egui/src/shortcuts.rs` | Photo menu and grid context menu (coloured, named), Info-panel swatches, label dot in grid cells; editable label names (undoable, journaled); no purple key (as in Lightroom), no custom extra labels | +| LR-LIB-LABEL | Colour labels | P1 | ✅ | `cmd:photo.label`, `cmd:label.setNames`, `cmd:label.names`, `cmd:dialog.labelNames`, keys 6–9 in `crates/ui-egui/src/shortcuts.rs` | Photo menu and grid context menu (coloured, named), Info-panel swatches, label dot in grid cells; setting/clearing through keys (including Shift), menus or Info swatches shows the same bottom toast as ratings, with the colour/custom name; editable label names (undoable, journaled); no purple key (as in Lightroom), no custom extra labels | | LR-LIB-KEYWORD | Keywords | P0 | ✅ | `cmd:panel.keywords`, `cmd:photo.setMeta` (`addKeywords`/`removeKeywords`), `cmd:keyword.list`, `cmd:keyword.rename`, `cmd:keyword.delete`, `cmd:keyword.merge`, `cmd:keyword.suggest`, `crates/catalog/src/keywords.rs`, `crates/ui-egui/src/panels/left.rs` | left-panel keyword tree with counts (click filters, children included; context menu rename / merge / delete / add / remove); library-wide ops are one undo step and replay from the op log; suggestions (co-occurring / most used / completions) in the Keywords panel; no keyword drag-and-drop | | LR-LIB-PEOPLE | People / faces | P2 | 🟡 | `cmd:view.people`, `cmd:view.faceBoxes`, `cmd:photo.removeRegion`, `cmd:photo.setRegion`, `cmd:library.filter`, `crates/ui-egui/src/panels/people.rs`, `crates/catalog/src/query.rs` (`people`), `crates/engine/src/media.rs` (`face_job`), `crates/meta/src/xmp.rs` | MVP: named face regions read from XMP (MWG-RS, read-only, see `docs/xmp-interop.md`; clipped to the photo; Lightroom's `Rotation` of ±π/2 and π handled, mirrored orientations taken as written; boxes follow Rotate Left/Right and flips; a sidecar stating `mwg-rs:Regions`, even empty, replaces the photo's on re-read, one without leaves them) become cards in the People view (View ▸ People, or the People button in the toolbar): a close-up of the person's largest face, name and photo count; a click filters the grid (`person` in `library.filter`, `person:` search token, filter chip). Face boxes in the loupe can be switched off (View ▸ Face Boxes, toolbar) resized with eight drag handles and removed with the × shown on hover (catalog-only and undoable, one undo step per drag; the sidecar is never rewritten for either); no moving a box by dragging it yet. No face detection, no unnamed people / suggestions, no manual tagging, no writing regions back | | LR-LIB-STACK | Stacks | P1 | ✅ | `cmd:stack.group`, `cmd:stack.ungroup`, `cmd:stack.toggle`, `cmd:stack.setTop`, `cmd:stack.remove`, `cmd:stack.auto`, `crates/catalog/src/stacks.rs` | grid/filmstrip count badges, expand/collapse, auto-stack by capture time; no visual-similarity auto-stack | diff --git a/docs/whats-new.md b/docs/whats-new.md index 7293ffd9..e01893cf 100644 --- a/docs/whats-new.md +++ b/docs/whats-new.md @@ -3,6 +3,7 @@ ## October 2026 ### Library keyboard culling +- Setting or clearing a colour label shows a brief bottom confirmation, like rating a photo; custom label names appear in the message too. - On macOS, ratings `0–5`, labels `6–9` and pick/unflag `P/U` now reach the app even when shown in the native menu (issue #283; adapted from PR #261). - `Shift+6–9` labels and advances. `Shift+P` picks and advances in Photo Grid and Square Grid; it opens Presets in other views. With Auto Advance on, Shift still moves only once. - Help → Keyboard Shortcuts includes the number-key bindings. See [library shortcuts](library-shortcuts.md). From 4256865cf49624d2b33874831519ab5d0a2fea6a Mon Sep 17 00:00:00 2001 From: Hanmo Date: Thu, 8 Oct 2026 21:42:21 +0800 Subject: [PATCH 15/41] M1: tint labelled thumbnails and colour label feedback --- apps/lightcraft-web/src/web.rs | 2 +- crates/ui-egui/src/lib.rs | 13 +++-- crates/ui-egui/src/merge.rs | 2 +- crates/ui-egui/src/panels/detail.rs | 12 ++-- crates/ui-egui/src/panels/filterbar.rs | 10 +--- crates/ui-egui/src/panels/grid.rs | 12 +++- crates/ui-egui/src/panels/mod.rs | 10 ++-- crates/ui-egui/src/shortcuts.rs | 2 + crates/ui-egui/src/state.rs | 4 +- crates/ui-egui/src/tests_labels.rs | 80 ++++++++++++++++++++++++++ crates/ui-egui/src/theme.rs | 22 +++++++ docs/library-shortcuts.md | 4 ++ docs/parity.md | 6 +- docs/whats-new.md | 1 + 14 files changed, 149 insertions(+), 31 deletions(-) create mode 100644 crates/ui-egui/src/tests_labels.rs diff --git a/apps/lightcraft-web/src/web.rs b/apps/lightcraft-web/src/web.rs index 6d0483a9..3201bf83 100644 --- a/apps/lightcraft-web/src/web.rs +++ b/apps/lightcraft-web/src/web.rs @@ -600,7 +600,7 @@ impl eframe::App for WebApp { fn logic(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) { for text in safety::take_notices() { let now = ctx.input(|i| i.time); - self.app.ui.toast = Some((text, now + NOTICE_SECS)); + self.app.ui.toast = Some((text, now + NOTICE_SECS, None)); } self.run_inbox(); self.import_dropped(ctx); diff --git a/crates/ui-egui/src/lib.rs b/crates/ui-egui/src/lib.rs index e46e8663..2b15b418 100644 --- a/crates/ui-egui/src/lib.rs +++ b/crates/ui-egui/src/lib.rs @@ -30,6 +30,8 @@ mod tests_curve; #[cfg(test)] mod tests_grid; #[cfg(test)] +mod tests_labels; +#[cfg(test)] mod tests_library_problem; #[cfg(test)] mod tests_masking; @@ -268,7 +270,8 @@ impl LightcraftApp { } let r = self.session.execute(id, ¶ms).map_err(|e| e.to_string()); if r.is_ok() && id == "photo.label" { - let text = match params.get("label").and_then(Value::as_str).and_then(lightcraft_catalog::ColorLabel::parse) { + let label = params.get("label").and_then(Value::as_str).and_then(lightcraft_catalog::ColorLabel::parse); + let text = match label { Some(label) => { let name = self.session.catalog.custom_label_name(label).map(str::to_owned).unwrap_or_else(|| i18n::tr(&format!("{label:?}")).into()); @@ -278,7 +281,7 @@ impl LightcraftApp { }; // Native menu clicks can arrive before logic() updates last_time after an idle gap. let now = self.tasks.repaint.as_ref().map(|ctx| ctx.input(|i| i.time)).unwrap_or(self.last_time); - self.ui.toast = Some((text, now + TOAST_SECONDS)); + self.ui.toast = Some((text, now + TOAST_SECONDS, label)); } if let Err(e) = &r { log::warn!("{id}: {e}"); @@ -354,7 +357,7 @@ impl LightcraftApp { self.ui.unsaved_seen = true; let t = ctx.input(|i| i.time); let what = if n == 1 { "1 change".to_string() } else { format!("{n} changes") }; - self.ui.toast = Some((format!("{what} saved in memory but not written to disk: {e} — LightCraft will retry"), t + 6.0)); + self.ui.toast = Some((format!("{what} saved in memory but not written to disk: {e} — LightCraft will retry"), t + 6.0, None)); } (None, true) => { self.ui.unsaved_seen = false; @@ -372,7 +375,7 @@ impl LightcraftApp { /// A toast that stays `secs` seconds (messages that say where to look or what to do next). pub fn toast_for(&mut self, ctx: &egui::Context, text: impl Into, secs: f64) { let t = ctx.input(|i| i.time); - self.ui.toast = Some((text.into(), t + secs)); + self.ui.toast = Some((text.into(), t + secs, None)); } /// AI masks: apply finished background requests (clicks, descriptions, detail passes) and @@ -450,7 +453,7 @@ impl LightcraftApp { /// A toast for an error the user has to read and act on (stays 6 s). pub fn toast_error(&mut self, ctx: &egui::Context, text: impl Into) { let t = ctx.input(|i| i.time); - self.ui.toast = Some((text.into(), t + 6.0)); + self.ui.toast = Some((text.into(), t + 6.0, None)); } fn drain_control(&mut self, ctx: &egui::Context) { diff --git a/crates/ui-egui/src/merge.rs b/crates/ui-egui/src/merge.rs index bbb5252a..cf9efd17 100644 --- a/crates/ui-egui/src/merge.rs +++ b/crates/ui-egui/src/merge.rs @@ -268,7 +268,7 @@ pub fn poll(app: &mut LightcraftApp, ctx: &egui::Context) { Err(_) => { let (f, stage) = t.progress.lock().map(|g| g.clone()).unwrap_or_default(); let now = ctx.input(|i| i.time); - app.ui.toast = Some((crate::i18n::tr_format!("Merging… {stage} {:.0}%", f * 100.0, stage = stage), now + 0.5)); + app.ui.toast = Some((crate::i18n::tr_format!("Merging… {stage} {:.0}%", f * 100.0, stage = stage), now + 0.5, None)); ctx.request_repaint_after(std::time::Duration::from_millis(100)); } } diff --git a/crates/ui-egui/src/panels/detail.rs b/crates/ui-egui/src/panels/detail.rs index 3b3622be..732dd019 100644 --- a/crates/ui-egui/src/panels/detail.rs +++ b/crates/ui-egui/src/panels/detail.rs @@ -1528,11 +1528,15 @@ pub(crate) fn filmstrip(app: &mut LightcraftApp, ui: &mut egui::Ui, r: Rect) { register(ui.ctx(), format!("film:{}", id.0), cr); let sel = Some(*id) == active; let p = ui.painter(); - if sel { - p.rect_filled(cr, 0.0, t.cell_selected); + let label = app.session.catalog.photo(*id).and_then(|ph| ph.label); + let base = if sel { + t.cell_selected } else if resp.hovered() { - p.rect_filled(cr, 0.0, t.cell_selected.gamma_multiply(0.6)); - } + t.cell_selected.gamma_multiply(0.6) + } else { + t.canvas + }; + p.rect_filled(cr, 0.0, crate::theme::label_background(base, label, sel)); let names = app.ui.settings.film_names; if let Some(ph) = app.session.catalog.photo(*id).filter(|_| names) { let name = ph.file_name.rsplit_once('.').map(|(n, _)| n).unwrap_or(&ph.file_name); diff --git a/crates/ui-egui/src/panels/filterbar.rs b/crates/ui-egui/src/panels/filterbar.rs index c8de2cff..e1240967 100644 --- a/crates/ui-egui/src/panels/filterbar.rs +++ b/crates/ui-egui/src/panels/filterbar.rs @@ -16,15 +16,7 @@ pub const HEIGHT: f32 = 40.0; const WIDE: f32 = 1240.0; /// Colour-label swatches (UI colours, our own choice). -pub fn label_color(l: ColorLabel) -> Color32 { - match l { - ColorLabel::Red => Color32::from_rgb(222, 72, 72), - ColorLabel::Yellow => Color32::from_rgb(232, 196, 58), - ColorLabel::Green => Color32::from_rgb(88, 176, 92), - ColorLabel::Blue => Color32::from_rgb(72, 130, 222), - ColorLabel::Purple => Color32::from_rgb(158, 100, 210), - } -} +pub use crate::theme::label_color; fn filter(app: &mut LightcraftApp, patch: Value) { let _ = app.run("library.filter", patch); diff --git a/crates/ui-egui/src/panels/grid.rs b/crates/ui-egui/src/panels/grid.rs index e83fec8b..0a38a6a4 100644 --- a/crates/ui-egui/src/panels/grid.rs +++ b/crates/ui-egui/src/panels/grid.rs @@ -476,7 +476,8 @@ fn cell(app: &mut LightcraftApp, ui: &mut egui::Ui, id: PhotoId, r: Rect, square let active = app.session.selection.active == Some(id); let p = ui.painter(); let img_rect = if square { - p.rect_filled(r, 0.0, if selected { t.cell_selected } else { t.cell }); + let base = if selected { t.cell_selected } else { t.cell }; + p.rect_filled(r, 0.0, crate::theme::label_background(base, photo.label, selected)); Rect::from_min_max(r.min + vec2(10.0, 24.0), r.max - vec2(10.0, 10.0)) } else { r @@ -539,9 +540,14 @@ fn cell(app: &mut LightcraftApp, ui: &mut egui::Ui, id: PhotoId, r: Rect, square crate::state::GridBadges::Always => true, crate::state::GridBadges::Never => false, }; + let badge_bar = Rect::from_min_max(pos2(img_rect.left(), img_rect.bottom() - 24.0), img_rect.right_bottom()); + if !square && photo.label.is_some() { + // Justified photos fill the whole cell: reserve the badge footer as coloured chrome. + p.rect_filled(badge_bar, 0.0, crate::theme::label_background(t.cell, photo.label, selected)); + } if show_badges { - let bar = Rect::from_min_max(pos2(img_rect.left(), img_rect.bottom() - 24.0), img_rect.right_bottom()); - if resp.hovered() || selected { + let bar = badge_bar; + if (resp.hovered() || selected) && (square || photo.label.is_none()) { p.rect_filled(bar, 0.0, Color32::from_black_alpha(120)); } let mut x = bar.left() + 6.0; diff --git a/crates/ui-egui/src/panels/mod.rs b/crates/ui-egui/src/panels/mod.rs index 8317dda7..7b8f1e2d 100644 --- a/crates/ui-egui/src/panels/mod.rs +++ b/crates/ui-egui/src/panels/mod.rs @@ -63,7 +63,7 @@ pub fn resizable_side( /// The HUD toast at the bottom centre of the canvas. pub fn toast(app: &mut LightcraftApp, ctx: &egui::Context) { let now = ctx.input(|i| i.time); - let Some((text, until)) = app.ui.toast.clone() else { return }; + let Some((text, until, label)) = app.ui.toast.clone() else { return }; if now > until { app.ui.toast = None; return; @@ -74,11 +74,13 @@ pub fn toast(app: &mut LightcraftApp, ctx: &egui::Context) { let painter = ctx.layer_painter(egui::LayerId::new(egui::Order::Foreground, egui::Id::new("toast"))); // long messages (a save warning, the web build's notices) wrap within the canvas let wrap = (canvas.width() - 80.0).clamp(200.0, 720.0); - let galley = painter.layout(text, t.font(14.0), t.text.gamma_multiply(fade), wrap); + let (background, foreground) = label.map(crate::theme::label_toast_colors).unwrap_or((egui::Color32::from_black_alpha(200), t.text)); + let foreground = foreground.gamma_multiply(fade); + let galley = painter.layout(text, t.font(14.0), foreground, wrap); let size = vec2(galley.size().x + 40.0, (galley.size().y + 22.0).max(40.0)); let r = Rect::from_center_size(pos2(canvas.center().x, canvas.bottom() - 60.0), size); - painter.rect_filled(r, 6.0, egui::Color32::from_black_alpha((200.0 * fade) as u8)); - painter.galley(r.center() - galley.size() / 2.0, galley, t.text); + painter.rect_filled(r, 6.0, background.gamma_multiply(fade)); + painter.galley(r.center() - galley.size() / 2.0, galley, foreground); ctx.request_repaint_after(std::time::Duration::from_millis(30)); } diff --git a/crates/ui-egui/src/shortcuts.rs b/crates/ui-egui/src/shortcuts.rs index 8c47e191..b91306cf 100644 --- a/crates/ui-egui/src/shortcuts.rs +++ b/crates/ui-egui/src/shortcuts.rs @@ -434,6 +434,7 @@ mod tests { h.app.ui.toast = None; key(&mut h, k, shift); assert_eq!(h.app.ui.toast.as_ref().map(|t| t.0.as_str()), Some(format!("{name} Label").as_str())); + assert_eq!(h.app.ui.toast.as_ref().and_then(|t| t.2), h.app.session.catalog.photo(PhotoId(1)).unwrap().label); assert_eq!(h.app.session.active(), Some(PhotoId(if shift { 2 } else { 1 }))); } } @@ -442,6 +443,7 @@ mod tests { assert_eq!(h.app.ui.toast.as_ref().map(|t| t.0.as_str()), Some("Purple Label")); h.app.run("photo.label", json!({"label": "none"})).unwrap(); assert_eq!(h.app.ui.toast.as_ref().map(|t| t.0.as_str()), Some("Color label cleared")); + assert!(h.app.ui.toast.as_ref().is_some_and(|t| t.2.is_none()), "clear uses neutral feedback"); h.app.session.execute("label.setNames", &json!({"names": {"red": "Needs review"}})).unwrap(); key(&mut h, "6", false); assert_eq!(h.app.ui.toast.as_ref().map(|t| t.0.as_str()), Some("Needs review Label")); diff --git a/crates/ui-egui/src/state.rs b/crates/ui-egui/src/state.rs index bdbbcf29..45288568 100644 --- a/crates/ui-egui/src/state.rs +++ b/crates/ui-egui/src/state.rs @@ -371,9 +371,9 @@ pub struct UiState { /// Reference view: the reference photo. #[serde(skip)] pub reference: Option, - /// Transient toast text and its expiry (seconds of app time). + /// Transient toast text, expiry (seconds of app time), and optional colour-label styling. #[serde(skip)] - pub toast: Option<(String, f64)>, + pub toast: Option<(String, f64, Option)>, /// The result of the last Find Missing Photos (it searches in the background). #[serde(skip)] pub last_find_missing: Option, diff --git a/crates/ui-egui/src/tests_labels.rs b/crates/ui-egui/src/tests_labels.rs new file mode 100644 index 00000000..8a24b988 --- /dev/null +++ b/crates/ui-egui/src/tests_labels.rs @@ -0,0 +1,80 @@ +//! Rendered regressions: label colour belongs on thumbnail chrome and the label HUD, not pixels. + +use std::time::Duration; + +use egui::{Color32, ColorImage, Pos2}; +use lightcraft_catalog::{Op, Photo, PhotoId, Source}; +use serde_json::json; + +use crate::headless::Headless; +use crate::state::ViewMode; +use crate::{LightcraftApp, Services}; + +fn app(view: ViewMode) -> Headless { + let mut s = lightcraft_engine::Session::new(); + for id in 1..=6 { + let p = Photo::new(PhotoId(id), Source::Demo { scene: 1 }, &format!("{id}.jpg"), "JPEG", 640, 480, "2026-10-08"); + s.catalog.apply(Op::AddPhoto { photo: Box::new(p) }).unwrap(); + } + s.execute("library.sort", &json!({"key": "fileName", "ascending": true, "group": "none"})).unwrap(); + s.execute("library.select", &json!({"ids": [1]})).unwrap(); + let mut h = Headless::new(LightcraftApp::new(s, Services { png: None, ..Default::default() }), [1200.0, 800.0], 1.0); + h.app.ui.view = view; + h.app.ui.right = crate::state::RightPanel::None; + h.app.ui.thumb_size = 160.0; + assert!(h.settle(Duration::from_secs(30))); + h +} + +fn rect(h: &Headless, id: &str) -> egui::Rect { + h.app.widgets.iter().find(|(w, _)| w == id).unwrap().1 +} + +fn pixel(image: &ColorImage, p: Pos2) -> Color32 { + image.pixels[p.y as usize * image.size[0] + p.x as usize] +} + +#[test] +fn label_tints_grid_and_filmstrip_chrome_without_changing_photo_pixels() { + for view in [ViewMode::SquareGrid, ViewMode::PhotoGrid, ViewMode::Detail] { + let mut h = app(view); + let widget = if view == ViewMode::Detail { "film:1" } else { "thumb:1" }; + let cell = rect(&h, widget); + let sample = if view == ViewMode::PhotoGrid { + egui::pos2(cell.right() - 40.0, cell.bottom() - 8.0) + } else { + egui::pos2(cell.left() + 4.0, cell.center().y) + }; + let neutral = h.paint(); + let centre = pixel(&neutral, cell.center()); + let mut colours = Vec::new(); + for label in ["red", "yellow", "green", "blue", "purple"] { + h.app.run("photo.label", json!({"label": label})).unwrap(); + h.app.ui.toast = None; + h.step(); + let image = h.paint(); + let c = pixel(&image, sample); + assert_ne!(c, pixel(&neutral, sample), "{view:?} {label} must tint the thumbnail surround/footer"); + assert!(!colours.contains(&c), "each label has a distinct tint: {view:?} {label}"); + colours.push(c); + assert_eq!(pixel(&image, cell.center()), centre, "{view:?} {label} must not tint image pixels"); + } + h.app.run("photo.label", json!({"label": "none"})).unwrap(); + h.app.ui.toast = None; + h.step(); + assert_eq!(pixel(&h.paint(), sample), pixel(&neutral, sample), "clearing restores neutral chrome"); + } +} + +#[test] +fn label_toast_has_pale_colour_and_rating_restores_neutral_style() { + let mut h = app(ViewMode::SquareGrid); + h.app.run("photo.label", json!({"label": "red"})).unwrap(); + h.step(); + let at = egui::pos2(h.app.canvas_rect.unwrap().center().x, h.app.canvas_rect.unwrap().bottom() - 45.0); + let red = pixel(&h.paint(), at); + assert!(red.r() > 180 && red.r() > red.g() && red.r() > red.b(), "pale red toast: {red:?}"); + h.request("ui.key", json!({"key": "5"}), Duration::from_secs(20)); + let rating = pixel(&h.paint(), at); + assert!(rating.r() < 80 && rating.r() == rating.g() && rating.g() == rating.b(), "neutral rating toast: {rating:?}"); +} diff --git a/crates/ui-egui/src/theme.rs b/crates/ui-egui/src/theme.rs index c77fb0a2..9534ebb6 100644 --- a/crates/ui-egui/src/theme.rs +++ b/crates/ui-egui/src/theme.rs @@ -7,6 +7,28 @@ use egui::{Color32, CornerRadius, FontData, FontDefinitions, FontFamily, FontId, pub const FONT_SEMIBOLD: &str = "semibold"; +/// Shared colour-label palette for badges, thumbnail surrounds and feedback. +pub fn label_color(label: lightcraft_catalog::ColorLabel) -> Color32 { + use lightcraft_catalog::ColorLabel; + match label { + ColorLabel::Red => Color32::from_rgb(222, 72, 72), + ColorLabel::Yellow => Color32::from_rgb(232, 196, 58), + ColorLabel::Green => Color32::from_rgb(88, 176, 92), + ColorLabel::Blue => Color32::from_rgb(72, 130, 222), + ColorLabel::Purple => Color32::from_rgb(158, 100, 210), + } +} + +/// A translucent label colour over thumbnail chrome; selection remains brighter. +pub fn label_background(base: Color32, label: Option, selected: bool) -> Color32 { + label.map_or(base, |l| base.lerp_to_gamma(label_color(l), if selected { 0.32 } else { 0.22 })) +} + +/// Label confirmations use a pale colour and dark text, distinct from neutral/error HUDs. +pub fn label_toast_colors(label: lightcraft_catalog::ColorLabel) -> (Color32, Color32) { + (Color32::WHITE.lerp_to_gamma(label_color(label), 0.3), Color32::from_rgb(36, 24, 24)) +} + #[derive(Clone, Copy, Debug)] pub struct Tokens { /// Top bar, side panels, bottom bar, tool strip. diff --git a/docs/library-shortcuts.md b/docs/library-shortcuts.md index 4adddd51..5305fe57 100644 --- a/docs/library-shortcuts.md +++ b/docs/library-shortcuts.md @@ -17,6 +17,10 @@ menu. Purple has no default number key, matching Lightroom Classic. Rejection ma culling; it does not delete the original or remove it from the library. Setting a colour label shows the same brief bottom toast as a rating, using the colour or your custom label name. Clearing it shows a confirmation too, whether applied by a key, menu or swatch. +Labelled thumbnails have a translucent matching colour on the square-grid surround, photo-grid +footer and Detail filmstrip surround. The photo pixels and white active-photo outline remain clear. +The label confirmation uses a pale matching background with dark text; ratings and errors retain +their neutral HUD styling. Custom names use the underlying label's colour. Grid actions apply to all selected photos and support Undo / Redo. In Compare and Survey, culling actions apply to the active photo. Auto Advance moves forward after an action; holding Shift with diff --git a/docs/parity.md b/docs/parity.md index 4fbd4184..47f805c5 100644 --- a/docs/parity.md +++ b/docs/parity.md @@ -60,7 +60,9 @@ that decide whether a photographer can switch (see the honest assessment in [ROA Take the first one nobody is working on. Library culling keys now cover ratings, labels and flags with Shift-to-advance (see -[library shortcuts](library-shortcuts.md), issue #283 and PR #261). **KEYC-RATING** remains partial: +[library shortcuts](library-shortcuts.md), issue #283 and PR #261). Colour labels also tint thumbnail +surrounds/footers and use matching pale confirmations, with rendered regressions for all five colours. +**KEYC-RATING** remains partial: rating-step keys, flag cycling and filter-bar bindings are still open; Shift+P is contextual. 1. **LR-PROF-CAMERACOLOR** (P0): our own camera colour calibration. Sony ARW and Nikon NEF now get a guarded file-local fit to their own embedded JPEG (colour matrix + hue/saturation/value table + tone and chroma curves, relative WB; per-model profiles pooled from many photos via `lightcraft-cli calibrate`; ARW: 7 of 10 public samples accepted, mean ΔE vs the camera JPEG 17–26 → 3–10; NEF: 13 of 13 decodable samples from 6 bodies accepted, ΔE 13–46 → 3–8, one mixed-light scene 13 → 12); measured calibration and fidelity remain missing. Other non-DNG raws and rejected fits still use a neutral matrix. Expand validated preview fitting and use matrices the files carry themselves; never Adobe data. @@ -151,7 +153,7 @@ under the UI command that wraps it (a no-op error); the UI command now wins. | LR-LIB-TARGET | Target album | P2 | ✅ | `cmd:album.setTarget`, `cmd:album.toggleTarget` | see LRC-LIB-COLLECTIONS | | LR-LIB-RATING | Star ratings | P0 | ✅ | `cmd:photo.rate` (`advance`), `crates/ui-egui/src/shortcuts.rs` | 0 clears; 1–5 set; Shift+0–5 applies and advances once, including with Auto Advance. Batch grid and undo/redo regressions; macOS native ownership corrected (PR #261) | | LR-LIB-FLAG | Pick / reject flags | P0 | ✅ | `cmd:photo.pick`, `cmd:photo.reject`, `cmd:photo.unflag`, `cmd:photo.flag` | P pick, X reject, U clear; Shift+P picks and advances in both grids (Presets elsewhere); Shift+X/U and legacy Shift+Z advance; no flag cycle; rejection does not delete the file | -| LR-LIB-LABEL | Colour labels | P1 | ✅ | `cmd:photo.label`, `cmd:label.setNames`, `cmd:label.names`, `cmd:dialog.labelNames`, keys 6–9 in `crates/ui-egui/src/shortcuts.rs` | Photo menu and grid context menu (coloured, named), Info-panel swatches, label dot in grid cells; setting/clearing through keys (including Shift), menus or Info swatches shows the same bottom toast as ratings, with the colour/custom name; editable label names (undoable, journaled); no purple key (as in Lightroom), no custom extra labels | +| LR-LIB-LABEL | Colour labels | P1 | ✅ | `cmd:photo.label`, `cmd:label.setNames`, `cmd:label.names`, `cmd:dialog.labelNames`, keys 6–9 in `crates/ui-egui/src/shortcuts.rs` | Photo menu and grid context menu (coloured, named), Info-panel swatches, label dot plus translucent colour on square-grid/filmstrip surrounds and photo-grid footers (photo pixels unchanged); colour-label toasts have pale matching backgrounds; setting/clearing through keys (including Shift), menus or Info swatches shows the same bottom toast as ratings, with the colour/custom name; editable label names (undoable, journaled); no purple key (as in Lightroom), no custom extra labels | | LR-LIB-KEYWORD | Keywords | P0 | ✅ | `cmd:panel.keywords`, `cmd:photo.setMeta` (`addKeywords`/`removeKeywords`), `cmd:keyword.list`, `cmd:keyword.rename`, `cmd:keyword.delete`, `cmd:keyword.merge`, `cmd:keyword.suggest`, `crates/catalog/src/keywords.rs`, `crates/ui-egui/src/panels/left.rs` | left-panel keyword tree with counts (click filters, children included; context menu rename / merge / delete / add / remove); library-wide ops are one undo step and replay from the op log; suggestions (co-occurring / most used / completions) in the Keywords panel; no keyword drag-and-drop | | LR-LIB-PEOPLE | People / faces | P2 | 🟡 | `cmd:view.people`, `cmd:view.faceBoxes`, `cmd:photo.removeRegion`, `cmd:photo.setRegion`, `cmd:library.filter`, `crates/ui-egui/src/panels/people.rs`, `crates/catalog/src/query.rs` (`people`), `crates/engine/src/media.rs` (`face_job`), `crates/meta/src/xmp.rs` | MVP: named face regions read from XMP (MWG-RS, read-only, see `docs/xmp-interop.md`; clipped to the photo; Lightroom's `Rotation` of ±π/2 and π handled, mirrored orientations taken as written; boxes follow Rotate Left/Right and flips; a sidecar stating `mwg-rs:Regions`, even empty, replaces the photo's on re-read, one without leaves them) become cards in the People view (View ▸ People, or the People button in the toolbar): a close-up of the person's largest face, name and photo count; a click filters the grid (`person` in `library.filter`, `person:` search token, filter chip). Face boxes in the loupe can be switched off (View ▸ Face Boxes, toolbar) resized with eight drag handles and removed with the × shown on hover (catalog-only and undoable, one undo step per drag; the sidecar is never rewritten for either); no moving a box by dragging it yet. No face detection, no unnamed people / suggestions, no manual tagging, no writing regions back | | LR-LIB-STACK | Stacks | P1 | ✅ | `cmd:stack.group`, `cmd:stack.ungroup`, `cmd:stack.toggle`, `cmd:stack.setTop`, `cmd:stack.remove`, `cmd:stack.auto`, `crates/catalog/src/stacks.rs` | grid/filmstrip count badges, expand/collapse, auto-stack by capture time; no visual-similarity auto-stack | diff --git a/docs/whats-new.md b/docs/whats-new.md index e01893cf..be89ddbc 100644 --- a/docs/whats-new.md +++ b/docs/whats-new.md @@ -3,6 +3,7 @@ ## October 2026 ### Library keyboard culling +- Colour labels tint thumbnail surrounds in Square Grid and the Detail filmstrip, and the footer in Photo Grid. Label confirmations use a matching pale colour; the photo pixels retain their original appearance. - Setting or clearing a colour label shows a brief bottom confirmation, like rating a photo; custom label names appear in the message too. - On macOS, ratings `0–5`, labels `6–9` and pick/unflag `P/U` now reach the app even when shown in the native menu (issue #283; adapted from PR #261). - `Shift+6–9` labels and advances. `Shift+P` picks and advances in Photo Grid and Square Grid; it opens Presets in other views. With Auto Advance on, Shift still moves only once. From 90c63af9510cefc7c75fcbfdeaae772b0819952f Mon Sep 17 00:00:00 2001 From: Nathaniel Ledford Date: Thu, 8 Oct 2026 10:35:46 -0400 Subject: [PATCH 16/41] Crop: drag_crop domain operation for handle drags (#295) Dragging a crop handle past the image edge used to hand the raw rectangle to CropGeometry::constrained, which slid the whole frame toward the centre and moved the anchored corner. drag_crop owns the drag instead: the opposite edges stay put, a locked ratio holds on all eight handles (edges grow about their midline, sliding along a blocked side), the frame stops at the image edge and slides along it, and it keeps a minimum size. Hostile numbers return the start crop. Covered by Given/When/Then scenarios and property tests. Co-Authored-By: Claude Sonnet 5.5 --- crates/geom/src/crop_drag.rs | 483 +++++++++++++++++++++++++++++++++++ crates/geom/src/lib.rs | 2 + 2 files changed, 485 insertions(+) create mode 100644 crates/geom/src/crop_drag.rs diff --git a/crates/geom/src/crop_drag.rs b/crates/geom/src/crop_drag.rs new file mode 100644 index 00000000..f600de6d --- /dev/null +++ b/crates/geom/src/crop_drag.rs @@ -0,0 +1,483 @@ +//! Interactive crop editing: what dragging a crop handle does. +//! +//! The crop rectangle lives in the straightened (rotated) frame and must stay inside the rotated +//! image. A drag is a *domain operation* on the crop, not a UI detail: the handle being dragged +//! decides which edges are anchored, an optional aspect lock decides the shape, and the image +//! bounds decide how far the drag may go. [`drag_crop`] answers all three in one place so the app, +//! CLI, control channel and MCP agree. + +use crate::{CropGeometry, Point, Rect}; + +/// Smallest crop side, as a fraction of the image's shorter side. +pub const MIN_CROP_FRACTION: f64 = 0.02; + +/// Most elongated locked crop shape (width:height or its inverse) the drag supports. +pub const MAX_RATIO: f64 = 20.0; + +/// A grab point on the crop frame. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum CropHandle { + TopLeft, + TopRight, + BottomRight, + BottomLeft, + Top, + Right, + Bottom, + Left, + /// Drag the whole frame. + Move, +} + +impl CropHandle { + /// The overlay's numbering: 0..3 corners (clockwise from top-left), 4..7 edges (top, right, + /// bottom, left), 8 = move. + pub fn from_index(i: u8) -> Option { + Some(match i { + 0 => CropHandle::TopLeft, + 1 => CropHandle::TopRight, + 2 => CropHandle::BottomRight, + 3 => CropHandle::BottomLeft, + 4 => CropHandle::Top, + 5 => CropHandle::Right, + 6 => CropHandle::Bottom, + 7 => CropHandle::Left, + 8 => CropHandle::Move, + _ => return None, + }) + } + + /// Parse a command-parameter name (`"topLeft"`, `"top"`, `"move"`, …). + pub fn from_name(n: &str) -> Option { + Some(match n { + "topLeft" => CropHandle::TopLeft, + "topRight" => CropHandle::TopRight, + "bottomRight" => CropHandle::BottomRight, + "bottomLeft" => CropHandle::BottomLeft, + "top" => CropHandle::Top, + "right" => CropHandle::Right, + "bottom" => CropHandle::Bottom, + "left" => CropHandle::Left, + "move" => CropHandle::Move, + _ => return None, + }) + } +} + +/// Drag `handle` of `start` from `grab` to `pointer` (both normalized, in the straightened frame) +/// on a `w × h` image. `aspect` is the locked width/height ratio in pixels, if any. +/// +/// Guarantees (for any input, including hostile numbers): +/// - the result lies inside the rotated image; +/// - the edges opposite the handle stay where they were (a move keeps the size); +/// - with `aspect`, the frame keeps that shape on every handle (when `start` had it); +/// - the drag stops at the image edge instead of pushing the frame around, and slides along it. +pub fn drag_crop(start: CropGeometry, handle: CropHandle, grab: Point, pointer: Point, w: f64, h: f64, aspect: Option) -> CropGeometry { + let sane = |v: f64| v.is_finite(); + let r0 = start.rect; + if ![w, h, grab.x, grab.y, pointer.x, pointer.y, start.angle, r0.x0, r0.y0, r0.x1, r0.y1].iter().copied().all(sane) + || w <= 0.0 + || h <= 0.0 + || r0.is_empty() + || aspect.is_some_and(|a| !(a.is_finite() && a > 0.0)) + { + return start; + } + // Start from a valid frame, then work in pixels of the straightened frame. + let mut base = start.constrained(w, h); + if base.angle == 0.0 { + // absorb the 1e-6 tolerance of `is_within_image` so the exact test below accepts the start + let r = base.rect; + base.rect = Rect::new(r.x0.clamp(0.0, 1.0), r.y0.clamp(0.0, 1.0), r.x1.clamp(0.0, 1.0), r.y1.clamp(0.0, 1.0)); + } + let sr = base.rect_px(w, h); + // Moving further than twice the image never matters; clamping keeps the searches well-conditioned. + let (dx, dy) = (((pointer.x - grab.x) * w).clamp(-2.0 * w, 2.0 * w), ((pointer.y - grab.y) * h).clamp(-2.0 * h, 2.0 * h)); + // Ratios beyond 20:1 make even the minimum frame larger than the image; treat them as 20:1. + let aspect = aspect.map(|a| a.clamp(1.0 / MAX_RATIO, MAX_RATIO)); + // Unrotated, the frame must be exactly inside the image (`is_within_image` tolerates 1e-6, which + // would leak out as a sliver past the edge and, after any clamp, a shifted ratio). + let feasible = |r: Rect| { + if base.angle == 0.0 { + r.x0 >= 0.0 && r.y0 >= 0.0 && r.x1 <= w && r.y1 <= h + } else { + CropGeometry { rect: r.scale(1.0 / w, 1.0 / h), angle: base.angle }.is_within_image(w, h) + } + }; + let min_side = MIN_CROP_FRACTION * w.min(h); + + let out = match (handle, aspect) { + (CropHandle::Move, _) => { + let slide = |cur: Rect, ax: f64, ay: f64| { + let t = bisect(0.0, 1.0, |t| feasible(cur.translate(crate::Vec2::new(ax * t, ay * t)))); + cur.translate(crate::Vec2::new(ax * t, ay * t)) + }; + let cur = slide(sr, dx, 0.0); + slide(cur, 0.0, dy) + } + (hd, None) => { + let (mx0, mx1, my0, my1) = sides(hd); + let grow = |cur: Rect, tx: f64, ty: f64| { + // `tx`/`ty`: how far the moving edges would go; interpolate towards it + let target = Rect::new( + if mx0 { (cur.x0 + tx).min(cur.x1 - min_side) } else { cur.x0 }, + if my0 { (cur.y0 + ty).min(cur.y1 - min_side) } else { cur.y0 }, + if mx1 { (cur.x1 + tx).max(cur.x0 + min_side) } else { cur.x1 }, + if my1 { (cur.y1 + ty).max(cur.y0 + min_side) } else { cur.y1 }, + ); + let at = |t: f64| lerp_rect(cur, target, t); + at(bisect(0.0, 1.0, |t| feasible(at(t)))) + }; + let cur = grow(sr, dx, 0.0); + grow(cur, 0.0, dy) + } + (hd, Some(a)) => { + let (mx0, _, my0, _) = sides(hd); + let (min_w, min_h) = (min_side.max(min_side * a), min_side.max(min_side / a)); + let c = sr.center(); + // Anchor: the opposite corner / edge midpoint. `build(n, o)` is the frame of width `n` that + // keeps it, shifted by `o` along the anchored edge (edge handles only). + let (target_w, build): (f64, Box Rect>) = match hd { + CropHandle::Top | CropHandle::Bottom => { + let (ay, sy, edge) = if my0 { (sr.y1, -1.0, sr.y0) } else { (sr.y0, 1.0, sr.y1) }; + let nh = (sy * (edge + dy - ay)).max(min_h); + (nh * a, Box::new(move |nw, o| vband(c.x + o, ay, sy, nw, a))) + } + CropHandle::Left | CropHandle::Right => { + let (ax, sx, edge) = if mx0 { (sr.x1, -1.0, sr.x0) } else { (sr.x0, 1.0, sr.x1) }; + let nw = (sx * (edge + dx - ax)).max(min_w); + (nw, Box::new(move |nw, o| hband(ax, sx, c.y + o, nw, a))) + } + _ => { + let (ax, sx, ex) = if mx0 { (sr.x1, -1.0, sr.x0) } else { (sr.x0, 1.0, sr.x1) }; + let (ay, sy, ey) = if my0 { (sr.y1, -1.0, sr.y0) } else { (sr.y0, 1.0, sr.y1) }; + let (wx, wy) = (sx * (ex + dx - ax), sy * (ey + dy - ay)); + let nw = ((wx + wy * a) / 2.0).max(min_w); + (nw, Box::new(move |nw, _| corner_rect(ax, sx, ay, sy, nw, a))) + } + }; + let target_w = target_w.min(2.0 * w.max(h * a)); + let slides = matches!(hd, CropHandle::Top | CropHandle::Bottom | CropHandle::Left | CropHandle::Right); + let extent = if matches!(hd, CropHandle::Top | CropHandle::Bottom) { w } else { h }; + // Frame of width `n`, centred on the anchored edge if it fits, else slid the least distance. + let place = |n: f64| -> Option { + let at = |o: f64| build(n, o); + if feasible(at(0.0)) { + return Some(at(0.0)); + } + if !slides { + return None; + } + const STEPS: u32 = 128; + let step = extent / STEPS as f64; + for k in 1..=STEPS { + for sign in [1.0, -1.0] { + let hi = sign * k as f64 * step; + if feasible(at(hi)) { + // closest feasible offset: bisect between the last infeasible and this one + let (mut bad, mut good) = (sign * (k - 1) as f64 * step, hi); + for _ in 0..40 { + let mid = (bad + good) / 2.0; + if feasible(at(mid)) { + good = mid; + } else { + bad = mid; + } + } + return Some(at(good)); + } + } + } + None + }; + if place(min_w).is_some() { + let n = bisect_range(min_w, target_w.max(min_w), |n| place(n).is_some()); + place(n).unwrap_or(sr) + } else { + sr + } + } + }; + let rect = out.scale(1.0 / w, 1.0 / h); + if [rect.x0, rect.y0, rect.x1, rect.y1].iter().all(|v| v.is_finite()) { CropGeometry { rect, angle: base.angle } } else { start } +} + +/// Which sides a handle moves: (left, right, top, bottom). +fn sides(h: CropHandle) -> (bool, bool, bool, bool) { + use CropHandle::*; + ( + matches!(h, TopLeft | BottomLeft | Left), + matches!(h, TopRight | BottomRight | Right), + matches!(h, TopLeft | TopRight | Top), + matches!(h, BottomLeft | BottomRight | Bottom), + ) +} + +fn lerp_rect(a: Rect, b: Rect, t: f64) -> Rect { + let l = |p: f64, q: f64| p + (q - p) * t; + Rect::new(l(a.x0, b.x0), l(a.y0, b.y0), l(a.x1, b.x1), l(a.y1, b.y1)) +} + +/// Frame of width `nw` and ratio `a` extending from corner (`ax`, `ay`) in directions `sx`, `sy`. +fn corner_rect(ax: f64, sx: f64, ay: f64, sy: f64, nw: f64, a: f64) -> Rect { + let nh = nw / a; + Rect::from_points(Point::new(ax, ay), Point::new(ax + sx * nw, ay + sy * nh)) +} + +/// Frame whose top/bottom edge (at `ay`, growing in direction `sy`) is anchored, centred on `cx`. +fn vband(cx: f64, ay: f64, sy: f64, nw: f64, a: f64) -> Rect { + let nh = nw / a; + Rect::from_points(Point::new(cx - nw / 2.0, ay), Point::new(cx + nw / 2.0, ay + sy * nh)) +} + +/// Frame whose left/right edge (at `ax`, growing in direction `sx`) is anchored, centred on `cy`. +fn hband(ax: f64, sx: f64, cy: f64, nw: f64, a: f64) -> Rect { + let nh = nw / a; + Rect::from_points(Point::new(ax, cy - nh / 2.0), Point::new(ax + sx * nw, cy + nh / 2.0)) +} + +/// Largest `t` in `[lo, hi]` for which `ok` holds, given that it holds at `lo` and the set of good +/// `t` is an interval (the feasible region is convex). +fn bisect_range(lo: f64, hi: f64, ok: impl Fn(f64) -> bool) -> f64 { + if ok(hi) { + return hi; + } + let (mut good, mut bad) = (lo, hi); + for _ in 0..48 { + let mid = (good + bad) / 2.0; + if ok(mid) { + good = mid; + } else { + bad = mid; + } + } + good +} + +fn bisect(lo: f64, hi: f64, ok: impl Fn(f64) -> bool) -> f64 { + bisect_range(lo, hi, ok) +} + +#[cfg(test)] +mod tests { + use super::*; + use CropHandle::*; + + const W: f64 = 600.0; + const H: f64 = 400.0; + const EPS: f64 = 1e-6; + + fn geo(x0: f64, y0: f64, x1: f64, y1: f64) -> CropGeometry { + CropGeometry { rect: Rect::new(x0, y0, x1, y1), angle: 0.0 } + } + fn p(x: f64, y: f64) -> Point { + Point::new(x, y) + } + fn drag(g: CropGeometry, hd: CropHandle, from: Point, to: Point, aspect: Option) -> CropGeometry { + drag_crop(g, hd, from, to, W, H, aspect) + } + fn px_aspect(g: &CropGeometry) -> f64 { + g.rect.width() * W / (g.rect.height() * H) + } + fn near(a: f64, b: f64) -> bool { + (a - b).abs() < EPS + } + + // Feature: dragging a crop handle + // Scenario: a free corner drag inside the image + #[test] + fn given_free_crop_when_corner_dragged_inside_then_corner_follows_and_opposite_corner_stays() { + let g = drag(geo(0.2, 0.2, 0.8, 0.8), BottomRight, p(0.8, 0.8), p(0.9, 0.7), None); + assert!(near(g.rect.x0, 0.2) && near(g.rect.y0, 0.2)); + assert!(near(g.rect.x1, 0.9) && near(g.rect.y1, 0.7)); + } + + // Scenario: dragging past the right edge (the weird behaviour of issue 295) + #[test] + fn given_corner_dragged_past_image_edge_then_it_stops_at_the_edge_and_anchor_stays() { + let g = drag(geo(0.2, 0.2, 0.8, 0.8), BottomRight, p(0.8, 0.8), p(1.3, 0.7), None); + assert!(near(g.rect.x0, 0.2) && near(g.rect.y0, 0.2), "anchor moved: {:?}", g.rect); + assert!(near(g.rect.x1, 1.0), "stops at the edge: {:?}", g.rect); + assert!(near(g.rect.y1, 0.7), "other axis still follows the pointer: {:?}", g.rect); + } + + #[test] + fn given_locked_aspect_when_corner_dragged_past_edge_then_ratio_and_anchor_hold() { + let a = 1.5; + let g = drag(geo(0.2, 0.2, 0.8, 0.2 + 0.6 * W / H / a), BottomRight, p(0.8, 0.6), p(1.4, 0.9), Some(a)); + assert!(near(g.rect.x0, 0.2) && near(g.rect.y0, 0.2), "{:?}", g.rect); + assert!(near(px_aspect(&g), a), "{}", px_aspect(&g)); + assert!(g.rect.x1 <= 1.0 + EPS && g.rect.y1 <= 1.0 + EPS); + assert!(g.rect.x1 > 0.95 || g.rect.y1 > 0.95, "grows until it touches an edge: {:?}", g.rect); + } + + #[test] + fn given_locked_aspect_when_edge_dragged_then_ratio_holds_and_frame_grows_about_its_midline() { + let a = 2.0; + let start = geo(0.3, 0.3, 0.7, 0.3 + 0.4 * W / H / a); + let g = drag(start, Bottom, p(0.5, start.rect.y1), p(0.5, start.rect.y1 + 0.1), Some(a)); + assert!(near(g.rect.y0, start.rect.y0), "opposite edge stays"); + assert!(near(g.rect.center().x, start.rect.center().x), "grows symmetrically sideways"); + assert!(near(px_aspect(&g), a)); + assert!(g.rect.height() > start.rect.height()); + } + + #[test] + fn given_free_crop_when_edge_dragged_then_only_that_edge_moves() { + let g = drag(geo(0.2, 0.2, 0.8, 0.8), Left, p(0.2, 0.5), p(0.1, 0.9), None); + assert!(near(g.rect.x0, 0.1) && near(g.rect.y0, 0.2) && near(g.rect.x1, 0.8) && near(g.rect.y1, 0.8)); + } + + #[test] + fn given_move_past_edge_then_size_is_kept_and_frame_slides_along_the_edge() { + let start = geo(0.5, 0.25, 0.9, 0.65); + let g = drag(start, Move, p(0.7, 0.45), p(1.3, 0.55), None); + assert!(near(g.rect.width(), 0.4) && near(g.rect.height(), 0.4)); + assert!(near(g.rect.x1, 1.0), "flush with the right edge: {:?}", g.rect); + assert!(near(g.rect.y0, 0.35), "still follows the pointer vertically: {:?}", g.rect); + } + + #[test] + fn given_edge_dragged_across_the_opposite_edge_then_crop_keeps_a_minimum_size() { + let g = drag(geo(0.2, 0.2, 0.8, 0.8), Right, p(0.8, 0.5), p(-0.5, 0.5), None); + assert!(g.rect.width() * W >= MIN_CROP_FRACTION * H - EPS && g.rect.x1 > g.rect.x0); + assert!(near(g.rect.x0, 0.2)); + } + + #[test] + fn given_straightened_image_when_dragged_outside_then_crop_stays_inside_the_rotated_image() { + let start = crate::crop_fit_angle(W, H, 12.0, Some(1.5)); + let c = start.rect.center(); + for hd in [TopLeft, TopRight, BottomRight, BottomLeft, Top, Right, Bottom, Left, Move] { + for aspect in [None, Some(1.5)] { + let g = drag(start, hd, c, p(3.0, -3.0), aspect); + assert!(g.is_within_image(W, H), "{hd:?} {aspect:?}: {:?}", g.rect); + assert_eq!(g.angle, 12.0); + } + } + } + + #[test] + fn given_hostile_numbers_then_the_start_crop_is_returned_not_a_panic() { + let s = geo(0.2, 0.2, 0.8, 0.8); + for bad in [f64::NAN, f64::INFINITY, f64::NEG_INFINITY] { + assert_eq!(drag(s, BottomRight, p(0.8, 0.8), p(bad, 0.5), None), s); + } + assert_eq!(drag_crop(s, BottomRight, p(0.8, 0.8), p(0.9, 0.9), 0.0, 400.0, None), s); + assert_eq!(drag(s, BottomRight, p(0.8, 0.8), p(0.9, 0.9), Some(f64::NAN)), s); + assert_eq!(drag(s, BottomRight, p(0.8, 0.8), p(0.9, 0.9), Some(-1.0)), s); + } + + // Scenario: a locked edge drag when the frame already touches the image edge (review finding) + #[test] + fn given_locked_frame_flush_with_the_top_when_right_edge_dragged_out_then_it_grows_sliding_down() { + let a = 1.5; + let start = geo(0.2, 0.0, 0.5, 0.3 * W / H * 0.0 + 0.3 * W / (a * H)); + let g = drag(start, Right, p(0.5, 0.2), p(0.9, 0.2), Some(a)); + assert!(near(g.rect.x0, 0.2), "anchor edge stays: {:?}", g.rect); + assert!(g.rect.width() > start.rect.width() + 0.05, "it must grow: {:?}", g.rect); + assert!(near(px_aspect(&g), a)); + assert!(g.is_within_image(W, H)); + } + + #[test] + fn given_full_fit_straightened_crop_when_locked_edge_dragged_then_it_still_respects_the_image() { + let start = crate::crop_fit_angle(W, H, 20.0, Some(1.5)); + let g = drag(start, Right, p(0.9, 0.5), p(1.5, 0.5), Some(1.5)); + assert!(g.is_within_image(W, H) && near(px_aspect(&g), 1.5)); + } + + // Scenario: extreme or hostile magnitudes + #[test] + fn given_an_extreme_locked_ratio_then_the_frame_can_still_be_moved() { + let start = geo(0.1, 0.4, 0.9, 0.4 + 0.8 * W / (60.0 * H)); + let g = drag(start, Move, p(0.5, 0.4), p(0.5, 0.7), Some(60.0)); + assert!(g.rect.y0 > 0.5, "moved: {:?}", g.rect); + } + + #[test] + fn given_a_huge_finite_pointer_then_a_locked_corner_still_grows_to_the_edge() { + let a = 1.5; + let start = geo(0.2, 0.2, 0.5, 0.2 + 0.3 * W / (a * H)); + let g = drag(start, BottomRight, p(0.5, 0.4), p(1e300, 1e300), Some(a)); + assert!(g.rect.x1 > 0.95 || g.rect.y1 > 0.95, "{:?}", g.rect); + let g = drag(start, BottomRight, p(0.5, 0.4), p(1e300, 1e300), None); + assert!(near(g.rect.x1, 1.0) && near(g.rect.y1, 1.0), "{:?}", g.rect); + } + + #[test] + fn given_an_unrotated_image_then_results_never_leave_the_unit_square() { + let g = drag(geo(0.2, 0.2, 0.8, 0.8), BottomRight, p(0.8, 0.8), p(5.0, 5.0), None); + assert!(g.rect.x1 <= 1.0 && g.rect.y1 <= 1.0 && g.rect.x0 >= 0.0 && g.rect.y0 >= 0.0, "{:?}", g.rect); + } + + #[test] + fn handle_numbering_matches_the_overlay() { + assert_eq!(CropHandle::from_index(0), Some(TopLeft)); + assert_eq!(CropHandle::from_index(6), Some(Bottom)); + assert_eq!(CropHandle::from_index(8), Some(Move)); + assert_eq!(CropHandle::from_index(9), None); + assert_eq!(CropHandle::from_name("bottomLeft"), Some(BottomLeft)); + assert_eq!(CropHandle::from_name("nope"), None); + } + + mod props { + use super::*; + use proptest::prelude::*; + + fn any_handle() -> impl Strategy { + (0u8..9).prop_map(|i| CropHandle::from_index(i).unwrap_or(Move)) + } + + proptest! { + /// Whatever the pointer does, the crop stays valid; the anchor and the ratio hold. + #[test] + fn drag_is_always_inside_anchored_and_shaped( + angle in -45.0f64..45.0, + ratio in prop::option::of(0.3f64..4.0), + hd in any_handle(), + tx in -4.0f64..5.0, ty in -4.0f64..5.0, + shrink in 0.2f64..1.0, + ) { + let base = crate::crop_fit_angle(W, H, angle, ratio); + // a smaller start crop so there is room to move + let c = base.rect.center(); + let start = CropGeometry { rect: Rect::from_center(c, base.rect.width() * shrink, base.rect.height() * shrink), angle }; + prop_assume!(start.is_within_image(W, H)); + let g = drag_crop(start, hd, c, p(tx, ty), W, H, ratio); + prop_assert!(g.is_within_image(W, H), "{:?} -> {:?}", start.rect, g.rect); + prop_assert!(g.rect.width() > 0.0 && g.rect.height() > 0.0); + let min = MIN_CROP_FRACTION * W.min(H) - 1e-6; + prop_assert!(g.rect.width() * W >= min.min(start.rect.width() * W) && g.rect.height() * H >= min.min(start.rect.height() * H), "min size {:?}", g.rect); + if let Some(a) = ratio { + prop_assert!((px_aspect(&g) - a).abs() < 1e-6 * a.max(1.0), "ratio {a}: {}", px_aspect(&g)); + } + // anchors: edges opposite the handle do not move + let (s, r) = (start.rect, g.rect); + let keep_l = matches!(hd, TopRight | BottomRight | Right); + let keep_r = matches!(hd, TopLeft | BottomLeft | Left); + let keep_t = matches!(hd, BottomLeft | BottomRight | Bottom); + let keep_b = matches!(hd, TopLeft | TopRight | Top); + { + if keep_l { prop_assert!((r.x0 - s.x0).abs() < 1e-6, "x0"); } + if keep_r { prop_assert!((r.x1 - s.x1).abs() < 1e-6, "x1"); } + if keep_t { prop_assert!((r.y0 - s.y0).abs() < 1e-6, "y0"); } + if keep_b { prop_assert!((r.y1 - s.y1).abs() < 1e-6, "y1"); } + } + if hd == Move { + prop_assert!((r.width() - s.width()).abs() < 1e-9 && (r.height() - s.height()).abs() < 1e-9); + } + } + + #[test] + fn drag_never_panics_on_arbitrary_numbers( + x0 in -2.0f64..3.0, y0 in -2.0f64..3.0, x1 in -2.0f64..3.0, y1 in -2.0f64..3.0, + angle in -90.0f64..90.0, tx in -1e9f64..1e9, ty in -1e9f64..1e9, + ratio in prop::option::of(-1.0f64..10.0), hd in any_handle(), + ) { + let g = drag_crop(CropGeometry { rect: Rect::new(x0, y0, x1, y1), angle }, hd, p(0.5, 0.5), p(tx, ty), W, H, ratio); + prop_assert!(g.rect.x0.is_finite() && g.rect.y1.is_finite()); + } + } + } +} diff --git a/crates/geom/src/lib.rs b/crates/geom/src/lib.rs index 6de121b0..1d81279d 100644 --- a/crates/geom/src/lib.rs +++ b/crates/geom/src/lib.rs @@ -6,10 +6,12 @@ #![deny(clippy::unwrap_used, clippy::expect_used, clippy::panic, clippy::unimplemented, clippy::todo, clippy::unreachable)] mod crop; +mod crop_drag; mod homography; mod real; pub use crop::{CropGeometry, crop_fit_angle, max_inscribed_scale}; +pub use crop_drag::{CropHandle, MAX_RATIO, MIN_CROP_FRACTION, drag_crop}; pub use homography::Homography; pub use real::{Interval, Real}; From 8d8178dc0aca3f0982f23b8de5685674b157837a Mon Sep 17 00:00:00 2001 From: Nathaniel Ledford Date: Thu, 8 Oct 2026 10:35:46 -0400 Subject: [PATCH 17/41] Engine: crop.drag command; validate custom crop ratios (#295) crop.drag {handle, from, to, start?} runs drag_crop through the command layer so the UI, CLI, control channel and MCP agree; a locked crop keeps the shape of its frame. crop.aspect now rejects non-finite, non-positive and beyond-20:1 ratios and non-numeric arrays instead of storing a degenerate lock that produced an empty crop. Co-Authored-By: Claude Sonnet 5.5 --- crates/engine/src/cmd/develop.rs | 77 +++++++++++++++++++++++++++++--- crates/engine/src/tests.rs | 55 +++++++++++++++++++++++ 2 files changed, 127 insertions(+), 5 deletions(-) diff --git a/crates/engine/src/cmd/develop.rs b/crates/engine/src/cmd/develop.rs index 8f197429..e0aaaf95 100644 --- a/crates/engine/src/cmd/develop.rs +++ b/crates/engine/src/cmd/develop.rs @@ -2,7 +2,7 @@ use lightcraft_catalog::{Op, PhotoId, Version}; use lightcraft_develop::{DevelopSettings, Preset, Section, SettingsGroup, Treatment, Upright, WbMode, controls}; -use lightcraft_geom::{CropGeometry, Point, Rect, crop_fit_angle}; +use lightcraft_geom::{CropGeometry, CropHandle, Point, Rect, crop_fit_angle, drag_crop}; use serde_json::{Value, json}; use super::{CommandSpec, always, bad, bool_or, cmd, f64_or, f64_req, has_active, has_clipboard, has_selection, ok, str_param}; @@ -37,6 +37,32 @@ fn crop_dims(s: &Session, id: PhotoId) -> (f64, f64) { if o.swaps_axes() { (h, w) } else { (w, h) } } +/// `v` as a list of numbers; `None` unless it is an array of nothing but numbers. +fn numbers(v: Option<&Value>) -> Option> { + v?.as_array()?.iter().map(Value::as_f64).collect() +} + +/// A width × height lock stored as hundredths. Rejects non-finite, non-positive and +/// extreme (beyond 20:1) shapes, which would give an empty or unusable crop. +fn aspect_tuple(c: &str, x: f64, y: f64) -> Result<(u32, u32)> { + let ratio = x / y; + if !(x.is_finite() + && y.is_finite() + && x > 0.0 + && y > 0.0 + && ratio.is_finite() + && (1.0 / lightcraft_geom::MAX_RATIO..=lightcraft_geom::MAX_RATIO).contains(&ratio)) + { + return Err(bad(c, "aspect needs two positive numbers with a ratio between 1:20 and 20:1")); + } + let to = |v: f64| (v * 100.0).round(); + let (a, b) = (to(x), to(y)); + if a < 1.0 || b < 1.0 || a > f64::from(u32::MAX) || b > f64::from(u32::MAX) { + return Err(bad(c, "aspect numbers are out of range")); + } + Ok((a as u32, b as u32)) +} + fn set_aspect(d: &mut DevelopSettings, w: f64, h: f64, aspect: Option<(u32, u32)>) { d.crop.aspect = aspect; let a = aspect.map(|(aw, ah)| { @@ -395,6 +421,46 @@ pub fn specs() -> Vec { Ok(()) }) }), + cmd!( + "crop.drag", + "Drag Crop Handle", + [], + None, + "{handle: topLeft|topRight|bottomRight|bottomLeft|top|right|bottom|left|move | 0..8, from: [x,y], to: [x,y] (normalized, straightened frame), start?: [x0,y0,x1,y1] (default: the current crop)} — drags a crop handle; stops at the image edge, keeps the opposite edges, honours the aspect lock", + has_active, + |s, p| { + let id = active(s, "crop.drag")?; + let (w, h) = crop_dims(s, id); + let handle = match p.get("handle") { + Some(Value::String(n)) => CropHandle::from_name(n), + Some(v) => v.as_u64().and_then(|i| u8::try_from(i).ok()).and_then(CropHandle::from_index), + None => None, + } + .ok_or_else(|| bad("crop.drag", "unknown handle"))?; + let point = |k: &str| -> Result { + match numbers(p.get(k)).as_deref() { + Some([x, y]) if x.is_finite() && y.is_finite() => Ok(Point::new(*x, *y)), + _ => Err(bad("crop.drag", format!("`{k}` needs [x, y]"))), + } + }; + let (from, to) = (point("from")?, point("to")?); + let given = match (p.get("start"), numbers(p.get("start")).as_deref()) { + (None | Some(Value::Null), _) => None, + (_, Some(&[x0, y0, x1, y1])) if [x0, y0, x1, y1].iter().all(|v| v.is_finite()) => { + Some(Rect::new(x0.min(x1), y0.min(y1), x0.max(x1), y0.max(y1))) + } + _ => return Err(bad("crop.drag", "`start` needs 4 numbers")), + }; + edit(s, "crop.drag", "Crop", |d| { + let start = CropGeometry { rect: given.unwrap_or(d.crop.geometry.rect), angle: d.crop.geometry.angle }; + // A locked crop keeps the shape it has: the lock stores a width:height tuple whose + // orientation is reinterpreted per image, so the frame itself is the truth. + let ratio = d.crop.aspect.and(Some(start.rect_px(w, h).aspect())).filter(|a| a.is_finite() && *a > 0.0); + d.crop.geometry = drag_crop(start, handle, from, to, w, h, ratio); + Ok(()) + }) + } + ), cmd!( "crop.straighten", "Straighten", @@ -470,11 +536,12 @@ pub fn specs() -> Vec { let (x, y) = a.split_once('x').ok_or_else(|| bad("crop.aspect", "use WxH"))?; let (x, y): (f64, f64) = (x.parse().map_err(|_| bad("crop.aspect", "bad number"))?, y.parse().map_err(|_| bad("crop.aspect", "bad number"))?); - Some(((x * 100.0) as u32, (y * 100.0) as u32)) - } - Some(Value::Array(a)) if a.len() == 2 => { - Some(((a[0].as_f64().unwrap_or(1.0) * 100.0) as u32, (a[1].as_f64().unwrap_or(1.0) * 100.0) as u32)) + Some(aspect_tuple("crop.aspect", x, y)?) } + Some(v @ Value::Array(_)) => match numbers(Some(v)).as_deref() { + Some(&[x, y]) => Some(aspect_tuple("crop.aspect", x, y)?), + _ => return Err(bad("crop.aspect", "use [w, h] with two numbers")), + }, _ => return Err(bad("crop.aspect", "missing aspect")), }; edit(s, "crop.aspect", "Crop Aspect", |d| { diff --git a/crates/engine/src/tests.rs b/crates/engine/src/tests.rs index d005d508..afcf29b4 100644 --- a/crates/engine/src/tests.rs +++ b/crates/engine/src/tests.rs @@ -382,6 +382,61 @@ fn crop_aspect_lock_current_and_toggle() { assert!(active_dev(&s).crop.aspect.is_none(), "toggle unlocks"); } +// Feature: dragging crop handles (issue 295) +#[test] +fn crop_drag_stops_at_the_image_edge_and_keeps_the_anchor() { + let mut s = demo(); + s.execute("crop.set", &json!({"rect": [0.2, 0.2, 0.8, 0.8]})).unwrap(); + s.execute("crop.drag", &json!({"handle": "bottomRight", "from": [0.8, 0.8], "to": [1.4, 0.7]})).unwrap(); + let r = active_dev(&s).crop.geometry.rect; + assert!((r.x0 - 0.2).abs() < 1e-6 && (r.y0 - 0.2).abs() < 1e-6, "anchor moved: {r:?}"); + assert!((r.x1 - 1.0).abs() < 1e-6 && (r.y1 - 0.7).abs() < 1e-6, "{r:?}"); +} + +#[test] +fn crop_drag_keeps_a_locked_ratio_on_every_handle() { + let mut s = demo(); + s.execute("crop.aspect", &json!({"aspect": "16x9"})).unwrap(); + let d = active_dev(&s); + assert!(d.crop.aspect.is_some()); + let want = d.crop.geometry.rect.aspect(); + let r = d.crop.geometry.rect; + for (handle, to) in [("left", [r.x0 + 0.1, 0.5]), ("top", [0.5, r.y0 + 0.05]), ("bottomRight", [1.3, 1.3]), ("right", [0.9, 0.5])] { + s.execute("crop.drag", &json!({"handle": handle, "from": [0.5, 0.5], "to": to, "start": [r.x0, r.y0, r.x1, r.y1]})).unwrap(); + let g = active_dev(&s).crop.geometry.rect; + assert!((g.aspect() - want).abs() < 1e-6, "{handle}: {} vs {want}", g.aspect()); + } +} + +#[test] +fn crop_drag_rejects_unknown_handles_and_bad_points() { + let mut s = demo(); + assert!(s.execute("crop.drag", &json!({"handle": "sideways", "from": [0.5, 0.5], "to": [0.6, 0.6]})).is_err()); + assert!(s.execute("crop.drag", &json!({"handle": "move", "from": [0.5], "to": [0.6, 0.6]})).is_err()); + assert!(s.execute("crop.drag", &json!({"handle": "move", "to": [0.6, 0.6]})).is_err()); +} + +#[test] +fn crop_aspect_rejects_degenerate_custom_ratios() { + let mut s = demo(); + let before = active_dev(&s).crop; + for bad in [json!([0.004, 3]), json!([3, 0]), json!([-1, 2]), json!([1000, 0.01]), json!(["a", 2]), json!([1, 2, 3])] { + assert!(s.execute("crop.aspect", &json!({"aspect": bad.clone()})).is_err(), "{bad}"); + } + assert!(s.execute("crop.aspect", &json!({"aspect": "0x5"})).is_err()); + let after = active_dev(&s).crop; + assert_eq!(before.geometry, after.geometry, "a rejected ratio leaves the crop alone"); + s.execute("crop.aspect", &json!({"aspect": [2.5, 1]})).unwrap(); + assert!(!active_dev(&s).crop.geometry.rect.is_empty()); +} + +#[test] +fn crop_drag_rejects_non_numeric_points() { + let mut s = demo(); + assert!(s.execute("crop.drag", &json!({"handle": "move", "from": ["a", 0.5, 0.5], "to": [0.6, 0.6]})).is_err()); + assert!(s.execute("crop.drag", &json!({"handle": "move", "from": [0.5, 0.5], "to": [0.6, 0.6], "start": [0.1, "x", 0.5, 0.5]})).is_err()); +} + #[test] fn paste_from_previous_and_copy_paste_metadata() { let mut s = demo(); From 0e254bfbc64190dd725802ade09b26381e84c598 Mon Sep 17 00:00:00 2001 From: Nathaniel Ledford Date: Thu, 8 Oct 2026 10:35:46 -0400 Subject: [PATCH 18/41] Crop UI: lock toggle, custom ratio, edge-safe handle drags (#295) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The crop overlay now reports pointer positions to crop.drag instead of computing rectangles itself. The Crop panel gets a visible Lock toggle, a custom W × H entry, a dropdown chevron and a button label that names the active ratio ("Original", a preset, or 1.37 : 1) instead of pixel sizes. LR-CROP-RECT / LR-CROP-ASPECT parity rows updated. Co-Authored-By: Claude Sonnet 5.5 --- crates/ui-egui/src/panels/detail.rs | 40 ++-------- crates/ui-egui/src/panels/right.rs | 113 ++++++++++++++++++++++++---- docs/parity.md | 4 +- 3 files changed, 106 insertions(+), 51 deletions(-) diff --git a/crates/ui-egui/src/panels/detail.rs b/crates/ui-egui/src/panels/detail.rs index 3b3622be..7cee2a93 100644 --- a/crates/ui-egui/src/panels/detail.rs +++ b/crates/ui-egui/src/panels/detail.rs @@ -921,42 +921,14 @@ fn crop_overlay(app: &mut LightcraftApp, ui: &mut egui::Ui, resp: &egui::Respons { match app.gesture.clone() { Some(Gesture::CropHandle { handle, start, angle }) => { + // The crop model decides what the drag does (anchor, aspect lock, image bounds): see + // `lightcraft_geom::drag_crop`; the UI only reports where the pointer started and is. let n = to_straight(map.norm(q), angle, frame); - let o = resp.interact_pointer_pos().map(|q0| q0 - resp.drag_delta()).unwrap_or(q); - let _ = o; - let mut r = start; let orig = ui.input(|i| i.pointer.press_origin()).map(|q0| to_straight(map.norm(q0), angle, frame)).unwrap_or(n); - let (dx, dy) = (n.x - orig.x, n.y - orig.y); - match handle { - 0 => (r.x0, r.y0) = (start.x0 + dx, start.y0 + dy), - 1 => (r.x1, r.y0) = (start.x1 + dx, start.y0 + dy), - 2 => (r.x1, r.y1) = (start.x1 + dx, start.y1 + dy), - 3 => (r.x0, r.y1) = (start.x0 + dx, start.y1 + dy), - 4 => r.y0 = start.y0 + dy, - 5 => r.x1 = start.x1 + dx, - 6 => r.y1 = start.y1 + dy, - 7 => r.x0 = start.x0 + dx, - _ => r = start.translate(lightcraft_geom::Vec2::new(dx, dy)), - } - // aspect lock - if let Some((aw, ah)) = d.crop.aspect - && handle < 4 - { - let (iw, ih) = (frame.ow, frame.oh); - let a = if (iw >= ih) == (aw >= ah) { aw as f64 / ah as f64 } else { ah as f64 / aw as f64 }; - let w_px = (r.x1 - r.x0).abs() * iw; - let h_px = w_px / a; - let hn = h_px / ih; - if handle == 0 || handle == 1 { - r.y0 = r.y1 - hn; - } else { - r.y1 = r.y0 + hn; - } - } - let rr = lightcraft_geom::Rect::new(r.x0.min(r.x1), r.y0.min(r.y1), r.x0.max(r.x1), r.y0.max(r.y1)); - if rr.width() > 0.02 && rr.height() > 0.02 { - let _ = app.run("crop.set", json!({"rect": [rr.x0, rr.y0, rr.x1, rr.y1]})); - } + let _ = app.run( + "crop.drag", + json!({"handle": handle, "from": [orig.x, orig.y], "to": [n.x, n.y], "start": [start.x0, start.y0, start.x1, start.y1]}), + ); } Some(Gesture::CropRotate { start_angle, a0 }) => { let c = map.screen(Point::new(0.5, 0.5)); diff --git a/crates/ui-egui/src/panels/right.rs b/crates/ui-egui/src/panels/right.rs index 6985e1c9..faf5e822 100644 --- a/crates/ui-egui/src/panels/right.rs +++ b/crates/ui-egui/src/panels/right.rs @@ -62,33 +62,99 @@ fn padded(ui: &mut egui::Ui, add: impl FnOnce(&mut egui::Ui)) { egui::Frame::NONE.inner_margin(egui::Margin { left: 24, right: 22, top: 6, bottom: 6 }).show(ui, add); } +/// The aspect presets of the Crop panel: (menu label, `crop.aspect` value). +const ASPECT_PRESETS: [(&str, &str); 10] = [ + ("Free", "free"), + ("Original", "original"), + ("1 × 1", "1x1"), + ("4 × 5 / 8 × 10", "4x5"), + ("8.5 × 11", "8.5x11"), + ("5 × 7", "5x7"), + ("2 × 3 / 4 × 6", "2x3"), + ("4 × 3", "4x3"), + ("16 × 9", "16x9"), + ("16 × 10", "16x10"), +]; + +/// What the aspect button says for the stored lock (`aspect` = width × 100, height × 100): +/// a preset's name when the ratio matches one in either orientation, "Original" for the photo's own +/// shape, otherwise the ratio as `1.37 : 1`. +fn aspect_label(aspect: Option<(u32, u32)>, original: Option) -> String { + let Some((w, h)) = aspect else { return "Free".into() }; + if w == 0 || h == 0 { + return "Free".into(); + } + let r = w as f64 / h as f64; + let is = |q: f64| q.is_finite() && q > 0.0 && ((r - q).abs() / q < 0.004 || (1.0 / r - q).abs() / q < 0.004); + if original.is_some_and(is) { + return "Original".into(); + } + for (label, key) in ASPECT_PRESETS { + if let Some((x, y)) = key.split_once('x') + && let (Ok(x), Ok(y)) = (x.parse::(), y.parse::()) + && is(x / y) + { + return label.to_string(); + } + } + format!("{:.2} : 1", r.max(1.0 / r)) +} + +/// "Custom" row of the aspect menu: two number fields and an Apply button (`crop.aspect` `[w, h]`). +fn custom_aspect(app: &mut LightcraftApp, ui: &mut egui::Ui) { + let key = egui::Id::new("crop-custom-aspect"); + let (mut w, mut h): (String, String) = ui.data_mut(|d| d.get_temp(key)).unwrap_or_else(|| ("3".into(), "2".into())); + let mut apply = false; + ui.horizontal(|ui| { + ui.label(crate::i18n::tr("Custom")); + let a = ui.add(egui::TextEdit::singleline(&mut w).desired_width(34.0)); + register(ui.ctx(), "field:cropCustomW", a.rect); + ui.label("×"); + let b = ui.add(egui::TextEdit::singleline(&mut h).desired_width(34.0)); + register(ui.ctx(), "field:cropCustomH", b.rect); + let go = text_button(ui, "cropCustomApply", "Apply", false); + apply = go.clicked() || ((a.lost_focus() || b.lost_focus()) && ui.input(|i| i.key_pressed(egui::Key::Enter))); + }); + if apply { + // Accept `3`, `2.5` and `3,5`; anything else (zero, negative, text, huge) is ignored. + let num = |t: &str| t.trim().replace(',', ".").parse::().ok().filter(|v| v.is_finite() && *v > 0.0 && *v <= 1000.0); + if let (Some(x), Some(y)) = (num(&w), num(&h)) + && (1.0 / lightcraft_geom::MAX_RATIO..=lightcraft_geom::MAX_RATIO).contains(&(x / y)) + { + let _ = app.run("crop.aspect", json!({"aspect": [x, y]})); + ui.close(); + } + } + ui.data_mut(|d| d.insert_temp(key, (w, h))); +} + fn crop(app: &mut LightcraftApp, ui: &mut egui::Ui, id: PhotoId) { let d = app.session.develop_of(id).unwrap_or_default(); header(ui, "Crop"); padded(ui, |ui| { + let original = app.session.catalog.photo(id).map(|p| { + let (w, h) = (p.width.max(1) as f64, p.height.max(1) as f64); + if d.orientation.swaps_axes() { h / w } else { w / h } + }); ui.horizontal(|ui| { ui.label(crate::i18n::tr("Aspect Ratio")); - let cur = d.crop.aspect.map(|(w, h)| format!("{} × {}", w as f64 / 100.0, h as f64 / 100.0)).unwrap_or_else(|| "Free".into()); - let r = ui.add(egui::Button::new(crate::i18n::tr(&cur)).frame(false)); - register(ui.ctx(), "button:cropAspect", r.rect); + let cur = aspect_label(d.crop.aspect, original); + let t = Tokens::get(ui.ctx()); + let r = crate::widgets::dropdown(ui, "cropAspect", crate::i18n::tr(&cur), t.font(12.5), t.text); + egui::Popup::menu(&r).show(|ui| { - for (label, a) in [ - ("Free", "free"), - ("Original", "original"), - ("1 × 1", "1x1"), - ("4 × 5 / 8 × 10", "4x5"), - ("8.5 × 11", "8.5x11"), - ("5 × 7", "5x7"), - ("2 × 3 / 4 × 6", "2x3"), - ("4 × 3", "4x3"), - ("16 × 9", "16x9"), - ("16 × 10", "16x10"), - ] { + for (label, a) in ASPECT_PRESETS { if ui.button(crate::i18n::tr(label)).clicked() { let _ = app.run("crop.aspect", json!({"aspect": a})); } } + ui.separator(); + custom_aspect(app, ui); }); + let locked = d.crop.aspect.is_some(); + if text_button(ui, "cropLock", if locked { "Locked" } else { "Lock" }, locked).clicked() { + let _ = app.run("crop.aspect", json!({"aspect": "toggle"})); + } }); ui.add_space(6.0); ui.horizontal_wrapped(|ui| { @@ -969,6 +1035,23 @@ fn activity(app: &mut LightcraftApp, ui: &mut egui::Ui, id: PhotoId) { #[cfg(test)] mod tests { + // Feature: the aspect button tells what is locked + #[test] + fn aspect_label_names_presets_in_either_orientation() { + assert_eq!(super::aspect_label(None, Some(1.5)), "Free"); + assert_eq!(super::aspect_label(Some((1600, 900)), None), "16 × 9"); + assert_eq!(super::aspect_label(Some((900, 1600)), None), "16 × 9", "portrait 16:9"); + assert_eq!(super::aspect_label(Some((400, 500)), None), "4 × 5 / 8 × 10"); + } + + #[test] + fn aspect_label_prefers_original_over_a_coinciding_preset_and_never_shows_pixel_sizes() { + assert_eq!(super::aspect_label(Some((300, 200)), Some(1.5)), "Original"); + assert_eq!(super::aspect_label(Some((600_000, 400_000)), Some(1.5)), "Original"); + assert_eq!(super::aspect_label(Some((137, 100)), Some(1.5)), "1.37 : 1"); + assert_eq!(super::aspect_label(Some((0, 5)), None), "Free"); + } + #[test] fn short_times() { assert_eq!(super::short_time("2026-09-30T12:00:05"), "Sep 30, 2026, 12:00 PM"); diff --git a/docs/parity.md b/docs/parity.md index d5a31ea9..f8397196 100644 --- a/docs/parity.md +++ b/docs/parity.md @@ -287,8 +287,8 @@ under the UI command that wraps it (a no-op error); the UI command now wins. | Id | Feature | Tier | Status | Evidence | Missing / notes | |---|---|---|---|---|---| -| LR-CROP-RECT | Crop rectangle | P0 | ✅ | `cmd:crop.set`, `crates/ui-egui/src/panels/detail.rs` | double-click inside the box applies the crop (like Return) | -| LR-CROP-ASPECT | Aspect ratios | P0 | ✅ | `cmd:crop.aspect`, `cmd:crop.rotateAspect` | no "As Shot"; custom ratio via command params only | +| LR-CROP-RECT | Crop rectangle | P0 | ✅ | `cmd:crop.set`, `cmd:crop.drag`, `crates/geom/src/crop_drag.rs`, `crates/ui-egui/src/panels/detail.rs` | double-click inside the box applies the crop (like Return) | +| LR-CROP-ASPECT | Aspect ratios | P0 | ✅ | `cmd:crop.aspect`, `cmd:crop.rotateAspect`, `crates/ui-egui/src/panels/right.rs` | menu with presets + custom W × H, visible Lock toggle (also A), button names the active ratio; every handle honours the lock and the drag stops at the image edge instead of shifting the frame (issue #295); "Original" is the photo's own shape (no separate As Shot) | | LR-CROP-STRAIGHTEN | Straighten tool | P0 | ✅ | `cmd:crop.straighten`, `cmd:crop.autoStraighten` | Straighten Tool button: drag along a horizon/vertical; double-click or Auto levels automatically | | LR-CROP-AUTO | Auto straighten | P1 | ✅ | `cmd:crop.autoStraighten` | crop-angle leveling from detected horizon/plumb lines (consensus required) | | LR-CROP-ANGLE | Angle slider | P0 | ✅ | `ctl:crop.angle` | | From 912f56cac8dd280807e2097769d3cb5f5581324c Mon Sep 17 00:00:00 2001 From: sebastian pfluegelmeier Date: Thu, 8 Oct 2026 16:38:56 +0200 Subject: [PATCH 19/41] LR-VIEW-ZOOM: add trackpad zoom and pan with stable cursors --- ROADMAP.md | 16 ++- crates/ui-egui/src/control.rs | 8 ++ crates/ui-egui/src/headless.rs | 179 ++++++++++++++++++++++++- crates/ui-egui/src/menus.rs | 40 +++++- crates/ui-egui/src/panels/bottombar.rs | 10 +- crates/ui-egui/src/panels/compare.rs | 24 ++-- crates/ui-egui/src/panels/detail.rs | 146 +++++++++++++++----- crates/ui-egui/src/state.rs | 4 +- docs/control-protocol.md | 11 +- docs/parity.md | 12 +- 10 files changed, 374 insertions(+), 76 deletions(-) diff --git a/ROADMAP.md b/ROADMAP.md index aeb07567..acc5e55b 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -10,7 +10,7 @@ projects (DrawCraft reached its first four milestones in ≈ 4½ h) and are revi [`docs/parity.md`](docs/parity.md) counts features that **exist**; this section is about whether a photographer can **switch** from Lightroom. Update it when a gap below closes.* -**In one line:** the checklist says **79%** (P0 98.5%, P1 95.9%, P2 39%), but measured by whether a working +**In one line:** the checklist says **79.7%** (P0 98.2%, P1 95.6%, P2 41.6%), but measured by whether a working photographer could replace Lightroom without noticing, we are at roughly **60–70%**. The remaining gap is mostly **quality of results and camera coverage**, not missing buttons. @@ -22,7 +22,7 @@ duplicate Local entries #22, black GPU exports on an Intel iGPU #78). | Dimension | Estimate | What's true today | Biggest gaps | |---|---:|---|---| -| **Feature checklist** | 79% | P0 core and P1 nearly complete: import (Add / Copy / Move, templates, devices), library, grid/loupe/compare/survey, every Edit slider, curves, colour grading, masking tools, crop/Upright, heal/clone, presets/profiles, versions/history, sync, export, menus, shortcuts | P1: lens-profile database, content-aware fill (patch synthesis), video playback/trim | +| **Feature checklist** | 79.7% | P0 core and P1 nearly complete: import (Add / Copy / Move, templates, devices), library, grid/loupe/compare/survey, every Edit slider, curves, colour grading, masking tools, crop/Upright, heal/clone, presets/profiles, versions/history, sync, export, menus, shortcuts | P0: full-resolution zoom detail; P1: lens-profile database, content-aware fill (patch synthesis), video playback/trim | | **RAW coverage** (formats people shoot) | ~55% | DNG (all kinds), CR2, ARW, NEF (uncompressed + Huffman lossless/lossy), uncompressed RAF/ORF, RW2 / RWL / Panasonic RAW (every raw format, checked on 178 files from 118 Panasonic and Leica bodies), PEF; every container's embedded preview (incl. CR3) | **CR3** (every Canon since ~2018), compressed RAF/ORF, Nikon lossy-after-split, Canon sRAW, HEIC/AVIF. NEFs labelled compressed but stored uncompressed (Z 6 packed 14-bit, D850 12-bit uncompressed). Per-model verification is thin (~55 corpus files vs >1,000 models) | | **Colour & image quality** | ~55–65% | Pipeline is complete and fast; GPU path CPU-exact within 1/255 | **No measured camera calibration database**: ARW, NEF and RW2 have a guarded per-file embedded-JPEG colour estimate (docs/camera-preview-colour.md); other non-DNG raws and rejected estimates use a neutral matrix. Colour fidelity remains incomplete. No lens-profile database. No measured fidelity against Lightroom (tone, highlights, texture/clarity, NR, sharpening are tuned by eye) | | **AI & computational** | ~15–20% | Assisted culling (focus, bursts), auto tone, HDR/panorama merge; subject/sky/background masks as classical heuristics | Real segmentation masks (subject, sky, people, objects, landscape, depth), AI denoise, super resolution, lens blur, generative remove, faces/people, natural-language search. **Blocked on a model strategy** (licensable weights or our own training; pure-Rust inference is feasible) | @@ -67,6 +67,10 @@ Already closed in the week of 2026-10-03: compressed NEF (#10), Move import (#29 Local roots / cleanup, grid and catalog performance at 85k photos (#35, #37), failed-save reporting, GPU export hardening (#78), copyright metadata (#51), GPX geotagging (#60), import tag help and folder templates (#31, #32). +Image navigation now supports native pinch zoom and two-finger pan in Detail, Compare and Reference views. +The whole-image preview still has a configurable size cap (2,560 px default), so 100% on larger photos does not +yet guarantee original-pixel detail; visible-region rendering remains a gap (LR-VIEW-ZOOM). + ## Milestones **Status legend:** ✅ done · 🚧 in progress · ⬜ not started @@ -98,10 +102,10 @@ parity` prints this line on every run, so it stays current): | Scope | Weighted completion | Rows | 2026-10-02 | |---|---:|---:|---:| -| P0 (core) | **98.5%** | 200 | 98.7% | -| P1 (important parity) | **95.9%** | 147 | 94.4% | -| P2 (later / AI / niche, incl. Classic modules) | **39.2%** | 158 | 29.4% | -| **All in-scope rows** | **79.2%** | 505 | 75.6% | +| P0 (core) | **98.2%** | 200 | 98.7% | +| P1 (important parity) | **95.6%** | 149 | 94.4% | +| P2 (later / AI / niche, incl. Classic modules) | **41.6%** | 160 | 29.4% | +| **All in-scope rows** | **79.7%** | 509 | 75.6% | The 2026-10-05 count includes three new 🟡 rows that make quality gaps visible (camera colour calibration, verified camera coverage, render fidelity), which is why P0 dipped slightly. diff --git a/crates/ui-egui/src/control.rs b/crates/ui-egui/src/control.rs index 1a6abdde..beafbffa 100644 --- a/crates/ui-egui/src/control.rs +++ b/crates/ui-egui/src/control.rs @@ -258,6 +258,14 @@ pub fn handle(app: &mut LightcraftApp, ctx: &egui::Context, req: &ControlRequest ctx.request_repaint(); ok(Value::Null) } + "ui.zoom" => { + let Some(factor) = f("factor").map(|v| v as f32).filter(|v| v.is_finite() && *v > 0.0) else { + return err("ui.zoom: factor must be a finite positive number (1 = unchanged)"); + }; + app.synthetic.push(egui::Event::Zoom(factor)); + ctx.request_repaint(); + ok(Value::Null) + } "ui.set" => { let mut v = serde_json::to_value(&app.ui).unwrap_or_default(); lightcraft_develop::presets::deep_merge(&mut v, p); diff --git a/crates/ui-egui/src/headless.rs b/crates/ui-egui/src/headless.rs index 976ecd55..ef686588 100644 --- a/crates/ui-egui/src/headless.rs +++ b/crates/ui-egui/src/headless.rs @@ -1782,7 +1782,7 @@ mod tests { assert_ne!(r["ok"], true, "{r}"); let fit = h.app.image_rect.unwrap(); h.request("ui.clickWidget", json!({"id": "canvas:image", "fx": 0.5, "fy": 0.5}), t); - assert_eq!(h.app.ui.zoom, Zoom::Percent(300)); + assert_eq!(h.app.ui.zoom, Zoom::Percent(300.0)); assert!(h.app.ui.zoom_anim, "animation started"); // every animation frame asks for the same (final-size) render let mut keys = std::collections::HashSet::new(); @@ -1801,12 +1801,185 @@ mod tests { assert_eq!(h.app.ui.zoom, Zoom::Fit); // Z zooms to the same ratio as a click h.request("ui.key", json!({"key": "z"}), t); - assert_eq!(h.app.ui.zoom, Zoom::Percent(300)); + assert_eq!(h.app.ui.zoom, Zoom::Percent(300.0)); h.request("ui.key", json!({"key": "z"}), t); assert_eq!(h.app.ui.zoom, Zoom::Fit); h.settle(SETTLE); } + #[test] + fn trackpad_navigation_keeps_the_image_cursor_between_events() { + let mut h = demo([1000.0, 700.0]); + let t = Duration::from_secs(10); + h.request("ui.set", json!({"view": "detail", "right": "none", "filmstrip": false}), t); + h.request("engine.execute", json!({"command": "view.zoom100"}), t); + h.settle(SETTLE); + let anchor = h.app.canvas_rect.unwrap().center(); + h.request("ui.move", json!({"x": anchor.x, "y": anchor.y}), t); + for (tool, expected) in [ + ("", egui::CursorIcon::Grab), + ("wbPicker", egui::CursorIcon::Crosshair), + ("colorRange", egui::CursorIcon::Crosshair), + ("pointColor", egui::CursorIcon::Crosshair), + ("tat:curve", egui::CursorIcon::ResizeVertical), + ] { + h.app.ui.tool = tool.into(); + for event in [ + None, + Some(egui::Event::Zoom(1.01)), + None, + Some(egui::Event::Zoom(1.01)), + Some(egui::Event::MouseWheel { + unit: egui::MouseWheelUnit::Point, + delta: egui::vec2(10.0, -10.0), + phase: egui::TouchPhase::Move, + modifiers: egui::Modifiers::NONE, + }), + None, + ] { + let raw = HeadlessView::raw_input(h.size, h.pixels_per_point, h.time, event.into_iter().collect()); + let mut cursor = egui::CursorIcon::Default; + h.view.run(raw, |ui| { + h.app.logic(ui.ctx()); + h.app.ui(ui); + cursor = ui.ctx().output(|output| output.cursor_icon); + }); + h.time += FRAME_DT; + assert_eq!(cursor, expected, "cursor changed between navigation events with tool {tool:?}"); + } + } + } + + #[test] + fn trackpad_pinch_anchors_fractional_zoom_and_scroll_pans() { + use crate::state::Zoom; + let mut h = demo([1000.0, 700.0]); + h.pixels_per_point = 2.0; + let t = Duration::from_secs(10); + h.request("ui.set", json!({"view": "detail", "right": "none", "leftPanel": false, "filmstrip": false}), t); + h.request("engine.execute", json!({"command": "view.zoom100"}), t); + h.settle(SETTLE); + let before = h.app.image_rect.unwrap(); + let anchor = h.app.canvas_rect.unwrap().center() + egui::vec2(30.0, -20.0); + h.request("ui.move", json!({"x": anchor.x, "y": anchor.y}), t); + let point = (anchor - before.min) / before.size(); + h.request("ui.zoom", json!({"factor": 1.001}), t); + let after = h.app.image_rect.unwrap(); + assert!((after.width() / before.width() - 1.001).abs() < 0.00001); + assert!(((anchor - after.min) / after.size() - point).length() < 0.00001, "point under pointer moved"); + assert!(matches!(h.app.ui.zoom, Zoom::Percent(p) if (p - 100.1).abs() < 0.001), "{:?}", h.app.ui.zoom); + assert!(!h.app.ui.zoom_anim, "pinch follows the fingers immediately"); + + h.request("ui.scroll", json!({"dx": 40.0, "dy": -30.0}), t); + for _ in 0..40 { + h.step(); + } + let panned = h.app.image_rect.unwrap(); + assert!(panned.left() > after.left() && panned.top() < after.top(), "two-finger pan must move both axes"); + h.request("ui.scroll", json!({"dx": 100000.0, "dy": -100000.0}), t); + for _ in 0..40 { + h.step(); + } + let edge = h.app.image_rect.unwrap(); + let area = h.app.canvas_rect.unwrap().shrink(24.0); + assert!((edge.left() - area.left()).abs() < 0.01 && (edge.bottom() - area.bottom()).abs() < 0.01, "{edge:?} vs {area:?}"); + + h.request("ui.zoom", json!({"factor": 1e20}), t); + assert_eq!(h.app.ui.zoom, Zoom::Percent(800.0)); + h.request("ui.zoom", json!({"factor": 0.000001}), t); + assert_eq!(h.app.ui.zoom, Zoom::Fit); + assert_eq!(h.app.ui.pan, (0.5, 0.5)); + // Panel / toolbar gestures must not move the image. + h.request("ui.move", json!({"x": 10, "y": 10}), t); + h.request("ui.zoom", json!({"factor": 2}), t); + h.request("ui.scroll", json!({"dx": 100, "dy": 100}), t); + assert_eq!(h.app.ui.zoom, Zoom::Fit); + assert_eq!(h.app.ui.pan, (0.5, 0.5)); + } + + #[test] + fn trackpad_navigation_works_in_tools_compare_reference_and_before_after() { + use crate::state::Zoom; + let mut h = demo([1000.0, 700.0]); + let t = Duration::from_secs(10); + h.request("ui.set", json!({"view": "detail", "leftPanel": false, "filmstrip": false}), t); + h.settle(SETTLE); + let active = h.app.session.active().unwrap(); + let settings = h.app.session.catalog.photo(active).unwrap().develop.clone(); + for right in ["crop", "masking", "remove", "redEye", "none"] { + h.request("ui.set", json!({"right": right, "zoom": "fit", "pan": [0.5, 0.5]}), t); + h.request("ui.hoverWidget", json!({"id": "canvas:image"}), t); + h.request("ui.zoom", json!({"factor": 2}), t); + assert!(matches!(h.app.ui.zoom, Zoom::Percent(_)), "pinch in {right}"); + assert_eq!(h.app.session.catalog.photo(active).unwrap().develop, settings, "navigation must not edit the photo"); + } + h.request("ui.set", json!({"fullscreen": true, "zoom": "fit"}), t); + h.request("ui.hoverWidget", json!({"id": "canvas:image"}), t); + h.request("ui.zoom", json!({"factor": 2}), t); + assert!(matches!(h.app.ui.zoom, Zoom::Percent(_)), "full-screen pinch"); + + h.request("ui.set", json!({"fullscreen": false, "zoom": "fit", "beforeAfter": "sideBySide"}), t); + let canvas = h.app.canvas_rect.unwrap(); + h.request("ui.move", json!({"x": canvas.left() + canvas.width() * 0.25, "y": canvas.center().y}), t); + h.request("ui.zoom", json!({"factor": 2}), t); + assert!(matches!(h.app.ui.zoom, Zoom::Percent(_)), "pinch over the Before pane"); + + h.request("ui.set", json!({"beforeAfter": "off", "zoom": "fit"}), t); + h.request("engine.execute", json!({"command": "view.compare"}), t); + // The left Compare pane must navigate too; zoom/pan are shared with the right pane. + let canvas = h.app.canvas_rect.unwrap(); + h.request("ui.move", json!({"x": canvas.left() + canvas.width() * 0.25, "y": canvas.center().y}), t); + h.request("ui.zoom", json!({"factor": 2}), t); + assert!(matches!(h.app.ui.zoom, Zoom::Percent(_)), "Compare pinch"); + let pan = h.app.ui.pan; + h.request("ui.scroll", json!({"dx": -50, "dy": -50}), t); + assert_ne!(h.app.ui.pan, pan, "Compare pan"); + + h.request("ui.set", json!({"zoom": "fit", "pan": [0.5, 0.5]}), t); + h.request("engine.execute", json!({"command": "view.reference"}), t); + h.request("ui.move", json!({"x": canvas.left() + canvas.width() * 0.25, "y": canvas.center().y}), t); + h.request("ui.zoom", json!({"factor": 2}), t); + assert!(matches!(h.app.ui.zoom, Zoom::Percent(_)), "Reference pinch"); + } + + #[test] + fn navigation_rejects_invalid_input_and_preserves_old_zoom_state() { + use crate::state::Zoom; + let mut h = demo([1000.0, 700.0]); + let t = Duration::from_secs(10); + assert_eq!(serde_json::from_value::(json!({"percent": 100})).unwrap(), Zoom::Percent(100.0)); + for factor in [json!(0), json!(-1), json!(1e308), json!("big"), Value::Null] { + let r = h.request("ui.zoom", json!({"factor": factor}), t); + assert_eq!(r["ok"], false, "{r}"); + } + for params in [ + json!({"zoom": {"percent": -1}}), + json!({"zoom": {"percent": 1e308}}), + json!({"zoom": "oops"}), + json!({"zoom": {"percent": 123.4}, "pan": [-1, 0.5]}), + json!({"pan": [0.5, 1e308]}), + json!({"pan": [0.5]}), + ] { + assert!(h.app.run("view.navigate", params).is_err()); + assert_eq!(h.app.ui.zoom, Zoom::Fit, "failed request changed zoom"); + assert_eq!(h.app.ui.pan, (0.5, 0.5), "failed request changed pan"); + } + h.app.run("view.navigate", json!({"zoom": {"percent": 123.4}, "pan": [0.4, 0.6]})).unwrap(); + assert_eq!(h.app.ui.zoom, Zoom::Percent(123.4)); + assert_eq!(h.app.ui.pan, (0.4, 0.6)); + h.app.run("view.zoomIn", json!({})).unwrap(); + assert_eq!(h.app.ui.zoom, Zoom::Percent(200.0)); + h.app.run("view.zoomOut", json!({})).unwrap(); + assert_eq!(h.app.ui.zoom, Zoom::Percent(100.0)); + + let area = egui::Rect::from_min_size(egui::pos2(100.0, 100.0), egui::vec2(1000.0, 700.0)); + for pan in [(0.0, 0.0), (1.0, 1.0)] { + let image = crate::panels::detail::fit_rect(area, 10.0, Zoom::Percent(100.0), [4000, 400], 1.0, pan); + assert_eq!(image.center().y, area.center().y, "smaller axis must stay centred"); + assert!(image.left() <= area.left() && image.right() >= area.right(), "pan exposed space outside the photo"); + } + } + /// The Navigator appears when zoomed in; clicking it pans to that point. #[test] fn navigator_pans_the_zoomed_loupe() { @@ -1821,7 +1994,7 @@ mod tests { h.request("ui.clickWidget", json!({"id": "canvas:navigator", "fx": 0.1, "fy": 0.2}), t); let (u, v) = h.app.ui.pan; assert!((u - 0.1).abs() < 0.03 && (v - 0.2).abs() < 0.03, "pan {:?}", h.app.ui.pan); - assert_eq!(h.app.ui.zoom, crate::state::Zoom::Percent(100), "the click didn't reach the loupe (which would zoom out)"); + assert_eq!(h.app.ui.zoom, crate::state::Zoom::Percent(100.0), "the click didn't reach the loupe (which would zoom out)"); h.settle(SETTLE); let nav = h.app.widgets.iter().rev().find(|(w, _)| w == "canvas:navigator").map(|(_, r)| *r).unwrap(); let to = nav.min + egui::vec2(nav.width() * 0.8, nav.height() * 0.7); diff --git a/crates/ui-egui/src/menus.rs b/crates/ui-egui/src/menus.rs index 9e62a382..f37ebd49 100644 --- a/crates/ui-egui/src/menus.rs +++ b/crates/ui-egui/src/menus.rs @@ -67,6 +67,7 @@ pub const UI_COMMANDS: &[UiCommand] = &[ ("view.zoomToggle", "Toggle Zoom", Some("Z"), "View"), // the ratio a click (and Z / Space) zooms to ("view.clickZoom", "Click Zoom Ratio", None, ""), + ("view.navigate", "Set Image Zoom and Pan", None, ""), ("view.zoomIn", "Zoom In", Some("Cmd+="), "View"), ("view.zoomOut", "Zoom Out", Some("Cmd+-"), "View"), ("view.clipping", "Show Clipping", Some("J"), "View"), @@ -463,12 +464,12 @@ pub fn run_ui_command(app: &mut LightcraftApp, id: &str, p: &Value) -> Option { - app.ui.zoom = Zoom::Percent(100); + app.ui.zoom = Zoom::Percent(100.0); Ok(Value::Null) } "view.zoomToggle" => { // the same ratio a click on the photo zooms to - app.ui.zoom = if app.ui.zoom == Zoom::Fit { Zoom::Percent(app.ui.click_zoom) } else { Zoom::Fit }; + app.ui.zoom = if app.ui.zoom == Zoom::Fit { Zoom::Percent(app.ui.click_zoom as f32) } else { Zoom::Fit }; app.ui.zoom_anim = true; Ok(Value::Null) } @@ -484,19 +485,44 @@ pub fn run_ui_command(app: &mut LightcraftApp, id: &str, p: &Value) -> Option { - let steps = [25u32, 50, 100, 200, 400, 800]; + let steps = [25.0, 50.0, 100.0, 200.0, 400.0, 800.0]; let cur = match app.ui.zoom { Zoom::Percent(p) => p, - _ => 25, + _ => 25.0, }; let next = if id == "view.zoomIn" { - steps.iter().find(|s| **s > cur).copied().unwrap_or(800) + steps.iter().find(|s| **s > cur).copied().unwrap_or(800.0) } else { - steps.iter().rev().find(|s| **s < cur).copied().unwrap_or(0) + steps.iter().rev().find(|s| **s < cur).copied().unwrap_or(0.0) }; - app.ui.zoom = if next == 0 { Zoom::Fit } else { Zoom::Percent(next) }; + app.ui.zoom = if next == 0.0 { Zoom::Fit } else { Zoom::Percent(next) }; Ok(Value::Null) } + "view.navigate" => { + // {zoom?: "fit"|"fill"|{percent: number}, pan?: [x, y]} (normalized image centre). + // Validate the complete request before changing either part of the viewport. + let zoom = match p.get("zoom") { + Some(v) => match serde_json::from_value::(v.clone()) { + Ok(Zoom::Percent(p)) if !p.is_finite() || p <= 0.0 || p > 800.0 => { + return Some(Err("view.navigate: zoom percent must be greater than 0 and at most 800".into())); + } + Ok(z) => z, + Err(e) => return Some(Err(format!("view.navigate: {e}"))), + }, + None => app.ui.zoom, + }; + let pan = match p.get("pan") { + Some(v) => match serde_json::from_value::<(f32, f32)>(v.clone()) { + Ok((x, y)) if x.is_finite() && y.is_finite() && (0.0..=1.0).contains(&x) && (0.0..=1.0).contains(&y) => (x, y), + _ => return Some(Err("view.navigate: pan must be [x, y] with finite coordinates from 0 to 1".into())), + }, + None => app.ui.pan, + }; + app.ui.zoom = zoom; + app.ui.pan = pan; + app.ui.zoom_anim = false; + Ok(json!({"zoom": zoom, "pan": pan})) + } "view.clipping" => { app.ui.show_clipping = !app.ui.show_clipping; Ok(Value::Null) diff --git a/crates/ui-egui/src/panels/bottombar.rs b/crates/ui-egui/src/panels/bottombar.rs index f75feca4..bd3ba928 100644 --- a/crates/ui-egui/src/panels/bottombar.rs +++ b/crates/ui-egui/src/panels/bottombar.rs @@ -178,17 +178,17 @@ fn right_side(app: &mut LightcraftApp, ui: &mut egui::Ui, full: Rect) -> f32 { let zoom_label = match app.ui.zoom { Zoom::Fit => "Fit".to_string(), Zoom::Fill => "Fill".to_string(), - Zoom::Percent(p) => format!("{p}%"), + Zoom::Percent(p) => format!("{p:.0}%"), }; let zr = crate::widgets::dropdown(&mut child, "zoom", crate::i18n::tr(&zoom_label), t.font(13.0), t.text_label); egui::Popup::menu(&zr).show(|ui| { for (label, z) in [ ("Fit", Zoom::Fit), ("Fill", Zoom::Fill), - ("50%", Zoom::Percent(50)), - ("100%", Zoom::Percent(100)), - ("200%", Zoom::Percent(200)), - ("400%", Zoom::Percent(400)), + ("50%", Zoom::Percent(50.0)), + ("100%", Zoom::Percent(100.0)), + ("200%", Zoom::Percent(200.0)), + ("400%", Zoom::Percent(400.0)), ] { if ui.selectable_label(app.ui.zoom == z, label).clicked() { app.ui.zoom = z; diff --git a/crates/ui-egui/src/panels/compare.rs b/crates/ui-egui/src/panels/compare.rs index 07abae24..16db642e 100644 --- a/crates/ui-egui/src/panels/compare.rs +++ b/crates/ui-egui/src/panels/compare.rs @@ -160,7 +160,14 @@ fn photo_tile(app: &mut LightcraftApp, ui: &mut egui::Ui, id: PhotoId, slot: Slo ); let aspect = frame.aspect() as f32; let native = [photo.width.max(1) as usize, photo.height.max(1) as usize]; - let img = super::detail::fit_rect(img_area, aspect, zoom, native, ppp, app.ui.pan); + let mut img = super::detail::fit_rect(img_area, aspect, zoom, native, ppp, app.ui.pan); + if matches!(app.ui.view, ViewMode::Compare | ViewMode::Reference) { + if super::detail::navigate_gesture(app, ui, &resp, img_area, img, native) { + img = super::detail::fit_rect(img_area, aspect, app.ui.zoom, native, ppp, app.ui.pan); + } else if resp.dragged() { + super::detail::pan_image(app, img_area, img, resp.drag_delta()); + } + } let want = (img.width().max(img.height()).min(img_area.width().max(img_area.height()) * 4.0) * ppp).min(2560.0) as usize; let (rw, rh) = if aspect >= 1.0 { (want, (want as f32 / aspect) as usize) } else { ((want as f32 * aspect) as usize, want) }; if let Some(job) = app.session.render_job(id, rw.max(8), rh.max(8), false, true) { @@ -228,15 +235,6 @@ fn slot_index(s: Slot) -> u8 { } } -/// Drag-to-pan when zoomed in (shared by both compare panes: zoom and pan are synced). -fn pan(app: &mut LightcraftApp, resp: &egui::Response, img: Rect) { - if app.ui.zoom != Zoom::Fit && resp.dragged() { - let d = resp.drag_delta(); - let (px, py) = app.ui.pan; - app.ui.pan = ((px - d.x / img.width().max(1.0)).clamp(0.0, 1.0), (py - d.y / img.height().max(1.0)).clamp(0.0, 1.0)); - } -} - pub fn show_compare(app: &mut LightcraftApp, ui: &mut egui::Ui) { let canvas = area_and_filmstrip(app, ui); let Some((sel, cand)) = compare_pair(app) else { @@ -255,12 +253,11 @@ pub fn show_compare(app: &mut LightcraftApp, ui: &mut egui::Ui) { if i == 1 { app.image_rect = Some(img); } - pan(app, &resp, img); if resp.clicked() { select_pair(app, sel, cand, id); } if resp.double_clicked() { - app.ui.zoom = if app.ui.zoom == Zoom::Fit { Zoom::Percent(100) } else { Zoom::Fit }; + app.ui.zoom = if app.ui.zoom == Zoom::Fit { Zoom::Percent(100.0) } else { Zoom::Fit }; } resp.context_menu(|ui| { if ui.button(crate::i18n::tr("Swap")).clicked() { @@ -356,9 +353,8 @@ pub fn show_reference(app: &mut LightcraftApp, ui: &mut egui::Ui) { let left = Rect::from_min_size(area.min, vec2(half, area.height())); let right = Rect::from_min_size(pos2(area.right() - half, area.top()), vec2(half, area.height())); let (_, lresp) = photo_tile(app, ui, r, Slot::Compare(0), left, "Reference", zoom); - let (img, resp) = photo_tile(app, ui, active, Slot::Compare(1), right, "Active", zoom); + let (img, _) = photo_tile(app, ui, active, Slot::Compare(1), right, "Active", zoom); app.image_rect = Some(img); - pan(app, &resp, img); lresp.context_menu(|ui| { if ui.button(crate::i18n::tr("Clear Reference")).clicked() { app.ui.reference = None; diff --git a/crates/ui-egui/src/panels/detail.rs b/crates/ui-egui/src/panels/detail.rs index 3b3622be..b15a7fd8 100644 --- a/crates/ui-egui/src/panels/detail.rs +++ b/crates/ui-egui/src/panels/detail.rs @@ -94,6 +94,10 @@ impl CanvasMap { } pub(crate) fn fit_rect(area: Rect, aspect: f32, zoom: Zoom, img_px: [usize; 2], ppp: f32, pan: (f32, f32)) -> Rect { + let zoom = match zoom { + Zoom::Percent(p) if !p.is_finite() || p <= 0.0 => Zoom::Fit, + _ => zoom, + }; let (aw, ah) = (area.width(), area.height()); let (w, h) = match zoom { Zoom::Fit => { @@ -112,19 +116,69 @@ pub(crate) fn fit_rect(area: Rect, aspect: f32, zoom: Zoom, img_px: [usize; 2], } Zoom::Percent(p) => { // full-resolution pixels at p% (the photo's native width) - let w = img_px[0] as f32 * p as f32 / 100.0 / ppp; + let w = img_px[0] as f32 * p / 100.0 / ppp; (w, w / aspect) } }; - let c = if w <= aw && h <= ah { - area.center() - } else { - // pan: which normalized point of the image sits at the area centre - pos2(area.center().x - (pan.0 - 0.5) * w, area.center().y - (pan.1 - 0.5) * h) - }; + let pan = bounded_pan(area, vec2(w, h), pan); + let c = pos2(area.center().x - (pan.0 - 0.5) * w, area.center().y - (pan.1 - 0.5) * h); Rect::from_center_size(c, vec2(w, h)) } +/// Keep the viewport inside the photo; axes smaller than the viewport stay centred. +fn bounded_pan(area: Rect, size: egui::Vec2, pan: (f32, f32)) -> (f32, f32) { + let bound = |visible: f32, image: f32, centre: f32| { + let centre = if centre.is_finite() { centre } else { 0.5 }; + if image > visible.max(0.0) { + let half = visible.max(0.0) / image / 2.0; + centre.clamp(half, 1.0 - half) + } else { + 0.5 + } + }; + (bound(area.width(), size.x, pan.0), bound(area.height(), size.y, pan.1)) +} + +pub(crate) fn pan_image(app: &mut LightcraftApp, area: Rect, img: Rect, delta: egui::Vec2) { + let centre = area.center() - img.min - delta; + let pan = bounded_pan(area, img.size(), (centre.x / img.width().max(1.0), centre.y / img.height().max(1.0))); + let _ = app.run("view.navigate", json!({"pan": pan})); +} + +/// Native pinch (also modifier-wheel zoom) and two-finger scroll, scoped to this image view. +pub(crate) fn navigate_gesture(app: &mut LightcraftApp, ui: &mut egui::Ui, resp: &egui::Response, area: Rect, img: Rect, native: [usize; 2]) -> bool { + if !resp.hovered() { + return false; + } + let (factor, delta, anchor) = + ui.input(|i| (i.zoom_delta(), i.translation_delta(), i.multi_touch().map(|t| t.center_pos).or(i.pointer.hover_pos()))); + if !factor.is_finite() || factor <= 0.0 || !delta.is_finite() || (factor == 1.0 && delta == egui::Vec2::ZERO) { + return false; + } + let Some(anchor) = anchor.filter(|p| area.contains(*p)) else { return false }; + let ppp = ui.ctx().pixels_per_point(); + let aspect = img.width() / img.height().max(1.0); + let fit = fit_rect(area, aspect, Zoom::Fit, native, ppp, (0.5, 0.5)); + let fit_pct = fit.width() * ppp * 100.0 / native[0].max(1) as f32; + let mut zoom = app.ui.zoom; + let size = if factor != 1.0 { + // Start at the displayed scale (including Fit/Fill and an interrupted click animation). + let pct = (img.width() * ppp * 100.0 / native[0].max(1) as f32 * factor).clamp(fit_pct.min(800.0), 800.0); + zoom = if pct <= fit_pct { Zoom::Fit } else { Zoom::Percent(pct) }; + fit_rect(area, aspect, zoom, native, ppp, (0.5, 0.5)).size() + } else { + img.size() + }; + // The image point under the pointer remains there as its size changes, then follows the pan. + let point = (anchor - img.min) / img.size(); + let offset = area.center() - anchor - delta; + let pan = bounded_pan(area, size, (point.x + offset.x / size.x.max(1.0), point.y + offset.y / size.y.max(1.0))); + let _ = app.run("view.navigate", json!({"zoom": zoom, "pan": pan})); + ui.input_mut(|i| i.smooth_scroll_delta = egui::Vec2::ZERO); + ui.ctx().request_repaint(); + true +} + /// Ease the loupe rect toward `target` while a click-zoom animation runs; otherwise follow it exactly. fn animated_rect(ctx: &egui::Context, anim: &mut bool, target: Rect) -> Rect { let t = if *anim { 0.22 } else { 0.0 }; @@ -191,8 +245,19 @@ pub fn show(app: &mut LightcraftApp, ui: &mut egui::Ui) { _ => vec![area], }; let main_area = *areas.last().unwrap_or(&area); - let target_rect = fit_rect(main_area, aspect, app.ui.zoom, native, ppp, app.ui.pan); - let img_rect = animated_rect(ui.ctx(), &mut app.ui.zoom_anim, target_rect); + let resp = ui.interact(canvas, egui::Id::new("loupe"), Sense::click_and_drag()); + let mut target_rect = fit_rect(main_area, aspect, app.ui.zoom, native, ppp, app.ui.pan); + let mut img_rect = animated_rect(ui.ctx(), &mut app.ui.zoom_anim, target_rect); + let mut navigating = false; + for &view_area in &areas { + let view_img = if view_area == main_area { img_rect } else { fit_rect(view_area, aspect, app.ui.zoom, native, ppp, app.ui.pan) }; + if navigate_gesture(app, ui, &resp, view_area, view_img, native) { + navigating = true; + target_rect = fit_rect(main_area, aspect, app.ui.zoom, native, ppp, app.ui.pan); + img_rect = target_rect; + break; + } + } app.image_rect = Some(img_rect); // request renders: the loupe at display resolution (drafts during drags) let interacting = app.session.interaction.is_some(); @@ -332,7 +397,6 @@ pub fn show(app: &mut LightcraftApp, ui: &mut egui::Ui) { } register(ui.ctx(), "canvas:image", img_rect); let map = CanvasMap::new(&frame, img_rect); - let resp = ui.interact(canvas, egui::Id::new("loupe"), Sense::click_and_drag()); info_overlay(app, &p, canvas, &photo); if !fullscreen { filter_pill(app, ui, canvas); @@ -362,12 +426,18 @@ pub fn show(app: &mut LightcraftApp, ui: &mut egui::Ui) { p.line_segment([pos2(img_rect.left(), fy), pos2(img_rect.right(), fy)], stroke); } } - match right { - RightPanel::Crop => crop_overlay(app, ui, &resp, &map, &frame, &d, id), - RightPanel::Masking => mask_overlay(app, ui, &resp, &map, &d), - RightPanel::Remove => remove_overlay(app, ui, &resp, &map, &d), - RightPanel::RedEye => eye_overlay(app, ui, &resp, &map, &d), - _ => general_interaction(app, ui, &resp, &map, img_rect, canvas, native, aspect), + // Cursor selection must also run on gesture frames, when tool input is suppressed. + if !matches!(right, RightPanel::Crop | RightPanel::Masking | RightPanel::Remove | RightPanel::RedEye) { + general_cursor(app, ui, &resp, img_rect, main_area); + } + if !navigating { + match right { + RightPanel::Crop => crop_overlay(app, ui, &resp, &map, &frame, &d, id), + RightPanel::Masking => mask_overlay(app, ui, &resp, &map, &d), + RightPanel::Remove => remove_overlay(app, ui, &resp, &map, &d), + RightPanel::RedEye => eye_overlay(app, ui, &resp, &map, &d), + _ => general_interaction(app, ui, &resp, &map, img_rect, main_area, native, aspect), + } } // drawn and hit-tested above the loupe and its tools: clicks on it pan navigator(app, ui, canvas, img_rect, id); @@ -669,8 +739,7 @@ fn quick_name(q: lightcraft_engine::media::QuickSource) -> &'static str { /// Targeted adjustment tool: dragging up/down on the photo raises/lowers the tone-curve region or /// the colour-mixer bands under the press point (`develop.targeted`, one call per whole step, all /// in one interaction = one undo step). -fn targeted_drag(app: &mut LightcraftApp, ui: &mut egui::Ui, resp: &egui::Response, map: &CanvasMap, target: &str) { - ui.ctx().set_cursor_icon(egui::CursorIcon::ResizeVertical); +fn targeted_drag(app: &mut LightcraftApp, resp: &egui::Response, map: &CanvasMap, target: &str) { if resp.drag_started() && let Some(q) = resp.interact_pointer_pos() { @@ -740,6 +809,26 @@ fn clipping_overlay(app: &LightcraftApp, p: &egui::Painter, r: Rect) { } } +fn general_cursor(app: &LightcraftApp, ui: &egui::Ui, resp: &egui::Response, img: Rect, canvas: Rect) { + // A panel or overlay under the pointer keeps its own cursor. + if !resp.dragged() && !resp.hovered() { + return; + } + let cursor = match app.ui.tool.as_str() { + "wbPicker" | "colorRange" | "pointColor" => egui::CursorIcon::Crosshair, + tool if tool.starts_with("tat:") => egui::CursorIcon::ResizeVertical, + _ if img.width() > canvas.width() + 1.0 || img.height() > canvas.height() + 1.0 => { + if resp.dragged() { + egui::CursorIcon::Grabbing + } else { + egui::CursorIcon::Grab + } + } + _ => return, + }; + ui.ctx().set_cursor_icon(cursor); +} + fn general_interaction( app: &mut LightcraftApp, ui: &mut egui::Ui, @@ -751,7 +840,6 @@ fn general_interaction( aspect: f32, ) { if app.ui.tool == "wbPicker" { - ui.ctx().set_cursor_icon(egui::CursorIcon::Crosshair); if resp.clicked() && let Some(q) = resp.interact_pointer_pos() { @@ -762,12 +850,11 @@ fn general_interaction( return; } if let Some(target) = app.ui.tool.strip_prefix("tat:").map(str::to_string) { - targeted_drag(app, ui, resp, map, &target); + targeted_drag(app, resp, map, &target); return; } if app.ui.tool == "colorRange" { // click: sample the colour for the selected mask's colour range; ⇧-click adds (up to 5) - ui.ctx().set_cursor_icon(egui::CursorIcon::Crosshair); if resp.clicked() && let Some(q) = resp.interact_pointer_pos() { @@ -780,7 +867,6 @@ fn general_interaction( return; } if app.ui.tool == "pointColor" { - ui.ctx().set_cursor_icon(egui::CursorIcon::Crosshair); if resp.clicked() && let Some(q) = resp.interact_pointer_pos() { @@ -792,7 +878,7 @@ fn general_interaction( } return; } - // click toggles Fit ↔ the click-zoom ratio (2:1/3:1/5:1) at the clicked point; drag pans when zoomed + // click toggles Fit ↔ the chosen click-zoom ratio at the clicked point; drag pans when zoomed let zoomed = img.width() > canvas.width() + 1.0 || img.height() > canvas.height() + 1.0; if resp.double_clicked() || (resp.clicked() && !zoomed) { if let Some(q) = resp.interact_pointer_pos() { @@ -800,22 +886,14 @@ fn general_interaction( let v = ((q.y - img.top()) / img.height()).clamp(0.0, 1.0); app.ui.pan = (u, v); } - app.ui.zoom = if matches!(app.ui.zoom, Zoom::Fit) { Zoom::Percent(app.ui.click_zoom) } else { Zoom::Fit }; + app.ui.zoom = if matches!(app.ui.zoom, Zoom::Fit) { Zoom::Percent(app.ui.click_zoom as f32) } else { Zoom::Fit }; app.ui.zoom_anim = true; } else if resp.clicked() && zoomed { app.ui.zoom = Zoom::Fit; app.ui.zoom_anim = true; } - if zoomed { - // only over the photo: a panel drawn earlier (sliders) must keep its own cursor - if resp.dragged() || resp.hovered() { - ui.ctx().set_cursor_icon(if resp.dragged() { egui::CursorIcon::Grabbing } else { egui::CursorIcon::Grab }); - } - if resp.dragged() { - let dlt = resp.drag_delta(); - app.ui.pan.0 = (app.ui.pan.0 - dlt.x / img.width()).clamp(0.0, 1.0); - app.ui.pan.1 = (app.ui.pan.1 - dlt.y / img.height()).clamp(0.0, 1.0); - } + if zoomed && resp.dragged() { + pan_image(app, canvas, img, resp.drag_delta()); } let _ = (native, aspect); } diff --git a/crates/ui-egui/src/state.rs b/crates/ui-egui/src/state.rs index 9d4df859..e32a29d8 100644 --- a/crates/ui-egui/src/state.rs +++ b/crates/ui-egui/src/state.rs @@ -45,14 +45,14 @@ impl RightPanel { } } -#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Clone, Copy, Debug, Default, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub enum Zoom { #[default] Fit, Fill, /// 100 % = one image pixel per physical screen pixel. - Percent(u32), + Percent(f32), } #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] diff --git a/docs/control-protocol.md b/docs/control-protocol.md index 24fd2bdb..b5868873 100644 --- a/docs/control-protocol.md +++ b/docs/control-protocol.md @@ -37,7 +37,8 @@ mode ([mcp.md](mcp.md)) is a thin layer over this channel. Implementation: | `ui.pointer` | `{events: [{kind: down\|drag\|up, x, y}], alt?, shift?, cmd?}` | Gesture in normalized image coordinates (Detail view) | | `ui.key` | `{key, cmd?, shift?, alt?, ctrl?}` | Key press | | `ui.text` | `{text}` | Text input | -| `ui.scroll` | `{dx, dy}` | Mouse wheel | +| `ui.scroll` | `{dx, dy, cmd?, ctrl?, shift?, alt?}` | Wheel / two-finger scroll at the current pointer; pans over the image, modifier-scroll zooms | +| `ui.zoom` | `{factor}` | Pinch zoom at the current pointer (positive scale multiplier; 1 = unchanged). Position it first with `ui.move` or `ui.hoverWidget` | | `ui.set` | partial UI state, e.g. `{"view": "detail"}` | Resulting UI state | | `ui.dialog.confirm` / `ui.dialog.cancel` | — | Close the open dialog | | `ui.resize` | `{width, height}` | Resize the window | @@ -46,6 +47,14 @@ mode ([mcp.md](mcp.md)) is a thin layer over this channel. Implementation: | `ui.render` | `{id?, size?, path?}` | Render a photo (PNG to `path`), `{width, height}` | | `app.quit` | — | Close the app | +Image navigation is also available directly as the UI command `view.navigate`, with +`{zoom?: "fit" | "fill" | {"percent": number}, pan?: [x, y]}`. Percentage zoom accepts fractional +values greater than 0 and at most 800; pan is the normalized image centre, with coordinates from 0 to 1. +Pinching keeps the image point under the pointer steady and zooms between Fit and 800%; two-finger +scrolling pans in both axes and respects the operating system's scrolling direction and momentum. +These gestures work in Detail (including editing tools and full-screen preview), Compare and Reference +views, and only apply over their image areas. Panning stops at the image edges. + ### When the library can't be saved With a persistent library, every command that changes something is written to the catalog journal (fsynced) before diff --git a/docs/parity.md b/docs/parity.md index d5a31ea9..ccd24528 100644 --- a/docs/parity.md +++ b/docs/parity.md @@ -23,7 +23,7 @@ and prints the summary; `cargo xtask parity --write` refreshes the summary table |---|---:|---:|---:|---:|---:|---:| | A. Import (IMP) | 11 | 3 | 1 | 1 | 3/5 (60%) | 5/5 (100%) | | B. Library management (LIB) | 22 | 2 | 1 | 2 | 9/9 (100%) | 9/9 (100%) | -| C. Views & navigation (VIEW) | 16 | 0 | 1 | 0 | 9/9 (100%) | 4/4 (100%) | +| C. Views & navigation (VIEW) | 15 | 1 | 1 | 0 | 8/9 (89%) | 4/4 (100%) | | D. Search & filter (FILT) | 11 | 1 | 1 | 0 | 4/4 (100%) | 4/4 (100%) | | E. Metadata (META) | 5 | 1 | 0 | 0 | 2/2 (100%) | 2/2 (100%) | | F. Edit panel — global adjustments (EDIT) | 42 | 1 | 5 | 1 | 28/28 (100%) | 13/14 (93%) | @@ -48,9 +48,9 @@ and prints the summary; `cargo xtask parity --write` refreshes the summary table | Y. Menus | 81 | 0 | 4 | 8 | 47/47 (100%) | 23/23 (100%) | | Z. Keyboard shortcuts (desktop) | 74 | 3 | 3 | 1 | 49/52 (94%) | 22/23 (96%) | | Lightroom Classic extras | 31 | 10 | 39 | 9 | — | 21/22 (95%) | -| **Total** | 389 | 34 | 86 | 37 | 194/200 (97%) | 139/149 (93%) | +| **Total** | 388 | 35 | 86 | 37 | 193/200 (97%) | 139/149 (93%) | -Weighted completion (✅ = 1, 🟡 = ½, 🚫 left out): **79.8%** of 509 in-scope rows — P0 98.5% of 200 · P1 95.6% of 149 · P2 41.6% of 160. +Weighted completion (✅ = 1, 🟡 = ½, 🚫 left out): **79.7%** of 509 in-scope rows — P0 98.2% of 200 · P1 95.6% of 149 · P2 41.6% of 160. ## Top gaps @@ -75,6 +75,10 @@ Take the first one nobody is working on. use the same model with fixed prompts; denoise / super-resolution models remain a maintainer decision. 7. **HDR** (Q. HDR, LR-EXP-HDR), **video** (R. Video), **Classic output modules** (Map view, Book, Slideshow module, Print, publish): large, well understood, lower priority than 1–5. +8. **LR-VIEW-ZOOM** (P0): pinch zoom and two-finger pan now work, with stable image cursors between gesture events. + The whole-image preview is still capped by + Settings → Performance → Preview size (2,560 px by default). Render visible regions at native resolution so + 100% can reliably show original-pixel detail on larger photos. ## Shortcuts: conflicts and missing bindings @@ -172,7 +176,7 @@ under the UI command that wraps it (a no-op error); the UI command now wins. | LR-VIEW-EDIT | Edit view | P0 | ✅ | `cmd:panel.edit` | | | LR-VIEW-FULLSCREEN | Full-screen preview | P1 | ✅ | `cmd:view.fullScreenPreview`, `cmd:view.enterFullScreen` | photo on black, arrows step, Esc exits; ⇧⌘F window full screen | | LR-VIEW-FILMSTRIP | Filmstrip | P0 | ✅ | `cmd:view.filmstrip`, `crates/ui-egui/src/panels/detail.rs` | the photo context menu on right-click; the mouse wheel scrolls it sideways; it follows the active photo only when that changes (centred if off screen), so a scrolled strip stays put | -| LR-VIEW-ZOOM | Zoom & pan | P0 | ✅ | `cmd:view.zoomFit`, `cmd:view.zoom100`, `cmd:view.zoomIn`, `cmd:view.zoomOut`, `cmd:view.zoomToggle`, `cmd:view.clickZoom` | steps 25–800 % (not 6–1600 %); Fill only in the bottom bar; a click on the photo (and Z / Space) eases to the click-zoom ratio (1:1 default, 2:1, 3:1, 4:1, 8:1 in the bottom bar) | +| LR-VIEW-ZOOM | Zoom & pan | P0 | 🟡 | `cmd:view.zoomFit`, `cmd:view.zoom100`, `cmd:view.zoomIn`, `cmd:view.zoomOut`, `cmd:view.zoomToggle`, `cmd:view.clickZoom`, `cmd:view.navigate`, `crates/ui-egui/src/panels/detail.rs`, `crates/ui-egui/src/headless.rs` | continuous native pinch / modifier-wheel zoom (Fit–800%, anchored under the pointer), two-finger scroll / touch pan with edge limits; Detail (including tools, Before/After and full-screen preview), Compare and Reference; fractional percentages preserve small gestures; image, picker and targeted-adjustment cursors stay steady between gesture events; keyboard steps 25–800 % (not 6–1600 %); Fill in the bottom bar; click / Z / Space eases to the chosen ratio (1:1 default, 2:1, 3:1, 4:1, 8:1). Missing: native-resolution detail beyond the configured whole-image preview size (2,560 px default); no visible-region tiles | | LR-VIEW-NAVIGATOR | Navigator mini map | P1 | ✅ | `cmd:view.navigator`, `crates/ui-egui/src/panels/detail.rs` | shown while zoomed (bottom right); click/drag pans | | LR-VIEW-BEFOREAFTER | Before / after | P0 | ✅ | `cmd:view.showOriginal`, `cmd:view.beforeAfter`, `cmd:view.beforeAfterSplit`, `cmd:view.beforeAfterTopBottom`, `cmd:view.beforeAfterSplitTopBottom`, `cmd:beforeAfter.setBefore`, `cmd:beforeAfter.copyAfterToBefore`, `cmd:beforeAfter.copyBeforeToAfter`, `cmd:beforeAfter.swap`, `cmd:beforeAfter.resetBefore` | all four layouts; before = the import state (defaults + import preset) or a chosen history step / version / the current settings; copy and swap (View → Before/After Settings, History row menu). The chosen before lasts for the session | | LR-VIEW-COMPARE | Compare two photos | P1 | ✅ | `cmd:view.compare`, `cmd:compare.swap`, `cmd:compare.makeSelect`, `crates/ui-egui/src/panels/compare.rs` | select / candidate, synced zoom + pan, arrows move the candidate; no zoom-link toggle | From 7f6e4e48f7ca0a8a1d865b29f88b9ffef6789ea3 Mon Sep 17 00:00:00 2001 From: Nathaniel Ledford Date: Thu, 8 Oct 2026 11:01:50 -0400 Subject: [PATCH 20/41] Selection::state_of: one answer to active / selected / not selected for every view Co-Authored-By: Claude Sonnet 5.5 --- crates/engine/src/lib.rs | 2 +- crates/engine/src/view.rs | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/crates/engine/src/lib.rs b/crates/engine/src/lib.rs index ec39222a..8ecaf9ac 100644 --- a/crates/engine/src/lib.rs +++ b/crates/engine/src/lib.rs @@ -46,7 +46,7 @@ use lightcraft_catalog::{Catalog, Filter, Op, PhotoId, Sort}; use lightcraft_develop::DevelopSettings; pub use media::{RenderJob, SourceLevel}; use serde_json::Value; -pub use view::{Browse, FilterChip, LibrarySource, Selection, filter_chips}; +pub use view::{Browse, FilterChip, LibrarySource, Selection, SelectionState, filter_chips}; pub use {lightcraft_catalog as catalog, lightcraft_develop as develop, lightcraft_gpu as gpu, lightcraft_pipeline as pipeline}; #[derive(Debug, thiserror::Error)] diff --git a/crates/engine/src/view.rs b/crates/engine/src/view.rs index 757adfe9..49d7462b 100644 --- a/crates/engine/src/view.rs +++ b/crates/engine/src/view.rs @@ -74,7 +74,27 @@ pub struct Selection { pub active: Option, } +/// How a photo relates to the selection: what every view draws (grid cell, filmstrip cell…). +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum SelectionState { + NotSelected, + /// Selected, but not the photo the loupe and panels act on. + Selected, + /// The most-selected photo. + Active, +} + impl Selection { + /// What views draw for `id`: active, selected or neither (one answer for grid, filmstrip, …). + pub fn state_of(&self, id: PhotoId) -> SelectionState { + if self.active == Some(id) { + SelectionState::Active + } else if self.contains(id) { + SelectionState::Selected + } else { + SelectionState::NotSelected + } + } pub fn single(id: PhotoId) -> Selection { Selection { ids: vec![id], active: Some(id) } } @@ -203,3 +223,18 @@ fn date_label(d: &str) -> String { _ => d.to_string(), } } + +#[cfg(test)] +mod selection_state_tests { + use super::*; + use lightcraft_catalog::PhotoId; + + #[test] + fn select_all_marks_the_others_selected_and_one_active() { + let ids: Vec = (1..=3).map(PhotoId).collect(); + let s = Selection { ids: ids.clone(), active: Some(ids[0]) }; + assert_eq!(s.state_of(ids[0]), SelectionState::Active); + assert_eq!(s.state_of(ids[1]), SelectionState::Selected); + assert_eq!(s.state_of(PhotoId(9)), SelectionState::NotSelected); + } +} From 14a03052de6506eec0c00557f6d25b79d6cf08fd Mon Sep 17 00:00:00 2001 From: Nathaniel Ledford Date: Thu, 8 Oct 2026 11:01:50 -0400 Subject: [PATCH 21/41] Filmstrip: show every selected photo as selected, not only the active one (#298) After Select All in the loupe only the active filmstrip cell was highlighted, so the selection looked like a single photo. Cells now use Selection::state_of; selected-but-not-active cells get the selected fill and a grey outline. Co-Authored-By: Claude Sonnet 5.5 --- crates/ui-egui/src/panels/detail.rs | 7 +++++-- crates/ui-egui/src/tests_panels.rs | 28 ++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/crates/ui-egui/src/panels/detail.rs b/crates/ui-egui/src/panels/detail.rs index 3b3622be..176047b6 100644 --- a/crates/ui-egui/src/panels/detail.rs +++ b/crates/ui-egui/src/panels/detail.rs @@ -1526,9 +1526,10 @@ pub(crate) fn filmstrip(app: &mut LightcraftApp, ui: &mut egui::Ui, r: Rect) { } let resp = ui.interact(cr, egui::Id::new(("film", id.0)), Sense::click()); register(ui.ctx(), format!("film:{}", id.0), cr); - let sel = Some(*id) == active; + let state = app.session.selection.state_of(*id); + let sel = state == lightcraft_engine::SelectionState::Active; let p = ui.painter(); - if sel { + if state != lightcraft_engine::SelectionState::NotSelected { p.rect_filled(cr, 0.0, t.cell_selected); } else if resp.hovered() { p.rect_filled(cr, 0.0, t.cell_selected.gamma_multiply(0.6)); @@ -1549,6 +1550,8 @@ pub(crate) fn filmstrip(app: &mut LightcraftApp, ui: &mut egui::Ui, r: Rect) { p.image(tex.tex.id(), fr, Rect::from_min_max(pos2(0.0, 0.0), pos2(1.0, 1.0)), Color32::WHITE); if sel { p.rect_stroke(fr, 0.0, Stroke::new(1.5, Color32::WHITE), StrokeKind::Outside); + } else if state == lightcraft_engine::SelectionState::Selected { + p.rect_stroke(fr, 0.0, Stroke::new(1.5, Color32::from_gray(170)), StrokeKind::Outside); } if app.ui.settings.film_badges && let Some(ph) = app.session.catalog.photo(*id) diff --git a/crates/ui-egui/src/tests_panels.rs b/crates/ui-egui/src/tests_panels.rs index a289e2ee..2bb14207 100644 --- a/crates/ui-egui/src/tests_panels.rs +++ b/crates/ui-egui/src/tests_panels.rs @@ -199,3 +199,31 @@ fn sidebar_sections_collapse_and_remember_it() { click(&mut h, "icon:albumNew"); assert!(!h.app.ui.sidebar_section_collapsed("albums"), "the plus does not fold Albums"); } + +/// Select All in the loupe: every filmstrip cell of a selected photo is drawn selected, not just +/// the active one (issue #298). +#[test] +fn filmstrip_marks_every_selected_photo() { + let mut h = demo([1400.0, 900.0], json!({"view": "detail"})); + let r = h.request("ui.key", json!({"key": "a", "cmd": true}), T); + assert_eq!(r["ok"], true, "{r}"); + let img = h.snapshot(SETTLE); + assert_eq!(h.view.ctx.pixels_per_point(), 1.0, "the test samples pixels at point coordinates"); + let active = h.app.session.selection.active.expect("active photo"); + let t = crate::theme::Tokens::get(&h.view.ctx); + let ids = h.app.session.visible_cloned(); + assert!(ids.len() > 3 && h.app.session.selection.ids.len() == ids.len()); + let mut checked = 0; + for id in ids.iter().filter(|id| **id != active) { + let Some(r) = h.app.widgets.iter().find(|(w, _)| *w == format!("film:{}", id.0)).map(|(_, r)| *r) else { continue }; + // cells scrolled under the right panel (default width plus the tool rail) are not visible + if r.max.x > 1400.0 - RIGHT_WIDTH.default - 48.0 { + continue; + } + // a pixel in the cell's left margin at mid height, clear of the name text and the thumbnail + let (x, y) = ((r.min.x + 4.0) as usize, r.center().y as usize); + assert_eq!(img[(x, y)], t.cell_selected, "film cell of selected photo {} is drawn selected", id.0); + checked += 1; + } + assert!(checked >= 3, "checked {checked} cells"); +} From 2928880e8120e91465a7dd4ed2c18bfbdecc6e11 Mon Sep 17 00:00:00 2001 From: Nathaniel Ledford Date: Thu, 8 Oct 2026 11:16:42 -0400 Subject: [PATCH 22/41] Import: onDeleted policy (skip|restore|fresh) for files in Recently Deleted; library.emptyRecentlyDeleted A file that is in Recently Deleted was always skipped as a duplicate with no way forward (#298). The report now flags such duplicates (existingDeleted); onDeleted restores the trashed photo with its edits or replaces it with a fresh import; and Empty Recently Deleted removes every trashed photo in one undoable step. Co-Authored-By: Claude Sonnet 5.5 --- crates/engine/src/cmd/library.rs | 27 +++++++++++- crates/engine/src/import.rs | 64 ++++++++++++++++++++++++++- crates/engine/src/tests_import.rs | 73 +++++++++++++++++++++++++++++++ 3 files changed, 162 insertions(+), 2 deletions(-) diff --git a/crates/engine/src/cmd/library.rs b/crates/engine/src/cmd/library.rs index 57632ea8..c3e6b824 100644 --- a/crates/engine/src/cmd/library.rs +++ b/crates/engine/src/cmd/library.rs @@ -66,7 +66,14 @@ pub fn import_params(s: &Session, p: &Value) -> Result { { return Err(bad("library.import", format!("unknown metadata preset `{n}`"))); } + let on_deleted = match str_param(p, "onDeleted") { + Some(v) => { + crate::import::OnDeleted::parse(v).ok_or_else(|| bad("library.import", format!("unknown onDeleted `{v}` (skip|restore|fresh)")))? + } + None => Default::default(), + }; let opts = crate::import::ImportOptions { + on_deleted, mode, preset, keywords: strs(p, "keywords"), @@ -560,6 +567,24 @@ pub fn specs() -> Vec { id, deleted: false }))), + cmd!( + "library.emptyRecentlyDeleted", + "Empty Recently Deleted", + ["Photo"], + None, + "{} → {deleted} — removes every photo in Recently Deleted from the library (files on disk stay)", + always, + |s, _| { + let ids: Vec = s.catalog.photos().filter(|p| p.deleted).map(|p| p.id).collect(); + if ids.is_empty() { + return Ok(json!({"deleted": 0})); + } + let ops = ids.iter().map(|id| s.catalog.delete_permanently_ops(*id)).collect::>(); + s.commit("Empty Recently Deleted", Op::Batch { ops })?; + s.selection = Selection::default(); + Ok(json!({"deleted": ids.len()})) + } + ), cmd!("photo.deletePermanently", "Delete Permanently", ["Photo"], None, "{ids?}", has_selection, |s, p| { let t = s.targets(p); let ops = t.iter().map(|id| s.catalog.delete_permanently_ops(*id)).collect::>(); @@ -956,7 +981,7 @@ pub fn specs() -> Vec { "Import Photos", ["File"], Some("Cmd+Shift+I"), - "{paths: [file or folder (recursive)], mode?: add|copy|move (add = reference the files in place; copy = into the library's Originals/YYYY/YYYY-MM-DD/; move = as copy, then each original and its XMP sidecars are removed from the source — only after the copy is verified (a hard link on the same volume, else copied, synced and compared byte for byte) and its catalog record is saved; failed, duplicate and unchecked files keep their sources; a taken name gets -1, -2…; undo removes the photos from the library but leaves the files at the destination), destination?: folder for copies / moves, organize?: date (YYYY/YYYY-MM-DD) | month (YYYY/YYYY-MM) | flat | a folder template, e.g. `{date:%Y}/{date:%Y%m%d}` → 2026/20260114 (the template's `/` make the folders, each level expanded with the rename tokens and made a safe folder name: never outside the destination; must be relative, no `..`; a level with missing metadata is `unknown`) — dated by capture time, else the import time, rename?: file-name template for copies, original extension added (tokens: {name} {num} {seq} {seq:N} {date} {date:%Y%m%d} {folder} {camera} {lens} {iso} {rating} {title} {creator} {ext}; photo.renameTokens explains each; blank = keep names), renameStart?: 1, metadataPreset?: name, dng?: bool (copy raws as DNG; copy only), local?: bool (browsing: the photos stay out of the library, like library.browse; not with move), album?: albumId, albumName?: new album, preset?: presetId, keywords?: [..]} → {imported, duplicates, failed, moved?: [{from, to, sidecars?}], kept?: [{path, reason}] (move: sources left in place and why), album?}", + "{paths: [file or folder (recursive)], mode?: add|copy|move (add = reference the files in place; copy = into the library's Originals/YYYY/YYYY-MM-DD/; move = as copy, then each original and its XMP sidecars are removed from the source — only after the copy is verified (a hard link on the same volume, else copied, synced and compared byte for byte) and its catalog record is saved; failed, duplicate and unchecked files keep their sources; a taken name gets -1, -2…; undo removes the photos from the library but leaves the files at the destination), destination?: folder for copies / moves, organize?: date (YYYY/YYYY-MM-DD) | month (YYYY/YYYY-MM) | flat | a folder template, e.g. `{date:%Y}/{date:%Y%m%d}` → 2026/20260114 (the template's `/` make the folders, each level expanded with the rename tokens and made a safe folder name: never outside the destination; must be relative, no `..`; a level with missing metadata is `unknown`) — dated by capture time, else the import time, rename?: file-name template for copies, original extension added (tokens: {name} {num} {seq} {seq:N} {date} {date:%Y%m%d} {folder} {camera} {lens} {iso} {rating} {title} {creator} {ext}; photo.renameTokens explains each; blank = keep names), renameStart?: 1, metadataPreset?: name, dng?: bool (copy raws as DNG; copy only), local?: bool (browsing: the photos stay out of the library, like library.browse; not with move), album?: albumId, albumName?: new album, preset?: presetId, keywords?: [..], onDeleted?: skip|restore|fresh (a file that is in Recently Deleted: skip = leave it there and say so, restore = bring the photo back with its edits, fresh = delete the trashed record and import the file as new)} → {imported, duplicates: [{path, existing, reason, existingDeleted?}], restored?: [photoId], failed, moved?: [{from, to, sidecars?}], kept?: [{path, reason}] (move: sources left in place and why), album?}", always, |s, p| { let req = import_params(s, p)?; diff --git a/crates/engine/src/import.rs b/crates/engine/src/import.rs index 4895eee7..1bbbd64f 100644 --- a/crates/engine/src/import.rs +++ b/crates/engine/src/import.rs @@ -48,9 +48,34 @@ pub enum ImportMode { Move, } +/// What an import does with a file the library already has in Recently Deleted. +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] +pub enum OnDeleted { + /// Leave it there; the report says so ([`Duplicate::existing_deleted`]). + #[default] + Skip, + /// Bring the trashed photo back, with its edits ([`ImportReport::restored`]). + Restore, + /// Delete the trashed record permanently and import the file as a new photo. + Fresh, +} + +impl OnDeleted { + pub fn parse(s: &str) -> Option { + match s { + "skip" => Some(Self::Skip), + "restore" => Some(Self::Restore), + "fresh" => Some(Self::Fresh), + _ => None, + } + } +} + /// What an import does besides adding the photos. #[derive(Clone, Debug, Default)] pub struct ImportOptions { + /// A file that is in Recently Deleted: skip (default), restore, or import afresh. + pub on_deleted: OnDeleted, pub mode: ImportMode, /// Applied to every imported photo (one History entry). pub preset: Option, @@ -147,12 +172,18 @@ pub struct Duplicate { pub existing: Option, /// `"path"` (already imported from there) or `"content"` (same bytes elsewhere). pub reason: &'static str, + /// That photo is in Recently Deleted (see [`OnDeleted`]). + #[serde(rename = "existingDeleted", skip_serializing_if = "std::ops::Not::not")] + pub existing_deleted: bool, } #[derive(Clone, Debug, Default, Serialize)] pub struct ImportReport { pub imported: Vec, pub duplicates: Vec, + /// [`OnDeleted::Restore`]: photos brought back from Recently Deleted instead of imported. + #[serde(skip_serializing_if = "Vec::is_empty")] + pub restored: Vec, /// Move: the originals that were moved (and removed from the source). #[serde(skip_serializing_if = "Vec::is_empty")] pub moved: Vec, @@ -638,6 +669,10 @@ impl ImportJob { let mut by_path = HashMap::new(); let mut by_hash = HashMap::new(); for p in s.catalog.photos() { + // a trashed photo is replaced by a fresh import, so it doesn't count as a duplicate + if p.deleted && opts.on_deleted == OnDeleted::Fresh { + continue; + } if let Source::File { path } = &p.source { by_path.insert(path.clone(), (p.id, p.local)); } @@ -840,6 +875,19 @@ pub fn commit_prepared(s: &mut Session, opts: &ImportOptions, now: &str, prepare let mut placed: Vec = Vec::new(); // photos added by this batch, by content (a duplicate of a file earlier in the import names it) let mut new_hash: HashMap = HashMap::new(); + // Fresh: trashed photos a new import replaces (by content or by file), each removed once + let (mut trash_hash, mut trash_path): (HashMap, HashMap) = Default::default(); + if opts.on_deleted == OnDeleted::Fresh { + for p in s.catalog.photos().filter(|p| p.deleted) { + if let Some(h) = &p.content_hash { + trash_hash.insert(h.clone(), p.id); + } + if let Source::File { path } = &p.source { + trash_path.insert(path.clone(), p.id); + } + } + } + let mut purged: std::collections::HashSet = Default::default(); for it in prepared.items { match it { PreparedItem::Promote(id) => { @@ -853,7 +901,16 @@ pub fn commit_prepared(s: &mut Session, opts: &ImportOptions, now: &str, prepare let h = hash?; new_hash.get(&h).copied().or_else(|| s.catalog.photos().find(|p| p.content_hash.as_deref() == Some(h.as_str())).map(|p| p.id)) }); - report.duplicates.push(Duplicate { path, existing: existing.map(|i| i.0), reason }); + let trashed = existing.filter(|id| s.catalog.photo(*id).is_some_and(|p| p.deleted)); + match trashed { + Some(id) if opts.on_deleted == OnDeleted::Restore => { + if !report.restored.contains(&id.0) { + ops.push(Op::SetDeleted { id, deleted: false }); + report.restored.push(id.0); + } + } + _ => report.duplicates.push(Duplicate { path, existing: existing.map(|i| i.0), reason, existing_deleted: trashed.is_some() }), + } } PreparedItem::Failed(path, e) => report.failed.push((path, e)), PreparedItem::Kept(k) => report.kept.push(k), @@ -864,6 +921,11 @@ pub fn commit_prepared(s: &mut Session, opts: &ImportOptions, now: &str, prepare if let Some(h) = &info.content_hash { new_hash.insert(h.clone(), id); } + for old in info.content_hash.as_ref().and_then(|h| trash_hash.get(h)).into_iter().chain(trash_path.get(&stored)) { + if purged.insert(*old) { + ops.push(s.catalog.delete_permanently_ops(*old)); + } + } let sidecar = sidecar.map(|sc| sc.resolve_label(&s.catalog)).filter(|sc| *sc != crate::sidecar::SidecarData::default()); // a copy is catalogued under its new name let name = Path::new(&stored).file_name().map(|n| n.to_string_lossy().to_string()).unwrap_or_else(|| path.clone()); diff --git a/crates/engine/src/tests_import.rs b/crates/engine/src/tests_import.rs index af106765..0853a7e9 100644 --- a/crates/engine/src/tests_import.rs +++ b/crates/engine/src/tests_import.rs @@ -858,3 +858,76 @@ fn missing_photos_skip_local_browse_records() { s.execute("library.source", &json!({"kind": "missing"})).unwrap(); assert_eq!(s.visible_cloned().len(), 4, "the view lists what the count counts"); } + +/// Import a file that is in Recently Deleted (issue #298): by default it is skipped but the +/// report says it is in the trash; `onDeleted: restore` brings it back with its edits; +/// `onDeleted: fresh` replaces the trashed record with a new one. +fn trashed_photo(tag: &str) -> (Session, std::path::PathBuf, u64) { + let src = temp_dir(tag); + write_png(&src.join("a.png"), 1); + let mut s = Session::new().with_fs(); + let r = s.execute("library.import", &json!({"paths": [src.to_string_lossy()]})).unwrap(); + let id = r["imported"][0].as_u64().unwrap(); + s.execute("photo.rate", &json!({"ids": [id], "rating": 4})).unwrap(); + s.execute("photo.delete", &json!({"ids": [id]})).unwrap(); + (s, src, id) +} + +#[test] +fn duplicate_in_recently_deleted_is_reported_as_such() { + let (mut s, src, id) = trashed_photo("trash-report"); + let r = s.execute("library.import", &json!({"paths": [src.to_string_lossy()]})).unwrap(); + assert_eq!(ids(&r, "imported"), 0, "{r}"); + assert_eq!(r["duplicates"][0]["existing"], id, "{r}"); + assert_eq!(r["duplicates"][0]["existingDeleted"], true, "{r}"); + assert!(s.catalog.photo(lightcraft_catalog::PhotoId(id)).unwrap().deleted, "default: left in the trash"); + let _ = std::fs::remove_dir_all(&src); +} + +#[test] +fn import_can_restore_a_duplicate_from_recently_deleted() { + let (mut s, src, id) = trashed_photo("trash-restore"); + let r = s.execute("library.import", &json!({"paths": [src.to_string_lossy()], "onDeleted": "restore"})).unwrap(); + assert_eq!(r["restored"], json!([id]), "{r}"); + assert_eq!(ids(&r, "duplicates"), 0, "{r}"); + let p = s.catalog.photo(lightcraft_catalog::PhotoId(id)).unwrap(); + assert!(!p.deleted && p.rating == 4, "restored with its edits"); + let _ = std::fs::remove_dir_all(&src); +} + +#[test] +fn import_can_replace_a_duplicate_from_recently_deleted() { + let (mut s, src, id) = trashed_photo("trash-fresh"); + let r = s.execute("library.import", &json!({"paths": [src.to_string_lossy()], "onDeleted": "fresh"})).unwrap(); + assert_eq!(ids(&r, "imported"), 1, "{r}"); + let new = r["imported"][0].as_u64().unwrap(); + assert_ne!(new, id); + assert!(s.catalog.photo(lightcraft_catalog::PhotoId(id)).is_none(), "the trashed record is gone"); + let p = s.catalog.photo(lightcraft_catalog::PhotoId(new)).unwrap(); + assert!(!p.deleted && p.rating == 0, "a new photo without the old edits"); + assert_eq!(s.catalog.len(), 1); + let _ = std::fs::remove_dir_all(&src); +} + +#[test] +fn unknown_on_deleted_is_an_error() { + let mut s = Session::new().with_fs(); + let e = s.execute("library.import", &json!({"paths": [], "onDeleted": "bogus"})).unwrap_err().to_string(); + assert!(e.contains("onDeleted"), "{e}"); +} + +#[test] +fn empty_recently_deleted_removes_only_trashed_photos() { + let (mut s, src, id) = trashed_photo("trash-empty"); + write_png(&src.join("b.png"), 2); + let r = s.execute("library.import", &json!({"paths": [src.join("b.png").to_string_lossy()]})).unwrap(); + let keep = r["imported"][0].as_u64().unwrap(); + let r = s.execute("library.emptyRecentlyDeleted", &json!({})).unwrap(); + assert_eq!(r["deleted"], 1, "{r}"); + assert!(s.catalog.photo(lightcraft_catalog::PhotoId(id)).is_none()); + assert!(s.catalog.photo(lightcraft_catalog::PhotoId(keep)).is_some()); + // nothing to empty is not an error + assert_eq!(s.execute("library.emptyRecentlyDeleted", &json!({})).unwrap()["deleted"], 0); + s.execute("edit.undo", &json!({})).unwrap(); + let _ = std::fs::remove_dir_all(&src); +} From 0a474cc06e98778f1f7cfeb7d36ab41d1bb66a18 Mon Sep 17 00:00:00 2001 From: Nathaniel Ledford Date: Thu, 8 Oct 2026 11:29:05 -0400 Subject: [PATCH 23/41] Permanent delete of several photos is one consistent op list; restored photos join album/selection Review of the onDeleted work found that purging photos one at a time built each op list against the unchanged catalog, so photos sharing an album or a stack made Empty Recently Deleted (and Fresh imports) fail with NoStack or leave dangling album entries. Catalog::delete_photos_permanently_ops builds one list for all of them; Empty, Delete Permanently and the Fresh purge use it. Photos an import restores now take part in album/albumName/selection, and undo labels say Restore. Co-Authored-By: Claude Sonnet 5.5 --- crates/catalog/src/lib.rs | 16 +++++++ crates/engine/src/cmd/library.rs | 19 ++++++--- crates/engine/src/import.rs | 14 +++++- crates/engine/src/tests_import.rs | 71 +++++++++++++++++++++++++++++++ 4 files changed, 111 insertions(+), 9 deletions(-) diff --git a/crates/catalog/src/lib.rs b/crates/catalog/src/lib.rs index 208a86d0..ba984901 100644 --- a/crates/catalog/src/lib.rs +++ b/crates/catalog/src/lib.rs @@ -612,6 +612,22 @@ impl Catalog { Op::Batch { ops } } + /// [`Self::delete_permanently_ops`] for several photos at once. One op list built against the + /// catalog as it is now: albums lose all of them in one write each, and stacks are shortened + /// (or dissolved) once, however many of their photos go. + pub fn delete_photos_permanently_ops(&self, ids: &[PhotoId]) -> Op { + let gone: std::collections::HashSet = ids.iter().copied().collect(); + let mut ops: Vec = self + .albums + .values() + .filter(|a| a.photos.iter().any(|p| gone.contains(p))) + .map(|a| Op::SetAlbumPhotos { id: a.id, photos: a.photos.iter().copied().filter(|p| !gone.contains(p)).collect() }) + .collect(); + ops.extend(self.remove_from_stacks_ops(ids)); + ops.extend(gone.iter().map(|id| Op::RemovePhoto { id: *id })); + Op::Batch { ops } + } + // ---- persistence /// Full snapshot as JSON. diff --git a/crates/engine/src/cmd/library.rs b/crates/engine/src/cmd/library.rs index c3e6b824..b71796df 100644 --- a/crates/engine/src/cmd/library.rs +++ b/crates/engine/src/cmd/library.rs @@ -94,7 +94,9 @@ pub fn import_params(s: &Session, p: &Value) -> Result { /// `album`). pub fn import_batch_done(s: &mut Session, report: crate::import::ImportReport, mut album: Option, album_name: Option<&str>) -> Result { let mut report = serde_json::to_value(report).unwrap_or_default(); - let imported: Vec = report["imported"].as_array().map(|a| a.iter().filter_map(Value::as_u64).collect()).unwrap_or_default(); + // photos an import restored from Recently Deleted take part like new ones (album, selection) + let imported: Vec = + ["imported", "restored"].iter().flat_map(|k| report[*k].as_array().into_iter().flatten().filter_map(Value::as_u64)).collect(); if album.is_none() && let Some(name) = album_name && !imported.is_empty() @@ -579,16 +581,16 @@ pub fn specs() -> Vec { if ids.is_empty() { return Ok(json!({"deleted": 0})); } - let ops = ids.iter().map(|id| s.catalog.delete_permanently_ops(*id)).collect::>(); - s.commit("Empty Recently Deleted", Op::Batch { ops })?; + let op = s.catalog.delete_photos_permanently_ops(&ids); + s.commit("Empty Recently Deleted", op)?; s.selection = Selection::default(); Ok(json!({"deleted": ids.len()})) } ), cmd!("photo.deletePermanently", "Delete Permanently", ["Photo"], None, "{ids?}", has_selection, |s, p| { let t = s.targets(p); - let ops = t.iter().map(|id| s.catalog.delete_permanently_ops(*id)).collect::>(); - s.commit("Delete Permanently", Op::Batch { ops })?; + let op = s.catalog.delete_photos_permanently_ops(&t); + s.commit("Delete Permanently", op)?; s.selection = Selection::default(); Ok(json!({"deleted": t.len()})) }), @@ -992,9 +994,12 @@ pub fn specs() -> Vec { let report = crate::import::import_with(s, &req.paths, &req.opts)?; let report = import_batch_done(s, report, req.album, req.album_name.as_deref())?; // one undo step for the whole import - let imported: Vec = report["imported"].as_array().map(|a| a.iter().filter_map(Value::as_u64).collect()).unwrap_or_default(); + let ids_of = |k: &str| -> Vec { report[k].as_array().map(|a| a.iter().filter_map(Value::as_u64).collect()).unwrap_or_default() }; + let (imported, restored) = (ids_of("imported"), ids_of("restored")); let n = s.undo.len().saturating_sub(undo0); - s.merge_undo(n, &format!("Add {} Photo{}", imported.len(), if imported.len() == 1 { "" } else { "s" })); + let (verb, count) = if imported.is_empty() && !restored.is_empty() { ("Restore", restored.len()) } else { ("Add", imported.len()) }; + s.merge_undo(n, &format!("{verb} {count} Photo{}", if count == 1 { "" } else { "s" })); + let imported = if imported.is_empty() { restored } else { imported }; if let Some(f) = imported.first() { s.selection = Selection::single(PhotoId(*f)); } diff --git a/crates/engine/src/import.rs b/crates/engine/src/import.rs index 1bbbd64f..5df94988 100644 --- a/crates/engine/src/import.rs +++ b/crates/engine/src/import.rs @@ -887,6 +887,7 @@ pub fn commit_prepared(s: &mut Session, opts: &ImportOptions, now: &str, prepare } } } + let mut purge: Vec = Vec::new(); let mut purged: std::collections::HashSet = Default::default(); for it in prepared.items { match it { @@ -923,7 +924,7 @@ pub fn commit_prepared(s: &mut Session, opts: &ImportOptions, now: &str, prepare } for old in info.content_hash.as_ref().and_then(|h| trash_hash.get(h)).into_iter().chain(trash_path.get(&stored)) { if purged.insert(*old) { - ops.push(s.catalog.delete_permanently_ops(*old)); + purge.push(*old); } } let sidecar = sidecar.map(|sc| sc.resolve_label(&s.catalog)).filter(|sc| *sc != crate::sidecar::SidecarData::default()); @@ -969,9 +970,18 @@ pub fn commit_prepared(s: &mut Session, opts: &ImportOptions, now: &str, prepare } } } + // (built against the catalog as it is: one op list for all of them, ahead of the additions) + if !purge.is_empty() { + ops.insert(0, s.catalog.delete_photos_permanently_ops(&purge)); + } let log0 = s.pending_log.len(); + let (verb, n) = if report.imported.is_empty() && !report.restored.is_empty() { + ("Restore", report.restored.len()) + } else { + ("Add", report.imported.len().max(1)) + }; if !ops.is_empty() - && let Err(e) = s.commit(&format!("Add {} Photo{}", ops.len(), if ops.len() == 1 { "" } else { "s" }), Op::Batch { ops }) + && let Err(e) = s.commit(&format!("{verb} {n} Photo{}", if n == 1 { "" } else { "s" }), Op::Batch { ops }) { // nothing was catalogued: take the moves back (their sources were never touched) placed.iter().for_each(crate::import_move::rollback); diff --git a/crates/engine/src/tests_import.rs b/crates/engine/src/tests_import.rs index 0853a7e9..1c5edf9e 100644 --- a/crates/engine/src/tests_import.rs +++ b/crates/engine/src/tests_import.rs @@ -931,3 +931,74 @@ fn empty_recently_deleted_removes_only_trashed_photos() { s.execute("edit.undo", &json!({})).unwrap(); let _ = std::fs::remove_dir_all(&src); } + +/// Two trashed photos that share an album and a two-photo stack: emptying (or permanently +/// deleting) them together must not fail or leave dangling references. +fn two_trashed_in_album_and_stack(tag: &str) -> (Session, std::path::PathBuf, [u64; 2], u64) { + let src = temp_dir(tag); + write_png(&src.join("a.png"), 1); + write_png(&src.join("b.png"), 2); + let mut s = Session::new().with_fs(); + let r = s.execute("library.import", &json!({"paths": [src.to_string_lossy()]})).unwrap(); + let ids = [r["imported"][0].as_u64().unwrap(), r["imported"][1].as_u64().unwrap()]; + let al = s.execute("album.create", &json!({"name": "Trip"})).unwrap()["id"].as_u64().unwrap(); + s.execute("album.addPhotos", &json!({"id": al, "ids": ids})).unwrap(); + s.execute("library.select", &json!({"ids": ids})).unwrap(); + s.execute("stack.group", &json!({"ids": ids})).unwrap(); + s.execute("photo.delete", &json!({"ids": ids})).unwrap(); + (s, src, ids, al) +} + +#[test] +fn emptying_trashed_photos_of_one_album_and_stack_leaves_nothing_dangling() { + let (mut s, src, ids, al) = two_trashed_in_album_and_stack("trash-empty-refs"); + let r = s.execute("library.emptyRecentlyDeleted", &json!({})).unwrap(); + assert_eq!(r["deleted"], 2, "{r}"); + assert_eq!(s.catalog.len(), 0); + assert!(s.catalog.album(lightcraft_catalog::AlbumId(al)).unwrap().photos.is_empty(), "album still lists a removed photo"); + assert!(ids.iter().all(|i| s.catalog.stack_of(lightcraft_catalog::PhotoId(*i)).is_none())); + s.execute("edit.undo", &json!({})).unwrap(); + assert_eq!(s.catalog.len(), 2, "one undo step brings both back"); + assert_eq!(s.catalog.album(lightcraft_catalog::AlbumId(al)).unwrap().photos.len(), 2); + assert!(s.catalog.stack_of(lightcraft_catalog::PhotoId(ids[0])).is_some()); + assert!(s.catalog.photos().all(|p| p.deleted), "back in Recently Deleted"); + let _ = std::fs::remove_dir_all(&src); +} + +#[test] +fn deleting_several_photos_permanently_leaves_nothing_dangling() { + let (mut s, src, ids, al) = two_trashed_in_album_and_stack("trash-perm-refs"); + s.execute("library.select", &json!({"ids": ids})).unwrap(); + s.execute("photo.deletePermanently", &json!({"ids": ids})).unwrap(); + assert!(s.catalog.album(lightcraft_catalog::AlbumId(al)).unwrap().photos.is_empty()); + let _ = std::fs::remove_dir_all(&src); +} + +#[test] +fn fresh_import_of_two_trashed_photos_in_one_album_and_stack_works() { + let (mut s, src, ids, al) = two_trashed_in_album_and_stack("trash-fresh-refs"); + let r = s.execute("library.import", &json!({"paths": [src.to_string_lossy()], "onDeleted": "fresh"})).unwrap(); + assert_eq!(ids_len(&r), 2, "{r}"); + assert!(s.catalog.album(lightcraft_catalog::AlbumId(al)).unwrap().photos.is_empty()); + assert!(ids.iter().all(|i| s.catalog.photo(lightcraft_catalog::PhotoId(*i)).is_none())); + let _ = std::fs::remove_dir_all(&src); +} + +fn ids_len(r: &Value) -> usize { + ids(r, "imported") +} + +#[test] +fn restore_import_applies_the_album_to_restored_photos() { + let (mut s, src, ids, _) = trashed_photo_ids("trash-restore-album"); + let r = s.execute("library.import", &json!({"paths": [src.to_string_lossy()], "onDeleted": "restore", "albumName": "Back"})).unwrap(); + assert_eq!(r["restored"], json!(ids), "{r}"); + let album = r["album"].as_u64().expect("album created for the restored photos"); + assert_eq!(s.catalog.album(lightcraft_catalog::AlbumId(album)).unwrap().photos.len(), 1); + let _ = std::fs::remove_dir_all(&src); +} + +fn trashed_photo_ids(tag: &str) -> (Session, std::path::PathBuf, [u64; 1], u64) { + let (s, src, id) = trashed_photo(tag); + (s, src, [id], 0) +} From 5b80fc6bcf1dec98f428bd74b4eb9051d32dba69 Mon Sep 17 00:00:00 2001 From: Nathaniel Ledford Date: Thu, 8 Oct 2026 11:29:05 -0400 Subject: [PATCH 24/41] MCP import onDeleted; Empty Recently Deleted in the trash view's Photo menu; parity row Co-Authored-By: Claude Sonnet 5.5 --- crates/mcp/src/tools.rs | 3 ++- crates/ui-egui/src/headless.rs | 3 +++ crates/ui-egui/src/menubar.rs | 2 ++ docs/parity.md | 2 +- 4 files changed, 8 insertions(+), 2 deletions(-) diff --git a/crates/mcp/src/tools.rs b/crates/mcp/src/tools.rs index c5419425..bfe2a4b3 100644 --- a/crates/mcp/src/tools.rs +++ b/crates/mcp/src/tools.rs @@ -102,6 +102,7 @@ pub fn helper_tools(has_ui: bool) -> Vec { "mode": {"type": "string", "enum": ["add", "copy", "move"], "description": "add (default) = reference the files in place; copy = copy them into `destination` (default: the library's Originals/); move = as copy, then each original and its XMP sidecars are removed from the source once the copy is verified and catalogued (duplicates and failures keep their sources; `kept` lists sources left in place and why; undo leaves the moved files at the destination)"}, "destination": {"type": "string", "description": "Copy or move: destination folder"}, "organize": {"type": "string", "description": "Copy or move: folders inside the destination — date (YYYY/YYYY-MM-DD, default), month (YYYY/YYYY-MM), flat, or a folder template such as {date:%Y}/{date:%Y%m%d} (→ 2026/20260114; the template's / make the levels, tokens as for rename; relative, no ..). Dated by capture time, else the import time"}, + "onDeleted": {"type": "string", "enum": ["skip", "restore", "fresh"], "description": "A file that is in Recently Deleted: skip (default) = leave it there (the duplicate is reported with existingDeleted); restore = bring the photo back with its edits (`restored` lists it); fresh = delete the trashed record and import the file as a new photo"}, "rename": {"type": "string", "description": "Copy or move: file-name template, e.g. {date:%Y%m%d_%H%M%S}_{seq:3} (run_command photo.renameTokens lists the tags); the extension is kept"} }), &["paths"], @@ -475,7 +476,7 @@ pub fn call_tool(b: &mut dyn Backend, name: &str, args: &Value) -> ToolResult { return ToolResult::error(format!("no photos found in {paths:?}")); } let mut p = json!({"paths": files}); - for k in ["album", "mode", "destination", "organize", "rename"] { + for k in ["album", "mode", "destination", "organize", "rename", "onDeleted"] { if let Some(v) = args.get(k).filter(|v| !v.is_null()) { p[k] = v.clone(); } diff --git a/crates/ui-egui/src/headless.rs b/crates/ui-egui/src/headless.rs index 976ecd55..8a548502 100644 --- a/crates/ui-egui/src/headless.rs +++ b/crates/ui-egui/src/headless.rs @@ -1417,6 +1417,8 @@ mod tests { h.settle(SETTLE); assert!(egui::Popup::is_any_open(&h.view.ctx), "filmstrip context menu"); + // Empty Recently Deleted is offered while the trash is the source + assert!(photo_items(&h.app).contains(&"library.emptyRecentlyDeleted".to_string())); let r = h.request("ui.menu.invoke", json!({"id": "photo.restore"}), t); assert_eq!(r["ok"], true, "{r}"); assert!(!h.app.session.catalog.photo(id).unwrap().deleted, "restored"); @@ -1426,6 +1428,7 @@ mod tests { assert_eq!(h.app.session.selection.ids, vec![id]); let toast = h.app.ui.toast.clone().expect("toast").0; assert!(toast.contains("All Photos"), "{toast}"); + assert!(!photo_items(&h.app).contains(&"library.emptyRecentlyDeleted".to_string()), "Empty is for the trash view only"); // deleted permanently, the file imports afresh as a new photo h.request("engine.execute", json!({"command": "photo.delete", "params": {"ids": [id.0]}}), t); diff --git a/crates/ui-egui/src/menubar.rs b/crates/ui-egui/src/menubar.rs index 8012a26b..9fcece9e 100644 --- a/crates/ui-egui/src/menubar.rs +++ b/crates/ui-egui/src/menubar.rs @@ -272,6 +272,8 @@ fn host_supports(app: &LightcraftApp, id: &str) -> bool { "file.restoreLibrary" => app.services.restore_library.is_some(), "photo.restore" | "photo.deletePermanently" => selection_deleted(app), "photo.delete" => !selection_deleted(app), + // only where the trash is on screen + "library.emptyRecentlyDeleted" => app.session.source == lightcraft_engine::LibrarySource::RecentlyDeleted, _ => true, } } diff --git a/docs/parity.md b/docs/parity.md index d5a31ea9..628532ef 100644 --- a/docs/parity.md +++ b/docs/parity.md @@ -150,7 +150,7 @@ under the UI command that wraps it (a no-op error); the UI command now wins. | LR-LIB-PEOPLE | People / faces | P2 | 🟡 | `cmd:view.people`, `cmd:view.faceBoxes`, `cmd:photo.removeRegion`, `cmd:photo.setRegion`, `cmd:library.filter`, `crates/ui-egui/src/panels/people.rs`, `crates/catalog/src/query.rs` (`people`), `crates/engine/src/media.rs` (`face_job`), `crates/meta/src/xmp.rs` | MVP: named face regions read from XMP (MWG-RS, read-only, see `docs/xmp-interop.md`; clipped to the photo; Lightroom's `Rotation` of ±π/2 and π handled, mirrored orientations taken as written; boxes follow Rotate Left/Right and flips; a sidecar stating `mwg-rs:Regions`, even empty, replaces the photo's on re-read, one without leaves them) become cards in the People view (View ▸ People, or the People button in the toolbar): a close-up of the person's largest face, name and photo count; a click filters the grid (`person` in `library.filter`, `person:` search token, filter chip). Face boxes in the loupe can be switched off (View ▸ Face Boxes, toolbar) resized with eight drag handles and removed with the × shown on hover (catalog-only and undoable, one undo step per drag; the sidecar is never rewritten for either); no moving a box by dragging it yet. No face detection, no unnamed people / suggestions, no manual tagging, no writing regions back | | LR-LIB-STACK | Stacks | P1 | ✅ | `cmd:stack.group`, `cmd:stack.ungroup`, `cmd:stack.toggle`, `cmd:stack.setTop`, `cmd:stack.remove`, `cmd:stack.auto`, `crates/catalog/src/stacks.rs` | grid/filmstrip count badges, expand/collapse, auto-stack by capture time; no visual-similarity auto-stack | | LR-LIB-VERSIONS | Versions | P1 | ✅ | `cmd:version.create` | see section M | -| LR-LIB-DELETE | Delete / Recently Deleted | P0 | ✅ | `cmd:photo.delete`, `cmd:photo.restore`, `cmd:photo.deletePermanently` | Restore and Delete Permanently in the Photo menu and the photo context menus for photos in Recently Deleted (before, the UI had no way to restore); adding a file that is in Recently Deleted again selects it there (side panel opened) and says how to restore it or import it afresh; no confirmation dialog, no auto-purge after N days, no "Empty" | +| LR-LIB-DELETE | Delete / Recently Deleted | P0 | ✅ | `cmd:photo.delete`, `cmd:photo.restore`, `cmd:photo.deletePermanently` | Restore and Delete Permanently in the Photo menu and the photo context menus for photos in Recently Deleted (before, the UI had no way to restore); adding a file that is in Recently Deleted again selects it there (side panel opened) and says how to restore it or import it afresh; Photo ▸ Empty Recently Deleted (`cmd:library.emptyRecentlyDeleted`, one undo step, shown while the trash is the source); import takes `onDeleted` skip / restore / fresh for files in the trash (`cmd:library.import`; the report flags `existingDeleted` and lists `restored`; the desktop import dialog still uses skip and points at Restore); no confirmation dialog, no auto-purge after N days (needs a deleted-at date in the catalog) | | LR-LIB-REMOVE-ALBUM | Remove from album | P0 | ✅ | `cmd:album.removePhotos` | | | LR-LIB-DUPLICATE | Duplicate a photo | P2 | ✅ | `cmd:photo.duplicate` | Photo ▸ Duplicate: a real `-copy` file with the same settings, metadata and albums (virtual copies share the file) | | LR-LIB-RENAME | Batch rename | P1 | ✅ | `cmd:photo.rename`, `cmd:photo.renamePreview`, `cmd:photo.renameTokens`, `cmd:dialog.rename`, `crates/engine/src/rename.rs` (`TOKENS`) | template tokens {name} {num} {seq:N} {date:%Y%m%d} {folder} {camera} {lens} {iso} {rating} {title} {creator} {ext} (shared with export naming and import rename); a Tags picker beside every template field (Rename Photos, import Rename, export File name) lists each tag with meaning + live example, the date directives and the rules, and inserts the clicked tag at the text cursor; unknown tags are flagged; preview; renames files on disk with their XMP sidecars (a stem sidecar another file still shares, e.g. raw + JPEG, is copied rather than taken), never overwriting (-1, -2… suffixes; a change of letter case only is checked by file identity, so on case-sensitive volumes `img_1.jpg` next to `IMG_1.JPG` is a collision), rolls back on failure (a file that can't be moved back is reported old → new and the library follows it as an undoable partial rename; the same for undo/redo); undo/redo move the files; virtual copies follow | From 6c19090fde989f2403c0af54c3dd394196b97e3b Mon Sep 17 00:00:00 2001 From: Nathaniel Ledford Date: Thu, 8 Oct 2026 11:29:05 -0400 Subject: [PATCH 25/41] Grid: selected photos whose thumbnail isn't drawn yet still show as selected (#298) The grid and filmstrip now both ask Selection::state_of. Co-Authored-By: Claude Sonnet 5.5 --- crates/ui-egui/src/panels/grid.rs | 11 +++++++++-- crates/ui-egui/src/tests_grid.rs | 24 ++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/crates/ui-egui/src/panels/grid.rs b/crates/ui-egui/src/panels/grid.rs index 7dc07602..2ffd1ea4 100644 --- a/crates/ui-egui/src/panels/grid.rs +++ b/crates/ui-egui/src/panels/grid.rs @@ -471,7 +471,8 @@ fn cell(app: &mut LightcraftApp, ui: &mut egui::Ui, id: PhotoId, r: Rect, square let Some(photo) = app.session.catalog.photo(id).cloned() else { return }; let resp = ui.interact(r, egui::Id::new(("cell", id.0)), Sense::click_and_drag()); register(ui.ctx(), format!("thumb:{}", id.0), r); - let selected = app.session.selection.contains(id); + let state = app.session.selection.state_of(id); + let selected = state != lightcraft_engine::SelectionState::NotSelected; // screen readers: the file, then rating / flag / label let mut spoken = photo.file_name.clone(); if photo.rating > 0 { @@ -486,7 +487,7 @@ fn cell(app: &mut LightcraftApp, ui: &mut egui::Ui, id: PhotoId, r: Rect, square spoken.push_str(&format!(", {} label", app.session.catalog.label_name(l))); } resp.widget_info(|| egui::WidgetInfo::selected(egui::WidgetType::SelectableLabel, true, selected, &spoken)); - let active = app.session.selection.active == Some(id); + let active = state == lightcraft_engine::SelectionState::Active; let p = ui.painter(); let img_rect = if square { p.rect_filled(r, 0.0, if selected { t.cell_selected } else { t.cell }); @@ -514,6 +515,12 @@ fn cell(app: &mut LightcraftApp, ui: &mut egui::Ui, id: PhotoId, r: Rect, square } else { let ph = img_rect.shrink(if square { 20.0 } else { 0.0 }); p.rect_filled(ph, 0.0, Color32::from_gray(38)); + // not drawn yet (or unreadable): the selection is still visible + if active { + p.rect_stroke(ph, 0.0, Stroke::new(2.0, Color32::WHITE), StrokeKind::Outside); + } else if selected { + p.rect_stroke(ph, 0.0, Stroke::new(2.0, Color32::from_gray(170)), StrokeKind::Outside); + } if app.renderer.failure(Slot::Thumb(id)).is_some() { // unreadable / missing file p.text(ph.center(), Align2::CENTER_CENTER, "!", t.semibold(18.0), t.text_dim); diff --git a/crates/ui-egui/src/tests_grid.rs b/crates/ui-egui/src/tests_grid.rs index 3fe92327..e1a815a5 100644 --- a/crates/ui-egui/src/tests_grid.rs +++ b/crates/ui-egui/src/tests_grid.rs @@ -131,3 +131,27 @@ fn grid_headers_selection_scrolling_stacks_and_shape_changes() { assert!(after.height() > after.width(), "{after:?}"); assert_cheap_frames(&mut h); } + +/// Selected photos whose thumbnails have not loaded (or can't) are still marked selected in the +/// justified grid (issue #298): the active one white, the others grey. +#[test] +fn unloaded_thumbnails_show_their_selection() { + let mut h = grid(ViewMode::PhotoGrid); + h.request("engine.execute", json!({"command": "library.selectAll"}), T); + let img = h.snapshot(SETTLE); + assert_eq!(h.view.ctx.pixels_per_point(), 1.0); + let active = h.app.session.selection.active.expect("active"); + let canvas = h.app.canvas_rect.expect("grid drawn"); + let mut checked = 0; + for (w, r) in h.app.widgets.iter().filter(|(w, _)| w.starts_with("thumb:")) { + if !canvas.shrink(4.0).contains_rect(*r) || r.min.x < 6.0 { + continue; + } + let id = w.trim_start_matches("thumb:").parse::().unwrap(); + let want = if id == active.0 { egui::Color32::WHITE } else { egui::Color32::from_gray(170) }; + let px = img[((r.min.x - 1.0) as usize, r.center().y as usize)]; + assert_eq!(px, want, "photo {id}"); + checked += 1; + } + assert!(checked >= 3, "checked {checked}"); +} From 0b5ef3fe11fbc9fdaae7019c297d5bf68dcea16e Mon Sep 17 00:00:00 2001 From: Nathaniel Ledford Date: Thu, 8 Oct 2026 11:39:28 -0400 Subject: [PATCH 26/41] Import review: choose what happens to files in Recently Deleted (#298) Files whose photo is in the trash stay unchecked until Restore them or Import as new is chosen; the toast and undo label say what was restored. Co-Authored-By: Claude Sonnet 5.5 --- crates/ui-egui/src/headless.rs | 65 +++++++++++++++++++++++++ crates/ui-egui/src/import.rs | 88 +++++++++++++++++++++++++++++++--- docs/parity.md | 2 +- 3 files changed, 147 insertions(+), 8 deletions(-) diff --git a/crates/ui-egui/src/headless.rs b/crates/ui-egui/src/headless.rs index 8a548502..1cf3211a 100644 --- a/crates/ui-egui/src/headless.rs +++ b/crates/ui-egui/src/headless.rs @@ -1358,6 +1358,71 @@ mod tests { let _ = std::fs::remove_dir_all(&dir); } + /// The import review offers what to do with files that are in Recently Deleted (issue #298): + /// they are unchecked until Restore or Import as new is chosen; then confirming restores the + /// photo (with its edits) or imports the file afresh. + #[test] + fn import_review_offers_recently_deleted_files() { + for (choice, tag) in [("restore", "r"), ("fresh", "f")] { + let dir = std::env::temp_dir().join(format!("lc-ui-import-trash-{tag}-{}", std::process::id())); + let _ = std::fs::remove_dir_all(&dir); + std::fs::create_dir_all(&dir).unwrap(); + for i in 0..2u8 { + let data: Vec<[u8; 4]> = (0..40 * 30).map(|k| [(k % 40 * 6) as u8, i * 90, 7, 255]).collect(); + let img = lightcraft_raster::Rgba8 { width: 40, height: 30, data }; + let png = + lightcraft_codecs::encode_png(&lightcraft_codecs::EncodeImage::rgba8(&img), &lightcraft_codecs::EncodeMeta::default()).unwrap(); + std::fs::write(dir.join(format!("img{i}.png")), png).unwrap(); + } + let services = crate::Services { png: None, ..Default::default() }; + let mut app = LightcraftApp::new(lightcraft_engine::Session::new().with_fs(), services); + app.ui.view = crate::state::ViewMode::PhotoGrid; + let mut h = Headless::new(app, [1300.0, 900.0], 1.0); + let t = Duration::from_secs(10); + let r = + h.request("engine.execute", json!({"command": "library.import", "params": {"paths": [dir.join("img0.png").to_string_lossy()]}}), t); + let id = r["result"]["imported"][0].as_u64().unwrap(); + h.request("engine.execute", json!({"command": "photo.rate", "params": {"ids": [id], "rating": 3}}), t); + h.request("engine.execute", json!({"command": "photo.delete", "params": {"ids": [id]}}), t); + h.request("engine.execute", json!({"command": "file.addPhotos", "params": {"paths": [dir.to_string_lossy()]}}), t); + h.step_until(SETTLE, |h| matches!(h.app.ui.dialog, Some(crate::state::Dialog::Import { .. }))); + let Some(crate::state::Dialog::Import { opts }) = &mut h.app.ui.dialog else { panic!("no import review") }; + assert_eq!(opts.selected_paths().len(), 1, "only the new file: the trashed one waits for a choice"); + opts.set_on_deleted(choice); + assert_eq!(opts.selected_paths().len(), 2, "the trashed file is checked once a choice is made"); + // a per-cell choice survives switching between the two options, and "Leave them" unchecks + let trashed = (0..opts.candidates.len()).find(|i| opts.is_trashed(*i)).unwrap(); + opts.checked[trashed] = false; + opts.set_on_deleted(if choice == "restore" { "fresh" } else { "restore" }); + assert_eq!(opts.selected_paths().len(), 1, "still unchecked"); + opts.set_on_deleted(""); + assert_eq!(opts.selected_paths().len(), 1); + opts.set_on_deleted(choice); + assert_eq!(opts.selected_paths().len(), 2); + let r = h.request("ui.dialog.confirm", json!({}), t); + assert_eq!(r["ok"], true, "{r}"); + // frames until the import has finished, so its (timed) toast is read before it expires + let t0 = std::time::Instant::now(); + while h.app.import.is_some() && t0.elapsed() < Duration::from_secs(30) { + h.step(); + } + assert!(h.app.import.is_none(), "finished"); + let toast = h.app.ui.toast.clone().map(|t| t.0).unwrap_or_default(); + let photos = &h.app.session.catalog; + assert_eq!(photos.photos().filter(|p| !p.deleted).count(), 2, "{choice}"); + let old = photos.photo(lightcraft_catalog::PhotoId(id)); + if choice == "restore" { + assert!(toast.contains("1 restored"), "{toast}"); + assert!(old.is_some_and(|p| !p.deleted && p.rating == 3), "restored with its edits"); + assert_eq!(photos.len(), 2); + } else { + assert!(old.is_none(), "the trashed record is gone"); + assert_eq!(photos.len(), 2); + } + let _ = std::fs::remove_dir_all(&dir); + } + } + /// Adding a file again that is in Recently Deleted shows it there (side panel opened, photo /// selected) instead of only saying "duplicate skipped"; its menus offer Restore, and once /// restored a re-add selects it in All Photos. diff --git a/crates/ui-egui/src/import.rs b/crates/ui-egui/src/import.rs index 3ac7c9eb..e88e518a 100644 --- a/crates/ui-egui/src/import.rs +++ b/crates/ui-egui/src/import.rs @@ -53,6 +53,10 @@ pub struct ImportDialog { pub metadata_preset: String, /// Copy: raws are copied as DNG. pub dng: bool, + /// Per candidate: the library already has it in Recently Deleted. + pub trashed: Vec, + /// What to do with those: "" (leave them), `restore` or `fresh` (the `onDeleted` param). + pub on_deleted: String, } impl ImportDialog { @@ -61,14 +65,33 @@ impl ImportDialog { ImportDialog { candidates, checked, ..Default::default() } } pub fn importable(&self, i: usize) -> bool { - self.candidates.get(i).is_some_and(|c| c.duplicate.is_none() && c.error.is_none()) + self.candidates.get(i).is_some_and(|c| c.error.is_none() && (c.duplicate.is_none() || self.is_trashed(i) && !self.on_deleted.is_empty())) + } + /// Candidate `i` is a file whose photo is in Recently Deleted. + pub fn is_trashed(&self, i: usize) -> bool { + self.trashed.get(i).copied().unwrap_or(false) + } + /// Choose what happens to the files in Recently Deleted (`""`, `restore` or `fresh`); they are + /// checked once there is a choice and unchecked again without one. + pub fn set_on_deleted(&mut self, choice: &str) { + // switching between restore and fresh keeps the user's per-cell choices + let changed = self.on_deleted.is_empty() != choice.is_empty(); + self.on_deleted = choice.to_string(); + for i in (0..self.candidates.len()).filter(|_| changed) { + if self.is_trashed(i) + && let Some(c) = self.checked.get_mut(i) + { + *c = !choice.is_empty(); + } + } } pub fn selected_paths(&self) -> Vec { self.candidates .iter() .zip(&self.checked) - .filter(|(c, on)| **on && c.duplicate.is_none() && c.error.is_none()) - .map(|(c, _)| c.path.clone()) + .enumerate() + .filter(|(i, (_, on))| **on && self.importable(*i)) + .map(|(_, (c, _))| c.path.clone()) .collect() } /// The `organize` param of `library.import` (`None` = the default, by day); an unusable @@ -104,6 +127,8 @@ pub struct ImportTask { pub done: usize, params: Value, pub imported: usize, + /// Brought back from Recently Deleted (`onDeleted: restore`). + pub restored: usize, pub duplicates: usize, /// The library photos the skipped duplicates match (shown when nothing new was added). existing: Vec, @@ -400,6 +425,14 @@ pub fn poll_scan(app: &mut LightcraftApp, ctx: &egui::Context) { } app.renderer.forget_imports(); let mut d = ImportDialog::new(out.candidates); + d.trashed = d + .candidates + .iter() + .map(|c| { + c.duplicate.is_some() + && c.existing.is_some_and(|id| app.session.catalog.photo(lightcraft_catalog::PhotoId(id)).is_some_and(|p| p.deleted)) + }) + .collect(); d.copy = task.copy; d.sources = task.sources; app.ui.dialog = Some(crate::state::Dialog::Import { opts: Box::new(d) }); @@ -461,6 +494,9 @@ pub fn start(app: &mut LightcraftApp, d: &ImportDialog) -> Result (true, true) => "move", }; let mut params = json!({"mode": mode, "keywords": d.keywords()}); + if !d.on_deleted.is_empty() { + params["onDeleted"] = json!(d.on_deleted); + } if let Some(a) = album { params["album"] = json!(a); } @@ -564,6 +600,7 @@ fn commit_batch(app: &mut LightcraftApp, task: &mut ImportTask, prepared: lightc } let len = |k: &str| v[k].as_array().map_or(0, Vec::len); task.imported += len("imported"); + task.restored += len("restored"); task.duplicates += len("duplicates"); task.existing.extend(v["duplicates"].as_array().into_iter().flatten().filter_map(|d| d["existing"].as_u64())); task.failed += len("failed"); @@ -573,7 +610,7 @@ fn commit_batch(app: &mut LightcraftApp, task: &mut ImportTask, prepared: lightc log::warn!("import: kept {}: {}", k["path"].as_str().unwrap_or(""), k["reason"].as_str().unwrap_or("")); } if task.first.is_none() { - task.first = v["imported"].as_array().and_then(|a| a.first()).and_then(Value::as_u64); + task.first = ["imported", "restored"].iter().find_map(|k| v[*k].as_array().and_then(|a| a.first()).and_then(Value::as_u64)); } } Err(e) => { @@ -586,7 +623,14 @@ fn commit_batch(app: &mut LightcraftApp, task: &mut ImportTask, prepared: lightc /// The import is done (or cancelled): one undo step, select the first photo, say what happened. fn finish(app: &mut LightcraftApp, ctx: &egui::Context, task: ImportTask) { let steps = app.session.undo.len().saturating_sub(task.undo0); - let label = crate::i18n::tr_format!("Add {} Photo{}", task.imported, if task.imported == 1 { "" } else { "s" }); + let plural = |n: usize| if n == 1 { "" } else { "s" }; + let label = if task.imported == 0 && task.restored > 0 { + format!("Restore {} Photo{}", task.restored, plural(task.restored)) + } else if task.restored > 0 { + format!("Add {} Photo{}, restore {}", task.imported, plural(task.imported), task.restored) + } else { + crate::i18n::tr_format!("Add {} Photo{}", task.imported, if task.imported == 1 { "" } else { "s" }) + }; app.session.merge_undo(steps, &label); if task.auto { if task.imported > 0 { @@ -606,7 +650,7 @@ fn finish(app: &mut LightcraftApp, ctx: &egui::Context, task: ImportTask) { let plural = |n: usize| if n == 1 { "" } else { "s" }; // nothing new, only files the library already has: show where they are (Recently Deleted is // easy to miss, and the side panel that lists it starts collapsed) - if task.imported == 0 && task.failed == 0 && !task.cancelled && show_existing(app, ctx, &task.existing) { + if task.imported == 0 && task.restored == 0 && task.failed == 0 && !task.cancelled && show_existing(app, ctx, &task.existing) { return; } let mut msg = if task.cancelled { @@ -614,6 +658,9 @@ fn finish(app: &mut LightcraftApp, ctx: &egui::Context, task: ImportTask) { } else { crate::i18n::tr_format!("Imported {} photo{}", task.imported, plural(task.imported)) }; + if task.restored > 0 { + msg.push_str(&format!(" · {} restored from Recently Deleted", task.restored)); + } if task.params["mode"] == "move" { msg.push_str(&crate::i18n::tr_format!(" · {} moved", task.moved)); if task.kept > 0 { @@ -895,6 +942,32 @@ pub fn body(app: &mut LightcraftApp, ui: &mut egui::Ui, d: &mut ImportDialog) { ); } } + if d.trashed.iter().any(|t| *t) { + field(ui, "In Recently Deleted", |ui| { + let cur = match d.on_deleted.as_str() { + "restore" => "Restore them", + "fresh" => "Import as new", + _ => "Leave them", + }; + let mut pick = None; + egui::ComboBox::from_id_salt("import-on-deleted").selected_text(crate::i18n::tr(cur)).show_ui(ui, |ui| { + for (v, label) in [("", "Leave them"), ("restore", "Restore them"), ("fresh", "Import as new")] { + if ui.selectable_label(d.on_deleted == v, crate::i18n::tr(label)).clicked() { + pick = Some(v); + } + } + }); + if let Some(v) = pick { + d.set_on_deleted(v); + } + let t = Tokens::get(ui.ctx()); + ui.label( + egui::RichText::new(crate::i18n::tr("Restore keeps their edits; Import as new deletes the old record first.")) + .color(t.text_dim) + .small(), + ); + }); + } let albums: Vec<(u64, String)> = { let mut v: Vec<(u64, String)> = app.session.catalog.albums().filter(|a| !a.folder && !a.is_smart()).map(|a| (a.id.0, a.name.clone())).collect(); @@ -1000,6 +1073,7 @@ fn candidate_cell(app: &mut LightcraftApp, ui: &mut egui::Ui, d: &mut ImportDial } let in_trash = c.existing.is_some_and(|id| app.session.catalog.photo(lightcraft_catalog::PhotoId(id)).is_some_and(|p| p.deleted)); let badge = match (&c.duplicate, &c.error) { + (Some(_), _) if in_trash && ok => Some(if d.on_deleted == "restore" { "Will be restored" } else { "Will be replaced" }), (Some(_), _) if in_trash => Some("In Recently Deleted"), (Some(r), _) if r == "path" => Some("In library"), (Some(_), _) => Some("Duplicate"), @@ -1117,7 +1191,7 @@ pub const DEFAULT_FOLDER_TEMPLATE: &str = "{date:%Y}/{date:%Y%m%d}"; /// Where the first selected photo would be copied to (destination, folders, name), for the /// dialog's example line; `None` without a photo or with an unusable folder template. pub fn example_destination(app: &LightcraftApp, d: &ImportDialog) -> Option { - let c = d.candidates.iter().zip(&d.checked).find(|(c, on)| **on && c.duplicate.is_none() && c.error.is_none()).map(|(c, _)| c)?; + let c = d.candidates.iter().zip(&d.checked).enumerate().find(|(i, (_, on))| **on && d.importable(*i)).map(|(_, (c, _))| c)?; let organize = match d.organize_param().ok()? { Some(o) => lightcraft_engine::import::Organize::parse(&o)?, None => Default::default(), diff --git a/docs/parity.md b/docs/parity.md index 628532ef..eeafbd17 100644 --- a/docs/parity.md +++ b/docs/parity.md @@ -150,7 +150,7 @@ under the UI command that wraps it (a no-op error); the UI command now wins. | LR-LIB-PEOPLE | People / faces | P2 | 🟡 | `cmd:view.people`, `cmd:view.faceBoxes`, `cmd:photo.removeRegion`, `cmd:photo.setRegion`, `cmd:library.filter`, `crates/ui-egui/src/panels/people.rs`, `crates/catalog/src/query.rs` (`people`), `crates/engine/src/media.rs` (`face_job`), `crates/meta/src/xmp.rs` | MVP: named face regions read from XMP (MWG-RS, read-only, see `docs/xmp-interop.md`; clipped to the photo; Lightroom's `Rotation` of ±π/2 and π handled, mirrored orientations taken as written; boxes follow Rotate Left/Right and flips; a sidecar stating `mwg-rs:Regions`, even empty, replaces the photo's on re-read, one without leaves them) become cards in the People view (View ▸ People, or the People button in the toolbar): a close-up of the person's largest face, name and photo count; a click filters the grid (`person` in `library.filter`, `person:` search token, filter chip). Face boxes in the loupe can be switched off (View ▸ Face Boxes, toolbar) resized with eight drag handles and removed with the × shown on hover (catalog-only and undoable, one undo step per drag; the sidecar is never rewritten for either); no moving a box by dragging it yet. No face detection, no unnamed people / suggestions, no manual tagging, no writing regions back | | LR-LIB-STACK | Stacks | P1 | ✅ | `cmd:stack.group`, `cmd:stack.ungroup`, `cmd:stack.toggle`, `cmd:stack.setTop`, `cmd:stack.remove`, `cmd:stack.auto`, `crates/catalog/src/stacks.rs` | grid/filmstrip count badges, expand/collapse, auto-stack by capture time; no visual-similarity auto-stack | | LR-LIB-VERSIONS | Versions | P1 | ✅ | `cmd:version.create` | see section M | -| LR-LIB-DELETE | Delete / Recently Deleted | P0 | ✅ | `cmd:photo.delete`, `cmd:photo.restore`, `cmd:photo.deletePermanently` | Restore and Delete Permanently in the Photo menu and the photo context menus for photos in Recently Deleted (before, the UI had no way to restore); adding a file that is in Recently Deleted again selects it there (side panel opened) and says how to restore it or import it afresh; Photo ▸ Empty Recently Deleted (`cmd:library.emptyRecentlyDeleted`, one undo step, shown while the trash is the source); import takes `onDeleted` skip / restore / fresh for files in the trash (`cmd:library.import`; the report flags `existingDeleted` and lists `restored`; the desktop import dialog still uses skip and points at Restore); no confirmation dialog, no auto-purge after N days (needs a deleted-at date in the catalog) | +| LR-LIB-DELETE | Delete / Recently Deleted | P0 | ✅ | `cmd:photo.delete`, `cmd:photo.restore`, `cmd:photo.deletePermanently` | Restore and Delete Permanently in the Photo menu and the photo context menus for photos in Recently Deleted (before, the UI had no way to restore); adding a file that is in Recently Deleted again selects it there (side panel opened) and says how to restore it or import it afresh; Photo ▸ Empty Recently Deleted (`cmd:library.emptyRecentlyDeleted`, one undo step, shown while the trash is the source); import takes `onDeleted` skip / restore / fresh for files in the trash (`cmd:library.import`; the report flags `existingDeleted` and lists `restored`; the import review dialog offers Leave them / Restore them / Import as new for files that are in the trash; dropped files and Add Photos without a review still skip and point at Restore); no confirmation dialog, no auto-purge after N days (needs a deleted-at date in the catalog) | | LR-LIB-REMOVE-ALBUM | Remove from album | P0 | ✅ | `cmd:album.removePhotos` | | | LR-LIB-DUPLICATE | Duplicate a photo | P2 | ✅ | `cmd:photo.duplicate` | Photo ▸ Duplicate: a real `-copy` file with the same settings, metadata and albums (virtual copies share the file) | | LR-LIB-RENAME | Batch rename | P1 | ✅ | `cmd:photo.rename`, `cmd:photo.renamePreview`, `cmd:photo.renameTokens`, `cmd:dialog.rename`, `crates/engine/src/rename.rs` (`TOKENS`) | template tokens {name} {num} {seq:N} {date:%Y%m%d} {folder} {camera} {lens} {iso} {rating} {title} {creator} {ext} (shared with export naming and import rename); a Tags picker beside every template field (Rename Photos, import Rename, export File name) lists each tag with meaning + live example, the date directives and the rules, and inserts the clicked tag at the text cursor; unknown tags are flagged; preview; renames files on disk with their XMP sidecars (a stem sidecar another file still shares, e.g. raw + JPEG, is copied rather than taken), never overwriting (-1, -2… suffixes; a change of letter case only is checked by file identity, so on case-sensitive volumes `img_1.jpg` next to `IMG_1.JPG` is a collision), rolls back on failure (a file that can't be moved back is reported old → new and the library follows it as an undoable partial rename; the same for undo/redo); undo/redo move the files; virtual copies follow | From e4ab8422b4e139dd9d1a853b7a35094c0df71db2 Mon Sep 17 00:00:00 2001 From: Xus Badia Date: Thu, 8 Oct 2026 15:12:32 +0200 Subject: [PATCH 27/41] macOS DMG: branded window background and icon layout The DMG opened as a plain Finder window. It now shows the app icon's figure on the LightCraft colour, with LightCraft.app and the Applications link side by side. Same approach as VectorCraft's (storytold/vectorcraft#493). - packaging/macos/dmg/: background.svg (source; text outlined, the figure an of assets/app-icon), background.tiff (1x + 2x HiDPI, Deflate, sRGB) and dmg-layout.DS_Store (Finder view settings: window, icon size 128, positions, background). - generate.py writes the TIFF and the layout on any OS, byte-for-byte reproducible, with nothing from the machine that ran it (no paths, users, disk names or volume UUIDs). - package.sh copies them into the staging folder; the image is still built with hdiutil makehybrid. Nothing extra runs in the signing job. - The volume name drops the version: the layout finds the background through an alias that includes the volume name. The DMG file name keeps the version. Co-Authored-By: Claude Opus 5.5 --- assets/ATTRIBUTION.md | 1 + packaging/macos/dmg/README.md | 44 +++++++ packaging/macos/dmg/background.svg | 31 +++++ packaging/macos/dmg/background.tiff | Bin 0 -> 1045648 bytes packaging/macos/dmg/dmg-layout.DS_Store | Bin 0 -> 16388 bytes packaging/macos/dmg/generate.py | 149 ++++++++++++++++++++++++ packaging/macos/package.sh | 7 +- 7 files changed, 231 insertions(+), 1 deletion(-) create mode 100644 packaging/macos/dmg/README.md create mode 100644 packaging/macos/dmg/background.svg create mode 100644 packaging/macos/dmg/background.tiff create mode 100644 packaging/macos/dmg/dmg-layout.DS_Store create mode 100644 packaging/macos/dmg/generate.py diff --git a/assets/ATTRIBUTION.md b/assets/ATTRIBUTION.md index 208cc39c..47f129a6 100644 --- a/assets/ATTRIBUTION.md +++ b/assets/ATTRIBUTION.md @@ -11,6 +11,7 @@ covers a whole directory), or if a licence file referenced in the Licence column | `assets/fonts/Inter-*.ttf` (Regular, Medium, SemiBold) | Inter | Rasmus Andersson | https://github.com/rsms/inter | SIL Open Font License 1.1 (`assets/fonts/OFL-Inter.txt`) | 2026-09-30 | none | | `assets/app-icon/` (`lightcraft.svg` and the PNG/ICNS/ICO/SVG files derived from it) | LightCraft app icon (lynx) | LightCraft project owner (drawn in ArtCraft) | original work (vectorised from the owner's drawing; see `assets/app-icon/README.md`) | MIT OR Apache-2.0 (`assets/app-icon/LICENSE.txt`) | 2026-10-02 | vectorised to SVG; rendered to PNG/ICNS/ICO by `packaging/icons.sh` | | `assets/camera-profiles/*.json` | Camera colour profiles (matrix + hue/saturation/value table), built into LightCraft | ILCE-7M4: Harald Hoyer | original work: fitted by `lightcraft-cli calibrate` to the contributor's own raw photos and their in-camera JPEGs (597 ILCE-7M4 photos, 2026); aggregate colour statistics only, no image content; no Adobe or third-party profile data | MIT OR Apache-2.0 | 2026-10-07 | none | +| `packaging/macos/dmg/` (`background.svg`, the `background.tiff` rendered from it by `generate.py`, and the `dmg-layout.DS_Store` layout file) | LightCraft macOS DMG window background | @XusBadia | original work for this repository, derived from the LightCraft app icon (`assets/app-icon/lightcraft.svg`, referenced via ``, not copied); text is outlined Inter / JetBrains Mono glyph paths | MIT OR Apache-2.0 | 2026-10-08 | none (original); used for the Finder window of the macOS DMG, `packaging/macos/dmg/generate.py` renders it to `background.tiff` | | `crates/scenes/` (generated images) | Procedural demo photographs | LightCraft contributors | original work (generated at runtime, no source imagery) | MIT OR Apache-2.0 | 2026-09-30 | n/a | | `crates/ui-egui/src/icons.rs` | UI icons drawn as vector paths in code | LightCraft contributors | original work | MIT OR Apache-2.0 | 2026-09-30 | n/a | | `docs/images/*-tetons.jpg`, `docs/images/grid-pd.jpg` (screenshots containing it) | "The Tetons and the Snake River" | Ansel Adams (1942), U.S. National Archives | https://commons.wikimedia.org/wiki/File:Adams_The_Tetons_and_the_Snake_River.jpg | Public domain (U.S. federal government work) | 2026-09-30 | developed in LightCraft; shown inside app screenshots | diff --git a/packaging/macos/dmg/README.md b/packaging/macos/dmg/README.md new file mode 100644 index 00000000..2b2e1a13 --- /dev/null +++ b/packaging/macos/dmg/README.md @@ -0,0 +1,44 @@ +# macOS DMG window + +What Finder shows when the DMG opens: a 660 × 400 pt background with the app icon and the +`Applications` link side by side. `package.sh` copies these files into the image; nothing here is +generated at build time, so the DMG still builds with `hdiutil makehybrid` (no mounted device, no +Finder scripting on CI, nothing extra installed in the signing job). + +| File | What | +|---|---| +| `background.svg` | Source of the background: the app icon (`assets/app-icon/lightcraft.svg`, linked, not copied) cropped as a cover on the LightCraft colour field (`#f2a516`), Ink and Paper. Its text (Inter, JetBrains Mono) is outlined, so rendering it needs no fonts. | +| `background.tiff` | The background at 1x (660 × 400 px, 72 dpi) and 2x (1320 × 800 px, 144 dpi) in one HiDPI TIFF (Deflate, sRGB). Goes to `.background/background.tiff`. | +| `dmg-layout.DS_Store` | Finder's view settings for the volume: window size, icon size 128, LightCraft.app at (326, 205), `Applications` at (574, 205), and the background. Goes to `.DS_Store` in the image; named so it isn't mistaken for (or ignored like) a Finder-generated `.DS_Store`. | +| `generate.py` | Writes `background.tiff` and `dmg-layout.DS_Store` from the SVG and the layout above. | + +## The volume name has no version + +The mounted volume is called `LightCraft`, not `LightCraft `. The layout points at the background +through an alias that includes the volume name, and Finder resolves it by that name: with a +versioned name the window keeps its size and icon positions but shows no background (tested). +The DMG file name (`lightcraft--macos-.dmg`) still carries the version. + +## Rules + +- **Finder draws the icon labels in black in light and dark mode** when a window has a background, + so the area under both icons stays light (Paper). +- **Nothing goes inside the icon boxes:** artwork keeps 10 pt clear of each 128 pt icon box and of + the label strip under it. + +## Regenerate + +Edit `background.svg` (or the layout constants in `generate.py`), then run, on any OS: + +```sh +pip install 'pillow>=12' ds_store==1.3.3 mac_alias==2.2.3 +python3 packaging/macos/dmg/generate.py # needs resvg on PATH +``` + +Both outputs are byte-for-byte reproducible and carry nothing from the machine that ran it: the +SVG renders without any fonts, and `dmg-layout.DS_Store` is written from scratch, its background +alias holding only the volume name and `/.background/background.tiff`. If you add text to the SVG, +outline it (`usvg` from resvg converts text to paths). The window is 660 × 432 with Finder's 32 pt +title bar; the content area is 660 × 400. + +This follows VectorCraft's `packaging/macos/dmg/` (storytold/vectorcraft#493). diff --git a/packaging/macos/dmg/background.svg b/packaging/macos/dmg/background.svg new file mode 100644 index 00000000..7dd108ea --- /dev/null +++ b/packaging/macos/dmg/background.svg @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packaging/macos/dmg/background.tiff b/packaging/macos/dmg/background.tiff new file mode 100644 index 0000000000000000000000000000000000000000..0c1fdef7ea1eba92b2594a983ccd4abc77a242a8 GIT binary patch literal 1045648 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 z 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 -feQr^Z% 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

mJg>tki?42en1?tqE9Q~ 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%0A}fB5+0U~p$F=1mcC-oBr*kKJON_I1U+nZg%)@-O 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%0DS?0ns}eoSX$Y<~^-Ho3OFAg@aOW$^C;^T}xa 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%0D9}`BNA7bI@aU6JSo1aPgb5TA5{+d9Zjd|l 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�eyZhdA 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ύ+ZD%+lUNyrEGNP%B{#{H~Q7yWAt($=I6 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

4p!4?py7Mk8ZoH~nw_ew2vf^3{Zoo^ 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 z%0D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 I|7ZFC06RbYZ~y=R literal 0 HcmV?d00001 diff --git a/packaging/macos/dmg/dmg-layout.DS_Store b/packaging/macos/dmg/dmg-layout.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..ce0faf287d972b6cc8a7b2155f4c71948b9ce026 GIT binary patch literal 16388 zcmeI2O-vI(6oB6>pyjt)3JB<-rZL7yN(Ci^gO)@>SuPzeb) zFM2Q@^=iDSi6`U1s~4|ay%`T4ym(dL>~3wT8WZD%_-502Z{EH)JMY`{XI>WoShi9; z2M~eUOYOkHU4TS8O4*b^KnqF+poT*dM1Tko0U|&Ih`=Ex&=jmaBERoX;rD*$A>Y5%?bo1pn=)^li7STS7+!FZ8lyx?VcH&y=R-mLo@Hi>#_W zQ^NK%)8z?LS+-!JeLDLsazU_N02w`EwjccY|6x&%*v5PKF z+rP6jl1>jLE{u%t4kdQR#!x>$IFqmqtbbr|S#uoCHhA{-sv6g|!iM3dgS_Vs*O zE10&iWEO;-H}CKjOVhbEXJ>`yiOp3t>LU3Zp1kVtE#!K`AA5W=XP3m1$8UKu3Rfpj z&Xqk=*zO{CT$Bs8uzB**=K$VZjQLox$1-Z$tZA%!Q;t^h8rTAS_;i2o%31#rkb!6| zm6T_eNEmBLmjaI)z>u+CAuo6T@}$4>wL9 zhxqWKuqvC}9j+}2WR9wmtfrUDEC*^bvlb|=kz^(vsH&`jpMg=x!5XgY9z24l@Dkp@ zTiAz>@CClIIO}I8l?Td0<+1Wad8Ry9UMR1EXr=lVIUbCwz4970e2Z-59orJN;g0}1 zU=qrBSeR=8bCd)G(r|N@D1;vdSWH(gqW?R2uVg0Jx_(I~g)`u62V>bxL##gmbwq#& v5CI}U1c(3;AOb{y2oM1xKm>>Y5g-CYfCvx)B0vO)01+SpM1TnVV*=j+Xn7*b literal 0 HcmV?d00001 diff --git a/packaging/macos/dmg/generate.py b/packaging/macos/dmg/generate.py new file mode 100644 index 00000000..35305238 --- /dev/null +++ b/packaging/macos/dmg/generate.py @@ -0,0 +1,149 @@ +#!/usr/bin/env python3 +"""Regenerate background.tiff and dmg-layout.DS_Store for the macOS DMG window (see README.md here). + +Needs resvg on PATH and: + pip install 'pillow>=12' ds_store==1.3.3 mac_alias==2.2.3 + +Runs on any OS, no Mac needed. Both outputs come from this script alone, so they carry nothing +from the machine that made them: no local paths, user or disk names, dates or volume UUIDs. + + python3 packaging/macos/dmg/generate.py +""" + +import datetime +import os +import shutil +import subprocess +import sys +import tempfile + +from ds_store import DSStore +from mac_alias import Alias, TargetInfo, VolumeInfo +from PIL import Image, ImageCms + +HERE = os.path.dirname(os.path.abspath(__file__)) + +# Keep in sync with package.sh (volume name) and README.md (layout). +VOLUME = "LightCraft" +WIDTH, HEIGHT = 660, 400 # window content, pt; the background's 1x size +TITLE_BAR = 32 +ICON_SIZE = 128 +ICONS = {"LightCraft.app": (326, 205), "Applications": (574, 205)} +# A fixed date for the alias and the colour profile. Finder never matches it against the image +# (each build is a new volume): it finds the background by volume name and path. +FIXED_DATE = datetime.datetime(2026, 10, 8, tzinfo=datetime.timezone.utc) + + +def render(svg, width, out): + # The SVG's text is already outlined and no fonts are loaded, so every machine draws the + # same pixels. + subprocess.run( + ["resvg", "--skip-system-fonts", "-w", str(width), svg, out], + check=True, + ) + with Image.open(out) as im: + return im.convert("RGB") + + +def srgb_profile(): + # LittleCMS's built-in sRGB ("No copyright, use freely"), with its creation date fixed. + icc = bytearray(ImageCms.ImageCmsProfile(ImageCms.createProfile("sRGB")).tobytes()) + d = FIXED_DATE + icc[24:36] = b"".join(v.to_bytes(2, "big") for v in (d.year, d.month, d.day, 0, 0, 0)) + if icc[84:100] != bytes(16): + sys.exit("error: the sRGB profile has an ID; recompute it after fixing the date") + return bytes(icc) + + +def write_tiff(svg, out): + """1x (72 dpi) and 2x (144 dpi) in one TIFF, as `tiffutil -cathidpicheck` writes them.""" + with tempfile.TemporaryDirectory() as tmp: + one, two = (render(svg, WIDTH * s, os.path.join(tmp, f"{s}x.png")) for s in (1, 2)) + two.encoderinfo = {"dpi": (144, 144)} # the rest as the first page + # Opaque RGB, Deflate without a predictor: the paper grain is noise, which a predictor inflates. + one.save( + out, + "TIFF", + save_all=True, + append_images=[two], + compression="tiff_adobe_deflate", # brand-ok: Pillow's name for TIFF compression 8 (Deflate) + dpi=(72, 72), + icc_profile=srgb_profile(), + ) + with Image.open(out) as im: + im.seek(1) + if im.info.get("dpi") != (144, 144): + sys.exit("error: the 2x page isn't 144 dpi (needs Pillow 12 or later)") + strips = [] + for page in (0, 1): + im.seek(page) + strips += zip(im.tag_v2[273], im.tag_v2[279]) # StripOffsets, StripByteCounts + # TIFF keeps directories and tag data on even offsets. When a strip ends on an odd one, libtiff + # leaves the padding byte after it uninitialised, so the file changed from run to run with + # identical pixels. Zero it: nothing refers to that byte. + starts = {o for o, _ in strips} + with open(out, "r+b") as f: + for o, n in strips: + if (o + n) % 2 and o + n not in starts: + f.seek(o + n) + f.write(bytes(1)) + + +def background_alias(): + # What Finder stores for /.background/background.tiff on the volume, minus anything about the + # machine (dmgbuild writes the same kind of alias from a mounted image). Disk type 5 (ejectable), + # the volume flags and the folder/file ids (25, 26) are what Finder wrote on a test image; the ids + # are only hints, as the volume's creation date. + volume = VolumeInfo(VOLUME, FIXED_DATE, b"H+", 5, 0x0D02, b"\0\0") + volume.posix_path = f"/Volumes/{VOLUME}" + target = TargetInfo(0, "background.tiff", 25, 26, FIXED_DATE, b"\0\0\0\0", b"\0\0\0\0") + target.folder_name = ".background" + target.cnid_path = [25] + target.carbon_path = f"{VOLUME}:.background:\0background.tiff" + target.posix_path = "/.background/background.tiff" + return Alias(volume=volume, target=target).to_bytes() + + +def write_ds_store(out): + if os.path.exists(out): + os.remove(out) + with DSStore.open(out, "w+") as d: + d["."]["bwsp"] = { + "ShowStatusBar": False, + "ShowToolbar": False, + "ShowTabView": False, + "ContainerShowSidebar": False, + "ShowSidebar": False, + "WindowBounds": f"{{{{200, 528}}, {{{WIDTH}, {HEIGHT + TITLE_BAR}}}}}", + } + d["."]["icvp"] = { + "viewOptionsVersion": 1, + "backgroundType": 2, + "backgroundImageAlias": background_alias(), + "backgroundColorRed": 1.0, + "backgroundColorGreen": 1.0, + "backgroundColorBlue": 1.0, + "gridOffsetX": 0.0, + "gridOffsetY": 0.0, + "gridSpacing": 100.0, + "arrangeBy": "none", + "showIconPreview": True, + "showItemInfo": False, + "labelOnBottom": True, + "textSize": 16.0, + "iconSize": float(ICON_SIZE), + } + d["."]["vSrn"] = ("long", 1) + for name, pos in ICONS.items(): + d[name]["Iloc"] = pos + + +def main(): + if not shutil.which("resvg"): + sys.exit("error: resvg not found (brew install resvg / cargo install resvg --locked)") + write_tiff(os.path.join(HERE, "background.svg"), os.path.join(HERE, "background.tiff")) + write_ds_store(os.path.join(HERE, "dmg-layout.DS_Store")) + + +if __name__ == "__main__": + main() diff --git a/packaging/macos/package.sh b/packaging/macos/package.sh index c6070055..5416e0db 100755 --- a/packaging/macos/package.sh +++ b/packaging/macos/package.sh @@ -132,10 +132,15 @@ STAGE="$WORK/dmg" mkdir -p "$STAGE" ditto "$APP" "$STAGE/LightCraft.app" ln -s /Applications "$STAGE/Applications" +# Finder window layout: background, icon size and positions (packaging/macos/dmg/README.md). +mkdir -p "$STAGE/.background" +cp "$HERE/dmg/background.tiff" "$STAGE/.background/background.tiff" +cp "$HERE/dmg/dmg-layout.DS_Store" "$STAGE/.DS_Store" rm -f "$DMG" "$WORK/raw.dmg" # makehybrid + convert builds the image without attaching a device, unlike `create -srcfolder`, # which is flaky on CI runners ("Resource busy") and hangs in sandboxed sessions. -hdiutil makehybrid -hfs -hfs-volume-name "LightCraft $VERSION" -hfs-openfolder "$STAGE" -o "$WORK/raw.dmg" "$STAGE" +# The volume name has no version: .DS_Store finds the background through an alias that includes it. +hdiutil makehybrid -hfs -hfs-volume-name "LightCraft" -hfs-openfolder "$STAGE" -o "$WORK/raw.dmg" "$STAGE" hdiutil convert "$WORK/raw.dmg" -format UDZO -imagekey zlib-level=9 -o "$DMG" rm -f "$WORK/raw.dmg" sign "$DMG" From 493600743d89294bf2ab16243b42e8f61a74b5c6 Mon Sep 17 00:00:00 2001 From: peiyan0 Date: Fri, 9 Oct 2026 00:43:19 +0800 Subject: [PATCH 28/41] engine, ui: support imported LUT profiles in profile dropdown and browser (#328) --- crates/engine/src/library.rs | 28 +++++----- crates/engine/src/presets.rs | 11 ++++ crates/engine/src/tests_import.rs | 6 ++- crates/ui-egui/src/panels/edit.rs | 63 +++++++++++++--------- crates/ui-egui/src/panels/profiles.rs | 77 ++++++++++++++++++--------- docs/parity.md | 2 +- 6 files changed, 123 insertions(+), 64 deletions(-) diff --git a/crates/engine/src/library.rs b/crates/engine/src/library.rs index 6a63e378..f49c6aa5 100644 --- a/crates/engine/src/library.rs +++ b/crates/engine/src/library.rs @@ -314,20 +314,6 @@ impl Session { journal.snapshot(&self.catalog)?; } let mut settings = SettingsLoad::default(); - // presets - if let Some(f) = settings.read::(files.as_mut(), "presets.json") { - for p in &mut self.presets { - p.favorite = p.builtin && f.favorites.contains(&p.id) || (!p.builtin && p.favorite); - } - for u in f.user { - if !self.presets.iter().any(|p| p.id == u.id) { - self.presets.push(u); - } - } - let known = |id: &String| crate::presets::profile(id).is_some(); - self.profile_favorites = f.profile_favorites.into_iter().filter(known).collect(); - self.profile_recent = f.profile_recent.into_iter().filter(known).take(crate::presets::RECENT_PROFILES).collect(); - } // preferences let prefs = settings.read::(files.as_mut(), "prefs.json").unwrap_or_default(); self.xmp = prefs.xmp; @@ -349,6 +335,20 @@ impl Session { if let Some(d) = &self.smart_previews_dir { self.media.smart_dir = Some(d.clone()); } + // presets + if let Some(f) = settings.read::(files.as_mut(), "presets.json") { + for p in &mut self.presets { + p.favorite = p.builtin && f.favorites.contains(&p.id) || (!p.builtin && p.favorite); + } + for u in f.user { + if !self.presets.iter().any(|p| p.id == u.id) { + self.presets.push(u); + } + } + let known = |id: &String| crate::presets::profile(id).is_some() || self.lut_profiles.iter().any(|p| &p.id == id); + self.profile_favorites = f.profile_favorites.into_iter().filter(known).collect(); + self.profile_recent = f.profile_recent.into_iter().filter(known).take(crate::presets::RECENT_PROFILES).collect(); + } // view state if let Some(v) = settings.read::(files.as_mut(), "view.json") { self.source = v.source; diff --git a/crates/engine/src/presets.rs b/crates/engine/src/presets.rs index 57a82ac5..f77b435d 100644 --- a/crates/engine/src/presets.rs +++ b/crates/engine/src/presets.rs @@ -446,6 +446,17 @@ pub fn profile(id: &str) -> Option<&'static ProfileInfo> { pub const RECENT_PROFILES: usize = 5; impl Session { + /// A profile by id, looking up built-in profiles first and imported LUT profiles second. + pub fn profile_info(&self, id: &str) -> Option<(&str, &str)> { + if let Some(p) = profile(id) { + return Some((p.name, p.group)); + } + self.lut_profiles + .iter() + .find(|p| p.id == id) + .map(|p| (p.name.as_str(), p.group.as_str())) + } + /// Remember `id` as the most recently applied profile. pub fn note_profile_used(&mut self, id: &str) { self.profile_recent.retain(|p| p != id); diff --git a/crates/engine/src/tests_import.rs b/crates/engine/src/tests_import.rs index af106765..2a508fc8 100644 --- a/crates/engine/src/tests_import.rs +++ b/crates/engine/src/tests_import.rs @@ -544,13 +544,17 @@ fn cube_luts_become_profiles() { mean(&warm, 2) ); s.execute("develop.profile", &json!({"id": id, "amount": 0})).unwrap(); - assert_eq!(s.render_now(photo, 48, 32).unwrap().image.data, base.data, "Amount 0 = the photo as it was"); + assert_eq!(s.profile_info(&id), Some(("Warm Test", "Film Looks"))); + s.execute("profile.favorite", &json!({"id": id})).unwrap(); + assert!(s.profile_favorites.contains(&id)); // the library keeps it drop(s); lightcraft_pipeline::lut::unregister(&id); let mut s = Session::new().with_fs(); s.open_library(&lib, false).unwrap(); assert!(s.lut_profiles.iter().any(|p| p.id == id)); + assert!(s.profile_favorites.contains(&id), "imported LUT favorite survives reopen"); + assert_eq!(s.profile_info(&id), Some(("Warm Test", "Film Looks"))); assert!(lightcraft_pipeline::lut::get(&id).is_some(), "registered again on open"); s.execute("profile.deleteImported", &json!({"id": id})).unwrap(); assert!(lightcraft_pipeline::lut::get(&id).is_none()); diff --git a/crates/ui-egui/src/panels/edit.rs b/crates/ui-egui/src/panels/edit.rs index cddb984e..2d79f5f5 100644 --- a/crates/ui-egui/src/panels/edit.rs +++ b/crates/ui-egui/src/panels/edit.rs @@ -139,7 +139,7 @@ pub fn show(app: &mut LightcraftApp, ui: &mut egui::Ui, id: PhotoId) { egui::Frame::NONE.inner_margin(egui::Margin { left: 24, right: 22, top: 14, bottom: 14 }).show(ui, |ui| { ui.horizontal(|ui| { ui.label(egui::RichText::new(crate::i18n::tr("Profile")).font(t.font(13.0)).color(t.text_dim)); - let name = lightcraft_engine::presets::profile(&d.profile.id).map(|p| p.name).unwrap_or("Color"); + let name = app.session.profile_info(&d.profile.id).map(|(name, _)| name).unwrap_or("Color"); let r = crate::widgets::dropdown(ui, "profile", crate::i18n::tr(name), t.font(15.0), t.text_label); egui::Popup::menu(&r).show(|ui| profile_menu(app, ui, &d)); ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| { @@ -345,36 +345,37 @@ pub fn show(app: &mut LightcraftApp, ui: &mut egui::Ui, id: PhotoId) { /// The profile dropdown: Favorites, Recent, one submenu per group, then favourite toggle and /// Browse…. fn profile_menu(app: &mut LightcraftApp, ui: &mut egui::Ui, d: &DevelopSettings) { - use lightcraft_engine::presets::{PROFILES, profile, profile_groups}; + use lightcraft_engine::presets::{PROFILES, profile_groups}; let t = Tokens::get(ui.ctx()); ui.set_min_width(200.0); let cur = d.profile.id.as_str(); // (clicked, hovered) - let mut pick: (Option<&'static str>, Option<&'static str>) = (None, None); - let item = |ui: &mut egui::Ui, - key: &str, - p: &'static lightcraft_engine::presets::ProfileInfo, - pick: &mut (Option<&'static str>, Option<&'static str>)| { - let r = ui.selectable_label(p.id == cur, crate::i18n::tr(p.name)); - register(ui.ctx(), format!("profileMenu:{key}:{}", p.id), r.rect); + let mut pick: (Option, Option) = (None, None); + let mut item = |ui: &mut egui::Ui, + key: &str, + id: &str, + name: &str, + pick: &mut (Option, Option)| { + let r = ui.selectable_label(id == cur, crate::i18n::tr(name)); + register(ui.ctx(), format!("profileMenu:{key}:{id}"), r.rect); if r.clicked() { - pick.0 = Some(p.id); + pick.0 = Some(id.to_string()); } if r.hovered() { - pick.1 = Some(p.id); + pick.1 = Some(id.to_string()); } }; let heading = |ui: &mut egui::Ui, s: &str| { ui.label(egui::RichText::new(crate::i18n::tr(s)).font(t.semibold(11.5)).color(t.text_dim)); }; for (title, key, ids) in [("Favorites", "fav", app.session.profile_favorites.clone()), ("Recent", "recent", app.session.profile_recent.clone())] { - let list: Vec<_> = ids.iter().filter_map(|id| profile(id)).collect(); + let list: Vec<_> = ids.iter().filter_map(|id| app.session.profile_info(id).map(|(name, _)| (id.as_str(), name))).collect(); if list.is_empty() { continue; } heading(ui, title); - for p in list { - item(ui, key, p, &mut pick); + for (id, name) in list { + item(ui, key, id, name, &mut pick); } ui.separator(); } @@ -382,23 +383,36 @@ fn profile_menu(app: &mut LightcraftApp, ui: &mut egui::Ui, d: &DevelopSettings) let r = ui.menu_button(crate::i18n::tr(g), |ui| { ui.set_min_width(170.0); for p in PROFILES.iter().filter(|p| p.group == g) { - item(ui, "group", p, &mut pick); + item(ui, "group", p.id, p.name, &mut pick); + } + }); + register(ui.ctx(), format!("profileMenu:groupMenu:{g}"), r.response.rect); + } + // imported LUT profile groups + let mut lut_groups: Vec = app.session.lut_profiles.iter().map(|p| p.group.clone()).collect(); + lut_groups.sort_unstable(); + lut_groups.dedup(); + for g in &lut_groups { + let r = ui.menu_button(crate::i18n::tr(g), |ui| { + ui.set_min_width(170.0); + for p in app.session.lut_profiles.iter().filter(|p| &p.group == g) { + item(ui, "group", &p.id, &p.name, &mut pick); } }); register(ui.ctx(), format!("profileMenu:groupMenu:{g}"), r.response.rect); } ui.separator(); - if let Some(p) = profile(cur) { - let fav = app.session.profile_favorites.iter().any(|f| f == p.id); + if let Some((name, _)) = app.session.profile_info(cur) { + let fav = app.session.profile_favorites.iter().any(|f| f == cur); let label = if fav { - crate::i18n::tr_format!("Remove “{}” from Favorites", crate::i18n::tr(p.name)) + crate::i18n::tr_format!("Remove “{}” from Favorites", crate::i18n::tr(name)) } else { - crate::i18n::tr_format!("Add “{}” to Favorites", crate::i18n::tr(p.name)) + crate::i18n::tr_format!("Add “{}” to Favorites", crate::i18n::tr(name)) }; let r = ui.button(label); register(ui.ctx(), "profileMenu:toggleFavorite", r.rect); if r.clicked() { - let _ = app.run("profile.favorite", json!({"id": p.id})); + let _ = app.run("profile.favorite", json!({"id": cur})); } } let r = ui.button(crate::i18n::tr("Browse…")); @@ -407,13 +421,14 @@ fn profile_menu(app: &mut LightcraftApp, ui: &mut egui::Ui, d: &DevelopSettings) let _ = app.run("panel.profiles", json!({})); } // resting on a profile previews it in the loupe - if let Some(p) = pick.1.and_then(profile) - && p.id != cur + if let Some(hovered_id) = &pick.1 + && hovered_id != cur + && let Some((name, _)) = app.session.profile_info(hovered_id) { let mut s = d.clone(); - s.profile.id = p.id.to_string(); + s.profile.id = hovered_id.clone(); s.profile.amount = 100.0; - app.hover_preview = Some(crate::HoverPreview { label: crate::i18n::tr_format!("Profile: {}", crate::i18n::tr(p.name)), settings: s }); + app.hover_preview = Some(crate::HoverPreview { label: crate::i18n::tr_format!("Profile: {}", crate::i18n::tr(name)), settings: s }); } if let Some(id) = pick.0 { let _ = app.run("develop.profile", json!({"id": id})); diff --git a/crates/ui-egui/src/panels/profiles.rs b/crates/ui-egui/src/panels/profiles.rs index ec4ddc57..1ae321f5 100644 --- a/crates/ui-egui/src/panels/profiles.rs +++ b/crates/ui-egui/src/panels/profiles.rs @@ -5,7 +5,7 @@ use egui::{Align2, Color32, Rect, Sense, Stroke, pos2, vec2}; use lightcraft_catalog::PhotoId; use lightcraft_develop::DevelopSettings; -use lightcraft_engine::presets::{PROFILES, ProfileInfo, profile, profile_groups}; +use lightcraft_engine::presets::{PROFILES, profile_groups}; use serde_json::json; use super::presets::cover_uv; @@ -33,7 +33,7 @@ pub fn show(app: &mut LightcraftApp, ui: &mut egui::Ui, id: PhotoId) { divider(ui); // the applied profile and its amount let (cr, _) = ui.allocate_exact_size(vec2(ui.available_width(), 34.0), Sense::hover()); - let name = profile(&d.profile.id).map(|p| p.name).unwrap_or("Color"); + let name = app.session.profile_info(&d.profile.id).map(|(name, _)| name).unwrap_or("Color"); ui.painter().text(pos2(cr.left() + 24.0, cr.center().y), Align2::LEFT_CENTER, crate::i18n::tr("Profile"), t.font(13.0), t.text_dim); ui.painter().text(pos2(cr.left() + 84.0, cr.center().y), Align2::LEFT_CENTER, crate::i18n::tr(name), t.font(14.0), t.text_label); if d.profile.id != "lc.color" { @@ -41,13 +41,32 @@ pub fn show(app: &mut LightcraftApp, ui: &mut egui::Ui, id: PhotoId) { ui.add_space(6.0); } divider(ui); - let mut sections: Vec<(&str, &str, Vec<&'static ProfileInfo>)> = Vec::new(); - let favs: Vec<&'static ProfileInfo> = app.session.profile_favorites.iter().filter_map(|f| profile(f)).collect(); + let mut sections: Vec<(&str, &str, Vec<(String, String, String)>)> = Vec::new(); + let favs: Vec<(String, String, String)> = app + .session + .profile_favorites + .iter() + .filter_map(|f| app.session.profile_info(f).map(|(name, group)| (f.clone(), name.to_string(), group.to_string()))) + .collect(); if !favs.is_empty() { sections.push(("Favorites", "fav", favs)); } for g in profile_groups() { - sections.push((g, "", PROFILES.iter().filter(|p| p.group == g).collect())); + sections.push((g, "", PROFILES.iter().filter(|p| p.group == g).map(|p| (p.id.to_string(), p.name.to_string(), p.group.to_string())).collect())); + } + // imported LUT profile groups + let mut lut_groups: Vec = app.session.lut_profiles.iter().map(|p| p.group.clone()).collect(); + lut_groups.sort_unstable(); + lut_groups.dedup(); + for g in &lut_groups { + let profiles = app + .session + .lut_profiles + .iter() + .filter(|p| &p.group == g) + .map(|p| (p.id.clone(), p.name.clone(), p.group.clone())) + .collect(); + sections.push((g.as_str(), "", profiles)); } for (title, tag, list) in sections { let open_id = egui::Id::new(("profile-group", title)); @@ -87,34 +106,44 @@ pub fn show(app: &mut LightcraftApp, ui: &mut egui::Ui, id: PhotoId) { if app.hover_preview.is_none() && let Some((saved_id, saved_profile_id)) = ui.data(|m| m.get_temp::<(PhotoId, String)>(last_hover_id)) && saved_id == id - && let Some(p) = profile(&saved_profile_id) + && let Some((p_name, _)) = app.session.profile_info(&saved_profile_id) { - let s = with_profile(&d, p); - app.hover_preview = Some(HoverPreview { label: crate::i18n::tr_format!("Profile: {}", crate::i18n::tr(p.name)), settings: s }); + let s = with_profile_id(&d, &saved_profile_id); + app.hover_preview = Some(HoverPreview { label: crate::i18n::tr_format!("Profile: {}", crate::i18n::tr(p_name)), settings: s }); } } else { ui.data_mut(|m| m.remove::<(PhotoId, String)>(last_hover_id)); } } -/// `d` with profile `p` (at 100 % unless it is the applied one). -fn with_profile(d: &DevelopSettings, p: &ProfileInfo) -> DevelopSettings { +/// `d` with profile `profile_id` (at 100 % unless it is the applied one). +fn with_profile_id(d: &DevelopSettings, profile_id: &str) -> DevelopSettings { let mut s = d.clone(); - if s.profile.id != p.id { - s.profile.id = p.id.to_string(); + if s.profile.id != profile_id { + s.profile.id = profile_id.to_string(); s.profile.amount = 100.0; } s } #[allow(clippy::too_many_arguments)] -fn cell_ui(app: &mut LightcraftApp, ui: &mut egui::Ui, d: &DevelopSettings, id: PhotoId, tag: &str, p: &'static ProfileInfo, cell: Rect, th: f32) { +fn cell_ui( + app: &mut LightcraftApp, + ui: &mut egui::Ui, + d: &DevelopSettings, + id: PhotoId, + tag: &str, + p: &(String, String, String), + cell: Rect, + th: f32, +) { + let (p_id, p_name, p_group) = (p.0.as_str(), p.1.as_str(), p.2.as_str()); let t = Tokens::get(ui.ctx()); - let key = if tag.is_empty() { format!("profileCell:{}", p.id) } else { format!("profileCell:{tag}:{}", p.id) }; + let key = if tag.is_empty() { format!("profileCell:{p_id}") } else { format!("profileCell:{tag}:{p_id}") }; let resp = ui.interact(cell, egui::Id::new(&key), Sense::click()); register(ui.ctx(), key, cell); let thumb = Rect::from_min_size(cell.min, vec2(cell.width(), th)); - let s = with_profile(d, p); + let s = with_profile_id(d, p_id); let painter = ui.painter(); painter.rect_filled(thumb, 3.0, t.canvas); if let Some(job) = app.session.variant_job(id, &s, THUMB_EDGE) @@ -122,33 +151,33 @@ fn cell_ui(app: &mut LightcraftApp, ui: &mut egui::Ui, d: &DevelopSettings, id: { painter.image(tex.tex.id(), thumb, cover_uv(thumb, tex.size), Color32::WHITE); } - let applied = d.profile.id == p.id; + let applied = d.profile.id == p_id; if applied { painter.rect_stroke(thumb.expand(1.0), 4.0, Stroke::new(2.0, t.pick), egui::StrokeKind::Outside); } else if resp.hovered() { painter.rect_stroke(thumb, 3.0, Stroke::new(1.0, t.text_dim), egui::StrokeKind::Outside); } let name_color = if applied { t.text } else { t.text_label }; - painter.text(pos2(cell.left() + 2.0, thumb.bottom() + 12.0), Align2::LEFT_CENTER, crate::i18n::tr(p.name), t.font(12.0), name_color); + painter.text(pos2(cell.left() + 2.0, thumb.bottom() + 12.0), Align2::LEFT_CENTER, crate::i18n::tr(p_name), t.font(12.0), name_color); // favourite star (always shown when set, on hover otherwise) - let fav = app.session.profile_favorites.iter().any(|f| f == p.id); + let fav = app.session.profile_favorites.iter().any(|f| f == p_id); let star = Rect::from_center_size(pos2(thumb.right() - 13.0, thumb.top() + 13.0), vec2(20.0, 20.0)); - register(ui.ctx(), format!("profileStar:{}{}", if tag.is_empty() { "" } else { "fav:" }, p.id), star); + register(ui.ctx(), format!("profileStar:{}{}", if tag.is_empty() { "" } else { "fav:" }, p_id), star); let over_star = resp.hover_pos().is_some_and(|q| star.contains(q)); if fav || resp.hovered() { painter.circle_filled(star.center(), 10.0, Color32::from_black_alpha(110)); paint(painter, star.shrink(4.0), if fav { Icon::StarFilled } else { Icon::Star }, if fav || over_star { t.star } else { Color32::WHITE }); } if resp.hovered() && !applied && !over_star { - app.hover_preview = Some(HoverPreview { label: crate::i18n::tr_format!("Profile: {}", crate::i18n::tr(p.name)), settings: s.clone() }); - ui.data_mut(|m| m.insert_temp(egui::Id::new("last-profile-hover"), (id, p.id.to_string()))); + app.hover_preview = Some(HoverPreview { label: crate::i18n::tr_format!("Profile: {}", crate::i18n::tr(p_name)), settings: s.clone() }); + ui.data_mut(|m| m.insert_temp(egui::Id::new("last-profile-hover"), (id, p_id.to_string()))); } - let resp = resp.on_hover_text(format!("{} ({})", crate::i18n::tr(p.name), crate::i18n::tr(p.group))); + let resp = resp.on_hover_text(format!("{} ({})", crate::i18n::tr(p_name), crate::i18n::tr(p_group))); if resp.clicked() { if resp.interact_pointer_pos().is_some_and(|q| star.contains(q)) { - let _ = app.run("profile.favorite", json!({"id": p.id})); + let _ = app.run("profile.favorite", json!({"id": p_id})); } else if !applied { - let _ = app.run("develop.profile", json!({"id": p.id, "amount": 100})); + let _ = app.run("develop.profile", json!({"id": p_id, "amount": 100})); ui.data_mut(|m| m.remove::<(PhotoId, String)>(egui::Id::new("last-profile-hover"))); } } diff --git a/docs/parity.md b/docs/parity.md index d5a31ea9..97e23881 100644 --- a/docs/parity.md +++ b/docs/parity.md @@ -281,7 +281,7 @@ under the UI command that wraps it (a no-op error); the UI command now wins. | LR-PROF-LEGACY | Legacy profiles | P2 | ⬜ | | | | LR-PROF-NONRAW | Profiles for non-raw files | P0 | ✅ | `cmd:develop.profile` | same looks apply to JPEG/TIFF | | LR-PROF-AMOUNT | Profile amount | P1 | ✅ | `ctl:profile.amount` | | -| LR-PROF-IMPORT | Import profiles | P1 | 🟡 | `cmd:profile.import`, `cmd:profile.deleteImported`, `crates/pipeline/src/lut.rs`, `crates/engine/src/cmd/lut_profiles.rs` | `.cube` 3D LUTs (files, folders, zips) become creative profiles with Amount, grouped in the profile browser, kept with the library; rendered on the CPU; Adobe profile formats deliberately unsupported | +| LR-PROF-IMPORT | Import profiles | P1 | ✅ | `cmd:profile.import`, `cmd:profile.deleteImported`, `crates/pipeline/src/lut.rs`, `crates/engine/src/cmd/lut_profiles.rs`, `crates/ui-egui/src/panels/edit.rs`, `crates/ui-egui/src/panels/profiles.rs` | `.cube` 3D LUTs (files, folders, zips) become creative profiles with Amount, grouped in the profile browser, kept with the library; rendered on the CPU; Adobe profile formats deliberately unsupported | ## H. Crop & rotate (CROP) From a6cedcc9e8443cb92b33ed1e9ec859a73594cb0a Mon Sep 17 00:00:00 2001 From: DWAA1660 <80738438+DWAA1660@users.noreply.github.com> Date: Thu, 8 Oct 2026 11:04:19 -0600 Subject: [PATCH 29/41] Issue #168: the desktop app and CLI log warnings to stderr; RUST_LOG works lightcraft-cli installed no `log` backend, so every log::warn!/info! record (the GPU-to-CPU fallback, an unknown LIGHTCRAFT_GPU_BACKEND, the adapter chosen) was dropped and RUST_LOG, which docs/gpu-pipeline.md documents for the fallback warning, did nothing. The desktop app had its own logger that read only LIGHTCRAFT_LOG. Both now install lightcraft_engine::logging: warnings and errors on stderr by default; LIGHTCRAFT_LOG or RUST_LOG (off, error, warn, info, debug, trace) picks another level, LIGHTCRAFT_LOG winning, and RUST_LOG's per-target directives (`lightcraft=debug,wgpu=warn`) set LightCraft's level. Below warnings only LightCraft's own records are shown, as the app's logger did. The app's --help lists the variable. Tests: level parsing and target filtering (engine logging::tests); the CLI prints "LIGHTCRAFT_GPU_BACKEND=bogus names no known backend" with RUST_LOG=warn and nothing with RUST_LOG=off (cli.rs). Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01K3vVft4VXvvy8SpbbUceTW --- apps/lightcraft-cli/src/main.rs | 3 + apps/lightcraft-cli/tests/cli.rs | 26 +++++++ apps/lightcraft/Cargo.toml | 1 - apps/lightcraft/src/main.rs | 33 ++------- crates/engine/src/lib.rs | 1 + crates/engine/src/logging.rs | 118 +++++++++++++++++++++++++++++++ 6 files changed, 152 insertions(+), 30 deletions(-) create mode 100644 crates/engine/src/logging.rs diff --git a/apps/lightcraft-cli/src/main.rs b/apps/lightcraft-cli/src/main.rs index b9e2641c..88f011cd 100644 --- a/apps/lightcraft-cli/src/main.rs +++ b/apps/lightcraft-cli/src/main.rs @@ -122,6 +122,9 @@ fn main() -> ExitCode { dhat::Profiler::builder().file_name(file).build() }; alloc_release::install(); + // Warnings (a GPU render redone on the CPU, an unknown backend name) on stderr; LIGHTCRAFT_LOG + // or RUST_LOG picks another level (#168). + lightcraft_engine::logging::install("lightcraft-cli"); let args: Vec = std::env::args().skip(1).collect(); let r = match args.first().map(String::as_str) { Some("run") => run(&args[1..]), diff --git a/apps/lightcraft-cli/tests/cli.rs b/apps/lightcraft-cli/tests/cli.rs index 7cd0710b..e48b94bc 100644 --- a/apps/lightcraft-cli/tests/cli.rs +++ b/apps/lightcraft-cli/tests/cli.rs @@ -336,3 +336,29 @@ fn connect_failure_hint_names_the_attempted_port() { assert!(err.contains("--control 18437"), "the hint must name the port it tried: {err}"); assert!(!err.contains("--control 7980"), "{err}"); } + +/// The CLI logs warnings on stderr (#168): with `RUST_LOG=warn` an unknown GPU backend name is +/// reported, and `RUST_LOG=off` silences it. Before, no logger was installed and nothing appeared. +#[test] +fn warnings_are_logged_on_stderr() { + let input = tmp("log-in.png"); + gradient_png(&input); + let out = tmp("log-out.jpg"); + let run = |level: &str| { + let o = Command::new(BIN) + .args(["render", input.to_str().unwrap(), "-o", out.to_str().unwrap()]) + .env("LIGHTCRAFT_GPU_BACKEND", "bogus") + .env("RUST_LOG", level) + .env_remove("LIGHTCRAFT_LOG") + .env_remove("LIGHTCRAFT_GPU") + .output() + .unwrap(); + let stderr = String::from_utf8_lossy(&o.stderr).into_owned(); + assert!(o.status.success(), "{stderr}"); + stderr + }; + let warn = run("warn"); + assert!(warn.contains("LIGHTCRAFT_GPU_BACKEND=bogus names no known backend"), "{warn}"); + let off = run("off"); + assert!(!off.contains("names no known backend"), "{off}"); +} diff --git a/apps/lightcraft/Cargo.toml b/apps/lightcraft/Cargo.toml index a29299da..a4ca7ddd 100644 --- a/apps/lightcraft/Cargo.toml +++ b/apps/lightcraft/Cargo.toml @@ -18,7 +18,6 @@ lightcraft-raster = { workspace = true } eframe = { workspace = true, features = ["default_fonts", "wgpu", "accesskit", "wayland", "x11"] } egui = { workspace = true } serde_json = { workspace = true } -log = { workspace = true } [build-dependencies] # Icon + VERSIONINFO for lightcraft.exe; build.rs only uses it when targeting Windows. diff --git a/apps/lightcraft/src/main.rs b/apps/lightcraft/src/main.rs index 4bb1c6ff..c4a930b4 100644 --- a/apps/lightcraft/src/main.rs +++ b/apps/lightcraft/src/main.rs @@ -409,39 +409,14 @@ ENVIRONMENT: LIGHTCRAFT_SAM3_DIR=DIR the SAM 3 model for Object / Describe masks (default: /models/sam3; optional: LightCraft offers to download it when first needed) LIGHTCRAFT_SAM3_MIRRORS=URL,… where to download the SAM 3 model from (base URLs, tried in order) + LIGHTCRAFT_LOG=LEVEL (or RUST_LOG) what is logged on stderr: off, error, warn (default), info, debug, trace "; -/// Warnings and errors (failed commands, AI mask analysis) on stderr; `LIGHTCRAFT_LOG=info` -/// (or `debug`) shows more. -struct StderrLog(log::LevelFilter); - -impl log::Log for StderrLog { - fn enabled(&self, m: &log::Metadata) -> bool { - m.level() <= self.0 && (m.level() <= log::Level::Warn || m.target().starts_with("lightcraft")) - } - fn log(&self, r: &log::Record) { - if self.enabled(r.metadata()) { - eprintln!("lightcraft: {} {}: {}", r.level(), r.target(), r.args()); - } - } - fn flush(&self) {} -} - -fn install_log() { - let level = match std::env::var("LIGHTCRAFT_LOG").unwrap_or_default().as_str() { - "debug" => log::LevelFilter::Debug, - "info" => log::LevelFilter::Info, - _ => log::LevelFilter::Warn, - }; - static LOGGER: std::sync::OnceLock = std::sync::OnceLock::new(); - if log::set_logger(LOGGER.get_or_init(|| StderrLog(level))).is_ok() { - log::set_max_level(level); - } -} - fn main() -> eframe::Result { lightcraft_engine::guard::install_hook(std::env::temp_dir().join("lightcraft-panics.log")); - install_log(); + // Warnings and errors (failed commands, AI mask analysis, GPU fallbacks) on stderr; + // LIGHTCRAFT_LOG or RUST_LOG picks another level. + lightcraft_engine::logging::install("lightcraft"); alloc_release::install(); let mut control_port: Option = std::env::var("LIGHTCRAFT_CONTROL_PORT").ok().and_then(|p| p.parse().ok()); let mut files = Vec::new(); diff --git a/crates/engine/src/lib.rs b/crates/engine/src/lib.rs index ec39222a..10d8fa14 100644 --- a/crates/engine/src/lib.rs +++ b/crates/engine/src/lib.rs @@ -25,6 +25,7 @@ pub mod guard; pub mod import; mod import_move; pub mod library; +pub mod logging; pub mod media; pub mod memory; pub mod merge; diff --git a/crates/engine/src/logging.rs b/crates/engine/src/logging.rs new file mode 100644 index 00000000..380bf61c --- /dev/null +++ b/crates/engine/src/logging.rs @@ -0,0 +1,118 @@ +//! `log` records on stderr for the native desktop app and CLI (#168). The web build installs +//! eframe's `WebLogger` instead. +//! +//! Warnings and errors are shown by default. `LIGHTCRAFT_LOG` or `RUST_LOG` picks another level +//! (`off`, `error`, `warn`, `info`, `debug`, `trace`); `LIGHTCRAFT_LOG` wins when both are set. +//! `RUST_LOG` also accepts per-target directives (`lightcraft=debug,wgpu=warn`): a bare level +//! applies to everything, and the most verbose `lightcraft…=level` directive sets LightCraft's. +//! Below warnings only LightCraft's own records are shown, so `info` and `debug` aren't drowned +//! out by the GPU and windowing libraries. + +use log::{Level, LevelFilter, Metadata, Record}; + +/// Records shown at a level, written to stderr as `: LEVEL target: message`. +pub struct StderrLog { + prefix: &'static str, + level: LevelFilter, +} + +impl StderrLog { + pub fn new(prefix: &'static str, level: LevelFilter) -> Self { + Self { prefix, level } + } +} + +impl log::Log for StderrLog { + fn enabled(&self, m: &Metadata) -> bool { + m.level() <= self.level && (m.level() <= Level::Warn || m.target().starts_with("lightcraft")) + } + fn log(&self, r: &Record) { + if self.enabled(r.metadata()) { + eprintln!("{}: {} {}: {}", self.prefix, r.level(), r.target(), r.args()); + } + } + fn flush(&self) {} +} + +fn parse_level(s: &str) -> Option { + match s.trim().to_ascii_lowercase().as_str() { + "off" | "none" => Some(LevelFilter::Off), + "error" => Some(LevelFilter::Error), + "warn" | "warning" => Some(LevelFilter::Warn), + "info" => Some(LevelFilter::Info), + "debug" => Some(LevelFilter::Debug), + "trace" => Some(LevelFilter::Trace), + _ => None, + } +} + +/// The level that `LIGHTCRAFT_LOG` / `RUST_LOG` ask for; warnings when neither names one. +pub fn level_from(lightcraft_log: Option<&str>, rust_log: Option<&str>) -> LevelFilter { + if let Some(l) = lightcraft_log.and_then(parse_level) { + return l; + } + let Some(rust_log) = rust_log else { return LevelFilter::Warn }; + let mut bare = None; + let mut ours: Option = None; + for directive in rust_log.split(',') { + match directive.split_once('=') { + None => bare = parse_level(directive).or(bare), + Some((target, level)) if target.trim().starts_with("lightcraft") => { + if let Some(l) = parse_level(level) { + ours = Some(ours.map_or(l, |o| o.max(l))); + } + } + Some(_) => {} + } + } + ours.or(bare).unwrap_or(LevelFilter::Warn) +} + +/// Install the stderr logger at the level the environment asks for. Does nothing when a logger is +/// already installed. +pub fn install(prefix: &'static str) { + let level = level_from(std::env::var("LIGHTCRAFT_LOG").ok().as_deref(), std::env::var("RUST_LOG").ok().as_deref()); + static LOGGER: std::sync::OnceLock = std::sync::OnceLock::new(); + if log::set_logger(LOGGER.get_or_init(|| StderrLog::new(prefix, level))).is_ok() { + log::set_max_level(level); + } +} + +#[cfg(test)] +mod tests { + use super::*; + use log::Log; + + #[test] + fn warnings_by_default_and_either_variable_picks_the_level() { + assert_eq!(level_from(None, None), LevelFilter::Warn); + assert_eq!(level_from(None, Some("")), LevelFilter::Warn); + assert_eq!(level_from(None, Some("warn")), LevelFilter::Warn); + assert_eq!(level_from(None, Some("TRACE")), LevelFilter::Trace); + assert_eq!(level_from(None, Some("off")), LevelFilter::Off); + assert_eq!(level_from(Some("info"), None), LevelFilter::Info); + assert_eq!(level_from(Some("error"), Some("debug")), LevelFilter::Error, "LIGHTCRAFT_LOG wins"); + assert_eq!(level_from(Some("nonsense"), Some("debug")), LevelFilter::Debug, "an unknown LIGHTCRAFT_LOG is ignored"); + } + + #[test] + fn rust_log_directives_set_lightcraft_s_level() { + assert_eq!(level_from(None, Some("lightcraft=debug,wgpu=warn")), LevelFilter::Debug); + assert_eq!(level_from(None, Some("wgpu_core=trace")), LevelFilter::Warn, "other crates' directives don't apply"); + assert_eq!(level_from(None, Some("error,lightcraft_gpu=info")), LevelFilter::Info); + assert_eq!(level_from(None, Some("lightcraft_gpu=info,lightcraft_engine=trace")), LevelFilter::Trace); + assert_eq!(level_from(None, Some("info,naga=off")), LevelFilter::Info); + } + + #[test] + fn only_lightcraft_records_below_warnings() { + let log = StderrLog::new("test", LevelFilter::Debug); + let meta = |level, target| Metadata::builder().level(level).target(target).build(); + assert!(log.enabled(&meta(Level::Warn, "wgpu_core::device"))); + assert!(log.enabled(&meta(Level::Debug, "lightcraft_gpu::ctx"))); + assert!(!log.enabled(&meta(Level::Info, "wgpu_core::device"))); + assert!(!log.enabled(&meta(Level::Trace, "lightcraft_gpu::ctx"))); + let quiet = StderrLog::new("test", LevelFilter::Off); + assert!(!quiet.enabled(&meta(Level::Error, "lightcraft_engine"))); + } +} From 3a9fe13d3ad3b896ecf36fc4fabf4f1f9cfaeb4d Mon Sep 17 00:00:00 2001 From: MarekSpodBiedry <73122968+MarekSpodBiedry@users.noreply.github.com> Date: Thu, 8 Oct 2026 19:22:48 +0200 Subject: [PATCH 30/41] Import: Shift-click checks or unchecks a range of photos in the review (#338) --- crates/ui-egui/src/import.rs | 86 +++++++++++++++++++++++++++++++++--- 1 file changed, 79 insertions(+), 7 deletions(-) diff --git a/crates/ui-egui/src/import.rs b/crates/ui-egui/src/import.rs index 3ac7c9eb..7465ca36 100644 --- a/crates/ui-egui/src/import.rs +++ b/crates/ui-egui/src/import.rs @@ -1,11 +1,11 @@ //! The import review dialog (File → Import Photos… / Import from Folder… / Import from Device): //! the source it scanned (a scanned folder is *not* added to Local), the files found under it as -//! a grid of thumbnails with checkboxes (duplicates marked and unchecked), the destination (add in -//! place / copy or move into the library's `Originals/` or a chosen folder, filed by day, by month, -//! into one folder or by a custom folder template, optionally renamed), an album -//! (existing or new), a preset and keywords to apply. Importing runs on a worker thread (files are -//! probed, copied or moved there) and its batches join the catalog between frames, with a progress -//! window and Cancel; the whole import is one undo step. +//! a grid of thumbnails with checkboxes (duplicates marked and unchecked; Shift-click sets a +//! range), the destination (add in place / copy or move into the library's `Originals/` or a +//! chosen folder, filed by day, by month, into one folder or by a custom folder template, +//! optionally renamed), an album (existing or new), a preset and keywords to apply. Importing +//! runs on a worker thread (files are probed, copied or moved there) and its batches join the +//! catalog between frames, with a progress window and Cancel; the whole import is one undo step. use egui::{Align2, Color32, Rect, Sense, Stroke, StrokeKind, pos2, vec2}; use lightcraft_engine::import::{ImportCandidate, ScanInput, ScanOutput, ScanProgress, scan_with}; @@ -53,6 +53,9 @@ pub struct ImportDialog { pub metadata_preset: String, /// Copy: raws are copied as DNG. pub dng: bool, + /// The candidate clicked last: where a Shift-click range starts ([`ImportDialog::click`]). + #[serde(skip)] + pub last_clicked: Option, } impl ImportDialog { @@ -63,6 +66,24 @@ impl ImportDialog { pub fn importable(&self, i: usize) -> bool { self.candidates.get(i).is_some_and(|c| c.duplicate.is_none() && c.error.is_none()) } + /// A click on candidate `i` toggles its checkbox. With `shift`, every importable candidate + /// from the last clicked one to `i` takes `i`'s new state, as in a file manager: click the + /// first, Shift-click the last, and the whole range is checked (or unchecked). + pub fn click(&mut self, i: usize, shift: bool) { + if !self.importable(i) { + return; + } + let Some(on) = self.checked.get(i).map(|c| !c) else { return }; + let from = if shift { self.last_clicked.filter(|a| *a < self.checked.len()).unwrap_or(i) } else { i }; + for k in from.min(i)..=from.max(i) { + if self.importable(k) + && let Some(c) = self.checked.get_mut(k) + { + *c = on; + } + } + self.last_clicked = Some(i); + } pub fn selected_paths(&self) -> Vec { self.candidates .iter() @@ -1025,7 +1046,8 @@ fn candidate_cell(app: &mut LightcraftApp, ui: &mut egui::Ui, d: &mut ImportDial ); let resp = resp.on_hover_text(tip); if resp.clicked() && ok { - d.checked[i] = !d.checked[i]; + let shift = ui.input(|input| input.modifiers.shift); + d.click(i, shift); } } @@ -1179,3 +1201,53 @@ fn field(ui: &mut egui::Ui, label: &str, add: impl FnOnce(&mut egui::Ui) -> R }) .inner } + +#[cfg(test)] +mod tests { + use super::*; + + /// Six candidates, all checked; candidate 3 is a duplicate (unchecked, not importable). + fn dialog() -> ImportDialog { + let mut candidates: Vec = (0..6).map(|i| ImportCandidate { path: format!("img{i}.jpg"), ..Default::default() }).collect(); + candidates[3].duplicate = Some("content".into()); + ImportDialog::new(candidates) + } + + #[test] + fn click_toggles_one_candidate() { + let mut d = dialog(); + d.click(1, false); + assert_eq!(d.checked, [true, false, true, false, true, true]); + d.click(1, false); + assert_eq!(d.checked, [true, true, true, false, true, true]); + d.click(3, false); + assert_eq!(d.checked, [true, true, true, false, true, true], "a duplicate can't be checked"); + } + + #[test] + fn shift_click_sets_the_range_to_the_clicked_state() { + let mut d = dialog(); + d.click(1, false); + d.click(4, true); + assert_eq!(d.checked, [true, false, false, false, false, true], "1..=4 unchecked, the duplicate left alone"); + assert_eq!(d.selected_paths(), ["img0.jpg", "img5.jpg"]); + // backwards from the new anchor (4) re-checks 2..=4 + d.click(2, true); + assert_eq!(d.checked, [true, false, true, false, true, true]); + } + + #[test] + fn shift_click_without_an_earlier_click_toggles_one() { + let mut d = dialog(); + d.click(2, true); + assert_eq!(d.checked, [true, true, false, false, true, true]); + } + + #[test] + fn shift_click_with_a_stale_anchor_toggles_one() { + let mut d = dialog(); + d.last_clicked = Some(99); + d.click(2, true); + assert_eq!(d.checked, [true, true, false, false, true, true]); + } +} From 4d506494d9df29b552fa1811f62ce0a364b52f12 Mon Sep 17 00:00:00 2001 From: MarekSpodBiedry <73122968+MarekSpodBiedry@users.noreply.github.com> Date: Thu, 8 Oct 2026 19:30:37 +0200 Subject: [PATCH 31/41] Issue #337: the Import Photos dialog is bigger and resizable; the photo grid fills it The review dialog was a fixed-size window whose thumbnail grid was capped at 330 points (about 6 x 2.5 thumbnails). It now opens at 960 x 720, can be resized by its edges and corners, and the grid takes whatever height the options and buttons leave. The dialog has a window id of its own so its size does not carry over to the other dialogs. --- crates/ui-egui/src/headless.rs | 51 ++++++++++++++++++++++++++ crates/ui-egui/src/import.rs | 55 +++++++++++++++++++++++++++- crates/ui-egui/src/panels/dialogs.rs | 16 ++++++-- docs/parity.md | 2 +- 4 files changed, 119 insertions(+), 5 deletions(-) diff --git a/crates/ui-egui/src/headless.rs b/crates/ui-egui/src/headless.rs index 976ecd55..d27088dc 100644 --- a/crates/ui-egui/src/headless.rs +++ b/crates/ui-egui/src/headless.rs @@ -1358,6 +1358,57 @@ mod tests { let _ = std::fs::remove_dir_all(&dir); } + /// The import review opens bigger than the old fixed 6 × 2.5 grid and can be resized by its + /// corner: the photo grid takes the new height, and the window then keeps its size (issue #337). + #[test] + fn import_review_dialog_resizes() { + let mut h = demo([1400.0, 1000.0]); + let t = Duration::from_secs(10); + let candidates = (0..60) + .map(|i| lightcraft_engine::import::ImportCandidate { + path: format!("/lc-test/img{i}.png"), + name: format!("img{i}.png"), + error: Some("not read in this test".into()), + ..Default::default() + }) + .collect(); + h.app.ui.dialog = Some(crate::state::Dialog::Import { opts: crate::import::ImportDialog::new(candidates) }); + let rect = |h: &mut Headless| { + let r = h.request("ui.widgets", json!({}), t); + let w = r["result"].as_array().and_then(|a| a.iter().find(|w| w["id"] == "dialog:window")).expect("dialog on screen"); + [0usize, 1, 2, 3].map(|i| w["rect"][i].as_f64().unwrap()) + }; + for _ in 0..10 { + h.step(); + } + let r0 = rect(&mut h); + assert!(r0[3] > 600.0, "{r0:?}"); + // the Copy options add rows below the grid: the grid makes room, the window doesn't grow + for copy in [true, false] { + if let Some(crate::state::Dialog::Import { opts }) = &mut h.app.ui.dialog { + opts.copy = copy; + } + for _ in 0..10 { + h.step(); + } + let r = rect(&mut h); + assert!((r[3] - r0[3]).abs() < 0.5, "copy {copy}: {r0:?} → {r:?}"); + } + let (x, y) = (r0[0] + r0[2] - 3.0, r0[1] + r0[3] - 3.0); + h.request("ui.drag", json!({"x": x, "y": y, "toX": x + 160.0, "toY": y + 120.0, "steps": 12}), t); + for _ in 0..10 { + h.step(); + } + let r1 = rect(&mut h); + assert!(r1[2] > r0[2] + 100.0 && r1[3] > r0[3] + 80.0, "dragging the corner resized it: {r0:?} → {r1:?}"); + for _ in 0..60 { + h.step(); + } + let r2 = rect(&mut h); + assert!((r2[2] - r1[2]).abs() < 0.5 && (r2[3] - r1[3]).abs() < 0.5, "the dialog kept its size: {r1:?} → {r2:?}"); + assert!(matches!(h.app.ui.dialog, Some(crate::state::Dialog::Import { .. })), "still open"); + } + /// Adding a file again that is in Recently Deleted shows it there (side panel opened, photo /// selected) instead of only saying "duplicate skipped"; its menus offer Restore, and once /// restored a re-add selects it in All Photos. diff --git a/crates/ui-egui/src/import.rs b/crates/ui-egui/src/import.rs index 3ac7c9eb..0c6a7b55 100644 --- a/crates/ui-egui/src/import.rs +++ b/crates/ui-egui/src/import.rs @@ -21,6 +21,31 @@ use crate::widgets::register; /// Files per batch (each batch joins the catalog as it is ready, so the progress window updates). pub(crate) const BATCH: usize = 8; +/// The review dialog's first size (points). It can be resized; the photo grid takes the height. +pub(crate) const DIALOG_SIZE: [f32; 2] = [960.0, 720.0]; +/// Memory keys: where the photo grid ended and where the dialog's content (its buttons) ended, last +/// frame. Their difference is the room the grid leaves below it. +const GRID_BOTTOM: &str = "import-grid-bottom"; +const CONTENT_BOTTOM: &str = "import-content-bottom"; +/// Room below the grid before it has been measured (more than the options and buttons take). +const DEFAULT_BELOW_GRID: f32 = 360.0; + +/// Height of the photo grid: what is `available` once `below` (options and buttons) is left free, +/// in whole points so the window never grows by a fraction each frame, and at least one `row`. +pub(crate) fn grid_height(available: f32, below: f32, row: f32) -> f32 { + let h = (available - below.max(0.0)).floor(); + if h.is_finite() { h.max(row) } else { (DIALOG_SIZE[1] - DEFAULT_BELOW_GRID).max(row) } +} + +/// Remember where the review dialog's content ends this frame (after its buttons), for +/// [`grid_height`] on the next. +pub(crate) fn note_dialog_bottom(ui: &egui::Ui) { + if !ui.is_sizing_pass() { + let y = ui.min_rect().bottom(); + ui.ctx().data_mut(|m| m.insert_temp(egui::Id::new(CONTENT_BOTTOM), y)); + } +} + #[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)] #[serde(default, rename_all = "camelCase")] pub struct ImportDialog { @@ -731,7 +756,14 @@ pub fn body(app: &mut LightcraftApp, ui: &mut egui::Ui, d: &mut ImportDialog) { let avail = ui.available_width(); let cols = ((avail + 6.0) / (cell + 6.0)).floor().max(1.0) as usize; let rows = n.div_ceil(cols); - egui::ScrollArea::vertical().id_salt("import-grid").max_height(330.0).auto_shrink([false, true]).show_viewport(ui, |ui, viewport| { + // as tall as the window leaves after the options and buttons below it (measured last frame), + // so resizing the dialog resizes the grid + let below = ui.ctx().data(|m| { + let y = |key: &str| m.get_temp::(egui::Id::new(key)); + y(CONTENT_BOTTOM).zip(y(GRID_BOTTOM)).map(|(content, grid)| content - grid) + }); + let grid_h = grid_height(ui.available_height(), below.unwrap_or(DEFAULT_BELOW_GRID), cell + 26.0); + let grid = egui::ScrollArea::vertical().id_salt("import-grid").max_height(grid_h).auto_shrink([false, false]).show_viewport(ui, |ui, viewport| { let (area, _) = ui.allocate_exact_size(vec2(avail, rows as f32 * (cell + 26.0)), Sense::hover()); for i in 0..n { let (c, r) = (i % cols, i / cols); @@ -743,6 +775,9 @@ pub fn body(app: &mut LightcraftApp, ui: &mut egui::Ui, d: &mut ImportDialog) { candidate_cell(app, ui, d, i, rect); } }); + if !ui.is_sizing_pass() { + ui.ctx().data_mut(|m| m.insert_temp(egui::Id::new(GRID_BOTTOM), grid.inner_rect.bottom())); + } ui.add_space(4.0); // options if !d.sources.is_empty() { @@ -1179,3 +1214,21 @@ fn field(ui: &mut egui::Ui, label: &str, add: impl FnOnce(&mut egui::Ui) -> R }) .inner } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn grid_height_follows_the_window() { + // the grid takes what the options and buttons leave, in whole points + assert_eq!(grid_height(700.0, 300.0, 142.0), 400.0); + assert_eq!(grid_height(1000.5, 300.0, 142.0), 700.0); + // never less than one row of thumbnails + assert_eq!(grid_height(200.0, 300.0, 142.0), 142.0); + // a bad measurement doesn't take the grid away or make it endless + assert_eq!(grid_height(700.0, -50.0, 142.0), 700.0); + assert_eq!(grid_height(700.0, f32::NAN, 142.0), 700.0); + assert_eq!(grid_height(f32::INFINITY, 300.0, 142.0), DIALOG_SIZE[1] - DEFAULT_BELOW_GRID); + } +} diff --git a/crates/ui-egui/src/panels/dialogs.rs b/crates/ui-egui/src/panels/dialogs.rs index 91ffb1c9..58d7f7a8 100644 --- a/crates/ui-egui/src/panels/dialogs.rs +++ b/crates/ui-egui/src/panels/dialogs.rs @@ -103,13 +103,20 @@ pub fn show(app: &mut LightcraftApp, ctx: &egui::Context) { } .to_string(); let frame = egui::Frame::window(&ctx.global_style()).inner_margin(egui::Margin::symmetric(16, 12)); - let shown = egui::Window::new(crate::i18n::tr(&title)).id(egui::Id::new("lightcraft-dialog")) + // The import review can be resized (its photo grid takes the room); it keeps a window id of its + // own so the size it is given doesn't carry over to the other dialogs. Its content scrolls + // rather than growing the window when the options below the grid get taller (e.g. Copy). + let import = matches!(dlg, Dialog::Import { .. }); + let window_id = egui::Id::new(if import { "lightcraft-import-dialog" } else { "lightcraft-dialog" }); + let shown = egui::Window::new(crate::i18n::tr(&title)).id(window_id) .collapsible(false) - .resizable(false) + .resizable(import) + .vscroll(import) + .resize(|r| if import { r.default_height(crate::import::DIALOG_SIZE[1]) } else { r }) .frame(frame) .anchor(egui::Align2::CENTER_CENTER, [0.0, 0.0]) .default_width(match dlg { - Dialog::Import { .. } => 760.0, + Dialog::Import { .. } => crate::import::DIALOG_SIZE[0], Dialog::SmartRules { .. } => 680.0, Dialog::AllMetadata { .. } => 620.0, _ => 380.0, @@ -836,6 +843,9 @@ pub fn show(app: &mut LightcraftApp, ctx: &egui::Context) { } } }); + if import { + crate::import::note_dialog_bottom(ui); + } }); if let Some(w) = shown { ctx.move_to_top(w.response.layer_id); diff --git a/docs/parity.md b/docs/parity.md index d5a31ea9..d1b3612a 100644 --- a/docs/parity.md +++ b/docs/parity.md @@ -113,7 +113,7 @@ under the UI command that wraps it (a no-op error); the UI command now wins. | Id | Feature | Tier | Status | Evidence | Missing / notes | |---|---|---|---|---|---| -| LR-IMP-ADD-DIALOG | Add photos/folders | P0 | ✅ | `cmd:file.addPhotos`, `cmd:library.importPreview`, `cmd:library.import` (`mode`, `album`, `albumName`, `preset`, `keywords`), `crates/ui-egui/src/import.rs` | review dialog (Import Photos): the scanned source (a scanned folder is not added to Local), candidate grid with thumbnails and checkboxes (duplicates by path/content marked and unchecked), transfer mode (add in place / copy / move, each explained), album (existing/new), preset, keywords; the folder scan runs on a background thread with a progress window and Cancel (a NAS folder no longer freezes the window); the import itself (probing, sidecars, copying / moving files) runs on a worker thread and its batches join the library between frames, with a progress window and Cancel; one undo step. Drag-and-drop goes through the same background import | +| LR-IMP-ADD-DIALOG | Add photos/folders | P0 | ✅ | `cmd:file.addPhotos`, `cmd:library.importPreview`, `cmd:library.import` (`mode`, `album`, `albumName`, `preset`, `keywords`), `crates/ui-egui/src/import.rs` | review dialog (Import Photos): the scanned source (a scanned folder is not added to Local), candidate grid with thumbnails and checkboxes (duplicates by path/content marked and unchecked) filling a resizable window (issue #337), transfer mode (add in place / copy / move, each explained), album (existing/new), preset, keywords; the folder scan runs on a background thread with a progress window and Cancel (a NAS folder no longer freezes the window); the import itself (probing, sidecars, copying / moving files) runs on a worker thread and its batches join the library between frames, with a progress window and Cancel; one undo step. Drag-and-drop goes through the same background import | | LR-IMP-DRAGDROP | Drop files/folders to import | P0 | ✅ | `crates/ui-egui/src/lib.rs` (dropped files → `cmd:library.import`) | dropping onto a specific album not supported | | LR-IMP-DUPES | Skip duplicates by content | P1 | ✅ | `crates/engine/src/import.rs`, `crates/engine/src/tests_import.rs` | | | LR-IMP-DEVICE | Import from camera/card | P1 | ✅ | `cmd:library.devices`, `cmd:file.addFromDevice`, `crates/engine/src/devices.rs` | mounted volumes with a DCIM folder (macOS /Volumes, Linux /media and /run/media, Windows drive letters); File → Import from Device → the import review, copying into the library by default; menus serve the last scan and rescan in the background (never blocking a frame; hot-plugs repaint); copies are verified (a new file, synced, checked against the content hash the scan computed — byte compare with the source when there is none, issue #134; a bad copy is removed and reported as failed, never counted as imported; taken names get -1, -2…, never replaced — issue #96, `crates/engine/src/import_move.rs`); no PTP/MTP (cameras that don't mount as a disk) | From b7f60a1fac9581374fa18982817644ef19c34cdf Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 8 Oct 2026 17:37:06 +0000 Subject: [PATCH 32/41] i18n: translate the strings #312, #313 and #325 added (ja, pt-br, zh-hans, zh-hant) Crop Apply / Lock / Locked, Set Image Zoom and Pan, the import review's Recently Deleted choices and Empty Recently Deleted. The import's undo labels and its "restored from Recently Deleted" summary used format!, so they could not be translated; they go through tr_format! now. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01912WaK6Rmew3wxZuPyX7E8 --- crates/ui-egui/locales/ja-formats.json | 3 +++ crates/ui-egui/locales/ja.json | 10 ++++++++++ crates/ui-egui/locales/pt-br-formats.json | 3 +++ crates/ui-egui/locales/pt-br.json | 10 ++++++++++ crates/ui-egui/locales/zh-hans-formats.json | 3 +++ crates/ui-egui/locales/zh-hans.json | 10 ++++++++++ crates/ui-egui/locales/zh-hant-formats.json | 3 +++ crates/ui-egui/locales/zh-hant.json | 10 ++++++++++ crates/ui-egui/src/import.rs | 6 +++--- 9 files changed, 55 insertions(+), 3 deletions(-) diff --git a/crates/ui-egui/locales/ja-formats.json b/crates/ui-egui/locales/ja-formats.json index 5a7d7a11..f5d7f284 100644 --- a/crates/ui-egui/locales/ja-formats.json +++ b/crates/ui-egui/locales/ja-formats.json @@ -4,9 +4,11 @@ " · {} failed: {}": "・{}件失敗:{}", " · {} moved": "・{}件移動", " · {} not readable": "・{}件は読み込み不可", + " · {} restored from Recently Deleted": "・最近削除した写真から{}枚を復元", ", {failed} file{} not readable": "、{failed}件を読み込めません{:.0}", "Add {local_n} to My Photos": "{local_n}枚をマイフォトに追加", "Add {} Photo{}": "{}枚の写真を追加{:.0}", + "Add {} Photo{}, restore {}": "{}枚の写真を追加{:.0}、{}枚を復元", "Add “{}” to Favorites": "「{}」をお気に入りに追加", "Added {n} photo{} to My Photos": "マイフォトに{n}枚を追加しました{:.0}", "Added {n} photo{} to “{}”": "「{1}」に{n}枚を追加しました{0:.0}", @@ -53,6 +55,7 @@ "Rename “{name}”": "「{name}」の名前を変更", "Rendering on the CPU: {why}": "CPUで描画:{why}", "Reset {} Channel": "{}チャンネルをリセット", + "Restore {} Photo{}": "{}枚の写真を復元{:.0}", "Saved export preset “{}”": "書き出しプリセット「{}」を保存しました", "Show {hidden} hidden location{}": "非表示の{hidden}か所を表示{:.0}", "Showing {n} of {}": "{}枚中{n}枚を表示", diff --git a/crates/ui-egui/locales/ja.json b/crates/ui-egui/locales/ja.json index b6694c5c..ab802438 100644 --- a/crates/ui-egui/locales/ja.json +++ b/crates/ui-egui/locales/ja.json @@ -40,6 +40,7 @@ "Amount": "適用量", "Aperture": "絞り", "Application": "アプリケーション", + "Apply": "適用", "Apply Preset": "プリセットを適用", "Approved": "承認済み", "Aqua": "アクア", @@ -257,6 +258,7 @@ "Edit in External Editor": "外部エディターで編集", "Edited": "編集済み", "Effects": "効果", + "Empty Recently Deleted": "最近削除した写真を空にする", "Enable Lens Corrections": "レンズ補正を有効にする", "Enter Full Screen": "全画面表示", "Erase": "消去", @@ -287,6 +289,9 @@ "File Name": "ファイル名", "File Path": "ファイルの場所", "File Size": "ファイルサイズ", + "Import as new": "新規として読み込む", + "Lock": "ロック", + "Locked": "ロック中", "Random": "ランダム", "Reshuffle": "シャッフルし直す", "File name": "ファイル名", @@ -717,6 +722,8 @@ "Resize to": "サイズ変更", "Resolution (ppi)": "解像度(ppi)", "Restore": "復元", + "Restore keeps their edits; Import as new deletes the old record first.": "復元すると編集内容が残ります。新規として読み込むと、先に古い記録を削除します。", + "Restore them": "復元する", "Retouch": "レタッチ", "Review Import": "読み込み内容を確認", "Right-click a photo ▸ Set as Reference Photo, then View → Reference View (Shift+R)": "写真を右クリックして参照写真に設定し、「表示 → 参照表示」(⇧R)を選択してください", @@ -760,6 +767,7 @@ "Set Color Label": "カラーラベルを設定", "Set Develop Values": "現像設定を変更", "Set Flag": "フラグを設定", + "Set Image Zoom and Pan": "画像のズームとパンを設定", "Set Point Curve": "ポイントカーブを変更", "Set Profile": "プロファイルを設定", "Set Rating": "評価を設定", @@ -934,6 +942,8 @@ "Width (% of photo)": "幅(写真に対する%)", "Width (px)": "幅(px)", "Width × Height": "幅 × 高さ", + "Will be replaced": "置き換えられます", + "Will be restored": "復元されます", "Window": "ウィンドウ", "XMP sidecars": "XMPサイドカーファイル", "Year": "年", diff --git a/crates/ui-egui/locales/pt-br-formats.json b/crates/ui-egui/locales/pt-br-formats.json index 59605687..6c95cb3d 100644 --- a/crates/ui-egui/locales/pt-br-formats.json +++ b/crates/ui-egui/locales/pt-br-formats.json @@ -5,9 +5,11 @@ " · {} failed: {}": " · {} falhou: {}", " · {} moved": " · {} movida(s)", " · {} not readable": " · {} não legível", + " · {} restored from Recently Deleted": " · {} restaurada(s) de Excluídos recentemente", ", {failed} file{} not readable": ", {failed} arquivo{:.0} não legível", "Add {local_n} to My Photos": "Adicionar {local_n} a Minhas fotos", "Add {} Photo{}": "Adicionar {} foto{:.0}", + "Add {} Photo{}, restore {}": "Adicionar {} foto{:.0}, restaurar {}", "Add “{}” to Favorites": "Adicionar “{}” aos favoritos", "Added {n} photo{} to My Photos": "Adicionada {n} foto{:.0} a Minhas fotos", "Added {n} photo{} to “{}”": "Adicionada {n} foto{:.0} a “{}”", @@ -68,6 +70,7 @@ "Rendering on the CPU: {why}": "Renderizando na CPU: {why}", "Reset {name}": "Redefinir {name}", "Reset {} Channel": "Redefinir canal {}", + "Restore {} Photo{}": "Restaurar {} foto{:.0}", "Saved export preset “{}”": "Predefinição de exportação “{}” salva", "Show {hidden} hidden location{}": "Mostrar {hidden} local{:.0} oculto", "Showing {n} of {}": "Mostrando {n} de {}", diff --git a/crates/ui-egui/locales/pt-br.json b/crates/ui-egui/locales/pt-br.json index 133de7d6..1f3a42fe 100644 --- a/crates/ui-egui/locales/pt-br.json +++ b/crates/ui-egui/locales/pt-br.json @@ -49,6 +49,7 @@ "Amount": "Quantidade", "Aperture": "Abertura", "Application": "Aplicativo", + "Apply": "Aplicar", "Apply Color Label Set": "Aplicar conjunto de rótulos de cor", "Apply Filter Preset": "Aplicar predefinição de filtro", "Apply Metadata Preset": "Aplicar predefinição de metadados", @@ -355,6 +356,7 @@ "Edited": "Editada", "Effect": "Efeito", "Effects": "Efeitos", + "Empty Recently Deleted": "Esvaziar Excluídos recentemente", "Enable Lens Corrections": "Ativar correções de lente", "End Interaction": "Encerrar interação", "English": "Inglês", @@ -526,6 +528,7 @@ "Import Presets…": "Importar predefinições…", "Import Profiles": "Importar perfis", "Import Profiles & Presets…": "Importar perfis e predefinições…", + "Import as new": "Importar como novas", "Import from Device": "Importar de dispositivo", "Import from Folder…": "Importar de pasta…", "Import photos with File → Import Photos… (Cmd+Shift+I), or drop them here": "Importe fotos com Arquivo → Importar fotos… (Cmd+Shift+I) ou solte-as aqui", @@ -613,6 +616,8 @@ "Locate Missing File…": "Localizar arquivo ausente…", "Locate Photo": "Localizar foto", "Location": "Local", + "Lock": "Travar", + "Locked": "Travada", "Long Edge": "Aresta longa", "Looking for photos…": "Procurando fotos…", "Lossless": "Sem perdas", @@ -952,6 +957,8 @@ "Restore": "Restaurar", "Restore Library from Backup…": "Restaurar biblioteca do backup…", "Restore Version": "Restaurar versão", + "Restore keeps their edits; Import as new deletes the old record first.": "Restaurar mantém as edições; Importar como novas apaga antes o registro antigo.", + "Restore them": "Restaurá-las", "Retouch": "Retoque", "Revert": "Reverter", "Review Import": "Revisar importação", @@ -1018,6 +1025,7 @@ "Set Crop": "Definir recorte", "Set Develop Values": "Definir valores de revelação", "Set Flag": "Definir marcação", + "Set Image Zoom and Pan": "Definir zoom e posição da imagem", "Set Mask Adjustments": "Definir ajustes da máscara", "Set Point Curve": "Definir curva de pontos", "Set Profile": "Definir perfil", @@ -1245,6 +1253,8 @@ "Width (% of photo)": "Largura (% da foto)", "Width (px)": "Largura (px)", "Width × Height": "Largura × altura", + "Will be replaced": "Será substituída", + "Will be restored": "Será restaurada", "Window": "Janela", "Winter Blue": "Azul invernal", "XMP Sidecar Preferences": "Preferências de sidecar XMP", diff --git a/crates/ui-egui/locales/zh-hans-formats.json b/crates/ui-egui/locales/zh-hans-formats.json index e4ae48f4..b87d35fd 100644 --- a/crates/ui-egui/locales/zh-hans-formats.json +++ b/crates/ui-egui/locales/zh-hans-formats.json @@ -4,9 +4,11 @@ " · {} failed: {}": " · {0} 个失败:{1}", " · {} moved": " · 已移动 {0} 个", " · {} not readable": " · {0} 个无法读取", + " · {} restored from Recently Deleted": " · 已从“最近删除”恢复 {} 张", ", {failed} file{} not readable": ",{failed} 个文件无法读取{0:.0}", "Add {local_n} to My Photos": "将 {local_n} 张照片添加到我的照片", "Add {} Photo{}": "添加 {0} 张照片{1:.0}", + "Add {} Photo{}, restore {}": "添加 {0} 张照片{1:.0},恢复 {2} 张", "Add “{}” to Favorites": "将“{0}”添加到收藏夹", "Added {n} photo{} to My Photos": "已将 {n} 张照片添加到我的照片{0:.0}", "Added {n} photo{} to “{}”": "已将 {n} 张照片添加到“{1}”{0:.0}", @@ -53,6 +55,7 @@ "Rename “{name}”": "重命名“{name}”", "Rendering on the CPU: {why}": "正在使用 CPU 渲染:{why}", "Reset {} Channel": "重置 {0} 通道", + "Restore {} Photo{}": "恢复 {0} 张照片{1:.0}", "Saved export preset “{}”": "已保存导出预设“{0}”", "Show {hidden} hidden location{}": "显示 {hidden} 个隐藏位置{0:.0}", "Showing {n} of {}": "显示 {0} 张中的 {n} 张", diff --git a/crates/ui-egui/locales/zh-hans.json b/crates/ui-egui/locales/zh-hans.json index 4a669391..e9282fdb 100644 --- a/crates/ui-egui/locales/zh-hans.json +++ b/crates/ui-egui/locales/zh-hans.json @@ -46,6 +46,7 @@ "Amount": "数量", "Aperture": "光圈", "Application": "应用程序", + "Apply": "应用", "Apply Color Label Set": "应用颜色标签集", "Apply Filter Preset": "应用筛选器预设", "Apply Metadata Preset": "应用元数据预设", @@ -348,6 +349,7 @@ "Edited": "已编辑", "Effect": "效果", "Effects": "效果", + "Empty Recently Deleted": "清空“最近删除”", "Enable Lens Corrections": "启用镜头校正", "End Interaction": "结束交互", "English": "English", @@ -389,6 +391,9 @@ "File Name": "文件名", "File Path": "文件路径", "File Size": "文件大小", + "Import as new": "作为新照片导入", + "Lock": "锁定", + "Locked": "已锁定", "Random": "随机", "Reshuffle": "重新随机排序", "File name": "文件名", @@ -936,6 +941,8 @@ "Restore": "恢复", "Restore Library from Backup…": "从备份恢复图库…", "Restore Version": "恢复版本", + "Restore keeps their edits; Import as new deletes the old record first.": "恢复会保留编辑;作为新照片导入会先删除旧记录。", + "Restore them": "恢复", "Retouch": "润饰", "Revert": "恢复", "Review Import": "查看导入内容", @@ -1001,6 +1008,7 @@ "Set Crop": "设置裁切", "Set Develop Values": "设置编辑参数", "Set Flag": "设置旗标", + "Set Image Zoom and Pan": "设置图像缩放和平移", "Set Mask Adjustments": "设置蒙版调整", "Set Point Curve": "设置点曲线", "Set Profile": "设置配置文件", @@ -1222,6 +1230,8 @@ "Width (% of photo)": "宽度(照片百分比)", "Width (px)": "宽度 (px)", "Width × Height": "宽度 × 高度", + "Will be replaced": "将替换", + "Will be restored": "将恢复", "Window": "窗口", "Winter Blue": "冬日蓝调", "XMP Sidecar Preferences": "XMP 附属文件偏好设置", diff --git a/crates/ui-egui/locales/zh-hant-formats.json b/crates/ui-egui/locales/zh-hant-formats.json index 1c1266c0..fa975732 100644 --- a/crates/ui-egui/locales/zh-hant-formats.json +++ b/crates/ui-egui/locales/zh-hant-formats.json @@ -4,9 +4,11 @@ " · {} failed: {}": " · {} 個失敗:{}", " · {} moved": " · 已移動 {} 個", " · {} not readable": " · {} 個無法讀取", + " · {} restored from Recently Deleted": " · 已從「最近刪除」還原 {} 張", ", {failed} file{} not readable": ",{failed} 個檔案無法讀取{:.0}", "Add {local_n} to My Photos": "將 {local_n} 張加入我的照片", "Add {} Photo{}": "新增 {} 張照片{:.0}", + "Add {} Photo{}, restore {}": "新增 {} 張照片{:.0},還原 {} 張", "Add “{}” to Favorites": "將「{}」加入喜好項目", "Added {n} photo{} to My Photos": "已將 {n} 張照片加入我的照片{:.0}", "Added {n} photo{} to “{}”": "已將 {n} 張照片加入「{1}」{0:.0}", @@ -64,6 +66,7 @@ "Rendering on the CPU: {why}": "使用 CPU 算繪:{why}", "Reset {name}": "重設{name}", "Reset {} Channel": "重設 {} 通道", + "Restore {} Photo{}": "還原 {} 張照片{:.0}", "Saved export preset “{}”": "已儲存匯出預設集「{}」", "Show {hidden} hidden location{}": "顯示 {hidden} 個隱藏的位置{:.0}", "Showing {n} of {}": "顯示 {n}/{} 張", diff --git a/crates/ui-egui/locales/zh-hant.json b/crates/ui-egui/locales/zh-hant.json index 128c1411..f04bdbd9 100644 --- a/crates/ui-egui/locales/zh-hant.json +++ b/crates/ui-egui/locales/zh-hant.json @@ -46,6 +46,7 @@ "Amount": "強度", "Aperture": "光圈", "Application": "應用程式", + "Apply": "套用", "Apply Color Label Set": "套用色彩標籤集", "Apply Filter Preset": "套用篩選預設集", "Apply Metadata Preset": "套用中繼資料預設集", @@ -348,6 +349,7 @@ "Edited": "已編輯", "Effect": "效果", "Effects": "效果", + "Empty Recently Deleted": "清空「最近刪除」", "Enable Lens Corrections": "啟用鏡頭校正", "End Interaction": "結束互動", "English": "English", @@ -389,6 +391,9 @@ "File Name": "檔案名稱", "File Path": "檔案路徑", "File Size": "檔案大小", + "Import as new": "以新照片匯入", + "Lock": "鎖定", + "Locked": "已鎖定", "Random": "隨機", "Reshuffle": "重新隨機排序", "File name": "檔案名稱", @@ -936,6 +941,8 @@ "Restore": "還原", "Restore Library from Backup…": "從備份還原照片庫…", "Restore Version": "還原版本", + "Restore keeps their edits; Import as new deletes the old record first.": "還原會保留編輯;以新照片匯入會先刪除舊紀錄。", + "Restore them": "還原", "Retouch": "修飾", "Revert": "回復至上次儲存", "Review Import": "檢視匯入", @@ -1001,6 +1008,7 @@ "Set Crop": "設定裁切", "Set Develop Values": "設定顯像參數", "Set Flag": "設定旗標", + "Set Image Zoom and Pan": "設定影像縮放與平移", "Set Mask Adjustments": "設定遮色片調整", "Set Point Curve": "設定點曲線", "Set Profile": "設定描述檔", @@ -1222,6 +1230,8 @@ "Width (% of photo)": "寬度(照片百分比)", "Width (px)": "寬度(像素)", "Width × Height": "寬度 × 高度", + "Will be replaced": "將取代", + "Will be restored": "將還原", "Window": "視窗", "Winter Blue": "冬日藍調", "XMP Sidecar Preferences": "XMP 附屬檔案偏好設定", diff --git a/crates/ui-egui/src/import.rs b/crates/ui-egui/src/import.rs index e88e518a..922d08fe 100644 --- a/crates/ui-egui/src/import.rs +++ b/crates/ui-egui/src/import.rs @@ -625,9 +625,9 @@ fn finish(app: &mut LightcraftApp, ctx: &egui::Context, task: ImportTask) { let steps = app.session.undo.len().saturating_sub(task.undo0); let plural = |n: usize| if n == 1 { "" } else { "s" }; let label = if task.imported == 0 && task.restored > 0 { - format!("Restore {} Photo{}", task.restored, plural(task.restored)) + crate::i18n::tr_format!("Restore {} Photo{}", task.restored, plural(task.restored)) } else if task.restored > 0 { - format!("Add {} Photo{}, restore {}", task.imported, plural(task.imported), task.restored) + crate::i18n::tr_format!("Add {} Photo{}, restore {}", task.imported, plural(task.imported), task.restored) } else { crate::i18n::tr_format!("Add {} Photo{}", task.imported, if task.imported == 1 { "" } else { "s" }) }; @@ -659,7 +659,7 @@ fn finish(app: &mut LightcraftApp, ctx: &egui::Context, task: ImportTask) { crate::i18n::tr_format!("Imported {} photo{}", task.imported, plural(task.imported)) }; if task.restored > 0 { - msg.push_str(&format!(" · {} restored from Recently Deleted", task.restored)); + msg.push_str(&crate::i18n::tr_format!(" · {} restored from Recently Deleted", task.restored)); } if task.params["mode"] == "move" { msg.push_str(&crate::i18n::tr_format!(" · {} moved", task.moved)); From bac4666e7fbdfb436893781e8cd79d83d3f3100b Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 8 Oct 2026 17:37:27 +0000 Subject: [PATCH 33/41] Docs: What's new for the crop lock, trackpad zoom/pan, Folders and Select All (#312, #313, #307, #325) Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01912WaK6Rmew3wxZuPyX7E8 --- docs/whats-new.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/whats-new.md b/docs/whats-new.md index acbffd49..2de2748a 100644 --- a/docs/whats-new.md +++ b/docs/whats-new.md @@ -17,6 +17,16 @@ - The Tint slider works the right way round (issues #188, #321): left adds green, right adds magenta, as its track shows and as in Lightroom, and Tint values in Lightroom XMP sidecars now render as they do there. A custom Tint saved in an earlier version now shifts the other way; set it again (or re-run Auto / the white-balance picker). +- Crop (issue #295): a Lock toggle keeps the aspect ratio on every handle, Custom takes your own ratio (Apply), and + dragging a handle into the image edge stops there instead of pushing the crop out of shape. + +### Library and views +- Trackpads: pinch to zoom around the pointer and scroll with two fingers to pan the photo; panning keeps the photo + inside the view. A plain mouse wheel over a zoomed photo pans it too. +- A Folders section in the sidebar lists the folders your photos were imported from; choose one to see its photos. +- Select All and multi-selection show every selected photo in the grid and filmstrip, not only the active one + (issues #187, #298). Importing files that are in Recently Deleted asks whether to leave them there, restore them + (with their edits) or import them as new; the trash view's Photo menu has Empty Recently Deleted. ### Reliability - On macOS, single-key shortcuts that appear in the menu bar now work: E, C, H, M, ⇧P, I, K, D, ratings 0–5, From a12fabd588abe4bc3357c62855a6005d95db4c5f Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 8 Oct 2026 17:39:23 +0000 Subject: [PATCH 34/41] Library: the Photo Grid label footer is translucent; pt-br for "Color label cleared" (#308) #308 painted an opaque label-coloured bar over the bottom 24 px of every labelled photo in Photo Grid, hiding part of the photo, while its docs said the photo pixels stay as they are. The footer is now translucent over the photo and the docs say so. Also adds the one string pt-br lacked. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01912WaK6Rmew3wxZuPyX7E8 --- crates/ui-egui/locales/pt-br.json | 1 + crates/ui-egui/src/panels/grid.rs | 5 +++-- docs/library-shortcuts.md | 2 +- docs/whats-new.md | 2 +- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/crates/ui-egui/locales/pt-br.json b/crates/ui-egui/locales/pt-br.json index 1f3a42fe..a6ee0e79 100644 --- a/crates/ui-egui/locales/pt-br.json +++ b/crates/ui-egui/locales/pt-br.json @@ -200,6 +200,7 @@ "Color Noise Reduction": "Redução de ruído de cor", "Color Priority": "Prioridade de cor", "Color Range": "Intervalo de cor", + "Color label cleared": "Rótulo de cor removido", "Color space": "Espaço de cor", "Color space: sRGB (AVIF)": "Espaço de cor: sRGB (AVIF)", "Combine files…": "Combinar arquivos…", diff --git a/crates/ui-egui/src/panels/grid.rs b/crates/ui-egui/src/panels/grid.rs index 1ef6beb0..0cbd4119 100644 --- a/crates/ui-egui/src/panels/grid.rs +++ b/crates/ui-egui/src/panels/grid.rs @@ -564,8 +564,9 @@ fn cell(app: &mut LightcraftApp, ui: &mut egui::Ui, id: PhotoId, r: Rect, square }; let badge_bar = Rect::from_min_max(pos2(img_rect.left(), img_rect.bottom() - 24.0), img_rect.right_bottom()); if !square && photo.label.is_some() { - // Justified photos fill the whole cell: reserve the badge footer as coloured chrome. - p.rect_filled(badge_bar, 0.0, crate::theme::label_background(t.cell, photo.label, selected)); + // Justified photos fill the whole cell, so the label shows as a translucent tinted footer + // over the photo's bottom edge (opaque, it would hide 24 px of every labelled photo). + p.rect_filled(badge_bar, 0.0, crate::theme::label_background(t.cell, photo.label, selected).gamma_multiply(0.7)); } if show_badges { let bar = badge_bar; diff --git a/docs/library-shortcuts.md b/docs/library-shortcuts.md index 5305fe57..2b4c96f8 100644 --- a/docs/library-shortcuts.md +++ b/docs/library-shortcuts.md @@ -18,7 +18,7 @@ culling; it does not delete the original or remove it from the library. Setting a colour label shows the same brief bottom toast as a rating, using the colour or your custom label name. Clearing it shows a confirmation too, whether applied by a key, menu or swatch. Labelled thumbnails have a translucent matching colour on the square-grid surround, photo-grid -footer and Detail filmstrip surround. The photo pixels and white active-photo outline remain clear. +footer (translucent, over the bottom of the photo) and Detail filmstrip surround. The white active-photo outline remains clear. The label confirmation uses a pale matching background with dark text; ratings and errors retain their neutral HUD styling. Custom names use the underlying label's colour. diff --git a/docs/whats-new.md b/docs/whats-new.md index 111b6201..c15f29b3 100644 --- a/docs/whats-new.md +++ b/docs/whats-new.md @@ -3,7 +3,7 @@ ## October 2026 ### Library keyboard culling -- Colour labels tint thumbnail surrounds in Square Grid and the Detail filmstrip, and the footer in Photo Grid. Label confirmations use a matching pale colour; the photo pixels retain their original appearance. +- Colour labels tint thumbnail surrounds in Square Grid and the Detail filmstrip, and a translucent footer along the bottom of the photo in Photo Grid. Label confirmations use a matching pale colour. - Setting or clearing a colour label shows a brief bottom confirmation, like rating a photo; custom label names appear in the message too. - On macOS, ratings `0–5`, labels `6–9` and pick/unflag `P/U` now reach the app even when shown in the native menu (issue #283; adapted from PR #261). - `Shift+6–9` labels and advances. `Shift+P` picks and advances in Photo Grid and Square Grid; it opens Presets in other views. With Auto Advance on, Shift still moves only once. From d80874979232a0ad10c22f2ca78574aa37afb9b8 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 8 Oct 2026 17:52:46 +0000 Subject: [PATCH 35/41] Desktop: a --memory session writes no log file (#286 with #288) #288 made --memory sessions write nothing (issue #164); #286's log file would have been the exception. It is not opened there. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01912WaK6Rmew3wxZuPyX7E8 --- apps/lightcraft/src/main.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/lightcraft/src/main.rs b/apps/lightcraft/src/main.rs index f26725cf..409519be 100644 --- a/apps/lightcraft/src/main.rs +++ b/apps/lightcraft/src/main.rs @@ -514,7 +514,8 @@ fn main() -> eframe::Result { // The log file: opened after the arguments, so `--version`, `--help` and a usage error leave // no file behind. Records logged until now are written to it first. if let Some(logger) = logger { - match log_dir() { + // a --memory session writes nothing (issue #164), a log file included + match log_dir().filter(|_| !in_memory) { Some(dir) => match logger.attach_dir(&dir) { Ok(path) => log::info!("LightCraft {}, log file {}", env!("CARGO_PKG_VERSION"), path.display()), // The file sink has given up by now, so this goes to standard error only. From 4eb9b565e01363a919bd52ca25cd48943d04d5c5 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 8 Oct 2026 17:54:50 +0000 Subject: [PATCH 36/41] Profiles: imported LUT names shown as named, own ids for their folders (#333) #333 passed imported LUT names and folder names through tr(), so a LUT named like a message ("Vivid") was shown translated; they are now shown as the file names them (builtin_label / new profile_label, as for user presets). A LUT folder named like a built-in group ("Film") shared that group's menu widget id and the browser section's open state; both are keyed apart now. Restores the "Amount 0 = the photo as it was" assertion #333 replaced, and adds a What's new line. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01912WaK6Rmew3wxZuPyX7E8 --- crates/engine/src/tests_import.rs | 1 + crates/ui-egui/src/i18n.rs | 6 ++++++ crates/ui-egui/src/panels/edit.rs | 15 +++++++------- crates/ui-egui/src/panels/profiles.rs | 28 ++++++++++++++------------- docs/whats-new.md | 2 ++ 5 files changed, 32 insertions(+), 20 deletions(-) diff --git a/crates/engine/src/tests_import.rs b/crates/engine/src/tests_import.rs index ba657701..ce36c447 100644 --- a/crates/engine/src/tests_import.rs +++ b/crates/engine/src/tests_import.rs @@ -544,6 +544,7 @@ fn cube_luts_become_profiles() { mean(&warm, 2) ); s.execute("develop.profile", &json!({"id": id, "amount": 0})).unwrap(); + assert_eq!(s.render_now(photo, 48, 32).unwrap().image.data, base.data, "Amount 0 = the photo as it was"); assert_eq!(s.profile_info(&id), Some(("Warm Test", "Film Looks"))); s.execute("profile.favorite", &json!({"id": id})).unwrap(); assert!(s.profile_favorites.contains(&id)); diff --git a/crates/ui-egui/src/i18n.rs b/crates/ui-egui/src/i18n.rs index 7ed26d56..b95cb630 100644 --- a/crates/ui-egui/src/i18n.rs +++ b/crates/ui-egui/src/i18n.rs @@ -191,6 +191,12 @@ pub fn builtin_label(source: &str, builtin: bool) -> &str { if builtin { tr(source) } else { source } } +/// A profile's name for display: a built-in profile's is translated, an imported LUT's is shown as +/// its file named it (it may happen to match a message, like "Vivid"). +pub fn profile_label<'a>(id: &str, name: &'a str) -> &'a str { + builtin_label(name, lightcraft_engine::presets::profile(id).is_some()) +} + /// An Activity (history) step for display. The step's stored label stays English; generated steps /// are translated around the names they carry, and a user preset's name is kept verbatim. pub fn history_label(source: &str, presets: &[lightcraft_develop::Preset]) -> String { diff --git a/crates/ui-egui/src/panels/edit.rs b/crates/ui-egui/src/panels/edit.rs index 2d79f5f5..163ae6fb 100644 --- a/crates/ui-egui/src/panels/edit.rs +++ b/crates/ui-egui/src/panels/edit.rs @@ -140,7 +140,7 @@ pub fn show(app: &mut LightcraftApp, ui: &mut egui::Ui, id: PhotoId) { ui.horizontal(|ui| { ui.label(egui::RichText::new(crate::i18n::tr("Profile")).font(t.font(13.0)).color(t.text_dim)); let name = app.session.profile_info(&d.profile.id).map(|(name, _)| name).unwrap_or("Color"); - let r = crate::widgets::dropdown(ui, "profile", crate::i18n::tr(name), t.font(15.0), t.text_label); + let r = crate::widgets::dropdown(ui, "profile", crate::i18n::profile_label(&d.profile.id, name), t.font(15.0), t.text_label); egui::Popup::menu(&r).show(|ui| profile_menu(app, ui, &d)); ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| { if crate::widgets::icon_button(ui, "profileBrowser", Icon::ProfileGrid, vec2(28.0, 28.0), false, true, "Browse Profiles").clicked() { @@ -356,7 +356,7 @@ fn profile_menu(app: &mut LightcraftApp, ui: &mut egui::Ui, d: &DevelopSettings) id: &str, name: &str, pick: &mut (Option, Option)| { - let r = ui.selectable_label(id == cur, crate::i18n::tr(name)); + let r = ui.selectable_label(id == cur, crate::i18n::profile_label(id, name)); register(ui.ctx(), format!("profileMenu:{key}:{id}"), r.rect); if r.clicked() { pick.0 = Some(id.to_string()); @@ -393,21 +393,22 @@ fn profile_menu(app: &mut LightcraftApp, ui: &mut egui::Ui, d: &DevelopSettings) lut_groups.sort_unstable(); lut_groups.dedup(); for g in &lut_groups { - let r = ui.menu_button(crate::i18n::tr(g), |ui| { + // a folder's name as it is, and its own widget id even when it matches a built-in group + let r = ui.menu_button(g.as_str(), |ui| { ui.set_min_width(170.0); for p in app.session.lut_profiles.iter().filter(|p| &p.group == g) { item(ui, "group", &p.id, &p.name, &mut pick); } }); - register(ui.ctx(), format!("profileMenu:groupMenu:{g}"), r.response.rect); + register(ui.ctx(), format!("profileMenu:lutGroupMenu:{g}"), r.response.rect); } ui.separator(); if let Some((name, _)) = app.session.profile_info(cur) { let fav = app.session.profile_favorites.iter().any(|f| f == cur); let label = if fav { - crate::i18n::tr_format!("Remove “{}” from Favorites", crate::i18n::tr(name)) + crate::i18n::tr_format!("Remove “{}” from Favorites", crate::i18n::profile_label(cur, name)) } else { - crate::i18n::tr_format!("Add “{}” to Favorites", crate::i18n::tr(name)) + crate::i18n::tr_format!("Add “{}” to Favorites", crate::i18n::profile_label(cur, name)) }; let r = ui.button(label); register(ui.ctx(), "profileMenu:toggleFavorite", r.rect); @@ -428,7 +429,7 @@ fn profile_menu(app: &mut LightcraftApp, ui: &mut egui::Ui, d: &DevelopSettings) let mut s = d.clone(); s.profile.id = hovered_id.clone(); s.profile.amount = 100.0; - app.hover_preview = Some(crate::HoverPreview { label: crate::i18n::tr_format!("Profile: {}", crate::i18n::tr(name)), settings: s }); + app.hover_preview = Some(crate::HoverPreview { label: crate::i18n::tr_format!("Profile: {}", crate::i18n::profile_label(hovered_id, name)), settings: s }); } if let Some(id) = pick.0 { let _ = app.run("develop.profile", json!({"id": id})); diff --git a/crates/ui-egui/src/panels/profiles.rs b/crates/ui-egui/src/panels/profiles.rs index 1ae321f5..03a62451 100644 --- a/crates/ui-egui/src/panels/profiles.rs +++ b/crates/ui-egui/src/panels/profiles.rs @@ -35,13 +35,14 @@ pub fn show(app: &mut LightcraftApp, ui: &mut egui::Ui, id: PhotoId) { let (cr, _) = ui.allocate_exact_size(vec2(ui.available_width(), 34.0), Sense::hover()); let name = app.session.profile_info(&d.profile.id).map(|(name, _)| name).unwrap_or("Color"); ui.painter().text(pos2(cr.left() + 24.0, cr.center().y), Align2::LEFT_CENTER, crate::i18n::tr("Profile"), t.font(13.0), t.text_dim); - ui.painter().text(pos2(cr.left() + 84.0, cr.center().y), Align2::LEFT_CENTER, crate::i18n::tr(name), t.font(14.0), t.text_label); + ui.painter().text(pos2(cr.left() + 84.0, cr.center().y), Align2::LEFT_CENTER, crate::i18n::profile_label(&d.profile.id, name), t.font(14.0), t.text_label); if d.profile.id != "lc.color" { super::edit::control(app, ui, &d, "profile.amount", true); ui.add_space(6.0); } divider(ui); - let mut sections: Vec<(&str, &str, Vec<(String, String, String)>)> = Vec::new(); + // (title, cell tag, imported LUT folder, profiles) + let mut sections: Vec<(&str, &str, bool, Vec<(String, String, String)>)> = Vec::new(); let favs: Vec<(String, String, String)> = app .session .profile_favorites @@ -49,10 +50,10 @@ pub fn show(app: &mut LightcraftApp, ui: &mut egui::Ui, id: PhotoId) { .filter_map(|f| app.session.profile_info(f).map(|(name, group)| (f.clone(), name.to_string(), group.to_string()))) .collect(); if !favs.is_empty() { - sections.push(("Favorites", "fav", favs)); + sections.push(("Favorites", "fav", false, favs)); } for g in profile_groups() { - sections.push((g, "", PROFILES.iter().filter(|p| p.group == g).map(|p| (p.id.to_string(), p.name.to_string(), p.group.to_string())).collect())); + sections.push((g, "", false, PROFILES.iter().filter(|p| p.group == g).map(|p| (p.id.to_string(), p.name.to_string(), p.group.to_string())).collect())); } // imported LUT profile groups let mut lut_groups: Vec = app.session.lut_profiles.iter().map(|p| p.group.clone()).collect(); @@ -66,20 +67,21 @@ pub fn show(app: &mut LightcraftApp, ui: &mut egui::Ui, id: PhotoId) { .filter(|p| &p.group == g) .map(|p| (p.id.clone(), p.name.clone(), p.group.clone())) .collect(); - sections.push((g.as_str(), "", profiles)); + sections.push((g.as_str(), "", true, profiles)); } - for (title, tag, list) in sections { - let open_id = egui::Id::new(("profile-group", title)); + for (title, tag, imported, list) in sections { + // an imported folder named like a built-in group still opens and closes on its own + let open_id = egui::Id::new(("profile-group", imported, title)); let open: bool = ui.data(|m| m.get_temp(open_id)).unwrap_or(true); let (r, resp) = ui.allocate_exact_size(vec2(ui.available_width(), 32.0), Sense::click()); - register(ui.ctx(), format!("profileGroup:{title}"), r); + register(ui.ctx(), if imported { format!("profileGroup:lut:{title}") } else { format!("profileGroup:{title}") }, r); paint( ui.painter(), Rect::from_center_size(pos2(r.left() + 22.0, r.center().y), vec2(12.0, 12.0)), if open { Icon::ChevronDown } else { Icon::ChevronRight }, t.text_label, ); - ui.painter().text(pos2(r.left() + 36.0, r.center().y), Align2::LEFT_CENTER, crate::i18n::tr(title), t.semibold(13.0), t.text_label); + ui.painter().text(pos2(r.left() + 36.0, r.center().y), Align2::LEFT_CENTER, crate::i18n::builtin_label(title, !imported), t.semibold(13.0), t.text_label); ui.painter().text(pos2(r.right() - 22.0, r.center().y), Align2::RIGHT_CENTER, list.len().to_string(), t.font(12.0), t.text_dim); if resp.clicked() { ui.data_mut(|m| m.insert_temp(open_id, !open)); @@ -109,7 +111,7 @@ pub fn show(app: &mut LightcraftApp, ui: &mut egui::Ui, id: PhotoId) { && let Some((p_name, _)) = app.session.profile_info(&saved_profile_id) { let s = with_profile_id(&d, &saved_profile_id); - app.hover_preview = Some(HoverPreview { label: crate::i18n::tr_format!("Profile: {}", crate::i18n::tr(p_name)), settings: s }); + app.hover_preview = Some(HoverPreview { label: crate::i18n::tr_format!("Profile: {}", crate::i18n::profile_label(p_id, p_name)), settings: s }); } } else { ui.data_mut(|m| m.remove::<(PhotoId, String)>(last_hover_id)); @@ -158,7 +160,7 @@ fn cell_ui( painter.rect_stroke(thumb, 3.0, Stroke::new(1.0, t.text_dim), egui::StrokeKind::Outside); } let name_color = if applied { t.text } else { t.text_label }; - painter.text(pos2(cell.left() + 2.0, thumb.bottom() + 12.0), Align2::LEFT_CENTER, crate::i18n::tr(p_name), t.font(12.0), name_color); + painter.text(pos2(cell.left() + 2.0, thumb.bottom() + 12.0), Align2::LEFT_CENTER, crate::i18n::profile_label(p_id, p_name), t.font(12.0), name_color); // favourite star (always shown when set, on hover otherwise) let fav = app.session.profile_favorites.iter().any(|f| f == p_id); let star = Rect::from_center_size(pos2(thumb.right() - 13.0, thumb.top() + 13.0), vec2(20.0, 20.0)); @@ -169,10 +171,10 @@ fn cell_ui( paint(painter, star.shrink(4.0), if fav { Icon::StarFilled } else { Icon::Star }, if fav || over_star { t.star } else { Color32::WHITE }); } if resp.hovered() && !applied && !over_star { - app.hover_preview = Some(HoverPreview { label: crate::i18n::tr_format!("Profile: {}", crate::i18n::tr(p_name)), settings: s.clone() }); + app.hover_preview = Some(HoverPreview { label: crate::i18n::tr_format!("Profile: {}", crate::i18n::profile_label(p_id, p_name)), settings: s.clone() }); ui.data_mut(|m| m.insert_temp(egui::Id::new("last-profile-hover"), (id, p_id.to_string()))); } - let resp = resp.on_hover_text(format!("{} ({})", crate::i18n::tr(p_name), crate::i18n::tr(p_group))); + let resp = resp.on_hover_text(format!("{} ({})", crate::i18n::profile_label(p_id, p_name), crate::i18n::builtin_label(p_group, lightcraft_engine::presets::profile(p_id).is_some()))); if resp.clicked() { if resp.interact_pointer_pos().is_some_and(|q| star.contains(q)) { let _ = app.run("profile.favorite", json!({"id": p_id})); diff --git a/docs/whats-new.md b/docs/whats-new.md index 745a8188..5ea9eea8 100644 --- a/docs/whats-new.md +++ b/docs/whats-new.md @@ -26,6 +26,8 @@ - Luminar looks: `.lmp` files and `.mplumpack` collections import as presets (grouped by collection); the sliders with a counterpart here come along, the rest is listed. - 23 new built-in presets: Portrait, Landscape, Urban, Food, Seasons, Vintage and B&W toners. +- Imported `.cube` LUT profiles appear in the Profile menu and the profile browser, grouped by their folder, and stay + favourites across restarts (issue #328). ### Editing - The Tint slider works the right way round (issues #188, #321): left adds green, right adds magenta, as its track From cab6af8608257ac555d89c127ab362a89fe5f090 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 8 Oct 2026 17:55:31 +0000 Subject: [PATCH 37/41] Fetch: say where the bearer token goes (#259) The token_env comment said the token goes to the first mirror's host; it goes to every configured mirror's own host (never to a redirect's). Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01912WaK6Rmew3wxZuPyX7E8 --- crates/fetch/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/fetch/src/lib.rs b/crates/fetch/src/lib.rs index 24172529..c5c96a90 100644 --- a/crates/fetch/src/lib.rs +++ b/crates/fetch/src/lib.rs @@ -79,8 +79,8 @@ pub struct Options { pub stall_timeout: Duration, /// Tries per mirror and file (each resumes where the last one stopped). pub attempts: u32, - /// The environment variable holding a bearer token for the first mirror's host (sent only over https, never to a - /// host a redirect leads to). `None`: nothing is ever sent, so one model's token cannot leak to another's server. + /// The environment variable holding a bearer token, sent to each configured mirror's own host (only over https, + /// never to a host a redirect leads to), so list only mirrors that may see it. `None`: nothing is ever sent, so one model's token cannot leak to another's server. pub token_env: Option<&'static str>, } From 399aedd23a137fe25915520a2290b852d735c2f0 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 8 Oct 2026 17:57:54 +0000 Subject: [PATCH 38/41] Docs: What's new for the import review size and Shift-click ranges, CLI logging (#346, #347, #340) Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01912WaK6Rmew3wxZuPyX7E8 --- docs/whats-new.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/whats-new.md b/docs/whats-new.md index 5ea9eea8..62615b95 100644 --- a/docs/whats-new.md +++ b/docs/whats-new.md @@ -43,6 +43,8 @@ - Select All and multi-selection show every selected photo in the grid and filmstrip, not only the active one (issues #187, #298). Importing files that are in Recently Deleted asks whether to leave them there, restore them (with their edits) or import them as new; the trash view's Photo menu has Empty Recently Deleted. +- The Import Photos review opens bigger and can be resized; its photo grid fills it (issue #337). Shift-click checks + or unchecks a range of photos (issue #338). ### Reliability - On macOS, single-key shortcuts that appear in the menu bar now work: E, C, H, M, ⇧P, I, K, D, ratings 0–5, @@ -56,6 +58,7 @@ with the logs of the two previous runs beside it, so warnings and crashes of a run started from a desktop menu or the Dock can be attached to a bug report. `LIGHTCRAFT_LOG` works as before; `RUST_LOG` takes env_logger-style directives. See README → Quick start → Logs. +- `lightcraft-cli` logs warnings on stderr too (issue #168); `LIGHTCRAFT_LOG` or `RUST_LOG` picks another level. - LightCraft no longer crashes at launch on Windows PCs whose Vulkan driver is broken (issue #136, e.g. some Intel UHD 630 drivers): on Windows the window and GPU rendering use DirectX 12 only and never load the Vulkan driver unless asked to. `LIGHTCRAFT_GPU_BACKEND=dx12 | vulkan | metal | off` (or wgpu's `WGPU_BACKEND`, which GPU rendering From 7f386aa258001e2a6b6b91324d8509f9964798f3 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 8 Oct 2026 17:58:41 +0000 Subject: [PATCH 39/41] Merge fixes: close the test functions the #307/#325 and #325/#346 merges ran together Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01912WaK6Rmew3wxZuPyX7E8 --- crates/engine/src/presets.rs | 5 +-- crates/ui-egui/src/headless.rs | 2 ++ crates/ui-egui/src/import.rs | 23 ++++++------ crates/ui-egui/src/panels/edit.rs | 9 ++--- crates/ui-egui/src/panels/profiles.rs | 51 +++++++++++++++++++-------- crates/ui-egui/src/tests_panels.rs | 2 ++ 6 files changed, 57 insertions(+), 35 deletions(-) diff --git a/crates/engine/src/presets.rs b/crates/engine/src/presets.rs index f77b435d..ec63cc23 100644 --- a/crates/engine/src/presets.rs +++ b/crates/engine/src/presets.rs @@ -451,10 +451,7 @@ impl Session { if let Some(p) = profile(id) { return Some((p.name, p.group)); } - self.lut_profiles - .iter() - .find(|p| p.id == id) - .map(|p| (p.name.as_str(), p.group.as_str())) + self.lut_profiles.iter().find(|p| p.id == id).map(|p| (p.name.as_str(), p.group.as_str())) } /// Remember `id` as the most recently applied profile. diff --git a/crates/ui-egui/src/headless.rs b/crates/ui-egui/src/headless.rs index a60051bb..e6e79000 100644 --- a/crates/ui-egui/src/headless.rs +++ b/crates/ui-egui/src/headless.rs @@ -1421,6 +1421,8 @@ mod tests { } let _ = std::fs::remove_dir_all(&dir); } + } + /// The import review opens bigger than the old fixed 6 × 2.5 grid and can be resized by its /// corner: the photo grid takes the new height, and the window then keeps its size (issue #337). #[test] diff --git a/crates/ui-egui/src/import.rs b/crates/ui-egui/src/import.rs index 2122aa8d..f5ac3c63 100644 --- a/crates/ui-egui/src/import.rs +++ b/crates/ui-egui/src/import.rs @@ -831,18 +831,19 @@ pub fn body(app: &mut LightcraftApp, ui: &mut egui::Ui, d: &mut ImportDialog) { y(CONTENT_BOTTOM).zip(y(GRID_BOTTOM)).map(|(content, grid)| content - grid) }); let grid_h = grid_height(ui.available_height(), below.unwrap_or(DEFAULT_BELOW_GRID), cell + 26.0); - let grid = egui::ScrollArea::vertical().id_salt("import-grid").max_height(grid_h).auto_shrink([false, false]).show_viewport(ui, |ui, viewport| { - let (area, _) = ui.allocate_exact_size(vec2(avail, rows as f32 * (cell + 26.0)), Sense::hover()); - for i in 0..n { - let (c, r) = (i % cols, i / cols); - let local = Rect::from_min_size(pos2(c as f32 * (cell + 6.0), r as f32 * (cell + 26.0)), vec2(cell, cell + 20.0)); - if !local.intersects(viewport.expand(cell)) { - continue; + let grid = + egui::ScrollArea::vertical().id_salt("import-grid").max_height(grid_h).auto_shrink([false, false]).show_viewport(ui, |ui, viewport| { + let (area, _) = ui.allocate_exact_size(vec2(avail, rows as f32 * (cell + 26.0)), Sense::hover()); + for i in 0..n { + let (c, r) = (i % cols, i / cols); + let local = Rect::from_min_size(pos2(c as f32 * (cell + 6.0), r as f32 * (cell + 26.0)), vec2(cell, cell + 20.0)); + if !local.intersects(viewport.expand(cell)) { + continue; + } + let rect = local.translate(area.min.to_vec2()); + candidate_cell(app, ui, d, i, rect); } - let rect = local.translate(area.min.to_vec2()); - candidate_cell(app, ui, d, i, rect); - } - }); + }); if !ui.is_sizing_pass() { ui.ctx().data_mut(|m| m.insert_temp(egui::Id::new(GRID_BOTTOM), grid.inner_rect.bottom())); } diff --git a/crates/ui-egui/src/panels/edit.rs b/crates/ui-egui/src/panels/edit.rs index 163ae6fb..cd2aae46 100644 --- a/crates/ui-egui/src/panels/edit.rs +++ b/crates/ui-egui/src/panels/edit.rs @@ -351,11 +351,7 @@ fn profile_menu(app: &mut LightcraftApp, ui: &mut egui::Ui, d: &DevelopSettings) let cur = d.profile.id.as_str(); // (clicked, hovered) let mut pick: (Option, Option) = (None, None); - let mut item = |ui: &mut egui::Ui, - key: &str, - id: &str, - name: &str, - pick: &mut (Option, Option)| { + let mut item = |ui: &mut egui::Ui, key: &str, id: &str, name: &str, pick: &mut (Option, Option)| { let r = ui.selectable_label(id == cur, crate::i18n::profile_label(id, name)); register(ui.ctx(), format!("profileMenu:{key}:{id}"), r.rect); if r.clicked() { @@ -429,7 +425,8 @@ fn profile_menu(app: &mut LightcraftApp, ui: &mut egui::Ui, d: &DevelopSettings) let mut s = d.clone(); s.profile.id = hovered_id.clone(); s.profile.amount = 100.0; - app.hover_preview = Some(crate::HoverPreview { label: crate::i18n::tr_format!("Profile: {}", crate::i18n::profile_label(hovered_id, name)), settings: s }); + app.hover_preview = + Some(crate::HoverPreview { label: crate::i18n::tr_format!("Profile: {}", crate::i18n::profile_label(hovered_id, name)), settings: s }); } if let Some(id) = pick.0 { let _ = app.run("develop.profile", json!({"id": id})); diff --git a/crates/ui-egui/src/panels/profiles.rs b/crates/ui-egui/src/panels/profiles.rs index 03a62451..c5045b74 100644 --- a/crates/ui-egui/src/panels/profiles.rs +++ b/crates/ui-egui/src/panels/profiles.rs @@ -35,7 +35,13 @@ pub fn show(app: &mut LightcraftApp, ui: &mut egui::Ui, id: PhotoId) { let (cr, _) = ui.allocate_exact_size(vec2(ui.available_width(), 34.0), Sense::hover()); let name = app.session.profile_info(&d.profile.id).map(|(name, _)| name).unwrap_or("Color"); ui.painter().text(pos2(cr.left() + 24.0, cr.center().y), Align2::LEFT_CENTER, crate::i18n::tr("Profile"), t.font(13.0), t.text_dim); - ui.painter().text(pos2(cr.left() + 84.0, cr.center().y), Align2::LEFT_CENTER, crate::i18n::profile_label(&d.profile.id, name), t.font(14.0), t.text_label); + ui.painter().text( + pos2(cr.left() + 84.0, cr.center().y), + Align2::LEFT_CENTER, + crate::i18n::profile_label(&d.profile.id, name), + t.font(14.0), + t.text_label, + ); if d.profile.id != "lc.color" { super::edit::control(app, ui, &d, "profile.amount", true); ui.add_space(6.0); @@ -53,20 +59,19 @@ pub fn show(app: &mut LightcraftApp, ui: &mut egui::Ui, id: PhotoId) { sections.push(("Favorites", "fav", false, favs)); } for g in profile_groups() { - sections.push((g, "", false, PROFILES.iter().filter(|p| p.group == g).map(|p| (p.id.to_string(), p.name.to_string(), p.group.to_string())).collect())); + sections.push(( + g, + "", + false, + PROFILES.iter().filter(|p| p.group == g).map(|p| (p.id.to_string(), p.name.to_string(), p.group.to_string())).collect(), + )); } // imported LUT profile groups let mut lut_groups: Vec = app.session.lut_profiles.iter().map(|p| p.group.clone()).collect(); lut_groups.sort_unstable(); lut_groups.dedup(); for g in &lut_groups { - let profiles = app - .session - .lut_profiles - .iter() - .filter(|p| &p.group == g) - .map(|p| (p.id.clone(), p.name.clone(), p.group.clone())) - .collect(); + let profiles = app.session.lut_profiles.iter().filter(|p| &p.group == g).map(|p| (p.id.clone(), p.name.clone(), p.group.clone())).collect(); sections.push((g.as_str(), "", true, profiles)); } for (title, tag, imported, list) in sections { @@ -81,7 +86,13 @@ pub fn show(app: &mut LightcraftApp, ui: &mut egui::Ui, id: PhotoId) { if open { Icon::ChevronDown } else { Icon::ChevronRight }, t.text_label, ); - ui.painter().text(pos2(r.left() + 36.0, r.center().y), Align2::LEFT_CENTER, crate::i18n::builtin_label(title, !imported), t.semibold(13.0), t.text_label); + ui.painter().text( + pos2(r.left() + 36.0, r.center().y), + Align2::LEFT_CENTER, + crate::i18n::builtin_label(title, !imported), + t.semibold(13.0), + t.text_label, + ); ui.painter().text(pos2(r.right() - 22.0, r.center().y), Align2::RIGHT_CENTER, list.len().to_string(), t.font(12.0), t.text_dim); if resp.clicked() { ui.data_mut(|m| m.insert_temp(open_id, !open)); @@ -111,7 +122,8 @@ pub fn show(app: &mut LightcraftApp, ui: &mut egui::Ui, id: PhotoId) { && let Some((p_name, _)) = app.session.profile_info(&saved_profile_id) { let s = with_profile_id(&d, &saved_profile_id); - app.hover_preview = Some(HoverPreview { label: crate::i18n::tr_format!("Profile: {}", crate::i18n::profile_label(p_id, p_name)), settings: s }); + app.hover_preview = + Some(HoverPreview { label: crate::i18n::tr_format!("Profile: {}", crate::i18n::profile_label(p_id, p_name)), settings: s }); } } else { ui.data_mut(|m| m.remove::<(PhotoId, String)>(last_hover_id)); @@ -160,7 +172,13 @@ fn cell_ui( painter.rect_stroke(thumb, 3.0, Stroke::new(1.0, t.text_dim), egui::StrokeKind::Outside); } let name_color = if applied { t.text } else { t.text_label }; - painter.text(pos2(cell.left() + 2.0, thumb.bottom() + 12.0), Align2::LEFT_CENTER, crate::i18n::profile_label(p_id, p_name), t.font(12.0), name_color); + painter.text( + pos2(cell.left() + 2.0, thumb.bottom() + 12.0), + Align2::LEFT_CENTER, + crate::i18n::profile_label(p_id, p_name), + t.font(12.0), + name_color, + ); // favourite star (always shown when set, on hover otherwise) let fav = app.session.profile_favorites.iter().any(|f| f == p_id); let star = Rect::from_center_size(pos2(thumb.right() - 13.0, thumb.top() + 13.0), vec2(20.0, 20.0)); @@ -171,10 +189,15 @@ fn cell_ui( paint(painter, star.shrink(4.0), if fav { Icon::StarFilled } else { Icon::Star }, if fav || over_star { t.star } else { Color32::WHITE }); } if resp.hovered() && !applied && !over_star { - app.hover_preview = Some(HoverPreview { label: crate::i18n::tr_format!("Profile: {}", crate::i18n::profile_label(p_id, p_name)), settings: s.clone() }); + app.hover_preview = + Some(HoverPreview { label: crate::i18n::tr_format!("Profile: {}", crate::i18n::profile_label(p_id, p_name)), settings: s.clone() }); ui.data_mut(|m| m.insert_temp(egui::Id::new("last-profile-hover"), (id, p_id.to_string()))); } - let resp = resp.on_hover_text(format!("{} ({})", crate::i18n::profile_label(p_id, p_name), crate::i18n::builtin_label(p_group, lightcraft_engine::presets::profile(p_id).is_some()))); + let resp = resp.on_hover_text(format!( + "{} ({})", + crate::i18n::profile_label(p_id, p_name), + crate::i18n::builtin_label(p_group, lightcraft_engine::presets::profile(p_id).is_some()) + )); if resp.clicked() { if resp.interact_pointer_pos().is_some_and(|q| star.contains(q)) { let _ = app.run("profile.favorite", json!({"id": p_id})); diff --git a/crates/ui-egui/src/tests_panels.rs b/crates/ui-egui/src/tests_panels.rs index 46b2cb6c..37d05131 100644 --- a/crates/ui-egui/src/tests_panels.rs +++ b/crates/ui-egui/src/tests_panels.rs @@ -534,6 +534,8 @@ fn the_selection_bar_stays_inside_the_panel() { let panel = widget(&h, "panel:left_panel"); let bar = widget(&h, "highlight:libfolder:/a/b/c/d/e/f/g/h/i/j/k/l"); assert!(bar.right() <= panel.right(), "the bar {bar:?} ends inside the panel {panel:?}"); +} + /// Select All in the loupe: every filmstrip cell of a selected photo is drawn selected, not just /// the active one (issue #298). #[test] From dc67d348e6438defa64038082ab419c7be1a94b3 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 8 Oct 2026 18:00:16 +0000 Subject: [PATCH 40/41] Merge fixes: #333 hover label id, #346's test boxes the import dialog (#325), unused mut Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01912WaK6Rmew3wxZuPyX7E8 --- crates/ui-egui/src/headless.rs | 2 +- crates/ui-egui/src/panels/edit.rs | 2 +- crates/ui-egui/src/panels/profiles.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/ui-egui/src/headless.rs b/crates/ui-egui/src/headless.rs index e6e79000..b04759d4 100644 --- a/crates/ui-egui/src/headless.rs +++ b/crates/ui-egui/src/headless.rs @@ -1437,7 +1437,7 @@ mod tests { ..Default::default() }) .collect(); - h.app.ui.dialog = Some(crate::state::Dialog::Import { opts: crate::import::ImportDialog::new(candidates) }); + h.app.ui.dialog = Some(crate::state::Dialog::Import { opts: Box::new(crate::import::ImportDialog::new(candidates)) }); let rect = |h: &mut Headless| { let r = h.request("ui.widgets", json!({}), t); let w = r["result"].as_array().and_then(|a| a.iter().find(|w| w["id"] == "dialog:window")).expect("dialog on screen"); diff --git a/crates/ui-egui/src/panels/edit.rs b/crates/ui-egui/src/panels/edit.rs index cd2aae46..a8178268 100644 --- a/crates/ui-egui/src/panels/edit.rs +++ b/crates/ui-egui/src/panels/edit.rs @@ -351,7 +351,7 @@ fn profile_menu(app: &mut LightcraftApp, ui: &mut egui::Ui, d: &DevelopSettings) let cur = d.profile.id.as_str(); // (clicked, hovered) let mut pick: (Option, Option) = (None, None); - let mut item = |ui: &mut egui::Ui, key: &str, id: &str, name: &str, pick: &mut (Option, Option)| { + let item = |ui: &mut egui::Ui, key: &str, id: &str, name: &str, pick: &mut (Option, Option)| { let r = ui.selectable_label(id == cur, crate::i18n::profile_label(id, name)); register(ui.ctx(), format!("profileMenu:{key}:{id}"), r.rect); if r.clicked() { diff --git a/crates/ui-egui/src/panels/profiles.rs b/crates/ui-egui/src/panels/profiles.rs index c5045b74..67218937 100644 --- a/crates/ui-egui/src/panels/profiles.rs +++ b/crates/ui-egui/src/panels/profiles.rs @@ -123,7 +123,7 @@ pub fn show(app: &mut LightcraftApp, ui: &mut egui::Ui, id: PhotoId) { { let s = with_profile_id(&d, &saved_profile_id); app.hover_preview = - Some(HoverPreview { label: crate::i18n::tr_format!("Profile: {}", crate::i18n::profile_label(p_id, p_name)), settings: s }); + Some(HoverPreview { label: crate::i18n::tr_format!("Profile: {}", crate::i18n::profile_label(&saved_profile_id, p_name)), settings: s }); } } else { ui.data_mut(|m| m.remove::<(PhotoId, String)>(last_hover_id)); From 4fb8ad4dcd6900546e3aae5e8fd9d7662a640dd9 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 8 Oct 2026 18:01:00 +0000 Subject: [PATCH 41/41] Format the #333 merge fix Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01912WaK6Rmew3wxZuPyX7E8 --- crates/ui-egui/src/panels/profiles.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/crates/ui-egui/src/panels/profiles.rs b/crates/ui-egui/src/panels/profiles.rs index 67218937..6294d1e6 100644 --- a/crates/ui-egui/src/panels/profiles.rs +++ b/crates/ui-egui/src/panels/profiles.rs @@ -122,8 +122,10 @@ pub fn show(app: &mut LightcraftApp, ui: &mut egui::Ui, id: PhotoId) { && let Some((p_name, _)) = app.session.profile_info(&saved_profile_id) { let s = with_profile_id(&d, &saved_profile_id); - app.hover_preview = - Some(HoverPreview { label: crate::i18n::tr_format!("Profile: {}", crate::i18n::profile_label(&saved_profile_id, p_name)), settings: s }); + app.hover_preview = Some(HoverPreview { + label: crate::i18n::tr_format!("Profile: {}", crate::i18n::profile_label(&saved_profile_id, p_name)), + settings: s, + }); } } else { ui.data_mut(|m| m.remove::<(PhotoId, String)>(last_hover_id));