visual enhancements
This commit is contained in:
parent
62470673fe
commit
2410e689b8
@ -373,8 +373,8 @@ input[type="text"]:focus, select:focus {
|
||||
padding: 0.75rem;
|
||||
font-family: 'Roboto Mono', monospace;
|
||||
font-size: 0.8rem;
|
||||
max-height: 40%;
|
||||
overflow-y: auto;
|
||||
/*max-height: 40%;
|
||||
overflow-y: auto;*/
|
||||
display: flex;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ class GraphManager {
|
||||
this.contextMenu = null;
|
||||
this.history = [];
|
||||
this.filterPanel = null;
|
||||
this.trueRootIds = new Set();
|
||||
|
||||
this.options = {
|
||||
nodes: {
|
||||
@ -363,6 +364,8 @@ class GraphManager {
|
||||
this.nodes.update(processedNodes);
|
||||
this.edges.update(processedEdges);
|
||||
|
||||
// After data is loaded, compute roots and apply filters
|
||||
this.computeTrueRoots();
|
||||
this.updateFilterControls();
|
||||
this.applyAllFilters();
|
||||
|
||||
@ -935,27 +938,55 @@ class GraphManager {
|
||||
};
|
||||
}
|
||||
|
||||
computeTrueRoots() {
|
||||
this.trueRootIds.clear();
|
||||
const allNodes = this.nodes.get({ returnType: 'Object' });
|
||||
const allEdges = this.edges.get();
|
||||
const inDegrees = {};
|
||||
|
||||
for (const nodeId in allNodes) {
|
||||
inDegrees[nodeId] = 0;
|
||||
}
|
||||
allEdges.forEach(edge => {
|
||||
if (inDegrees[edge.to] !== undefined) {
|
||||
inDegrees[edge.to]++;
|
||||
}
|
||||
});
|
||||
|
||||
for (const nodeId in allNodes) {
|
||||
if (inDegrees[nodeId] === 0) {
|
||||
this.trueRootIds.add(nodeId);
|
||||
}
|
||||
}
|
||||
console.log("Computed true roots:", this.trueRootIds);
|
||||
}
|
||||
|
||||
updateFilterControls() {
|
||||
if (!this.filterPanel) return;
|
||||
|
||||
const nodeTypes = new Set(this.nodes.get().map(n => n.type));
|
||||
const edgeTypes = new Set(this.edges.get().map(e => e.metadata.relationship_type));
|
||||
|
||||
let nodeCheckboxes = '<div class="filter-column"><h4>Nodes</h4><div class="checkbox-group">';
|
||||
// Wrap both columns in a single container with vertical layout
|
||||
let filterHTML = '<div class="filter-container">';
|
||||
|
||||
// Nodes section
|
||||
filterHTML += '<div class="filter-column"><h4>Nodes</h4><div class="checkbox-group">';
|
||||
nodeTypes.forEach(type => {
|
||||
const label = type === 'correlation_object' ? 'latent correlations' : type;
|
||||
const isChecked = type !== 'correlation_object';
|
||||
nodeCheckboxes += `<label><input type="checkbox" data-filter-type="node" value="${type}" ${isChecked ? 'checked' : ''}> ${label}</label>`;
|
||||
filterHTML += `<label><input type="checkbox" data-filter-type="node" value="${type}" ${isChecked ? 'checked' : ''}> ${label}</label>`;
|
||||
});
|
||||
nodeCheckboxes += '</div></div>';
|
||||
filterHTML += '</div></div>';
|
||||
|
||||
let edgeCheckboxes = '<div class="filter-column"><h4>Edges</h4><div class="checkbox-group">';
|
||||
// Edges section
|
||||
filterHTML += '<div class="filter-column"><h4>Edges</h4><div class="checkbox-group">';
|
||||
edgeTypes.forEach(type => {
|
||||
edgeCheckboxes += `<label><input type="checkbox" data-filter-type="edge" value="${type}" checked> ${type}</label>`;
|
||||
filterHTML += `<label><input type="checkbox" data-filter-type="edge" value="${type}" checked> ${type}</label>`;
|
||||
});
|
||||
edgeCheckboxes += '</div></div>';
|
||||
filterHTML += '</div></div>';
|
||||
|
||||
this.filterPanel.innerHTML = nodeCheckboxes + edgeCheckboxes;
|
||||
filterHTML += '</div>'; // Close filter-container
|
||||
this.filterPanel.innerHTML = filterHTML;
|
||||
|
||||
this.filterPanel.querySelectorAll('input[type="checkbox"]').forEach(checkbox => {
|
||||
checkbox.addEventListener('change', () => this.applyAllFilters());
|
||||
@ -963,6 +994,10 @@ class GraphManager {
|
||||
}
|
||||
|
||||
applyAllFilters() {
|
||||
console.log("Applying all filters (robust orphan detection)...");
|
||||
if (this.nodes.length === 0) return;
|
||||
|
||||
// 1. Get filter criteria from the UI
|
||||
const hiddenNodeTypes = new Set();
|
||||
this.filterPanel.querySelectorAll('input[data-filter-type="node"]:not(:checked)').forEach(cb => {
|
||||
hiddenNodeTypes.add(cb.value);
|
||||
@ -973,32 +1008,60 @@ class GraphManager {
|
||||
hiddenEdgeTypes.add(cb.value);
|
||||
});
|
||||
|
||||
const nodeUpdates = [];
|
||||
const edgeUpdates = [];
|
||||
const visibleEdges = new Set();
|
||||
|
||||
this.edges.get().forEach(edge => {
|
||||
const isVisible = !hiddenEdgeTypes.has(edge.metadata.relationship_type);
|
||||
edgeUpdates.push({ id: edge.id, hidden: !isVisible });
|
||||
if (isVisible) {
|
||||
visibleEdges.add(edge.id);
|
||||
// 2. Build adjacency list for the visible part of the graph
|
||||
const adj = {};
|
||||
this.nodes.getIds().forEach(id => adj[id] = []);
|
||||
this.edges.forEach(edge => {
|
||||
if (!hiddenEdgeTypes.has(edge.metadata.relationship_type)) {
|
||||
adj[edge.from].push(edge.to);
|
||||
}
|
||||
});
|
||||
|
||||
this.nodes.get().forEach(node => {
|
||||
let isVisible = !hiddenNodeTypes.has(node.type);
|
||||
if (isVisible) {
|
||||
const connectedEdges = this.network.getConnectedEdges(node.id);
|
||||
const hasVisibleConnection = connectedEdges.some(edgeId => visibleEdges.has(edgeId));
|
||||
if (!hasVisibleConnection && connectedEdges.length > 0) {
|
||||
isVisible = false;
|
||||
// 3. Traverse from "true roots" to find all reachable nodes
|
||||
const reachableNodes = new Set();
|
||||
const queue = [];
|
||||
|
||||
// Start the traversal from true roots that aren't hidden by type
|
||||
this.trueRootIds.forEach(rootId => {
|
||||
const node = this.nodes.get(rootId);
|
||||
if (node && !hiddenNodeTypes.has(node.type)) {
|
||||
if (!reachableNodes.has(rootId)) {
|
||||
queue.push(rootId);
|
||||
reachableNodes.add(rootId);
|
||||
}
|
||||
}
|
||||
nodeUpdates.push({ id: node.id, hidden: !isVisible });
|
||||
});
|
||||
|
||||
this.edges.update(edgeUpdates);
|
||||
let head = 0;
|
||||
while (head < queue.length) {
|
||||
const u = queue[head++];
|
||||
|
||||
for (const v of (adj[u] || [])) {
|
||||
if (!reachableNodes.has(v)) {
|
||||
const node = this.nodes.get(v);
|
||||
if (node && !hiddenNodeTypes.has(node.type)) {
|
||||
reachableNodes.add(v);
|
||||
queue.push(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Create final node and edge visibility updates
|
||||
const nodeUpdates = this.nodes.map(node => ({
|
||||
id: node.id,
|
||||
hidden: !reachableNodes.has(node.id)
|
||||
}));
|
||||
|
||||
const edgeUpdates = this.edges.map(edge => ({
|
||||
id: edge.id,
|
||||
hidden: hiddenEdgeTypes.has(edge.metadata.relationship_type) || !reachableNodes.has(edge.from) || !reachableNodes.has(edge.to)
|
||||
}));
|
||||
|
||||
this.nodes.update(nodeUpdates);
|
||||
this.edges.update(edgeUpdates);
|
||||
|
||||
console.log(`Filters applied. Reachable nodes: ${reachableNodes.size}`);
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user