Skip to main content

Store

The store is the immutable Scene model. Every field is a ReadonlyMap or frozen value, and every mutation goes through a pure operation that returns a fresh Scene plus a Patch.

The Scene shapeโ€‹

A Scene holds elements, links, layers, annotations, files, and a viewport. Use emptyScene() for a fresh scene with a single default layer.

import { emptyScene, addElement, orderForTop, DEFAULT_LAYER_ID } from "@oh-just-another/scene";
import type { Element } from "@oh-just-another/scene";
import { elementId } from "@oh-just-another/types";

const rect: Element = {
id: elementId("r1"),
layerId: DEFAULT_LAYER_ID,
type: "rectangle",
position: { x: 0, y: 0 },
rotation: 0,
scale: { x: 1, y: 1 },
order: orderForTop([]),
style: { fill: "#88f" },
width: 100,
height: 50,
};

const { scene, patch } = addElement(emptyScene(), rect);

Operations and patchesโ€‹

Mutating operations โ€” addElement, removeElement, updateElement, moveElement, addLink, addLayer, setViewport, and more โ€” each return { scene, patch }. The Patch records before and after, so invert(patch) produces the exact inverse.

import { apply, invert } from "@oh-just-another/scene";

const undone = apply(scene, invert(patch)); // back to the original scene

Structural sharingโ€‹

Applying a patch shallow-copies only the touched map (elements, links, layers, annotations, or files); entry objects and the untouched maps are shared by reference, so deriving a new scene stays cheap. This immutable-patch design is what powers undo/redo history and collaborative replay.