Copy Metadata and Paste Metadata had no shortcut. They now use
Cmd+Option+Shift+C and Cmd+Option+Shift+V on macOS and
Ctrl+Alt+Shift+C and Ctrl+Alt+Shift+V on Windows and Linux, the keys
Lightroom Classic's keyboard shortcut page lists for copying and
pasting metadata in the Library module. Both keys also work when
Windows and Linux deliver them as clipboard events. The KEYC-METADATA
row now lists the keys LightCraft binds (Cmd+S, the two new keys and
Alt+1 to Alt+9 for keyword sets) and every other key of that table
that is still missing. Gap 28 in docs/gaps.md and the Library keys in
docs/ui-parity.md list the two new keys.
The template file tests now create their folder through a Scratch guard
that removes it when the test ends, including when an assertion fails,
as tests_folders.rs and tests_slideshow_export.rs do.
On Windows, tests_launch_library::listing_the_libraries_leaves_every_folder_byte_identical
failed every time with os error 33. Its snapshot read every file in the folder, including the
lock file of the library the test holds with LibraryLock::acquire. On Windows that lock is
LockFileEx, and reading the locked bytes through another handle fails with
ERROR_LOCK_VIOLATION.
The snapshot now records each file's length as well as its contents and modification time.
When the read of a catalog.lock fails with os error 33 on Windows, its contents are left out
and the file is compared by its length and modification time. Every other file is still
compared byte for byte, and on other platforms the held lock file is too. The app does not
read a held lock file anywhere, so only the test changes.
Resolves the parity summary (regenerated), drops a duplicated #[cfg(test)], files the What's New entry
under today's date and bumps the checklist's Last updated line.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
main replaced "Show Clipping" with "Clipping": the PR's Beschneidung term moves to the new key.
Also takes CodeRabbit's two wording suggestions.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A pass over all 2,565 interface strings and 299 formats messages of the
German catalogs (What's New lines not included). 108 messages change:
- Lightroom terms: Tönung (Tint), Beschneidung (Clipping)
- one word per thing: Bedienfeld (panel; Bereich stays for sidebar
sections), Begleitdatei (sidecar), Dia (slide), Zuschnitt (crop),
Farbmarkierung (label) where "Markierung" read as the pick/reject flag,
Verschlusszeit, Smart-Album, Perspektivkorrektur (Upright),
Aktivitätsstapel
- "Ihre Dateien/Fotos" at the start of a sentence read as the formal
"your" in a du catalog; rephrased
- double colons and "1 Fotos" in count messages, awkward phrasings
- spaced en dash instead of em dash, no space before a final ellipsis,
German quotes around {profile}
The terms are listed in docs/localization-de.md; localization-parity
records the review.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Found while testing crates/catalog/src/local.rs:62 (folder_of), used by forget_local_plan at :192 and :204
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
print.template.save, delete and import changed the template list before
saving the preferences, and left the change in place when the save failed.
A retried import then saw its own unsaved copy and stored another one as
"Name (2)". The list now goes back to what it was when the save fails, as
library.holdView and the slide templates do.
The slideshow always advanced on its own after the slide duration.
The new Manual slideshow setting (Settings > Slideshow > Playback, or
manual in slideshow.playback and view.slideshow) keeps each slide, the
intro and the ending up until the right or left arrow or the control
bar changes it. Space, Esc, random order and repeat work as before.
Exports still use the slide duration. The setting is saved with the
library and in slideshow templates; a template without it reads as off.
The script runs under #!/bin/sh; on dash the array assignment was a
syntax error. Pass the auth option and its value as two plain words.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* security: bound codec allocation to what a hostile file declares
A GIF's first image descriptor could name a frame up to four times the
canvas budget and be charged that allocation before any size check saw
it; PNG's compressed text chunks were expanded through an unbounded
inflate; JPEG handed hostile frame headers to the allocating decoders
(zune for the common cases, jpeg-decoder for CMYK/scaled/lossless) and
let an APP1 flood drive quadratic metadata reassembly. Each now refuses
on the same max_pixels budget the other decoders honour, with a
regression test per gate.
🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
* Fix clippy collapsible_if in DCT-scale size check
* Check jpeg-decoder header on scaled path; cap APP1 markers in decode_codes8
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Codebuff <noreply@codebuff.com>
Co-authored-by: Dylan Taylor <dylan@craftmaker.dev>
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
- Reverse slider loop order in UI to match Lightroom layout (Highlights, Lights, Darks, Shadows).
- Sanitize splits and amounts against NaN, inf, and inverted split bounds in ParametricCurve::new, at, and region_at.
- Guard param_drag against zero/negative widget dimensions.
- Update live region readout to display the current frame's dragged value immediately.
- Add regression test for non-finite and inverted split values.
Co-Authored-By: deepseek-flash <noreply@deepseek.com>
FredApps on #2689: a re-check started while the launch list is up (a
library not found looked for again, the window back to the front) never
marked its row as checking, so `apply_answers` skipped the new answer: a
library plugged back in stayed "not found" in the list. A row is re-armed
when its path is asked again about an answer it has (not after a failed
open, whose reason stays on the row). Test
a_library_found_again_comes_back_to_the_launch_list fails without it.
Fixes#2774
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JbBu4MRiaKxEbSrMTZiLj8
- Expose ParametricCurve from lightcraft-pipeline and use it in both
curve_luts and the UI curve editor, eliminating duplicate implementations
and ensuring render and graph never drift.
- Invert the parametric model while holding the other three regions fixed,
converting vertical pointer motion relative to the grabbed region's initial
amount so a horizontal drag with zero vertical travel leaves all sliders
untouched even with heavy overlapping neighbouring bumps (fixes AlexChambers
report: Darks=60, Lights=0 grabbed at x=0.51).
- Replace all raw table indexing with .get() per never-crash rules.
- Add unit and headless integration tests verifying that horizontal drag
with other regions non-zero changes nothing.
Co-Authored-By: deepseek-flash <noreply@deepseek.com>
The Print dialog's templates could be saved, applied and deleted, but
not moved to another library or another computer. Each saved template
now has an Export… button that writes it to a .lcprint file, and
Import… beside Save adds templates from such files.
print.template.export {name, path} writes {format, version, name,
settings}. print.template.import {path} reads at most 256 KB, checks
the format, version, name and every setting as a saved template's
settings are checked, drops cell photos and guides, and adds the
template under its name, or under "name (2)" and so on when the name is
in use. The buttons appear where the host provides open and save
dialogs: the desktop app does, and the web build does not.
CodeRabbit nitpick on #2701: a failing assertion skipped giving `Locked` its
permissions back, and a folder left at mode 000 can't be removed. A Drop
guard restores the mode and removes the test's folder on every exit
(found one left behind by an earlier failing run).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JbBu4MRiaKxEbSrMTZiLj8
The gate on cf9334907 failed on
tests_slideshow::playback_settings_drive_the_slideshow_and_bad_ones_are_refused
and ::settings_shows_the_slide_layout_and_a_live_preview (#1287); they passed
on the two gates before and in 15 of 15 local runs of the module with 4
threads, and nothing since touches the slideshow. Each checked its result
one frame after the click / after the preview changed, which a loaded
runner doesn't always give: they wait for it now (the checkbox state, the
green backdrop pixel). Test code only.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JbBu4MRiaKxEbSrMTZiLj8
The revision field and Catalog::apply said an error changes nothing and that the revision counts applied ops; a batch whose rollback fails changes the catalog and advances it.