feat(web): multi-select relationship-type + ontology filters#369
Conversation
Task #20. The shared store already declared filters.relationshipTypes and filters.ontologies (string[], default []); this wires the UI and filter application, mirroring the existing universal minConfidence pattern. - graphStore: add filterOptions {relationshipTypes, ontologies} + setter — the *available* values (distinct from filters.* which are *selected*). Ephemeral, not persisted (partialize unchanged). - ForceGraph: publish distinct e.type / n.category from the engine data (the SAME strings the filter compares against — deriving from raw API data would mismatch, since the raw->engine transform maps empty ontology to 'Unknown'). filteredData now also narrows by relationship type (edges) and ontology (nodes, dropping orphaned edges). Empty = show all, matching convention. - SettingsPanel: two checkbox-lists in the Filters section, options from store filterOptions, selection via setFilters — same shared-store, universal-across-explorers model as minConfidence. Scoped to Force Graph + universal store per the task; Document Explorer parity (separate data path) deferred. No select-all/none control (not requested). Pre-existing eslint debt in the touched files (unused RawGraphNode/RawGraphLink import, onNodeClick/mergeRawGraphData, one any) left as-is — out of scope, not introduced here, not CI-gated. Typecheck clean, 10 web tests pass, no new lint errors.
Bounded consolidation step (the pin system, colour schemes, Legend
reconciliation, and the raw-vs-grouped effector-model decision are
deferred — captured in a design note for a future build-and-learn
session, not an ADR; pre-build UX ADRs ossify into contrarian
artifacts since feel is only known once built).
- graphStore: filterOptions entries are now FilterOption {value,color}
instead of bare strings.
- ForceGraph: publishes each option's rendered colour from the SAME
palette the scene uses — relationship type -> category colour
(mirrors the edgeColors 'type' branch), ontology -> palette(ontology)
— so swatch == on-screen colour.
- SettingsPanel: each row shows its colour swatch (50 identical rows
were unreadable — explicit user constraint), plus per-list
all / none shortcuts. 'none' clears to [] = the store's documented
empty-means-show-all (true hide-all is part of the deferred
effector-model decision).
tsc clean, 10/10 web tests, no new eslint.
Swatch dot alone wasn't enough — the checkbox (accent-color) and the label text now also render in the value's rendered colour, matching how the Legend frame reads. tsc clean, 10/10 tests.
Code Review — PR #369What this changes: Wires UI + filter application for the already-declared Assessment: Solid, faithful to stated intent. Verified clean: Correctness — verified sound
Non-blocking suggestions (consider for #370 or a follow-up)
Quality / SOLID
Recommendation: merge-with-followupsWiring is correct and faithful to the documented intent; types/tests/lint all clean against baseline; deferrals are explicit and bounded. The two non-blocking items (re-render churn, missing memo test) are good candidates to fold into #370 or a quick follow-up but do not gate this merge. AI-assisted review via Claude Code (code-reviewer) |
What
Wires UI + filter application for the universal
relationshipTypes/ontologiesfilters (Task #20). The sharedgraphStorealready declared these fields (string[], default[]); this completes them, mirroring the existing universalminConfidencepattern.Changes
filterOptions {relationshipTypes, ontologies}+ setter — the available values (vsfilters.*= selected). Ephemeral, not persisted.e.type/n.categoryfrom the engine data (same strings the filter compares against — raw API data would mismatch since the raw→engine transform maps empty ontology to'Unknown').filteredDatanow narrows by relationship type (edges) and ontology (nodes, dropping orphaned edges). Empty = show all.filterOptions, selection viasetFilters— same shared-store, universal-across-explorers model asminConfidence.Scope
RawGraphNode/RawGraphLink,onNodeClick,mergeRawGraphData, oneany) left as-is — not introduced here, not CI-gated, out of scope.Verification
tsc --noEmitclean, 10/10 web tests pass, no new lint errors.