Commit Graph
301 Commits
Author SHA1 Message Date
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 ad23861454 Merge PR #1625
# Conflicts:
#	apps/lightcraft/src/main.rs
#	docs/parity-checklist.md
#	docs/whats-new.md
2026-10-14 22:33:14 -04:00
Brandon Thomas c8c97883cd Merge PR #1548
# 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
#	docs/parity-checklist.md
#	docs/whats-new.md
2026-10-14 22:32:39 -04:00
Brandon Thomas 18db8588b9 Merge PR #1490
# Conflicts:
#	docs/parity-checklist.md
#	docs/ui-parity.md
2026-10-14 22:32:25 -04:00
Brandon Thomas de5ec99f61 Merge PR #1489
# Conflicts:
#	docs/parity-checklist.md
#	docs/whats-new.md
2026-10-14 22:32:24 -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
Brandon Thomas 8a406d0dfc Merge PR #1211 2026-10-14 22:29:02 -04:00
Brandon Thomas 2138dc6e7d Merge PR #1009
# Conflicts:
#	docs/parity-checklist.md
2026-10-14 21:55:05 -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
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
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
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
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
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
Brandon Thomas 93f8ee7c0d Merge PR #1427
# Conflicts:
#	crates/ui-egui/src/lib.rs
#	docs/parity-checklist.md
2026-10-13 11:52:07 -04:00
Jason Baldwin d747c9e0f6 Merge slideshow-titles into slideshow-templates (origin/main: batches 32–33) 2026-10-13 17:01:24 +02:00
Jason Baldwin a021ad2c56 Merge slideshow-playback into slideshow-composer (origin/main: batches 32–33) 2026-10-13 16:55:11 +02:00
dev-null-undefinedandClaude 6c5b4d5701 UI: find gsettings on PATH for Appearance Mode ▸ Auto (NixOS has no /usr/bin/gsettings)
Without the XDG settings portal, the start-up fallback asks gsettings for
the GNOME colour scheme, but only looked in /usr/bin, /bin and
/usr/local/bin. NixOS keeps it in /run/current-system/sw/bin or the
per-user profile, so Auto never followed the desktop there. It now tries
the absolute PATH entries first, then those three; relative entries are
still skipped, so nothing runs from the working folder.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-13 13:07:36 +02:00
Atmik Shetty 21861c2f14 LR-EDIT-DETAIL-NR: noise reduction works in the original's pixels, so previews match the full-size render scaled down (fixes #960)
Grid thumbnails looked much more denoised than the loupe. `nr_params` sized
the radii from the output's scale relative to the source buffer it was given,
not the original: a 256-px cell drawn from the 512-px thumbnail level used
half the full-size radius on pixels that each average ~23 sensor pixels, and
below 0.4x (0.35x for colour) the radius stopped shrinking at all. The
guided filter's eps (the variance treated as noise) also stayed at its
full-size value although downscaling divides the noise variance by 1/scale²,
so texture in small previews was flattened as noise.

- `SourceInfo::native_long` (filled by the engine from the catalog's photo
  size, kept when the decoder supplies its own facts, carried to web
  workers) and `Plan::detail_scale` = output pixels per original pixel
  (from `px_per_long`, so a crop no longer changes it).
- `nr_params(s, scale)`: radii x scale with no floor, eps x scale². Below
  1 px the smallest blur the box Gaussian makes is blended in by the ratio
  of variances (`blur::box_sigma`); a fade under 1% skips the filter.
  At scale 1 the parameters are unchanged: 1:1 views and full-size exports
  render as before. The GPU takes the same parameters.
- RENDER_CACHE_VERSION 63: cached thumbnails are redrawn.

Measured on a 1536x1024 noisy synthetic scene (tests_nr_scale.rs), error
of the preview against the full-size render scaled down, as a fraction of
leaving NR off, luminance NR 50 at preview scales 0.5 / 0.33 / 0.25 /
0.167 / 0.1 / 0.05: before 0.53 / 0.70 / 1.04 / 2.26 / 3.87 / 6.07, after
0.28 / 0.37 / 0.66 / 0.90 / 0.99 / 1.00. A 256-px render from the 512-px
level now matches one from the original (mean 0.07 LSB vs 0.71 before).
2026-10-13 16:14:19 +05:30
Atmik Shetty db69e288c8 KEYC-RATING: backtick cycles the flag; Cmd+Up / Cmd+Down raise / lower it
photo.cycleFlag steps each selected photo's own flag unflagged -> pick ->
reject -> unflagged; photo.increaseFlag / photo.decreaseFlag move it up or
down reject < unflagged < pick without wrapping. One batch undo step, Compare
and Survey act on the active photo, Auto Advance follows, text fields keep
the key. Shown in Photo > Set Flag and Help > Keyboard Shortcuts.
2026-10-13 16:11:45 +05:30
Arekatla Nishanth ChowdaryandClaude Opus 5.5 edc3661680 Issue #1200: portable mode, a portable.txt beside the executable keeps settings in LightCraftData
As in PhotoCraft: when portable.txt (or LightCraft.portable) sits next to the
executable, config_dir() is <exe dir>/LightCraftData, so settings, logs and
models stay with a portable copy instead of %APPDATA%. LIGHTCRAFT_CONFIG_DIR
still wins and is used without probing beside the executable. A portable
folder that can't be written logs a warning and the platform folder is used.
The check runs once per process.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 11:34:33 +05:30
Jason Baldwin a89daef74a Merge slideshow-titles into slideshow-templates (origin/main: batch 32; toast clear of the slideshow controls) 2026-10-13 06:55:03 +02:00
Jason Baldwin 2faee15203 Merge slideshow-playback into slideshow-composer (origin/main: batch 32; toast clear of the slideshow controls) 2026-10-13 06:55:02 +02:00
sanyamsingh001andCodex 056b0f7a28 LR-MASK-OBJECTS: open the configured model folder and preserve retry
Open the configured SAM directory itself and preserve the pending mask
action and existing files when folder creation or the opener fails.
Run the Linux desktop opener in a background task and deliver failures
to the UI for retry. Directory creation remains synchronous.

Cover configured paths, creation and opener failures, retry state and
worker completion.

Partially addresses #366; model download hosting remains unresolved.
Credit: sanyamsingh001

Co-authored-by: Codex <codex@openai.com>
2026-10-13 04:22:23 +00:00
Brandon ThomasandClaude Opus 5.5 2f0931d3ca Batch 32: rustfmt after merges
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 00:13:49 -04:00
bflatastic ed1d641f22 Merge PR #1174
# Conflicts:
#	crates/catalog/src/lock.rs
#	crates/pipeline/src/spots.rs
#	docs/parity-checklist.md
2026-10-13 11:06:26 +07:00
bflatastic 32253d862a Merge PR #1017
# Conflicts:
#	crates/pipeline/src/spots.rs
#	docs/gaps.md
#	docs/parity-checklist.md
2026-10-13 11:05:54 +07:00
bflatastic 8ca6a835ec Merge PR #1411
# Conflicts:
#	crates/ui-egui/locales/pt-br.json
#	docs/whats-new.md
2026-10-13 11:05:35 +07:00
bflatastic 74a0b5796a Merge PR #1070
# Conflicts:
#	crates/pipeline/src/spots.rs
2026-10-13 11:03:05 +07:00
bflatastic ac19978d37 Merge PR #1182 2026-10-13 11:02:53 +07:00
bflatastic 5475691ea4 Merge PR #1353 2026-10-13 11:02:41 +07:00
bflatastic f28b9c46d3 Merge PR #1051
# Conflicts:
#	crates/pipeline/src/spots.rs
#	docs/gaps.md
#	docs/parity-checklist.md
2026-10-13 11:01:45 +07:00
bflatastic b2a5997e55 Merge PR #1412 2026-10-13 11:00:53 +07:00
bflatastic 035c378758 Merge PR #1052 2026-10-13 10:39:12 +07:00
Brandon Thomas 765e94e0a1 Merge PR #1255 2026-10-12 23:31:19 -04:00
dev-null-undefinedandClaude f47fb86077 LR-BEHAV-GPU: say once when no GPU is found, instead of quietly rendering on the CPU
With GPU rendering on and no graphics adapter at all (wgpu: "no GPU adapter among …", e.g. no
Vulkan loader: a `cargo run` outside `nix develop` on NixOS), every render ran on the CPU, much
slower, and only Settings ▸ Performance, Help ▸ System Info and `perf.gpuReason` said why.

The desktop app now queues one start-up notice per session once the device has been tried
(`gpu::ready()`, never blocking): "No graphics processor found: LightCraft renders on the CPU, which
is much slower. Details in Help → System Info.", on Linux with "On NixOS, start it from `nix
develop`." (→, not ▸: no installed font paints ▸.) `panels::notices::gpu_notice` decides from the
reason: nothing when the GPU was turned off (preference, LIGHTCRAFT_GPU, LIGHTCRAFT_GPU_BACKEND=off),
only a software adapter was skipped, or it failed later. Headless sessions and tests never show it
(`app.gpu_notice` is set by the desktop app only). Translated in all nine catalogs; one README
sentence in the Nix section.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-13 01:58:40 +02:00
dev-null-undefinedandClaude a99c084805 M5.6: a slider drag no longer decodes the edited photo's thumbnail
Each drag tick changed the edited photo's settings, so the filmstrip asked
for a new thumbnail render every frame. For an unedited raw shown with its
embedded preview, the thumbnail-level source had never been decoded, so each
of those renders decoded the whole raw (a 64 MP ARW: over a second), several
side by side, and the loupe's drafts queued behind them.

- The edited photo's thumbnail is not re-rendered during a slider drag while
  it has one; it follows once the drag ends.
- A thumbnail of a photo whose preview-level source is decoded (the one in
  the loupe) is cut from that source (SourceRef::Fit) instead of decoding the
  file again; not when the photo has a denoised picture.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-13 00:29:45 +02:00
Marco Arnulfo 3480c99b3e macOS: closing the window ends the process in on_exit, as menu Quit does (fixes #1331)
The close button let eframe drop the window while AppKit's run loop was
still running; AccessKit restored the content view's class and AppKit's
Touch Bar KVO observer threw on the next display cycle. on_exit now ends
the process on macOS once settings and library are saved and background
work has stopped, as #444 does for Quit from the menu bar. Same fix as
storytold/gridcraft#117.
2026-10-12 23:58:13 +02:00
Brandon ThomasandClaude Opus 5.5 701f78ea2e Release 0.7.1: fix the macOS build
eframe 0.36's App::on_exit takes the glow context as a second argument.
The macOS-only menu-bar Quit path still called on_exit(self), so the
0.7.0 macOS release job failed with E0061. Pass None; our on_exit ignores
it. macOS isn't built in CI, so only the release build caught it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 11:13:08 -04:00
Vikram Bhaskaran fbfa87d614 Treat a numeric --connect value as a loopback port. 2026-10-13 01:24:02 +11:00
Matthew James SivakandClaude Opus 5.5 b446a5b829 LR-VIEW-ZOOM: time drag drafts with the frame that shows them, give up early (#330)
- A full-size draft's time is its render plus the CPU time of the frame
  that uploaded it (eframe's cpu_usage, Perf::host_frame_ms): render
  times alone said 231 of 234 1:1 drafts fit 16 ms while no complete
  update did (PR #1009 benchmarks).
- A drag whose full-size drafts take more than twice the budget drops to
  0.6 after three of them instead of at its end (a refined mask ran at
  190 ms a step for the whole first drag).
- Under a zoom window the hidden whole-frame draft is the 0.6 one, and it
  is not timed (the frame's time there is the window's upload).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 14:06:23 +02:00
Royce 1f45462529 fix(cli): --connect 7980 takes a bare port as loopback (#1178)
After `lightcraft --control 7980` was running, the natural next thing to type
was `lightcraft-cli run --connect 7980 ui.inspect`. The CLI's argument parser
only consumed the value if it contained a colon (it was looking for a full
`HOST:PORT`), so `7980` fell through and was parsed as a command id, producing
`{"error":"unknown command `7980`","ok":false}`. The default address was used
behind the scenes, but only by coincidence matched `127.0.0.1:7980`.

The parser now also consumes a token that is all digits (a bare port) and
formats it as `127.0.0.1:<port>`. Non-numeric, non-`HOST:PORT`-shaped tokens
(e.g. `ui.inspect` after `--connect`) keep falling through so the existing
`--connect ui.inspect` form (use the default address, then run ui.inspect)
still works.

Two parser sites have the same shape — the global `--connect` in `cli()`
and the per-step `--connect` in `run_step()` — and both are updated.

Test: connect_bare_port_is_treated_as_loopback_port. cargo test -p
lightcraft-cli: 21 passed, 0 failed. cargo clippy -p lightcraft-cli
--no-deps clean.
2026-10-12 12:36:35 +08:00
ChippedWandClaude Opus 5.5 17aafcd5bf LR-EXP-PSD: Photo ▸ Edit In ▸ Open Original in Other Editor (RAW, no copy)
Hands each selected photo's original file itself (a raw stays a raw: no
render, no copy, nothing added to the library) to the editor set in
Settings ▸ External Editing ▸ Other editor, e.g. DxO PhotoLab. Also on a
photo's right-click menu (Edit In).

- `photo.openOriginalInExternal {ids?, app?}` (UI command, menus.rs): up to
  50 photos; demo photos and missing files are skipped with the reason; when
  none has a file it fails saying why (toast + error, no panic). Enabled when
  the active photo has a file and the host can start programs (not the web).
- Several photos go to one start of the editor (`Services::open_all_with`,
  editors.rs `open_all_with`): a single-window editor such as DxO PhotoLab
  drops the files that arrive while it is still starting when started once
  per file. The system default ("" editor) still opens them one by one.
- Settings hint, translations of the two new toasts, parity rows
  LR-EXP-PSD / MENU-FILE-PHOTOSHOP.

Tested by hand on Windows with DxO PhotoLab 8 (one and several raws).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 04:31:34 +02:00
Andrew FrahnandClaude Opus 5.5 2c3d7a743f Control channel: give up a request the app draws no frames for, never run it late (#718)
On Wayland a window on a hidden workspace or minimized gets no frame
callbacks, so queued control requests waited 60 s and then ran when the
window came back, doubling retried work. The server now abandons a request
the app hasn't started after 5 s without a frame, says why, and the app
skips abandoned requests. Running requests keep the full 60 s. Also
as_chunks in spots.rs for the newer clippy lint.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-12 05:13:02 +11:00