Documentation Index
Fetch the complete documentation index at: https://mintlify.com/open-pencil/open-pencil/llms.txt
Use this file to discover all available pages before exploring further.
SceneGraph API
Central data structure for the document tree. Nodes stored in a flat Map<string, SceneNode> with parent-child relationships.
import { SceneGraph, generateId } from '@open-pencil/core'
import type { SceneNode, NodeType } from '@open-pencil/core'
Constructor
const graph = new SceneGraph()
Creates a new scene graph with a root FRAME node (id 0:1) and a default page (CANVAS node).
Properties
nodes: Map<string, SceneNode>
Flat storage of all nodes. Use getNode(id) instead of direct access.
images: Map<string, Uint8Array>
Image hash → raw bytes. Used by IMAGE fills.
variables
variables: Map<string, Variable>
Design variables (colors, numbers, strings, booleans).
variableCollections
variableCollections: Map<string, VariableCollection>
Variable collections with modes.
ID of the root document node (always 0:1).
Node Management
createNode()
createNode(
type: NodeType,
parentId: string,
overrides?: Partial<SceneNode>
): SceneNode
Creates a new node and adds it to the parent’s children.
const rect = graph.createNode('RECTANGLE', pageId, {
x: 50,
y: 50,
width: 100,
height: 100,
fills: [{ type: 'SOLID', color: { r: 1, g: 0, b: 0 }, opacity: 1, visible: true }]
})
getNode()
getNode(id: string): SceneNode | undefined
Returns node by ID.
updateNode()
updateNode(id: string, changes: Partial<SceneNode>): void
Updates node properties and clears absolute position cache.
graph.updateNode(rect.id, {
width: 200,
fills: [{ type: 'SOLID', color: { r: 0, g: 1, b: 0 }, opacity: 1, visible: true }]
})
deleteNode()
deleteNode(id: string): void
Deletes node and all descendants. Cannot delete root.
getAllNodes()
getAllNodes(): Iterable<SceneNode>
Returns all nodes in the graph.
Tree Operations
getChildren()
getChildren(id: string): SceneNode[]
Returns direct children of a node.
reparentNode()
reparentNode(nodeId: string, newParentId: string): void
Moves node to a new parent, preserving absolute position. Prevents circular references.
graph.reparentNode(rect.id, frameId)
reorderChild()
reorderChild(nodeId: string, parentId: string, insertIndex: number): void
Moves node to a specific index in parent’s children array (z-order).
cloneTree()
cloneTree(
sourceId: string,
parentId: string,
overrides?: Partial<SceneNode>
): SceneNode | null
Recursively clones a node and all descendants.
const copy = graph.cloneTree(rect.id, pageId, { x: 200 })
isDescendant()
isDescendant(childId: string, ancestorId: string): boolean
Checks if a node is a descendant of another.
isContainer()
isContainer(id: string): boolean
Returns true for CANVAS, FRAME, GROUP, SECTION, COMPONENT, COMPONENT_SET, INSTANCE.
Geometry
getAbsolutePosition()
getAbsolutePosition(id: string): { x: number; y: number }
Returns node position in canvas coordinates (sum of ancestor positions). Result is cached.
getAbsoluteBounds()
getAbsoluteBounds(id: string): Rect
Returns { x, y, width, height } in canvas coordinates.
clearAbsPosCache()
Invalidates absolute position cache. Called automatically by updateNode() and reparentNode().
Hit Testing
hitTest()
hitTest(px: number, py: number, scopeId?: string): SceneNode | null
Finds topmost visible node at canvas coordinates. Stops at component/instance boundaries.
hitTestDeep()
hitTestDeep(px: number, py: number, scopeId?: string): SceneNode | null
Like hitTest() but recurses into components/instances (for double-click).
hitTestFrame()
hitTestFrame(
px: number,
py: number,
excludeIds: Set<string>,
scopeId?: string
): SceneNode | null
Finds deepest container frame at coordinates, excluding specified IDs.
addPage()
addPage(name: string): SceneNode
Creates a new CANVAS node (page) under root.
getPages()
getPages(includeInternal = false): SceneNode[]
Returns all pages. Excludes internal pages (e.g., component library) unless includeInternal is true.
Components & Instances
createInstance()
createInstance(
componentId: string,
parentId: string,
overrides?: Partial<SceneNode>
): SceneNode | null
Creates an instance of a component. Clones children with componentId links.
const instance = graph.createInstance(component.id, pageId, { x: 300 })
syncInstances()
syncInstances(componentId: string): void
Propagates changes from component to all instances. Respects instance overrides.
getInstances()
getInstances(componentId: string): SceneNode[]
Returns all instances of a component.
getMainComponent()
getMainComponent(instanceId: string): SceneNode | undefined
Returns the component for an instance.
detachInstance()
detachInstance(instanceId: string): void
Converts instance to a regular frame.
Variables
createVariable()
createVariable(
name: string,
type: VariableType,
collectionId: string,
value?: VariableValue
): Variable
Creates a design variable.
const colorVar = graph.createVariable('Primary', 'COLOR', collectionId, {
r: 0.2,
g: 0.4,
b: 0.8
})
addVariable()
addVariable(variable: Variable): void
Adds an existing variable to the graph.
removeVariable()
removeVariable(id: string): void
Removes variable and unbinds it from all nodes.
resolveVariable()
resolveVariable(
variableId: string,
modeId?: string,
visited?: Set<string>
): VariableValue | undefined
Resolves variable value for a mode, following alias chains.
bindVariable()
bindVariable(nodeId: string, field: string, variableId: string): void
Binds a node property to a variable.
graph.bindVariable(rect.id, 'fills.0.color', colorVar.id)
unbindVariable()
unbindVariable(nodeId: string, field: string): void
Removes variable binding.
Variable Collections
createCollection()
createCollection(name: string): VariableCollection
Creates a variable collection with default mode.
addCollection()
addCollection(collection: VariableCollection): void
removeCollection()
removeCollection(id: string): void
Removes collection and all its variables.
setActiveMode()
setActiveMode(collectionId: string, modeId: string): void
Sets the active mode for a collection (for variable resolution).
getActiveModeId()
getActiveModeId(collectionId: string): string
Returns the active mode ID for a collection.
Tree Traversal
flattenTree()
flattenTree(parentId?: string, depth = 0): Array<{ node: SceneNode; depth: number }>
Returns flattened tree for layer panel rendering.
const flat = graph.flattenTree(pageId)
for (const { node, depth } of flat) {
console.log(' '.repeat(depth * 2) + node.name)
}
Utilities
generateId()
export function generateId(): string
Generates a unique node ID in the format 0:N (local IDs).
import { generateId } from '@open-pencil/core'
const id = generateId() // "0:42"
Type Definitions
export interface SceneNode {
id: string
type: NodeType
name: string
parentId: string | null
childIds: string[]
x: number
y: number
width: number
height: number
rotation: number
fills: Fill[]
strokes: Stroke[]
effects: Effect[]
opacity: number
cornerRadius: number
visible: boolean
locked: boolean
clipsContent: boolean
blendMode: BlendMode
// Text
text: string
fontSize: number
fontFamily: string
fontWeight: number
italic: boolean
// Layout
layoutMode: LayoutMode
primaryAxisAlign: LayoutAlign
itemSpacing: number
paddingTop: number
// ... 40+ more properties
}
export type VariableType = 'COLOR' | 'FLOAT' | 'STRING' | 'BOOLEAN'
export interface Variable {
id: string
name: string
type: VariableType
collectionId: string
valuesByMode: Record<string, VariableValue>
description: string
hiddenFromPublishing: boolean
}