Commit Graph
925 Commits
Author SHA1 Message Date
Alex ChambersandClaude Opus 5.5 f05a52968f LR-PROF-CAMERACOLOR: a tone curve from black-and-white camera JPEGs (#577)
A raw shot in a monochrome picture style records full colour, but its
camera JPEG is grey: neither the full look nor the partial look has
colour to fit, so the photo started from the spectral matrices or the
neutral fallback with the default tone curve, about 1.3 EV darker than
the same body's colour-shot photos. The grey JPEG still shows the
camera's tone curve.

When no look was fitted and the JPEG is black and white (`monochrome`),
fit the quantile tone curve alone on top of the colour the photo gets
otherwise: the camera profile's (in `fit_preview`), else the spectral
matrices or the fallback (`monochrome_look`, after the spectral step).
The chroma curve stays neutral; the partial look's checks apply. A
tone-only look keeps the file's colour model for white balance.

LOOK_VERSION 2 -> 3, RENDER_CACHE_VERSION 42 -> 43.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 11:19:49 +02:00
Andrew FrahnandClaude Opus 5.5 80e58ed55b engine: lens profile corrections from a lensfun-format database
Raws without an embedded lens correction look their EXIF lens up in the
user-supplied database; the profile goes through the existing Enable Profile
Corrections path (amount sliders, CPU/GPU geometry stage, DNG export).
Synthetic-grid test renders a distorted chart through the pipeline.

Sources: own design (see lightcraft-lensdb for the model sources).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-11 19:30:18 +11:00
ingolfteetzandClaude Opus 5.5 4700c8a74e LR-IMP-FORMATS: decode Canon EOS R5 Mark II C-RAW (CRX QP-map first literal and escape)
The CRX 0x200 QP map opens with its first symbol as a 32-bit literal; every
later symbol is Rice coded with a limited-length escape: 23 zeros, the
terminating 1 and the symbol in 8 bits (the ITU-T T.87 layout for a 32-bit
limit and 8-bit symbols, without its minus-one offset). The old rule (an
escape after 16 zeros with a 16-bit literal) matched only the first symbol,
which is all the R8 and R7 maps need. R5 Mark II maps (QP 150-162) jump by up
to 12 where the Rice parameter is 0, giving 18- to 22-zero prefixes that
misparsed ("Canon CRX adaptive QP value -21087"): 174 of 306 sampled files
from a private R5 Mark II library opened as previews. Every QP map of its
33,665 C-RAW files now decodes to the exact end of its entropy stream; the six
oracle fixtures are unchanged. Established by black-box analysis of the
bitstream; no third-party decoder source consulted.

Adds the four CC0 R5 Mark II samples (raw.pixls.us 7881-7884) to the corpus
with pinned sensor hashes (the crop-mode framing is left to #754), bumps
RENDER_CACHE_VERSION to 43, and updates cr3.md, raw-parity (235 verified
models), coverage, gaps, the checklist rows, ROADMAP, target-app-parity and
What's New.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 09:20:49 +02:00
Alex ChambersandClaude Opus 5.5 484c160c13 LR-EXP-PHOTOS: Apple Photos imports in the activity stack (#345)
Rebased onto the activity stack (#601). An export adding its files to
Photos keeps its row through that step, with "Adding to Apple Photos…" as
the detail. The worker turns the row's ✕ off (TaskHandle::set_cancellable)
before its last look at the cancel flag and reports what it saw, so a
cancel either stops the export before Photos is asked or is refused, never
both "cancelled" and imported. activity: a per-task gate orders a cancel's
decision against a step switching cancelling off, so a cancel can't be
accepted between the switch and the step's look at the flag and then be
ignored. An import of its own (export.addToPhotos, or
the Photos step left running after a foreground app.export) holds a row of
kind applePhotos while Photos works: no ✕, no count, the album as detail.
The desktop app refuses export.addToPhotos {wait: true}: its window's
thread never waits for Photos. The label is in every catalog; tests assert
the rows, the refused cancel and the refused wait.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 09:19:00 +02:00
Alex ChambersandClaude Opus 5.5 c722e6bb64 LR-EXP-PHOTOS: Apple Photos fixes from the live test and review
- Ten minutes plus five seconds a file per import (was one minute): the
  first live run timed out while macOS's permission prompt was still up,
  and Photos' duplicates question waits for a person too.
- Files go to osascript 200 at a time, all within the import's one time
  limit: one run with every path went over macOS's 1 MiB argument limit
  at about 10,000 files, and Photos got nothing. A failed later batch says
  how many files Photos had added.
- The progress line "Adding to Apple Photos…" is translated on the UI
  thread; on the export worker it was always English.
- docs/apple-photos.md: who can add what (the control port is reachable by
  any local program, other users' included), batching, the new limit, and
  tested end to end (macOS 26.6.2, Photos 11.0, packaged app). The parity
  row says so; What's New has an entry.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 09:19:00 +02:00
Alex ChambersandClaude Opus 5.5 a6431c152d LR-EXP-PHOTOS: add exported files to Apple Photos (macOS, #236)
The Export dialog gets "Add to Apple Photos" with an optional album (macOS
only). app.export takes addToPhotos / photosAlbum (dialog, Export with
Previous, presets, MCP, lightcraft-cli run and render --opt); the new
export.addToPhotos {paths, album?, wait?} adds files already on disk and
export.photosImports {job?} reports how imports went.

A constant AppleScript run by /usr/bin/osascript asks Photos' import
command (Apple Events, not synthetic input) to import the files into a
top-level album of exactly that name (made when missing; albums in folders
are never used; several of that name: refused), skip check duplicates
false, and returns the media item ids. Album names and paths are passed
only as separate arguments (on run argv), never spliced into the script.
The runner is injectable (Session::apple_photos): with_fs installs the real
one on macOS only, and tests use fakes, so no test starts osascript or
Photos.

Imports are jobs (Session::apple_photos_imports), one at a time. The
desktop app (Session::apple_photos_background, set in open_session) never
waits for Photos on its UI thread: the background export adds on its
worker, app.export without background and export.addToPhotos answer with
the running job, and the app announces the outcome. Without a window (MCP,
lightcraft-cli) imports wait by default, and both headless front ends (the
MCP/CLI backend and the snapshot UI) wait for a running one before the
process ends, on every exit path, through one engine helper
(Session::wait_for_photos_imports) whose deadline follows the job: a
writing allowance while an export still writes, then the import's own
time limit from when Photos was asked, under an overall cap. A runner that panics, waiting or on a
worker, ends its import as failed and frees the slot (panic guard plus a
completion guard). Session::after_export reserves
the import slot before an export writes anything and frees it on every
exit path (handed the files, cancelled, failed, dropped), so a competing
import is refused with the export's job number instead of the export
failing its Photos step after writing. Permission (-1743), missing Photos,
cancel, timeouts, ambiguous albums, busy imports, other osascript failures
and partial imports produce actionable messages; the export's files are
kept either way. Hidden on other platforms; the web build compiles it out.

Packaging: the hardened-runtime entitlements gain
com.apple.security.automation.apple-events (app and CLI) and Info.plist
gains NSAppleEventsUsageDescription, without which macOS refuses the
events without asking.

Not yet tested end to end against a real Photos library: manual steps,
with the packaged app, in docs/apple-photos.md. Parity row LR-EXP-PHOTOS is
now partial.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 09:19:00 +02:00
Jason Baldwin 43f31532a7 LRC-LIB-CATALOG-IMPORT: Open Library recognises a Lightroom catalog and offers to import it (#891)
Open Library given a Lightroom Classic catalog folder, one of its .lrdata /
.lrcat-data folders, Backups/, the .lrcat or its -wal/-shm/.lock files used to
create an empty LightCraft library inside Lightroom's folder (or, for the
.lrcat file, close the current library and then fail). Now:

- engine: lightroom_folder::lightroom_catalog_at (names only, top-level scan
  bounded to 10 000 entries, never fails); Session::open_library refuses those
  places with EngineError::LightroomCatalog {path, catalog} before creating
  anything. A LightCraft library (catalog.snap / catalog.log) always wins.
- app: app.openLibrary checks before closing the current library, keeps it,
  returns {opened: false, lightroomCatalog, path} and shows a dialog with
  Import Lightroom Catalog… (file.importLightroom with the catalog found) and
  Cancel. The library problem window gets the error instead.
- Lightroom import status keeps the catalog path after the task ends.
- CLI --library explains how to import instead.
2026-10-11 08:40:18 +02:00
cookingwithsimonandClaude Opus 5.5 5bb43a51f7 LR-VIEW-ZOOM: sharp 1:1 in Compare and Reference (zoom windows per pane)
Compare and Reference drew each pane from a whole-frame render of at most
2560 px, so 1:1 was a magnified preview and focus could not be checked.
Each pane now also renders the window on screen from the photo's own
pixels (Slot::CompareRegion), as the loupe does (#323). The media cache
keeps up to two full-size originals while either view is open, so one
pane's window does not evict the other's source.

Docs: gaps.md entry 4 shrinks to overlays and soft proofing; checklist
LR-VIEW-ZOOM / LR-VIEW-COMPARE and ui-parity zoom row updated.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UyHnpE9573a7nJFQqnGNex
2026-10-11 05:55:48 +00:00
JordyUSAandClaude Opus 5.5 15f6718a2e LR-HDR-EDIT: read crs:HDREditMode from XMP into hdr.enabled
Lightroom's Light panel "HDR" switch (`crs:HDREditMode`, `1`/`True` on,
`0`/`False` off) now sets `hdr.enabled` when importing XMP sidecars, presets,
DNG presets and Lightroom catalogs, instead of being reported as unmapped (an
off value was already silent). The rest of the HDR settings keep their
defaults. Follow-up to #646, ported from #466.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 12:47:04 +07:00
JordyUSAandClaude Opus 5.5 7154ee149b LR-EDIT-HDR-MODE: HDR button next to B&W, as Lightroom has it
HDR editing is switched with an HDR button in the Edit panel header, beside
Auto and B&W and drawn the same way (the shared text_button: highlighted
while on), instead of a checkbox at the top of the Light panel. While HDR is
on, the Light panel still shows Visualize HDR and the headroom slider above
the light sliders; with HDR off they're hidden.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 12:46:28 +07:00
JordyUSAandClaude Opus 5.5 fb678b9e35 LR-VIEW-HISTOGRAM: HDR histogram for HDR edits
An HDR edit's preview shows its SDR rendition, so its histogram said nothing
about the highlights above SDR white. Now it shows the HDR rendition:
`pipeline::hdr_histogram` renders the edit small (384 px, float HDR, same
framing) and bins it with SDR on the left half and the HDR range on the right
(`Histogram::of_hdr`, `HdrBins`), on a fixed scale of Hdr::MAX_EV_LIMIT (5)
stops above SDR white so it doesn't rescale when the headroom limit changes.
The engine's `develop()` (CPU and GPU renders) and the web worker swap it in
for HDR edits; high-bit-depth, windowed and proofed requests keep the image's
histogram, and a display profile's histogram doesn't replace it. Web workers
carry the HDR bins across their wire (`Histogram::to_words` / `from_words`).

The panel draws it in Lightroom's layout: a solid line at SDR white, dashed
lines between the stops, the headroom limit as an orange line with the range
past it dimmed, a bar under the plot (white; one band per stop in Visualize
HDR range's colours while that is on), SDR / HDR labels and an info tooltip.
Follow-up to #646, ported from #466.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 12:46:27 +07:00
JordyUSAandClaude Opus 5.5 c9c0268f8b LR-HDR-EXPORT: say how viewers without cICP show a PQ PNG
Readers that don't read the PNG cICP chunk show the PQ samples as if they
were sRGB (flat, washed out): expected for PQ PNG, but it surprises people.
Noted in What's new (with the gain map JPEG as the look-right-everywhere
choice), as the export dialog's HDR PNG hint, and in the option and encoder
docs. Review suggestion on #664.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 12:46:27 +07:00
JordyUSAandClaude Opus 5.5 cbc0e2d7db LR-HDR-EXPORT: HDR PNG export as 16-bit Rec. 2020 PQ (cICP, cLLI)
With HDR output on, a photo edited in HDR exports as a 16-bit PNG with the PQ
curve in Rec. 2020 (SDR white at HDR_REFERENCE_WHITE_NITS), described by a
`cICP` chunk (PNG third edition: BT.2020 primaries, PQ, RGB, full range) and
a `cLLI` content light level chunk (the levels the HDR AVIF records). EXIF,
XMP and print resolution are kept; no ICC profile next to cICP. Photos without
an HDR edit, and HDR edits with HDR output off, export as before.

`codecs::encode_png_pq` sits next to `encode_avif_pq` and takes the same
input. Follow-up to #646, ported from #466.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 12:46:27 +07:00
Chad ElliottandClaude Opus 5.5 8c84677079 docs(windows): #399 note the pre-0.6 install-folder upgrade fix
Add a What's New entry for keeping the install folder when upgrading from a
pre-0.6 Windows installer, and describe the new Windows MSI job in the
Packaging lint workflow header.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 00:46:21 -05:00
Jason Baldwin c52ff0f014 Merge origin/main into albums-library-photos-only (0.6.0 and shared docking; locale catalogs: kept both sides' keys) 2026-10-11 06:40:00 +02:00
Claude 61fef5cfd5 Progress docs: gaps.md reviewed after 0.6.0 (R7 C-RAW, refused CRX variants, riscv64 build)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HXAV8vMWLidoocKuPEpUVk
2026-10-11 04:34:33 +00:00
sanyamsingh001andCodex 0b61450c7b LR-BEHAV-UNDO: keep file history consistent after failed renames
Co-authored-by: Codex <codex@openai.com>
2026-10-11 12:05:37 +10:00
goujichengandDeepSeek V3.2 bc2cbf41db LR-IMP-PREVIEW-READ: an unedited raw's grid thumbnail is its embedded JPEG
The grid already showed the camera's embedded preview first, then queued a render of the whole raw
to replace it. For a 512 px thumbnail that render decodes 50 of a 56 MB Fuji RAF's bytes for a
picture the 4 416 × 2 944 embedded JPEG already covers — 45 s a photo on the 1.2 MB/s share this was
measured on, 8.3 GB of traffic for a 149-photo folder, and a queue that delayed the render the user
was actually waiting for (measured: the first loupe render of a cold photo finished 174 s after the
click, most of it spent behind thumbnails). Idle for a minute afterwards, the same folder read 12
previews (26.9 MB) and decoded zero raws for thumbnails, against 638 MB of whole-file reads before.

- `Session::embedded_thumb_is_final` answers whether a photo is an unedited raw whose camera JPEG the
  viewer is showing — the same `has_import_look` gate the preview path uses.
- `grid::skips_the_real_render` is the decision (unit-tested): a cached render as stand-in, an edited
  photo and a raw without an embedded JPEG all still render, because their thumbnail must show the
  pipeline's output rather than the camera's.
- `Renderer::remember_embedded_thumb` records the quick render's key and the bucket, and
  `thumb_quick_is_final` / `thumb_quick_pending` let the per-frame grid request stop at the embedded
  preview instead of rebuilding the render job (and its cache key) every frame.
- The thumbnail key still follows the settings hash, so editing a photo re-renders its thumbnail
  through the ordinary background path.

`docs/raw-preview-reads.md` and the `LR-IMP-PREVIEW-READ` parity row record the behaviour and the
numbers.

Co-Authored-By: DeepSeek V3.2 <noreply@deepseek.com>
2026-10-11 09:39:32 +08: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
john01px2022andClaude Opus 5.5 6807421f64 LR-CROP-STRAIGHTEN: the rotate pointer faces away from its side of the crop box
The crop tool's rotate pointer always bulged up, which only reads right
above the box. It now turns to bulge away from the nearest point of the box
outline: right beside the right edge, down below it, left beside the left
edge, diagonally off a corner, and following a box that is already rotated.
icons::paint_turned draws an icon turned about its centre; icons::paint is
unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 17:09:44 -07: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
john01px2022andClaude Opus 5.5 6f44b33ca7 LR-EXP-WATERMARK: rotate the watermark in quarter turns (#827)
A watermark (text, horizontal or vertical, or a graphic) can be turned 0,
90, 180 or 270 degrees clockwise, like Lightroom Classic's rotate buttons.
It is sized before it is turned and placed and inset by the turned box; the
shadow still falls down and to the right. New watermark key `rotation`
(other values are errors); settings without it draw unturned. The Export
dialog gets a Rotate row.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 16:18:57 -07:00
john01px2022andClaude Opus 5.5 ffd737ceab LR-EXP-WATERMARK: separate horizontal and vertical watermark insets (#827)
Watermark.inset becomes inset_x (from the left or right edge) and inset_y
(from the top or bottom edge), like Lightroom Classic's watermark editor.
The watermark keys are insetX and insetY; the old inset key still reads as
both, so saved presets and existing MCP calls keep working.

The Export dialog's Inset slider becomes Horizontal inset and Vertical
inset, each greyed out when the position is centred along its axis.
cargo fmt reflows WM_POSITIONS and its test from the previous commits.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 16:11:03 -07:00
john01px2022andClaude Opus 5.5 aa6054cd1a LR-EXP-WATERMARK: preview the watermark in the Export dialog (#827)
With Watermark on, the Export dialog shows the first photo it will export
(its cached thumbnail) with the watermark drawn as the file will have it.
The watermark is redrawn on a copy only when it or the output size changes.

draw_watermark_preview scales the 6 px minimum text size and the 1 px
minimum shadow offset by the preview size over the export size, so a small
watermark doesn't look bigger in the preview than in the file. Exports draw
with a scale of 1 and are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 15:58:53 -07:00
skylar taylor-barrick 9bfabc98b1 UI.9: integrate shared docking and preserve panel interactions 2026-10-10 16:22:21 -06:00
PiMinusMaxandClaude Opus 5.5 40d7b86ecb LR-VIEW-BEFOREAFTER: click tools work in the Before view of side by side / top-bottom
In the side-by-side and top/bottom Before/After layouts a click in the
Before view was mapped to image coordinates through the After view's
rectangle, so the White Balance Selector sampled the wrong point (at Fit,
the photo's edge). The click tools in general_interaction now use the
Before view's image rectangle when the pointer is in the Before view's
pane; the pane decides, since zoomed in an image reaches past its pane.

New headless test tests_before_after.rs clicks the same image point in
the After and Before views (side by side, top/bottom; Fit and 100 %) and
expects the same white balance. Without the fix: Before 8202 K / +116,
After 2710 K / +33.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 00:11:49 +02:00
PiMinusMaxandClaude Opus 5.5 a67d571656 LR-VIEW-COMPARE: a filmstrip click shows the photo in Compare
In Compare a filmstrip click made the clicked photo active without
showing it, so the Edit panel and the rating keys acted on a photo on
neither side (a headless test rated the hidden photo 2 -> 3 stars). The
click now puts the photo into the active side (the candidate after
entering Compare, the select after clicking the select's photo); a photo
already on the other side swaps the two. The active photo is always one
of the two shown. Other views keep the usual selection clicks.

New UI command compare.set {id, side?: "select" | "candidate"} for the
same through the control channel and MCP.

New headless tests in tests_compare.rs: the active photo stays on screen
and no hidden photo is rated (Fit and 100 %), and replace / swap /
explicit side / unknown side.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 23:51:43 +02:00
eliets2 a940dab727 web: stop the release zip shipping Cargo build artifacts
lightcraft-web-<version>.zip carried Cargo host build output next to the site: deps/ (81 files, 90.6 MB), build/ (149 files, 22.4 MB) and .fingerprint/ (297 files), ~113 MB that the app never references and that leaked build paths and crate names to anyone hosting the zip.

Cause: xtask wrote the bundle to <target>/web (SITE in packaging/web/package.sh) while building with --profile web. Cross-compiling with --target wasm32-unknown-unknown makes Cargo write the selected profile host artifacts (build scripts, proc-macros, deps/, build/, .fingerprint/) to <target>/<profile>/, so they landed in the bundle directory and package.sh copied the whole tree into the zip.

Fix: write the bundle to <target>/web-site, a directory that is not a Cargo profile name, and point SITE at it. The wasm32/web/ target artifacts and the web profile are unchanged, so the rust-cache keys and the CI web job keep working.

Guard: package.sh now fails if deps/, build/ or .fingerprint/ exists in $SITE, and a unit test asserts the bundle directory is not a Cargo profile name, so the collision cannot come back silently.

Reference (#793)
2026-10-11 00:46:10 +03:00
PiMinusMaxandClaude Opus 5.5 8e9c5aff7e LR-EDIT-COLOR-WB-PICKER: the selector button does what W does
The White Balance Selector button in the Edit panel only armed the tool,
so in Compare (which doesn't handle on-image tools) it did nothing while
W opened the photo in Detail with the selector armed. The button now runs
tool.wbPicker like W; a second click still disarms it.

New headless test tests_wb_selector.rs clicks the button in Compare and
expects Detail with the selector armed on the photo that was active; it
fails without the fix (the view stays Compare).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 23:32:08 +02:00
Sajidur RahmanandClaude Opus 5.5 d6cfab8223 LR-EDIT-COLOR-GRADING: grading tabs, per-wheel eye, precision guides (#737, #559)
Color Grading gets 3-Way / Shadows / Midtones / Highlights tabs (3-Way by
default). 3-Way shows three equal wheels; a range tab shows one large wheel
with that range's Hue, Saturation and Luminance sliders. Blending and Balance
are in every tab.

Precision (#559):
- Shift-drag on a wheel locks the hue and changes saturation only; the whole
  spoke is drawn as a guide. Shift can be pressed or released mid-drag.
- A white dot marks each wheel's neutral centre.
- The large wheel has dashed 25/50/75 % saturation rings.
- 3-Way shows the hovered wheel's hue and saturation above it.

Each wheel has an eye (bottom-left, on hover) that hides its effect for
viewing only: RenderJob::with_hidden_grading renders those wheels neutral
under their own key, never cached as the photo's preview. Not an edit, not
saved, reset on another photo; export is unaffected.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 03:24:09 +06:00
radialmonster 5a975d0d75 LR-IMP-FORMATS: CR3 CRX quantization 44 (QP 168) decodes, continuing the step sequence
Canon EOS R50 C-RAW files reach QP 168 (quant 44, step 102, measured about 100). The quantization step and the adaptive QP map range accept it; anything above stays rejected. Bumps RENDER_CACHE_VERSION since such files change from the embedded preview to a raw decode.
2026-10-10 17:20:31 -04:00
PiMinusMaxandClaude Opus 5.5 0067879a58 KEY-ESC: Esc leaves the crop tool before leaving Detail, and a text field's Esc stays its own
view.back (Esc) closed dialogs, the keyword painter, full screen and an
armed on-image tool, but not the crop tool (a right panel, not
app.ui.tool), so Esc with the crop tool open went straight to the grid.
It now closes the crop tool like C does (the crop stays); the next Esc
goes back to the grid.

Esc that ends an edit in a text field (the crop tool's Angle field) also
ran Back: egui drops the field's focus as Esc arrives, before
shortcuts::handle checks egui_wants_keyboard_input(). The app now
records whether a text field had the keyboard at the end of the last
frame (LightcraftApp::text_focus); that Esc is then left to the field,
like one that closes a popup.

New headless tests in tests_crop_tool.rs; both fail without the fix
(the existing escape_in_the_angle_field_keeps_the_angle fails with only
the crop step).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 23:15:13 +02:00
PiMinusMaxandClaude Opus 5.5 0adcb21166 LR-CROP-FLIP: Flip H / Flip V show in the crop tool
The frame dropped the flips together with the crop (flip_h: apply_crop &&
...), so the crop tool's uncropped view (render and screen <-> image
mapping) ignored Flip H / Flip V until the tool was closed. A flip isn't
part of the crop: the frame now keeps it without the crop. Cropped
renders and exports are unchanged.

The crop overlay's dimming paired the photo's corners with the crop
box's corners in data order; flipped, the box reaches the screen
mirrored and the photo was dimmed inside the box. It now takes the box's
corners in screen order (flipped_corners). Box, handles, drags and guides
already go through the frame's mapping.

Tests: geometry uncropped_frame_keeps_the_flips (fails without the fix)
and detail flipped_crop_box_corners_come_in_screen_order; headless
screenshots for H, V and H+V checked.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 23:03:29 +02:00
akvaithiandClaude Opus 5.5 88f424e7e7 LR-EDIT-DETAIL-DENOISE: share tiles with a server only when it is faster
Measured with RawNIND on a 20.8 MP raw: an Apple M3 does a tile in 116 ms, an
RTX 3060 Ti over Tailscale 345 ms there and back; sharing blindly made the
export take 34 s instead of 6.4 s while the photo waited for the server's last
tiles. The greeting now times a check tile there and back, and tiles are
shared only when that beats this computer's own time per tile (the card's, or
the CPU's with its threads counted as `card_is_faster` does). Otherwise
`denoise.status` → `device.remote` says `slower` with both times. With the
server configured but slower the export takes 7.2 s (greeting + check tile).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 15:53:39 -05:00
PiMinusMaxandClaude Opus 5.5 bc5c561dd8 LR-VIEW-BEFOREAFTER: zoomed side by side / stacked views stay in their panes
Zoomed in, each view's photo reaches past its pane. The whole-frame renders
were drawn unclipped (only the window renders were clipped to their pane),
so the After's magnified whole-frame render covered the Before pane's margins
and the gap between the panes with a blurred picture, and the Before side
looked like it didn't fill its pane. Both whole-frame renders are now clipped
to their pane, like the windows over them; the Before / After labels stay
under the pane instead of moving off screen with the photo's bottom edge.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 22:47:43 +02:00
Adrian D'souza 5855ab80a6 XMP.434: reconcile current main & complete warning locales
Preserve Target-aware sidecar operations, add French/Ukrainian warnings, and ensure omitted diagnostic fields decode in the native UI.

Generated with Codex.
2026-10-11 02:11:00 +05:30
akvaithiandClaude Opus 5.5 84b8995d7b LR-EDIT-DETAIL-DENOISE: run tiles on another computer's graphics card
- `lightcraft_denoise::remote`: protocol v1 over TCP (JSON lines + byte-shuffled,
  deflated half-float tiles), a `Remote` client (connection pool, four slots,
  greeting checks the model's SHA-256 and tile size, gives up after three failed
  tiles) and `serve` (shared token compared in constant time, size caps including
  payloads that inflate past their tile, connection limit, timeouts).
- Engine: with LIGHTCRAFT_DENOISE_URL / LIGHTCRAFT_DENOISE_TOKEN set, the loaded
  model greets the server on first use and adds its slots to this computer's
  tiles at once; a tile goes to a free server slot, else runs here, and a tile
  the server fails runs here too. `denoise.status` → `device.remote` says where.
- `lightcraft-cli denoise-serve` serves the installed, selected model on this
  computer's card (checked against the CPU, as in the app) or CPU.
- docs/denoise.md: setup, safety (unencrypted: VPN/LAN only) and the protocol.

Tested: protocol round trips, refusals (token, model, tile size, hostile
payloads), fallback and give-up; the engine model with a live, a mismatched
and a dead server; the CLI binary serving a synthetic model end to end.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 15:21:48 -05:00
Jason Baldwin a3c33658bb Merge origin/main into brush-hold-hardens (clean merge; tracker edit followed the parity-checklist.md rename) 2026-10-10 21:49:33 +02:00
Jason Baldwin 7216c8589a LR-EDIT-COLOR-TEMP: raw-parity.md describes Kelvin WB for matrix cameras (#730) 2026-10-10 21:47:36 +02:00
Jason Baldwin 45227de921 Merge origin/main into kelvin-wb-with-matrices (no conflicts; docs/parity.md rename followed cleanly) 2026-10-10 21:46:44 +02:00
Jason Baldwin 0fdf6c5785 Merge remote-tracking branch 'origin/main' into brush-live-preview-gaps 2026-10-10 21:44:40 +02:00
Jason Baldwin 78c5b4d099 Merge origin/main into crop-angle-constrained 2026-10-10 21:39:04 +02:00
Jason Baldwin 62dacf2791 Merge origin/main into crop-rotation-artefact 2026-10-10 21:37:05 +02:00
Jason Baldwin 7a232ada8b Merge remote-tracking branch 'origin/main' into nef-probe-header-only 2026-10-10 21:37:02 +02:00
Jason Baldwin 7765255ec6 Merge follow-up: whats-new entry under 2026-10-10 2026-10-10 21:36:32 +02:00
Jason Baldwin de6b33c662 Merge remote-tracking branch 'origin/main' into reimport-after-delete 2026-10-10 21:33:45 +02:00
Jason Baldwin b4d7cd1a03 Merge remote-tracking branch 'origin/main' into gpu-fallback-per-render 2026-10-10 21:33:40 +02:00
Jason Baldwin 89516d2e3e Merge origin/main into lock-unlock-on-drop (whats-new entry moved under 2026-10-10) 2026-10-10 21:32:55 +02:00
Jason Baldwin e7076762c0 Merge remote-tracking branch 'origin/main' into sony-arw-washed-out 2026-10-10 21:31:53 +02:00