From 4af3b93dfa14eb1debf6841bb7c48beed38b9ec6 Mon Sep 17 00:00:00 2001 From: Duncan Tourolle Date: Sun, 30 Aug 2026 18:49:08 +0200 Subject: [PATCH] Index faces from the native render, not from a preview of it Implements the FR-CULL-8 written two commits ago. The sweep fetched the JPEG preview embedded in each RAW and used that one buffer for both detection and the crop; it now fetches the original, renders it through the same path export uses, reduces that for the detector, and warps the crop back out of the native frame. Three pieces, and each exists for a reason worth stating. dr_face::Pixels lets the warp sample 8-bit RGBA directly. A 24 MP native frame is 96 MB as RGBA and 288 MB converted to the f32 RGB align.rs was written against, and the warp reads about forty thousand pixels out of it. Converting the whole frame to sample 0.2% of it is NFR-RES-2's budget spent on a copy, per image, for a whole library. The variant costs one branch per sample and a test asserts both layouts produce identical crops. The detector gets a box-filtered reduction to 1600px, not the native frame and not a point-sampled one. Averaging rather than sampling because the detector's job is finding small faces and decimation is precisely the operation that removes them: at 4x, fifteen of every sixteen pixels are discarded and a 40px face survives or not depending on where it falls relative to the sample grid. 1600 rather than 640 leaves the letterbox a mild 2.5x rather than a 9x, and bounds the f32 buffer at 20 MB. Landmarks come back in the reduction's coordinates and are scaled to native in one place before any crop pixel is read. This is the failure mode that would not announce itself -- unscaled landmarks put every crop near the top-left corner, which yields faces of something else, cleanly embedded and confidently clustered. The sweep fetches SWEEP_LANES-wide and renders sequentially. Not a placeholder for a parallel version: there is one GPU, so concurrent renders queue on it regardless, and each materialises a native frame. Overlapping them would multiply the one allocation that threatens the memory budget while buying parallelism that does not exist. The chunk drops from 96 to 6 for the same reason -- 96 held 8 MB previews, this holds whole RAWs. The stored edit is deliberately not applied, which is where this departs from export::render_from_library. Face geometry is normalised to the frame, so indexing a cropped render would record boxes against a frame that changes whenever the user changes their mind, and every stored box would quietly become wrong. Orientation is applied: that is a fact about the file rather than an edit. examples/face_native.rs renders one file and indexes it both ways, so the claim behind all of this can be checked against photographs rather than re-read out of the catalog it came from. Co-Authored-By: Claude Opus 5 (1M context) --- core/dr-face/src/align.rs | 101 ++++++- core/dr-face/src/lib.rs | 2 +- docs/traceability.md | 106 +++---- ui/dr-ui/examples/face_native.rs | 144 +++++++++ ui/dr-ui/src/faces.rs | 310 ++++++++++++++++++++ ui/dr-ui/src/identity_ui.rs | 15 + ui/dr-ui/src/lib.rs | 6 + ui/dr-ui/src/library.rs | 484 ++++++++++++++++++------------- 8 files changed, 898 insertions(+), 270 deletions(-) create mode 100644 ui/dr-ui/examples/face_native.rs diff --git a/core/dr-face/src/align.rs b/core/dr-face/src/align.rs index 8ef84c0..e7c6cc5 100644 --- a/core/dr-face/src/align.rs +++ b/core/dr-face/src/align.rs @@ -285,7 +285,67 @@ pub fn warp( height: usize, landmarks: &[(f32, f32); 5], ) -> Option { - if rgb.len() != width * height * 3 { + warp_pixels(Pixels::RgbF32(rgb), width, height, landmarks) +} + +/// TRACES: FR-CULL-8 +/// What the warp may sample, in whichever layout the caller already holds. +/// +/// # Why the 8-bit variant exists +/// +/// FR-CULL-8 requires the crop to come from the **native** render, and a native +/// render is large: a 24 MP frame is 96 MB as `RGBA8` and 288 MB converted to +/// the `f32` RGB this module was originally written against. Converting the +/// whole frame to sample 112×112 from it is three hundred megabytes allocated +/// to read about forty thousand pixels, per image, on a pass that runs over a +/// whole library — and on Android it is NFR-RES-2's budget spent outright. +/// +/// So the warp reads whatever the caller has instead. It touches so few pixels +/// that the per-sample conversion is free, and the buffer never has to be +/// duplicated in another layout. +#[derive(Debug, Clone, Copy)] +pub enum Pixels<'a> { + /// Tightly packed `f32` RGB in `0.0..=1.0`, row-major. + RgbF32(&'a [f32]), + /// Tightly packed 8-bit RGBA, row-major. Alpha is ignored: a face crop has + /// no use for it and carrying it would change what the embedder receives. + Rgba8(&'a [u8]), +} + +impl Pixels<'_> { + /// Whether the buffer is the size `width × height` implies. + fn fits(&self, width: usize, height: usize) -> bool { + match self { + Pixels::RgbF32(v) => v.len() == width * height * 3, + Pixels::Rgba8(v) => v.len() == width * height * 4, + } + } + + /// One channel of one pixel, as `0.0..=1.0`. Outside the buffer reads black. + /// + /// Public because the face *crop* stored for the People screen is cut from + /// the same buffer by the same caller, and it should not need a second + /// copy of this to do it. + pub fn channel(&self, w: usize, h: usize, x: isize, y: isize, c: usize) -> f32 { + if x < 0 || y < 0 || x >= w as isize || y >= h as isize { + return 0.0; + } + let i = y as usize * w + x as usize; + match self { + Pixels::RgbF32(v) => v[i * 3 + c], + Pixels::Rgba8(v) => v[i * 4 + c] as f32 / 255.0, + } + } +} + +/// [`warp`], over any layout [`Pixels`] describes. +pub fn warp_pixels( + px: Pixels<'_>, + width: usize, + height: usize, + landmarks: &[(f32, f32); 5], +) -> Option { + if !px.fits(width, height) { return None; } let m = fit_similarity(landmarks, &ARCFACE_TEMPLATE)?; @@ -300,7 +360,7 @@ pub fn warp( let (x, y) = m.invert(u as f32 + 0.5, v as f32 + 0.5); let (x, y) = (x - 0.5, y - 0.5); let out = (v * e + u) * 3; - sample_bilinear(rgb, width, height, x, y, &mut pixels[out..out + 3]); + sample_bilinear(px, width, height, x, y, &mut pixels[out..out + 3]); } } @@ -312,7 +372,7 @@ pub fn warp( }) } -fn sample_bilinear(rgb: &[f32], w: usize, h: usize, x: f32, y: f32, out: &mut [f32]) { +fn sample_bilinear(px: Pixels<'_>, w: usize, h: usize, x: f32, y: f32, out: &mut [f32]) { let x0 = x.floor(); let y0 = y.floor(); let fx = x - x0; @@ -321,13 +381,7 @@ fn sample_bilinear(rgb: &[f32], w: usize, h: usize, x: f32, y: f32, out: &mut [f let y0 = y0 as isize; for (c, o) in out.iter_mut().enumerate() { - let get = |xi: isize, yi: isize| -> f32 { - if xi < 0 || yi < 0 || xi >= w as isize || yi >= h as isize { - 0.0 - } else { - rgb[(yi as usize * w + xi as usize) * 3 + c] - } - }; + let get = |xi: isize, yi: isize| -> f32 { px.channel(w, h, xi, yi, c) }; let top = get(x0, y0) * (1.0 - fx) + get(x0 + 1, y0) * fx; let bot = get(x0, y0 + 1) * (1.0 - fx) + get(x0 + 1, y0 + 1) * fx; *o = top * (1.0 - fy) + bot * fy; @@ -508,6 +562,33 @@ mod tests { /// against a bright sky is low-contrast, and a raw Laplacian variance would /// reject it as blurred — which would quietly throw away every backlit /// portrait in the library. + #[test] + fn both_pixel_layouts_warp_to_the_same_crop() { + // The 8-bit path exists so a native render need not be converted to + // f32 whole; it has to agree with the path it replaces to within the + // quantisation it introduces. + let (w, h) = (64usize, 64usize); + let mut rgba = vec![0u8; w * h * 4]; + let mut rgb = vec![0.0f32; w * h * 3]; + for y in 0..h { + for x in 0..w { + let v = [(x * 4 % 256) as u8, (y * 4 % 256) as u8, ((x + y) % 256) as u8]; + for c in 0..3 { + rgba[(y * w + x) * 4 + c] = v[c]; + rgb[(y * w + x) * 3 + c] = v[c] as f32 / 255.0; + } + rgba[(y * w + x) * 4 + 3] = 255; + } + } + let lm = shifted_scaled(0.35, 32.0, 32.0, 0.2); + let a = warp_pixels(Pixels::RgbF32(&rgb), w, h, &lm).unwrap(); + let b = warp_pixels(Pixels::Rgba8(&rgba), w, h, &lm).unwrap(); + assert_eq!(a.source_px(), b.source_px()); + for (x, y) in a.pixels().iter().zip(b.pixels()) { + assert!((x - y).abs() < 1e-6, "{x} vs {y}"); + } + } + #[test] fn sharpness_survives_the_contrast_being_halved() { let edge = 200; diff --git a/core/dr-face/src/lib.rs b/core/dr-face/src/lib.rs index 9e16b98..96e95b5 100644 --- a/core/dr-face/src/lib.rs +++ b/core/dr-face/src/lib.rs @@ -64,7 +64,7 @@ pub mod neighbours; /// from the wrong tier. pub const MIN_CROP_EDGE: u32 = 1025; -pub use align::{warp, Aligned112, Similarity, ALIGNED_EDGE, ARCFACE_TEMPLATE}; +pub use align::{warp, warp_pixels, Aligned112, Pixels, Similarity, ALIGNED_EDGE, ARCFACE_TEMPLATE}; pub use assign::{identity_shares, RIVAL_FLOOR, TOP_MATCHES}; pub use calibrate::{Calibration, Pairs, ReliabilityBand}; pub use cluster::{ diff --git a/docs/traceability.md b/docs/traceability.md index d2a04b4..1cfb58e 100644 --- a/docs/traceability.md +++ b/docs/traceability.md @@ -9,18 +9,18 @@ Denominators are parsed from [`requirements.md`](requirements.md) at run time, n | Metric | Value | |---|---| -| Source files scanned | 329 | -| TRACES tags found | 1029 | +| Source files scanned | 330 | +| TRACES tags found | 1039 | | Requirements defined | 179 | -| Requirements covered | 125 | -| **Coverage** | **69.8%** (125/179) | +| Requirements covered | 126 | +| **Coverage** | **70.4%** (126/179) | ### By type | Type | Covered | Defined | |---|---|---| | FR | 93 | 124 | -| NFR | 29 | 49 | +| NFR | 30 | 49 | | R | 3 | 6 | ## Orphan tags @@ -34,111 +34,111 @@ _None._ | ID | Tagged in | |---|---| | FR-CAT-1 | [`core/dr-catalog/src/scan.rs:1`](../core/dr-catalog/src/scan.rs#L1), [`core/dr-catalog/src/walk.rs:109`](../core/dr-catalog/src/walk.rs#L109), [`core/dr-catalog/src/walk.rs:162`](../core/dr-catalog/src/walk.rs#L162), [`core/dr-catalog/src/walk.rs:1`](../core/dr-catalog/src/walk.rs#L1), [`core/dr-sync/src/scan.rs:93`](../core/dr-sync/src/scan.rs#L93), [`core/dr-types/src/lib.rs:201`](../core/dr-types/src/lib.rs#L201), [`core/dr-types/src/lib.rs:270`](../core/dr-types/src/lib.rs#L270), [`core/dr-types/src/lib.rs:303`](../core/dr-types/src/lib.rs#L303), [`platform/dr-plat/src/storage.rs:1`](../platform/dr-plat/src/storage.rs#L1), [`platform/dr-plat/src/storage.rs:216`](../platform/dr-plat/src/storage.rs#L216), [`ui/dr-ui/src/activity.rs:1`](../ui/dr-ui/src/activity.rs#L1), [`ui/dr-ui/src/library.rs:1`](../ui/dr-ui/src/library.rs#L1) | -| FR-CAT-10 | [`core/dr-ingest/src/layout.rs:1`](../core/dr-ingest/src/layout.rs#L1), [`core/dr-ingest/src/lib.rs:1`](../core/dr-ingest/src/lib.rs#L1), [`core/dr-ingest/src/lib.rs:733`](../core/dr-ingest/src/lib.rs#L733), [`core/dr-types/src/settings.rs:206`](../core/dr-types/src/settings.rs#L206), [`platform/dr-plat/src/storage.rs:287`](../platform/dr-plat/src/storage.rs#L287), [`platform/dr-plat/src/storage.rs:586`](../platform/dr-plat/src/storage.rs#L586), [`platform/dr-plat/src/volumes.rs:1`](../platform/dr-plat/src/volumes.rs#L1), [`platform/dr-plat/src/volumes.rs:62`](../platform/dr-plat/src/volumes.rs#L62), [`ui/dr-ui/src/import.rs:1`](../ui/dr-ui/src/import.rs#L1), [`ui/dr-ui/src/import.rs:336`](../ui/dr-ui/src/import.rs#L336), [`ui/dr-ui/src/import_ui.rs:1`](../ui/dr-ui/src/import_ui.rs#L1), [`ui/dr-ui/src/lib.rs:1254`](../ui/dr-ui/src/lib.rs#L1254), [`ui/dr-ui/ui/import.slint:5`](../ui/dr-ui/ui/import.slint#L5), [`ui/dr-ui/ui/library.slint:1140`](../ui/dr-ui/ui/library.slint#L1140), [`ui/dr-ui/ui/library.slint:870`](../ui/dr-ui/ui/library.slint#L870), [`ui/dr-ui/ui/library.slint:924`](../ui/dr-ui/ui/library.slint#L924) | -| FR-CAT-11 | [`core/dr-catalog/src/dedup.rs:1`](../core/dr-catalog/src/dedup.rs#L1), [`core/dr-ingest/src/lib.rs:1`](../core/dr-ingest/src/lib.rs#L1), [`core/dr-ingest/src/lib.rs:392`](../core/dr-ingest/src/lib.rs#L392), [`core/dr-sync/src/upload.rs:40`](../core/dr-sync/src/upload.rs#L40), [`ui/dr-ui/src/import.rs:1`](../ui/dr-ui/src/import.rs#L1), [`ui/dr-ui/src/import_ui.rs:1`](../ui/dr-ui/src/import_ui.rs#L1), [`ui/dr-ui/src/lib.rs:1254`](../ui/dr-ui/src/lib.rs#L1254), [`ui/dr-ui/src/library.rs:175`](../ui/dr-ui/src/library.rs#L175), [`ui/dr-ui/src/library.rs:2491`](../ui/dr-ui/src/library.rs#L2491), [`ui/dr-ui/ui/import.slint:5`](../ui/dr-ui/ui/import.slint#L5) | +| FR-CAT-10 | [`core/dr-ingest/src/layout.rs:1`](../core/dr-ingest/src/layout.rs#L1), [`core/dr-ingest/src/lib.rs:1`](../core/dr-ingest/src/lib.rs#L1), [`core/dr-ingest/src/lib.rs:733`](../core/dr-ingest/src/lib.rs#L733), [`core/dr-types/src/settings.rs:206`](../core/dr-types/src/settings.rs#L206), [`platform/dr-plat/src/storage.rs:287`](../platform/dr-plat/src/storage.rs#L287), [`platform/dr-plat/src/storage.rs:586`](../platform/dr-plat/src/storage.rs#L586), [`platform/dr-plat/src/volumes.rs:1`](../platform/dr-plat/src/volumes.rs#L1), [`platform/dr-plat/src/volumes.rs:62`](../platform/dr-plat/src/volumes.rs#L62), [`ui/dr-ui/src/import.rs:1`](../ui/dr-ui/src/import.rs#L1), [`ui/dr-ui/src/import.rs:336`](../ui/dr-ui/src/import.rs#L336), [`ui/dr-ui/src/import_ui.rs:1`](../ui/dr-ui/src/import_ui.rs#L1), [`ui/dr-ui/src/lib.rs:1260`](../ui/dr-ui/src/lib.rs#L1260), [`ui/dr-ui/ui/import.slint:5`](../ui/dr-ui/ui/import.slint#L5), [`ui/dr-ui/ui/library.slint:1140`](../ui/dr-ui/ui/library.slint#L1140), [`ui/dr-ui/ui/library.slint:870`](../ui/dr-ui/ui/library.slint#L870), [`ui/dr-ui/ui/library.slint:924`](../ui/dr-ui/ui/library.slint#L924) | +| FR-CAT-11 | [`core/dr-catalog/src/dedup.rs:1`](../core/dr-catalog/src/dedup.rs#L1), [`core/dr-ingest/src/lib.rs:1`](../core/dr-ingest/src/lib.rs#L1), [`core/dr-ingest/src/lib.rs:392`](../core/dr-ingest/src/lib.rs#L392), [`core/dr-sync/src/upload.rs:40`](../core/dr-sync/src/upload.rs#L40), [`ui/dr-ui/src/import.rs:1`](../ui/dr-ui/src/import.rs#L1), [`ui/dr-ui/src/import_ui.rs:1`](../ui/dr-ui/src/import_ui.rs#L1), [`ui/dr-ui/src/lib.rs:1260`](../ui/dr-ui/src/lib.rs#L1260), [`ui/dr-ui/src/library.rs:175`](../ui/dr-ui/src/library.rs#L175), [`ui/dr-ui/src/library.rs:2491`](../ui/dr-ui/src/library.rs#L2491), [`ui/dr-ui/ui/import.slint:5`](../ui/dr-ui/ui/import.slint#L5) | | FR-CAT-12 | [`core/dr-pipeline/src/sidecar.rs:119`](../core/dr-pipeline/src/sidecar.rs#L119) | -| FR-CAT-15 | [`core/dr-catalog/src/schema.rs:753`](../core/dr-catalog/src/schema.rs#L753), [`core/dr-catalog/src/trash.rs:1`](../core/dr-catalog/src/trash.rs#L1), [`core/dr-sync-nextcloud/src/lib.rs:457`](../core/dr-sync-nextcloud/src/lib.rs#L457), [`core/dr-sync/src/lib.rs:135`](../core/dr-sync/src/lib.rs#L135), [`core/dr-sync/src/scan.rs:563`](../core/dr-sync/src/scan.rs#L563), [`core/dr-sync/src/scan.rs:57`](../core/dr-sync/src/scan.rs#L57), [`core/dr-thumbs/src/lib.rs:376`](../core/dr-thumbs/src/lib.rs#L376), [`ui/dr-ui/src/collections_ui.rs:1283`](../ui/dr-ui/src/collections_ui.rs#L1283), [`ui/dr-ui/src/collections_ui.rs:2189`](../ui/dr-ui/src/collections_ui.rs#L2189), [`ui/dr-ui/src/library.rs:175`](../ui/dr-ui/src/library.rs#L175), [`ui/dr-ui/src/library.rs:192`](../ui/dr-ui/src/library.rs#L192), [`ui/dr-ui/src/library.rs:241`](../ui/dr-ui/src/library.rs#L241), [`ui/dr-ui/src/library.rs:4045`](../ui/dr-ui/src/library.rs#L4045), [`ui/dr-ui/src/library.rs:4079`](../ui/dr-ui/src/library.rs#L4079), [`ui/dr-ui/src/library_ui.rs:195`](../ui/dr-ui/src/library_ui.rs#L195), [`ui/dr-ui/src/library_ui.rs:784`](../ui/dr-ui/src/library_ui.rs#L784), [`ui/dr-ui/src/trash.rs:1`](../ui/dr-ui/src/trash.rs#L1), [`ui/dr-ui/ui/collections.slint:621`](../ui/dr-ui/ui/collections.slint#L621) | +| FR-CAT-15 | [`core/dr-catalog/src/schema.rs:753`](../core/dr-catalog/src/schema.rs#L753), [`core/dr-catalog/src/trash.rs:1`](../core/dr-catalog/src/trash.rs#L1), [`core/dr-sync-nextcloud/src/lib.rs:457`](../core/dr-sync-nextcloud/src/lib.rs#L457), [`core/dr-sync/src/lib.rs:135`](../core/dr-sync/src/lib.rs#L135), [`core/dr-sync/src/scan.rs:563`](../core/dr-sync/src/scan.rs#L563), [`core/dr-sync/src/scan.rs:57`](../core/dr-sync/src/scan.rs#L57), [`core/dr-thumbs/src/lib.rs:376`](../core/dr-thumbs/src/lib.rs#L376), [`ui/dr-ui/src/collections_ui.rs:1283`](../ui/dr-ui/src/collections_ui.rs#L1283), [`ui/dr-ui/src/collections_ui.rs:2189`](../ui/dr-ui/src/collections_ui.rs#L2189), [`ui/dr-ui/src/library.rs:175`](../ui/dr-ui/src/library.rs#L175), [`ui/dr-ui/src/library.rs:192`](../ui/dr-ui/src/library.rs#L192), [`ui/dr-ui/src/library.rs:241`](../ui/dr-ui/src/library.rs#L241), [`ui/dr-ui/src/library.rs:4117`](../ui/dr-ui/src/library.rs#L4117), [`ui/dr-ui/src/library.rs:4151`](../ui/dr-ui/src/library.rs#L4151), [`ui/dr-ui/src/library_ui.rs:195`](../ui/dr-ui/src/library_ui.rs#L195), [`ui/dr-ui/src/library_ui.rs:784`](../ui/dr-ui/src/library_ui.rs#L784), [`ui/dr-ui/src/trash.rs:1`](../ui/dr-ui/src/trash.rs#L1), [`ui/dr-ui/ui/collections.slint:621`](../ui/dr-ui/ui/collections.slint#L621) | | FR-CAT-1a | [`core/dr-catalog/src/walk.rs:1`](../core/dr-catalog/src/walk.rs#L1), [`core/dr-types/src/lib.rs:54`](../core/dr-types/src/lib.rs#L54), [`platform/dr-plat/src/storage.rs:1`](../platform/dr-plat/src/storage.rs#L1), [`platform/dr-plat/src/storage.rs:216`](../platform/dr-plat/src/storage.rs#L216), [`platform/dr-plat/src/storage.rs:46`](../platform/dr-plat/src/storage.rs#L46) | | FR-CAT-2 | [`core/dr-catalog/src/lib.rs:1`](../core/dr-catalog/src/lib.rs#L1), [`core/dr-catalog/src/schema.rs:1`](../core/dr-catalog/src/schema.rs#L1) | -| FR-CAT-3 | [`core/dr-catalog/src/jobs.rs:1`](../core/dr-catalog/src/jobs.rs#L1), [`core/dr-catalog/src/walk.rs:66`](../core/dr-catalog/src/walk.rs#L66), [`core/dr-sync/src/scan.rs:69`](../core/dr-sync/src/scan.rs#L69), [`core/dr-thumbs/src/codec.rs:1`](../core/dr-thumbs/src/codec.rs#L1), [`core/dr-thumbs/src/lib.rs:1`](../core/dr-thumbs/src/lib.rs#L1), [`ui/dr-ui/src/derived_sync.rs:1`](../ui/dr-ui/src/derived_sync.rs#L1), [`ui/dr-ui/src/import.rs:463`](../ui/dr-ui/src/import.rs#L463), [`ui/dr-ui/src/import.rs:488`](../ui/dr-ui/src/import.rs#L488), [`ui/dr-ui/src/library.rs:2964`](../ui/dr-ui/src/library.rs#L2964), [`ui/dr-ui/src/library.rs:3488`](../ui/dr-ui/src/library.rs#L3488), [`ui/dr-ui/src/library_ui.rs:177`](../ui/dr-ui/src/library_ui.rs#L177), [`ui/dr-ui/src/library_ui.rs:4001`](../ui/dr-ui/src/library_ui.rs#L4001), [`ui/dr-ui/src/library_ui.rs:5021`](../ui/dr-ui/src/library_ui.rs#L5021), [`ui/dr-ui/ui/app.slint:412`](../ui/dr-ui/ui/app.slint#L412), [`ui/dr-ui/ui/settings.slint:387`](../ui/dr-ui/ui/settings.slint#L387), [`ui/dr-ui/ui/settings.slint:73`](../ui/dr-ui/ui/settings.slint#L73) | +| FR-CAT-3 | [`core/dr-catalog/src/jobs.rs:1`](../core/dr-catalog/src/jobs.rs#L1), [`core/dr-catalog/src/walk.rs:66`](../core/dr-catalog/src/walk.rs#L66), [`core/dr-sync/src/scan.rs:69`](../core/dr-sync/src/scan.rs#L69), [`core/dr-thumbs/src/codec.rs:1`](../core/dr-thumbs/src/codec.rs#L1), [`core/dr-thumbs/src/lib.rs:1`](../core/dr-thumbs/src/lib.rs#L1), [`ui/dr-ui/src/derived_sync.rs:1`](../ui/dr-ui/src/derived_sync.rs#L1), [`ui/dr-ui/src/import.rs:463`](../ui/dr-ui/src/import.rs#L463), [`ui/dr-ui/src/import.rs:488`](../ui/dr-ui/src/import.rs#L488), [`ui/dr-ui/src/library.rs:2964`](../ui/dr-ui/src/library.rs#L2964), [`ui/dr-ui/src/library.rs:3560`](../ui/dr-ui/src/library.rs#L3560), [`ui/dr-ui/src/library_ui.rs:177`](../ui/dr-ui/src/library_ui.rs#L177), [`ui/dr-ui/src/library_ui.rs:4001`](../ui/dr-ui/src/library_ui.rs#L4001), [`ui/dr-ui/src/library_ui.rs:5021`](../ui/dr-ui/src/library_ui.rs#L5021), [`ui/dr-ui/ui/app.slint:412`](../ui/dr-ui/ui/app.slint#L412), [`ui/dr-ui/ui/settings.slint:387`](../ui/dr-ui/ui/settings.slint#L387), [`ui/dr-ui/ui/settings.slint:73`](../ui/dr-ui/ui/settings.slint#L73) | | FR-CAT-4 | [`core/dr-catalog/src/lib.rs:1`](../core/dr-catalog/src/lib.rs#L1), [`core/dr-catalog/src/query.rs:1`](../core/dr-catalog/src/query.rs#L1), [`core/dr-catalog/src/schema.rs:337`](../core/dr-catalog/src/schema.rs#L337), [`ui/dr-ui/src/library.rs:1`](../ui/dr-ui/src/library.rs#L1), [`ui/dr-ui/src/library.rs:222`](../ui/dr-ui/src/library.rs#L222), [`ui/dr-ui/src/library_ui.rs:1`](../ui/dr-ui/src/library_ui.rs#L1) | -| FR-CAT-5 | [`core/dr-catalog/src/keywords.rs:1`](../core/dr-catalog/src/keywords.rs#L1), [`core/dr-catalog/src/merge.rs:1`](../core/dr-catalog/src/merge.rs#L1), [`core/dr-catalog/src/rating.rs:1`](../core/dr-catalog/src/rating.rs#L1), [`core/dr-catalog/src/schema.rs:1171`](../core/dr-catalog/src/schema.rs#L1171), [`core/dr-catalog/src/schema.rs:668`](../core/dr-catalog/src/schema.rs#L668), [`core/dr-decode/src/lib.rs:285`](../core/dr-decode/src/lib.rs#L285), [`core/dr-decode/src/lib.rs:404`](../core/dr-decode/src/lib.rs#L404), [`core/dr-pipeline/src/sidecar.rs:136`](../core/dr-pipeline/src/sidecar.rs#L136), [`ui/dr-ui/src/collections_ui.rs:1579`](../ui/dr-ui/src/collections_ui.rs#L1579), [`ui/dr-ui/src/collections_ui.rs:1685`](../ui/dr-ui/src/collections_ui.rs#L1685), [`ui/dr-ui/src/collections_ui.rs:1734`](../ui/dr-ui/src/collections_ui.rs#L1734), [`ui/dr-ui/src/collections_ui.rs:238`](../ui/dr-ui/src/collections_ui.rs#L238), [`ui/dr-ui/src/collections_ui.rs:335`](../ui/dr-ui/src/collections_ui.rs#L335), [`ui/dr-ui/src/collections_ui.rs:87`](../ui/dr-ui/src/collections_ui.rs#L87), [`ui/dr-ui/src/library.rs:4093`](../ui/dr-ui/src/library.rs#L4093), [`ui/dr-ui/src/library_ui.rs:659`](../ui/dr-ui/src/library_ui.rs#L659), [`ui/dr-ui/src/library_ui.rs:6953`](../ui/dr-ui/src/library_ui.rs#L6953), [`ui/dr-ui/src/library_ui.rs:6964`](../ui/dr-ui/src/library_ui.rs#L6964), [`ui/dr-ui/src/library_ui.rs:6977`](../ui/dr-ui/src/library_ui.rs#L6977), [`ui/dr-ui/src/library_ui.rs:6992`](../ui/dr-ui/src/library_ui.rs#L6992), [`ui/dr-ui/src/library_ui.rs:7001`](../ui/dr-ui/src/library_ui.rs#L7001), [`ui/dr-ui/ui/app.slint:333`](../ui/dr-ui/ui/app.slint#L333), [`ui/dr-ui/ui/app.slint:547`](../ui/dr-ui/ui/app.slint#L547), [`ui/dr-ui/ui/library.slint:1246`](../ui/dr-ui/ui/library.slint#L1246), [`ui/dr-ui/ui/library.slint:1270`](../ui/dr-ui/ui/library.slint#L1270), [`ui/dr-ui/ui/library.slint:1323`](../ui/dr-ui/ui/library.slint#L1323), [`ui/dr-ui/ui/library.slint:19`](../ui/dr-ui/ui/library.slint#L19), [`ui/dr-ui/ui/library.slint:3963`](../ui/dr-ui/ui/library.slint#L3963) | -| FR-CAT-6 | [`core/dr-catalog/src/collections.rs:1`](../core/dr-catalog/src/collections.rs#L1), [`core/dr-catalog/src/keywords.rs:1`](../core/dr-catalog/src/keywords.rs#L1), [`core/dr-catalog/src/lib.rs:1`](../core/dr-catalog/src/lib.rs#L1), [`core/dr-catalog/src/query.rs:1`](../core/dr-catalog/src/query.rs#L1), [`core/dr-catalog/src/rating.rs:1`](../core/dr-catalog/src/rating.rs#L1), [`core/dr-catalog/src/schema.rs:668`](../core/dr-catalog/src/schema.rs#L668), [`core/dr-types/src/selector.rs:1`](../core/dr-types/src/selector.rs#L1), [`core/dr-types/src/settings.rs:64`](../core/dr-types/src/settings.rs#L64), [`core/dr-types/src/time.rs:67`](../core/dr-types/src/time.rs#L67), [`core/dr-types/src/time.rs:90`](../core/dr-types/src/time.rs#L90), [`ui/dr-ui/src/library.rs:246`](../ui/dr-ui/src/library.rs#L246), [`ui/dr-ui/src/library.rs:4486`](../ui/dr-ui/src/library.rs#L4486), [`ui/dr-ui/src/library_ui.rs:342`](../ui/dr-ui/src/library_ui.rs#L342), [`ui/dr-ui/src/library_ui.rs:416`](../ui/dr-ui/src/library_ui.rs#L416), [`ui/dr-ui/src/library_ui.rs:5688`](../ui/dr-ui/src/library_ui.rs#L5688), [`ui/dr-ui/src/library_ui.rs:5741`](../ui/dr-ui/src/library_ui.rs#L5741), [`ui/dr-ui/src/library_ui.rs:7093`](../ui/dr-ui/src/library_ui.rs#L7093), [`ui/dr-ui/ui/app.slint:345`](../ui/dr-ui/ui/app.slint#L345), [`ui/dr-ui/ui/app.slint:547`](../ui/dr-ui/ui/app.slint#L547), [`ui/dr-ui/ui/app.slint:850`](../ui/dr-ui/ui/app.slint#L850), [`ui/dr-ui/ui/library.slint:110`](../ui/dr-ui/ui/library.slint#L110), [`ui/dr-ui/ui/library.slint:1433`](../ui/dr-ui/ui/library.slint#L1433), [`ui/dr-ui/ui/library.slint:3963`](../ui/dr-ui/ui/library.slint#L3963), [`ui/dr-ui/ui/settings.slint:150`](../ui/dr-ui/ui/settings.slint#L150) | -| FR-CAT-7 | [`core/dr-catalog/src/collections.rs:1`](../core/dr-catalog/src/collections.rs#L1), [`core/dr-catalog/src/merge.rs:1`](../core/dr-catalog/src/merge.rs#L1), [`core/dr-catalog/src/sync.rs:1`](../core/dr-catalog/src/sync.rs#L1), [`core/dr-types/src/selector.rs:1`](../core/dr-types/src/selector.rs#L1), [`ui/dr-ui/src/collections_ui.rs:1073`](../ui/dr-ui/src/collections_ui.rs#L1073), [`ui/dr-ui/src/collections_ui.rs:117`](../ui/dr-ui/src/collections_ui.rs#L117), [`ui/dr-ui/src/collections_ui.rs:1552`](../ui/dr-ui/src/collections_ui.rs#L1552), [`ui/dr-ui/src/collections_ui.rs:1598`](../ui/dr-ui/src/collections_ui.rs#L1598), [`ui/dr-ui/src/collections_ui.rs:1848`](../ui/dr-ui/src/collections_ui.rs#L1848), [`ui/dr-ui/src/collections_ui.rs:1913`](../ui/dr-ui/src/collections_ui.rs#L1913), [`ui/dr-ui/src/collections_ui.rs:1973`](../ui/dr-ui/src/collections_ui.rs#L1973), [`ui/dr-ui/src/collections_ui.rs:1`](../ui/dr-ui/src/collections_ui.rs#L1), [`ui/dr-ui/src/collections_ui.rs:2963`](../ui/dr-ui/src/collections_ui.rs#L2963), [`ui/dr-ui/src/collections_ui.rs:365`](../ui/dr-ui/src/collections_ui.rs#L365), [`ui/dr-ui/src/collections_ui.rs:569`](../ui/dr-ui/src/collections_ui.rs#L569), [`ui/dr-ui/src/collections_ui.rs:965`](../ui/dr-ui/src/collections_ui.rs#L965), [`ui/dr-ui/src/derived_sync.rs:1`](../ui/dr-ui/src/derived_sync.rs#L1), [`ui/dr-ui/src/library.rs:4291`](../ui/dr-ui/src/library.rs#L4291), [`ui/dr-ui/src/library.rs:4317`](../ui/dr-ui/src/library.rs#L4317), [`ui/dr-ui/src/library.rs:4372`](../ui/dr-ui/src/library.rs#L4372), [`ui/dr-ui/src/library_ui.rs:2467`](../ui/dr-ui/src/library_ui.rs#L2467), [`ui/dr-ui/src/library_ui.rs:3863`](../ui/dr-ui/src/library_ui.rs#L3863), [`ui/dr-ui/src/library_ui.rs:4590`](../ui/dr-ui/src/library_ui.rs#L4590), [`ui/dr-ui/ui/app.slint:336`](../ui/dr-ui/ui/app.slint#L336), [`ui/dr-ui/ui/app.slint:542`](../ui/dr-ui/ui/app.slint#L542), [`ui/dr-ui/ui/app.slint:571`](../ui/dr-ui/ui/app.slint#L571), [`ui/dr-ui/ui/collections.slint:4`](../ui/dr-ui/ui/collections.slint#L4), [`ui/dr-ui/ui/library.slint:1213`](../ui/dr-ui/ui/library.slint#L1213), [`ui/dr-ui/ui/library.slint:2748`](../ui/dr-ui/ui/library.slint#L2748), [`ui/dr-ui/ui/library.slint:3941`](../ui/dr-ui/ui/library.slint#L3941), [`ui/dr-ui/ui/library.slint:889`](../ui/dr-ui/ui/library.slint#L889) | -| FR-CAT-8 | [`core/dr-pipeline/src/graph.rs:346`](../core/dr-pipeline/src/graph.rs#L346), [`core/dr-pipeline/src/graph.rs:385`](../core/dr-pipeline/src/graph.rs#L385), [`core/dr-pipeline/src/ops/curve.rs:137`](../core/dr-pipeline/src/ops/curve.rs#L137), [`core/dr-pipeline/src/ops/curve.rs:656`](../core/dr-pipeline/src/ops/curve.rs#L656), [`core/dr-pipeline/src/sidecar.rs:1669`](../core/dr-pipeline/src/sidecar.rs#L1669), [`core/dr-pipeline/src/sidecar.rs:93`](../core/dr-pipeline/src/sidecar.rs#L93), [`core/dr-pipeline/src/state.rs:1`](../core/dr-pipeline/src/state.rs#L1), [`core/dr-pipeline/src/state.rs:75`](../core/dr-pipeline/src/state.rs#L75), [`core/dr-pipeline/tests/tone_curve.rs:34`](../core/dr-pipeline/tests/tone_curve.rs#L34), [`ui/dr-ui/src/develop.rs:3954`](../ui/dr-ui/src/develop.rs#L3954), [`ui/dr-ui/src/develop.rs:3983`](../ui/dr-ui/src/develop.rs#L3983), [`ui/dr-ui/src/export.rs:750`](../ui/dr-ui/src/export.rs#L750), [`ui/dr-ui/src/lib.rs:1037`](../ui/dr-ui/src/lib.rs#L1037), [`ui/dr-ui/src/lib.rs:1548`](../ui/dr-ui/src/lib.rs#L1548), [`ui/dr-ui/src/lib.rs:2038`](../ui/dr-ui/src/lib.rs#L2038), [`ui/dr-ui/src/lib.rs:2173`](../ui/dr-ui/src/lib.rs#L2173), [`ui/dr-ui/src/lib.rs:589`](../ui/dr-ui/src/lib.rs#L589), [`ui/dr-ui/src/library.rs:1903`](../ui/dr-ui/src/library.rs#L1903), [`ui/dr-ui/src/library.rs:492`](../ui/dr-ui/src/library.rs#L492), [`ui/dr-ui/src/library.rs:539`](../ui/dr-ui/src/library.rs#L539), [`ui/dr-ui/src/library.rs:576`](../ui/dr-ui/src/library.rs#L576), [`ui/dr-ui/src/library.rs:823`](../ui/dr-ui/src/library.rs#L823), [`ui/dr-ui/src/library_ui.rs:5313`](../ui/dr-ui/src/library_ui.rs#L5313), [`ui/dr-ui/src/sidecar_cache.rs:1`](../ui/dr-ui/src/sidecar_cache.rs#L1) | -| FR-CAT-9 | [`core/dr-catalog/src/cache.rs:1`](../core/dr-catalog/src/cache.rs#L1), [`core/dr-catalog/src/scan.rs:1`](../core/dr-catalog/src/scan.rs#L1), [`core/dr-catalog/src/schema.rs:725`](../core/dr-catalog/src/schema.rs#L725), [`core/dr-catalog/src/walk.rs:1022`](../core/dr-catalog/src/walk.rs#L1022), [`core/dr-catalog/src/walk.rs:162`](../core/dr-catalog/src/walk.rs#L162), [`core/dr-catalog/src/walk.rs:1`](../core/dr-catalog/src/walk.rs#L1), [`core/dr-catalog/src/walk.rs:435`](../core/dr-catalog/src/walk.rs#L435), [`core/dr-catalog/src/walk.rs:728`](../core/dr-catalog/src/walk.rs#L728), [`core/dr-sync-nextcloud/src/desktop_client.rs:30`](../core/dr-sync-nextcloud/src/desktop_client.rs#L30), [`core/dr-sync/src/error.rs:113`](../core/dr-sync/src/error.rs#L113), [`core/dr-sync/src/reachability.rs:1`](../core/dr-sync/src/reachability.rs#L1), [`core/dr-sync/src/scan.rs:175`](../core/dr-sync/src/scan.rs#L175), [`core/dr-sync/src/scan.rs:464`](../core/dr-sync/src/scan.rs#L464), [`core/dr-types/src/lib.rs:120`](../core/dr-types/src/lib.rs#L120), [`ui/dr-ui/src/develop.rs:3381`](../ui/dr-ui/src/develop.rs#L3381), [`ui/dr-ui/src/library.rs:1222`](../ui/dr-ui/src/library.rs#L1222), [`ui/dr-ui/src/library.rs:1275`](../ui/dr-ui/src/library.rs#L1275), [`ui/dr-ui/src/library.rs:1306`](../ui/dr-ui/src/library.rs#L1306), [`ui/dr-ui/src/library.rs:1411`](../ui/dr-ui/src/library.rs#L1411), [`ui/dr-ui/src/library.rs:161`](../ui/dr-ui/src/library.rs#L161), [`ui/dr-ui/src/library.rs:1863`](../ui/dr-ui/src/library.rs#L1863), [`ui/dr-ui/src/library.rs:1939`](../ui/dr-ui/src/library.rs#L1939), [`ui/dr-ui/src/library.rs:266`](../ui/dr-ui/src/library.rs#L266), [`ui/dr-ui/src/library.rs:4594`](../ui/dr-ui/src/library.rs#L4594), [`ui/dr-ui/src/library.rs:576`](../ui/dr-ui/src/library.rs#L576), [`ui/dr-ui/src/library.rs:807`](../ui/dr-ui/src/library.rs#L807), [`ui/dr-ui/src/library.rs:823`](../ui/dr-ui/src/library.rs#L823), [`ui/dr-ui/src/library.rs:877`](../ui/dr-ui/src/library.rs#L877), [`ui/dr-ui/src/library_ui.rs:1241`](../ui/dr-ui/src/library_ui.rs#L1241), [`ui/dr-ui/src/library_ui.rs:1832`](../ui/dr-ui/src/library_ui.rs#L1832), [`ui/dr-ui/src/library_ui.rs:1876`](../ui/dr-ui/src/library_ui.rs#L1876), [`ui/dr-ui/src/library_ui.rs:1892`](../ui/dr-ui/src/library_ui.rs#L1892), [`ui/dr-ui/src/library_ui.rs:1986`](../ui/dr-ui/src/library_ui.rs#L1986), [`ui/dr-ui/src/library_ui.rs:237`](../ui/dr-ui/src/library_ui.rs#L237), [`ui/dr-ui/src/library_ui.rs:2706`](../ui/dr-ui/src/library_ui.rs#L2706), [`ui/dr-ui/src/library_ui.rs:270`](../ui/dr-ui/src/library_ui.rs#L270), [`ui/dr-ui/src/library_ui.rs:278`](../ui/dr-ui/src/library_ui.rs#L278), [`ui/dr-ui/src/library_ui.rs:3144`](../ui/dr-ui/src/library_ui.rs#L3144), [`ui/dr-ui/src/library_ui.rs:3366`](../ui/dr-ui/src/library_ui.rs#L3366), [`ui/dr-ui/src/library_ui.rs:3644`](../ui/dr-ui/src/library_ui.rs#L3644), [`ui/dr-ui/src/library_ui.rs:3732`](../ui/dr-ui/src/library_ui.rs#L3732), [`ui/dr-ui/src/library_ui.rs:3914`](../ui/dr-ui/src/library_ui.rs#L3914), [`ui/dr-ui/src/library_ui.rs:4028`](../ui/dr-ui/src/library_ui.rs#L4028), [`ui/dr-ui/src/library_ui.rs:469`](../ui/dr-ui/src/library_ui.rs#L469), [`ui/dr-ui/src/library_ui.rs:532`](../ui/dr-ui/src/library_ui.rs#L532), [`ui/dr-ui/src/library_ui.rs:5786`](../ui/dr-ui/src/library_ui.rs#L5786), [`ui/dr-ui/src/library_ui.rs:5901`](../ui/dr-ui/src/library_ui.rs#L5901), [`ui/dr-ui/src/presets.rs:446`](../ui/dr-ui/src/presets.rs#L446), [`ui/dr-ui/src/presets.rs:458`](../ui/dr-ui/src/presets.rs#L458), [`ui/dr-ui/src/sidecar_cache.rs:1`](../ui/dr-ui/src/sidecar_cache.rs#L1) | +| FR-CAT-5 | [`core/dr-catalog/src/keywords.rs:1`](../core/dr-catalog/src/keywords.rs#L1), [`core/dr-catalog/src/merge.rs:1`](../core/dr-catalog/src/merge.rs#L1), [`core/dr-catalog/src/rating.rs:1`](../core/dr-catalog/src/rating.rs#L1), [`core/dr-catalog/src/schema.rs:1171`](../core/dr-catalog/src/schema.rs#L1171), [`core/dr-catalog/src/schema.rs:668`](../core/dr-catalog/src/schema.rs#L668), [`core/dr-decode/src/lib.rs:285`](../core/dr-decode/src/lib.rs#L285), [`core/dr-decode/src/lib.rs:404`](../core/dr-decode/src/lib.rs#L404), [`core/dr-pipeline/src/sidecar.rs:136`](../core/dr-pipeline/src/sidecar.rs#L136), [`ui/dr-ui/src/collections_ui.rs:1579`](../ui/dr-ui/src/collections_ui.rs#L1579), [`ui/dr-ui/src/collections_ui.rs:1685`](../ui/dr-ui/src/collections_ui.rs#L1685), [`ui/dr-ui/src/collections_ui.rs:1734`](../ui/dr-ui/src/collections_ui.rs#L1734), [`ui/dr-ui/src/collections_ui.rs:238`](../ui/dr-ui/src/collections_ui.rs#L238), [`ui/dr-ui/src/collections_ui.rs:335`](../ui/dr-ui/src/collections_ui.rs#L335), [`ui/dr-ui/src/collections_ui.rs:87`](../ui/dr-ui/src/collections_ui.rs#L87), [`ui/dr-ui/src/library.rs:4165`](../ui/dr-ui/src/library.rs#L4165), [`ui/dr-ui/src/library_ui.rs:659`](../ui/dr-ui/src/library_ui.rs#L659), [`ui/dr-ui/src/library_ui.rs:6953`](../ui/dr-ui/src/library_ui.rs#L6953), [`ui/dr-ui/src/library_ui.rs:6964`](../ui/dr-ui/src/library_ui.rs#L6964), [`ui/dr-ui/src/library_ui.rs:6977`](../ui/dr-ui/src/library_ui.rs#L6977), [`ui/dr-ui/src/library_ui.rs:6992`](../ui/dr-ui/src/library_ui.rs#L6992), [`ui/dr-ui/src/library_ui.rs:7001`](../ui/dr-ui/src/library_ui.rs#L7001), [`ui/dr-ui/ui/app.slint:333`](../ui/dr-ui/ui/app.slint#L333), [`ui/dr-ui/ui/app.slint:547`](../ui/dr-ui/ui/app.slint#L547), [`ui/dr-ui/ui/library.slint:1246`](../ui/dr-ui/ui/library.slint#L1246), [`ui/dr-ui/ui/library.slint:1270`](../ui/dr-ui/ui/library.slint#L1270), [`ui/dr-ui/ui/library.slint:1323`](../ui/dr-ui/ui/library.slint#L1323), [`ui/dr-ui/ui/library.slint:19`](../ui/dr-ui/ui/library.slint#L19), [`ui/dr-ui/ui/library.slint:3963`](../ui/dr-ui/ui/library.slint#L3963) | +| FR-CAT-6 | [`core/dr-catalog/src/collections.rs:1`](../core/dr-catalog/src/collections.rs#L1), [`core/dr-catalog/src/keywords.rs:1`](../core/dr-catalog/src/keywords.rs#L1), [`core/dr-catalog/src/lib.rs:1`](../core/dr-catalog/src/lib.rs#L1), [`core/dr-catalog/src/query.rs:1`](../core/dr-catalog/src/query.rs#L1), [`core/dr-catalog/src/rating.rs:1`](../core/dr-catalog/src/rating.rs#L1), [`core/dr-catalog/src/schema.rs:668`](../core/dr-catalog/src/schema.rs#L668), [`core/dr-types/src/selector.rs:1`](../core/dr-types/src/selector.rs#L1), [`core/dr-types/src/settings.rs:64`](../core/dr-types/src/settings.rs#L64), [`core/dr-types/src/time.rs:67`](../core/dr-types/src/time.rs#L67), [`core/dr-types/src/time.rs:90`](../core/dr-types/src/time.rs#L90), [`ui/dr-ui/src/library.rs:246`](../ui/dr-ui/src/library.rs#L246), [`ui/dr-ui/src/library.rs:4558`](../ui/dr-ui/src/library.rs#L4558), [`ui/dr-ui/src/library_ui.rs:342`](../ui/dr-ui/src/library_ui.rs#L342), [`ui/dr-ui/src/library_ui.rs:416`](../ui/dr-ui/src/library_ui.rs#L416), [`ui/dr-ui/src/library_ui.rs:5688`](../ui/dr-ui/src/library_ui.rs#L5688), [`ui/dr-ui/src/library_ui.rs:5741`](../ui/dr-ui/src/library_ui.rs#L5741), [`ui/dr-ui/src/library_ui.rs:7093`](../ui/dr-ui/src/library_ui.rs#L7093), [`ui/dr-ui/ui/app.slint:345`](../ui/dr-ui/ui/app.slint#L345), [`ui/dr-ui/ui/app.slint:547`](../ui/dr-ui/ui/app.slint#L547), [`ui/dr-ui/ui/app.slint:850`](../ui/dr-ui/ui/app.slint#L850), [`ui/dr-ui/ui/library.slint:110`](../ui/dr-ui/ui/library.slint#L110), [`ui/dr-ui/ui/library.slint:1433`](../ui/dr-ui/ui/library.slint#L1433), [`ui/dr-ui/ui/library.slint:3963`](../ui/dr-ui/ui/library.slint#L3963), [`ui/dr-ui/ui/settings.slint:150`](../ui/dr-ui/ui/settings.slint#L150) | +| FR-CAT-7 | [`core/dr-catalog/src/collections.rs:1`](../core/dr-catalog/src/collections.rs#L1), [`core/dr-catalog/src/merge.rs:1`](../core/dr-catalog/src/merge.rs#L1), [`core/dr-catalog/src/sync.rs:1`](../core/dr-catalog/src/sync.rs#L1), [`core/dr-types/src/selector.rs:1`](../core/dr-types/src/selector.rs#L1), [`ui/dr-ui/src/collections_ui.rs:1073`](../ui/dr-ui/src/collections_ui.rs#L1073), [`ui/dr-ui/src/collections_ui.rs:117`](../ui/dr-ui/src/collections_ui.rs#L117), [`ui/dr-ui/src/collections_ui.rs:1552`](../ui/dr-ui/src/collections_ui.rs#L1552), [`ui/dr-ui/src/collections_ui.rs:1598`](../ui/dr-ui/src/collections_ui.rs#L1598), [`ui/dr-ui/src/collections_ui.rs:1848`](../ui/dr-ui/src/collections_ui.rs#L1848), [`ui/dr-ui/src/collections_ui.rs:1913`](../ui/dr-ui/src/collections_ui.rs#L1913), [`ui/dr-ui/src/collections_ui.rs:1973`](../ui/dr-ui/src/collections_ui.rs#L1973), [`ui/dr-ui/src/collections_ui.rs:1`](../ui/dr-ui/src/collections_ui.rs#L1), [`ui/dr-ui/src/collections_ui.rs:2963`](../ui/dr-ui/src/collections_ui.rs#L2963), [`ui/dr-ui/src/collections_ui.rs:365`](../ui/dr-ui/src/collections_ui.rs#L365), [`ui/dr-ui/src/collections_ui.rs:569`](../ui/dr-ui/src/collections_ui.rs#L569), [`ui/dr-ui/src/collections_ui.rs:965`](../ui/dr-ui/src/collections_ui.rs#L965), [`ui/dr-ui/src/derived_sync.rs:1`](../ui/dr-ui/src/derived_sync.rs#L1), [`ui/dr-ui/src/library.rs:4363`](../ui/dr-ui/src/library.rs#L4363), [`ui/dr-ui/src/library.rs:4389`](../ui/dr-ui/src/library.rs#L4389), [`ui/dr-ui/src/library.rs:4444`](../ui/dr-ui/src/library.rs#L4444), [`ui/dr-ui/src/library_ui.rs:2467`](../ui/dr-ui/src/library_ui.rs#L2467), [`ui/dr-ui/src/library_ui.rs:3863`](../ui/dr-ui/src/library_ui.rs#L3863), [`ui/dr-ui/src/library_ui.rs:4590`](../ui/dr-ui/src/library_ui.rs#L4590), [`ui/dr-ui/ui/app.slint:336`](../ui/dr-ui/ui/app.slint#L336), [`ui/dr-ui/ui/app.slint:542`](../ui/dr-ui/ui/app.slint#L542), [`ui/dr-ui/ui/app.slint:571`](../ui/dr-ui/ui/app.slint#L571), [`ui/dr-ui/ui/collections.slint:4`](../ui/dr-ui/ui/collections.slint#L4), [`ui/dr-ui/ui/library.slint:1213`](../ui/dr-ui/ui/library.slint#L1213), [`ui/dr-ui/ui/library.slint:2748`](../ui/dr-ui/ui/library.slint#L2748), [`ui/dr-ui/ui/library.slint:3941`](../ui/dr-ui/ui/library.slint#L3941), [`ui/dr-ui/ui/library.slint:889`](../ui/dr-ui/ui/library.slint#L889) | +| FR-CAT-8 | [`core/dr-pipeline/src/graph.rs:346`](../core/dr-pipeline/src/graph.rs#L346), [`core/dr-pipeline/src/graph.rs:385`](../core/dr-pipeline/src/graph.rs#L385), [`core/dr-pipeline/src/ops/curve.rs:137`](../core/dr-pipeline/src/ops/curve.rs#L137), [`core/dr-pipeline/src/ops/curve.rs:656`](../core/dr-pipeline/src/ops/curve.rs#L656), [`core/dr-pipeline/src/sidecar.rs:1669`](../core/dr-pipeline/src/sidecar.rs#L1669), [`core/dr-pipeline/src/sidecar.rs:93`](../core/dr-pipeline/src/sidecar.rs#L93), [`core/dr-pipeline/src/state.rs:1`](../core/dr-pipeline/src/state.rs#L1), [`core/dr-pipeline/src/state.rs:75`](../core/dr-pipeline/src/state.rs#L75), [`core/dr-pipeline/tests/tone_curve.rs:34`](../core/dr-pipeline/tests/tone_curve.rs#L34), [`ui/dr-ui/src/develop.rs:3954`](../ui/dr-ui/src/develop.rs#L3954), [`ui/dr-ui/src/develop.rs:3983`](../ui/dr-ui/src/develop.rs#L3983), [`ui/dr-ui/src/export.rs:750`](../ui/dr-ui/src/export.rs#L750), [`ui/dr-ui/src/lib.rs:1038`](../ui/dr-ui/src/lib.rs#L1038), [`ui/dr-ui/src/lib.rs:1554`](../ui/dr-ui/src/lib.rs#L1554), [`ui/dr-ui/src/lib.rs:2044`](../ui/dr-ui/src/lib.rs#L2044), [`ui/dr-ui/src/lib.rs:2179`](../ui/dr-ui/src/lib.rs#L2179), [`ui/dr-ui/src/lib.rs:590`](../ui/dr-ui/src/lib.rs#L590), [`ui/dr-ui/src/library.rs:1903`](../ui/dr-ui/src/library.rs#L1903), [`ui/dr-ui/src/library.rs:492`](../ui/dr-ui/src/library.rs#L492), [`ui/dr-ui/src/library.rs:539`](../ui/dr-ui/src/library.rs#L539), [`ui/dr-ui/src/library.rs:576`](../ui/dr-ui/src/library.rs#L576), [`ui/dr-ui/src/library.rs:823`](../ui/dr-ui/src/library.rs#L823), [`ui/dr-ui/src/library_ui.rs:5313`](../ui/dr-ui/src/library_ui.rs#L5313), [`ui/dr-ui/src/sidecar_cache.rs:1`](../ui/dr-ui/src/sidecar_cache.rs#L1) | +| FR-CAT-9 | [`core/dr-catalog/src/cache.rs:1`](../core/dr-catalog/src/cache.rs#L1), [`core/dr-catalog/src/scan.rs:1`](../core/dr-catalog/src/scan.rs#L1), [`core/dr-catalog/src/schema.rs:725`](../core/dr-catalog/src/schema.rs#L725), [`core/dr-catalog/src/walk.rs:1022`](../core/dr-catalog/src/walk.rs#L1022), [`core/dr-catalog/src/walk.rs:162`](../core/dr-catalog/src/walk.rs#L162), [`core/dr-catalog/src/walk.rs:1`](../core/dr-catalog/src/walk.rs#L1), [`core/dr-catalog/src/walk.rs:435`](../core/dr-catalog/src/walk.rs#L435), [`core/dr-catalog/src/walk.rs:728`](../core/dr-catalog/src/walk.rs#L728), [`core/dr-sync-nextcloud/src/desktop_client.rs:30`](../core/dr-sync-nextcloud/src/desktop_client.rs#L30), [`core/dr-sync/src/error.rs:113`](../core/dr-sync/src/error.rs#L113), [`core/dr-sync/src/reachability.rs:1`](../core/dr-sync/src/reachability.rs#L1), [`core/dr-sync/src/scan.rs:175`](../core/dr-sync/src/scan.rs#L175), [`core/dr-sync/src/scan.rs:464`](../core/dr-sync/src/scan.rs#L464), [`core/dr-types/src/lib.rs:120`](../core/dr-types/src/lib.rs#L120), [`ui/dr-ui/src/develop.rs:3381`](../ui/dr-ui/src/develop.rs#L3381), [`ui/dr-ui/src/library.rs:1222`](../ui/dr-ui/src/library.rs#L1222), [`ui/dr-ui/src/library.rs:1275`](../ui/dr-ui/src/library.rs#L1275), [`ui/dr-ui/src/library.rs:1306`](../ui/dr-ui/src/library.rs#L1306), [`ui/dr-ui/src/library.rs:1411`](../ui/dr-ui/src/library.rs#L1411), [`ui/dr-ui/src/library.rs:161`](../ui/dr-ui/src/library.rs#L161), [`ui/dr-ui/src/library.rs:1863`](../ui/dr-ui/src/library.rs#L1863), [`ui/dr-ui/src/library.rs:1939`](../ui/dr-ui/src/library.rs#L1939), [`ui/dr-ui/src/library.rs:266`](../ui/dr-ui/src/library.rs#L266), [`ui/dr-ui/src/library.rs:4666`](../ui/dr-ui/src/library.rs#L4666), [`ui/dr-ui/src/library.rs:576`](../ui/dr-ui/src/library.rs#L576), [`ui/dr-ui/src/library.rs:807`](../ui/dr-ui/src/library.rs#L807), [`ui/dr-ui/src/library.rs:823`](../ui/dr-ui/src/library.rs#L823), [`ui/dr-ui/src/library.rs:877`](../ui/dr-ui/src/library.rs#L877), [`ui/dr-ui/src/library_ui.rs:1241`](../ui/dr-ui/src/library_ui.rs#L1241), [`ui/dr-ui/src/library_ui.rs:1832`](../ui/dr-ui/src/library_ui.rs#L1832), [`ui/dr-ui/src/library_ui.rs:1876`](../ui/dr-ui/src/library_ui.rs#L1876), [`ui/dr-ui/src/library_ui.rs:1892`](../ui/dr-ui/src/library_ui.rs#L1892), [`ui/dr-ui/src/library_ui.rs:1986`](../ui/dr-ui/src/library_ui.rs#L1986), [`ui/dr-ui/src/library_ui.rs:237`](../ui/dr-ui/src/library_ui.rs#L237), [`ui/dr-ui/src/library_ui.rs:2706`](../ui/dr-ui/src/library_ui.rs#L2706), [`ui/dr-ui/src/library_ui.rs:270`](../ui/dr-ui/src/library_ui.rs#L270), [`ui/dr-ui/src/library_ui.rs:278`](../ui/dr-ui/src/library_ui.rs#L278), [`ui/dr-ui/src/library_ui.rs:3144`](../ui/dr-ui/src/library_ui.rs#L3144), [`ui/dr-ui/src/library_ui.rs:3366`](../ui/dr-ui/src/library_ui.rs#L3366), [`ui/dr-ui/src/library_ui.rs:3644`](../ui/dr-ui/src/library_ui.rs#L3644), [`ui/dr-ui/src/library_ui.rs:3732`](../ui/dr-ui/src/library_ui.rs#L3732), [`ui/dr-ui/src/library_ui.rs:3914`](../ui/dr-ui/src/library_ui.rs#L3914), [`ui/dr-ui/src/library_ui.rs:4028`](../ui/dr-ui/src/library_ui.rs#L4028), [`ui/dr-ui/src/library_ui.rs:469`](../ui/dr-ui/src/library_ui.rs#L469), [`ui/dr-ui/src/library_ui.rs:532`](../ui/dr-ui/src/library_ui.rs#L532), [`ui/dr-ui/src/library_ui.rs:5786`](../ui/dr-ui/src/library_ui.rs#L5786), [`ui/dr-ui/src/library_ui.rs:5901`](../ui/dr-ui/src/library_ui.rs#L5901), [`ui/dr-ui/src/presets.rs:446`](../ui/dr-ui/src/presets.rs#L446), [`ui/dr-ui/src/presets.rs:458`](../ui/dr-ui/src/presets.rs#L458), [`ui/dr-ui/src/sidecar_cache.rs:1`](../ui/dr-ui/src/sidecar_cache.rs#L1) | | FR-CULL-1 | [`core/dr-decode/src/preview.rs:121`](../core/dr-decode/src/preview.rs#L121) | -| FR-CULL-10 | [`core/dr-catalog/src/faces.rs:1`](../core/dr-catalog/src/faces.rs#L1), [`core/dr-catalog/src/schema.rs:376`](../core/dr-catalog/src/schema.rs#L376), [`core/dr-catalog/src/schema.rs:554`](../core/dr-catalog/src/schema.rs#L554), [`core/dr-face/src/assign.rs:1`](../core/dr-face/src/assign.rs#L1), [`core/dr-face/src/neighbours.rs:1`](../core/dr-face/src/neighbours.rs#L1), [`core/dr-types/src/settings.rs:117`](../core/dr-types/src/settings.rs#L117), [`ui/dr-ui/src/develop.rs:120`](../ui/dr-ui/src/develop.rs#L120), [`ui/dr-ui/src/develop.rs:129`](../ui/dr-ui/src/develop.rs#L129), [`ui/dr-ui/src/develop.rs:195`](../ui/dr-ui/src/develop.rs#L195), [`ui/dr-ui/src/develop.rs:2011`](../ui/dr-ui/src/develop.rs#L2011), [`ui/dr-ui/src/develop.rs:688`](../ui/dr-ui/src/develop.rs#L688), [`ui/dr-ui/src/faces.rs:1`](../ui/dr-ui/src/faces.rs#L1), [`ui/dr-ui/src/identity.rs:1`](../ui/dr-ui/src/identity.rs#L1), [`ui/dr-ui/src/identity_ui.rs:1`](../ui/dr-ui/src/identity_ui.rs#L1), [`ui/dr-ui/src/lib.rs:2145`](../ui/dr-ui/src/lib.rs#L2145), [`ui/dr-ui/ui/identity.slint:1`](../ui/dr-ui/ui/identity.slint#L1) | +| FR-CULL-10 | [`core/dr-catalog/src/faces.rs:1`](../core/dr-catalog/src/faces.rs#L1), [`core/dr-catalog/src/schema.rs:376`](../core/dr-catalog/src/schema.rs#L376), [`core/dr-catalog/src/schema.rs:554`](../core/dr-catalog/src/schema.rs#L554), [`core/dr-face/src/assign.rs:1`](../core/dr-face/src/assign.rs#L1), [`core/dr-face/src/neighbours.rs:1`](../core/dr-face/src/neighbours.rs#L1), [`core/dr-types/src/settings.rs:117`](../core/dr-types/src/settings.rs#L117), [`ui/dr-ui/src/develop.rs:120`](../ui/dr-ui/src/develop.rs#L120), [`ui/dr-ui/src/develop.rs:129`](../ui/dr-ui/src/develop.rs#L129), [`ui/dr-ui/src/develop.rs:195`](../ui/dr-ui/src/develop.rs#L195), [`ui/dr-ui/src/develop.rs:2011`](../ui/dr-ui/src/develop.rs#L2011), [`ui/dr-ui/src/develop.rs:688`](../ui/dr-ui/src/develop.rs#L688), [`ui/dr-ui/src/faces.rs:1`](../ui/dr-ui/src/faces.rs#L1), [`ui/dr-ui/src/identity.rs:1`](../ui/dr-ui/src/identity.rs#L1), [`ui/dr-ui/src/identity_ui.rs:1`](../ui/dr-ui/src/identity_ui.rs#L1), [`ui/dr-ui/src/lib.rs:2151`](../ui/dr-ui/src/lib.rs#L2151), [`ui/dr-ui/ui/identity.slint:1`](../ui/dr-ui/ui/identity.slint#L1) | | FR-CULL-11 | [`core/dr-catalog/src/faces.rs:1`](../core/dr-catalog/src/faces.rs#L1), [`core/dr-catalog/src/schema.rs:554`](../core/dr-catalog/src/schema.rs#L554), [`ui/dr-ui/src/identity.rs:1`](../ui/dr-ui/src/identity.rs#L1), [`ui/dr-ui/src/identity_ui.rs:1`](../ui/dr-ui/src/identity_ui.rs#L1), [`ui/dr-ui/src/library.rs:287`](../ui/dr-ui/src/library.rs#L287), [`ui/dr-ui/src/library.rs:317`](../ui/dr-ui/src/library.rs#L317), [`ui/dr-ui/ui/identity.slint:1`](../ui/dr-ui/ui/identity.slint#L1) | | FR-CULL-12 | [`core/dr-catalog/src/faces.rs:1`](../core/dr-catalog/src/faces.rs#L1), [`core/dr-catalog/src/schema.rs:376`](../core/dr-catalog/src/schema.rs#L376), [`core/dr-catalog/src/schema.rs:554`](../core/dr-catalog/src/schema.rs#L554), [`ui/dr-ui/src/identity.rs:1`](../ui/dr-ui/src/identity.rs#L1), [`ui/dr-ui/ui/identity.slint:1`](../ui/dr-ui/ui/identity.slint#L1) | | FR-CULL-2 | [`core/dr-decode/src/locate.rs:1`](../core/dr-decode/src/locate.rs#L1), [`core/dr-decode/src/preview.rs:148`](../core/dr-decode/src/preview.rs#L148), [`ui/dr-ui/src/import.rs:463`](../ui/dr-ui/src/import.rs#L463) | -| FR-CULL-3 | [`core/dr-gpu/src/focus.rs:154`](../core/dr-gpu/src/focus.rs#L154), [`core/dr-gpu/src/focus.rs:186`](../core/dr-gpu/src/focus.rs#L186), [`core/dr-gpu/src/focus.rs:1`](../core/dr-gpu/src/focus.rs#L1), [`core/dr-gpu/src/focus.rs:317`](../core/dr-gpu/src/focus.rs#L317), [`core/dr-gpu/src/raw_histogram.rs:129`](../core/dr-gpu/src/raw_histogram.rs#L129), [`core/dr-gpu/src/raw_histogram.rs:1`](../core/dr-gpu/src/raw_histogram.rs#L1), [`core/dr-gpu/src/raw_histogram.rs:272`](../core/dr-gpu/src/raw_histogram.rs#L272), [`core/dr-gpu/src/raw_histogram.rs:407`](../core/dr-gpu/src/raw_histogram.rs#L407), [`core/dr-gpu/src/shaders/focus_peak.wgsl:1`](../core/dr-gpu/src/shaders/focus_peak.wgsl#L1), [`core/dr-gpu/src/shaders/raw_histogram.wgsl:1`](../core/dr-gpu/src/shaders/raw_histogram.wgsl#L1), [`ui/dr-ui/src/develop.rs:3106`](../ui/dr-ui/src/develop.rs#L3106), [`ui/dr-ui/src/develop.rs:3119`](../ui/dr-ui/src/develop.rs#L3119), [`ui/dr-ui/src/develop.rs:3164`](../ui/dr-ui/src/develop.rs#L3164), [`ui/dr-ui/src/develop.rs:3175`](../ui/dr-ui/src/develop.rs#L3175), [`ui/dr-ui/src/develop.rs:3181`](../ui/dr-ui/src/develop.rs#L3181), [`ui/dr-ui/src/develop.rs:3198`](../ui/dr-ui/src/develop.rs#L3198), [`ui/dr-ui/src/develop.rs:6081`](../ui/dr-ui/src/develop.rs#L6081), [`ui/dr-ui/src/develop.rs:6145`](../ui/dr-ui/src/develop.rs#L6145), [`ui/dr-ui/src/develop.rs:6168`](../ui/dr-ui/src/develop.rs#L6168), [`ui/dr-ui/src/develop.rs:726`](../ui/dr-ui/src/develop.rs#L726), [`ui/dr-ui/src/develop.rs:746`](../ui/dr-ui/src/develop.rs#L746), [`ui/dr-ui/src/develop.rs:752`](../ui/dr-ui/src/develop.rs#L752), [`ui/dr-ui/src/histogram.rs:1`](../ui/dr-ui/src/histogram.rs#L1), [`ui/dr-ui/src/histogram.rs:208`](../ui/dr-ui/src/histogram.rs#L208), [`ui/dr-ui/src/histogram.rs:228`](../ui/dr-ui/src/histogram.rs#L228), [`ui/dr-ui/src/histogram.rs:272`](../ui/dr-ui/src/histogram.rs#L272), [`ui/dr-ui/src/histogram.rs:544`](../ui/dr-ui/src/histogram.rs#L544), [`ui/dr-ui/src/histogram.rs:565`](../ui/dr-ui/src/histogram.rs#L565), [`ui/dr-ui/src/histogram.rs:593`](../ui/dr-ui/src/histogram.rs#L593), [`ui/dr-ui/src/histogram.rs:621`](../ui/dr-ui/src/histogram.rs#L621), [`ui/dr-ui/src/histogram.rs:658`](../ui/dr-ui/src/histogram.rs#L658), [`ui/dr-ui/src/lib.rs:1587`](../ui/dr-ui/src/lib.rs#L1587), [`ui/dr-ui/src/lib.rs:1665`](../ui/dr-ui/src/lib.rs#L1665), [`ui/dr-ui/src/lib.rs:1764`](../ui/dr-ui/src/lib.rs#L1764), [`ui/dr-ui/src/lib.rs:1805`](../ui/dr-ui/src/lib.rs#L1805), [`ui/dr-ui/src/lib.rs:3055`](../ui/dr-ui/src/lib.rs#L3055), [`ui/dr-ui/src/lib.rs:349`](../ui/dr-ui/src/lib.rs#L349), [`ui/dr-ui/src/peaking.rs:1`](../ui/dr-ui/src/peaking.rs#L1), [`ui/dr-ui/ui/app.slint:1820`](../ui/dr-ui/ui/app.slint#L1820), [`ui/dr-ui/ui/app.slint:2398`](../ui/dr-ui/ui/app.slint#L2398), [`ui/dr-ui/ui/app.slint:89`](../ui/dr-ui/ui/app.slint#L89), [`ui/dr-ui/ui/peaking.slint:1`](../ui/dr-ui/ui/peaking.slint#L1), [`ui/dr-ui/ui/peaking.slint:25`](../ui/dr-ui/ui/peaking.slint#L25), [`ui/dr-ui/ui/peaking.slint:56`](../ui/dr-ui/ui/peaking.slint#L56) | +| FR-CULL-3 | [`core/dr-gpu/src/focus.rs:154`](../core/dr-gpu/src/focus.rs#L154), [`core/dr-gpu/src/focus.rs:186`](../core/dr-gpu/src/focus.rs#L186), [`core/dr-gpu/src/focus.rs:1`](../core/dr-gpu/src/focus.rs#L1), [`core/dr-gpu/src/focus.rs:317`](../core/dr-gpu/src/focus.rs#L317), [`core/dr-gpu/src/raw_histogram.rs:129`](../core/dr-gpu/src/raw_histogram.rs#L129), [`core/dr-gpu/src/raw_histogram.rs:1`](../core/dr-gpu/src/raw_histogram.rs#L1), [`core/dr-gpu/src/raw_histogram.rs:272`](../core/dr-gpu/src/raw_histogram.rs#L272), [`core/dr-gpu/src/raw_histogram.rs:407`](../core/dr-gpu/src/raw_histogram.rs#L407), [`core/dr-gpu/src/shaders/focus_peak.wgsl:1`](../core/dr-gpu/src/shaders/focus_peak.wgsl#L1), [`core/dr-gpu/src/shaders/raw_histogram.wgsl:1`](../core/dr-gpu/src/shaders/raw_histogram.wgsl#L1), [`ui/dr-ui/src/develop.rs:3106`](../ui/dr-ui/src/develop.rs#L3106), [`ui/dr-ui/src/develop.rs:3119`](../ui/dr-ui/src/develop.rs#L3119), [`ui/dr-ui/src/develop.rs:3164`](../ui/dr-ui/src/develop.rs#L3164), [`ui/dr-ui/src/develop.rs:3175`](../ui/dr-ui/src/develop.rs#L3175), [`ui/dr-ui/src/develop.rs:3181`](../ui/dr-ui/src/develop.rs#L3181), [`ui/dr-ui/src/develop.rs:3198`](../ui/dr-ui/src/develop.rs#L3198), [`ui/dr-ui/src/develop.rs:6081`](../ui/dr-ui/src/develop.rs#L6081), [`ui/dr-ui/src/develop.rs:6145`](../ui/dr-ui/src/develop.rs#L6145), [`ui/dr-ui/src/develop.rs:6168`](../ui/dr-ui/src/develop.rs#L6168), [`ui/dr-ui/src/develop.rs:726`](../ui/dr-ui/src/develop.rs#L726), [`ui/dr-ui/src/develop.rs:746`](../ui/dr-ui/src/develop.rs#L746), [`ui/dr-ui/src/develop.rs:752`](../ui/dr-ui/src/develop.rs#L752), [`ui/dr-ui/src/histogram.rs:1`](../ui/dr-ui/src/histogram.rs#L1), [`ui/dr-ui/src/histogram.rs:208`](../ui/dr-ui/src/histogram.rs#L208), [`ui/dr-ui/src/histogram.rs:228`](../ui/dr-ui/src/histogram.rs#L228), [`ui/dr-ui/src/histogram.rs:272`](../ui/dr-ui/src/histogram.rs#L272), [`ui/dr-ui/src/histogram.rs:544`](../ui/dr-ui/src/histogram.rs#L544), [`ui/dr-ui/src/histogram.rs:565`](../ui/dr-ui/src/histogram.rs#L565), [`ui/dr-ui/src/histogram.rs:593`](../ui/dr-ui/src/histogram.rs#L593), [`ui/dr-ui/src/histogram.rs:621`](../ui/dr-ui/src/histogram.rs#L621), [`ui/dr-ui/src/histogram.rs:658`](../ui/dr-ui/src/histogram.rs#L658), [`ui/dr-ui/src/lib.rs:1593`](../ui/dr-ui/src/lib.rs#L1593), [`ui/dr-ui/src/lib.rs:1671`](../ui/dr-ui/src/lib.rs#L1671), [`ui/dr-ui/src/lib.rs:1770`](../ui/dr-ui/src/lib.rs#L1770), [`ui/dr-ui/src/lib.rs:1811`](../ui/dr-ui/src/lib.rs#L1811), [`ui/dr-ui/src/lib.rs:3061`](../ui/dr-ui/src/lib.rs#L3061), [`ui/dr-ui/src/lib.rs:350`](../ui/dr-ui/src/lib.rs#L350), [`ui/dr-ui/src/peaking.rs:1`](../ui/dr-ui/src/peaking.rs#L1), [`ui/dr-ui/ui/app.slint:1820`](../ui/dr-ui/ui/app.slint#L1820), [`ui/dr-ui/ui/app.slint:2398`](../ui/dr-ui/ui/app.slint#L2398), [`ui/dr-ui/ui/app.slint:89`](../ui/dr-ui/ui/app.slint#L89), [`ui/dr-ui/ui/peaking.slint:1`](../ui/dr-ui/ui/peaking.slint#L1), [`ui/dr-ui/ui/peaking.slint:25`](../ui/dr-ui/ui/peaking.slint#L25), [`ui/dr-ui/ui/peaking.slint:56`](../ui/dr-ui/ui/peaking.slint#L56) | | FR-CULL-4 | [`core/dr-catalog/src/rating.rs:1`](../core/dr-catalog/src/rating.rs#L1), [`core/dr-pipeline/src/sidecar.rs:136`](../core/dr-pipeline/src/sidecar.rs#L136), [`ui/dr-ui/src/library.rs:246`](../ui/dr-ui/src/library.rs#L246), [`ui/dr-ui/src/library.rs:492`](../ui/dr-ui/src/library.rs#L492) | -| FR-CULL-5 | [`core/dr-catalog/src/bursts.rs:1`](../core/dr-catalog/src/bursts.rs#L1), [`core/dr-catalog/src/schema.rs:419`](../core/dr-catalog/src/schema.rs#L419), [`ui/dr-ui/src/bursts.rs:1`](../ui/dr-ui/src/bursts.rs#L1), [`ui/dr-ui/src/library.rs:202`](../ui/dr-ui/src/library.rs#L202), [`ui/dr-ui/src/library.rs:5195`](../ui/dr-ui/src/library.rs#L5195) | -| FR-CULL-8 | [`core/dr-catalog/src/face_shard.rs:1`](../core/dr-catalog/src/face_shard.rs#L1), [`core/dr-catalog/src/faces.rs:1`](../core/dr-catalog/src/faces.rs#L1), [`core/dr-catalog/src/schema.rs:487`](../core/dr-catalog/src/schema.rs#L487), [`core/dr-catalog/src/schema.rs:513`](../core/dr-catalog/src/schema.rs#L513), [`core/dr-catalog/src/schema.rs:554`](../core/dr-catalog/src/schema.rs#L554), [`ui/dr-ui/src/faces.rs:1`](../ui/dr-ui/src/faces.rs#L1), [`ui/dr-ui/src/library.rs:2965`](../ui/dr-ui/src/library.rs#L2965), [`ui/dr-ui/src/library.rs:3069`](../ui/dr-ui/src/library.rs#L3069), [`ui/dr-ui/ui/settings.slint:419`](../ui/dr-ui/ui/settings.slint#L419), [`ui/dr-ui/ui/settings.slint:82`](../ui/dr-ui/ui/settings.slint#L82) | +| FR-CULL-5 | [`core/dr-catalog/src/bursts.rs:1`](../core/dr-catalog/src/bursts.rs#L1), [`core/dr-catalog/src/schema.rs:419`](../core/dr-catalog/src/schema.rs#L419), [`ui/dr-ui/src/bursts.rs:1`](../ui/dr-ui/src/bursts.rs#L1), [`ui/dr-ui/src/library.rs:202`](../ui/dr-ui/src/library.rs#L202), [`ui/dr-ui/src/library.rs:5267`](../ui/dr-ui/src/library.rs#L5267) | +| FR-CULL-8 | [`core/dr-catalog/src/face_shard.rs:1`](../core/dr-catalog/src/face_shard.rs#L1), [`core/dr-catalog/src/faces.rs:1`](../core/dr-catalog/src/faces.rs#L1), [`core/dr-catalog/src/schema.rs:487`](../core/dr-catalog/src/schema.rs#L487), [`core/dr-catalog/src/schema.rs:513`](../core/dr-catalog/src/schema.rs#L513), [`core/dr-catalog/src/schema.rs:554`](../core/dr-catalog/src/schema.rs#L554), [`core/dr-face/src/align.rs:291`](../core/dr-face/src/align.rs#L291), [`ui/dr-ui/examples/face_native.rs:1`](../ui/dr-ui/examples/face_native.rs#L1), [`ui/dr-ui/src/faces.rs:1`](../ui/dr-ui/src/faces.rs#L1), [`ui/dr-ui/src/faces.rs:330`](../ui/dr-ui/src/faces.rs#L330), [`ui/dr-ui/src/faces.rs:346`](../ui/dr-ui/src/faces.rs#L346), [`ui/dr-ui/src/identity_ui.rs:540`](../ui/dr-ui/src/identity_ui.rs#L540), [`ui/dr-ui/src/lib.rs:1212`](../ui/dr-ui/src/lib.rs#L1212), [`ui/dr-ui/src/library.rs:2965`](../ui/dr-ui/src/library.rs#L2965), [`ui/dr-ui/src/library.rs:3058`](../ui/dr-ui/src/library.rs#L3058), [`ui/dr-ui/src/library.rs:3279`](../ui/dr-ui/src/library.rs#L3279), [`ui/dr-ui/src/library.rs:3423`](../ui/dr-ui/src/library.rs#L3423), [`ui/dr-ui/src/library.rs:3462`](../ui/dr-ui/src/library.rs#L3462), [`ui/dr-ui/ui/settings.slint:419`](../ui/dr-ui/ui/settings.slint#L419), [`ui/dr-ui/ui/settings.slint:82`](../ui/dr-ui/ui/settings.slint#L82) | | FR-CULL-9 | [`core/dr-catalog/src/faces.rs:1`](../core/dr-catalog/src/faces.rs#L1), [`core/dr-catalog/src/schema.rs:554`](../core/dr-catalog/src/schema.rs#L554), [`core/dr-face/src/assign.rs:1`](../core/dr-face/src/assign.rs#L1), [`core/dr-face/src/neighbours.rs:1`](../core/dr-face/src/neighbours.rs#L1), [`core/dr-types/src/settings.rs:117`](../core/dr-types/src/settings.rs#L117), [`ui/dr-ui/src/faces.rs:1`](../ui/dr-ui/src/faces.rs#L1), [`ui/dr-ui/src/identity_ui.rs:1`](../ui/dr-ui/src/identity_ui.rs#L1), [`ui/dr-ui/ui/identity.slint:283`](../ui/dr-ui/ui/identity.slint#L283) | | FR-DEV-1 | [`core/dr-pipeline/src/graph.rs:1`](../core/dr-pipeline/src/graph.rs#L1), [`core/dr-pipeline/src/sidecar.rs:1`](../core/dr-pipeline/src/sidecar.rs#L1) | | FR-DEV-2 | [`core/dr-pipeline/src/operation.rs:389`](../core/dr-pipeline/src/operation.rs#L389) | -| FR-DEV-3 | [`core/dr-gpu/src/adjust.rs:2203`](../core/dr-gpu/src/adjust.rs#L2203), [`core/dr-gpu/src/adjust.rs:651`](../core/dr-gpu/src/adjust.rs#L651), [`core/dr-gpu/src/adjust.rs:770`](../core/dr-gpu/src/adjust.rs#L770), [`core/dr-gpu/src/adjust.rs:84`](../core/dr-gpu/src/adjust.rs#L84), [`core/dr-gpu/tests/tone_curve.rs:1`](../core/dr-gpu/tests/tone_curve.rs#L1), [`core/dr-pipeline/src/detail.rs:434`](../core/dr-pipeline/src/detail.rs#L434), [`core/dr-pipeline/src/detail.rs:524`](../core/dr-pipeline/src/detail.rs#L524), [`core/dr-pipeline/src/framing.rs:177`](../core/dr-pipeline/src/framing.rs#L177), [`core/dr-pipeline/src/framing.rs:234`](../core/dr-pipeline/src/framing.rs#L234), [`core/dr-pipeline/src/framing.rs:314`](../core/dr-pipeline/src/framing.rs#L314), [`core/dr-pipeline/src/framing.rs:488`](../core/dr-pipeline/src/framing.rs#L488), [`core/dr-pipeline/src/framing.rs:743`](../core/dr-pipeline/src/framing.rs#L743), [`core/dr-pipeline/src/graph.rs:170`](../core/dr-pipeline/src/graph.rs#L170), [`core/dr-pipeline/src/graph.rs:578`](../core/dr-pipeline/src/graph.rs#L578), [`core/dr-pipeline/src/mask.rs:134`](../core/dr-pipeline/src/mask.rs#L134), [`core/dr-pipeline/src/operation.rs:330`](../core/dr-pipeline/src/operation.rs#L330), [`core/dr-pipeline/src/operation.rs:516`](../core/dr-pipeline/src/operation.rs#L516), [`core/dr-pipeline/src/ops/capture_sharpen.rs:1`](../core/dr-pipeline/src/ops/capture_sharpen.rs#L1), [`core/dr-pipeline/src/ops/capture_sharpen.rs:210`](../core/dr-pipeline/src/ops/capture_sharpen.rs#L210), [`core/dr-pipeline/src/ops/curve.rs:100`](../core/dr-pipeline/src/ops/curve.rs#L100), [`core/dr-pipeline/src/ops/curve.rs:1`](../core/dr-pipeline/src/ops/curve.rs#L1), [`core/dr-pipeline/src/ops/curve.rs:219`](../core/dr-pipeline/src/ops/curve.rs#L219), [`core/dr-pipeline/src/ops/curve.rs:635`](../core/dr-pipeline/src/ops/curve.rs#L635), [`core/dr-pipeline/src/ops/local_contrast.rs:1`](../core/dr-pipeline/src/ops/local_contrast.rs#L1), [`core/dr-pipeline/src/ops/noise_reduction.rs:1`](../core/dr-pipeline/src/ops/noise_reduction.rs#L1), [`core/dr-pipeline/src/ops/noise_reduction.rs:273`](../core/dr-pipeline/src/ops/noise_reduction.rs#L273), [`core/dr-pipeline/src/sidecar.rs:157`](../core/dr-pipeline/src/sidecar.rs#L157), [`core/dr-pipeline/src/sidecar.rs:1669`](../core/dr-pipeline/src/sidecar.rs#L1669), [`core/dr-pipeline/src/sidecar.rs:1729`](../core/dr-pipeline/src/sidecar.rs#L1729), [`core/dr-pipeline/tests/tone_curve.rs:1`](../core/dr-pipeline/tests/tone_curve.rs#L1), [`core/dr-segment/src/refine.rs:442`](../core/dr-segment/src/refine.rs#L442), [`ui/dr-ui/src/develop.rs:102`](../ui/dr-ui/src/develop.rs#L102), [`ui/dr-ui/src/develop.rs:1472`](../ui/dr-ui/src/develop.rs#L1472), [`ui/dr-ui/src/develop.rs:164`](../ui/dr-ui/src/develop.rs#L164), [`ui/dr-ui/src/develop.rs:1993`](../ui/dr-ui/src/develop.rs#L1993), [`ui/dr-ui/src/develop.rs:2011`](../ui/dr-ui/src/develop.rs#L2011), [`ui/dr-ui/src/develop.rs:2025`](../ui/dr-ui/src/develop.rs#L2025), [`ui/dr-ui/src/develop.rs:2047`](../ui/dr-ui/src/develop.rs#L2047), [`ui/dr-ui/src/develop.rs:2193`](../ui/dr-ui/src/develop.rs#L2193), [`ui/dr-ui/src/develop.rs:2291`](../ui/dr-ui/src/develop.rs#L2291), [`ui/dr-ui/src/develop.rs:327`](../ui/dr-ui/src/develop.rs#L327), [`ui/dr-ui/src/develop.rs:364`](../ui/dr-ui/src/develop.rs#L364), [`ui/dr-ui/src/develop.rs:3652`](../ui/dr-ui/src/develop.rs#L3652), [`ui/dr-ui/src/develop.rs:3682`](../ui/dr-ui/src/develop.rs#L3682), [`ui/dr-ui/src/develop.rs:3748`](../ui/dr-ui/src/develop.rs#L3748), [`ui/dr-ui/src/develop.rs:3762`](../ui/dr-ui/src/develop.rs#L3762), [`ui/dr-ui/src/develop.rs:3954`](../ui/dr-ui/src/develop.rs#L3954), [`ui/dr-ui/src/develop.rs:4614`](../ui/dr-ui/src/develop.rs#L4614), [`ui/dr-ui/src/develop.rs:4668`](../ui/dr-ui/src/develop.rs#L4668), [`ui/dr-ui/src/develop.rs:4712`](../ui/dr-ui/src/develop.rs#L4712), [`ui/dr-ui/src/develop.rs:4762`](../ui/dr-ui/src/develop.rs#L4762), [`ui/dr-ui/src/develop.rs:587`](../ui/dr-ui/src/develop.rs#L587), [`ui/dr-ui/src/develop.rs:648`](../ui/dr-ui/src/develop.rs#L648), [`ui/dr-ui/src/develop.rs:766`](../ui/dr-ui/src/develop.rs#L766), [`ui/dr-ui/src/develop.rs:813`](../ui/dr-ui/src/develop.rs#L813), [`ui/dr-ui/src/develop.rs:842`](../ui/dr-ui/src/develop.rs#L842), [`ui/dr-ui/src/lib.rs:1646`](../ui/dr-ui/src/lib.rs#L1646), [`ui/dr-ui/src/lib.rs:2448`](../ui/dr-ui/src/lib.rs#L2448), [`ui/dr-ui/src/lib.rs:2644`](../ui/dr-ui/src/lib.rs#L2644), [`ui/dr-ui/src/lib.rs:2816`](../ui/dr-ui/src/lib.rs#L2816), [`ui/dr-ui/src/lib.rs:2880`](../ui/dr-ui/src/lib.rs#L2880), [`ui/dr-ui/src/lib.rs:2934`](../ui/dr-ui/src/lib.rs#L2934), [`ui/dr-ui/src/lib.rs:355`](../ui/dr-ui/src/lib.rs#L355), [`ui/dr-ui/src/lib.rs:398`](../ui/dr-ui/src/lib.rs#L398), [`ui/dr-ui/src/lib.rs:421`](../ui/dr-ui/src/lib.rs#L421), [`ui/dr-ui/src/library.rs:539`](../ui/dr-ui/src/library.rs#L539), [`ui/dr-ui/src/masks_ui.rs:249`](../ui/dr-ui/src/masks_ui.rs#L249), [`ui/dr-ui/src/masks_ui.rs:41`](../ui/dr-ui/src/masks_ui.rs#L41), [`ui/dr-ui/src/masks_ui.rs:884`](../ui/dr-ui/src/masks_ui.rs#L884), [`ui/dr-ui/src/masks_ui.rs:998`](../ui/dr-ui/src/masks_ui.rs#L998), [`ui/dr-ui/src/segmentation.rs:305`](../ui/dr-ui/src/segmentation.rs#L305), [`ui/dr-ui/src/segmentation.rs:321`](../ui/dr-ui/src/segmentation.rs#L321), [`ui/dr-ui/src/segmentation.rs:451`](../ui/dr-ui/src/segmentation.rs#L451), [`ui/dr-ui/src/segmentation.rs:479`](../ui/dr-ui/src/segmentation.rs#L479), [`ui/dr-ui/ui/adjust.slint:547`](../ui/dr-ui/ui/adjust.slint#L547), [`ui/dr-ui/ui/adjust.slint:666`](../ui/dr-ui/ui/adjust.slint#L666), [`ui/dr-ui/ui/app.slint:150`](../ui/dr-ui/ui/app.slint#L150), [`ui/dr-ui/ui/app.slint:196`](../ui/dr-ui/ui/app.slint#L196), [`ui/dr-ui/ui/app.slint:2022`](../ui/dr-ui/ui/app.slint#L2022), [`ui/dr-ui/ui/app.slint:960`](../ui/dr-ui/ui/app.slint#L960), [`ui/dr-ui/ui/masks.slint:611`](../ui/dr-ui/ui/masks.slint#L611) | -| FR-DEV-3a | [`core/dr-pipeline/ops/exposure.yaml:1`](../core/dr-pipeline/ops/exposure.yaml#L1), [`core/dr-pipeline/src/descriptor.rs:194`](../core/dr-pipeline/src/descriptor.rs#L194), [`core/dr-pipeline/src/descriptor.rs:234`](../core/dr-pipeline/src/descriptor.rs#L234), [`core/dr-pipeline/src/descriptor.rs:258`](../core/dr-pipeline/src/descriptor.rs#L258), [`core/dr-pipeline/src/descriptor.rs:313`](../core/dr-pipeline/src/descriptor.rs#L313), [`core/dr-pipeline/src/framing.rs:385`](../core/dr-pipeline/src/framing.rs#L385), [`core/dr-pipeline/src/graph.rs:24`](../core/dr-pipeline/src/graph.rs#L24), [`core/dr-pipeline/src/graph.rs:251`](../core/dr-pipeline/src/graph.rs#L251), [`core/dr-pipeline/src/graph.rs:46`](../core/dr-pipeline/src/graph.rs#L46), [`core/dr-pipeline/src/graph.rs:59`](../core/dr-pipeline/src/graph.rs#L59), [`core/dr-pipeline/src/mask.rs:1024`](../core/dr-pipeline/src/mask.rs#L1024), [`core/dr-pipeline/src/operation.rs:232`](../core/dr-pipeline/src/operation.rs#L232), [`core/dr-pipeline/src/operation.rs:365`](../core/dr-pipeline/src/operation.rs#L365), [`core/dr-pipeline/src/ops/curve.rs:319`](../core/dr-pipeline/src/ops/curve.rs#L319), [`ui/dr-ui/src/develop.rs:1369`](../ui/dr-ui/src/develop.rs#L1369), [`ui/dr-ui/src/lib.rs:704`](../ui/dr-ui/src/lib.rs#L704), [`ui/dr-ui/tests/ui_names_no_operation.rs:1`](../ui/dr-ui/tests/ui_names_no_operation.rs#L1) | +| FR-DEV-3 | [`core/dr-gpu/src/adjust.rs:2203`](../core/dr-gpu/src/adjust.rs#L2203), [`core/dr-gpu/src/adjust.rs:651`](../core/dr-gpu/src/adjust.rs#L651), [`core/dr-gpu/src/adjust.rs:770`](../core/dr-gpu/src/adjust.rs#L770), [`core/dr-gpu/src/adjust.rs:84`](../core/dr-gpu/src/adjust.rs#L84), [`core/dr-gpu/tests/tone_curve.rs:1`](../core/dr-gpu/tests/tone_curve.rs#L1), [`core/dr-pipeline/src/detail.rs:434`](../core/dr-pipeline/src/detail.rs#L434), [`core/dr-pipeline/src/detail.rs:524`](../core/dr-pipeline/src/detail.rs#L524), [`core/dr-pipeline/src/framing.rs:177`](../core/dr-pipeline/src/framing.rs#L177), [`core/dr-pipeline/src/framing.rs:234`](../core/dr-pipeline/src/framing.rs#L234), [`core/dr-pipeline/src/framing.rs:314`](../core/dr-pipeline/src/framing.rs#L314), [`core/dr-pipeline/src/framing.rs:488`](../core/dr-pipeline/src/framing.rs#L488), [`core/dr-pipeline/src/framing.rs:743`](../core/dr-pipeline/src/framing.rs#L743), [`core/dr-pipeline/src/graph.rs:170`](../core/dr-pipeline/src/graph.rs#L170), [`core/dr-pipeline/src/graph.rs:578`](../core/dr-pipeline/src/graph.rs#L578), [`core/dr-pipeline/src/mask.rs:134`](../core/dr-pipeline/src/mask.rs#L134), [`core/dr-pipeline/src/operation.rs:330`](../core/dr-pipeline/src/operation.rs#L330), [`core/dr-pipeline/src/operation.rs:516`](../core/dr-pipeline/src/operation.rs#L516), [`core/dr-pipeline/src/ops/capture_sharpen.rs:1`](../core/dr-pipeline/src/ops/capture_sharpen.rs#L1), [`core/dr-pipeline/src/ops/capture_sharpen.rs:210`](../core/dr-pipeline/src/ops/capture_sharpen.rs#L210), [`core/dr-pipeline/src/ops/curve.rs:100`](../core/dr-pipeline/src/ops/curve.rs#L100), [`core/dr-pipeline/src/ops/curve.rs:1`](../core/dr-pipeline/src/ops/curve.rs#L1), [`core/dr-pipeline/src/ops/curve.rs:219`](../core/dr-pipeline/src/ops/curve.rs#L219), [`core/dr-pipeline/src/ops/curve.rs:635`](../core/dr-pipeline/src/ops/curve.rs#L635), [`core/dr-pipeline/src/ops/local_contrast.rs:1`](../core/dr-pipeline/src/ops/local_contrast.rs#L1), [`core/dr-pipeline/src/ops/noise_reduction.rs:1`](../core/dr-pipeline/src/ops/noise_reduction.rs#L1), [`core/dr-pipeline/src/ops/noise_reduction.rs:273`](../core/dr-pipeline/src/ops/noise_reduction.rs#L273), [`core/dr-pipeline/src/sidecar.rs:157`](../core/dr-pipeline/src/sidecar.rs#L157), [`core/dr-pipeline/src/sidecar.rs:1669`](../core/dr-pipeline/src/sidecar.rs#L1669), [`core/dr-pipeline/src/sidecar.rs:1729`](../core/dr-pipeline/src/sidecar.rs#L1729), [`core/dr-pipeline/tests/tone_curve.rs:1`](../core/dr-pipeline/tests/tone_curve.rs#L1), [`core/dr-segment/src/refine.rs:442`](../core/dr-segment/src/refine.rs#L442), [`ui/dr-ui/src/develop.rs:102`](../ui/dr-ui/src/develop.rs#L102), [`ui/dr-ui/src/develop.rs:1472`](../ui/dr-ui/src/develop.rs#L1472), [`ui/dr-ui/src/develop.rs:164`](../ui/dr-ui/src/develop.rs#L164), [`ui/dr-ui/src/develop.rs:1993`](../ui/dr-ui/src/develop.rs#L1993), [`ui/dr-ui/src/develop.rs:2011`](../ui/dr-ui/src/develop.rs#L2011), [`ui/dr-ui/src/develop.rs:2025`](../ui/dr-ui/src/develop.rs#L2025), [`ui/dr-ui/src/develop.rs:2047`](../ui/dr-ui/src/develop.rs#L2047), [`ui/dr-ui/src/develop.rs:2193`](../ui/dr-ui/src/develop.rs#L2193), [`ui/dr-ui/src/develop.rs:2291`](../ui/dr-ui/src/develop.rs#L2291), [`ui/dr-ui/src/develop.rs:327`](../ui/dr-ui/src/develop.rs#L327), [`ui/dr-ui/src/develop.rs:364`](../ui/dr-ui/src/develop.rs#L364), [`ui/dr-ui/src/develop.rs:3652`](../ui/dr-ui/src/develop.rs#L3652), [`ui/dr-ui/src/develop.rs:3682`](../ui/dr-ui/src/develop.rs#L3682), [`ui/dr-ui/src/develop.rs:3748`](../ui/dr-ui/src/develop.rs#L3748), [`ui/dr-ui/src/develop.rs:3762`](../ui/dr-ui/src/develop.rs#L3762), [`ui/dr-ui/src/develop.rs:3954`](../ui/dr-ui/src/develop.rs#L3954), [`ui/dr-ui/src/develop.rs:4614`](../ui/dr-ui/src/develop.rs#L4614), [`ui/dr-ui/src/develop.rs:4668`](../ui/dr-ui/src/develop.rs#L4668), [`ui/dr-ui/src/develop.rs:4712`](../ui/dr-ui/src/develop.rs#L4712), [`ui/dr-ui/src/develop.rs:4762`](../ui/dr-ui/src/develop.rs#L4762), [`ui/dr-ui/src/develop.rs:587`](../ui/dr-ui/src/develop.rs#L587), [`ui/dr-ui/src/develop.rs:648`](../ui/dr-ui/src/develop.rs#L648), [`ui/dr-ui/src/develop.rs:766`](../ui/dr-ui/src/develop.rs#L766), [`ui/dr-ui/src/develop.rs:813`](../ui/dr-ui/src/develop.rs#L813), [`ui/dr-ui/src/develop.rs:842`](../ui/dr-ui/src/develop.rs#L842), [`ui/dr-ui/src/lib.rs:1652`](../ui/dr-ui/src/lib.rs#L1652), [`ui/dr-ui/src/lib.rs:2454`](../ui/dr-ui/src/lib.rs#L2454), [`ui/dr-ui/src/lib.rs:2650`](../ui/dr-ui/src/lib.rs#L2650), [`ui/dr-ui/src/lib.rs:2822`](../ui/dr-ui/src/lib.rs#L2822), [`ui/dr-ui/src/lib.rs:2886`](../ui/dr-ui/src/lib.rs#L2886), [`ui/dr-ui/src/lib.rs:2940`](../ui/dr-ui/src/lib.rs#L2940), [`ui/dr-ui/src/lib.rs:356`](../ui/dr-ui/src/lib.rs#L356), [`ui/dr-ui/src/lib.rs:399`](../ui/dr-ui/src/lib.rs#L399), [`ui/dr-ui/src/lib.rs:422`](../ui/dr-ui/src/lib.rs#L422), [`ui/dr-ui/src/library.rs:539`](../ui/dr-ui/src/library.rs#L539), [`ui/dr-ui/src/masks_ui.rs:249`](../ui/dr-ui/src/masks_ui.rs#L249), [`ui/dr-ui/src/masks_ui.rs:41`](../ui/dr-ui/src/masks_ui.rs#L41), [`ui/dr-ui/src/masks_ui.rs:884`](../ui/dr-ui/src/masks_ui.rs#L884), [`ui/dr-ui/src/masks_ui.rs:998`](../ui/dr-ui/src/masks_ui.rs#L998), [`ui/dr-ui/src/segmentation.rs:305`](../ui/dr-ui/src/segmentation.rs#L305), [`ui/dr-ui/src/segmentation.rs:321`](../ui/dr-ui/src/segmentation.rs#L321), [`ui/dr-ui/src/segmentation.rs:451`](../ui/dr-ui/src/segmentation.rs#L451), [`ui/dr-ui/src/segmentation.rs:479`](../ui/dr-ui/src/segmentation.rs#L479), [`ui/dr-ui/ui/adjust.slint:547`](../ui/dr-ui/ui/adjust.slint#L547), [`ui/dr-ui/ui/adjust.slint:666`](../ui/dr-ui/ui/adjust.slint#L666), [`ui/dr-ui/ui/app.slint:150`](../ui/dr-ui/ui/app.slint#L150), [`ui/dr-ui/ui/app.slint:196`](../ui/dr-ui/ui/app.slint#L196), [`ui/dr-ui/ui/app.slint:2022`](../ui/dr-ui/ui/app.slint#L2022), [`ui/dr-ui/ui/app.slint:960`](../ui/dr-ui/ui/app.slint#L960), [`ui/dr-ui/ui/masks.slint:611`](../ui/dr-ui/ui/masks.slint#L611) | +| FR-DEV-3a | [`core/dr-pipeline/ops/exposure.yaml:1`](../core/dr-pipeline/ops/exposure.yaml#L1), [`core/dr-pipeline/src/descriptor.rs:194`](../core/dr-pipeline/src/descriptor.rs#L194), [`core/dr-pipeline/src/descriptor.rs:234`](../core/dr-pipeline/src/descriptor.rs#L234), [`core/dr-pipeline/src/descriptor.rs:258`](../core/dr-pipeline/src/descriptor.rs#L258), [`core/dr-pipeline/src/descriptor.rs:313`](../core/dr-pipeline/src/descriptor.rs#L313), [`core/dr-pipeline/src/framing.rs:385`](../core/dr-pipeline/src/framing.rs#L385), [`core/dr-pipeline/src/graph.rs:24`](../core/dr-pipeline/src/graph.rs#L24), [`core/dr-pipeline/src/graph.rs:251`](../core/dr-pipeline/src/graph.rs#L251), [`core/dr-pipeline/src/graph.rs:46`](../core/dr-pipeline/src/graph.rs#L46), [`core/dr-pipeline/src/graph.rs:59`](../core/dr-pipeline/src/graph.rs#L59), [`core/dr-pipeline/src/mask.rs:1024`](../core/dr-pipeline/src/mask.rs#L1024), [`core/dr-pipeline/src/operation.rs:232`](../core/dr-pipeline/src/operation.rs#L232), [`core/dr-pipeline/src/operation.rs:365`](../core/dr-pipeline/src/operation.rs#L365), [`core/dr-pipeline/src/ops/curve.rs:319`](../core/dr-pipeline/src/ops/curve.rs#L319), [`ui/dr-ui/src/develop.rs:1369`](../ui/dr-ui/src/develop.rs#L1369), [`ui/dr-ui/src/lib.rs:705`](../ui/dr-ui/src/lib.rs#L705), [`ui/dr-ui/tests/ui_names_no_operation.rs:1`](../ui/dr-ui/tests/ui_names_no_operation.rs#L1) | | FR-DEV-3b | [`core/dr-pipeline/src/descriptor.rs:258`](../core/dr-pipeline/src/descriptor.rs#L258), [`core/dr-pipeline/src/framing.rs:385`](../core/dr-pipeline/src/framing.rs#L385), [`core/dr-pipeline/src/graph.rs:59`](../core/dr-pipeline/src/graph.rs#L59), [`core/dr-pipeline/src/operation.rs:365`](../core/dr-pipeline/src/operation.rs#L365) | | FR-DEV-3c | [`core/dr-pipeline/ops/exposure.yaml:1`](../core/dr-pipeline/ops/exposure.yaml#L1), [`core/dr-pipeline/src/graph.rs:251`](../core/dr-pipeline/src/graph.rs#L251), [`core/dr-pipeline/src/graph.rs:46`](../core/dr-pipeline/src/graph.rs#L46), [`core/dr-pipeline/src/mask.rs:1024`](../core/dr-pipeline/src/mask.rs#L1024), [`ui/dr-ui/src/develop.rs:5341`](../ui/dr-ui/src/develop.rs#L5341) | | FR-DEV-3d | [`core/dr-gpu/src/adjust.rs:104`](../core/dr-gpu/src/adjust.rs#L104), [`core/dr-gpu/src/adjust.rs:1079`](../core/dr-gpu/src/adjust.rs#L1079), [`core/dr-gpu/src/adjust.rs:770`](../core/dr-gpu/src/adjust.rs#L770), [`core/dr-gpu/src/adjust.rs:84`](../core/dr-gpu/src/adjust.rs#L84), [`core/dr-gpu/src/adjust.rs:986`](../core/dr-gpu/src/adjust.rs#L986), [`core/dr-gpu/tests/capture_sharpen.rs:434`](../core/dr-gpu/tests/capture_sharpen.rs#L434), [`core/dr-gpu/tests/detail_stage.rs:242`](../core/dr-gpu/tests/detail_stage.rs#L242), [`core/dr-gpu/tests/local_contrast.rs:558`](../core/dr-gpu/tests/local_contrast.rs#L558), [`core/dr-gpu/tests/noise_reduction.rs:556`](../core/dr-gpu/tests/noise_reduction.rs#L556), [`core/dr-pipeline/src/framing.rs:314`](../core/dr-pipeline/src/framing.rs#L314), [`core/dr-pipeline/src/graph.rs:617`](../core/dr-pipeline/src/graph.rs#L617), [`core/dr-pipeline/src/operation.rs:32`](../core/dr-pipeline/src/operation.rs#L32), [`core/dr-pipeline/src/operation.rs:389`](../core/dr-pipeline/src/operation.rs#L389), [`core/dr-pipeline/src/operation.rs:53`](../core/dr-pipeline/src/operation.rs#L53), [`core/dr-pipeline/src/operation.rs:71`](../core/dr-pipeline/src/operation.rs#L71) | | FR-DEV-3e | [`core/dr-decode/src/base_curve.rs:145`](../core/dr-decode/src/base_curve.rs#L145), [`core/dr-decode/src/base_curve.rs:158`](../core/dr-decode/src/base_curve.rs#L158), [`core/dr-decode/src/base_curve.rs:1`](../core/dr-decode/src/base_curve.rs#L1), [`core/dr-decode/src/base_curve.rs:267`](../core/dr-decode/src/base_curve.rs#L267), [`core/dr-decode/src/base_curve.rs:347`](../core/dr-decode/src/base_curve.rs#L347), [`core/dr-decode/src/base_curve.rs:55`](../core/dr-decode/src/base_curve.rs#L55), [`core/dr-decode/src/lib.rs:121`](../core/dr-decode/src/lib.rs#L121), [`core/dr-decode/src/lib.rs:708`](../core/dr-decode/src/lib.rs#L708), [`core/dr-decode/src/lib.rs:748`](../core/dr-decode/src/lib.rs#L748), [`core/dr-decode/src/profile.rs:102`](../core/dr-decode/src/profile.rs#L102), [`core/dr-decode/src/profile.rs:151`](../core/dr-decode/src/profile.rs#L151), [`core/dr-decode/src/profile.rs:1`](../core/dr-decode/src/profile.rs#L1), [`core/dr-decode/src/profile.rs:235`](../core/dr-decode/src/profile.rs#L235), [`core/dr-decode/src/profile.rs:286`](../core/dr-decode/src/profile.rs#L286), [`core/dr-decode/src/profile.rs:343`](../core/dr-decode/src/profile.rs#L343), [`core/dr-decode/src/profile.rs:458`](../core/dr-decode/src/profile.rs#L458), [`core/dr-decode/src/profile.rs:492`](../core/dr-decode/src/profile.rs#L492), [`core/dr-decode/src/profile.rs:630`](../core/dr-decode/src/profile.rs#L630), [`core/dr-gpu/src/adjust.rs:37`](../core/dr-gpu/src/adjust.rs#L37), [`core/dr-gpu/src/adjust.rs:967`](../core/dr-gpu/src/adjust.rs#L967), [`core/dr-gpu/src/demosaic.rs:121`](../core/dr-gpu/src/demosaic.rs#L121), [`core/dr-gpu/src/demosaic.rs:86`](../core/dr-gpu/src/demosaic.rs#L86), [`core/dr-gpu/tests/base_curve.rs:1`](../core/dr-gpu/tests/base_curve.rs#L1), [`core/dr-pipeline/src/operation.rs:1495`](../core/dr-pipeline/src/operation.rs#L1495), [`core/dr-pipeline/src/operation.rs:1576`](../core/dr-pipeline/src/operation.rs#L1576), [`core/dr-pipeline/src/operation.rs:1601`](../core/dr-pipeline/src/operation.rs#L1601), [`core/dr-pipeline/src/operation.rs:1616`](../core/dr-pipeline/src/operation.rs#L1616), [`core/dr-pipeline/src/operation.rs:1640`](../core/dr-pipeline/src/operation.rs#L1640), [`core/dr-pipeline/src/operation.rs:310`](../core/dr-pipeline/src/operation.rs#L310), [`core/dr-pipeline/src/operation.rs:440`](../core/dr-pipeline/src/operation.rs#L440), [`core/dr-pipeline/src/operation.rs:450`](../core/dr-pipeline/src/operation.rs#L450), [`core/dr-pipeline/src/operation.rs:600`](../core/dr-pipeline/src/operation.rs#L600) | -| FR-DEV-3f | [`core/dr-film/src/bake.rs:271`](../core/dr-film/src/bake.rs#L271), [`core/dr-film/src/bake.rs:62`](../core/dr-film/src/bake.rs#L62), [`core/dr-film/src/boolean_grain.rs:1`](../core/dr-film/src/boolean_grain.rs#L1), [`core/dr-film/src/boolean_grain.rs:78`](../core/dr-film/src/boolean_grain.rs#L78), [`core/dr-film/src/grain.rs:140`](../core/dr-film/src/grain.rs#L140), [`core/dr-film/src/grain.rs:1`](../core/dr-film/src/grain.rs#L1), [`core/dr-film/src/grain.rs:302`](../core/dr-film/src/grain.rs#L302), [`core/dr-film/src/grain.rs:79`](../core/dr-film/src/grain.rs#L79), [`core/dr-film/src/lib.rs:160`](../core/dr-film/src/lib.rs#L160), [`core/dr-film/src/lib.rs:1`](../core/dr-film/src/lib.rs#L1), [`core/dr-film/src/profile.rs:100`](../core/dr-film/src/profile.rs#L100), [`core/dr-film/src/profile.rs:142`](../core/dr-film/src/profile.rs#L142), [`core/dr-film/src/profile.rs:182`](../core/dr-film/src/profile.rs#L182), [`core/dr-film/src/profile.rs:259`](../core/dr-film/src/profile.rs#L259), [`core/dr-film/src/profile.rs:502`](../core/dr-film/src/profile.rs#L502), [`core/dr-film/src/profile.rs:73`](../core/dr-film/src/profile.rs#L73), [`core/dr-gpu/src/adjust.rs:139`](../core/dr-gpu/src/adjust.rs#L139), [`core/dr-gpu/src/adjust.rs:196`](../core/dr-gpu/src/adjust.rs#L196), [`core/dr-gpu/src/adjust.rs:357`](../core/dr-gpu/src/adjust.rs#L357), [`core/dr-gpu/src/adjust.rs:483`](../core/dr-gpu/src/adjust.rs#L483), [`core/dr-gpu/src/adjust.rs:77`](../core/dr-gpu/src/adjust.rs#L77), [`core/dr-gpu/tests/film_sim.rs:191`](../core/dr-gpu/tests/film_sim.rs#L191), [`core/dr-gpu/tests/film_sim.rs:1`](../core/dr-gpu/tests/film_sim.rs#L1), [`core/dr-pipeline/src/graph.rs:102`](../core/dr-pipeline/src/graph.rs#L102), [`core/dr-pipeline/src/graph.rs:125`](../core/dr-pipeline/src/graph.rs#L125), [`core/dr-pipeline/src/graph.rs:325`](../core/dr-pipeline/src/graph.rs#L325), [`core/dr-pipeline/src/operation.rs:1065`](../core/dr-pipeline/src/operation.rs#L1065), [`core/dr-pipeline/src/operation.rs:1094`](../core/dr-pipeline/src/operation.rs#L1094), [`core/dr-pipeline/src/operation.rs:1495`](../core/dr-pipeline/src/operation.rs#L1495), [`core/dr-pipeline/src/operation.rs:296`](../core/dr-pipeline/src/operation.rs#L296), [`core/dr-pipeline/src/operation.rs:310`](../core/dr-pipeline/src/operation.rs#L310), [`core/dr-pipeline/src/ops/film_sim.rs:129`](../core/dr-pipeline/src/ops/film_sim.rs#L129), [`core/dr-pipeline/src/ops/film_sim.rs:153`](../core/dr-pipeline/src/ops/film_sim.rs#L153), [`core/dr-pipeline/src/ops/film_sim.rs:1`](../core/dr-pipeline/src/ops/film_sim.rs#L1), [`core/dr-pipeline/src/ops/film_sim.rs:331`](../core/dr-pipeline/src/ops/film_sim.rs#L331), [`core/dr-pipeline/src/ops/film_sim.rs:43`](../core/dr-pipeline/src/ops/film_sim.rs#L43), [`core/dr-pipeline/src/ops/film_sim.rs:87`](../core/dr-pipeline/src/ops/film_sim.rs#L87), [`core/dr-pipeline/src/ops/film_sim.rs:92`](../core/dr-pipeline/src/ops/film_sim.rs#L92), [`core/dr-pipeline/src/sidecar.rs:112`](../core/dr-pipeline/src/sidecar.rs#L112), [`core/dr-pipeline/src/sidecar.rs:168`](../core/dr-pipeline/src/sidecar.rs#L168), [`core/dr-pipeline/src/sidecar.rs:2015`](../core/dr-pipeline/src/sidecar.rs#L2015), [`core/dr-pipeline/src/sidecar.rs:2091`](../core/dr-pipeline/src/sidecar.rs#L2091), [`core/dr-pipeline/src/sidecar.rs:534`](../core/dr-pipeline/src/sidecar.rs#L534), [`core/dr-pipeline/src/sidecar.rs:661`](../core/dr-pipeline/src/sidecar.rs#L661), [`core/dr-pipeline/src/sidecar.rs:793`](../core/dr-pipeline/src/sidecar.rs#L793), [`core/dr-pipeline/src/state.rs:100`](../core/dr-pipeline/src/state.rs#L100), [`core/dr-pipeline/src/state.rs:115`](../core/dr-pipeline/src/state.rs#L115), [`core/dr-pipeline/src/state.rs:60`](../core/dr-pipeline/src/state.rs#L60), [`ui/dr-ui/src/develop.rs:3396`](../ui/dr-ui/src/develop.rs#L3396), [`ui/dr-ui/src/develop.rs:3413`](../ui/dr-ui/src/develop.rs#L3413), [`ui/dr-ui/src/develop.rs:3425`](../ui/dr-ui/src/develop.rs#L3425), [`ui/dr-ui/src/develop.rs:3463`](../ui/dr-ui/src/develop.rs#L3463), [`ui/dr-ui/src/develop.rs:3472`](../ui/dr-ui/src/develop.rs#L3472), [`ui/dr-ui/src/develop.rs:3575`](../ui/dr-ui/src/develop.rs#L3575), [`ui/dr-ui/src/develop.rs:3991`](../ui/dr-ui/src/develop.rs#L3991), [`ui/dr-ui/src/develop.rs:4006`](../ui/dr-ui/src/develop.rs#L4006), [`ui/dr-ui/src/lib.rs:2422`](../ui/dr-ui/src/lib.rs#L2422), [`ui/dr-ui/src/lib.rs:637`](../ui/dr-ui/src/lib.rs#L637), [`ui/dr-ui/src/lib.rs:695`](../ui/dr-ui/src/lib.rs#L695), [`ui/dr-ui/src/library.rs:530`](../ui/dr-ui/src/library.rs#L530), [`ui/dr-ui/src/library.rs:778`](../ui/dr-ui/src/library.rs#L778), [`ui/dr-ui/src/presets.rs:395`](../ui/dr-ui/src/presets.rs#L395), [`ui/dr-ui/ui/adjust.slint:1072`](../ui/dr-ui/ui/adjust.slint#L1072), [`ui/dr-ui/ui/adjust.slint:996`](../ui/dr-ui/ui/adjust.slint#L996), [`ui/dr-ui/ui/app.slint:2595`](../ui/dr-ui/ui/app.slint#L2595), [`ui/dr-ui/ui/app.slint:688`](../ui/dr-ui/ui/app.slint#L688) | +| FR-DEV-3f | [`core/dr-film/src/bake.rs:271`](../core/dr-film/src/bake.rs#L271), [`core/dr-film/src/bake.rs:62`](../core/dr-film/src/bake.rs#L62), [`core/dr-film/src/boolean_grain.rs:1`](../core/dr-film/src/boolean_grain.rs#L1), [`core/dr-film/src/boolean_grain.rs:78`](../core/dr-film/src/boolean_grain.rs#L78), [`core/dr-film/src/grain.rs:140`](../core/dr-film/src/grain.rs#L140), [`core/dr-film/src/grain.rs:1`](../core/dr-film/src/grain.rs#L1), [`core/dr-film/src/grain.rs:302`](../core/dr-film/src/grain.rs#L302), [`core/dr-film/src/grain.rs:79`](../core/dr-film/src/grain.rs#L79), [`core/dr-film/src/lib.rs:160`](../core/dr-film/src/lib.rs#L160), [`core/dr-film/src/lib.rs:1`](../core/dr-film/src/lib.rs#L1), [`core/dr-film/src/profile.rs:100`](../core/dr-film/src/profile.rs#L100), [`core/dr-film/src/profile.rs:142`](../core/dr-film/src/profile.rs#L142), [`core/dr-film/src/profile.rs:182`](../core/dr-film/src/profile.rs#L182), [`core/dr-film/src/profile.rs:259`](../core/dr-film/src/profile.rs#L259), [`core/dr-film/src/profile.rs:502`](../core/dr-film/src/profile.rs#L502), [`core/dr-film/src/profile.rs:73`](../core/dr-film/src/profile.rs#L73), [`core/dr-gpu/src/adjust.rs:139`](../core/dr-gpu/src/adjust.rs#L139), [`core/dr-gpu/src/adjust.rs:196`](../core/dr-gpu/src/adjust.rs#L196), [`core/dr-gpu/src/adjust.rs:357`](../core/dr-gpu/src/adjust.rs#L357), [`core/dr-gpu/src/adjust.rs:483`](../core/dr-gpu/src/adjust.rs#L483), [`core/dr-gpu/src/adjust.rs:77`](../core/dr-gpu/src/adjust.rs#L77), [`core/dr-gpu/tests/film_sim.rs:191`](../core/dr-gpu/tests/film_sim.rs#L191), [`core/dr-gpu/tests/film_sim.rs:1`](../core/dr-gpu/tests/film_sim.rs#L1), [`core/dr-pipeline/src/graph.rs:102`](../core/dr-pipeline/src/graph.rs#L102), [`core/dr-pipeline/src/graph.rs:125`](../core/dr-pipeline/src/graph.rs#L125), [`core/dr-pipeline/src/graph.rs:325`](../core/dr-pipeline/src/graph.rs#L325), [`core/dr-pipeline/src/operation.rs:1065`](../core/dr-pipeline/src/operation.rs#L1065), [`core/dr-pipeline/src/operation.rs:1094`](../core/dr-pipeline/src/operation.rs#L1094), [`core/dr-pipeline/src/operation.rs:1495`](../core/dr-pipeline/src/operation.rs#L1495), [`core/dr-pipeline/src/operation.rs:296`](../core/dr-pipeline/src/operation.rs#L296), [`core/dr-pipeline/src/operation.rs:310`](../core/dr-pipeline/src/operation.rs#L310), [`core/dr-pipeline/src/ops/film_sim.rs:129`](../core/dr-pipeline/src/ops/film_sim.rs#L129), [`core/dr-pipeline/src/ops/film_sim.rs:153`](../core/dr-pipeline/src/ops/film_sim.rs#L153), [`core/dr-pipeline/src/ops/film_sim.rs:1`](../core/dr-pipeline/src/ops/film_sim.rs#L1), [`core/dr-pipeline/src/ops/film_sim.rs:331`](../core/dr-pipeline/src/ops/film_sim.rs#L331), [`core/dr-pipeline/src/ops/film_sim.rs:43`](../core/dr-pipeline/src/ops/film_sim.rs#L43), [`core/dr-pipeline/src/ops/film_sim.rs:87`](../core/dr-pipeline/src/ops/film_sim.rs#L87), [`core/dr-pipeline/src/ops/film_sim.rs:92`](../core/dr-pipeline/src/ops/film_sim.rs#L92), [`core/dr-pipeline/src/sidecar.rs:112`](../core/dr-pipeline/src/sidecar.rs#L112), [`core/dr-pipeline/src/sidecar.rs:168`](../core/dr-pipeline/src/sidecar.rs#L168), [`core/dr-pipeline/src/sidecar.rs:2015`](../core/dr-pipeline/src/sidecar.rs#L2015), [`core/dr-pipeline/src/sidecar.rs:2091`](../core/dr-pipeline/src/sidecar.rs#L2091), [`core/dr-pipeline/src/sidecar.rs:534`](../core/dr-pipeline/src/sidecar.rs#L534), [`core/dr-pipeline/src/sidecar.rs:661`](../core/dr-pipeline/src/sidecar.rs#L661), [`core/dr-pipeline/src/sidecar.rs:793`](../core/dr-pipeline/src/sidecar.rs#L793), [`core/dr-pipeline/src/state.rs:100`](../core/dr-pipeline/src/state.rs#L100), [`core/dr-pipeline/src/state.rs:115`](../core/dr-pipeline/src/state.rs#L115), [`core/dr-pipeline/src/state.rs:60`](../core/dr-pipeline/src/state.rs#L60), [`ui/dr-ui/src/develop.rs:3396`](../ui/dr-ui/src/develop.rs#L3396), [`ui/dr-ui/src/develop.rs:3413`](../ui/dr-ui/src/develop.rs#L3413), [`ui/dr-ui/src/develop.rs:3425`](../ui/dr-ui/src/develop.rs#L3425), [`ui/dr-ui/src/develop.rs:3463`](../ui/dr-ui/src/develop.rs#L3463), [`ui/dr-ui/src/develop.rs:3472`](../ui/dr-ui/src/develop.rs#L3472), [`ui/dr-ui/src/develop.rs:3575`](../ui/dr-ui/src/develop.rs#L3575), [`ui/dr-ui/src/develop.rs:3991`](../ui/dr-ui/src/develop.rs#L3991), [`ui/dr-ui/src/develop.rs:4006`](../ui/dr-ui/src/develop.rs#L4006), [`ui/dr-ui/src/lib.rs:2428`](../ui/dr-ui/src/lib.rs#L2428), [`ui/dr-ui/src/lib.rs:638`](../ui/dr-ui/src/lib.rs#L638), [`ui/dr-ui/src/lib.rs:696`](../ui/dr-ui/src/lib.rs#L696), [`ui/dr-ui/src/library.rs:530`](../ui/dr-ui/src/library.rs#L530), [`ui/dr-ui/src/library.rs:778`](../ui/dr-ui/src/library.rs#L778), [`ui/dr-ui/src/presets.rs:395`](../ui/dr-ui/src/presets.rs#L395), [`ui/dr-ui/ui/adjust.slint:1072`](../ui/dr-ui/ui/adjust.slint#L1072), [`ui/dr-ui/ui/adjust.slint:996`](../ui/dr-ui/ui/adjust.slint#L996), [`ui/dr-ui/ui/app.slint:2595`](../ui/dr-ui/ui/app.slint#L2595), [`ui/dr-ui/ui/app.slint:688`](../ui/dr-ui/ui/app.slint#L688) | | FR-DEV-3h | [`core/dr-decode/src/lib.rs:404`](../core/dr-decode/src/lib.rs#L404), [`core/dr-decode/src/preview.rs:29`](../core/dr-decode/src/preview.rs#L29), [`core/dr-pipeline/src/framing.rs:1050`](../core/dr-pipeline/src/framing.rs#L1050), [`core/dr-pipeline/src/framing.rs:328`](../core/dr-pipeline/src/framing.rs#L328), [`core/dr-pipeline/src/framing.rs:488`](../core/dr-pipeline/src/framing.rs#L488), [`core/dr-types/src/lib.rs:337`](../core/dr-types/src/lib.rs#L337), [`core/dr-types/src/lib.rs:445`](../core/dr-types/src/lib.rs#L445), [`core/dr-types/src/lib.rs:457`](../core/dr-types/src/lib.rs#L457), [`core/dr-types/src/lib.rs:473`](../core/dr-types/src/lib.rs#L473), [`ui/dr-ui/src/develop.rs:139`](../ui/dr-ui/src/develop.rs#L139), [`ui/dr-ui/src/develop.rs:2210`](../ui/dr-ui/src/develop.rs#L2210), [`ui/dr-ui/src/segmentation.rs:451`](../ui/dr-ui/src/segmentation.rs#L451) | | FR-DEV-4 | [`core/dr-gpu/src/adjust.rs:770`](../core/dr-gpu/src/adjust.rs#L770), [`core/dr-gpu/src/lib.rs:351`](../core/dr-gpu/src/lib.rs#L351), [`ui/dr-ui/ui/crop.slint:1`](../ui/dr-ui/ui/crop.slint#L1) | -| FR-DEV-5 | [`core/dr-pipeline/src/graph.rs:346`](../core/dr-pipeline/src/graph.rs#L346), [`core/dr-pipeline/src/graph.rs:385`](../core/dr-pipeline/src/graph.rs#L385), [`core/dr-pipeline/src/history.rs:102`](../core/dr-pipeline/src/history.rs#L102), [`core/dr-pipeline/src/history.rs:110`](../core/dr-pipeline/src/history.rs#L110), [`core/dr-pipeline/src/history.rs:127`](../core/dr-pipeline/src/history.rs#L127), [`core/dr-pipeline/src/history.rs:184`](../core/dr-pipeline/src/history.rs#L184), [`core/dr-pipeline/src/history.rs:1`](../core/dr-pipeline/src/history.rs#L1), [`core/dr-pipeline/src/history.rs:214`](../core/dr-pipeline/src/history.rs#L214), [`core/dr-pipeline/src/history.rs:234`](../core/dr-pipeline/src/history.rs#L234), [`core/dr-pipeline/src/history.rs:293`](../core/dr-pipeline/src/history.rs#L293), [`core/dr-pipeline/src/history.rs:479`](../core/dr-pipeline/src/history.rs#L479), [`core/dr-pipeline/src/history.rs:489`](../core/dr-pipeline/src/history.rs#L489), [`core/dr-pipeline/src/history.rs:499`](../core/dr-pipeline/src/history.rs#L499), [`core/dr-pipeline/src/history.rs:526`](../core/dr-pipeline/src/history.rs#L526), [`core/dr-pipeline/src/history.rs:86`](../core/dr-pipeline/src/history.rs#L86), [`core/dr-pipeline/src/state.rs:1`](../core/dr-pipeline/src/state.rs#L1), [`core/dr-pipeline/src/state.rs:75`](../core/dr-pipeline/src/state.rs#L75), [`ui/dr-ui/src/develop.rs:3425`](../ui/dr-ui/src/develop.rs#L3425), [`ui/dr-ui/src/develop.rs:4006`](../ui/dr-ui/src/develop.rs#L4006), [`ui/dr-ui/src/develop.rs:4036`](../ui/dr-ui/src/develop.rs#L4036), [`ui/dr-ui/src/develop.rs:4049`](../ui/dr-ui/src/develop.rs#L4049), [`ui/dr-ui/src/develop.rs:4061`](../ui/dr-ui/src/develop.rs#L4061), [`ui/dr-ui/src/develop.rs:4077`](../ui/dr-ui/src/develop.rs#L4077), [`ui/dr-ui/src/develop.rs:4109`](../ui/dr-ui/src/develop.rs#L4109), [`ui/dr-ui/src/develop.rs:4113`](../ui/dr-ui/src/develop.rs#L4113), [`ui/dr-ui/src/develop.rs:4132`](../ui/dr-ui/src/develop.rs#L4132), [`ui/dr-ui/src/develop.rs:4148`](../ui/dr-ui/src/develop.rs#L4148), [`ui/dr-ui/src/develop.rs:709`](../ui/dr-ui/src/develop.rs#L709), [`ui/dr-ui/src/labels.rs:12`](../ui/dr-ui/src/labels.rs#L12), [`ui/dr-ui/src/labels.rs:217`](../ui/dr-ui/src/labels.rs#L217), [`ui/dr-ui/src/lib.rs:1581`](../ui/dr-ui/src/lib.rs#L1581), [`ui/dr-ui/src/lib.rs:1624`](../ui/dr-ui/src/lib.rs#L1624), [`ui/dr-ui/src/lib.rs:1632`](../ui/dr-ui/src/lib.rs#L1632), [`ui/dr-ui/src/lib.rs:2618`](../ui/dr-ui/src/lib.rs#L2618), [`ui/dr-ui/ui/history.slint:1`](../ui/dr-ui/ui/history.slint#L1) | -| FR-DEV-6 | [`core/dr-pipeline/src/preset.rs:1`](../core/dr-pipeline/src/preset.rs#L1), [`core/dr-pipeline/src/preset.rs:381`](../core/dr-pipeline/src/preset.rs#L381), [`core/dr-pipeline/src/preset.rs:414`](../core/dr-pipeline/src/preset.rs#L414), [`core/dr-pipeline/src/starter.rs:1`](../core/dr-pipeline/src/starter.rs#L1), [`core/dr-preset-xmp/src/lib.rs:1`](../core/dr-preset-xmp/src/lib.rs#L1), [`core/dr-types/src/settings.rs:272`](../core/dr-types/src/settings.rs#L272), [`ui/dr-ui/src/develop.rs:3948`](../ui/dr-ui/src/develop.rs#L3948), [`ui/dr-ui/src/develop.rs:3969`](../ui/dr-ui/src/develop.rs#L3969), [`ui/dr-ui/src/lib.rs:1548`](../ui/dr-ui/src/lib.rs#L1548), [`ui/dr-ui/src/lib.rs:2233`](../ui/dr-ui/src/lib.rs#L2233), [`ui/dr-ui/src/lib.rs:2262`](../ui/dr-ui/src/lib.rs#L2262), [`ui/dr-ui/src/library.rs:1903`](../ui/dr-ui/src/library.rs#L1903), [`ui/dr-ui/src/library.rs:492`](../ui/dr-ui/src/library.rs#L492), [`ui/dr-ui/src/library.rs:520`](../ui/dr-ui/src/library.rs#L520), [`ui/dr-ui/src/library_ui.rs:2966`](../ui/dr-ui/src/library_ui.rs#L2966), [`ui/dr-ui/src/library_ui.rs:3366`](../ui/dr-ui/src/library_ui.rs#L3366), [`ui/dr-ui/src/library_ui.rs:500`](../ui/dr-ui/src/library_ui.rs#L500), [`ui/dr-ui/src/preset_store.rs:1`](../ui/dr-ui/src/preset_store.rs#L1), [`ui/dr-ui/src/presets.rs:1`](../ui/dr-ui/src/presets.rs#L1), [`ui/dr-ui/src/presets.rs:657`](../ui/dr-ui/src/presets.rs#L657), [`ui/dr-ui/src/presets.rs:691`](../ui/dr-ui/src/presets.rs#L691), [`ui/dr-ui/ui/adjust.slint:740`](../ui/dr-ui/ui/adjust.slint#L740), [`ui/dr-ui/ui/adjust.slint:767`](../ui/dr-ui/ui/adjust.slint#L767), [`ui/dr-ui/ui/adjust.slint:801`](../ui/dr-ui/ui/adjust.slint#L801), [`ui/dr-ui/ui/app.slint:1575`](../ui/dr-ui/ui/app.slint#L1575), [`ui/dr-ui/ui/app.slint:2684`](../ui/dr-ui/ui/app.slint#L2684), [`ui/dr-ui/ui/app.slint:760`](../ui/dr-ui/ui/app.slint#L760), [`ui/dr-ui/ui/app.slint:771`](../ui/dr-ui/ui/app.slint#L771), [`ui/dr-ui/ui/library.slint:1460`](../ui/dr-ui/ui/library.slint#L1460), [`ui/dr-ui/ui/library.slint:1466`](../ui/dr-ui/ui/library.slint#L1466), [`ui/dr-ui/ui/library.slint:3979`](../ui/dr-ui/ui/library.slint#L3979), [`ui/dr-ui/ui/library.slint:3994`](../ui/dr-ui/ui/library.slint#L3994), [`ui/dr-ui/ui/presets.slint:105`](../ui/dr-ui/ui/presets.slint#L105), [`ui/dr-ui/ui/presets.slint:113`](../ui/dr-ui/ui/presets.slint#L113), [`ui/dr-ui/ui/presets.slint:19`](../ui/dr-ui/ui/presets.slint#L19), [`ui/dr-ui/ui/presets.slint:276`](../ui/dr-ui/ui/presets.slint#L276), [`ui/dr-ui/ui/presets.slint:297`](../ui/dr-ui/ui/presets.slint#L297), [`ui/dr-ui/ui/presets.slint:5`](../ui/dr-ui/ui/presets.slint#L5), [`ui/dr-ui/ui/presets.slint:60`](../ui/dr-ui/ui/presets.slint#L60), [`ui/dr-ui/ui/settings.slint:101`](../ui/dr-ui/ui/settings.slint#L101), [`ui/dr-ui/ui/settings.slint:557`](../ui/dr-ui/ui/settings.slint#L557) | -| FR-DEV-7 | [`core/dr-pipeline/src/history.rs:214`](../core/dr-pipeline/src/history.rs#L214), [`core/dr-pipeline/src/history.rs:499`](../core/dr-pipeline/src/history.rs#L499), [`core/dr-pipeline/src/history.rs:526`](../core/dr-pipeline/src/history.rs#L526), [`ui/dr-ui/src/develop.rs:4077`](../ui/dr-ui/src/develop.rs#L4077), [`ui/dr-ui/src/develop.rs:4109`](../ui/dr-ui/src/develop.rs#L4109), [`ui/dr-ui/src/lib.rs:1632`](../ui/dr-ui/src/lib.rs#L1632), [`ui/dr-ui/src/lib.rs:2618`](../ui/dr-ui/src/lib.rs#L2618), [`ui/dr-ui/ui/history.slint:1`](../ui/dr-ui/ui/history.slint#L1) | -| FR-DEV-8 | [`core/dr-gpu/src/detail.rs:405`](../core/dr-gpu/src/detail.rs#L405), [`core/dr-gpu/src/detail.rs:623`](../core/dr-gpu/src/detail.rs#L623), [`core/dr-gpu/tests/detail_instances.rs:1`](../core/dr-gpu/tests/detail_instances.rs#L1), [`core/dr-gpu/tests/spot_removal.rs:1`](../core/dr-gpu/tests/spot_removal.rs#L1), [`core/dr-pipeline/src/detail.rs:410`](../core/dr-pipeline/src/detail.rs#L410), [`core/dr-pipeline/src/detail.rs:434`](../core/dr-pipeline/src/detail.rs#L434), [`core/dr-pipeline/src/detail.rs:469`](../core/dr-pipeline/src/detail.rs#L469), [`core/dr-pipeline/src/detail.rs:555`](../core/dr-pipeline/src/detail.rs#L555), [`core/dr-pipeline/src/graph.rs:114`](../core/dr-pipeline/src/graph.rs#L114), [`core/dr-pipeline/src/graph.rs:192`](../core/dr-pipeline/src/graph.rs#L192), [`core/dr-pipeline/src/graph.rs:686`](../core/dr-pipeline/src/graph.rs#L686), [`core/dr-pipeline/src/operation.rs:330`](../core/dr-pipeline/src/operation.rs#L330), [`core/dr-pipeline/src/operation.rs:554`](../core/dr-pipeline/src/operation.rs#L554), [`core/dr-pipeline/src/sidecar.rs:184`](../core/dr-pipeline/src/sidecar.rs#L184), [`core/dr-pipeline/src/sidecar.rs:353`](../core/dr-pipeline/src/sidecar.rs#L353), [`core/dr-pipeline/src/sidecar.rs:673`](../core/dr-pipeline/src/sidecar.rs#L673), [`core/dr-pipeline/src/sidecar.rs:809`](../core/dr-pipeline/src/sidecar.rs#L809), [`core/dr-pipeline/src/sidecar.rs:863`](../core/dr-pipeline/src/sidecar.rs#L863), [`core/dr-pipeline/src/sidecar.rs:893`](../core/dr-pipeline/src/sidecar.rs#L893), [`core/dr-pipeline/src/spot.rs:115`](../core/dr-pipeline/src/spot.rs#L115), [`core/dr-pipeline/src/spot.rs:151`](../core/dr-pipeline/src/spot.rs#L151), [`core/dr-pipeline/src/spot.rs:1`](../core/dr-pipeline/src/spot.rs#L1), [`core/dr-pipeline/src/spot.rs:207`](../core/dr-pipeline/src/spot.rs#L207), [`core/dr-pipeline/src/spot.rs:387`](../core/dr-pipeline/src/spot.rs#L387), [`core/dr-pipeline/src/spot.rs:472`](../core/dr-pipeline/src/spot.rs#L472), [`core/dr-pipeline/src/spot.rs:582`](../core/dr-pipeline/src/spot.rs#L582), [`core/dr-pipeline/src/spot.rs:673`](../core/dr-pipeline/src/spot.rs#L673), [`core/dr-pipeline/src/state.rs:103`](../core/dr-pipeline/src/state.rs#L103), [`core/dr-pipeline/tests/spot_sidecar.rs:1`](../core/dr-pipeline/tests/spot_sidecar.rs#L1), [`core/dr-pipeline/tests/spots.rs:1`](../core/dr-pipeline/tests/spots.rs#L1), [`ui/dr-ui/src/develop.rs:2362`](../ui/dr-ui/src/develop.rs#L2362), [`ui/dr-ui/src/develop.rs:2400`](../ui/dr-ui/src/develop.rs#L2400), [`ui/dr-ui/src/develop.rs:2465`](../ui/dr-ui/src/develop.rs#L2465), [`ui/dr-ui/src/develop.rs:2548`](../ui/dr-ui/src/develop.rs#L2548), [`ui/dr-ui/src/develop.rs:2562`](../ui/dr-ui/src/develop.rs#L2562), [`ui/dr-ui/src/develop.rs:796`](../ui/dr-ui/src/develop.rs#L796), [`ui/dr-ui/src/labels.rs:53`](../ui/dr-ui/src/labels.rs#L53), [`ui/dr-ui/src/lib.rs:1653`](../ui/dr-ui/src/lib.rs#L1653), [`ui/dr-ui/src/lib.rs:2736`](../ui/dr-ui/src/lib.rs#L2736), [`ui/dr-ui/src/lib.rs:360`](../ui/dr-ui/src/lib.rs#L360), [`ui/dr-ui/src/spots_ui.rs:19`](../ui/dr-ui/src/spots_ui.rs#L19), [`ui/dr-ui/src/spots_ui.rs:1`](../ui/dr-ui/src/spots_ui.rs#L1), [`ui/dr-ui/src/spots_ui.rs:265`](../ui/dr-ui/src/spots_ui.rs#L265), [`ui/dr-ui/ui/adjust.slint:832`](../ui/dr-ui/ui/adjust.slint#L832), [`ui/dr-ui/ui/app.slint:140`](../ui/dr-ui/ui/app.slint#L140), [`ui/dr-ui/ui/app.slint:1925`](../ui/dr-ui/ui/app.slint#L1925), [`ui/dr-ui/ui/app.slint:2100`](../ui/dr-ui/ui/app.slint#L2100), [`ui/dr-ui/ui/app.slint:2496`](../ui/dr-ui/ui/app.slint#L2496), [`ui/dr-ui/ui/icons.slint:338`](../ui/dr-ui/ui/icons.slint#L338), [`ui/dr-ui/ui/spots.slint:172`](../ui/dr-ui/ui/spots.slint#L172), [`ui/dr-ui/ui/spots.slint:48`](../ui/dr-ui/ui/spots.slint#L48), [`ui/dr-ui/ui/spots.slint:5`](../ui/dr-ui/ui/spots.slint#L5) | -| FR-DSP-1 | [`core/dr-gpu/src/adjust.rs:2126`](../core/dr-gpu/src/adjust.rs#L2126), [`core/dr-gpu/src/adjust.rs:2203`](../core/dr-gpu/src/adjust.rs#L2203), [`core/dr-gpu/src/adjust.rs:2288`](../core/dr-gpu/src/adjust.rs#L2288), [`core/dr-gpu/src/adjust.rs:54`](../core/dr-gpu/src/adjust.rs#L54), [`core/dr-gpu/src/adjust.rs:770`](../core/dr-gpu/src/adjust.rs#L770), [`core/dr-gpu/src/lib.rs:172`](../core/dr-gpu/src/lib.rs#L172), [`core/dr-gpu/src/lib.rs:192`](../core/dr-gpu/src/lib.rs#L192), [`core/dr-gpu/src/lib.rs:70`](../core/dr-gpu/src/lib.rs#L70), [`core/dr-gpu/src/lib.rs:93`](../core/dr-gpu/src/lib.rs#L93), [`core/dr-gpu/tests/capture_sharpen.rs:200`](../core/dr-gpu/tests/capture_sharpen.rs#L200), [`core/dr-gpu/tests/detail_stage.rs:328`](../core/dr-gpu/tests/detail_stage.rs#L328), [`core/dr-gpu/tests/local_contrast.rs:264`](../core/dr-gpu/tests/local_contrast.rs#L264), [`core/dr-gpu/tests/noise_reduction.rs:378`](../core/dr-gpu/tests/noise_reduction.rs#L378), [`core/dr-pipeline/src/detail.rs:136`](../core/dr-pipeline/src/detail.rs#L136), [`core/dr-pipeline/src/detail.rs:524`](../core/dr-pipeline/src/detail.rs#L524), [`core/dr-pipeline/src/graph.rs:548`](../core/dr-pipeline/src/graph.rs#L548), [`core/dr-pipeline/src/graph.rs:578`](../core/dr-pipeline/src/graph.rs#L578), [`core/dr-pipeline/src/ops/capture_sharpen.rs:1`](../core/dr-pipeline/src/ops/capture_sharpen.rs#L1), [`core/dr-pipeline/src/ops/capture_sharpen.rs:659`](../core/dr-pipeline/src/ops/capture_sharpen.rs#L659), [`core/dr-pipeline/src/ops/local_contrast.rs:1`](../core/dr-pipeline/src/ops/local_contrast.rs#L1), [`core/dr-pipeline/src/ops/local_contrast.rs:971`](../core/dr-pipeline/src/ops/local_contrast.rs#L971), [`core/dr-pipeline/src/ops/noise_reduction.rs:700`](../core/dr-pipeline/src/ops/noise_reduction.rs#L700), [`core/dr-pipeline/src/spot.rs:673`](../core/dr-pipeline/src/spot.rs#L673), [`ui/dr-ui/src/develop.rs:2995`](../ui/dr-ui/src/develop.rs#L2995), [`ui/dr-ui/src/develop.rs:4403`](../ui/dr-ui/src/develop.rs#L4403), [`ui/dr-ui/src/develop.rs:4886`](../ui/dr-ui/src/develop.rs#L4886), [`ui/dr-ui/src/develop.rs:4920`](../ui/dr-ui/src/develop.rs#L4920), [`ui/dr-ui/src/lib.rs:845`](../ui/dr-ui/src/lib.rs#L845), [`ui/dr-ui/src/lib.rs:904`](../ui/dr-ui/src/lib.rs#L904), [`ui/dr-ui/src/lib.rs:91`](../ui/dr-ui/src/lib.rs#L91) | +| FR-DEV-5 | [`core/dr-pipeline/src/graph.rs:346`](../core/dr-pipeline/src/graph.rs#L346), [`core/dr-pipeline/src/graph.rs:385`](../core/dr-pipeline/src/graph.rs#L385), [`core/dr-pipeline/src/history.rs:102`](../core/dr-pipeline/src/history.rs#L102), [`core/dr-pipeline/src/history.rs:110`](../core/dr-pipeline/src/history.rs#L110), [`core/dr-pipeline/src/history.rs:127`](../core/dr-pipeline/src/history.rs#L127), [`core/dr-pipeline/src/history.rs:184`](../core/dr-pipeline/src/history.rs#L184), [`core/dr-pipeline/src/history.rs:1`](../core/dr-pipeline/src/history.rs#L1), [`core/dr-pipeline/src/history.rs:214`](../core/dr-pipeline/src/history.rs#L214), [`core/dr-pipeline/src/history.rs:234`](../core/dr-pipeline/src/history.rs#L234), [`core/dr-pipeline/src/history.rs:293`](../core/dr-pipeline/src/history.rs#L293), [`core/dr-pipeline/src/history.rs:479`](../core/dr-pipeline/src/history.rs#L479), [`core/dr-pipeline/src/history.rs:489`](../core/dr-pipeline/src/history.rs#L489), [`core/dr-pipeline/src/history.rs:499`](../core/dr-pipeline/src/history.rs#L499), [`core/dr-pipeline/src/history.rs:526`](../core/dr-pipeline/src/history.rs#L526), [`core/dr-pipeline/src/history.rs:86`](../core/dr-pipeline/src/history.rs#L86), [`core/dr-pipeline/src/state.rs:1`](../core/dr-pipeline/src/state.rs#L1), [`core/dr-pipeline/src/state.rs:75`](../core/dr-pipeline/src/state.rs#L75), [`ui/dr-ui/src/develop.rs:3425`](../ui/dr-ui/src/develop.rs#L3425), [`ui/dr-ui/src/develop.rs:4006`](../ui/dr-ui/src/develop.rs#L4006), [`ui/dr-ui/src/develop.rs:4036`](../ui/dr-ui/src/develop.rs#L4036), [`ui/dr-ui/src/develop.rs:4049`](../ui/dr-ui/src/develop.rs#L4049), [`ui/dr-ui/src/develop.rs:4061`](../ui/dr-ui/src/develop.rs#L4061), [`ui/dr-ui/src/develop.rs:4077`](../ui/dr-ui/src/develop.rs#L4077), [`ui/dr-ui/src/develop.rs:4109`](../ui/dr-ui/src/develop.rs#L4109), [`ui/dr-ui/src/develop.rs:4113`](../ui/dr-ui/src/develop.rs#L4113), [`ui/dr-ui/src/develop.rs:4132`](../ui/dr-ui/src/develop.rs#L4132), [`ui/dr-ui/src/develop.rs:4148`](../ui/dr-ui/src/develop.rs#L4148), [`ui/dr-ui/src/develop.rs:709`](../ui/dr-ui/src/develop.rs#L709), [`ui/dr-ui/src/labels.rs:12`](../ui/dr-ui/src/labels.rs#L12), [`ui/dr-ui/src/labels.rs:217`](../ui/dr-ui/src/labels.rs#L217), [`ui/dr-ui/src/lib.rs:1587`](../ui/dr-ui/src/lib.rs#L1587), [`ui/dr-ui/src/lib.rs:1630`](../ui/dr-ui/src/lib.rs#L1630), [`ui/dr-ui/src/lib.rs:1638`](../ui/dr-ui/src/lib.rs#L1638), [`ui/dr-ui/src/lib.rs:2624`](../ui/dr-ui/src/lib.rs#L2624), [`ui/dr-ui/ui/history.slint:1`](../ui/dr-ui/ui/history.slint#L1) | +| FR-DEV-6 | [`core/dr-pipeline/src/preset.rs:1`](../core/dr-pipeline/src/preset.rs#L1), [`core/dr-pipeline/src/preset.rs:381`](../core/dr-pipeline/src/preset.rs#L381), [`core/dr-pipeline/src/preset.rs:414`](../core/dr-pipeline/src/preset.rs#L414), [`core/dr-pipeline/src/starter.rs:1`](../core/dr-pipeline/src/starter.rs#L1), [`core/dr-preset-xmp/src/lib.rs:1`](../core/dr-preset-xmp/src/lib.rs#L1), [`core/dr-types/src/settings.rs:272`](../core/dr-types/src/settings.rs#L272), [`ui/dr-ui/src/develop.rs:3948`](../ui/dr-ui/src/develop.rs#L3948), [`ui/dr-ui/src/develop.rs:3969`](../ui/dr-ui/src/develop.rs#L3969), [`ui/dr-ui/src/lib.rs:1554`](../ui/dr-ui/src/lib.rs#L1554), [`ui/dr-ui/src/lib.rs:2239`](../ui/dr-ui/src/lib.rs#L2239), [`ui/dr-ui/src/lib.rs:2268`](../ui/dr-ui/src/lib.rs#L2268), [`ui/dr-ui/src/library.rs:1903`](../ui/dr-ui/src/library.rs#L1903), [`ui/dr-ui/src/library.rs:492`](../ui/dr-ui/src/library.rs#L492), [`ui/dr-ui/src/library.rs:520`](../ui/dr-ui/src/library.rs#L520), [`ui/dr-ui/src/library_ui.rs:2966`](../ui/dr-ui/src/library_ui.rs#L2966), [`ui/dr-ui/src/library_ui.rs:3366`](../ui/dr-ui/src/library_ui.rs#L3366), [`ui/dr-ui/src/library_ui.rs:500`](../ui/dr-ui/src/library_ui.rs#L500), [`ui/dr-ui/src/preset_store.rs:1`](../ui/dr-ui/src/preset_store.rs#L1), [`ui/dr-ui/src/presets.rs:1`](../ui/dr-ui/src/presets.rs#L1), [`ui/dr-ui/src/presets.rs:657`](../ui/dr-ui/src/presets.rs#L657), [`ui/dr-ui/src/presets.rs:691`](../ui/dr-ui/src/presets.rs#L691), [`ui/dr-ui/ui/adjust.slint:740`](../ui/dr-ui/ui/adjust.slint#L740), [`ui/dr-ui/ui/adjust.slint:767`](../ui/dr-ui/ui/adjust.slint#L767), [`ui/dr-ui/ui/adjust.slint:801`](../ui/dr-ui/ui/adjust.slint#L801), [`ui/dr-ui/ui/app.slint:1575`](../ui/dr-ui/ui/app.slint#L1575), [`ui/dr-ui/ui/app.slint:2684`](../ui/dr-ui/ui/app.slint#L2684), [`ui/dr-ui/ui/app.slint:760`](../ui/dr-ui/ui/app.slint#L760), [`ui/dr-ui/ui/app.slint:771`](../ui/dr-ui/ui/app.slint#L771), [`ui/dr-ui/ui/library.slint:1460`](../ui/dr-ui/ui/library.slint#L1460), [`ui/dr-ui/ui/library.slint:1466`](../ui/dr-ui/ui/library.slint#L1466), [`ui/dr-ui/ui/library.slint:3979`](../ui/dr-ui/ui/library.slint#L3979), [`ui/dr-ui/ui/library.slint:3994`](../ui/dr-ui/ui/library.slint#L3994), [`ui/dr-ui/ui/presets.slint:105`](../ui/dr-ui/ui/presets.slint#L105), [`ui/dr-ui/ui/presets.slint:113`](../ui/dr-ui/ui/presets.slint#L113), [`ui/dr-ui/ui/presets.slint:19`](../ui/dr-ui/ui/presets.slint#L19), [`ui/dr-ui/ui/presets.slint:276`](../ui/dr-ui/ui/presets.slint#L276), [`ui/dr-ui/ui/presets.slint:297`](../ui/dr-ui/ui/presets.slint#L297), [`ui/dr-ui/ui/presets.slint:5`](../ui/dr-ui/ui/presets.slint#L5), [`ui/dr-ui/ui/presets.slint:60`](../ui/dr-ui/ui/presets.slint#L60), [`ui/dr-ui/ui/settings.slint:101`](../ui/dr-ui/ui/settings.slint#L101), [`ui/dr-ui/ui/settings.slint:557`](../ui/dr-ui/ui/settings.slint#L557) | +| FR-DEV-7 | [`core/dr-pipeline/src/history.rs:214`](../core/dr-pipeline/src/history.rs#L214), [`core/dr-pipeline/src/history.rs:499`](../core/dr-pipeline/src/history.rs#L499), [`core/dr-pipeline/src/history.rs:526`](../core/dr-pipeline/src/history.rs#L526), [`ui/dr-ui/src/develop.rs:4077`](../ui/dr-ui/src/develop.rs#L4077), [`ui/dr-ui/src/develop.rs:4109`](../ui/dr-ui/src/develop.rs#L4109), [`ui/dr-ui/src/lib.rs:1638`](../ui/dr-ui/src/lib.rs#L1638), [`ui/dr-ui/src/lib.rs:2624`](../ui/dr-ui/src/lib.rs#L2624), [`ui/dr-ui/ui/history.slint:1`](../ui/dr-ui/ui/history.slint#L1) | +| FR-DEV-8 | [`core/dr-gpu/src/detail.rs:405`](../core/dr-gpu/src/detail.rs#L405), [`core/dr-gpu/src/detail.rs:623`](../core/dr-gpu/src/detail.rs#L623), [`core/dr-gpu/tests/detail_instances.rs:1`](../core/dr-gpu/tests/detail_instances.rs#L1), [`core/dr-gpu/tests/spot_removal.rs:1`](../core/dr-gpu/tests/spot_removal.rs#L1), [`core/dr-pipeline/src/detail.rs:410`](../core/dr-pipeline/src/detail.rs#L410), [`core/dr-pipeline/src/detail.rs:434`](../core/dr-pipeline/src/detail.rs#L434), [`core/dr-pipeline/src/detail.rs:469`](../core/dr-pipeline/src/detail.rs#L469), [`core/dr-pipeline/src/detail.rs:555`](../core/dr-pipeline/src/detail.rs#L555), [`core/dr-pipeline/src/graph.rs:114`](../core/dr-pipeline/src/graph.rs#L114), [`core/dr-pipeline/src/graph.rs:192`](../core/dr-pipeline/src/graph.rs#L192), [`core/dr-pipeline/src/graph.rs:686`](../core/dr-pipeline/src/graph.rs#L686), [`core/dr-pipeline/src/operation.rs:330`](../core/dr-pipeline/src/operation.rs#L330), [`core/dr-pipeline/src/operation.rs:554`](../core/dr-pipeline/src/operation.rs#L554), [`core/dr-pipeline/src/sidecar.rs:184`](../core/dr-pipeline/src/sidecar.rs#L184), [`core/dr-pipeline/src/sidecar.rs:353`](../core/dr-pipeline/src/sidecar.rs#L353), [`core/dr-pipeline/src/sidecar.rs:673`](../core/dr-pipeline/src/sidecar.rs#L673), [`core/dr-pipeline/src/sidecar.rs:809`](../core/dr-pipeline/src/sidecar.rs#L809), [`core/dr-pipeline/src/sidecar.rs:863`](../core/dr-pipeline/src/sidecar.rs#L863), [`core/dr-pipeline/src/sidecar.rs:893`](../core/dr-pipeline/src/sidecar.rs#L893), [`core/dr-pipeline/src/spot.rs:115`](../core/dr-pipeline/src/spot.rs#L115), [`core/dr-pipeline/src/spot.rs:151`](../core/dr-pipeline/src/spot.rs#L151), [`core/dr-pipeline/src/spot.rs:1`](../core/dr-pipeline/src/spot.rs#L1), [`core/dr-pipeline/src/spot.rs:207`](../core/dr-pipeline/src/spot.rs#L207), [`core/dr-pipeline/src/spot.rs:387`](../core/dr-pipeline/src/spot.rs#L387), [`core/dr-pipeline/src/spot.rs:472`](../core/dr-pipeline/src/spot.rs#L472), [`core/dr-pipeline/src/spot.rs:582`](../core/dr-pipeline/src/spot.rs#L582), [`core/dr-pipeline/src/spot.rs:673`](../core/dr-pipeline/src/spot.rs#L673), [`core/dr-pipeline/src/state.rs:103`](../core/dr-pipeline/src/state.rs#L103), [`core/dr-pipeline/tests/spot_sidecar.rs:1`](../core/dr-pipeline/tests/spot_sidecar.rs#L1), [`core/dr-pipeline/tests/spots.rs:1`](../core/dr-pipeline/tests/spots.rs#L1), [`ui/dr-ui/src/develop.rs:2362`](../ui/dr-ui/src/develop.rs#L2362), [`ui/dr-ui/src/develop.rs:2400`](../ui/dr-ui/src/develop.rs#L2400), [`ui/dr-ui/src/develop.rs:2465`](../ui/dr-ui/src/develop.rs#L2465), [`ui/dr-ui/src/develop.rs:2548`](../ui/dr-ui/src/develop.rs#L2548), [`ui/dr-ui/src/develop.rs:2562`](../ui/dr-ui/src/develop.rs#L2562), [`ui/dr-ui/src/develop.rs:796`](../ui/dr-ui/src/develop.rs#L796), [`ui/dr-ui/src/labels.rs:53`](../ui/dr-ui/src/labels.rs#L53), [`ui/dr-ui/src/lib.rs:1659`](../ui/dr-ui/src/lib.rs#L1659), [`ui/dr-ui/src/lib.rs:2742`](../ui/dr-ui/src/lib.rs#L2742), [`ui/dr-ui/src/lib.rs:361`](../ui/dr-ui/src/lib.rs#L361), [`ui/dr-ui/src/spots_ui.rs:19`](../ui/dr-ui/src/spots_ui.rs#L19), [`ui/dr-ui/src/spots_ui.rs:1`](../ui/dr-ui/src/spots_ui.rs#L1), [`ui/dr-ui/src/spots_ui.rs:265`](../ui/dr-ui/src/spots_ui.rs#L265), [`ui/dr-ui/ui/adjust.slint:832`](../ui/dr-ui/ui/adjust.slint#L832), [`ui/dr-ui/ui/app.slint:140`](../ui/dr-ui/ui/app.slint#L140), [`ui/dr-ui/ui/app.slint:1925`](../ui/dr-ui/ui/app.slint#L1925), [`ui/dr-ui/ui/app.slint:2100`](../ui/dr-ui/ui/app.slint#L2100), [`ui/dr-ui/ui/app.slint:2496`](../ui/dr-ui/ui/app.slint#L2496), [`ui/dr-ui/ui/icons.slint:338`](../ui/dr-ui/ui/icons.slint#L338), [`ui/dr-ui/ui/spots.slint:172`](../ui/dr-ui/ui/spots.slint#L172), [`ui/dr-ui/ui/spots.slint:48`](../ui/dr-ui/ui/spots.slint#L48), [`ui/dr-ui/ui/spots.slint:5`](../ui/dr-ui/ui/spots.slint#L5) | +| FR-DSP-1 | [`core/dr-gpu/src/adjust.rs:2126`](../core/dr-gpu/src/adjust.rs#L2126), [`core/dr-gpu/src/adjust.rs:2203`](../core/dr-gpu/src/adjust.rs#L2203), [`core/dr-gpu/src/adjust.rs:2288`](../core/dr-gpu/src/adjust.rs#L2288), [`core/dr-gpu/src/adjust.rs:54`](../core/dr-gpu/src/adjust.rs#L54), [`core/dr-gpu/src/adjust.rs:770`](../core/dr-gpu/src/adjust.rs#L770), [`core/dr-gpu/src/lib.rs:172`](../core/dr-gpu/src/lib.rs#L172), [`core/dr-gpu/src/lib.rs:192`](../core/dr-gpu/src/lib.rs#L192), [`core/dr-gpu/src/lib.rs:70`](../core/dr-gpu/src/lib.rs#L70), [`core/dr-gpu/src/lib.rs:93`](../core/dr-gpu/src/lib.rs#L93), [`core/dr-gpu/tests/capture_sharpen.rs:200`](../core/dr-gpu/tests/capture_sharpen.rs#L200), [`core/dr-gpu/tests/detail_stage.rs:328`](../core/dr-gpu/tests/detail_stage.rs#L328), [`core/dr-gpu/tests/local_contrast.rs:264`](../core/dr-gpu/tests/local_contrast.rs#L264), [`core/dr-gpu/tests/noise_reduction.rs:378`](../core/dr-gpu/tests/noise_reduction.rs#L378), [`core/dr-pipeline/src/detail.rs:136`](../core/dr-pipeline/src/detail.rs#L136), [`core/dr-pipeline/src/detail.rs:524`](../core/dr-pipeline/src/detail.rs#L524), [`core/dr-pipeline/src/graph.rs:548`](../core/dr-pipeline/src/graph.rs#L548), [`core/dr-pipeline/src/graph.rs:578`](../core/dr-pipeline/src/graph.rs#L578), [`core/dr-pipeline/src/ops/capture_sharpen.rs:1`](../core/dr-pipeline/src/ops/capture_sharpen.rs#L1), [`core/dr-pipeline/src/ops/capture_sharpen.rs:659`](../core/dr-pipeline/src/ops/capture_sharpen.rs#L659), [`core/dr-pipeline/src/ops/local_contrast.rs:1`](../core/dr-pipeline/src/ops/local_contrast.rs#L1), [`core/dr-pipeline/src/ops/local_contrast.rs:971`](../core/dr-pipeline/src/ops/local_contrast.rs#L971), [`core/dr-pipeline/src/ops/noise_reduction.rs:700`](../core/dr-pipeline/src/ops/noise_reduction.rs#L700), [`core/dr-pipeline/src/spot.rs:673`](../core/dr-pipeline/src/spot.rs#L673), [`ui/dr-ui/src/develop.rs:2995`](../ui/dr-ui/src/develop.rs#L2995), [`ui/dr-ui/src/develop.rs:4403`](../ui/dr-ui/src/develop.rs#L4403), [`ui/dr-ui/src/develop.rs:4886`](../ui/dr-ui/src/develop.rs#L4886), [`ui/dr-ui/src/develop.rs:4920`](../ui/dr-ui/src/develop.rs#L4920), [`ui/dr-ui/src/lib.rs:846`](../ui/dr-ui/src/lib.rs#L846), [`ui/dr-ui/src/lib.rs:905`](../ui/dr-ui/src/lib.rs#L905), [`ui/dr-ui/src/lib.rs:92`](../ui/dr-ui/src/lib.rs#L92) | | FR-DSP-3 | [`core/dr-gpu/tests/frame_budget.rs:101`](../core/dr-gpu/tests/frame_budget.rs#L101), [`core/dr-gpu/tests/local_contrast.rs:329`](../core/dr-gpu/tests/local_contrast.rs#L329), [`core/dr-pipeline/src/ops/local_contrast.rs:447`](../core/dr-pipeline/src/ops/local_contrast.rs#L447) | | FR-DSP-5 | [`core/dr-gpu/tests/frame_budget.rs:101`](../core/dr-gpu/tests/frame_budget.rs#L101), [`core/dr-gpu/tests/zoom_resolution.rs:135`](../core/dr-gpu/tests/zoom_resolution.rs#L135), [`core/dr-gpu/tests/zoom_resolution.rs:166`](../core/dr-gpu/tests/zoom_resolution.rs#L166), [`core/dr-gpu/tests/zoom_resolution.rs:1`](../core/dr-gpu/tests/zoom_resolution.rs#L1), [`core/dr-gpu/tests/zoom_resolution.rs:216`](../core/dr-gpu/tests/zoom_resolution.rs#L216) | -| FR-DSP-6 | [`core/dr-pipeline/src/operation.rs:483`](../core/dr-pipeline/src/operation.rs#L483), [`core/dr-types/src/colour.rs:1`](../core/dr-types/src/colour.rs#L1), [`ui/dr-ui/src/develop.rs:3025`](../ui/dr-ui/src/develop.rs#L3025), [`ui/dr-ui/src/develop.rs:6314`](../ui/dr-ui/src/develop.rs#L6314), [`ui/dr-ui/src/lib.rs:1609`](../ui/dr-ui/src/lib.rs#L1609), [`ui/dr-ui/src/lib.rs:3119`](../ui/dr-ui/src/lib.rs#L3119) | -| FR-DSP-7 | [`core/dr-gpu/src/histogram.rs:147`](../core/dr-gpu/src/histogram.rs#L147), [`core/dr-gpu/src/histogram.rs:1`](../core/dr-gpu/src/histogram.rs#L1), [`core/dr-gpu/src/histogram.rs:281`](../core/dr-gpu/src/histogram.rs#L281), [`core/dr-gpu/src/histogram.rs:50`](../core/dr-gpu/src/histogram.rs#L50), [`core/dr-gpu/src/shaders/histogram.wgsl:1`](../core/dr-gpu/src/shaders/histogram.wgsl#L1), [`ui/dr-ui/src/develop.rs:3074`](../ui/dr-ui/src/develop.rs#L3074), [`ui/dr-ui/src/develop.rs:5988`](../ui/dr-ui/src/develop.rs#L5988), [`ui/dr-ui/src/develop.rs:6020`](../ui/dr-ui/src/develop.rs#L6020), [`ui/dr-ui/src/develop.rs:720`](../ui/dr-ui/src/develop.rs#L720), [`ui/dr-ui/src/histogram.rs:1`](../ui/dr-ui/src/histogram.rs#L1), [`ui/dr-ui/src/lib.rs:1719`](../ui/dr-ui/src/lib.rs#L1719), [`ui/dr-ui/src/lib.rs:339`](../ui/dr-ui/src/lib.rs#L339), [`ui/dr-ui/ui/app.slint:74`](../ui/dr-ui/ui/app.slint#L74), [`ui/dr-ui/ui/histogram.slint:168`](../ui/dr-ui/ui/histogram.slint#L168), [`ui/dr-ui/ui/histogram.slint:1`](../ui/dr-ui/ui/histogram.slint#L1) | -| FR-DSP-8 | [`platform/dr-plat/src/display.rs:1`](../platform/dr-plat/src/display.rs#L1), [`platform/dr-plat/src/display/icc.rs:1`](../platform/dr-plat/src/display/icc.rs#L1), [`platform/dr-plat/src/display/wayland.rs:1`](../platform/dr-plat/src/display/wayland.rs#L1), [`platform/dr-plat/src/display/x11.rs:1`](../platform/dr-plat/src/display/x11.rs#L1), [`ui/dr-ui/src/develop.rs:2971`](../ui/dr-ui/src/develop.rs#L2971), [`ui/dr-ui/src/develop.rs:3025`](../ui/dr-ui/src/develop.rs#L3025), [`ui/dr-ui/src/develop.rs:6314`](../ui/dr-ui/src/develop.rs#L6314), [`ui/dr-ui/src/develop.rs:6360`](../ui/dr-ui/src/develop.rs#L6360), [`ui/dr-ui/src/develop.rs:6379`](../ui/dr-ui/src/develop.rs#L6379), [`ui/dr-ui/src/develop.rs:827`](../ui/dr-ui/src/develop.rs#L827), [`ui/dr-ui/src/display_ui.rs:192`](../ui/dr-ui/src/display_ui.rs#L192), [`ui/dr-ui/src/display_ui.rs:1`](../ui/dr-ui/src/display_ui.rs#L1), [`ui/dr-ui/src/display_ui.rs:325`](../ui/dr-ui/src/display_ui.rs#L325), [`ui/dr-ui/src/display_ui.rs:346`](../ui/dr-ui/src/display_ui.rs#L346), [`ui/dr-ui/src/display_ui.rs:379`](../ui/dr-ui/src/display_ui.rs#L379), [`ui/dr-ui/src/lib.rs:1570`](../ui/dr-ui/src/lib.rs#L1570), [`ui/dr-ui/src/lib.rs:1609`](../ui/dr-ui/src/lib.rs#L1609), [`ui/dr-ui/src/lib.rs:3032`](../ui/dr-ui/src/lib.rs#L3032), [`ui/dr-ui/src/lib.rs:3119`](../ui/dr-ui/src/lib.rs#L3119), [`ui/dr-ui/ui/app.slint:1773`](../ui/dr-ui/ui/app.slint#L1773), [`ui/dr-ui/ui/app.slint:53`](../ui/dr-ui/ui/app.slint#L53), [`ui/dr-ui/ui/settings.slint:124`](../ui/dr-ui/ui/settings.slint#L124), [`ui/dr-ui/ui/settings.slint:794`](../ui/dr-ui/ui/settings.slint#L794) | +| FR-DSP-6 | [`core/dr-pipeline/src/operation.rs:483`](../core/dr-pipeline/src/operation.rs#L483), [`core/dr-types/src/colour.rs:1`](../core/dr-types/src/colour.rs#L1), [`ui/dr-ui/src/develop.rs:3025`](../ui/dr-ui/src/develop.rs#L3025), [`ui/dr-ui/src/develop.rs:6314`](../ui/dr-ui/src/develop.rs#L6314), [`ui/dr-ui/src/lib.rs:1615`](../ui/dr-ui/src/lib.rs#L1615), [`ui/dr-ui/src/lib.rs:3125`](../ui/dr-ui/src/lib.rs#L3125) | +| FR-DSP-7 | [`core/dr-gpu/src/histogram.rs:147`](../core/dr-gpu/src/histogram.rs#L147), [`core/dr-gpu/src/histogram.rs:1`](../core/dr-gpu/src/histogram.rs#L1), [`core/dr-gpu/src/histogram.rs:281`](../core/dr-gpu/src/histogram.rs#L281), [`core/dr-gpu/src/histogram.rs:50`](../core/dr-gpu/src/histogram.rs#L50), [`core/dr-gpu/src/shaders/histogram.wgsl:1`](../core/dr-gpu/src/shaders/histogram.wgsl#L1), [`ui/dr-ui/src/develop.rs:3074`](../ui/dr-ui/src/develop.rs#L3074), [`ui/dr-ui/src/develop.rs:5988`](../ui/dr-ui/src/develop.rs#L5988), [`ui/dr-ui/src/develop.rs:6020`](../ui/dr-ui/src/develop.rs#L6020), [`ui/dr-ui/src/develop.rs:720`](../ui/dr-ui/src/develop.rs#L720), [`ui/dr-ui/src/histogram.rs:1`](../ui/dr-ui/src/histogram.rs#L1), [`ui/dr-ui/src/lib.rs:1725`](../ui/dr-ui/src/lib.rs#L1725), [`ui/dr-ui/src/lib.rs:340`](../ui/dr-ui/src/lib.rs#L340), [`ui/dr-ui/ui/app.slint:74`](../ui/dr-ui/ui/app.slint#L74), [`ui/dr-ui/ui/histogram.slint:168`](../ui/dr-ui/ui/histogram.slint#L168), [`ui/dr-ui/ui/histogram.slint:1`](../ui/dr-ui/ui/histogram.slint#L1) | +| FR-DSP-8 | [`platform/dr-plat/src/display.rs:1`](../platform/dr-plat/src/display.rs#L1), [`platform/dr-plat/src/display/icc.rs:1`](../platform/dr-plat/src/display/icc.rs#L1), [`platform/dr-plat/src/display/wayland.rs:1`](../platform/dr-plat/src/display/wayland.rs#L1), [`platform/dr-plat/src/display/x11.rs:1`](../platform/dr-plat/src/display/x11.rs#L1), [`ui/dr-ui/src/develop.rs:2971`](../ui/dr-ui/src/develop.rs#L2971), [`ui/dr-ui/src/develop.rs:3025`](../ui/dr-ui/src/develop.rs#L3025), [`ui/dr-ui/src/develop.rs:6314`](../ui/dr-ui/src/develop.rs#L6314), [`ui/dr-ui/src/develop.rs:6360`](../ui/dr-ui/src/develop.rs#L6360), [`ui/dr-ui/src/develop.rs:6379`](../ui/dr-ui/src/develop.rs#L6379), [`ui/dr-ui/src/develop.rs:827`](../ui/dr-ui/src/develop.rs#L827), [`ui/dr-ui/src/display_ui.rs:192`](../ui/dr-ui/src/display_ui.rs#L192), [`ui/dr-ui/src/display_ui.rs:1`](../ui/dr-ui/src/display_ui.rs#L1), [`ui/dr-ui/src/display_ui.rs:325`](../ui/dr-ui/src/display_ui.rs#L325), [`ui/dr-ui/src/display_ui.rs:346`](../ui/dr-ui/src/display_ui.rs#L346), [`ui/dr-ui/src/display_ui.rs:379`](../ui/dr-ui/src/display_ui.rs#L379), [`ui/dr-ui/src/lib.rs:1576`](../ui/dr-ui/src/lib.rs#L1576), [`ui/dr-ui/src/lib.rs:1615`](../ui/dr-ui/src/lib.rs#L1615), [`ui/dr-ui/src/lib.rs:3038`](../ui/dr-ui/src/lib.rs#L3038), [`ui/dr-ui/src/lib.rs:3125`](../ui/dr-ui/src/lib.rs#L3125), [`ui/dr-ui/ui/app.slint:1773`](../ui/dr-ui/ui/app.slint#L1773), [`ui/dr-ui/ui/app.slint:53`](../ui/dr-ui/ui/app.slint#L53), [`ui/dr-ui/ui/settings.slint:124`](../ui/dr-ui/ui/settings.slint#L124), [`ui/dr-ui/ui/settings.slint:794`](../ui/dr-ui/ui/settings.slint#L794) | | FR-EXP-1 | [`core/dr-export/src/encode.rs:1`](../core/dr-export/src/encode.rs#L1), [`core/dr-export/src/lib.rs:1`](../core/dr-export/src/lib.rs#L1), [`core/dr-types/src/settings.rs:1`](../core/dr-types/src/settings.rs#L1), [`ui/dr-ui/src/settings_ui.rs:1`](../ui/dr-ui/src/settings_ui.rs#L1) | | FR-EXP-2 | [`core/dr-export/src/encode.rs:1`](../core/dr-export/src/encode.rs#L1), [`core/dr-export/src/error.rs:26`](../core/dr-export/src/error.rs#L26), [`core/dr-export/src/icc.rs:1`](../core/dr-export/src/icc.rs#L1), [`core/dr-export/src/lib.rs:153`](../core/dr-export/src/lib.rs#L153), [`core/dr-export/src/lib.rs:1`](../core/dr-export/src/lib.rs#L1), [`core/dr-export/src/lib.rs:53`](../core/dr-export/src/lib.rs#L53), [`core/dr-gpu/src/adjust.rs:2436`](../core/dr-gpu/src/adjust.rs#L2436), [`core/dr-pipeline/src/graph.rs:538`](../core/dr-pipeline/src/graph.rs#L538), [`core/dr-pipeline/src/graph.rs:592`](../core/dr-pipeline/src/graph.rs#L592), [`core/dr-pipeline/src/operation.rs:483`](../core/dr-pipeline/src/operation.rs#L483), [`core/dr-types/src/colour.rs:1`](../core/dr-types/src/colour.rs#L1), [`core/dr-types/src/settings.rs:1`](../core/dr-types/src/settings.rs#L1), [`core/dr-types/src/settings.rs:713`](../core/dr-types/src/settings.rs#L713), [`ui/dr-ui/src/develop.rs:6379`](../ui/dr-ui/src/develop.rs#L6379), [`ui/dr-ui/src/settings_ui.rs:1`](../ui/dr-ui/src/settings_ui.rs#L1) | | FR-EXP-3 | [`core/dr-export/src/lib.rs:182`](../core/dr-export/src/lib.rs#L182), [`core/dr-export/src/lib.rs:1`](../core/dr-export/src/lib.rs#L1), [`core/dr-export/src/size.rs:136`](../core/dr-export/src/size.rs#L136), [`core/dr-export/src/size.rs:1`](../core/dr-export/src/size.rs#L1), [`core/dr-export/src/size.rs:25`](../core/dr-export/src/size.rs#L25), [`core/dr-export/src/size.rs:84`](../core/dr-export/src/size.rs#L84), [`core/dr-types/src/settings.rs:1`](../core/dr-types/src/settings.rs#L1), [`core/dr-types/src/settings.rs:758`](../core/dr-types/src/settings.rs#L758), [`core/dr-types/src/settings.rs:766`](../core/dr-types/src/settings.rs#L766), [`core/dr-types/src/settings.rs:782`](../core/dr-types/src/settings.rs#L782), [`core/dr-types/src/settings.rs:851`](../core/dr-types/src/settings.rs#L851), [`core/dr-types/src/settings.rs:879`](../core/dr-types/src/settings.rs#L879), [`core/dr-types/src/settings.rs:890`](../core/dr-types/src/settings.rs#L890), [`core/dr-types/src/settings.rs:896`](../core/dr-types/src/settings.rs#L896), [`ui/dr-ui/src/settings_ui.rs:1`](../ui/dr-ui/src/settings_ui.rs#L1), [`ui/dr-ui/src/settings_ui.rs:207`](../ui/dr-ui/src/settings_ui.rs#L207), [`ui/dr-ui/src/settings_ui.rs:220`](../ui/dr-ui/src/settings_ui.rs#L220), [`ui/dr-ui/src/settings_ui.rs:235`](../ui/dr-ui/src/settings_ui.rs#L235), [`ui/dr-ui/src/settings_ui.rs:565`](../ui/dr-ui/src/settings_ui.rs#L565), [`ui/dr-ui/ui/settings.slint:697`](../ui/dr-ui/ui/settings.slint#L697), [`ui/dr-ui/ui/settings.slint:707`](../ui/dr-ui/ui/settings.slint#L707) | | FR-EXP-4 | [`core/dr-export/src/lib.rs:1`](../core/dr-export/src/lib.rs#L1), [`core/dr-export/src/sharpen.rs:1`](../core/dr-export/src/sharpen.rs#L1), [`core/dr-export/src/size.rs:1`](../core/dr-export/src/size.rs#L1), [`ui/dr-ui/src/settings_ui.rs:1`](../ui/dr-ui/src/settings_ui.rs#L1) | | FR-EXP-5 | [`ui/dr-ui/src/settings_store.rs:1`](../ui/dr-ui/src/settings_store.rs#L1) | -| FR-EXP-6 | [`core/dr-export/src/lib.rs:1`](../core/dr-export/src/lib.rs#L1), [`core/dr-export/src/name.rs:1`](../core/dr-export/src/name.rs#L1), [`core/dr-types/src/settings.rs:1`](../core/dr-types/src/settings.rs#L1), [`ui/dr-ui/src/export.rs:1`](../ui/dr-ui/src/export.rs#L1), [`ui/dr-ui/src/lib.rs:450`](../ui/dr-ui/src/lib.rs#L450), [`ui/dr-ui/src/settings_ui.rs:1`](../ui/dr-ui/src/settings_ui.rs#L1), [`ui/dr-ui/src/settings_ui.rs:49`](../ui/dr-ui/src/settings_ui.rs#L49), [`ui/dr-ui/src/settings_ui.rs:679`](../ui/dr-ui/src/settings_ui.rs#L679) | -| FR-EXP-7 | [`ui/dr-ui/src/activity.rs:83`](../ui/dr-ui/src/activity.rs#L83), [`ui/dr-ui/src/export.rs:1`](../ui/dr-ui/src/export.rs#L1), [`ui/dr-ui/src/export.rs:942`](../ui/dr-ui/src/export.rs#L942), [`ui/dr-ui/src/lib.rs:229`](../ui/dr-ui/src/lib.rs#L229), [`ui/dr-ui/src/lib.rs:2364`](../ui/dr-ui/src/lib.rs#L2364), [`ui/dr-ui/src/lib.rs:450`](../ui/dr-ui/src/lib.rs#L450), [`ui/dr-ui/src/lib.rs:485`](../ui/dr-ui/src/lib.rs#L485), [`ui/dr-ui/src/lib.rs:512`](../ui/dr-ui/src/lib.rs#L512), [`ui/dr-ui/src/library_ui.rs:3749`](../ui/dr-ui/src/library_ui.rs#L3749), [`ui/dr-ui/src/library_ui.rs:591`](../ui/dr-ui/src/library_ui.rs#L591), [`ui/dr-ui/src/library_ui.rs:691`](../ui/dr-ui/src/library_ui.rs#L691), [`ui/dr-ui/src/library_ui.rs:6935`](../ui/dr-ui/src/library_ui.rs#L6935), [`ui/dr-ui/src/library_ui.rs:7012`](../ui/dr-ui/src/library_ui.rs#L7012), [`ui/dr-ui/src/library_ui.rs:7024`](../ui/dr-ui/src/library_ui.rs#L7024), [`ui/dr-ui/src/library_ui.rs:748`](../ui/dr-ui/src/library_ui.rs#L748), [`ui/dr-ui/ui/app.slint:1099`](../ui/dr-ui/ui/app.slint#L1099), [`ui/dr-ui/ui/app.slint:1585`](../ui/dr-ui/ui/app.slint#L1585), [`ui/dr-ui/ui/library.slint:1469`](../ui/dr-ui/ui/library.slint#L1469), [`ui/dr-ui/ui/library.slint:4019`](../ui/dr-ui/ui/library.slint#L4019) | +| FR-EXP-6 | [`core/dr-export/src/lib.rs:1`](../core/dr-export/src/lib.rs#L1), [`core/dr-export/src/name.rs:1`](../core/dr-export/src/name.rs#L1), [`core/dr-types/src/settings.rs:1`](../core/dr-types/src/settings.rs#L1), [`ui/dr-ui/src/export.rs:1`](../ui/dr-ui/src/export.rs#L1), [`ui/dr-ui/src/lib.rs:451`](../ui/dr-ui/src/lib.rs#L451), [`ui/dr-ui/src/settings_ui.rs:1`](../ui/dr-ui/src/settings_ui.rs#L1), [`ui/dr-ui/src/settings_ui.rs:49`](../ui/dr-ui/src/settings_ui.rs#L49), [`ui/dr-ui/src/settings_ui.rs:679`](../ui/dr-ui/src/settings_ui.rs#L679) | +| FR-EXP-7 | [`ui/dr-ui/src/activity.rs:83`](../ui/dr-ui/src/activity.rs#L83), [`ui/dr-ui/src/export.rs:1`](../ui/dr-ui/src/export.rs#L1), [`ui/dr-ui/src/export.rs:942`](../ui/dr-ui/src/export.rs#L942), [`ui/dr-ui/src/lib.rs:230`](../ui/dr-ui/src/lib.rs#L230), [`ui/dr-ui/src/lib.rs:2370`](../ui/dr-ui/src/lib.rs#L2370), [`ui/dr-ui/src/lib.rs:451`](../ui/dr-ui/src/lib.rs#L451), [`ui/dr-ui/src/lib.rs:486`](../ui/dr-ui/src/lib.rs#L486), [`ui/dr-ui/src/lib.rs:513`](../ui/dr-ui/src/lib.rs#L513), [`ui/dr-ui/src/library_ui.rs:3749`](../ui/dr-ui/src/library_ui.rs#L3749), [`ui/dr-ui/src/library_ui.rs:591`](../ui/dr-ui/src/library_ui.rs#L591), [`ui/dr-ui/src/library_ui.rs:691`](../ui/dr-ui/src/library_ui.rs#L691), [`ui/dr-ui/src/library_ui.rs:6935`](../ui/dr-ui/src/library_ui.rs#L6935), [`ui/dr-ui/src/library_ui.rs:7012`](../ui/dr-ui/src/library_ui.rs#L7012), [`ui/dr-ui/src/library_ui.rs:7024`](../ui/dr-ui/src/library_ui.rs#L7024), [`ui/dr-ui/src/library_ui.rs:748`](../ui/dr-ui/src/library_ui.rs#L748), [`ui/dr-ui/ui/app.slint:1099`](../ui/dr-ui/ui/app.slint#L1099), [`ui/dr-ui/ui/app.slint:1585`](../ui/dr-ui/ui/app.slint#L1585), [`ui/dr-ui/ui/library.slint:1469`](../ui/dr-ui/ui/library.slint#L1469), [`ui/dr-ui/ui/library.slint:4019`](../ui/dr-ui/ui/library.slint#L4019) | | FR-EXP-8 | [`core/dr-decode/src/lib.rs:326`](../core/dr-decode/src/lib.rs#L326), [`core/dr-decode/src/lib.rs:350`](../core/dr-decode/src/lib.rs#L350), [`core/dr-decode/src/lib.rs:364`](../core/dr-decode/src/lib.rs#L364), [`core/dr-decode/src/lib.rs:71`](../core/dr-decode/src/lib.rs#L71), [`core/dr-decode/src/lib.rs:79`](../core/dr-decode/src/lib.rs#L79), [`core/dr-decode/src/lib.rs:82`](../core/dr-decode/src/lib.rs#L82), [`core/dr-decode/src/locate.rs:1164`](../core/dr-decode/src/locate.rs#L1164), [`core/dr-decode/src/locate.rs:1223`](../core/dr-decode/src/locate.rs#L1223), [`core/dr-decode/src/locate.rs:316`](../core/dr-decode/src/locate.rs#L316), [`core/dr-decode/src/locate.rs:487`](../core/dr-decode/src/locate.rs#L487), [`core/dr-decode/src/locate.rs:571`](../core/dr-decode/src/locate.rs#L571), [`core/dr-decode/src/locate.rs:584`](../core/dr-decode/src/locate.rs#L584), [`core/dr-decode/src/locate.rs:667`](../core/dr-decode/src/locate.rs#L667), [`core/dr-export/examples/export.rs:99`](../core/dr-export/examples/export.rs#L99), [`core/dr-export/src/encode.rs:117`](../core/dr-export/src/encode.rs#L117), [`core/dr-export/src/encode.rs:161`](../core/dr-export/src/encode.rs#L161), [`core/dr-export/src/encode.rs:1`](../core/dr-export/src/encode.rs#L1), [`core/dr-export/src/encode.rs:206`](../core/dr-export/src/encode.rs#L206), [`core/dr-export/src/encode.rs:235`](../core/dr-export/src/encode.rs#L235), [`core/dr-export/src/encode.rs:311`](../core/dr-export/src/encode.rs#L311), [`core/dr-export/src/encode.rs:325`](../core/dr-export/src/encode.rs#L325), [`core/dr-export/src/encode.rs:408`](../core/dr-export/src/encode.rs#L408), [`core/dr-export/src/encode.rs:456`](../core/dr-export/src/encode.rs#L456), [`core/dr-export/src/encode.rs:70`](../core/dr-export/src/encode.rs#L70), [`core/dr-export/src/encode.rs:795`](../core/dr-export/src/encode.rs#L795), [`core/dr-export/src/encode.rs:809`](../core/dr-export/src/encode.rs#L809), [`core/dr-export/src/encode.rs:850`](../core/dr-export/src/encode.rs#L850), [`core/dr-export/src/encode.rs:898`](../core/dr-export/src/encode.rs#L898), [`core/dr-export/src/exif.rs:1`](../core/dr-export/src/exif.rs#L1), [`core/dr-export/src/lib.rs:136`](../core/dr-export/src/lib.rs#L136), [`core/dr-export/src/metadata.rs:1`](../core/dr-export/src/metadata.rs#L1), [`core/dr-export/src/metadata.rs:41`](../core/dr-export/src/metadata.rs#L41), [`core/dr-export/src/metadata.rs:74`](../core/dr-export/src/metadata.rs#L74), [`core/dr-types/src/lib.rs:653`](../core/dr-types/src/lib.rs#L653), [`core/dr-types/src/settings.rs:426`](../core/dr-types/src/settings.rs#L426), [`ui/dr-ui/src/export.rs:619`](../ui/dr-ui/src/export.rs#L619), [`ui/dr-ui/src/export.rs:647`](../ui/dr-ui/src/export.rs#L647), [`ui/dr-ui/src/export.rs:777`](../ui/dr-ui/src/export.rs#L777), [`ui/dr-ui/src/export.rs:794`](../ui/dr-ui/src/export.rs#L794), [`ui/dr-ui/src/settings_ui.rs:1`](../ui/dr-ui/src/settings_ui.rs#L1) | -| FR-EXP-9 | [`core/dr-decode/src/lib.rs:506`](../core/dr-decode/src/lib.rs#L506), [`core/dr-export/src/lib.rs:128`](../core/dr-export/src/lib.rs#L128), [`core/dr-export/src/lib.rs:1`](../core/dr-export/src/lib.rs#L1), [`core/dr-gpu/src/adjust.rs:1106`](../core/dr-gpu/src/adjust.rs#L1106), [`ui/dr-ui/src/develop.rs:3340`](../ui/dr-ui/src/develop.rs#L3340), [`ui/dr-ui/src/lib.rs:450`](../ui/dr-ui/src/lib.rs#L450) | +| FR-EXP-9 | [`core/dr-decode/src/lib.rs:506`](../core/dr-decode/src/lib.rs#L506), [`core/dr-export/src/lib.rs:128`](../core/dr-export/src/lib.rs#L128), [`core/dr-export/src/lib.rs:1`](../core/dr-export/src/lib.rs#L1), [`core/dr-gpu/src/adjust.rs:1106`](../core/dr-gpu/src/adjust.rs#L1106), [`ui/dr-ui/examples/face_native.rs:1`](../ui/dr-ui/examples/face_native.rs#L1), [`ui/dr-ui/src/develop.rs:3340`](../ui/dr-ui/src/develop.rs#L3340), [`ui/dr-ui/src/lib.rs:451`](../ui/dr-ui/src/lib.rs#L451), [`ui/dr-ui/src/library.rs:3058`](../ui/dr-ui/src/library.rs#L3058), [`ui/dr-ui/src/library.rs:3423`](../ui/dr-ui/src/library.rs#L3423), [`ui/dr-ui/src/library.rs:3462`](../ui/dr-ui/src/library.rs#L3462) | | FR-NC-1 | [`core/dr-sync-nextcloud/src/auth.rs:132`](../core/dr-sync-nextcloud/src/auth.rs#L132), [`core/dr-sync-nextcloud/src/auth.rs:44`](../core/dr-sync-nextcloud/src/auth.rs#L44), [`core/dr-sync-nextcloud/src/provider.rs:1`](../core/dr-sync-nextcloud/src/provider.rs#L1), [`core/dr-sync/src/account.rs:355`](../core/dr-sync/src/account.rs#L355), [`ui/dr-ui/src/launch.rs:277`](../ui/dr-ui/src/launch.rs#L277), [`ui/dr-ui/src/launch.rs:61`](../ui/dr-ui/src/launch.rs#L61), [`ui/dr-ui/src/launch_ui.rs:417`](../ui/dr-ui/src/launch_ui.rs#L417) | -| FR-NC-10 | [`core/dr-sync/src/account.rs:220`](../core/dr-sync/src/account.rs#L220), [`ui/dr-ui/src/export.rs:1`](../ui/dr-ui/src/export.rs#L1), [`ui/dr-ui/src/lib.rs:512`](../ui/dr-ui/src/lib.rs#L512), [`ui/dr-ui/src/library.rs:1101`](../ui/dr-ui/src/library.rs#L1101), [`ui/dr-ui/src/library.rs:1939`](../ui/dr-ui/src/library.rs#L1939), [`ui/dr-ui/src/library.rs:576`](../ui/dr-ui/src/library.rs#L576), [`ui/dr-ui/src/library.rs:877`](../ui/dr-ui/src/library.rs#L877), [`ui/dr-ui/src/library_ui.rs:1892`](../ui/dr-ui/src/library_ui.rs#L1892), [`ui/dr-ui/src/library_ui.rs:3749`](../ui/dr-ui/src/library_ui.rs#L3749), [`ui/dr-ui/src/library_ui.rs:532`](../ui/dr-ui/src/library_ui.rs#L532), [`ui/dr-ui/src/sidecar_cache.rs:1`](../ui/dr-ui/src/sidecar_cache.rs#L1) | +| FR-NC-10 | [`core/dr-sync/src/account.rs:220`](../core/dr-sync/src/account.rs#L220), [`ui/dr-ui/src/export.rs:1`](../ui/dr-ui/src/export.rs#L1), [`ui/dr-ui/src/lib.rs:513`](../ui/dr-ui/src/lib.rs#L513), [`ui/dr-ui/src/library.rs:1101`](../ui/dr-ui/src/library.rs#L1101), [`ui/dr-ui/src/library.rs:1939`](../ui/dr-ui/src/library.rs#L1939), [`ui/dr-ui/src/library.rs:576`](../ui/dr-ui/src/library.rs#L576), [`ui/dr-ui/src/library.rs:877`](../ui/dr-ui/src/library.rs#L877), [`ui/dr-ui/src/library_ui.rs:1892`](../ui/dr-ui/src/library_ui.rs#L1892), [`ui/dr-ui/src/library_ui.rs:3749`](../ui/dr-ui/src/library_ui.rs#L3749), [`ui/dr-ui/src/library_ui.rs:532`](../ui/dr-ui/src/library_ui.rs#L532), [`ui/dr-ui/src/sidecar_cache.rs:1`](../ui/dr-ui/src/sidecar_cache.rs#L1) | | FR-NC-12 | [`core/dr-sync-folder/src/lib.rs:1`](../core/dr-sync-folder/src/lib.rs#L1), [`core/dr-sync-nextcloud/src/lib.rs:40`](../core/dr-sync-nextcloud/src/lib.rs#L40), [`core/dr-sync-nextcloud/src/lib.rs:914`](../core/dr-sync-nextcloud/src/lib.rs#L914), [`core/dr-sync-nextcloud/src/provider.rs:1`](../core/dr-sync-nextcloud/src/provider.rs#L1), [`core/dr-sync/src/account.rs:1`](../core/dr-sync/src/account.rs#L1), [`core/dr-sync/src/account.rs:81`](../core/dr-sync/src/account.rs#L81), [`core/dr-sync/src/lib.rs:218`](../core/dr-sync/src/lib.rs#L218), [`core/dr-sync/src/lib.rs:51`](../core/dr-sync/src/lib.rs#L51), [`core/dr-sync/src/provider.rs:106`](../core/dr-sync/src/provider.rs#L106), [`core/dr-sync/src/provider.rs:1`](../core/dr-sync/src/provider.rs#L1), [`core/dr-sync/src/provider.rs:53`](../core/dr-sync/src/provider.rs#L53), [`core/dr-sync/src/reachability.rs:1`](../core/dr-sync/src/reachability.rs#L1), [`ui/dr-ui/src/remote.rs:1`](../ui/dr-ui/src/remote.rs#L1) | | FR-NC-13 | [`core/dr-sync-folder/src/lib.rs:197`](../core/dr-sync-folder/src/lib.rs#L197), [`core/dr-sync-folder/src/lib.rs:1`](../core/dr-sync-folder/src/lib.rs#L1), [`core/dr-sync-folder/src/lib.rs:73`](../core/dr-sync-folder/src/lib.rs#L73), [`core/dr-sync/src/provider.rs:106`](../core/dr-sync/src/provider.rs#L106), [`ui/dr-ui/src/launch_ui.rs:316`](../ui/dr-ui/src/launch_ui.rs#L316), [`ui/dr-ui/src/remote.rs:1`](../ui/dr-ui/src/remote.rs#L1) | | FR-NC-2 | [`core/dr-sync/src/account.rs:1`](../core/dr-sync/src/account.rs#L1), [`core/dr-sync/src/account.rs:298`](../core/dr-sync/src/account.rs#L298), [`core/dr-sync/src/account.rs:355`](../core/dr-sync/src/account.rs#L355), [`core/dr-sync/src/account.rs:59`](../core/dr-sync/src/account.rs#L59), [`platform/dr-plat/src/secrets.rs:82`](../platform/dr-plat/src/secrets.rs#L82) | -| FR-NC-3 | [`core/dr-decode/src/locate.rs:1`](../core/dr-decode/src/locate.rs#L1), [`core/dr-decode/src/preview.rs:148`](../core/dr-decode/src/preview.rs#L148), [`core/dr-sync/src/capability.rs:85`](../core/dr-sync/src/capability.rs#L85), [`core/dr-thumbs/src/lib.rs:1`](../core/dr-thumbs/src/lib.rs#L1), [`ui/dr-ui/src/library.rs:1`](../ui/dr-ui/src/library.rs#L1), [`ui/dr-ui/src/library.rs:2964`](../ui/dr-ui/src/library.rs#L2964), [`ui/dr-ui/src/library.rs:3488`](../ui/dr-ui/src/library.rs#L3488), [`ui/dr-ui/src/library_ui.rs:1`](../ui/dr-ui/src/library_ui.rs#L1), [`ui/dr-ui/src/library_ui.rs:4001`](../ui/dr-ui/src/library_ui.rs#L4001), [`ui/dr-ui/src/library_ui.rs:5021`](../ui/dr-ui/src/library_ui.rs#L5021), [`ui/dr-ui/ui/app.slint:412`](../ui/dr-ui/ui/app.slint#L412), [`ui/dr-ui/ui/settings.slint:387`](../ui/dr-ui/ui/settings.slint#L387), [`ui/dr-ui/ui/settings.slint:73`](../ui/dr-ui/ui/settings.slint#L73) | +| FR-NC-3 | [`core/dr-decode/src/locate.rs:1`](../core/dr-decode/src/locate.rs#L1), [`core/dr-decode/src/preview.rs:148`](../core/dr-decode/src/preview.rs#L148), [`core/dr-sync/src/capability.rs:85`](../core/dr-sync/src/capability.rs#L85), [`core/dr-thumbs/src/lib.rs:1`](../core/dr-thumbs/src/lib.rs#L1), [`ui/dr-ui/src/library.rs:1`](../ui/dr-ui/src/library.rs#L1), [`ui/dr-ui/src/library.rs:2964`](../ui/dr-ui/src/library.rs#L2964), [`ui/dr-ui/src/library.rs:3560`](../ui/dr-ui/src/library.rs#L3560), [`ui/dr-ui/src/library_ui.rs:1`](../ui/dr-ui/src/library_ui.rs#L1), [`ui/dr-ui/src/library_ui.rs:4001`](../ui/dr-ui/src/library_ui.rs#L4001), [`ui/dr-ui/src/library_ui.rs:5021`](../ui/dr-ui/src/library_ui.rs#L5021), [`ui/dr-ui/ui/app.slint:412`](../ui/dr-ui/ui/app.slint#L412), [`ui/dr-ui/ui/settings.slint:387`](../ui/dr-ui/ui/settings.slint#L387), [`ui/dr-ui/ui/settings.slint:73`](../ui/dr-ui/ui/settings.slint#L73) | | FR-NC-4 | [`core/dr-sync-folder/src/lib.rs:197`](../core/dr-sync-folder/src/lib.rs#L197), [`core/dr-sync-nextcloud/src/propfind.rs:103`](../core/dr-sync-nextcloud/src/propfind.rs#L103), [`core/dr-sync-nextcloud/src/propfind.rs:51`](../core/dr-sync-nextcloud/src/propfind.rs#L51), [`core/dr-sync/src/capability.rs:6`](../core/dr-sync/src/capability.rs#L6), [`core/dr-sync/src/lib.rs:218`](../core/dr-sync/src/lib.rs#L218), [`core/dr-sync/src/scan.rs:93`](../core/dr-sync/src/scan.rs#L93), [`ui/dr-ui/src/launch.rs:61`](../ui/dr-ui/src/launch.rs#L61) | | FR-NC-5 | [`core/dr-sync-nextcloud/src/propfind.rs:51`](../core/dr-sync-nextcloud/src/propfind.rs#L51), [`ui/dr-ui/src/import.rs:488`](../ui/dr-ui/src/import.rs#L488) | | FR-NC-6 | [`ui/dr-ui/src/activity.rs:1`](../ui/dr-ui/src/activity.rs#L1) | -| FR-NC-6a | [`core/dr-catalog/src/cache.rs:1`](../core/dr-catalog/src/cache.rs#L1), [`core/dr-catalog/src/cache.rs:225`](../core/dr-catalog/src/cache.rs#L225), [`core/dr-catalog/src/schema.rs:1126`](../core/dr-catalog/src/schema.rs#L1126), [`core/dr-catalog/src/schema.rs:725`](../core/dr-catalog/src/schema.rs#L725), [`core/dr-sync-folder/src/borrow.rs:1`](../core/dr-sync-folder/src/borrow.rs#L1), [`core/dr-types/src/selector.rs:1`](../core/dr-types/src/selector.rs#L1), [`core/dr-types/src/settings.rs:1`](../core/dr-types/src/settings.rs#L1), [`ui/dr-ui/src/collections_ui.rs:3413`](../ui/dr-ui/src/collections_ui.rs#L3413), [`ui/dr-ui/src/collections_ui.rs:636`](../ui/dr-ui/src/collections_ui.rs#L636), [`ui/dr-ui/src/collections_ui.rs:704`](../ui/dr-ui/src/collections_ui.rs#L704), [`ui/dr-ui/src/lib.rs:2068`](../ui/dr-ui/src/lib.rs#L2068), [`ui/dr-ui/src/lib.rs:3191`](../ui/dr-ui/src/lib.rs#L3191), [`ui/dr-ui/src/library.rs:1565`](../ui/dr-ui/src/library.rs#L1565), [`ui/dr-ui/src/library.rs:1588`](../ui/dr-ui/src/library.rs#L1588), [`ui/dr-ui/src/library.rs:1863`](../ui/dr-ui/src/library.rs#L1863), [`ui/dr-ui/src/library_ui.rs:1317`](../ui/dr-ui/src/library_ui.rs#L1317), [`ui/dr-ui/src/library_ui.rs:1390`](../ui/dr-ui/src/library_ui.rs#L1390), [`ui/dr-ui/src/library_ui.rs:1491`](../ui/dr-ui/src/library_ui.rs#L1491), [`ui/dr-ui/src/library_ui.rs:1546`](../ui/dr-ui/src/library_ui.rs#L1546), [`ui/dr-ui/src/library_ui.rs:1681`](../ui/dr-ui/src/library_ui.rs#L1681), [`ui/dr-ui/src/library_ui.rs:1799`](../ui/dr-ui/src/library_ui.rs#L1799), [`ui/dr-ui/src/library_ui.rs:2424`](../ui/dr-ui/src/library_ui.rs#L2424), [`ui/dr-ui/src/library_ui.rs:294`](../ui/dr-ui/src/library_ui.rs#L294), [`ui/dr-ui/src/library_ui.rs:305`](../ui/dr-ui/src/library_ui.rs#L305), [`ui/dr-ui/src/library_ui.rs:313`](../ui/dr-ui/src/library_ui.rs#L313), [`ui/dr-ui/src/library_ui.rs:325`](../ui/dr-ui/src/library_ui.rs#L325), [`ui/dr-ui/src/library_ui.rs:334`](../ui/dr-ui/src/library_ui.rs#L334), [`ui/dr-ui/src/library_ui.rs:428`](../ui/dr-ui/src/library_ui.rs#L428), [`ui/dr-ui/src/library_ui.rs:438`](../ui/dr-ui/src/library_ui.rs#L438), [`ui/dr-ui/src/library_ui.rs:485`](../ui/dr-ui/src/library_ui.rs#L485), [`ui/dr-ui/src/library_ui.rs:548`](../ui/dr-ui/src/library_ui.rs#L548), [`ui/dr-ui/src/library_ui.rs:579`](../ui/dr-ui/src/library_ui.rs#L579), [`ui/dr-ui/src/library_ui.rs:5803`](../ui/dr-ui/src/library_ui.rs#L5803), [`ui/dr-ui/src/library_ui.rs:5821`](../ui/dr-ui/src/library_ui.rs#L5821), [`ui/dr-ui/src/library_ui.rs:5833`](../ui/dr-ui/src/library_ui.rs#L5833), [`ui/dr-ui/src/library_ui.rs:591`](../ui/dr-ui/src/library_ui.rs#L591), [`ui/dr-ui/src/settings_store.rs:1`](../ui/dr-ui/src/settings_store.rs#L1), [`ui/dr-ui/src/settings_ui.rs:1`](../ui/dr-ui/src/settings_ui.rs#L1), [`ui/dr-ui/ui/app.slint:1483`](../ui/dr-ui/ui/app.slint#L1483), [`ui/dr-ui/ui/app.slint:2674`](../ui/dr-ui/ui/app.slint#L2674), [`ui/dr-ui/ui/app.slint:536`](../ui/dr-ui/ui/app.slint#L536), [`ui/dr-ui/ui/collections.slint:249`](../ui/dr-ui/ui/collections.slint#L249), [`ui/dr-ui/ui/collections.slint:360`](../ui/dr-ui/ui/collections.slint#L360), [`ui/dr-ui/ui/collections.slint:369`](../ui/dr-ui/ui/collections.slint#L369), [`ui/dr-ui/ui/collections.slint:372`](../ui/dr-ui/ui/collections.slint#L372), [`ui/dr-ui/ui/collections.slint:395`](../ui/dr-ui/ui/collections.slint#L395), [`ui/dr-ui/ui/collections.slint:52`](../ui/dr-ui/ui/collections.slint#L52), [`ui/dr-ui/ui/collections.slint:596`](../ui/dr-ui/ui/collections.slint#L596), [`ui/dr-ui/ui/collections.slint:747`](../ui/dr-ui/ui/collections.slint#L747), [`ui/dr-ui/ui/collections.slint:84`](../ui/dr-ui/ui/collections.slint#L84), [`ui/dr-ui/ui/icons.slint:261`](../ui/dr-ui/ui/icons.slint#L261) | +| FR-NC-6a | [`core/dr-catalog/src/cache.rs:1`](../core/dr-catalog/src/cache.rs#L1), [`core/dr-catalog/src/cache.rs:225`](../core/dr-catalog/src/cache.rs#L225), [`core/dr-catalog/src/schema.rs:1126`](../core/dr-catalog/src/schema.rs#L1126), [`core/dr-catalog/src/schema.rs:725`](../core/dr-catalog/src/schema.rs#L725), [`core/dr-sync-folder/src/borrow.rs:1`](../core/dr-sync-folder/src/borrow.rs#L1), [`core/dr-types/src/selector.rs:1`](../core/dr-types/src/selector.rs#L1), [`core/dr-types/src/settings.rs:1`](../core/dr-types/src/settings.rs#L1), [`ui/dr-ui/src/collections_ui.rs:3413`](../ui/dr-ui/src/collections_ui.rs#L3413), [`ui/dr-ui/src/collections_ui.rs:636`](../ui/dr-ui/src/collections_ui.rs#L636), [`ui/dr-ui/src/collections_ui.rs:704`](../ui/dr-ui/src/collections_ui.rs#L704), [`ui/dr-ui/src/lib.rs:2074`](../ui/dr-ui/src/lib.rs#L2074), [`ui/dr-ui/src/lib.rs:3197`](../ui/dr-ui/src/lib.rs#L3197), [`ui/dr-ui/src/library.rs:1565`](../ui/dr-ui/src/library.rs#L1565), [`ui/dr-ui/src/library.rs:1588`](../ui/dr-ui/src/library.rs#L1588), [`ui/dr-ui/src/library.rs:1863`](../ui/dr-ui/src/library.rs#L1863), [`ui/dr-ui/src/library_ui.rs:1317`](../ui/dr-ui/src/library_ui.rs#L1317), [`ui/dr-ui/src/library_ui.rs:1390`](../ui/dr-ui/src/library_ui.rs#L1390), [`ui/dr-ui/src/library_ui.rs:1491`](../ui/dr-ui/src/library_ui.rs#L1491), [`ui/dr-ui/src/library_ui.rs:1546`](../ui/dr-ui/src/library_ui.rs#L1546), [`ui/dr-ui/src/library_ui.rs:1681`](../ui/dr-ui/src/library_ui.rs#L1681), [`ui/dr-ui/src/library_ui.rs:1799`](../ui/dr-ui/src/library_ui.rs#L1799), [`ui/dr-ui/src/library_ui.rs:2424`](../ui/dr-ui/src/library_ui.rs#L2424), [`ui/dr-ui/src/library_ui.rs:294`](../ui/dr-ui/src/library_ui.rs#L294), [`ui/dr-ui/src/library_ui.rs:305`](../ui/dr-ui/src/library_ui.rs#L305), [`ui/dr-ui/src/library_ui.rs:313`](../ui/dr-ui/src/library_ui.rs#L313), [`ui/dr-ui/src/library_ui.rs:325`](../ui/dr-ui/src/library_ui.rs#L325), [`ui/dr-ui/src/library_ui.rs:334`](../ui/dr-ui/src/library_ui.rs#L334), [`ui/dr-ui/src/library_ui.rs:428`](../ui/dr-ui/src/library_ui.rs#L428), [`ui/dr-ui/src/library_ui.rs:438`](../ui/dr-ui/src/library_ui.rs#L438), [`ui/dr-ui/src/library_ui.rs:485`](../ui/dr-ui/src/library_ui.rs#L485), [`ui/dr-ui/src/library_ui.rs:548`](../ui/dr-ui/src/library_ui.rs#L548), [`ui/dr-ui/src/library_ui.rs:579`](../ui/dr-ui/src/library_ui.rs#L579), [`ui/dr-ui/src/library_ui.rs:5803`](../ui/dr-ui/src/library_ui.rs#L5803), [`ui/dr-ui/src/library_ui.rs:5821`](../ui/dr-ui/src/library_ui.rs#L5821), [`ui/dr-ui/src/library_ui.rs:5833`](../ui/dr-ui/src/library_ui.rs#L5833), [`ui/dr-ui/src/library_ui.rs:591`](../ui/dr-ui/src/library_ui.rs#L591), [`ui/dr-ui/src/settings_store.rs:1`](../ui/dr-ui/src/settings_store.rs#L1), [`ui/dr-ui/src/settings_ui.rs:1`](../ui/dr-ui/src/settings_ui.rs#L1), [`ui/dr-ui/ui/app.slint:1483`](../ui/dr-ui/ui/app.slint#L1483), [`ui/dr-ui/ui/app.slint:2674`](../ui/dr-ui/ui/app.slint#L2674), [`ui/dr-ui/ui/app.slint:536`](../ui/dr-ui/ui/app.slint#L536), [`ui/dr-ui/ui/collections.slint:249`](../ui/dr-ui/ui/collections.slint#L249), [`ui/dr-ui/ui/collections.slint:360`](../ui/dr-ui/ui/collections.slint#L360), [`ui/dr-ui/ui/collections.slint:369`](../ui/dr-ui/ui/collections.slint#L369), [`ui/dr-ui/ui/collections.slint:372`](../ui/dr-ui/ui/collections.slint#L372), [`ui/dr-ui/ui/collections.slint:395`](../ui/dr-ui/ui/collections.slint#L395), [`ui/dr-ui/ui/collections.slint:52`](../ui/dr-ui/ui/collections.slint#L52), [`ui/dr-ui/ui/collections.slint:596`](../ui/dr-ui/ui/collections.slint#L596), [`ui/dr-ui/ui/collections.slint:747`](../ui/dr-ui/ui/collections.slint#L747), [`ui/dr-ui/ui/collections.slint:84`](../ui/dr-ui/ui/collections.slint#L84), [`ui/dr-ui/ui/icons.slint:261`](../ui/dr-ui/ui/icons.slint#L261) | | FR-NC-6b | [`ui/dr-ui/src/library_ui.rs:1546`](../ui/dr-ui/src/library_ui.rs#L1546), [`ui/dr-ui/src/memory.rs:1`](../ui/dr-ui/src/memory.rs#L1) | -| FR-NC-6c | [`core/dr-catalog/src/cache.rs:225`](../core/dr-catalog/src/cache.rs#L225), [`core/dr-sync-folder/src/borrow.rs:1`](../core/dr-sync-folder/src/borrow.rs#L1), [`core/dr-sync-folder/src/lib.rs:197`](../core/dr-sync-folder/src/lib.rs#L197), [`core/dr-sync-folder/src/lib.rs:73`](../core/dr-sync-folder/src/lib.rs#L73), [`core/dr-sync-folder/src/lib.rs:818`](../core/dr-sync-folder/src/lib.rs#L818), [`core/dr-sync-folder/src/lib.rs:857`](../core/dr-sync-folder/src/lib.rs#L857), [`core/dr-sync-folder/src/vfs.rs:1`](../core/dr-sync-folder/src/vfs.rs#L1), [`core/dr-sync-nextcloud/src/desktop_client.rs:167`](../core/dr-sync-nextcloud/src/desktop_client.rs#L167), [`core/dr-sync-nextcloud/src/desktop_client.rs:30`](../core/dr-sync-nextcloud/src/desktop_client.rs#L30), [`core/dr-sync/src/capability.rs:41`](../core/dr-sync/src/capability.rs#L41), [`core/dr-sync/src/error.rs:39`](../core/dr-sync/src/error.rs#L39), [`core/dr-sync/src/lib.rs:158`](../core/dr-sync/src/lib.rs#L158), [`core/dr-sync/src/types.rs:101`](../core/dr-sync/src/types.rs#L101), [`core/dr-types/src/lib.rs:120`](../core/dr-types/src/lib.rs#L120), [`core/dr-types/src/lib.rs:202`](../core/dr-types/src/lib.rs#L202), [`ui/dr-ui/src/activity.rs:1`](../ui/dr-ui/src/activity.rs#L1), [`ui/dr-ui/src/collections_ui.rs:3413`](../ui/dr-ui/src/collections_ui.rs#L3413), [`ui/dr-ui/src/collections_ui.rs:636`](../ui/dr-ui/src/collections_ui.rs#L636), [`ui/dr-ui/src/collections_ui.rs:704`](../ui/dr-ui/src/collections_ui.rs#L704), [`ui/dr-ui/src/derived_sync.rs:555`](../ui/dr-ui/src/derived_sync.rs#L555), [`ui/dr-ui/src/derived_sync.rs:627`](../ui/dr-ui/src/derived_sync.rs#L627), [`ui/dr-ui/src/library.rs:1685`](../ui/dr-ui/src/library.rs#L1685), [`ui/dr-ui/src/library.rs:1775`](../ui/dr-ui/src/library.rs#L1775), [`ui/dr-ui/src/library.rs:3254`](../ui/dr-ui/src/library.rs#L3254), [`ui/dr-ui/src/library.rs:3608`](../ui/dr-ui/src/library.rs#L3608), [`ui/dr-ui/src/library.rs:981`](../ui/dr-ui/src/library.rs#L981), [`ui/dr-ui/src/library_ui.rs:1317`](../ui/dr-ui/src/library_ui.rs#L1317), [`ui/dr-ui/src/library_ui.rs:1390`](../ui/dr-ui/src/library_ui.rs#L1390), [`ui/dr-ui/src/library_ui.rs:1589`](../ui/dr-ui/src/library_ui.rs#L1589), [`ui/dr-ui/src/remote.rs:40`](../ui/dr-ui/src/remote.rs#L40), [`ui/dr-ui/ui/collections.slint:249`](../ui/dr-ui/ui/collections.slint#L249), [`ui/dr-ui/ui/collections.slint:747`](../ui/dr-ui/ui/collections.slint#L747), [`ui/dr-ui/ui/icons.slint:261`](../ui/dr-ui/ui/icons.slint#L261) | +| FR-NC-6c | [`core/dr-catalog/src/cache.rs:225`](../core/dr-catalog/src/cache.rs#L225), [`core/dr-sync-folder/src/borrow.rs:1`](../core/dr-sync-folder/src/borrow.rs#L1), [`core/dr-sync-folder/src/lib.rs:197`](../core/dr-sync-folder/src/lib.rs#L197), [`core/dr-sync-folder/src/lib.rs:73`](../core/dr-sync-folder/src/lib.rs#L73), [`core/dr-sync-folder/src/lib.rs:818`](../core/dr-sync-folder/src/lib.rs#L818), [`core/dr-sync-folder/src/lib.rs:857`](../core/dr-sync-folder/src/lib.rs#L857), [`core/dr-sync-folder/src/vfs.rs:1`](../core/dr-sync-folder/src/vfs.rs#L1), [`core/dr-sync-nextcloud/src/desktop_client.rs:167`](../core/dr-sync-nextcloud/src/desktop_client.rs#L167), [`core/dr-sync-nextcloud/src/desktop_client.rs:30`](../core/dr-sync-nextcloud/src/desktop_client.rs#L30), [`core/dr-sync/src/capability.rs:41`](../core/dr-sync/src/capability.rs#L41), [`core/dr-sync/src/error.rs:39`](../core/dr-sync/src/error.rs#L39), [`core/dr-sync/src/lib.rs:158`](../core/dr-sync/src/lib.rs#L158), [`core/dr-sync/src/types.rs:101`](../core/dr-sync/src/types.rs#L101), [`core/dr-types/src/lib.rs:120`](../core/dr-types/src/lib.rs#L120), [`core/dr-types/src/lib.rs:202`](../core/dr-types/src/lib.rs#L202), [`ui/dr-ui/src/activity.rs:1`](../ui/dr-ui/src/activity.rs#L1), [`ui/dr-ui/src/collections_ui.rs:3413`](../ui/dr-ui/src/collections_ui.rs#L3413), [`ui/dr-ui/src/collections_ui.rs:636`](../ui/dr-ui/src/collections_ui.rs#L636), [`ui/dr-ui/src/collections_ui.rs:704`](../ui/dr-ui/src/collections_ui.rs#L704), [`ui/dr-ui/src/derived_sync.rs:555`](../ui/dr-ui/src/derived_sync.rs#L555), [`ui/dr-ui/src/derived_sync.rs:627`](../ui/dr-ui/src/derived_sync.rs#L627), [`ui/dr-ui/src/library.rs:1685`](../ui/dr-ui/src/library.rs#L1685), [`ui/dr-ui/src/library.rs:1775`](../ui/dr-ui/src/library.rs#L1775), [`ui/dr-ui/src/library.rs:3242`](../ui/dr-ui/src/library.rs#L3242), [`ui/dr-ui/src/library.rs:3680`](../ui/dr-ui/src/library.rs#L3680), [`ui/dr-ui/src/library.rs:981`](../ui/dr-ui/src/library.rs#L981), [`ui/dr-ui/src/library_ui.rs:1317`](../ui/dr-ui/src/library_ui.rs#L1317), [`ui/dr-ui/src/library_ui.rs:1390`](../ui/dr-ui/src/library_ui.rs#L1390), [`ui/dr-ui/src/library_ui.rs:1589`](../ui/dr-ui/src/library_ui.rs#L1589), [`ui/dr-ui/src/remote.rs:40`](../ui/dr-ui/src/remote.rs#L40), [`ui/dr-ui/ui/collections.slint:249`](../ui/dr-ui/ui/collections.slint#L249), [`ui/dr-ui/ui/collections.slint:747`](../ui/dr-ui/ui/collections.slint#L747), [`ui/dr-ui/ui/icons.slint:261`](../ui/dr-ui/ui/icons.slint#L261) | | FR-NC-6d | [`core/dr-sync-folder/src/borrow.rs:1`](../core/dr-sync-folder/src/borrow.rs#L1), [`core/dr-sync-folder/src/lib.rs:1`](../core/dr-sync-folder/src/lib.rs#L1), [`core/dr-sync-folder/src/vfs.rs:1`](../core/dr-sync-folder/src/vfs.rs#L1) | -| FR-NC-7 | [`core/dr-catalog/src/face_shard.rs:1`](../core/dr-catalog/src/face_shard.rs#L1), [`core/dr-sync-nextcloud/src/lib.rs:105`](../core/dr-sync-nextcloud/src/lib.rs#L105), [`ui/dr-ui/src/derived_sync.rs:1`](../ui/dr-ui/src/derived_sync.rs#L1), [`ui/dr-ui/src/library.rs:3488`](../ui/dr-ui/src/library.rs#L3488), [`ui/dr-ui/src/library_ui.rs:4001`](../ui/dr-ui/src/library_ui.rs#L4001), [`ui/dr-ui/ui/settings.slint:387`](../ui/dr-ui/ui/settings.slint#L387) | -| FR-NC-7a | [`core/dr-ingest/src/layout.rs:1`](../core/dr-ingest/src/layout.rs#L1), [`core/dr-sync/src/upload.rs:1`](../core/dr-sync/src/upload.rs#L1), [`core/dr-sync/src/upload.rs:40`](../core/dr-sync/src/upload.rs#L40), [`core/dr-types/src/settings.rs:206`](../core/dr-types/src/settings.rs#L206), [`ui/dr-ui/src/import.rs:1`](../ui/dr-ui/src/import.rs#L1), [`ui/dr-ui/src/import.rs:97`](../ui/dr-ui/src/import.rs#L97), [`ui/dr-ui/src/import_ui.rs:1`](../ui/dr-ui/src/import_ui.rs#L1), [`ui/dr-ui/src/lib.rs:1254`](../ui/dr-ui/src/lib.rs#L1254), [`ui/dr-ui/ui/import.slint:5`](../ui/dr-ui/ui/import.slint#L5) | -| FR-NC-7b | [`core/dr-ingest/src/lib.rs:733`](../core/dr-ingest/src/lib.rs#L733), [`core/dr-sync/src/upload.rs:1`](../core/dr-sync/src/upload.rs#L1), [`ui/dr-ui/src/import.rs:122`](../ui/dr-ui/src/import.rs#L122), [`ui/dr-ui/src/import.rs:336`](../ui/dr-ui/src/import.rs#L336), [`ui/dr-ui/src/import.rs:584`](../ui/dr-ui/src/import.rs#L584), [`ui/dr-ui/src/import.rs:97`](../ui/dr-ui/src/import.rs#L97), [`ui/dr-ui/src/import_ui.rs:1`](../ui/dr-ui/src/import_ui.rs#L1), [`ui/dr-ui/src/lib.rs:1254`](../ui/dr-ui/src/lib.rs#L1254) | -| FR-NC-8 | [`core/dr-pipeline/src/sidecar.rs:119`](../core/dr-pipeline/src/sidecar.rs#L119), [`core/dr-pipeline/src/sidecar.rs:93`](../core/dr-pipeline/src/sidecar.rs#L93), [`ui/dr-ui/src/lib.rs:2038`](../ui/dr-ui/src/lib.rs#L2038), [`ui/dr-ui/src/library.rs:492`](../ui/dr-ui/src/library.rs#L492), [`ui/dr-ui/src/library_ui.rs:500`](../ui/dr-ui/src/library_ui.rs#L500) | +| FR-NC-7 | [`core/dr-catalog/src/face_shard.rs:1`](../core/dr-catalog/src/face_shard.rs#L1), [`core/dr-sync-nextcloud/src/lib.rs:105`](../core/dr-sync-nextcloud/src/lib.rs#L105), [`ui/dr-ui/src/derived_sync.rs:1`](../ui/dr-ui/src/derived_sync.rs#L1), [`ui/dr-ui/src/library.rs:3560`](../ui/dr-ui/src/library.rs#L3560), [`ui/dr-ui/src/library_ui.rs:4001`](../ui/dr-ui/src/library_ui.rs#L4001), [`ui/dr-ui/ui/settings.slint:387`](../ui/dr-ui/ui/settings.slint#L387) | +| FR-NC-7a | [`core/dr-ingest/src/layout.rs:1`](../core/dr-ingest/src/layout.rs#L1), [`core/dr-sync/src/upload.rs:1`](../core/dr-sync/src/upload.rs#L1), [`core/dr-sync/src/upload.rs:40`](../core/dr-sync/src/upload.rs#L40), [`core/dr-types/src/settings.rs:206`](../core/dr-types/src/settings.rs#L206), [`ui/dr-ui/src/import.rs:1`](../ui/dr-ui/src/import.rs#L1), [`ui/dr-ui/src/import.rs:97`](../ui/dr-ui/src/import.rs#L97), [`ui/dr-ui/src/import_ui.rs:1`](../ui/dr-ui/src/import_ui.rs#L1), [`ui/dr-ui/src/lib.rs:1260`](../ui/dr-ui/src/lib.rs#L1260), [`ui/dr-ui/ui/import.slint:5`](../ui/dr-ui/ui/import.slint#L5) | +| FR-NC-7b | [`core/dr-ingest/src/lib.rs:733`](../core/dr-ingest/src/lib.rs#L733), [`core/dr-sync/src/upload.rs:1`](../core/dr-sync/src/upload.rs#L1), [`ui/dr-ui/src/import.rs:122`](../ui/dr-ui/src/import.rs#L122), [`ui/dr-ui/src/import.rs:336`](../ui/dr-ui/src/import.rs#L336), [`ui/dr-ui/src/import.rs:584`](../ui/dr-ui/src/import.rs#L584), [`ui/dr-ui/src/import.rs:97`](../ui/dr-ui/src/import.rs#L97), [`ui/dr-ui/src/import_ui.rs:1`](../ui/dr-ui/src/import_ui.rs#L1), [`ui/dr-ui/src/lib.rs:1260`](../ui/dr-ui/src/lib.rs#L1260) | +| FR-NC-8 | [`core/dr-pipeline/src/sidecar.rs:119`](../core/dr-pipeline/src/sidecar.rs#L119), [`core/dr-pipeline/src/sidecar.rs:93`](../core/dr-pipeline/src/sidecar.rs#L93), [`ui/dr-ui/src/lib.rs:2044`](../ui/dr-ui/src/lib.rs#L2044), [`ui/dr-ui/src/library.rs:492`](../ui/dr-ui/src/library.rs#L492), [`ui/dr-ui/src/library_ui.rs:500`](../ui/dr-ui/src/library_ui.rs#L500) | | FR-NC-9 | [`core/dr-catalog/src/merge.rs:1`](../core/dr-catalog/src/merge.rs#L1), [`core/dr-catalog/src/schema.rs:668`](../core/dr-catalog/src/schema.rs#L668), [`core/dr-catalog/src/sync.rs:1`](../core/dr-catalog/src/sync.rs#L1), [`core/dr-pipeline/src/sidecar.rs:157`](../core/dr-pipeline/src/sidecar.rs#L157), [`core/dr-pipeline/src/sidecar.rs:184`](../core/dr-pipeline/src/sidecar.rs#L184), [`core/dr-pipeline/src/sidecar.rs:2091`](../core/dr-pipeline/src/sidecar.rs#L2091), [`core/dr-pipeline/src/sidecar.rs:353`](../core/dr-pipeline/src/sidecar.rs#L353), [`core/dr-pipeline/src/sidecar.rs:451`](../core/dr-pipeline/src/sidecar.rs#L451), [`core/dr-pipeline/src/spot.rs:245`](../core/dr-pipeline/src/spot.rs#L245), [`core/dr-pipeline/tests/spot_sidecar.rs:1`](../core/dr-pipeline/tests/spot_sidecar.rs#L1), [`ui/dr-ui/src/derived_sync.rs:555`](../ui/dr-ui/src/derived_sync.rs#L555), [`ui/dr-ui/src/library.rs:1031`](../ui/dr-ui/src/library.rs#L1031), [`ui/dr-ui/src/library.rs:877`](../ui/dr-ui/src/library.rs#L877) | | FR-PLAT-AND-2 | [`core/dr-catalog/src/walk.rs:1022`](../core/dr-catalog/src/walk.rs#L1022), [`core/dr-catalog/src/walk.rs:435`](../core/dr-catalog/src/walk.rs#L435), [`core/dr-sync-folder/src/lib.rs:242`](../core/dr-sync-folder/src/lib.rs#L242), [`core/dr-sync-folder/src/tests.rs:51`](../core/dr-sync-folder/src/tests.rs#L51), [`core/dr-sync/src/error.rs:113`](../core/dr-sync/src/error.rs#L113), [`core/dr-sync/src/error.rs:195`](../core/dr-sync/src/error.rs#L195), [`core/dr-sync/src/scan.rs:175`](../core/dr-sync/src/scan.rs#L175), [`core/dr-sync/src/scan.rs:464`](../core/dr-sync/src/scan.rs#L464), [`core/dr-sync/src/scan.rs:490`](../core/dr-sync/src/scan.rs#L490), [`core/dr-sync/src/scan.rs:519`](../core/dr-sync/src/scan.rs#L519), [`ui/dr-ui/src/library.rs:1222`](../ui/dr-ui/src/library.rs#L1222), [`ui/dr-ui/src/library.rs:1275`](../ui/dr-ui/src/library.rs#L1275), [`ui/dr-ui/src/library.rs:1306`](../ui/dr-ui/src/library.rs#L1306), [`ui/dr-ui/src/library.rs:1411`](../ui/dr-ui/src/library.rs#L1411), [`ui/dr-ui/src/library_ui.rs:1162`](../ui/dr-ui/src/library_ui.rs#L1162), [`ui/dr-ui/src/library_ui.rs:1241`](../ui/dr-ui/src/library_ui.rs#L1241), [`ui/dr-ui/src/library_ui.rs:1840`](../ui/dr-ui/src/library_ui.rs#L1840), [`ui/dr-ui/src/library_ui.rs:278`](../ui/dr-ui/src/library_ui.rs#L278), [`ui/dr-ui/src/library_ui.rs:469`](../ui/dr-ui/src/library_ui.rs#L469) | | FR-PLAT-AND-3 | [`core/dr-catalog/src/jobs.rs:1`](../core/dr-catalog/src/jobs.rs#L1), [`core/dr-catalog/src/runner.rs:1`](../core/dr-catalog/src/runner.rs#L1) | | FR-PLAT-AND-4 | [`core/dr-catalog/src/runner.rs:1`](../core/dr-catalog/src/runner.rs#L1) | -| FR-PLAT-AND-5 | [`apps/darkroom-android/src/lib.rs:166`](../apps/darkroom-android/src/lib.rs#L166), [`core/dr-gpu/src/adjust.rs:1029`](../core/dr-gpu/src/adjust.rs#L1029), [`core/dr-gpu/src/detail.rs:161`](../core/dr-gpu/src/detail.rs#L161), [`core/dr-gpu/src/detail.rs:547`](../core/dr-gpu/src/detail.rs#L547), [`core/dr-gpu/tests/detail_stage.rs:417`](../core/dr-gpu/tests/detail_stage.rs#L417), [`ui/dr-ui/src/develop.rs:3303`](../ui/dr-ui/src/develop.rs#L3303), [`ui/dr-ui/src/identity_ui.rs:123`](../ui/dr-ui/src/identity_ui.rs#L123), [`ui/dr-ui/src/lib.rs:1078`](../ui/dr-ui/src/lib.rs#L1078), [`ui/dr-ui/src/lib.rs:1522`](../ui/dr-ui/src/lib.rs#L1522), [`ui/dr-ui/src/memory.rs:163`](../ui/dr-ui/src/memory.rs#L163), [`ui/dr-ui/src/memory.rs:1`](../ui/dr-ui/src/memory.rs#L1) | +| FR-PLAT-AND-5 | [`apps/darkroom-android/src/lib.rs:166`](../apps/darkroom-android/src/lib.rs#L166), [`core/dr-gpu/src/adjust.rs:1029`](../core/dr-gpu/src/adjust.rs#L1029), [`core/dr-gpu/src/detail.rs:161`](../core/dr-gpu/src/detail.rs#L161), [`core/dr-gpu/src/detail.rs:547`](../core/dr-gpu/src/detail.rs#L547), [`core/dr-gpu/tests/detail_stage.rs:417`](../core/dr-gpu/tests/detail_stage.rs#L417), [`ui/dr-ui/src/develop.rs:3303`](../ui/dr-ui/src/develop.rs#L3303), [`ui/dr-ui/src/identity_ui.rs:121`](../ui/dr-ui/src/identity_ui.rs#L121), [`ui/dr-ui/src/lib.rs:1079`](../ui/dr-ui/src/lib.rs#L1079), [`ui/dr-ui/src/lib.rs:1528`](../ui/dr-ui/src/lib.rs#L1528), [`ui/dr-ui/src/memory.rs:163`](../ui/dr-ui/src/memory.rs#L163), [`ui/dr-ui/src/memory.rs:1`](../ui/dr-ui/src/memory.rs#L1) | | FR-PLAT-AND-6 | [`apps/darkroom-android/src/lib.rs:388`](../apps/darkroom-android/src/lib.rs#L388) | -| FR-PLAT-LIN-1 | [`core/dr-types/src/settings.rs:1`](../core/dr-types/src/settings.rs#L1), [`platform/dr-plat/src/storage.rs:344`](../platform/dr-plat/src/storage.rs#L344), [`ui/dr-ui/src/lib.rs:954`](../ui/dr-ui/src/lib.rs#L954), [`ui/dr-ui/src/preset_store.rs:1`](../ui/dr-ui/src/preset_store.rs#L1), [`ui/dr-ui/src/settings_store.rs:1`](../ui/dr-ui/src/settings_store.rs#L1) | +| FR-PLAT-LIN-1 | [`core/dr-types/src/settings.rs:1`](../core/dr-types/src/settings.rs#L1), [`platform/dr-plat/src/storage.rs:344`](../platform/dr-plat/src/storage.rs#L344), [`ui/dr-ui/src/lib.rs:955`](../ui/dr-ui/src/lib.rs#L955), [`ui/dr-ui/src/preset_store.rs:1`](../ui/dr-ui/src/preset_store.rs#L1), [`ui/dr-ui/src/settings_store.rs:1`](../ui/dr-ui/src/settings_store.rs#L1) | | FR-PLAT-LIN-2 | [`platform/dr-plat/src/display.rs:1`](../platform/dr-plat/src/display.rs#L1), [`platform/dr-plat/src/display/wayland.rs:1`](../platform/dr-plat/src/display/wayland.rs#L1), [`platform/dr-plat/src/display/x11.rs:1`](../platform/dr-plat/src/display/x11.rs#L1) | | FR-PLG-2 | [`core/dr-pipeline/src/declared/decl.rs:1`](../core/dr-pipeline/src/declared/decl.rs#L1), [`core/dr-pipeline/src/declared/expr.rs:152`](../core/dr-pipeline/src/declared/expr.rs#L152), [`core/dr-pipeline/src/declared/expr.rs:1`](../core/dr-pipeline/src/declared/expr.rs#L1), [`core/dr-pipeline/src/declared/mod.rs:1`](../core/dr-pipeline/src/declared/mod.rs#L1), [`core/dr-pipeline/src/declared/mod.rs:82`](../core/dr-pipeline/src/declared/mod.rs#L82), [`core/dr-pipeline/src/descriptor.rs:15`](../core/dr-pipeline/src/descriptor.rs#L15), [`core/dr-pipeline/src/descriptor.rs:652`](../core/dr-pipeline/src/descriptor.rs#L652), [`core/dr-pipeline/src/operation.rs:232`](../core/dr-pipeline/src/operation.rs#L232), [`core/dr-pipeline/tests/declared_parity.rs:1`](../core/dr-pipeline/tests/declared_parity.rs#L1), [`core/dr-pipeline/tests/declared_parity.rs:240`](../core/dr-pipeline/tests/declared_parity.rs#L240), [`core/dr-pipeline/tests/declared_parity.rs:305`](../core/dr-pipeline/tests/declared_parity.rs#L305), [`core/dr-pipeline/tests/declared_parity.rs:358`](../core/dr-pipeline/tests/declared_parity.rs#L358), [`core/dr-pipeline/tests/declared_parity.rs:416`](../core/dr-pipeline/tests/declared_parity.rs#L416) | | FR-PLG-2d | [`core/dr-pipeline/src/declared/decl.rs:112`](../core/dr-pipeline/src/declared/decl.rs#L112), [`core/dr-pipeline/src/declared/decl.rs:152`](../core/dr-pipeline/src/declared/decl.rs#L152), [`core/dr-pipeline/src/declared/decl.rs:1`](../core/dr-pipeline/src/declared/decl.rs#L1), [`core/dr-pipeline/src/declared/decl.rs:420`](../core/dr-pipeline/src/declared/decl.rs#L420), [`core/dr-pipeline/src/declared/decl.rs:67`](../core/dr-pipeline/src/declared/decl.rs#L67), [`core/dr-pipeline/src/declared/mod.rs:1`](../core/dr-pipeline/src/declared/mod.rs#L1), [`core/dr-pipeline/src/declared/mod.rs:384`](../core/dr-pipeline/src/declared/mod.rs#L384), [`core/dr-pipeline/src/declared/mod.rs:403`](../core/dr-pipeline/src/declared/mod.rs#L403) | | FR-PLG-8 | [`core/dr-pipeline/src/sidecar.rs:1762`](../core/dr-pipeline/src/sidecar.rs#L1762), [`core/dr-pipeline/src/sidecar.rs:1795`](../core/dr-pipeline/src/sidecar.rs#L1795) | | FR-RAW-1 | [`core/dr-decode/src/lib.rs:243`](../core/dr-decode/src/lib.rs#L243), [`core/dr-types/src/lib.rs:130`](../core/dr-types/src/lib.rs#L130), [`core/dr-types/src/lib.rs:201`](../core/dr-types/src/lib.rs#L201) | | FR-RAW-3 | [`core/dr-decode/src/lib.rs:139`](../core/dr-decode/src/lib.rs#L139), [`core/dr-decode/src/lib.rs:506`](../core/dr-decode/src/lib.rs#L506), [`core/dr-decode/src/locate.rs:1366`](../core/dr-decode/src/locate.rs#L1366) | -| FR-RAW-4 | [`core/dr-decode/src/error.rs:1`](../core/dr-decode/src/error.rs#L1), [`ui/dr-ui/src/lib.rs:229`](../ui/dr-ui/src/lib.rs#L229) | +| FR-RAW-4 | [`core/dr-decode/src/error.rs:1`](../core/dr-decode/src/error.rs#L1), [`ui/dr-ui/src/lib.rs:230`](../ui/dr-ui/src/lib.rs#L230) | | FR-RAW-5 | [`core/dr-decode/src/lib.rs:167`](../core/dr-decode/src/lib.rs#L167), [`core/dr-gpu/src/demosaic.rs:34`](../core/dr-gpu/src/demosaic.rs#L34), [`core/dr-gpu/src/demosaic.rs:602`](../core/dr-gpu/src/demosaic.rs#L602), [`core/dr-gpu/src/demosaic.rs:681`](../core/dr-gpu/src/demosaic.rs#L681), [`core/dr-gpu/src/demosaic.rs:805`](../core/dr-gpu/src/demosaic.rs#L805) | -| FR-UI-1 | [`ui/dr-ui/src/lib.rs:106`](../ui/dr-ui/src/lib.rs#L106), [`ui/dr-ui/src/lib.rs:3273`](../ui/dr-ui/src/lib.rs#L3273), [`ui/dr-ui/src/lib.rs:99`](../ui/dr-ui/src/lib.rs#L99), [`ui/dr-ui/src/masks_ui.rs:884`](../ui/dr-ui/src/masks_ui.rs#L884), [`ui/dr-ui/ui/app.slint:2213`](../ui/dr-ui/ui/app.slint#L2213), [`ui/dr-ui/ui/identity.slint:188`](../ui/dr-ui/ui/identity.slint#L188), [`ui/dr-ui/ui/library.slint:967`](../ui/dr-ui/ui/library.slint#L967), [`ui/dr-ui/ui/toolrail.slint:88`](../ui/dr-ui/ui/toolrail.slint#L88) | -| FR-UI-2 | [`ui/dr-ui/src/collections_ui.rs:1045`](../ui/dr-ui/src/collections_ui.rs#L1045), [`ui/dr-ui/src/collections_ui.rs:1055`](../ui/dr-ui/src/collections_ui.rs#L1055), [`ui/dr-ui/src/collections_ui.rs:116`](../ui/dr-ui/src/collections_ui.rs#L116), [`ui/dr-ui/src/collections_ui.rs:154`](../ui/dr-ui/src/collections_ui.rs#L154), [`ui/dr-ui/src/collections_ui.rs:1552`](../ui/dr-ui/src/collections_ui.rs#L1552), [`ui/dr-ui/src/collections_ui.rs:1569`](../ui/dr-ui/src/collections_ui.rs#L1569), [`ui/dr-ui/src/collections_ui.rs:1821`](../ui/dr-ui/src/collections_ui.rs#L1821), [`ui/dr-ui/src/collections_ui.rs:2926`](../ui/dr-ui/src/collections_ui.rs#L2926), [`ui/dr-ui/src/gestures.rs:1`](../ui/dr-ui/src/gestures.rs#L1), [`ui/dr-ui/src/lib.rs:99`](../ui/dr-ui/src/lib.rs#L99), [`ui/dr-ui/src/library_ui.rs:313`](../ui/dr-ui/src/library_ui.rs#L313), [`ui/dr-ui/src/library_ui.rs:5688`](../ui/dr-ui/src/library_ui.rs#L5688), [`ui/dr-ui/src/library_ui.rs:5833`](../ui/dr-ui/src/library_ui.rs#L5833), [`ui/dr-ui/src/library_ui.rs:7043`](../ui/dr-ui/src/library_ui.rs#L7043), [`ui/dr-ui/ui/app.slint:566`](../ui/dr-ui/ui/app.slint#L566), [`ui/dr-ui/ui/app.slint:580`](../ui/dr-ui/ui/app.slint#L580), [`ui/dr-ui/ui/app.slint:60`](../ui/dr-ui/ui/app.slint#L60), [`ui/dr-ui/ui/app.slint:931`](../ui/dr-ui/ui/app.slint#L931), [`ui/dr-ui/ui/controls.slint:593`](../ui/dr-ui/ui/controls.slint#L593), [`ui/dr-ui/ui/gestures.slint:1`](../ui/dr-ui/ui/gestures.slint#L1), [`ui/dr-ui/ui/library.slint:1173`](../ui/dr-ui/ui/library.slint#L1173), [`ui/dr-ui/ui/library.slint:1180`](../ui/dr-ui/ui/library.slint#L1180), [`ui/dr-ui/ui/library.slint:1277`](../ui/dr-ui/ui/library.slint#L1277), [`ui/dr-ui/ui/library.slint:2461`](../ui/dr-ui/ui/library.slint#L2461), [`ui/dr-ui/ui/library.slint:4057`](../ui/dr-ui/ui/library.slint#L4057), [`ui/dr-ui/ui/library.slint:856`](../ui/dr-ui/ui/library.slint#L856), [`ui/dr-ui/ui/library.slint:889`](../ui/dr-ui/ui/library.slint#L889), [`ui/dr-ui/ui/presets.slint:19`](../ui/dr-ui/ui/presets.slint#L19), [`ui/dr-ui/ui/settings.slint:110`](../ui/dr-ui/ui/settings.slint#L110) | +| FR-UI-1 | [`ui/dr-ui/src/lib.rs:100`](../ui/dr-ui/src/lib.rs#L100), [`ui/dr-ui/src/lib.rs:107`](../ui/dr-ui/src/lib.rs#L107), [`ui/dr-ui/src/lib.rs:3279`](../ui/dr-ui/src/lib.rs#L3279), [`ui/dr-ui/src/masks_ui.rs:884`](../ui/dr-ui/src/masks_ui.rs#L884), [`ui/dr-ui/ui/app.slint:2213`](../ui/dr-ui/ui/app.slint#L2213), [`ui/dr-ui/ui/identity.slint:188`](../ui/dr-ui/ui/identity.slint#L188), [`ui/dr-ui/ui/library.slint:967`](../ui/dr-ui/ui/library.slint#L967), [`ui/dr-ui/ui/toolrail.slint:88`](../ui/dr-ui/ui/toolrail.slint#L88) | +| FR-UI-2 | [`ui/dr-ui/src/collections_ui.rs:1045`](../ui/dr-ui/src/collections_ui.rs#L1045), [`ui/dr-ui/src/collections_ui.rs:1055`](../ui/dr-ui/src/collections_ui.rs#L1055), [`ui/dr-ui/src/collections_ui.rs:116`](../ui/dr-ui/src/collections_ui.rs#L116), [`ui/dr-ui/src/collections_ui.rs:154`](../ui/dr-ui/src/collections_ui.rs#L154), [`ui/dr-ui/src/collections_ui.rs:1552`](../ui/dr-ui/src/collections_ui.rs#L1552), [`ui/dr-ui/src/collections_ui.rs:1569`](../ui/dr-ui/src/collections_ui.rs#L1569), [`ui/dr-ui/src/collections_ui.rs:1821`](../ui/dr-ui/src/collections_ui.rs#L1821), [`ui/dr-ui/src/collections_ui.rs:2926`](../ui/dr-ui/src/collections_ui.rs#L2926), [`ui/dr-ui/src/gestures.rs:1`](../ui/dr-ui/src/gestures.rs#L1), [`ui/dr-ui/src/lib.rs:100`](../ui/dr-ui/src/lib.rs#L100), [`ui/dr-ui/src/library_ui.rs:313`](../ui/dr-ui/src/library_ui.rs#L313), [`ui/dr-ui/src/library_ui.rs:5688`](../ui/dr-ui/src/library_ui.rs#L5688), [`ui/dr-ui/src/library_ui.rs:5833`](../ui/dr-ui/src/library_ui.rs#L5833), [`ui/dr-ui/src/library_ui.rs:7043`](../ui/dr-ui/src/library_ui.rs#L7043), [`ui/dr-ui/ui/app.slint:566`](../ui/dr-ui/ui/app.slint#L566), [`ui/dr-ui/ui/app.slint:580`](../ui/dr-ui/ui/app.slint#L580), [`ui/dr-ui/ui/app.slint:60`](../ui/dr-ui/ui/app.slint#L60), [`ui/dr-ui/ui/app.slint:931`](../ui/dr-ui/ui/app.slint#L931), [`ui/dr-ui/ui/controls.slint:593`](../ui/dr-ui/ui/controls.slint#L593), [`ui/dr-ui/ui/gestures.slint:1`](../ui/dr-ui/ui/gestures.slint#L1), [`ui/dr-ui/ui/library.slint:1173`](../ui/dr-ui/ui/library.slint#L1173), [`ui/dr-ui/ui/library.slint:1180`](../ui/dr-ui/ui/library.slint#L1180), [`ui/dr-ui/ui/library.slint:1277`](../ui/dr-ui/ui/library.slint#L1277), [`ui/dr-ui/ui/library.slint:2461`](../ui/dr-ui/ui/library.slint#L2461), [`ui/dr-ui/ui/library.slint:4057`](../ui/dr-ui/ui/library.slint#L4057), [`ui/dr-ui/ui/library.slint:856`](../ui/dr-ui/ui/library.slint#L856), [`ui/dr-ui/ui/library.slint:889`](../ui/dr-ui/ui/library.slint#L889), [`ui/dr-ui/ui/presets.slint:19`](../ui/dr-ui/ui/presets.slint#L19), [`ui/dr-ui/ui/settings.slint:110`](../ui/dr-ui/ui/settings.slint#L110) | | FR-UI-3 | [`ui/dr-ui/src/develop.rs:2291`](../ui/dr-ui/src/develop.rs#L2291), [`ui/dr-ui/src/develop.rs:2400`](../ui/dr-ui/src/develop.rs#L2400), [`ui/dr-ui/src/library_ui.rs:4795`](../ui/dr-ui/src/library_ui.rs#L4795), [`ui/dr-ui/src/masks_ui.rs:249`](../ui/dr-ui/src/masks_ui.rs#L249), [`ui/dr-ui/src/masks_ui.rs:976`](../ui/dr-ui/src/masks_ui.rs#L976), [`ui/dr-ui/src/masks_ui.rs:998`](../ui/dr-ui/src/masks_ui.rs#L998), [`ui/dr-ui/src/spots_ui.rs:19`](../ui/dr-ui/src/spots_ui.rs#L19), [`ui/dr-ui/ui/app.slint:2022`](../ui/dr-ui/ui/app.slint#L2022), [`ui/dr-ui/ui/collections.slint:4`](../ui/dr-ui/ui/collections.slint#L4), [`ui/dr-ui/ui/collections.slint:747`](../ui/dr-ui/ui/collections.slint#L747), [`ui/dr-ui/ui/masks.slint:611`](../ui/dr-ui/ui/masks.slint#L611) | | FR-UI-4 | [`tools/traceability/src/gestures.rs:1`](../tools/traceability/src/gestures.rs#L1), [`ui/dr-ui/src/collections_ui.rs:1045`](../ui/dr-ui/src/collections_ui.rs#L1045), [`ui/dr-ui/src/collections_ui.rs:1055`](../ui/dr-ui/src/collections_ui.rs#L1055), [`ui/dr-ui/src/collections_ui.rs:116`](../ui/dr-ui/src/collections_ui.rs#L116), [`ui/dr-ui/src/collections_ui.rs:117`](../ui/dr-ui/src/collections_ui.rs#L117), [`ui/dr-ui/src/collections_ui.rs:126`](../ui/dr-ui/src/collections_ui.rs#L126), [`ui/dr-ui/src/collections_ui.rs:154`](../ui/dr-ui/src/collections_ui.rs#L154), [`ui/dr-ui/src/collections_ui.rs:1552`](../ui/dr-ui/src/collections_ui.rs#L1552), [`ui/dr-ui/src/collections_ui.rs:1569`](../ui/dr-ui/src/collections_ui.rs#L1569), [`ui/dr-ui/src/collections_ui.rs:1685`](../ui/dr-ui/src/collections_ui.rs#L1685), [`ui/dr-ui/src/collections_ui.rs:1821`](../ui/dr-ui/src/collections_ui.rs#L1821), [`ui/dr-ui/src/collections_ui.rs:1848`](../ui/dr-ui/src/collections_ui.rs#L1848), [`ui/dr-ui/src/collections_ui.rs:1964`](../ui/dr-ui/src/collections_ui.rs#L1964), [`ui/dr-ui/src/collections_ui.rs:2926`](../ui/dr-ui/src/collections_ui.rs#L2926), [`ui/dr-ui/src/collections_ui.rs:2963`](../ui/dr-ui/src/collections_ui.rs#L2963), [`ui/dr-ui/src/collections_ui.rs:365`](../ui/dr-ui/src/collections_ui.rs#L365), [`ui/dr-ui/src/collections_ui.rs:569`](../ui/dr-ui/src/collections_ui.rs#L569), [`ui/dr-ui/src/collections_ui.rs:589`](../ui/dr-ui/src/collections_ui.rs#L589), [`ui/dr-ui/src/gesture_book.rs:3`](../ui/dr-ui/src/gesture_book.rs#L3), [`ui/dr-ui/src/gestures.rs:1`](../ui/dr-ui/src/gestures.rs#L1), [`ui/dr-ui/src/library_ui.rs:4795`](../ui/dr-ui/src/library_ui.rs#L4795), [`ui/dr-ui/src/library_ui.rs:4807`](../ui/dr-ui/src/library_ui.rs#L4807), [`ui/dr-ui/src/library_ui.rs:4861`](../ui/dr-ui/src/library_ui.rs#L4861), [`ui/dr-ui/src/library_ui.rs:4964`](../ui/dr-ui/src/library_ui.rs#L4964), [`ui/dr-ui/src/library_ui.rs:4992`](../ui/dr-ui/src/library_ui.rs#L4992), [`ui/dr-ui/src/library_ui.rs:5821`](../ui/dr-ui/src/library_ui.rs#L5821), [`ui/dr-ui/src/library_ui.rs:5833`](../ui/dr-ui/src/library_ui.rs#L5833), [`ui/dr-ui/ui/app.slint:1862`](../ui/dr-ui/ui/app.slint#L1862), [`ui/dr-ui/ui/app.slint:542`](../ui/dr-ui/ui/app.slint#L542), [`ui/dr-ui/ui/app.slint:571`](../ui/dr-ui/ui/app.slint#L571), [`ui/dr-ui/ui/app.slint:580`](../ui/dr-ui/ui/app.slint#L580), [`ui/dr-ui/ui/app.slint:665`](../ui/dr-ui/ui/app.slint#L665), [`ui/dr-ui/ui/gestures.slint:1`](../ui/dr-ui/ui/gestures.slint#L1), [`ui/dr-ui/ui/library.slint:1148`](../ui/dr-ui/ui/library.slint#L1148), [`ui/dr-ui/ui/library.slint:1173`](../ui/dr-ui/ui/library.slint#L1173), [`ui/dr-ui/ui/library.slint:1180`](../ui/dr-ui/ui/library.slint#L1180), [`ui/dr-ui/ui/library.slint:1213`](../ui/dr-ui/ui/library.slint#L1213), [`ui/dr-ui/ui/library.slint:1270`](../ui/dr-ui/ui/library.slint#L1270), [`ui/dr-ui/ui/library.slint:1277`](../ui/dr-ui/ui/library.slint#L1277), [`ui/dr-ui/ui/library.slint:2461`](../ui/dr-ui/ui/library.slint#L2461), [`ui/dr-ui/ui/library.slint:3941`](../ui/dr-ui/ui/library.slint#L3941), [`ui/dr-ui/ui/library.slint:4057`](../ui/dr-ui/ui/library.slint#L4057), [`ui/dr-ui/ui/library.slint:856`](../ui/dr-ui/ui/library.slint#L856), [`ui/dr-ui/ui/library.slint:883`](../ui/dr-ui/ui/library.slint#L883), [`ui/dr-ui/ui/library.slint:889`](../ui/dr-ui/ui/library.slint#L889), [`ui/dr-ui/ui/library.slint:944`](../ui/dr-ui/ui/library.slint#L944) | -| FR-UI-5 | [`ui/dr-ui/src/collections_ui.rs:1`](../ui/dr-ui/src/collections_ui.rs#L1), [`ui/dr-ui/src/lib.rs:2701`](../ui/dr-ui/src/lib.rs#L2701), [`ui/dr-ui/src/lib.rs:3312`](../ui/dr-ui/src/lib.rs#L3312), [`ui/dr-ui/src/lib.rs:3497`](../ui/dr-ui/src/lib.rs#L3497), [`ui/dr-ui/src/masks_ui.rs:931`](../ui/dr-ui/src/masks_ui.rs#L931), [`ui/dr-ui/ui/app.slint:121`](../ui/dr-ui/ui/app.slint#L121), [`ui/dr-ui/ui/collections.slint:4`](../ui/dr-ui/ui/collections.slint#L4) | +| FR-UI-5 | [`ui/dr-ui/src/collections_ui.rs:1`](../ui/dr-ui/src/collections_ui.rs#L1), [`ui/dr-ui/src/lib.rs:2707`](../ui/dr-ui/src/lib.rs#L2707), [`ui/dr-ui/src/lib.rs:3318`](../ui/dr-ui/src/lib.rs#L3318), [`ui/dr-ui/src/lib.rs:3503`](../ui/dr-ui/src/lib.rs#L3503), [`ui/dr-ui/src/masks_ui.rs:931`](../ui/dr-ui/src/masks_ui.rs#L931), [`ui/dr-ui/ui/app.slint:121`](../ui/dr-ui/ui/app.slint#L121), [`ui/dr-ui/ui/collections.slint:4`](../ui/dr-ui/ui/collections.slint#L4) | | FR-UI-6 | [`ui/dr-ui/ui/widgets.slint:1`](../ui/dr-ui/ui/widgets.slint#L1) | | FR-UI-7 | [`core/dr-pipeline/src/descriptor.rs:177`](../core/dr-pipeline/src/descriptor.rs#L177), [`core/dr-pipeline/src/framing.rs:385`](../core/dr-pipeline/src/framing.rs#L385) | | NFR-A11Y-2 | [`ui/dr-ui/tests/ui_controls_are_accessible.rs:1`](../ui/dr-ui/tests/ui_controls_are_accessible.rs#L1) | | NFR-A11Y-3 | [`ui/dr-ui/src/histogram.rs:356`](../ui/dr-ui/src/histogram.rs#L356), [`ui/dr-ui/ui/histogram.slint:137`](../ui/dr-ui/ui/histogram.slint#L137), [`ui/dr-ui/ui/library.slint:652`](../ui/dr-ui/ui/library.slint#L652), [`ui/dr-ui/ui/library.slint:800`](../ui/dr-ui/ui/library.slint#L800), [`ui/dr-ui/ui/peaking.slint:1`](../ui/dr-ui/ui/peaking.slint#L1) | -| NFR-ARCH-2 | [`core/dr-catalog/src/jobs.rs:1`](../core/dr-catalog/src/jobs.rs#L1), [`ui/dr-ui/src/faces.rs:1`](../ui/dr-ui/src/faces.rs#L1), [`ui/dr-ui/src/library.rs:3069`](../ui/dr-ui/src/library.rs#L3069) | -| NFR-ARCH-3 | [`ui/dr-ui/src/export.rs:1553`](../ui/dr-ui/src/export.rs#L1553), [`ui/dr-ui/src/export.rs:1579`](../ui/dr-ui/src/export.rs#L1579), [`ui/dr-ui/src/export.rs:409`](../ui/dr-ui/src/export.rs#L409), [`ui/dr-ui/src/export.rs:435`](../ui/dr-ui/src/export.rs#L435), [`ui/dr-ui/src/lib.rs:2398`](../ui/dr-ui/src/lib.rs#L2398), [`ui/dr-ui/ui/app.slint:1110`](../ui/dr-ui/ui/app.slint#L1110), [`ui/dr-ui/ui/library.slint:4019`](../ui/dr-ui/ui/library.slint#L4019) | +| NFR-ARCH-2 | [`core/dr-catalog/src/jobs.rs:1`](../core/dr-catalog/src/jobs.rs#L1), [`ui/dr-ui/src/faces.rs:1`](../ui/dr-ui/src/faces.rs#L1), [`ui/dr-ui/src/library.rs:3058`](../ui/dr-ui/src/library.rs#L3058) | +| NFR-ARCH-3 | [`ui/dr-ui/src/export.rs:1553`](../ui/dr-ui/src/export.rs#L1553), [`ui/dr-ui/src/export.rs:1579`](../ui/dr-ui/src/export.rs#L1579), [`ui/dr-ui/src/export.rs:409`](../ui/dr-ui/src/export.rs#L409), [`ui/dr-ui/src/export.rs:435`](../ui/dr-ui/src/export.rs#L435), [`ui/dr-ui/src/lib.rs:2404`](../ui/dr-ui/src/lib.rs#L2404), [`ui/dr-ui/ui/app.slint:1110`](../ui/dr-ui/ui/app.slint#L1110), [`ui/dr-ui/ui/library.slint:4019`](../ui/dr-ui/ui/library.slint#L4019) | | NFR-ARCH-4 | [`core/dr-catalog/src/error.rs:1`](../core/dr-catalog/src/error.rs#L1), [`core/dr-export/src/error.rs:1`](../core/dr-export/src/error.rs#L1), [`core/dr-thumbs/src/error.rs:1`](../core/dr-thumbs/src/error.rs#L1), [`platform/dr-plat/src/storage.rs:148`](../platform/dr-plat/src/storage.rs#L148), [`ui/dr-ui/src/export.rs:499`](../ui/dr-ui/src/export.rs#L499) | | NFR-OPS-1 | [`platform/dr-plat/src/diagnostics.rs:1`](../platform/dr-plat/src/diagnostics.rs#L1), [`platform/dr-plat/src/state.rs:1`](../platform/dr-plat/src/state.rs#L1) | | NFR-OPS-2 | [`platform/dr-plat/src/crash.rs:1`](../platform/dr-plat/src/crash.rs#L1) | | NFR-OPS-3 | [`core/dr-types/src/settings.rs:1`](../core/dr-types/src/settings.rs#L1), [`ui/dr-ui/src/settings_store.rs:1`](../ui/dr-ui/src/settings_store.rs#L1) | | NFR-P1 | [`core/dr-catalog/src/lib.rs:1`](../core/dr-catalog/src/lib.rs#L1), [`core/dr-catalog/src/scan.rs:1`](../core/dr-catalog/src/scan.rs#L1), [`core/dr-catalog/src/walk.rs:162`](../core/dr-catalog/src/walk.rs#L162), [`core/dr-catalog/src/walk.rs:1`](../core/dr-catalog/src/walk.rs#L1), [`tools/bench/src/catalog_open.rs:1`](../tools/bench/src/catalog_open.rs#L1) | | NFR-P13 | [`core/dr-decode/src/preview.rs:121`](../core/dr-decode/src/preview.rs#L121) | -| NFR-P14 | [`core/dr-gpu/src/focus.rs:186`](../core/dr-gpu/src/focus.rs#L186), [`core/dr-gpu/src/focus.rs:1`](../core/dr-gpu/src/focus.rs#L1), [`core/dr-gpu/src/focus.rs:317`](../core/dr-gpu/src/focus.rs#L317), [`core/dr-gpu/src/shaders/focus_peak.wgsl:1`](../core/dr-gpu/src/shaders/focus_peak.wgsl#L1), [`ui/dr-ui/src/develop.rs:3198`](../ui/dr-ui/src/develop.rs#L3198), [`ui/dr-ui/src/lib.rs:1764`](../ui/dr-ui/src/lib.rs#L1764) | +| NFR-P14 | [`core/dr-gpu/src/focus.rs:186`](../core/dr-gpu/src/focus.rs#L186), [`core/dr-gpu/src/focus.rs:1`](../core/dr-gpu/src/focus.rs#L1), [`core/dr-gpu/src/focus.rs:317`](../core/dr-gpu/src/focus.rs#L317), [`core/dr-gpu/src/shaders/focus_peak.wgsl:1`](../core/dr-gpu/src/shaders/focus_peak.wgsl#L1), [`ui/dr-ui/src/develop.rs:3198`](../ui/dr-ui/src/develop.rs#L3198), [`ui/dr-ui/src/lib.rs:1770`](../ui/dr-ui/src/lib.rs#L1770) | | NFR-P3 | [`tools/bench/src/thumbnails.rs:1`](../tools/bench/src/thumbnails.rs#L1) | -| NFR-P5 | [`core/dr-catalog/src/schema.rs:337`](../core/dr-catalog/src/schema.rs#L337), [`ui/dr-ui/src/library.rs:4185`](../ui/dr-ui/src/library.rs#L4185), [`ui/dr-ui/src/library.rs:5119`](../ui/dr-ui/src/library.rs#L5119), [`ui/dr-ui/src/library_ui.rs:4468`](../ui/dr-ui/src/library_ui.rs#L4468), [`ui/dr-ui/src/library_ui.rs:64`](../ui/dr-ui/src/library_ui.rs#L64) | +| NFR-P5 | [`core/dr-catalog/src/schema.rs:337`](../core/dr-catalog/src/schema.rs#L337), [`ui/dr-ui/src/library.rs:4257`](../ui/dr-ui/src/library.rs#L4257), [`ui/dr-ui/src/library.rs:5191`](../ui/dr-ui/src/library.rs#L5191), [`ui/dr-ui/src/library_ui.rs:4468`](../ui/dr-ui/src/library_ui.rs#L4468), [`ui/dr-ui/src/library_ui.rs:64`](../ui/dr-ui/src/library_ui.rs#L64) | | NFR-P9 | [`ui/dr-ui/src/collections_ui.rs:1`](../ui/dr-ui/src/collections_ui.rs#L1), [`ui/dr-ui/src/export.rs:942`](../ui/dr-ui/src/export.rs#L942), [`ui/dr-ui/src/library.rs:1`](../ui/dr-ui/src/library.rs#L1), [`ui/dr-ui/src/library_ui.rs:1`](../ui/dr-ui/src/library_ui.rs#L1), [`ui/dr-ui/src/trash.rs:1`](../ui/dr-ui/src/trash.rs#L1) | | NFR-PORT-1 | [`core/dr-catalog/src/walk.rs:1`](../core/dr-catalog/src/walk.rs#L1), [`core/dr-types/src/lib.rs:270`](../core/dr-types/src/lib.rs#L270), [`core/dr-types/src/lib.rs:303`](../core/dr-types/src/lib.rs#L303), [`platform/dr-plat/src/display.rs:1`](../platform/dr-plat/src/display.rs#L1), [`platform/dr-plat/src/storage.rs:1`](../platform/dr-plat/src/storage.rs#L1), [`platform/dr-plat/src/storage.rs:216`](../platform/dr-plat/src/storage.rs#L216), [`platform/dr-plat/src/storage.rs:287`](../platform/dr-plat/src/storage.rs#L287), [`platform/dr-plat/src/storage.rs:344`](../platform/dr-plat/src/storage.rs#L344), [`platform/dr-plat/src/volumes.rs:1`](../platform/dr-plat/src/volumes.rs#L1), [`platform/dr-plat/src/volumes.rs:62`](../platform/dr-plat/src/volumes.rs#L62) | | NFR-PORT-2 | [`core/dr-gpu/src/lib.rs:1`](../core/dr-gpu/src/lib.rs#L1) | @@ -149,7 +149,8 @@ _None._ | NFR-R6 | [`core/dr-catalog/src/error.rs:1`](../core/dr-catalog/src/error.rs#L1), [`core/dr-catalog/src/lib.rs:255`](../core/dr-catalog/src/lib.rs#L255), [`core/dr-catalog/src/recovery.rs:1`](../core/dr-catalog/src/recovery.rs#L1) | | NFR-R7 | [`core/dr-gpu/src/error.rs:1`](../core/dr-gpu/src/error.rs#L1) | | NFR-R8 | [`core/dr-gpu/src/error.rs:1`](../core/dr-gpu/src/error.rs#L1) | -| NFR-RES-1 | [`core/dr-gpu/src/adjust.rs:1029`](../core/dr-gpu/src/adjust.rs#L1029), [`core/dr-pipeline/src/history.rs:86`](../core/dr-pipeline/src/history.rs#L86), [`ui/dr-ui/src/develop.rs:3303`](../ui/dr-ui/src/develop.rs#L3303), [`ui/dr-ui/src/identity_ui.rs:123`](../ui/dr-ui/src/identity_ui.rs#L123), [`ui/dr-ui/src/lib.rs:91`](../ui/dr-ui/src/lib.rs#L91), [`ui/dr-ui/src/memory.rs:1`](../ui/dr-ui/src/memory.rs#L1) | +| NFR-RES-1 | [`core/dr-gpu/src/adjust.rs:1029`](../core/dr-gpu/src/adjust.rs#L1029), [`core/dr-pipeline/src/history.rs:86`](../core/dr-pipeline/src/history.rs#L86), [`ui/dr-ui/src/develop.rs:3303`](../ui/dr-ui/src/develop.rs#L3303), [`ui/dr-ui/src/identity_ui.rs:121`](../ui/dr-ui/src/identity_ui.rs#L121), [`ui/dr-ui/src/lib.rs:92`](../ui/dr-ui/src/lib.rs#L92), [`ui/dr-ui/src/memory.rs:1`](../ui/dr-ui/src/memory.rs#L1) | +| NFR-RES-2 | [`ui/dr-ui/src/faces.rs:346`](../ui/dr-ui/src/faces.rs#L346), [`ui/dr-ui/src/library.rs:3058`](../ui/dr-ui/src/library.rs#L3058), [`ui/dr-ui/src/library.rs:3249`](../ui/dr-ui/src/library.rs#L3249) | | NFR-RES-4 | [`core/dr-catalog/src/cache.rs:1`](../core/dr-catalog/src/cache.rs#L1), [`core/dr-catalog/src/face_shard.rs:1`](../core/dr-catalog/src/face_shard.rs#L1), [`core/dr-catalog/src/schema.rs:725`](../core/dr-catalog/src/schema.rs#L725), [`core/dr-gpu/src/lib.rs:93`](../core/dr-gpu/src/lib.rs#L93), [`core/dr-thumbs/src/codec.rs:1`](../core/dr-thumbs/src/codec.rs#L1), [`core/dr-thumbs/src/lib.rs:1`](../core/dr-thumbs/src/lib.rs#L1), [`core/dr-thumbs/src/lib.rs:376`](../core/dr-thumbs/src/lib.rs#L376), [`ui/dr-ui/src/library.rs:2964`](../ui/dr-ui/src/library.rs#L2964) | | NFR-SEC-1 | [`core/dr-decode/src/error.rs:1`](../core/dr-decode/src/error.rs#L1) | | NFR-SEC-2 | [`core/dr-sync/src/account.rs:298`](../core/dr-sync/src/account.rs#L298), [`platform/dr-plat/src/crash.rs:1`](../platform/dr-plat/src/crash.rs#L1), [`platform/dr-plat/src/diagnostics.rs:1`](../platform/dr-plat/src/diagnostics.rs#L1), [`platform/dr-plat/src/diagnostics/redact.rs:1`](../platform/dr-plat/src/diagnostics/redact.rs#L1), [`platform/dr-plat/src/secrets.rs:82`](../platform/dr-plat/src/secrets.rs#L82) | @@ -161,7 +162,7 @@ _None._ ## Not yet tagged -54 of 179 requirements have no implementation tag. Expected while the codebase is young; each should gain one as it is built. +53 of 179 requirements have no implementation tag. Expected while the codebase is young; each should gain one as it is built.
Show untagged requirements @@ -212,7 +213,6 @@ _None._ - NFR-P8 - NFR-R3 - NFR-R4 -- NFR-RES-2 - NFR-RES-3 - NFR-SEC-4 - NFR-SEC-6 diff --git a/ui/dr-ui/examples/face_native.rs b/ui/dr-ui/examples/face_native.rs new file mode 100644 index 0000000..e44be93 --- /dev/null +++ b/ui/dr-ui/examples/face_native.rs @@ -0,0 +1,144 @@ +//! TRACES: FR-CULL-8 | FR-EXP-9 +//! Measure what indexing at native resolution is actually worth. +//! +//! cargo run -p dr-ui --example face_native -- DET.onnx EMB.onnx FILE [FILE…] +//! +//! Renders each file once at native resolution, then indexes it twice: the way +//! FR-CULL-8 now specifies, and the way it used to be done — everything, both +//! stages, from a 1024px proxy. Prints the faces found and the `crop_px` each +//! run gave the embedder. +//! +//! It exists because the case for the change was made from `crop_px` readings +//! taken out of a catalog after the fact. That is evidence about what happened; +//! this is evidence about what the new code does, on the same photographs, with +//! nothing between the two runs but the resolution. +//! +//! The models must have had their input dims frozen first; see +//! `tools/fix-face-model-shapes.sh`. + +use std::path::PathBuf; + +const MODEL_ID: &str = "w600k_mbf"; + +fn main() { + env_logger::init(); + + let args: Vec = std::env::args().skip(1).collect(); + if args.len() < 3 { + eprintln!("usage: face_native DETECTOR.onnx EMBEDDER.onnx FILE [FILE…]"); + std::process::exit(2); + } + let (detector_model, embedder_model) = (PathBuf::from(&args[0]), PathBuf::from(&args[1])); + + let Some(gpu) = pollster::block_on(dr_gpu::GpuContext::new_headless()).ok() else { + eprintln!("no GPU adapter; a native render needs one"); + std::process::exit(1); + }; + + let mut detector = match dr_face::Detector::from_path(&detector_model) { + Ok(d) => d, + Err(e) => { + eprintln!("detector: {e}"); + std::process::exit(1); + } + }; + let mut embedder = + match dr_face::Embedder::from_path(&embedder_model, dr_face::ModelId::new(MODEL_ID)) { + Ok(e) => e, + Err(e) => { + eprintln!("embedder: {e}"); + std::process::exit(1); + } + }; + let options = dr_face::DetectOptions::default(); + + println!( + "{:<28} {:>11} {:>17} {:>17}", + "file", "native", "native faces/px", "1024 faces/px" + ); + + let (mut n_native, mut n_proxy) = (0usize, 0usize); + let (mut px_native, mut px_proxy) = (0.0f32, 0.0f32); + + for path in &args[2..] { + let bytes = match std::fs::read(path) { + Ok(b) => b, + Err(e) => { + println!("{path}: cannot read: {e}"); + continue; + } + }; + let frame = match dr_ui::render_native(&gpu, &bytes) { + Ok(f) => f, + Err(e) => { + println!("{path}: cannot render: {e}"); + continue; + } + }; + let (w, h) = (frame.width as usize, frame.height as usize); + + let native = dr_ui::faces::index_native( + &mut detector, + &mut embedder, + &frame.rgba, + w, + h, + &options, + ) + .unwrap_or_default(); + + // The old path, reproduced exactly: one buffer at 1024, used for both + // detection and the crop. + let mut small = dr_decode::Preview { + width: frame.width, + height: frame.height, + rgba: frame.rgba.clone(), + }; + small.downscale_to(dr_thumbs::ThumbSize::Large.edge()); + let proxy = dr_ui::faces::index_preview(&mut detector, &mut embedder, &small, &options) + .map(|(f, _)| f) + .unwrap_or_default(); + + let mean = |v: &[dr_catalog::faces::DetectedFace]| { + if v.is_empty() { + 0.0 + } else { + v.iter().map(|f| f.crop_px).sum::() / v.len() as f32 + } + }; + let name = std::path::Path::new(path) + .file_name() + .map(|s| s.to_string_lossy().into_owned()) + .unwrap_or_else(|| path.clone()); + + println!( + "{:<28} {:>11} {:>10} /{:>5.0} {:>10} /{:>5.0}", + name, + format!("{}x{}", frame.width, frame.height), + native.len(), + mean(&native), + proxy.len(), + mean(&proxy), + ); + + n_native += native.len(); + n_proxy += proxy.len(); + px_native += native.iter().map(|f| f.crop_px).sum::(); + px_proxy += proxy.iter().map(|f| f.crop_px).sum::(); + } + + println!( + "\ntotal: native {n_native} face(s), mean crop {:.0}px | \ + 1024 proxy {n_proxy} face(s), mean crop {:.0}px", + if n_native == 0 { + 0.0 + } else { + px_native / n_native as f32 + }, + if n_proxy == 0 { + 0.0 + } else { + px_proxy / n_proxy as f32 + }, + ); +} diff --git a/ui/dr-ui/src/faces.rs b/ui/dr-ui/src/faces.rs index 8f62189..79f66be 100644 --- a/ui/dr-ui/src/faces.rs +++ b/ui/dr-ui/src/faces.rs @@ -327,6 +327,253 @@ pub fn index_proxy( Ok(out) } +/// TRACES: FR-CULL-8 +/// Long edge the detector's input is reduced to, in pixels. +/// +/// Detection is indifferent above this — `dr_face::detect` letterboxes into a +/// fixed 640×640 whatever it receives, so a face is the same size to the model +/// from a 1600px buffer as from a 6000px one (faces.md §4.1). What the +/// reduction buys is the aliasing that a single bilinear step from native to +/// 640 would introduce: a 9× decimation samples one pixel in nine and drops +/// small faces into the gaps between samples. Box-filtering to 1600 first, and +/// letting the letterbox take the remaining 2.5×, keeps every source pixel in +/// the average. +/// +/// It also bounds the buffer that has to exist as `f32`: 1600×1067 is 20 MB +/// against 288 MB for a native 24 MP frame. +const DETECT_EDGE: usize = 1600; + +/// TRACES: FR-CULL-8 | NFR-RES-2 +/// Detect and embed every face in one **native-resolution** render. +/// +/// The pass FR-CULL-8 specifies, and the two resolutions in it are the whole +/// point: +/// +/// - the **detector** is handed a box-filtered reduction, because it discards +/// anything above its own 640px input anyway; +/// - the **crop** is warped out of the native buffer, because that is the one +/// place source resolution becomes embedding quality. +/// +/// Boxes and landmarks come back in the reduction's coordinates and are scaled +/// to native before a single crop pixel is read. Getting that scaling wrong +/// does not fail loudly — it yields faces in plausible-looking places with +/// crops taken from beside them — so it is one multiply applied in one place +/// rather than at each use. +/// +/// `rgba` is the native render, tightly packed 8-bit RGBA. It is never +/// converted wholesale: [`dr_face::Pixels`] samples it where the warp and the +/// stored crop actually touch it. +pub fn index_native( + detector: &mut Detector, + embedder: &mut Embedder, + rgba: &[u8], + width: usize, + height: usize, + options: &DetectOptions, +) -> Result, dr_face::FaceError> { + if !index_native_shape_ok(rgba, width, height) { + return Ok(Vec::new()); + } + let long_edge = width.max(height) as f32; + let native = dr_face::Pixels::Rgba8(rgba); + + // One reduction, reused for every face on the image. + let (dw, dh, small) = reduce_for_detection(rgba, width, height); + if dw == 0 || dh == 0 { + return Ok(Vec::new()); + } + let dets = detector.detect(&small, dw, dh, options)?; + + // Detector coordinates to native. Separate factors rather than one, because + // the reduction rounds each axis independently and assuming they match puts + // every landmark a fraction of a face off on the shorter one. + let (sx, sy) = (width as f32 / dw as f32, height as f32 / dh as f32); + + let mut out = Vec::with_capacity(dets.len()); + for d in &dets { + let landmarks = scale_landmarks(&d.landmarks, sx, sy); + let Some(aligned) = dr_face::warp_pixels(native, width, height, &landmarks) else { + log::debug!("face with degenerate landmarks skipped"); + continue; + }; + + // Both gates read the aligned crop, so they mean what they say only now + // that the crop comes from native pixels — `source_px` is the real + // count, not the proxy's idea of it. See `index_proxy` for why each + // one is here rather than in the detector. + if aligned.source_px() < options.min_source_px { + log::debug!( + "face skipped: {:.0} source px below {:.0}", + aligned.source_px(), + options.min_source_px + ); + continue; + } + let sharpness = aligned.sharpness(); + if sharpness < options.min_sharpness { + log::debug!( + "face at {:.0}px skipped: sharpness {sharpness:.4} below {:.4}", + aligned.source_px(), + options.min_sharpness + ); + continue; + } + + let embedding = embedder.embed(&aligned)?; + let (bx, by) = (d.bbox.0 * sx, d.bbox.1 * sy); + let (bw, bh) = (d.width() * sx, d.height() * sy); + + out.push(DetectedFace { + x: bx / long_edge, + y: by / long_edge, + w: bw / long_edge, + h: bh / long_edge, + landmarks: normalise_landmarks(&landmarks, long_edge), + confidence: d.confidence, + embedding: embedding.to_f16_bytes(), + crop_px: aligned.source_px(), + model_id: embedder.model().as_str().to_string(), + crop: cut_crop_native(native, width, height, (bx, by, bw, bh)).unwrap_or_default(), + }); + } + + Ok(out) +} + +/// Whether a buffer is the native frame it claims to be. +/// +/// Checked before anything expensive, and before the detector above all: a +/// mismatched buffer would otherwise be read past its end by the reduction. +fn index_native_shape_ok(rgba: &[u8], width: usize, height: usize) -> bool { + width > 0 && height > 0 && rgba.len() == width * height * 4 +} + +/// Landmarks from the detector's reduction into native coordinates. +fn scale_landmarks(lm: &[(f32, f32); 5], sx: f32, sy: f32) -> [(f32, f32); 5] { + let mut out = [(0.0_f32, 0.0_f32); 5]; + for (o, &(x, y)) in out.iter_mut().zip(lm.iter()) { + *o = (x * sx, y * sy); + } + out +} + +/// Box-filter a native RGBA render down to [`DETECT_EDGE`], as packed `f32` RGB. +/// +/// Averaging rather than sampling. The detector's job is to find small faces, +/// and a point-sampled reduction is exactly the operation that removes them: +/// at a 4× decimation fifteen of every sixteen pixels are discarded, and a +/// 40px face survives or not depending on where it happens to sit relative to +/// the sample grid. +fn reduce_for_detection(rgba: &[u8], width: usize, height: usize) -> (usize, usize, Vec) { + reduce_to(rgba, width, height, DETECT_EDGE) +} + +/// [`reduce_for_detection`], to a stated long edge. +/// +/// Split out so the averaging can be tested at a size a test can reason about +/// by hand, rather than by building a 1600px fixture. +fn reduce_to( + rgba: &[u8], + width: usize, + height: usize, + target: usize, +) -> (usize, usize, Vec) { + let long = width.max(height); + if long <= target { + // Already small enough; convert without resampling rather than round + // -tripping through a scale of 1. + let mut out = vec![0.0_f32; width * height * 3]; + for (i, px) in rgba.chunks_exact(4).enumerate() { + for c in 0..3 { + out[i * 3 + c] = px[c] as f32 / 255.0; + } + } + return (width, height, out); + } + + let scale = target as f32 / long as f32; + let (dw, dh) = ( + ((width as f32 * scale).round() as usize).max(1), + ((height as f32 * scale).round() as usize).max(1), + ); + let mut out = vec![0.0_f32; dw * dh * 3]; + for oy in 0..dh { + // Source span of this destination row, as a half-open range, so + // adjacent rows tile the source exactly and no row is counted twice. + let y0 = oy * height / dh; + let y1 = (((oy + 1) * height) / dh).max(y0 + 1).min(height); + for ox in 0..dw { + let x0 = ox * width / dw; + let x1 = (((ox + 1) * width) / dw).max(x0 + 1).min(width); + + let mut acc = [0.0_f32; 3]; + let mut n = 0.0_f32; + for y in y0..y1 { + for x in x0..x1 { + let i = (y * width + x) * 4; + for (c, a) in acc.iter_mut().enumerate() { + *a += rgba[i + c] as f32; + } + n += 1.0; + } + } + let o = (oy * dw + ox) * 3; + for c in 0..3 { + out[o + c] = acc[c] / n / 255.0; + } + } + } + (dw, dh, out) +} + +/// The stored face thumbnail, cut from the native buffer. +/// +/// Same framing as [`cut_crop`] and deliberately a separate function rather +/// than a generalisation of it: this one takes a box already in native +/// coordinates, and blurring that distinction is how a crop ends up sampled +/// from the wrong scale. +fn cut_crop_native( + px: dr_face::Pixels<'_>, + width: usize, + height: usize, + bbox: (f32, f32, f32, f32), +) -> Option> { + if width == 0 || height == 0 { + return None; + } + let (bx, by, bw, bh) = bbox; + let cx = bx + bw * 0.5; + let cy = by + bh * 0.5; + let half = bw.max(bh) * 0.5 * (1.0 + CROP_MARGIN); + if !(half.is_finite() && half > 0.5 && cx.is_finite() && cy.is_finite()) { + return None; + } + + let edge = STORED_CROP_EDGE; + let mut out = vec![0u8; (edge * edge * 4) as usize]; + let step = (half * 2.0) / edge as f32; + for oy in 0..edge { + let sy = cy - half + (oy as f32 + 0.5) * step; + for ox in 0..edge { + let sx = cx - half + (ox as f32 + 0.5) * step; + let o = ((oy * edge + ox) * 4) as usize; + for c in 0..3 { + let v = px.channel(width, height, sx as isize, sy as isize, c); + out[o + c] = (v.clamp(0.0, 1.0) * 255.0).round() as u8; + } + out[o + 3] = 255; + } + } + + match dr_thumbs::encode_rgba(edge, edge, &out) { + Ok(bytes) => Some(bytes), + Err(e) => { + log::debug!("encoding a face crop: {e}"); + None + } + } +} + fn normalise_landmarks(lm: &[(f32, f32); 5], long_edge: f32) -> [(f32, f32); 5] { let mut out = [(0.0_f32, 0.0_f32); 5]; for (o, &(x, y)) in out.iter_mut().zip(lm.iter()) { @@ -1084,6 +1331,69 @@ fn rgba_to_rgb_f32(rgba: &[u8]) -> Vec { mod tests { use super::*; + #[test] + fn the_detector_reduction_averages_rather_than_samples() { + // Four source pixels per destination pixel, with one bright pixel in + // each group. A point sampler returns either 255 or 0 depending on + // which corner it lands on; the average is the same every time, and + // that stability is what stops a small face vanishing on a grid + // alignment it has no control over. + let (w, h) = (4usize, 4usize); + let mut rgba = vec![0u8; w * h * 4]; + for y in 0..h { + for x in 0..w { + let i = (y * w + x) * 4; + let bright = x % 2 == 0 && y % 2 == 0; + for c in 0..3 { + rgba[i + c] = if bright { 255 } else { 0 }; + } + rgba[i + 3] = 255; + } + } + // Force a 2x reduction regardless of DETECT_EDGE by reducing by hand + // through the same helper on a source larger than the target. + let (dw, dh, out) = reduce_to(&rgba, w, h, 2); + assert_eq!((dw, dh), (2, 2)); + for v in out.chunks_exact(3) { + assert!( + (v[0] - 0.25).abs() < 1e-6, + "each destination pixel averages one bright of four, got {}", + v[0] + ); + } + } + + #[test] + fn the_reduction_keeps_the_aspect_ratio_it_was_given() { + let (w, h) = (400usize, 100usize); + let rgba = vec![128u8; w * h * 4]; + let (dw, dh, out) = reduce_to(&rgba, w, h, 100); + assert_eq!(dw, 100); + assert_eq!(dh, 25); + assert_eq!(out.len(), dw * dh * 3); + } + + #[test] + fn landmarks_scale_back_to_the_native_frame() { + // The failure this guards is silent: landmarks left in the detector's + // coordinates put every crop near the top-left corner of the frame, + // which produces faces that look like faces of something else. + let lm = [(10.0, 20.0), (30.0, 40.0), (50.0, 60.0), (70.0, 80.0), (90.0, 100.0)]; + let out = scale_landmarks(&lm, 4.0, 2.0); + assert_eq!(out[0], (40.0, 40.0)); + assert_eq!(out[4], (360.0, 200.0)); + } + + #[test] + fn a_buffer_that_is_not_the_stated_size_indexes_nothing() { + // No model is loaded here, so reaching the detector would panic. The + // point is that it does not: the shape check comes first. + let rgba = vec![0u8; 10]; + assert!(!index_native_shape_ok(&rgba, 100, 100)); + assert!(!index_native_shape_ok(&rgba, 0, 0)); + assert!(index_native_shape_ok(&vec![0u8; 4 * 100 * 100], 100, 100)); + } + #[test] fn an_audit_summary_names_both_kinds_of_outstanding() { let a = IndexAudit { diff --git a/ui/dr-ui/src/identity_ui.rs b/ui/dr-ui/src/identity_ui.rs index e261555..7348c71 100644 --- a/ui/dr-ui/src/identity_ui.rs +++ b/ui/dr-ui/src/identity_ui.rs @@ -537,11 +537,18 @@ pub fn wire( store: S, models: M, paths: P, + // TRACES: FR-CULL-8 + // `None` on a build with no adapter. Indexing needs a native render and a + // native render needs the GPU, so the button reports that the same way it + // reports a missing model rather than starting a pass that cannot produce + // anything. + gpu: Option, ) where S: Fn() -> Option> + 'static, M: Fn() -> Option + 'static, P: Fn() -> Option + 'static, { + let gpu = Rc::new(gpu); let store: Rc Option>> = Rc::new(store); let models: Rc Option> = Rc::new(models); let paths: Rc Option> = Rc::new(paths); @@ -1026,6 +1033,7 @@ pub fn wire( let store = store.clone(); let models = models.clone(); let paths = paths.clone(); + let gpu = gpu.clone(); window.on_identity_index(move || { let Some(w) = weak.upgrade() else { return }; if ctl.sweep.borrow().is_some() { @@ -1038,6 +1046,12 @@ pub fn wire( let Some((conn, catalog_path, store_dir)) = paths() else { return; }; + // Same reporting path as a missing model: both mean the pass + // cannot run, and both are states a fresh install can be in. + let Some(gpu) = gpu.as_ref().clone() else { + w.set_identity_model_missing(true); + return; + }; ctl.progress.set((0, 0)); ctl.faces_found.set(0); @@ -1052,6 +1066,7 @@ pub fn wire( embedder, MODEL_ID.to_string(), dr_face::DetectOptions::default(), + gpu, )); w.set_identity_indexing(true); w.set_identity_indexing_status("looking for images to index…".into()); diff --git a/ui/dr-ui/src/lib.rs b/ui/dr-ui/src/lib.rs index ba2da58..3b94b23 100644 --- a/ui/dr-ui/src/lib.rs +++ b/ui/dr-ui/src/lib.rs @@ -27,6 +27,7 @@ mod develop; mod display_ui; mod export; pub mod faces; +pub use library::render_native; // Generated from the `GESTURE:` comments beside the code that implements each // one — see `tools/traceability`. Regenerate with // `cargo run -p traceability -- gestures`; CI fails if it has drifted. @@ -1208,6 +1209,11 @@ pub fn run(paths: Vec) -> Result<()> { Some((conn, catalog, thumbs)) } }, + // TRACES: FR-CULL-8 + // The same device the develop view draws with. Face indexing + // renders natively through FR-EXP-9's path, so it needs a GPU + // for the same reason export does. + gpu.clone(), ); } diff --git a/ui/dr-ui/src/library.rs b/ui/dr-ui/src/library.rs index 5fdc360..b73b002 100644 --- a/ui/dr-ui/src/library.rs +++ b/ui/dr-ui/src/library.rs @@ -3007,17 +3007,6 @@ fn faces_unindexed( Ok(rows) } -/// One image the face sweep got through, on its way back from a fetch lane. -/// -/// The catalog id, the faces found, the long edge they were normalised -/// against, and the proxy to keep — `None` where the image held no face, since -/// nothing will ever ask to crop one out of it. -type IndexedImage = ( - i64, - Vec, - u32, - Option<(u64, dr_thumbs::Thumbnail)>, -); /// Images whose faces have nothing left to be cut out of. /// @@ -3066,40 +3055,47 @@ fn faces_without_proxy( Ok(rows) } -/// TRACES: FR-CULL-8 | NFR-ARCH-2 -/// Index faces across the **whole** library, fetching what it needs. +/// TRACES: FR-CULL-8 | FR-EXP-9 | NFR-ARCH-2 | NFR-RES-2 +/// Index faces across the **whole** library, at native resolution. /// -/// # Why this replaced a pass that read the thumbnail store +/// # The two resolutions, and why they are not one /// -/// The previous sweep filtered its work list down to images that already had a -/// `ThumbSize::Large` proxy on disk, on the reading that FR-CULL-8 keeps face -/// indexing off the network. It does not: it keeps indexing off the *full -/// decode*, and says plainly that "where no proxy exists, the job requests one -/// at background priority". Nothing filled the large class for a whole library -/// — `SWEEP_THUMB_SIZE` is deliberately `Grid` — so the pass could only ever -/// reach photographs the user had personally zoomed into. Measured on the -/// reference library: 220 images indexed out of 23,529. +/// FR-CULL-8 asks for a native render, a *reduction* for the detector, and the +/// crop taken back out of the native buffer. That is not three sizes for the +/// sake of it. The detector letterboxes whatever it is handed into a fixed +/// 640×640, so above that its input resolution decides nothing and paying for +/// it is waste; `align::warp` produces the fixed 112×112 ArcFace sees, so +/// *its* input resolution decides everything and economising there is a +/// silent loss. The two stages want opposite things, and a single buffer +/// serving both is how this pass previously came to store 47% of the +/// reference library's faces upsampled (faces.md §7b). /// -/// So this fetches, by exactly the two-stage route the thumbnail sweep uses: -/// the header, then the located preview's own byte range (FR-NC-3). No full -/// file is pulled and no RAW is decoded — an embedded preview is a JPEG. +/// # Why this replaced a pass that read an embedded preview /// -/// # Why it does not simply ride the thumbnail sweep +/// The previous version range-fetched the JPEG preview embedded in each RAW +/// (FR-NC-3) and used it for both stages. It was cheap and it was the tier +/// FR-CULL-8 named at the time. On the reference library that preview tops out +/// at 3072 px against a ~6000 px sensor, which is what put those 8,505 faces +/// below the embedder's 112 px with no way to tell from the catalog that +/// anything was wrong. /// -/// It could, and it would be free: that pass already fetches the largest -/// preview, decodes it, and downscales it to 256. Riding it is the right shape -/// for *new* images and is the obvious next step. It cannot serve this -/// operation, though, because the thumbnail sweep's work list is what the store -/// does not have — so every image already thumbnailed, which for an established -/// library is most of them, would never come back past the detector. +/// Before that it filtered its work list to images with a `ThumbSize::Large` +/// proxy already on disk, which nothing filled for a whole library, so it +/// reached 220 images out of 23,529. /// /// # Cost, stated plainly /// -/// One preview fetch per un-indexed image, capped at [`MAX_PREVIEW_BYTES`]. -/// That is the same transfer the thumbnail sweep pays per image, paid a second -/// time because the first one kept only 256 px. Resumable by construction: the -/// work list is what the catalog has no `face_index` row for, so a kill costs -/// the images in flight and nothing else. +/// **One whole original per un-indexed image, and one full render.** On the +/// reference library that is 412 GB and roughly a hundred minutes of decode — +/// a different order of thing from the byte ranges this used to pay, which is +/// why FR-CULL-8 makes a whole-library pass a transfer under FR-NC-6 rather +/// than something that may start on its own. +/// +/// Nothing is kept that was not already wanted: the original is borrowed and +/// given back (ARCH §9.0a), and the only thing written per image is the +/// 1024 px proxy the People screen crops from, and only where a face was +/// found. Resumable by construction — the work list is what the catalog has no +/// `face_index` row for, so a kill costs the images in flight and nothing else. #[allow(clippy::too_many_arguments)] pub fn spawn_face_sweep( conn: Connection, @@ -3109,6 +3105,7 @@ pub fn spawn_face_sweep( embedder_model: PathBuf, model_id: String, options: dr_face::DetectOptions, + gpu: dr_gpu::GpuContext, ) -> Receiver { use crate::faces::FaceSweepMessage; let (tx, rx) = std::sync::mpsc::channel(); @@ -3239,16 +3236,7 @@ pub fn spawn_face_sweep( } }; - // One detector and one embedder for every lane. - // - // The lanes are concurrent futures on a single thread, not threads, - // so they interleave only at await points — and inference contains - // none. A `RefCell` borrow therefore never overlaps another, and - // the alternative, a pair per lane, would be ~16 MB of weights - // duplicated for no parallelism at all. - let models = std::cell::RefCell::new((&mut detector, &mut embedder)); - - let (mut done, mut images, mut found, mut failed) = (0usize, 0usize, 0usize, 0usize); + let (mut images, mut found, mut failed) = (0usize, 0usize, 0usize); let mut offline = false; // TRACES: FR-NC-6c @@ -3258,180 +3246,152 @@ pub fn spawn_face_sweep( // the later one finished. Each gives its own back (ARCH §9.0a). let pool = dr_sync_folder::BorrowPool::new(); - for chunk in wanted.chunks(SWEEP_CHUNK) { - let lanes: Vec> = (0..SWEEP_LANES) - .map(|lane| chunk.iter().skip(lane).step_by(SWEEP_LANES).collect()) - .collect(); - - let results = futures_join_all(lanes.into_iter().map(|lane| { + // TRACES: NFR-RES-2 + // **Fetch wide, render narrow.** The chunk is one original per + // lane and not the 96 the preview sweep used, because the two + // passes hold different things: that one kept an 8 MB preview per + // image, this one keeps whole RAWs. Six at ~23 MB is a working set + // a phone can carry; ninety-six is not. + // + // The render is then sequential, and that is not a limitation to + // be optimised away later. There is one GPU, so concurrent renders + // would queue on it anyway, and each one materialises a native + // frame -- 96 MB for a 24 MP photograph. Overlapping them would + // multiply the one allocation that actually threatens the budget + // while buying no parallelism that exists. + for chunk in wanted.chunks(SWEEP_LANES) { + let fetched = futures_join_all(chunk.iter().map(|req| { let backend = &*backend; - let models = ⊧ - let options = &options; let pool = &pool; async move { - let mut indexed: Vec = Vec::new(); - let mut discard = Vec::new(); - let mut attempted = 0usize; - let mut failed = 0usize; - let mut offline = false; - for req in lane { - attempted += 1; - - let _held = - match pool.borrow(backend, &RemotePath::new(&req.path)).await { - Ok(h) => h, - Err(e) if e.indicates_offline() => { - log::info!("face sweep: {e}"); - attempted -= 1; - offline = true; - break; - } - Err(e) => { - log::debug!("face sweep: {}: {e}", req.path); - failed += 1; - continue; - } - }; - - match fetch_preview(backend, req, &mut discard).await { - PreviewOutcome::Ready(mut preview) => { - // No await inside this borrow — see the - // note where `models` is built. - let found = { - let mut m = models.borrow_mut(); - let (det, emb) = &mut *m; - crate::faces::index_preview(det, emb, &preview, options) - }; - match found { - Ok((faces, edge)) => { - // Keep the proxy only where there - // is a face to cut out of it. Two - // thirds of a personal library is - // landscapes and documents - // (docs/faces.md §7a), and those - // never need a crop — so this fills - // the large class for the images - // the People screen will actually - // ask about and leaves the rest - // alone, rather than paying the - // whole-library cost - // `SWEEP_THUMB_SIZE` avoids. - // - // Downscaled only now: detection - // needed the full buffer, and this - // is the last use of it. - let keep = match (faces.is_empty(), req.file_id) { - (false, Some(file_id)) => { - preview.downscale_to( - dr_thumbs::ThumbSize::Large.edge(), - ); - encode_preview(file_id, &preview) - .map(|t| (file_id, t)) - } - _ => None, - }; - indexed.push((req.image_id, faces, edge, keep)); - } - Err(e) => { - log::debug!("face sweep: {}: {e}", req.path); - failed += 1; - } - } - } - PreviewOutcome::Unavailable(reason) => { - log::debug!("face sweep: {}: {reason}", req.path); - failed += 1; - } - PreviewOutcome::Offline(reason) => { - log::info!("face sweep: server unreachable: {reason}"); - attempted -= 1; - offline = true; - break; - } + let held = match pool.borrow(backend, &RemotePath::new(&req.path)).await { + Ok(h) => h, + Err(e) if e.indicates_offline() => { + log::info!("face sweep: {e}"); + return (req, Err(FetchOutcome::Offline)); } - } - (indexed, attempted, failed, offline) + Err(e) => { + log::debug!("face sweep: {}: {e}", req.path); + return (req, Err(FetchOutcome::Failed)); + } + }; + + // TRACES: FR-CULL-8 + // The whole file, not FR-NC-3's byte range. The + // requirement now asks for a native render and there + // is no native render without the original -- which + // is why the pass is a transfer under FR-NC-6 and says + // so before it starts. + let id = RemoteId::Path(RemotePath::new(&req.path)); + let got = match backend.get(&id, None).await { + Ok(b) => Ok(b), + Err(e) if e.indicates_offline() => { + log::info!("face sweep: server unreachable: {e}"); + Err(FetchOutcome::Offline) + } + Err(e) => { + log::debug!("face sweep: {}: {e}", req.path); + Err(FetchOutcome::Failed) + } + }; + drop(held); + (req, got) } })) .await; - for (indexed, attempted, lane_failed, lane_offline) in results { - done += attempted; - failed += lane_failed; - offline |= lane_offline; - // Before the indexed ones, so a screen watching this sees - // the count move for work that produced nothing. Images - // refused for size are *not* reported here: they are in - // `indexed` below, having earned a marker, and counting - // them twice would run the progress figure past its total. - if lane_failed > 0 - && tx - .send(FaceSweepMessage::Failed { - images: lane_failed, - }) - .is_err() - { - log::info!("face sweep: cancelled after {images} image(s)"); - pool.release_all(&*backend).await; - return; - } - for (image_id, faces, edge, keep) in indexed { - // Before the detections, so a kill between the two - // leaves a proxy with no faces recorded — which the - // next pass simply re-indexes — rather than faces with - // no proxy, which is the state that draws an empty - // grid and cannot repair itself. - if let Some((file_id, thumb)) = keep { - store_thumbnail( - &mut store, - file_id, - dr_thumbs::ThumbSize::Large, - &thumb, - ); + let mut lane_failed = 0usize; + for (req, got) in fetched { + let bytes = match got { + Ok(b) => b, + Err(FetchOutcome::Offline) => { + offline = true; + continue; } - // Written per image, including the ones with no face in - // them: `face_index` records that detection *ran*, and - // zero is its most valuable value — without the row, - // every landscape and document scan returns on the next - // pass, for ever (docs/faces.md §7a). - match dr_catalog::faces::record_detections( - catalog.connection(), - dr_types::ImageId(image_id as u64), - &model_id, - edge, - &faces, - ) { - Ok(_) => { - images += 1; - found += faces.len(); - if tx - .send(FaceSweepMessage::Indexed { - image: dr_types::ImageId(image_id as u64), - faces: faces.len(), - }) - .is_err() - { - // Receiver dropped: the screen closed, or - // the user pressed Stop. Everything written - // so far stays written — and everything - // borrowed is given back. A cancelled pass - // that kept the library hydrated would be - // the worst of both: the disk spent and - // the work abandoned. - log::info!("face sweep: cancelled after {images} image(s)"); - pool.release_all(&*backend).await; - return; + Err(FetchOutcome::Failed) => { + lane_failed += 1; + continue; + } + }; + + match index_one_native( + &gpu, + &mut detector, + &mut embedder, + &bytes, + &options, + ) { + Ok((faces, edge, proxy)) => { + // Before the detections, so a kill between the two + // leaves a proxy with no faces recorded -- which + // the next pass simply re-indexes -- rather than + // faces with no proxy, which is the state that + // draws an empty grid and cannot repair itself. + if let (Some(file_id), Some(thumb)) = (req.file_id, proxy) { + store_thumbnail( + &mut store, + file_id, + dr_thumbs::ThumbSize::Large, + &thumb, + ); + } + match dr_catalog::faces::record_detections( + catalog.connection(), + dr_types::ImageId(req.image_id as u64), + &model_id, + edge, + &faces, + ) { + Ok(_) => { + images += 1; + found += faces.len(); + if tx + .send(FaceSweepMessage::Indexed { + image: dr_types::ImageId(req.image_id as u64), + faces: faces.len(), + }) + .is_err() + { + // Receiver dropped: the screen closed, + // or the user pressed Stop. Everything + // written so far stays written -- and + // everything borrowed is given back. + log::info!( + "face sweep: cancelled after {images} image(s)" + ); + pool.release_all(&*backend).await; + return; + } + } + Err(e) => { + log::warn!( + "face sweep: storing faces for {}: {e}", + req.image_id + ); + lane_failed += 1; } } - Err(e) => { - log::warn!("face sweep: storing faces for {image_id}: {e}"); - failed += 1; - } + } + Err(e) => { + log::debug!("face sweep: {}: {e}", req.path); + lane_failed += 1; } } } - let _ = done; + failed += lane_failed; + if lane_failed > 0 + && tx + .send(FaceSweepMessage::Failed { + images: lane_failed, + }) + .is_err() + { + log::info!("face sweep: cancelled after {images} image(s)"); + pool.release_all(&*backend).await; + return; + } + if offline { break; } @@ -3460,6 +3420,118 @@ pub fn spawn_face_sweep( rx } +/// TRACES: FR-CULL-8 | FR-EXP-9 +/// Open one original for a native render, orientation applied and nothing else. +/// +/// The half of [`index_one_native`] that has nothing to do with faces, exposed +/// because measuring what this pass is worth means rendering the same file two +/// ways and comparing the crops — see `examples/face_native.rs`. A tool that +/// had to reimplement the render would be measuring its own reimplementation. +pub fn render_native( + gpu: &dr_gpu::GpuContext, + bytes: &[u8], +) -> Result { + open_native(gpu, bytes)?.render_for_export(dr_types::ColourSpace::Srgb) +} + +/// The session behind [`render_native`], kept private because `DevelopSession` +/// is. The indexing path needs the session itself rather than just its frame: +/// it renders the People screen's proxy from the same open session rather than +/// opening the file twice. +fn open_native( + gpu: &dr_gpu::GpuContext, + bytes: &[u8], +) -> Result { + let orientation = dr_decode::metadata(bytes) + .ok() + .and_then(|m| m.orientation) + .unwrap_or_default(); + crate::open_session(gpu, bytes, orientation) +} + +/// Why one original did not arrive. +/// +/// Named rather than a bool because the two mean opposite things to the loop: +/// one image failing is one image, and the server going away means nothing +/// after it would have worked either. +enum FetchOutcome { + Failed, + Offline, +} + +/// TRACES: FR-CULL-8 | FR-EXP-9 +/// Render one original at native resolution and index the faces in it. +/// +/// The FR-CULL-8 pipeline end to end, for one photograph: decode, render +/// through the same path export uses, detect on a reduction, crop from the +/// native frame. Returns the faces, the native long edge that went into the +/// run marker, and the 1024px proxy the People screen later cuts thumbnails +/// from. +/// +/// # Why the stored edit is not applied +/// +/// `export::render_from_library` fetches the sidecar and applies it, because +/// an export is of the photograph the user has made. This is not: the face +/// geometry stored in the catalog is normalised to the frame, so applying a +/// crop would record faces against a frame that changes whenever the user +/// changes their mind, and every stored box would silently become wrong. What +/// is applied is the orientation, which is a fact about the file rather than +/// an edit. +fn index_one_native( + gpu: &dr_gpu::GpuContext, + detector: &mut dr_face::Detector, + embedder: &mut dr_face::Embedder, + bytes: &[u8], + options: &dr_face::DetectOptions, +) -> Result<(Vec, u32, Option), String> { + let mut session = open_native(gpu, bytes)?; + let frame = session.render_for_export(dr_types::ColourSpace::Srgb)?; + + let edge = frame.width.max(frame.height); + let faces = crate::faces::index_native( + detector, + embedder, + &frame.rgba, + frame.width as usize, + frame.height as usize, + options, + ) + .map_err(|e| e.to_string())?; + + // Only where there is a face to cut out of it. Two thirds of a personal + // library is landscapes and documents, and those never need a crop -- so + // this fills the large class for the images the People screen will + // actually ask about and leaves the rest alone. + // + // Rendered rather than downscaled from the frame in hand: the session is + // still open and `render_thumbnail` is the path the grid's own thumbnails + // take, so the proxy this writes is the one the store would have had + // anyway. + let proxy = if faces.is_empty() { + None + } else { + match session.render_thumbnail(dr_thumbs::ThumbSize::Large.edge()) { + Ok((w, h, rgba)) => match dr_thumbs::encode_rgba(w, h, &rgba) { + Ok(bytes) => Some(dr_thumbs::Thumbnail { + width: w, + height: h, + bytes, + }), + Err(e) => { + log::debug!("encoding a face proxy: {e}"); + None + } + }, + Err(e) => { + log::debug!("rendering a face proxy: {e}"); + None + } + } + }; + + Ok((faces, edge, proxy)) +} + /// The class the whole-library pass fills. /// /// Grid only, deliberately. The large class is four times the transfer for a