My App
BoardKitCore

Scene Graph

Immutable SceneState with pure mutation functions

The scene graph is the central data structure representing all elements on a page. All mutations are immutable — they return new SceneState objects rather than modifying in place.

SceneState

interface SceneState {
    elements: Map<string, Element>;
    elementOrder: string[]; // z-order sorted IDs
}
FieldDescription
elementsMap of element ID to Element for O(1) lookup
elementOrderArray of element IDs in z-order (rendering order)

Functions

createScene

Creates an empty scene.

function createScene(): SceneState;
import { createScene } from '@hfu.digital/boardkit-core';

const scene = createScene();
// { elements: Map(0), elementOrder: [] }

addElement

Adds an element to the scene. Returns a new SceneState.

function addElement(scene: SceneState, element: Element): SceneState;
import { createScene, addElement } from '@hfu.digital/boardkit-core';

const scene = createScene();
const updated = addElement(scene, myElement);
// original scene is unchanged

removeElement

Removes an element by ID. Returns a new SceneState.

function removeElement(scene: SceneState, id: string): SceneState;

updateElement

Patches an element with partial data. Returns a new SceneState. Returns the original scene if the element is not found.

function updateElement(scene: SceneState, id: string, patch: Partial<Element>): SceneState;

getElement

Retrieves an element by ID. Returns undefined if not found.

function getElement(scene: SceneState, id: string): Element | undefined;

getElementsByPage

Returns all elements belonging to a specific page, in z-order.

function getElementsByPage(scene: SceneState, pageId: string): Element[];

loadElements

Creates a SceneState from an array of elements, sorted by zIndex.

function loadElements(elements: Element[]): SceneState;

This is useful for initializing the scene from server data:

import { loadElements } from '@hfu.digital/boardkit-core';

const elements = await fetch('/api/boards/123/pages/456/elements').then(r => r.json());
const scene = loadElements(elements);

Immutability Pattern

All mutation functions follow the same pattern: they create new Map and Array instances rather than modifying the existing ones. This is critical for React integration, where useSyncExternalStore relies on reference equality to detect changes.

// Each call returns a NEW SceneState
const s1 = createScene();
const s2 = addElement(s1, elementA);
const s3 = addElement(s2, elementB);
const s4 = removeElement(s3, elementA.id);

// s1, s2, s3, s4 are all different objects
console.log(s1 === s2); // false
console.log(s2.elements.size); // 1
console.log(s3.elements.size); // 2
console.log(s4.elements.size); // 1

On this page