Skip to content

SpatialBoard 0.3.0

Choose a tag to compare

@hishamk hishamk released this 23 Aug 10:14
· 18 commits to main since this release

Six fixes and additions driven by real host-app usage of the data-flow board.

Added

  • fitToContent / fitToNodes (and their animated variants) accept options: padding (canvas-space margin, defaults unchanged), insets (screen-pixel bands reserved for host chrome floating over the canvas — content centers in the remaining region), and maxZoom (framing-zoom cap, so fitting a single small node stops zooming all the way in). FitOptions / FitInsets are exported from both the board and spatialboard/engine entries. Option values are sanitized: a NaN/Infinity padding or maxZoom and negative/non-finite insets can never poison the viewport transform.
  • Fits requested before the board's first size measurement are parked and applied when the container measures, instead of framing the 2000×1500 stand-in defaults. A fitToContent() inside a seeding effect now just works — no more double-rAF / setTimeout dances in hosts. fitToFrame parks the same way, and attaching a different container re-arms parking (a reused engine remounted into another panel waits for the new measurement instead of framing the previous container's size). Headless engines (no container attached) keep fitting immediately.
  • PortValue admits arrays (unknown[]): matrices, vectors, and token lists flow through ports directly instead of being wrapped in an object to satisfy the type. (Note for consumers narrowing PortValue: after typeof v === "object" the type is now Record<string, unknown> | unknown[] — code assigning that to a Record or indexing by string needs an Array.isArray branch.)

Changed

  • Output change detection is deep (SameValueZero primitives, arrays, plain objects, depth-capped), and a flush now skips downstream nodes whose upstream outputs did not actually change — a freshly built but structurally equal payload no longer recomputes the downstream graph. A compute that reads external state and relied on those accidental recomputes should call markDirty explicitly. Structural comparison applies to plain data only: Date, Map, Set, class instances, and subclassed arrays compare by reference, so a fresh exotic instance always counts as a change.

Fixed

  • Duplicate port wires between the same pair of nodes no longer make the target read as a cycle (its compute silently never ran; cycleNodeIds flagged it). The topological sort now counts in-degree per unique adjacency, so twin wires — easy to create from a StrictMode double-run seeding effect — cannot wedge a node. createEdge additionally returns the existing edge id for an exact duplicate port wire instead of stacking a twin.
  • animatePanTo(x, y, 0) — and any camera animation given a zero, negative, or NaN duration — now snaps to the target instead of tweening elapsed/0 into NaN viewport transforms that blanked the canvas.
  • Port wires no longer intercept pointer events over the solid cards they cross: buttons, inputs, and draggable cells inside a card stay clickable under a crossing wire. The wire remains selectable in the gaps, and the right-click "Objects here" list reaches it even where a card covers it. Freeform edges keep their full hit priority, so annotation arrows over images behave as before.
  • An inline nodeTypes prop (nodeTypes={[...coreBoardNodes, myType]}, the README pattern) no longer rebuilds the registry — and recomputes the whole data-flow graph — on every host render; when a compute published into host state, that rebuild loop could hang the page.

Full details in CHANGELOG.md.