Commit Graph
4107 Commits
Author SHA1 Message Date
Bruno-Alumn cbf809a37e Masks: mask-effect ids and component indexes that aren't whole numbers are errors
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.
2026-10-21 12:03:42 +00:00
Bruno-Alumn 45f61ef5ef Web: a finished save repaints, so the title drops its unsaved mark (#2397) 2026-10-21 11:58:16 +00:00
Bruno-Alumn a136fb5bb1 Web: docs say JSON null params mean {} as empty text does 2026-10-21 11:58:06 +00:00
Bruno-Alumn dad4d6254e LR-PRE-PANEL: an active photo outside the selection isn't marked with the preset applied to the selection (#2392)
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.
2026-10-21 11:57:58 +00:00
Marco Arnulfo 3ac21d18a5 LR-EXP-BITDEPTH: savePreset from the previous export drops a stored depth its format lacks 2026-10-21 12:40:04 +02:00
Arekatla Nishanth ChowdaryandClaude Sonnet 5.5 110aea1a1a fix(ui): Show in Folders reveals the right-clicked photo and stays off for folders below the tree's depth
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-21 15:42:57 +05:30
Nikolai Nyegaard c262f54f19 Settings and Edit ▸ Language: list the languages alphabetically by their own name
- 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
2026-10-21 12:01:48 +02:00
Marco Arnulfo bcef909d40 tests_import: without a library the imported profile keeps no file path at all 2026-10-21 10:28:39 +02:00
Marco Arnulfo 4a82c0774e tests_import: keep the folder rename comment with its own test 2026-10-21 10:17:57 +02:00
Arekatla Nishanth ChowdaryandClaude Sonnet 5.5 4ab5dcc319 feat(ui): add Photo > Show in Folders (#2437)
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>
2026-10-21 13:02:31 +05:30
Dylan M. Taylor abb49bdd76 ci: switch to shared nix workflow from craft-common-actions (#1980) 2026-10-21 02:38:14 -04:00
Bruno-Alumn 205a7ec120 photo.label: a missing label, or one that isn't text, is an error, not a clear
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.
2026-10-21 01:49:30 +00:00
Bruno-Alumn 622988d7f7 Masks and spots: an id or index that isn't one is an error, not the selected one
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.
2026-10-21 01:39:29 +00:00
tylerphotosandCodex 4cb90d4b7e LR-IMP-ADD-DIALOG: clear deleted import destinations
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>
2026-10-20 20:34:52 -05:00
Bruno-Alumn 42c7cc03a4 Commands: an id or ids that isn't a photo id is an error, not the selection
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.
2026-10-21 01:21:45 +00:00
NetterSilverandClaude Haiku 5.5 c3a5d9b7f1 Keep file name and query for fragment-only redirect Location
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>
2026-10-21 01:13:10 +00:00
Bruno-Alumn 79f11e098c Web: lightcraft.command refuses params that aren't a JSON object
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.
2026-10-21 01:05:03 +00:00
Bruno-Alumn 7ab5b150f3 KEYC-PANELS: F8 shows and hides the right panel
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.
2026-10-21 00:39:53 +00:00
Bruno-Alumn 61823b1736 LR-PRE-PANEL: the applied preset stays marked on returning to the photo (#2392)
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.
2026-10-21 00:27:45 +00:00
Bruno-Alumn 360bf89903 LR-MASK-OVERLAY: a new or picked mask shows its overlay again (#1800)
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.
2026-10-21 00:13:57 +00:00
joblonfk001 b8e335370d fix(web): derive file picker accept list from engine extensions
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.
2026-10-21 08:09:19 +08:00
Bruno-Alumn 8ef46aa088 Web: the page title names the open photo and unsaved changes (#2397)
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.
2026-10-21 00:02:23 +00:00
Bruno-Alumn f16c6ef07f LRC-LIB-COMPARE: the Info panel labels the active photo while culling (#2398)
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.
2026-10-20 23:49:03 +00:00
tylerphotosandCodex 286171e2e5 LR-IMP-ADD-DIALOG: preserve import destination album hierarchy
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>
2026-10-20 16:41:40 -05:00
NetterSilverandClaude Haiku 5.5 8fe440c759 Carry rounded GPS minutes into degrees in XMP writer
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>
2026-10-20 19:59:03 +00:00
NetterSilverandClaude Haiku 5.5 dee5f7910a Match URL schemes case-insensitively in Url::parse and Url::join
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>
2026-10-20 19:17:55 +02:00
NetterSilverandClaude Haiku 5.5 81b8f0e598 Drop non-finite elevation values in the GPX parser
Found while testing crates/meta/src/gpx.rs:150

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-20 16:48:29 +00:00
helgehelge123andClaude Opus 5.5 d1eeff9600 LR-BEHAV-LOCALIZE: say what German's 59 and its 3,754 count
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 18:44:36 +02:00
KetbomeandClaude Opus 5.5 4a6c43ce06 LR-BEHAV-DIALOGS: the click that opens a dialog never closes it
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> 89a916a31d Update docs/localization-parity.md
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2026-10-20 18:54:28 +03:00
Brandon ThomasandClaude Opus 5.5 5fe74557ec Skip CodeRabbit reviews on Dependabot PRs (#2407)
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>
2026-10-20 11:38:50 -04:00
Dylan M. Taylor 666f78432b docs: add SonarCloud and CodeRabbit badges to README (#2240)
* docs: add SonarCloud and CodeRabbit badges to README

* docs: put SonarCloud and CodeRabbit badges on their own line
2026-10-20 11:31:44 -04:00
Roope GröhnandClaude Sonnet 5.5 494d9d77bb i18n: add Finnish (fi) interface language
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>
2026-10-20 18:29:23 +03:00
NetterSilverandClaude Haiku 5.5 cee9349658 Remove dot segments when resolving redirect Locations
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>
2026-10-20 15:21:33 +00:00
NetterSilverandClaude Haiku 5.5 78f0e6e650 Keep file name when a redirect Location is query-only
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>
2026-10-20 17:03:00 +02:00
fabiodalez-dev b9c9a97d7a i18n(it): translate the mask overlay views 2026-10-20 16:11:56 +02:00
fabiodalez-dev fa135940e7 i18n(it): native-speaker corrections and every interface string translated
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.
2026-10-20 16:08:27 +02:00
helgehelge123andClaude Opus 5.5 f42792ff2c i18n(de): × instead of ✕ in three What's New lines
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>
2026-10-20 15:09:03 +02:00
Matthew James SivakandPaperclip 10c4d0c0ab Gap 9: only a PR's own pushes share a CI concurrency group
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>
2026-10-20 15:07:19 +02:00
helgehelge123andClaude Opus 5.5 f64d9a2e3c i18n(de): speak to the user with du throughout
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>
2026-10-20 14:59:38 +02:00
helgehelge123andClaude Opus 5.5 6d5a3b02d2 i18n(de): translate the 862 What's New lines the German catalog lacked
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>
2026-10-20 14:57:40 +02:00
helgehelge123andClaude Opus 5.5 8fdbbad623 i18n(de): translate the 217 interface strings the German catalog lacked
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>
2026-10-20 14:57:40 +02:00
Hamza 7b4b300d75 Crop: the spiral guide is complete at every aspect ratio
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.
2026-10-20 17:30:08 +05:00
Matthew James SivakandClaude Opus 5.5 30fea8d34c LR-VIEW-CLIPPING: profiled diagnostic views keep the sRGB histogram; GPU equivalence for the clipping overlays
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>
2026-10-20 13:14:16 +02:00
Matthew James SivakandClaude Opus 5.5 960e1f14e8 Gap 9: ci --quick reads git's -z output, tests the crates that embed changed files; CI keeps main's runs
- 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>
2026-10-20 13:10:53 +02:00
Matthew James SivakandClaude Opus 5.5 9ed971149b LR-IMP-PREVIEW-READ: exports never decode a DNG's reduced copy
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>
2026-10-20 13:08:03 +02:00
Matthew James SivakandClaude Opus 5.5 f08522b8a9 LR-VIEW-ZOOM: the 20 ms rule clears a slow verdict only on full-size drags (docs)
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>
2026-10-20 12:56:48 +02:00
Davit MatchakhelidzeandClaude Opus 5.5 2015f44c06 Release: keep the web build without craft-fonts for now (its WASM would pass Cloudflare's 25 MiB limit); docs say so
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YMTAbGNRxnMEzRvYnQGphF
2026-10-20 12:56:17 +04:00
nevicia2010-hub ab96162e03 export: preserve graphic watermark insets when fitting 2026-10-20 09:50:18 +02:00
insideTheFluxandClaude Opus 5.5 4ea9be25b2 Gap 27: the Windows x64 installer is verified on Windows 11
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>
2026-10-19 22:40:21 -07:00