From e3102ab2e52352765114eb39913404984f02f26c Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Sat, 24 Oct 2026 19:10:18 -0700 Subject: [PATCH 1/2] KEYC-METADATA: Cmd+Shift+K / Ctrl+Shift+K edits the keyword picked in the Keyword List Lightroom Classic's keyboard shortcut page lists Command+Shift+K (Ctrl+Shift+K on Windows) for Edit keywords, and its keyword help page describes editing a keyword in Edit Keyword Tag from the Keyword List. LightCraft had no key for it. Edit Keywords (panel.editKeywords, Cmd+Shift+K) shows the Keywords panel, opens its Keyword List when it is folded shut, and opens the keyword picked in the list in Edit Keyword Tag. With no keyword picked, or with the picked keyword deleted, it opens no dialog. The command label has a row in every catalog. The KEYC-METADATA row, docs/ui-parity.md and docs/library-shortcuts.md list the key. The KEYC-METADATA row also lists the existing Alt+1 to Alt+9 keyword set keys in place of "no keyword sets". --- crates/ui-egui/locales/cs.json | 1 + crates/ui-egui/locales/de.json | 1 + crates/ui-egui/locales/es.json | 1 + crates/ui-egui/locales/fi.json | 1 + crates/ui-egui/locales/fr.json | 1 + crates/ui-egui/locales/id.json | 1 + crates/ui-egui/locales/it.json | 1 + crates/ui-egui/locales/ja.json | 1 + crates/ui-egui/locales/ka.json | 1 + crates/ui-egui/locales/pl.json | 1 + crates/ui-egui/locales/pt-br.json | 1 + crates/ui-egui/locales/ru.json | 1 + crates/ui-egui/locales/sv.json | 1 + crates/ui-egui/locales/uk.json | 1 + crates/ui-egui/locales/vi.json | 1 + crates/ui-egui/locales/zh-hans.json | 1 + crates/ui-egui/locales/zh-hant.json | 1 + crates/ui-egui/src/menus.rs | 14 ++++++++ crates/ui-egui/src/tests_keyword_list.rs | 42 ++++++++++++++++++++++++ docs/library-shortcuts.md | 1 + docs/parity-checklist.md | 5 +-- docs/ui-parity.md | 5 ++- 22 files changed, 81 insertions(+), 3 deletions(-) diff --git a/crates/ui-egui/locales/cs.json b/crates/ui-egui/locales/cs.json index 16095a43e..a09bc9bb6 100644 --- a/crates/ui-egui/locales/cs.json +++ b/crates/ui-egui/locales/cs.json @@ -623,6 +623,7 @@ "Edit Date": "Datum úprav", "Edit Info": "Upravit informace", "Edit Keyword Set": "Upravit sadu klíčových slov", + "Edit Keywords": "Upravit klíčová slova", "Edit Keyword Tag": "Upravit značku klíčového slova", "Edit Keyword Tag…": "Upravit značku klíčového slova…", "Edit Label Names": "Upravit názvy štítků", diff --git a/crates/ui-egui/locales/de.json b/crates/ui-egui/locales/de.json index 679e3b00f..b20fbd131 100644 --- a/crates/ui-egui/locales/de.json +++ b/crates/ui-egui/locales/de.json @@ -2042,6 +2042,7 @@ "& Containing": "& Übergeordnete", "Edit Set…": "Satz bearbeiten…", "Edit Keyword Set": "Stichwortsatz bearbeiten", + "Edit Keywords": "Stichwörter bearbeiten", "Keywords (⌥1–⌥9)": "Stichwörter (⌥1–⌥9)", "A keyword set needs a name": "Ein Stichwortsatz braucht einen Namen", "Save as a new set": "Als neuen Satz speichern", diff --git a/crates/ui-egui/locales/es.json b/crates/ui-egui/locales/es.json index af22570ef..9046dd22c 100644 --- a/crates/ui-egui/locales/es.json +++ b/crates/ui-egui/locales/es.json @@ -2070,6 +2070,7 @@ "& Containing": "y contenedoras", "Edit Set…": "Editar conjunto…", "Edit Keyword Set": "Editar conjunto de palabras clave", + "Edit Keywords": "Editar palabras clave", "Keywords (⌥1–⌥9)": "Palabras clave (⌥1–⌥9)", "A keyword set needs a name": "Un conjunto de palabras clave necesita un nombre", "Save as a new set": "Guardar como conjunto nuevo", diff --git a/crates/ui-egui/locales/fi.json b/crates/ui-egui/locales/fi.json index 6456a6624..5ce0e8fdf 100644 --- a/crates/ui-egui/locales/fi.json +++ b/crates/ui-egui/locales/fi.json @@ -586,6 +586,7 @@ "Edit Info": "Muokkaa tietoja", "Edit Keyword": "Muokkaa avainsanaa", "Edit Keyword Set": "Muokkaa avainsanajoukkoa", + "Edit Keywords": "Muokkaa avainsanoja", "Edit Keyword Tag": "Muokkaa avainsanatunnistetta", "Edit Keyword Tag…": "Muokkaa avainsanatunnistetta…", "Edit Label Names": "Muokkaa merkintöjen nimiä", diff --git a/crates/ui-egui/locales/fr.json b/crates/ui-egui/locales/fr.json index 33a892ecf..52eb4bc0d 100644 --- a/crates/ui-egui/locales/fr.json +++ b/crates/ui-egui/locales/fr.json @@ -1878,6 +1878,7 @@ "& Containing": "et parents", "Edit Set…": "Modifier le jeu…", "Edit Keyword Set": "Modifier le jeu de mots-clés", + "Edit Keywords": "Modifier les mots-clés", "Keywords (⌥1–⌥9)": "Mots-clés (⌥1–⌥9)", "A keyword set needs a name": "Un jeu de mots-clés doit avoir un nom", "Save as a new set": "Enregistrer comme nouveau jeu", diff --git a/crates/ui-egui/locales/id.json b/crates/ui-egui/locales/id.json index abb6a732e..46bbf0c5e 100644 --- a/crates/ui-egui/locales/id.json +++ b/crates/ui-egui/locales/id.json @@ -590,6 +590,7 @@ "Edit Info": "Edit info", "Edit Keyword": "Edit kata kunci", "Edit Keyword Set": "Edit set kata kunci", + "Edit Keywords": "Edit kata kunci", "Edit Keyword Tag": "Edit tag kata kunci", "Edit Keyword Tag…": "Edit tag kata kunci…", "Edit Label Names": "Edit nama label", diff --git a/crates/ui-egui/locales/it.json b/crates/ui-egui/locales/it.json index a75448fbb..e8935834e 100644 --- a/crates/ui-egui/locales/it.json +++ b/crates/ui-egui/locales/it.json @@ -1998,6 +1998,7 @@ "& Containing": "e contenenti", "Edit Set…": "Modifica set…", "Edit Keyword Set": "Modifica set di parole chiave", + "Edit Keywords": "Modifica parole chiave", "Keywords (⌥1–⌥9)": "Parole chiave (⌥1-⌥9)", "A keyword set needs a name": "Un set di parole chiave ha bisogno di un nome", "Save as a new set": "Salva come nuovo set", diff --git a/crates/ui-egui/locales/ja.json b/crates/ui-egui/locales/ja.json index c3f11e6e5..ee41e1a30 100644 --- a/crates/ui-egui/locales/ja.json +++ b/crates/ui-egui/locales/ja.json @@ -1890,6 +1890,7 @@ "& Containing": "と親キーワード", "Edit Set…": "セットを編集…", "Edit Keyword Set": "キーワードセットを編集", + "Edit Keywords": "キーワードを編集", "Keywords (⌥1–⌥9)": "キーワード (⌥1–⌥9)", "A keyword set needs a name": "キーワードセットには名前が必要です", "Save as a new set": "新しいセットとして保存", diff --git a/crates/ui-egui/locales/ka.json b/crates/ui-egui/locales/ka.json index 13fd48d3c..c81ebc7fc 100644 --- a/crates/ui-egui/locales/ka.json +++ b/crates/ui-egui/locales/ka.json @@ -655,6 +655,7 @@ "Edit Info": "ინფორმაციის რედაქტირება", "Edit Keyword": "საკვანძო სიტყვის რედაქტირება", "Edit Keyword Set": "საკვანძო სიტყვების ნაკრების რედაქტირება", + "Edit Keywords": "საკვანძო სიტყვების რედაქტირება", "Edit Keyword Tag": "საკვანძო სიტყვის რედაქტირება", "Edit Keyword Tag…": "საკვანძო სიტყვის რედაქტირება…", "Edit Label Names": "იარლიყების სახელების რედაქტირება", diff --git a/crates/ui-egui/locales/pl.json b/crates/ui-egui/locales/pl.json index f03a81762..1cd5dc9f5 100644 --- a/crates/ui-egui/locales/pl.json +++ b/crates/ui-egui/locales/pl.json @@ -1830,6 +1830,7 @@ "& Containing": "i nadrzędne", "Edit Set…": "Edytuj zestaw…", "Edit Keyword Set": "Edytuj zestaw słów kluczowych", + "Edit Keywords": "Edytuj słowa kluczowe", "Keywords (⌥1–⌥9)": "Słowa kluczowe (⌥1–⌥9)", "A keyword set needs a name": "Zestaw słów kluczowych musi mieć nazwę", "Save as a new set": "Zapisz jako nowy zestaw", diff --git a/crates/ui-egui/locales/pt-br.json b/crates/ui-egui/locales/pt-br.json index 88e955d26..701c94fc2 100644 --- a/crates/ui-egui/locales/pt-br.json +++ b/crates/ui-egui/locales/pt-br.json @@ -1888,6 +1888,7 @@ "& Containing": "e as que as contêm", "Edit Set…": "Editar conjunto…", "Edit Keyword Set": "Editar conjunto de palavras-chave", + "Edit Keywords": "Editar palavras-chave", "Keywords (⌥1–⌥9)": "Palavras-chave (⌥1–⌥9)", "A keyword set needs a name": "Um conjunto de palavras-chave precisa de um nome", "Save as a new set": "Salvar como novo conjunto", diff --git a/crates/ui-egui/locales/ru.json b/crates/ui-egui/locales/ru.json index 5c8816c5e..dd6c5d0f3 100644 --- a/crates/ui-egui/locales/ru.json +++ b/crates/ui-egui/locales/ru.json @@ -1598,6 +1598,7 @@ "& Containing": "и родительские", "Edit Set…": "Изменить набор…", "Edit Keyword Set": "Изменить набор ключевых слов", + "Edit Keywords": "Изменить ключевые слова", "Keywords (⌥1–⌥9)": "Ключевые слова (⌥1–⌥9)", "A keyword set needs a name": "Набору ключевых слов нужно название", "Set name": "Название набора", diff --git a/crates/ui-egui/locales/sv.json b/crates/ui-egui/locales/sv.json index d9af4b8e8..77e8f0942 100644 --- a/crates/ui-egui/locales/sv.json +++ b/crates/ui-egui/locales/sv.json @@ -643,6 +643,7 @@ "Edit Info": "Redigera info", "Edit Keyword": "Redigera nyckelord", "Edit Keyword Set": "Redigera nyckelordsuppsättning", + "Edit Keywords": "Redigera nyckelord", "Edit Keyword Tag": "Redigera nyckelordstagg", "Edit Keyword Tag…": "Redigera nyckelordstagg…", "Edit Label Names": "Redigera etikettnamn", diff --git a/crates/ui-egui/locales/uk.json b/crates/ui-egui/locales/uk.json index 39090c0b3..7def2893f 100644 --- a/crates/ui-egui/locales/uk.json +++ b/crates/ui-egui/locales/uk.json @@ -2072,6 +2072,7 @@ "& Containing": "і батьківські", "Edit Set…": "Редагувати набір…", "Edit Keyword Set": "Редагувати набір ключових слів", + "Edit Keywords": "Редагувати ключові слова", "Keywords (⌥1–⌥9)": "Ключові слова (⌥1–⌥9)", "A keyword set needs a name": "Набір ключових слів потребує назви", "Save as a new set": "Зберегти як новий набір", diff --git a/crates/ui-egui/locales/vi.json b/crates/ui-egui/locales/vi.json index 98adc91b8..3186f6fb6 100644 --- a/crates/ui-egui/locales/vi.json +++ b/crates/ui-egui/locales/vi.json @@ -545,6 +545,7 @@ "Edit Info": "Thông tin chỉnh sửa", "Edit Keyword": "Sửa từ khóa", "Edit Keyword Set": "Sửa bộ từ khóa", + "Edit Keywords": "Sửa từ khóa", "Edit Keyword Tag": "Sửa thẻ từ khóa", "Edit Keyword Tag…": "Sửa thẻ từ khóa…", "Edit Label Names": "Sửa tên nhãn", diff --git a/crates/ui-egui/locales/zh-hans.json b/crates/ui-egui/locales/zh-hans.json index 73caf6407..031e9c9ec 100644 --- a/crates/ui-egui/locales/zh-hans.json +++ b/crates/ui-egui/locales/zh-hans.json @@ -1882,6 +1882,7 @@ "& Containing": "及上级", "Edit Set…": "编辑关键字集…", "Edit Keyword Set": "编辑关键字集", + "Edit Keywords": "编辑关键字", "Keywords (⌥1–⌥9)": "关键字 (⌥1–⌥9)", "A keyword set needs a name": "关键字集需要名称", "Save as a new set": "另存为新关键字集", diff --git a/crates/ui-egui/locales/zh-hant.json b/crates/ui-egui/locales/zh-hant.json index 72cc3ab7f..cf16c73f5 100644 --- a/crates/ui-egui/locales/zh-hant.json +++ b/crates/ui-egui/locales/zh-hant.json @@ -1888,6 +1888,7 @@ "& Containing": "及上層", "Edit Set…": "編輯關鍵字集…", "Edit Keyword Set": "編輯關鍵字集", + "Edit Keywords": "編輯關鍵字", "Keywords (⌥1–⌥9)": "關鍵字 (⌥1–⌥9)", "A keyword set needs a name": "關鍵字集需要名稱", "Save as a new set": "另存為新關鍵字集", diff --git a/crates/ui-egui/src/menus.rs b/crates/ui-egui/src/menus.rs index a162e6ca6..c12961f63 100644 --- a/crates/ui-egui/src/menus.rs +++ b/crates/ui-egui/src/menus.rs @@ -159,6 +159,8 @@ pub const UI_COMMANDS: &[UiCommand] = &[ ("panel.info", "Info", Some("I"), "Window"), ("panel.keywords", "Keywords", Some("K"), "Window"), ("panel.versions", "Versions", Some("Shift+V"), "Window"), + // Edit Keyword Tag for the keyword picked in the Keyword List + ("panel.editKeywords", "Edit Keywords", Some("Cmd+Shift+K"), ""), ("panel.activity", "History", None, "Window"), ("panel.close", "Close Panel", None, ""), // {solo?}: true opens just that section (⌥-click on its header); otherwise opens or closes it @@ -1246,6 +1248,18 @@ pub fn run_ui_command(app: &mut LightcraftApp, id: &str, p: &Value) -> Option { + // the Keywords panel with its Keyword List open; the keyword picked there opens in + // Edit Keyword Tag (with none picked, no dialog opens) + if app.ui.right != RightPanel::Keywords { + panel(app, &ctx, RightPanel::Keywords, "Keywords"); + } + app.ui.folded_sections.retain(|s| s != "keywordList"); + if let Some(k) = app.ui.keyword_list_selected.clone().filter(|k| app.session.catalog.has_keyword(k)) { + app.ui.dialog = Some(crate::panels::keyword_list::edit_dialog(app, &k)); + } + Ok(Value::Null) + } "panel.activity" => { panel(app, &ctx, RightPanel::Activity, "History"); Ok(Value::Null) diff --git a/crates/ui-egui/src/tests_keyword_list.rs b/crates/ui-egui/src/tests_keyword_list.rs index bdeaef0dc..c31324968 100644 --- a/crates/ui-egui/src/tests_keyword_list.rs +++ b/crates/ui-egui/src/tests_keyword_list.rs @@ -154,6 +154,48 @@ fn double_clicking_a_keyword_edits_it() { assert_eq!(h.app.session.undo.len(), undo + 1, "one undo step"); } +/// ⌘⇧K (Ctrl+Shift+K) is Edit Keywords: it shows the Keywords panel with its Keyword List open, +/// and the keyword picked there opens in Edit Keyword Tag. +#[test] +fn cmd_shift_k_edits_the_picked_keyword() { + let (mut h, _) = keywords_panel(); + run(&mut h, "keyword.create", json!({"name": "Weddings", "synonyms": ["marriage"]})); + find(&mut h, "wedd"); + ask(&mut h, "ui.clickWidget", json!({"id": "keywordRow:Weddings"})); + ask(&mut h, "ui.clickWidget", json!({"id": "section:keywordList"})); + assert!(h.app.ui.section_folded("keywordList")); + // (K closes the panel; ui.set would reset the pick, which is not saved) + ask(&mut h, "ui.key", json!({"key": "K"})); + assert_eq!(h.app.ui.right, crate::state::RightPanel::None, "the panel is closed"); + ask(&mut h, "ui.key", json!({"key": "K", "cmd": true, "shift": true})); + assert_eq!(h.app.ui.right, crate::state::RightPanel::Keywords, "the Keywords panel"); + assert!(!h.app.ui.section_folded("keywordList"), "the Keyword List is open"); + let Some(crate::state::Dialog::KeywordTag { editing, name, synonyms, .. }) = h.app.ui.dialog.clone() else { panic!("{:?}", h.app.ui.dialog) }; + assert_eq!((editing.as_deref(), name.as_str(), synonyms.as_str()), (Some("Weddings"), "Weddings", "marriage")); + ask(&mut h, "ui.dialog.cancel", json!({})); + ask(&mut h, "ui.key", json!({"key": "K", "cmd": true, "shift": true})); + assert_eq!(h.app.ui.right, crate::state::RightPanel::Keywords, "pressed again: the panel stays"); + assert!(matches!(&h.app.ui.dialog, Some(crate::state::Dialog::KeywordTag { editing: Some(k), .. }) if k == "Weddings"), "{:?}", h.app.ui.dialog); +} + +/// With no keyword picked in the Keyword List, or with the picked one deleted, ⌘⇧K shows the +/// Keywords panel and its list and opens no dialog. +#[test] +fn cmd_shift_k_without_a_picked_keyword_opens_no_dialog() { + let (mut h, _) = keywords_panel(); + ask(&mut h, "ui.set", json!({"right": "none"})); + ask(&mut h, "ui.key", json!({"key": "K", "cmd": true, "shift": true})); + assert_eq!(h.app.ui.right, crate::state::RightPanel::Keywords, "the Keywords panel"); + assert!(has(&h, "field:keywordFilter"), "the Keyword List is shown"); + assert_eq!(h.app.ui.dialog, None, "nothing picked"); + run(&mut h, "keyword.create", json!({"name": "Weddings"})); + find(&mut h, "wedd"); + ask(&mut h, "ui.clickWidget", json!({"id": "keywordRow:Weddings"})); + run(&mut h, "keyword.delete", json!({"keyword": "Weddings"})); + ask(&mut h, "ui.key", json!({"key": "K", "cmd": true, "shift": true})); + assert_eq!(h.app.ui.dialog, None, "the picked keyword is gone"); +} + /// The − button deletes the picked keyword after asking how many photos lose it; Cancel keeps it, and undo /// brings a deleted one back. #[test] diff --git a/docs/library-shortcuts.md b/docs/library-shortcuts.md index 81bc2cede..5066b449f 100644 --- a/docs/library-shortcuts.md +++ b/docs/library-shortcuts.md @@ -6,6 +6,7 @@ Select photos in Photo Grid or Square Grid, then use: |---|---| | `Enter` | Open the active photo in Detail from Photo Grid or Square Grid | | `Cmd+Shift+D` (macOS) / `Ctrl+Shift+D` (Windows/Linux) | Select only the active photo | +| ⌘⇧K / Ctrl+Shift+K | Edit Keywords: show the Keywords panel and open the keyword picked in its Keyword List in Edit Keyword Tag | | `0` | Clear the star rating | | `1`–`5` | Set the star rating | | `[` / `]` | Decrease / increase each selected photo's rating by one, within 0–5 | diff --git a/docs/parity-checklist.md b/docs/parity-checklist.md index d059494ce..e56ef8e15 100644 --- a/docs/parity-checklist.md +++ b/docs/parity-checklist.md @@ -1,6 +1,6 @@ # Lightroom parity checklist -> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-24 · **Change:** minor (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-24 · **Change:** minor (KEYC-METADATA: ⌘⇧K / Ctrl+Shift+K Edit 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 @@ -782,7 +782,7 @@ From `08-lightroom-classic-extras.md` (Classic-only features) and part 2 of `06- | KEYC-COMPARE | Classic grid/compare keys (Z, Home/End, =/−, ⌘⇧D, S…) | P2 | 🟡 | `cmd:view.zoomToggle`, `cmd:view.thumbSizeUp`, `cmd:view.thumbSizeDown`, `crates/ui-egui/src/tests_zoom_keys.rs`, `cmd:library.select`, `crates/ui-egui/src/shortcuts.rs`, `crates/ui-egui/src/tests_keymap.rs` (`home_and_end_select_the_ends_of_the_grid`, `shift_arrows_extend_the_grid_selection`) | Z toggles zoom; `=` / `-` (and `+`) step the grid thumbnail size by 30 pt within the bottom-bar slider's 90–480 range in Photo Grid, Square Grid and People (nothing in Detail, Compare, Survey or Reference; a text field keeps the keys; ⌘= / ⌘− still zoom); Home / End make the first / last photo of the view the active, selected one and ⇧← / ⇧→ add the previous / next photo to the selection (the added photo becomes active; nothing is ever removed; at either end nothing happens), in Photo Grid and Square Grid only: they wait in the other views, in the full-screen preview, while a text field or a dialog has the keyboard, and when the user gave the key to a command; no compare, stacks 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`, `cmd:photo.cycleFlag`, `cmd:photo.increaseFlag`, `cmd:photo.decreaseFlag`, `cmd:library.toggleFilters` | 0–5 / Shift+0–5 and 6–9 / Shift+6–9; P/X/U, Shift+X/U and grid Shift+P work; ` (backtick) cycles each selected photo's flag unflagged → pick → reject → unflagged and ⌘↑ / ⌘↓ (Ctrl on Windows/Linux) raise / lower it (reject < unflagged < pick, clamped); one undo step, Compare/Survey act on the active photo, Auto Advance follows; tests cover batch/undo, active Compare candidate and text focus; `\` shows / hides the Filter Bar in the grids (Show Original in Detail); ⌘L (Ctrl+L) Enable Filters puts the filter aside and brings it back as it was. Missing: Shift+P outside grids (Presets) | | 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-METADATA | Classic keyword/metadata keys (⌘K, ⌘S, ⌘⌥⇧C/V…) | P2 | 🟡 | `cmd:photo.saveMetadataToFile`, `cmd:panel.editKeywords`, `cmd:keyword.toggleFromSet` | ⌘S saves metadata; ⌘⇧K (Ctrl+Shift+K) Edit Keywords opens the keyword picked in the Keyword List in Edit Keyword Tag; ⌥1–⌥9 toggle the current keyword set's keywords. Missing: 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 | | KEYC-MODULE-OUTPUT | Book / slideshow / print / map / web keys | P2 | ⬜ | | modules not implemented | | KEYC-HELP | Classic help keys (⌘/, F1) | P2 | 🟡 | `cmd:app.shortcuts` | ⌘/ only | @@ -799,6 +799,7 @@ Latin stays upright. Advanced Japanese composition remains open. Regression cove | Date | Change | Summary | |---|---|---| +| 2026-10-24 | minor | KEYC-METADATA: ⌘⇧K / Ctrl+Shift+K (`panel.editKeywords`, Edit Keywords) shows the Keywords panel with its Keyword List open and opens the picked keyword in Edit Keyword Tag; status counts unchanged (KEYC-METADATA stays 🟡) | | 2026-10-24 | patch | LR-LIB-RATING, LR-LIB-DELETE: a slider drag still open when a rating, flag or label takes the photo out of the view, or when the photo is deleted, ends as its own undo step before the selection moves on, as moving to the next photo does; LR-BEHAV-LOCALIZE lists all 18 languages; status unchanged | | 2026-10-24 | patch | Integration batch 43: Integrated main 98702b5c, preserving the new fail-closed GPS metadata check for Original export. Watermark geometry/orientation and HDR JPEG size-limit fixes remain in their shared helpers; no feature-count changes. | | 2026-10-23 | patch | Export integration: Integrated main 5ca49b3e, retaining Width & Height resizing, export format/bit-depth validation and batch panic cancellation alongside the existing watermark and HDR JPEG size-limit fixes. No feature-count changes. | diff --git a/docs/ui-parity.md b/docs/ui-parity.md index 16665f3d4..586d787f6 100644 --- a/docs/ui-parity.md +++ b/docs/ui-parity.md @@ -1,6 +1,6 @@ # UI parity -> **Last reviewed:** 2026-10-11 · **Last updated:** 2026-10-23 · **Change:** minor (KEYC-RATING: ⌘L Enable Filters; context menus: a right-click elsewhere on the same widget moves its open menu instead of flashing it, issue #1877; Reference Escape, catalog-local pins and deleted-photo handling verified on Windows; KEYC-RATING: `\` shows / hides the Filter Bar in the grids; earlier: zoom row soft proofing sharp at 1:1, canvas tools by keyboard, focus-order test, ~50 unnamed hand-drawn widgets named) · **Target:** Adobe Lightroom Classic 15.5 / Lightroom 9.6 +> **Last reviewed:** 2026-10-11 · **Last updated:** 2026-10-24 · **Change:** minor (KEYC-METADATA: ⌘⇧K Edit Keywords; KEYC-RATING: ⌘L Enable Filters; context menus: a right-click elsewhere on the same widget moves its open menu instead of flashing it, issue #1877; Reference Escape, catalog-local pins and deleted-photo handling verified on Windows; KEYC-RATING: `\` shows / hides the Filter Bar in the grids; earlier: zoom row soft proofing sharp at 1:1, canvas tools by keyboard, focus-order test, ~50 unnamed hand-drawn widgets named) · **Target:** Adobe Lightroom Classic 15.5 / Lightroom 9.6 How LightCraft's interaction compares with Lightroom's: views, panels, tools and handles, modifier keys, shortcuts, zoom, context menus and feel. Rows for individual menu items and shortcuts live in @@ -68,6 +68,8 @@ under the UI command that wraps it (a no-op error); the UI command now wins. **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. F6, F7 and F8 show / hide the filmstrip, the left panel and the right panel (F8 brings back the panel it hid; `view.rightPanel`). `` ` `` cycles the flag (unflagged → pick → reject → unflagged) and ⌘↑ / ⌘↓ raise / lower it; the native menu leaves ⌘↑ / ⌘↓ to egui so Compare/Survey act on the active photo. `\` shows / hides the Filter Bar in Photo Grid / Square Grid and keeps Show Original in Detail. ⌘L / Ctrl+L (Enable Filters) puts the filter aside and shows every photo, and the next press brings back the same filter, stars, flag and text together; setting or clearing a filter while it is off drops the one put aside (not documented by Adobe, our choice). +**Edit Keywords key (KEYC-METADATA):** `⌘⇧K` (`Ctrl+Shift+K` on Windows and Linux) is Edit Keywords (`panel.editKeywords`): it shows the Keywords panel, opens its Keyword List when that is folded shut, and opens the keyword picked in the list in Edit Keyword Tag, as the keyword's Edit Keyword Tag… menu item does. With no keyword picked, or with the picked keyword deleted, it opens no dialog. The tests are in `crates/ui-egui/src/tests_keyword_list.rs`. + **Grid thumbnail size (KEYC-COMPARE):** Classic's `=` and `-` increase and decrease the Grid thumbnail size (same keys on macOS and Windows; `+` also works for the larger size here, as Shift+`=` or the keypad). They step the bottom-bar slider by 30 pt inside its 90–480 pt range, in Photo Grid, Square Grid and People only; Detail, Compare, Survey and Reference ignore them, and ⌘= / ⌘− stay Zoom In / Out. See [library shortcuts](library-shortcuts.md). **Library navigation keys (KEYC-COMPARE):** in Photo Grid and Square Grid, `Home` / `End` make the first / last photo of the view the active, selected one, and `Shift+Left` / `Shift+Right` add the previous / next photo of the view to the selection (the added photo becomes active; the keys never remove a photo from the selection, and do nothing at either end of the view). They wait in the other views, in the full-screen preview, while a text field or a dialog has the keyboard, and when you gave the key to a command; they are listed under Fixed keys in Help → Keyboard Shortcuts. `Cmd+O` (`Ctrl+O` on Windows and Linux) runs File → Open Library… ; View → Visualize HDR has no default key (`Shift+H` cycles the Tool Overlay, issue #224; `Cmd+Shift+H` stays the Histogram). Covered by `crates/ui-egui/src/tests_keymap.rs`; the macOS native menu path for `Cmd+O` is not exercised by the headless tests. @@ -148,6 +150,7 @@ establish OS keyboard delivery or cross-platform acceptance. | Date | Change | Summary | |---|---|---| +| 2026-10-24 | minor | KEYC-METADATA: ⌘⇧K / Ctrl+Shift+K (Edit Keywords) opens the keyword picked in the Keyword List in Edit Keyword Tag | | 2026-10-15 | patch | Context menus: a right-click elsewhere on the same widget while its menu is open moves the menu to the pointer; before it flashed at the new spot and closed (issue #1877); every site uses `widgets::context_menu`; 70% unchanged | | 2026-10-23 | patch | KEYC-RATING: ⌘L / Ctrl+L (Library ▸ Enable Filters) switches the filter off and on, restoring it as it was | | 2026-10-15 | patch | KEYC-RATING: `\` shows / hides the Filter Bar in Photo Grid and Square Grid (Show Original in Detail); ⌘L is the filter-bar key left | From 71a5fbc7c30a990efd8909e8e2c545c654735f0a Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Wed, 28 Oct 2026 03:49:58 -0700 Subject: [PATCH 2/2] Keywords: opening another library clears the keyword picked in the Keyword List A keyword picked in one library stayed picked after another library opened, so Cmd+Shift+K edited a keyword with the same path in the new library without a new pick. --- .../ui-egui/src/dialogs/settings/libraries.rs | 2 ++ crates/ui-egui/src/tests/keyword_list.rs | 24 +++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/crates/ui-egui/src/dialogs/settings/libraries.rs b/crates/ui-egui/src/dialogs/settings/libraries.rs index 8e41e8dd4..c1379705a 100644 --- a/crates/ui-egui/src/dialogs/settings/libraries.rs +++ b/crates/ui-egui/src/dialogs/settings/libraries.rs @@ -87,6 +87,8 @@ fn switch_library(app: &mut LightcraftApp, path: &str) -> Result app.ui.reference = None; app.ui.compare = None; app.ui.brief_info = crate::state::BriefInfo::default(); + // a keyword picked in the Keyword List belongs to the library being left + app.ui.keyword_list_selected = None; if app.ui.view == crate::state::ViewMode::Reference { app.ui.view = crate::state::ViewMode::PhotoGrid; } diff --git a/crates/ui-egui/src/tests/keyword_list.rs b/crates/ui-egui/src/tests/keyword_list.rs index 023f9a786..5ada8c174 100644 --- a/crates/ui-egui/src/tests/keyword_list.rs +++ b/crates/ui-egui/src/tests/keyword_list.rs @@ -196,6 +196,30 @@ fn cmd_shift_k_without_a_picked_keyword_opens_no_dialog() { assert_eq!(h.app.ui.dialog, None, "the picked keyword is gone"); } +/// A keyword picked in one library is not picked in the next one opened, even when that library +/// has a keyword at the same path: ⌘⇧K there opens no dialog. +#[test] +fn opening_another_library_drops_the_picked_keyword() { + let dir = std::env::temp_dir().join(format!("lc-ui-keyword-list-switch-{}", std::process::id())); + let _ = std::fs::remove_dir_all(&dir); + let mut session = lightcraft_engine::Session::new().with_fs(); + session.open_library(dir.join("first"), false).unwrap(); + let app = LightcraftApp::new(session, Services { png: None, ..Default::default() }); + let mut h = Headless::new(app, [1400.0, 1000.0], 1.0); + ask(&mut h, "ui.set", json!({"view": "photoGrid", "right": "keywords"})); + run(&mut h, "keyword.create", json!({"name": "Weddings"})); + h.app.ui.keyword_list_selected = Some("Weddings".into()); + let r = h.app.run("app.openLibrary", json!({"path": dir.join("second").to_string_lossy()})); + assert!(r.is_ok(), "{r:?}"); + h.settle(SETTLE); + run(&mut h, "keyword.create", json!({"name": "Weddings"})); + ask(&mut h, "ui.key", json!({"key": "K", "cmd": true, "shift": true})); + assert_eq!(h.app.ui.keyword_list_selected, None, "nothing is picked in the second library"); + assert_eq!(h.app.ui.dialog, None, "no dialog opens"); + drop(h); + let _ = std::fs::remove_dir_all(&dir); +} + /// The − button deletes the picked keyword after asking how many photos lose it; Cancel keeps it, and undo /// brings a deleted one back. #[test]