fix: canvas loop bug and node placement
This commit is contained in:
parent
7da7ad5041
commit
1c539076e5
2 changed files with 61 additions and 10 deletions
|
|
@ -158,6 +158,7 @@ export const Automation2FlowEditor: React.FC<Automation2FlowEditorProps> = ({ in
|
|||
const [versions, setVersions] = useState<AutoVersion[]>([]);
|
||||
const [currentVersionId, setCurrentVersionId] = useState<string | null>(null);
|
||||
const [versionLoading, setVersionLoading] = useState(false);
|
||||
const didBootstrapEmptyCanvasRef = useRef(false);
|
||||
|
||||
const [targetFeatureInstanceId, setTargetFeatureInstanceId] = useState<string | null>(instanceId);
|
||||
|
||||
|
|
@ -598,8 +599,22 @@ export const Automation2FlowEditor: React.FC<Automation2FlowEditorProps> = ({ in
|
|||
|
||||
useEffect(() => {
|
||||
if (loading || nodeTypes.length === 0) return;
|
||||
if (currentWorkflowId || initialWorkflowId) return;
|
||||
if (canvasNodes.length > 0) return;
|
||||
if (currentWorkflowId || initialWorkflowId) {
|
||||
didBootstrapEmptyCanvasRef.current = false;
|
||||
return;
|
||||
}
|
||||
if (didBootstrapEmptyCanvasRef.current) return;
|
||||
didBootstrapEmptyCanvasRef.current = true;
|
||||
if (canvasNodes.length === 0 && canvasConnections.length === 0 && invocations.length === 0) {
|
||||
return;
|
||||
}
|
||||
console.debug(`${LOG} bootstrapping empty canvas`, {
|
||||
currentWorkflowId,
|
||||
initialWorkflowId,
|
||||
canvasNodes: canvasNodes.length,
|
||||
canvasConnections: canvasConnections.length,
|
||||
invocations: invocations.length,
|
||||
});
|
||||
applyGraphWithSync({ nodes: [], connections: [] }, [], {
|
||||
skipHistory: true,
|
||||
});
|
||||
|
|
@ -609,8 +624,9 @@ export const Automation2FlowEditor: React.FC<Automation2FlowEditorProps> = ({ in
|
|||
currentWorkflowId,
|
||||
initialWorkflowId,
|
||||
canvasNodes.length,
|
||||
canvasConnections.length,
|
||||
invocations.length,
|
||||
applyGraphWithSync,
|
||||
t,
|
||||
]);
|
||||
|
||||
const toggleCategory = useCallback((id: string) => {
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@ import { useLanguage } from '../../../providers/language/LanguageContext';
|
|||
import { AiBadge } from '../nodes/shared/AiBadge';
|
||||
import { switchOutputLabel } from '../nodes/shared/graphUtils';
|
||||
|
||||
const LOG = '[FlowCanvas]';
|
||||
|
||||
export interface CanvasNode {
|
||||
id: string;
|
||||
type: string;
|
||||
|
|
@ -842,6 +844,8 @@ export const FlowCanvas = forwardRef<FlowCanvasHandle, FlowCanvasProps>(function
|
|||
|
||||
const onHistoryCheckpointRef = useRef(onHistoryCheckpoint);
|
||||
onHistoryCheckpointRef.current = onHistoryCheckpoint;
|
||||
const onSelectionChangeRef = useRef(onSelectionChange);
|
||||
onSelectionChangeRef.current = onSelectionChange;
|
||||
|
||||
const emitHistoryCheckpoint = useCallback(() => {
|
||||
onHistoryCheckpointRef.current?.();
|
||||
|
|
@ -1019,12 +1023,19 @@ export const FlowCanvas = forwardRef<FlowCanvasHandle, FlowCanvasProps>(function
|
|||
]
|
||||
);
|
||||
|
||||
const lastEmittedSelectionRef = useRef<{ nodeId: string | null; signature: string | null }>({
|
||||
nodeId: null,
|
||||
signature: null,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (onSelectionChange) {
|
||||
const node = selectedNodeId ? nodes.find((n) => n.id === selectedNodeId) ?? null : null;
|
||||
onSelectionChange(node);
|
||||
}
|
||||
}, [selectedNodeId, nodes, onSelectionChange]);
|
||||
const node = selectedNodeId ? nodes.find((n) => n.id === selectedNodeId) ?? null : null;
|
||||
const signature = node ? JSON.stringify(node) : null;
|
||||
const last = lastEmittedSelectionRef.current;
|
||||
if (last.nodeId === selectedNodeId && last.signature === signature) return;
|
||||
lastEmittedSelectionRef.current = { nodeId: selectedNodeId, signature };
|
||||
onSelectionChangeRef.current?.(node);
|
||||
}, [selectedNodeId, nodes]);
|
||||
|
||||
const handleConnectionClick = useCallback((e: React.MouseEvent, connId: string) => {
|
||||
e.stopPropagation();
|
||||
|
|
@ -1088,6 +1099,11 @@ export const FlowCanvas = forwardRef<FlowCanvasHandle, FlowCanvasProps>(function
|
|||
const handleDrop = useCallback(
|
||||
async (e: React.DragEvent) => {
|
||||
e.preventDefault();
|
||||
console.debug(`${LOG} drop received`, {
|
||||
types: Array.from(e.dataTransfer.types),
|
||||
clientX: e.clientX,
|
||||
clientY: e.clientY,
|
||||
});
|
||||
// 1) externe Drop-Targets (z. B. ``application/json+workflow`` aus UDB-FilesTab)
|
||||
if (onExternalDrop) {
|
||||
const reservedMimes = new Set([
|
||||
|
|
@ -1113,16 +1129,35 @@ export const FlowCanvas = forwardRef<FlowCanvasHandle, FlowCanvasProps>(function
|
|||
}
|
||||
// 2) Standard: Node-Type aus der NodeSidebar
|
||||
const raw = e.dataTransfer.getData('application/json');
|
||||
if (!raw || !containerRef.current) return;
|
||||
if (!raw || !containerRef.current) {
|
||||
console.debug(`${LOG} drop ignored`, {
|
||||
hasRaw: Boolean(raw),
|
||||
hasContainer: Boolean(containerRef.current),
|
||||
});
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const { type } = JSON.parse(raw);
|
||||
const el = containerRef.current;
|
||||
const rect = el.getBoundingClientRect();
|
||||
const x = (e.clientX - rect.left - panOffset.x) / zoom - NODE_WIDTH / 2;
|
||||
const y = (e.clientY - rect.top - panOffset.y) / zoom - NODE_HEIGHT / 2;
|
||||
console.debug(`${LOG} placing node from drop`, {
|
||||
type,
|
||||
raw,
|
||||
dropX: x,
|
||||
dropY: y,
|
||||
panOffset,
|
||||
zoom,
|
||||
});
|
||||
onDropNodeType(type, Math.max(0, x), Math.max(0, y));
|
||||
emitHistoryCheckpoint();
|
||||
} catch (_) {}
|
||||
} catch (error) {
|
||||
console.debug(`${LOG} drop parse failed`, {
|
||||
raw,
|
||||
error,
|
||||
});
|
||||
}
|
||||
},
|
||||
[onDropNodeType, onExternalDrop, panOffset, zoom, emitHistoryCheckpoint]
|
||||
);
|
||||
|
|
|
|||
Loading…
Reference in a new issue