Commit Graph
3794 Commits
Author SHA1 Message Date
aiongg c414ef06ec Merge pull request #2689 from francoisgn/round3a-integration
Round 3a integration: 42 PRs of #2580, built and tested locally
2026-10-24 09:39:25 -04:00
Dylan M. TaylorandClaude Opus 5.5 23f54803e6 fix(tools): keep install-sam3.sh POSIX sh (no arrays for curl auth options) (#2760)
The script runs under #!/bin/sh; on dash the array assignment was a
syntax error. Pass the auth option and its value as two plain words.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 04:43:15 -04:00
e83bf7742e security: bound codec allocation to what a hostile file declares (#2769)
* security: bound codec allocation to what a hostile file declares

A GIF's first image descriptor could name a frame up to four times the
canvas budget and be charged that allocation before any size check saw
it; PNG's compressed text chunks were expanded through an unbounded
inflate; JPEG handed hostile frame headers to the allocating decoders
(zune for the common cases, jpeg-decoder for CMYK/scaled/lossless) and
let an APP1 flood drive quadratic metadata reassembly. Each now refuses
on the same max_pixels budget the other decoders honour, with a
regression test per gate.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>

* Fix clippy collapsible_if in DCT-scale size check

* Check jpeg-decoder header on scaled path; cap APP1 markers in decode_codes8

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Codebuff <noreply@codebuff.com>
Co-authored-by: Dylan Taylor <dylan@craftmaker.dev>
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-24 04:32:58 -04:00
francoisgnandClaude Opus 5.5 3eca6ede75 Merge #2701@f1f169ede (a launch-list row waits for a re-check's answer) into round3a-integration
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JbBu4MRiaKxEbSrMTZiLj8
2026-10-24 10:39:09 +03:00
francoisgnandClaude Opus 5.5 f1f169ede1 #1290: a launch-list row waits for a re-check's answer
FredApps on #2689: a re-check started while the launch list is up (a
library not found looked for again, the window back to the front) never
marked its row as checking, so `apply_answers` skipped the new answer: a
library plugged back in stayed "not found" in the list. A row is re-armed
when its path is asked again about an answer it has (not after a failed
open, whose reason stays on the row). Test
a_library_found_again_comes_back_to_the_launch_list fails without it.

Fixes #2774

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JbBu4MRiaKxEbSrMTZiLj8
2026-10-24 10:39:09 +03:00
aiongg 47801e96d2 Merge round3a-integration (c305861a4) into the import review fixes 2026-10-24 06:03:26 +00:00
francoisgnandClaude Opus 5.5 c305861a47 Merge #2701@2ae63a189 (the permission test cleans up even when it fails) into round3a-integration
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JbBu4MRiaKxEbSrMTZiLj8
2026-10-24 07:53:58 +03:00
francoisgnandClaude Opus 5.5 2ae63a189a #1290: the permission test cleans up even when it fails
CodeRabbit nitpick on #2701: a failing assertion skipped giving `Locked` its
permissions back, and a folder left at mode 000 can't be removed. A Drop
guard restores the mode and removes the test's folder on every exit
(found one left behind by an earlier failing run).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JbBu4MRiaKxEbSrMTZiLj8
2026-10-24 07:53:57 +03:00
francoisgnandClaude Opus 5.5 670fb9c930 Round 3a: two Slideshow settings tests wait for their result instead of one frame
The gate on cf9334907 failed on
tests_slideshow::playback_settings_drive_the_slideshow_and_bad_ones_are_refused
and ::settings_shows_the_slide_layout_and_a_live_preview (#1287); they passed
on the two gates before and in 15 of 15 local runs of the module with 4
threads, and nothing since touches the slideshow. Each checked its result
one frame after the click / after the preview changed, which a loaded
runner doesn't always give: they wait for it now (the checkbox state, the
green backdrop pixel). Test code only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JbBu4MRiaKxEbSrMTZiLj8
2026-10-24 07:53:57 +03:00
aiongg 8d606db724 Merge the deferred import review fixes (francoisgn/lightcraft#2) into round3a-integration 2026-10-24 04:53:30 +00:00
francoisgnandClaude Opus 5.5 cf93349071 Merge #2701@9c7bdbd44 (exists docs, root-safe permission test) into round3a-integration
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 06:56:55 +03:00
francoisgnandClaude Opus 5.5 1b22c3b870 Round 3a: cull takes the view before an open drag ends; language commands listed
CodeRabbit on #2689 (head 496291276):
- `cull` takes the view it hands on from before ending an open drag, so the
  drag's own commit can't change it first. (In practice the view already
  follows the drag's preview, so the photo is out of it mid-drag either way;
  this is the order every caller should use.)
- LR-BEHAV-LOCALE cites the commands of all 18 languages.
- Two 2026-10-21 revision rows moved above the 2026-10-20 ones.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 06:56:54 +03:00
francoisgnandClaude Opus 5.5 9c7bdbd44c #1290: exists docs match the code; the permission test skips as root
CodeRabbit on #2689 (head 496291276):
- docs/control-protocol.md, `app.recentLibraries`'s doc, `is_missing`'s
  doc: a folder that can't be listed is `exists: null` (couldn't tell), not
  `false`, since 142e496f5; the launch window still shows why for every
  reason.
- a_failed_lookup_doesnt_mark_a_recent_library_missing skips (System) when a
  folder with no permissions can still be read, as when running as root.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 06:54:39 +03:00
aiongg dc406df9b4 Import: a deferred review never replaces a running import
A finished scan's review that waits for a dialog to close counts as an
import in progress: dropped photos and another scan are refused
meanwhile. Confirming a review while an import (or a Local folder
browse scan, which ends in one) runs is refused too, since replacing
it would stop its worker.
2026-10-24 03:34:40 +00:00
aiongg 9c9f87e589 Import: a deferred review belongs to the library that scanned it
A scan can finish while Settings is open, and Settings can then open
another library. The review records the library generation it was made
in: a review waiting for a dialog to close is dropped once its library
is gone, and confirming one in another library imports nothing.
2026-10-24 03:34:40 +00:00
francoisgnandClaude Opus 5.5 496291276c Merge PR #2468@218e7451c (its main merge) into round3a-integration
Its code is already in the batch; docs keep the batch's version, headers list
#2468's change entries too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 05:50:46 +03:00
francoisgnandClaude Opus 5.5 538b261067 Merge upstream/main (#2763) into round3a-integration
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 05:50:04 +03:00
Dylan M. TaylorandCodebuff 4ae3089420 security: bounded keyword, repair and journal-recovery work (CodeRabbit) (#2763)
* security: harden dev-tool scripts and the testkit skip-log test (CodeRabbit)

- tools/install-sam3.sh: the HF bearer token went through curl's argv, where
  `ps` exposes it to other local accounts (CWE-214); it now travels through
  a private 0600 temp config file removed on exit.
- packaging/macos/import-cert.sh: the decoded .p12 was written to a
  predictable path in a shared TMPDIR, where a precreated attacker-readable
  file would receive it (CWE-377); both cert and keychain are created inside
  a mktemp -d 0700 directory now.
- docs/showcase/run.py: the reply reader copied whole responses with no size
  cap (obvious quadratic-copy and unbounded-memory DoS). It accumulates in a
  bytearray now and closes the connection past 128 MiB.
- tools/spectral_camera_table.py: alias/make/model strings and comments from
  the rawtoaces-data checkout moved directly into Rust source; one newline in
  a comment became Rust code. External text is now escaped as Rust literal
  content and comments are normalized to one printable line.
- crates/testkit: appended_lines_accumulate reused a pip-predictable temp
  dir by creating it with create_dir_all and carried on after failing to
  remove its log path, so the append would follow an attacker-planted
  symlink (CWE-59); it now fails the test if the directory exists instead of
  testing through it, and removes any existing log path only after clearly
  checking it, always failing the test instead of proceeding when removal fails.

Findings #10080/#10078 PR (rows 2, 6, 8, 9, 15 of the
storytold-lightcraft-agent-findings report).

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>

* security: bounded catalog keyword, repair, journal recovery work (CodeRabbit)

- keywords: from_file searched every hierarchical level for each flat keyword
  and `same` lower-cased two strings per comparison — one lowered level set
  instead (CWE-407, row 97).
- keywords: parse_keyword_list scanned each keyword's growing synonym list per
  line — a lowered-synonym set per keyword instead (CWE-407, row 100).
- keywords: keyword_tree stored every level's prefix twice, so a photo XMP
  keyword nested deeper than a list file accepts made stored bytes quadratic
  in the level count; the tree caps at MAX_LEVELS, and keyword_list_text names
  whatever lies deeper from the photos' and list's own spellings, so the file
  still reads back (CWE-400, row 103).
- catalog: repair validated a folder chain once per album — memoized by chain
  now, so a valid chain in a flat snapshot costs one walk overall (CWE-407,
  row 101).
- store: journal append and truncate follow a planted catalog.log symlink;
  both refuse any non-regular file at the name before writing through it
  (CWE-59, row 107), like the staging writes since #1502.
- journal: resync re-validated the whole remaining suffix UTF-8 for every
  candidate record start; a whole line is validated once (covering every
  candidate in it), and an invalid line's re-checks stop at a 64 MiB budget,
  with the tail then staying a torn one the damaged path keeps and reports
  (CWE-407, row 109).

New tests: from_file case-insensitive level match, synonyms kept once, a
4096-level keyword cut at MAX_LEVELS, a link at catalog.log refused, and
resync over a fragment of 1024 record starts.

Findings rows 97, 100, 101, 103, 107, 109 of the
storytold-lightcraft-agent-findings report.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>

---------

Co-authored-by: Codebuff <noreply@codebuff.com>
2026-10-23 22:45:21 -04:00
francoisgnandClaude Opus 5.5 8ada8a196a Merge #2701@142e496f5 (a failed lookup isn't "not found"; no timer on found libraries) into round3a-integration
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 05:34:24 +03:00
francoisgnandClaude Opus 5.5 142e496f5b #1290: a failed lookup isn't "not found"; recent libraries aren't checked on a timer
CodeRabbit on #2689:
- `unavailable` used `exists()` / `is_dir()`, which are false for any
  error: a parent folder that can't be read or a share that timed out came
  out as "Not found" and greyed the entry out. Only an absent path is "not
  found" now; other lookup errors are "This library couldn't be checked:
  <reason>" (LookupFailed), and a folder that refuses to be listed is no
  longer counted as missing either (it is there: permissions, macOS
  privacy), so neither is greyed out and opening says why.
- Every recent library was checked again every 30 s while the app ran,
  waking external drives and network shares. A library not found is looked
  for again every 30 s (a drive plugged back in; an absent path wakes
  nothing); the others only when the window comes back to the front, for
  the 8 entries shown; paths never checked are checked once.
Tests a_failed_lookup_doesnt_mark_a_recent_library_missing and
recent_libraries_are_checked_again_only_when_the_window_comes_back fail with
the old lookup / the old timer / no timer for libraries not found.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 05:34:11 +03:00
francoisgnandClaude Opus 5.5 dc3263065e Round 3a: an open slider drag ends before a rating or a deletion moves the selection on
CodeRabbit on #2689 (head 02743d187):
- crates/engine/src/cmd/library.rs: `cull` (#2327: a rating, flag or label
  that takes the active photo out of the view) and `photo.delete` /
  `photo.deletePermanently` moved the active photo without ending an open
  interaction, unlike `step` and `library.select`: the drag stayed open with
  no undo step. Each ends it now before its own change, so the drag is its
  own undo step and comes first: undo brings the rating or the photo back,
  with its edit, then the edit (Delete Permanently's undo restores the
  edit too). Test an_open_edit_ends_before_the_selection_moves_on (rating,
  delete, delete permanently) fails without the fix.
- docs/parity-checklist.md (LR-BEHAV-LOCALIZE) and docs/localization.md: list
  every shipped language (Polish, and the others the row lacked; Italian in
  the localization doc).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 05:30:45 +03:00
midasdf 218e7451c0 Merge remote-tracking branch 'origin/main' into jp/photo-paper-sizes
# Conflicts:
#	docs/gaps.md
#	docs/parity-checklist.md
2026-10-24 10:08:32 +09:00
francoisgnandClaude Opus 5.5 02743d187d Merge PR #1922@6c17d29ae (middle-button pan whatever the tool) into round3a-integration
crates/ui-egui/src/lib.rs: the batch's module list (tests_capture_time) with
#1922's. LR-VIEW-ZOOM: the batch's row plus #1922's middle-button sentence.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 02:45:31 +03:00
francoisgnandClaude Opus 5.5 2e8a50a40c Merge PR #1914@2f88d847f into round3a-integration
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 02:44:32 +03:00
francoisgnandClaude Opus 5.5 c5df3eea40 Merge #2701@f56e8c67a (its upstream merge) into round3a-integration
Docs only, and the batch's side already holds everything #2701's merge
resolved (its rows that differ are main's rows other batch PRs edited).
Tracker summary recomputed (`cargo xtask parity --write`).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 02:43:16 +03:00
KetbomeandClaude Opus 5.5 2f88d847f4 LR-BEHAV-DIALOGS: a backdrop click that closes an open dropdown keeps the dialog
Read whether a popup is open before the dialog window is drawn: drawing it
closes a dropdown the click lands outside of, so a quick click (pressed and
released in one frame) used to see no popup and close the dialog too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 20:34:47 -03:00
francoisgnandClaude Opus 5.5 f56e8c67a7 Merge upstream/main (integration batch 43) into feat/issue-1290-launch-library-choice
docs/parity-checklist.md: header lists both sides' changes, revision rows of
both sides newest-first, summary recomputed (`cargo xtask parity --write`).
docs/whats-new.md: #1290's entry joins upstream's 2026-10-23 section.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 02:32:25 +03:00
francoisgnandClaude Opus 5.5 987c54bb51 Merge PR #1499@8d71b7999 (graphic watermarks and HDR JPEG size limits, onto batch 43) into round3a-integration
docs/parity-checklist.md: header lists both sides' changes; #1499's two new
dated prose entries (2026-10-23, 2026-10-24) become revision rows like its
two earlier ones, so the history stays one table.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 02:27:33 +03:00
francoisgnandClaude Opus 5.5 144d900c21 Merge upstream/main (integration batch 43, #2729, #2737, #2412) into round3a-integration
Conflicts:
- apps/lightcraft/src/native_menu.rs: upstream's item swapping (macOS keys
  given up for real, Help menu not swapped, Settings / Quit tracked) with
  KEY-GRID's keymap-aware rows. `Item.label` stays the text the row shows
  (native_item_presentation), so `Item::native_label` reuses it instead of
  formatting it again; Settings / Quit store theirs through
  display_item_label, as upstream's native_label did. No early return after
  a rebuild (upstream).
- crates/ui-egui/src/i18n.rs: upstream's tests (#2729: one test per check
  across languages, gaps reported, never a failure) plus the batch's two new
  ones (dropped-photos notice, Polish counts); production code as merged.
- grid.rs, keyword_list.rs, masking.rs: the translation tests #2729 removed
  stay removed; the batch's new keyword and segmenter tests stay.
- cmd/library.rs, ui-egui/src/lib.rs: both test modules, each with its
  #[cfg(test)].
- xtask/src/main.rs: #2302's test_selected and upstream's translation_gaps.
- docs: tracker / gap rows take both sides' edits, headers list both sides'
  changes, history rows of both sides newest-first. What's New: one section
  per date, newest first (the batch had 10-22 above 10-23, and second 10-21
  and 10-19 sections); no line lost.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 02:26:48 +03:00
KetbomeandClaude Opus 5.5 6c17d29aed LR-VIEW-ZOOM: a middle-button drag pans the photo whatever tool is active
In Detail, dragging with the middle button (the mouse wheel pressed) pans the
zoomed photo with a hand cursor, in every tool. Remove / Heal / Clone, the
mask brushes and Crop no longer have to be left to look around a zoomed photo,
and they don't paint or move during that drag: it is handled with the pinch and
scroll gestures, before the tool overlays.

Right-click keeps the photo's context menu and Space keeps the zoom toggle.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 20:17:57 -03:00
nevicia2010-hub 8d71b79993 LR-EXP-WATERMARK: integrate batch 43 while preserving export fixes 2026-10-24 00:14:33 +02:00
nevicia2010-hub 11bbada692 LR-EXP: integrate upstream export changes and parity records 2026-10-24 00:01:09 +02:00
Brandon Thomas 98702b5c44 Merge PR #2744: integration batch 43 (21 community PRs)
Integration batch 43: 21 community PRs
2026-10-23 17:36:11 -04:00
Brandon ThomasandClaude Opus 5.5 82eef5b266 Integration: What's New lists 2026-10-23 above 2026-10-22
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 17:21:34 -04:00
Brandon Thomas 73f1b4906e Merge PR #2622
# Conflicts:
#	docs/gaps.md
2026-10-23 17:21:18 -04:00
Brandon Thomas 1acd2938ea Merge PR #2546
# Conflicts:
#	docs/gaps.md
#	docs/parity-checklist.md
#	docs/whats-new.md
2026-10-23 17:21:18 -04:00
Brandon Thomas 708e8a8bfc Merge PR #2688
# Conflicts:
#	docs/parity-checklist.md
#	docs/whats-new.md
2026-10-23 17:21:17 -04:00
Brandon Thomas 92bd08ba5c Merge PR #2618
# Conflicts:
#	docs/whats-new.md
2026-10-23 17:21:17 -04:00
Brandon Thomas bbf9b713f2 Merge PR #2526
# Conflicts:
#	docs/gaps.md
#	docs/parity-checklist.md
#	docs/whats-new.md
2026-10-23 17:21:17 -04:00
Brandon Thomas e700170dc6 Merge PR #2727
# Conflicts:
#	docs/parity-checklist.md
#	docs/whats-new.md
2026-10-23 17:21:17 -04:00
Brandon Thomas b106ceaca1 Merge PR #2621
# Conflicts:
#	docs/whats-new.md
2026-10-23 17:21:16 -04:00
Brandon Thomas 0605272e69 Merge PR #2620 2026-10-23 17:21:16 -04:00
Brandon Thomas 4d9914d4db Merge PR #2709 2026-10-23 17:21:16 -04:00
Brandon Thomas 652fa88c12 Merge PR #2670 2026-10-23 17:21:16 -04:00
Brandon Thomas d7763492c4 Merge PR #2210 2026-10-23 17:21:16 -04:00
Brandon Thomas c342c480cf Merge PR #2665 2026-10-23 17:21:16 -04:00
Brandon Thomas a344e277f0 Merge PR #2728 2026-10-23 17:21:16 -04:00
Brandon Thomas 433ce2375d Merge PR #2109 2026-10-23 17:21:16 -04:00
Brandon Thomas 8c52e2ee95 Merge PR #2630 2026-10-23 17:21:16 -04:00
Brandon Thomas 3ab88f50ac Merge PR #2612 2026-10-23 17:21:16 -04:00