Page the grid along an index instead of sorting the library each time
Scrolling jittered, and this was the largest single reason. Every window the grid loads is `ORDER BY ... LIMIT n OFFSET k`, and neither half of that was being answered the cheap way. **The sort.** `GRID_ORDER` leads with `captured_at IS NULL`, so undated frames fall to the end. No ordinary index answers that — the leading term is an expression, not a column — so SQLite sorted the whole library into a temp b-tree on every window read, then threw away the first `k` rows of it. Schema V7 indexes the expression exactly as the query writes it, partial on the same `shadowed_by IS NULL AND trashed_at IS NULL` the grid filters by, so the read becomes a walk along the index. **The join.** `LEFT JOIN remote` was paged *after* it was joined, so reading 280 cells at offset 20,000 first seeked into `remote` for all 24,000 rows and then discarded 23,720 of them. The file ids are now fetched for the 280 rows that survived — the shape the badge and rating reads already use, one query for the window rather than one per cell. Measured together on 24,000 images at offset 20,000: **15.2 ms → 0.36 ms**, inside a scroll handler that has 16.7 ms to draw a frame. The test asserts on the query plan rather than on a duration, because there is no other symptom. A `GRID_ORDER` edited out of step with the index, or a column added back that drags `remote` in again, both still return exactly the right cells — just after sorting the library — and the jitter would come back with nothing to point at. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -15,7 +15,7 @@ use rusqlite::Connection;
|
||||
use crate::error::CatalogError;
|
||||
|
||||
/// Schema version this build writes and understands.
|
||||
pub const SCHEMA_VERSION: i64 = 6;
|
||||
pub const SCHEMA_VERSION: i64 = 7;
|
||||
|
||||
/// Apply migrations up to [`SCHEMA_VERSION`].
|
||||
///
|
||||
@@ -72,6 +72,12 @@ pub fn migrate(conn: &Connection) -> Result<i64, CatalogError> {
|
||||
tx.pragma_update(None, "user_version", 6)?;
|
||||
tx.commit()?;
|
||||
}
|
||||
if from < 7 {
|
||||
let tx = conn.unchecked_transaction()?;
|
||||
tx.execute_batch(V7)?;
|
||||
tx.pragma_update(None, "user_version", 7)?;
|
||||
tx.commit()?;
|
||||
}
|
||||
|
||||
Ok(from)
|
||||
}
|
||||
@@ -173,6 +179,13 @@ pub fn v1_for_attached(schema_name: &str) -> String {
|
||||
/// `ALTER TABLE ... ADD COLUMN`, and the columns they add are local index
|
||||
/// state — shadowing, trashing, cache pinning — that a merge never reads
|
||||
/// across the attachment.
|
||||
///
|
||||
/// V7 creates an object and is still excluded, which is the one exception to
|
||||
/// that rule and not an oversight: it indexes `shadowed_by` and `trashed_at`,
|
||||
/// the very columns V2 through V5 add and this function leaves out, so
|
||||
/// creating it over there would fail on columns that are not there. Nothing is
|
||||
/// lost by its absence — it exists to make the *grid* page quickly, and the
|
||||
/// grid never reads across an attachment.
|
||||
pub fn for_attached(schema_name: &str) -> String {
|
||||
format!(
|
||||
"{}\n{}",
|
||||
@@ -276,6 +289,50 @@ fn stem_of(path: &str) -> &str {
|
||||
}
|
||||
}
|
||||
|
||||
/// TRACES: FR-CAT-4 | NFR-P5
|
||||
/// The order the grid reads in, as an index.
|
||||
///
|
||||
/// # What this is for
|
||||
///
|
||||
/// Every window the grid loads is `ORDER BY ... LIMIT n OFFSET k`, and without
|
||||
/// an index that matches the ordering SQLite answers it by sorting the whole
|
||||
/// library into a temp b-tree and then discarding the first `k` rows. Measured
|
||||
/// on 24,000 images at offset 20,000, one window read cost 15 ms — a frame
|
||||
/// budget of 16.7 ms, spent inside the scroll handler, several times per
|
||||
/// screenful. That is the jitter.
|
||||
///
|
||||
/// With this index the same read is a walk along it: 0.36 ms.
|
||||
///
|
||||
/// # Why the shape is what it is
|
||||
///
|
||||
/// `captured_at IS NULL` leads, because [`crate::library`]'s `GRID_ORDER` does
|
||||
/// — undated images sort last, and an ordinary index on `captured_at` cannot
|
||||
/// answer that, since the expression is not a column. SQLite indexes
|
||||
/// expressions, so it is spelled out here exactly as the query spells it; the
|
||||
/// two must stay identical or the planner silently falls back to sorting and
|
||||
/// the cost comes back with no other symptom.
|
||||
///
|
||||
/// `source_ref` is included because it breaks ties in the same ordering, and an
|
||||
/// index that stopped at `captured_at` would leave a sort for the ties.
|
||||
///
|
||||
/// # Partial, on the same predicate the grid filters by
|
||||
///
|
||||
/// The grid never lists shadowed or trashed rows, so an index carrying them
|
||||
/// would be larger than the question ever asks about, and — more to the point —
|
||||
/// a partial index is only usable when its `WHERE` is implied by the query's,
|
||||
/// which is what makes this one apply to the grid's reads and to nothing else.
|
||||
///
|
||||
/// # It does not cover the rating filter or a collection scope
|
||||
///
|
||||
/// Both narrow the walk rather than reorder it, so the index still supplies the
|
||||
/// ordering and SQLite tests the extra predicate per row. That is the cheap
|
||||
/// direction: the expensive part was never the filtering, it was the sort.
|
||||
const V7: &str = r#"
|
||||
CREATE INDEX images_grid_order
|
||||
ON images(captured_at IS NULL, captured_at, source_ref)
|
||||
WHERE shadowed_by IS NULL AND trashed_at IS NULL;
|
||||
"#;
|
||||
|
||||
const V6: &str = r#"
|
||||
-- TRACES: FR-CAT-5 | FR-CAT-6 | FR-NC-9
|
||||
-- Keywords gain an identity, so that renaming and deleting one can cross
|
||||
|
||||
+28
-28
@@ -10,17 +10,17 @@ Denominators are parsed from [`requirements.md`](requirements.md) at run time, n
|
||||
| Metric | Value |
|
||||
|---|---|
|
||||
| Source files scanned | 180 |
|
||||
| TRACES tags found | 562 |
|
||||
| TRACES tags found | 567 |
|
||||
| Requirements defined | 177 |
|
||||
| Requirements covered | 89 |
|
||||
| **Coverage** | **50.3%** (89/177) |
|
||||
| Requirements covered | 90 |
|
||||
| **Coverage** | **50.8%** (90/177) |
|
||||
|
||||
### By type
|
||||
|
||||
| Type | Covered | Defined |
|
||||
|---|---|---|
|
||||
| FR | 71 | 122 |
|
||||
| NFR | 16 | 49 |
|
||||
| NFR | 17 | 49 |
|
||||
| R | 2 | 6 |
|
||||
|
||||
## Orphan tags
|
||||
@@ -38,16 +38,16 @@ _None._
|
||||
| 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:949`](../ui/dr-ui/src/lib.rs#L949), [`ui/dr-ui/src/library.rs:152`](../ui/dr-ui/src/library.rs#L152), [`ui/dr-ui/src/library.rs:2105`](../ui/dr-ui/src/library.rs#L2105), [`ui/dr-ui/ui/import.slint:5`](../ui/dr-ui/ui/import.slint#L5) |
|
||||
| FR-CAT-12 | [`core/dr-pipeline/src/sidecar.rs:109`](../core/dr-pipeline/src/sidecar.rs#L109) |
|
||||
| FR-CAT-13 | [`core/dr-catalog/src/keywords.rs:1`](../core/dr-catalog/src/keywords.rs#L1) |
|
||||
| FR-CAT-15 | [`core/dr-catalog/src/schema.rs:365`](../core/dr-catalog/src/schema.rs#L365), [`core/dr-catalog/src/trash.rs:1`](../core/dr-catalog/src/trash.rs#L1), [`core/dr-sync-nextcloud/src/lib.rs:447`](../core/dr-sync-nextcloud/src/lib.rs#L447), [`core/dr-sync/src/lib.rs:124`](../core/dr-sync/src/lib.rs#L124), [`core/dr-sync/src/scan.rs:426`](../core/dr-sync/src/scan.rs#L426), [`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:1225`](../ui/dr-ui/src/collections_ui.rs#L1225), [`ui/dr-ui/src/collections_ui.rs:1995`](../ui/dr-ui/src/collections_ui.rs#L1995), [`ui/dr-ui/src/library.rs:152`](../ui/dr-ui/src/library.rs#L152), [`ui/dr-ui/src/library.rs:169`](../ui/dr-ui/src/library.rs#L169), [`ui/dr-ui/src/library.rs:198`](../ui/dr-ui/src/library.rs#L198), [`ui/dr-ui/src/library.rs:2986`](../ui/dr-ui/src/library.rs#L2986), [`ui/dr-ui/src/library.rs:3018`](../ui/dr-ui/src/library.rs#L3018), [`ui/dr-ui/src/library_ui.rs:151`](../ui/dr-ui/src/library_ui.rs#L151), [`ui/dr-ui/src/library_ui.rs:722`](../ui/dr-ui/src/library_ui.rs#L722), [`ui/dr-ui/src/trash.rs:1`](../ui/dr-ui/src/trash.rs#L1), [`ui/dr-ui/ui/collections.slint:572`](../ui/dr-ui/ui/collections.slint#L572) |
|
||||
| FR-CAT-15 | [`core/dr-catalog/src/schema.rs:422`](../core/dr-catalog/src/schema.rs#L422), [`core/dr-catalog/src/trash.rs:1`](../core/dr-catalog/src/trash.rs#L1), [`core/dr-sync-nextcloud/src/lib.rs:447`](../core/dr-sync-nextcloud/src/lib.rs#L447), [`core/dr-sync/src/lib.rs:124`](../core/dr-sync/src/lib.rs#L124), [`core/dr-sync/src/scan.rs:426`](../core/dr-sync/src/scan.rs#L426), [`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:1225`](../ui/dr-ui/src/collections_ui.rs#L1225), [`ui/dr-ui/src/collections_ui.rs:1995`](../ui/dr-ui/src/collections_ui.rs#L1995), [`ui/dr-ui/src/library.rs:152`](../ui/dr-ui/src/library.rs#L152), [`ui/dr-ui/src/library.rs:169`](../ui/dr-ui/src/library.rs#L169), [`ui/dr-ui/src/library.rs:198`](../ui/dr-ui/src/library.rs#L198), [`ui/dr-ui/src/library.rs:2990`](../ui/dr-ui/src/library.rs#L2990), [`ui/dr-ui/src/library.rs:3024`](../ui/dr-ui/src/library.rs#L3024), [`ui/dr-ui/src/library_ui.rs:184`](../ui/dr-ui/src/library_ui.rs#L184), [`ui/dr-ui/src/library_ui.rs:757`](../ui/dr-ui/src/library_ui.rs#L757), [`ui/dr-ui/src/trash.rs:1`](../ui/dr-ui/src/trash.rs#L1), [`ui/dr-ui/ui/collections.slint:572`](../ui/dr-ui/ui/collections.slint#L572) |
|
||||
| FR-CAT-1a | [`core/dr-catalog/src/walk.rs:1`](../core/dr-catalog/src/walk.rs#L1), [`core/dr-types/src/lib.rs:53`](../core/dr-types/src/lib.rs#L53) |
|
||||
| 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), [`tools/traceability/src/lib.rs:479`](../tools/traceability/src/lib.rs#L479) |
|
||||
| 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:464`](../ui/dr-ui/src/import.rs#L464), [`ui/dr-ui/src/import.rs:489`](../ui/dr-ui/src/import.rs#L489), [`ui/dr-ui/src/library.rs:2581`](../ui/dr-ui/src/library.rs#L2581), [`ui/dr-ui/src/library.rs:2607`](../ui/dr-ui/src/library.rs#L2607), [`ui/dr-ui/src/library_ui.rs:138`](../ui/dr-ui/src/library_ui.rs#L138), [`ui/dr-ui/src/library_ui.rs:3539`](../ui/dr-ui/src/library_ui.rs#L3539), [`ui/dr-ui/src/library_ui.rs:4470`](../ui/dr-ui/src/library_ui.rs#L4470), [`ui/dr-ui/ui/app.slint:513`](../ui/dr-ui/ui/app.slint#L513), [`ui/dr-ui/ui/settings.slint:340`](../ui/dr-ui/ui/settings.slint#L340), [`ui/dr-ui/ui/settings.slint:72`](../ui/dr-ui/ui/settings.slint#L72) |
|
||||
| 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), [`ui/dr-ui/src/library.rs:179`](../ui/dr-ui/src/library.rs#L179), [`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) |
|
||||
| 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:280`](../core/dr-catalog/src/schema.rs#L280), [`core/dr-catalog/src/schema.rs:783`](../core/dr-catalog/src/schema.rs#L783), [`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:126`](../core/dr-pipeline/src/sidecar.rs#L126), [`ui/dr-ui/src/collections_ui.rs:1578`](../ui/dr-ui/src/collections_ui.rs#L1578), [`ui/dr-ui/src/collections_ui.rs:1597`](../ui/dr-ui/src/collections_ui.rs#L1597), [`ui/dr-ui/src/collections_ui.rs:243`](../ui/dr-ui/src/collections_ui.rs#L243), [`ui/dr-ui/src/collections_ui.rs:340`](../ui/dr-ui/src/collections_ui.rs#L340), [`ui/dr-ui/src/collections_ui.rs:89`](../ui/dr-ui/src/collections_ui.rs#L89), [`ui/dr-ui/src/library.rs:3032`](../ui/dr-ui/src/library.rs#L3032), [`ui/dr-ui/src/library_ui.rs:593`](../ui/dr-ui/src/library_ui.rs#L593), [`ui/dr-ui/src/library_ui.rs:6151`](../ui/dr-ui/src/library_ui.rs#L6151), [`ui/dr-ui/src/library_ui.rs:6162`](../ui/dr-ui/src/library_ui.rs#L6162), [`ui/dr-ui/src/library_ui.rs:6175`](../ui/dr-ui/src/library_ui.rs#L6175), [`ui/dr-ui/src/library_ui.rs:6190`](../ui/dr-ui/src/library_ui.rs#L6190), [`ui/dr-ui/src/library_ui.rs:6199`](../ui/dr-ui/src/library_ui.rs#L6199), [`ui/dr-ui/ui/app.slint:458`](../ui/dr-ui/ui/app.slint#L458), [`ui/dr-ui/ui/app.slint:587`](../ui/dr-ui/ui/app.slint#L587), [`ui/dr-ui/ui/library.slint:1209`](../ui/dr-ui/ui/library.slint#L1209), [`ui/dr-ui/ui/library.slint:1212`](../ui/dr-ui/ui/library.slint#L1212), [`ui/dr-ui/ui/library.slint:18`](../ui/dr-ui/ui/library.slint#L18), [`ui/dr-ui/ui/library.slint:850`](../ui/dr-ui/ui/library.slint#L850), [`ui/dr-ui/ui/library.slint:901`](../ui/dr-ui/ui/library.slint#L901) |
|
||||
| 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:280`](../core/dr-catalog/src/schema.rs#L280), [`core/dr-types/src/selector.rs:1`](../core/dr-types/src/selector.rs#L1), [`core/dr-types/src/settings.rs:63`](../core/dr-types/src/settings.rs#L63), [`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:203`](../ui/dr-ui/src/library.rs#L203), [`ui/dr-ui/src/library.rs:3209`](../ui/dr-ui/src/library.rs#L3209), [`ui/dr-ui/src/library_ui.rs:282`](../ui/dr-ui/src/library_ui.rs#L282), [`ui/dr-ui/src/library_ui.rs:352`](../ui/dr-ui/src/library_ui.rs#L352), [`ui/dr-ui/src/library_ui.rs:5020`](../ui/dr-ui/src/library_ui.rs#L5020), [`ui/dr-ui/src/library_ui.rs:5073`](../ui/dr-ui/src/library_ui.rs#L5073), [`ui/dr-ui/src/library_ui.rs:6291`](../ui/dr-ui/src/library_ui.rs#L6291), [`ui/dr-ui/ui/app.slint:461`](../ui/dr-ui/ui/app.slint#L461), [`ui/dr-ui/ui/app.slint:587`](../ui/dr-ui/ui/app.slint#L587), [`ui/dr-ui/ui/app.slint:816`](../ui/dr-ui/ui/app.slint#L816), [`ui/dr-ui/ui/library.slint:109`](../ui/dr-ui/ui/library.slint#L109), [`ui/dr-ui/ui/library.slint:1278`](../ui/dr-ui/ui/library.slint#L1278), [`ui/dr-ui/ui/library.slint:901`](../ui/dr-ui/ui/library.slint#L901), [`ui/dr-ui/ui/settings.slint:115`](../ui/dr-ui/ui/settings.slint#L115) |
|
||||
| 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:1693`](../ui/dr-ui/src/collections_ui.rs#L1693), [`ui/dr-ui/src/collections_ui.rs:1`](../ui/dr-ui/src/collections_ui.rs#L1), [`ui/dr-ui/src/derived_sync.rs:1`](../ui/dr-ui/src/derived_sync.rs#L1), [`ui/dr-ui/src/library_ui.rs:3397`](../ui/dr-ui/src/library_ui.rs#L3397), [`ui/dr-ui/src/library_ui.rs:4060`](../ui/dr-ui/src/library_ui.rs#L4060), [`ui/dr-ui/ui/app.slint:582`](../ui/dr-ui/ui/app.slint#L582), [`ui/dr-ui/ui/collections.slint:4`](../ui/dr-ui/ui/collections.slint#L4), [`ui/dr-ui/ui/library.slint:2516`](../ui/dr-ui/ui/library.slint#L2516), [`ui/dr-ui/ui/library.slint:872`](../ui/dr-ui/ui/library.slint#L872), [`ui/dr-ui/ui/library.slint:891`](../ui/dr-ui/ui/library.slint#L891) |
|
||||
| FR-CAT-8 | [`core/dr-pipeline/src/ops/curve.rs:136`](../core/dr-pipeline/src/ops/curve.rs#L136), [`core/dr-pipeline/src/ops/curve.rs:652`](../core/dr-pipeline/src/ops/curve.rs#L652), [`core/dr-pipeline/src/sidecar.rs:1273`](../core/dr-pipeline/src/sidecar.rs#L1273), [`core/dr-pipeline/src/sidecar.rs:90`](../core/dr-pipeline/src/sidecar.rs#L90), [`core/dr-pipeline/tests/tone_curve.rs:33`](../core/dr-pipeline/tests/tone_curve.rs#L33), [`ui/dr-ui/src/develop.rs:2600`](../ui/dr-ui/src/develop.rs#L2600), [`ui/dr-ui/src/develop.rs:2628`](../ui/dr-ui/src/develop.rs#L2628), [`ui/dr-ui/src/export.rs:749`](../ui/dr-ui/src/export.rs#L749), [`ui/dr-ui/src/lib.rs:1186`](../ui/dr-ui/src/lib.rs#L1186), [`ui/dr-ui/src/lib.rs:1526`](../ui/dr-ui/src/lib.rs#L1526), [`ui/dr-ui/src/lib.rs:1631`](../ui/dr-ui/src/lib.rs#L1631), [`ui/dr-ui/src/lib.rs:464`](../ui/dr-ui/src/lib.rs#L464), [`ui/dr-ui/src/lib.rs:796`](../ui/dr-ui/src/lib.rs#L796), [`ui/dr-ui/src/library.rs:1528`](../ui/dr-ui/src/library.rs#L1528), [`ui/dr-ui/src/library.rs:364`](../ui/dr-ui/src/library.rs#L364), [`ui/dr-ui/src/library.rs:402`](../ui/dr-ui/src/library.rs#L402), [`ui/dr-ui/src/library.rs:439`](../ui/dr-ui/src/library.rs#L439), [`ui/dr-ui/src/library.rs:679`](../ui/dr-ui/src/library.rs#L679), [`ui/dr-ui/src/library_ui.rs:4762`](../ui/dr-ui/src/library_ui.rs#L4762), [`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:337`](../core/dr-catalog/src/schema.rs#L337), [`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:704`](../core/dr-catalog/src/walk.rs#L704), [`core/dr-sync-nextcloud/src/desktop_client.rs:30`](../core/dr-sync-nextcloud/src/desktop_client.rs#L30), [`core/dr-sync/src/reachability.rs:1`](../core/dr-sync/src/reachability.rs#L1), [`core/dr-types/src/lib.rs:119`](../core/dr-types/src/lib.rs#L119), [`ui/dr-ui/src/develop.rs:2325`](../ui/dr-ui/src/develop.rs#L2325), [`ui/dr-ui/src/library.rs:138`](../ui/dr-ui/src/library.rs#L138), [`ui/dr-ui/src/library.rs:1488`](../ui/dr-ui/src/library.rs#L1488), [`ui/dr-ui/src/library.rs:1565`](../ui/dr-ui/src/library.rs#L1565), [`ui/dr-ui/src/library.rs:220`](../ui/dr-ui/src/library.rs#L220), [`ui/dr-ui/src/library.rs:3316`](../ui/dr-ui/src/library.rs#L3316), [`ui/dr-ui/src/library.rs:439`](../ui/dr-ui/src/library.rs#L439), [`ui/dr-ui/src/library.rs:663`](../ui/dr-ui/src/library.rs#L663), [`ui/dr-ui/src/library.rs:679`](../ui/dr-ui/src/library.rs#L679), [`ui/dr-ui/src/library.rs:733`](../ui/dr-ui/src/library.rs#L733), [`ui/dr-ui/src/library_ui.rs:1529`](../ui/dr-ui/src/library_ui.rs#L1529), [`ui/dr-ui/src/library_ui.rs:1555`](../ui/dr-ui/src/library_ui.rs#L1555), [`ui/dr-ui/src/library_ui.rs:1571`](../ui/dr-ui/src/library_ui.rs#L1571), [`ui/dr-ui/src/library_ui.rs:1665`](../ui/dr-ui/src/library_ui.rs#L1665), [`ui/dr-ui/src/library_ui.rs:193`](../ui/dr-ui/src/library_ui.rs#L193), [`ui/dr-ui/src/library_ui.rs:2252`](../ui/dr-ui/src/library_ui.rs#L2252), [`ui/dr-ui/src/library_ui.rs:226`](../ui/dr-ui/src/library_ui.rs#L226), [`ui/dr-ui/src/library_ui.rs:2685`](../ui/dr-ui/src/library_ui.rs#L2685), [`ui/dr-ui/src/library_ui.rs:2907`](../ui/dr-ui/src/library_ui.rs#L2907), [`ui/dr-ui/src/library_ui.rs:3188`](../ui/dr-ui/src/library_ui.rs#L3188), [`ui/dr-ui/src/library_ui.rs:3260`](../ui/dr-ui/src/library_ui.rs#L3260), [`ui/dr-ui/src/library_ui.rs:3448`](../ui/dr-ui/src/library_ui.rs#L3448), [`ui/dr-ui/src/library_ui.rs:3566`](../ui/dr-ui/src/library_ui.rs#L3566), [`ui/dr-ui/src/library_ui.rs:405`](../ui/dr-ui/src/library_ui.rs#L405), [`ui/dr-ui/src/library_ui.rs:463`](../ui/dr-ui/src/library_ui.rs#L463), [`ui/dr-ui/src/library_ui.rs:5118`](../ui/dr-ui/src/library_ui.rs#L5118), [`ui/dr-ui/src/library_ui.rs:5233`](../ui/dr-ui/src/library_ui.rs#L5233), [`ui/dr-ui/src/presets.rs:309`](../ui/dr-ui/src/presets.rs#L309), [`ui/dr-ui/src/presets.rs:321`](../ui/dr-ui/src/presets.rs#L321), [`ui/dr-ui/src/sidecar_cache.rs:1`](../ui/dr-ui/src/sidecar_cache.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:464`](../ui/dr-ui/src/import.rs#L464), [`ui/dr-ui/src/import.rs:489`](../ui/dr-ui/src/import.rs#L489), [`ui/dr-ui/src/library.rs:2581`](../ui/dr-ui/src/library.rs#L2581), [`ui/dr-ui/src/library.rs:2607`](../ui/dr-ui/src/library.rs#L2607), [`ui/dr-ui/src/library_ui.rs:171`](../ui/dr-ui/src/library_ui.rs#L171), [`ui/dr-ui/src/library_ui.rs:3603`](../ui/dr-ui/src/library_ui.rs#L3603), [`ui/dr-ui/src/library_ui.rs:4569`](../ui/dr-ui/src/library_ui.rs#L4569), [`ui/dr-ui/ui/app.slint:513`](../ui/dr-ui/ui/app.slint#L513), [`ui/dr-ui/ui/settings.slint:340`](../ui/dr-ui/ui/settings.slint#L340), [`ui/dr-ui/ui/settings.slint:72`](../ui/dr-ui/ui/settings.slint#L72) |
|
||||
| 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:292`](../core/dr-catalog/src/schema.rs#L292), [`ui/dr-ui/src/library.rs:179`](../ui/dr-ui/src/library.rs#L179), [`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) |
|
||||
| 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:337`](../core/dr-catalog/src/schema.rs#L337), [`core/dr-catalog/src/schema.rs:840`](../core/dr-catalog/src/schema.rs#L840), [`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:126`](../core/dr-pipeline/src/sidecar.rs#L126), [`ui/dr-ui/src/collections_ui.rs:1578`](../ui/dr-ui/src/collections_ui.rs#L1578), [`ui/dr-ui/src/collections_ui.rs:1597`](../ui/dr-ui/src/collections_ui.rs#L1597), [`ui/dr-ui/src/collections_ui.rs:243`](../ui/dr-ui/src/collections_ui.rs#L243), [`ui/dr-ui/src/collections_ui.rs:340`](../ui/dr-ui/src/collections_ui.rs#L340), [`ui/dr-ui/src/collections_ui.rs:89`](../ui/dr-ui/src/collections_ui.rs#L89), [`ui/dr-ui/src/library.rs:3038`](../ui/dr-ui/src/library.rs#L3038), [`ui/dr-ui/src/library_ui.rs:6250`](../ui/dr-ui/src/library_ui.rs#L6250), [`ui/dr-ui/src/library_ui.rs:6261`](../ui/dr-ui/src/library_ui.rs#L6261), [`ui/dr-ui/src/library_ui.rs:6274`](../ui/dr-ui/src/library_ui.rs#L6274), [`ui/dr-ui/src/library_ui.rs:6289`](../ui/dr-ui/src/library_ui.rs#L6289), [`ui/dr-ui/src/library_ui.rs:628`](../ui/dr-ui/src/library_ui.rs#L628), [`ui/dr-ui/src/library_ui.rs:6298`](../ui/dr-ui/src/library_ui.rs#L6298), [`ui/dr-ui/ui/app.slint:458`](../ui/dr-ui/ui/app.slint#L458), [`ui/dr-ui/ui/app.slint:587`](../ui/dr-ui/ui/app.slint#L587), [`ui/dr-ui/ui/library.slint:1209`](../ui/dr-ui/ui/library.slint#L1209), [`ui/dr-ui/ui/library.slint:1212`](../ui/dr-ui/ui/library.slint#L1212), [`ui/dr-ui/ui/library.slint:18`](../ui/dr-ui/ui/library.slint#L18), [`ui/dr-ui/ui/library.slint:850`](../ui/dr-ui/ui/library.slint#L850), [`ui/dr-ui/ui/library.slint:901`](../ui/dr-ui/ui/library.slint#L901) |
|
||||
| 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:337`](../core/dr-catalog/src/schema.rs#L337), [`core/dr-types/src/selector.rs:1`](../core/dr-types/src/selector.rs#L1), [`core/dr-types/src/settings.rs:63`](../core/dr-types/src/settings.rs#L63), [`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:203`](../ui/dr-ui/src/library.rs#L203), [`ui/dr-ui/src/library.rs:3285`](../ui/dr-ui/src/library.rs#L3285), [`ui/dr-ui/src/library_ui.rs:315`](../ui/dr-ui/src/library_ui.rs#L315), [`ui/dr-ui/src/library_ui.rs:387`](../ui/dr-ui/src/library_ui.rs#L387), [`ui/dr-ui/src/library_ui.rs:5119`](../ui/dr-ui/src/library_ui.rs#L5119), [`ui/dr-ui/src/library_ui.rs:5172`](../ui/dr-ui/src/library_ui.rs#L5172), [`ui/dr-ui/src/library_ui.rs:6390`](../ui/dr-ui/src/library_ui.rs#L6390), [`ui/dr-ui/ui/app.slint:461`](../ui/dr-ui/ui/app.slint#L461), [`ui/dr-ui/ui/app.slint:587`](../ui/dr-ui/ui/app.slint#L587), [`ui/dr-ui/ui/app.slint:816`](../ui/dr-ui/ui/app.slint#L816), [`ui/dr-ui/ui/library.slint:109`](../ui/dr-ui/ui/library.slint#L109), [`ui/dr-ui/ui/library.slint:1278`](../ui/dr-ui/ui/library.slint#L1278), [`ui/dr-ui/ui/library.slint:901`](../ui/dr-ui/ui/library.slint#L901), [`ui/dr-ui/ui/settings.slint:115`](../ui/dr-ui/ui/settings.slint#L115) |
|
||||
| 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:1693`](../ui/dr-ui/src/collections_ui.rs#L1693), [`ui/dr-ui/src/collections_ui.rs:1`](../ui/dr-ui/src/collections_ui.rs#L1), [`ui/dr-ui/src/derived_sync.rs:1`](../ui/dr-ui/src/derived_sync.rs#L1), [`ui/dr-ui/src/library_ui.rs:3461`](../ui/dr-ui/src/library_ui.rs#L3461), [`ui/dr-ui/src/library_ui.rs:4159`](../ui/dr-ui/src/library_ui.rs#L4159), [`ui/dr-ui/ui/app.slint:582`](../ui/dr-ui/ui/app.slint#L582), [`ui/dr-ui/ui/collections.slint:4`](../ui/dr-ui/ui/collections.slint#L4), [`ui/dr-ui/ui/library.slint:2516`](../ui/dr-ui/ui/library.slint#L2516), [`ui/dr-ui/ui/library.slint:872`](../ui/dr-ui/ui/library.slint#L872), [`ui/dr-ui/ui/library.slint:891`](../ui/dr-ui/ui/library.slint#L891) |
|
||||
| FR-CAT-8 | [`core/dr-pipeline/src/ops/curve.rs:136`](../core/dr-pipeline/src/ops/curve.rs#L136), [`core/dr-pipeline/src/ops/curve.rs:652`](../core/dr-pipeline/src/ops/curve.rs#L652), [`core/dr-pipeline/src/sidecar.rs:1273`](../core/dr-pipeline/src/sidecar.rs#L1273), [`core/dr-pipeline/src/sidecar.rs:90`](../core/dr-pipeline/src/sidecar.rs#L90), [`core/dr-pipeline/tests/tone_curve.rs:33`](../core/dr-pipeline/tests/tone_curve.rs#L33), [`ui/dr-ui/src/develop.rs:2600`](../ui/dr-ui/src/develop.rs#L2600), [`ui/dr-ui/src/develop.rs:2628`](../ui/dr-ui/src/develop.rs#L2628), [`ui/dr-ui/src/export.rs:749`](../ui/dr-ui/src/export.rs#L749), [`ui/dr-ui/src/lib.rs:1186`](../ui/dr-ui/src/lib.rs#L1186), [`ui/dr-ui/src/lib.rs:1526`](../ui/dr-ui/src/lib.rs#L1526), [`ui/dr-ui/src/lib.rs:1631`](../ui/dr-ui/src/lib.rs#L1631), [`ui/dr-ui/src/lib.rs:464`](../ui/dr-ui/src/lib.rs#L464), [`ui/dr-ui/src/lib.rs:796`](../ui/dr-ui/src/lib.rs#L796), [`ui/dr-ui/src/library.rs:1528`](../ui/dr-ui/src/library.rs#L1528), [`ui/dr-ui/src/library.rs:364`](../ui/dr-ui/src/library.rs#L364), [`ui/dr-ui/src/library.rs:402`](../ui/dr-ui/src/library.rs#L402), [`ui/dr-ui/src/library.rs:439`](../ui/dr-ui/src/library.rs#L439), [`ui/dr-ui/src/library.rs:679`](../ui/dr-ui/src/library.rs#L679), [`ui/dr-ui/src/library_ui.rs:4861`](../ui/dr-ui/src/library_ui.rs#L4861), [`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:394`](../core/dr-catalog/src/schema.rs#L394), [`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:704`](../core/dr-catalog/src/walk.rs#L704), [`core/dr-sync-nextcloud/src/desktop_client.rs:30`](../core/dr-sync-nextcloud/src/desktop_client.rs#L30), [`core/dr-sync/src/reachability.rs:1`](../core/dr-sync/src/reachability.rs#L1), [`core/dr-types/src/lib.rs:119`](../core/dr-types/src/lib.rs#L119), [`ui/dr-ui/src/develop.rs:2325`](../ui/dr-ui/src/develop.rs#L2325), [`ui/dr-ui/src/library.rs:138`](../ui/dr-ui/src/library.rs#L138), [`ui/dr-ui/src/library.rs:1488`](../ui/dr-ui/src/library.rs#L1488), [`ui/dr-ui/src/library.rs:1565`](../ui/dr-ui/src/library.rs#L1565), [`ui/dr-ui/src/library.rs:220`](../ui/dr-ui/src/library.rs#L220), [`ui/dr-ui/src/library.rs:3392`](../ui/dr-ui/src/library.rs#L3392), [`ui/dr-ui/src/library.rs:439`](../ui/dr-ui/src/library.rs#L439), [`ui/dr-ui/src/library.rs:663`](../ui/dr-ui/src/library.rs#L663), [`ui/dr-ui/src/library.rs:679`](../ui/dr-ui/src/library.rs#L679), [`ui/dr-ui/src/library.rs:733`](../ui/dr-ui/src/library.rs#L733), [`ui/dr-ui/src/library_ui.rs:1564`](../ui/dr-ui/src/library_ui.rs#L1564), [`ui/dr-ui/src/library_ui.rs:1590`](../ui/dr-ui/src/library_ui.rs#L1590), [`ui/dr-ui/src/library_ui.rs:1606`](../ui/dr-ui/src/library_ui.rs#L1606), [`ui/dr-ui/src/library_ui.rs:1700`](../ui/dr-ui/src/library_ui.rs#L1700), [`ui/dr-ui/src/library_ui.rs:226`](../ui/dr-ui/src/library_ui.rs#L226), [`ui/dr-ui/src/library_ui.rs:2316`](../ui/dr-ui/src/library_ui.rs#L2316), [`ui/dr-ui/src/library_ui.rs:259`](../ui/dr-ui/src/library_ui.rs#L259), [`ui/dr-ui/src/library_ui.rs:2749`](../ui/dr-ui/src/library_ui.rs#L2749), [`ui/dr-ui/src/library_ui.rs:2971`](../ui/dr-ui/src/library_ui.rs#L2971), [`ui/dr-ui/src/library_ui.rs:3252`](../ui/dr-ui/src/library_ui.rs#L3252), [`ui/dr-ui/src/library_ui.rs:3324`](../ui/dr-ui/src/library_ui.rs#L3324), [`ui/dr-ui/src/library_ui.rs:3512`](../ui/dr-ui/src/library_ui.rs#L3512), [`ui/dr-ui/src/library_ui.rs:3630`](../ui/dr-ui/src/library_ui.rs#L3630), [`ui/dr-ui/src/library_ui.rs:440`](../ui/dr-ui/src/library_ui.rs#L440), [`ui/dr-ui/src/library_ui.rs:498`](../ui/dr-ui/src/library_ui.rs#L498), [`ui/dr-ui/src/library_ui.rs:5217`](../ui/dr-ui/src/library_ui.rs#L5217), [`ui/dr-ui/src/library_ui.rs:5332`](../ui/dr-ui/src/library_ui.rs#L5332), [`ui/dr-ui/src/presets.rs:309`](../ui/dr-ui/src/presets.rs#L309), [`ui/dr-ui/src/presets.rs:321`](../ui/dr-ui/src/presets.rs#L321), [`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:134`](../core/dr-decode/src/preview.rs#L134) |
|
||||
| FR-CULL-2 | [`core/dr-decode/src/locate.rs:1`](../core/dr-decode/src/locate.rs#L1), [`core/dr-decode/src/preview.rs:161`](../core/dr-decode/src/preview.rs#L161), [`ui/dr-ui/src/import.rs:464`](../ui/dr-ui/src/import.rs#L464) |
|
||||
| FR-CULL-4 | [`core/dr-catalog/src/rating.rs:1`](../core/dr-catalog/src/rating.rs#L1), [`core/dr-pipeline/src/sidecar.rs:126`](../core/dr-pipeline/src/sidecar.rs#L126), [`ui/dr-ui/src/library.rs:203`](../ui/dr-ui/src/library.rs#L203), [`ui/dr-ui/src/library.rs:364`](../ui/dr-ui/src/library.rs#L364) |
|
||||
@@ -61,7 +61,7 @@ _None._
|
||||
| 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:202`](../core/dr-pipeline/src/framing.rs#L202), [`core/dr-types/src/lib.rs:336`](../core/dr-types/src/lib.rs#L336) |
|
||||
| FR-DEV-4 | [`core/dr-gpu/src/adjust.rs:506`](../core/dr-gpu/src/adjust.rs#L506), [`core/dr-gpu/src/lib.rs:217`](../core/dr-gpu/src/lib.rs#L217) |
|
||||
| FR-DEV-5 | [`core/dr-pipeline/src/history.rs:124`](../core/dr-pipeline/src/history.rs#L124), [`core/dr-pipeline/src/history.rs:1`](../core/dr-pipeline/src/history.rs#L1), [`core/dr-pipeline/src/history.rs:55`](../core/dr-pipeline/src/history.rs#L55), [`core/dr-pipeline/src/history.rs:71`](../core/dr-pipeline/src/history.rs#L71), [`core/dr-pipeline/src/history.rs:79`](../core/dr-pipeline/src/history.rs#L79), [`ui/dr-ui/src/develop.rs:2644`](../ui/dr-ui/src/develop.rs#L2644), [`ui/dr-ui/src/develop.rs:2654`](../ui/dr-ui/src/develop.rs#L2654), [`ui/dr-ui/src/develop.rs:488`](../ui/dr-ui/src/develop.rs#L488), [`ui/dr-ui/src/lib.rs:1214`](../ui/dr-ui/src/lib.rs#L1214) |
|
||||
| FR-DEV-6 | [`core/dr-pipeline/src/preset.rs:1`](../core/dr-pipeline/src/preset.rs#L1), [`core/dr-types/src/settings.rs:182`](../core/dr-types/src/settings.rs#L182), [`ui/dr-ui/src/develop.rs:2594`](../ui/dr-ui/src/develop.rs#L2594), [`ui/dr-ui/src/develop.rs:2615`](../ui/dr-ui/src/develop.rs#L2615), [`ui/dr-ui/src/lib.rs:1186`](../ui/dr-ui/src/lib.rs#L1186), [`ui/dr-ui/src/library.rs:1528`](../ui/dr-ui/src/library.rs#L1528), [`ui/dr-ui/src/library.rs:364`](../ui/dr-ui/src/library.rs#L364), [`ui/dr-ui/src/library.rs:392`](../ui/dr-ui/src/library.rs#L392), [`ui/dr-ui/src/library_ui.rs:2512`](../ui/dr-ui/src/library_ui.rs#L2512), [`ui/dr-ui/src/library_ui.rs:2907`](../ui/dr-ui/src/library_ui.rs#L2907), [`ui/dr-ui/src/library_ui.rs:431`](../ui/dr-ui/src/library_ui.rs#L431), [`ui/dr-ui/src/presets.rs:1`](../ui/dr-ui/src/presets.rs#L1), [`ui/dr-ui/src/settings_ui.rs:535`](../ui/dr-ui/src/settings_ui.rs#L535), [`ui/dr-ui/ui/adjust.slint:598`](../ui/dr-ui/ui/adjust.slint#L598), [`ui/dr-ui/ui/library.slint:1305`](../ui/dr-ui/ui/library.slint#L1305), [`ui/dr-ui/ui/library.slint:831`](../ui/dr-ui/ui/library.slint#L831), [`ui/dr-ui/ui/library.slint:912`](../ui/dr-ui/ui/library.slint#L912), [`ui/dr-ui/ui/settings.slint:87`](../ui/dr-ui/ui/settings.slint#L87) |
|
||||
| FR-DEV-6 | [`core/dr-pipeline/src/preset.rs:1`](../core/dr-pipeline/src/preset.rs#L1), [`core/dr-types/src/settings.rs:182`](../core/dr-types/src/settings.rs#L182), [`ui/dr-ui/src/develop.rs:2594`](../ui/dr-ui/src/develop.rs#L2594), [`ui/dr-ui/src/develop.rs:2615`](../ui/dr-ui/src/develop.rs#L2615), [`ui/dr-ui/src/lib.rs:1186`](../ui/dr-ui/src/lib.rs#L1186), [`ui/dr-ui/src/library.rs:1528`](../ui/dr-ui/src/library.rs#L1528), [`ui/dr-ui/src/library.rs:364`](../ui/dr-ui/src/library.rs#L364), [`ui/dr-ui/src/library.rs:392`](../ui/dr-ui/src/library.rs#L392), [`ui/dr-ui/src/library_ui.rs:2576`](../ui/dr-ui/src/library_ui.rs#L2576), [`ui/dr-ui/src/library_ui.rs:2971`](../ui/dr-ui/src/library_ui.rs#L2971), [`ui/dr-ui/src/library_ui.rs:466`](../ui/dr-ui/src/library_ui.rs#L466), [`ui/dr-ui/src/presets.rs:1`](../ui/dr-ui/src/presets.rs#L1), [`ui/dr-ui/src/settings_ui.rs:535`](../ui/dr-ui/src/settings_ui.rs#L535), [`ui/dr-ui/ui/adjust.slint:598`](../ui/dr-ui/ui/adjust.slint#L598), [`ui/dr-ui/ui/library.slint:1305`](../ui/dr-ui/ui/library.slint#L1305), [`ui/dr-ui/ui/library.slint:831`](../ui/dr-ui/ui/library.slint#L831), [`ui/dr-ui/ui/library.slint:912`](../ui/dr-ui/ui/library.slint#L912), [`ui/dr-ui/ui/settings.slint:87`](../ui/dr-ui/ui/settings.slint#L87) |
|
||||
| FR-DEV-8 | [`core/dr-pipeline/src/detail.rs:364`](../core/dr-pipeline/src/detail.rs#L364), [`core/dr-pipeline/src/operation.rs:265`](../core/dr-pipeline/src/operation.rs#L265) |
|
||||
| FR-DSP-1 | [`core/dr-gpu/src/adjust.rs:1783`](../core/dr-gpu/src/adjust.rs#L1783), [`core/dr-gpu/src/adjust.rs:1860`](../core/dr-gpu/src/adjust.rs#L1860), [`core/dr-gpu/src/adjust.rs:1945`](../core/dr-gpu/src/adjust.rs#L1945), [`core/dr-gpu/src/adjust.rs:506`](../core/dr-gpu/src/adjust.rs#L506), [`core/dr-gpu/src/adjust.rs:54`](../core/dr-gpu/src/adjust.rs#L54), [`core/dr-gpu/src/lib.rs:54`](../core/dr-gpu/src/lib.rs#L54), [`core/dr-gpu/src/lib.rs:94`](../core/dr-gpu/src/lib.rs#L94), [`core/dr-gpu/tests/capture_sharpen.rs:199`](../core/dr-gpu/tests/capture_sharpen.rs#L199), [`core/dr-gpu/tests/detail_stage.rs:327`](../core/dr-gpu/tests/detail_stage.rs#L327), [`core/dr-gpu/tests/local_contrast.rs:262`](../core/dr-gpu/tests/local_contrast.rs#L262), [`core/dr-gpu/tests/noise_reduction.rs:377`](../core/dr-gpu/tests/noise_reduction.rs#L377), [`core/dr-pipeline/src/detail.rs:136`](../core/dr-pipeline/src/detail.rs#L136), [`core/dr-pipeline/src/detail.rs:439`](../core/dr-pipeline/src/detail.rs#L439), [`core/dr-pipeline/src/graph.rs:367`](../core/dr-pipeline/src/graph.rs#L367), [`core/dr-pipeline/src/graph.rs:397`](../core/dr-pipeline/src/graph.rs#L397), [`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:647`](../core/dr-pipeline/src/ops/capture_sharpen.rs#L647), [`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:658`](../core/dr-pipeline/src/ops/local_contrast.rs#L658), [`core/dr-pipeline/src/ops/noise_reduction.rs:691`](../core/dr-pipeline/src/ops/noise_reduction.rs#L691), [`ui/dr-ui/src/develop.rs:2161`](../ui/dr-ui/src/develop.rs#L2161), [`ui/dr-ui/src/develop.rs:2792`](../ui/dr-ui/src/develop.rs#L2792), [`ui/dr-ui/src/develop.rs:3275`](../ui/dr-ui/src/develop.rs#L3275), [`ui/dr-ui/src/develop.rs:3309`](../ui/dr-ui/src/develop.rs#L3309), [`ui/dr-ui/src/lib.rs:59`](../ui/dr-ui/src/lib.rs#L59), [`ui/dr-ui/src/lib.rs:667`](../ui/dr-ui/src/lib.rs#L667) |
|
||||
| FR-DSP-6 | [`core/dr-pipeline/src/operation.rs:412`](../core/dr-pipeline/src/operation.rs#L412), [`core/dr-types/src/colour.rs:1`](../core/dr-types/src/colour.rs#L1) |
|
||||
@@ -72,25 +72,25 @@ _None._
|
||||
| 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:328`](../ui/dr-ui/src/lib.rs#L328), [`ui/dr-ui/src/settings_ui.rs:1`](../ui/dr-ui/src/settings_ui.rs#L1), [`ui/dr-ui/src/settings_ui.rs:48`](../ui/dr-ui/src/settings_ui.rs#L48), [`ui/dr-ui/src/settings_ui.rs:590`](../ui/dr-ui/src/settings_ui.rs#L590) |
|
||||
| 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:941`](../ui/dr-ui/src/export.rs#L941), [`ui/dr-ui/src/lib.rs:175`](../ui/dr-ui/src/lib.rs#L175), [`ui/dr-ui/src/lib.rs:1797`](../ui/dr-ui/src/lib.rs#L1797), [`ui/dr-ui/src/lib.rs:328`](../ui/dr-ui/src/lib.rs#L328), [`ui/dr-ui/src/lib.rs:363`](../ui/dr-ui/src/lib.rs#L363), [`ui/dr-ui/src/lib.rs:390`](../ui/dr-ui/src/lib.rs#L390), [`ui/dr-ui/src/library_ui.rs:3277`](../ui/dr-ui/src/library_ui.rs#L3277), [`ui/dr-ui/src/library_ui.rs:522`](../ui/dr-ui/src/library_ui.rs#L522), [`ui/dr-ui/src/library_ui.rs:6133`](../ui/dr-ui/src/library_ui.rs#L6133), [`ui/dr-ui/src/library_ui.rs:6210`](../ui/dr-ui/src/library_ui.rs#L6210), [`ui/dr-ui/src/library_ui.rs:6222`](../ui/dr-ui/src/library_ui.rs#L6222), [`ui/dr-ui/src/library_ui.rs:625`](../ui/dr-ui/src/library_ui.rs#L625), [`ui/dr-ui/src/library_ui.rs:682`](../ui/dr-ui/src/library_ui.rs#L682), [`ui/dr-ui/ui/app.slint:1374`](../ui/dr-ui/ui/app.slint#L1374), [`ui/dr-ui/ui/app.slint:994`](../ui/dr-ui/ui/app.slint#L994), [`ui/dr-ui/ui/library.slint:1313`](../ui/dr-ui/ui/library.slint#L1313), [`ui/dr-ui/ui/library.slint:835`](../ui/dr-ui/ui/library.slint#L835), [`ui/dr-ui/ui/library.slint:927`](../ui/dr-ui/ui/library.slint#L927) |
|
||||
| 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:941`](../ui/dr-ui/src/export.rs#L941), [`ui/dr-ui/src/lib.rs:175`](../ui/dr-ui/src/lib.rs#L175), [`ui/dr-ui/src/lib.rs:1797`](../ui/dr-ui/src/lib.rs#L1797), [`ui/dr-ui/src/lib.rs:328`](../ui/dr-ui/src/lib.rs#L328), [`ui/dr-ui/src/lib.rs:363`](../ui/dr-ui/src/lib.rs#L363), [`ui/dr-ui/src/lib.rs:390`](../ui/dr-ui/src/lib.rs#L390), [`ui/dr-ui/src/library_ui.rs:3341`](../ui/dr-ui/src/library_ui.rs#L3341), [`ui/dr-ui/src/library_ui.rs:557`](../ui/dr-ui/src/library_ui.rs#L557), [`ui/dr-ui/src/library_ui.rs:6232`](../ui/dr-ui/src/library_ui.rs#L6232), [`ui/dr-ui/src/library_ui.rs:6309`](../ui/dr-ui/src/library_ui.rs#L6309), [`ui/dr-ui/src/library_ui.rs:6321`](../ui/dr-ui/src/library_ui.rs#L6321), [`ui/dr-ui/src/library_ui.rs:660`](../ui/dr-ui/src/library_ui.rs#L660), [`ui/dr-ui/src/library_ui.rs:717`](../ui/dr-ui/src/library_ui.rs#L717), [`ui/dr-ui/ui/app.slint:1374`](../ui/dr-ui/ui/app.slint#L1374), [`ui/dr-ui/ui/app.slint:994`](../ui/dr-ui/ui/app.slint#L994), [`ui/dr-ui/ui/library.slint:1313`](../ui/dr-ui/ui/library.slint#L1313), [`ui/dr-ui/ui/library.slint:835`](../ui/dr-ui/ui/library.slint#L835), [`ui/dr-ui/ui/library.slint:927`](../ui/dr-ui/ui/library.slint#L927) |
|
||||
| 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:1222`](../core/dr-decode/src/locate.rs#L1222), [`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:444`](../core/dr-types/src/lib.rs#L444), [`core/dr-types/src/settings.rs:313`](../core/dr-types/src/settings.rs#L313), [`ui/dr-ui/src/export.rs:620`](../ui/dr-ui/src/export.rs#L620), [`ui/dr-ui/src/export.rs:648`](../ui/dr-ui/src/export.rs#L648), [`ui/dr-ui/src/export.rs:776`](../ui/dr-ui/src/export.rs#L776), [`ui/dr-ui/src/export.rs:793`](../ui/dr-ui/src/export.rs#L793), [`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:791`](../core/dr-gpu/src/adjust.rs#L791), [`ui/dr-ui/src/develop.rs:2284`](../ui/dr-ui/src/develop.rs#L2284), [`ui/dr-ui/src/lib.rs:328`](../ui/dr-ui/src/lib.rs#L328) |
|
||||
| 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/session.rs:128`](../core/dr-sync-nextcloud/src/session.rs#L128), [`ui/dr-ui/src/launch.rs:256`](../ui/dr-ui/src/launch.rs#L256), [`ui/dr-ui/src/launch.rs:49`](../ui/dr-ui/src/launch.rs#L49), [`ui/dr-ui/src/launch_ui.rs:344`](../ui/dr-ui/src/launch_ui.rs#L344) |
|
||||
| FR-NC-10 | [`ui/dr-ui/src/export.rs:1`](../ui/dr-ui/src/export.rs#L1), [`ui/dr-ui/src/lib.rs:390`](../ui/dr-ui/src/lib.rs#L390), [`ui/dr-ui/src/library.rs:1565`](../ui/dr-ui/src/library.rs#L1565), [`ui/dr-ui/src/library.rs:439`](../ui/dr-ui/src/library.rs#L439), [`ui/dr-ui/src/library.rs:733`](../ui/dr-ui/src/library.rs#L733), [`ui/dr-ui/src/library.rs:930`](../ui/dr-ui/src/library.rs#L930), [`ui/dr-ui/src/library_ui.rs:1571`](../ui/dr-ui/src/library_ui.rs#L1571), [`ui/dr-ui/src/library_ui.rs:3277`](../ui/dr-ui/src/library_ui.rs#L3277), [`ui/dr-ui/src/library_ui.rs:463`](../ui/dr-ui/src/library_ui.rs#L463), [`ui/dr-ui/src/sidecar_cache.rs:1`](../ui/dr-ui/src/sidecar_cache.rs#L1) |
|
||||
| FR-NC-10 | [`ui/dr-ui/src/export.rs:1`](../ui/dr-ui/src/export.rs#L1), [`ui/dr-ui/src/lib.rs:390`](../ui/dr-ui/src/lib.rs#L390), [`ui/dr-ui/src/library.rs:1565`](../ui/dr-ui/src/library.rs#L1565), [`ui/dr-ui/src/library.rs:439`](../ui/dr-ui/src/library.rs#L439), [`ui/dr-ui/src/library.rs:733`](../ui/dr-ui/src/library.rs#L733), [`ui/dr-ui/src/library.rs:930`](../ui/dr-ui/src/library.rs#L930), [`ui/dr-ui/src/library_ui.rs:1606`](../ui/dr-ui/src/library_ui.rs#L1606), [`ui/dr-ui/src/library_ui.rs:3341`](../ui/dr-ui/src/library_ui.rs#L3341), [`ui/dr-ui/src/library_ui.rs:498`](../ui/dr-ui/src/library_ui.rs#L498), [`ui/dr-ui/src/sidecar_cache.rs:1`](../ui/dr-ui/src/sidecar_cache.rs#L1) |
|
||||
| FR-NC-12 | [`core/dr-sync-nextcloud/src/lib.rs:34`](../core/dr-sync-nextcloud/src/lib.rs#L34), [`core/dr-sync-nextcloud/src/lib.rs:892`](../core/dr-sync-nextcloud/src/lib.rs#L892), [`core/dr-sync/src/lib.rs:157`](../core/dr-sync/src/lib.rs#L157), [`core/dr-sync/src/lib.rs:40`](../core/dr-sync/src/lib.rs#L40), [`core/dr-sync/src/reachability.rs:1`](../core/dr-sync/src/reachability.rs#L1) |
|
||||
| FR-NC-2 | [`core/dr-sync-nextcloud/src/session.rs:128`](../core/dr-sync-nextcloud/src/session.rs#L128), [`core/dr-sync-nextcloud/src/session.rs:34`](../core/dr-sync-nextcloud/src/session.rs#L34) |
|
||||
| FR-NC-3 | [`core/dr-decode/src/locate.rs:1`](../core/dr-decode/src/locate.rs#L1), [`core/dr-decode/src/preview.rs:161`](../core/dr-decode/src/preview.rs#L161), [`core/dr-sync/src/capability.rs:41`](../core/dr-sync/src/capability.rs#L41), [`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:2581`](../ui/dr-ui/src/library.rs#L2581), [`ui/dr-ui/src/library.rs:2607`](../ui/dr-ui/src/library.rs#L2607), [`ui/dr-ui/src/library_ui.rs:1`](../ui/dr-ui/src/library_ui.rs#L1), [`ui/dr-ui/src/library_ui.rs:3539`](../ui/dr-ui/src/library_ui.rs#L3539), [`ui/dr-ui/src/library_ui.rs:4470`](../ui/dr-ui/src/library_ui.rs#L4470), [`ui/dr-ui/ui/app.slint:513`](../ui/dr-ui/ui/app.slint#L513), [`ui/dr-ui/ui/settings.slint:340`](../ui/dr-ui/ui/settings.slint#L340), [`ui/dr-ui/ui/settings.slint:72`](../ui/dr-ui/ui/settings.slint#L72) |
|
||||
| FR-NC-3 | [`core/dr-decode/src/locate.rs:1`](../core/dr-decode/src/locate.rs#L1), [`core/dr-decode/src/preview.rs:161`](../core/dr-decode/src/preview.rs#L161), [`core/dr-sync/src/capability.rs:41`](../core/dr-sync/src/capability.rs#L41), [`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:2581`](../ui/dr-ui/src/library.rs#L2581), [`ui/dr-ui/src/library.rs:2607`](../ui/dr-ui/src/library.rs#L2607), [`ui/dr-ui/src/library_ui.rs:1`](../ui/dr-ui/src/library_ui.rs#L1), [`ui/dr-ui/src/library_ui.rs:3603`](../ui/dr-ui/src/library_ui.rs#L3603), [`ui/dr-ui/src/library_ui.rs:4569`](../ui/dr-ui/src/library_ui.rs#L4569), [`ui/dr-ui/ui/app.slint:513`](../ui/dr-ui/ui/app.slint#L513), [`ui/dr-ui/ui/settings.slint:340`](../ui/dr-ui/ui/settings.slint#L340), [`ui/dr-ui/ui/settings.slint:72`](../ui/dr-ui/ui/settings.slint#L72) |
|
||||
| FR-NC-4 | [`core/dr-sync-nextcloud/src/propfind.rs:100`](../core/dr-sync-nextcloud/src/propfind.rs#L100), [`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:157`](../core/dr-sync/src/lib.rs#L157), [`core/dr-sync/src/scan.rs:93`](../core/dr-sync/src/scan.rs#L93), [`ui/dr-ui/src/launch.rs:49`](../ui/dr-ui/src/launch.rs#L49) |
|
||||
| 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:489`](../ui/dr-ui/src/import.rs#L489) |
|
||||
| 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/schema.rs:337`](../core/dr-catalog/src/schema.rs#L337), [`core/dr-catalog/src/schema.rs:738`](../core/dr-catalog/src/schema.rs#L738), [`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:3195`](../ui/dr-ui/src/collections_ui.rs#L3195), [`ui/dr-ui/src/collections_ui.rs:621`](../ui/dr-ui/src/collections_ui.rs#L621), [`ui/dr-ui/src/collections_ui.rs:683`](../ui/dr-ui/src/collections_ui.rs#L683), [`ui/dr-ui/src/lib.rs:1557`](../ui/dr-ui/src/lib.rs#L1557), [`ui/dr-ui/src/lib.rs:2297`](../ui/dr-ui/src/lib.rs#L2297), [`ui/dr-ui/src/library.rs:1307`](../ui/dr-ui/src/library.rs#L1307), [`ui/dr-ui/src/library.rs:1330`](../ui/dr-ui/src/library.rs#L1330), [`ui/dr-ui/src/library.rs:1488`](../ui/dr-ui/src/library.rs#L1488), [`ui/dr-ui/src/library_ui.rs:1030`](../ui/dr-ui/src/library_ui.rs#L1030), [`ui/dr-ui/src/library_ui.rs:1103`](../ui/dr-ui/src/library_ui.rs#L1103), [`ui/dr-ui/src/library_ui.rs:1204`](../ui/dr-ui/src/library_ui.rs#L1204), [`ui/dr-ui/src/library_ui.rs:1259`](../ui/dr-ui/src/library_ui.rs#L1259), [`ui/dr-ui/src/library_ui.rs:1377`](../ui/dr-ui/src/library_ui.rs#L1377), [`ui/dr-ui/src/library_ui.rs:1496`](../ui/dr-ui/src/library_ui.rs#L1496), [`ui/dr-ui/src/library_ui.rs:2007`](../ui/dr-ui/src/library_ui.rs#L2007), [`ui/dr-ui/src/library_ui.rs:234`](../ui/dr-ui/src/library_ui.rs#L234), [`ui/dr-ui/src/library_ui.rs:245`](../ui/dr-ui/src/library_ui.rs#L245), [`ui/dr-ui/src/library_ui.rs:253`](../ui/dr-ui/src/library_ui.rs#L253), [`ui/dr-ui/src/library_ui.rs:265`](../ui/dr-ui/src/library_ui.rs#L265), [`ui/dr-ui/src/library_ui.rs:274`](../ui/dr-ui/src/library_ui.rs#L274), [`ui/dr-ui/src/library_ui.rs:364`](../ui/dr-ui/src/library_ui.rs#L364), [`ui/dr-ui/src/library_ui.rs:374`](../ui/dr-ui/src/library_ui.rs#L374), [`ui/dr-ui/src/library_ui.rs:416`](../ui/dr-ui/src/library_ui.rs#L416), [`ui/dr-ui/src/library_ui.rs:479`](../ui/dr-ui/src/library_ui.rs#L479), [`ui/dr-ui/src/library_ui.rs:510`](../ui/dr-ui/src/library_ui.rs#L510), [`ui/dr-ui/src/library_ui.rs:5135`](../ui/dr-ui/src/library_ui.rs#L5135), [`ui/dr-ui/src/library_ui.rs:5153`](../ui/dr-ui/src/library_ui.rs#L5153), [`ui/dr-ui/src/library_ui.rs:5165`](../ui/dr-ui/src/library_ui.rs#L5165), [`ui/dr-ui/src/library_ui.rs:522`](../ui/dr-ui/src/library_ui.rs#L522), [`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:2403`](../ui/dr-ui/ui/app.slint#L2403), [`ui/dr-ui/ui/app.slint:576`](../ui/dr-ui/ui/app.slint#L576), [`ui/dr-ui/ui/collections.slint:249`](../ui/dr-ui/ui/collections.slint#L249), [`ui/dr-ui/ui/collections.slint:369`](../ui/dr-ui/ui/collections.slint#L369), [`ui/dr-ui/ui/collections.slint:52`](../ui/dr-ui/ui/collections.slint#L52), [`ui/dr-ui/ui/collections.slint:682`](../ui/dr-ui/ui/collections.slint#L682), [`ui/dr-ui/ui/collections.slint:84`](../ui/dr-ui/ui/collections.slint#L84), [`ui/dr-ui/ui/icons.slint:260`](../ui/dr-ui/ui/icons.slint#L260), [`ui/dr-ui/ui/library.slint:973`](../ui/dr-ui/ui/library.slint#L973) |
|
||||
| FR-NC-6b | [`ui/dr-ui/src/library_ui.rs:1259`](../ui/dr-ui/src/library_ui.rs#L1259) |
|
||||
| FR-NC-6c | [`core/dr-sync-nextcloud/src/desktop_client.rs:30`](../core/dr-sync-nextcloud/src/desktop_client.rs#L30), [`core/dr-types/src/lib.rs:119`](../core/dr-types/src/lib.rs#L119), [`core/dr-types/src/lib.rs:201`](../core/dr-types/src/lib.rs#L201), [`ui/dr-ui/src/activity.rs:1`](../ui/dr-ui/src/activity.rs#L1), [`ui/dr-ui/src/collections_ui.rs:3195`](../ui/dr-ui/src/collections_ui.rs#L3195), [`ui/dr-ui/src/collections_ui.rs:621`](../ui/dr-ui/src/collections_ui.rs#L621), [`ui/dr-ui/src/collections_ui.rs:683`](../ui/dr-ui/src/collections_ui.rs#L683), [`ui/dr-ui/src/library_ui.rs:1030`](../ui/dr-ui/src/library_ui.rs#L1030), [`ui/dr-ui/src/library_ui.rs:1103`](../ui/dr-ui/src/library_ui.rs#L1103), [`ui/dr-ui/ui/collections.slint:249`](../ui/dr-ui/ui/collections.slint#L249), [`ui/dr-ui/ui/collections.slint:682`](../ui/dr-ui/ui/collections.slint#L682), [`ui/dr-ui/ui/icons.slint:260`](../ui/dr-ui/ui/icons.slint#L260) |
|
||||
| FR-NC-7 | [`core/dr-sync-nextcloud/src/lib.rs:95`](../core/dr-sync-nextcloud/src/lib.rs#L95), [`ui/dr-ui/src/derived_sync.rs:1`](../ui/dr-ui/src/derived_sync.rs#L1), [`ui/dr-ui/src/library.rs:2607`](../ui/dr-ui/src/library.rs#L2607), [`ui/dr-ui/src/library_ui.rs:3539`](../ui/dr-ui/src/library_ui.rs#L3539), [`ui/dr-ui/ui/settings.slint:340`](../ui/dr-ui/ui/settings.slint#L340) |
|
||||
| FR-NC-6a | [`core/dr-catalog/src/cache.rs:1`](../core/dr-catalog/src/cache.rs#L1), [`core/dr-catalog/src/schema.rs:394`](../core/dr-catalog/src/schema.rs#L394), [`core/dr-catalog/src/schema.rs:795`](../core/dr-catalog/src/schema.rs#L795), [`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:3195`](../ui/dr-ui/src/collections_ui.rs#L3195), [`ui/dr-ui/src/collections_ui.rs:621`](../ui/dr-ui/src/collections_ui.rs#L621), [`ui/dr-ui/src/collections_ui.rs:683`](../ui/dr-ui/src/collections_ui.rs#L683), [`ui/dr-ui/src/lib.rs:1557`](../ui/dr-ui/src/lib.rs#L1557), [`ui/dr-ui/src/lib.rs:2297`](../ui/dr-ui/src/lib.rs#L2297), [`ui/dr-ui/src/library.rs:1307`](../ui/dr-ui/src/library.rs#L1307), [`ui/dr-ui/src/library.rs:1330`](../ui/dr-ui/src/library.rs#L1330), [`ui/dr-ui/src/library.rs:1488`](../ui/dr-ui/src/library.rs#L1488), [`ui/dr-ui/src/library_ui.rs:1065`](../ui/dr-ui/src/library_ui.rs#L1065), [`ui/dr-ui/src/library_ui.rs:1138`](../ui/dr-ui/src/library_ui.rs#L1138), [`ui/dr-ui/src/library_ui.rs:1239`](../ui/dr-ui/src/library_ui.rs#L1239), [`ui/dr-ui/src/library_ui.rs:1294`](../ui/dr-ui/src/library_ui.rs#L1294), [`ui/dr-ui/src/library_ui.rs:1412`](../ui/dr-ui/src/library_ui.rs#L1412), [`ui/dr-ui/src/library_ui.rs:1531`](../ui/dr-ui/src/library_ui.rs#L1531), [`ui/dr-ui/src/library_ui.rs:2058`](../ui/dr-ui/src/library_ui.rs#L2058), [`ui/dr-ui/src/library_ui.rs:267`](../ui/dr-ui/src/library_ui.rs#L267), [`ui/dr-ui/src/library_ui.rs:278`](../ui/dr-ui/src/library_ui.rs#L278), [`ui/dr-ui/src/library_ui.rs:286`](../ui/dr-ui/src/library_ui.rs#L286), [`ui/dr-ui/src/library_ui.rs:298`](../ui/dr-ui/src/library_ui.rs#L298), [`ui/dr-ui/src/library_ui.rs:307`](../ui/dr-ui/src/library_ui.rs#L307), [`ui/dr-ui/src/library_ui.rs:399`](../ui/dr-ui/src/library_ui.rs#L399), [`ui/dr-ui/src/library_ui.rs:409`](../ui/dr-ui/src/library_ui.rs#L409), [`ui/dr-ui/src/library_ui.rs:451`](../ui/dr-ui/src/library_ui.rs#L451), [`ui/dr-ui/src/library_ui.rs:514`](../ui/dr-ui/src/library_ui.rs#L514), [`ui/dr-ui/src/library_ui.rs:5234`](../ui/dr-ui/src/library_ui.rs#L5234), [`ui/dr-ui/src/library_ui.rs:5252`](../ui/dr-ui/src/library_ui.rs#L5252), [`ui/dr-ui/src/library_ui.rs:5264`](../ui/dr-ui/src/library_ui.rs#L5264), [`ui/dr-ui/src/library_ui.rs:545`](../ui/dr-ui/src/library_ui.rs#L545), [`ui/dr-ui/src/library_ui.rs:557`](../ui/dr-ui/src/library_ui.rs#L557), [`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:2403`](../ui/dr-ui/ui/app.slint#L2403), [`ui/dr-ui/ui/app.slint:576`](../ui/dr-ui/ui/app.slint#L576), [`ui/dr-ui/ui/collections.slint:249`](../ui/dr-ui/ui/collections.slint#L249), [`ui/dr-ui/ui/collections.slint:369`](../ui/dr-ui/ui/collections.slint#L369), [`ui/dr-ui/ui/collections.slint:52`](../ui/dr-ui/ui/collections.slint#L52), [`ui/dr-ui/ui/collections.slint:682`](../ui/dr-ui/ui/collections.slint#L682), [`ui/dr-ui/ui/collections.slint:84`](../ui/dr-ui/ui/collections.slint#L84), [`ui/dr-ui/ui/icons.slint:260`](../ui/dr-ui/ui/icons.slint#L260), [`ui/dr-ui/ui/library.slint:973`](../ui/dr-ui/ui/library.slint#L973) |
|
||||
| FR-NC-6b | [`ui/dr-ui/src/library_ui.rs:1294`](../ui/dr-ui/src/library_ui.rs#L1294) |
|
||||
| FR-NC-6c | [`core/dr-sync-nextcloud/src/desktop_client.rs:30`](../core/dr-sync-nextcloud/src/desktop_client.rs#L30), [`core/dr-types/src/lib.rs:119`](../core/dr-types/src/lib.rs#L119), [`core/dr-types/src/lib.rs:201`](../core/dr-types/src/lib.rs#L201), [`ui/dr-ui/src/activity.rs:1`](../ui/dr-ui/src/activity.rs#L1), [`ui/dr-ui/src/collections_ui.rs:3195`](../ui/dr-ui/src/collections_ui.rs#L3195), [`ui/dr-ui/src/collections_ui.rs:621`](../ui/dr-ui/src/collections_ui.rs#L621), [`ui/dr-ui/src/collections_ui.rs:683`](../ui/dr-ui/src/collections_ui.rs#L683), [`ui/dr-ui/src/library_ui.rs:1065`](../ui/dr-ui/src/library_ui.rs#L1065), [`ui/dr-ui/src/library_ui.rs:1138`](../ui/dr-ui/src/library_ui.rs#L1138), [`ui/dr-ui/ui/collections.slint:249`](../ui/dr-ui/ui/collections.slint#L249), [`ui/dr-ui/ui/collections.slint:682`](../ui/dr-ui/ui/collections.slint#L682), [`ui/dr-ui/ui/icons.slint:260`](../ui/dr-ui/ui/icons.slint#L260) |
|
||||
| FR-NC-7 | [`core/dr-sync-nextcloud/src/lib.rs:95`](../core/dr-sync-nextcloud/src/lib.rs#L95), [`ui/dr-ui/src/derived_sync.rs:1`](../ui/dr-ui/src/derived_sync.rs#L1), [`ui/dr-ui/src/library.rs:2607`](../ui/dr-ui/src/library.rs#L2607), [`ui/dr-ui/src/library_ui.rs:3603`](../ui/dr-ui/src/library_ui.rs#L3603), [`ui/dr-ui/ui/settings.slint:340`](../ui/dr-ui/ui/settings.slint#L340) |
|
||||
| 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:116`](../core/dr-types/src/settings.rs#L116), [`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:949`](../ui/dr-ui/src/lib.rs#L949), [`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:123`](../ui/dr-ui/src/import.rs#L123), [`ui/dr-ui/src/import.rs:337`](../ui/dr-ui/src/import.rs#L337), [`ui/dr-ui/src/import.rs:585`](../ui/dr-ui/src/import.rs#L585), [`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:949`](../ui/dr-ui/src/lib.rs#L949) |
|
||||
| FR-NC-8 | [`core/dr-pipeline/src/sidecar.rs:109`](../core/dr-pipeline/src/sidecar.rs#L109), [`core/dr-pipeline/src/sidecar.rs:90`](../core/dr-pipeline/src/sidecar.rs#L90), [`ui/dr-ui/src/lib.rs:1526`](../ui/dr-ui/src/lib.rs#L1526), [`ui/dr-ui/src/library.rs:364`](../ui/dr-ui/src/library.rs#L364), [`ui/dr-ui/src/library_ui.rs:431`](../ui/dr-ui/src/library_ui.rs#L431) |
|
||||
| FR-NC-9 | [`core/dr-catalog/src/merge.rs:1`](../core/dr-catalog/src/merge.rs#L1), [`core/dr-catalog/src/schema.rs:280`](../core/dr-catalog/src/schema.rs#L280), [`core/dr-catalog/src/sync.rs:1`](../core/dr-catalog/src/sync.rs#L1), [`core/dr-pipeline/src/sidecar.rs:147`](../core/dr-pipeline/src/sidecar.rs#L147), [`core/dr-pipeline/src/sidecar.rs:301`](../core/dr-pipeline/src/sidecar.rs#L301), [`ui/dr-ui/src/library.rs:733`](../ui/dr-ui/src/library.rs#L733), [`ui/dr-ui/src/library.rs:863`](../ui/dr-ui/src/library.rs#L863) |
|
||||
| FR-NC-8 | [`core/dr-pipeline/src/sidecar.rs:109`](../core/dr-pipeline/src/sidecar.rs#L109), [`core/dr-pipeline/src/sidecar.rs:90`](../core/dr-pipeline/src/sidecar.rs#L90), [`ui/dr-ui/src/lib.rs:1526`](../ui/dr-ui/src/lib.rs#L1526), [`ui/dr-ui/src/library.rs:364`](../ui/dr-ui/src/library.rs#L364), [`ui/dr-ui/src/library_ui.rs:466`](../ui/dr-ui/src/library_ui.rs#L466) |
|
||||
| FR-NC-9 | [`core/dr-catalog/src/merge.rs:1`](../core/dr-catalog/src/merge.rs#L1), [`core/dr-catalog/src/schema.rs:337`](../core/dr-catalog/src/schema.rs#L337), [`core/dr-catalog/src/sync.rs:1`](../core/dr-catalog/src/sync.rs#L1), [`core/dr-pipeline/src/sidecar.rs:147`](../core/dr-pipeline/src/sidecar.rs#L147), [`core/dr-pipeline/src/sidecar.rs:301`](../core/dr-pipeline/src/sidecar.rs#L301), [`ui/dr-ui/src/library.rs:733`](../ui/dr-ui/src/library.rs#L733), [`ui/dr-ui/src/library.rs:863`](../ui/dr-ui/src/library.rs#L863) |
|
||||
| FR-PLAT-AND-1 | [`core/dr-types/src/lib.rs:53`](../core/dr-types/src/lib.rs#L53) |
|
||||
| FR-PLAT-AND-3 | [`core/dr-catalog/src/jobs.rs:1`](../core/dr-catalog/src/jobs.rs#L1) |
|
||||
| FR-PLAT-LIN-1 | [`core/dr-types/src/settings.rs:1`](../core/dr-types/src/settings.rs#L1), [`ui/dr-ui/src/lib.rs:736`](../ui/dr-ui/src/lib.rs#L736), [`ui/dr-ui/src/settings_store.rs:1`](../ui/dr-ui/src/settings_store.rs#L1) |
|
||||
@@ -99,9 +99,9 @@ _None._
|
||||
| FR-RAW-4 | [`core/dr-decode/src/error.rs:1`](../core/dr-decode/src/error.rs#L1), [`ui/dr-ui/src/lib.rs:175`](../ui/dr-ui/src/lib.rs#L175) |
|
||||
| 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:2379`](../ui/dr-ui/src/lib.rs#L2379), [`ui/dr-ui/src/lib.rs:67`](../ui/dr-ui/src/lib.rs#L67), [`ui/dr-ui/src/masks_ui.rs:816`](../ui/dr-ui/src/masks_ui.rs#L816), [`ui/dr-ui/ui/library.slint:1031`](../ui/dr-ui/ui/library.slint#L1031) |
|
||||
| FR-UI-2 | [`ui/dr-ui/src/collections_ui.rs:1005`](../ui/dr-ui/src/collections_ui.rs#L1005), [`ui/dr-ui/src/collections_ui.rs:118`](../ui/dr-ui/src/collections_ui.rs#L118), [`ui/dr-ui/src/collections_ui.rs:1508`](../ui/dr-ui/src/collections_ui.rs#L1508), [`ui/dr-ui/src/collections_ui.rs:1522`](../ui/dr-ui/src/collections_ui.rs#L1522), [`ui/dr-ui/src/collections_ui.rs:1568`](../ui/dr-ui/src/collections_ui.rs#L1568), [`ui/dr-ui/src/collections_ui.rs:159`](../ui/dr-ui/src/collections_ui.rs#L159), [`ui/dr-ui/src/collections_ui.rs:1666`](../ui/dr-ui/src/collections_ui.rs#L1666), [`ui/dr-ui/src/collections_ui.rs:485`](../ui/dr-ui/src/collections_ui.rs#L485), [`ui/dr-ui/src/collections_ui.rs:514`](../ui/dr-ui/src/collections_ui.rs#L514), [`ui/dr-ui/src/collections_ui.rs:995`](../ui/dr-ui/src/collections_ui.rs#L995), [`ui/dr-ui/src/lib.rs:67`](../ui/dr-ui/src/lib.rs#L67), [`ui/dr-ui/src/library_ui.rs:253`](../ui/dr-ui/src/library_ui.rs#L253), [`ui/dr-ui/src/library_ui.rs:5020`](../ui/dr-ui/src/library_ui.rs#L5020), [`ui/dr-ui/src/library_ui.rs:5165`](../ui/dr-ui/src/library_ui.rs#L5165), [`ui/dr-ui/src/library_ui.rs:6241`](../ui/dr-ui/src/library_ui.rs#L6241), [`ui/dr-ui/ui/app.slint:2132`](../ui/dr-ui/ui/app.slint#L2132), [`ui/dr-ui/ui/app.slint:262`](../ui/dr-ui/ui/app.slint#L262), [`ui/dr-ui/ui/app.slint:606`](../ui/dr-ui/ui/app.slint#L606), [`ui/dr-ui/ui/app.slint:613`](../ui/dr-ui/ui/app.slint#L613), [`ui/dr-ui/ui/library.slint:1186`](../ui/dr-ui/ui/library.slint#L1186), [`ui/dr-ui/ui/library.slint:1193`](../ui/dr-ui/ui/library.slint#L1193), [`ui/dr-ui/ui/library.slint:1199`](../ui/dr-ui/ui/library.slint#L1199), [`ui/dr-ui/ui/library.slint:2266`](../ui/dr-ui/ui/library.slint#L2266), [`ui/dr-ui/ui/library.slint:823`](../ui/dr-ui/ui/library.slint#L823), [`ui/dr-ui/ui/library.slint:872`](../ui/dr-ui/ui/library.slint#L872), [`ui/dr-ui/ui/settings.slint:93`](../ui/dr-ui/ui/settings.slint#L93) |
|
||||
| FR-UI-3 | [`ui/dr-ui/src/develop.rs:1831`](../ui/dr-ui/src/develop.rs#L1831), [`ui/dr-ui/src/library_ui.rs:4265`](../ui/dr-ui/src/library_ui.rs#L4265), [`ui/dr-ui/src/masks_ui.rs:218`](../ui/dr-ui/src/masks_ui.rs#L218), [`ui/dr-ui/src/masks_ui.rs:908`](../ui/dr-ui/src/masks_ui.rs#L908), [`ui/dr-ui/src/masks_ui.rs:930`](../ui/dr-ui/src/masks_ui.rs#L930), [`ui/dr-ui/ui/app.slint:1905`](../ui/dr-ui/ui/app.slint#L1905), [`ui/dr-ui/ui/collections.slint:4`](../ui/dr-ui/ui/collections.slint#L4), [`ui/dr-ui/ui/collections.slint:682`](../ui/dr-ui/ui/collections.slint#L682) |
|
||||
| FR-UI-4 | [`ui/dr-ui/src/collections_ui.rs:1005`](../ui/dr-ui/src/collections_ui.rs#L1005), [`ui/dr-ui/src/collections_ui.rs:118`](../ui/dr-ui/src/collections_ui.rs#L118), [`ui/dr-ui/src/collections_ui.rs:131`](../ui/dr-ui/src/collections_ui.rs#L131), [`ui/dr-ui/src/collections_ui.rs:1508`](../ui/dr-ui/src/collections_ui.rs#L1508), [`ui/dr-ui/src/collections_ui.rs:1522`](../ui/dr-ui/src/collections_ui.rs#L1522), [`ui/dr-ui/src/collections_ui.rs:1568`](../ui/dr-ui/src/collections_ui.rs#L1568), [`ui/dr-ui/src/collections_ui.rs:159`](../ui/dr-ui/src/collections_ui.rs#L159), [`ui/dr-ui/src/collections_ui.rs:1666`](../ui/dr-ui/src/collections_ui.rs#L1666), [`ui/dr-ui/src/collections_ui.rs:1693`](../ui/dr-ui/src/collections_ui.rs#L1693), [`ui/dr-ui/src/collections_ui.rs:485`](../ui/dr-ui/src/collections_ui.rs#L485), [`ui/dr-ui/src/collections_ui.rs:514`](../ui/dr-ui/src/collections_ui.rs#L514), [`ui/dr-ui/src/collections_ui.rs:582`](../ui/dr-ui/src/collections_ui.rs#L582), [`ui/dr-ui/src/collections_ui.rs:995`](../ui/dr-ui/src/collections_ui.rs#L995), [`ui/dr-ui/src/library_ui.rs:4265`](../ui/dr-ui/src/library_ui.rs#L4265), [`ui/dr-ui/src/library_ui.rs:4310`](../ui/dr-ui/src/library_ui.rs#L4310), [`ui/dr-ui/src/library_ui.rs:4413`](../ui/dr-ui/src/library_ui.rs#L4413), [`ui/dr-ui/src/library_ui.rs:4441`](../ui/dr-ui/src/library_ui.rs#L4441), [`ui/dr-ui/src/library_ui.rs:5153`](../ui/dr-ui/src/library_ui.rs#L5153), [`ui/dr-ui/src/library_ui.rs:5165`](../ui/dr-ui/src/library_ui.rs#L5165), [`ui/dr-ui/ui/app.slint:1601`](../ui/dr-ui/ui/app.slint#L1601), [`ui/dr-ui/ui/app.slint:582`](../ui/dr-ui/ui/app.slint#L582), [`ui/dr-ui/ui/app.slint:613`](../ui/dr-ui/ui/app.slint#L613), [`ui/dr-ui/ui/library.slint:1186`](../ui/dr-ui/ui/library.slint#L1186), [`ui/dr-ui/ui/library.slint:1193`](../ui/dr-ui/ui/library.slint#L1193), [`ui/dr-ui/ui/library.slint:1199`](../ui/dr-ui/ui/library.slint#L1199), [`ui/dr-ui/ui/library.slint:2266`](../ui/dr-ui/ui/library.slint#L2266), [`ui/dr-ui/ui/library.slint:823`](../ui/dr-ui/ui/library.slint#L823), [`ui/dr-ui/ui/library.slint:872`](../ui/dr-ui/ui/library.slint#L872), [`ui/dr-ui/ui/library.slint:891`](../ui/dr-ui/ui/library.slint#L891) |
|
||||
| FR-UI-2 | [`ui/dr-ui/src/collections_ui.rs:1005`](../ui/dr-ui/src/collections_ui.rs#L1005), [`ui/dr-ui/src/collections_ui.rs:118`](../ui/dr-ui/src/collections_ui.rs#L118), [`ui/dr-ui/src/collections_ui.rs:1508`](../ui/dr-ui/src/collections_ui.rs#L1508), [`ui/dr-ui/src/collections_ui.rs:1522`](../ui/dr-ui/src/collections_ui.rs#L1522), [`ui/dr-ui/src/collections_ui.rs:1568`](../ui/dr-ui/src/collections_ui.rs#L1568), [`ui/dr-ui/src/collections_ui.rs:159`](../ui/dr-ui/src/collections_ui.rs#L159), [`ui/dr-ui/src/collections_ui.rs:1666`](../ui/dr-ui/src/collections_ui.rs#L1666), [`ui/dr-ui/src/collections_ui.rs:485`](../ui/dr-ui/src/collections_ui.rs#L485), [`ui/dr-ui/src/collections_ui.rs:514`](../ui/dr-ui/src/collections_ui.rs#L514), [`ui/dr-ui/src/collections_ui.rs:995`](../ui/dr-ui/src/collections_ui.rs#L995), [`ui/dr-ui/src/lib.rs:67`](../ui/dr-ui/src/lib.rs#L67), [`ui/dr-ui/src/library_ui.rs:286`](../ui/dr-ui/src/library_ui.rs#L286), [`ui/dr-ui/src/library_ui.rs:5119`](../ui/dr-ui/src/library_ui.rs#L5119), [`ui/dr-ui/src/library_ui.rs:5264`](../ui/dr-ui/src/library_ui.rs#L5264), [`ui/dr-ui/src/library_ui.rs:6340`](../ui/dr-ui/src/library_ui.rs#L6340), [`ui/dr-ui/ui/app.slint:2132`](../ui/dr-ui/ui/app.slint#L2132), [`ui/dr-ui/ui/app.slint:262`](../ui/dr-ui/ui/app.slint#L262), [`ui/dr-ui/ui/app.slint:606`](../ui/dr-ui/ui/app.slint#L606), [`ui/dr-ui/ui/app.slint:613`](../ui/dr-ui/ui/app.slint#L613), [`ui/dr-ui/ui/library.slint:1186`](../ui/dr-ui/ui/library.slint#L1186), [`ui/dr-ui/ui/library.slint:1193`](../ui/dr-ui/ui/library.slint#L1193), [`ui/dr-ui/ui/library.slint:1199`](../ui/dr-ui/ui/library.slint#L1199), [`ui/dr-ui/ui/library.slint:2266`](../ui/dr-ui/ui/library.slint#L2266), [`ui/dr-ui/ui/library.slint:823`](../ui/dr-ui/ui/library.slint#L823), [`ui/dr-ui/ui/library.slint:872`](../ui/dr-ui/ui/library.slint#L872), [`ui/dr-ui/ui/settings.slint:93`](../ui/dr-ui/ui/settings.slint#L93) |
|
||||
| FR-UI-3 | [`ui/dr-ui/src/develop.rs:1831`](../ui/dr-ui/src/develop.rs#L1831), [`ui/dr-ui/src/library_ui.rs:4364`](../ui/dr-ui/src/library_ui.rs#L4364), [`ui/dr-ui/src/masks_ui.rs:218`](../ui/dr-ui/src/masks_ui.rs#L218), [`ui/dr-ui/src/masks_ui.rs:908`](../ui/dr-ui/src/masks_ui.rs#L908), [`ui/dr-ui/src/masks_ui.rs:930`](../ui/dr-ui/src/masks_ui.rs#L930), [`ui/dr-ui/ui/app.slint:1905`](../ui/dr-ui/ui/app.slint#L1905), [`ui/dr-ui/ui/collections.slint:4`](../ui/dr-ui/ui/collections.slint#L4), [`ui/dr-ui/ui/collections.slint:682`](../ui/dr-ui/ui/collections.slint#L682) |
|
||||
| FR-UI-4 | [`ui/dr-ui/src/collections_ui.rs:1005`](../ui/dr-ui/src/collections_ui.rs#L1005), [`ui/dr-ui/src/collections_ui.rs:118`](../ui/dr-ui/src/collections_ui.rs#L118), [`ui/dr-ui/src/collections_ui.rs:131`](../ui/dr-ui/src/collections_ui.rs#L131), [`ui/dr-ui/src/collections_ui.rs:1508`](../ui/dr-ui/src/collections_ui.rs#L1508), [`ui/dr-ui/src/collections_ui.rs:1522`](../ui/dr-ui/src/collections_ui.rs#L1522), [`ui/dr-ui/src/collections_ui.rs:1568`](../ui/dr-ui/src/collections_ui.rs#L1568), [`ui/dr-ui/src/collections_ui.rs:159`](../ui/dr-ui/src/collections_ui.rs#L159), [`ui/dr-ui/src/collections_ui.rs:1666`](../ui/dr-ui/src/collections_ui.rs#L1666), [`ui/dr-ui/src/collections_ui.rs:1693`](../ui/dr-ui/src/collections_ui.rs#L1693), [`ui/dr-ui/src/collections_ui.rs:485`](../ui/dr-ui/src/collections_ui.rs#L485), [`ui/dr-ui/src/collections_ui.rs:514`](../ui/dr-ui/src/collections_ui.rs#L514), [`ui/dr-ui/src/collections_ui.rs:582`](../ui/dr-ui/src/collections_ui.rs#L582), [`ui/dr-ui/src/collections_ui.rs:995`](../ui/dr-ui/src/collections_ui.rs#L995), [`ui/dr-ui/src/library_ui.rs:4364`](../ui/dr-ui/src/library_ui.rs#L4364), [`ui/dr-ui/src/library_ui.rs:4409`](../ui/dr-ui/src/library_ui.rs#L4409), [`ui/dr-ui/src/library_ui.rs:4512`](../ui/dr-ui/src/library_ui.rs#L4512), [`ui/dr-ui/src/library_ui.rs:4540`](../ui/dr-ui/src/library_ui.rs#L4540), [`ui/dr-ui/src/library_ui.rs:5252`](../ui/dr-ui/src/library_ui.rs#L5252), [`ui/dr-ui/src/library_ui.rs:5264`](../ui/dr-ui/src/library_ui.rs#L5264), [`ui/dr-ui/ui/app.slint:1601`](../ui/dr-ui/ui/app.slint#L1601), [`ui/dr-ui/ui/app.slint:582`](../ui/dr-ui/ui/app.slint#L582), [`ui/dr-ui/ui/app.slint:613`](../ui/dr-ui/ui/app.slint#L613), [`ui/dr-ui/ui/library.slint:1186`](../ui/dr-ui/ui/library.slint#L1186), [`ui/dr-ui/ui/library.slint:1193`](../ui/dr-ui/ui/library.slint#L1193), [`ui/dr-ui/ui/library.slint:1199`](../ui/dr-ui/ui/library.slint#L1199), [`ui/dr-ui/ui/library.slint:2266`](../ui/dr-ui/ui/library.slint#L2266), [`ui/dr-ui/ui/library.slint:823`](../ui/dr-ui/ui/library.slint#L823), [`ui/dr-ui/ui/library.slint:872`](../ui/dr-ui/ui/library.slint#L872), [`ui/dr-ui/ui/library.slint:891`](../ui/dr-ui/ui/library.slint#L891) |
|
||||
| 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:2024`](../ui/dr-ui/src/lib.rs#L2024), [`ui/dr-ui/src/lib.rs:2413`](../ui/dr-ui/src/lib.rs#L2413), [`ui/dr-ui/src/lib.rs:2598`](../ui/dr-ui/src/lib.rs#L2598), [`ui/dr-ui/src/masks_ui.rs:863`](../ui/dr-ui/src/masks_ui.rs#L863), [`ui/dr-ui/ui/app.slint:297`](../ui/dr-ui/ui/app.slint#L297), [`ui/dr-ui/ui/collections.slint:4`](../ui/dr-ui/ui/collections.slint#L4) |
|
||||
| FR-UI-7 | [`core/dr-pipeline/src/descriptor.rs:100`](../core/dr-pipeline/src/descriptor.rs#L100), [`core/dr-pipeline/src/framing.rs:259`](../core/dr-pipeline/src/framing.rs#L259) |
|
||||
| NFR-ARCH-2 | [`core/dr-catalog/src/jobs.rs:1`](../core/dr-catalog/src/jobs.rs#L1) |
|
||||
@@ -110,6 +110,7 @@ _None._
|
||||
| NFR-OPS-1 | [`tools/traceability/src/lib.rs:266`](../tools/traceability/src/lib.rs#L266) |
|
||||
| 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/traceability/src/lib.rs:479`](../tools/traceability/src/lib.rs#L479) |
|
||||
| NFR-P13 | [`core/dr-decode/src/preview.rs:134`](../core/dr-decode/src/preview.rs#L134) |
|
||||
| NFR-P5 | [`core/dr-catalog/src/schema.rs:292`](../core/dr-catalog/src/schema.rs#L292), [`ui/dr-ui/src/library.rs:3125`](../ui/dr-ui/src/library.rs#L3125), [`ui/dr-ui/src/library.rs:3898`](../ui/dr-ui/src/library.rs#L3898), [`ui/dr-ui/src/library_ui.rs:4049`](../ui/dr-ui/src/library_ui.rs#L4049), [`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:941`](../ui/dr-ui/src/export.rs#L941), [`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:269`](../core/dr-types/src/lib.rs#L269), [`core/dr-types/src/lib.rs:302`](../core/dr-types/src/lib.rs#L302) |
|
||||
| NFR-R1 | [`core/dr-catalog/src/sync.rs:1`](../core/dr-catalog/src/sync.rs#L1), [`ui/dr-ui/src/library.rs:930`](../ui/dr-ui/src/library.rs#L930) |
|
||||
@@ -118,14 +119,14 @@ _None._
|
||||
| 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-pipeline/src/history.rs:55`](../core/dr-pipeline/src/history.rs#L55), [`ui/dr-ui/src/lib.rs:59`](../ui/dr-ui/src/lib.rs#L59) |
|
||||
| NFR-RES-4 | [`core/dr-catalog/src/cache.rs:1`](../core/dr-catalog/src/cache.rs#L1), [`core/dr-catalog/src/schema.rs:337`](../core/dr-catalog/src/schema.rs#L337), [`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:2581`](../ui/dr-ui/src/library.rs#L2581) |
|
||||
| NFR-RES-4 | [`core/dr-catalog/src/cache.rs:1`](../core/dr-catalog/src/cache.rs#L1), [`core/dr-catalog/src/schema.rs:394`](../core/dr-catalog/src/schema.rs#L394), [`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:2581`](../ui/dr-ui/src/library.rs#L2581) |
|
||||
| NFR-SEC-1 | [`core/dr-decode/src/error.rs:1`](../core/dr-decode/src/error.rs#L1) |
|
||||
| R1 | [`tools/traceability/src/lib.rs:495`](../tools/traceability/src/lib.rs#L495), [`tools/traceability/src/lib.rs:499`](../tools/traceability/src/lib.rs#L499) |
|
||||
| R4 | [`core/dr-gpu/src/lib.rs:217`](../core/dr-gpu/src/lib.rs#L217) |
|
||||
|
||||
## Not yet tagged
|
||||
|
||||
88 of 177 requirements have no implementation tag. Expected while the codebase is young; each should gain one as it is built.
|
||||
87 of 177 requirements have no implementation tag. Expected while the codebase is young; each should gain one as it is built.
|
||||
|
||||
<details><summary>Show untagged requirements</summary>
|
||||
|
||||
@@ -197,7 +198,6 @@ _None._
|
||||
- NFR-P2
|
||||
- NFR-P3
|
||||
- NFR-P4
|
||||
- NFR-P5
|
||||
- NFR-P6
|
||||
- NFR-P7
|
||||
- NFR-P8
|
||||
|
||||
+168
-35
@@ -2936,10 +2936,8 @@ pub fn read_cells_scoped(
|
||||
.join(",");
|
||||
let rated = filter.sql();
|
||||
let sql = format!(
|
||||
"SELECT i.id, i.source_ref, r.file_id, i.file_size,
|
||||
i.metadata_state, i.captured_at
|
||||
"SELECT {CELL_COLUMNS}
|
||||
FROM images i
|
||||
LEFT JOIN remote r ON r.image_id = i.id
|
||||
WHERE {VISIBLE}{rated}
|
||||
AND i.id IN (SELECT image_id FROM collection_members
|
||||
WHERE collection_id IN ({placeholders}))
|
||||
@@ -2954,10 +2952,14 @@ pub fn read_cells_scoped(
|
||||
params.push(rusqlite::types::Value::Integer(limit as i64));
|
||||
params.push(rusqlite::types::Value::Integer(offset as i64));
|
||||
|
||||
let mut stmt = catalog.connection().prepare(&sql)?;
|
||||
let rows = stmt
|
||||
.query_map(rusqlite::params_from_iter(params.iter()), row_to_cell)?
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
let mut rows = {
|
||||
let mut stmt = catalog.connection().prepare(&sql)?;
|
||||
let read = stmt
|
||||
.query_map(rusqlite::params_from_iter(params.iter()), row_to_cell)?
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
read
|
||||
};
|
||||
attach_file_ids(catalog, &mut rows);
|
||||
Ok(rows)
|
||||
}
|
||||
|
||||
@@ -2968,18 +2970,20 @@ fn read_cells_all(
|
||||
limit: usize,
|
||||
) -> Result<Vec<LibraryCell>, dr_catalog::CatalogError> {
|
||||
let rated = filter.sql();
|
||||
let mut stmt = catalog.connection().prepare(&format!(
|
||||
"SELECT i.id, i.source_ref, r.file_id, i.file_size,
|
||||
i.metadata_state, i.captured_at
|
||||
FROM images i
|
||||
LEFT JOIN remote r ON r.image_id = i.id
|
||||
WHERE {VISIBLE}{rated}
|
||||
{GRID_ORDER}
|
||||
LIMIT ?1 OFFSET ?2"
|
||||
))?;
|
||||
let rows = stmt
|
||||
.query_map(rusqlite::params![limit as i64, offset as i64], row_to_cell)?
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
let mut rows = {
|
||||
let mut stmt = catalog.connection().prepare(&format!(
|
||||
"SELECT {CELL_COLUMNS}
|
||||
FROM images i
|
||||
WHERE {VISIBLE}{rated}
|
||||
{GRID_ORDER}
|
||||
LIMIT ?1 OFFSET ?2"
|
||||
))?;
|
||||
let read = stmt
|
||||
.query_map(rusqlite::params![limit as i64, offset as i64], row_to_cell)?
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
read
|
||||
};
|
||||
attach_file_ids(catalog, &mut rows);
|
||||
Ok(rows)
|
||||
}
|
||||
|
||||
@@ -3000,18 +3004,20 @@ pub fn read_trashed_cells(
|
||||
offset: usize,
|
||||
limit: usize,
|
||||
) -> Result<Vec<LibraryCell>, dr_catalog::CatalogError> {
|
||||
let mut stmt = catalog.connection().prepare(&format!(
|
||||
"SELECT i.id, i.source_ref, r.file_id, i.file_size,
|
||||
i.metadata_state, i.captured_at
|
||||
FROM images i
|
||||
LEFT JOIN remote r ON r.image_id = i.id
|
||||
WHERE {TRASHED}
|
||||
{TRASH_ORDER}
|
||||
LIMIT ?1 OFFSET ?2"
|
||||
))?;
|
||||
let rows = stmt
|
||||
.query_map(rusqlite::params![limit as i64, offset as i64], row_to_cell)?
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
let mut rows = {
|
||||
let mut stmt = catalog.connection().prepare(&format!(
|
||||
"SELECT {CELL_COLUMNS}
|
||||
FROM images i
|
||||
WHERE {TRASHED}
|
||||
{TRASH_ORDER}
|
||||
LIMIT ?1 OFFSET ?2"
|
||||
))?;
|
||||
let read = stmt
|
||||
.query_map(rusqlite::params![limit as i64, offset as i64], row_to_cell)?
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
read
|
||||
};
|
||||
attach_file_ids(catalog, &mut rows);
|
||||
Ok(rows)
|
||||
}
|
||||
|
||||
@@ -3091,20 +3097,90 @@ pub fn read_ids_span(
|
||||
Ok(ids)
|
||||
}
|
||||
|
||||
/// The columns every windowed read selects, in the order [`row_to_cell`] reads
|
||||
/// them.
|
||||
///
|
||||
/// Named rather than repeated so the three readers cannot drift — and so that
|
||||
/// the one column that is *not* here stays conspicuous. See
|
||||
/// [`attach_file_ids`] for why the server's file id is fetched separately.
|
||||
const CELL_COLUMNS: &str =
|
||||
"i.id, i.source_ref, i.file_size, i.metadata_state, i.captured_at";
|
||||
|
||||
/// Shared row mapping, so the scoped and unscoped queries cannot drift.
|
||||
///
|
||||
/// `file_id` is left empty here and filled by [`attach_file_ids`].
|
||||
fn row_to_cell(r: &rusqlite::Row) -> rusqlite::Result<LibraryCell> {
|
||||
let path: String = r.get(1)?;
|
||||
Ok(LibraryCell {
|
||||
image_id: r.get(0)?,
|
||||
name: path.rsplit(['/', ':']).next().unwrap_or(&path).to_string(),
|
||||
remote_path: path,
|
||||
file_id: r.get::<_, Option<i64>>(2)?.map(|v| v as u64),
|
||||
size: r.get::<_, Option<i64>>(3)?.unwrap_or(0) as u64,
|
||||
metadata_state: r.get::<_, i64>(4)? as u8,
|
||||
captured_at: r.get(5)?,
|
||||
file_id: None,
|
||||
size: r.get::<_, Option<i64>>(2)?.unwrap_or(0) as u64,
|
||||
metadata_state: r.get::<_, i64>(3)? as u8,
|
||||
captured_at: r.get(4)?,
|
||||
})
|
||||
}
|
||||
|
||||
/// TRACES: NFR-P5
|
||||
/// Fill in each cell's server file id, in one query for the whole window.
|
||||
///
|
||||
/// # Why this is not a `LEFT JOIN` any more
|
||||
///
|
||||
/// It was, and it was the single most expensive thing the grid did while a
|
||||
/// finger was on it. A window is `ORDER BY ... LIMIT n OFFSET k`, and SQLite
|
||||
/// answers a join like that by joining *first* and paging after — so reading
|
||||
/// 280 cells at offset 20,000 meant an index seek into `remote` for all 24,000
|
||||
/// rows, 23,720 of which were then discarded. Measured at 15.2 ms, inside the
|
||||
/// scroll handler, against a 16.7 ms frame.
|
||||
///
|
||||
/// Paging over `images` alone is 0.36 ms with `images_grid_order` (schema V7),
|
||||
/// and this fetches the ids for the 280 rows that survived. The same shape the
|
||||
/// badge and rating reads already use: one query for the window, never one per
|
||||
/// cell.
|
||||
///
|
||||
/// Silent on failure, and cells keep `file_id: None`: that is the same state a
|
||||
/// photograph the scan has not reached the server for is in, and the callers
|
||||
/// already treat it as "no cached thumbnail to key on" rather than an error.
|
||||
fn attach_file_ids(catalog: &Catalog, cells: &mut [LibraryCell]) {
|
||||
if cells.is_empty() {
|
||||
return;
|
||||
}
|
||||
let placeholders = std::iter::repeat_n("?", cells.len())
|
||||
.collect::<Vec<_>>()
|
||||
.join(",");
|
||||
let sql = format!("SELECT image_id, file_id FROM remote WHERE image_id IN ({placeholders})");
|
||||
let params: Vec<rusqlite::types::Value> = cells
|
||||
.iter()
|
||||
.map(|c| rusqlite::types::Value::Integer(c.image_id))
|
||||
.collect();
|
||||
|
||||
let mut stmt = match catalog.connection().prepare(&sql) {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
log::debug!("reading file ids for the window: {e}");
|
||||
return;
|
||||
}
|
||||
};
|
||||
let rows = stmt.query_map(rusqlite::params_from_iter(params.iter()), |r| {
|
||||
Ok((r.get::<_, i64>(0)?, r.get::<_, Option<i64>>(1)?))
|
||||
});
|
||||
let found: std::collections::HashMap<i64, Option<i64>> = match rows {
|
||||
Ok(rows) => rows.flatten().collect(),
|
||||
Err(e) => {
|
||||
log::debug!("reading file ids for the window: {e}");
|
||||
return;
|
||||
}
|
||||
};
|
||||
for cell in cells {
|
||||
cell.file_id = found
|
||||
.get(&cell.image_id)
|
||||
.copied()
|
||||
.flatten()
|
||||
.map(|v| v as u64);
|
||||
}
|
||||
}
|
||||
|
||||
/// Total images in the catalog, or in one collection and its descendants.
|
||||
///
|
||||
/// Counts exactly what [`read_cells_scoped`] would list, filter included. The
|
||||
@@ -3819,6 +3895,63 @@ mod tests {
|
||||
assert_eq!(page[0].name, "img010.CR2");
|
||||
}
|
||||
|
||||
/// TRACES: NFR-P5
|
||||
/// The grid's window read must be answered by walking `images_grid_order`,
|
||||
/// never by sorting the library into a temp b-tree.
|
||||
///
|
||||
/// This asserts on the *query plan* rather than on a duration, because the
|
||||
/// failure has no other symptom: a `GRID_ORDER` edited out of step with the
|
||||
/// index in schema V7, or a column added back into the paging query that
|
||||
/// drags `remote` in again, both still return the right cells. They just
|
||||
/// return them after sorting 24,000 rows, inside the scroll handler — which
|
||||
/// is the jitter this pair was introduced to remove, and it would come back
|
||||
/// silently.
|
||||
#[test]
|
||||
fn the_window_read_walks_the_ordering_index() {
|
||||
let catalog = with_images(20);
|
||||
let plan: Vec<String> = catalog
|
||||
.connection()
|
||||
.prepare(&format!(
|
||||
"EXPLAIN QUERY PLAN
|
||||
SELECT {CELL_COLUMNS} FROM images i
|
||||
WHERE {VISIBLE}
|
||||
{GRID_ORDER}
|
||||
LIMIT 10 OFFSET 5"
|
||||
))
|
||||
.unwrap()
|
||||
.query_map([], |r| r.get::<_, String>(3))
|
||||
.unwrap()
|
||||
.flatten()
|
||||
.collect();
|
||||
let plan = plan.join(" | ");
|
||||
|
||||
assert!(
|
||||
plan.contains("images_grid_order"),
|
||||
"the window read is not using the ordering index: {plan}"
|
||||
);
|
||||
assert!(
|
||||
!plan.contains("TEMP B-TREE"),
|
||||
"the window read is still sorting the whole library: {plan}"
|
||||
);
|
||||
assert!(
|
||||
!plan.to_lowercase().contains("remote"),
|
||||
"the window read is joining `remote` again, which pages the whole \
|
||||
library before it discards it: {plan}"
|
||||
);
|
||||
}
|
||||
|
||||
/// The file ids still arrive, now that they come from a second query.
|
||||
#[test]
|
||||
fn a_window_still_carries_the_server_file_ids() {
|
||||
let catalog = with_images(20);
|
||||
let page = read_cells(&catalog, 5, 4).unwrap();
|
||||
assert_eq!(page.len(), 4);
|
||||
assert!(
|
||||
page.iter().all(|c| c.file_id.is_some()),
|
||||
"a cell lost its file id when the join was split out"
|
||||
);
|
||||
}
|
||||
|
||||
/// A catalog with `n` images, ready to file into collections.
|
||||
fn with_images(n: usize) -> Catalog {
|
||||
let catalog = Catalog::in_memory().unwrap();
|
||||
|
||||
Reference in New Issue
Block a user