Commit Graph
3164 Commits
Author SHA1 Message Date
radialmonster 03bc1469f9 LR-EDIT-OPTICS-PROFILE: apply the distortion correction Panasonic/Leica record in RW2/RWL (#256)
The RW2 reader reads PanasonicRaw tag 0x0119 (16 LE words) and expresses the
camera's own correction as the equivalent DNG WarpRectilinear in OpcodeList3,
so embedded_lens() turns it into the existing EmbeddedLens/EmbeddedWarp and
"Enable Profile Corrections" applies it like a DNG's warp (no new warp path;
Convert to DNG keeps it). Rule, from our own measurements against the
embedded camera JPEGs (clean-room; ExifTool tag names only):
corrected = rho*(1 + (w8 rho^2 + w4 rho^4 + w11 rho^6)/32768), rho = r/w12
(w12 = active-area half diagonal), output = corrected / (1 + w5/32768),
on when (w7 & 0xf) == 1. Chosen on 38 GX85 photos, held out on 35 photos
from 12 other bodies / 9 other lenses; corrected renders line up with the
camera JPEG within 0.75 px at 1440 px at the corners (up to 13 % of the
half diagonal before). Implausible values leave the photo uncorrected.

The loader now removes lens opcodes before the RW2 camera-look fit (its
binned proxy needs an empty OpcodeList3); Opcode::is_lens_correction()
replaces the duplicated matches. RENDER_CACHE_VERSION 11 -> 12.
2026-10-08 00:13:58 -04:00
Gardy 4e012a59a6 macOS menu: single-key shortcuts reach egui (E, C, ⇧P, ratings…)
On macOS every shortcut that appears in the menu bar was left to the native
menu, and the egui shortcut handler skipped those keys. AppKit only offers a
key press to the menu bar when it carries ⌘ or ⌃, or is a function key; a
plain key (E, C, H, M, I, K, D, 0–9, P, U, Y…) or one with only ⇧ / ⌥ (⇧P,
⇧V, ⌥Y…) goes to the window's winit view instead. So all of those did
nothing on macOS, while keys outside the menus (Space, G, X, ⌫) and ⌘
shortcuts worked.

`publish_shortcuts` now lists only the shortcuts the menu bar really receives
(`menu_delivers`: ⌘, ⌃ or a function key) in `native_shortcuts`. The others
keep their accelerator on the menu item, so the menu still shows the key, and
egui runs them.

Checked in a release build by pressing keys (not via the control channel,
which bypasses the native menu): C opens Crop & Rotate, E Edit, ⇧P Presets,
3 / 0 set and clear the rating; ⌘⇧H toggles the histogram once per press;
typing "c" in the search field types and leaves Crop closed. The same build
without this change ignored C, ⇧P and 3. ⌃ shortcuts (merges) are unchanged
and were not tried.

Regression test: native_menu::tests::single_key_shortcuts_stay_with_egui
(fails with the previous behaviour: "E must be left to egui").
2026-10-07 22:10:21 -06:00
Wondarkus 6c7405ed3e Fix Russian translations in ru.json 2026-10-08 07:06:23 +04:00
Daniel VoshartandClaude Opus 5.5 8dd3afce51 Fetch: validate caller-supplied names, sizes and hashes; FileSpec borrows its strings; download_url for an exact address
The fetch crate is about to serve downloads whose file names, sizes and addresses come from a user-editable list,
so it checks them before touching the disk: an ordinary, unique file name (no separators, drive or stream
syntax, control characters, trailing dots, Windows device names or .part suffix), a non-zero size within the
caller's bound, a well-formed SHA-256, bounded file, mirror and attempt counts, and URLs capped at 16 KiB.
FileSpec now borrows its strings (callers with static names are unchanged) so a name read from a file needs no
leaking. download_url fetches one file from an exact address, query string included, for signed links.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 22:59:22 -04:00
Daniel VoshartandClaude Sonnet 5.5 4b57d5dc5a Fetch: a bearer token is sent only when the caller names its variable
Options::token_env (default none) replaces the hard-coded LIGHTCRAFT_SAM3_TOKEN, so a download of one model can
never send another model's token to its host. SAM 3 names its variable through lightcraft_segment::fetch::options().
The decision (non-empty, https, the host the download began at) is a small function with its own test.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-07 22:59:01 -04:00
Wondarkus 6a34a1f8b8 Add files for further editing 2026-10-08 06:56:08 +04:00
radialmonsterandClaude Opus 5.5 fc4b054fa4 LR-IMP-FORMATS: RW2 format 8: invalidate cached thumbnails
The decoder change alters format-8 renders while the photo's content hash
and settings stay the same, so thumbnails rendered before it (edited
photos keep theirs on disk) would survive. Bump RENDER_CACHE_VERSION.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 22:52:45 -04:00
Hanmo d614dc72bd M3: isolate global Tint direction correction for compatibility review 2026-10-08 10:26:18 +08:00
Hanmo 024e983a7e M3: honour Nikon CropArea and embedded preview colour space 2026-10-08 10:16:44 +08:00
Brandon ThomasandClaude Opus 5.5 629e39380e Release CI: macOS job on macos-15-xlarge
Custom macOS larger runners in a runner group do not pick up jobs; the
built-in M2 Pro label does. Only release.yml uses it, and the
actions_macos_xl budget caps the monthly spend.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 22:07:59 -04:00
radialmonsterandClaude Opus 5.5 1ba0421a85 LR-IMP-FORMATS: RW2 format 8: decode each 2×2 cell column by column
Raw format 8 codes a cell top-left, bottom-left, top-right, bottom-right.
decode_strip placed the values in row order, which put the two green
samples of every RGGB cell on each other's sites: flat areas were right,
but every edge got a one-pixel colour zipper. Measured on a G9 II and the
corpus GH6, S5 II and S9: the Gb plane sat at (+0.5, -0.5) half-res cells
from Gr instead of the Bayer (-0.5, +0.5); with this change all four match.

The test encoder used the same order, so the round trip passed either way;
a new known-answer test hand-codes one cell and checks where it lands.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 21:58:09 -04:00
Brandon ThomasandClaude Opus 5.5 8734e95dc8 Release CI: macOS job on the org macos-release runner
The standard macOS runners are shared with all CI and queue for an hour or
more. The macos-release runner (GitHub-hosted M2 Pro) is in a runner group
usable only by release.yml on the release branch, with a monthly spend cap
on the actions_macos_xl SKU.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 21:54:56 -04:00
Brandon ThomasandClaude Opus 5.5 4159ceb1b0 About: contributor and model credits compiled in (grab bag + table, self-submitted names) (#252)
* About: contributors compiled in from contributors.json (Contributors + Models tabs, grab bag + table, sortable, name toggle)

Adds the craftrules standard (standards/contributors.md) to LightCraft: contributors/contributors.json
is generated by craftrules/scripts/contributors.py and baked into the binary by
crates/ui-egui/build.rs (merged next to the tr_format! generation) as static tables. The About
dialog gets tabs About · Contributors · Models. Each contributor line: @username, opt-in
display/real name, PRs, commits, lines +/−/Δ, binary assets +/−, first/last commit. Sortable by
every column; alphabetical ignores case and the leading @. Tabs and the list view are
control-channel widgets (button:aboutTab-*, button:creditsTable / button:creditsGrabBag).

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

* Contributors docs: craftrules is two levels up (../../craftrules)

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

* Contributors: self-submitted names in contributors/people.toml; AGENTS.md credit-yourself section

build.rs reads contributors/people.toml next to the generated contributors.json and shows a name only
for entries the generator verified as written by that person (craftrules standards/contributors.md).

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

* Contributors: regenerate (verified people.toml entries)

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

* Contributors: regenerate after rebase onto main

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

* Contributors: regenerate (verified people.toml entries)

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 21:22:08 -04:00
Brandon Thomas 2694d7089f Merge pull request #249 from radialmonster/ci-available-ram 2026-10-07 21:06:13 -04:00
Brandon Thomas 88f38c7e2a Merge pull request #248 from andrewyao (web_time in raw preview decode timing) 2026-10-07 21:06:13 -04:00
Brandon Thomas df574a537a Merge PR #213 from Orthic-Labs/orthic/arw-lossless-ycbcr
# Conflicts:
#	crates/ui-egui/src/headless.rs
2026-10-07 20:53:51 -04:00
Brandon Thomas 5bc923b4f9 Merge PR #246 2026-10-07 20:53:29 -04:00
Brandon ThomasandClaude Opus 5.5 71c8382b10 Merge main into #213 (keep main's Windows test fixes from #207; parity row combines RW2 and the downsized ARW)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 20:53:25 -04:00
Andrew YaoandClaude Opus 5.5 0a952b3536 Web: time raw preview decodes with web_time so wasm doesn't panic
load_bytes_now timed the binned demosaic with std::time::Instant, which
panics on wasm32-unknown-unknown ("time not implemented on this
platform"). Opening a raw in the web build aborted the decode. Use
web_time::Instant, as library.rs already does.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-07 17:53:19 -07:00
radialmonsterandClaude Opus 5.5 f9cfe15db6 CI: pick build jobs from available RAM, not installed RAM
Installed RAM overcommits a busy machine: on a 32 GB desktop with browsers,
a VM and editors open, about 7 GB was free and the installed-RAM rule chose
16 jobs. A fresh build then pushed free RAM below 1 GB.

`cargo xtask ci` now uses one build job per 1.5 GB of RAM available when it
starts (Linux MemAvailable, Windows AvailableMBytes, macOS free + inactive +
speculative pages), capped at the CPU count. With line-table debuginfo,
4 jobs measured about 3 GB in use, so this leaves about half the available
RAM spare. If available RAM can't be read, half the installed RAM is
assumed; if neither can, 4 jobs. Test threads follow, capped at 4.
Explicit CARGO_BUILD_JOBS / RUST_TEST_THREADS still win.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 20:46:10 -04:00
radialmonsterandClaude Opus 5.5 ed0a2a4f07 GPU tests: drop the edge-pixel tolerance; the coverage mask makes edges match
#207 let up to two edge pixels differ because the GPU's f32 framing could
fall on the other side of the lens-warp boundary. 'GPU: decide warped-geometry
edges with a CPU coverage mask' (this PR) makes the GPU use the CPU's f64
decision, so every case is back within the plain MAX_LSB check (embedded
lens + perspective: max 1 LSB, was 91 before the mask).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 20:40:47 -04:00
Brandon ThomasandClaude Opus 5.5 0801219b17 Merge #243 (with main) into #246
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 20:38:46 -04:00
Brandon Thomas e716ede8e4 Merge remote-tracking branch 'origin/main' into HEAD 2026-10-07 20:33:23 -04:00
Brandon ThomasandClaude Opus 5.5 2e48f25997 Merge main into #243 (combine with #207's Windows test fixes)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 20:33:23 -04:00
Brandon ThomasandClaude Opus 5.5 d716d74af5 Merge main into #243 (combine with #207's Windows test fixes: hide_home_above plus local.addRoot, FakeFs path normalising plus case folding)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 20:32:47 -04:00
Brandon ThomasandClaude Opus 5.5 a1c5e2dad1 Tests: three headless UI tests wait for the textures and dialog they check
settle() returns after a quiet spell, which on a loaded machine can come before the first grid
thumbnails, the profile browser's variant thumbnails or the async import review (and its
thumbnails) land. demo_grid_snapshot_has_ui_pixels, profile_browser_previews_on_hover_and_applies_on_click
and import_review_dialog failed that way when two test runs shared the machine; they (and the
two other import-review tests) now step until what they assert is there. 2 concurrent runs: 138/138 each.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 20:32:34 -04:00
radialmonsterandClaude Opus 5.5 c816ca8e35 UI tests: wait for sidebar rows with main's step_until
main gained Headless::step_until for the same job as this branch's
test-only wait_for_widget (run frames until a condition holds). Use the one
helper and drop the duplicate.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 20:23:25 -04:00
radialmonsterandClaude Opus 5.5 62325bd45e Thumbnails: retire the replaced preview cache so its old jobs can't write
Reopening a library (or opening another one, or set_disk_cache_bytes
falling back to attaching) replaced MediaCache::rendered with a new
PreviewCache, but render jobs still in flight kept the old object.
Each PreviewCache checked only its own generation, so after a clear on
the new cache the old one's generation was still current: a finishing
thumbnail job wrote its pixels through it into the directory the clear
had just emptied. The UI rejected the result, but the JPEG stayed.

attach_disk_cache now retires the cache it replaces before swapping it:
PreviewCache::retire takes the generation write lock (so it waits for a
write in progress, like clear) and marks the object retired; every
get/put/put_deferred through it is then refused and its clear() is a
no-op, as its directory may now belong to the replacement. Files on
disk are left alone, so a plain reopen still finds its thumbnails, and
resizing the budget in place keeps the same cache object as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 20:23:17 -04:00
radialmonsterandClaude Opus 5.5 4d5504cf16 Web: watch the thumbnail cache from worker start so an early clear invalidates
The main thread's Workers tracked no rendered-thumbnail cache until the
first render request carried one. If Clear Preview Cache ran before that
(e.g. starting on an empty filtered view), the per-frame observation in
finished() had nothing to compare with, and the first request only
recorded the already-cleared generation as its baseline. The persisted
ThumbIndex was never invalidated, so once the filter was removed the
request touched the old entry and the worker read the pre-clear JPEG
back from browser storage. Replacing the cache (another library) before
the first request had the same gap.

Workers::start now takes the session's cache and records it, with its
current generation, as the baseline the persisted index belongs to. A
normal start with no clear observes the same cache and generation and
keeps the index. The comparison moves into wire::CacheWatch so it is
tested natively (cargo test --workspace) next to ThumbIndex.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 20:23:17 -04:00
Brandon Thomas aec820033b Merge pull request #206 from anarion80/add_nix_build
Packaging: add a Nix flake (nix build / nix develop)
2026-10-07 20:15:50 -04:00
Brandon Thomas b90c06b347 Merge pull request #210 from voshart/pr/fetch-crate
Fetch: the model downloader becomes its own crate (no behaviour change)
2026-10-07 20:15:46 -04:00
Brandon Thomas 52f64ce0b9 Merge pull request #215 from XRTce/rw2-panasonic-lumix
LR-IMP-FORMATS: Panasonic RW2 / Leica RWL / Panasonic RAW in every raw format, with the RW2 camera look
2026-10-07 20:15:41 -04:00
Brandon Thomas 767ed15adc Merge pull request #235 from Gremiger/fix/issue-212-nef-dng-white-balance
Issue #212: Raw: keep vendor white balance when writing DNG
2026-10-07 20:15:37 -04:00
Brandon Thomas 1c025fbb97 Merge pull request #207 from voshart/pr/tests-windows
Tests: pass on Windows (path separators, temp folder inside home, one GPU edge pixel)
2026-10-07 20:15:33 -04:00
Brandon ThomasandClaude Opus 5.5 9b41e33a24 Merge main into #213 (keep main's waits in the two flaky UI tests)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 20:14:41 -04:00
Brandon ThomasandClaude Opus 5.5 0143704c7d Merge #243 (with main) into #246
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 20:14:28 -04:00
Brandon ThomasandClaude Opus 5.5 9da8163ae2 Merge main into #243 (keep main's settle and step_until in the two flaky UI tests)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 20:12:10 -04:00
Brandon ThomasandClaude Opus 5.5 46f5d212e7 Merge main into #207 (keep main's step_until wait for the folder row)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 20:11:00 -04:00
Brandon Thomas eba0b9723b Merge pull request #223 from Ndsanes/l10n/zh-hans-catalog-gap
LR-BEHAV-LOCALIZE: fill the zh-hans catalog to match zh-hant
2026-10-07 20:09:36 -04:00
Brandon Thomas 5372309ad8 Merge pull request #228 from RicSchonfelder/i18n-pt-br
i18n: add Brazilian Portuguese (pt-br) UI translation
2026-10-07 20:09:32 -04:00
Brandon Thomas b63ea8f5c2 Merge pull request #222 from Ndsanes/fix/craft-fonts-relative-path
Build: resolve a relative CRAFT_FONTS_DIR against the workspace root
2026-10-07 20:09:28 -04:00
Brandon Thomas 2dda7cf842 Merge pull request #241 from agorevski/copilot/issue-cluster-docs-consistency
Docs: fix release, sibling, and command references
2026-10-07 20:09:24 -04:00
Brandon Thomas be87e1b6e7 Merge pull request #238 from DWAA1660/fix/export-missing-folder
Export: clear error when there's no export folder (instead of "Access is denied")
2026-10-07 20:09:19 -04:00
Brandon Thomas 97e34434d0 Merge pull request #217 from nledford/feat/random-sort
Library: Random sort with a stable seed and Reshuffle
2026-10-07 20:09:14 -04:00
Brandon Thomas 30192541ea Merge pull request #242 from nledford/feat/collapsible-sidebar-sections
Sidebar: collapsible Albums, Local, By Date and Keywords sections
2026-10-07 20:09:10 -04:00
Brandon Thomas c4d7c00e74 Merge pull request #225 from nledford/fix/stale-panels-after-library-switch
Library: switching libraries no longer shows the old library's sidebar
2026-10-07 20:09:06 -04:00
Brandon Thomas 3fe0bef364 Merge pull request #239 from nledford/feat/smart-album-file-path
Smart albums: File Path rule field
2026-10-07 20:09:03 -04:00
Brandon Thomas 590b387a53 Merge pull request #221 from MusseJusse/codex/navigation-arrows-pr
M16.1: complete navigation arrowheads
2026-10-07 20:08:59 -04:00
Brandon Thomas 51c878ddd8 Merge pull request #199 from storytold/fix/flaky-profile-hover-test
Tests: fix three FreeBSD-flaky headless UI tests
2026-10-07 20:08:55 -04:00
Brandon ThomasandClaude Opus 5.5 034bb6e324 Rename: drop a redundant from != to (clippy nonminimal_bool)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 20:08:52 -04:00