Since the web zip moved from trunk to cargo xtask web (aeaf4154), packaging/web/package.sh needs the
wasm-bindgen CLI at Cargo.lock's version, and the shared release workflow installs only trunk, so
0.9.0's Web (wasm) job failed. In CI, install it first (as FilmCraft and EffectCraft do).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Found while testing crates/catalog/src/dates.rs:52 (shift_iso, tail = iso.trim().get(19..)); used by crates/engine/src/cmd/manage.rs:222
Co-authored-by: Claude Haiku 5.5 <noreply@anthropic.com>
Found while testing crates/engine/src/cmd/cull.rs:17-20 (secs, used by the burst grouping at cull.rs:45)
Co-authored-by: Claude Haiku 5.5 <noreply@anthropic.com>
craft-libs#169 is merged, so the fork pin is no longer needed. Pinned to
storytold/craft-libs 82a24b006eefa036b43c09a67fb9a3fbd17ffe63 (main after the merge) rather than the older 12281d07,
which predates resolve_absolute and the Windows colon refusal.
Unrelated to the #2135 security fix in the commit before this one: both lines
predate this branch and are rustc/clippy 1.96 lint churn. `cargo clippy
--workspace --all-targets` only warns about them, but `cargo xtask ci` runs it
with `-D warnings` (xtask/src/main.rs), so the gate fails before any test runs.
- `clippy::nonminimal_bool` in `slide_overlay::shadow` (reported twice, two
overlapping suggestions on the one expression): `!(A && B) || !C || !D` is
`!(A && B && C && D)` by De Morgan, which is what the line now says. The
operands are pure float comparisons and `is_finite` guards with no side
effects, so this is an exact rewrite — a NaN or infinite `mx0`/`my0` still
makes `shadow` return without drawing, as before. (Clippy's second suggestion
drops the `my0.is_finite()` term and is *not* equivalent; it is not used.)
- `clippy::drop_non_drop` on `drop(write)` in
`same_folder_as_original_refuses_photos_without_a_folder`. The line is not
dead: `write` is a closure holding a mutable borrow of `written`, and the
`drop` ended that borrow so the next assertion could read it. The closure now
lives in a block of its own instead, which ends the borrow at the brace
rather than relying on `drop` of a non-`Drop` type. Every assertion of the
test is unchanged.
Co-Authored-By: Claude <noreply@anthropic.com>
Photo ▸ Edit In ▸ Open Original in Other Editor handed a photo's stored path
straight to `open` / `xdg-open` / `explorer.exe`, with `Path::is_file()` as the
only check, and Lightroom catalog import kept original paths exactly as the
catalog had them. A crafted `.lrcat` could therefore put `payload.exe`,
`start.lnk`, `run.bat`, `page.hta`, `Evil.app` or `go.command` into the library
and have it run on one user click.
- `lightcraft_engine::open_path`: one gate for every path that leaves
LightCraft for the OS. The extension must be one LightCraft reads
(`import::EXTENSIONS`, which already covers the Edit In copy formats
`tif`/`tiff`/`psd`), it must be a regular file (so a `.app` bundle and any
other folder are out), what a link resolves to must be a read type too, and
what is handed over is the absolute path. A refusal is the sentence shown to
the user.
- Open Original and Edit In go through it; a refused photo is listed in
`skipped` with the reason, and the menu item is greyed out for a record that
can never be opened (extension only, so no disk or share is touched while a
menu is drawn).
- `lightroom_catalog::refuse_non_media` drops such images where the catalog is
read and again where its photos become records, with a warning each, so a
refused entry never becomes an openable library record. A virtual copy of a
refused original goes with it.
Tests: a catalog naming a program (and a virtual copy of it) imports neither
while the catalog's real photos do; a library record naming a program is never
handed to the stub launcher, which records no invocation, and a program
selected beside a photo leaves only the photo, by absolute path. Both fail
before this change.
Co-Authored-By: Claude <noreply@anthropic.com>
Batches 36's doc conflicts kept main's side of rows both sides had touched; for some, main had not changed the row,
so the PR's update was lost. Restored: LR-VIEW-FILMSTRIP (#1482), KEYC-COMPARE (#1491 + #1555), LR-IMP-LOCAL
(#1547 + #1820), LR-LIB-STACK (#1556), gaps 28 (#1555), 2 (#1029) and 23 (#1038), ROADMAP UI/UX (#1038). The
raw-parity by-maker and by-use tables are re-measured with coverage.py (545 verified, ~94% / ~85%).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Windows ARM64 on main failed since batch 36: #1479's scripts pinned 8dcdacd, which predates the Thai face #1311
made required.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Georgian next to Italian in the language table, menus, catalogs list and docs; ka-formats.json lists the format
strings other PRs added, in English until translated.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>