mask.applyEffect, saveEffect and updateEffect read an `id` that isn't a
mask id as the selected mask (applyEffect {"id": "1"} changed mask 2);
mask.update, mask.component and mask.refineDetail cast `component` with
as-casts and fell back to component 0 or the last selection. They share
the strict opt_mask_id / opt_index helpers now.
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.
- English (and Auto where offered) stays first as the source language and fallback; the rest sort by native name with accents folded, so Čeština is under C and Magyar under M; Cyrillic and CJK names follow the Latin ones
- The list used the registry order, which is the order the languages were added
Selects the active photo's folder in the sidebar's Folders section,
opens the rows above it and scrolls it into view. Also on the photo
right-click menu. Adds the parity row and the label in all locales.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
photo.label read its label with str_param, which is None both when the
label is missing and when it isn't a string, and None meant "none": so
photo.label {} or {"label": 3} cleared the colour label of every selected
photo. Only "none" or null clear now; anything else that isn't a known
label is an error. Every caller in the app already passes a label.
mask_id read `id` with `as_u64().map(|v| v as u32)`, so text fell back to
the selected mask (mask.delete {"id": "1"} deleted it) and an id past
u32 wrapped onto another mask (4294967297 as u32 is 1). spot_index and
spot.select did the same with `index`, and a colour range's `component`
fell back to the first colour range part. They now refuse such values,
as the photo ids do; a missing or null value still means the selection.
Keep the stored destination consistent with the picker's None fallback after
catalog changes. Extend the real-file import regression to delete the chosen
album before confirmation, then verify import membership and undo.
Document the helper contracts and destination transitions for review coverage.
Co-Authored-By: Codex <noreply@openai.com>
Several commands read their photo parameters leniently. An `ids` that
wasn't a list of whole numbers (`"ids": 3`, `["3"]`) fell back to the
selection or the active photo, and an `id` that wasn't a whole number
(`"id": "5"`) fell back to the active photo, so a call meant for one
photo acted on another:
- ids: album.addPhotos / removePhotos / toggleTarget, library.select,
develop.set, denoise.queue, faces.index, mask.applyEffect (mask ids)
and app.export;
- id: photo.relink (relinked the active photo to the file),
photo.removeRegion / setRegion, faces.setName, stack.setTop / moveUp /
moveDown / split, library.findSimilar, the photo queries,
denoise.cancel (cancelled every photo) / status and ui.render.
They now refuse such values with an error, as Session::targets already
does for the other commands (shared cmd::photo_ids / photo_id). A
missing or null value still means the selection or the active photo.
Found while testing crates/fetch/src/http.rs:139 (Url::join)
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The page's automation call parsed its params with a fallback to {}, so a
typo such as lightcraft.command("photo.delete", "{ids: [3]}") ran
photo.delete with no params, which acts on the selection. Params must
now be a JSON object (blank text or null is {}); anything else rejects
the promise and runs nothing, as the control channel and MCP already do.
Lightroom Classic's F8 hides the right panel; F6 and F7 (filmstrip, left
panel) were bound but F8 did nothing. View > Right Panel (view.rightPanel,
F8 as a secondary key like F6 / F7) hides the right panel and shows the
same one again next time, or Edit when none was hidden. Hiding a tool
ends it, as closing its panel does.
Gap 28 listed rating [ ], flag cycling and F1 help as missing; they are
bound, so the entry now lists only filter-bar keys, Tab / Shift+Tab and F5.
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.
A mask slider hides the overlay so the adjustment can be judged, and
nothing showed it again: creating another mask, picking one in the list,
duplicating it or adding a part left it hidden while the Overlay menu
still named Color Overlay. Those now show the overlay, as Lightroom does;
so does a new AI selection from a click or a description.
The web import picker hard-coded a 13-extension accept list, so raws the engine imports (CR3, NRW, RAF, ORF, RW2, RWL, RAW, PEF, SRW, …) were silently filtered out of the dialog on Windows/Edge, which matches accept case-sensitively. Generate the list from EXTENSIONS, each extension in both cases. Fixes#2212.
eframe's web runner doesn't handle ViewportCommand::Title, so the web
build never named what was open, and a host embedding the page could
only guess whether a dropped file had opened. The page title is now
"<file name> — LightCraft" for the active photo, with " •" after the
name while changes are not yet written to browser storage, and the
window title when no photo is active. It is set only when it changes.
In Compare and Survey the selection holds both photos on screen, so the
Info panel's colour-label swatches and stars (and the Set Color Label
context menu) applied to the whole selection, while the bottom bar and the
rating/label keys act on the active photo only. They now go through
compare::target_active like the bottom bar does.
The culling panes' captions showed stars and flags but not the colour
label, so labelling there looked like it did nothing; each caption now
shows the label as a dot.
Reuse the shared album picker in Import Photos, retaining None/New album,
editable names, album-context preselection and existing import semantics.
Keep long paths within the viewport and cover hierarchy, localization,
keyboard choices, real imports, undo and layout regressions.
Co-Authored-By: Codex <noreply@openai.com>
Found while testing crates/meta/src/xmp.rs:458 (fmt_gps_coord)
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Found while testing crates/fetch/src/http.rs:71 (Url::parse), also Url::join at :115
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
A quick click on a slow frame presses and releases in the frame where the
panels open a dialog, and the backdrop check read that press as one on the
backdrop, closing the dialog at once. Input now counts only once the dialog
was already shown the frame before; a pending press is dropped otherwise.
The test opens Copy Settings from the bottom-bar gear with press and release
in one frame and checks it stays open, then that the next backdrop click
closes it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 13:07:20 -03:00
Roope Gröhnandcoderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Adds .coderabbit.yaml so CodeRabbit no longer reviews or comments on PRs opened by dependabot[bot].
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Adds Finnish catalogs (fi.json, fi-formats.json), the language table entry
and menu command, Finnish tests, docs and an attribution row. What's New text
stays English, as in most catalogs.
AI-assisted translation (Claude Code), reviewed by a native Finnish speaker.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Found while testing crates/fetch/src/http.rs:136 (Url::join)
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Found while testing crates/fetch/src/http.rs:139 (Url::join)
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Corrections from my review of #1198: Exposure is Esposizione (not Tempo di esposizione, the shutter time), Sharpening is Nitidezza (not Contrasto), Preset stays Preset in every preset label, and blurry photos are sfocate, not mosse.
Adds the 339 interface messages Italian lacked (print, slideshow, mask effects, faces, HDR, external editors, keywords, focus points and their dialogs) and translates the 60 format strings still in English in it-formats.json. Only the What's New lines stay in English, as before: 2,628 of 3,278 messages.
Main now rejects ✕ (U+2715) in translations, since Noto Sans Georgian has no
glyph for it (0c3c500e); × reads the same.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A group holds one active and one pending run, so `ci-${{ github.ref }}` let a
later push to main replace a queued run and skip its `cargo xtask ci`. Pull
request pushes keep the shared group (that supersession is the point); every
other run — a push to main, a manual one — now gets `ci-<run_id>`, a group of
its own, and always runs.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
The German catalog mixed du and Sie. It now uses du everywhere: the 11
messages that used Sie (imperatives such as "Wählen Sie", and "Ihr Name oder
Studio") changed; "Sie" and "Ihr" that mean they and their stay.
docs/localization-parity.md records the choice for the next batch.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Help → What's New now reads in German down to the oldest entry: every
wrapped line of docs/whats-new.md without a German entry has one. Dates
read DD.MM.YYYY; issue numbers, code spans, file names and link targets
are unchanged, and link texts stay English because the pages they open
are English. Each wrapped line is its own catalog key, so the German
lines break near the English line breaks. "Raw(s)" is "RAW-Datei(en)",
menu paths use →, and the text keeps the catalog's "du" form.
Held back: the 59 lines whose English text contains a ▸ menu path.
Their translations are ready (with →) and go in once #2016 (for #1950)
renames those keys; adding them now would only conflict with it.
localization-parity.md and ROADMAP.md: German re-measured with the
documented count over the catalogs, 2,572 → 3,644 of 3,754 messages
(80% → 97%). The denominator grows because German now translates
What's New lines no other catalog has; other rows keep their counts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every command, control and menu label and every tr() message in the
interface that another catalog already translates now has a German
entry too: mask effect presets, print and contact sheet, slideshow
settings and templates, External Editing (PhotoCraft / other editor),
monitor profiles (Settings → Display), HDR editing (SDR sliders, HDR
headroom), Tool Overlay and Loupe Overlay, keyword commands, background
tasks and face model setup.
Terms follow the existing catalog (Vorgabe, Stichwort, Maske,
Begleitdatei, Überlagerung, Dia, RAW-Datei) and its "du" form; menu
paths use → instead of ▸. The one English message that contains ▸
(the PhotoCraft help text in Settings → General) is left for after
#1950, which renames those source strings.
i18n::tests now report no missing German display labels.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The squares were carved off the crop rectangle itself, so the guide
depended on the crop's shape. A square crop lost everything after the
first arc -- the first subtraction leaves no rectangle to carve, and the
loop stops -- and other ratios kept different amounts. Counting the
quarter-circle arcs:
crop before after
1:1 1 10
phi:1 10 10
3:2 3 10
16:9 4 10
4:5 3 10
Only a golden-ratio rectangle subdivides into squares forever, so that
is the one shape that yields a complete spiral. The construction now
uses a PHI x 1 rectangle whatever the crop is, and the normalisation
already at the end of the function stretches it to fit.
That distorts the curve on a crop of another ratio, which is the price
of the guide staying complete, and is the approach the report proposes.
The portrait transpose and the orientation flips are unchanged, so a
portrait crop still gets a portrait spiral and all four orientations
still mirror it.
Fixes#2393.
On a profiled display the Grey and Sharpening-mask views rendered in the display's primaries, so
their histogram counted display-primary pixels, not the photo's sRGB ones. They now render in sRGB
like the clipping views (`Overlay::renders_in_srgb`) and are converted for the display after; they
show no photo colour, so nothing is lost.
The GPU equivalence test now covers Grey (mean/max LSB limits) and the shadow/highlight clipping
views (share of differing pixels, as for Visualize Spots: both renderers draw them with the same
`visualize::apply`, and a 1-LSB difference at 0/255 flips a pixel).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- ci.yml: cancel-in-progress only for pull requests, so a push to main never cancels the run of
the push before it.
- ci --quick: `git diff --name-status -z -M` and `git ls-files -z`, parsed as NUL-separated
fields (a rename or copy takes two paths), so a non-ASCII path, which git otherwise quotes and
escapes, still maps to its package.
- ci --quick: files outside a package that it compiles in (include_str!/include_bytes!, a build
script's rerun-if-changed: fonts, camera profiles and the app icon under assets/,
docs/whats-new.md, docs/raw/coverage-sweep.tsv, docs/raf-corpus.sha256, contributors/) count
as a change to that package. A test scans the workspace and fails when one is not mapped.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Reduced decoding is now opt-in: `files::load_vec_reduced` and the session's `reduced_loader` are used by grid
thumbnail jobs only; `load_bytes` / `load_vec` / `load_vec_pair` always decode the full image. A source developed
from a copy is marked (`DecodedSource::reduced`), so an export at a thumbnail's size reads the file again instead
of the cached thumbnail source, and Build Smart Previews never rebuilds a stale proxy from one.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Only a drag timed at full size (median of its full-size drafts, after the
first, at most 20 ms) forgets a slow-drag verdict; a drag that drafts small
from its start, or times fewer than two full-size drafts, leaves it unchanged,
as `DragScale::finish` does.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The released 0.9.1 x64 MSI was tested on Windows 11 Pro 24H2 (build
26100.9711) with verbose msiexec logs. A fresh install and an upgrade
from 0.8.0 into a custom folder both end on the completion page, so
#790 is fixed (by #1569). The upgrade keeps the folder, Launch starts
the app as the signed-in user, and both removals leave nothing behind.
Uninstalling from Settings -> Apps still ends without any confirmation
(#892, #931), so that stays in the gap. Hours and percentages are
unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>