83 Commits
Author SHA1 Message Date
aiongg f3bb7a9f30 CI: one test binary per crate, line tables only, one WASM check (#2710)
* CI: dev profile keeps line tables for workspace crates and no debug info for dependencies

* Control server tests: persistent_requests_and_slow_replies_outlive_the_idle_read_timeout no longer races the idle timeout

Each request (and the closing half-shutdown) reaches the socket before the
worker reads, so on a slow machine only the reply waits outlast the 100 ms
idle timeout, as the test intends. The idle test sends its input the same
way, and the no-frames test allows 5 s instead of 2 s to give up.

* CI: one integration-test binary per crate

Each crate's tests/*.rs become modules of tests/it/main.rs (51 test files
into 13 binaries, plus 6 that stay their own). Inner #![cfg] attributes
move to the mod lines. Files that change state for the whole process keep
a binary of their own: codecs jxl_tail_heap and raw sr2_heap (dhat global
allocator), gpu shutdown, shutdown_first and memory, ui-egui shutdown.
Docs, comments and the GPU probe name the new paths and targets
(--test it <module>::); the CI report names an it binary's skips after
the crate.

* CI: tests under nextest, doctests apart, one wasm check

xtask ci runs the tests with cargo nextest run --lib --bins --tests when
cargo-nextest is installed, else cargo test with the same targets and a
one-line notice; doctests run after them with cargo test --workspace --doc.
The heif and hardware GPU steps use the same runner. Examples stay out of
the test build (clippy --all-targets still checks them). Concurrent tests
under nextest follow the RUST_TEST_THREADS cap (NEXTEST_TEST_THREADS).
The wasm check is one cargo check --keep-going over the same crates, and
clippy runs with --keep-going.

* CI: tests run under cargo test by default, nextest with LIGHTCRAFT_NEXTEST=1 (per-process scene cache)
2026-10-23 13:34:30 -04:00
Brandon ThomasandClaude Opus 5.5 23e181283c Release 0.13.0
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 20:06:09 -04:00
Brandon ThomasandClaude Opus 5.5 479a05a0be Release 0.12.0
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 15:39:28 -04:00
Dylan M. Taylor bfb23db2ca Use chrono instead of hand-rolled UTC conversion (#2457)
* Use chrono for UTC/calendar conversion instead of hand-rolled copies

Replaces the hand-rolled Howard Hinnant civil/days-from-civil copies and the
leap-year/days-in-month table with chrono as a workspace dependency:
- apps/lightcraft/src/logging.rs: timestamp (log line time)
- crates/catalog/src/dates.rs: civil + weekday
- crates/catalog/src/stacks.rs: iso_seconds (date -> seconds)
- crates/engine/src/cmd/face_recognize.rs: capture_secs (native only)
- crates/meta/src/datetime.rs: DateTime::unix_seconds and the valid() day check
Output formats and semantics are unchanged (pre-epoch clamp, day rollover that
normalize_iso relies on, leap-second tolerance, EXIF/XMP parsing). chrono uses
only std, so wasm stays safe. AGENTS.md/CLAUDE.md now say time goes through chrono.

Follow-up to storytold/cadcraft#1785

* Never panic on a clock outside chrono's range; keep 5-digit years unprefixed

chrono's From<SystemTime> unwraps, so a clock past year 262143 panicked
inside the logger. timestamp now uses the fallible DateTime::from_timestamp
and reads such a clock as the epoch, keeping the old pre-epoch clamp.

timestamp and catalog's civil write years past 9999 with {:04} like the old
{y:04} did, because %Y would add a '+'. Tests cover the out-of-range and
5-digit-year cases.

* Don't build an unrepresentable SystemTime in the far-future test

UNIX_EPOCH + Duration::from_secs(u64::MAX / 2) overflows Windows'
SystemTime and panicked before timestamp() ran, failing the Windows
test job. Build it with checked_add and only assert where it exists.

* time: preserve old out-of-range date conversion (no epoch fallback)

`DateTime::from_timestamp(...).unwrap_or_default()` silently reset any instant
past chrono's year range to 1970-01-01, changing output the old hand-rolled
Howard-Hinnant code converted exactly (e.g. 10_000_000_000_000 s was
318857-05-20 17:46:40, not the epoch). Restore the old conversion domain with a
cycle-based helper that routes the calendar through chrono (the calendar
repeats every 400 years = 146_097 days, so month/day/time come from chrono and
only the year is carried in i128, keeping every i64 exact).

- apps/lightcraft/src/logging.rs: `timestamp` via `utc_parts`; keep the
  pre-1970 epoch clamp; u64 secs past i64::MAX saturate, not epoch.
- crates/catalog/src/dates.rs: `civil` cycle-based (the callers stay inside
  0000-9999, but a far clock no longer resets to the epoch).

Pinned old outputs in tests:
  logging u64::MAX/2 s -> 292277026596-12-04T15:30:07.000Z (was epoch),
          10_000_000_000_000 s -> 318857-05-20T17:46:40.000Z
  civil 10_000_000_000_000 -> 318857-05-20T17:46:40,
        -10_000_000_000_000 -> -314918-08-13T06:13:20, i64 extremes

* Don't build a year-318857 SystemTime unchecked in the logging test

Windows' SystemTime only reaches about year 30828, so
UNIX_EPOCH + Duration::from_secs(10_000_000_000_000) panicked before
timestamp() ran and failed the Windows test job. Build it with
checked_add and assert where the platform can hold it. The year-10000
case fits everywhere and stays unconditional.

* Name the 400-year range extension as the one exception to the time rule

The rule said never to hand-roll calendar math, but the range fix
(shifting by whole 400-year Gregorian cycles so dates past chrono's year
range keep their old output) is cycle arithmetic, so the rule and the code
disagreed. The rule now names that exception: the library still computes
month, day and time, and only whole cycles are carried in the year.

* Say the library computes month and day; time-of-day is plain arithmetic

Narrows the range-extension wording: the date library computes the
calendar fields, and splitting seconds into hours, minutes and seconds
is ordinary arithmetic, which is what the code does.
2026-10-21 19:36:36 -04:00
Brandon ThomasandClaude Opus 5.5 79d916c149 Release 0.11.0
The web release now builds with craft-fonts too (web-craft-fonts), as VectorCraft's
does, so the published web app ships the Chinese and Japanese faces docs/releasing.md
promises.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 14:05:43 -04:00
Brandon ThomasandClaude Opus 5.5 a2a41383c4 Release 0.10.0
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 22:19:04 -04:00
Brandon ThomasandClaude Opus 5.5 4f0b110bcb Release 0.9.1: the release web build installs the wasm-bindgen CLI
Since the web zip moved from trunk to cargo xtask web (aeaf4154), packaging/web/package.sh needs the
wasm-bindgen CLI at Cargo.lock's version, and the shared release workflow installs only trunk, so
0.9.0's Web (wasm) job failed. In CI, install it first (as FilmCraft and EffectCraft do).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 23:16:37 -04:00
Brandon ThomasandClaude Opus 5.5 20ccedf56e Release 0.9.0
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 21:42:57 -04:00
skylar taylor-barrick 2b173f0bee Build: take craft-docpath from the merged craft-libs rev
craft-libs#169 is merged, so the fork pin is no longer needed. Pinned to
storytold/craft-libs 82a24b006eefa036b43c09a67fb9a3fbd17ffe63 (main after the merge) rather than the older 12281d07,
which predates resolve_absolute and the Windows colon refusal.
2026-10-17 12:03:10 -06:00
aiongg 62eb6627a6 Build: use craft-docpath from craft-libs 2026-10-17 02:21:02 +00:00
aiongg 346681561a Lightroom import: follow catalog paths only on this computer or the catalog's share 2026-10-16 17:44:52 +00:00
Brandon Thomas 9ca113defa Merge PR #1135 (rebased)
# Conflicts:
#	xtask/src/main.rs
2026-10-15 00:53:45 -04:00
Brandon Thomas f613aeba59 Merge PR #1176
# Conflicts:
#	crates/raw/src/vendor/arw.rs
#	docs/parity-checklist.md
#	docs/sony-lens-corrections.md
2026-10-14 23:54:06 -04:00
Brandon Thomas 9ca3cd0926 Merge PR #1135
# Conflicts:
#	Cargo.lock
#	Cargo.toml
#	README.md
#	crates/raw/Cargo.toml
#	crates/ui-egui/src/theme.rs
#	xtask/src/main.rs
2026-10-14 23:23:18 -04:00
Brandon Thomas 618b4c723a Merge PR #821
# Conflicts:
#	crates/ui-egui/locales/de.json
#	crates/ui-egui/locales/es.json
#	crates/ui-egui/locales/fr.json
#	crates/ui-egui/locales/ja.json
#	crates/ui-egui/locales/pt-br.json
#	crates/ui-egui/locales/ru.json
#	crates/ui-egui/locales/uk.json
#	crates/ui-egui/locales/zh-hans.json
#	crates/ui-egui/locales/zh-hant.json
2026-10-14 21:54:47 -04:00
Brandon ThomasandClaude Opus 5.5 16dbb90499 Release 0.8.0
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 09:34:55 -04:00
Alex ChambersandClaude Opus 5.5 ac677a2624 LR-EDIT-OPTICS-PROFILE: decode Sony shading, CA and distortion tables with settings and provenance; apply nothing new
Sony ARWs record shading, lateral chromatic aberration and distortion as a camera setting plus a radial
table in the raw image IFD (0x7031-0x7037), with copies in the encrypted SR2SubIFD (0x797c-0x7982).
They are now decoded for every body that records them and carried through RawInfo and RawImage as
`lens_metadata.sony` (types in crates/raw/src/lens_metadata.rs, decoder in
crates/raw/src/vendor/arw_lens.rs):

- Raw-IFD fields are the ones the TIFF reader parsed, which is what the image itself uses. A scan of
  the directory adds diagnostics only: repeated tags, and why an entry the reader skipped was dropped
  (values outside the file, an unknown type, past the reader's limits). SR2SubIFD fields are read
  from their first entry. Only the exact layouts are accepted: SHORT[1] or non-negative SSHORT[1]
  settings, SSHORT[17] headed by 16 for shading and distortion, SSHORT[33] headed by 32 for CA.
  Nothing is allocated from a count in the file.
- The raw IFD's copy wins. The SR2SubIFD copy fills in only for a tag the raw IFD lacks, never for a
  malformed one, and the two copies' agreement is recorded.
- Settings keep their number and their documented meaning, Unknown(value) otherwise (never Off).
- Framing evidence (stored frame, Sony and default crops, reference rectangle, coding, 3:2) and a
  per-component status. Only Applied components become opcodes.

Applied corrections are unchanged: the ILCE-7RM4A distortion conversion receives exactly the table it
received before, and no other body or component gains a correction. The ILCE-7CR distortion misses its
camera JPEG at 24 mm, and shading and CA semantics are not validated on any body; see
docs/sony-lens-corrections.md.

Locating the SR2SubIFD, now done for every ARW that has one, selects the SR2Private offset, length and
key with the TIFF reader's own rules, which lightcraft-tiff now exposes and builds its reader on
(directory_entries, entry_span, ParseOptions::budget_for, decode_value). Only each field's first value
is decoded, so the directory's other entries cost no memory sized by the file: a 16 MiB file whose
SR2Private holds eight fields aliasing it peaks at 37 kB. DirectoryEntries keeps its layout private,
and its positions() iterator uses checked arithmetic.

Tests: synthetic decoder tests; a differential test of the SR2Private selection against the TIFF
reader's in both byte orders (0 to 8,193 filler entries, unreadable and unusual first occurrences of
each field, a used-up value budget); a dhat peak-heap test; public-corpus checks against the values
ExifTool prints, with pinned and anchored SHA-256s (LIGHTCRAFT_CORPUS_REQUIRED=1 makes absent files
fail); and an opt-in local inventory check (crates/raw/tests/sony_lens_inventory.rs).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 08:19:21 +02:00
Jason Baldwin 2faee15203 Merge slideshow-playback into slideshow-composer (origin/main: batch 32; toast clear of the slideshow controls) 2026-10-13 06:55:02 +02:00
Brandon ThomasandClaude Opus 5.5 701f78ea2e Release 0.7.1: fix the macOS build
eframe 0.36's App::on_exit takes the glow context as a second argument.
The macOS-only menu-bar Quit path still called on_exit(self), so the
0.7.0 macOS release job failed with E0061. Pass None; our on_exit ignores
it. macOS isn't built in CI, so only the release build caught it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 11:13:08 -04:00
Brandon ThomasandClaude Opus 5.5 25ef65c81e Release prep: 0.7.0
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 05:44:33 -04:00
Chad ElliottandClaude Opus 5.5 3e42f2e05c feat(testkit): #1037 tests record why they skip
libtest has no skipped status and hides the output of passing tests, so a
test that returns early without a GPU, a corpus file, a craft-fonts face or
a model passed silently. `lightcraft-testkit` (the testkit slot the layering
table already reserves, dev-dependency only, std only) adds
`skip!(Need, "reason")`: it prints the note as before and, when
LIGHTCRAFT_SKIP_LOG names a file, appends one JSON line (need, reason, test,
binary, file:line). Plain `cargo test` is unchanged. The 61 existing skip
sites use it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 16:33:54 -05:00
Jason Baldwin 2138175826 Merge slideshow-playback into slideshow-composer (origin/main: batch 31; locales merged keeping main's values, whats-new keeps main's Reliability entry, parity summary refreshed) 2026-10-11 17:11:12 +02:00
Jason Baldwin efaa39b8b5 Merge slideshow-playback into slideshow-composer (origin/main: geocode + slideshow crates side by side in the manifests, locale keys from both, tracker summary refreshed) 2026-10-11 16:22:50 +02:00
Claude f4a205b4ff M-export: pin jixel to =0.3.4
Young crate with a fast release cadence; take updates deliberately.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BKWB9TCnvzJHV6xMLAj2J4
2026-10-11 12:41:00 +00:00
Claude 4a0d04b994 Merge PR #780
JPEG XL export via the pure-Rust jixel crate (robmdunn; fixes #247).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BKWB9TCnvzJHV6xMLAj2J4
2026-10-11 12:40:55 +00:00
Claude 37b26c56a3 Merge batch 29b (#946, #947, #953, #950)
# Conflicts:
#	Cargo.toml
#	crates/engine/src/rename.rs
#	crates/ui-egui/locales/de.json
#	crates/ui-egui/locales/es.json
#	crates/ui-egui/locales/fr.json
#	crates/ui-egui/locales/ja.json
#	crates/ui-egui/locales/pt-br.json
#	crates/ui-egui/locales/ru.json
#	crates/ui-egui/locales/uk.json
#	crates/ui-egui/locales/zh-hans.json
#	crates/ui-egui/locales/zh-hant.json
#	crates/ui-egui/src/icons.rs
#	crates/ui-egui/src/lib.rs
#	crates/ui-egui/src/menubar.rs
#	crates/ui-egui/src/menus.rs
#	crates/ui-egui/src/panels/left.rs
#	docs/whats-new.md
2026-10-11 12:08:44 +00:00
Claude 10ebeccdc8 Merge PR #950
# Conflicts:
#	crates/ui-egui/locales/de.json
#	crates/ui-egui/locales/es.json
#	crates/ui-egui/locales/fr.json
#	crates/ui-egui/locales/ja.json
#	crates/ui-egui/locales/pt-br.json
#	crates/ui-egui/locales/ru.json
#	crates/ui-egui/locales/uk.json
#	crates/ui-egui/locales/zh-hans.json
#	crates/ui-egui/locales/zh-hant.json
#	crates/ui-egui/src/lib.rs
#	crates/ui-egui/src/panels/dialogs.rs
#	crates/ui-egui/src/state.rs
#	docs/parity-checklist.md
2026-10-11 12:06:31 +00:00
Claude 76fc27a7bc Merge PR #791
# Conflicts:
#	Cargo.lock
#	apps/lightcraft/src/main.rs
#	docs/parity-checklist.md
#	docs/whats-new.md
2026-10-11 12:01:19 +00:00
Claude 17dfa9709e Merge PR #926
# Conflicts:
#	Cargo.lock
#	Cargo.toml
#	crates/ui-egui/locales/de.json
#	crates/ui-egui/locales/es.json
#	crates/ui-egui/locales/fr.json
#	crates/ui-egui/locales/ja.json
#	crates/ui-egui/locales/pt-br.json
#	crates/ui-egui/locales/ru.json
#	crates/ui-egui/locales/uk.json
#	crates/ui-egui/locales/zh-hans.json
#	crates/ui-egui/locales/zh-hant.json
#	xtask/src/layers.rs
2026-10-11 11:57:45 +00:00
Claude b8d39063db Merge PR #930 2026-10-11 11:53:12 +00:00
Andrew FrahnandClaude Opus 5.5 b28b05c475 Reverse geocoding: fill City / State / Country from GPS (offline GeoNames)
New crate lightcraft-geocode (L1): parses the GeoNames cities dump and finds the
nearest place within 50 km through a 1-degree cell index; NaN / out-of-range
positions and malformed lines find nothing. Engine commands
metadata.reverseGeocode (Photo menu, Info panel "Fill Location"; fills only
empty fields unless overwrite, one undo step, activity row for batches),
metadata.downloadPlaceNames (background download of the CC-BY 4.0 GeoNames
files into the settings folder, cancellable activity row) and
metadata.placeNamesStatus. No data is bundled; NOTICE records the licence.

Fixes #582.

Sources: GeoNames dump readme (prose); haversine (textbook); PKWARE APPNOTE
(ZIP, prose); cell index own design.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-11 20:49:36 +11:00
lloydandClaude Opus 5.5 9815ca1310 Merge upstream/main into fix-export-saturation
Brings the branch up to date with main (#869 shared docking and the docs it
added); no conflicts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 11:40:04 +02:00
Andrew FrahnandClaude Opus 5.5 fe6d7b0192 Content-aware Remove: inpaint crate (PatchMatch completion) and a Fill spot mode
New `inpaint` crate (L1): multi-scale patch-based completion with a PatchMatch
nearest-neighbour field, seeded by a membrane fill. Deterministic, no panics on
hostile sizes, cancellable. Ported from PhotoCraft (MIT OR Apache-2.0).

Remove tool: a fourth spot mode, `fill` ("Content-Aware"), rebuilds the spot
from patches of its surroundings instead of one source; coarse-to-fine, so the
structure is chosen at a scale set by the spot size and preview and export agree
(tested). spot.add leaves its source unset.

Sources: C. Barnes et al., PatchMatch (SIGGRAPH 2009); Y. Wexler, E. Shechtman,
M. Irani, Space-Time Completion of Video (TPAMI 2007); P. Perez et al., Poisson
Image Editing (SIGGRAPH 2003) for the membrane fill; code ported from PhotoCraft
crates/algo/src/inpaint.rs + poisson.rs (a680ed22).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-11 19:30:42 +11:00
Andrew FrahnandClaude Opus 5.5 00ea386f7f lensdb: lens distortion (poly3/poly5/PTLens), TCA (linear/poly3) and vignetting (pa) profiles, lensfun-format loader
Profiles are interpolated in focal length (vignetting also in aperture and
focus distance), scaled by crop factor, and converted by least squares to the
DNG WarpRectilinear / FixVignetteRadial polynomials the pipeline already
applies. The lensfun database (CC-BY-SA) is only read from a directory the
user supplies; nothing is bundled.

Sources: lensfun's published database format and lens calibration
documentation (no lensfun code read), PanoTools wiki (a, b, c distortion
model), DNG specification 1.6 (WarpRectilinear, FixVignetteRadial);
interpolation and fitting: own design.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-11 19:30:18 +11:00
Jason Baldwin 1b8cb436a8 Merge slideshow-playback (with main) into slideshow-composer
# Conflicts:
#	crates/ui-egui/locales/fr.json
#	crates/ui-egui/locales/uk.json
2026-10-11 06:40:14 +02:00
robmdunnandClaude 77fcd81dde Merge main into jxl-export
ROADMAP.md: take main's restructured version; JPEG XL export is now
recorded in docs/file-format-parity.md and docs/gaps.md (gap 25).

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-10 21:32:39 -04:00
skylar taylor-barrick a7e8f1cc26 UI.9: reconcile upstream before shared UI landing 2026-10-10 19:07:51 -06:00
Brandon ThomasandClaude Opus 5.5 9e6c4c6fc9 Release prep: 0.6.0 and Linux riscv64 release build (#844)
- release.yml: new linux-riscv64 job (ported from GridCraft): cross-compiles on
  ubuntu-24.04 against ports.ubuntu.com riscv64 libs, packages a tar.gz with
  package.sh --formats tar, smoke-tests lightcraft-cli under QEMU. The release
  job needs it, so its tarball lands in the draft and SHA256SUMS.
- packaging/linux/package.sh: CROSS_ARCH/CROSS_TARGET/CROSS_COMPILE/EMULATOR
  support (target dir, cross strip, emulated --version).
- README Downloads and docs/releasing.md list the riscv64 tarball.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 19:38:10 -04:00
skylar taylor-barrick 9bfabc98b1 UI.9: integrate shared docking and preserve panel interactions 2026-10-10 16:22:21 -06:00
Jason Baldwin ae033aaf32 LRC-SS-LAYOUT: slideshow layout, options and backdrop: slides composed with margins, stroke, shadow and backdrop
- New crate lightcraft-slideshow (L1, no UI): SlideSettings (margins linked or
  independent, guides, aspect preview, zoom to fill, stroke border, cast shadow,
  backdrop colour, colour wash, background image; lengths relative to the slide's
  shorter side; validated and clamped) and a CPU compose() used by playback, the
  Settings preview and slideshow.preview.
- Engine: slideshow.layout (partial merge, saved in prefs.json) and
  slideshow.preview {id?, width?, height?, path?} (PNG to path or base64);
  RenderJob::with_slide composes on the render worker; the background photo is
  rendered once and shared.
- Playback: with a non-default layout each slide is the composed screen-sized
  slide, prepared ahead like before; the default layout plays as before.
- Settings > Slideshow: Layout, Options and Backdrop sections and a debounced
  live preview of the active photo's slide (with margin guides).
- Tests: composer geometry/pixels/hostile input, command get/set/persist/preview,
  headless playback of a composed slide and of the default layout.
2026-10-10 21:47:42 +02:00
lloydandClaude Opus 5.5 d7eb6b2ab4 LR-PREF-DISPLAY: macOS previews follow the screen's profile, so they match exports (#787)
Previews looked more saturated than the exported files on Mac screens. The
pixels are the same: preview and export match to within one level on the CPU
and GPU paths, and exports embed a correct sRGB profile. But wgpu leaves the
window's Metal layer without a colour space for sRGB surfaces, and Core
Animation shows such a layer without colour matching ("If nil, no
colormatching occurs", CAMetalLayer.h). On a P3 screen the sRGB previews
were therefore shown too saturated, while Preview and Photos convert the
exports properly.

While no monitor profile is chosen in Settings > Display, the desktop app now
shows previews through the profile of the screen the window is on
(NSWindow.screen.colorSpace.ICCProfileData, safe objc2 calls), through the
existing display-profile path. It is read again about once a second, so
moving the window to another screen is followed. A chosen profile still wins;
"Use Screen Profile" goes back to it. app.displayProfile reports it as
path: null, screen: true. Other platforms and headless sessions are
unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 19:51:01 +02:00
robmdunnandClaude Opus 5.5 afbc0f9744 LR-EXP-TYPE: JPEG XL export (#247)
Export as JPEG XL from the dialog, CLI, MCP and control channel (format jxl): lossy with
quality or lossless, 8/16-bit, effort fast/normal, progressive, EXIF/XMP boxes; HDR edits
as 16-bit Rec. 2020 PQ. Encoder: jixel (pure Rust, BSD-3-Clause OR Apache-2.0, native
only: it spawns its own threads), wrapped in catch_unwind. Lossy files declare an enum
encoding (sRGB / P3 / Rec. 2020; Adobe RGB and ProPhoto written as Rec. 2020); lossless
files embed the ICC profile.

Decoder fix: jxl-oxide desaturated colours outside sRGB when rendering XYB images to
wider primaries; lossy wide-gamut JXL now renders to linear sRGB primaries and is
converted to Rec. 2020 here. jixel's slowest effort is left out: jxl-oxide misdecodes
its (valid) files.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 12:19:30 -04:00
Luniiya 28a773d460 Merge remote-tracking branch 'upstream/main' into feat/system-appearance
# Conflicts:
#	crates/ui-egui/locales/de.json
#	crates/ui-egui/locales/es.json
#	crates/ui-egui/locales/ja.json
#	crates/ui-egui/locales/pt-br.json
#	crates/ui-egui/locales/ru.json
#	crates/ui-egui/locales/zh-hans.json
#	crates/ui-egui/locales/zh-hant.json
#	crates/ui-egui/src/lib.rs
#	crates/ui-egui/src/state.rs
#	crates/ui-egui/src/theme.rs
#	docs/control-protocol.md
#	docs/parity.md
#	docs/whats-new.md
2026-10-10 11:31:38 +02:00
Luniiya 216efab838 UI: Add system appearance and light themes 2026-10-10 10:18:31 +02:00
akvaithiandClaude Opus 5.5 7828f93c32 LR-HDR-EXPORT: HDR AVIF (10-bit Rec. 2020 PQ)
`encode_avif_pq`: linear Rec. 2020 with SDR white at 1.0 → PQ (SDR white at
203 cd/m², BT.2408), BT.2020 NCL YCbCr 4:4:4, 10-bit, with CICP nclx
(9/16/9, full range) and a clli box. ravif fixes the sRGB curve, so this
drives the rav1e + avif-serialize it already uses (no new crates).

Export: AVIF with `hdr` renders the HDR edit in Rec. 2020 and writes PQ.
Photos without an HDR edit export as before, in every format. CoreImage
decodes the file as HDR (same 3.1 % of the frame above SDR white as the gain
map JPEG of the same edit).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 22:40:37 -05:00
Brandon ThomasandClaude Opus 5.5 1cc0dde747 Bump version to 0.5.0 (from 0.4.0) for release; refresh contributors
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 21:02:18 -04:00
Brandon ThomasandClaude Opus 5.5 c65065de2f Merge the faces stack (#172/#173/#174, integrate-batch-14) into AI RAW denoise (#384)
One config-dir helper (engine::config::config_dir; camera_profiles re-exports it) with both
the face-models and denoise-models defaults; hosts set up both model folders; Settings has
Faces and AI Denoise tabs; both command sets registered; both model dialogs; locale entries
from both; one [features] table per app manifest; Cargo.lock regenerated by cargo; the
DecodedSource cache cost counts both segmentation mattes and the denoised twin.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 00:29:59 -04:00
Brandon Thomas a628d5bc38 Merge PR #427
# Conflicts:
#	ROADMAP.md
#	docs/parity.md
2026-10-08 23:46:58 -04:00
Brandon Thomas 42f966eec2 Merge PR #335 2026-10-08 23:46:17 -04:00
Daniel Voshart fd4019300a LR-EDIT-DETAIL-DENOISE: address installation and model-policy review
Generated with Codex.
2026-10-08 22:07:23 -04:00