On some Windows machines FXC fails to compile egui's own shaders on DX12
("The parameter is incorrect. (0x80070057)" in egui_pipeline) and wgpu's
default error handler panicked. Each renderer (DX12, Vulkan, GL, glow) is
now probed with error scopes and a logging error handler before the window
opens; the one that works is remembered; a start that dies leaves a marker
and relaunches with the next renderer. LIGHTCRAFT_GPU_BACKEND=glow added.
Sources: own design.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Open Library given a Lightroom Classic catalog folder, one of its .lrdata /
.lrcat-data folders, Backups/, the .lrcat or its -wal/-shm/.lock files used to
create an empty LightCraft library inside Lightroom's folder (or, for the
.lrcat file, close the current library and then fail). Now:
- engine: lightroom_folder::lightroom_catalog_at (names only, top-level scan
bounded to 10 000 entries, never fails); Session::open_library refuses those
places with EngineError::LightroomCatalog {path, catalog} before creating
anything. A LightCraft library (catalog.snap / catalog.log) always wins.
- app: app.openLibrary checks before closing the current library, keeps it,
returns {opened: false, lightroomCatalog, path} and shows a dialog with
Import Lightroom Catalog… (file.importLightroom with the catalog found) and
Cancel. The library problem window gets the error instead.
- Lightroom import status keeps the catalog path after the task ends.
- CLI --library explains how to import instead.
On a 2015 MacBook Pro (Intel Iris/Metal), 0.5.0 never opened its window, even with
LIGHTCRAFT_GPU=0: wgpu's indirect-call validation builds an internal compute pipeline when the
window's device is created, Metal's compiler fails on it ("Compiler encountered an internal
error"), the device is lost and eframe exits. That validation only guards indirect draws and
dispatches, which neither LightCraft nor egui issue.
backend::instance_flags clears InstanceFlags::VALIDATION_INDIRECT_CALL for every instance
LightCraft creates (the window's, Gpu::new and the denoise device), unless
WGPU_VALIDATION_INDIRECT_CALL asks for it. Tests: the flag rule, and that the window's instance
descriptor goes without it.
A render still running on a worker thread while the process exits crashes in the
driver (SIGSEGV in the NVIDIA Vulkan driver on Linux), where neither the panic
hook nor catch_unwind helps: pool workers were detached, the devices are statics
that are never dropped, and nothing on the way out waited for GPU work.
- gpu: every entry into a device (render, device creation, pool trim, denoise
runner and tiles) is counted as work in flight (exit.rs). begin_shutdown()
lets nothing new in; wait_idle(timeout) waits for what is inside.
- preview: JobPool keeps its workers. shutdown(timeout) and Drop discard the
queued jobs and wait, bounded, for the running ones (2 s on drop); never for
the calling thread, and not twice.
- engine: a render job caught by the shutdown gives up instead of rendering on
the CPU, so quitting doesn't wait for it.
- app: on_exit ends with LightcraftApp::shutdown (2 s for everything), after
settings and library are saved; the macOS menu-bar Quit goes through on_exit.
Not changed: the export, merge, denoise and import threads are still detached;
their GPU work goes through the gate, their CPU work is cut off at exit as before.
Tests: pool drop waits / is bounded / survives a panicked job and its own job
dropping it; the gate; GPU renders refused and the device not created after
begin_shutdown; the app's shutdown leaves no render worker. The crash itself
needs Linux + NVIDIA and was not reproduced here (Windows, DX12): 9 of 9 quits
of the app mid-render exit with code 0 in under 0.8 s.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The CLI snapshot test and the headless resize test sent Cmd+Plus to change egui's
interface scale; with the keys handed to View > Zoom they no longer did. A
ui.zoomFactor control request (0.5-3, documented) sets the scale explicitly.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The File menu (beside the presets) imports a keyword list file
(Lightroom Classic's, Capture One's, Photo Supreme's .utf8) and exports
LightCraft's, with native file dialogs off the UI thread like the
other file commands; the desktop app gives the synchronous ones too.
Import says how many keywords it added and how many gained synonyms;
export says how many it wrote and names the keywords Capture One won't
import (; , < or > in a name).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Previews were always sRGB, which wide-gamut monitors show oversaturated.
Settings > Display > Choose Profile... (and the UI command
app.displayProfile) now shows photos through the monitor's ICC profile,
matrix/TRC or LUT-based.
- codecs: DisplayProfile (moxcms): the display's primaries (colorant tags,
or measured through the A2B tables of a LUT-only profile), the proxy ->
device correction (per-channel curve tables for matrix/TRC profiles, a
CMS transform for LUT profiles) and sRGB <-> display transforms.
- pipeline: DisplaySpace / RenderRequest::display: the per-pixel stage
renders into the display's primaries and gamut maps into its gamut
(CPU and GPU: only to_out / out_luma change). Soft proofing's display
gamut warning uses the display's gamut.
- engine: RenderJob::with_display; display::present converts sRGB
results (thumbnails, cached previews, embedded JPEGs). Histograms and
the preview caches stay sRGB; exports are unchanged.
- ui: the loupe, Before, Compare, hover and second window render for the
display; everything else is converted on the render workers. Settings >
Display tab, ui.json setting, file picker opening in the system's ICC
folder.
Tests: profile parsing and transforms (sRGB, P3, gamma 2.2, a synthetic
LUT-only profile, corrupted files), display renders equal 8-bit renders
into the matching output space, sampled histograms, and a headless UI test
of the whole flow. docs/display-profiles.md describes the design.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WEuxxPyG8t6KcADbZSEAUj
egui's zoom_with_keyboard was left on, so at the end of every frame it took the
same keys as View ▸ Zoom In / Zoom Out / Zoom to Fit for its browser-style
interface zoom. On macOS the native menu bar got the keys first; on Windows and
Linux the whole UI scaled. theme::apply turns the option off for every context
(desktop, headless, tests).
Headless tests that pressed Cmd+Plus / Cmd+Minus to exercise the interface
zoom's viewport handling set the zoom factor directly; the CLI snapshot test
that did the same is dropped (the unit tests cover the mechanism). New
tests_zoom_keys.rs: the shortcuts step the photo zoom while the zoom factor
stays, and Ctrl+wheel over the photo zooms it in the Edit view.
std::process::exit skipped eframe's on_exit (ui.json, close_library) and the
may_close check, so changes still in memory could be lost on Cmd+Q.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Explorer only selects a file when the path after /select, is quoted. Rust's argument quoting wraps the whole "/select,C:\My Photos\a.jpg" switch in quotes as soon as the path has a space, so Explorer opened Documents. Build the switch as /select,"<path>" (backslashes, verbatim \\?\ and \\?\UNC\ prefixes stripped) and pass it with raw_arg on Windows. The library folder tree's context menu now shows the platform's reveal label too, and the Russian catalog gains "Show in Explorer" and "Show in File Manager".
wgpu's default DX12 compiler (Auto) loads whichever dxcompiler.dll the DLL
search path finds first. LightCraft ships none, so it picked up another
program's copy; one without dxil.dll beside it (e.g. DXC 1.7) warns that
the DXIL is unsigned, wgpu treats that as a compile error, its
indirect-validation pipelines fail, the device is lost and the window
never opens ("Parent device is lost", exit code 1).
Every wgpu instance (window and both compute devices) now uses
lightcraft_gpu::backend::backend_options(): wgpu's env options with the
DX12 compiler pinned to FXC (d3dcompiler_47.dll, part of Windows), unless
WGPU_DX12_COMPILER asks for another.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1.Added Simplified Chinese font support for web and desktop builds.
2.Kept Inter as the default font, with Chinese used only as a fallback.
3.Verified Chinese glyph tests and a macOS universal build.
Signed-off-by: supper_yijie <syj794844854@163.com>
### Summary
When selecting "Quit LightCraft" from the native macOS menu bar, the application occasionally crashes with an `EXC_BAD_INSTRUCTION` (SIGILL) error report during shutdown.
### Root Cause
1. The native menu action triggered `ctx.send_viewport_cmd(egui::ViewportCommand::Close)`.
2. Forcing an explicit viewport close via the command queue while macOS and AppKit are initiating their own termination sequence creates a race condition with `winit`.
3. This conflict during native window, view, and observer teardown leads to an illegal instruction fault.
### Solution
- Replaced the manual `ViewportCommand::Close` trigger on `QUIT` with a clean `std::process::exit(0)`.
- This bypasses the problematic `winit`/AppKit teardown race condition on macOS, allowing the application to shut down instantly and cleanly without generating crash reports.
One config-dir helper (engine::config::config_dir; camera_profiles re-exports it) with both
the face-models and denoise-models defaults; hosts set up both model folders; Settings has
Faces and AI Denoise tabs; both command sets registered; both model dialogs; locale entries
from both; one [features] table per app manifest; Cargo.lock regenerated by cargo; the
DecodedSource cache cost counts both segmentation mattes and the denoised twin.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Resolve the docs/parity.md conflict, keep the HEIC rows. `cargo xtask ci` gets
a heif step (clippy + tests for lightcraft-codecs with --features heif), since
the workspace build leaves the feature off. The Cargo.toml comments no longer
say official builds enable it; the web one notes that wasm32 aborts on panic.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Keep current upstream UI behavior and the later detector contract checks. Validate candidate models before replacing working weights and records, with a synthetic regression that failed before the fix. Remove the unrelated GPU equivalence-test relaxation. All seven quality gates pass with the pinned hosted fonts.
Generated with Codex.