Commit Graph
151 Commits
Author SHA1 Message Date
Brandon ThomasandClaude Opus 5.5 3ac1e55a6c Local: drop the needless struct update in library.import (clippy)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 10:19:54 -04:00
Brandon Thomas 94fd0e6994 Merge main into fix/issue-23-local-folder-async
# Conflicts:
#	crates/ui-egui/src/panels/grid.rs
2026-10-04 10:19:54 -04:00
Brandon Thomas 4b863e9d47 Merge pull request #45 from GlemNL/fix/issue-25-active-filters
Show active filters as chips and count matching of total photos
2026-10-04 10:19:04 -04:00
Brandon Thomas cdaacc1e83 Merge pull request #48 from GlemNL/fix/issue-26-smart-previews-location
Smart Previews: choose the folder per library
2026-10-04 10:18:57 -04:00
Brandon Thomas e1857511af Merge pull request #44 from storybored379/feat/export-naming-tokens
LR-EXP-NAMING: export file names use the Rename Photos tokens (+ num, folder, lens, iso, rating, creator)
2026-10-04 10:18:50 -04:00
Clément LandClaude Sonnet 5.5 78f627228f Smart Previews: choose the folder per library, with a move/leave/delete choice (#26)
Smart previews always lived in <library>/Smart Previews, so a large set of proxies
filled the library's drive. The folder is now a per-library setting (prefs.json):

- library.smartPreviewsLocation shows the effective folder (path, count, size,
  availability) or changes it; changing it needs an explicit choice for the
  previews already built (move, leave or discard), and reset returns to the
  library's own folder.
- The new folder must accept writes before anything moves. A folder whose parent
  is missing (an unplugged drive) is an error and is never created on the current
  drive; building also refuses while a chosen folder is unavailable.
- Building, discarding, status and offline rendering all use the chosen folder.
- Settings -> Performance -> Smart previews shows the path before building, what
  it holds, the existing-previews choice, Choose Folder... and Use Library Folder.

The thumbnail cache stays in the library.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-04 11:21:25 +02:00
Clément LandClaude Sonnet 5.5 431186a24e Library: show active filters as removable chips and count matching of total photos (#25)
A filter set from the sidebar (By Date), the search field or the filter bar
narrows the current source, but nothing said so: a folder with a hidden date
filter looked empty.

- Chips under the grid header, one per active filter, each with an X that
  clears only that filter; "+N more" lists the ones that do not fit; Clear all.
- The header counts "N of M photos" (matching of the source's total) while a
  filter is on; the total is cached per source/catalog revision.
- A badge on the top-bar filter icon counts the active filters, filter bar
  open or not.
- An empty result says no photos match the active filters, and how many the
  source holds, instead of implying the folder is empty.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-04 10:53:25 +02:00
storybored379andClaude Opus 5.5 309922eac9 LR-EXP-NAMING: export file names use the Rename Photos tokens
rename::expand_tokens is shared by Rename Photos, import renaming and export naming, so {seq:N},
{date:%…}, {camera}, {title}, {ext} work in exports too (a bare {seq} still pads to 3 digits there).
New shared tokens: {num} (number at the end of the name), {folder}, {lens}, {iso}, {rating},
{creator}. An export name that expands to nothing keeps the original name. The MCP export tool
passes startNumber through and documents the tokens.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 01:26:57 -07:00
Clément LandClaude Sonnet 5.5 919e65efaf Local: browse a folder in the background with progress; keep picked folders in the sidebar
Browse Folder listed, probed and imported the whole folder on the UI thread, so a
network share froze the window with no feedback. It now lists and reads on a worker
(progress window with Cancel), then adds the photos in small batches reusing the
scan's probes. Folders picked with Browse Folder… stay in the Local list after
browsing elsewhere.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-04 10:22:47 +02:00
Brandon ThomasandClaude Opus 5.5 966db71295 LR-EDIT-CURVE: point-curve presets (built-in, save, delete, import/export)
A Point Curve dropdown under the curve graph shows the preset the photo's
point curves match (or Custom) and lists our own built-ins (Linear, Medium
Contrast, Strong Contrast: symmetric S-curves, values chosen here) and the
user's presets; Save Point Curve... names and stores the current four
channels, right-click deletes, Import/Export Presets... read and write
.lccurve JSON files. Presets set only the point curves (parametric curve and
Refine Saturation stay) and are saved with the library preferences.

Engine commands: curve.presets, curve.applyPreset, curve.savePreset,
curve.deletePreset, curve.importPresets, curve.exportPresets; UI commands
file.importCurvePresets / file.exportCurvePresets (file dialogs on desktop).

Fixes #14

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 02:59:25 -04:00
Brandon ThomasandClaude Opus 5.5 ae29c0569f LR-EDIT-CURVE: reset a curve channel or the whole tone curve
New engine command curve.reset {channel?: all|point|parametric|master|red|
green|blue} (default all = the whole Tone Curve), one undo step, usable from
CLI / MCP / control channel. In the editor: double-click a channel selector
resets that channel, a Reset button under the graph resets every curve
(point curves and parametric), and right-clicking the graph offers
Reset <channel> / Reset All Curves.

Fixes #13

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 02:59:25 -04:00
Brandon ThomasandClaude Opus 5.5 1169a1beda LR-IMP-FORMATS: mark raws shown from their embedded preview as "preview only"
Raw variants the decoder can't read yet (e.g. Nikon Huffman-compressed NEF
from a D3200/D5100) silently fell back to the camera's embedded JPEG, so
users edited a JPEG with the camera's picture style (e.g. Monochrome) baked
in without being told.

- catalog: Photo.preview_only (serde default; old catalogs load), set /
  cleared by Op::SetContent with an exact inverse; Photo::develops_raw().
- engine: ProbeInfo.preview_only carries the decoder's Unsupported reason
  into import, the import review (previewOnly) and catalog.query
  (previewOnly); photo.inspect includes it. photo.reload and photo.relink
  re-probe and clear/set it when the file decodes (or not) now.
- source_info: a preview-only raw is a rendered source (relative WB scale,
  display tone curve) instead of raw:true/5500 K + filmic tone map on a
  display-referred JPEG; import uses the JPEG default preset for it; the
  Edit panel shows the rendered-file WB sliders.
- UI: "Preview" badge on grid thumbnails, a pill on the loupe, a notice in
  Edit and Info (amber caution token, Info icon drawn in code).
- Tests: catalog serde/undo, engine import -> reopen -> reload/relink,
  headless UI badge/notice widgets.

Refs #10

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 02:59:25 -04:00
Brandon ThomasandClaude Opus 5.5 4a70fcfe4f LR-PRE-IMPORT: bound plist nesting (a hostile look can't overflow the stack)
Nested <dict>/<array> recursed without limit; a crafted .lmp with deep
nesting would crash the app on import. Reading stops with an error past
64 levels. Test: hostile_nesting_is_an_error_not_a_crash.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 02:59:25 -04:00
Brandon ThomasandClaude Opus 5.5 6c3c155c99 LR-PRE-IMPORT: import Luminar looks (.lmp, .mplumpack)
An .lmp look is an XML property list of adjustment layers with named
sliders (newer looks: a Name.lmp/Contents/preset.lmp bundle); an
.mplumpack is a zip of looks plus PresetsInfo.plist (GroupName).
preset.import, the file dialog and drag & drop now accept both. Sliders
with a clear counterpart (exposure, contrast, highlights/shadows,
whites/blacks, WB shift, saturation/vibrance, clarity/structure,
dehaze, HSL, point curves, vignette, grain) map onto develop settings;
the rest is reported in `unmapped`. Looks are named after the look and
grouped by the pack. Tests use synthetic looks and packs only.

Fixes #9

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 02:59:25 -04:00
Brandon Thomas c0794b95e8 Merge remote-tracking branch 'origin/main' into review/pr-1 2026-10-04 01:36:45 -04:00
Brandon Thomas 2956f97352 Merge branch 'pr-1' into review/pr-1 2026-10-04 01:13:20 -04:00
storybored379andClaude Opus 5.5 6a83818192 Library: an album source serializes (library.state no longer panics)
LibrarySource was an internally tagged enum (tag = "kind") whose Album
variant holds an AlbumId, and serde cannot serialize an integer newtype
in an internally tagged enum. With an album open:

- library.state unwrapped the error and panicked; over the control
  channel this closed the desktop app.
- view.json (source, filter, sort, selection) was written empty
  (to_vec_pretty(...).unwrap_or_default()), so the view was lost on the
  next launch.

Adjacent tagging (content = "id") keeps the unit variants as
{"kind": "all"} and writes the album as {"kind": "album", "id": 8}: the
same shape library.source takes, so the reported source can be passed
back as is. Existing view.json files read unchanged.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-03 17:38:43 -07:00
Clément LandClaude Sonnet 5.5 5828a7d2d3 Import: scan folders on a background thread with progress and Cancel
Add Folder read every file on the UI thread before opening the review; on a
NAS (~26k files) the window froze and was dropped. The scan now runs in a
worker (ScanInput/scan_with) and the review opens when it finishes.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-03 17:43:02 +02:00
Brandon ThomasandClaude Opus 5.5 439cbd6ca0 LRC-DEV-SOFTPROOF, MENU-CTX-MASK: soft proofing with gamut warnings; mask component menu
Soft proofing (S in the loupe; S in grids stays Expand/Collapse Stack):
- pipeline Proof {space, destWarning, displayWarning}: gamut-map into the
  proof space, show it in the output space; red/blue gamut warnings
  (neutral clipping excluded). CPU only; the GPU path declines proofs.
- RenderJob::with_proof; paper-white surround and Proof Preview label;
  Soft Proofing strip under the histogram (profile, warnings, Create
  Proof Copy = a named virtual copy). view.softProof for control/MCP.
- photo.virtualCopy takes an optional name.

Mask components: MaskComponent.name; mask.component {component, action:
invert|duplicate|delete|rename|op}; right-click / … menu per component,
double-click rename, Intersect button.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 04:01:01 -04:00
Brandon ThomasandClaude Opus 5.5 db8a574fea LRC-LIB-QUICKDEV, LRC-LIB-VISUALSEARCH, LRC-DEV-MATCHEXP: Quick Develop, Find Similar, Match Exposures
- develop.quickAdjust {control, delta}: adds a step to every target photo's own value (one undo
  step); the Edit panel shows Quick Develop rows (exposure ⅓ / 1 stop, contrast, highlights,
  shadows, whites, blacks, clarity, vibrance, temperature) in the grid with several selected.
- library.findSimilar {id?, similarity?}: look-alike photos by their culling signature (cached
  per content), most similar first, as a view filter (Filter.only); photo menu entry.
- develop.matchExposure: selected photos' Exposure set so shutter × ISO ÷ aperture² + slider
  matches the active photo; photos without exposure data are skipped.
- Impromptu slideshow row marked (View ▸ Slideshow).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 03:40:34 -04:00
Brandon ThomasandClaude Opus 5.5 5d864c0374 LR-MASK-FEATHER-EDGE: Refine Edges for masks
Mask.refine (0–100, mask.refine; Masking panel ▸ Refine Edges): after a mask's components are
combined, a guided filter steered by the photo's luminance (window up to ~4 % of the long
edge) snaps soft mask edges to the photo's edges. Rendered on the CPU (the GPU path has no
kernel for it yet).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 03:16:54 -04:00
Brandon ThomasandClaude Opus 5.5 8d40b3e372 LR-REM-DUST: Find Dust Spots
lightcraft-pipeline dust.rs: sensor-dust detection on a rendered image — pixels darker than
their blurred surroundings by more than 3× the local texture, grouped into small (0.15–2 % of
the long edge), roughly round blobs. spot.findDust {sensitivity?, add?} renders the uncropped
photo, detects and adds a heal spot on each (auto source; one undo step). Remove panel ▸
Find Dust Spots.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 03:07:49 -04:00
Brandon ThomasandClaude Opus 5.5 26b00014a8 LR-LIB-CULL: assisted culling — focus scores, bursts, best-of-burst
lightcraft-pipeline cull.rs: focus (Laplacian energy of the sharpest 16-px blocks at 512 px,
0–100, calibrated on the CC0 corpus), clipping share and an 8×8 look-alike signature.
photo.analyze scores the selection (else the view), groups look-alike shots taken within 10 s
into bursts and marks each burst's sharpest; optionally rejects photos below a focus score and
picks each burst's best (one undo step). Scores live on the photo (Analysis, Op::SetAnalysis):
rule fields Focus and Best of Similar Shots, Info panel line. Photo ▸ Assisted Culling….

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 02:58:52 -04:00
Brandon ThomasandClaude Opus 5.5 1b4f623e55 LRC-LIB-FOLDERS: rename and move folders on disk
folder.rename {path, name} and folder.move {path, into}: the folder is renamed / moved on disk
in one step (its files and sidecars go along) and the photos inside are relinked (and a
browsed folder follows); like the file manager it is not an undo step. Refuses clashes, moves
into itself and the library's own folders. Local folder tree: Rename Folder…, Move Folder To…,
Show in Finder.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 02:40:44 -04:00
Brandon ThomasandClaude Opus 5.5 4a490db261 LR-PROF-IMPORT: .cube 3D LUTs as creative profiles
lightcraft-pipeline gains lut.rs: a .cube parser (LUT_3D_SIZE, DOMAIN_MIN/MAX, TITLE), a
trilinear lookup and an in-process registry; the finishing stage applies a `lut:` profile to
the display-encoded colour, blended by the profile amount (0–200 %); LUT profiles render on the
CPU. profile.import {paths} reads files, folders and zips, copies each LUT into the library's
Profiles/ folder, lists them in prefs and registers them when the library opens; the profile
browser shows them under their folder / zip name; profile.deleteImported. File ▸ Import
Profiles & Presets…, the file picker and drag & drop take .cube files.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 02:20:39 -04:00
Brandon ThomasandClaude Opus 5.5 1aaafb6218 LR-EXP-DNGOPT: DNG export compression (lossless / ZIP / none)
ExportOptions.dng_compression (`dngCompression`: lossless (default) | deflate | uncompressed),
in the export dialog when the format is DNG. Corpus-gated test: all three decode, uncompressed
is the biggest.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 00:27:45 -04:00
Brandon ThomasandClaude Opus 5.5 ec78cb8236 Perf: big libraries — keyword tree 5.6× faster, per-frame library queries cached
A 100k-photo scaling check (ignored test `scale_100k_library_queries`) showed the left
panel rebuilding the keyword tree every frame (323 ms), the grid recomputing date headers
(57 ms) and the Keywords panel its suggestions (44 ms) every frame.
- keyword_tree builds with hash maps instead of linear sibling scans: 323 → 57 ms at 100k.
- LightcraftApp::caches keeps the keyword tree, date runs and keyword suggestions per catalog
  revision (and inputs), so each is computed once per change instead of every frame.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 00:05:38 -04:00
Brandon ThomasandClaude Opus 5.5 1fe5dea3ab LR-META-LOCATION: edit GPS by hand, Show on Map
photo.setMeta {gps: "lat, lon" | [lat, lon] | null} — decimal, or degrees / minutes / seconds
with N/S/E/W ("48°51'30"N 2°17'40"E"); applied to all target photos, saved in XMP / exports.
Info: the GPS line is an editable field, and Show on Map opens the place in OpenStreetMap.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 23:56:13 -04:00
Brandon ThomasandClaude Opus 5.5 544914bb66 LR-CLOUD-SMARTPREVIEW + LR-LIB-DUPLICATE: smart previews for offline originals, Duplicate
- Smart previews (smart.rs): a ~1 MB proxy per photo in the library's "Smart Previews" folder
  (the decoded preview-size source, scaled, sRGB-encoded JPEG behind a one-line header). When
  a photo's original is missing (an unplugged drive), its source loads from the proxy: the
  photo renders, edits and exports at proxy size. library.smartPreviews {discard?},
  photo.smartPreview status; File ▸ Previews ▸ Build / Discard Smart Previews; Info shows
  "Original offline · editing the smart preview".
- Photo ▸ Duplicate (photo.duplicate): a real "-copy" file with the same settings, metadata,
  rating, flag, label and albums; one undo step.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 23:25:35 -04:00
Brandon ThomasandClaude Opus 5.5 608fb6dc13 LR-IMP-AUTO: watched-folder auto import
library.autoImport {folder, copy, album} (in prefs) and library.autoImportScan: files in the
watched folder the library doesn't have are added once their size held between two scans
(still-copying files wait), in place or copied into Originals, into the named album; each file
is tried once; arrivals don't take the selection. The desktop app scans every 3 s with a toast;
Settings ▸ Import ▸ Auto Import picks the folder.
Fix: library.import into an album no longer fails when nothing was selected.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 23:15:34 -04:00
Brandon ThomasandClaude Opus 5.5 a98ea7858b LR-PRE-PREMIUM + KEYC-CATALOG: 41 built-in presets, F2 renames
- 23 more own-authored presets: Portrait (Moody Portrait, Golden Glow, Clean Studio),
  Landscape (Crisp Vista, Deep Sky, Lush Greens, Desert Warmth, Misty Morning), Urban,
  Food, Seasons, Vintage (Instant '70s, Cross Process, Bleach Bypass), Sepia B&W.
- F2 opens Rename Photos…; ⌘' / ⌘R / ⇧⌘E Classic keys verified.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 23:09:09 -04:00
Brandon ThomasandClaude Opus 5.5 27387af44c LR-EXP-PSD: pick up files saved by the external editor (photo.reload)
Op::SetContent records a photo's file facts (size, dimensions, content hash; undoable).
photo.reload re-probes the photos' files and, for those that changed on disk, updates them and
drops their cached sources; render keys now include the file's content key, so the grid and
loupe re-render. The app reloads this session's external-edit copies whenever its window
regains focus (with a toast).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 23:03:39 -04:00
Brandon ThomasandClaude Opus 5.5 9affd0e8d2 LR-EXP-PSD + KEY-PHOTOSHOP: Edit in External Editor (⇧⌘E)
photo.editExternal renders the active photo with its edits as a 16-bit TIFF "<name>-Edit.tif"
(Adobe RGB by default; ProPhoto / P3 / sRGB) next to the original, never overwriting, adds it
stacked on top of the original and selects it. Photo ▸ Edit in External Editor (⇧⌘E, as
observed; the export dialog stays on ⇧E) then opens it in the application set in Settings ▸
General ▸ External Editor, or the system default (desktop: open -a / start / xdg-open,
spawned, never waited for).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 22:58:19 -04:00
Brandon ThomasandClaude Opus 5.5 0fafacfccd LR-VER-AUTO, LR-VIEW-SLIDESHOW, LRC-LIB-CONVERT and more: auto versions, slideshow, DNG
- Auto versions: leaving an edited photo keeps its settings as an Auto version (when they
  differ from its latest version; 20 kept); saved with the library, not an undo step.
- View ▸ Slideshow (⌥⌘↩, `interval`): the photos in view full screen, wrapping; Space
  pauses, ←/→ step, Esc ends.
- Photo ▸ Convert to DNG (photo.convertToDng): lossless DNG next to the raw with the settings
  embedded; the photo and its virtual copies are relinked (Relink gains an optional format),
  originals kept, undoable. Import: Copy as DNG (`dng`) for copy imports.
- A fine grid over the photo while a geometry slider is dragged.
- View ▸ Show Photo Counts; Help ▸ System Info… (app.systemInfo, JSON for agents).
- Native menus: modified Enter accelerators.
- Parity rows refreshed (target / quick collection, auto sync, location rules, bring to front).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 22:49:39 -04:00
Brandon ThomasandClaude Opus 5.5 b163d0e16e LRC-LIB-COLLECTIONS: Quick Collection and target album (B)
Albums gain a `quick` flag: the Quick Collection is created the first time B adds to it.
album.toggleTarget adds the selection to the target album (the Quick Collection unless
album.setTarget names another regular album) or removes it when all of it is already there;
album.clearQuick empties it. B in the photo / square grids does this (with a toast); in the
loupe and edit views B stays the masking brush. Album menu: Set as Target Album / Stop Using
as Target Album / Clear Quick Collection; the target is marked + in the list.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 22:28:44 -04:00
Brandon ThomasandClaude Opus 5.5 def15b0346 LR-META-EXIF-FULL: All Metadata — every EXIF / GPS / XMP field of the file
lightcraft-meta gains tags.rs: every tag of IFD0 and the EXIF, GPS and interoperability IFDs
as (group, name, value) rows, named per the public TIFF / EXIF / DNG specifications, with
common values spelled out (exposure as 1/250 s, f/2.8, 35 mm, orientation, metering, flash,
compression…) and blobs summarised. photo.allMetadata adds the XMP fields (sidecar, else the
file's packet). Info ▸ All Metadata… opens a searchable dialog grouped by TIFF / EXIF / GPS /
XMP.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 22:20:43 -04:00
Brandon ThomasandClaude Opus 5.5 f65c2310f2 LR-MASK-COLORRANGE: pick colour-range samples on the photo
The Color mask tile adds an empty colour range and arms the picker: click the photo to sample
(⇧-click adds, up to five); Pick in the Masking panel re-arms it. mask.sampleColor {x, y, add?}
samples via lightcraft_pipeline::color_range_sample, which reads the exposed scene colour at
that point in the same OkLab space the mask compares in (so a click selects what was clicked).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 22:03:14 -04:00
Brandon ThomasandClaude Opus 5.5 6a0d5346e8 LRC-LIB-PREVIEWS: build standard-sized / 1:1 previews in the background
library.buildPreviews {size: standard (Settings → Performance preview size) | full, ids?,
wait?} renders each photo's grid thumbnail and loupe view into the memory + disk preview cache
(selected photos, else all in view) on a background thread; library.previewProgress /
cancelPreviews. File ▸ Previews: Build Standard-Sized / 1:1, Stop, Discard Preview Cache;
toasts when a build starts and finishes. Also silences the wasm-only unused `threads` warning.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 21:42:33 -04:00
Brandon ThomasandClaude Opus 5.5 b8b2379215 LRC-LIB-FILTERBAR: colour-label multi-select (any of the chosen labels)
Filter.labels: a photo matches when its label is any of them. The filter bar's label toggles
add / remove labels from the set instead of replacing the single label; the summary reads
"label red or yellow". The filter already persists across albums (locked, as in the desktop
app).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 21:33:43 -04:00
Brandon ThomasandClaude Opus 5.5 a3d487d22d LRC-LIB-SMARTCOLL: smart-album rule editor — all / any / none, nested groups, 26 fields
lightcraft-catalog gains rules.rs: RuleSet {match: all|any|none, rules: [{field, op, value} |
{group}]} with text, keyword, number, date (is / before / after / between / in the last N
hours…years) and choice operators over 26 fields (rating, flag, label, type, edits, keywords,
any text, filename, title, camera, lens, location, dates, ISO, aperture, focal length,
megapixels, GPS, virtual copy, album…). Filter.ruleSet: smart albums and library.filter use it;
"in the last" counts from the session clock. album.ruleFields lists fields and operators for
agents; rules are validated.
UI: File ▸ New Smart Album… / album context menu ▸ Edit Smart Album… open the rule editor
(field / operator / value rows with per-kind editors, + / − / ⌥+ for groups, live match count).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 21:27:56 -04:00
Brandon ThomasandClaude Opus 5.5 b8344aa5eb LRC-LIB-KEYWORDS + KEY-SECTIONS: keyword sets with ⌥1–⌥9, ⌘1–⌘5 edit sections
- Keyword sets: Recent Keywords (the nine most recently added, newest first) plus user sets in
  prefs (keyword.sets, useSet, saveSet, deleteSet). keyword.toggleFromSet {index}: adds keyword N
  to the selection, or removes it when every photo has it; applying from Recent Keywords keeps
  the numbering. Keywords panel: set picker (save current as a set, delete) and a 3×3 grid of
  toggles; ⌥1–⌥9.
- ⌘1–⌘5 expand/collapse Light, Color, Effects, Detail, Optics as observed in the reference;
  Zoom 100% moves to ⌥⌘0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 21:17:15 -04:00
Brandon ThomasandClaude Opus 5.5 86c4048ad6 LRC-LIB-IMPORT: copy to any folder, organise by day / month / flat, rename on import, metadata preset
library.import gains destination (copies go there instead of the library's Originals/; no
library needed), organize (date | month | flat), rename (file-name template with the rename
tokens, numbered from renameStart; copies are catalogued under their new names) and
metadataPreset. The Add Photos dialog: Copy → Copy to (Choose… / Library), Folders, Rename with
a live example, Metadata preset; batches keep the numbering going.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 21:08:33 -04:00
Brandon ThomasandClaude Opus 5.5 f81c81b7a0 LRC-DEV-AUTOSYNC + LRC-LIB-LABELS + KEY-STRAIGHTEN: auto sync, label sets, ⌘-drag straighten
- develop.autoSync (Edit ▸ Auto Sync, ⌥⇧⌘A): an edit of the active photo carries only the
  settings that changed to the other selected photos, in one undo step; slider drags sync on
  release; spots / red eye and history restores stay per photo. Edit ▸ Sync Settings in the
  menu; Edit panel banner while it's on with several photos selected.
- Colour-label sets: built-in Colors / Review sets plus user sets in prefs (label.sets,
  applySet, saveSet, deleteSet); listed in Photo ▸ Set Color Label; the names dialog can start
  from a set and save one. XMP sidecars and exported originals write the label's name, and
  reading resolves custom names back to their colour.
- Crop: ⌘-drag draws a straighten line and keeps the crop tool active.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 20:41:05 -04:00
Brandon ThomasandClaude Opus 5.5 b7d497d049 LR-IMP-PROFILES: carry masks over from presets, .lrtemplate and XMP sidecars
Local corrections stored as crs: structures (MaskGroupBasedCorrections and the older
gradient / circular gradient / paint containers) become our masks: linear and radial
gradients, brushes (dab runs → strokes), Subject / Sky, luminance and depth ranges, with
add / subtract / intersect and invert, and all local sliders. lightcraft-meta's XmpData gains
`values`, a structured view of every property (arrays of structs survive); .lrtemplate Lua
tables convert into the same shape.
Applying a preset adds its masks to the photo's own (once; Amount scales them); sidecar masks
replace them and radial masks are fitted to the photo's aspect. Unsupported components
(colour range, other AI selections) are reported as "Mask: <kind>".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 20:31:40 -04:00
Brandon ThomasandClaude Opus 5.5 1dc501bc3a LR-IMP-PROFILES: import presets from .lrtemplate, DNG presets, zip bundles and folders
preset.import (File ▸ Import Presets…, Presets panel ⋯, drag & drop, CLI, MCP) now reads
everything people actually download or carry over from other editors:
- classic .lrtemplate files (Lua table literal parser; value.settings uses crs: field names),
  including old process-version fields (Exposure, Contrast, FillLight, HighlightRecovery,
  Shadows, Brightness, Clarity, ToneCurve) approximated with today's sliders;
- photos carrying their edits in XMP ("DNG presets" from mobile apps, edited JPEG/TIFF): the
  look only, without the photo's crop, geometry or custom white balance;
- .zip bundles (stored + deflate) of any of these, skipping __MACOSX / hidden files;
- folders: the pack's folder name becomes the group, skipping app/format container folders
  ("Lightroom Classic", "XMP", "Mobile (DNG)"…).
Each imported preset reports the settings that had no counterpart (`unmapped`: profiles,
looks, local masks); the app's toast names them. dryRun previews, group overrides.
Dropped .lcpreset/.lrtemplate/.xmp/.zip files import as presets instead of photos.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 20:14:51 -04:00
Brandon ThomasandClaude Opus 5.5 9e8d75300f LR-EDIT-COLOR-MIXER-BW + LR-FILT-TYPE: Auto B&W mix, HDR / panorama type filters
- develop.autoBwMix (B&W Mixer → Auto): each hue band's colourful pixels are pushed away from
  the image's mean lightness (light bands brighter, dark ones darker; bands with almost no
  colour untouched), clamped to ±60; switches to B&W; one undo step.
- library.filter {merged: hdr | panorama | hdrPanorama | any}: Photo Merge results recognised
  by the "-HDR" / "-Pano" / "-HDR-Pano" names merges write; filter bar type picker lists HDR,
  Panoramas, HDR Panoramas.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 16:45:30 -04:00
Brandon ThomasandClaude Opus 5.5 478f328cc0 LR-FILT-SAVED + KEY-COMMIT: filter presets, Return commits a tool
- filter.savePreset / applyPreset / presets / deletePreset: the filter bar's settings saved
  under a name with the library; filter bar → Presets (apply, save current, delete).
- Return (tool.done) closes Crop / Remove / Red Eye / Masking back to the Edit panel.
- Parity: zoom while cropping verified (works), hover previews now include versions.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 16:41:04 -04:00
Brandon ThomasandClaude Opus 5.5 deff6b6d06 LR-EXP-WATERMARK + LR-LIB-RECENT-ADDED: graphic watermarks, Recently Added by import
- Export watermark can be a graphic (watermark {image, imageWidth}): a logo with transparency,
  scaled premultiplied to a share of the photo's width, converted into the output colour
  space, placed with the same anchor / inset / opacity as text; decoded once per file.
  Export dialog: Text / Graphic, Choose…, width; size and shadow apply to text only.
- Recently Added: everything imported in the 30 days up to the latest import, newest import
  first, grouped by import day (was the latest import day only).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 16:37:26 -04:00
Brandon ThomasandClaude Opus 5.5 b27c3bc20f LRC-LIB-METADATA + LRC-LIB-STACKS: metadata presets, split stack, move within a stack
- metadata.savePreset / applyPreset / presets / deletePreset: named sets of copyright,
  creator, title, caption, place and keywords (text replaces, keywords add; one undo step),
  saved with the library; Photo → Metadata Preset (apply, Save Metadata Preset… from the active
  photo's copyright, creator and place); Settings → Import applies one to every import
  (library.preferences import.metadataPreset).
- stack.split (this photo and those after it become their own stack; single leftovers are
  unstacked), stack.moveUp / moveDown; in the Stack menus.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 16:33:11 -04:00
Brandon ThomasandClaude Opus 5.5 b2c8d9611f KEY-SLIDER-NUDGE + KEY-FLAGADVANCE: slider nudges, ⇧Z pick and advance, Missing Photos source
- ↑/↓ over any slider nudge it by ≈ 1/200 of its range (exposure 0.05, most sliders 1,
  temperature 50 K; ⇧ ×5), one undo step each; the key doesn't also move the selection.
- ⇧Z picks and moves to the next photo (with ⇧X / ⇧U).
- library.source {kind: missing}: photos whose file can't be found; a "Missing Photos" row
  appears in the left panel when there are any (checked every 5 s).
- Loupe context menu: Zoom submenu. Parity: stale rows fixed (compare/survey, per-camera
  defaults, paste from previous, capture-time edit).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 16:27:35 -04:00