mirror of
https://github.com/storytold/lightcraft.git
synced 2026-11-01 22:17:53 -06:00
Merge PR #2917
# Conflicts: # docs/parity-checklist.md # docs/whats-new.md
This commit is contained in:
+13
-11
@@ -20,7 +20,7 @@ pub enum RatingOp {
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
pub struct Filter {
|
||||
/// Free-text search: every token must match filename, title, caption, keywords, camera, lens,
|
||||
/// location or format. Tokens like `rating:3`, `flag:pick`, `iso:>800`, `camera:x2` are fielded.
|
||||
/// location or format. Tokens like `rating:3`, `rating:>=3`, `flag:pick`, `iso:>800`, `camera:x2` are fielded.
|
||||
pub text: String,
|
||||
pub rating: u8,
|
||||
pub rating_op: RatingOp,
|
||||
@@ -179,23 +179,25 @@ fn token_matches(p: &Photo, tok: &str) -> bool {
|
||||
// a token that starts with a quote is a phrase, even with a colon in it
|
||||
if let Some((field, quoted)) = t.split_once(':').filter(|_| !t.starts_with('"')) {
|
||||
let val = unquote(quoted);
|
||||
let num = |s: &str| -> Option<(char, f64)> {
|
||||
let (op, rest) = match s.chars().next()? {
|
||||
c @ ('>' | '<' | '=') => (c, &s[1..]),
|
||||
_ => ('=', s),
|
||||
};
|
||||
rest.parse().ok().map(|v| (op, v))
|
||||
// `>=` / `<=` before `>` / `<`: otherwise `rating:>=3` reads `=3` as the number and matches nothing
|
||||
let num = |s: &str| -> Option<(&str, f64)> {
|
||||
let op = [">=", "<=", ">", "<", "="].into_iter().find(|op| s.starts_with(op)).unwrap_or("");
|
||||
s.get(op.len()..)?.parse().ok().map(|v| (op, v))
|
||||
};
|
||||
let cmp = |x: f64, s: &str| match num(s) {
|
||||
Some(('>', v)) => x > v,
|
||||
Some(('<', v)) => x < v,
|
||||
Some((">", v)) => x > v,
|
||||
Some(("<", v)) => x < v,
|
||||
Some((">=", v)) => x >= v,
|
||||
Some(("<=", v)) => x <= v,
|
||||
Some((_, v)) => (x - v).abs() < 1e-9,
|
||||
None => false,
|
||||
};
|
||||
// Fields stored as `f32` compare at f32 precision: 2.8f32 widened to f64 is not 2.8.
|
||||
let cmp32 = |x: f32, s: &str| match num(s) {
|
||||
Some(('>', v)) => x > v as f32,
|
||||
Some(('<', v)) => x < v as f32,
|
||||
Some((">", v)) => x > v as f32,
|
||||
Some(("<", v)) => x < v as f32,
|
||||
Some((">=", v)) => x >= v as f32,
|
||||
Some(("<=", v)) => x <= v as f32,
|
||||
Some((_, v)) => x == v as f32,
|
||||
None => false,
|
||||
};
|
||||
|
||||
@@ -163,6 +163,42 @@ fn search_tokens_match_f32_aperture_and_focal() {
|
||||
assert!(q("f:>2.8").is_empty(), "f:>2.8");
|
||||
assert!(q("focal:<5.6").is_empty(), "focal:<5.6");
|
||||
assert_eq!(q("f:>2.7"), vec![a], "f:>2.7");
|
||||
assert_eq!(q("f:>=2.8"), vec![a], "f:>=2.8 counts the photo's own value");
|
||||
assert_eq!(q("focal:<=5.6"), vec![a], "focal:<=5.6");
|
||||
assert!(q("f:>=2.9").is_empty(), "f:>=2.9");
|
||||
assert!(q("focal:<=5.5").is_empty(), "focal:<=5.5");
|
||||
}
|
||||
|
||||
/// `>=` and `<=` in a fielded token are the inclusive comparisons. They used to read the `=` as
|
||||
/// part of the number, so `rating:>=3` or `iso:<=400` silently matched no photo at all.
|
||||
#[test]
|
||||
fn search_tokens_compare_inclusively() {
|
||||
let mut c = Catalog::new();
|
||||
let (two, three, four) = (
|
||||
photo(&mut c, "two.jpg", "2026-05-01T10:00:00"),
|
||||
photo(&mut c, "three.jpg", "2026-05-02T10:00:00"),
|
||||
photo(&mut c, "four.jpg", "2026-05-03T10:00:00"),
|
||||
);
|
||||
for (id, rating, iso) in [(two, 2, 200), (three, 3, 400), (four, 4, 800)] {
|
||||
c.apply(Op::SetRating { id, rating }).unwrap();
|
||||
let mut m = c.photo(id).unwrap().meta.clone();
|
||||
m.iso = Some(iso);
|
||||
c.apply(Op::SetMeta { id, meta: Box::new(m) }).unwrap();
|
||||
}
|
||||
let q = |t: &str| {
|
||||
let mut ids = c.query(&Filter { text: t.into(), ..Default::default() }, &Sort::default());
|
||||
ids.sort();
|
||||
ids
|
||||
};
|
||||
assert_eq!(q("rating:>=3"), vec![three, four]);
|
||||
assert_eq!(q("stars:<=3"), vec![two, three]);
|
||||
assert_eq!(q("iso:>=400"), vec![three, four]);
|
||||
assert_eq!(q("iso:<=400"), vec![two, three]);
|
||||
assert_eq!(q("rating:>3"), vec![four], "strict comparisons are unchanged");
|
||||
assert_eq!(q("rating:=3"), vec![three]);
|
||||
assert_eq!(q("rating:3"), vec![three]);
|
||||
assert!(q("rating:>=").is_empty(), "an operator without a number matches nothing");
|
||||
assert!(q("rating:=>3").is_empty(), "not an operator");
|
||||
}
|
||||
|
||||
/// A `captured` string that is not ASCII (the Lightroom importer copies `captureTime` into an
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Lightroom parity checklist
|
||||
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-27 · **Change:** minor (MENU-FILE-CLOSE, KEY-CLOSEWINDOW: the notes say Close Window quits the app, after the same check as Quit; MENU-EDIT-SELECTNONE, MENU-EDIT-PASTESELECTED: Edit lists Select Only Active Photo after Deselect All and Paste Settings from Previous after Paste Selected Settings…; on macOS no doubled separators are left where Settings… moved to the app menu; LR-BEHAV-TITLEBAR: a double-click follows the system setting, and a panel over the bar keeps the window still; KEYC-METADATA, LRC-LIB-KEYWORDS: ⌥0 / ⌥⇧0 go to the next / previous keyword set; LRC-DEV-HISTOGRAM-DRAG: the histogram's five tonal zones — hover for the value, drag to adjust the Basic control the press locked, double-click to reset; LR-VIEW-HISTOGRAM note updated; MENU-BAR: on macOS a menu item that yields its key to a focused text field or to shortcut recording really lets it go; LR-MERGE-HDR / LR-MERGE-PANO: Lanczos-3 warps; LR-IMP-LOCAL: browsing from the sidebar records the folder; LR-VIEW-ZOOM: only a full-size drag within 20 ms clears a slow-drag verdict; LR-IMP-ADD-DIALOG: hierarchical import album destination reconciled with main, #2362; KEY-SLIDER-NUDGE: vertical wheel nudges need a click or drag in the row; leaving it disarms them; LR-EDIT-OPTICS-PROFILE: Fujifilm nine-knot lens tables read at the plain radius; LR-VIEW-ZOOM: only a full-size drag within 20 ms clears a slow-drag verdict; LRC-LIB-LIBFOLDERS, LR-BEHAV-BG-TASKS: Synchronize Folder's scan shows its folder listing and stops it on ✕; LRC-LIB-LIBFOLDERS: Synchronize Folder says a folder is up to date; LR-LIB-STACK: filmstrip stack badge click; LRC-LIB-REFVIEW / CATALOG-SWITCH: Escape preserves edits; catalog-local pins; deleted and malformed references refused; LRC-LIB-COMPARE: the Info panel labels the active photo while culling, #2398; KEYC-RATING: `\` shows / hides the Filter Bar in the grids; LR-VIEW-PHOTOGRID: the grid reads only the photos an import batch or an edit touched, not the whole list; earlier: LRC-PRINT-PAGE: Print Page Options; LR-META-XMP: fewer false unimported-XMP warnings; KEYC-METADATA: ⌘K / Ctrl+K Add Keywords; LRC-DEV-HISTOGRAM-DRAG: the histogram's five tonal zones — hover for the value, drag to adjust the Basic control the press locked, double-click to reset; LR-VIEW-HISTOGRAM note updated; MENU-BAR: on macOS a menu item that yields its key to a focused text field or to shortcut recording really lets it go; LR-MERGE-HDR / LR-MERGE-PANO: Lanczos-3 warps; LR-IMP-LOCAL: browsing from the sidebar records the folder; LR-VIEW-ZOOM: only a full-size drag within 20 ms clears a slow-drag verdict; LR-IMP-ADD-DIALOG: hierarchical import album destination reconciled with main, #2362; KEY-SLIDER-NUDGE: vertical wheel nudges need a click or drag in the row; leaving it disarms them; LR-EDIT-OPTICS-PROFILE: Fujifilm nine-knot lens tables read at the plain radius; LR-VIEW-ZOOM: only a full-size drag within 20 ms clears a slow-drag verdict; LRC-LIB-LIBFOLDERS, LR-BEHAV-BG-TASKS: Synchronize Folder's scan shows its folder listing and stops it on ✕; LRC-LIB-LIBFOLDERS: Synchronize Folder says a folder is up to date; LR-LIB-STACK: filmstrip stack badge click; LRC-LIB-REFVIEW / CATALOG-SWITCH: Escape preserves edits; catalog-local pins; deleted and malformed references refused; LRC-LIB-COMPARE: the Info panel labels the active photo while culling, #2398; KEYC-RATING: `\` shows / hides the Filter Bar in the grids; LR-VIEW-PHOTOGRID: the grid reads only the photos an import batch or an edit touched, not the whole list; earlier: LRC-PRINT-PAGE: Print Page Options; LR-META-XMP: fewer false unimported-XMP warnings) · **Target:** Adobe Lightroom Classic 15.5 / Lightroom 9.6
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-27 · **Change:** minor (LR-FILT-SEARCH-META: `>=` / `<=` in fielded search tokens; MENU-FILE-CLOSE, KEY-CLOSEWINDOW: the notes say Close Window quits the app, after the same check as Quit; MENU-EDIT-SELECTNONE, MENU-EDIT-PASTESELECTED: Edit lists Select Only Active Photo after Deselect All and Paste Settings from Previous after Paste Selected Settings…; on macOS no doubled separators are left where Settings… moved to the app menu; LR-BEHAV-TITLEBAR: a double-click follows the system setting, and a panel over the bar keeps the window still; KEYC-METADATA, LRC-LIB-KEYWORDS: ⌥0 / ⌥⇧0 go to the next / previous keyword set; LRC-DEV-HISTOGRAM-DRAG: the histogram's five tonal zones — hover for the value, drag to adjust the Basic control the press locked, double-click to reset; LR-VIEW-HISTOGRAM note updated; MENU-BAR: on macOS a menu item that yields its key to a focused text field or to shortcut recording really lets it go; LR-MERGE-HDR / LR-MERGE-PANO: Lanczos-3 warps; LR-IMP-LOCAL: browsing from the sidebar records the folder; LR-VIEW-ZOOM: only a full-size drag within 20 ms clears a slow-drag verdict; LR-IMP-ADD-DIALOG: hierarchical import album destination reconciled with main, #2362; KEY-SLIDER-NUDGE: vertical wheel nudges need a click or drag in the row; leaving it disarms them; LR-EDIT-OPTICS-PROFILE: Fujifilm nine-knot lens tables read at the plain radius; LR-VIEW-ZOOM: only a full-size drag within 20 ms clears a slow-drag verdict; LRC-LIB-LIBFOLDERS, LR-BEHAV-BG-TASKS: Synchronize Folder's scan shows its folder listing and stops it on ✕; LRC-LIB-LIBFOLDERS: Synchronize Folder says a folder is up to date; LR-LIB-STACK: filmstrip stack badge click; LRC-LIB-REFVIEW / CATALOG-SWITCH: Escape preserves edits; catalog-local pins; deleted and malformed references refused; LRC-LIB-COMPARE: the Info panel labels the active photo while culling, #2398; KEYC-RATING: `\` shows / hides the Filter Bar in the grids; LR-VIEW-PHOTOGRID: the grid reads only the photos an import batch or an edit touched, not the whole list; earlier: LRC-PRINT-PAGE: Print Page Options; LR-META-XMP: fewer false unimported-XMP warnings; KEYC-METADATA: ⌘K / Ctrl+K Add Keywords; LRC-DEV-HISTOGRAM-DRAG: the histogram's five tonal zones — hover for the value, drag to adjust the Basic control the press locked, double-click to reset; LR-VIEW-HISTOGRAM note updated; MENU-BAR: on macOS a menu item that yields its key to a focused text field or to shortcut recording really lets it go; LR-MERGE-HDR / LR-MERGE-PANO: Lanczos-3 warps; LR-IMP-LOCAL: browsing from the sidebar records the folder; LR-VIEW-ZOOM: only a full-size drag within 20 ms clears a slow-drag verdict; LR-IMP-ADD-DIALOG: hierarchical import album destination reconciled with main, #2362; KEY-SLIDER-NUDGE: vertical wheel nudges need a click or drag in the row; leaving it disarms them; LR-EDIT-OPTICS-PROFILE: Fujifilm nine-knot lens tables read at the plain radius; LR-VIEW-ZOOM: only a full-size drag within 20 ms clears a slow-drag verdict; LRC-LIB-LIBFOLDERS, LR-BEHAV-BG-TASKS: Synchronize Folder's scan shows its folder listing and stops it on ✕; LRC-LIB-LIBFOLDERS: Synchronize Folder says a folder is up to date; LR-LIB-STACK: filmstrip stack badge click; LRC-LIB-REFVIEW / CATALOG-SWITCH: Escape preserves edits; catalog-local pins; deleted and malformed references refused; LRC-LIB-COMPARE: the Info panel labels the active photo while culling, #2398; KEYC-RATING: `\` shows / hides the Filter Bar in the grids; LR-VIEW-PHOTOGRID: the grid reads only the photos an import batch or an edit touched, not the whole list; earlier: LRC-PRINT-PAGE: Print Page Options; LR-META-XMP: fewer false unimported-XMP warnings) · **Target:** Adobe Lightroom Classic 15.5 / Lightroom 9.6
|
||||
|
||||
The measured, row-by-row checklist: one row per Lightroom feature, menu item and shortcut, what we
|
||||
have and what is missing. It answers *does the feature exist?* (feature breadth). Whether it is good
|
||||
@@ -146,7 +146,7 @@ differences and missing bindings are in [ui-parity.md](ui-parity.md#shortcuts).
|
||||
|
||||
| Id | Feature | Tier | Status | Evidence | Missing / notes |
|
||||
|---|---|---|---|---|---|
|
||||
| LR-FILT-SEARCH-META | Text search | P0 | ✅ | `cmd:library.filter` (`text`), `crates/ui-egui/src/panels/topbar.rs`, `crates/catalog/src/query.rs` | fielded tokens (`rating:3`, `iso:>800`, `camera:…`); plain words also match the people named on faces; double quotes keep several words in one token (`person:"Jane Doe"`, `"jane doe"`, issue #1449); no suggestions dropdown |
|
||||
| LR-FILT-SEARCH-META | Text search | P0 | ✅ | `cmd:library.filter` (`text`), `crates/ui-egui/src/panels/topbar.rs`, `crates/catalog/src/query.rs` | fielded tokens (`rating:3`, `iso:>800`, `rating:>=3` / `iso:<=400`, `camera:…`); plain words also match the people named on faces; double quotes keep several words in one token (`person:"Jane Doe"`, `"jane doe"`, issue #1449); no suggestions dropdown |
|
||||
| LR-FILT-SEARCH-AI | Natural-language search | P2 | ⬜ | | |
|
||||
| LR-FILT-RATING | Rating filter | P0 | ✅ | `cmd:library.filter` (`rating`, `ratingOp`), `crates/ui-egui/src/panels/filterbar.rs` | ≥ / = / ≤ stars in the filter bar (`cmd:view.filterBar`) |
|
||||
| LR-FILT-FLAG | Flag filter | P0 | ✅ | `cmd:library.filter` (`flag`), `crates/ui-egui/src/panels/filterbar.rs` | picked / rejected / unflagged (one at a time) |
|
||||
|
||||
@@ -5,6 +5,12 @@
|
||||
### The Edit menu groups Select Only Active Photo and Paste Settings from Previous with their kin
|
||||
- Select Only Active Photo now sits under Deselect All, and Paste Settings from Previous under Paste Selected Settings…, instead of both after Settings…, near the end of the Edit menu. On macOS, where Settings… lives in the application menu, the Edit menu no longer shows two separator lines in a row.
|
||||
|
||||
## 2026-10-25
|
||||
|
||||
### `>=` and `<=` work in the search box
|
||||
- Fielded search tokens now take inclusive comparisons: `rating:>=3`, `iso:<=400`, `f:>=2.8`, `focal:<=50`. Before,
|
||||
the `=` was read as part of the number, so such a search quietly showed no photos at all.
|
||||
|
||||
## 2026-10-24
|
||||
|
||||
### Relative-date filters and smart albums follow the clock
|
||||
|
||||
Reference in New Issue
Block a user