scenegraph

The scenegraph is a node tree which represents the structure of the XD document. It closely matches the hierarchy seen in the Layers panel
inside XD. Some scenenodes may contain children (e.g., a Group or Artboard), while others are leaf nodes (e.g., a Rectangle or Text node).
The root of the scenegraph contains all Artboards that exist in the document, as well as all pasteboard content (nodes that are not
contained by any artboard).

You can modify properties on any scenenodes within the current edit context, and add leaf nodes to the current
edit context, but you cannot make structural changes directly to the scenegraph tree. Instead, use commands.

Typically, you access scenegraph nodes via the selection argument that is passed to your plugin command, or by
traversing the entire document tree using the documentRoot argument that is passed to your plugin command. These
objects are also accessible on the scenegraph module for convenience.

sceneNode.guid : string

Returns a unique identifier for this node that stays the same when the file is closed & reopened, or if the node is moved to a different part of the document. Cut-Paste will result in a new guid, however.

sceneNode.visible : boolean

False if this node has been hidden by the user (eyeball toggle in Layers panel). If true, the node may still be invisible for other reasons: a parent or grandparent has visible=false, the node has opacity=0%, the node is clipped by a mask, etc.

sceneNode.opacity : number (0.0 - 1.0)

Node's opacity setting. The overall visual opacity seen in the document is determined by combining this value with the opacities of the node's entire parent chain, as well as the opacity settings of its fill/stroke properties if this is a leaf node.

sceneNode.transform : !Matrix

Affine transform matrix that converts from the node's local coordinate space to its parent's coordinate space. The matrix never has
skew or scale components, and if this node is an Artboard the matrix never has rotation either. Rather than reading the raw matrix values
directly, it may be easier to use the translation and rotation properties.

sceneNode.translation : !{x:number, y:number}

The translate component of this node's transform. Since translation is applied after any rotation in
the transform Matrix, translation occurs along the parent's X/Y axes, not the node's own local X/Y axes. This is equivalent to
the e & f fields in the transform Matrix.

The node's path bounds in its own local coordinate space. This coordinate space may be rotated and translated relative to the parent's coordinate space. Path bounds match the selection outline seen in XD, but exclude some visual parts of the node (outerstroke, drop shadow / blur, etc.).

The visual top-left of a node's path bounds is located at (localBounds.x, localBounds.y). This value is not necessarily (0,0) in the local coordinate space: for example, a text node's baseline is at y=0 in local coordinates, so the top of the text has a negative y value.

The node's path bounds in its parent's coordinate space (represented by a bounding box aligned with the parent's X/Y axes - so if the node has rotation, the top-left of the node is not necessarily located at the top-left of boundsInParent). Path bounds match the selection outline seen in XD, but exclude some visual parts of the node (outer stroke, drop shadow / blur, etc.).

The position of the node's upper-left corner (localBounds.x, localBounds.y) in its parent's coordinate space. If the node is
rotated, this is not the same as the top-left corner of boundsInParent.
This is a shortcut for node.transform.transformPoint({x: node.localBounds.x, y: node.localBounds.y})

The position of the node's centerpoint in its own local coordinate space. Useful as an argument to rotateAround.
This is a shortcut for {x: localBounds.x + localBounds.width/2, y: localBounds.y + localBounds.height/2})

The node's draw bounds in document-global coordinate space. Draw bounds are larger than the selection outline seen in XD, including outer stroke, drop shadow / blur, etc. - every visible pixel of the node is encompassed by these bounds. This matches the image dimensions if the node is exported as a PNG/JPEG bitmap.

sceneNode.pluginData : *

Since: XD 14

Metadata specific to your plugin. Must be a value which can be converted to a JSON string, or undefined to clear the
stored metadata on this node.

Metadata is persisted with the document when it is saved. Duplicating a node (including across documents, via copy-paste)
will duplicate the metadata with it. If the node lies within a Symbol or Repeat Grid, all instances of the node will have
identical metadata (changes in one copy will automatically be synced to the other copy). Metadata stored by this plugin
cannot be accessed by other plugins - each plugin has its own isolated metadata storage.

sceneNode.removeFromParent()

sceneNode.moveInParentCoordinates(deltaX, deltaY)

Move the node by the given number of pixels along the parent's X/Y axes (if this node has no rotation, this is identical to
moving the node along its own local X/Y axes). This is equivalent to modifying the value returned by 'translation' and then
setting it back.

Point in this node's parent's coordinate space to move registrationPoint to

Example

// Place this node's top-left corner at the centerpoint of its parentlet parentCenter = node.parent.localCenterPoint; // parent's center in parent's coordinateslet nodeBounds = node.localBounds; // node's bounds in its own local coordinateslet nodeTopLeft = {x: nodeBounds.x, y: nodeBounds.y}; // node's top left corner in its own local coordinates
node.placeInParentCoordinates(nodeTopLeft, parentCenter);

sceneNode.rotateAround(deltaAngle, rotationCenter)

Rotate the node clockwise by the given number of degrees around the given point in the plugin's local coordinate space. If this node
already has nonzero rotation, this operation adds to its existing angle.

RootNode

Class representing the root node of the document. All Artboards are children of this node, as well as any pasteboard content that
does not lie within an Artboard. Artboards must be grouped contiguously at the bottom of this node's z order. The root node has no
visual appearance of its own.

Group

Masked Groups, created by the Object > Mask With Shape command
You can determine whether a group is masked by checking the mask property.

Groups and other containers cannot be created directly using scenenode constructors, since you can't add a populated Group to the
scenegraph (you can't add subtrees all at once) nor can you add an empty Group and then add children to it (can't add nodes outside
the scope of the current edit context). Instead, to create Groups and other nested structures, use commands.

In a Mask Group, the mask shape is included in the group's children list, at the top of the z order. It is not visible - only its
path outline is used, for clipping the group.

Example

let commands = require("commands");
// Newly created shape nodeslet shape1 = ...,
shape2 = ...;
// Add both nodes to the current edit context first
selection.insertionParent.addChild(shape1);
selection.insertionParent.addChild(shape2);
// Select both shapes, then run the Group command
selection.items = [shape1, shape2];
commands.group();
let group = selection.items[0]; // selection has been set to the new Group node afterward

group.removeAllChildren()

The mask shape applied to this group, if any. This object is also present in the group's children list. Though it has no direct visual appearance of its own, the mask affects the entire group's appearance by clipping all its other content.

GraphicNode

Base class for nodes that have a stroke and/or fill. This includes leaf nodes such as Rectangle, as well as BooleanGroup
which is a container node. If you create a shape node, it will not be visible unless you explicitly give it either a stroke
or a fill.

graphicNode.fillEnabled : boolean

graphicNode.stroke : ?Color

Default: null

The stroke color applied to this shape, if any. If this property is null orstrokeEnabled is false, no stroke is drawn.
Freshly created nodes have no stroke by default. Artboard objects ignore stroke settings.

Depending on the strokeWidth and strokePosition, the path outline
of a node may need to be positioned on fractional pixels in order for the stroke itself to be crisply aligned to the pixel grid.
For example, if a horizontal line uses a 1px center stroke, the line's y should end in .5 to keep the stroke on-pixel.

graphicNode.strokeMiterLimit : number >= 0

graphicNode.strokeDashArray : !Array<number>

Default: []

Empty array indicates a solid stroke. If non-empty, values represent the lengths of rendered and blank segments of the
stroke's dash pattern, repeated along the length of the stroke. The first value is the length of the first solid segment.
If the array is odd length, the items are copied to double the array length. For example, [3] produces the same effect
as [3, 3].

The appearance of each segment's start/end follows the strokeEndCaps setting.

graphicNode.blur : ?Blur

Default: null

The node's object blur or background blur settings, if applicable (a node may not have both types of blur at once). If there is no blur
effect applied, this property may be null orblur.visible may be false.

To modify an existing blur, always be sure to re-invoke the blur setter rather than just changing the Blur object's properties inline.
See "Properties with object values".

graphicNode.pathData : string

Returns a representation of the node's outline in SVG <path> syntax. Note that only nodes with strokePosition ==
GraphicNode.CENTER_STROKE can be faithfully rendered in actual SVG using the exact pathData shown here.

rectangle.hasRoundedCorners : boolean

rectangle.setAllCornerRadii(radius)

Set the rounding radius of all four corners of the Rectangle to the same value. The actual corner radius that is rendered is capped based on
the size of the rectangle even if the radius value set here is higher (see effectiveCornerRadii.

The actual corner radius that is rendered may be capped by the size of the rectangle. Returns the actual radii that
are currently in effect, which may be smaller than the cornerRadii values as a result.

Artboard

Artboard container node. All Artboards must be children of the root node (they cannot be nested), and they must be placed below all
pasteboard content in the z order.

Artboards can have a background fill, but the stroke, shadow, and blur settings are all ignored. Artboards cannot be locked or hidden,
or have opacity < 100%.

Generally, all nodes that overlap an Artboard are children of that artboard, and nodes that don't overlap any Artboard are children
of the root (pasteboard). XD ensures this automatically: if a node is modified in any way that changes whether it overlaps an
Artboard, its parent will automatically be changed accordingly after the edit operation finishes.

line.setStartEnd(startX, startY, endX, endY)

Set the start and end points of the Line in local coordinate space. The values may be normalized by this setter, shifting the node's
translation and counter-shifting the start/end points. So the start/end getters may return values different from the values you
passed this setter, even though the line's visual bounds and appearance are the same.

Path

Arbitrary vector Path leaf node shape. Paths can be open or closed, and a Path may include multiple disjoint sections (aka a "compound
path"). Even open Paths may have a fill - the fill is drawn as if the Path were closed with a final "Z" segment.

The path may not start at (0,0) in local coordinates, for example if it starts with a move ("M") segment.

path.pathData : string

Representation of the path outline in SVG <path> syntax. Unlike other node types, pathData is writable here. Syntax is
automatically normalized, so the getter may return a slightly different string than what you passed to the setter.

BooleanGroup

BooleanGroup container node - although it has fill/stroke/etc. properties like a leaf shape node, it is a container
with children. Its visual appearance is determined by generating a path via a nondestructive boolean operation on all
its children's paths.

It is not currently possible for plugins to create a new BooleanGroup node, aside from using commands.duplicate
to clone existing BooleanGroups.

Text bounds and layout work differently depending on the type of text:

Point Text - The baseline is at y=0 in the node's local coordinate system. Horizontally, local x=0 is the anchor point that the
text grows from / shrinks toward when edited. This anchor depends on the justification: for example, if the text is centered, x=0 is
the horizontal centerpoint of the text. The bounding box leaves enough space for descenders, uppercase letters, and accent marks,
even if the current string does not contain any of those characters. This makes aligning text based on its bounds behave more
consistently.

Area Text - The baseline is at a positive y value in local coordinates, and its local (0, 0) is the top left of the areaBox. Text
always flows to the right and down from this local origin regardless of justification.

Array of text ranges and their character style settings. Each range covers a set number of characters in the text content. Ranges
are contiguous, with each one starting immediately after the previous one. Any characters past the end of the last range use the
same style as the last range.

When setting styleRanges, any fields missing from a given range default to the existing values from the last range in the old
value of styleRanges. The styleRanges getter always returns fully realized range objects with all fields specified.

text.fontFamily : string

Since: XD 14

Set the font family across all style ranges, or get the font family of the last style range (font family of all the text
if one range covers all the text). Plugins should not assume any particular default value for fontFamily.

text.fontSize : number > 0

Since: XD 14

Font size in document pixels. Set the font size across all style ranges, or get the font size of the last style range
(font size of all the text if one range covers all the text). Plugins should not assume any particular default value for
fontSize.

text.fill : ?Color

Default: null

Set the text color across all style ranges, or get the color of the last style range (color of all the text if one range
covers all the text). Unlike most other nodes, text only allows a solid color fill - gradients and image fills are not
supported.

text.flipY : boolean

text.textAlign : string

Default: ALIGN_LEFT
Horizontal alignment: Text.ALIGN_LEFT, ALIGN_CENTER, or ALIGN_RIGHT. This setting affects the layout of multiline text, and for point
text it also affects how the text is positioned relative to its anchor point (x=0 in local coordinates) and what direction the text
grows when edited by the user.

Changing textAlign on existing point text will cause it to shift horizontally. To change textAlign while keeping the text in a fixed
position, shift the text horizontally (moving its anchor point) to compensate:

text.lineSpacing : number > 0, or 0 for default spacing

Default: 0

Distance between baselines in multiline text, in document pixels. The special value 0 causes XD to use the default line spacing
defined by the font given the current font size & style.

This property is not automatically adjusted when fontSize changes, if line spacing is not set to 0, the line spacing will stay
fixed while the font size changes, shifting the spacing's proportional relationship to font size. If the value is 0, then the
rendered line spacing will change to match the new font size, since 0 means the spacing is dynamically calculated from the current
font settings.

text.paragraphSpacing : number >= 0

Default: 0Since: XD 14

Additional distance between paragraphs, in document pixels, added to the lineSpacing amount (soft line breaks in area text are
separated only by lineSpacing, while hard line breaks are separated by lineSpacing + paragraphSpacing). Unlike lineSpacing, 0
is not a special value; it just means no added spacing.

Similar to lineSpacing, this property is not automatically adjusted when fontSize changes. The paragraph spacing amount will stay
fixed while the font size changes, shifting the spacing's proportional relationship to font size.

text.areaBox : ?{width:number, height:number}

Null for point text. For area text, specifies the size of the rectangle within which text is wrapped and clipped.

Changing point text to area text or vice versa will change the origin / anchor point of the text, thus changing its localBounds,
but it will also automatically change the node's transform so its globalBounds and boundsInParent origins remain unchanged.

Changing area text to point text will also automatically insert hard line breaks ("\n") into the text to match the previous line
wrapping's appearance exactly.

SymbolInstance

Container node representing one instance of a Symbol. Changes made within a symbol instance are automatically synced to all other
other instances of the symbol - with certain exceptions, called "overrides."

It is not currently possible for plugins to create a new Symbol definition or a new SymbolInstance node, aside from using
commands.duplicate to clone existing SymbolInstances.

symbolInstance.isMaster : boolean

RepeatGrid

Repeat Grid container node containing multiple grid cells, each one a child Group. Changes within one cell are automatically synced
to all the other cells - with certain exceptions, called "overrides." A Repeat Grid also defines a rectangular clipping mask which
determines how may cells are visible (new cells are automatically generated as needed if the Repeat Grid is resized larger).

Each grid cell is a Group that is an immediate child of the RepeatGrid. These groups are automatically created and destroyed as
needed when the RepeatGrid is resized.

It is not currently possible for plugins to create a new RepeatGrid node, aside from using commands.duplicate
to clone existing RepeatGrids.

repeatGrid.paddingX : number

repeatGrid.paddingY : number

repeatGrid.cellSize : !{width: number, height: number}

The size of each grid cell. The size of each cell's content can vary slightly due to text overrides; the cell size is always set to the width of the widest cell content and the height of the tallest cell content.

repeatGrid.attachTextDataSeries(textNode, textValues)

Attach a sequence of text values to the instances of a given text node across all the cells of a Repeat Grid. The sequence is
repeated as necessary to cover all the grid cells. This is a persistent data binding, so if the Repeat Grid is resized later
to increase the number of grid cells, items from this sequence will be used to fill the text values of the new cells.

You can call this API from either of two different edit contexts:

Edit context where the RepeatGrid node is in scope (where properties of the RepeatGrid node itself could be edited) - e.g.
when the RepeatGrid is selected

Edit context where textNode is in scope (where properties of the textNode could be edited) - e.g. when textNode is selected
or when the user has otherwise drilled down into the grid cell containing it.

A Text node exemplar that would be in scope for editing if the current edit context was one of this RepeatGrid's cells. The data series will be bound to this text node and all corresponding copies of it in the other grid cells.

textValues

!Array<string>

Array of one or more strings. Empty strings are ignored.

repeatGrid.attachImageDataSeries(shapeNode, images)

Attach a sequence of image fills to the instances of a given shape node across all the cells of a Repeat Grid. The sequence is
repeated as necessary to cover all the grid cells. This is a persistent data binding, so if the Repeat Grid is resized later
to increase the number of grid cells, items from this sequence will be used to set the image fill in the new cells.

You can call this API from either of two different edit contexts:

Edit context where the RepeatGrid node is in scope (where properties of the RepeatGrid node itself could be edited) - e.g.
when the RepeatGrid is selected

Edit context where shapeNode is in scope (where properties of the shapeNode could be edited) - e.g. when shapeNode is selected
or when the user has otherwise drilled down into the grid cell containing it.

A shape node exemplar that would be in scope for editing if the current edit context was one of this RepeatGrid's cells. The image series will be bound to this node and all corresponding copies of it in the other grid cells. Must be a node type that supports image fills (e.g. Rectangle, but not Text or Line).