Commit Graph
1498 Commits
Author SHA1 Message Date
Michael Anderson 6d080de447 Never crash: Check raw data size before allocating in PEF, TIFF tiles and ARW6 (#1512)
A compressed PEF too short for its size, an uncompressed tile claiming far more samples than it holds, and an ARW6 with a failed tile now get refused before the big buffer is allocated.
2026-10-14 13:56:34 -05:00
Michael Anderson 9ef40b9886 Commands: Refuse malformed photo ids instead of using the selection (#1506)
A wrongly typed id or ids fell back to the selection, or kept only the valid part of the list. Session::targets now returns an error for those, and callers pass it on.
2026-10-14 13:56:33 -05:00
Michael Anderson 30ec48d9b3 LR-META-XMP: Retry automatic XMP sidecar writes that failed
A sidecar write that failed because the folder was offline or read-only was only logged. Keep those photos waiting, write them again once their files are reachable, and tell the user.
2026-10-14 13:56:23 -05:00
Davit MatchakhelidzeandClaude Opus 5.5 af0c1638fe i18n: Georgian (ka) interface language
Adds Georgian (ქართული, code ka, system tags ka / ka-GE / ka_GE) with
complete plain and formats catalogs, the Edit > Language command and the
Settings choice. Sentence-cased messages go through i18n::upper_first,
which leaves Georgian letters alone (upper-casing Mkhedruli would turn it
into Mtavruli). The Settings language choices wrap now that eleven
endonyms no longer fit on one line, and the face-model Remove button goes
through tr().

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YMTAbGNRxnMEzRvYnQGphF
2026-10-14 22:50:28 +04:00
Michael Anderson ef3642c39f LR-IMP-CAMERA-COVERAGE: Nikon D600, D610 and D5000 NEFs drop the columns past the image (#1627) 2026-10-14 13:44:58 -05:00
Michael Anderson 4352147910 Faces: Drop faces made from a photo's old file when it is replaced (#1524) 2026-10-14 13:19:22 -05:00
Marco Arnulfo 2df471b142 Top bar: in full screen the left buttons move into the traffic lights' space (fixes #1763) 2026-10-14 20:19:21 +02:00
Michael Anderson bb9d3d4022 Catalog and downloads: Don't write through a link at a temp file's name (#1502) 2026-10-14 13:13:27 -05: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
BuonBredaandClaude Opus 5.5 4551790eab LRC-DEV-LOUPEOVERLAY: a layout image over the loupe (View ▸ Loupe Overlay)
Lightroom Classic's Loupe Overlay ▸ Layout Image: a PNG with transparency (a
cover's masthead and cover lines, a banner's safe areas) drawn over the photo
in the loupe, fitted and centred, following zoom and pan. Opacity and matte
(the photo darkened outside the graphic's box) in steps, ⌥⌘O shows / hides it,
the choice is saved with the UI state. The file is read off the UI thread; an
unreadable one is reported on the canvas. engine::export::read_graphic is the
watermark logo's reader, now shared.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Mj2w3LwYjFhMHALZuFp2rU
2026-10-14 19:32:06 +02:00
LabDennis 1f4aaaec47 Fonts: finish Thai fallback delivery for desktop and web releases
Retain the complete reviewed change, including prior integration resolutions,
on current upstream.

#736: deliver Thai UI fonts in desktop and web releases

Use the merged craft-fonts input containing Noto Sans Thai across release,
FreeBSD and Nix builds. Retain the small Thai face in the wasm subset and
reject required release inputs that lack it. Exercise the reported Thai
folder name through all UI families and include original font licence texts
in Windows installers as well as portable packages.

Refs #736; completes delivery of the fallback added in #750.

Validation: UI tests, UI-only strict Clippy, 28 wasm checks, HEIF checks,
assets, layers, parity and formatting pass. All 21 unrelated workspace
failures reproduce with upstream Rust sources and the old font input;
existing Rust 1.95 metadata/engine Clippy findings remain unchanged.
2026-10-14 18:52:15 +02:00
thanat0sandGPT-6-Sol medium 37b8c256ad LRC-LIB-PEOPLE: confirm ranked face suggestions in one batch (#1442)
Co-Authored-By: GPT-6-Sol medium <noreply@openai.com>
2026-10-14 18:40:52 +02:00
Claude 05352e18dd Merge storytold/lightcraft main into LR-MERGE-HDR (white balance)
Brings in batch 35 / v0.8.0. Code merged cleanly; conflicts only in docs: the LR-MERGE-HDR row keeps main's
lens-correction text plus this branch's white-balance item, and this branch's What's New entry moves to the
2026-10-14 section.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015gRtdWF1E9358FfzhzYqCS
2026-10-14 16:22:53 +00:00
Claude ac17c580f5 Merge storytold/lightcraft main into LR-MERGE-HDR (frame order, clipped pixels)
Brings in #1423 (HDR merges keep the lens corrections), which landed in batch 35 / v0.8.0. hdr.rs merged cleanly:
the result's lens corrections come from the reference frame after the brightness ordering. Conflicts only in docs:
the LR-MERGE-HDR row keeps main's lens-correction text plus this branch's two items, and this branch's What's New
entry moves to the 2026-10-14 section.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015gRtdWF1E9358FfzhzYqCS
2026-10-14 16:22:37 +00:00
Marco Arnulfo 17e05759b4 DNG export and Convert / Copy as DNG write the colour a raw without matrices opens with (fixes #1745) 2026-10-14 18:08:53 +02:00
Michael Anderson e8818281d5 LR-IMP-LOCAL: Forget deleted files when browsing from the sidebar too
The sidebar reads the folder on a worker thread and skipped the cleanup library.browse does, so the fix for #1392 didn't reach the app.
2026-10-14 10:40:41 -05:00
G6billandCopilot App 877ef342dd MCP-CONTROL: validate remote reply framing and IDs (#1716)
The connect-mode MCP client accepted any newline-less or wrong-id reply as
the answer to its request. Require a complete newline-terminated reply of
at most 64 MiB, the matching integer id, boolean ok and a result or string
error; otherwise close the connection and report an unknown outcome without
resending. Idless pre-dispatch refusals from the control server still work.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
2026-10-14 15:25:13 +02:00
aGiuliano eac616b542 Merge branch 'main' into fix/Chinese_Japanese_Text_warning 2026-10-14 14:53:25 +02:00
Jason Baldwin 3aa6b3ba31 Merge slideshow-templates into slideshow-export (origin/main: batch 35)
# Conflicts:
#	docs/parity-checklist.md
2026-10-14 14:42:03 +02:00
Jason Baldwin 5f3bdbcb73 Merge slideshow-titles into slideshow-templates (origin/main: batch 35)
# Conflicts:
#	docs/parity-checklist.md
2026-10-14 14:39:47 +02:00
Jason Baldwin 7919c1471d Merge slideshow-overlays into slideshow-titles (origin/main: batch 35)
# Conflicts:
#	docs/parity-checklist.md
2026-10-14 14:37:20 +02:00
Jason Baldwin f776d98167 Merge slideshow-composer into slideshow-overlays (origin/main: batch 35)
# Conflicts:
#	docs/parity-checklist.md
2026-10-14 14:35:02 +02:00
Jason Baldwin 2b766142bc Merge origin/main into focus-point (tracker header/summary/change log, whats-new entry moved under 2026-10-14) 2026-10-14 14:33:49 +02:00
Michael Anderson ead24aded9 LR-PROF-CAMERACOLOR: Add a bundled Nikon D5300 camera profile
Fitted with lightcraft-cli calibrate to 264 of my own D5300 photos.
2026-10-14 07:32:56 -05:00
Michael Anderson 975b08c334 LR-PROF-CAMERACOLOR: Add a bundled Nikon Z 6II camera profile
Fitted with lightcraft-cli calibrate to 296 of my own Z 6II photos.
2026-10-14 07:32:56 -05:00
Michael Anderson 2bc2f60277 LR-PROF-CAMERACOLOR: Add a bundled Nikon Z 8 camera profile
Fitted with lightcraft-cli calibrate to 261 of my own Z 8 photos.
2026-10-14 07:32:56 -05:00
Michael Anderson c854e172e4 LR-PROF-CAMERACOLOR: Match the camera look against lens-corrected JPEGs
The fit compared the uncorrected raw with the corrected JPEG. It now warps the raw with the file's own lens correction when that lines up better.
2026-10-14 07:32:47 -05:00
Michael Anderson 441f46683a LR-IMP-DEVICE: Show the import review when a card is connected
Adds an option under Settings > Import > Cameras and Cards that opens Import from Device when a camera or memory card shows up. It's off by default, and a card that was already in at startup doesn't ask.
2026-10-14 07:32:47 -05:00
Michael Anderson 1acbf88661 LR-META-INFO: Show the lens for Nikon DSLR raws
They don't write a lens name, so read the range from the maker note's Lens tag.
2026-10-14 07:32:46 -05:00
Michael Anderson 02d5d6bc03 LR-MERGE-PANO: Keep panoramas at full resolution when memory allows (#1455)
Drop the 40 MP default and free each photo's pixels once it's projected.
2026-10-14 07:32:45 -05:00
Michael Anderson b0b86aca84 LR-BEHAV-GPU: Turn GPU rendering off after the GPU is lost while running (#1337)
Report it as LightCraft stopping instead of the window failing to open, and start on the CPU next time.
2026-10-14 07:32:44 -05:00
Michael Anderson 697010d520 LR-IMP-LOCAL: Forget untouched records of deleted files when browsing a folder again (#1392)
Rated, flagged or edited photos stay and show as missing.
2026-10-14 07:32:43 -05:00
Jason Baldwin b60c74a6d7 Merge slideshow-playback into slideshow-composer (origin/main: batch 35)
# Conflicts:
#	crates/engine/src/import.rs
#	crates/engine/src/media.rs
#	docs/parity-checklist.md
2026-10-14 14:31:53 +02:00
Jason Baldwin 3aaadd1456 Merge origin/main into slideshow-playback (batch 35: tracker summary refreshed, slideshow rows kept) 2026-10-14 14:29:54 +02:00
Michael Anderson e0690b5f63 LR-EXP-QUALITY: Use optimized Huffman tables for JPEG exports (#1373)
Build the tables from each image's own symbol counts. Same pixels, smaller files.
2026-10-14 07:28:30 -05:00
Nathaniel LedfordandClaude Opus 5.5 d2523e8544 Preview cache: cached thumbnails are no longer taken for the person's photos
The thumbnail cache wrote .jpg files into the library's thumbs folder, which is usually inside
Pictures: photo galleries listed every thumbnail as a photo (issues #610, #1066), and every read
brought the file's date forward, so each launch made them all look new.

- Cache files are named <32 hex>.lcthumb (JPEG data as before). A .jpg of before is taken over
  under the new name when it is asked for; the rest when the cache is tidied, which the engine does
  once per cache folder, on a thread of its own with a row in the activity stack that shows its
  progress and can stop it. Nothing is rendered again.
- Reading writes nothing: what was read is remembered for the session (a full cache lets it go
  last), and only a file that went unused for a week has its date brought forward.
- A folder that holds nothing but the cache gets a CACHEDIR.TAG, when the cache makes it and when
  it is tidied; one that also holds somebody's files is not tagged (issue #98).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 08:26:17 -04:00
Nathaniel LedfordandClaude Opus 5.5 afacec1747 LR-BEHAV-WINDOW: the window opens at the size it was left at, zoomed if it was
The desktop app opened at 1600 x 1000 every time: a resize, or a zoom by a
double-click on the top bar, was forgotten at quit.

The size the window rests at and whether it is zoomed (macOS) / maximized are
kept in ui.json (`window: {size, zoomed}`) and the window is built from them.
A zoomed window keeps the size it goes back to. A size counts once the window
has rested at it for half a second, so the sizes a resize or the zoom
animation passes through do not; one given just before quitting is taken at
exit. Full screen and the window's place on the desktop are not remembered.

Windows and Linux say whether a window is maximized. On macOS egui-winit only
refreshes that at launch and when the app zooms the window itself, so there
the window's size is gone by as well: zoomed when the system said so or the
window was seen to come to fill its screen, and for as long as it keeps that
size. Whatever is in doubt is a window of the size it has, which opens as
large as it was left.

A `window` in the settings that is not one reads as none and costs no other
setting.

Run on macOS with one display; the other platforms follow from reading winit
and egui-winit, so the tracker row is partial.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 07:55:41 -04:00
Nathaniel LedfordandClaude Opus 5.5 c2c85f5d97 LRC-LIB-KEYWORDS: what two independent reviews found in the Keywords panel
- "+N more" was for the whole session: it is for the selection it was
  asked of, and another selection's long list is short again.
- A keyword two photos spell differently was offered twice under one
  widget id, and a click on it gave nothing: it is offered once.
- The offers followed the box out of the panel when it scrolled away and
  covered it near the bottom of the window: they are not drawn while the
  box is out of sight, and go above it where there is no room below.
- The highlight was a line number, so a change of the library moved it to
  another keyword, which Return then gave: it stays with its keyword.
- Esc after an offer was taken brought back keywords just added; Shift or
  Alt with the arrow keys moved the highlight instead of the text cursor;
  a refused add lost the typed text without a word.
- A name the library lacks was looked up in the library twice per change
  of the text: once.
- A wrong value under keywordChips or foldedSections discarded the whole
  settings file: it is read as the default.
- Groups follow the tree ("Places > Italy" right after "Places"); a heading cut
  short says its whole name on hover; the number of chips measured for a
  shortened list follows the box's width; the selection is asked for once
  a frame.
- The nine new plain messages are translated in every language.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 07:52:50 -04:00
Nathaniel LedfordandClaude Opus 5.5 4ce0aad79c LRC-LIB-KEYWORDS: the Keywording box says what a removal touched
With several photos selected the box says how many its keywords are of.
A chip's x is a target as tall as the chip and 22 pt wide (it was 14 pt
square) that lights up on its own, and the chip is 4 pt narrower for it.
Taking a keyword off several photos, by the x or the chip's menu, says
which keyword left how many photos and that Edit > Undo puts it back; a
refused removal says why. Chips and their x show the keyboard focus, and
the Add keyword box says in its tooltip that commas separate keywords.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 07:52:50 -04:00
Nathaniel LedfordandClaude Opus 5.5 5ca5e0547c LRC-LIB-KEYWORDS: the Add keyword box offers the library's keywords under itself
What matches the text being typed (after the last comma) is offered right
under the box instead of in the Suggestions block further down: Down / Up
move through the offers, Return or a click gives the highlighted one (with
the keywords typed before it) and leaves the cursor in the box. With none
highlighted, Return gives what was typed, as before.

A name the library doesn't have is announced as a new keyword, with the
keyword it would go inside, before Return creates it.

The offers have a cache of their own: they and the suggestions are both
asked for on every frame of typing, and each looks through the library's
keywords.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 07:52:50 -04:00
Nathaniel LedfordandClaude Opus 5.5 366e6891a0 LRC-LIB-KEYWORDS: the Keywords panel folds its sections and can group the selection's keywords
The titles of Keywords and of the Keyword List fold their sections shut
(kept in the UI state as foldedSections), so the list of every keyword is
reachable however many keywords the selection has.

Settings > Interface > Keywords of the selection > Grouped by parent
(off by default): keywords inside the same keyword share a line naming
it, their chips say only what is below it and sit side by side, a list
of more than ten lines stops after eight with "+N more", and the
suggestions say the keyword's own name.

A selection without keywords says "No keywords yet", as the other two
views of the box do.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 07:52:50 -04:00
Nathaniel LedfordandClaude Opus 5.5 43ba500c6e PERF-EDIT: a smart album's count follows an edit, asking only the photos it touched
The left panel shows a count beside every album. Each catalog revision had
every album counted again, and a smart album's count asks every photo of
the library whether its rules hold: one pass over the library for each smart
album after each keyword, rating or slider tick (about 25 ms an album in a
library of 67,000 photos).

`AlbumTally` (`crates/catalog/src/tally.rs`) keeps the counts and, for each
smart album, which photos it holds. After a small edit each smart album asks
only the photos the edit touched (`Catalog::touched_since`); an album of
chosen photos is counted again only when a photo came, went or was deleted.
Counted from every photo as before: new rules, a change to an album, a stack
or the keyword list, an edit to more than 256 photos, another library, and
each new minute while an album asks about "the last N days".

What an album remembers is in proportion to the photos it holds: a bit a
photo id while ids are close together, a set when they are far apart.

The frame after a keyword in that library, both side panels open: about
54 ms -> 27 ms (156 ms before #1660).

Tests: 900 random edits against eight smart albums (keywords, flags,
ratings, labels, sliders, capture dates, file names, an album of chosen
photos) and an ordinary one, the followed counts compared with
`album_count` after each; a delete followed, three hundred photos counted
whole; the members' bits at the edges and for ids far apart.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 07:46:42 -04:00
Nathaniel LedfordandClaude Opus 5.5 5f1037a192 PERF-EDIT: an edit is followed from what it touched, not worked out from every photo
Everything derived from the catalog was keyed on its revision, which every
op raises. So each edit (a keyword, a rating, every tick of a slider) had
the grid's list queried and sorted again from every photo, the source
counted by a second full query, and the side panels tally the whole library
again. In a library of 67,000 photos the frame after one keyword took about
156 ms with both side panels open (an idle frame: 2 ms).

The catalog now remembers which photos its latest ops touched and what of
them they changed (`Catalog::touched_since`, `aspects_since`, `Aspects`). It
answers "not known" for an op that changes more than photos of its own (an
album, a stack, the keyword list), for changes too long ago, and for a
revision it did not make itself (a library just loaded).

- The view (`Session::visible`): when the same view is asked again after a
  small edit, the photos the edit touched are taken out of the list it had,
  asked about again, and put back at their place in the order
  (`Catalog::follow`). 37 ms -> 0.6 ms. Worked out whole as before: more
  than 256 photos touched, a change that is not photos' own, an album in its
  own order, Recently Added, Missing Photos, and a view that asks about "the
  last N days" (it changes with the clock too).
- The source's count: taken from the list when no filter is set, else
  counted without sorting (`Catalog::count`). 37 ms -> 0.
- The side panels (`Caches`): the folder tree, keyword tree, date groups,
  people's names and library counts are kept through an edit that changed
  nothing they read. A rating or a slider changes none; a keyword leaves the
  folders, dates and counts.

The frame after a keyword: about 156 ms -> 54 ms with both side panels
open, 71 ms -> 2 ms with the grid alone (`tests_view_cost.rs`, a synthetic
library, run with --ignored --nocapture).

Still paid on each edit: the count beside every album and smart album in the
left panel (about 25 ms for a smart album there), and the filter bar's lists.

Tests: 1,440 random edits of every photo-local kind across sources, sorts,
filters and the hold, each compared with the list worked out whole; 600
random edits comparing every kept tally with a fresh one; the clock; the
record itself, with its failure paths. `ui.inspect` -> `perf` reports
`viewFollowed` and `viewWorkedOut`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 07:46:30 -04:00
Nathaniel LedfordandClaude Opus 5.5 540a281ccd PERF-SYNC: a Synchronize Folder scan reads off the folder's listing whether a photo and its sidecar are there
The scan asked the disk about every library photo in the folder: is its
file there, and has it a sidecar under any of four names. On a network
share each question is a round trip, and the scan had just listed the
folder. Measured on a hard disk over SMB, for unchanged folders of 1,600
to 2,400 photos: 4.6 to 7.1 s of questions after a listing of 4 to 5 s.

- The walk hands each folder's listing over (`walk::files_in_noting`:
  every entry's name and kind, hidden ones too, and whether that is all of
  them). The scan walks the folder once, then checks the photos beside
  the probing of new files.
- `sync::Listing` answers "is it there" / "is it a file" where that holds
  on any file system: a name the folder lists is there; a plain ASCII name
  is not there when the folder was listed to its end, all its names are
  ASCII and none is that name in another case. Everything else is asked
  of the disk as before (a link, a name Windows reads another way, a
  folder that was not walked, and every name not listed in a folder that
  holds a name outside ASCII).

Now under 0.05 s for those folders. Two things differ on purpose: a file
the folder lists is there though it can't be opened by its path (a folder
that may be listed but not searched, an over-long path), where such
photos were listed as missing; and the answers are the folder as the
walk saw it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 07:45:43 -04:00
Nathaniel LedfordandClaude Opus 5.5 73b9cc3708 LR-LIB-SMARTALBUM: a view can hold its photos until it is refreshed
A view is asked again after every edit, so a photo that an edit makes stop
matching it leaves the grid at once and every photo after it moves up: a
keyword given in a smart album of photos without keywords takes the photo
away from under the cursor, and in a random order, with no landmarks, the
shift reads as a new shuffle (the seed does not change).

View > Hold View Until Refreshed (`library.holdView`, off by default, kept
with the library's preferences): a photo the view shows stays in it when an
edit makes it stop matching a smart album's rules, Picks or the filter bar.
View > Refresh View (`library.refresh`, also in the sort menu while the hold
is on), Reshuffle, another source, filter or sort, or new rules for the
smart album shown, shows what matches now.

- Photos that start matching still appear at once.
- A photo that is deleted, or taken out of the album or folder shown, still
  leaves at once: a held photo has to be where the view looks
  (`Catalog::query_keeping`, `within`).
- A source with no filter over it holds nothing and is queried as before.
- A refresh keeps the seed.
- The grid's count no longer reads "3 of 2" when a held view shows more than
  now match its source.

Each edit still asks the whole library again; that cost is not addressed
here.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 07:41:44 -04:00
dev-null-undefinedandClaude Opus 5.5 857ffcd41c LR-MERGE-HDR: 🟡 after testing on real brackets; gap 29
Tested on real Sony A7R VI brackets (67 MP): the merged radiance agrees with every frame within ~1 % in all three
channels and the measured exposure steps match EXIF within 0.03 EV, and the black-hole stars and the merge memory are
fixed. What keeps the row 🟡 goes to gap 29: AI Denoise doesn't run on merged (linear) DNGs as Lightroom's does, JPEG
brackets have no camera response curve, and every frame is held as 32-bit float (~0.86 GB per 67 MP frame). Merge area
100 % → 88 % in target-app-parity.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 13:37: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
Andrew FrahnandClaude Opus 5.5 971ad25b74 docs: leave upstream's remaining-hours figure unchanged (the estimate was ours)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 22:22:15 +11:00
dev-null-undefinedandClaude Opus 5.5 6fb0d22326 LR-MERGE-HDR: decode full-size merge frames one at a time; AHD writes its tiles straight into the picture
A 7-frame bracket of 67 MP raws ran out of memory: the merge decoded the frames four at a time, and the AHD demosaic
collected every tile before copying them into a second full-size picture. Full-size frames now decode one after the
other (the demosaic is parallel inside; previews stay parallel), and AHD fills the picture by rows of tiles. Peak memory
of 67 MP HDR merges: 3 frames 4.8 → 3.8 GB, 4 frames 6.3 → 4.7 GB, about the same time; AHD output is pixel-identical.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 12:56:59 +02:00
dev-null-undefinedandClaude Opus 5.5 a142179f20 LR-MERGE-HDR: keep the reference's clipped highlights where other frames moved; deghost catches ghosts in dark skies
A star moving across a night bracket became a black hole: where the reference was clipped, frames in which the star
had moved read the dark sky and replaced it. Such frames are now left out there (a clipped reference pixel means at
least that bright). Deghost compared ≤ 1024 px versions and, in dark areas, let the brighter frames predict
themselves, so their ghosts looked consistent; it now uses ≤ 2048 px maps (same feather share) and trusts the
reference down to 0.1 % of its clip. Aligned frames are warped one at a time, each replacing its original, instead of
all copies at once.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 12:56:59 +02:00