Build Luminar Neo

KINDAback to the verdict

0%0 of 17 items done

Saved on this device only. Tick prerequisites first, then work the phases in order · do not start one until the checks above it pass.

A non-destructive photo editor whose hard problems are a pipeline that shows at preview size what it will produce at full size, colour handled in a working space rather than in whatever the file happened to be, and a recipe format that still opens years later, with the AI tools as optional local models rather than the foundation.

estimated effort multi-daythe files for this build are in the project pack

ShellTauri 2 with React and TypeScript for the interface onlyPipelineRust, operating on a linear floating point working bufferEditsAn ordered recipe of operations in a versioned sidecar beside the fileMasksParametric where possible, compressed bitmaps referenced from the sidecar otherwiseModelsOptional local segmentation models loaded from diskFormatsJPEG, TIFF and PNG in, JPEG and TIFF out with an embedded profile

Before step 1

Everything below is assumed from the first step. Tick each one when you actually have it, not when you plan to.

  1. installfree

    Why Builds the interface and runs its tests.

    Get it nodejs.org, or nvm install 22. open ↗

    Verify node --version prints v22 or higher

  2. installfree

    Why The pixel pipeline runs in Rust, and the interface is a Tauri shell around it.

    Get it rustup.rs for the toolchain, then the Tauri 2 prerequisites page for your platform. open ↗

    Verify cargo --version prints and a blank Tauri 2 application launches

  3. have readyfree

    Why A sideways portrait, a file with no profile, a 24 megapixel frame and a gradient sky are where orientation, colour and banding go wrong, and a stock sample has none of them.

    Get it Your own photographs, including at least one of each of those cases, in JPEG and TIFF.

    Verify At least a thousand files with the four awkward cases identified

  4. installfree

    Why The only way to know an export's colour is right is to open it somewhere you did not write.

    Get it Any mature viewer or editor with colour management on your platform.

    Verify A known profiled fixture displays identically in it and in your editor

  5. have readyfree

    Why Subject and sky masks come from it, and the editor must work identically when it is absent.

    Get it A published segmentation model in a format your runtime loads, with its licence file kept beside it.

    Verify It produces a mask for a portrait and the editor still works when the file is deleted

Data model

Create these before the first phase that stores anything. Changing a table later is the expensive kind of change.

- photos(id, path, content_hash, width, height, orientation, embedded_profile, imported_at) · a reference to the original, never a copy of it
- recipes(photo_id, schema_version, ops_json, updated_at) · the sidecar, one per photograph, operations in application order
- an op carries kind, params_json, mask_id and enabled, and every spatial parameter is a fraction of the image size rather than a count of pixels
- masks(id, photo_id, kind, params_json, bitmap_ref) · brush and gradient masks are parametric; a model's output is a compressed bitmap
- versions(photo_id, name, recipe_snapshot_json, created_at) · named states of the recipe, not copies of pixels
- exports(id, photo_id, recipe_hash, output_path, format, profile, metadata_policy, at) · every export records what produced it
- models(name, path, sha256, licence, loaded) · no rows means manual masks only, which is a complete mode
- The original's hash is checked after every command in the suite, so the one promise this editor makes is tested rather than intended.

Environment variables

These go in a .env file the app reads at startup. The pack's .env.example is this table as a file · copy it, never commit the filled-in version.

VariableNeededExampleWhere the value comes from
LIBRARY_DBrequired~/.local/share/photos/library.dbReferences and hashes. Disposable; the sidecars beside the files are the record.
SIDECAR_SUFFIXrequired.recipe.jsonAppended to the photograph's name. Never written into the photograph.
WORKING_SPACErequiredlinear_srgbThe linear floating point space every operation runs in.
DEFAULT_EXPORT_PROFILErequiredsRGBConverted to and embedded on export. A file without one looks wrong everywhere.
METADATA_POLICY_DEFAULTrequiredkeep_except_locationPer export. Location stripping removes coordinates and nothing else.
MODELS_DIRoptional~/.local/share/photos/modelsOptional local models. Empty means manual masks, which is a complete mode.
WRITE_ORIGINALSrequiredneverPresent as a constant. There is no other value.

The build, in order

  1. Importing without touching anything

    A photograph is referenced, oriented and profiled, and its bytes are never changed.

    1. The path and the hash are what you keep; the bytes stay where they were.

      Files src-tauri/src/library.rssrc-tauri/src/sidecar.rs

    2. A sideways portrait and an untagged file are both routine.

      Files src-tauri/src/decode.rs

    3. The promise is tested against each operation, not stated once.

      Files src-tauri/tests/untouched.rs

    done when · tick each as it passes
    watch out
    • Writing a star rating into the file's own metadata block seems harmless, and it rewrites the file, changes its hash and on some formats re-encodes the image, so the first thing a photographer notices is that every original was modified on the day it was imported. Everything you know about a photograph lives in the sidecar, and the suite proves the original is untouched after every operation.
  2. A preview that is the export, smaller

    What the screen shows at viewport size is what the file contains at full size.

    1. Gamma-encoded eight-bit arithmetic bands, and the banding appears after the third adjustment.

      Files src-tauri/src/pipeline/mod.rssrc-tauri/src/colour.rs

    2. A radius in pixels is the reason previews lie.

      Files src-tauri/src/pipeline/ops.rs

    3. Numerically, per operation, with a stated tolerance.

      Files src-tauri/tests/preview_matches_export.rs

    done when · tick each as it passes
    watch out
    • A sharpening radius of two pixels means something different on a 1,200 pixel preview and a 6,000 pixel export, so the preview looks crisp and the file looks untouched, and the person exports four times before deciding the tool is broken. Express every spatial parameter as a fraction of the image dimensions, run one pipeline at both sizes, and compare the results numerically in the suite.
  3. Masks, including the ones a model draws

    Adjustments apply to part of an image, and a model's mask is a starting point you can edit.

    1. A brush stroke is points and a radius, not a six megapixel bitmap.

      Files src-tauri/src/masks.rs

    2. It is a proposal, and a portrait needs the missing hairs.

      Files src-tauri/src/models/segment.rssrc/panels/MaskPanel.tsx

    3. Tested by deleting the model files, not by reading the code.

      Files src-tauri/tests/no_model.rs

    done when · tick each as it passes
    watch out
    • Treating the model's mask as the mask, uneditable, means every hair it missed and every window it included is permanent, and a subject mask that is ninety percent right is useless for a portrait. Store the model's output as a bitmap the brush can add to and subtract from, and keep the parametric masks parametric so the sidecar stays small.
  4. History, versions and doing it to a hundred files

    Any step can be undone or revisited, and a look transfers to other photographs without transferring the crop.

    1. Undo is a change to a list, not a stack of pixel buffers.

      Files src-tauri/src/recipe.rssrc/state/history.ts

    2. Global adjustments copy; crop and masks do not, and the skip is reported.

      Files src-tauri/src/transfer.rs

    3. With no partial file left behind on cancel.

      Files src-tauri/src/batch.rs

    done when · tick each as it passes
    watch out
    • Pasting a recipe including its crop and its subject mask onto a photograph framed differently produces a crop through the subject's face and a mask over the wrong region, and it does so on a hundred files at once. Transfer only the operations that do not depend on where things are in the frame, list the ones skipped, and let the person add them by hand.
what this build does not replace
after v1, if you want it

Need the files? The project pack on the verdict page hands your agent the whole brief · more design.