Put the developer docs under docs/dev and index the folder for users first
docs/ had 26 developer documents flat beside the manual, and the two audiences are very differently sized: most readers want the manual and the gesture reference, a few want the register, the designs and the measurements. The manual and gestures.md stay at the top; everything for someone changing the code moves to docs/dev/, and the two documents that name their own successors — the v0.1 milestone and the UI-refinement plan — go to docs/dev/archive/ rather than being deleted, since both are still cited. docs/README.md is the index, users first. Every reference follows: code comments, Cargo manifests, the workflows, the pre-commit hook, the bench and traceability tools (which locate the repo root by docs/dev/requirements.md now), packaging, the Docker READMEs, CLAUDE.md, CONTRIBUTING.md and the README. The matrix links one level deeper and is regenerated. Links out of the moved documents into the tree gain a level; a link checker over every Markdown file finds none broken.
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
# Documentation
|
||||
|
||||
Two audiences, two folders. Most people want the first table and never the
|
||||
second.
|
||||
|
||||
## Using DarkRoom
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| [The manual](manual/README.md) | Every feature, pictured from the application itself — opening a library, rating and filing, developing, local masks, repair, film, panoramas, export |
|
||||
| [How it is driven](gestures.md) | Every gesture and shortcut, by screen. Generated from the code, so it cannot describe one the application does not have |
|
||||
|
||||
The [top-level README](../README.md) says what DarkRoom is, how to get it on
|
||||
each platform, and what is still missing.
|
||||
|
||||
## Changing DarkRoom
|
||||
|
||||
Everything under [`dev/`](dev/) is for someone working on the code. Start with
|
||||
[CONTRIBUTING.md](../CONTRIBUTING.md), which says how to land a first change
|
||||
without reading the rest.
|
||||
|
||||
**The register and the record.** What must be built, how it is built, and how
|
||||
far along it is.
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| [requirements.md](dev/requirements.md) | What the software must do — the numbered register, the decisions (D-numbers) and the spikes (S-numbers) |
|
||||
| [architecture.md](dev/architecture.md) | How it is built — crates, the GPU pipeline, the data model, sync |
|
||||
| [traceability.md](dev/traceability.md) | Generated: which requirement is claimed by which file. Never edited by hand |
|
||||
| [outstanding.md](dev/outstanding.md) | What is specified and not built, and whether that is a decision or a gap |
|
||||
| [technical-debt.md](dev/technical-debt.md) | Compromises taken deliberately, each with the condition that retires it |
|
||||
| [code-health.md](dev/code-health.md) | What a contribution costs, per seam, measured |
|
||||
|
||||
**Designs, one per subsystem.** Each is the specification the code was built
|
||||
to, kept current as the code moved.
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| [catalog.md](dev/catalog.md) | The index, the library view, incremental scan, the job queue |
|
||||
| [storage.md](dev/storage.md) | Storage backends: the seam a folder, a sync client and a Nextcloud account share |
|
||||
| [faces.md](dev/faces.md) | Face detection, identity, clustering and the eye-state models |
|
||||
| [segmentation.md](dev/segmentation.md) | How the application finds the regions a local mask snaps to |
|
||||
| [mask-editing.md](dev/mask-editing.md) | Painting, erasing and combining masks |
|
||||
| [spot-removal.md](dev/spot-removal.md) | Clone and heal as parameters in the edit graph |
|
||||
| [panorama.md](dev/panorama.md) | Alignment, projection, the chunked composite and the border fill |
|
||||
| [inference.md](dev/inference.md) | The neural runtime and model chosen per device, with the measurements |
|
||||
| [display-and-extension.md](dev/display-and-extension.md) | The display contract, and why the fused pipeline is already most of a plugin format |
|
||||
| [view-composition.md](dev/view-composition.md) | A controller for the display layer |
|
||||
| [ui-navigation.md](dev/ui-navigation.md) | Finding things in the interface once there are many |
|
||||
|
||||
**Measurements.** Numbers committed so a regression is a diff rather than a
|
||||
recollection.
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| [benchmarks.md](dev/benchmarks.md) | The per-commit suite: what it covers, what it does not, how to read a failure |
|
||||
| [bench-baseline.json](dev/bench-baseline.json) | The committed numbers the suite checks against |
|
||||
| [frame-budget.md](dev/frame-budget.md) | What a frame costs on each device, and the decision those figures settled |
|
||||
|
||||
**Platforms and distribution.**
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| [distribution.md](dev/distribution.md) | Which channels v1 targets and what each one constrains |
|
||||
| [windows.md](dev/windows.md) | The Windows installer, cross-built from the Linux CI |
|
||||
| [android-signing.md](dev/android-signing.md) | Which key signs the APK, and keeping it |
|
||||
|
||||
**Archive.** Kept as the record of what was asked for, not as plans.
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| [milestone-v0.1.md](dev/archive/milestone-v0.1.md) | The first milestone, delivered 2026-08-30 and superseded |
|
||||
| [ui-refinement.md](dev/archive/ui-refinement.md) | How the interface should look; succeeded by [ui-navigation.md](dev/ui-navigation.md) |
|
||||
|
||||
## Conventions
|
||||
|
||||
Two files here are generated and must not be edited by hand:
|
||||
`gestures.md` and `dev/traceability.md`. Both come from
|
||||
`cargo run -p traceability` and the pre-commit hook keeps them in step with
|
||||
the tree. The manual's pictures are recorded by
|
||||
[`tools/manual`](../tools/manual/README.md) and live in LFS.
|
||||
|
||||
A design document links to the requirements it satisfies and to the code
|
||||
that satisfies them. When the code moves, the link moves with it; a document
|
||||
that has stopped being true goes to `dev/archive/` with a note saying what
|
||||
replaced it, rather than being deleted.
|
||||
@@ -4,8 +4,8 @@
|
||||
|
||||
> Kept as the record of what the first milestone asked for, not as a plan.
|
||||
> Everything below shipped, and the application went well past it — see
|
||||
> [outstanding.md](outstanding.md) for what is still missing at 0.9.0.
|
||||
**Companion to:** [requirements.md](requirements.md) · [architecture.md](architecture.md)
|
||||
> [outstanding.md](../outstanding.md) for what is still missing at 0.9.0.
|
||||
**Companion to:** [requirements.md](../requirements.md) · [architecture.md](../architecture.md)
|
||||
|
||||
The first buildable milestone: connect to a Nextcloud folder, index it locally, and display RAW
|
||||
previews on both Linux and Android.
|
||||
@@ -39,7 +39,7 @@ building on sand.
|
||||
|
||||
## 2. The four assumptions under test
|
||||
|
||||
Each maps to a spike in [requirements.md §9](requirements.md).
|
||||
Each maps to a spike in [requirements.md §9](../requirements.md).
|
||||
|
||||
| # | Assumption | If wrong | Spike |
|
||||
|---|---|---|---|
|
||||
@@ -55,7 +55,7 @@ all, so it should be proven in the first week, before catalog or sync work begin
|
||||
|
||||
## 3. Functional scope
|
||||
|
||||
Requirement IDs reference [requirements.md](requirements.md); a v0.1 suffix marks a reduced subset
|
||||
Requirement IDs reference [requirements.md](../requirements.md); a v0.1 suffix marks a reduced subset
|
||||
of the full requirement.
|
||||
|
||||
### 3.1 Account and connection
|
||||
@@ -2,9 +2,9 @@
|
||||
|
||||
**Status:** Built, not yet recorded · 2026-08-30
|
||||
**Companion to:** [requirements.md](requirements.md) §4.1 (performance targets) · §8 (verification)
|
||||
**Instrument:** [`tools/bench`](../tools/bench) — `cargo run --release -p dr-bench -- check`
|
||||
**Instrument:** [`tools/bench`](../../tools/bench) — `cargo run --release -p dr-bench -- check`
|
||||
**Committed numbers:** [`bench-baseline.json`](bench-baseline.json)
|
||||
**GPU half:** [`core/dr-gpu/tests/frame_budget.rs`](../core/dr-gpu/tests/frame_budget.rs) ·
|
||||
**GPU half:** [`core/dr-gpu/tests/frame_budget.rs`](../../core/dr-gpu/tests/frame_budget.rs) ·
|
||||
[frame-budget.md](frame-budget.md)
|
||||
|
||||
§8 has said since it was written that performance is verified by *"an automated
|
||||
@@ -60,7 +60,7 @@ Two of those rows carry a qualifier, and the qualifiers are the point.
|
||||
library view cannot paint without: `Catalog::open` (which connects, migrates and
|
||||
**backfills**, and the backfill is three passes over the images table on every
|
||||
open), `count`, the first 400-row `window`, and the monthly `timeline`. Tagged
|
||||
`TRACES: NFR-P1` in [`tools/bench/src/catalog_open.rs`](../tools/bench/src/catalog_open.rs),
|
||||
`TRACES: NFR-P1` in [`tools/bench/src/catalog_open.rs`](../../tools/bench/src/catalog_open.rs),
|
||||
because a build that breaks it fails this gate.
|
||||
|
||||
**NFR-P3 — ≥ 100 images per second on the embedded preview path.** The
|
||||
@@ -69,7 +69,7 @@ per-image work is exactly what `spawn_thumbnail_sweep` does — `decode_jpeg`,
|
||||
`ThumbStore::put` — arranged in the same shape: chunks of 96, lanes owning
|
||||
disjoint slices, and the single thread that owns the store writing the finished
|
||||
chunk. Tagged `TRACES: NFR-P3` in
|
||||
[`tools/bench/src/thumbnails.rs`](../tools/bench/src/thumbnails.rs).
|
||||
[`tools/bench/src/thumbnails.rs`](../../tools/bench/src/thumbnails.rs).
|
||||
|
||||
### Requirements this can only half-answer, and is not tagged for
|
||||
|
||||
@@ -114,7 +114,7 @@ instead of ~2 TB, and neither half is flattered by that.
|
||||
| Rows | 50,000 images, 50,000 default versions, 400 folders, one root |
|
||||
| Capture times | Twelve years from a fixed epoch, so the timeline has ~144 monthly buckets |
|
||||
| Sources | 12 synthesised JPEGs at 1620 × 1080 — the size `dr-decode` records a CR2 carrying in IFD2 |
|
||||
| Seed | 20260829, in [`tools/bench/src/main.rs`](../tools/bench/src/main.rs) |
|
||||
| Seed | 20260829, in [`tools/bench/src/main.rs`](../../tools/bench/src/main.rs) |
|
||||
| Location | `$DR_BENCH_DIR`, else the system temporary directory |
|
||||
|
||||
It is reproducible from the seed, and a `stamp.json` beside it records what it
|
||||
@@ -17,8 +17,8 @@ permission to a package rather than after.
|
||||
|
||||
| Platform | Channel | State | What it constrains |
|
||||
|---|---|---|---|
|
||||
| Linux | Arch source package — [`packaging/PKGBUILD`](../packaging/PKGBUILD) | Built, in tree | Nothing. Full filesystem access, system Vulkan, system secret daemon |
|
||||
| Linux | Flatpak — [`packaging/flatpak/`](../packaging/flatpak/) | Manifest in tree, **library selection does not work** (§4) | Portals only. No `--filesystem=`, no host mount table, no typed paths |
|
||||
| Linux | Arch source package — [`packaging/PKGBUILD`](../../packaging/PKGBUILD) | Built, in tree | Nothing. Full filesystem access, system Vulkan, system secret daemon |
|
||||
| Linux | Flatpak — [`packaging/flatpak/`](../../packaging/flatpak/) | Manifest in tree, **library selection does not work** (§4) | Portals only. No `--filesystem=`, no host mount table, no typed paths |
|
||||
| Linux | AppImage | v1 channel, **recipe not yet written** (§5) | Oldest supported glibc, and no sandbox at all |
|
||||
| Android | F-Droid | v1 channel, not yet submitted | GPLv3-clean build, reproducible, no proprietary blobs |
|
||||
| Android | Play Store | **Not v1** (§6) | Would make ARCH §6.9 binding as policy rather than as engineering |
|
||||
@@ -39,7 +39,7 @@ somewhere:
|
||||
that misses one of them costs the icon in the shell or the association in the
|
||||
software centre, and neither failure announces itself.
|
||||
- **The metainfo, not just the desktop entry.**
|
||||
[`packaging/paris.tourolle.darkroom.metainfo.xml`](../packaging/paris.tourolle.darkroom.metainfo.xml)
|
||||
[`packaging/paris.tourolle.darkroom.metainfo.xml`](../../packaging/paris.tourolle.darkroom.metainfo.xml)
|
||||
is the single description of the application, installed by every channel that
|
||||
has somewhere to put it. Its `metadata_license` is CC0-1.0 and its
|
||||
`project_license` is GPL-3.0-or-later; those differ on purpose — see the
|
||||
@@ -173,7 +173,7 @@ Two changes, in this order:
|
||||
chooser instead of a volume list.
|
||||
|
||||
**Done when:** a Flatpak built from
|
||||
[`packaging/flatpak/paris.tourolle.darkroom.yml`](../packaging/flatpak/paris.tourolle.darkroom.yml),
|
||||
[`packaging/flatpak/paris.tourolle.darkroom.yml`](../../packaging/flatpak/paris.tourolle.darkroom.yml),
|
||||
with its `finish-args` unchanged and no `flatpak override` applied, can select a
|
||||
library root, scan it, and write a sidecar back into it.
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
**Status:** Measured · 2026-08-27
|
||||
**Companion to:** [display-and-extension.md](display-and-extension.md) §2–3 ·
|
||||
[requirements.md](requirements.md) §3.4 FR-DSP-2, FR-DSP-3, FR-DSP-4
|
||||
**Instrument:** [`core/dr-gpu/examples/frame_budget.rs`](../core/dr-gpu/examples/frame_budget.rs)
|
||||
**Guard:** [`core/dr-gpu/tests/frame_budget.rs`](../core/dr-gpu/tests/frame_budget.rs)
|
||||
**Instrument:** [`core/dr-gpu/examples/frame_budget.rs`](../../core/dr-gpu/examples/frame_budget.rs)
|
||||
**Guard:** [`core/dr-gpu/tests/frame_budget.rs`](../../core/dr-gpu/tests/frame_budget.rs)
|
||||
|
||||
[display-and-extension.md](display-and-extension.md) §2 fixed a decision rule in
|
||||
advance and made three measurements the thing that settles it. This file is
|
||||
@@ -18,11 +18,11 @@ The core is further along than the interface, and it is worth being exact
|
||||
about which half is missing, because it changes the size of the work.
|
||||
|
||||
**Painting exists everywhere except where a finger is.**
|
||||
[`MaskSource::Brush`](../core/dr-pipeline/src/mask.rs), [`Stroke`], the
|
||||
[`MaskSource::Brush`](../../core/dr-pipeline/src/mask.rs), [`Stroke`], the
|
||||
simplification and the point budgets, the sidecar's `stroke = …` line and its
|
||||
parser, the GPU's per-stroke bounding-box draw with add and erase blend
|
||||
states — all of it is written, tested, and reachable from no control in the
|
||||
application. [`toolrail.slint:138`](../ui/dr-ui/ui/toolrail.slint#L138) says so
|
||||
application. [`toolrail.slint:138`](../../ui/dr-ui/ui/toolrail.slint#L138) says so
|
||||
in as many words: *"what is missing is the canvas interaction"*.
|
||||
|
||||
**A mask has exactly one source.** A layer is one `MaskSource` and a shaping
|
||||
@@ -37,7 +37,7 @@ shoulder and leaks four pixels into the hair, no global number fixes both, and
|
||||
that is the ordinary case rather than a corner one.
|
||||
|
||||
**And you cannot see the mask.** *(Built — see §6.)* The overlay on the canvas
|
||||
was [`overlay_rgba`](../ui/dr-ui/src/segmentation.rs) and nothing else — a
|
||||
was [`overlay_rgba`](../../ui/dr-ui/src/segmentation.rs) and nothing else — a
|
||||
CPU-built false-colour picture of *what the model detected*, at proxy
|
||||
resolution. It is not the layer's alpha: it knows nothing of the layer's
|
||||
feather, its falloff, its morphology, its invert, or its opacity. Nobody can
|
||||
@@ -234,7 +234,7 @@ writes with one part is byte-identical to what it writes today**:
|
||||
3. `stroke = …` lines keep their position and their meaning. The mode token
|
||||
gains `push`, and `cling` is a fourth number written only when non-zero.
|
||||
An older build meeting either drops *that stroke and only that stroke*,
|
||||
which is the rule [`parse_stroke`](../core/dr-pipeline/src/sidecar.rs)
|
||||
which is the rule [`parse_stroke`](../../core/dr-pipeline/src/sidecar.rs)
|
||||
already documents and already implements.
|
||||
|
||||
**Merge (FR-NC-9).** A part is a block with an id, so two devices that added
|
||||
@@ -272,7 +272,7 @@ The set operations are already expressible in fixed-function blending over
|
||||
| Intersect | `Zero`, `Src`, `Add` | `dst · src` | M2 |
|
||||
|
||||
The middle row is `brush_erase`, already constructed in
|
||||
[`MaskPass::new`](../core/dr-gpu/src/mask.rs). The other two are the same
|
||||
[`MaskPass::new`](../../core/dr-gpu/src/mask.rs). The other two are the same
|
||||
three vertices with a different `BlendState`, and nothing is read back.
|
||||
|
||||
**One correction to the first draft of this section, found in the building.**
|
||||
@@ -289,7 +289,7 @@ the first time any layer has more than one part) and blended from there. A
|
||||
layer of one part still takes the old path exactly — straight into its slice,
|
||||
no scratch, no combine pass — which is what keeps every existing mask
|
||||
rendering as it did. `an_erase_stroke_holes_its_own_part_and_not_the_mask` in
|
||||
[`local_adjustments.rs`](../core/dr-gpu/tests/local_adjustments.rs) is the test
|
||||
[`local_adjustments.rs`](../../core/dr-gpu/tests/local_adjustments.rs) is the test
|
||||
that holds this in place.
|
||||
|
||||
`Max` blending on `r8unorm` is core WGPU and universally supported on the
|
||||
@@ -342,7 +342,7 @@ Three honest costs:
|
||||
|
||||
### 5.4 Painting has to be incremental
|
||||
|
||||
Today [`MaskPass::render`](../core/dr-gpu/src/mask.rs) clears each slice and
|
||||
Today [`MaskPass::render`](../../core/dr-gpu/src/mask.rs) clears each slice and
|
||||
redraws every stroke of the layer. That is right when a shape changes and
|
||||
wrong while a finger is down: at 120 reports a second, a layer holding 4096
|
||||
points redraws all of them per dab, and the cost of a stroke grows as it is
|
||||
@@ -359,14 +359,14 @@ changing falls back to the full rebuild it does now.
|
||||
|
||||
This is required, not an optimisation to schedule later: it is what decides
|
||||
whether painting is usable on the phone, and it is the specific failure
|
||||
[`mask.rs`'s module docs](../core/dr-pipeline/src/mask.rs) say this whole
|
||||
[`mask.rs`'s module docs](../../core/dr-pipeline/src/mask.rs) say this whole
|
||||
design exists to avoid.
|
||||
|
||||
### 5.5 Distance fields become per part
|
||||
|
||||
[`SubjectMasks`](../core/dr-gpu/src/mask.rs) uploads one signed distance field
|
||||
[`SubjectMasks`](../../core/dr-gpu/src/mask.rs) uploads one signed distance field
|
||||
**per active layer, in stack order**, and
|
||||
[`DevelopSession`](../ui/dr-ui/src/develop.rs) builds them on the same
|
||||
[`DevelopSession`](../../ui/dr-ui/src/develop.rs) builds them on the same
|
||||
indexing. With parts, a field belongs to the part that shaped it: the upload
|
||||
becomes one field per *model-backed part*, flattened in `(layer, part)` order,
|
||||
and the rasteriser indexes it by a running counter rather than by `slot`.
|
||||
@@ -540,7 +540,7 @@ layer's.
|
||||
or painted. One code path, three joins.
|
||||
|
||||
**Folding two layers into one** uses the multi-selection
|
||||
[`masks_ui.rs`](../ui/dr-ui/src/masks_ui.rs) already supports: with two layers
|
||||
[`masks_ui.rs`](../../ui/dr-ui/src/masks_ui.rs) already supports: with two layers
|
||||
selected, "Combine" appends the second's parts to the first and removes it.
|
||||
Offered only when the second layer's adjustments are neutral, and otherwise
|
||||
offered with a warning that names what will be lost — quietly discarding an
|
||||
@@ -560,7 +560,7 @@ who sets a small eraser expects it to still be small the next time they erase.
|
||||
### 7.4 Gestures
|
||||
|
||||
Each of these needs a `GESTURE:` block beside its implementation — that is the
|
||||
only place [gestures.md](gestures.md) can be written from.
|
||||
only place [gestures.md](../gestures.md) can be written from.
|
||||
|
||||
| Gesture | Touch | Pointer | Keyboard |
|
||||
|---------|-------|---------|----------|
|
||||
@@ -611,7 +611,7 @@ removing or re-joining a part.
|
||||
The mask stack is already snapshotted per step and shared by `Arc` when a step
|
||||
does not touch it, so the cost of an undoable stroke is a clone of one layer's
|
||||
parts, not of the picture. New keys in
|
||||
[`labels.rs`](../ui/dr-ui/src/labels.rs):
|
||||
[`labels.rs`](../../ui/dr-ui/src/labels.rs):
|
||||
|
||||
```
|
||||
history.mask_painted "Paint Mask"
|
||||
@@ -196,7 +196,7 @@ inside `#[cfg(test)]` — `LocalStorage::open` refuses it, and the test that pro
|
||||
`dr_plat::imports_supported`, which *returns false on Android* and whose own documentation says it
|
||||
"stops being false when a SAF implementation lands". The second tag documented the absence of the
|
||||
thing it was counted as evidence for. Both have been removed; this is the "plumbing a future feature
|
||||
would use" case [CONTRIBUTING.md](../CONTRIBUTING.md) and [code-health.md CH-4](code-health.md) both
|
||||
would use" case [CONTRIBUTING.md](../../CONTRIBUTING.md) and [code-health.md CH-4](code-health.md) both
|
||||
warn about. Android reaches a library through a Nextcloud account or a folder, over paths, like the
|
||||
desktop.
|
||||
|
||||
@@ -355,10 +355,10 @@ synthetic 50k catalog, run per commit, where **"a regression beyond a stated tol
|
||||
failure, not a notification."** For most of this project's life it did not exist — no `benches/`, no
|
||||
criterion, no synthetic catalog, and three CI workflows that between them measured nothing.
|
||||
|
||||
**It exists now, for everything that does not need a frame.** [`tools/bench`](../tools/bench) builds
|
||||
**It exists now, for everything that does not need a frame.** [`tools/bench`](../../tools/bench) builds
|
||||
a deterministic 50,000-row catalog over a pool of a dozen real files, measures against it, and fails
|
||||
the build on a violated budget or a drift past tolerance;
|
||||
[`.gitea/workflows/benchmark.yml`](../.gitea/workflows/benchmark.yml) runs it on every push, and
|
||||
[`.gitea/workflows/benchmark.yml`](../../.gitea/workflows/benchmark.yml) runs it on every push, and
|
||||
[benchmarks.md](benchmarks.md) is the account of what it does and does not cover. **NFR-P1** and
|
||||
**NFR-P3** are now genuinely gated, and R2's "catalog opens in under 2s" clause with them.
|
||||
|
||||
@@ -404,7 +404,7 @@ tag anything against.
|
||||
NFR-OPS-1 was covered by tags that were real rather than fixtures, which is the worse case of the
|
||||
two: one on `compute_coverage` and one on the gesture extractor, both on the traceability tool. A
|
||||
coverage calculation and a documentation generator are not diagnostics under any reading, so both
|
||||
tags were removed. It is the case [CONTRIBUTING.md](../CONTRIBUTING.md) warns about in its own words:
|
||||
tags were removed. It is the case [CONTRIBUTING.md](../../CONTRIBUTING.md) warns about in its own words:
|
||||
a tag proves a tag exists. The requirement has since been built where it says: the rotating,
|
||||
size-capped log and its redaction in `platform/dr-plat/src/diagnostics.rs` (2026-08-30), and the
|
||||
bundle in `diagnostics/bundle.rs` (2026-09-12) — the log, the crash records, the version, the schema
|
||||
@@ -179,7 +179,7 @@ exports the network alone at 768×1024 — thirteen operator types, all
|
||||
standard: `Conv`, `InstanceNormalization`, `AveragePool`, `Resize`, `Slice`,
|
||||
`Transpose`, `Reshape`, `Concat`, `Add`, `Relu`, `Sigmoid`, `ReduceMean`,
|
||||
`Unsqueeze` — and
|
||||
[`examples/onnx_probe.rs`](../core/dr-segment/examples/onnx_probe.rs) loads
|
||||
[`examples/onnx_probe.rs`](../../core/dr-segment/examples/onnx_probe.rs) loads
|
||||
the 2.8 MB file through the app's own `ort`-over-tract backend with nothing
|
||||
unsupported, in 28 ms, and runs it in **~300 ms on the reference desktop's
|
||||
CPU**. The weights ship as `models/keypoints/xfeat-1024.onnx`, recorded in
|
||||
@@ -255,7 +255,7 @@ Two containers were candidates and S15.1 decided, on 2026-09-19:
|
||||
the existing encoder with a different sample type, and nothing about it is
|
||||
uncertain.
|
||||
|
||||
**Linear DNG.** [`examples/linear_dng.rs`](../core/dr-decode/examples/linear_dng.rs)
|
||||
**Linear DNG.** [`examples/linear_dng.rs`](../../core/dr-decode/examples/linear_dng.rs)
|
||||
hand-rolls a 64 × 48 `LinearRaw` DNG — one IFD, uncompressed 16-bit RGB,
|
||||
`DNGVersion`, `ColorMatrix1`, `AsShotNeutral`, `CalibrationIlluminant1` — and
|
||||
rawler 0.7 reads it back: `cpp 3`, the samples interleaved as written, the
|
||||
@@ -2140,7 +2140,7 @@ Rationale, evidence, and the eliminated alternatives are recorded in
|
||||
|---|---|---|
|
||||
| D1 | Language and UI framework | Rust + Slint, rendering through wgpu |
|
||||
| D2 | RAW decoder | rawler; LibRaw fallback behind a trait |
|
||||
| D3 | First milestone | Delivered — [milestone-v0.1.md](milestone-v0.1.md), closed 2026-08-30 |
|
||||
| D3 | First milestone | Delivered — [milestone-v0.1.md](archive/milestone-v0.1.md), closed 2026-08-30 |
|
||||
| D4 | Nextcloud sync mechanism | ETag pruning, chunked upload v2, Login Flow v2 |
|
||||
| D5 | Colour management | lcms2 + GPU-side matrix/LUT transforms |
|
||||
| D6 | Shader authoring | Hand-written WGSL |
|
||||
@@ -21,11 +21,11 @@ one in its class and the workflow still breaks at the first frame with a mark on
|
||||
the sky.
|
||||
|
||||
It is also, unusually, a feature whose cost has already been paid twice over.
|
||||
The neighbourhood stage exists ([`crate::detail`](../core/dr-pipeline/src/detail.rs)),
|
||||
The neighbourhood stage exists ([`crate::detail`](../../core/dr-pipeline/src/detail.rs)),
|
||||
the convention for storing geometry in normalised source coordinates exists
|
||||
([`mask.rs`](../core/dr-pipeline/src/mask.rs)), the canvas-drag pattern exists
|
||||
([`gradient.rs`](../ui/dr-ui/src/gradient.rs)), and the merge-by-id rule exists
|
||||
([`sidecar.rs`](../core/dr-pipeline/src/sidecar.rs)). What is genuinely new is
|
||||
([`mask.rs`](../../core/dr-pipeline/src/mask.rs)), the canvas-drag pattern exists
|
||||
([`gradient.rs`](../../ui/dr-ui/src/gradient.rs)), and the merge-by-id rule exists
|
||||
([`sidecar.rs`](../../core/dr-pipeline/src/sidecar.rs)). What is genuinely new is
|
||||
small and is named in §3.
|
||||
|
||||
## 2. Non-goals
|
||||
File diff suppressed because one or more lines are too long
@@ -2,7 +2,7 @@
|
||||
|
||||
TRACES: FR-UI-1 | FR-UI-3 | FR-UI-5 | FR-DEV-3a | FR-DEV-3c
|
||||
|
||||
Successor to `ui-refinement.md`, which asked how the interface should *look*.
|
||||
Successor to [`ui-refinement.md`](archive/ui-refinement.md), which asked how the interface should *look*.
|
||||
This asks how someone finds anything in it. The two are sequenced together at
|
||||
the end.
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
TRACES: FR-UI-1 | FR-UI-6 | FR-UI-8 | FR-DEV-3a | NFR-P9
|
||||
|
||||
**Status:** Draft · 2026-08-09
|
||||
**Companion to:** [architecture.md](architecture.md) §4.3a, [ui-refinement.md](ui-refinement.md)
|
||||
**Companion to:** [architecture.md](architecture.md) §4.3a, [ui-refinement.md](archive/ui-refinement.md)
|
||||
|
||||
## Why
|
||||
|
||||
@@ -11,8 +11,8 @@ and — because there is no Windows hardware on the runner — exactly how much
|
||||
verified before a person double-clicks it.
|
||||
|
||||
**Written as a spec; §10 is the report.** Every step of §9 has since been run —
|
||||
[`docker/windows/`](../docker/windows/) is the container, [`packaging/windows/darkroom.nsi`](../packaging/windows/darkroom.nsi)
|
||||
the installer, [`.gitea/workflows/windows-image.yml`](../.gitea/workflows/windows-image.yml) and
|
||||
[`docker/windows/`](../../docker/windows/) is the container, [`packaging/windows/darkroom.nsi`](../../packaging/windows/darkroom.nsi)
|
||||
the installer, [`.gitea/workflows/windows-image.yml`](../../.gitea/workflows/windows-image.yml) and
|
||||
the `windows` job in `build-and-test.yml` the CI leg, and §6's gate passes through row 4 under
|
||||
Wine. Four claims in the first draft were wrong and are corrected in place with a note; §10 lists
|
||||
them. Where a claim still rests on reading rather than running, it says so.
|
||||
@@ -105,7 +105,7 @@ keeps DX12 out here. It is one flag away if that turns out to be wrong.
|
||||
|
||||
The same shape as the Android leg: a job container built from a Dockerfile in the tree and pushed
|
||||
to the Gitea registry, tagged by the tree id of its directory so an unrelated push reuses it
|
||||
([`android-image.yml`](../.gitea/workflows/android-image.yml) already does this and the comment
|
||||
([`android-image.yml`](../../.gitea/workflows/android-image.yml) already does this and the comment
|
||||
there explains why).
|
||||
|
||||
```
|
||||
@@ -157,7 +157,7 @@ Ordered by what blocks a first sign-in. **All four are done**; each item says ho
|
||||
`is_available`'s probe always succeeds on Windows, which is correct — Credential Manager is
|
||||
always present, so FR-NC-2's degraded mode does not arise.
|
||||
|
||||
2. **Paths.** *Done* — [`platform/dr-plat/src/dirs.rs`](../platform/dr-plat/src/dirs.rs). FR-PLAT-LIN-1
|
||||
2. **Paths.** *Done* — [`platform/dr-plat/src/dirs.rs`](../../platform/dr-plat/src/dirs.rs). FR-PLAT-LIN-1
|
||||
says XDG, and the code said it in five places by reading `XDG_*_HOME` and falling back to
|
||||
`$HOME/.local/...`. On Windows `HOME` is normally unset, so every one of these degraded to a
|
||||
relative path from the working directory — which for a Start Menu launch is
|
||||
@@ -198,15 +198,15 @@ Ordered by what blocks a first sign-in. **All four are done**; each item says ho
|
||||
|
||||
6. **The executable's identity.** *Done.* Windows takes the icon and the version block from a
|
||||
resource compiled into the `.exe`, not from a `.desktop` file.
|
||||
[`apps/darkroom-desktop/build.rs`](../apps/darkroom-desktop/build.rs) uses `winresource`
|
||||
[`apps/darkroom-desktop/build.rs`](../../apps/darkroom-desktop/build.rs) uses `winresource`
|
||||
(which invokes MinGW's `windres` when cross-compiling) to embed
|
||||
[`ui/dr-ui/ui/app-icon.png`](../ui/dr-ui/ui/app-icon.png) — wrapped into an `.ico` in
|
||||
[`ui/dr-ui/ui/app-icon.png`](../../ui/dr-ui/ui/app-icon.png) — wrapped into an `.ico` in
|
||||
`OUT_DIR` at build time, since an ICO entry may be a PNG, so no generated binary is committed —
|
||||
plus the version from `CARGO_PKG_VERSION` and the product name. The script returns before
|
||||
touching the crate on every other target, and `winresource` is an unconditional
|
||||
build-dependency because **a `cfg(windows)` on a build-dependency is evaluated against the
|
||||
host**, which is Linux. This is the fifth place the identifier lives, and
|
||||
[`tools/set-version.sh`](../tools/set-version.sh) does not need to learn it: the resource
|
||||
[`tools/set-version.sh`](../../tools/set-version.sh) does not need to learn it: the resource
|
||||
reads the version cargo already knows. The same commit made the release binary a GUI-subsystem
|
||||
executable (`windows_subsystem = "windows"`), or Windows keeps a console window open behind
|
||||
the application.
|
||||
@@ -257,7 +257,7 @@ and running them means Wine. §6 does that for exactly one binary, deliberately.
|
||||
|
||||
## 5. The installer
|
||||
|
||||
[`packaging/windows/darkroom.nsi`](../packaging/windows/darkroom.nsi), compiled by `makensis` on
|
||||
[`packaging/windows/darkroom.nsi`](../../packaging/windows/darkroom.nsi), compiled by `makensis` on
|
||||
the runner into `DarkRoom-<version>-x86_64-setup.exe`. `package.sh` passes the version in
|
||||
(`/DVERSION=…`, from `tools/set-version.sh`'s single source, the workspace `Cargo.toml`) and refuses
|
||||
to run if any `models/face/*.onnx` is smaller than 100 KB — the LFS-pointer guard every other
|
||||
@@ -370,7 +370,7 @@ say which of these were checked and on what.
|
||||
|
||||
## 7. The CI job
|
||||
|
||||
A fourth leg of [`build-and-test.yml`](../.gitea/workflows/build-and-test.yml), beside desktop,
|
||||
A fourth leg of [`build-and-test.yml`](../../.gitea/workflows/build-and-test.yml), beside desktop,
|
||||
Android and traceability:
|
||||
|
||||
```yaml
|
||||
@@ -5,7 +5,7 @@ this page was captured from the desktop build driving itself — nothing is a
|
||||
mock-up, and nothing has been retouched outside DarkRoom. Where a feature is
|
||||
better seen moving, it moves.
|
||||
|
||||
The requirements behind each feature are in [requirements.md](../requirements.md);
|
||||
The requirements behind each feature are in [requirements.md](../dev/requirements.md);
|
||||
the reasoning is in the design documents linked from each section. This page
|
||||
is only about what you see.
|
||||
|
||||
@@ -209,19 +209,19 @@ sidecars, and a diagnostics bundle for a bug report.
|
||||
Face detection and identity run over the library and group faces by person;
|
||||
the `Identity` page is where suggestions are confirmed, rejected and split,
|
||||
and `People` on the filter bar narrows the grid to someone. Not pictured
|
||||
here, for the obvious reason — [faces.md](../faces.md) has the design.
|
||||
here, for the obvious reason — [faces.md](../dev/faces.md) has the design.
|
||||
|
||||
## Where things are written down
|
||||
|
||||
| Feature | Design |
|
||||
|---|---|
|
||||
| Local masks and segmentation | [segmentation.md](../segmentation.md), [mask-editing.md](../mask-editing.md) |
|
||||
| Repair | [spot-removal.md](../spot-removal.md) |
|
||||
| Panorama | [panorama.md](../panorama.md) |
|
||||
| Faces and identity | [faces.md](../faces.md) |
|
||||
| Local masks and segmentation | [segmentation.md](../dev/segmentation.md), [mask-editing.md](../dev/mask-editing.md) |
|
||||
| Repair | [spot-removal.md](../dev/spot-removal.md) |
|
||||
| Panorama | [panorama.md](../dev/panorama.md) |
|
||||
| Faces and identity | [faces.md](../dev/faces.md) |
|
||||
| Gestures, generated from the code | [gestures.md](../gestures.md) |
|
||||
| Navigation and layout | [ui-navigation.md](../ui-navigation.md) |
|
||||
| Sync and storage | [storage.md](../storage.md) |
|
||||
| Navigation and layout | [ui-navigation.md](../dev/ui-navigation.md) |
|
||||
| Sync and storage | [storage.md](../dev/storage.md) |
|
||||
|
||||
## How this page is made
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user