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: {
 | 
			
		||||
@ -362,7 +363,9 @@ class GraphManager {
 | 
			
		||||
            // Update existing data
 | 
			
		||||
            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,42 +994,74 @@ 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);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
        const hiddenEdgeTypes = new Set();
 | 
			
		||||
        this.filterPanel.querySelectorAll('input[data-filter-type="edge"]:not(:checked)').forEach(cb => {
 | 
			
		||||
            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 });
 | 
			
		||||
        });
 | 
			
		||||
    
 | 
			
		||||
        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)
 | 
			
		||||
        }));
 | 
			
		||||
        
 | 
			
		||||
        this.edges.update(edgeUpdates);
 | 
			
		||||
        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