Commit Graph
116 Commits
Author SHA1 Message Date
FredApps 36b244286a Merge remote-tracking branch 'origin/main' into pr-3073
# Conflicts:
#	docs/gaps.md
2026-11-04 12:11:30 +01:00
Dylan M. Taylor 4d2fa1269b lightcraft-cli: calibrate walks folders with the engine's bounded walk (#3643)
* lightcraft-cli: calibrate walks folders with the engine's bounded walk

* lightcraft-cli: calibrate keeps only regular raw files
2026-11-03 11:51:22 -05:00
06c1b9ae07 Use crw craftlib (#3503)
* CRW-LIB: decode Canon CRW through the shared craft-crw crate

LightCraft decodes Canon CRW (the CIFF raws of the EOS D30, D60, 10D, 300D and the
PowerShot G1/G6) through craft-libs' `craft-crw`, the clean-room decoder derived and
verified on the `clean-room-crw-format-decode` branch and moved there with its frozen
tables and derivation record. `crates/raw/src/vendor/crw.rs` is now the adapter: it maps
the crate's header and samples onto `RawImage` (CFA, levels, active area, orientation,
as-shot white balance, the make/model/time records a CRW keeps instead of Exif) and
converts the capture time through chrono. The dependency is a local path
(`../craft-libs/crates/crw`) until the craft-libs branch merges; then it becomes a git
revision.

Everything LightCraft-side from that branch comes along unchanged: `probe_bytes` fills
metadata gaps from the decoder's records so CRW imports name their camera; the EOS D60
bundled camera profile and its chart-measured dual-illuminant matrices (`chart_table.rs`,
`spectral::Camera` carries its illuminant pair) give the D60 a Kelvin white-balance scale;
CRW joins the JPEG-fitted starting look, the catalog's raw formats and `lightcraft-cli
calibrate`; `RENDER_CACHE_VERSION` 71; the CC0 CRW corpus entries for `cargo xtask corpus`;
coverage, parity, gaps, raw-parity, target-app-parity and ROADMAP updates. The G5 stays
preview-only (the crate refuses it with the reason) and the robustness test keeps its CRW
sample.

What does not come along: the decoder, its tables and `crw_oracle.rs` (now craft-crw's
digest-pinned sample test) and the derivation record, which lives in craft-libs
`crates/crw/clean-room/`. `clean-room/crw/` here keeps only the EOS D60 chart-calibration
document and its capture list, with a README pointing at the record.

Gates: fmt, clippy -D warnings (raw all targets; engine, catalog, cli), lightcraft-raw tests
(475 + 134, the corpus files decoding through the adapter), catalog tests, engine lib tests
(1,300 pass; the two failures, `tests_import::recursive_discovery_…` and
`originals::tests::write_targets_pin_absent_paths_…`, are in files unchanged from main),
`cargo xtask parity`, `assets`, `layers`, `coverage.py --check`.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* no message

* no message

* Update documentation after moving code to craft-libs

* Review coment fixes

* CRW: pin craft-crw f3b62ec, a truncated entropy stream is Corrupt

craft-libs #472 makes craft_crw::decode refuse a stream that ends more than
Limits::max_stream_end_fill (64 samples, one block) before the frame does,
instead of filling the rest with predictor values and returning success.
The adapter already maps craft_crw::Error::Corrupt to RawError::Corrupt;
a regression test proves a truncated CRW now fails at this boundary (it
decoded as a flat frame on the old pin) while its header still reads.

Only craft-crw moves; craft-ui/docpath/update/window stay on c5a1cbf.

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

* Code review comment fix

* Add some tests

---------

Co-authored-by: Robert Woldberg <314159+rwoldberg@users.noreply.github.com>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-02 20:41:34 -05:00
OneSlipsandClaude Opus 5.5 29443e117c LR-EDIT-OPTICS-PROFILE: merge main (#3430 integration round 3, #3390)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-01 03:45:27 +10:00
aiongg ee0a627536 Merge track r3-t10
# Conflicts:
#	crates/engine/src/cmd/masks.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/ka.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-hant.json
#	docs/gaps.md
#	docs/parity-checklist.md
#	docs/whats-new.md
2026-10-31 02:31:38 +00:00
aiongg 057ef1fd27 Integration: #3006 use binary() in the non-finite --set test 2026-10-31 02:17:56 +00:00
aiongg 7e68f1a31e Merge PR #3124: LC-003: reject non-Unicode CLI arguments without panicking 2026-10-31 02:06:54 +00:00
aiongg b1f3e00f3c Merge PR #3123: LC-001: stop commands after failed imports 2026-10-31 02:06:53 +00:00
aiongg 328e42373c Merge PR #3122: LC-002: reject non-object script parameters 2026-10-31 02:06:53 +00:00
aiongg 15a046c537 Merge PR #3098: Issue #3070: the CLI's refused-connection tests use a free port, not 18437 2026-10-31 02:06:53 +00:00
aiongg 6ad10a6bf4 Merge PR #3006: CLI-HOSTILE: render --set refuses inf / NaN instead of dropping the value 2026-10-31 02:06:53 +00:00
aiongg 035564c828 Merge PR #2963: Accept a UTF-8 BOM at the start of lightcraft-cli --script files 2026-10-31 02:06:52 +00:00
Chad ElliottandClaude Opus 5.5 e407ed0e3d test(cli): #3070 the tests that release an ephemeral port before use take turns
unused_port() lets its port go before the CLI connects, and the denoise-serve test
does the same before starting its server: with several test threads the server could
start on a port a refused-connection test had picked, which then reached it instead
of being refused. The three tests now hold one lock for their whole run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 13:02:10 -05:00
Chad ElliottandClaude Opus 5.5 54193e7cb8 test(cli): #3070 refused-connection tests use a free port, not 18437
The `--connect` failure-hint tests connected to 127.0.0.1:18437 and assumed
nothing listened there, but agents give their apps' control servers ports in
18000-19999. With an app on 18437 the tests sent it `ui.inspect` and failed.
They now bind 127.0.0.1:0, drop the listener and use that port, and check the
`--control <port>` hint against it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 13:02:10 -05:00
Dominic Anaeto 3d357617c4 CLI: cover invalid Windows arguments 2026-10-27 18:05:24 -04:00
OneSlipsandClaude Opus 5.5 8793fa83f8 Issue #136: merge main (upstream file split)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 06:37:52 +10:00
OneSlipsandClaude Opus 5.5 f267ddb504 LR-EDIT-OPTICS-PROFILE: pinned profiles, whole-folder install, shared hosts (review, #3073)
Render caches: a database lens profile is pinned to the photo. A decode never
overrides the catalog's database profile (media::source::pinned_lens), and
the profile is part of the render, thumbnail and variant cache keys
(media::quick::lens_key; only database profiles with the correction on, so
existing thumbnails keep their keys). When a download replaces the
database's files (lens_profiles::replaced), the refresh also re-reads raws
whose profile came from the database and raws that named no lens; an
ordinary start does not. library.refreshLensProfiles takes {all: true}.

Installation: files are staged beside the database folder, SOURCE.txt is
written there last, and the staged folder replaces the old one as a whole;
a failed swap puts the previous database back (and recover() does at the
next start). The app's own folder is read only with its marker, and
installed() checks every pinned file at its pinned size.

Shared hosts: with_default_lens_database() only selects the existing folder;
the permission to fetch is with_lens_auto_download() (desktop only), and the
first-start decision is one function, lensfun_download::fetch_on_start.
CLI render / merge and the headless MCP server select the folder;
lens.downloadDatabase selects the folder it downloads into.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 03:20:10 +10:00
Dominic Anaeto a55218d221 CLI: select each imported photo once 2026-10-27 11:12:10 -04:00
OneSlipsandClaude Opus 5.5 f7ebead155 LR-EDIT-OPTICS-PROFILE: merge main
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 13:09:15 +10:00
Dominic Anaeto 939055897f LC-003: reject non-UTF-8 CLI arguments cleanly 2026-10-26 23:05:54 -04:00
Dominic Anaeto b576c72d0b LC-002: reject invalid script parameters 2026-10-26 22:59:44 -04:00
Dominic Anaeto 05ff4ca06d LC-001: stop commands after failed imports 2026-10-26 22:59:41 -04:00
skylar taylor-barrick 662df66f1c Preserve queued file drops across dialogs and restore Flatpak sources (#2975) 2026-10-27 03:50:41 +01:00
OneSlipsandClaude Opus 5.5 030e0edd0a LR-EDIT-OPTICS-PROFILE: catalog format 10 for the lens source, snapshots stay offline
- EmbeddedLens.source is a serialized field an older build would drop:
  catalog format 9 -> 10 (journal table row, NEWEST_FIELDS in the
  version guard).
- lightcraft-cli snapshot reads a downloaded lens database but never
  starts the download (scripts and tests stay offline).
- The staging folder name and file cap are native-only (unused in the
  web build).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 02:05:10 +10:00
OneSlips 2ec4add0d6 LR-EDIT-OPTICS-PROFILE: lensfun database downloaded on first run, library looked up, Optics names the profile
- lensfun's data/db (CC BY-SA 3.0) is fetched by the desktop app on first
  run into <config dir>/lensfun, pinned to commit 43f9e001 by name, size and
  SHA-256, staged until every file is verified, with an activity row (never
  bundled). LIGHTCRAFT_LENSFUN_URL lists other mirrors; LIGHTCRAFT_LENSFUN_DIR
  still wins. lens.downloadDatabase / lens.databaseStatus for agents.
- The database is read per folder and again after a download; when it
  changes, library raws without a profile whose lens it knows are probed in
  the background and get it (untouched photos switch the correction on as
  import would); ticking Enable Lens Corrections looks those photos up again
  (library.refreshLensProfiles).
- EmbeddedLens records its source (file / lens database); the Optics panel
  says "Profile: <lens> (lens database)", "Profile: from the file" or
  "No lens profile found for <lens>." (13 locales).
- Tests: pinned download and refusal of a wrong file, the lookup and its
  edited / untouched rules, and presets / Copy-Paste / Sync carrying only the
  switch and amounts, each photo keeping its own lens's profile.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 773740fbb22eb66e7f62bada9cb32b594b6ff4d7)
2026-10-27 01:14:00 +10:00
Andrew FrahnandClaude Opus 5.5 2085426230 CLI-HOSTILE: render --set refuses inf / NaN instead of dropping the value
`inf`, `NaN` and `1e999` parse as f64 but serialize to JSON null, so the control was
silently skipped and the photo rendered without the requested edit. They are now an error.
Hostile-input check added to the render subcommand test.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 11:13:34 +11:00
OneSlips 23c669ece5 Merge remote-tracking branch 'origin/main' into pr/gpu-setting-no-wait 2026-10-26 05:04:58 +10:00
NetterSilverandClaude Haiku 5.5 286154a17c Accept a UTF-8 BOM at the start of lightcraft-cli --script files
Fixes #2937

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-25 17:16:44 +00:00
OneSlipsandClaude Opus 5.5 9a6ac9aea7 Issue #136: a non-waiting GPU query starts the device; Settings repaints while it starts
- `app.gpu {wait: false}` with GPU rendering on starts creating the device in the background
  (`warm_up`), so a caller that never waits still gets an answer later. `warm_up` starts one
  thread at most, and may start again if GPU rendering was turned off before it ran.
- Settings > Performance asks for a repaint every 250 ms while it shows "GPU: starting…": the
  device is created on another thread, which doesn't wake the window.
- CLI test: `{enabled: true, wait: false}` answers enabled with available, adapter and reason
  null (skips with LIGHTCRAFT_GPU=0, since it starts creating a device).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 13:48:25 +10:00
OneSlips 1db9140028 Merge remote-tracking branch 'origin/main' into pr/gpu-setting-no-wait 2026-10-25 05:15:13 +10:00
OneSlipsandClaude Opus 5.5 32e55afee8 Issue #136: app.gpu {wait:false} still says why the GPU is off; Settings shows "GPU: starting…" while the device is created
Review follow-up: with GPU rendering off (preference, environment, earlier failure) the
no-wait reply now reports available=false and the reason at once, without creating the
device. The Performance tab no longer calls gpu::available() while the device is still
being created, which could block the UI thread.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 05:15:13 +10:00
skylartaylor 50df646183 M0.37: port LightCraft UI, input and capture lifecycle to egui 0.37 2026-10-24 08:02:12 +02:00
Brandon Thomas c342c480cf Merge PR #2665 2026-10-23 17:21:16 -04:00
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
Clyde C BrownandClaude Opus 5.5 22c261134d #2663: lightcraft-cli commands: list commands sorted by id
`lightcraft-cli commands` (text and --json) now sorts the registry by id
before printing, so each family's commands sit together. The engine's
registry order, used by engine.commands over MCP and the control channel,
is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 22:02:34 -06:00
Marco Arnulfo e14db89462 LR-EXP-BITDEPTH: app.export refuses a bitDepth the format doesn't have (part of #1952) 2026-10-16 02:21:07 +02:00
Brandon Thomas b3012745d5 Merge PR #1215 (rebased)
# Conflicts:
#	docs/gaps.md
#	docs/parity-checklist.md
2026-10-15 00:52:31 -04:00
Brandon Thomas b29a1a8cc2 Merge PR #1215
# Conflicts:
#	crates/engine/src/lib.rs
#	crates/engine/src/pdf.rs
#	crates/ui-egui/src/lib.rs
#	crates/ui-egui/src/menus.rs
#	crates/ui-egui/src/panels/dialogs.rs
#	crates/ui-egui/src/state.rs
#	docs/gaps.md
#	docs/parity-checklist.md
2026-10-15 00:22:08 -04:00
Brandon ThomasandClaude Opus 5.5 1a120a86a2 Merge PR #1761
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 23:59:13 -04:00
Brandon Thomas fe2ba9191a Merge PR #1791 2026-10-14 22:31:46 -04:00
Brandon Thomas 9d45f6b168 Merge PR #1733 2026-10-14 22:29:37 -04:00
Brandon Thomas a8f0f5d60b Merge PR #1715
# Conflicts:
#	docs/gaps.md
#	docs/parity-checklist.md
#	docs/target-app-parity.md
2026-10-14 22:29:33 -04:00
Brandon Thomas 9107ea4bdf Merge PR #1688 2026-10-14 22:29:25 -04:00
midasmatterhorn b0241748ee cli: refuse non-object snapshot params instead of panicking
A script line for `ui.screenshot` without a `path` had its generated path
assigned with `params["path"] = …`: `serde_json`'s mutable index panics on
an array, a string or a number, and the CLI installs no `catch_unwind`, so
a script handed to the documented headless workflow aborted the process.
Only an object (or null, like an absent `params`) takes the path now;
anything else is a script error naming the line, like the existing handling
of a malformed line.

The regression test runs the real binary on such a script and asserts a
non-zero exit, no "panicked" in stderr and the `params` must be a JSON
object message (it fails before the fix with `cannot access key "path" in
JSON array`).
2026-10-14 23:12:32 +03:00
Nathaniel LedfordandClaude Opus 5.5 18e808c08f LR-BEHAV-EMPTY-STATES: an empty library leads to Import
A library with no photos opened on the Detail view, whose empty state said "Choose a photo in
the grid or filmstrip" while the grid was empty and hidden: the only screen that mentioned
import was off screen, and Import itself was nowhere but the File menu and its shortcut.

- An empty library (no photos, or only deleted or browsed ones) opens on the Photo Grid with the
  sidebar (Square Grid stays as it is), in the desktop app and in `lightcraft-cli snapshot`. The
  desktop app switches before it notes what its settings file holds, so the switch alone isn't
  saved.
- The grid and the Detail view show "Your library is empty" with Import Photos… (primary) and
  Import Lightroom Catalog… (not on the web) as buttons, and a line saying photos and folders
  can be dropped into the window, with the key Import Photos… is bound to now (`⌘⇧I` on macOS,
  `Ctrl+Shift+I` elsewhere, or a remapped one). Not while a folder is browsed under Local or
  Recently Deleted is shown: their photos aren't in the library but are on screen.
- The right-hand panel says "No photos yet / Import photos to get started" there instead of
  "Select a photo to edit".
- The top bar has an Import button beside Export in every library. The centred search field's
  reserve grows by both sides' share (620, and a 190 minimum), so the filter icon stays clear of
  the icons on the right at every window width, also with macOS's title bar in the window. The
  top bar's icons are named for screen readers, and a refused click says why in a toast.
- `empty_state` is `empty_message` with buttons that do the next step.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 14:03:50 -04:00
Marco Arnulfo f360ad949d render --settings applies the preset in a .lcpreset, and refuses files it can't read (fixes #1730) 2026-10-14 16:25:30 +02:00
dev-null-undefinedandClaude Opus 5.5 b52762797f LR-MERGE-HEADLESS: HDR for Each Bracket Set
Photo ▸ Photo Merge ▸ HDR for Each Bracket Set (`merge.hdrBatch`) merges many brackets in one go with the last HDR
options, as Lightroom merges a selection of stacks: one HDR per stack among the photos, then per bracket among the
unstacked ones (`Catalog::bracket_sets`: photos taken less than about a second after the previous one's shutter
closed, at exposures at least ½ EV apart; bursts at one exposure and earlier merge results are left out). The app runs
the merges one after the other in the activity stack (cancelling one stops the rest; a failed set doesn't); the
command runs them inline for the CLI and MCP (`lightcraft-cli merge hdr-batch FILES…`) and reports merged and failed
sets.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 13:37:13 +02:00
euisuhandOpenAI Codex 707aa5aa92 fix(mcp): stop stdio cleanly after output failure
Wake the serving loop after protocol output fails and preserve the actual writer error. Use one process-owned stdin worker so the CLI can finish normal backend cleanup without waiting for client EOF, and terminate the output worker even when a progress hook is retained.

Closes #1527

Co-Authored-By: OpenAI Codex <noreply@openai.com>
2026-10-14 17:02:16 +09:00
Jason Baldwin d747c9e0f6 Merge slideshow-titles into slideshow-templates (origin/main: batches 32–33) 2026-10-13 17:01:24 +02:00
Atmik Shetty 6e5cd82ce7 Merge remote-tracking branch 'upstream/main' into feat/print-color
# Conflicts:
#	docs/gaps.md
#	docs/parity-checklist.md
2026-10-13 12:00:45 +05:30