Every workspace now renders as a first-class card on the canvas
regardless of parent_id. The old "parent card contains mini TeamMember
chips" layout is gone — if B is parented to A, B renders as a full card
inside A's coordinate space using React Flow's `parentId` binding, so
moving A carries B along and children have the same detail + actions as
root cards.
Details:
- canvas-topology.ts: topologically sort parents before children
(React Flow ordering requirement), compute each child's RF-native
parentId + relative position on load. DB keeps absolute x/y; the
abs→rel conversion happens here, reverse translation in
Canvas.onNodeDragStop before savePosition PATCHes the DB.
- WorkspaceNode.tsx: delete the EmbeddedTeam + TeamMemberChip blocks,
simplify the size classes, and add NodeResizer (visible when selected)
so users can drag any edge/corner to grow or shrink. Parent cards
default to a larger min size so nested children have breathing room.
- Canvas.tsx drop targeting rewritten: bounds-based hit test against
each node's measured absolute bbox, deepest match wins. Fixes two
prior bugs at once — dropping onto Claude Code with a nested same-
named Hermes no longer picks the wrong node, and the target can now
be a nested workspace when that's where the pointer actually released.
- canvas.ts nestNode + removeNode: translate position between old and
new parent's absolute origin on nest/unnest so the card doesn't jump,
and re-point the RF `parentId` alongside `data.parentId` on reparent.
- Tests: hidden-flag assertions replaced with parentId checks; obsolete
TeamMemberChip a11y/eject tests deleted (the UI component no longer
exists).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>