Commit Graph
322 Commits
Author SHA1 Message Date
Jason Baldwin 09ea6a3f77 Merge origin/main into slideshow-playback (painted toast from batch 33, kept at the top during a slideshow; both Slot variants; tracker rows) 2026-10-13 16:52:50 +02:00
bflatastic 8df468bfc6 Merge PR #1246
# Conflicts:
#	crates/pipeline/src/spots.rs
#	docs/parity-checklist.md
2026-10-13 11:05:44 +07:00
bflatastic 9b4047b1d4 Merge PR #661 2026-10-13 11:02:53 +07:00
bflatastic 2fc2419fa1 Merge PR #1193
# Conflicts:
#	docs/parity-checklist.md
2026-10-13 11:01:37 +07:00
bflatastic dca358d435 Merge PR #1389 2026-10-13 11:01:01 +07:00
Marco Arnulfo 0fa8070647 AI Denoise: Install from file says why a model file is refused (fixes #1381) 2026-10-13 02:33:10 +02:00
Andrew FrahnandClaude Opus 5.5 03a3bbd561 Up / Down move through the grid's rows (#1171)
library.up / library.down (Up / Down) go to the cell above / below in the
grids (nearest by position in justified rows; the top / bottom row stays
put), and step one photo in the filmstrip.

Sources: own design.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 01:39:01 +11:00
Andrew FrahnandClaude Opus 5.5 139e9633cf LRC-DEV-HISTORY: History cursor — going to a step adds no step
Go to History Step (and new Previous/Next History Step commands) loads the
step's settings and moves a per-photo cursor (`Photo.history_cursor`,
`Op::SetHistoryCursor`, catalog format v8; older catalogs open at the latest
step) instead of appending "History: …". Later steps stay listed, greyed in
the History panel, and can be returned to; a new edit made behind the end
drops them first. Each move is one undo step; undoing an edit restores the
dropped steps and the cursor. `history.list` returns `cursor`.

Also: spots.rs uses as_chunks (clippy chunks_exact_to_as_chunks).

Sources: own design (Lightroom Classic / Photoshop History panel behaviour, observed).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 00:58:05 +11:00
Alex ChambersandClaude Opus 5.5 f1c023ed3d LR-META-XMP: Read Metadata from File doesn't auto-write the sidecar it just read (#934)
With XMP auto-write on, photo.readMetadataFromFile committed what it read
and the command handler then wrote the sidecar back, adding lc:settings to
a sidecar that had only another app's crs: settings. Later reads prefer
lc:settings, so LRTimelapse's next crs: edits were ignored. The read now
sets the existing one-command skip_auto_write switch (as photo.removeRegion
does); edits made afterwards are still auto-written.

Tests: a read leaves the sidecar byte for byte and a later develop edit is
still written; the issue's crs:Exposure2012 0.5 -> 1.2 round trip is read.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 09:24:29 +02:00
Jason Baldwin d14a44630d Merge origin/main into slideshow-playback (whats-new: batch 31's Reliability entry kept, Slideshow playback after it under 2026-10-11; parity summary refreshed) 2026-10-11 17:09:28 +02:00
Jason Baldwin 7b0fc19ebb Merge origin/main into slideshow-playback (icons: Play/Pause beside grading and appearance icons; settings: Slideshow tab beside appearance cards; render: Slide and CompareRegion slots; tracker summary and whats-new) 2026-10-11 16:19:50 +02:00
Jason Baldwin 8d692ed0a9 Merge origin/main into slideshow-playback (menus.rs ui_enabled: keep main's compare.set and this branch's slideshow.* arm) 2026-10-11 14:51:59 +02:00
Claude 9978dc739d Merge PR #643
# Conflicts:
#	crates/engine/src/export.rs
#	crates/engine/src/tests_export.rs
#	crates/mcp/src/headless.rs
#	crates/mcp/src/tools.rs
#	crates/ui-egui/locales/de-formats.json
#	crates/ui-egui/locales/de.json
#	crates/ui-egui/locales/es-formats.json
#	crates/ui-egui/locales/es.json
#	crates/ui-egui/locales/fr-formats.json
#	crates/ui-egui/locales/fr.json
#	crates/ui-egui/locales/ja-formats.json
#	crates/ui-egui/locales/ja.json
#	crates/ui-egui/locales/pt-br-formats.json
#	crates/ui-egui/locales/pt-br.json
#	crates/ui-egui/locales/ru-formats.json
#	crates/ui-egui/locales/ru.json
#	crates/ui-egui/locales/uk-formats.json
#	crates/ui-egui/locales/uk.json
#	crates/ui-egui/locales/zh-hans-formats.json
#	crates/ui-egui/locales/zh-hans.json
#	crates/ui-egui/locales/zh-hant-formats.json
#	crates/ui-egui/locales/zh-hant.json
#	docs/parity-checklist.md
#	docs/whats-new.md
2026-10-11 12:43:20 +00:00
Claude 255b23816d LR-EXP-PSD: Edit In makes a copy of every selected photo and opens them all (#920)
photo.editExternal takes the selection (or ids, at most 50): each photo gets its own -Edit copy
stacked on its original, the copies are selected and returned in copies (path / id / original
still name the first). Photo ▸ Edit In opens each copy in the editor.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BKWB9TCnvzJHV6xMLAj2J4
2026-10-11 12:40:58 +00:00
Claude 4ec3494733 Merge PR #434
On merge: Lightroom's default profiles (Adobe Standard, Adobe Color), named by nearly every sidecar,
give no camera-profile warning; other profiles still do (test default_lightroom_profiles_are_not_reported).
The import help string keeps main's onDeleted wording and gains warnings.
2026-10-11 12:38:16 +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 b89fb8bbab Merge branch 'claude/fix-942-cube-import-messages'
# Conflicts:
#	docs/parity-checklist.md
2026-10-11 12:04:31 +00:00
Claude e1016acc0f Merge PR #953
# Conflicts:
#	crates/codecs/src/lib.rs
#	docs/parity-checklist.md
#	docs/whats-new.md
2026-10-11 12:03:58 +00:00
Claude 45a6a42ebf Merge PR #947
# Conflicts:
#	crates/ui-egui/locales/de-formats.json
#	crates/ui-egui/locales/de.json
#	crates/ui-egui/locales/es-formats.json
#	crates/ui-egui/locales/es.json
#	crates/ui-egui/locales/fr-formats.json
#	crates/ui-egui/locales/fr.json
#	crates/ui-egui/locales/ja-formats.json
#	crates/ui-egui/locales/ja.json
#	crates/ui-egui/locales/pt-br-formats.json
#	crates/ui-egui/locales/pt-br.json
#	crates/ui-egui/locales/ru-formats.json
#	crates/ui-egui/locales/ru.json
#	crates/ui-egui/locales/uk-formats.json
#	crates/ui-egui/locales/uk.json
#	crates/ui-egui/locales/zh-hans-formats.json
#	crates/ui-egui/locales/zh-hans.json
#	crates/ui-egui/locales/zh-hant-formats.json
#	crates/ui-egui/locales/zh-hant.json
2026-10-11 12:02:51 +00:00
Claude a0e422ce4a Merge PR #752
# Conflicts:
#	crates/engine/src/cmd/develop.rs
#	crates/engine/src/files.rs
#	docs/mcp.md
#	docs/parity-checklist.md
#	docs/whats-new.md
2026-10-11 12:02:46 +00:00
Claude 8339028a80 Merge PR #794
# Conflicts:
#	crates/engine/src/media.rs
#	crates/pipeline/src/optics.rs
#	docs/parity-checklist.md
2026-10-11 12:00:57 +00:00
Claude 11f41cbe97 Merge PR #813 2026-10-11 12:00:21 +00:00
Claude 477c078b35 Merge PR #834 2026-10-11 12:00:20 +00:00
Claude 8380415222 Merge PR #874
# Conflicts:
#	docs/parity-checklist.md
2026-10-11 12:00:17 +00:00
Claude da008ce53c LR-IMP-PROFILES: .cube-only imports report their profiles and failures (#942)
File > Import Profiles & Presets... now builds one toast for both halves
(a second toast replaced the first, so a .cube-only import said "No new
presets"). The preset half is skipped when no preset files were picked,
and profile.import's failures are shown with their reason. profile.import
also reports .cube files and zips it couldn't read instead of dropping
them silently. The command's result carries the profile half under
"profiles".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BKWB9TCnvzJHV6xMLAj2J4
2026-10-11 12:00:08 +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 5861591307 Merge PR #948
On merge: metadata.downloadPlaceNames accepts plain http only for the loopback address itself
(http://127.0.0.1.example.com/ and http://127.0.0.1@host/ are refused; test
download_base_is_https_or_loopback_only).
2026-10-11 11:52:57 +00:00
Claude 6c18c0a7c6 LR-PREF-PERFORMANCE: preview builds render the larger of view and thumbnail first (a view under 512 px no longer feeds an upscaled thumbnail, follow-up to #927)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BKWB9TCnvzJHV6xMLAj2J4
2026-10-11 11:52:24 +00:00
Claude 95007492cc Merge PR #927 2026-10-11 11:52:07 +00:00
Claude 4e6cd5d7b9 Merge PR #704
# Conflicts:
#	docs/parity-checklist.md
2026-10-11 11:49:37 +00:00
Claude 40492d238e Merge PR #690
# 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/tests_panels.rs
#	docs/parity-checklist.md
2026-10-11 11:48:06 +00:00
Claude 1f3bfbdb25 Merge PR #698 2026-10-11 11:47:26 +00:00
Claude b46adf0640 Merge PR #720
# Conflicts:
#	docs/parity-checklist.md
2026-10-11 11:47:25 +00:00
Claude 06ef27bb37 Merge PR #751
# Conflicts:
#	docs/parity-checklist.md
2026-10-11 11:44:45 +00:00
Claude ee2cd6f4bb Merge PR #726 2026-10-11 11:41:20 +00:00
Claude 14679de823 Merge PR #885 2026-10-11 11:39:25 +00:00
Andrew FrahnandClaude Opus 5.5 23e046daba Reverse geocoding: surface download failures, cache the gazetteer, e2e download test
- A failed, cancelled or finished place-names download is reported once
  as a toast (failures as an error toast that says what to do), and
  metadata.placeNamesStatus reports downloading / lastDownload / lastError.
- The parsed GeoNames data is cached in the session and re-read only when
  the cities file's size or modification time changes.
- End-to-end download test against a fake HTTP server on 127.0.0.1:
  a synthetic zip, a garbage zip, a truncated body and 404s.
- Info panel: Fill Location gets its own row (it was clipped beside
  Show on Map in a narrow panel).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-11 21:09:53 +11: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
Andrew FrahnandClaude Opus 5.5 8924ea9afd Library: create folders on disk and move photos between folders
folder.create {parent, name} makes a folder on disk (one undo step; undo
removes it again while it is empty). photo.moveToFolder {folder, ids?}
moves the photos' files with their XMP sidecars into another folder and
relinks them as one undo step (undo moves them back); virtual copies
follow their master. It never overwrites: a name already taken in the
destination refuses the whole move with an actionable error. Missing or
offline originals and Local photos are refused. Across volumes a file is
copied, compared byte for byte and only then removed. The move holds a
row in the activity stack.

UI: New Folder on Disk… and Move Selected Photos Here on a Folders row,
Move to Folder… in the photo context menu; strings in every catalog.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-11 20:49:19 +11:00
cookingwithsimonandClaude Opus 5.5 44b12efea3 LR-EXP-PSD: Edit in PhotoCraft (right-click Edit In, flat PSD hand-off)
Photo > Edit In > Edit in PhotoCraft (Cmd+Shift+E, also on the photo
context menu) renders a copy with the edits as a flat 16-bit PSD, which
PhotoCraft saves back to in place, stacks it on the original and opens
it in PhotoCraft, found where its installers put it. Edit in Other
Editor (Cmd+Alt+E) keeps the configured program. Per-editor format,
colour space and bit depth in Settings > External Editing. Export gains
a flat PSD format.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T4kqvR9KVCPia3SSXyB8WG
2026-10-11 09:35:43 +00: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 07ae88e923 Perf: preview builds render photos side by side and make the thumbnail from the view's source
library.buildPreviews rendered one photo at a time, and each photo's thumbnail job decoded the
file again at thumbnail level after (before, in fact) the view job had decoded it at preview
level. Now the view renders first and its decoded source serves the thumbnail, and photos
render in as many lanes as an export batch (two for 1:1 previews, a full-size source each);
a panic in one photo counts as that photo's failure.

100 24 MP ARWs, standard previews, loaded machine: 49.4 s -> 38.4 s (CPU 3m28 -> 2m49).

Sources: own design.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-11 19:30:29 +11: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 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
Nigel Ihza Firdausy 38756b89d0 fix(query): saturate pagination values in 32-bit web builds 2026-10-11 11:04:36 +07: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
Nigel Ihza Firdausy a66a133041 test(previews): cover legacy smart preview discovery and nonblocking look refresh 2026-10-11 04:54:04 +07:00
Nigel Ihza Firdausy e90d83ecc3 fix(previews): shard smart preview storage and preserve flat layout compatibility (#795) 2026-10-11 04:53:07 +07: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
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