Commit Graph
4107 Commits
Author SHA1 Message Date
aiongg 551defa04e Merge PR #2336: Treat Unicode minus and dash characters as negative in parse_gps
crates/engine/src/cmd/library.rs: gps_tests keeps both PRs' asserts
2026-10-22 02:37:33 +00:00
aiongg d8b2eb60d3 Merge PR #2172: i18n: add Finnish (fi) interface language
Conflicts:
- README.md, docs/localization.md: language lists keep every language, Finnish appended
- assets/ATTRIBUTION.md: union of rows
- crates/ui-egui/src/i18n.rs, crates/ui-egui/src/menus.rs: language table and commands keep sv and fi
- crates/ui-egui/src/panels/keyword_list.rs: code list gains fi; Locale::ALL.len() is 16
- docs/localization-parity.md: header keeps the newer date and both changes; revision rows are the union
2026-10-22 02:37:29 +00:00
aiongg 2965161505 Merge PR #1997: KEY-CROP-SWAP: X in the crop tool swaps the aspect once (#1866) 2026-10-22 02:37:24 +00:00
aiongg 590c17a8f3 Merge PR #1977: GUM-3203: Stress-test previews against full-size renders 2026-10-22 02:37:23 +00:00
aiongg b242cd6bfc Merge PR #1972: Tone: neutral display tone map is the identity up to white (#1945) 2026-10-22 02:37:23 +00:00
aiongg f8f68fa8b2 Merge PR #2341: Fix parse_gps for hemisphere-letter-first coordinates without comma 2026-10-22 02:37:22 +00:00
aiongg bb18036420 Merge PR #1909: docs(support): explain the Remove tool workflow (#1756) 2026-10-22 02:37:22 +00:00
aiongg b1c4cb7df9 Merge PR #1258: Ignore hidden and empty masks when choosing develop planes. 2026-10-22 02:37:22 +00:00
aiongg fb316ee5ea Merge PR #1402: Fix slider wheel edits after leaving and returning to the Edit panel
crates/ui-egui/src/widgets/tests_numeric_entry.rs: keep both sides' tests.
docs/parity-checklist.md: newer header (2026-10-21); KEY-SLIDER-NUDGE from #1402, KEY-SLIDER-TYPE from #1687; both revision rows.
2026-10-22 02:37:21 +00:00
aiongg 51bf538f27 Merge PR #1968: crop.set refuses a crop smaller than the crop tool allows, instead of exporting 1×1 2026-10-22 02:37:19 +00:00
aiongg 166380630b Merge PR #2366: LRC-LIB-LIBFOLDERS: Synchronize Folder says a folder is up to date, with one Close
docs/parity-checklist.md: header and revision rows from both sides; LRC-LIB-LIBFOLDERS row takes both wording changes
2026-10-22 02:37:19 +00:00
aiongg 67d6e1879f Merge PR #1991: Control port: don't clone the connection's socket (Windows: launched programs kept it open)
apps/lightcraft/src/control_server.rs: serve() keeps #2027's idle read timeout and #1991's single-stream serve_lines (no try_clone); #1991's Windows test passes IDLE_TIMEOUT
2026-10-22 02:37:18 +00:00
aiongg cf804ee9f6 Merge PR #699: LR-PROF-CAMERACOLOR: fit the tone curve of low-contrast scenes (range limit 1.5 to 1.2)
crates/engine/src/camera_preview.rs: LOOK_VERSION keeps both history lines and the higher value.
crates/engine/src/media.rs: RENDER_CACHE_VERSION keeps the higher value.
2026-10-22 02:37:09 +00:00
aiongg 4764767694 Merge PR #1855: i18n: Swedish (sv) interface translation
Conflicts:
- README.md, docs/localization.md: language lists keep every language, Swedish appended
- assets/ATTRIBUTION.md: union of rows
- crates/ui-egui/src/i18n.rs: language table and command test list keep it, ka and sv; both tests kept
- crates/ui-egui/src/menus.rs: language commands keep it, ka and sv
- crates/ui-egui/src/panels/keyword_list.rs: code list gains sv; Locale::ALL.len() is 15
2026-10-22 02:37:05 +00:00
aiongg 92c90ad743 Merge PR #1687: KEY-SLIDER-TYPE: a long click keeps the value field open; drag the value to scrub 2026-10-22 02:36:59 +00:00
aiongg 08f9334457 Merge PR #2452: LR-PRE-PANEL: the applied preset stays marked on returning to the pho… 2026-10-22 02:36:59 +00:00
aiongg a22f447523 Merge PR #1921: Context menus: a right-click elsewhere on the same widget moves its o… 2026-10-22 02:36:58 +00:00
aiongg f4dd312eac Merge PR #2177: LRC-DEV-AUTOSYNC: sync HDR state while retaining per-photo tuning
crates/engine/src/lib.rs: keep both explicit comparison leaves (camera_local_tone and hdr).
crates/engine/src/tests.rs: keep both sides' tests.
2026-10-22 02:36:58 +00:00
aiongg 04e20de345 Merge PR #2027: #1952: expire idle control connections
contributors/people.toml: union of new people entries
2026-10-22 02:36:51 +00:00
aiongg 81f304a867 Merge PR #2208: LRC-DEV-AUTOSYNC: sync camera local tone Off 2026-10-22 02:36:40 +00:00
aiongg 214c595794 Merge PR #2049: LRC-LIB-LIBFOLDERS: Synchronize Folder says it is listing the folder instead of sitting at "0 of N" 2026-10-22 02:36:15 +00:00
helgehelge123andClaude Opus 5.5 e07c2bd44e LRC-LIB-LIBFOLDERS: the deep-folder tests put a photo in every level (ready for #1243)
The two deep-chain tests now build their library with `deep_folders_app`, as #1243 does: every
level holds a photo, so it has a row of its own and starts closed, and the test opens it. They
pass the same whether or not the sidebar passes over folders that only lead on.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 04:31:08 +02:00
IgnatAi401andClaude Opus 5.5 e8c59ef113 docs: fix four dead links in README.zh-hans.md
docs/parity.md became docs/parity-checklist.md in #788, and ROADMAP.md no
longer has the "Where we stand" / "Where we're going" sections the
anchors pointed at. Link the checklist, the roadmap itself (as README.md
does) and its Upcoming section.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 10:24:01 +08:00
Nathaniel LedfordandClaude Opus 5.5 6234a0c917 LRC-LIB-MISSING: the availability worker sweeps a list of files at low priority and gives way to file work
The availability worker that answers "is this file there?" for the grid and the
Info panel can now also check a long list of files (every library photo's file,
for the sidebar's Missing Photos count), without that list getting in the way:

- Availability::sweep takes a list, checked one file at a time and only when
  nothing is asked; files checked less than 15 s ago are skipped, and one sweep
  runs at a time. The files found gone are kept (is_gone, gone_len), and
  gone_generation, which replaces generation, changes only when a file is found
  gone or back: a file found there at its first check changes nothing the
  Missing Photos view shows, and wakes nobody.
- Availability::restart_sweeps / sweep_in drop what is left of a sweep when
  another library is opened, and refuse a list that was being made for the old
  one.
- Availability::sweep_yields_to takes a callback; while it says file work is
  going on (an import, a scan, a folder listing), the worker starts no sweep
  check. What is asked is still answered at once, and the sweep goes on where it
  stopped, so a share is not asked for a stat of every file while a scan reads
  it: 32 readers got 101 files/s alone and 65 files/s with one stat loop beside
  them.

Nothing in the app starts a sweep yet; the worker tests exercise it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 21:48:09 -04:00
Clyde C BrownandClaude Opus 5.5 2292a91dd8 Merge origin/main into fix/capture-time-strict-shift
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 19:35:53 -06:00
Clyde C BrownandClaude Opus 5.5 3aa26d8273 #2505: Edit Capture Time leaves a capture time it can't fully read as it was
shift_iso writes back only a time it fully reads (a date alone, or HH:MM
with optional seconds and fraction, an optional zone, nothing after);
iso_seconds stays lenient for sorting and rules. photo.setCaptureTime
leaves such photos as they were and counts them as `unreadable`, and a
set that would move other photos by an unreadable time is refused; alone,
the photo gets the typed time. The dialog's preview says the photo's time
can't be read, OK says what was left, any error keeps the dialog open
(button and ui.dialog.confirm), and Set mode doesn't start from an
unreadable time.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 19:20:28 -06:00
Nathaniel Ledford 0e08318bad LR-VIEW-ZOOM: a pinch holds the loupe's fit size, so it no longer renders at every zoom step 2026-10-21 19:50:28 -04:00
Dylan M. Taylor bfb23db2ca Use chrono instead of hand-rolled UTC conversion (#2457)
* Use chrono for UTC/calendar conversion instead of hand-rolled copies

Replaces the hand-rolled Howard Hinnant civil/days-from-civil copies and the
leap-year/days-in-month table with chrono as a workspace dependency:
- apps/lightcraft/src/logging.rs: timestamp (log line time)
- crates/catalog/src/dates.rs: civil + weekday
- crates/catalog/src/stacks.rs: iso_seconds (date -> seconds)
- crates/engine/src/cmd/face_recognize.rs: capture_secs (native only)
- crates/meta/src/datetime.rs: DateTime::unix_seconds and the valid() day check
Output formats and semantics are unchanged (pre-epoch clamp, day rollover that
normalize_iso relies on, leap-second tolerance, EXIF/XMP parsing). chrono uses
only std, so wasm stays safe. AGENTS.md/CLAUDE.md now say time goes through chrono.

Follow-up to storytold/cadcraft#1785

* Never panic on a clock outside chrono's range; keep 5-digit years unprefixed

chrono's From<SystemTime> unwraps, so a clock past year 262143 panicked
inside the logger. timestamp now uses the fallible DateTime::from_timestamp
and reads such a clock as the epoch, keeping the old pre-epoch clamp.

timestamp and catalog's civil write years past 9999 with {:04} like the old
{y:04} did, because %Y would add a '+'. Tests cover the out-of-range and
5-digit-year cases.

* Don't build an unrepresentable SystemTime in the far-future test

UNIX_EPOCH + Duration::from_secs(u64::MAX / 2) overflows Windows'
SystemTime and panicked before timestamp() ran, failing the Windows
test job. Build it with checked_add and only assert where it exists.

* time: preserve old out-of-range date conversion (no epoch fallback)

`DateTime::from_timestamp(...).unwrap_or_default()` silently reset any instant
past chrono's year range to 1970-01-01, changing output the old hand-rolled
Howard-Hinnant code converted exactly (e.g. 10_000_000_000_000 s was
318857-05-20 17:46:40, not the epoch). Restore the old conversion domain with a
cycle-based helper that routes the calendar through chrono (the calendar
repeats every 400 years = 146_097 days, so month/day/time come from chrono and
only the year is carried in i128, keeping every i64 exact).

- apps/lightcraft/src/logging.rs: `timestamp` via `utc_parts`; keep the
  pre-1970 epoch clamp; u64 secs past i64::MAX saturate, not epoch.
- crates/catalog/src/dates.rs: `civil` cycle-based (the callers stay inside
  0000-9999, but a far clock no longer resets to the epoch).

Pinned old outputs in tests:
  logging u64::MAX/2 s -> 292277026596-12-04T15:30:07.000Z (was epoch),
          10_000_000_000_000 s -> 318857-05-20T17:46:40.000Z
  civil 10_000_000_000_000 -> 318857-05-20T17:46:40,
        -10_000_000_000_000 -> -314918-08-13T06:13:20, i64 extremes

* Don't build a year-318857 SystemTime unchecked in the logging test

Windows' SystemTime only reaches about year 30828, so
UNIX_EPOCH + Duration::from_secs(10_000_000_000_000) panicked before
timestamp() ran and failed the Windows test job. Build it with
checked_add and assert where the platform can hold it. The year-10000
case fits everywhere and stays unconditional.

* Name the 400-year range extension as the one exception to the time rule

The rule said never to hand-roll calendar math, but the range fix
(shifting by whole 400-year Gregorian cycles so dates past chrono's year
range keep their old output) is cycle arithmetic, so the rule and the code
disagreed. The rule now names that exception: the library still computes
month, day and time, and only whole cycles are carried in the year.

* Say the library computes month and day; time-of-day is plain arithmetic

Narrows the range-extension wording: the date library computes the
calendar fields, and splitting seconds into hours, minutes and seconds
is ordinary arithmetic, which is what the code does.
2026-10-21 19:36:36 -04:00
Dylan M. Taylor d79b1f33e3 security: enforce HTTPS on curl downloads (SonarCloud S6506) (#2493)
Add --proto '=https' to curl invocations so redirects cannot downgrade to
plaintext HTTP. Resolves SonarCloud S6506 (shell/docker/githubactions).
2026-10-21 19:35:59 -04:00
Bruno-Alumn f32116bee1 Merge remote-tracking branch 'upstream/main' into fix/strict-ids-params
# Conflicts:
#	crates/engine/src/cmd/mask_effects.rs
#	crates/engine/src/cmd/masks.rs
#	crates/ui-egui/src/control.rs
2026-10-21 22:48:03 +00:00
Bruno-Alumn d15fc0a6ce Merge remote-tracking branch 'upstream/main' into fix/f8-right-panel
# Conflicts:
#	docs/parity-checklist.md
2026-10-21 22:42:18 +00:00
Bruno-Alumn c96af686a1 Merge remote-tracking branch 'upstream/main' into fix/preset-mark-per-photo
# Conflicts:
#	docs/parity-checklist.md
2026-10-21 22:42:18 +00:00
Bruno-Alumn c733daf1c2 Merge remote-tracking branch 'upstream/main' into fix/cull-color-label
# Conflicts:
#	docs/parity-checklist.md
2026-10-21 22:42:17 +00:00
Bruno-Alumn 731b461d7d Merge remote-tracking branch 'upstream/main' into fix/mask-overlay-reshow
# Conflicts:
#	docs/parity-checklist.md
2026-10-21 22:42:17 +00:00
chordpli c28a537a15 LR-CROP-RECT: a crop a handle drag filled to the tilted edges still moves (#2299)
The E2E showed the previous fix missed the common case: enlarging a locked
crop until it stops leaves a frame that fills the photo's height, with its
top and bottom both at the edge of is_within_image's 1e-6 tolerance. No
translation keeps both there with room to spare, so the move stayed stuck.

When the frame can't move as it is, solve again with it shrunk about its
centre by the least that puts it strictly inside (at most 1e-5 of its size,
aspect unchanged). This also covers a frame at the edge on one side, so the
half-tolerance first pass is dropped and a click moves nothing again.
2026-10-22 07:34:48 +09:00
chordpli 89a35ded91 LR-CROP-RECT: a crop a handle drag left at a tilted edge still moves (#2299)
A handle drag stops where is_within_image's 1e-6 tolerance ends, so a
corner can sit right at it. The move solve then let that corner go 1e-9
further, the result failed the check, and the across-then-down fallback
left the frame stuck. Solve first with every corner kept within half the
tolerance, and only then where it is.
2026-10-22 07:34:48 +09:00
chordpli f443bed56f LR-CROP-RECT: trim the crop move comments (#2299) 2026-10-22 07:34:48 +09:00
chordpli 4bd40cda60 LR-CROP-RECT: a crop just past a tilted edge still moves, and a click leaves it be (#2299)
A handle drag can leave the frame up to is_within_image's tolerance past a
tilted edge. The move's bounds were tighter than that, so such a frame was
stuck again, and a click inside it moved it. The bounds are now never tighter
than where the frame is. A test pins how a smaller frame pushed past a tilted
edge slides along it, and the parity checklist's revision history notes the fix.
2026-10-22 07:34:48 +09:00
tylerphotosandCodex 4245d50f2f LR-IMP-ADD-DIALOG: reconcile album picker with integrated main (#2425)
Retain both sets of parity revision notes after the upstream integration.
Keep the hierarchical picker and deleted-destination guard unchanged.

Co-Authored-By: Codex <noreply@openai.com>
2026-10-21 17:34:40 -05:00
chordpli d63ce8e5f8 LR-CROP-RECT: a moved crop slides along a straightened photo's tilted edges (#2299)
On a straightened photo, a crop that fills the photo's height could not be
moved sideways: the move went across and then down, and no straight sideways
step stays inside the turned photo. The frame now goes to the place nearest
the pointer's that keeps it inside, so it slides along the tilted edges to the
photo's side. Unstraightened, that place is the old across-then-down clamp.
2026-10-22 07:34:28 +09:00
JackalCOBandClaude Opus 5.5 4771ae0ea5 LR-EDIT-OPTICS-PROFILE: read Fujifilm's nine-knot lens tables at the plain radius
Fujifilm's nine-knot distortion and vignetting tables (knots at sqrt(k/8))
end at the corner on the eighth knot; the ninth, sqrt(9/8), lies past it
with the eighth's value repeated. Reading them at rho = knot_last * r took
every value from 6 % further out. On the X-T4 the warp polynomial then
failed its fit checks on 253 of 425 zoom-lens raws, which got no
distortion correction at all, and corners came out up to 6.5 % brighter
than the camera's. Read at rho = r, the tables match the camera's measured
geometry and corner gain (docs/fuji-lens-corrections.md, "Knot radius").
Nothing changes for the 11-knot and 8-knot tables, whose last knot is 1.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 11:27:49 +13:00
Dylan M. Taylor 3a9b35c687 ci: exclude dev tool scripts from SonarCloud coverage gate
tools/ and docs/ scripts aren't unit-tested; exclude them from coverage so the
new-code coverage gate doesn't block hardening-only changes.
2026-10-21 17:31:18 -04:00
Dylan M. Taylor 68d5118a9d security: validate CLI paths/args in dev tools (SonarCloud S2083/S8707/S8705/S7044)
Resolve and validate command-line path arguments before use, enforce HTTPS at
download sinks, and confirm path containment. Hardens local codegen/fetch tools
against path-traversal and command-argument-injection findings.
2026-10-21 17:13:05 -04:00
Claude 55a094475d Merge main into fix/dx12-nn-denoise
The only conflict was the 2026-10-19 whats-new entries; both are kept.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K93HTyCiwYvbRGaAkjPvXn
2026-10-21 13:01:31 -07:00
Dylan M. Taylor 3dc3b955df ci: add sonar-project.properties for CI-based analysis
Marks the project ineligible for SonarCloud Automatic Analysis, resolving the
CI-vs-automatic-analysis conflict so the SonarCloud workflow succeeds.
2026-10-21 16:00:43 -04:00
Claude bfe3ab3d58 #2267: the Before label sits below the info overlay too
Review follow-up (CodeRabbit): zoomed in, the badge moves to the canvas'
top-left corner, where the info overlay draws its lines; with the overlay
on, the label was painted under its first line. The badge's top inset
now takes the overlay's extent as well as the filter pill's. The overlay
is split into its lines and an extent helper, laid out with the same
fonts, so the two can't disagree.

Test: zoomed_in_the_before_label_keeps_clear_of_the_info_overlay.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AbegtYaPuwqZ81pGfELsFK
2026-10-21 19:50:05 +00:00
Dylan M. Taylor 511908ab06 ci: add SonarCloud scan via shared reusable workflow
Calls storytold/craft-common-actions/.github/workflows/sonarcloud.yml@main
with secrets: inherit. Adds SonarCloud badge to README where missing and
removes the redundant sonar-project.properties (config now passed by the
reusable workflow).
2026-10-21 15:47:01 -04:00
Haocheng FangandClaude Opus 5.5 33585c93ef LRC-LIB-REFVIEW: release both Reference panes when the pin is not live
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 13:40:24 -05:00
Haocheng FangandClaude Opus 5.5 977bbebcdc KEY-SLIDER-NUDGE: a drag that leaves the row disarms wheel edits
The disarm waited for the button to be released outside the row, so a
drag that went out and came back kept the wheel armed. Leaving disarms
now even mid-drag, and a drag that starts outside the row does not arm.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 13:40:04 -05:00
Haocheng FangandClaude Opus 5.5 bb5d68978b LRC-LIB-REFVIEW: refuse malformed and deleted references clearly; release a dead pane
Review follow-ups for the Reference lifecycle fix:
- photo.setReference refuses an id that is not a photo id instead of
  pinning the active photo; only an absent or null id means the active one.
- A Recently Deleted photo is refused with a message that says to restore
  it (setReference and view.reference); the grid menu item is disabled for
  it and any refusal is shown as a toast, not dropped.
- Reference view with no live pin releases its panes (windows, region and
  pane textures, full capacity) like other views.
- A successful library switch leaves Reference for the grid; the Compare
  pair is now cleared only on success, like the reference pin.
- One compare::live helper replaces four copies of the "exists and not
  deleted" check.
- whats-new entry moved to 2026-10-21; checklist and ui-parity dates bumped.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 13:31:05 -05:00