mirror of
https://github.com/storytold/lightcraft.git
synced 2026-10-27 11:37:13 -06:00
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>
This commit is contained in:
+14
-3
@@ -2,7 +2,8 @@
|
||||
|
||||
LightCraft selects objects with **SAM 3** (Segment Anything with Concepts, Meta 2025), run in
|
||||
pure Rust by `crates/segment` on [candle](https://github.com/huggingface/candle): on the GPU
|
||||
through Metal on macOS, on the CPU elsewhere (for now).
|
||||
through Metal on macOS, on the CPU elsewhere (for now). If the Mac's GPU can't run it, it runs on
|
||||
the CPU (see the worker below).
|
||||
|
||||
**The model is optional.** It is not part of LightCraft, and nothing requires it: without it,
|
||||
Object and Describe offer to download it, every other feature (and every existing mask, see
|
||||
@@ -16,7 +17,10 @@ In the Masking panel (M):
|
||||
to leave it out. Clicks show as green (include) and red (leave out) dots.
|
||||
- **Describe**: type what to select — `sky`, `trees`, `the red car` — and press Return. Every
|
||||
instance the model finds (score above 0.5) joins the mask; if nothing matches, nothing is
|
||||
created and you are told so.
|
||||
created and you are told so. Around what it found, the mask follows the model's semantic mask
|
||||
for the phrase, which is sure inside and runs on where two instances touch (the instances'
|
||||
own masks alone left a sky or a net about 70–90 % selected and a thin line between two people
|
||||
side by side).
|
||||
- Several things at once: `car, road` (comma-separated) selects each and merges them.
|
||||
- **+ / −** under the selected mask (and the Add / Subtract / Intersect menus) combine
|
||||
selections: Describe `car`, click the mask, + ▸ Describe… `road`; − ▸ Describe… `people` takes
|
||||
@@ -214,7 +218,7 @@ wait and return the result (when built with the `sam` feature; the default CLI h
|
||||
`NOTICE` — the ported files say so and that they were modified): the 32-layer ViT backbone
|
||||
with 2-D RoPE and windowed attention, the feature pyramids, the SAM 2-style prompt encoder and
|
||||
two-way mask decoder for clicks, and the CLIP text encoder, DETR encoder/decoder (box
|
||||
relative-position bias, presence token) and pixel decoder for text. The CLIP tokenizer is a
|
||||
relative-position bias, presence token), pixel decoder and semantic head for text. The CLIP tokenizer is a
|
||||
small BPE in `tokenizer.rs`. The downloader (original code) is the `lightcraft-fetch` crate, shared by any model that is downloaded; `fetch/` here says what SAM 3 needs.
|
||||
- candle is pinned at 0.9.2: later releases make `candle-core` depend on `tokenizers` with the
|
||||
Oniguruma C library, and the product is pure Rust. Weights are read with positional reads (no
|
||||
@@ -225,6 +229,13 @@ wait and return the result (when built with the `sam` feature; the default CLI h
|
||||
encoded photo; the session sends it jobs and receives results over channels, never holding a
|
||||
lock the model holds. Each job runs under `catch_unwind`: a panic inside candle becomes an
|
||||
error and the model is reloaded on the next request; busy counters are reset by drop guards.
|
||||
- GPU fallback (issue #1974): a job that fails on the GPU (a panic or a candle error, e.g. an
|
||||
AMD GPU in an Intel Mac whose Metal compiler crashes on a matmul kernel) frees the model and runs
|
||||
once more on the CPU. If that works, the model stays on the CPU until LightCraft restarts and
|
||||
the user is told once ("The GPU couldn't run the AI mask model…"); if the CPU fails too, the
|
||||
error is reported and the next job tries the GPU again. Missing or damaged model files and
|
||||
remote-worker errors are not retried. `LIGHTCRAFT_GPU=0` or `LIGHTCRAFT_GPU_BACKEND=off` keep
|
||||
the model on the CPU from the start.
|
||||
- Hostile documents: a selection keeps at most 4 detail patches (when read and when rendered),
|
||||
each at most 1024² logits; more than 64 clicks are never sent to the model.
|
||||
- Accuracy: `crates/segment/tests/reference.rs` compares every stage against the Python
|
||||
|
||||
@@ -60,6 +60,7 @@ guard.detail(&file_name);
|
||||
| Lightroom catalog import | `crates/ui-egui/src/lightroom_import.rs` | stop being cancellable for a phase that must finish |
|
||||
| Face scan, AI Denoise | `crates/engine/src/cmd/face_recognize.rs`, `crates/engine/src/denoise.rs` | a continuous job with no ✕, kept in session state |
|
||||
| Find Missing Photos | `crates/ui-egui/src/tasks.rs` | a generic background task with a row (`kind: Some(..)`) |
|
||||
| Reload from Disk | `crates/ui-egui/src/reload.rs`, `crates/engine/src/cmd/reload.rs` | a command split into plan / probe / apply so the app reads the files on a worker thread (`tasks::spawn_tracked`: progress and `Cancel::Flag`); the result is committed through `Session::execute_fn` (undo step, save, XMP auto-write), and dropped for a photo a later-started reload has already updated; `wait: true` runs it in place |
|
||||
| Reading focus points | `crates/engine/src/focus_scan.rs` | a worker paced by the UI's per-frame pump (`focus.pump {pace}`); `Cancel::Flag`; commits in batches without undo steps |
|
||||
|
||||
## Testing
|
||||
|
||||
+12
@@ -127,6 +127,18 @@ tiles. All ten sensor comparisons pass. R8 HEVC preview tracks are distinguished
|
||||
the primary Bayer RAW; the HEVC preview itself is not decoded. Missing corpus
|
||||
files are reported as skipped by the tests.
|
||||
|
||||
In a tile with a neighbour on its right, the vertical high-pass bands of the level-1 and level-2
|
||||
low-pass columns (bands 8 and 5) are entropy coded at an odd width: an even band carries one extra
|
||||
column that synthesis does not use. No other band is extended, including the last level's (band 2).
|
||||
The decoder takes each band's coded width from this rule. It used to try the synthesis width and up to
|
||||
two more columns and keep the first that decoded to the exact end of the stream, but two widths can
|
||||
both do that and give different coefficients (#899). The rule was measured on every C-RAW plane in
|
||||
`cr3_corpus.rs`: two-tile files at tile widths 2816, 3144, 3192, 3444 and 3564 (G5 X Mark II, M50,
|
||||
R100, 250D, RP, R, 90D, M6 Mark II), whose left tiles extend bands 5 and 8 exactly where the rule says,
|
||||
and single-tile `0x100` and `0x200` files, which never extend a band. In every plane, each band decodes
|
||||
at its coded width and at no other width up to two columns wider, so all sensor hashes are unchanged.
|
||||
No public file has three or more C-RAW tiles; a middle tile follows the same rule, which is unverified.
|
||||
|
||||
The version `0x200` QP predictor and entropy framing were checked independently.
|
||||
Controlled input mutations establish floor averaging of paired QP rows before
|
||||
the nonlinear integer table, then multiplication, division by eight, addition
|
||||
|
||||
+1
-1
@@ -27,7 +27,7 @@ Hours are Opus 5.5 agent wall-clock hours (see [target-app-parity.md](target-app
|
||||
| 10 B | **Per-model verification thin, plus cheap per-body decode fixes**: 545 of 1,446 Adobe models verified (38%), ~85% of photographers on a verified body; Nikon 5 bodies verified, Canon 34, Leica 14 (the Q3 by a recorded check of 38 private DNGs). Cheap fixes: Z 6II packed 14-bit NEF (#769, PR #671), Sony A100 / F828 regression (#702), DSLR-A200…A390 / A850 12-bit ARW (#535) | measured: [raw/coverage.tsv](raw/coverage.tsv) | per-body bugs (levels, crops, WB) surface only from users | 25–45 | [raw-parity](raw-parity.md#by-maker-measured) |
|
||||
| 11 | **Enhance: Raw Details, Super Resolution; AI denoise without shippable weights or X-Trans** | ENH rows 0%; denoise Bayer only, RawNIND weights GPL and opt-in | Fujifilm users get no AI denoise; nobody gets it out of the box | 30–60 + model decision | [raw-parity](raw-parity.md#demosaic-levels-crops-and-lens-metadata) |
|
||||
| 12 | **Content-aware / generative remove, detect distractions, reflections removal** | REM rows 75%; patch synthesis missing | cleanup work goes back to Photoshop | 25–45 (+ generative model) | [target-app-parity](target-app-parity.md#ai) |
|
||||
| 13 | **Lightroom catalog migration is one-way and approximate**: unsupported Adobe profiles / AI settings archived, history kept as data; offline record counts and destination are now shown in a lasting import result | lightroom-catalog-import.md | switching users lose some looks | 8–12 | [file-format-parity](file-format-parity.md) |
|
||||
| 13 | **Lightroom catalog migration is one-way and approximate**: unsupported Adobe profiles / AI settings archived, history kept as data; main-file pages read on demand up to 16 GiB, with dense-table and WAL bounds retained (#798); offline record counts and destination are now shown in a lasting import result | lightroom-catalog-import.md | switching users lose some looks | 8–12 | [file-format-parity](file-format-parity.md) |
|
||||
| 14 | **HDR display** (EDR / Windows HDR); the headroom limit and SDR rendition from `crs` XMP (the HDR switch, `HDREditMode`, is read). HLG AVIF export done; the display's SDR/HDR decision is tested, blocked on eframe's 8-bit surface | HDR rows 70%; LR-VIEW-HDR-DISPLAY ⬜ | HDR editing works blind on HDR monitors | 12–25 | [hardware-parity](hardware-parity.md) |
|
||||
| 15 | **Print module**: identity plate / watermark / photo info overlays, Cut Guides for the packages, Custom Package Auto Layout, printer-driver Page Setup and direct printing, JPEG file output (the Print dialog writes a colour-managed PDF) | LRC-PRINT rows | Classic users who print | 15–25 | [target-app-parity](target-app-parity.md#features) |
|
||||
| 16 | **Map view, Book, Slideshow module, publish services** | LRC-MAP / BOOK / SS rows ⬜ | Classic users; publish services matter for Flickr / SmugMug workflows | 60–100 | [target-app-parity](target-app-parity.md#features) |
|
||||
|
||||
@@ -43,6 +43,8 @@ Overrides (read once at launch):
|
||||
- else `WGPU_BACKEND` (wgpu's own variable, same names) — before #136 only the window honoured it,
|
||||
the compute device always used Vulkan + DX12 (+ Metal).
|
||||
- `LIGHTCRAFT_GPU=0`: GPU rendering off for the process (the window is unaffected).
|
||||
- Both `LIGHTCRAFT_GPU=0` and `LIGHTCRAFT_GPU_BACKEND=off` also keep AI Denoise, the face models
|
||||
and the AI mask model (SAM 3 on Metal) on the CPU.
|
||||
|
||||
**DX12 shader compiler (issue #471).** Every instance (window and compute) compiles DX12 shaders with
|
||||
FXC (`d3dcompiler_47.dll`, part of Windows; `lightcraft_gpu::backend::backend_options`). wgpu's
|
||||
|
||||
@@ -28,6 +28,11 @@ settings are imported. Missing originals remain catalogued for relinking, count
|
||||
separately from failures reading files that are present. Known-missing originals are not probed
|
||||
again. Previously browsed Local records join All Photos even when their originals are offline;
|
||||
their promotion is counted once and undone together with the catalog import.
|
||||
An image whose file, folder or root folder record is gone from the catalog, or whose original path
|
||||
is empty (Lightroom leaves these behind and ignores them), is skipped together with its virtual
|
||||
copies instead of failing the whole import. The result lists them as data (`skipped`: each
|
||||
image id with its reason, `missingFile`, `missingFolder`, `missingRoot`, `incompletePath` or
|
||||
`masterSkipped`), also in `library.inspectLightroom`, and the desktop result window says how many in the UI language.
|
||||
Virtual copies count as added records too; reimport reports zero additions when all
|
||||
records already exist. Existing LightCraft
|
||||
edits are preserved by default; `updateExisting=true` explicitly replaces them. Persistent import
|
||||
@@ -44,11 +49,16 @@ retained. Oldest managed archives are retired; legacy `.json` archives remain un
|
||||
`Interop/lightroom-index.json` records imported identities. Source catalogs and originals are
|
||||
never overwritten.
|
||||
|
||||
Each native `.lrcat` database and WAL file is independently limited to 1 GiB
|
||||
(1,073,741,824 bytes). This bounds the current whole-file reader; referenced photo sizes and
|
||||
photo counts are not part of that limit. XMP packets are capped at 16 MiB and each table at one million rows.
|
||||
The in-tree, read-only Rust SQLite B-tree reader follows SQLite’s public file-format
|
||||
specification; it introduces no dependency or C runtime. Table payloads are capped at 256 MiB.
|
||||
Native import reads referenced database pages on demand (at most 64 KiB per page), rather than
|
||||
loading the whole `.lrcat`. Database files are capped at 16 GiB and 16,777,216 pages; the WAL
|
||||
remains capped at 1 GiB. Tables still have a 256 MiB encoded-payload budget and one million rows,
|
||||
individual records are capped at 64 MiB, and XMP packets at 16 MiB. These limits can still reject
|
||||
large catalogs with dense tables; file size alone no longer determines the memory allocation.
|
||||
The open file and source path are checked again after all tables are read. Close Lightroom before
|
||||
importing: metadata checks detect ordinary concurrent changes, but are not SQLite snapshot isolation.
|
||||
|
||||
The in-tree, read-only Rust SQLite B-tree reader follows SQLite's public file-format
|
||||
specification; it introduces no dependency or C runtime.
|
||||
Optional history, keyword and collection tables that exceed limits are skipped with warnings;
|
||||
required photo tables still report errors. Unrelated unsupported schemas are ignored. A needed
|
||||
WITHOUT ROWID or virtual table produces an explicit error, handled as a warning when optional.
|
||||
@@ -106,3 +116,7 @@ an absolute path is not looked at: the import lists it as missing with a warning
|
||||
photo and its virtual copies out of the library. To import originals on a share, open the catalog
|
||||
from that share. A path that is not a path on this computer (a Windows drive path read on macOS or
|
||||
Linux) is not looked at either and stays catalogued as missing.
|
||||
|
||||
## Historical local validation
|
||||
|
||||
Validated in a local Linux environment after upstream reconciliation: 47 catalog-focused tests, nine pipeline spot tests, the exact sparse-catalog regression, and all eight `cargo xtask ci` stages passed. The full gate recorded 3,166 passing test executions, zero failures, and 28 ignored tests. An original synthetic sparse catalog with a required image-table page beyond 1 GiB also passed headless application import and selection; its expected missing-original placeholder retained its five-star rating. This snapshot check does not establish native-window coverage or dense-catalog performance.
|
||||
|
||||
@@ -27,11 +27,13 @@ revizí rozsahu a terminologie. Rodilý mluvčí ani profesionální překladate
|
||||
plurálové pravidlo se nepřidává.
|
||||
- Písmo Inter dodávané s aplikací obsahuje české znaky i uvozovky „…“. Další font není potřeba.
|
||||
|
||||
Katalog obsahuje 2 056 běžných položek a 246 formátových šablon. Každý běžný klíč existuje
|
||||
v jiném katalogu nebo je používán kódem. Oproti německému katalogu má 108 dalších klíčů,
|
||||
Katalog obsahuje 2 394 běžných položek a 297 formátových šablon. Každý běžný klíč existuje
|
||||
v jiném katalogu nebo je používán kódem. Oproti německému katalogu má 215 dalších klíčů,
|
||||
všechny s doloženým použitím: popisky příkazů, menu, ovládacích prvků a nastavení,
|
||||
texty HDR/exportu a obličejů a kontextové hodnoty Ano/Ne. Z toho 41 klíčů již existuje
|
||||
v dalších katalozích a 67 doplňuje současné překladové lookupy.
|
||||
texty HDR/exportu a obličejů a kontextové hodnoty Ano/Ne. Z toho 66 klíčů již existuje
|
||||
v dalších katalozích a 149 doplňuje současné překladové lookupy. Doplnění z 18. 10. 2026
|
||||
přidalo texty novějších funkcí (prezentace, tisk, přednastavení efektů masek, zaostřovací
|
||||
body) a prošlo stejnou kontrolou.
|
||||
|
||||
Řádkové fragmenty poznámek k vydáním byly odstraněny, včetně 163 fragmentů převzatých
|
||||
ze sdílených katalogů; text poznámek v dialogu Co je nového používá anglický fallback.
|
||||
|
||||
@@ -23,8 +23,10 @@ interface never uses Mtavruli (a test checks the whole catalog). Commands are ve
|
||||
are count-neutral, noun first (`იმპორტირებულია ფოტო: 3`); English plural endings are dropped with
|
||||
`{:.0}`. Dates read `კვირა, 20.09.2026` (`{weekday}, {day:02}.{month:02}.{year}`), months `09.2026`.
|
||||
|
||||
Inter has no Georgian letters and craft-fonts has no Georgian face yet, so Georgian text shows boxes
|
||||
until a Georgian face is available to the UI.
|
||||
Inter has no Georgian letters, so the UI draws Georgian with craft-fonts' Noto Sans Georgian (`Geor`),
|
||||
like the Thai and Arabic faces: desktop release builds embed it, and a web build made with
|
||||
`CRAFT_FONTS_DIR` keeps it in the WASM (`crates/engine/build.rs`). The release web build doesn't use
|
||||
craft-fonts yet, and a build without `CRAFT_FONTS_DIR` has no Georgian face and shows boxes.
|
||||
|
||||
## Terminology
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Localization parity
|
||||
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-12 · **Change:** minor (de, es, fr, ja, uk catalogs filled; re-measured; Italian and Georgian interface catalogs added) · **Target:** Adobe Lightroom Classic 15.5 / Lightroom 9.6
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-15 · **Change:** trivial (Italian: every interface string translated, native-speaker corrections; previously minor: de, es, fr, ja, uk catalogs filled; re-measured; Italian and Georgian interface catalogs added) · **Target:** Adobe Lightroom Classic 15.5 / Lightroom 9.6
|
||||
|
||||
How far each interface language is from `full`. How to add or maintain a language is in
|
||||
[localization.md](localization.md); per-language notes are in `localization-<code>.md`
|
||||
@@ -65,7 +65,7 @@ that is promising but is not recorded as review, so the column says `no (communi
|
||||
| Traditional Chinese (Taiwan) | `zh-hant` | 1,993 / 2,304 (87%) | Hant; uses the Simplified Chinese face until craft-fonts has a TC face | no (community edits) | partial | 8–14 h (incl. the font) |
|
||||
| Russian | `ru` | 1,704 / 2,304 (74%) | Cyrillic (Inter) | no (community edits) | partial | 8–12 h |
|
||||
| Ukrainian | `uk` | 2,383 / 2,398 (99%; 2026-10-12) | Cyrillic (Inter) | no (community edits) | partial (lower-layer errors English) | 2–4 h |
|
||||
| Italian | `it` | 2,201 / 2,398 (92%, measured 2026-10-12; the rest is What's New) | Latin (Inter) | no (written with an agent; native-speaker pass pending) | partial (lower-layer errors English) | 3–5 h |
|
||||
| Italian | `it` | 2,628 / 3,278 (80%, measured 2026-10-15; every interface string is translated, the other 650 are What's New lines, left in English) | Latin (Inter) | partly (written with an agent; corrections and the 2026-10-15 additions by a native speaker) | partial (lower-layer errors English) | 3–5 h |
|
||||
| Georgian | `ka` | 2,398 / 2,398 (100%: every plain and formats message any catalog has on 2026-10-12, plus the 27 display labels and 397 What's New lines no other catalog translates) | Geor: no craft-fonts face yet (boxes until one is available) | yes (native speaker) | partial (lower-layer errors English) | 2–4 h (+ a Georgian face) |
|
||||
|
||||
Target languages LightCraft lacks: Korean, Dutch, Polish, Swedish, Norwegian Bokmål, Thai
|
||||
@@ -93,6 +93,7 @@ Target languages LightCraft lacks: Korean, Dutch, Polish, Swedish, Norwegian Bok
|
||||
|
||||
| Date | Change | Summary |
|
||||
|---|---|---|
|
||||
| 2026-10-15 | trivial | Italian (`it`): 339 interface messages and 60 format strings that were missing or still in English translated (2,628 of 3,278; only What's New left in English); native-speaker corrections: Exposure → *Esposizione*, Sharpening → *Nitidezza*, Preset → *Preset*, blurry → *sfocate* |
|
||||
| 2026-10-12 | minor | Italian interface catalog (`it`): 2,201 of 2,398 messages, What's New left in English |
|
||||
| 2026-10-11 | minor | Shared work: the interface decides on error kinds, not English error text (#1046) |
|
||||
| 2026-10-12 | minor | Georgian (`ka`) row: every catalog message |
|
||||
|
||||
+5
-2
@@ -146,8 +146,11 @@ Each edit in a batch has its own undo step. Connect mode also lists `ui_inspect`
|
||||
Ports and connect mode are unchanged.
|
||||
|
||||
Every listed tool has a title and all four MCP hints. Generated command tools are conservatively
|
||||
marked as edits. Helpers with optional output paths are marked as writers; `export` is not
|
||||
idempotent because the default conflict policy chooses a new filename on repeated calls.
|
||||
marked as edits. `export`, `render_photo` and `screenshot` have `readOnlyHint: false` and
|
||||
`destructiveHint: true`: their caller-supplied output paths can replace existing files, even
|
||||
when a path is optional. The image-only `render_preview` and `ui_screenshot` remain read-only
|
||||
and non-destructive. These annotations are client hints, not a file-access sandbox.
|
||||
`export` is not idempotent because the default conflict policy chooses a new filename on repeated calls.
|
||||
Unknown helper argument keys return JSON-RPC `-32602` naming the key and accepted arguments.
|
||||
Generated `cmd_*` tools and nested command params remain free-form: the registry has prose
|
||||
parameter docs, not machine-readable schemas. Existing command validation (including strict
|
||||
|
||||
+21
-15
File diff suppressed because one or more lines are too long
+4
-2
@@ -1,6 +1,6 @@
|
||||
# RAW parity: cameras, formats, demosaic, colour and lens profiles
|
||||
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-18 · **Change:** patch (X-Trans CMOS 4 bodies take the X-T3's spectral matrices) · **Target:** Adobe Lightroom Classic 15.5 / Lightroom 9.6 (Camera Raw 18.7)
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-18 · **Change:** minor (lossy JPEG XL DNG tiles with a non-linear declared transfer curve decode; X-Trans CMOS 4 bodies take the X-T3's spectral matrices) · **Target:** Adobe Lightroom Classic 15.5 / Lightroom 9.6 (Camera Raw 18.7)
|
||||
|
||||
Raw processing is the deepest single dimension of Lightroom parity: a photographer whose camera
|
||||
does not decode, or decodes with the wrong colour, cannot switch. This document measures it model by
|
||||
@@ -155,7 +155,7 @@ checked (the per-model CC0 sweeps of October 2026); compressed ORF (OM / Olympus
|
||||
| SRW | uncompressed, 12-bit packed | ✅ | EX1 corpus; NX10, NX20 |
|
||||
| SRW | compressed 32770 / 32772 / 32773 | ⬜ | refused |
|
||||
| DNG | uncompressed, LJ92, lossy JPEG / Smart Preview, Deflate incl. float, LinearRaw, tiles / strips, row / column interleave | ✅ | 6 corpus DNGs; 4 ILCE-6400 JXL DNGs; 38 Leica Q3 DNGs (lossless JPEG, 14-bit, private check) |
|
||||
| DNG | JPEG XL (DNG 1.7, lossless and lossy) | ✅ | synthetic bit-exact; real lossless and lossy files |
|
||||
| DNG | JPEG XL (DNG 1.7, lossless and lossy) | ✅ | synthetic bit-exact; real lossless and lossy files; lossy tiles keep their declared transfer curve (Lightroom Classic 15.1 LinearRaw DNGs label linear data with the sRGB curve); PQ / HLG tiles preview-only |
|
||||
| DNG | Apple ProRAW (incl. semantic mattes) | ✅ | iPhone 12 Pro corpus; ProfileGainTableMap read but off (ΔE00 19 against Lightroom with it on) |
|
||||
| DNG | phone DNGs (Pixel, Galaxy, Xiaomi…) | 🟡 | Pixel 2 XL corpus; 13 further Apple, Google and Samsung models in the pinned sweep; white-level pile-up fix #548 |
|
||||
| DNG | GoPro GPR (VC-5) | ⬜ | refused |
|
||||
@@ -288,6 +288,8 @@ Beyond decoding:
|
||||
| Date | Change | Summary |
|
||||
|---|---|---|
|
||||
| 2026-10-18 | patch | X-T30, X-T30 II, X-Pro3, X100V, X-S10 and X-E4 take the X-T3's spectral matrices (same 26.1 MP X-Trans CMOS 4 sensor): an X-T30 raw with a black-and-white (Acros) camera JPEG no longer opens in the muted neutral fallback |
|
||||
| 2026-10-15 | minor | DNGs with a look table and no tone curve (Lightroom-written, Adobe Standard) fit their tone to the embedded preview, also Lightroom's 1024 px one: two lossy ILCE-7CM2 DNGs no longer render 0.8 EV dark and flat |
|
||||
| 2026-10-15 | minor | Lossy JPEG XL DNG tiles that declare a non-linear SDR transfer curve (sRGB, BT.709, DCI, gamma) decode with that curve restored instead of falling back to the embedded preview: Lightroom Classic 15.1 LinearRaw DNGs (Rec. 2100 primaries, sRGB curve; 2 private ILCE-7CM2 files) |
|
||||
| 2026-10-15 | minor | Hasselblad 3FR / FFF embedded previews: the uncompressed 8-bit RGB image, handed on as a baseline TIFF (checked on the CC0 H4D-40 FFF, 1217 × 913, and CFV 3FR, 218 × 218, upright through the file's orientation); counts unchanged |
|
||||
| 2026-10-15 | minor | Hasselblad 3FR / FFF get their own reader (`crates/raw/src/vendor/hasselblad.rs`): an FFF takes its colour matrix from the original 3FR header in tag `0xC51B` instead of developing with the neutral matrix; RGGB checked against the camera preview on the CC0 H4D-40 FFF and CFV 3FR; counts unchanged |
|
||||
| 2026-10-14 | minor | Camera-coverage sweep (#1327): 30 models verified on 40 CC0 files (`coverage_sweep_corpus.rs`, `coverage-sweep.tsv`; `docs/raw/coverage.py` 515 → 545 verified rows); the counts above are not re-measured here. Sensor verification does not establish colour parity (#1277) |
|
||||
|
||||
+8
-1
@@ -111,7 +111,9 @@ are the same in every version, so serve them with `Cache-Control: no-cache` (not
|
||||
grant it, a notice says the library may be evicted under storage pressure.
|
||||
- **File ▸ Back Up Library…** downloads a zip of the library files and every stored original
|
||||
(`originals/<hash>/<file name>`, so an unzipped backup is browsable; entries are stored, not
|
||||
compressed; at most 4 GB). **File ▸ Restore Library from Backup…** reads such a zip into a
|
||||
compressed; at most 4 GB). While a save is failing it refuses to start and says how many
|
||||
changes are unsaved and why: the zip copies the stored library files, so it would miss them
|
||||
(issue #1508). **File ▸ Restore Library from Backup…** reads such a zip into a
|
||||
new library folder (`library-restored-<time>/`; originals already stored are skipped, every
|
||||
entry's checksum is verified, and each restored original must match its content hash) and
|
||||
only then switches to it (`active-library` names the folder in use) and reloads: the previous
|
||||
@@ -121,6 +123,11 @@ are the same in every version, so serve them with `Cache-Control: no-cache` (not
|
||||
writes, so commands report `saved in memory but not written`, the top bar shows the unsaved
|
||||
warning (as on the desktop, see `docs/control-protocol.md`), and saving is retried every
|
||||
2 s until it works (`web.stats` → `saveError`).
|
||||
- A failed read at start-up is not taken for a missing file: only `NotFoundError` means absent.
|
||||
When a stored library file (or the `active-library` pointer) exists but can't be read, the
|
||||
library isn't opened and nothing is saved, so the stored copy stays as it was; the window says
|
||||
which file failed (issue #1507). A thumbnail index that can't be read keeps the stored
|
||||
thumbnails instead of deleting them as unindexed.
|
||||
- A picked or dropped photo whose bytes can't be stored is not added (it would be gone after
|
||||
a reload); a notice says why.
|
||||
- One tab at a time: the page holds a Web Lock (`navigator.locks`, `lightcraft-library`); a
|
||||
|
||||
@@ -1,5 +1,50 @@
|
||||
# What's new in LightCraft
|
||||
|
||||
## 2026-10-19
|
||||
|
||||
### Hidden Local locations come back one at a time
|
||||
- With several locations removed from Local, "Show N hidden locations" now lists them, so each comes back on its own
|
||||
(Restore All is there too). The row used to bring every hidden location back at once (issue #2114).
|
||||
|
||||
## 2026-10-18
|
||||
|
||||
### Fill Location from GPS: no more neighbouring town in City
|
||||
- The offline place names list towns of 15,000 people or more, so a photo taken in a smaller one got the nearest larger
|
||||
town in City, sometimes another municipality (issue #2242). The Fill Location tooltip and the command help now say so,
|
||||
and `metadata.reverseGeocode` takes `maxDistance` (metres): past it, City stays empty (State and Country still fill)
|
||||
and the reply names the town it found as `nearestCity`.
|
||||
|
||||
### A preset that couldn't be saved is no longer lost silently
|
||||
- When presets.json can't be written (a full disk, or the browser's storage quota), the new or changed preset now
|
||||
counts as unsaved, like other changes: the top bar warns, saving is retried, and quitting asks first (issue #2266).
|
||||
Before, the failure was only logged, and the preset could be lost without any warning.
|
||||
|
||||
### Portrait crop ratios stay portrait
|
||||
- A crop ratio no longer flips to the photo's orientation (issue #2253): after X turns the crop portrait, picking
|
||||
another ratio (4 × 5, 2 × 3…) keeps it portrait, as in Lightroom, and a Custom ratio taller than wide gives a
|
||||
portrait crop.
|
||||
|
||||
## 2026-10-15
|
||||
|
||||
### Constrain Crop tightens the crop you see
|
||||
- Geometry ▸ Constrain Crop now changes the crop itself, as in Lightroom (issue #1927). Ticking it, or moving a
|
||||
Geometry slider, Upright or a lens correction while it is ticked, shrinks the crop about its centre until the blank
|
||||
corners left by the correction are gone, and the Crop tool shows the tighter crop. Before, it only applied when the
|
||||
photo was shown outside the Crop tool, so in the Crop panel, where the checkbox is, nothing seemed to change. A crop
|
||||
you draw by hand is kept as drawn, and unticking keeps the tighter crop.
|
||||
|
||||
### Web: no backup that misses unsaved changes
|
||||
- In the browser version, File ▸ Back Up Library… no longer reports success while a save is failing (storage full or
|
||||
cleared): the backup copies the stored library, so it left out the changes that couldn't be saved (issue #1508). It
|
||||
now refuses to start and says how many changes are unsaved and why. LightCraft retries the save with every change;
|
||||
back up once nothing is left unsaved.
|
||||
|
||||
### Web: an unreadable library is not opened
|
||||
- In the browser version, a library file that exists but can't be read at start-up is no longer taken for a missing
|
||||
one (issue #1507). LightCraft used to open the library without it, showing an incomplete catalog that the next save
|
||||
would write over the stored copy. It now leaves the library unopened, saves nothing and says which file couldn't be
|
||||
read; reload the page to try again.
|
||||
|
||||
## 2026-10-14
|
||||
|
||||
### Model downloads time out on a name that doesn't resolve
|
||||
|
||||
+5
-1
@@ -10,7 +10,7 @@ other tools), and let LightCraft pick up edits made elsewhere.
|
||||
|---|---|
|
||||
| Name | `<stem>.xmp` by default (`IMG_0001.CR3` → `IMG_0001.xmp`); `<file>.xmp` (`IMG_0001.CR3.xmp`) with `naming: "full"`. Reading accepts either (preferred first) and `.XMP`. With stem naming, files sharing a stem don't share a sidecar: see *Shared names* below. |
|
||||
| Write | `photo.saveMetadataToFile {ids?}` (Photo ▸ Save Metadata to File, ⌘S), or automatically after every change with `library.xmpPreferences {autoWrite: true}` (File ▸ Automatically Write Changes into XMP). Slider drags are written once, when the drag ends; undo/redo rewrite the sidecar. An existing sidecar is **merged into, never replaced** (see *Saving into an existing sidecar*). Written atomically (temp file, fsync, rename). The result lists `written`, `merged` and `backups`. |
|
||||
| Read | On import (`library.import`, the report counts `sidecars`), and `photo.readMetadataFromFile {ids?}` (one undo step). For raw/DNG files without a sidecar, the XMP embedded in the file is used. `photo.readMetadataFromFile` never writes the sidecar, even with auto-write on: writing back what it has just read would add `lc:settings`, which later reads prefer over another app's `crs:` edits (issue #934). |
|
||||
| Read | On import (`library.import`, the report counts `sidecars`), and `photo.readMetadataFromFile {ids?}` (one undo step). For raw/DNG files without a sidecar, the XMP embedded in the file is used. `photo.readMetadataFromFile` never writes the sidecar, even with auto-write on: writing back what it has just read would add `lc:settings`, which later reads prefer over another app's `crs:` edits (issue #934). A file arriving in an import is ranked as if it were catalogued already: it reads its own `<file>.xmp` when another file of its stem (a raw first) owns the shared stem sidecar (issue #1905). |
|
||||
| Preferences | `library.xmpPreferences {autoWrite?, naming?: stem\|full}`, stored in the library's `prefs.json`. |
|
||||
|
||||
What we write (standard namespaces, so other tools can read the metadata):
|
||||
@@ -74,6 +74,10 @@ library share a stem, the stem sidecar belongs to one of them — a raw first, o
|
||||
the others write and read `<file>.xmp` (`IMG_0001.JPG.xmp`), falling back to the stem sidecar for reading when they
|
||||
have none. So saving one photo never overwrites the other's metadata (`Session::sidecar_naming`).
|
||||
|
||||
An import ranks the files it is adding the same way, before they are catalogued: a TIFF or JPEG arriving beside a
|
||||
raw of its stem reads its own `<file>.xmp` and takes nothing from the raw's `IMG_0001.xmp`, whether it arrives
|
||||
with the raw or after it (issue #1905).
|
||||
|
||||
## Face regions (MWG)
|
||||
|
||||
LightCraft reads face, pet, focus and barcode regions in the Metadata Working Group's region schema
|
||||
|
||||
Reference in New Issue
Block a user