revert graph.js refactor

This commit is contained in:
overcuriousity 2025-09-15 18:06:11 +02:00
parent 2496ca26a5
commit 30ee21f087

View File

@ -1290,114 +1290,115 @@ class GraphManager {
}
/**
* Enhanced hide operation using true root detection
* @param {string} nodeId - The ID of the node to start hiding from
* Hide a node and recursively hide any neighbors that become disconnected.
* @param {string} nodeId - The ID of the node to start hiding from.
*/
hideNodeAndOrphans(nodeId) {
const historyData = { nodeIds: [] };
const queue = [nodeId];
const visited = new Set([nodeId]);
// Step 1: Hide the target node
this.nodes.update({ id: nodeId, hidden: true });
historyData.nodeIds.push(nodeId);
while (queue.length > 0) {
const currentId = queue.shift();
const node = this.nodes.get(currentId);
if (!node || node.hidden) continue;
// Step 2: Recompute what should remain visible using true root detection
const remainingVisible = this.computeReachableFromRoots();
// 1. Hide the current node and add to history
this.nodes.update({ id: currentId, hidden: true });
historyData.nodeIds.push(currentId);
// Step 3: Hide any nodes that are no longer reachable
const allNodes = this.nodes.get();
allNodes.forEach(node => {
if (!node.hidden && !remainingVisible.has(node.id) && node.id !== nodeId) {
this.nodes.update({ id: node.id, hidden: true });
historyData.nodeIds.push(node.id);
// 2. Check its neighbors
const neighbors = this.network.getConnectedNodes(currentId);
for (const neighborId of neighbors) {
if (visited.has(neighborId)) continue;
const connectedEdges = this.network.getConnectedEdges(neighborId);
let hasVisibleEdge = false;
// 3. See if the neighbor still has any visible connections
for (const edgeId of connectedEdges) {
const edge = this.edges.get(edgeId);
const sourceNode = this.nodes.get(edge.from);
const targetNode = this.nodes.get(edge.to);
if ((sourceNode && !sourceNode.hidden) && (targetNode && !targetNode.hidden)) {
hasVisibleEdge = true;
break;
}
}
// 4. If no visible connections, add to queue to be hidden
if (!hasVisibleEdge) {
queue.push(neighborId);
visited.add(neighborId);
}
}
}
});
if (historyData.nodeIds.length > 0) {
this.addToHistory('hide', historyData);
console.log(`Hidden ${historyData.nodeIds.length} nodes using true root detection`);
}
}
/**
* Enhanced delete operation using true root detection
* @param {string} nodeId - The ID of the node to start deleting from
* Delete a node and recursively delete any neighbors that become disconnected.
* @param {string} nodeId - The ID of the node to start deleting from.
*/
async deleteNodeAndOrphans(nodeId) {
const deletionQueue = [nodeId];
const processedForDeletion = new Set([nodeId]);
const historyData = { nodes: [], edges: [] };
let operationFailed = false;
// Step 1: Store current state and delete target node
const targetNode = this.nodes.get(nodeId);
const connectedEdgeIds = this.network.getConnectedEdges(nodeId);
const connectedEdges = this.edges.get(connectedEdgeIds);
while (deletionQueue.length > 0) {
const currentId = deletionQueue.shift();
const node = this.nodes.get(currentId);
if (!node) continue;
if (targetNode) {
historyData.nodes.push(targetNode);
historyData.edges.push(...connectedEdges);
}
const neighbors = this.network.getConnectedNodes(currentId);
const connectedEdgeIds = this.network.getConnectedEdges(currentId);
const edges = this.edges.get(connectedEdgeIds);
// Store state for potential revert
historyData.nodes.push(node);
historyData.edges.push(...edges);
try {
// Delete from backend first
const response = await fetch(`/api/graph/node/${nodeId}`, { method: 'DELETE' });
const response = await fetch(`/api/graph/node/${currentId}`, { method: 'DELETE' });
const result = await response.json();
if (!result.success) {
throw new Error(result.error || 'Failed to delete node from backend');
}
// Remove from frontend
this.nodes.remove({ id: nodeId });
console.log(`Node ${nodeId} deleted from backend and frontend`);
// Step 2: Identify orphaned nodes using true root detection
const reachableNodes = this.computeReachableFromRoots();
const allRemainingNodes = this.nodes.get().filter(node => node.id !== nodeId);
// Step 3: Delete orphaned nodes
const orphanedNodes = allRemainingNodes.filter(node => !reachableNodes.has(node.id));
for (const orphanNode of orphanedNodes) {
try {
const orphanEdgeIds = this.network.getConnectedEdges(orphanNode.id);
const orphanEdges = this.edges.get(orphanEdgeIds);
// Store for history
historyData.nodes.push(orphanNode);
historyData.edges.push(...orphanEdges);
// Delete from backend
const orphanResponse = await fetch(`/api/graph/node/${orphanNode.id}`, { method: 'DELETE' });
const orphanResult = await orphanResponse.json();
if (!orphanResult.success) {
console.warn(`Failed to delete orphaned node ${orphanNode.id}:`, orphanResult.error);
// Continue with other orphans rather than failing entirely
} else {
// Remove from frontend
this.nodes.remove({ id: orphanNode.id });
console.log(`Orphaned node ${orphanNode.id} deleted`);
}
} catch (error) {
console.error(`Error deleting orphaned node ${orphanNode.id}:`, error);
// Continue with other orphans
}
}
console.log(`Deleted ${orphanedNodes.length + 1} nodes using true root detection`);
} catch (error) {
console.error('Error during node deletion:', error);
console.error(`Failed to delete node ${currentId} from backend:`, result.error);
operationFailed = true;
break;
}
// Add to history only if primary deletion succeeded
console.log(`Node ${currentId} deleted from backend.`);
this.nodes.remove({ id: currentId }); // Remove from view
// Check if former neighbors are now orphans
neighbors.forEach(neighborId => {
if (!processedForDeletion.has(neighborId) && this.nodes.get(neighborId)) {
if (this.network.getConnectedEdges(neighborId).length === 0) {
deletionQueue.push(neighborId);
processedForDeletion.add(neighborId);
}
}
});
} catch (error) {
console.error('Error during node deletion API call:', error);
operationFailed = true;
break;
}
}
// Add to history only if the entire operation was successful
if (!operationFailed && historyData.nodes.length > 0) {
// Ensure edges in history are unique
historyData.edges = Array.from(new Map(historyData.edges.map(e => [e.id, e])).values());
this.addToHistory('delete', historyData);
} else if (operationFailed) {
console.log("Reverting UI changes due to failed delete operation");
// Restore the UI to original state
console.log("Reverting UI changes due to failed delete operation.");
// If any part of the chain failed, restore the UI to its original state
this.nodes.add(historyData.nodes);
this.edges.add(historyData.edges);
}