Preserve upstream compact-toolbar layout and keyboard regressions alongside the rating and flag padding regression.
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
export_progress_and_cancel failed every time on a fast machine (M5 Max, 4
export lanes): the 8 demo photos exported within the 100 ms progress
throttle, so the first progress above 0 was the final 8/8 and the cancel
arrived after the export had finished (a reply to the cancelled request,
or all 8 files on disk).
The test now wraps Headless in a test-only backend that, at the first
photo boundary, keeps polling the server's real progress hook (which
answers pings and reads cancels) until the cancel is read or the test
releases it, bounded at 20 s so a broken cancel fails rather than hangs.
The first export is released once the mid-batch ping is answered (that
was the same race), the second only ends by the cancel. That makes the
outcome exact, so the check tightens from "1..7 files" to "exactly the
one completed photo", which also proves the photos the other lanes had
already rendered are never written. No production code changes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Between a kept luminance share of 0.15 and 0.25 the table colour is now blended in with a smoothstep,
so noisy shadows show no step between pixels that get the table colour and pixels that keep theirs.
At 0.25 and above pixels render exactly as before.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Raws with a fitted camera look (e.g. Nikon NEF) rendered HDR with the
generic tone curve instead, more than a stop darker through the midtones
than the SDR render: PQ JPEG XL / AVIF exports looked dark everywhere,
and gain map JPEGs carried negative gains, so they went dark on HDR
displays. ToneMap::camera_hdr keeps the camera curve up to 1.5 EV over
grey and lifts highlights as ToneMap::hdr does.
Co-Authored-By: Claude <noreply@anthropic.com>
Exercise both separator directions for folder and individual-file imports.
Verify duplicate detection, stable photo IDs, added/deleted file detection,
and reuse of a confirmed scan when the folder spelling changes.
Co-Authored-By: GPT-6 Astra <noreply@openai.com>
Rebuild fixture path components with native separators so expected paths
match directory scans on Windows, including files that have been removed.
This is a prerequisite for Windows validation and overlaps the path-helper
fix in upstream PR #660; it can be dropped once that change lands.
Co-Authored-By: GPT-6 Astra <noreply@openai.com>
HueSat::apply restores luminance with before / after. Near black, where noise makes some channels
negative, the hue/saturation table can bring after close to 0, and the gain turned single pixels
white (isolated white dots in dark areas, most visible at fit). Luminance is now restored only if
the table kept at least 25 % of it; such colours keep their colour, like black and non-finite ones.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The crop tool's rotate pointer always bulged up, which only reads right
above the box. It now turns to bulge away from the nearest point of the box
outline: right beside the right edge, down below it, left beside the left
edge, diagonally off a corner, and following a box that is already rotated.
icons::paint_turned draws an icon turned about its centre; icons::paint is
unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- release.yml: new linux-riscv64 job (ported from GridCraft): cross-compiles on
ubuntu-24.04 against ports.ubuntu.com riscv64 libs, packages a tar.gz with
package.sh --formats tar, smoke-tests lightcraft-cli under QEMU. The release
job needs it, so its tarball lands in the draft and SHA256SUMS.
- packaging/linux/package.sh: CROSS_ARCH/CROSS_TARGET/CROSS_COMPILE/EMULATOR
support (target dir, cross strip, emulated --version).
- README Downloads and docs/releasing.md list the riscv64 tarball.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
In the side-by-side and top/bottom Before/After layouts a click in the
Before view was mapped to image coordinates through the After view's
rectangle, so the White Balance Selector sampled the wrong point (at Fit,
the photo's edge). The click tools in general_interaction now use the
Before view's image rectangle when the pointer is in the Before view's
pane; the pane decides, since zoomed in an image reaches past its pane.
New headless test tests_before_after.rs clicks the same image point in
the After and Before views (side by side, top/bottom; Fit and 100 %) and
expects the same white balance. Without the fix: Before 8202 K / +116,
After 2710 K / +33.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
In Compare a filmstrip click made the clicked photo active without
showing it, so the Edit panel and the rating keys acted on a photo on
neither side (a headless test rated the hidden photo 2 -> 3 stars). The
click now puts the photo into the active side (the candidate after
entering Compare, the select after clicking the select's photo); a photo
already on the other side swaps the two. The active photo is always one
of the two shown. Other views keep the usual selection clicks.
New UI command compare.set {id, side?: "select" | "candidate"} for the
same through the control channel and MCP.
New headless tests in tests_compare.rs: the active photo stays on screen
and no hidden photo is rated (Fit and 100 %), and replace / swap /
explicit side / unknown side.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>