chore: regenerate bindings and the traceability matrix

bindings.ts picks up the library-exclusion commands and types from tauri-specta.
The matrix regenerates because validation.ts and its test are gone — the doc
link checker caught the stale references, which is the first time that gate has
paid for itself on a generated artifact rather than a hand-written link.

Also drops exclusions::is_excluded: a wrapper over is_excluded_by that only a
test called, while the trait impls hoist the snapshot themselves. The test now
calls the same path production does.
This commit is contained in:
2026-08-20 20:14:15 +02:00
parent 0815445aa7
commit 68ca1d585d
4 changed files with 6489 additions and 5857 deletions
+6392 -5848
View File
File diff suppressed because it is too large Load Diff
+4 -1
View File
@@ -279,7 +279,10 @@ mod tests {
};
let json = serde_json::to_string(&settings).expect("serialises");
assert!(json.contains("\"excludedItemIds\""), "camelCase on the wire");
assert!(
json.contains("\"excludedItemIds\""),
"camelCase on the wire"
);
let parsed: LibrarySettings = serde_json::from_str(&json).expect("parses back");
assert_eq!(parsed, settings);
+1 -8
View File
@@ -113,13 +113,6 @@ pub fn is_excluded_by(excluded: &HashSet<String>, item: &MediaItem) -> bool {
}
}
/// Whether the currently-configured exclusions hide `item`.
///
/// TRACES: UR-076 | DR-209
pub fn is_excluded(item: &MediaItem) -> bool {
is_excluded_by(&excluded_snapshot(), item)
}
/// Drop the user's hidden items from a repository result.
///
/// Implemented as a trait so the hybrid repository's generic result helpers —
@@ -326,7 +319,7 @@ mod tests {
let _guard = EXCLUSION_TEST_LOCK.lock_safe();
set_excluded_item_ids(&["hidden".to_string()]);
let still_matches = is_excluded(&item("hidden"));
let still_matches = is_excluded_by(&excluded_snapshot(), &item("hidden"));
let survives = item("hidden").without_excluded();
set_excluded_item_ids(&[]);
+92
View File
@@ -1079,6 +1079,48 @@ async catalogSyncStatus() : Promise<CatalogSyncStatus> {
async setShowServerCatalog(show: boolean) : Promise<void> {
await TAURI_INVOKE("set_show_server_catalog", { show });
},
/**
* The library preferences currently in force.
*
* Read from the in-memory exclusion set rather than the database: that set is
* what queries actually consult, so reading it is the only answer that cannot
* disagree with what the user is seeing.
*
* TRACES: UR-076 | DR-209
*/
async libraryGetSettings() : Promise<LibrarySettings> {
return await TAURI_INVOKE("library_get_settings");
},
/**
* Replace the library preferences: apply them to every subsequent query and
* persist them.
*
* Returns the sanitised value actually applied, so the picker shows what was
* stored rather than what it sent.
*
* TRACES: UR-076 | DR-209
*/
async librarySetSettings(settings: LibrarySettings) : Promise<LibrarySettings> {
return await TAURI_INVOKE("library_set_settings", { settings });
},
/**
* The folders the user may choose to hide.
*
* Offers each music library and the folders directly inside it. Music is the
* only scope offered because it is the one where a foreign folder — podcasts,
* audiobooks, sound effects — routinely shares a library with the media the
* user actually browses; the scope is decided here rather than in the UI so the
* collection-type table stays out of the frontend
* (see `SearchScope::for_collection_type`).
*
* Reads through `HybridRepository::get_items_unfiltered` so folders that are
* *already* hidden still appear — otherwise the setting could never be undone.
*
* TRACES: UR-076 | DR-209
*/
async libraryGetExclusionCandidates(handle: string) : Promise<ExclusionCandidate[]> {
return await TAURI_INVOKE("library_get_exclusion_candidates", { handle });
},
/**
* Resolve the stream URL for every download row that was queued while offline
* (`status = 'pending' AND stream_url IS NULL`), then pump the queue so they
@@ -2080,6 +2122,34 @@ remaining: number }
* TRACES: UR-027 | DR-030
*/
export type EqPreset = "flat" | "rock" | "pop" | "jazz" | "classical" | "bassBoost" | "trebleBoost" | "vocal"
/**
* Something the user may choose to hide: a library, or a folder directly
* inside one.
*
* Which containers are *offerable* is a domain question (it depends on the
* library's Jellyfin collection type and on what counts as a folder), so the
* list is assembled here and the frontend renders it verbatim.
*
* TRACES: UR-076 | DR-209
*/
export type ExclusionCandidate = {
/**
* Stable Jellyfin item id — what gets stored when the user picks it.
*/
id: string;
/**
* Display name of the folder (or of the library, for a whole-library entry).
*/
name: string;
/**
* Library this candidate lives in, so the picker can group and disambiguate
* two folders that share a name.
*/
libraryName: string;
/**
* True when the candidate *is* a library rather than a folder inside one.
*/
isLibrary: boolean }
/**
* Genre
*/
@@ -2137,6 +2207,28 @@ export type Library = { id: string; name: string; collectionType: string; imageT
* TRACES: UR-075 | DR-175
*/
favoritesScope?: SearchScope | null }
/**
* Library browsing preferences.
*
* Currently a single list: the folders (or whole libraries) the user has asked
* to keep out of browsing. It is a *list of ids*, never names — names are
* unstable, locale-dependent and non-unique, and the hardcoded name filter this
* setting replaced broke on exactly that. What the ids then hide is decided in
* `repository::exclusions`; this struct is only how the choice is carried and
* persisted.
*
* The default is an empty list: nobody inherits another user's folder layout.
*
* TRACES: UR-076 | DR-209
*/
export type LibrarySettings = {
/**
* Stable item ids of the folders/libraries hidden from browsing.
*
* `#[serde(default)]` so settings JSON persisted before this field existed
* loads as the previous behaviour (nothing hidden).
*/
excludedItemIds?: string[] }
/**
* Live stream information returned from opening a Live TV / channel stream.
*