mirror of
				https://github.com/excalidraw/excalidraw.git
				synced 2025-11-03 20:34:40 +01:00 
			
		
		
		
	add design doc
This commit is contained in:
		
							
								
								
									
										101
									
								
								dev-docs/docs/codebase/hierarchy-plan.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										101
									
								
								dev-docs/docs/codebase/hierarchy-plan.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,101 @@
 | 
			
		||||
## Excalidraw Hierarchical Model Plan
 | 
			
		||||
 | 
			
		||||
### Background & Goals
 | 
			
		||||
 | 
			
		||||
Introduce a fully in-memory hierarchical (tree) model on top of the existing flat `elements[]` storage for more efficient complex operations (queries, selection, collision), while keeping flat arrays as the persistence/collab projection. Gradually move to tree-first edits with flat projection.
 | 
			
		||||
 | 
			
		||||
### Capabilities To Preserve
 | 
			
		||||
 | 
			
		||||
- z-index via fractional indices
 | 
			
		||||
- add/remove to frame
 | 
			
		||||
- group/ungroup
 | 
			
		||||
- bound texts (containerId)
 | 
			
		||||
- arrow bindings (start/endBinding)
 | 
			
		||||
- history (undo/redo) and collab (delta broadcast)
 | 
			
		||||
- load/save the flat array
 | 
			
		||||
 | 
			
		||||
### Existing Reusable Capabilities
 | 
			
		||||
 | 
			
		||||
- Deltas & History: `element/src/delta.ts` (ElementsDelta/AppStateDelta/StoreDelta), `excalidraw/history.ts` (HistoryDelta), auto rebind, text bbox redraw, z-index normalization.
 | 
			
		||||
- Store & Snapshot: `element/src/store.ts` provides commit levels, batching, and delta emission.
 | 
			
		||||
- Scene & Relationships: `element/src/Scene.ts`, `element/src/frame.ts`, `element/src/groups.ts` for frames and groups logic.
 | 
			
		||||
- Rendering: `excalidraw/renderer/staticScene.ts` with order by fractional index.
 | 
			
		||||
- Restore/Import: `excalidraw/data/restore.ts`.
 | 
			
		||||
 | 
			
		||||
### Data Model & Invariants
 | 
			
		||||
 | 
			
		||||
- Node types: `ElementNode`, logical `GroupNode` (id=groupId), `FrameNode` (bound to frame element). `Table*Node` reserved.
 | 
			
		||||
- Parent priority: container > group (deep→shallow) > frame > root; single parent per node.
 | 
			
		||||
- Groups must not span multiple frames.
 | 
			
		||||
- Drawing order remains by fractional index; the tree offers structural and sibling-order views only.
 | 
			
		||||
 | 
			
		||||
### Flat→Tree Build (`buildFromFlat`)
 | 
			
		||||
 | 
			
		||||
- Input: `elements[]`/`elementsMap` (optionally including deleted).
 | 
			
		||||
- Output: `{ nodesById, roots, orderHints, diagnostics }`.
 | 
			
		||||
- Rules:
 | 
			
		||||
  - Bound text attaches to its container; groups form deep→shallow parent chains from `groupIds`; frame parent from `frameId`; otherwise root.
 | 
			
		||||
  - Sibling order: ascending by the minimum `index` across the node’s represented elements.
 | 
			
		||||
  - Diagnostics: cross-frame groups, invalid container, cycles, missing refs (error/warn).
 | 
			
		||||
 | 
			
		||||
### Tree → Flat Projection (`flattenToArray`)
 | 
			
		||||
 | 
			
		||||
- Input: tree, optional "apply recommended reorder".
 | 
			
		||||
- Output: `{ nextFieldsByElementId, reorderIntent? }`.
 | 
			
		||||
- Rules:
 | 
			
		||||
  - `frameId` from nearest frame ancestor; `groupIds` nearest→farthest; `containerId` from nearest container.
 | 
			
		||||
  - Do not change draw order by default; any reordering is applied by the caller via `Scene.insert*` and `syncMovedIndices`.
 | 
			
		||||
 | 
			
		||||
### Operations Mapping (Tree edits → Flat deltas)
 | 
			
		||||
 | 
			
		||||
- z-index: sibling reordering → index deltas; normalized with `syncMovedIndices`.
 | 
			
		||||
- Frame membership: reparent to `FrameNode`/root → `frameId` updates; cross-frame groups disallowed.
 | 
			
		||||
- Group/ungroup: modify `GroupNode` structure → update `groupIds` chains.
 | 
			
		||||
- Bound text: reparent to container → update `containerId`/`boundElements`; text bbox redraw handled by `ElementsDelta`.
 | 
			
		||||
- Arrow binding: does not change parentage; only update start/endBinding; `ElementsDelta` handles rebind/unbind.
 | 
			
		||||
 | 
			
		||||
### History & Collab
 | 
			
		||||
 | 
			
		||||
- Transactional edits on the tree via `HierarchyManager.begin/commit/rollback`; commit projects to a minimal flat diff, wrapped as `StoreDelta`, and submitted via `Store.scheduleMicroAction` (IMMEDIATELY).
 | 
			
		||||
- Undo/redo uses `HistoryDelta`; replay re-emits flat deltas for sync.
 | 
			
		||||
- Collab remains flat-delta based; peers rebuild the tree deterministically from flats.
 | 
			
		||||
 | 
			
		||||
### Rendering Strategy
 | 
			
		||||
 | 
			
		||||
- Add a tree-backed rendering adapter beside `renderStaticScene` behind a feature flag, preserving draw-order semantics (fractional index). In the short term, use the tree for selection/collision pruning (frame → group → element).
 | 
			
		||||
 | 
			
		||||
### Challenges & Risks
 | 
			
		||||
 | 
			
		||||
- Cross-frame group handling (block or guided fix).
 | 
			
		||||
- Reorder consistency (tree sibling order vs fractional index).
 | 
			
		||||
- Collab conflicts (use `ElementsDelta.applyLatestChanges`).
 | 
			
		||||
- Performance (build O(n), queries O(1)/O(k)); cache/incremental via `sceneNonce`.
 | 
			
		||||
- Test coverage (round-trip, collab equivalence, history replay, deep groups/large frames/binding chains).
 | 
			
		||||
 | 
			
		||||
### Phased Plan
 | 
			
		||||
 | 
			
		||||
- Phase 0 Rules & Contracts
 | 
			
		||||
  - Lock invariants and priorities; define diagnostics (error/warn).
 | 
			
		||||
- Phase 1 Pure functions & Validation
 | 
			
		||||
  - Implement `buildFromFlat`, `flattenToArray`, `validateIntegrity`; cache by `sceneNonce`; add round-trip tests.
 | 
			
		||||
- Phase 2 Read-only integration
 | 
			
		||||
  - Tree-backed selection and collision pruning; measure wins.
 | 
			
		||||
- Phase 3 Parallel render adapter
 | 
			
		||||
  - Tree render adapter (flag) with preserved order semantics.
 | 
			
		||||
- Phase 4 Projection & Transactions
 | 
			
		||||
  - `HierarchyManager.begin/commit/rollback`; commit→`StoreDelta`→Store.
 | 
			
		||||
- Phase 5 Migrate operations
 | 
			
		||||
  - Frame membership and group/ungroup → tree+projection; then bound text; optional z-index reorder intent.
 | 
			
		||||
- Phase 6 Extensions & Tables
 | 
			
		||||
  - Introduce `Table*Node` (in-memory first, then projection), with validation and UI.
 | 
			
		||||
 | 
			
		||||
### Success Criteria
 | 
			
		||||
 | 
			
		||||
- Correctness: same flat → same tree; unchanged structure round-trip no-ops; existing operations equivalent.
 | 
			
		||||
- History/Collab: still record and broadcast minimal flat deltas; deterministic tree on peers.
 | 
			
		||||
- Performance: selection/collision candidate reduction on large scenes; build/query latency targets met.
 | 
			
		||||
- Rollback: feature flag to fall back to legacy path at any time.
 | 
			
		||||
 | 
			
		||||
### Next Steps
 | 
			
		||||
 | 
			
		||||
- Finalize invariants and IO contracts; implement `buildFromFlat`/`flattenToArray` and `validateIntegrity`; add round‑trip and failure-case tests; prototype read-only integration and render adapter.
 | 
			
		||||
		Reference in New Issue
	
	Block a user