Files
68b464011f Import review: an optional batch-edit recipe for newly imported photos (#2940)
* Import: an optional batch-edit recipe for newly imported photos

library.import takes a recipe, a list of develop control ids and values.
Each control is set, clamped to its range like a slider's, on every photo
the import actually adds, after the XMP sidecar, the camera default and
the chosen preset, as one more History step named Import adjustments.
Photos that are skipped as duplicates, restored from Recently Deleted or
joined from a browsed folder are not touched, as the preset and keywords
never touch them.

A recipe names plain adjustments only. White balance is refused, because
a temperature means Kelvin on a raw with colour matrices and a shift from
the as-shot white on a rendered photo, so one number cannot name the same
look for a whole batch, and the as-shot white balance of a raw is never
touched. Geometry and crop controls are refused, because the slider
refits the crop around a new angle and a batch write cannot. Per-photo
controls such as pointColor.0.hueShift are refused, because a write to a
photo without that instance is dropped. The rule is on the control's
spec (a control of the shared list, outside the Geometry section, not
white balance, and one that set accepts), so new controls follow it. An
id named twice counts once, the last value winning, and a list longer
than the control list is refused. ImportJob::new checks a recipe built in
code the same way, before any file is read or placed.

preset.saveRecipe saves a recipe as a user preset that stores only the
settings the recipe names, including a control the recipe sets to its
default, and refuses a recipe that stores nothing. The preset gets its id
the way imported presets do (user.<group>-<name>, then -2, -3 ...), so no
two presets share one: an id from the list length repeats once a preset
is deleted, and the first preset with an id is the one that gets applied.
A name or group over 100 characters is refused, and saving the same
recipe under the same name and group again returns the preset that
exists.

* Import recipe: tests

Cover a recipe on import (the values, the Import adjustments step, a
render, one undo step), the refusals (unknown ids, malformed items,
per-photo controls, geometry and crop, white balance, too many entries,
by the command and by ImportJob::new), clamping and a repeated id, a
raw's as-shot white balance staying as imported, a saved recipe preset
giving the same settings as the recipe (including a reset to a default),
the photos the recipe leaves alone (duplicates by path and by content, a
restored photo, a browsed photo that joins; a photo imported afresh gets
it), preset ids staying distinct after a delete and surviving a reopened
library, and both commands being listed with their parameters.

* Import review: a Batch edits section

The import review gets a collapsible Batch edits section: sliders for
the everyday adjustments (exposure, contrast, highlights, shadows,
whites, blacks, vibrance, saturation, clarity, texture, dehaze,
sharpening, grain, vignette). The controls moved off their default go to
library.import as its recipe, only when there are any, and a summary
beside the section counts them. The recipe stays in the dialog until the
import runs, so cancelling changes neither the catalog nor the presets.

Save as Preset, which needs a name and a recipe, saves the recipe with
preset.saveRecipe. It leaves the review's own preset choice as it is, so
a preset picked for the import is never replaced by the saved one. The
name goes in a TextField, as new text boxes do.

The summary line is a new tr_format message, added with a translation to
every formats catalog; the other new messages are plain and show in
English until a catalog translates them.

* Import review recipe: tests

Unit tests hold the dialog to the engine: every control it offers is one
a recipe may name, a slider moved back to its default leaves the recipe,
and start passes a recipe to library.import only when the dialog has one.
Driving the real review, Save as Preset saves nothing without a name or
without a recipe and with both saves exactly the recipe (leaving the
review's preset choice and the catalog alone), cancelling the review
discards the recipe, and confirming imports the photos with it as one
undo step, or without an Import adjustments step when there is none.

* Import recipe: tests for the disabled Save button and MCP reachability

The disabled state of Save as Preset was not pinned: the engine refuses
a nameless or empty recipe anyway, so the preset count stayed the same
even with the button always on. The test now also expects no error
toast, which only an enabled button that asked the engine would raise.

A new MCP end-to-end test drives both commands through the public
tools: list_commands documents them, run_command takes library.import
with a recipe (a recipe naming white balance is a tool error that
imports nothing), get_develop shows the values on the imported photo,
and the generated cmd_preset_saveRecipe tool saves a recipe.

* Import recipe: follow main's register(ui) and Id::unique after the rebase

Upstream main changed widgets::register to take &egui::Ui (every other
call site passes ui) and deprecated egui::Id::new in favour of Id::unique
(same hash, so the stored open/closed state is unchanged). Adapt the
batch-edit recipe section so lightcraft-ui-egui compiles again.

* docs: parity row LR-IMP-RECIPE for batch edits during import

* Keep HDR resets when a recipe is saved as a preset

A recipe that resets an HDR control to its default was dropped when it was saved as a preset, because the HDR subtree is left out of the settings JSON while it is the default. The recipe's settings are now compared with the HDR subtree always present, so the reset is stored alone and next to other adjustments. Round-trip tests apply the saved preset to settings with a non-default headroom.

* Address review findings

* Address review findings

* Import review: the imports the merge left in body.rs

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: eliets2 <287273517+eliets2@users.noreply.github.com>
Co-authored-by: FredApps <153173854+FredApps@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 01:23:04 +01:00
..