From fe63c7bc15a21e47102442be696d8271fefe6cd3 Mon Sep 17 00:00:00 2001 From: Giovanni Colapinto <4282925+njordr@users.noreply.github.com> Date: Mon, 14 Sep 2026 17:26:06 +0200 Subject: [PATCH] fix(viewer): make the graph view honour the active filters The list, card and timeline views all render from `state.filteredRecords`. The graph view read `state.graph` instead, which the generator builds once over the whole collection, so narrowing by status, category, project, author or tag left the graph drawing every record. The header said "2 of 3 records" above a canvas showing three. Filter the graph against the visible records before laying it out. Placeholder nodes, which stand for references to records outside the collection and carry no facets of their own, survive while the record pointing at them does. An empty graph now distinguishes "nothing matches the filters" from "nothing is related". --- CHANGELOG.md | 7 ++++++ templates/app.js | 63 +++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 64 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e7fc7ce..b3bee53 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Fixed + +- **[Viewer]**: The graph view now honours the active filters + - It drew every record and every edge regardless of the facets, contradicting the "N of M records" count above it + - A placeholder node stays while the record referencing it survives the filter + - An empty result now says "No ADRs match the current filters" instead of "No relationships to display" + ### Added - **[Docs]**: Add comprehensive dependencies reference documentation diff --git a/templates/app.js b/templates/app.js index c3a321f..0c360ee 100644 --- a/templates/app.js +++ b/templates/app.js @@ -736,6 +736,49 @@ }); } + // The graph is built once over every record, while the facets narrow the list. Without this + // the graph keeps drawing the whole estate after a filter, contradicting the record count. + function filteredGraph() { + var visible = {}; + state.filteredRecords.forEach(function(adr) { + visible[adr.id] = true; + }); + + var placeholder = {}; + state.graph.nodes.forEach(function(node) { + if (node.title === undefined) { + placeholder[node.id] = true; + } + }); + + // A placeholder stands for a reference to a record outside the collection, so it has no + // facets of its own. Keep it while the record pointing at it survives the filter. + var edges = state.graph.edges.filter(function(edge) { + var source = visible[edge.source] === true; + var target = visible[edge.target] === true; + + return (source && target) + || (source && placeholder[edge.target] === true) + || (target && placeholder[edge.source] === true); + }); + + var keep = {}; + Object.keys(visible).forEach(function(id) { + keep[id] = true; + }); + edges.forEach(function(edge) { + keep[edge.source] = true; + keep[edge.target] = true; + }); + + return { + nodes: state.graph.nodes.filter(function(node) { + return keep[node.id] === true; + }), + edges: edges + }; + } + function renderGraphView() { var canvas = elements.graphCanvas; var ctx = canvas.getContext('2d'); @@ -746,21 +789,29 @@ ctx.clearRect(0, 0, canvas.width, canvas.height); - if (state.graph.nodes.length === 0) { + var graph = filteredGraph(); + + if (graph.nodes.length === 0) { ctx.fillStyle = getComputedStyle(document.documentElement).getPropertyValue('--color-text-muted'); ctx.font = '14px sans-serif'; ctx.textAlign = 'center'; - ctx.fillText('No relationships to display', canvas.width / 2, canvas.height / 2); + ctx.fillText( + state.filteredRecords.length === 0 + ? 'No ADRs match the current filters' + : 'No relationships to display', + canvas.width / 2, + canvas.height / 2 + ); return; } // Simple force-directed layout simulation - var nodes = state.graph.nodes.map(function(n, i) { + var nodes = graph.nodes.map(function(n, i) { return { id: n.id, status: n.status, - x: canvas.width / 2 + Math.cos(i * 2 * Math.PI / state.graph.nodes.length) * 200, - y: canvas.height / 2 + Math.sin(i * 2 * Math.PI / state.graph.nodes.length) * 200, + x: canvas.width / 2 + Math.cos(i * 2 * Math.PI / graph.nodes.length) * 200, + y: canvas.height / 2 + Math.sin(i * 2 * Math.PI / graph.nodes.length) * 200, radius: 20 }; }); @@ -772,7 +823,7 @@ ctx.strokeStyle = getComputedStyle(document.documentElement).getPropertyValue('--color-border'); ctx.lineWidth = 1; - state.graph.edges.forEach(function(edge) { + graph.edges.forEach(function(edge) { var source = nodeMap[edge.source]; var target = nodeMap[edge.target]; if (source && target) {