* LR-PREF-SHORTCUTS: the shortcuts panel's Fixed keys ignore keys saved for unknown commands
* KEYC-METADATA: an Add Keywords request the Keywords panel can't take in time expires
* LR-META-INFO: Target Photo saves an Info edit to the photo it shows
Preserve current main, adapt frame/input and IME handling, and keep library
check results stable across layout passes. Include the matching Nix font lock
and reviewed attribution changes.
The GPS box was an egui TextEdit: Esc left the field with the typed text
kept, and leaving the field saved it. It is now the app's TextField, as the
other Info fields are: Esc puts the text back and saves nothing, and the
field gets the Cut / Copy / Paste / Select All menu.
The text fields and GPS saved with no `ids`, so `photo.setMeta` wrote to
whatever was selected when the edit ended. A selection change during an
edit then gave the typed text to the new selection. Each edit now keeps
the selected photos from its start and saves to them.
The active photo can be one that is not selected (library.select with an
`active` outside `ids`). With Selected Photos on, the Info fields then
showed that photo's values and marked fields Mixed against it. The fields
now show the active photo's values only when it is selected, and else the
first selected photo's, and compare the selected photos with those.
With several photos selected, the Info panel offers Show Metadata For:
Target Photo (the default) shows the active photo's values as before,
and Selected Photos shows the values the selected photos share. A text
field or GPS where they differ is empty with a Mixed hint, and
Copyright Status shows Mixed. What is typed there is saved to every selected
photo, and a Mixed field typed in and left empty clears it on all of
them. A field left as it was saves nothing, even when the choice
changes during the edit. The choice is saved with the app settings, and
`view.infoSelectedPhotos {on?}` sets it for an agent.
View Options gets the Loupe View tab's General section with "Show message
when loading or rendering photos". It is on by default, so the loupe keeps
saying "Rendering…" as before. With it off, the loupe does not show the
"Rendering…" pill while an edit renders, or the "Rendering…" text over an
empty loupe; the "can't be opened" messages still show. The choice is saved
with the app settings, and `view.viewOptions {loupeMessages}` sets it for
an agent.
View Options had no Show Briefly When Photo Changes. A tick box under
Loupe Info 1 and one under Loupe Info 2 now choose the Info that shows
for 3 seconds each time the loupe changes to another photo while the
info overlay is off. Ticking one unticks the other. An overlay that is
on stays on and shows its own Info. view.viewOptions takes
briefly (off, basic or exposure), and the choice is saved with the app
settings; a settings file without it reads as off.
Conflicts:
- apps/lightcraft/src/native_menu.rs: upstream's item swapping (macOS keys
given up for real, Help menu not swapped, Settings / Quit tracked) with
KEY-GRID's keymap-aware rows. `Item.label` stays the text the row shows
(native_item_presentation), so `Item::native_label` reuses it instead of
formatting it again; Settings / Quit store theirs through
display_item_label, as upstream's native_label did. No early return after
a rebuild (upstream).
- crates/ui-egui/src/i18n.rs: upstream's tests (#2729: one test per check
across languages, gaps reported, never a failure) plus the batch's two new
ones (dropped-photos notice, Polish counts); production code as merged.
- grid.rs, keyword_list.rs, masking.rs: the translation tests #2729 removed
stay removed; the batch's new keyword and segmenter tests stay.
- cmd/library.rs, ui-egui/src/lib.rs: both test modules, each with its
#[cfg(test)].
- xtask/src/main.rs: #2302's test_selected and upstream's translation_gaps.
- docs: tracker / gap rows take both sides' edits, headers list both sides'
changes, history rows of both sides newest-first. What's New: one section
per date, newest first (the batch had 10-22 above 10-23, and second 10-21
and 10-19 sections); no line lost.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A message a catalog lacks, or translates as an empty string, shows in
English and fails nothing:
- build.rs no longer requires every formats catalog to carry the same
messages, and tr_format! gets a last arm that formats the English
source, so a message no catalog has builds.
- Coverage tests (every language translates X) and tests of one
language's exact wording are gone; the language-switching, layout,
date, placeholder and user-value tests read the catalog's own value
instead of a hard-coded translation.
- i18n::tests::translation_gaps_report lists, per language, the plain
and format messages it lacks and its untranslated What's New lines.
It always passes; cargo xtask ci
runs it from the test step's binary after the tests.
- catalogs_load checks that every catalog file is a JSON object of
strings with each key once; it.json named "Dimensions" twice.
- settings::window draws the tab bar, which stays put, above the page of the open tab, which
scrolls: 560 pt wide and up to 640 pt tall on every tab (less in a smaller app window), each
page with a scroll position of its own and a handle that shows without the pointer on it.
- A row's controls wrap in the room beside the label; a label longer than its column wraps.
- Paths (the library's location, the Auto Import folder, the smart previews folder, a monitor
profile's file) are one line shortened from the front, the whole path in a tooltip only when cut.
- The Slideshow tab is one column the width of the page, the preview first; pointing at a
template no longer moves the page, and the Export buttons wrap.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
crates/ui-egui/src/panels/detail.rs: #2317's split (info_overlay / info_overlay_lines / info_overlay_extent) kept; info_overlay_lines builds its lines from #1965's View Options fields (info_line) and returns None when off or when no line is left
crates/ui-egui/src/panels/dialogs.rs: only_shows keeps Dialog::ViewOptions and the up-to-date Synchronize Folder case
crates/ui-egui/locales/it.json: union of keys
preset.apply changes the selection, else the active photo; the panel
remembered the preset on the active photo too, so an active photo outside
the selection showed a preset it never received.
The panel marked the last applied preset only while the selection was
the one it was applied to, and forgot it on the first frame with another
photo: back on the photo, nothing showed which preset it had. It now
remembers the preset per photo together with the photo's settings right
after, and marks it while the photo still has them; another photo shows
its own (or none), and a further edit drops the mark. The Amount slider
is no longer forgotten on a switch either: it comes back with the photo
while no later edit, undo or redo owns the undo step.
The test checked that Restore All was listed but clicked it only when the
machine already had a hidden location, so CI never exercised that path.
It now hides two places and restores them with Restore All, then hides
one and checks the row restores it directly, without a list.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AbegtYaPuwqZ81pGfELsFK
"Show N hidden locations" put every location removed from Local back at
once. With several hidden, the row now lists them (name, path on hover):
each comes back on its own, or all with Restore All. With one hidden, the
row restores it directly, as before. The command side already had
local.restoreHidden {path}; only the panel called it without a path.
The headless test hides a second place, opens the list from the row,
restores one, checks the other stays hidden, then restores the rest.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AbegtYaPuwqZ81pGfELsFK
app.export with a path like `output` or `output.xyz` and no `format` skipped the depth
check: the desktop app then wrote JPEG and the headless backend PNG, both at 8 bits.
ExportOptions::export_format resolves the format written (with the caller's fallback for
such a path), from_params_or validates against it, and both callers use it instead of
their own override.
With no aspect ever chosen, every crop started free, so the Lock had to
be switched on photo after photo. Nothing chosen yet now means locked to
Original, as Lightroom starts; switching the Lock off once is remembered
as free, and an explicit ratio is remembered as before.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011K1peyrh5MdZ6ttHkVq8Gh