Fix canvas connection interactions

This commit is contained in:
Codex
2026-05-11 21:57:10 +08:00
parent 65b3fe1100
commit 73134516a9
2 changed files with 48 additions and 5 deletions

View File

@@ -2267,6 +2267,13 @@ export function InfiniteCanvasWorkspace() {
}, [downloadImageFromNode, state.nodes]);
const handleFlowNodeSelect = useCallback((nodeId: string, additive: boolean) => {
if (connectingFromId && connectingFromId !== nodeId) {
addConnection(connectingFromId, nodeId);
setConnectingFromId(null);
setSelectedNodeId(nodeId);
setSelectedNodeIds([nodeId]);
return;
}
setAddMenu(null);
setConnectingFromId(null);
setSelectedNodeId(nodeId);
@@ -2274,7 +2281,7 @@ export function InfiniteCanvasWorkspace() {
if (!additive) return current.includes(nodeId) ? current : [nodeId];
return current.includes(nodeId) ? current.filter(id => id !== nodeId) : [...current, nodeId];
});
}, []);
}, [addConnection, connectingFromId]);
const handleFlowNodesCommit = useCallback((positions: { id: string; x: number; y: number }[], options?: { history?: boolean; dirty?: boolean }) => {
if (positions.length === 0) return;

View File

@@ -37,11 +37,13 @@ type CanvasFlowNodeData = {
connecting: boolean;
connections: CanvasConnection[];
allNodes: CanvasNode[];
connectingFromId: string | null;
imageModelOptions: CanvasModelOption[];
textModelOptions: CanvasModelOption[];
layerCanvasSize: number;
layerColors: Record<CanvasLayer['type'], string>;
onSelect: (id: string, additive: boolean) => void;
onConnect: (sourceId: string, targetId: string) => void;
onStartConnect: (id: string) => void;
onUpdateNode: (id: string, patch: Partial<CanvasNode>) => void;
onRunNode: (id: string) => void;
@@ -251,7 +253,7 @@ function NodeInlineParams({
}
function CanvasNodeCard({ data }: NodeProps<CanvasFlowNode>) {
const { node, selected, connecting, connections, allNodes, imageModelOptions, textModelOptions, layerCanvasSize, layerColors, onSelect, onStartConnect, onUpdateNode, onRunNode, onRunDownstreamNode, onCreateVariationNode, onPreviewNode, onDownloadNode } = data;
const { node, selected, connecting, connections, allNodes, connectingFromId, imageModelOptions, textModelOptions, layerCanvasSize, layerColors, onSelect, onConnect, onStartConnect, onUpdateNode, onRunNode, onRunDownstreamNode, onCreateVariationNode, onPreviewNode, onDownloadNode } = data;
const incomingNodes = allNodes.filter(item => connections.some(connection => connection.targetNodeId === node.id && connection.sourceNodeId === item.id));
const incomingImage = incomingNodes.find(item => !!getNodeImageUrl(item));
const incomingText = incomingNodes.map(getNodeTextValue).filter(Boolean).join('\n');
@@ -277,6 +279,11 @@ function CanvasNodeCard({ data }: NodeProps<CanvasFlowNode>) {
onPointerDown={(event) => {
const target = event.target as HTMLElement;
if (target.closest('button,input,textarea,[role="combobox"],.nodrag')) return;
if (connectingFromId && connectingFromId !== node.id) {
event.preventDefault();
onConnect(connectingFromId, node.id);
return;
}
onSelect(node.id, event.ctrlKey || event.metaKey || event.shiftKey);
}}
>
@@ -697,6 +704,7 @@ function sameFlowNodes(a: CanvasFlowNode[], b: CanvasFlowNode[]) {
&& node.data.connecting === next.data.connecting
&& node.data.connections === next.data.connections
&& node.data.allNodes === next.data.allNodes
&& node.data.connectingFromId === next.data.connectingFromId
&& node.data.imageModelOptions === next.data.imageModelOptions
&& node.data.textModelOptions === next.data.textModelOptions
&& node.data.onRunNode === next.data.onRunNode
@@ -772,11 +780,13 @@ function FlowCanvasInner({
connecting: connectingFromId === node.id,
connections,
allNodes: nodes,
connectingFromId,
imageModelOptions,
textModelOptions,
layerCanvasSize,
layerColors,
onSelect: onSelectNode,
onConnect,
onStartConnect,
onUpdateNode,
onRunNode,
@@ -790,9 +800,10 @@ function FlowCanvasInner({
width: node.width,
height: node.height,
},
})), [connectingFromId, connections, editable, imageModelOptions, layerCanvasSize, layerColors, nodes, onCreateVariationNode, onDownloadNode, onPreviewNode, onRemoveConnection, onRunDownstreamNode, onRunNode, onSelectNode, onStartConnect, onUpdateNode, selectedNodeIds, textModelOptions]);
})), [connectingFromId, connections, editable, imageModelOptions, layerCanvasSize, layerColors, nodes, onConnect, onCreateVariationNode, onDownloadNode, onPreviewNode, onRemoveConnection, onRunDownstreamNode, onRunNode, onSelectNode, onStartConnect, onUpdateNode, selectedNodeIds, textModelOptions]);
const [flowNodes, setFlowNodes] = useState<CanvasFlowNode[]>(incomingNodes);
const [localEdges, setLocalEdges] = useState<Edge[]>([]);
const flowEdges = useMemo<Edge[]>(() => connections.map((connection) => ({
id: connection.id,
@@ -808,6 +819,14 @@ function FlowCanvasInner({
},
interactionWidth: 24,
})), [connections, edgeStroke]);
const visibleEdges = useMemo(() => {
const realIds = new Set(flowEdges.map(edge => edge.id));
return [...flowEdges, ...localEdges.filter(edge => !realIds.has(edge.id))];
}, [flowEdges, localEdges]);
useEffect(() => {
setLocalEdges([]);
}, [connections]);
useEffect(() => {
setFlowNodes(current => {
@@ -881,8 +900,25 @@ function FlowCanvasInner({
const handleConnect = useCallback((connection: Connection) => {
if (!connection.source || !connection.target) return;
const edgeId = `pending-${connection.source}-${connection.target}`;
setLocalEdges(current => current.some(edge => edge.source === connection.source && edge.target === connection.target)
? current
: [
...current,
{
id: edgeId,
source: connection.source || '',
target: connection.target || '',
sourceHandle: connection.sourceHandle || 'output',
targetHandle: connection.targetHandle || 'input',
type: 'smoothstep',
markerEnd: { type: MarkerType.ArrowClosed, color: edgeStroke },
style: { stroke: edgeStroke, strokeWidth: 3 },
interactionWidth: 24,
},
]);
onConnect(connection.source, connection.target);
}, [onConnect]);
}, [edgeStroke, onConnect]);
const handleMoveEnd = useCallback((_event: MouseEvent | TouchEvent | null, nextViewport: FlowViewport) => {
if (!movingRef.current) return;
@@ -944,7 +980,7 @@ function FlowCanvasInner({
<ReactFlow
className="canvas-flow"
nodes={flowNodes}
edges={flowEdges}
edges={visibleEdges}
nodeTypes={CANVAS_NODE_TYPES}
minZoom={minZoom}
maxZoom={maxZoom}