Commit Graph
11 Commits
Author SHA1 Message Date
Brandon ThomasandClaude Opus 5.5 89b8aa3f77 LR-VIEW-PHOTOGRID: group the photo/square grid by date with day/month/year headers
Catalog date runs (stacks never split), Sort.group (auto/day/month/year/none) in the sort
menus, library.groups query, sticky headers, header click selects the group; layout unit
tests and a headless test.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 12:52:07 -04:00
Brandon ThomasandClaude Opus 5.5 3586c772cb M1.7: a raw with its import settings is not 'edited' (grid badge, edited filter)
Photo::import_defaults moves into the catalog; is_edited compares against it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 09:43:33 -04:00
Brandon Thomas 9886d49823 Merge branch 'worktree-agent-a69dcec0f0c61bc39'
# Conflicts:
#	crates/engine/src/media.rs
#	crates/ui-egui/src/render.rs
2026-10-01 09:39:30 -04:00
Brandon ThomasandClaude Opus 5.5 5fd5384bfb M5.4a: instant first view — cached view render / embedded raw preview, then the full render
- engine: QuickJob — what to show the moment a photo opens: the loupe's last render for the
  current settings (kept by Session::loupe_job under a size-independent "view" key in the
  preview cache, memory + disk with a library; the disk JPEG is written off the render
  thread via PreviewCache::put_deferred), else the embedded camera JPEG of a raw that still
  has its import look (import::has_import_look), else a thumbnail-level render.
- Grid / filmstrip: an unedited raw without a thumbnail shows its cached thumbnail, else its
  embedded preview (Slot::ThumbQuick) at once; the real rendered thumbnail follows at
  background priority (same cache keys, so it still ends up matching the develop render;
  edited photos render directly).
- Loupe (detail, compare, filmstrip selection): Slot::Preview holds the stand-in until
  Slot::Main has the photo; the full render then replaces it in place (no blank frame).
- files::embedded_preview_srgb + PreviewLoader hook (desktop/CLI/MCP and web).
- ui.inspect → loupe {photo, source: render|cached|embedded|small|thumb|none, pending}.

Real app (24 MP raws, loaded machine): first image 15–90 ms (thumbnail, then embedded
preview), full render 0.2–0.5 s (was 1.0–2.4 s with nothing shown before it).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 09:18:50 -04:00
Brandon ThomasandClaude Opus 5.5 baa30ca590 M0.12: web persistence — library, edits and imported originals survive a reload
The catalog Store on the web is an in-memory mirror (apps/lightcraft-web/src/files.rs) loaded
from browser storage at start-up and flushed in the background in modification order (so a
snapshot always lands before the log reset that follows it). Storage is OPFS when writable from
the main thread (createWritable), else IndexedDB (?store=idb forces it); ?reset clears it.
Imported originals are stored as originals/<content hash> and referenced as
web/<hash>/<name>; the main thread keeps a bounded memory cache and loads misses (and the
active photo) asynchronously. view.json and ui.json are saved every second when changed.

Engine: Session::open_library_in(LibraryStores) opens a library from any Store (presets, view
and prefs go through it too); Session::save_view; Copy imports into a non-disk library add.
JS automation API: lightcraft.command(id, paramsJson) (+ web.stats).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 08:18:01 -04:00
Brandon ThomasandClaude Opus 5.5 8cbdece6f2 Merge main into geometry/optics branch (embedded lens + XMP sidecar import, ProbeInfo fields)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 01:08:21 -04:00
Brandon Thomas 4dd57efcac Merge branch 'worktree-agent-af78b5603e6882a3f' 2026-10-01 01:06:29 -04:00
Brandon ThomasandClaude Opus 5.5 a0da99660d M4.2: optics — manual distortion/vignetting, lateral CA (auto + manual), defringe, DNG-embedded profile corrections
- pipeline::optics::Warp: one inverse map (lens-corrected → source) inside the single geometry resample:
  manual radial distortion, embedded DNG WarpRectilinear (per plane), per-channel lateral CA scale,
  radial vignetting gain (manual + embedded FixVignetteRadial); all in normalized radii
- automatic lateral CA estimate: radial edge profiles matched sub-pixel per plane, robust LSQ fit of
  displacement = α·r (cached per source)
- defringe: purple/green hue ranges (OkLab hue) desaturated next to high-contrast edges, width relative
  to the image long edge
- "Enable Lens Corrections" applies only lens data embedded in DNGs (opcodes are no longer baked at decode;
  stored on the photo record, on by default at import when present). No LCP data anywhere.
- new controls: optics.caRed/caBlue, defringe hue ranges; Optics panel: Manual sub-section, Defringe flyout
- tests: barrel grid straightened, CA removal (auto + manual), embedded corrections toggle/scale, preview vs
  export resolution independence, CA estimator accuracy, orientation of embedded lens data

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 00:57:47 -04:00
Brandon ThomasandClaude Opus 5.5 8af27c11b1 M10.8: XMP sidecars — save/read metadata + develop settings, auto-write, crs: interchange
- crates/engine/src/sidecar.rs: <stem>.xmp (default) or <file>.xmp next to originals with rating,
  label, title, caption, copyright, creator, keywords, capture time/GPS, pick flag + full develop
  settings in lc:settings; atomic writes; read on import (sidecar wins for metadata, edits restored)
  and via photo.readMetadataFromFile (one undo step); raw/DNG embedded XMP used without a sidecar.
- Commands: photo.saveMetadataToFile (Cmd+S), photo.readMetadataFromFile, library.xmpPreferences
  {autoWrite, naming}, library.toggleAutoWriteXmp; prefs persisted in the library's prefs.json.
  Auto-write rewrites sidecars after each committed change (not mid-drag; undo/redo too).
- crates/engine/src/crs.rs: crs: develop fields (PV2012 light, WB abs/incremental, presence, HSL,
  B&W mix, parametric + point curves, colour grading, sharpening, NR, vignette, grain, optics,
  geometry, crop) → partial settings; documented in docs/xmp-interop.md.
- meta: write_xmp_lc for extra lc: properties; crs namespace canonicalised on read (never written).
- Tests: packet round trip, crs mapping with hand-written snippets, save → delete library →
  re-import restores edits, auto-write + prefs persistence, foreign sidecar, DNG-embedded XMP.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 00:36:19 -04:00
Brandon ThomasandClaude Opus 5.5 daa32c3e4a Merge main into raw formats branch (nrw/raf/orf/rw2/pef/cr3 in the open dialog, nrw import)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 00:31:23 -04:00
Brandon ThomasandClaude Opus 5.5 759d3c7f8a M1.3: import (recursive folders, content-hash duplicates, add vs copy)
- engine::import: expand files/folders recursively (sorted; hidden entries
  and the library folder skipped), skip known paths, probe + hash in
  parallel (std::thread::scope), skip duplicates by content hash (against
  the library and within the batch), add in place or copy into
  <library>/Originals/YYYY/YYYY-MM-DD/ with unique names; one undoable op.
- library.import {paths, mode: add|copy, album?} returns
  {imported, duplicates[{path, existing, reason}], failed, scanned}.
- probe_bytes records the content hash (SipHash-1-3 128) on every photo.
- system clock for import/edit times on native (Session::with_system_clock).
- app: command-line folders are expanded by the engine (re-importing the
  same folder is a no-op); example gen_photos writes procedural test JPEGs
  (never committed).
- Measured (200 procedural 2000 px JPEGs, M-series): import 2.25 s; reopen:
  library load 1.7 ms, control ready 0.12 s, first thumbnail 0.17 s,
  visible grid drawn 0.48 s (thumbnails from the disk cache).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 00:04:07 -04:00