fix(canvas): skip already-nested workspaces as drop targets

Dragging one workspace onto another could pick a nested child as the
"nearest" drop target instead of the visible parent card the user
actually hovered. The effect: dropping a free-floating Hermes Agent
onto a Claude Code Agent that already had a Hermes Agent nested inside
showed "Move 'Hermes Agent' inside 'Hermes Agent'?" — the confirmation
referenced the nested same-named child, not Claude Code.

Why: getIntersectingNodes returns every overlapping node, including
hidden=true children that render inside their parent's card. The
parent and child share bounding boxes, so the child often "won" the
nearest-distance check. Filter them out at the source: a node that's
already got a parentId (or is hidden) is never a valid top-level drop
target.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Hongming Wang 2026-04-23 17:49:01 -07:00
parent 7356cf8d3a
commit 8a07cf4035

View File

@ -96,6 +96,14 @@ function CanvasInner() {
let nearest: { id: string; dist: number } | null = null;
for (const candidate of getIntersectingNodes(node)) {
if (candidate.id === node.id || isDescendant(node.id, candidate.id)) continue;
// Skip nodes already nested inside another workspace: they render
// as TEAM MEMBERS rows inside their parent card and share its
// bounding box, so getIntersectingNodes would otherwise pick the
// nested child (same "Hermes Agent" name) over the visible parent
// the user actually dropped onto. Hidden nodes + nodes with a
// parentId are never valid top-level drop targets.
const candData = candidate.data as WorkspaceNodeData | undefined;
if (candidate.hidden || candData?.parentId) continue;
const dx = candidate.position.x - node.position.x;
const dy = candidate.position.y - node.position.y;
const dist2 = dx * dx + dy * dy;