---
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

<!-- SECTION:DESCRIPTION:BEGIN -->
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.
<!-- SECTION:DESCRIPTION:END -->

## Acceptance Criteria
<!-- AC:BEGIN -->
- [ ] #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
<!-- AC:END -->
