Several commands read their photo parameters leniently. An `ids` that
wasn't a list of whole numbers (`"ids": 3`, `["3"]`) fell back to the
selection or the active photo, and an `id` that wasn't a whole number
(`"id": "5"`) fell back to the active photo, so a call meant for one
photo acted on another:
- ids: album.addPhotos / removePhotos / toggleTarget, library.select,
develop.set, denoise.queue, faces.index, mask.applyEffect (mask ids)
and app.export;
- id: photo.relink (relinked the active photo to the file),
photo.removeRegion / setRegion, faces.setName, stack.setTop / moveUp /
moveDown / split, library.findSimilar, the photo queries,
denoise.cancel (cancelled every photo) / status and ui.render.
They now refuse such values with an error, as Session::targets already
does for the other commands (shared cmd::photo_ids / photo_id). A
missing or null value still means the selection or the active photo.
Measured with RawNIND on a 20.8 MP raw: an Apple M3 does a tile in 116 ms, an
RTX 3060 Ti over Tailscale 345 ms there and back; sharing blindly made the
export take 34 s instead of 6.4 s while the photo waited for the server's last
tiles. The greeting now times a check tile there and back, and tiles are
shared only when that beats this computer's own time per tile (the card's, or
the CPU's with its threads counted as `card_is_faster` does). Otherwise
`denoise.status` → `device.remote` says `slower` with both times. With the
server configured but slower the export takes 7.2 s (greeting + check tile).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- `lightcraft_denoise::remote`: protocol v1 over TCP (JSON lines + byte-shuffled,
deflated half-float tiles), a `Remote` client (connection pool, four slots,
greeting checks the model's SHA-256 and tile size, gives up after three failed
tiles) and `serve` (shared token compared in constant time, size caps including
payloads that inflate past their tile, connection limit, timeouts).
- Engine: with LIGHTCRAFT_DENOISE_URL / LIGHTCRAFT_DENOISE_TOKEN set, the loaded
model greets the server on first use and adds its slots to this computer's
tiles at once; a tile goes to a free server slot, else runs here, and a tile
the server fails runs here too. `denoise.status` → `device.remote` says where.
- `lightcraft-cli denoise-serve` serves the installed, selected model on this
computer's card (checked against the CPU, as in the app) or CPU.
- docs/denoise.md: setup, safety (unencrypted: VPN/LAN only) and the protocol.
Tested: protocol round trips, refusals (token, model, tile size, hostile
payloads), fallback and give-up; the engine model with a live, a mismatched
and a dead server; the CLI binary serving a synthetic model end to end.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
While a photo's AI Denoise picture is being made or waits its turn, the
stack shows "AI Denoise" with the running photo's tiles as a percentage
and its file name (no cross: what is made follows the Denoise amounts and
Settings). The row goes when the queue is idle.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Downloading the SAM 3, face and AI Denoise models shows a row with the
model's name and the megabytes so far; its cross stops the download like
the Settings button. The denoise download tests get their own scratch
folders (they shared the face tests' names and could delete each
other's files).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Recover the unpublished denoise work against current upstream. Preserve later opt-in model setup, shared Rust HTTPS downloads, pending-photo resume and an Amount-independent switch. Keep original RAWs unchanged and share the current color finish between ordinary and denoised sources.
Bound public tiling inputs, refuse corrupt-cache exports, and retain synthetic/real-model coverage. Include two Windows path spelling fixes in existing tests. No model weights, face code, media or Tract runtime are included.
Validation: cargo xtask ci (all seven gates); installed RawNIND CPU/GPU agreement 1.38e-6; native CPU preview/export on a public 24 MP Sony RAW, unchanged original SHA-256.
Co-authored-by: Codex <noreply@openai.com>
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
Generated with Codex.