Files
ragflow/web/src/pages/agent/hooks/use-placeholder-manager.ts
balibabu 313e92dd9b Fix: Fixed the issue where the connection lines of placeholder nodes in the agent canvas could not be displayed #9869 (#10485)
### What problem does this PR solve?

Fix: Fixed the issue where the connection lines of placeholder nodes in
the agent canvas could not be displayed #9869

### Type of change

- [x] Bug Fix (non-breaking change which fixes an issue)
2025-10-11 15:24:27 +08:00

181 lines
5.4 KiB
TypeScript

import { useCallback, useRef } from 'react';
import { Operator } from '../constant';
import useGraphStore from '../store';
/**
* Placeholder node management Hook
* Responsible for managing placeholder node creation, deletion, and state tracking
*/
export const usePlaceholderManager = (reactFlowInstance: any) => {
// Reference to the created placeholder node ID
const createdPlaceholderRef = useRef<string | null>(null);
// Flag indicating whether user has selected a node
const userSelectedNodeRef = useRef(false);
/**
* Check if placeholder node exists and remove it if found
* Ensures only one placeholder can exist on the panel
*/
const checkAndRemoveExistingPlaceholder = useCallback(() => {
const { nodes, edges } = useGraphStore.getState();
// Find existing placeholder node
const existingPlaceholder = nodes.find(
(node) => node.data?.label === Operator.Placeholder,
);
if (existingPlaceholder && reactFlowInstance) {
// Remove edges related to placeholder
const edgesToRemove = edges.filter(
(edge) =>
edge.target === existingPlaceholder.id ||
edge.source === existingPlaceholder.id,
);
// Remove placeholder node
const nodesToRemove = [existingPlaceholder];
if (nodesToRemove.length > 0 || edgesToRemove.length > 0) {
reactFlowInstance.deleteElements({
nodes: nodesToRemove,
edges: edgesToRemove,
});
}
// Update ref reference
if (createdPlaceholderRef.current === existingPlaceholder.id) {
createdPlaceholderRef.current = null;
}
}
}, [reactFlowInstance]);
/**
* Function to remove placeholder node
* Called when user clicks blank area or cancels operation
*/
const removePlaceholderNode = useCallback(() => {
if (
createdPlaceholderRef.current &&
reactFlowInstance &&
!userSelectedNodeRef.current
) {
const { nodes, edges } = useGraphStore.getState();
// Remove edges related to placeholder
const edgesToRemove = edges.filter(
(edge) =>
edge.target === createdPlaceholderRef.current ||
edge.source === createdPlaceholderRef.current,
);
// Remove placeholder node
const nodesToRemove = nodes.filter(
(node) => node.id === createdPlaceholderRef.current,
);
if (nodesToRemove.length > 0 || edgesToRemove.length > 0) {
reactFlowInstance.deleteElements({
nodes: nodesToRemove,
edges: edgesToRemove,
});
}
createdPlaceholderRef.current = null;
}
// Reset user selection flag
userSelectedNodeRef.current = false;
}, [reactFlowInstance]);
/**
* User node selection callback
* Called when user selects a node type from dropdown menu
*/
const onNodeCreated = useCallback(
(newNodeId: string) => {
// First establish connection between new node and source, then delete placeholder
if (createdPlaceholderRef.current && reactFlowInstance) {
const { nodes, edges, addEdge, updateNode } = useGraphStore.getState();
// Find placeholder node to get its position
const placeholderNode = nodes.find(
(node) => node.id === createdPlaceholderRef.current,
);
// Find placeholder-related connection and get source node info
const placeholderEdge = edges.find(
(edge) => edge.target === createdPlaceholderRef.current,
);
// Update new node position to match placeholder position
if (placeholderNode) {
const newNode = nodes.find((node) => node.id === newNodeId);
if (newNode) {
updateNode({
...newNode,
position: placeholderNode.position,
});
}
}
if (placeholderEdge) {
// Establish connection between new node and source node
addEdge({
source: placeholderEdge.source,
target: newNodeId,
sourceHandle: placeholderEdge.sourceHandle || null,
targetHandle: placeholderEdge.targetHandle || null,
});
}
// Remove placeholder node and related connections
const edgesToRemove = edges.filter(
(edge) =>
edge.target === createdPlaceholderRef.current ||
edge.source === createdPlaceholderRef.current,
);
const nodesToRemove = nodes.filter(
(node) => node.id === createdPlaceholderRef.current,
);
if (nodesToRemove.length > 0 || edgesToRemove.length > 0) {
reactFlowInstance.deleteElements({
nodes: nodesToRemove,
edges: edgesToRemove,
});
}
}
// Mark that user has selected a node
userSelectedNodeRef.current = true;
createdPlaceholderRef.current = null;
},
[reactFlowInstance],
);
/**
* Set the created placeholder node ID
*/
const setCreatedPlaceholderRef = useCallback((nodeId: string | null) => {
createdPlaceholderRef.current = nodeId;
}, []);
/**
* Reset user selection flag
*/
const resetUserSelectedFlag = useCallback(() => {
userSelectedNodeRef.current = false;
}, []);
return {
removePlaceholderNode,
onNodeCreated,
setCreatedPlaceholderRef,
resetUserSelectedFlag,
checkAndRemoveExistingPlaceholder,
createdPlaceholderRef: createdPlaceholderRef.current,
userSelectedNodeRef: userSelectedNodeRef.current,
};
};