diff --git a/crates/engine/src/cmd/browse.rs b/crates/engine/src/cmd/browse.rs index 01c67c267..6f3f88963 100644 --- a/crates/engine/src/cmd/browse.rs +++ b/crates/engine/src/cmd/browse.rs @@ -482,6 +482,9 @@ pub(crate) fn follow_folder(s: &mut Session, from: &str, to: &str) { // 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(f) = s.shelved_filter.as_mut() { + follow_library_folder(&mut f.library_folder, from, to); + } if let Some(b) = &mut s.browse && let Ok(rel) = Path::new(&b.path).strip_prefix(from) { diff --git a/crates/engine/src/cmd/cull.rs b/crates/engine/src/cmd/cull.rs index 142accd5a..ec6f59feb 100644 --- a/crates/engine/src/cmd/cull.rs +++ b/crates/engine/src/cmd/cull.rs @@ -115,6 +115,7 @@ fn find_similar(s: &mut Session, p: &Value) -> Result { } hits.sort_by(|a, b| b.1.total_cmp(&a.1)); if p.get("filter").and_then(Value::as_bool).unwrap_or(true) { + s.shelved_filter = None; s.filter.only = hits.iter().map(|h| h.0).collect(); } Ok(json!({"photos": hits.iter().map(|(i, v)| json!({"id": i.0, "similarity": (v * 1000.0).round() / 1000.0})).collect::>()})) diff --git a/crates/engine/src/cmd/face_recognize.rs b/crates/engine/src/cmd/face_recognize.rs index e66bb56f3..e3b385e2c 100644 --- a/crates/engine/src/cmd/face_recognize.rs +++ b/crates/engine/src/cmd/face_recognize.rs @@ -176,6 +176,11 @@ fn rename_person(s: &mut Session, p: &Value) -> Result { if s.filter.person.as_deref().is_some_and(|n| n.trim().to_lowercase() == from_key) { s.filter.person = Some(to.clone()); } + if let Some(f) = s.shelved_filter.as_mut() + && f.person.as_deref().is_some_and(|n| n.trim().to_lowercase() == from_key) + { + f.person = Some(to.clone()); + } Ok(json!({"faces": faces, "photos": photos, "from": from, "to": to, "merged": other})) } diff --git a/crates/engine/src/cmd/keywords.rs b/crates/engine/src/cmd/keywords.rs index fe09a92f9..0f3b8827a 100644 --- a/crates/engine/src/cmd/keywords.rs +++ b/crates/engine/src/cmd/keywords.rs @@ -32,6 +32,12 @@ fn commit_keywords(s: &mut Session, label: &str, op: lightcraft_catalog::Op, fol if let Some(k) = s.filter.keyword.clone() { s.filter.keyword = follow(&k); } + // a filter put aside by Enable Filters follows the keyword too + if let Some(f) = s.shelved_filter.as_mut() + && let Some(k) = f.keyword.clone() + { + f.keyword = follow(&k); + } Ok(json!({"changed": photos, "listed": listed})) } diff --git a/crates/engine/src/cmd/library/tests_enable_filters.rs b/crates/engine/src/cmd/library/tests_enable_filters.rs index 7ec0f2a39..9eb3c1ad0 100644 --- a/crates/engine/src/cmd/library/tests_enable_filters.rs +++ b/crates/engine/src/cmd/library/tests_enable_filters.rs @@ -136,3 +136,19 @@ fn removing_the_only_criterion_put_aside_leaves_filtering_on() { s.execute("library.removeFolder", &json!({"path": "/lightcraft-enable-filters"})).unwrap(); assert!(s.shelved_filter.is_none(), "an empty filter is not left put aside"); } + +#[test] +fn a_keyword_renamed_while_off_is_followed_by_the_filter_put_aside() { + let mut s = session().unwrap(); + let mut photo = Photo::new(PhotoId(5), Source::File { path: "/lightcraft-enable-filters/5.jpg".into() }, "5.jpg", "JPEG", 40, 30, "2026-10-23"); + photo.meta.keywords = vec!["cat".into()]; + s.catalog.apply(Op::AddPhoto { photo: Box::new(photo) }).unwrap(); + s.filter.keyword = Some("cat".into()); + assert_eq!(s.visible().len(), 1); + s.execute("library.toggleFilters", &json!({})).unwrap(); + s.execute("keyword.rename", &json!({"from": "cat", "to": "dog"})).unwrap(); + let r = s.execute("library.toggleFilters", &json!({})).unwrap(); + assert_eq!(r["enabled"], json!(true)); + assert_eq!(s.filter.keyword.as_deref(), Some("dog"), "the put-aside filter follows the rename"); + assert_eq!(s.visible().len(), 1); +}