--- id: TASK-45 title: 'Implement Shape Nesting: shapes containing shapes + recursive canvas' status: To Do assignee: [] created_date: '2026-02-18 20:06' labels: - feature - phase-4 - ecosystem milestone: m-1 dependencies: - TASK-44 references: - rspace-online/lib/folk-shape.ts - rspace-online/lib/community-sync.ts priority: medium --- ## Description Allow shapes to contain other shapes, including a recursive canvas shape. Automerge schema additions (flat references, NOT nested objects): - shapes[id].parentId: string — nested inside this shape - shapes[id].childIds: string[] — contains these shapes New shape lib/folk-canvas.ts: - A shape that IS a canvas — renders child shapes inside scrollable/zoomable container - Optional linkedCommunitySlug to show shapes from another community (cross-canvas embedding) - Own zoom/pan controls within the mini-canvas Coordinate system: - Children store ABSOLUTE canvas coordinates in CRDT (simplifies sync, prevents jitter) - folk-canvas applies CSS transform offset so children appear inside it - When parent moves, nesting manager applies delta to all children - Nesting is a render-time concern, not a data-model concern FolkShape additions: - parentShape getter, childShapes getter - addChild(), removeChild() - toParentCoords(), toCanvasCoords() for coordinate transforms Canvas.html: drag-drop shape onto folk-canvas to nest it. ## Acceptance Criteria - [ ] #1 Shapes can be nested inside folk-canvas via drag-drop - [ ] #2 Nested shapes move with parent when parent is moved - [ ] #3 folk-canvas has its own zoom/pan controls - [ ] #4 parentId/childIds sync correctly via Automerge - [ ] #5 Un-nesting a shape restores it to top-level canvas - [ ] #6 No coordinate jitter when two users move parent and child simultaneously - [ ] #7 Optional cross-canvas linking via linkedCommunitySlug