CodeRabbit on #2689 (head b840f3f29):
- docs/control-protocol.md, docs/lightroom-catalog-import.md (#2685): the
destination window opens only when settings.lightroomImport is "ask"; the
three places that said it always asks now say so.
- docs/parity-checklist.md: two dated prose entries cut the revision table,
so the rows after them (#2685's among them) fell outside it. They are rows
now, and the batch's rows read newest-first.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Conflicts:
- xtask/src/main.rs: upstream's runners (cargo test or nextest, no
examples, doctests apart, one wasm `cargo check --keep-going`) with
#2302's `ci --quick` on top: test_selected builds the same targets
(--lib --bins --tests), the wasm check takes the changed crates in one
call, the full test step is upstream's.
- apps/lightcraft/src/control_server.rs: upstream's `before` closure, with
the batch's no-delay sockets kept.
- docs/parity-checklist.md: LR-BEHAV-ACCESS from main (tests/it path),
LR-BEHAV-LOCALE / LR-BEHAV-LOCALIZE from the batch (Polish).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CodeRabbit on #2689 (head 0d64d9ef3):
- Edit In (#1417) returned an error when the editor failed to open although
photo.editExternal had already added the copies to the library, so a script
retrying on ok:false made another set each time. It now succeeds with
`failed` set (null on success, as open_originals) and a toast saying why.
Test edit_in_reports_an_editor_that_wont_open_without_failing fails without
the fix.
- docs/gaps.md, docs/parity-checklist.md: one entry was listed twice on the
Change line.
- Revision-history rows the batch added now read newest-first (gaps,
parity-checklist, localization-parity, target-app-parity); upstream's own
rows are left where they were.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- ORF_DECODED's Camedia branch drops the redundant \\S+ before .*; with two anchored greedies the scanner can no longer retry the same substring count-for-count at lower and lower split points (same match set, verified on 14 body strings).
- {c: 0 for c in classes} is now dict.fromkeys(classes, 0), which the rule wants and which stones the per-class counters.
* CI: dev profile keeps line tables for workspace crates and no debug info for dependencies
* Control server tests: persistent_requests_and_slow_replies_outlive_the_idle_read_timeout no longer races the idle timeout
Each request (and the closing half-shutdown) reaches the socket before the
worker reads, so on a slow machine only the reply waits outlast the 100 ms
idle timeout, as the test intends. The idle test sends its input the same
way, and the no-frames test allows 5 s instead of 2 s to give up.
* CI: one integration-test binary per crate
Each crate's tests/*.rs become modules of tests/it/main.rs (51 test files
into 13 binaries, plus 6 that stay their own). Inner #![cfg] attributes
move to the mod lines. Files that change state for the whole process keep
a binary of their own: codecs jxl_tail_heap and raw sr2_heap (dhat global
allocator), gpu shutdown, shutdown_first and memory, ui-egui shutdown.
Docs, comments and the GPU probe name the new paths and targets
(--test it <module>::); the CI report names an it binary's skips after
the crate.
* CI: tests under nextest, doctests apart, one wasm check
xtask ci runs the tests with cargo nextest run --lib --bins --tests when
cargo-nextest is installed, else cargo test with the same targets and a
one-line notice; doctests run after them with cargo test --workspace --doc.
The heif and hardware GPU steps use the same runner. Examples stay out of
the test build (clippy --all-targets still checks them). Concurrent tests
under nextest follow the RUST_TEST_THREADS cap (NEXTEST_TEST_THREADS).
The wasm check is one cargo check --keep-going over the same crates, and
clippy runs with --keep-going.
* CI: tests run under cargo test by default, nextest with LIGHTCRAFT_NEXTEST=1 (per-process scene cache)
Review round: whether a recent library is there is kept apart from whether it can be
opened. `Unavailable::is_missing` (not found, a file, no library, a folder that can't be
listed) is the only answer that greys out File ▸ Open Recent Library and makes
`app.recentLibraries` say `exists: false`; an unreadable catalog or a failed check gives
`null`. A failed open from the launch window (say another program has the library)
drops the answer so it is checked again, instead of storing the open's error as one; the
launch list keeps the error on its row. Test: Open on a locked library from the launch
window, then the menu entry stays enabled and `exists` is true (fails if a failed open
marks it missing); an unreadable catalog gives `exists: null`. Docs worded to match.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review round (CodeRabbit + an adversarial review, handled together):
- The launch list starts with every row "Checking…" and a worker per path checks it (a
share whose server is gone, or a sleeping disk, can take seconds or never answer; one
that hangs doesn't hold the others). After 5 s a row says the drive isn't answering;
it can still be opened. Once every answer is in, the selection moves to the first
library that can be opened, unless the person moved it.
- File ▸ Open Recent Library and `app.recentLibraries` no longer look at folders either
(the menu is rebuilt every frame): they use the same answers, refreshed after 30 s, when
a library is opened, removed from the list, or fails to open. `app.recentLibraries`
gives `exists: null` and `checking` until a check answered.
- Reasons are found on the worker and put into words on the UI thread, in the interface
language. A check whose worker dies ends as "This library couldn't be checked".
- Each check holds an activity row ("Checking recent libraries"; quick ones don't show).
- Docs: control protocol (`checking`, `available`, `exists`), What's New; fr strings.
Tests: slow check never holds the window; late answers keep the person's selection; the
menu takes its answers from a worker (fails with the old is_dir()); reasons in French; one
hung drive doesn't hold the others; a dying check ends; reopening clears a missing answer.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review follow-up on #2685. CodeRabbit: the library-switch check ran for Merge only, so a window left over from before a switch could still make a library from defaults that described the old one; it now covers both choices (new test). The Mac session: on macOS the switch's own notice can replace the error toast before the test reads it, so the stale-window tests check confirm's error directly. docs/gaps.md gap 13 updated with the parity row (CodeRabbit).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AbegtYaPuwqZ81pGfELsFK
- fr-formats: "{n} photo{0} déplacée{0} vers « {1} »" (the adjective took a literal "(s)").
- docs/localization-parity.md: 7 languages in "Other languages LightCraft ships" (was 4);
Swedish and Polish ship, so they leave the "lacks" list and the target totals.
- xtask ci_lock (#2302): the lock-setup errors name the way out
(`LIGHTCRAFT_CI_LOCK=off` or `=<dir>`).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The launch window no longer probes each recent library with the lock check
of File > Open Library (which created catalog.lock and wrote
catalog.lock.owner into every free library at every launch, including
other drives, network shares and sleeping disks). A row is now checked by
looking only: the path exists, is a folder, can be listed, holds a
LightCraft catalog, and that catalog opens for reading. A library another
program has open is found when Open is pressed: the open fails with the
lock owner's message, the window stays and greys the row out.
New test: listing (and drawing the window) leaves every library folder
byte-identical (contents and modification times); it fails with the old
probe. Docs (What's New, parity row, control protocol) say so.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AbegtYaPuwqZ81pGfELsFK
Settings > General > At launch: Open the last library (default, as before)
or Ask me. With Ask me the desktop app opens nothing and the library-problem
window lists the recent libraries instead (name, folder, when last opened;
missing, locked or no-longer-library ones greyed out with the reason; the
last one selected): Open, Open Another..., New Library..., Remove from List,
Quit, with arrows / Return / Esc. Alt/Option held in the first 2 s after the
window appears shows the same list once over the library that opened (winit
reports no modifiers before the first frame; macOS only on a change).
File > Open Recent Library > Remove from List and
app.recentLibraries {remove: path} forget one entry; nothing on disk is
touched. The list reports lastOpened, kept under a new settings key
(recentLibrariesOpened) so older files and versions keep their list.
--library and LIGHTCRAFT_LIBRARY keep priority (launch_plan), --memory never
asks, lightcraft-cli never reads ui.json. Settings are saved while the list
shows; command-line files go into whichever library opens.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AbegtYaPuwqZ81pGfELsFK
LRC-LIB-CATALOG-IMPORT. Before File ▸ Import Lightroom Catalog… starts, a
window asks where the catalog goes: merge into the open library (named,
with its photo count; preselected when it is empty) or create a new
library named after the catalog file, next to the open library (both
editable, Choose… picks the folder). The new library is made and opened
through app.newLibrary (its rules: an empty or new folder, refused while
another import runs), then the catalog is imported into it. Cancel does
nothing. Open Library… on a Lightroom catalog goes through the same choice.
Opt-out: Settings ▸ General ▸ When importing a Lightroom catalog (Ask
where to import it / Always merge into the open library), saved as
ui.json settings.lightroomImport (lenient, default ask); the window's
"Don't ask again" picks Always merge. A session in memory merges without
asking, as before.
file.importLightroom takes {into?: open|new, name?, location?} to choose
without the window. UI layer only: library.importLightroom, the engine
and lightroom_import.rs are unchanged.
Tests: decision table (setting x library on disk/in memory x photos x
into), params, default name; headless window / merge / new library /
cancel / empty library / setting / Don't ask again persisted / folder
not empty / into / in memory / stale library; tests_open_lightroom
follows the new window. Format messages in all 17 catalogs, plain
strings in French. Docs: lightroom-catalog-import, control-protocol,
parity-checklist, whats-new.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AbegtYaPuwqZ81pGfELsFK
The engine already applies photo.setMeta to all selected photos via
targets(), but the Info panel and All Metadata dialog always show a
single photo's values with no mixed-value indication.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Measured on 68 photos straightened by hand in Lightroom Classic and 300 left as shot, Auto
straighten turned 79 of the 300 by more than 1° (65 by more than 2°): the edges of planks, a
branch or a bird's wing were taken for the horizon. It now answers only for at most 10° and on at
least 3 agreeing lines (Upright Level is unchanged): 19 of the 300, with the same 14 of the 68
levelled within 0.5° and 1 instead of 3 more than 1° off. When it finds nothing, the app says so
instead of leaving the photo without a word. LR-CROP-AUTO goes to 🟡: it still leaves most tilted
photos alone.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- docs/parity-checklist.md, docs/gaps.md, docs/target-app-parity.md: rows two PRs both
changed had been kept twice by the doc resolution. Each row is now upstream main's row
plus every merged PR's own edit (3-way against the PR's merge base), so main's newer
text is kept (LR-LIB-FLAG #1013, Lanczos-3 in LR-MERGE-HDR / -PANO, LR-EXP-DIM #1620,
gaps 8 and 10) and the PRs' additions too; LR-BEHAV-LOCALE / -LOCALIZE take main's
Georgian and #1846's Polish. `cargo xtask parity --write` refreshed the summary.
- #1313 added "Show Frame Rate", a switch; #2195 names switches for what they show
(menubar::tests::toggles_are_named_for_the_thing_not_show): now "Frame Rate".
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Find Missing Photos took a missing photo's file name with Path::file_name,
which doesn't split on `\` on macOS or Linux, so a library made on Windows
had photos it could only find by content: those with no hash on record
(Lightroom-catalog imports, files edited since) were never found. Both the
missing photos and the files found on disk are now named by one rule: the
part after the last `/` or `\`, in lower case.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>