Fix composite node duplication placement#542
Conversation
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit f1a14da. Configure here.
| if (draft) { | ||
| setMovingNode(draft as any) | ||
| setSelection({ selectedIds: [] }) | ||
| return |
There was a problem hiding this comment.
Stair duplicate drops placement offset
Medium Severity
3D duplicate now routes stairs through createFreshPlacementSubtree with no position patch, but the removed menu path used duplicateStairSubtree, which defaulted to a [1, 0, 1] offset. The draft is created at the source stair’s position, so it overlaps the original until the user moves it, unlike other positioned duplicates that get a small nudge.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit f1a14da. Configure here.


Summary
Validation
bun run check-typesbun test packages/core/src packages/editor/src packages/nodes/src(2100 passed, 1 skipped)bun run buildbunx biome checkon changed filesNote
Medium Risk
Touches scene graph duplication, undo/history pause-resume, and stair segment parenting—user-visible placement bugs are possible if draft rollback or subtree wiring regresses, but behavior is covered by new tests and mirrors an existing cabinet path.
Overview
Unifies duplicate → place for composite nodes by extending the fresh-placement lifecycle and tightening who uses it.
duplicatesAsFreshSubtreenow keys only oncapabilities.duplicable.subtree(not whether children exist), so childless subtree kinds (e.g. curved stairs, empty cabinet runs) still get a full fresh-subtree draft. Non-subtree copies go throughprepareFreshPlacementRootDuplicate, which stripsid, clearschildrenso the clone never aliases the source’s descendants, and setsmetadata.isNew.2D and 3D action menus share that split: subtree path uses
createFreshPlacementSubtreewith try/catch that deletes partial drafts and always resumes scene history; root path uses the helper instead of inlinestructuredClone. Floorplan duplicate also clears selection and sets 2D move origin after success.Stairs declare
duplicable: { subtree: true };duplicateStairSubtreeis rewritten oncreateFreshPlacementSubtree/commitFreshPlacementSubtree(no manual segment cloning or rAF registry wait). The floating menu’s bespoke stair duplicate branch is removed—stairs follow the generic subtree path like cabinets.Tests cover root-only child isolation, childless subtree eligibility, and stair move drafts (segments rewired, selection cleared).
Reviewed by Cursor Bugbot for commit f1a14da. Bugbot is set up for automated code reviews on this repo. Configure here.